From 20f4f68c4e015f4943148979a9a0ebb0bfff1cb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20M=C3=ADchal?= Date: Sun, 4 Jul 2021 00:03:05 +0200 Subject: [PATCH] cmd/root, pkg/utils: Add support for configuration files It looks like there are some oddities with Viper [1]. The errors can't be examined with errors.As [2] and Viper doesn't actually throw ConfigFileNotFoundError if a configuration file is not found. Secondly, there's no way to find out if a key was actually specified in a configuration file. The InConfig API doesn't return 'true' even if a key was mentioned in a configuration file, and the IsSet API returns 'true' even if the key was only set via SetDefault in the code. Some changes by Debarshi Ray. [1] https://pkg.go.dev/github.com/spf13/viper [2] https://blog.golang.org/go1.13-errors https://github.com/containers/toolbox/pull/828 https://github.com/containers/toolbox/pull/851 --- src/cmd/root.go | 4 ++ src/go.mod | 1 + src/go.sum | 11 +++++ src/pkg/utils/utils.go | 108 ++++++++++++++++++++++++++++++++++++++--- 4 files changed, 116 insertions(+), 8 deletions(-) diff --git a/src/cmd/root.go b/src/cmd/root.go index 1ad842b..5e6f587 100644 --- a/src/cmd/root.go +++ b/src/cmd/root.go @@ -143,6 +143,10 @@ func preRun(cmd *cobra.Command, args []string) error { return err } + if err := utils.SetUpConfiguration(); err != nil { + return err + } + return nil } diff --git a/src/go.mod b/src/go.mod index cf9c9d2..cce3e5a 100644 --- a/src/go.mod +++ b/src/go.mod @@ -12,6 +12,7 @@ require ( github.com/mattn/go-isatty v0.0.8 github.com/sirupsen/logrus v1.4.2 github.com/spf13/cobra v0.0.5 + github.com/spf13/viper v1.3.2 github.com/stretchr/testify v1.7.0 golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 golang.org/x/sys v0.0.0-20190422165155-953cdadca894 diff --git a/src/go.sum b/src/go.sum index 9254b92..fbad155 100644 --- a/src/go.sum +++ b/src/go.sum @@ -1,3 +1,4 @@ +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/HarryMichal/go-version v1.0.0 h1:fXYa5vT46C3pULfSIgnfeNfSxJ9bCGZ2ERn/wKPlD6c= github.com/HarryMichal/go-version v1.0.0/go.mod h1:w3uLQ2NlFmZ01qBywppIbDplbPEjeBW8xywlluMcMsc= @@ -21,31 +22,39 @@ github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -61,9 +70,11 @@ golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/src/pkg/utils/utils.go b/src/pkg/utils/utils.go index 6baf06a..ae7c596 100644 --- a/src/pkg/utils/utils.go +++ b/src/pkg/utils/utils.go @@ -35,6 +35,7 @@ import ( "github.com/docker/go-units" "github.com/godbus/dbus/v5" "github.com/sirupsen/logrus" + "github.com/spf13/viper" "golang.org/x/sys/unix" ) @@ -570,6 +571,71 @@ func ImageReferenceHasDomain(image string) bool { return true } +func SetUpConfiguration() error { + logrus.Debug("Setting up configuration") + + configFiles := []string{ + "/etc/containers/toolbox.conf", + } + + userConfigDir, err := os.UserConfigDir() + if err != nil { + logrus.Debugf("Setting up configuration: failed to get the user config directory: %s", err) + return errors.New("failed to get the user config directory") + } + + userConfigPath := userConfigDir + "/containers/toolbox.conf" + configFiles = append(configFiles, []string{ + userConfigPath, + }...) + + viper.SetConfigType("toml") + + for _, configFile := range configFiles { + viper.SetConfigFile(configFile) + + if err := viper.MergeInConfig(); err != nil { + // Seems like Viper's errors can't be examined with + // errors.As. + + // Seems like Viper doesn't actually throw + // viper.ConfigFileNotFoundError if a configuration + // file is not found. We still check for it for the + // sake of completion or in case Viper uses it in a + // different version. + _, ok := err.(viper.ConfigFileNotFoundError) + if ok || os.IsNotExist(err) { + logrus.Debugf("Setting up configuration: file %s not found", configFile) + continue + } + + if _, ok := err.(viper.ConfigParseError); ok { + logrus.Debugf("Setting up configuration: failed to parse file %s: %s", configFile, err) + return fmt.Errorf("failed to parse file %s", configFile) + } + + logrus.Debugf("Setting up configuration: failed to read file %s: %s", configFile, err) + return fmt.Errorf("failed to read file %s", configFile) + } + } + + image, release, err := ResolveImageName("", "", "") + if err != nil { + logrus.Debugf("Setting up configuration: failed to resolve image name: %s", err) + return errors.New("failed to resolve image name") + } + + container, err := ResolveContainerName("", image, release) + if err != nil { + logrus.Debugf("Setting up configuration: failed to resolve container name: %s", err) + return errors.New("failed to resolve container name") + } + + ContainerNameDefault = container + + return nil +} + // ShortID shortens provided id to first 12 characters. func ShortID(id string) string { if len(id) > idTruncLength { @@ -581,6 +647,9 @@ func ShortID(id string) string { func ParseRelease(distro, release string) (string, error) { if distro == "" { distro = distroDefault + if viper.IsSet("general.distro") { + distro = viper.GetString("general.distro") + } } if _, supportedDistro := supportedDistros[distro]; !supportedDistro { @@ -694,30 +763,53 @@ func ResolveContainerName(container, image, release string) (string, error) { // If no image name is specified then the base image will reflect the platform of the host (even the version). // // If the host system is unknown then the base image will be 'fedora-toolbox' with a default version -func ResolveImageName(distro, image, release string) (string, string, error) { +func ResolveImageName(distroCLI, imageCLI, releaseCLI string) (string, string, error) { logrus.Debug("Resolving image name") - logrus.Debugf("Distribution: '%s'", distro) - logrus.Debugf("Image: '%s'", image) - logrus.Debugf("Release: '%s'", release) + logrus.Debugf("Distribution (CLI): '%s'", distroCLI) + logrus.Debugf("Image (CLI): '%s'", imageCLI) + logrus.Debugf("Release (CLI): '%s'", releaseCLI) - if distro == "" { + distro, image, release := distroCLI, imageCLI, releaseCLI + + if distroCLI == "" { distro = distroDefault + if viper.IsSet("general.distro") { + distro = viper.GetString("general.distro") + } } - if distro != distroDefault && release == "" { + if distro != distroDefault && releaseCLI == "" && !viper.IsSet("general.release") { return "", "", fmt.Errorf("release not found for non-default distribution %s", distro) } - if release == "" { + if releaseCLI == "" { release = releaseDefault + if viper.IsSet("general.release") { + release = viper.GetString("general.release") + } } - if image == "" { + if imageCLI == "" { image = getDefaultImageForDistro(distro, release) + + if viper.IsSet("general.image") && distroCLI == "" && releaseCLI == "" { + image = viper.GetString("general.image") + + release = ImageReferenceGetTag(image) + if release == "" { + release = releaseDefault + if viper.IsSet("general.release") { + release = viper.GetString("general.release") + } + } + } } else { release = ImageReferenceGetTag(image) if release == "" { release = releaseDefault + if viper.IsSet("general.release") { + release = viper.GetString("general.release") + } } }