func (c *Config) Patch(patch map[string]interface{}) (map[string]string, error) {
	values, err := c.Dump()
	if err != nil {
		return nil, errors.WithStack(err)
	}
	for name, value := range patch {
		values[name] = value
	}
	return c.update(values)
}
Patch changes only the configuration keys in the given map.

Return what has actually changed.
package config
import (
	"strconv"
	"time"
	"github.com/pkg/errors"
	"github.com/spoke-d/thermionic/internal/cluster/heartbeat"
	"github.com/spoke-d/thermionic/internal/config"
	"github.com/spoke-d/thermionic/internal/db"
)
type Tx interface {
	Config() (map[string]string, error)
	UpdateConfig(map[string]string) error
}
type ReadOnlyConfig struct {
	configMap config.Map  
}
func (c *ReadOnlyConfig) DiscoveryAddress() (string, error) {
	return c.configMap.GetString("discovery.https_address")
}
func (c *ReadOnlyConfig) DiscoveryOfflineThreshold() (time.Duration, error) {
	n, err := c.configMap.GetInt64("discovery.offline_threshold")
	if err != nil {
		return 0, errors.WithStack(err)
	}
	return time.Duration(n) * time.Second, nil
}
func (c *ReadOnlyConfig) HTTPSAllowedHeaders() (string, error) {
	return c.configMap.GetString("core.https_allowed_headers")
}
func (c *ReadOnlyConfig) HTTPSAllowedMethods() (string, error) {
	return c.configMap.GetString("core.https_allowed_methods")
}
func (c *ReadOnlyConfig) HTTPSAllowedOrigin() (string, error) {
	return c.configMap.GetString("core.https_allowed_origin")
}
func (c *ReadOnlyConfig) HTTPSAllowedCredentials() (bool, error) {
	return c.configMap.GetBool("core.https_allowed_credentials")
}
func (c *ReadOnlyConfig) ProxyHTTPS() (string, error) {
	return c.configMap.GetString("core.proxy_https")
}
func (c *ReadOnlyConfig) ProxyHTTP() (string, error) {
	return c.configMap.GetString("core.proxy_http")
}
func (c *ReadOnlyConfig) OfflineThreshold() (time.Duration, error) {
	n, err := c.configMap.GetInt64("cluster.offline_threshold")
	if err != nil {
		return 0, errors.WithStack(err)
	}
	return time.Duration(n) * time.Second, nil
}
func NewReadOnlyConfig(values map[string]string, schema config.Schema) (*ReadOnlyConfig, error) {
	configMap, err := config.New(schema, values)
	if err != nil {
		return nil, errors.Wrap(err, "failed to load config")
	}
	return &ReadOnlyConfig{
		configMap: configMap,
	}, nil
}
func (c *Config) Dump() (map[string]interface{}, error) {
	return c.configMap.Dump()
}
type Config struct {
	*ReadOnlyConfig
	clusterTx Tx  
}
func NewConfig(configMap config.Map, options ...Option) *Config {
	opts := newOptions()
	opts.configMap = configMap
	for _, option := range options {
		option(opts)
	}
	return &Config{
		ReadOnlyConfig: &ReadOnlyConfig{
			configMap: opts.configMap,
		},
		clusterTx: opts.clusterTx,
	}
}
func Load(tx Tx, schema config.Schema) (*Config, error) {
	values, err := tx.Config()
	if err != nil {
		return nil, errors.Wrap(err, "cannot fetch node config from database")
	}
	configMap, err := config.New(schema, values)
	if err != nil {
		return nil, errors.Wrap(err, "failed to load node config")
	}
	return &Config{
		ReadOnlyConfig: &ReadOnlyConfig{
			configMap: configMap,
		},
		clusterTx: tx,
	}, nil
}
func (c *Config) ReadOnly() *ReadOnlyConfig {
	return &ReadOnlyConfig{
		configMap: c.ReadOnlyConfig.configMap.Clone(),
	}
}
func (c *Config) Replace(values map[string]interface{}) (map[string]string, error) {
	return c.update(values)
}