github.com/awesome-flow/flow@v0.0.3-0.20190918184116-508d75d68a2c/pkg/cfg/yaml_provider.go (about)

     1  package cfg
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  
     7  	"github.com/awesome-flow/flow/pkg/types"
     8  	fsnotify "github.com/fsnotify/fsnotify"
     9  	yaml "gopkg.in/yaml.v2"
    10  )
    11  
    12  // Redefined in tests
    13  var readRaw = func(source string) (map[interface{}]interface{}, error) {
    14  	out := make(map[interface{}]interface{})
    15  	data, err := ioutil.ReadFile(source)
    16  	if err != nil {
    17  		return nil, fmt.Errorf("failed to read yaml config file %q: %s", source, err)
    18  	}
    19  	if err := yaml.Unmarshal(data, &out); err != nil {
    20  		return nil, err
    21  	}
    22  	return out, nil
    23  }
    24  
    25  type YamlProvider struct {
    26  	weight   int
    27  	source   string
    28  	options  *YamlProviderOptions
    29  	watcher  *fsnotify.Watcher
    30  	registry map[string]types.Value
    31  	ready    chan struct{}
    32  }
    33  
    34  type YamlProviderOptions struct {
    35  	Watch bool
    36  }
    37  
    38  var _ Provider = (*YamlProvider)(nil)
    39  
    40  func NewYamlProvider(repo *Repository, weight int) (*YamlProvider, error) {
    41  	return NewYamlProviderWithOptions(repo, weight, &YamlProviderOptions{})
    42  }
    43  
    44  func NewYamlProviderWithOptions(repo *Repository, weight int, options *YamlProviderOptions) (*YamlProvider, error) {
    45  	return NewYamlProviderFromSource(repo, weight, options, "")
    46  }
    47  
    48  func NewYamlProviderFromSource(repo *Repository, weight int, options *YamlProviderOptions, source string) (*YamlProvider, error) {
    49  	prov := &YamlProvider{
    50  		source:   source,
    51  		weight:   weight,
    52  		options:  options,
    53  		registry: make(map[string]types.Value),
    54  		ready:    make(chan struct{}),
    55  	}
    56  	repo.RegisterProvider(prov)
    57  	return prov, nil
    58  }
    59  
    60  func (yp *YamlProvider) Name() string      { return "yaml" }
    61  func (yp *YamlProvider) Depends() []string { return []string{"cli", "env"} }
    62  func (yp *YamlProvider) Weight() int       { return yp.weight }
    63  
    64  func (yp *YamlProvider) SetUp(repo *Repository) error {
    65  	defer close(yp.ready)
    66  
    67  	if len(yp.source) == 0 {
    68  		source, ok := repo.Get(types.NewKey(CfgPathKey))
    69  		if !ok {
    70  			return fmt.Errorf("Failed to get yaml config path from repo")
    71  		}
    72  		yp.source = source.(string)
    73  	}
    74  
    75  	// if _, err := os.Stat(yp.source); err != nil {
    76  	// 	return fmt.Errorf("failed to read yaml config %q: %s", yp.source, err)
    77  	// }
    78  
    79  	if yp.options.Watch {
    80  		watcher, err := fsnotify.NewWatcher()
    81  		if err != nil {
    82  			return fmt.Errorf("failed to start a yaml watcher: %s", err)
    83  		}
    84  		if err := watcher.Add(yp.source); err != nil {
    85  			return fmt.Errorf("failed to add a new watchable file %q: %s", yp.source, err)
    86  		}
    87  		yp.watcher = watcher
    88  
    89  		go yp.watch()
    90  	}
    91  
    92  	rawData, err := readRaw(yp.source)
    93  	if err != nil {
    94  		return err
    95  	}
    96  	for k, v := range flatten(rawData) {
    97  		yp.registry[k] = v
    98  		if repo != nil {
    99  			if err := repo.RegisterKey(types.NewKey(k), yp); err != nil {
   100  				return err
   101  			}
   102  		}
   103  	}
   104  
   105  	return nil
   106  }
   107  
   108  func flatten(in map[interface{}]interface{}) map[string]types.Value {
   109  	out := make(map[string]types.Value)
   110  	for k, v := range in {
   111  		if vmap, ok := v.(map[interface{}]interface{}); ok {
   112  			for sk, sv := range flatten(vmap) {
   113  				out[k.(string)+types.KeySepCh+sk] = types.Value(sv)
   114  			}
   115  		} else {
   116  			out[k.(string)] = types.Value(v)
   117  		}
   118  	}
   119  	return out
   120  }
   121  
   122  func (yp *YamlProvider) watch() {
   123  	for event := range yp.watcher.Events {
   124  		fmt.Printf("Received a fsnotify event: %#v", event)
   125  		//TODO (olegs): not implemented
   126  		// if event.Op&fsnotify.Write != 1 {
   127  		// 	continue
   128  		// }
   129  	}
   130  }
   131  
   132  func (yp *YamlProvider) TearDown(repo *Repository) error {
   133  	if yp.watcher != nil {
   134  		if err := yp.watcher.Close(); err != nil {
   135  			return fmt.Errorf("failed to terminate the yaml watcher: %q", err)
   136  		}
   137  	}
   138  	return nil
   139  }
   140  
   141  func (yp *YamlProvider) Get(key types.Key) (*types.KeyValue, bool) {
   142  	<-yp.ready
   143  	if v, ok := yp.registry[key.String()]; ok {
   144  		return &types.KeyValue{Key: key, Value: v}, ok
   145  	}
   146  	return nil, false
   147  }