...

Source file src/github.com/cybertec-postgresql/pgwatch/v3/internal/sources/yaml.go

Documentation: github.com/cybertec-postgresql/pgwatch/v3/internal/sources

     1  package sources
     2  
     3  // This file contains the implementation of the ReaderWriter interface for the YAML file.
     4  
     5  import (
     6  	"context"
     7  	"io/fs"
     8  	"os"
     9  	"path/filepath"
    10  	"slices"
    11  	"strings"
    12  
    13  	"gopkg.in/yaml.v3"
    14  )
    15  
    16  func NewYAMLSourcesReaderWriter(ctx context.Context, path string) (ReaderWriter, error) {
    17  	return &fileSourcesReaderWriter{
    18  		ctx:  ctx,
    19  		path: path,
    20  	}, nil
    21  }
    22  
    23  type fileSourcesReaderWriter struct {
    24  	ctx  context.Context
    25  	path string
    26  }
    27  
    28  func (fcr *fileSourcesReaderWriter) WriteSources(mds Sources) error {
    29  	yamlData, _ := yaml.Marshal(mds)
    30  	return os.WriteFile(fcr.path, yamlData, 0644)
    31  }
    32  
    33  func (fcr *fileSourcesReaderWriter) UpdateSource(md Source) error {
    34  	dbs, err := fcr.GetSources()
    35  	if err != nil {
    36  		return err
    37  	}
    38  	for i, db := range dbs {
    39  		if db.Name == md.Name {
    40  			dbs[i] = md
    41  			return fcr.WriteSources(dbs)
    42  		}
    43  	}
    44  	dbs = append(dbs, md)
    45  	return fcr.WriteSources(dbs)
    46  }
    47  
    48  func (fcr *fileSourcesReaderWriter) CreateSource(md Source) error {
    49  	dbs, err := fcr.GetSources()
    50  	if err != nil {
    51  		return err
    52  	}
    53  	// Check if source already exists
    54  	for _, db := range dbs {
    55  		if db.Name == md.Name {
    56  			return ErrSourceExists
    57  		}
    58  	}
    59  	dbs = append(dbs, md)
    60  	return fcr.WriteSources(dbs)
    61  }
    62  
    63  func (fcr *fileSourcesReaderWriter) DeleteSource(name string) error {
    64  	dbs, err := fcr.GetSources()
    65  	if err != nil {
    66  		return err
    67  	}
    68  	dbs = slices.DeleteFunc(dbs, func(md Source) bool { return md.Name == name })
    69  	return fcr.WriteSources(dbs)
    70  }
    71  
    72  func (fcr *fileSourcesReaderWriter) GetSources() (dbs Sources, err error) {
    73  	var fi fs.FileInfo
    74  	if fi, err = os.Stat(fcr.path); err != nil {
    75  		return
    76  	}
    77  	switch mode := fi.Mode(); {
    78  	case mode.IsDir():
    79  		err = filepath.WalkDir(fcr.path, func(path string, d fs.DirEntry, err error) error {
    80  			if err != nil {
    81  				return err
    82  			}
    83  			ext := strings.ToLower(filepath.Ext(d.Name()))
    84  			if d.IsDir() || ext != ".yaml" && ext != ".yml" {
    85  				return nil
    86  			}
    87  			var mdbs Sources
    88  			if mdbs, err = fcr.getSources(path); err == nil {
    89  				dbs = append(dbs, mdbs...)
    90  			}
    91  			return err
    92  		})
    93  	case mode.IsRegular():
    94  		dbs, err = fcr.getSources(fcr.path)
    95  	}
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  	return dbs.Validate()
   100  }
   101  
   102  func (fcr *fileSourcesReaderWriter) getSources(configFilePath string) (dbs Sources, err error) {
   103  	var yamlFile []byte
   104  	if yamlFile, err = os.ReadFile(configFilePath); err != nil {
   105  		return
   106  	}
   107  	c := make(Sources, 0) // there can be multiple configs in a single file
   108  	if err = yaml.Unmarshal(yamlFile, &c); err != nil {
   109  		return
   110  	}
   111  	for _, v := range c {
   112  		dbs = append(dbs, fcr.expandEnvVars(v))
   113  	}
   114  	return
   115  }
   116  
   117  func (fcr *fileSourcesReaderWriter) expandEnvVars(md Source) Source {
   118  	if strings.HasPrefix(string(md.Kind), "$") {
   119  		md.Kind = Kind(os.ExpandEnv(string(md.Kind)))
   120  	}
   121  	if strings.HasPrefix(md.Name, "$") {
   122  		md.Name = os.ExpandEnv(md.Name)
   123  	}
   124  	if strings.HasPrefix(md.IncludePattern, "$") {
   125  		md.IncludePattern = os.ExpandEnv(md.IncludePattern)
   126  	}
   127  	if strings.HasPrefix(md.ExcludePattern, "$") {
   128  		md.ExcludePattern = os.ExpandEnv(md.ExcludePattern)
   129  	}
   130  	if strings.HasPrefix(md.PresetMetrics, "$") {
   131  		md.PresetMetrics = os.ExpandEnv(md.PresetMetrics)
   132  	}
   133  	if strings.HasPrefix(md.PresetMetricsStandby, "$") {
   134  		md.PresetMetricsStandby = os.ExpandEnv(md.PresetMetricsStandby)
   135  	}
   136  	return md
   137  }
   138