Skip to content

Commit

Permalink
[FAB-7771] Refactor config impl constructor
Browse files Browse the repository at this point in the history
This patch changes the config implementation to a functional option
style. This work is in preperation for the SDK cleanup task.
This patch does not include the config template functionality, but
this can be revisited in a future task.

Change-Id: Ie333b996f0551c6bacdaaa9487654c290831a1a5
Signed-off-by: Troy Ronda <troy@troyronda.com>
  • Loading branch information
troyronda committed Jan 17, 2018
1 parent a03806b commit 8d22afb
Show file tree
Hide file tree
Showing 16 changed files with 422 additions and 187 deletions.
11 changes: 0 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,17 +171,6 @@ make integration-tests-local
# Note: you should generally prefer the scripted version to setup parameters for you.
```

#### Using default config

Default SDK Go configurations are found in the code under /pkg/config/config.yaml

To override the default in non Dev environment, set the default path in the following environment variable:

**FABRIC_SDK_CONFIG_PATH**=/path/to/default/config yaml(without specifying the yaml file name)

This path value must be a directory. It must contain a default 'config.yaml' file.
Note that this default config is used only if environment configuration yaml file is missing to ensure all environment variables are created regardless of their values.

#### Testing with Local Build of Fabric (Advanced)

Alternatively you can use a local build of Fabric using the following commands:
Expand Down
2 changes: 1 addition & 1 deletion def/factory/defclient/orgfactory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func TestNewMSPClient(t *testing.T) {
func TestNewCredentialManager(t *testing.T) {
factory := NewOrgClientFactory()

config, err := config.InitConfig("../../../test/fixtures/config/config_test.yaml")
config, err := config.FromFile("../../../test/fixtures/config/config_test.yaml")
if err != nil {
t.Fatalf(err.Error())
}
Expand Down
4 changes: 2 additions & 2 deletions def/factory/defcore/corefactory.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ func NewProviderFactory() *ProviderFactory {
func (f *ProviderFactory) NewConfigProvider(sdkOpts apisdk.SDKOpts) (apiconfig.Config, error) {
// configBytes takes precedence over configFile
if sdkOpts.ConfigBytes != nil && len(sdkOpts.ConfigBytes) > 0 {
return configImpl.InitConfigFromBytes(sdkOpts.ConfigBytes, sdkOpts.ConfigType)
return configImpl.FromRaw(sdkOpts.ConfigBytes, sdkOpts.ConfigType)
}
return configImpl.InitConfig(sdkOpts.ConfigFile)
return configImpl.FromFile(sdkOpts.ConfigFile)
}

// NewStateStoreProvider creates a KeyValueStore using the SDK's default implementation
Expand Down
4 changes: 3 additions & 1 deletion def/factory/defcore/corefactory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ import (
func TestNewConfigProvider(t *testing.T) {
factory := NewProviderFactory()

sdkOpts := apisdk.SDKOpts{}
sdkOpts := apisdk.SDKOpts{
ConfigFile: "../../../test/fixtures/config/config_test.yaml",
}

config, err := factory.NewConfigProvider(sdkOpts)
if err != nil {
Expand Down
194 changes: 140 additions & 54 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"crypto/tls"
"crypto/x509"
"encoding/pem"
"io"
"io/ioutil"
"math/rand"
"os"
Expand All @@ -35,7 +36,7 @@ import (
var logger = logging.NewLogger(logModule)

const (
cmdRoot = "fabric_sdk"
cmdRoot = "FABRIC_SDK"
logModule = "fabric_sdk_go"
defaultTimeout = time.Second * 5
)
Expand All @@ -46,85 +47,169 @@ type Config struct {
networkConfig *apiconfig.NetworkConfig
networkConfigCached bool
configViper *viper.Viper
opts options
}

// InitConfigFromBytes will initialize the configs from a bytes array
func InitConfigFromBytes(configBytes []byte, configType string) (*Config, error) {
myViper := getNewViper(cmdRoot)
type options struct {
envPrefix string
templatePath string
template *Config
}

// Option configures the package.
type Option func(opts *options) error

// FromReader loads configuration from in.
// configType can be "json" or "yaml".
func FromReader(in io.Reader, configType string, opts ...Option) (*Config, error) {
c, err := newConfig(opts...)
if err != nil {
return nil, err
}

if configType == "" {
return nil, errors.New("empty config type")
}

if len(configBytes) > 0 {
buf := bytes.NewBuffer(configBytes)
logger.Debugf("buf Len is %d, Cap is %d: %s", buf.Len(), buf.Cap(), buf)
// read config from bytes array, but must set ConfigType
// for viper to properly unmarshal the bytes array
myViper.SetConfigType(configType)
myViper.ReadConfig(buf)
// read config from bytes array, but must set ConfigType
// for viper to properly unmarshal the bytes array
c.configViper.SetConfigType(configType)
c.configViper.MergeConfig(in)

return initConfig(c)
}

// FromFile reads from named config file
func FromFile(name string, opts ...Option) (*Config, error) {
c, err := newConfig(opts...)
if err != nil {
return nil, err
}

if name == "" {
return nil, errors.New("filename is required")
}

// create new viper
c.configViper.SetConfigFile(name)

// If a config file is found, read it in.
err = c.configViper.MergeInConfig()
if err == nil {
logger.Debugf("Using config file: %s", c.configViper.ConfigFileUsed())
} else {
return nil, errors.New("empty config bytes array")
return nil, errors.Wrap(err, "loading config file failed")
}

setLogLevel(myViper)
return initConfig(c)
}

tlsCertPool, err := getCertPool(myViper)
// FromRaw will initialize the configs from a byte array
func FromRaw(configBytes []byte, configType string, opts ...Option) (*Config, error) {

if len(configBytes) == 0 {
return nil, errors.New("empty config byte array")
}

buf := bytes.NewBuffer(configBytes)
logger.Debugf("config.FromRaw buf Len is %d, Cap is %d: %s", buf.Len(), buf.Cap(), buf)

return FromReader(buf, configType, opts...)
}

/*
// FromDefaultPath loads configuration from the default path
func FromDefaultPath(opts ...Option) (*Config, error) {
optsWithDef := append(opts, withTemplatePathFromEnv("CONFIG_PATH"))
c, err := newConfig(optsWithDef...)
if err != nil {
return nil, err
}
if c.opts.templatePath == "" {
return nil, errors.New("Configuration path is not set")
}
return &Config{tlsCertPool: tlsCertPool, configViper: myViper}, nil
return initConfig(c)
}
*/

// getNewViper returns a new instance of viper
func getNewViper(cmdRootPrefix string) *viper.Viper {
myViper := viper.New()
myViper.SetEnvPrefix(cmdRootPrefix)
myViper.AutomaticEnv()
replacer := strings.NewReplacer(".", "_")
myViper.SetEnvKeyReplacer(replacer)
return myViper
// WithEnvPrefix defines the prefix for environment variable overrides.
// See viper SetEnvPrefix for more information.
func WithEnvPrefix(prefix string) Option {
return func(opts *options) error {
opts.envPrefix = prefix
return nil
}
}

// InitConfig reads in config file
func InitConfig(configFile string) (*Config, error) {
return initConfigWithCmdRoot(configFile, cmdRoot)
/*
// WithTemplatePath loads the named file to populate a configuration template prior to loading the instance configuration.
func WithTemplatePath(path string) Option {
return func(opts *options) error {
opts.templatePath = path
return nil
}
}
*/

// initConfigWithCmdRoot reads in a config file and allows the
// environment variable prefixed to be specified
func initConfigWithCmdRoot(configFile string, cmdRootPrefix string) (*Config, error) {
myViper := getNewViper(cmdRootPrefix)
/*
func withTemplatePathFromEnv(e string) Option {
return func(opts *options) error {
if opts.templatePath == "" {
opts.templatePath = os.Getenv(opts.envPrefix + "_" + e)
}
// load default config
// for now, loading DefaultConfig only works with File, not []Byte due to viper's ConfigType
err := loadDefaultConfig(myViper)
if err != nil {
return nil, err
return nil
}
}
*/

if configFile != "" {
// create new viper
myViper.SetConfigFile(configFile)
// If a config file is found, read it in.
err := myViper.ReadInConfig()
func newConfig(opts ...Option) (*Config, error) {
o := options{
envPrefix: cmdRoot,
}

if err == nil {
logger.Debugf("Using config file: %s", myViper.ConfigFileUsed())
} else {
return nil, errors.Wrap(err, "loading config file failed")
for _, option := range opts {
err := option(&o)
if err != nil {
return nil, errors.WithMessage(err, "Error in option passed to New")
}
}

setLogLevel(myViper)
tlsCertPool, err := getCertPool(myViper)
v := newViper(o.envPrefix)
c := Config{
configViper: v,
opts: o,
}

err := c.loadTemplateConfig()
if err != nil {
return nil, err
}

return &c, nil
}

func newViper(cmdRootPrefix string) *viper.Viper {
myViper := viper.New()
myViper.SetEnvPrefix(cmdRootPrefix)
myViper.AutomaticEnv()
replacer := strings.NewReplacer(".", "_")
myViper.SetEnvKeyReplacer(replacer)
return myViper
}

func initConfig(c *Config) (*Config, error) {
setLogLevel(c.configViper)
tlsCertPool, err := getCertPool(c.configViper)
if err != nil {
return nil, err
}
c.tlsCertPool = tlsCertPool

logger.Infof("%s logging level is set to: %s", logModule, lu.LogLevelString(logging.GetLevel(logModule)))
return &Config{tlsCertPool: tlsCertPool, configViper: myViper}, nil
logger.Infof("config %s logging level is set to: %s", logModule, lu.LogLevelString(logging.GetLevel(logModule)))
return c, nil
}

func getCertPool(myViper *viper.Viper) (*x509.CertPool, error) {
Expand Down Expand Up @@ -155,16 +240,17 @@ func setLogLevel(myViper *viper.Viper) {
}

// load Default config
func loadDefaultConfig(myViper *viper.Viper) error {
func (c *Config) loadTemplateConfig() error {
// get Environment Default Config Path
defaultPath := os.Getenv("FABRIC_SDK_CONFIG_PATH")
if defaultPath == "" {
templatePath := c.opts.templatePath
if templatePath == "" {
return nil
}

// if set, use it to load default config
myViper.AddConfigPath(substPathVars(defaultPath))
err := myViper.ReadInConfig() // Find and read the config file
if err != nil { // Handle errors reading the config file
c.configViper.AddConfigPath(substPathVars(templatePath))
err := c.configViper.ReadInConfig() // Find and read the config file
if err != nil { // Handle errors reading the config file
return errors.Wrap(err, "loading config file failed")
}
return nil
Expand Down
Loading

0 comments on commit 8d22afb

Please sign in to comment.