github.com/influxdata/telegraf@v1.30.3/config/envvar.go (about)

     1  package config
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"io"
     7  	"os"
     8  	"strings"
     9  
    10  	"github.com/compose-spec/compose-go/template"
    11  	"github.com/compose-spec/compose-go/utils"
    12  )
    13  
    14  type trimmer struct {
    15  	input  *bytes.Reader
    16  	output bytes.Buffer
    17  }
    18  
    19  func removeComments(buf []byte) ([]byte, error) {
    20  	t := &trimmer{
    21  		input:  bytes.NewReader(buf),
    22  		output: bytes.Buffer{},
    23  	}
    24  	err := t.process()
    25  	return t.output.Bytes(), err
    26  }
    27  
    28  func (t *trimmer) process() error {
    29  	for {
    30  		// Read the next byte until EOF
    31  		c, err := t.input.ReadByte()
    32  		if err != nil {
    33  			if errors.Is(err, io.EOF) {
    34  				break
    35  			}
    36  			return err
    37  		}
    38  
    39  		// Switch states if we need to
    40  		switch c {
    41  		case '\\':
    42  			_ = t.input.UnreadByte()
    43  			err = t.escape()
    44  		case '\'':
    45  			_ = t.input.UnreadByte()
    46  			if t.hasNQuotes(c, 3) {
    47  				err = t.tripleSingleQuote()
    48  			} else {
    49  				err = t.singleQuote()
    50  			}
    51  		case '"':
    52  			_ = t.input.UnreadByte()
    53  			if t.hasNQuotes(c, 3) {
    54  				err = t.tripleDoubleQuote()
    55  			} else {
    56  				err = t.doubleQuote()
    57  			}
    58  		case '#':
    59  			err = t.comment()
    60  		default:
    61  			t.output.WriteByte(c)
    62  			continue
    63  		}
    64  		if err != nil {
    65  			if errors.Is(err, io.EOF) {
    66  				break
    67  			}
    68  			return err
    69  		}
    70  	}
    71  	return nil
    72  }
    73  
    74  func (t *trimmer) hasNQuotes(ref byte, limit int64) bool {
    75  	var count int64
    76  	// Look ahead check if the next characters are what we expect
    77  	for count = 0; count < limit; count++ {
    78  		c, err := t.input.ReadByte()
    79  		if err != nil || c != ref {
    80  			break
    81  		}
    82  	}
    83  	// We also need to unread the non-matching character
    84  	offset := -count
    85  	if count < limit {
    86  		offset--
    87  	}
    88  	// Unread the matched characters
    89  	_, _ = t.input.Seek(offset, io.SeekCurrent)
    90  	return count >= limit
    91  }
    92  
    93  func (t *trimmer) readWriteByte() (byte, error) {
    94  	c, err := t.input.ReadByte()
    95  	if err != nil {
    96  		return 0, err
    97  	}
    98  	return c, t.output.WriteByte(c)
    99  }
   100  
   101  func (t *trimmer) escape() error {
   102  	// Consumer the known starting backslash and quote
   103  	_, _ = t.readWriteByte()
   104  
   105  	// Read the next character which is the escaped one and exit
   106  	_, err := t.readWriteByte()
   107  	return err
   108  }
   109  
   110  func (t *trimmer) singleQuote() error {
   111  	// Consumer the known starting quote
   112  	_, _ = t.readWriteByte()
   113  
   114  	// Read bytes until EOF, line end or another single quote
   115  	for {
   116  		if c, err := t.readWriteByte(); err != nil || c == '\'' || c == '\n' {
   117  			return err
   118  		}
   119  	}
   120  }
   121  
   122  func (t *trimmer) tripleSingleQuote() error {
   123  	for i := 0; i < 3; i++ {
   124  		// Consumer the known starting quotes
   125  		_, _ = t.readWriteByte()
   126  	}
   127  
   128  	// Read bytes until EOF or another set of triple single quotes
   129  	for {
   130  		c, err := t.readWriteByte()
   131  		if err != nil {
   132  			return err
   133  		}
   134  
   135  		if c == '\'' && t.hasNQuotes('\'', 2) {
   136  			// Consumer the two additional ending quotes
   137  			_, _ = t.readWriteByte()
   138  			_, _ = t.readWriteByte()
   139  			return nil
   140  		}
   141  	}
   142  }
   143  
   144  func (t *trimmer) doubleQuote() error {
   145  	// Consumer the known starting quote
   146  	_, _ = t.readWriteByte()
   147  
   148  	// Read bytes until EOF, line end or another double quote
   149  	for {
   150  		c, err := t.input.ReadByte()
   151  		if err != nil {
   152  			return err
   153  		}
   154  		switch c {
   155  		case '\\':
   156  			// Found escaped character
   157  			_ = t.input.UnreadByte()
   158  			if err := t.escape(); err != nil {
   159  				return err
   160  			}
   161  			continue
   162  		case '"', '\n':
   163  			// Found terminator
   164  			return t.output.WriteByte(c)
   165  		}
   166  		t.output.WriteByte(c)
   167  	}
   168  }
   169  
   170  func (t *trimmer) tripleDoubleQuote() error {
   171  	for i := 0; i < 3; i++ {
   172  		// Consumer the known starting quotes
   173  		_, _ = t.readWriteByte()
   174  	}
   175  
   176  	// Read bytes until EOF or another set of triple double quotes
   177  	for {
   178  		c, err := t.input.ReadByte()
   179  		if err != nil {
   180  			return err
   181  		}
   182  		switch c {
   183  		case '\\':
   184  			// Found escaped character
   185  			_ = t.input.UnreadByte()
   186  			if err := t.escape(); err != nil {
   187  				return err
   188  			}
   189  			continue
   190  		case '"':
   191  			t.output.WriteByte(c)
   192  			if t.hasNQuotes('"', 2) {
   193  				// Consumer the two additional ending quotes
   194  				_, _ = t.readWriteByte()
   195  				_, _ = t.readWriteByte()
   196  				return nil
   197  			}
   198  			continue
   199  		}
   200  		t.output.WriteByte(c)
   201  	}
   202  }
   203  
   204  func (t *trimmer) comment() error {
   205  	// Read bytes until EOF or a line break
   206  	for {
   207  		c, err := t.input.ReadByte()
   208  		if err != nil {
   209  			return err
   210  		}
   211  		if c == '\n' {
   212  			return t.output.WriteByte(c)
   213  		}
   214  	}
   215  }
   216  
   217  func substituteEnvironment(contents []byte, oldReplacementBehavior bool) ([]byte, error) {
   218  	options := []template.Option{
   219  		template.WithReplacementFunction(func(s string, m template.Mapping, cfg *template.Config) (string, error) {
   220  			result, applied, err := template.DefaultReplacementAppliedFunc(s, m, cfg)
   221  			if err == nil && !applied {
   222  				// Keep undeclared environment-variable patterns to reproduce
   223  				// pre-v1.27 behavior
   224  				return s, nil
   225  			}
   226  			if err != nil && strings.HasPrefix(err.Error(), "Invalid template:") {
   227  				// Keep invalid template patterns to ignore regexp substitutions
   228  				// like ${1}
   229  				return s, nil
   230  			}
   231  			return result, err
   232  		}),
   233  		template.WithoutLogging,
   234  	}
   235  	if oldReplacementBehavior {
   236  		options = append(options, template.WithPattern(oldVarRe))
   237  	}
   238  
   239  	envMap := utils.GetAsEqualsMap(os.Environ())
   240  	retVal, err := template.SubstituteWithOptions(string(contents), func(k string) (string, bool) {
   241  		if v, ok := envMap[k]; ok {
   242  			return v, ok
   243  		}
   244  		return "", false
   245  	}, options...)
   246  	return []byte(retVal), err
   247  }