mirror of
				https://github.com/linuxkit/linuxkit.git
				synced 2025-10-31 06:39:19 +00:00 
			
		
		
		
	multiple containerd options
Signed-off-by: Avi Deitcher <avi@deitcher.net>
This commit is contained in:
		
							
								
								
									
										33
									
								
								docs/faq.md
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								docs/faq.md
									
									
									
									
									
								
							| @@ -37,18 +37,41 @@ If you're not seeing `containerd` logs in the console during boot, make sure tha | ||||
|  | ||||
| `init` and other processes like `containerd` will use the last defined console in the kernel `cmdline`. When using `qemu`, to see the console you need to list `ttyS0` as the last console to properly see the output. | ||||
|  | ||||
| ## Enabling debug or trace log levels on containerd | ||||
| ## Enabling and controlling containerd logs | ||||
|  | ||||
| On startup, linuxkit looks for and parses a file `/etc/containerd/cli-opts`. If it exists, the content is used as arguments to containerd. Thus, to enable | ||||
| On startup, linuxkit looks for and parses a file `/etc/containerd/runtime-config.toml`. If it exists, the content is used to configure containerd runtime. | ||||
|  | ||||
| Sample config is below: | ||||
|  | ||||
| ```toml | ||||
| cliopts="--log-level debug" | ||||
| stderr="/var/log/containerd.out.log" | ||||
| stdout="stdout" | ||||
| ``` | ||||
|  | ||||
| The options are as follows: | ||||
|  | ||||
| * `cliopts`: options to pass to the containerd command-line as is. | ||||
| * `stderr`: where to send stderr from containerd. If blank, it sends it to the default stderr, which is the console. | ||||
| * `stdout`: where to send stdout from containerd. If blank, it sends it to the default stdout, which is the console. containerd normally does not have any stdout. | ||||
|  | ||||
| The `stderr` and `stdout` options can take exactly one of the following options: | ||||
|  | ||||
| * `stderr` - send to stderr | ||||
| * `stdout` - send to stdout | ||||
| * any absolute path (beginning with `/`) - send to that file. If the file exists, append to it; if not, create it and append to it. | ||||
|  | ||||
| Thus, to enable | ||||
| a higher log level, for example `debug`, create a file whose contents are `--log-level debug` and place it on the image: | ||||
|  | ||||
| ```yml | ||||
| files: | ||||
|   - path: /etc/containerd/cli-opts | ||||
|     contents: "--log-level debug" | ||||
|   - path: /etc/containerd/runtime-config.toml | ||||
|     source: "/path/to/runtime-config.toml" | ||||
|     mode: "0644" | ||||
| ``` | ||||
|  | ||||
| Note that the package that parses the contents splits on _all_ whitespace. It does not, as of this writing, support shell-like parsing, so the following will work: | ||||
| Note that the package that parses the `cliopts` splits on _all_ whitespace. It does not, as of this writing, support shell-like parsing, so the following will work: | ||||
|  | ||||
| ``` | ||||
| --log-level debug --arg abcd | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import ( | ||||
| 	"context" | ||||
| 	"flag" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"os" | ||||
| 	"os/exec" | ||||
| @@ -14,12 +15,13 @@ import ( | ||||
|  | ||||
| 	"github.com/containerd/containerd" | ||||
| 	"github.com/containerd/containerd/errdefs" | ||||
| 	"github.com/pelletier/go-toml" | ||||
| 	"github.com/pkg/errors" | ||||
| 	log "github.com/sirupsen/logrus" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	containerdOptsFile = "/etc/containerd/cli-opts" | ||||
| 	containerdOptsFile = "/etc/containerd/runtime-config.toml" | ||||
| ) | ||||
|  | ||||
| func cleanupTask(ctx context.Context, ctr containerd.Container) error { | ||||
| @@ -85,14 +87,43 @@ func systemInitCmd(ctx context.Context, args []string) { | ||||
|  | ||||
| 	// look for containerd options | ||||
| 	ctrdArgs := []string{} | ||||
| 	var ( | ||||
| 		stderr io.Writer = os.Stderr | ||||
| 		stdout io.Writer = os.Stdout | ||||
| 	) | ||||
| 	if b, err := ioutil.ReadFile(containerdOptsFile); err == nil { | ||||
| 		ctrdArgs = strings.Fields(string(b)) | ||||
| 		config, err := toml.LoadBytes(b) | ||||
| 		if err != nil { | ||||
| 			log.Fatalf("error reading toml file %s: %v", containerdOptsFile, err) | ||||
| 		} | ||||
| 		if config != nil { | ||||
| 			// did we have any CLI opts? | ||||
| 			cliOptsLine := config.Get("cliopts") | ||||
| 			if cliOptsLine != nil { | ||||
| 				ctrdArgs = strings.Fields(cliOptsLine.(string)) | ||||
| 			} | ||||
| 			// stderr? | ||||
| 			stderrLine := config.Get("stderr") | ||||
| 			if stderrLine != nil { | ||||
| 				stderr, err = getWriter(stderrLine.(string)) | ||||
| 				if err != nil { | ||||
| 					log.Fatal(err) | ||||
| 				} | ||||
| 			} | ||||
| 			stdoutLine := config.Get("stdout") | ||||
| 			if stdoutLine != nil { | ||||
| 				stdout, err = getWriter(stdoutLine.(string)) | ||||
| 				if err != nil { | ||||
| 					log.Fatal(err) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// start up containerd | ||||
| 	cmd := exec.Command(*binary, ctrdArgs...) | ||||
| 	cmd.Stdout = os.Stdout | ||||
| 	cmd.Stderr = os.Stderr | ||||
| 	cmd.Stdout = stdout | ||||
| 	cmd.Stderr = stderr | ||||
| 	if err := cmd.Start(); err != nil { | ||||
| 		log.WithError(err).Fatal("cannot start containerd") | ||||
| 	} | ||||
| @@ -155,3 +186,21 @@ func systemInitCmd(ctx context.Context, args []string) { | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func getWriter(line string) (io.Writer, error) { | ||||
| 	switch { | ||||
| 	case line == "stderr": | ||||
| 		return os.Stderr, nil | ||||
| 	case line == "stdout": | ||||
| 		return os.Stdout, nil | ||||
| 	case strings.HasPrefix(line, "/"): | ||||
| 		// does the file exist? | ||||
| 		f, err := os.OpenFile(line, | ||||
| 			os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("unable to open file %s for creation or appending: %v", line, err) | ||||
| 		} | ||||
| 		return f, nil | ||||
| 	} | ||||
| 	return nil, fmt.Errorf("invalid option for writer: %s", line) | ||||
| } | ||||
|   | ||||
| @@ -1,2 +1,3 @@ | ||||
| github.com/vishvananda/netlink f5a6f697a596c788d474984a38a0ac4ba0719e93 | ||||
| github.com/vishvananda/netns 86bef332bfc3b59b7624a600bd53009ce91a9829 | ||||
| github.com/pelletier/go-toml 5b4e7e5dcc567bbc53b25ad81e06493ede66d301 | ||||
|   | ||||
							
								
								
									
										21
									
								
								pkg/init/cmd/service/vendor/github.com/pelletier/go-toml/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								pkg/init/cmd/service/vendor/github.com/pelletier/go-toml/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| The MIT License (MIT) | ||||
|  | ||||
| Copyright (c) 2013 - 2017 Thomas Pelletier, Eric Anderton | ||||
|  | ||||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| of this software and associated documentation files (the "Software"), to deal | ||||
| in the Software without restriction, including without limitation the rights | ||||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
| copies of the Software, and to permit persons to whom the Software is | ||||
| furnished to do so, subject to the following conditions: | ||||
|  | ||||
| The above copyright notice and this permission notice shall be included in all | ||||
| copies or substantial portions of the Software. | ||||
|  | ||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
| SOFTWARE. | ||||
							
								
								
									
										151
									
								
								pkg/init/cmd/service/vendor/github.com/pelletier/go-toml/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								pkg/init/cmd/service/vendor/github.com/pelletier/go-toml/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,151 @@ | ||||
| # go-toml | ||||
|  | ||||
| Go library for the [TOML](https://toml.io/) format. | ||||
|  | ||||
| This library supports TOML version | ||||
| [v1.0.0-rc.3](https://toml.io/en/v1.0.0-rc.3) | ||||
|  | ||||
| [](http://godoc.org/github.com/pelletier/go-toml) | ||||
| [](https://github.com/pelletier/go-toml/blob/master/LICENSE) | ||||
| [](https://dev.azure.com/pelletierthomas/go-toml-ci/_build/latest?definitionId=1&branchName=master) | ||||
| [](https://codecov.io/gh/pelletier/go-toml) | ||||
| [](https://goreportcard.com/report/github.com/pelletier/go-toml) | ||||
| [](https://app.fossa.io/projects/git%2Bgithub.com%2Fpelletier%2Fgo-toml?ref=badge_shield) | ||||
|  | ||||
| ## Features | ||||
|  | ||||
| Go-toml provides the following features for using data parsed from TOML documents: | ||||
|  | ||||
| * Load TOML documents from files and string data | ||||
| * Easily navigate TOML structure using Tree | ||||
| * Marshaling and unmarshaling to and from data structures | ||||
| * Line & column position data for all parsed elements | ||||
| * [Query support similar to JSON-Path](query/) | ||||
| * Syntax errors contain line and column numbers | ||||
|  | ||||
| ## Import | ||||
|  | ||||
| ```go | ||||
| import "github.com/pelletier/go-toml" | ||||
| ``` | ||||
|  | ||||
| ## Usage example | ||||
|  | ||||
| Read a TOML document: | ||||
|  | ||||
| ```go | ||||
| config, _ := toml.Load(` | ||||
| [postgres] | ||||
| user = "pelletier" | ||||
| password = "mypassword"`) | ||||
| // retrieve data directly | ||||
| user := config.Get("postgres.user").(string) | ||||
|  | ||||
| // or using an intermediate object | ||||
| postgresConfig := config.Get("postgres").(*toml.Tree) | ||||
| password := postgresConfig.Get("password").(string) | ||||
| ``` | ||||
|  | ||||
| Or use Unmarshal: | ||||
|  | ||||
| ```go | ||||
| type Postgres struct { | ||||
|     User     string | ||||
|     Password string | ||||
| } | ||||
| type Config struct { | ||||
|     Postgres Postgres | ||||
| } | ||||
|  | ||||
| doc := []byte(` | ||||
| [Postgres] | ||||
| User = "pelletier" | ||||
| Password = "mypassword"`) | ||||
|  | ||||
| config := Config{} | ||||
| toml.Unmarshal(doc, &config) | ||||
| fmt.Println("user=", config.Postgres.User) | ||||
| ``` | ||||
|  | ||||
| Or use a query: | ||||
|  | ||||
| ```go | ||||
| // use a query to gather elements without walking the tree | ||||
| q, _ := query.Compile("$..[user,password]") | ||||
| results := q.Execute(config) | ||||
| for ii, item := range results.Values() { | ||||
|     fmt.Printf("Query result %d: %v\n", ii, item) | ||||
| } | ||||
| ``` | ||||
|  | ||||
| ## Documentation | ||||
|  | ||||
| The documentation and additional examples are available at | ||||
| [godoc.org](http://godoc.org/github.com/pelletier/go-toml). | ||||
|  | ||||
| ## Tools | ||||
|  | ||||
| Go-toml provides two handy command line tools: | ||||
|  | ||||
| * `tomll`: Reads TOML files and lints them. | ||||
|  | ||||
|     ``` | ||||
|     go install github.com/pelletier/go-toml/cmd/tomll | ||||
|     tomll --help | ||||
|     ``` | ||||
| * `tomljson`: Reads a TOML file and outputs its JSON representation. | ||||
|  | ||||
|     ``` | ||||
|     go install github.com/pelletier/go-toml/cmd/tomljson | ||||
|     tomljson --help | ||||
|     ``` | ||||
|  | ||||
|  * `jsontoml`: Reads a JSON file and outputs a TOML representation. | ||||
|  | ||||
|     ``` | ||||
|     go install github.com/pelletier/go-toml/cmd/jsontoml | ||||
|     jsontoml --help | ||||
|     ``` | ||||
|  | ||||
| ### Docker image | ||||
|  | ||||
| Those tools are also availble as a Docker image from | ||||
| [dockerhub](https://hub.docker.com/r/pelletier/go-toml). For example, to | ||||
| use `tomljson`: | ||||
|  | ||||
| ``` | ||||
| docker run -v $PWD:/workdir pelletier/go-toml tomljson /workdir/example.toml | ||||
| ``` | ||||
|  | ||||
| Only master (`latest`) and tagged versions are published to dockerhub. You | ||||
| can build your own image as usual: | ||||
|  | ||||
| ``` | ||||
| docker build -t go-toml . | ||||
| ``` | ||||
|  | ||||
| ## Contribute | ||||
|  | ||||
| Feel free to report bugs and patches using GitHub's pull requests system on | ||||
| [pelletier/go-toml](https://github.com/pelletier/go-toml). Any feedback would be | ||||
| much appreciated! | ||||
|  | ||||
| ### Run tests | ||||
|  | ||||
| `go test ./...` | ||||
|  | ||||
| ### Fuzzing | ||||
|  | ||||
| The script `./fuzz.sh` is available to | ||||
| run [go-fuzz](https://github.com/dvyukov/go-fuzz) on go-toml. | ||||
|  | ||||
| ## Versioning | ||||
|  | ||||
| Go-toml follows [Semantic Versioning](http://semver.org/). The supported version | ||||
| of [TOML](https://github.com/toml-lang/toml) is indicated at the beginning of | ||||
| this document. The last two major versions of Go are supported | ||||
| (see [Go Release Policy](https://golang.org/doc/devel/release.html#policy)). | ||||
|  | ||||
| ## License | ||||
|  | ||||
| The MIT License (MIT). Read [LICENSE](LICENSE). | ||||
							
								
								
									
										23
									
								
								pkg/init/cmd/service/vendor/github.com/pelletier/go-toml/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								pkg/init/cmd/service/vendor/github.com/pelletier/go-toml/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| // Package toml is a TOML parser and manipulation library. | ||||
| // | ||||
| // This version supports the specification as described in | ||||
| // https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.5.0.md | ||||
| // | ||||
| // Marshaling | ||||
| // | ||||
| // Go-toml can marshal and unmarshal TOML documents from and to data | ||||
| // structures. | ||||
| // | ||||
| // TOML document as a tree | ||||
| // | ||||
| // Go-toml can operate on a TOML document as a tree. Use one of the Load* | ||||
| // functions to parse TOML data and obtain a Tree instance, then one of its | ||||
| // methods to manipulate the tree. | ||||
| // | ||||
| // JSONPath-like queries | ||||
| // | ||||
| // The package github.com/pelletier/go-toml/query implements a system | ||||
| // similar to JSONPath to quickly retrieve elements of a TOML document using a | ||||
| // single expression. See the package documentation for more information. | ||||
| // | ||||
| package toml | ||||
							
								
								
									
										31
									
								
								pkg/init/cmd/service/vendor/github.com/pelletier/go-toml/fuzz.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								pkg/init/cmd/service/vendor/github.com/pelletier/go-toml/fuzz.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| // +build gofuzz | ||||
|  | ||||
| package toml | ||||
|  | ||||
| func Fuzz(data []byte) int { | ||||
| 	tree, err := LoadBytes(data) | ||||
| 	if err != nil { | ||||
| 		if tree != nil { | ||||
| 			panic("tree must be nil if there is an error") | ||||
| 		} | ||||
| 		return 0 | ||||
| 	} | ||||
|  | ||||
| 	str, err := tree.ToTomlString() | ||||
| 	if err != nil { | ||||
| 		if str != "" { | ||||
| 			panic(`str must be "" if there is an error`) | ||||
| 		} | ||||
| 		panic(err) | ||||
| 	} | ||||
|  | ||||
| 	tree, err = Load(str) | ||||
| 	if err != nil { | ||||
| 		if tree != nil { | ||||
| 			panic("tree must be nil if there is an error") | ||||
| 		} | ||||
| 		return 0 | ||||
| 	} | ||||
|  | ||||
| 	return 1 | ||||
| } | ||||
							
								
								
									
										5
									
								
								pkg/init/cmd/service/vendor/github.com/pelletier/go-toml/go.mod
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								pkg/init/cmd/service/vendor/github.com/pelletier/go-toml/go.mod
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| module github.com/pelletier/go-toml | ||||
|  | ||||
| go 1.12 | ||||
|  | ||||
| require github.com/davecgh/go-spew v1.1.1 | ||||
							
								
								
									
										112
									
								
								pkg/init/cmd/service/vendor/github.com/pelletier/go-toml/keysparsing.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								pkg/init/cmd/service/vendor/github.com/pelletier/go-toml/keysparsing.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,112 @@ | ||||
| // Parsing keys handling both bare and quoted keys. | ||||
|  | ||||
| package toml | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| ) | ||||
|  | ||||
| // Convert the bare key group string to an array. | ||||
| // The input supports double quotation and single quotation, | ||||
| // but escape sequences are not supported. Lexers must unescape them beforehand. | ||||
| func parseKey(key string) ([]string, error) { | ||||
| 	runes := []rune(key) | ||||
| 	var groups []string | ||||
|  | ||||
| 	if len(key) == 0 { | ||||
| 		return nil, errors.New("empty key") | ||||
| 	} | ||||
|  | ||||
| 	idx := 0 | ||||
| 	for idx < len(runes) { | ||||
| 		for ; idx < len(runes) && isSpace(runes[idx]); idx++ { | ||||
| 			// skip leading whitespace | ||||
| 		} | ||||
| 		if idx >= len(runes) { | ||||
| 			break | ||||
| 		} | ||||
| 		r := runes[idx] | ||||
| 		if isValidBareChar(r) { | ||||
| 			// parse bare key | ||||
| 			startIdx := idx | ||||
| 			endIdx := -1 | ||||
| 			idx++ | ||||
| 			for idx < len(runes) { | ||||
| 				r = runes[idx] | ||||
| 				if isValidBareChar(r) { | ||||
| 					idx++ | ||||
| 				} else if r == '.' { | ||||
| 					endIdx = idx | ||||
| 					break | ||||
| 				} else if isSpace(r) { | ||||
| 					endIdx = idx | ||||
| 					for ; idx < len(runes) && isSpace(runes[idx]); idx++ { | ||||
| 						// skip trailing whitespace | ||||
| 					} | ||||
| 					if idx < len(runes) && runes[idx] != '.' { | ||||
| 						return nil, fmt.Errorf("invalid key character after whitespace: %c", runes[idx]) | ||||
| 					} | ||||
| 					break | ||||
| 				} else { | ||||
| 					return nil, fmt.Errorf("invalid bare key character: %c", r) | ||||
| 				} | ||||
| 			} | ||||
| 			if endIdx == -1 { | ||||
| 				endIdx = idx | ||||
| 			} | ||||
| 			groups = append(groups, string(runes[startIdx:endIdx])) | ||||
| 		} else if r == '\'' { | ||||
| 			// parse single quoted key | ||||
| 			idx++ | ||||
| 			startIdx := idx | ||||
| 			for { | ||||
| 				if idx >= len(runes) { | ||||
| 					return nil, fmt.Errorf("unclosed single-quoted key") | ||||
| 				} | ||||
| 				r = runes[idx] | ||||
| 				if r == '\'' { | ||||
| 					groups = append(groups, string(runes[startIdx:idx])) | ||||
| 					idx++ | ||||
| 					break | ||||
| 				} | ||||
| 				idx++ | ||||
| 			} | ||||
| 		} else if r == '"' { | ||||
| 			// parse double quoted key | ||||
| 			idx++ | ||||
| 			startIdx := idx | ||||
| 			for { | ||||
| 				if idx >= len(runes) { | ||||
| 					return nil, fmt.Errorf("unclosed double-quoted key") | ||||
| 				} | ||||
| 				r = runes[idx] | ||||
| 				if r == '"' { | ||||
| 					groups = append(groups, string(runes[startIdx:idx])) | ||||
| 					idx++ | ||||
| 					break | ||||
| 				} | ||||
| 				idx++ | ||||
| 			} | ||||
| 		} else if r == '.' { | ||||
| 			idx++ | ||||
| 			if idx >= len(runes) { | ||||
| 				return nil, fmt.Errorf("unexpected end of key") | ||||
| 			} | ||||
| 			r = runes[idx] | ||||
| 			if !isValidBareChar(r) && r != '\'' && r != '"' && r != ' ' { | ||||
| 				return nil, fmt.Errorf("expecting key part after dot") | ||||
| 			} | ||||
| 		} else { | ||||
| 			return nil, fmt.Errorf("invalid key character: %c", r) | ||||
| 		} | ||||
| 	} | ||||
| 	if len(groups) == 0 { | ||||
| 		return nil, fmt.Errorf("empty key") | ||||
| 	} | ||||
| 	return groups, nil | ||||
| } | ||||
|  | ||||
| func isValidBareChar(r rune) bool { | ||||
| 	return isAlphanumeric(r) || r == '-' || isDigit(r) | ||||
| } | ||||
							
								
								
									
										1031
									
								
								pkg/init/cmd/service/vendor/github.com/pelletier/go-toml/lexer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1031
									
								
								pkg/init/cmd/service/vendor/github.com/pelletier/go-toml/lexer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										281
									
								
								pkg/init/cmd/service/vendor/github.com/pelletier/go-toml/localtime.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										281
									
								
								pkg/init/cmd/service/vendor/github.com/pelletier/go-toml/localtime.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,281 @@ | ||||
| // Implementation of TOML's local date/time. | ||||
| // Copied over from https://github.com/googleapis/google-cloud-go/blob/master/civil/civil.go | ||||
| // to avoid pulling all the Google dependencies. | ||||
| // | ||||
| // Copyright 2016 Google LLC | ||||
| // | ||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| // you may not use this file except in compliance with the License. | ||||
| // You may obtain a copy of the License at | ||||
| // | ||||
| //      http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| // Unless required by applicable law or agreed to in writing, software | ||||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
|  | ||||
| // Package civil implements types for civil time, a time-zone-independent | ||||
| // representation of time that follows the rules of the proleptic | ||||
| // Gregorian calendar with exactly 24-hour days, 60-minute hours, and 60-second | ||||
| // minutes. | ||||
| // | ||||
| // Because they lack location information, these types do not represent unique | ||||
| // moments or intervals of time. Use time.Time for that purpose. | ||||
| package toml | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| // A LocalDate represents a date (year, month, day). | ||||
| // | ||||
| // This type does not include location information, and therefore does not | ||||
| // describe a unique 24-hour timespan. | ||||
| type LocalDate struct { | ||||
| 	Year  int        // Year (e.g., 2014). | ||||
| 	Month time.Month // Month of the year (January = 1, ...). | ||||
| 	Day   int        // Day of the month, starting at 1. | ||||
| } | ||||
|  | ||||
| // LocalDateOf returns the LocalDate in which a time occurs in that time's location. | ||||
| func LocalDateOf(t time.Time) LocalDate { | ||||
| 	var d LocalDate | ||||
| 	d.Year, d.Month, d.Day = t.Date() | ||||
| 	return d | ||||
| } | ||||
|  | ||||
| // ParseLocalDate parses a string in RFC3339 full-date format and returns the date value it represents. | ||||
| func ParseLocalDate(s string) (LocalDate, error) { | ||||
| 	t, err := time.Parse("2006-01-02", s) | ||||
| 	if err != nil { | ||||
| 		return LocalDate{}, err | ||||
| 	} | ||||
| 	return LocalDateOf(t), nil | ||||
| } | ||||
|  | ||||
| // String returns the date in RFC3339 full-date format. | ||||
| func (d LocalDate) String() string { | ||||
| 	return fmt.Sprintf("%04d-%02d-%02d", d.Year, d.Month, d.Day) | ||||
| } | ||||
|  | ||||
| // IsValid reports whether the date is valid. | ||||
| func (d LocalDate) IsValid() bool { | ||||
| 	return LocalDateOf(d.In(time.UTC)) == d | ||||
| } | ||||
|  | ||||
| // In returns the time corresponding to time 00:00:00 of the date in the location. | ||||
| // | ||||
| // In is always consistent with time.LocalDate, even when time.LocalDate returns a time | ||||
| // on a different day. For example, if loc is America/Indiana/Vincennes, then both | ||||
| //     time.LocalDate(1955, time.May, 1, 0, 0, 0, 0, loc) | ||||
| // and | ||||
| //     civil.LocalDate{Year: 1955, Month: time.May, Day: 1}.In(loc) | ||||
| // return 23:00:00 on April 30, 1955. | ||||
| // | ||||
| // In panics if loc is nil. | ||||
| func (d LocalDate) In(loc *time.Location) time.Time { | ||||
| 	return time.Date(d.Year, d.Month, d.Day, 0, 0, 0, 0, loc) | ||||
| } | ||||
|  | ||||
| // AddDays returns the date that is n days in the future. | ||||
| // n can also be negative to go into the past. | ||||
| func (d LocalDate) AddDays(n int) LocalDate { | ||||
| 	return LocalDateOf(d.In(time.UTC).AddDate(0, 0, n)) | ||||
| } | ||||
|  | ||||
| // DaysSince returns the signed number of days between the date and s, not including the end day. | ||||
| // This is the inverse operation to AddDays. | ||||
| func (d LocalDate) DaysSince(s LocalDate) (days int) { | ||||
| 	// We convert to Unix time so we do not have to worry about leap seconds: | ||||
| 	// Unix time increases by exactly 86400 seconds per day. | ||||
| 	deltaUnix := d.In(time.UTC).Unix() - s.In(time.UTC).Unix() | ||||
| 	return int(deltaUnix / 86400) | ||||
| } | ||||
|  | ||||
| // Before reports whether d1 occurs before d2. | ||||
| func (d1 LocalDate) Before(d2 LocalDate) bool { | ||||
| 	if d1.Year != d2.Year { | ||||
| 		return d1.Year < d2.Year | ||||
| 	} | ||||
| 	if d1.Month != d2.Month { | ||||
| 		return d1.Month < d2.Month | ||||
| 	} | ||||
| 	return d1.Day < d2.Day | ||||
| } | ||||
|  | ||||
| // After reports whether d1 occurs after d2. | ||||
| func (d1 LocalDate) After(d2 LocalDate) bool { | ||||
| 	return d2.Before(d1) | ||||
| } | ||||
|  | ||||
| // MarshalText implements the encoding.TextMarshaler interface. | ||||
| // The output is the result of d.String(). | ||||
| func (d LocalDate) MarshalText() ([]byte, error) { | ||||
| 	return []byte(d.String()), nil | ||||
| } | ||||
|  | ||||
| // UnmarshalText implements the encoding.TextUnmarshaler interface. | ||||
| // The date is expected to be a string in a format accepted by ParseLocalDate. | ||||
| func (d *LocalDate) UnmarshalText(data []byte) error { | ||||
| 	var err error | ||||
| 	*d, err = ParseLocalDate(string(data)) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // A LocalTime represents a time with nanosecond precision. | ||||
| // | ||||
| // This type does not include location information, and therefore does not | ||||
| // describe a unique moment in time. | ||||
| // | ||||
| // This type exists to represent the TIME type in storage-based APIs like BigQuery. | ||||
| // Most operations on Times are unlikely to be meaningful. Prefer the LocalDateTime type. | ||||
| type LocalTime struct { | ||||
| 	Hour       int // The hour of the day in 24-hour format; range [0-23] | ||||
| 	Minute     int // The minute of the hour; range [0-59] | ||||
| 	Second     int // The second of the minute; range [0-59] | ||||
| 	Nanosecond int // The nanosecond of the second; range [0-999999999] | ||||
| } | ||||
|  | ||||
| // LocalTimeOf returns the LocalTime representing the time of day in which a time occurs | ||||
| // in that time's location. It ignores the date. | ||||
| func LocalTimeOf(t time.Time) LocalTime { | ||||
| 	var tm LocalTime | ||||
| 	tm.Hour, tm.Minute, tm.Second = t.Clock() | ||||
| 	tm.Nanosecond = t.Nanosecond() | ||||
| 	return tm | ||||
| } | ||||
|  | ||||
| // ParseLocalTime parses a string and returns the time value it represents. | ||||
| // ParseLocalTime accepts an extended form of the RFC3339 partial-time format. After | ||||
| // the HH:MM:SS part of the string, an optional fractional part may appear, | ||||
| // consisting of a decimal point followed by one to nine decimal digits. | ||||
| // (RFC3339 admits only one digit after the decimal point). | ||||
| func ParseLocalTime(s string) (LocalTime, error) { | ||||
| 	t, err := time.Parse("15:04:05.999999999", s) | ||||
| 	if err != nil { | ||||
| 		return LocalTime{}, err | ||||
| 	} | ||||
| 	return LocalTimeOf(t), nil | ||||
| } | ||||
|  | ||||
| // String returns the date in the format described in ParseLocalTime. If Nanoseconds | ||||
| // is zero, no fractional part will be generated. Otherwise, the result will | ||||
| // end with a fractional part consisting of a decimal point and nine digits. | ||||
| func (t LocalTime) String() string { | ||||
| 	s := fmt.Sprintf("%02d:%02d:%02d", t.Hour, t.Minute, t.Second) | ||||
| 	if t.Nanosecond == 0 { | ||||
| 		return s | ||||
| 	} | ||||
| 	return s + fmt.Sprintf(".%09d", t.Nanosecond) | ||||
| } | ||||
|  | ||||
| // IsValid reports whether the time is valid. | ||||
| func (t LocalTime) IsValid() bool { | ||||
| 	// Construct a non-zero time. | ||||
| 	tm := time.Date(2, 2, 2, t.Hour, t.Minute, t.Second, t.Nanosecond, time.UTC) | ||||
| 	return LocalTimeOf(tm) == t | ||||
| } | ||||
|  | ||||
| // MarshalText implements the encoding.TextMarshaler interface. | ||||
| // The output is the result of t.String(). | ||||
| func (t LocalTime) MarshalText() ([]byte, error) { | ||||
| 	return []byte(t.String()), nil | ||||
| } | ||||
|  | ||||
| // UnmarshalText implements the encoding.TextUnmarshaler interface. | ||||
| // The time is expected to be a string in a format accepted by ParseLocalTime. | ||||
| func (t *LocalTime) UnmarshalText(data []byte) error { | ||||
| 	var err error | ||||
| 	*t, err = ParseLocalTime(string(data)) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // A LocalDateTime represents a date and time. | ||||
| // | ||||
| // This type does not include location information, and therefore does not | ||||
| // describe a unique moment in time. | ||||
| type LocalDateTime struct { | ||||
| 	Date LocalDate | ||||
| 	Time LocalTime | ||||
| } | ||||
|  | ||||
| // Note: We deliberately do not embed LocalDate into LocalDateTime, to avoid promoting AddDays and Sub. | ||||
|  | ||||
| // LocalDateTimeOf returns the LocalDateTime in which a time occurs in that time's location. | ||||
| func LocalDateTimeOf(t time.Time) LocalDateTime { | ||||
| 	return LocalDateTime{ | ||||
| 		Date: LocalDateOf(t), | ||||
| 		Time: LocalTimeOf(t), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // ParseLocalDateTime parses a string and returns the LocalDateTime it represents. | ||||
| // ParseLocalDateTime accepts a variant of the RFC3339 date-time format that omits | ||||
| // the time offset but includes an optional fractional time, as described in | ||||
| // ParseLocalTime. Informally, the accepted format is | ||||
| //     YYYY-MM-DDTHH:MM:SS[.FFFFFFFFF] | ||||
| // where the 'T' may be a lower-case 't'. | ||||
| func ParseLocalDateTime(s string) (LocalDateTime, error) { | ||||
| 	t, err := time.Parse("2006-01-02T15:04:05.999999999", s) | ||||
| 	if err != nil { | ||||
| 		t, err = time.Parse("2006-01-02t15:04:05.999999999", s) | ||||
| 		if err != nil { | ||||
| 			return LocalDateTime{}, err | ||||
| 		} | ||||
| 	} | ||||
| 	return LocalDateTimeOf(t), nil | ||||
| } | ||||
|  | ||||
| // String returns the date in the format described in ParseLocalDate. | ||||
| func (dt LocalDateTime) String() string { | ||||
| 	return dt.Date.String() + "T" + dt.Time.String() | ||||
| } | ||||
|  | ||||
| // IsValid reports whether the datetime is valid. | ||||
| func (dt LocalDateTime) IsValid() bool { | ||||
| 	return dt.Date.IsValid() && dt.Time.IsValid() | ||||
| } | ||||
|  | ||||
| // In returns the time corresponding to the LocalDateTime in the given location. | ||||
| // | ||||
| // If the time is missing or ambigous at the location, In returns the same | ||||
| // result as time.LocalDate. For example, if loc is America/Indiana/Vincennes, then | ||||
| // both | ||||
| //     time.LocalDate(1955, time.May, 1, 0, 30, 0, 0, loc) | ||||
| // and | ||||
| //     civil.LocalDateTime{ | ||||
| //         civil.LocalDate{Year: 1955, Month: time.May, Day: 1}}, | ||||
| //         civil.LocalTime{Minute: 30}}.In(loc) | ||||
| // return 23:30:00 on April 30, 1955. | ||||
| // | ||||
| // In panics if loc is nil. | ||||
| func (dt LocalDateTime) In(loc *time.Location) time.Time { | ||||
| 	return time.Date(dt.Date.Year, dt.Date.Month, dt.Date.Day, dt.Time.Hour, dt.Time.Minute, dt.Time.Second, dt.Time.Nanosecond, loc) | ||||
| } | ||||
|  | ||||
| // Before reports whether dt1 occurs before dt2. | ||||
| func (dt1 LocalDateTime) Before(dt2 LocalDateTime) bool { | ||||
| 	return dt1.In(time.UTC).Before(dt2.In(time.UTC)) | ||||
| } | ||||
|  | ||||
| // After reports whether dt1 occurs after dt2. | ||||
| func (dt1 LocalDateTime) After(dt2 LocalDateTime) bool { | ||||
| 	return dt2.Before(dt1) | ||||
| } | ||||
|  | ||||
| // MarshalText implements the encoding.TextMarshaler interface. | ||||
| // The output is the result of dt.String(). | ||||
| func (dt LocalDateTime) MarshalText() ([]byte, error) { | ||||
| 	return []byte(dt.String()), nil | ||||
| } | ||||
|  | ||||
| // UnmarshalText implements the encoding.TextUnmarshaler interface. | ||||
| // The datetime is expected to be a string in a format accepted by ParseLocalDateTime | ||||
| func (dt *LocalDateTime) UnmarshalText(data []byte) error { | ||||
| 	var err error | ||||
| 	*dt, err = ParseLocalDateTime(string(data)) | ||||
| 	return err | ||||
| } | ||||
							
								
								
									
										1279
									
								
								pkg/init/cmd/service/vendor/github.com/pelletier/go-toml/marshal.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1279
									
								
								pkg/init/cmd/service/vendor/github.com/pelletier/go-toml/marshal.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										508
									
								
								pkg/init/cmd/service/vendor/github.com/pelletier/go-toml/parser.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										508
									
								
								pkg/init/cmd/service/vendor/github.com/pelletier/go-toml/parser.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,508 @@ | ||||
| // TOML Parser. | ||||
|  | ||||
| package toml | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"math" | ||||
| 	"reflect" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| type tomlParser struct { | ||||
| 	flowIdx       int | ||||
| 	flow          []token | ||||
| 	tree          *Tree | ||||
| 	currentTable  []string | ||||
| 	seenTableKeys []string | ||||
| } | ||||
|  | ||||
| type tomlParserStateFn func() tomlParserStateFn | ||||
|  | ||||
| // Formats and panics an error message based on a token | ||||
| func (p *tomlParser) raiseError(tok *token, msg string, args ...interface{}) { | ||||
| 	panic(tok.Position.String() + ": " + fmt.Sprintf(msg, args...)) | ||||
| } | ||||
|  | ||||
| func (p *tomlParser) run() { | ||||
| 	for state := p.parseStart; state != nil; { | ||||
| 		state = state() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (p *tomlParser) peek() *token { | ||||
| 	if p.flowIdx >= len(p.flow) { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return &p.flow[p.flowIdx] | ||||
| } | ||||
|  | ||||
| func (p *tomlParser) assume(typ tokenType) { | ||||
| 	tok := p.getToken() | ||||
| 	if tok == nil { | ||||
| 		p.raiseError(tok, "was expecting token %s, but token stream is empty", tok) | ||||
| 	} | ||||
| 	if tok.typ != typ { | ||||
| 		p.raiseError(tok, "was expecting token %s, but got %s instead", typ, tok) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (p *tomlParser) getToken() *token { | ||||
| 	tok := p.peek() | ||||
| 	if tok == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	p.flowIdx++ | ||||
| 	return tok | ||||
| } | ||||
|  | ||||
| func (p *tomlParser) parseStart() tomlParserStateFn { | ||||
| 	tok := p.peek() | ||||
|  | ||||
| 	// end of stream, parsing is finished | ||||
| 	if tok == nil { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	switch tok.typ { | ||||
| 	case tokenDoubleLeftBracket: | ||||
| 		return p.parseGroupArray | ||||
| 	case tokenLeftBracket: | ||||
| 		return p.parseGroup | ||||
| 	case tokenKey: | ||||
| 		return p.parseAssign | ||||
| 	case tokenEOF: | ||||
| 		return nil | ||||
| 	case tokenError: | ||||
| 		p.raiseError(tok, "parsing error: %s", tok.String()) | ||||
| 	default: | ||||
| 		p.raiseError(tok, "unexpected token %s", tok.typ) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (p *tomlParser) parseGroupArray() tomlParserStateFn { | ||||
| 	startToken := p.getToken() // discard the [[ | ||||
| 	key := p.getToken() | ||||
| 	if key.typ != tokenKeyGroupArray { | ||||
| 		p.raiseError(key, "unexpected token %s, was expecting a table array key", key) | ||||
| 	} | ||||
|  | ||||
| 	// get or create table array element at the indicated part in the path | ||||
| 	keys, err := parseKey(key.val) | ||||
| 	if err != nil { | ||||
| 		p.raiseError(key, "invalid table array key: %s", err) | ||||
| 	} | ||||
| 	p.tree.createSubTree(keys[:len(keys)-1], startToken.Position) // create parent entries | ||||
| 	destTree := p.tree.GetPath(keys) | ||||
| 	var array []*Tree | ||||
| 	if destTree == nil { | ||||
| 		array = make([]*Tree, 0) | ||||
| 	} else if target, ok := destTree.([]*Tree); ok && target != nil { | ||||
| 		array = destTree.([]*Tree) | ||||
| 	} else { | ||||
| 		p.raiseError(key, "key %s is already assigned and not of type table array", key) | ||||
| 	} | ||||
| 	p.currentTable = keys | ||||
|  | ||||
| 	// add a new tree to the end of the table array | ||||
| 	newTree := newTree() | ||||
| 	newTree.position = startToken.Position | ||||
| 	array = append(array, newTree) | ||||
| 	p.tree.SetPath(p.currentTable, array) | ||||
|  | ||||
| 	// remove all keys that were children of this table array | ||||
| 	prefix := key.val + "." | ||||
| 	found := false | ||||
| 	for ii := 0; ii < len(p.seenTableKeys); { | ||||
| 		tableKey := p.seenTableKeys[ii] | ||||
| 		if strings.HasPrefix(tableKey, prefix) { | ||||
| 			p.seenTableKeys = append(p.seenTableKeys[:ii], p.seenTableKeys[ii+1:]...) | ||||
| 		} else { | ||||
| 			found = (tableKey == key.val) | ||||
| 			ii++ | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// keep this key name from use by other kinds of assignments | ||||
| 	if !found { | ||||
| 		p.seenTableKeys = append(p.seenTableKeys, key.val) | ||||
| 	} | ||||
|  | ||||
| 	// move to next parser state | ||||
| 	p.assume(tokenDoubleRightBracket) | ||||
| 	return p.parseStart | ||||
| } | ||||
|  | ||||
| func (p *tomlParser) parseGroup() tomlParserStateFn { | ||||
| 	startToken := p.getToken() // discard the [ | ||||
| 	key := p.getToken() | ||||
| 	if key.typ != tokenKeyGroup { | ||||
| 		p.raiseError(key, "unexpected token %s, was expecting a table key", key) | ||||
| 	} | ||||
| 	for _, item := range p.seenTableKeys { | ||||
| 		if item == key.val { | ||||
| 			p.raiseError(key, "duplicated tables") | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	p.seenTableKeys = append(p.seenTableKeys, key.val) | ||||
| 	keys, err := parseKey(key.val) | ||||
| 	if err != nil { | ||||
| 		p.raiseError(key, "invalid table array key: %s", err) | ||||
| 	} | ||||
| 	if err := p.tree.createSubTree(keys, startToken.Position); err != nil { | ||||
| 		p.raiseError(key, "%s", err) | ||||
| 	} | ||||
| 	destTree := p.tree.GetPath(keys) | ||||
| 	if target, ok := destTree.(*Tree); ok && target != nil && target.inline { | ||||
| 		p.raiseError(key, "could not re-define exist inline table or its sub-table : %s", | ||||
| 			strings.Join(keys, ".")) | ||||
| 	} | ||||
| 	p.assume(tokenRightBracket) | ||||
| 	p.currentTable = keys | ||||
| 	return p.parseStart | ||||
| } | ||||
|  | ||||
| func (p *tomlParser) parseAssign() tomlParserStateFn { | ||||
| 	key := p.getToken() | ||||
| 	p.assume(tokenEqual) | ||||
|  | ||||
| 	parsedKey, err := parseKey(key.val) | ||||
| 	if err != nil { | ||||
| 		p.raiseError(key, "invalid key: %s", err.Error()) | ||||
| 	} | ||||
|  | ||||
| 	value := p.parseRvalue() | ||||
| 	var tableKey []string | ||||
| 	if len(p.currentTable) > 0 { | ||||
| 		tableKey = p.currentTable | ||||
| 	} else { | ||||
| 		tableKey = []string{} | ||||
| 	} | ||||
|  | ||||
| 	prefixKey := parsedKey[0 : len(parsedKey)-1] | ||||
| 	tableKey = append(tableKey, prefixKey...) | ||||
|  | ||||
| 	// find the table to assign, looking out for arrays of tables | ||||
| 	var targetNode *Tree | ||||
| 	switch node := p.tree.GetPath(tableKey).(type) { | ||||
| 	case []*Tree: | ||||
| 		targetNode = node[len(node)-1] | ||||
| 	case *Tree: | ||||
| 		targetNode = node | ||||
| 	case nil: | ||||
| 		// create intermediate | ||||
| 		if err := p.tree.createSubTree(tableKey, key.Position); err != nil { | ||||
| 			p.raiseError(key, "could not create intermediate group: %s", err) | ||||
| 		} | ||||
| 		targetNode = p.tree.GetPath(tableKey).(*Tree) | ||||
| 	default: | ||||
| 		p.raiseError(key, "Unknown table type for path: %s", | ||||
| 			strings.Join(tableKey, ".")) | ||||
| 	} | ||||
|  | ||||
| 	if targetNode.inline { | ||||
| 		p.raiseError(key, "could not add key or sub-table to exist inline table or its sub-table : %s", | ||||
| 			strings.Join(tableKey, ".")) | ||||
| 	} | ||||
|  | ||||
| 	// assign value to the found table | ||||
| 	keyVal := parsedKey[len(parsedKey)-1] | ||||
| 	localKey := []string{keyVal} | ||||
| 	finalKey := append(tableKey, keyVal) | ||||
| 	if targetNode.GetPath(localKey) != nil { | ||||
| 		p.raiseError(key, "The following key was defined twice: %s", | ||||
| 			strings.Join(finalKey, ".")) | ||||
| 	} | ||||
| 	var toInsert interface{} | ||||
|  | ||||
| 	switch value.(type) { | ||||
| 	case *Tree, []*Tree: | ||||
| 		toInsert = value | ||||
| 	default: | ||||
| 		toInsert = &tomlValue{value: value, position: key.Position} | ||||
| 	} | ||||
| 	targetNode.values[keyVal] = toInsert | ||||
| 	return p.parseStart | ||||
| } | ||||
|  | ||||
| var errInvalidUnderscore = errors.New("invalid use of _ in number") | ||||
|  | ||||
| func numberContainsInvalidUnderscore(value string) error { | ||||
| 	// For large numbers, you may use underscores between digits to enhance | ||||
| 	// readability. Each underscore must be surrounded by at least one digit on | ||||
| 	// each side. | ||||
|  | ||||
| 	hasBefore := false | ||||
| 	for idx, r := range value { | ||||
| 		if r == '_' { | ||||
| 			if !hasBefore || idx+1 >= len(value) { | ||||
| 				// can't end with an underscore | ||||
| 				return errInvalidUnderscore | ||||
| 			} | ||||
| 		} | ||||
| 		hasBefore = isDigit(r) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| var errInvalidUnderscoreHex = errors.New("invalid use of _ in hex number") | ||||
|  | ||||
| func hexNumberContainsInvalidUnderscore(value string) error { | ||||
| 	hasBefore := false | ||||
| 	for idx, r := range value { | ||||
| 		if r == '_' { | ||||
| 			if !hasBefore || idx+1 >= len(value) { | ||||
| 				// can't end with an underscore | ||||
| 				return errInvalidUnderscoreHex | ||||
| 			} | ||||
| 		} | ||||
| 		hasBefore = isHexDigit(r) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func cleanupNumberToken(value string) string { | ||||
| 	cleanedVal := strings.Replace(value, "_", "", -1) | ||||
| 	return cleanedVal | ||||
| } | ||||
|  | ||||
| func (p *tomlParser) parseRvalue() interface{} { | ||||
| 	tok := p.getToken() | ||||
| 	if tok == nil || tok.typ == tokenEOF { | ||||
| 		p.raiseError(tok, "expecting a value") | ||||
| 	} | ||||
|  | ||||
| 	switch tok.typ { | ||||
| 	case tokenString: | ||||
| 		return tok.val | ||||
| 	case tokenTrue: | ||||
| 		return true | ||||
| 	case tokenFalse: | ||||
| 		return false | ||||
| 	case tokenInf: | ||||
| 		if tok.val[0] == '-' { | ||||
| 			return math.Inf(-1) | ||||
| 		} | ||||
| 		return math.Inf(1) | ||||
| 	case tokenNan: | ||||
| 		return math.NaN() | ||||
| 	case tokenInteger: | ||||
| 		cleanedVal := cleanupNumberToken(tok.val) | ||||
| 		var err error | ||||
| 		var val int64 | ||||
| 		if len(cleanedVal) >= 3 && cleanedVal[0] == '0' { | ||||
| 			switch cleanedVal[1] { | ||||
| 			case 'x': | ||||
| 				err = hexNumberContainsInvalidUnderscore(tok.val) | ||||
| 				if err != nil { | ||||
| 					p.raiseError(tok, "%s", err) | ||||
| 				} | ||||
| 				val, err = strconv.ParseInt(cleanedVal[2:], 16, 64) | ||||
| 			case 'o': | ||||
| 				err = numberContainsInvalidUnderscore(tok.val) | ||||
| 				if err != nil { | ||||
| 					p.raiseError(tok, "%s", err) | ||||
| 				} | ||||
| 				val, err = strconv.ParseInt(cleanedVal[2:], 8, 64) | ||||
| 			case 'b': | ||||
| 				err = numberContainsInvalidUnderscore(tok.val) | ||||
| 				if err != nil { | ||||
| 					p.raiseError(tok, "%s", err) | ||||
| 				} | ||||
| 				val, err = strconv.ParseInt(cleanedVal[2:], 2, 64) | ||||
| 			default: | ||||
| 				panic("invalid base") // the lexer should catch this first | ||||
| 			} | ||||
| 		} else { | ||||
| 			err = numberContainsInvalidUnderscore(tok.val) | ||||
| 			if err != nil { | ||||
| 				p.raiseError(tok, "%s", err) | ||||
| 			} | ||||
| 			val, err = strconv.ParseInt(cleanedVal, 10, 64) | ||||
| 		} | ||||
| 		if err != nil { | ||||
| 			p.raiseError(tok, "%s", err) | ||||
| 		} | ||||
| 		return val | ||||
| 	case tokenFloat: | ||||
| 		err := numberContainsInvalidUnderscore(tok.val) | ||||
| 		if err != nil { | ||||
| 			p.raiseError(tok, "%s", err) | ||||
| 		} | ||||
| 		cleanedVal := cleanupNumberToken(tok.val) | ||||
| 		val, err := strconv.ParseFloat(cleanedVal, 64) | ||||
| 		if err != nil { | ||||
| 			p.raiseError(tok, "%s", err) | ||||
| 		} | ||||
| 		return val | ||||
| 	case tokenLocalTime: | ||||
| 		val, err := ParseLocalTime(tok.val) | ||||
| 		if err != nil { | ||||
| 			p.raiseError(tok, "%s", err) | ||||
| 		} | ||||
| 		return val | ||||
| 	case tokenLocalDate: | ||||
| 		// a local date may be followed by: | ||||
| 		// * nothing: this is a local date | ||||
| 		// * a local time: this is a local date-time | ||||
|  | ||||
| 		next := p.peek() | ||||
| 		if next == nil || next.typ != tokenLocalTime { | ||||
| 			val, err := ParseLocalDate(tok.val) | ||||
| 			if err != nil { | ||||
| 				p.raiseError(tok, "%s", err) | ||||
| 			} | ||||
| 			return val | ||||
| 		} | ||||
|  | ||||
| 		localDate := tok | ||||
| 		localTime := p.getToken() | ||||
|  | ||||
| 		next = p.peek() | ||||
| 		if next == nil || next.typ != tokenTimeOffset { | ||||
| 			v := localDate.val + "T" + localTime.val | ||||
| 			val, err := ParseLocalDateTime(v) | ||||
| 			if err != nil { | ||||
| 				p.raiseError(tok, "%s", err) | ||||
| 			} | ||||
| 			return val | ||||
| 		} | ||||
|  | ||||
| 		offset := p.getToken() | ||||
|  | ||||
| 		layout := time.RFC3339Nano | ||||
| 		v := localDate.val + "T" + localTime.val + offset.val | ||||
| 		val, err := time.ParseInLocation(layout, v, time.UTC) | ||||
| 		if err != nil { | ||||
| 			p.raiseError(tok, "%s", err) | ||||
| 		} | ||||
| 		return val | ||||
| 	case tokenLeftBracket: | ||||
| 		return p.parseArray() | ||||
| 	case tokenLeftCurlyBrace: | ||||
| 		return p.parseInlineTable() | ||||
| 	case tokenEqual: | ||||
| 		p.raiseError(tok, "cannot have multiple equals for the same key") | ||||
| 	case tokenError: | ||||
| 		p.raiseError(tok, "%s", tok) | ||||
| 	default: | ||||
| 		panic(fmt.Errorf("unhandled token: %v", tok)) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func tokenIsComma(t *token) bool { | ||||
| 	return t != nil && t.typ == tokenComma | ||||
| } | ||||
|  | ||||
| func (p *tomlParser) parseInlineTable() *Tree { | ||||
| 	tree := newTree() | ||||
| 	var previous *token | ||||
| Loop: | ||||
| 	for { | ||||
| 		follow := p.peek() | ||||
| 		if follow == nil || follow.typ == tokenEOF { | ||||
| 			p.raiseError(follow, "unterminated inline table") | ||||
| 		} | ||||
| 		switch follow.typ { | ||||
| 		case tokenRightCurlyBrace: | ||||
| 			p.getToken() | ||||
| 			break Loop | ||||
| 		case tokenKey, tokenInteger, tokenString: | ||||
| 			if !tokenIsComma(previous) && previous != nil { | ||||
| 				p.raiseError(follow, "comma expected between fields in inline table") | ||||
| 			} | ||||
| 			key := p.getToken() | ||||
| 			p.assume(tokenEqual) | ||||
|  | ||||
| 			parsedKey, err := parseKey(key.val) | ||||
| 			if err != nil { | ||||
| 				p.raiseError(key, "invalid key: %s", err) | ||||
| 			} | ||||
|  | ||||
| 			value := p.parseRvalue() | ||||
| 			tree.SetPath(parsedKey, value) | ||||
| 		case tokenComma: | ||||
| 			if tokenIsComma(previous) { | ||||
| 				p.raiseError(follow, "need field between two commas in inline table") | ||||
| 			} | ||||
| 			p.getToken() | ||||
| 		default: | ||||
| 			p.raiseError(follow, "unexpected token type in inline table: %s", follow.String()) | ||||
| 		} | ||||
| 		previous = follow | ||||
| 	} | ||||
| 	if tokenIsComma(previous) { | ||||
| 		p.raiseError(previous, "trailing comma at the end of inline table") | ||||
| 	} | ||||
| 	tree.inline = true | ||||
| 	return tree | ||||
| } | ||||
|  | ||||
| func (p *tomlParser) parseArray() interface{} { | ||||
| 	var array []interface{} | ||||
| 	arrayType := reflect.TypeOf(newTree()) | ||||
| 	for { | ||||
| 		follow := p.peek() | ||||
| 		if follow == nil || follow.typ == tokenEOF { | ||||
| 			p.raiseError(follow, "unterminated array") | ||||
| 		} | ||||
| 		if follow.typ == tokenRightBracket { | ||||
| 			p.getToken() | ||||
| 			break | ||||
| 		} | ||||
| 		val := p.parseRvalue() | ||||
| 		if reflect.TypeOf(val) != arrayType { | ||||
| 			arrayType = nil | ||||
| 		} | ||||
| 		array = append(array, val) | ||||
| 		follow = p.peek() | ||||
| 		if follow == nil || follow.typ == tokenEOF { | ||||
| 			p.raiseError(follow, "unterminated array") | ||||
| 		} | ||||
| 		if follow.typ != tokenRightBracket && follow.typ != tokenComma { | ||||
| 			p.raiseError(follow, "missing comma") | ||||
| 		} | ||||
| 		if follow.typ == tokenComma { | ||||
| 			p.getToken() | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// if the array is a mixed-type array or its length is 0, | ||||
| 	// don't convert it to a table array | ||||
| 	if len(array) <= 0 { | ||||
| 		arrayType = nil | ||||
| 	} | ||||
| 	// An array of Trees is actually an array of inline | ||||
| 	// tables, which is a shorthand for a table array. If the | ||||
| 	// array was not converted from []interface{} to []*Tree, | ||||
| 	// the two notations would not be equivalent. | ||||
| 	if arrayType == reflect.TypeOf(newTree()) { | ||||
| 		tomlArray := make([]*Tree, len(array)) | ||||
| 		for i, v := range array { | ||||
| 			tomlArray[i] = v.(*Tree) | ||||
| 		} | ||||
| 		return tomlArray | ||||
| 	} | ||||
| 	return array | ||||
| } | ||||
|  | ||||
| func parseToml(flow []token) *Tree { | ||||
| 	result := newTree() | ||||
| 	result.position = Position{1, 1} | ||||
| 	parser := &tomlParser{ | ||||
| 		flowIdx:       0, | ||||
| 		flow:          flow, | ||||
| 		tree:          result, | ||||
| 		currentTable:  make([]string, 0), | ||||
| 		seenTableKeys: make([]string, 0), | ||||
| 	} | ||||
| 	parser.run() | ||||
| 	return result | ||||
| } | ||||
							
								
								
									
										29
									
								
								pkg/init/cmd/service/vendor/github.com/pelletier/go-toml/position.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								pkg/init/cmd/service/vendor/github.com/pelletier/go-toml/position.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| // Position support for go-toml | ||||
|  | ||||
| package toml | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| ) | ||||
|  | ||||
| // Position of a document element within a TOML document. | ||||
| // | ||||
| // Line and Col are both 1-indexed positions for the element's line number and | ||||
| // column number, respectively.  Values of zero or less will cause Invalid(), | ||||
| // to return true. | ||||
| type Position struct { | ||||
| 	Line int // line within the document | ||||
| 	Col  int // column within the line | ||||
| } | ||||
|  | ||||
| // String representation of the position. | ||||
| // Displays 1-indexed line and column numbers. | ||||
| func (p Position) String() string { | ||||
| 	return fmt.Sprintf("(%d, %d)", p.Line, p.Col) | ||||
| } | ||||
|  | ||||
| // Invalid returns whether or not the position is valid (i.e. with negative or | ||||
| // null values) | ||||
| func (p Position) Invalid() bool { | ||||
| 	return p.Line <= 0 || p.Col <= 0 | ||||
| } | ||||
							
								
								
									
										136
									
								
								pkg/init/cmd/service/vendor/github.com/pelletier/go-toml/token.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								pkg/init/cmd/service/vendor/github.com/pelletier/go-toml/token.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,136 @@ | ||||
| package toml | ||||
|  | ||||
| import "fmt" | ||||
|  | ||||
| // Define tokens | ||||
| type tokenType int | ||||
|  | ||||
| const ( | ||||
| 	eof = -(iota + 1) | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	tokenError tokenType = iota | ||||
| 	tokenEOF | ||||
| 	tokenComment | ||||
| 	tokenKey | ||||
| 	tokenString | ||||
| 	tokenInteger | ||||
| 	tokenTrue | ||||
| 	tokenFalse | ||||
| 	tokenFloat | ||||
| 	tokenInf | ||||
| 	tokenNan | ||||
| 	tokenEqual | ||||
| 	tokenLeftBracket | ||||
| 	tokenRightBracket | ||||
| 	tokenLeftCurlyBrace | ||||
| 	tokenRightCurlyBrace | ||||
| 	tokenLeftParen | ||||
| 	tokenRightParen | ||||
| 	tokenDoubleLeftBracket | ||||
| 	tokenDoubleRightBracket | ||||
| 	tokenLocalDate | ||||
| 	tokenLocalTime | ||||
| 	tokenTimeOffset | ||||
| 	tokenKeyGroup | ||||
| 	tokenKeyGroupArray | ||||
| 	tokenComma | ||||
| 	tokenColon | ||||
| 	tokenDollar | ||||
| 	tokenStar | ||||
| 	tokenQuestion | ||||
| 	tokenDot | ||||
| 	tokenDotDot | ||||
| 	tokenEOL | ||||
| ) | ||||
|  | ||||
| var tokenTypeNames = []string{ | ||||
| 	"Error", | ||||
| 	"EOF", | ||||
| 	"Comment", | ||||
| 	"Key", | ||||
| 	"String", | ||||
| 	"Integer", | ||||
| 	"True", | ||||
| 	"False", | ||||
| 	"Float", | ||||
| 	"Inf", | ||||
| 	"NaN", | ||||
| 	"=", | ||||
| 	"[", | ||||
| 	"]", | ||||
| 	"{", | ||||
| 	"}", | ||||
| 	"(", | ||||
| 	")", | ||||
| 	"]]", | ||||
| 	"[[", | ||||
| 	"LocalDate", | ||||
| 	"LocalTime", | ||||
| 	"TimeOffset", | ||||
| 	"KeyGroup", | ||||
| 	"KeyGroupArray", | ||||
| 	",", | ||||
| 	":", | ||||
| 	"$", | ||||
| 	"*", | ||||
| 	"?", | ||||
| 	".", | ||||
| 	"..", | ||||
| 	"EOL", | ||||
| } | ||||
|  | ||||
| type token struct { | ||||
| 	Position | ||||
| 	typ tokenType | ||||
| 	val string | ||||
| } | ||||
|  | ||||
| func (tt tokenType) String() string { | ||||
| 	idx := int(tt) | ||||
| 	if idx < len(tokenTypeNames) { | ||||
| 		return tokenTypeNames[idx] | ||||
| 	} | ||||
| 	return "Unknown" | ||||
| } | ||||
|  | ||||
| func (t token) String() string { | ||||
| 	switch t.typ { | ||||
| 	case tokenEOF: | ||||
| 		return "EOF" | ||||
| 	case tokenError: | ||||
| 		return t.val | ||||
| 	} | ||||
|  | ||||
| 	return fmt.Sprintf("%q", t.val) | ||||
| } | ||||
|  | ||||
| func isSpace(r rune) bool { | ||||
| 	return r == ' ' || r == '\t' | ||||
| } | ||||
|  | ||||
| func isAlphanumeric(r rune) bool { | ||||
| 	return 'a' <= r && r <= 'z' || 'A' <= r && r <= 'Z' || r == '_' | ||||
| } | ||||
|  | ||||
| func isKeyChar(r rune) bool { | ||||
| 	// Keys start with the first character that isn't whitespace or [ and end | ||||
| 	// with the last non-whitespace character before the equals sign. Keys | ||||
| 	// cannot contain a # character." | ||||
| 	return !(r == '\r' || r == '\n' || r == eof || r == '=') | ||||
| } | ||||
|  | ||||
| func isKeyStartChar(r rune) bool { | ||||
| 	return !(isSpace(r) || r == '\r' || r == '\n' || r == eof || r == '[') | ||||
| } | ||||
|  | ||||
| func isDigit(r rune) bool { | ||||
| 	return '0' <= r && r <= '9' | ||||
| } | ||||
|  | ||||
| func isHexDigit(r rune) bool { | ||||
| 	return isDigit(r) || | ||||
| 		(r >= 'a' && r <= 'f') || | ||||
| 		(r >= 'A' && r <= 'F') | ||||
| } | ||||
							
								
								
									
										529
									
								
								pkg/init/cmd/service/vendor/github.com/pelletier/go-toml/toml.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										529
									
								
								pkg/init/cmd/service/vendor/github.com/pelletier/go-toml/toml.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,529 @@ | ||||
| package toml | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"os" | ||||
| 	"runtime" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| type tomlValue struct { | ||||
| 	value     interface{} // string, int64, uint64, float64, bool, time.Time, [] of any of this list | ||||
| 	comment   string | ||||
| 	commented bool | ||||
| 	multiline bool | ||||
| 	position  Position | ||||
| } | ||||
|  | ||||
| // Tree is the result of the parsing of a TOML file. | ||||
| type Tree struct { | ||||
| 	values    map[string]interface{} // string -> *tomlValue, *Tree, []*Tree | ||||
| 	comment   string | ||||
| 	commented bool | ||||
| 	inline    bool | ||||
| 	position  Position | ||||
| } | ||||
|  | ||||
| func newTree() *Tree { | ||||
| 	return newTreeWithPosition(Position{}) | ||||
| } | ||||
|  | ||||
| func newTreeWithPosition(pos Position) *Tree { | ||||
| 	return &Tree{ | ||||
| 		values:   make(map[string]interface{}), | ||||
| 		position: pos, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // TreeFromMap initializes a new Tree object using the given map. | ||||
| func TreeFromMap(m map[string]interface{}) (*Tree, error) { | ||||
| 	result, err := toTree(m) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return result.(*Tree), nil | ||||
| } | ||||
|  | ||||
| // Position returns the position of the tree. | ||||
| func (t *Tree) Position() Position { | ||||
| 	return t.position | ||||
| } | ||||
|  | ||||
| // Has returns a boolean indicating if the given key exists. | ||||
| func (t *Tree) Has(key string) bool { | ||||
| 	if key == "" { | ||||
| 		return false | ||||
| 	} | ||||
| 	return t.HasPath(strings.Split(key, ".")) | ||||
| } | ||||
|  | ||||
| // HasPath returns true if the given path of keys exists, false otherwise. | ||||
| func (t *Tree) HasPath(keys []string) bool { | ||||
| 	return t.GetPath(keys) != nil | ||||
| } | ||||
|  | ||||
| // Keys returns the keys of the toplevel tree (does not recurse). | ||||
| func (t *Tree) Keys() []string { | ||||
| 	keys := make([]string, len(t.values)) | ||||
| 	i := 0 | ||||
| 	for k := range t.values { | ||||
| 		keys[i] = k | ||||
| 		i++ | ||||
| 	} | ||||
| 	return keys | ||||
| } | ||||
|  | ||||
| // Get the value at key in the Tree. | ||||
| // Key is a dot-separated path (e.g. a.b.c) without single/double quoted strings. | ||||
| // If you need to retrieve non-bare keys, use GetPath. | ||||
| // Returns nil if the path does not exist in the tree. | ||||
| // If keys is of length zero, the current tree is returned. | ||||
| func (t *Tree) Get(key string) interface{} { | ||||
| 	if key == "" { | ||||
| 		return t | ||||
| 	} | ||||
| 	return t.GetPath(strings.Split(key, ".")) | ||||
| } | ||||
|  | ||||
| // GetPath returns the element in the tree indicated by 'keys'. | ||||
| // If keys is of length zero, the current tree is returned. | ||||
| func (t *Tree) GetPath(keys []string) interface{} { | ||||
| 	if len(keys) == 0 { | ||||
| 		return t | ||||
| 	} | ||||
| 	subtree := t | ||||
| 	for _, intermediateKey := range keys[:len(keys)-1] { | ||||
| 		value, exists := subtree.values[intermediateKey] | ||||
| 		if !exists { | ||||
| 			return nil | ||||
| 		} | ||||
| 		switch node := value.(type) { | ||||
| 		case *Tree: | ||||
| 			subtree = node | ||||
| 		case []*Tree: | ||||
| 			// go to most recent element | ||||
| 			if len(node) == 0 { | ||||
| 				return nil | ||||
| 			} | ||||
| 			subtree = node[len(node)-1] | ||||
| 		default: | ||||
| 			return nil // cannot navigate through other node types | ||||
| 		} | ||||
| 	} | ||||
| 	// branch based on final node type | ||||
| 	switch node := subtree.values[keys[len(keys)-1]].(type) { | ||||
| 	case *tomlValue: | ||||
| 		return node.value | ||||
| 	default: | ||||
| 		return node | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // GetArray returns the value at key in the Tree. | ||||
| // It returns []string, []int64, etc type if key has homogeneous lists | ||||
| // Key is a dot-separated path (e.g. a.b.c) without single/double quoted strings. | ||||
| // Returns nil if the path does not exist in the tree. | ||||
| // If keys is of length zero, the current tree is returned. | ||||
| func (t *Tree) GetArray(key string) interface{} { | ||||
| 	if key == "" { | ||||
| 		return t | ||||
| 	} | ||||
| 	return t.GetArrayPath(strings.Split(key, ".")) | ||||
| } | ||||
|  | ||||
| // GetArrayPath returns the element in the tree indicated by 'keys'. | ||||
| // If keys is of length zero, the current tree is returned. | ||||
| func (t *Tree) GetArrayPath(keys []string) interface{} { | ||||
| 	if len(keys) == 0 { | ||||
| 		return t | ||||
| 	} | ||||
| 	subtree := t | ||||
| 	for _, intermediateKey := range keys[:len(keys)-1] { | ||||
| 		value, exists := subtree.values[intermediateKey] | ||||
| 		if !exists { | ||||
| 			return nil | ||||
| 		} | ||||
| 		switch node := value.(type) { | ||||
| 		case *Tree: | ||||
| 			subtree = node | ||||
| 		case []*Tree: | ||||
| 			// go to most recent element | ||||
| 			if len(node) == 0 { | ||||
| 				return nil | ||||
| 			} | ||||
| 			subtree = node[len(node)-1] | ||||
| 		default: | ||||
| 			return nil // cannot navigate through other node types | ||||
| 		} | ||||
| 	} | ||||
| 	// branch based on final node type | ||||
| 	switch node := subtree.values[keys[len(keys)-1]].(type) { | ||||
| 	case *tomlValue: | ||||
| 		switch n := node.value.(type) { | ||||
| 		case []interface{}: | ||||
| 			return getArray(n) | ||||
| 		default: | ||||
| 			return node.value | ||||
| 		} | ||||
| 	default: | ||||
| 		return node | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // if homogeneous array, then return slice type object over []interface{} | ||||
| func getArray(n []interface{}) interface{} { | ||||
| 	var s []string | ||||
| 	var i64 []int64 | ||||
| 	var f64 []float64 | ||||
| 	var bl []bool | ||||
| 	for _, value := range n { | ||||
| 		switch v := value.(type) { | ||||
| 		case string: | ||||
| 			s = append(s, v) | ||||
| 		case int64: | ||||
| 			i64 = append(i64, v) | ||||
| 		case float64: | ||||
| 			f64 = append(f64, v) | ||||
| 		case bool: | ||||
| 			bl = append(bl, v) | ||||
| 		default: | ||||
| 			return n | ||||
| 		} | ||||
| 	} | ||||
| 	if len(s) == len(n) { | ||||
| 		return s | ||||
| 	} else if len(i64) == len(n) { | ||||
| 		return i64 | ||||
| 	} else if len(f64) == len(n) { | ||||
| 		return f64 | ||||
| 	} else if len(bl) == len(n) { | ||||
| 		return bl | ||||
| 	} | ||||
| 	return n | ||||
| } | ||||
|  | ||||
| // GetPosition returns the position of the given key. | ||||
| func (t *Tree) GetPosition(key string) Position { | ||||
| 	if key == "" { | ||||
| 		return t.position | ||||
| 	} | ||||
| 	return t.GetPositionPath(strings.Split(key, ".")) | ||||
| } | ||||
|  | ||||
| // SetPositionPath sets the position of element in the tree indicated by 'keys'. | ||||
| // If keys is of length zero, the current tree position is set. | ||||
| func (t *Tree) SetPositionPath(keys []string, pos Position) { | ||||
| 	if len(keys) == 0 { | ||||
| 		t.position = pos | ||||
| 		return | ||||
| 	} | ||||
| 	subtree := t | ||||
| 	for _, intermediateKey := range keys[:len(keys)-1] { | ||||
| 		value, exists := subtree.values[intermediateKey] | ||||
| 		if !exists { | ||||
| 			return | ||||
| 		} | ||||
| 		switch node := value.(type) { | ||||
| 		case *Tree: | ||||
| 			subtree = node | ||||
| 		case []*Tree: | ||||
| 			// go to most recent element | ||||
| 			if len(node) == 0 { | ||||
| 				return | ||||
| 			} | ||||
| 			subtree = node[len(node)-1] | ||||
| 		default: | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| 	// branch based on final node type | ||||
| 	switch node := subtree.values[keys[len(keys)-1]].(type) { | ||||
| 	case *tomlValue: | ||||
| 		node.position = pos | ||||
| 		return | ||||
| 	case *Tree: | ||||
| 		node.position = pos | ||||
| 		return | ||||
| 	case []*Tree: | ||||
| 		// go to most recent element | ||||
| 		if len(node) == 0 { | ||||
| 			return | ||||
| 		} | ||||
| 		node[len(node)-1].position = pos | ||||
| 		return | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // GetPositionPath returns the element in the tree indicated by 'keys'. | ||||
| // If keys is of length zero, the current tree is returned. | ||||
| func (t *Tree) GetPositionPath(keys []string) Position { | ||||
| 	if len(keys) == 0 { | ||||
| 		return t.position | ||||
| 	} | ||||
| 	subtree := t | ||||
| 	for _, intermediateKey := range keys[:len(keys)-1] { | ||||
| 		value, exists := subtree.values[intermediateKey] | ||||
| 		if !exists { | ||||
| 			return Position{0, 0} | ||||
| 		} | ||||
| 		switch node := value.(type) { | ||||
| 		case *Tree: | ||||
| 			subtree = node | ||||
| 		case []*Tree: | ||||
| 			// go to most recent element | ||||
| 			if len(node) == 0 { | ||||
| 				return Position{0, 0} | ||||
| 			} | ||||
| 			subtree = node[len(node)-1] | ||||
| 		default: | ||||
| 			return Position{0, 0} | ||||
| 		} | ||||
| 	} | ||||
| 	// branch based on final node type | ||||
| 	switch node := subtree.values[keys[len(keys)-1]].(type) { | ||||
| 	case *tomlValue: | ||||
| 		return node.position | ||||
| 	case *Tree: | ||||
| 		return node.position | ||||
| 	case []*Tree: | ||||
| 		// go to most recent element | ||||
| 		if len(node) == 0 { | ||||
| 			return Position{0, 0} | ||||
| 		} | ||||
| 		return node[len(node)-1].position | ||||
| 	default: | ||||
| 		return Position{0, 0} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // GetDefault works like Get but with a default value | ||||
| func (t *Tree) GetDefault(key string, def interface{}) interface{} { | ||||
| 	val := t.Get(key) | ||||
| 	if val == nil { | ||||
| 		return def | ||||
| 	} | ||||
| 	return val | ||||
| } | ||||
|  | ||||
| // SetOptions arguments are supplied to the SetWithOptions and SetPathWithOptions functions to modify marshalling behaviour. | ||||
| // The default values within the struct are valid default options. | ||||
| type SetOptions struct { | ||||
| 	Comment   string | ||||
| 	Commented bool | ||||
| 	Multiline bool | ||||
| } | ||||
|  | ||||
| // SetWithOptions is the same as Set, but allows you to provide formatting | ||||
| // instructions to the key, that will be used by Marshal(). | ||||
| func (t *Tree) SetWithOptions(key string, opts SetOptions, value interface{}) { | ||||
| 	t.SetPathWithOptions(strings.Split(key, "."), opts, value) | ||||
| } | ||||
|  | ||||
| // SetPathWithOptions is the same as SetPath, but allows you to provide | ||||
| // formatting instructions to the key, that will be reused by Marshal(). | ||||
| func (t *Tree) SetPathWithOptions(keys []string, opts SetOptions, value interface{}) { | ||||
| 	subtree := t | ||||
| 	for i, intermediateKey := range keys[:len(keys)-1] { | ||||
| 		nextTree, exists := subtree.values[intermediateKey] | ||||
| 		if !exists { | ||||
| 			nextTree = newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col}) | ||||
| 			subtree.values[intermediateKey] = nextTree // add new element here | ||||
| 		} | ||||
| 		switch node := nextTree.(type) { | ||||
| 		case *Tree: | ||||
| 			subtree = node | ||||
| 		case []*Tree: | ||||
| 			// go to most recent element | ||||
| 			if len(node) == 0 { | ||||
| 				// create element if it does not exist | ||||
| 				node = append(node, newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col})) | ||||
| 				subtree.values[intermediateKey] = node | ||||
| 			} | ||||
| 			subtree = node[len(node)-1] | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	var toInsert interface{} | ||||
|  | ||||
| 	switch v := value.(type) { | ||||
| 	case *Tree: | ||||
| 		v.comment = opts.Comment | ||||
| 		v.commented = opts.Commented | ||||
| 		toInsert = value | ||||
| 	case []*Tree: | ||||
| 		for i := range v { | ||||
| 			v[i].commented = opts.Commented | ||||
| 		} | ||||
| 		toInsert = value | ||||
| 	case *tomlValue: | ||||
| 		v.comment = opts.Comment | ||||
| 		v.commented = opts.Commented | ||||
| 		v.multiline = opts.Multiline | ||||
| 		toInsert = v | ||||
| 	default: | ||||
| 		toInsert = &tomlValue{value: value, | ||||
| 			comment:   opts.Comment, | ||||
| 			commented: opts.Commented, | ||||
| 			multiline: opts.Multiline, | ||||
| 			position:  Position{Line: subtree.position.Line + len(subtree.values) + 1, Col: subtree.position.Col}} | ||||
| 	} | ||||
|  | ||||
| 	subtree.values[keys[len(keys)-1]] = toInsert | ||||
| } | ||||
|  | ||||
| // Set an element in the tree. | ||||
| // Key is a dot-separated path (e.g. a.b.c). | ||||
| // Creates all necessary intermediate trees, if needed. | ||||
| func (t *Tree) Set(key string, value interface{}) { | ||||
| 	t.SetWithComment(key, "", false, value) | ||||
| } | ||||
|  | ||||
| // SetWithComment is the same as Set, but allows you to provide comment | ||||
| // information to the key, that will be reused by Marshal(). | ||||
| func (t *Tree) SetWithComment(key string, comment string, commented bool, value interface{}) { | ||||
| 	t.SetPathWithComment(strings.Split(key, "."), comment, commented, value) | ||||
| } | ||||
|  | ||||
| // SetPath sets an element in the tree. | ||||
| // Keys is an array of path elements (e.g. {"a","b","c"}). | ||||
| // Creates all necessary intermediate trees, if needed. | ||||
| func (t *Tree) SetPath(keys []string, value interface{}) { | ||||
| 	t.SetPathWithComment(keys, "", false, value) | ||||
| } | ||||
|  | ||||
| // SetPathWithComment is the same as SetPath, but allows you to provide comment | ||||
| // information to the key, that will be reused by Marshal(). | ||||
| func (t *Tree) SetPathWithComment(keys []string, comment string, commented bool, value interface{}) { | ||||
| 	t.SetPathWithOptions(keys, SetOptions{Comment: comment, Commented: commented}, value) | ||||
| } | ||||
|  | ||||
| // Delete removes a key from the tree. | ||||
| // Key is a dot-separated path (e.g. a.b.c). | ||||
| func (t *Tree) Delete(key string) error { | ||||
| 	keys, err := parseKey(key) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return t.DeletePath(keys) | ||||
| } | ||||
|  | ||||
| // DeletePath removes a key from the tree. | ||||
| // Keys is an array of path elements (e.g. {"a","b","c"}). | ||||
| func (t *Tree) DeletePath(keys []string) error { | ||||
| 	keyLen := len(keys) | ||||
| 	if keyLen == 1 { | ||||
| 		delete(t.values, keys[0]) | ||||
| 		return nil | ||||
| 	} | ||||
| 	tree := t.GetPath(keys[:keyLen-1]) | ||||
| 	item := keys[keyLen-1] | ||||
| 	switch node := tree.(type) { | ||||
| 	case *Tree: | ||||
| 		delete(node.values, item) | ||||
| 		return nil | ||||
| 	} | ||||
| 	return errors.New("no such key to delete") | ||||
| } | ||||
|  | ||||
| // createSubTree takes a tree and a key and create the necessary intermediate | ||||
| // subtrees to create a subtree at that point. In-place. | ||||
| // | ||||
| // e.g. passing a.b.c will create (assuming tree is empty) tree[a], tree[a][b] | ||||
| // and tree[a][b][c] | ||||
| // | ||||
| // Returns nil on success, error object on failure | ||||
| func (t *Tree) createSubTree(keys []string, pos Position) error { | ||||
| 	subtree := t | ||||
| 	for i, intermediateKey := range keys { | ||||
| 		nextTree, exists := subtree.values[intermediateKey] | ||||
| 		if !exists { | ||||
| 			tree := newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col}) | ||||
| 			tree.position = pos | ||||
| 			tree.inline = subtree.inline | ||||
| 			subtree.values[intermediateKey] = tree | ||||
| 			nextTree = tree | ||||
| 		} | ||||
|  | ||||
| 		switch node := nextTree.(type) { | ||||
| 		case []*Tree: | ||||
| 			subtree = node[len(node)-1] | ||||
| 		case *Tree: | ||||
| 			subtree = node | ||||
| 		default: | ||||
| 			return fmt.Errorf("unknown type for path %s (%s): %T (%#v)", | ||||
| 				strings.Join(keys, "."), intermediateKey, nextTree, nextTree) | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // LoadBytes creates a Tree from a []byte. | ||||
| func LoadBytes(b []byte) (tree *Tree, err error) { | ||||
| 	defer func() { | ||||
| 		if r := recover(); r != nil { | ||||
| 			if _, ok := r.(runtime.Error); ok { | ||||
| 				panic(r) | ||||
| 			} | ||||
| 			err = errors.New(r.(string)) | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	if len(b) >= 4 && (hasUTF32BigEndianBOM4(b) || hasUTF32LittleEndianBOM4(b)) { | ||||
| 		b = b[4:] | ||||
| 	} else if len(b) >= 3 && hasUTF8BOM3(b) { | ||||
| 		b = b[3:] | ||||
| 	} else if len(b) >= 2 && (hasUTF16BigEndianBOM2(b) || hasUTF16LittleEndianBOM2(b)) { | ||||
| 		b = b[2:] | ||||
| 	} | ||||
|  | ||||
| 	tree = parseToml(lexToml(b)) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func hasUTF16BigEndianBOM2(b []byte) bool { | ||||
| 	return b[0] == 0xFE && b[1] == 0xFF | ||||
| } | ||||
|  | ||||
| func hasUTF16LittleEndianBOM2(b []byte) bool { | ||||
| 	return b[0] == 0xFF && b[1] == 0xFE | ||||
| } | ||||
|  | ||||
| func hasUTF8BOM3(b []byte) bool { | ||||
| 	return b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF | ||||
| } | ||||
|  | ||||
| func hasUTF32BigEndianBOM4(b []byte) bool { | ||||
| 	return b[0] == 0x00 && b[1] == 0x00 && b[2] == 0xFE && b[3] == 0xFF | ||||
| } | ||||
|  | ||||
| func hasUTF32LittleEndianBOM4(b []byte) bool { | ||||
| 	return b[0] == 0xFF && b[1] == 0xFE && b[2] == 0x00 && b[3] == 0x00 | ||||
| } | ||||
|  | ||||
| // LoadReader creates a Tree from any io.Reader. | ||||
| func LoadReader(reader io.Reader) (tree *Tree, err error) { | ||||
| 	inputBytes, err := ioutil.ReadAll(reader) | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 	tree, err = LoadBytes(inputBytes) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // Load creates a Tree from a string. | ||||
| func Load(content string) (tree *Tree, err error) { | ||||
| 	return LoadBytes([]byte(content)) | ||||
| } | ||||
|  | ||||
| // LoadFile creates a Tree from a file. | ||||
| func LoadFile(path string) (tree *Tree, err error) { | ||||
| 	file, err := os.Open(path) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer file.Close() | ||||
| 	return LoadReader(file) | ||||
| } | ||||
							
								
								
									
										155
									
								
								pkg/init/cmd/service/vendor/github.com/pelletier/go-toml/tomltree_create.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										155
									
								
								pkg/init/cmd/service/vendor/github.com/pelletier/go-toml/tomltree_create.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,155 @@ | ||||
| package toml | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"reflect" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| var kindToType = [reflect.String + 1]reflect.Type{ | ||||
| 	reflect.Bool:    reflect.TypeOf(true), | ||||
| 	reflect.String:  reflect.TypeOf(""), | ||||
| 	reflect.Float32: reflect.TypeOf(float64(1)), | ||||
| 	reflect.Float64: reflect.TypeOf(float64(1)), | ||||
| 	reflect.Int:     reflect.TypeOf(int64(1)), | ||||
| 	reflect.Int8:    reflect.TypeOf(int64(1)), | ||||
| 	reflect.Int16:   reflect.TypeOf(int64(1)), | ||||
| 	reflect.Int32:   reflect.TypeOf(int64(1)), | ||||
| 	reflect.Int64:   reflect.TypeOf(int64(1)), | ||||
| 	reflect.Uint:    reflect.TypeOf(uint64(1)), | ||||
| 	reflect.Uint8:   reflect.TypeOf(uint64(1)), | ||||
| 	reflect.Uint16:  reflect.TypeOf(uint64(1)), | ||||
| 	reflect.Uint32:  reflect.TypeOf(uint64(1)), | ||||
| 	reflect.Uint64:  reflect.TypeOf(uint64(1)), | ||||
| } | ||||
|  | ||||
| // typeFor returns a reflect.Type for a reflect.Kind, or nil if none is found. | ||||
| // supported values: | ||||
| // string, bool, int64, uint64, float64, time.Time, int, int8, int16, int32, uint, uint8, uint16, uint32, float32 | ||||
| func typeFor(k reflect.Kind) reflect.Type { | ||||
| 	if k > 0 && int(k) < len(kindToType) { | ||||
| 		return kindToType[k] | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func simpleValueCoercion(object interface{}) (interface{}, error) { | ||||
| 	switch original := object.(type) { | ||||
| 	case string, bool, int64, uint64, float64, time.Time: | ||||
| 		return original, nil | ||||
| 	case int: | ||||
| 		return int64(original), nil | ||||
| 	case int8: | ||||
| 		return int64(original), nil | ||||
| 	case int16: | ||||
| 		return int64(original), nil | ||||
| 	case int32: | ||||
| 		return int64(original), nil | ||||
| 	case uint: | ||||
| 		return uint64(original), nil | ||||
| 	case uint8: | ||||
| 		return uint64(original), nil | ||||
| 	case uint16: | ||||
| 		return uint64(original), nil | ||||
| 	case uint32: | ||||
| 		return uint64(original), nil | ||||
| 	case float32: | ||||
| 		return float64(original), nil | ||||
| 	case fmt.Stringer: | ||||
| 		return original.String(), nil | ||||
| 	case []interface{}: | ||||
| 		value := reflect.ValueOf(original) | ||||
| 		length := value.Len() | ||||
| 		arrayValue := reflect.MakeSlice(value.Type(), 0, length) | ||||
| 		for i := 0; i < length; i++ { | ||||
| 			val := value.Index(i).Interface() | ||||
| 			simpleValue, err := simpleValueCoercion(val) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			arrayValue = reflect.Append(arrayValue, reflect.ValueOf(simpleValue)) | ||||
| 		} | ||||
| 		return arrayValue.Interface(), nil | ||||
| 	default: | ||||
| 		return nil, fmt.Errorf("cannot convert type %T to Tree", object) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func sliceToTree(object interface{}) (interface{}, error) { | ||||
| 	// arrays are a bit tricky, since they can represent either a | ||||
| 	// collection of simple values, which is represented by one | ||||
| 	// *tomlValue, or an array of tables, which is represented by an | ||||
| 	// array of *Tree. | ||||
|  | ||||
| 	// holding the assumption that this function is called from toTree only when value.Kind() is Array or Slice | ||||
| 	value := reflect.ValueOf(object) | ||||
| 	insideType := value.Type().Elem() | ||||
| 	length := value.Len() | ||||
| 	if length > 0 { | ||||
| 		insideType = reflect.ValueOf(value.Index(0).Interface()).Type() | ||||
| 	} | ||||
| 	if insideType.Kind() == reflect.Map { | ||||
| 		// this is considered as an array of tables | ||||
| 		tablesArray := make([]*Tree, 0, length) | ||||
| 		for i := 0; i < length; i++ { | ||||
| 			table := value.Index(i) | ||||
| 			tree, err := toTree(table.Interface()) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			tablesArray = append(tablesArray, tree.(*Tree)) | ||||
| 		} | ||||
| 		return tablesArray, nil | ||||
| 	} | ||||
|  | ||||
| 	sliceType := typeFor(insideType.Kind()) | ||||
| 	if sliceType == nil { | ||||
| 		sliceType = insideType | ||||
| 	} | ||||
|  | ||||
| 	arrayValue := reflect.MakeSlice(reflect.SliceOf(sliceType), 0, length) | ||||
|  | ||||
| 	for i := 0; i < length; i++ { | ||||
| 		val := value.Index(i).Interface() | ||||
| 		simpleValue, err := simpleValueCoercion(val) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		arrayValue = reflect.Append(arrayValue, reflect.ValueOf(simpleValue)) | ||||
| 	} | ||||
| 	return &tomlValue{value: arrayValue.Interface(), position: Position{}}, nil | ||||
| } | ||||
|  | ||||
| func toTree(object interface{}) (interface{}, error) { | ||||
| 	value := reflect.ValueOf(object) | ||||
|  | ||||
| 	if value.Kind() == reflect.Map { | ||||
| 		values := map[string]interface{}{} | ||||
| 		keys := value.MapKeys() | ||||
| 		for _, key := range keys { | ||||
| 			if key.Kind() != reflect.String { | ||||
| 				if _, ok := key.Interface().(string); !ok { | ||||
| 					return nil, fmt.Errorf("map key needs to be a string, not %T (%v)", key.Interface(), key.Kind()) | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			v := value.MapIndex(key) | ||||
| 			newValue, err := toTree(v.Interface()) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			values[key.String()] = newValue | ||||
| 		} | ||||
| 		return &Tree{values: values, position: Position{}}, nil | ||||
| 	} | ||||
|  | ||||
| 	if value.Kind() == reflect.Array || value.Kind() == reflect.Slice { | ||||
| 		return sliceToTree(object) | ||||
| 	} | ||||
|  | ||||
| 	simpleValue, err := simpleValueCoercion(object) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &tomlValue{value: simpleValue, position: Position{}}, nil | ||||
| } | ||||
							
								
								
									
										517
									
								
								pkg/init/cmd/service/vendor/github.com/pelletier/go-toml/tomltree_write.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										517
									
								
								pkg/init/cmd/service/vendor/github.com/pelletier/go-toml/tomltree_write.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,517 @@ | ||||
| package toml | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"math" | ||||
| 	"math/big" | ||||
| 	"reflect" | ||||
| 	"sort" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| type valueComplexity int | ||||
|  | ||||
| const ( | ||||
| 	valueSimple valueComplexity = iota + 1 | ||||
| 	valueComplex | ||||
| ) | ||||
|  | ||||
| type sortNode struct { | ||||
| 	key        string | ||||
| 	complexity valueComplexity | ||||
| } | ||||
|  | ||||
| // Encodes a string to a TOML-compliant multi-line string value | ||||
| // This function is a clone of the existing encodeTomlString function, except that whitespace characters | ||||
| // are preserved. Quotation marks and backslashes are also not escaped. | ||||
| func encodeMultilineTomlString(value string, commented string) string { | ||||
| 	var b bytes.Buffer | ||||
| 	adjacentQuoteCount := 0 | ||||
|  | ||||
| 	b.WriteString(commented) | ||||
| 	for i, rr := range value { | ||||
| 		if rr != '"' { | ||||
| 			adjacentQuoteCount = 0 | ||||
| 		} else { | ||||
| 			adjacentQuoteCount++ | ||||
| 		} | ||||
| 		switch rr { | ||||
| 		case '\b': | ||||
| 			b.WriteString(`\b`) | ||||
| 		case '\t': | ||||
| 			b.WriteString("\t") | ||||
| 		case '\n': | ||||
| 			b.WriteString("\n" + commented) | ||||
| 		case '\f': | ||||
| 			b.WriteString(`\f`) | ||||
| 		case '\r': | ||||
| 			b.WriteString("\r") | ||||
| 		case '"': | ||||
| 			if adjacentQuoteCount >= 3 || i == len(value)-1 { | ||||
| 				adjacentQuoteCount = 0 | ||||
| 				b.WriteString(`\"`) | ||||
| 			} else { | ||||
| 				b.WriteString(`"`) | ||||
| 			} | ||||
| 		case '\\': | ||||
| 			b.WriteString(`\`) | ||||
| 		default: | ||||
| 			intRr := uint16(rr) | ||||
| 			if intRr < 0x001F { | ||||
| 				b.WriteString(fmt.Sprintf("\\u%0.4X", intRr)) | ||||
| 			} else { | ||||
| 				b.WriteRune(rr) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return b.String() | ||||
| } | ||||
|  | ||||
| // Encodes a string to a TOML-compliant string value | ||||
| func encodeTomlString(value string) string { | ||||
| 	var b bytes.Buffer | ||||
|  | ||||
| 	for _, rr := range value { | ||||
| 		switch rr { | ||||
| 		case '\b': | ||||
| 			b.WriteString(`\b`) | ||||
| 		case '\t': | ||||
| 			b.WriteString(`\t`) | ||||
| 		case '\n': | ||||
| 			b.WriteString(`\n`) | ||||
| 		case '\f': | ||||
| 			b.WriteString(`\f`) | ||||
| 		case '\r': | ||||
| 			b.WriteString(`\r`) | ||||
| 		case '"': | ||||
| 			b.WriteString(`\"`) | ||||
| 		case '\\': | ||||
| 			b.WriteString(`\\`) | ||||
| 		default: | ||||
| 			intRr := uint16(rr) | ||||
| 			if intRr < 0x001F { | ||||
| 				b.WriteString(fmt.Sprintf("\\u%0.4X", intRr)) | ||||
| 			} else { | ||||
| 				b.WriteRune(rr) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return b.String() | ||||
| } | ||||
|  | ||||
| func tomlTreeStringRepresentation(t *Tree, ord marshalOrder) (string, error) { | ||||
| 	var orderedVals []sortNode | ||||
| 	switch ord { | ||||
| 	case OrderPreserve: | ||||
| 		orderedVals = sortByLines(t) | ||||
| 	default: | ||||
| 		orderedVals = sortAlphabetical(t) | ||||
| 	} | ||||
|  | ||||
| 	var values []string | ||||
| 	for _, node := range orderedVals { | ||||
| 		k := node.key | ||||
| 		v := t.values[k] | ||||
|  | ||||
| 		repr, err := tomlValueStringRepresentation(v, "", "", ord, false) | ||||
| 		if err != nil { | ||||
| 			return "", err | ||||
| 		} | ||||
| 		values = append(values, quoteKeyIfNeeded(k)+" = "+repr) | ||||
| 	} | ||||
| 	return "{ " + strings.Join(values, ", ") + " }", nil | ||||
| } | ||||
|  | ||||
| func tomlValueStringRepresentation(v interface{}, commented string, indent string, ord marshalOrder, arraysOneElementPerLine bool) (string, error) { | ||||
| 	// this interface check is added to dereference the change made in the writeTo function. | ||||
| 	// That change was made to allow this function to see formatting options. | ||||
| 	tv, ok := v.(*tomlValue) | ||||
| 	if ok { | ||||
| 		v = tv.value | ||||
| 	} else { | ||||
| 		tv = &tomlValue{} | ||||
| 	} | ||||
|  | ||||
| 	switch value := v.(type) { | ||||
| 	case uint64: | ||||
| 		return strconv.FormatUint(value, 10), nil | ||||
| 	case int64: | ||||
| 		return strconv.FormatInt(value, 10), nil | ||||
| 	case float64: | ||||
| 		// Default bit length is full 64 | ||||
| 		bits := 64 | ||||
| 		// Float panics if nan is used | ||||
| 		if !math.IsNaN(value) { | ||||
| 			// if 32 bit accuracy is enough to exactly show, use 32 | ||||
| 			_, acc := big.NewFloat(value).Float32() | ||||
| 			if acc == big.Exact { | ||||
| 				bits = 32 | ||||
| 			} | ||||
| 		} | ||||
| 		if math.Trunc(value) == value { | ||||
| 			return strings.ToLower(strconv.FormatFloat(value, 'f', 1, bits)), nil | ||||
| 		} | ||||
| 		return strings.ToLower(strconv.FormatFloat(value, 'f', -1, bits)), nil | ||||
| 	case string: | ||||
| 		if tv.multiline { | ||||
| 			return "\"\"\"\n" + encodeMultilineTomlString(value, commented) + "\"\"\"", nil | ||||
| 		} | ||||
| 		return "\"" + encodeTomlString(value) + "\"", nil | ||||
| 	case []byte: | ||||
| 		b, _ := v.([]byte) | ||||
| 		return string(b), nil | ||||
| 	case bool: | ||||
| 		if value { | ||||
| 			return "true", nil | ||||
| 		} | ||||
| 		return "false", nil | ||||
| 	case time.Time: | ||||
| 		return value.Format(time.RFC3339), nil | ||||
| 	case LocalDate: | ||||
| 		return value.String(), nil | ||||
| 	case LocalDateTime: | ||||
| 		return value.String(), nil | ||||
| 	case LocalTime: | ||||
| 		return value.String(), nil | ||||
| 	case *Tree: | ||||
| 		return tomlTreeStringRepresentation(value, ord) | ||||
| 	case nil: | ||||
| 		return "", nil | ||||
| 	} | ||||
|  | ||||
| 	rv := reflect.ValueOf(v) | ||||
|  | ||||
| 	if rv.Kind() == reflect.Slice { | ||||
| 		var values []string | ||||
| 		for i := 0; i < rv.Len(); i++ { | ||||
| 			item := rv.Index(i).Interface() | ||||
| 			itemRepr, err := tomlValueStringRepresentation(item, commented, indent, ord, arraysOneElementPerLine) | ||||
| 			if err != nil { | ||||
| 				return "", err | ||||
| 			} | ||||
| 			values = append(values, itemRepr) | ||||
| 		} | ||||
| 		if arraysOneElementPerLine && len(values) > 1 { | ||||
| 			stringBuffer := bytes.Buffer{} | ||||
| 			valueIndent := indent + `  ` // TODO: move that to a shared encoder state | ||||
|  | ||||
| 			stringBuffer.WriteString("[\n") | ||||
|  | ||||
| 			for _, value := range values { | ||||
| 				stringBuffer.WriteString(valueIndent) | ||||
| 				stringBuffer.WriteString(commented + value) | ||||
| 				stringBuffer.WriteString(`,`) | ||||
| 				stringBuffer.WriteString("\n") | ||||
| 			} | ||||
|  | ||||
| 			stringBuffer.WriteString(indent + commented + "]") | ||||
|  | ||||
| 			return stringBuffer.String(), nil | ||||
| 		} | ||||
| 		return "[" + strings.Join(values, ", ") + "]", nil | ||||
| 	} | ||||
| 	return "", fmt.Errorf("unsupported value type %T: %v", v, v) | ||||
| } | ||||
|  | ||||
| func getTreeArrayLine(trees []*Tree) (line int) { | ||||
| 	// get lowest line number that is not 0 | ||||
| 	for _, tv := range trees { | ||||
| 		if tv.position.Line < line || line == 0 { | ||||
| 			line = tv.position.Line | ||||
| 		} | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func sortByLines(t *Tree) (vals []sortNode) { | ||||
| 	var ( | ||||
| 		line  int | ||||
| 		lines []int | ||||
| 		tv    *Tree | ||||
| 		tom   *tomlValue | ||||
| 		node  sortNode | ||||
| 	) | ||||
| 	vals = make([]sortNode, 0) | ||||
| 	m := make(map[int]sortNode) | ||||
|  | ||||
| 	for k := range t.values { | ||||
| 		v := t.values[k] | ||||
| 		switch v.(type) { | ||||
| 		case *Tree: | ||||
| 			tv = v.(*Tree) | ||||
| 			line = tv.position.Line | ||||
| 			node = sortNode{key: k, complexity: valueComplex} | ||||
| 		case []*Tree: | ||||
| 			line = getTreeArrayLine(v.([]*Tree)) | ||||
| 			node = sortNode{key: k, complexity: valueComplex} | ||||
| 		default: | ||||
| 			tom = v.(*tomlValue) | ||||
| 			line = tom.position.Line | ||||
| 			node = sortNode{key: k, complexity: valueSimple} | ||||
| 		} | ||||
| 		lines = append(lines, line) | ||||
| 		vals = append(vals, node) | ||||
| 		m[line] = node | ||||
| 	} | ||||
| 	sort.Ints(lines) | ||||
|  | ||||
| 	for i, line := range lines { | ||||
| 		vals[i] = m[line] | ||||
| 	} | ||||
|  | ||||
| 	return vals | ||||
| } | ||||
|  | ||||
| func sortAlphabetical(t *Tree) (vals []sortNode) { | ||||
| 	var ( | ||||
| 		node     sortNode | ||||
| 		simpVals []string | ||||
| 		compVals []string | ||||
| 	) | ||||
| 	vals = make([]sortNode, 0) | ||||
| 	m := make(map[string]sortNode) | ||||
|  | ||||
| 	for k := range t.values { | ||||
| 		v := t.values[k] | ||||
| 		switch v.(type) { | ||||
| 		case *Tree, []*Tree: | ||||
| 			node = sortNode{key: k, complexity: valueComplex} | ||||
| 			compVals = append(compVals, node.key) | ||||
| 		default: | ||||
| 			node = sortNode{key: k, complexity: valueSimple} | ||||
| 			simpVals = append(simpVals, node.key) | ||||
| 		} | ||||
| 		vals = append(vals, node) | ||||
| 		m[node.key] = node | ||||
| 	} | ||||
|  | ||||
| 	// Simples first to match previous implementation | ||||
| 	sort.Strings(simpVals) | ||||
| 	i := 0 | ||||
| 	for _, key := range simpVals { | ||||
| 		vals[i] = m[key] | ||||
| 		i++ | ||||
| 	} | ||||
|  | ||||
| 	sort.Strings(compVals) | ||||
| 	for _, key := range compVals { | ||||
| 		vals[i] = m[key] | ||||
| 		i++ | ||||
| 	} | ||||
|  | ||||
| 	return vals | ||||
| } | ||||
|  | ||||
| func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64, arraysOneElementPerLine bool) (int64, error) { | ||||
| 	return t.writeToOrdered(w, indent, keyspace, bytesCount, arraysOneElementPerLine, OrderAlphabetical, "  ", false) | ||||
| } | ||||
|  | ||||
| func (t *Tree) writeToOrdered(w io.Writer, indent, keyspace string, bytesCount int64, arraysOneElementPerLine bool, ord marshalOrder, indentString string, parentCommented bool) (int64, error) { | ||||
| 	var orderedVals []sortNode | ||||
|  | ||||
| 	switch ord { | ||||
| 	case OrderPreserve: | ||||
| 		orderedVals = sortByLines(t) | ||||
| 	default: | ||||
| 		orderedVals = sortAlphabetical(t) | ||||
| 	} | ||||
|  | ||||
| 	for _, node := range orderedVals { | ||||
| 		switch node.complexity { | ||||
| 		case valueComplex: | ||||
| 			k := node.key | ||||
| 			v := t.values[k] | ||||
|  | ||||
| 			combinedKey := quoteKeyIfNeeded(k) | ||||
| 			if keyspace != "" { | ||||
| 				combinedKey = keyspace + "." + combinedKey | ||||
| 			} | ||||
|  | ||||
| 			switch node := v.(type) { | ||||
| 			// node has to be of those two types given how keys are sorted above | ||||
| 			case *Tree: | ||||
| 				tv, ok := t.values[k].(*Tree) | ||||
| 				if !ok { | ||||
| 					return bytesCount, fmt.Errorf("invalid value type at %s: %T", k, t.values[k]) | ||||
| 				} | ||||
| 				if tv.comment != "" { | ||||
| 					comment := strings.Replace(tv.comment, "\n", "\n"+indent+"#", -1) | ||||
| 					start := "# " | ||||
| 					if strings.HasPrefix(comment, "#") { | ||||
| 						start = "" | ||||
| 					} | ||||
| 					writtenBytesCountComment, errc := writeStrings(w, "\n", indent, start, comment) | ||||
| 					bytesCount += int64(writtenBytesCountComment) | ||||
| 					if errc != nil { | ||||
| 						return bytesCount, errc | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				var commented string | ||||
| 				if parentCommented || t.commented || tv.commented { | ||||
| 					commented = "# " | ||||
| 				} | ||||
| 				writtenBytesCount, err := writeStrings(w, "\n", indent, commented, "[", combinedKey, "]\n") | ||||
| 				bytesCount += int64(writtenBytesCount) | ||||
| 				if err != nil { | ||||
| 					return bytesCount, err | ||||
| 				} | ||||
| 				bytesCount, err = node.writeToOrdered(w, indent+indentString, combinedKey, bytesCount, arraysOneElementPerLine, ord, indentString, parentCommented || t.commented || tv.commented) | ||||
| 				if err != nil { | ||||
| 					return bytesCount, err | ||||
| 				} | ||||
| 			case []*Tree: | ||||
| 				for _, subTree := range node { | ||||
| 					var commented string | ||||
| 					if parentCommented || t.commented || subTree.commented { | ||||
| 						commented = "# " | ||||
| 					} | ||||
| 					writtenBytesCount, err := writeStrings(w, "\n", indent, commented, "[[", combinedKey, "]]\n") | ||||
| 					bytesCount += int64(writtenBytesCount) | ||||
| 					if err != nil { | ||||
| 						return bytesCount, err | ||||
| 					} | ||||
|  | ||||
| 					bytesCount, err = subTree.writeToOrdered(w, indent+indentString, combinedKey, bytesCount, arraysOneElementPerLine, ord, indentString, parentCommented || t.commented || subTree.commented) | ||||
| 					if err != nil { | ||||
| 						return bytesCount, err | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		default: // Simple | ||||
| 			k := node.key | ||||
| 			v, ok := t.values[k].(*tomlValue) | ||||
| 			if !ok { | ||||
| 				return bytesCount, fmt.Errorf("invalid value type at %s: %T", k, t.values[k]) | ||||
| 			} | ||||
|  | ||||
| 			var commented string | ||||
| 			if parentCommented || t.commented || v.commented { | ||||
| 				commented = "# " | ||||
| 			} | ||||
| 			repr, err := tomlValueStringRepresentation(v, commented, indent, ord, arraysOneElementPerLine) | ||||
| 			if err != nil { | ||||
| 				return bytesCount, err | ||||
| 			} | ||||
|  | ||||
| 			if v.comment != "" { | ||||
| 				comment := strings.Replace(v.comment, "\n", "\n"+indent+"#", -1) | ||||
| 				start := "# " | ||||
| 				if strings.HasPrefix(comment, "#") { | ||||
| 					start = "" | ||||
| 				} | ||||
| 				writtenBytesCountComment, errc := writeStrings(w, "\n", indent, start, comment, "\n") | ||||
| 				bytesCount += int64(writtenBytesCountComment) | ||||
| 				if errc != nil { | ||||
| 					return bytesCount, errc | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			quotedKey := quoteKeyIfNeeded(k) | ||||
| 			writtenBytesCount, err := writeStrings(w, indent, commented, quotedKey, " = ", repr, "\n") | ||||
| 			bytesCount += int64(writtenBytesCount) | ||||
| 			if err != nil { | ||||
| 				return bytesCount, err | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return bytesCount, nil | ||||
| } | ||||
|  | ||||
| // quote a key if it does not fit the bare key format (A-Za-z0-9_-) | ||||
| // quoted keys use the same rules as strings | ||||
| func quoteKeyIfNeeded(k string) string { | ||||
| 	// when encoding a map with the 'quoteMapKeys' option enabled, the tree will contain | ||||
| 	// keys that have already been quoted. | ||||
| 	// not an ideal situation, but good enough of a stop gap. | ||||
| 	if len(k) >= 2 && k[0] == '"' && k[len(k)-1] == '"' { | ||||
| 		return k | ||||
| 	} | ||||
| 	isBare := true | ||||
| 	for _, r := range k { | ||||
| 		if !isValidBareChar(r) { | ||||
| 			isBare = false | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	if isBare { | ||||
| 		return k | ||||
| 	} | ||||
| 	return quoteKey(k) | ||||
| } | ||||
|  | ||||
| func quoteKey(k string) string { | ||||
| 	return "\"" + encodeTomlString(k) + "\"" | ||||
| } | ||||
|  | ||||
| func writeStrings(w io.Writer, s ...string) (int, error) { | ||||
| 	var n int | ||||
| 	for i := range s { | ||||
| 		b, err := io.WriteString(w, s[i]) | ||||
| 		n += b | ||||
| 		if err != nil { | ||||
| 			return n, err | ||||
| 		} | ||||
| 	} | ||||
| 	return n, nil | ||||
| } | ||||
|  | ||||
| // WriteTo encode the Tree as Toml and writes it to the writer w. | ||||
| // Returns the number of bytes written in case of success, or an error if anything happened. | ||||
| func (t *Tree) WriteTo(w io.Writer) (int64, error) { | ||||
| 	return t.writeTo(w, "", "", 0, false) | ||||
| } | ||||
|  | ||||
| // ToTomlString generates a human-readable representation of the current tree. | ||||
| // Output spans multiple lines, and is suitable for ingest by a TOML parser. | ||||
| // If the conversion cannot be performed, ToString returns a non-nil error. | ||||
| func (t *Tree) ToTomlString() (string, error) { | ||||
| 	b, err := t.Marshal() | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	return string(b), nil | ||||
| } | ||||
|  | ||||
| // String generates a human-readable representation of the current tree. | ||||
| // Alias of ToString. Present to implement the fmt.Stringer interface. | ||||
| func (t *Tree) String() string { | ||||
| 	result, _ := t.ToTomlString() | ||||
| 	return result | ||||
| } | ||||
|  | ||||
| // ToMap recursively generates a representation of the tree using Go built-in structures. | ||||
| // The following types are used: | ||||
| // | ||||
| //	* bool | ||||
| //	* float64 | ||||
| //	* int64 | ||||
| //	* string | ||||
| //	* uint64 | ||||
| //	* time.Time | ||||
| //	* map[string]interface{} (where interface{} is any of this list) | ||||
| //	* []interface{} (where interface{} is any of this list) | ||||
| func (t *Tree) ToMap() map[string]interface{} { | ||||
| 	result := map[string]interface{}{} | ||||
|  | ||||
| 	for k, v := range t.values { | ||||
| 		switch node := v.(type) { | ||||
| 		case []*Tree: | ||||
| 			var array []interface{} | ||||
| 			for _, item := range node { | ||||
| 				array = append(array, item.ToMap()) | ||||
| 			} | ||||
| 			result[k] = array | ||||
| 		case *Tree: | ||||
| 			result[k] = node.ToMap() | ||||
| 		case *tomlValue: | ||||
| 			result[k] = node.value | ||||
| 		} | ||||
| 	} | ||||
| 	return result | ||||
| } | ||||
| @@ -0,0 +1,3 @@ | ||||
| cliopts="--log-level trace" | ||||
| stderr="/var/log/containerd.out.log" | ||||
| stdout="stdout" | ||||
| @@ -2,7 +2,7 @@ kernel: | ||||
|   image: linuxkit/kernel:5.4.39 | ||||
|   cmdline: "console=ttyS0 console=ttyAMA0" | ||||
| init: | ||||
|   - linuxkit/init:4f6508f4f35b134dda3807bb5d75c117c193a86a | ||||
|   - linuxkit/init:946ebf1d74bc96da8d4b90c75ae0dd8a8e962a6b | ||||
|   - linuxkit/runc:v0.8 | ||||
|   - linuxkit/containerd:a4aa19c608556f7d786852557c36136255220c1f | ||||
|   - linuxkit/ca-certificates:v0.8 | ||||
| @@ -19,8 +19,8 @@ services: | ||||
| files: | ||||
|   - path: check.sh | ||||
|     source: ./check.sh | ||||
|   - path: /etc/containerd/cli-opts | ||||
|     contents: "--log-level trace" | ||||
|   - path: /etc/containerd/runtime-config.toml | ||||
|     source: ./runtime-config.toml | ||||
| trust: | ||||
|   org: | ||||
|     - linuxkit | ||||
|   | ||||
		Reference in New Issue
	
	Block a user