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 }