<commit_before>// Copyright 2015 The appc Authors
//
// 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 main

import (
	"archive/tar"
	"compress/gzip"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"strings"

	"github.com/appc/spec/aci"
	"github.com/appc/spec/schema"
	"github.com/appc/spec/schema/types"
)

var (
	inputFile  string
	outputFile string

	patchNocompress   bool
	patchOverwrite    bool
	patchReplace      bool
	patchManifestFile string
	patchName         string
	patchExec         string
	patchUser         string
	patchGroup        string
	patchCaps         string
	patchMounts       string
	patchPorts        string
	patchIsolators    string

	catPrettyPrint bool

	cmdPatchManifest = &Command{
		Name:        "patch-manifest",
		Description: `Copy an ACI and patch its manifest.`,
		Summary:     "Copy an ACI and patch its manifest (experimental)",
		Usage: `
		  [--manifest=MANIFEST_FILE]
		  [--name=example.com/app]
		  [--exec="/app --debug"]
		  [--user=uid] [--group=gid]
		  [--capability=CAP_SYS_ADMIN,CAP_NET_ADMIN]
		  [--mounts=work,path=/opt,readOnly=true[:work2,...]]
		  [--ports=query,protocol=tcp,port=8080[:query2,...]]
		  [--isolators=resource/cpu,request=50m,limit=100m[:resource/memory,...]]
		  [--replace]
		  INPUT_ACI_FILE
		  [OUTPUT_ACI_FILE]`,
		Run: runPatchManifest,
	}
	cmdCatManifest = &Command{
		Name:        "cat-manifest",
		Description: `Print the manifest from an ACI.`,
		Summary:     "Print the manifest from an ACI",
		Usage:       ` [--pretty-print] ACI_FILE`,
		Run:         runCatManifest,
	}
)

func init() {
	cmdPatchManifest.Flags.BoolVar(&patchOverwrite, "overwrite", false, "Overwrite target file if it already exists")
	cmdPatchManifest.Flags.BoolVar(&patchNocompress, "no-compression", false, "Do not gzip-compress the produced ACI")
	cmdPatchManifest.Flags.BoolVar(&patchReplace, "replace", false, "Replace the input file")

	cmdPatchManifest.Flags.StringVar(&patchManifestFile, "manifest", "", "Replace image manifest with this file. Incompatible with other replace options.")
	cmdPatchManifest.Flags.StringVar(&patchName, "name", "", "Replace name")
	cmdPatchManifest.Flags.StringVar(&patchExec, "exec", "", "Replace the command line to launch the executable")
	cmdPatchManifest.Flags.StringVar(&patchUser, "user", "", "Replace user")
	cmdPatchManifest.Flags.StringVar(&patchGroup, "group", "", "Replace group")
	cmdPatchManifest.Flags.StringVar(&patchCaps, "capability", "", "Replace capability")
	cmdPatchManifest.Flags.StringVar(&patchMounts, "mounts", "", "Replace mount points")
	cmdPatchManifest.Flags.StringVar(&patchPorts, "ports", "", "Replace ports")
	cmdPatchManifest.Flags.StringVar(&patchIsolators, "isolators", "", "Replace isolators")

	cmdCatManifest.Flags.BoolVar(&catPrettyPrint, "pretty-print", false, "Print with better style")
}

func getIsolatorStr(name, value string) string {
	return fmt.Sprintf(`
                {
                    "name": "%s",
                    "value": { %s }
                }`, name, value)
}

func isolatorStrFromString(is string) (types.ACIdentifier, string, error) {
	is = "name=" + is
	v, err := url.ParseQuery(strings.Replace(is, ",", "&", -1))
	if err != nil {
		return "", "", err
	}

	var name string
	var values []string
	var acn *types.ACIdentifier

	for key, val := range v {
		if len(val) > 1 {
			return "", "", fmt.Errorf("label %s with multiple values %q", key, val)
		}

		switch key {
		case "name":
			acn, err = types.NewACIdentifier(val[0])
			if err != nil {
				return "", "", err
			}
			name = val[0]
		default:
			// (TODO)yifan: Not support the default boolean yet.
			values = append(values, fmt.Sprintf(`"%s": "%s"`, key, val[0]))
		}
	}
	return *acn, getIsolatorStr(name, strings.Join(values, ", ")), nil
}

func patchManifest(im *schema.ImageManifest) error {

	if patchName != "" {
		name, err := types.NewACIdentifier(patchName)
		if err != nil {
			return err
		}
		im.Name = *name
	}

	var app *types.App = im.App
	if patchExec != "" {
		if app == nil {
			// if the original manifest was missing an app and
			// patchExec is set let's assume the user is trying to
			// inject one...
			app = &types.App{}
		}
		app.Exec = strings.Split(patchExec, " ")
	}

	if patchUser != "" || patchGroup != "" || patchCaps != "" || patchMounts != "" || patchPorts != "" || patchIsolators != "" {
		// ...but if we still don't have an app and the user is trying
		// to patch one of its other parameters, it's an error
		if app == nil {
			return fmt.Errorf("no app in the supplied manifest and no exec command provided")
		}
	}

	if patchUser != "" {
		app.User = patchUser
	}

	if patchGroup != "" {
		app.Group = patchGroup
	}

	if patchCaps != "" {
		isolator := app.Isolators.GetByName(types.LinuxCapabilitiesRetainSetName)
		if isolator != nil {
			return fmt.Errorf("isolator already exists")
		}

		// Instantiate a Isolator with the content specified by the --capability
		// parameter.

		// TODO: Instead of creating a JSON and then unmarshalling it, the isolator
		// should be instantiated directory. But it requires a constructor, see:
		// https://github.com/appc/spec/issues/268
		capsList := strings.Split(patchCaps, ",")
		caps := fmt.Sprintf(`"set": ["%s"]`, strings.Join(capsList, `", "`))
		isolatorStr := getIsolatorStr(types.LinuxCapabilitiesRetainSetName, caps)
		isolator = &types.Isolator{}
		err := isolator.UnmarshalJSON([]byte(isolatorStr))
		if err != nil {
			return fmt.Errorf("cannot parse capability %q: %v", patchCaps, err)
		}
		app.Isolators = append(app.Isolators, *isolator)
	}

	if patchMounts != "" {
		mounts := strings.Split(patchMounts, ":")
		for _, m := range mounts {
			mountPoint, err := types.MountPointFromString(m)
			if err != nil {
				return fmt.Errorf("cannot parse mount point %q: %v", m, err)
			}
			app.MountPoints = append(app.MountPoints, *mountPoint)
		}
	}

	if patchPorts != "" {
		ports := strings.Split(patchPorts, ":")
		for _, p := range ports {
			port, err := types.PortFromString(p)
			if err != nil {
				return fmt.Errorf("cannot parse port %q: %v", p, err)
			}
			app.Ports = append(app.Ports, *port)
		}
	}

	if patchIsolators != "" {
		isolators := strings.Split(patchIsolators, ":")
		for _, is := range isolators {
			name, isolatorStr, err := isolatorStrFromString(is)
			if err != nil {
				return fmt.Errorf("cannot parse isolator %q: %v", is, err)
			}

			if _, ok := types.ResourceIsolatorNames[name]; !ok {
				return fmt.Errorf("isolator %s is not supported for patching", name)
			}

			isolator := &types.Isolator{}
			if err := isolator.UnmarshalJSON([]byte(isolatorStr)); err != nil {
				return fmt.Errorf("cannot unmarshal isolator %v: %v", isolatorStr, err)
			}
			app.Isolators = append(app.Isolators, *isolator)
		}
	}
	return nil
}

// extractManifest iterates over the tar reader and locate the manifest. Once
// located, the manifest can be printed, replaced or patched.
func extractManifest(tr *tar.Reader, tw *tar.Writer, printManifest bool, newManifest []byte) error {
Tar:
	for {
		hdr, err := tr.Next()
		switch err {
		case io.EOF:
			break Tar
		case nil:
			if filepath.Clean(hdr.Name) == aci.ManifestFile {
				var new_bytes []byte

				bytes, err := ioutil.ReadAll(tr)
				if err != nil {
					return err
				}

				if printManifest && !catPrettyPrint {
					fmt.Println(string(bytes))
				}

				im := &schema.ImageManifest{}
				err = im.UnmarshalJSON(bytes)
				if err != nil {
					return err
				}

				if printManifest && catPrettyPrint {
					output, err := json.MarshalIndent(im, "", "    ")
					if err != nil {
						return err
					}
					fmt.Println(string(output))
				}

				if tw == nil {
					return nil
				}

				if len(newManifest) == 0 {
					err = patchManifest(im)
					if err != nil {
						return err
					}

					new_bytes, err = im.MarshalJSON()
					if err != nil {
						return err
					}
				} else {
					new_bytes = newManifest
				}

				hdr.Size = int64(len(new_bytes))
				err = tw.WriteHeader(hdr)
				if err != nil {
					return err
				}

				_, err = tw.Write(new_bytes)
				if err != nil {
					return err
				}
			} else if tw != nil {
				err := tw.WriteHeader(hdr)
				if err != nil {
					return err
				}
				_, err = io.Copy(tw, tr)
				if err != nil {
					return err
				}
			}
		default:
			return fmt.Errorf("error reading tarball: %v", err)
		}
	}

	return nil
}

func runPatchManifest(args []string) (exit int) {
	var fh *os.File
	var err error

	if patchReplace && patchOverwrite {
		stderr("patch-manifest: Cannot use both --replace and --overwrite")
		return 1
	}
	if !patchReplace && len(args) != 2 {
		stderr("patch-manifest: Must provide input and output files (or use --replace)")
		return 1
	}
	if patchReplace && len(args) != 1 {
		stderr("patch-manifest: Must provide one file")
		return 1
	}
	if patchManifestFile != "" && (patchName != "" || patchExec != "" || patchUser != "" || patchGroup != "" || patchCaps != "" || patchMounts != "") {
		stderr("patch-manifest: --manifest is incompatible with other manifest editing options")
		return 1
	}

	inputFile = args[0]

	// Prepare output writer

	if patchReplace {
		fh, err = ioutil.TempFile(path.Dir(inputFile), ".actool-tmp."+path.Base(inputFile)+"-")
		if err != nil {
			stderr("patch-manifest: Cannot create temporary file: %v", err)
			return 1
		}
	} else {
		outputFile = args[1]

		ext := filepath.Ext(outputFile)
		if ext != schema.ACIExtension {
			stderr("patch-manifest: Extension must be %s (given %s)", schema.ACIExtension, ext)
			return 1
		}

		mode := os.O_CREATE | os.O_WRONLY
		if patchOverwrite {
			mode |= os.O_TRUNC
		} else {
			mode |= os.O_EXCL
		}

		fh, err = os.OpenFile(outputFile, mode, 0644)
		if err != nil {
			if os.IsExist(err) {
				stderr("patch-manifest: Output file exists (try --overwrite)")
			} else {
				stderr("patch-manifest: Unable to open output %s: %v", outputFile, err)
			}
			return 1
		}
	}

	var gw *gzip.Writer
	var w io.WriteCloser = fh
	if !patchNocompress {
		gw = gzip.NewWriter(fh)
		w = gw
	}
	tw := tar.NewWriter(w)

	defer func() {
		tw.Close()
		if !patchNocompress {
			gw.Close()
		}
		fh.Close()
		if exit != 0 && !patchOverwrite {
			os.Remove(fh.Name())
		}
	}()

	// Prepare input reader

	input, err := os.Open(inputFile)
	if err != nil {
		stderr("patch-manifest: Cannot open %s: %v", inputFile, err)
		return 1
	}
	defer input.Close()

	tr, err := aci.NewCompressedTarReader(input)
	if err != nil {
		stderr("patch-manifest: Cannot extract %s: %v", inputFile, err)
		return 1
	}
	defer tr.Close()

	var newManifest []byte

	if patchManifestFile != "" {
		mr, err := os.Open(patchManifestFile)
		if err != nil {
			stderr("patch-manifest: Cannot open %s: %v", patchManifestFile, err)
			return 1
		}
		defer input.Close()

		newManifest, err = ioutil.ReadAll(mr)
		if err != nil {
			stderr("patch-manifest: Cannot read %s: %v", patchManifestFile, err)
			return 1
		}
	}

	err = extractManifest(tr.Reader, tw, false, newManifest)
	if err != nil {
		stderr("patch-manifest: Unable to read %s: %v", inputFile, err)
		return 1
	}

	if patchReplace {
		err = os.Rename(fh.Name(), inputFile)
		if err != nil {
			stderr("patch-manifest: Cannot rename %q to %q: %v", fh.Name, inputFile, err)
			return 1
		}
	}

	return
}

func runCatManifest(args []string) (exit int) {
	if len(args) != 1 {
		stderr("cat-manifest: Must provide one file")
		return 1
	}

	inputFile = args[0]

	input, err := os.Open(inputFile)
	if err != nil {
		stderr("cat-manifest: Cannot open %s: %v", inputFile, err)
		return 1
	}
	defer input.Close()

	tr, err := aci.NewCompressedTarReader(input)
	if err != nil {
		stderr("cat-manifest: Cannot extract %s: %v", inputFile, err)
		return 1
	}
	defer tr.Close()

	err = extractManifest(tr.Reader, nil, true, nil)
	if err != nil {
		stderr("cat-manifest: Unable to read %s: %v", inputFile, err)
		return 1
	}

	return
}
<commit_msg>actool/manifest.go: Create app section when patching exec.<commit_after>// Copyright 2015 The appc Authors
//
// 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 main

import (
	"archive/tar"
	"compress/gzip"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"strings"

	"github.com/appc/spec/aci"
	"github.com/appc/spec/schema"
	"github.com/appc/spec/schema/types"
)

var (
	inputFile  string
	outputFile string

	patchNocompress   bool
	patchOverwrite    bool
	patchReplace      bool
	patchManifestFile string
	patchName         string
	patchExec         string
	patchUser         string
	patchGroup        string
	patchCaps         string
	patchMounts       string
	patchPorts        string
	patchIsolators    string

	catPrettyPrint bool

	cmdPatchManifest = &Command{
		Name:        "patch-manifest",
		Description: `Copy an ACI and patch its manifest.`,
		Summary:     "Copy an ACI and patch its manifest (experimental)",
		Usage: `
		  [--manifest=MANIFEST_FILE]
		  [--name=example.com/app]
		  [--exec="/app --debug"]
		  [--user=uid] [--group=gid]
		  [--capability=CAP_SYS_ADMIN,CAP_NET_ADMIN]
		  [--mounts=work,path=/opt,readOnly=true[:work2,...]]
		  [--ports=query,protocol=tcp,port=8080[:query2,...]]
		  [--isolators=resource/cpu,request=50m,limit=100m[:resource/memory,...]]
		  [--replace]
		  INPUT_ACI_FILE
		  [OUTPUT_ACI_FILE]`,
		Run: runPatchManifest,
	}
	cmdCatManifest = &Command{
		Name:        "cat-manifest",
		Description: `Print the manifest from an ACI.`,
		Summary:     "Print the manifest from an ACI",
		Usage:       ` [--pretty-print] ACI_FILE`,
		Run:         runCatManifest,
	}
)

func init() {
	cmdPatchManifest.Flags.BoolVar(&patchOverwrite, "overwrite", false, "Overwrite target file if it already exists")
	cmdPatchManifest.Flags.BoolVar(&patchNocompress, "no-compression", false, "Do not gzip-compress the produced ACI")
	cmdPatchManifest.Flags.BoolVar(&patchReplace, "replace", false, "Replace the input file")

	cmdPatchManifest.Flags.StringVar(&patchManifestFile, "manifest", "", "Replace image manifest with this file. Incompatible with other replace options.")
	cmdPatchManifest.Flags.StringVar(&patchName, "name", "", "Replace name")
	cmdPatchManifest.Flags.StringVar(&patchExec, "exec", "", "Replace the command line to launch the executable")
	cmdPatchManifest.Flags.StringVar(&patchUser, "user", "", "Replace user")
	cmdPatchManifest.Flags.StringVar(&patchGroup, "group", "", "Replace group")
	cmdPatchManifest.Flags.StringVar(&patchCaps, "capability", "", "Replace capability")
	cmdPatchManifest.Flags.StringVar(&patchMounts, "mounts", "", "Replace mount points")
	cmdPatchManifest.Flags.StringVar(&patchPorts, "ports", "", "Replace ports")
	cmdPatchManifest.Flags.StringVar(&patchIsolators, "isolators", "", "Replace isolators")

	cmdCatManifest.Flags.BoolVar(&catPrettyPrint, "pretty-print", false, "Print with better style")
}

func getIsolatorStr(name, value string) string {
	return fmt.Sprintf(`
                {
                    "name": "%s",
                    "value": { %s }
                }`, name, value)
}

func isolatorStrFromString(is string) (types.ACIdentifier, string, error) {
	is = "name=" + is
	v, err := url.ParseQuery(strings.Replace(is, ",", "&", -1))
	if err != nil {
		return "", "", err
	}

	var name string
	var values []string
	var acn *types.ACIdentifier

	for key, val := range v {
		if len(val) > 1 {
			return "", "", fmt.Errorf("label %s with multiple values %q", key, val)
		}

		switch key {
		case "name":
			acn, err = types.NewACIdentifier(val[0])
			if err != nil {
				return "", "", err
			}
			name = val[0]
		default:
			// (TODO)yifan: Not support the default boolean yet.
			values = append(values, fmt.Sprintf(`"%s": "%s"`, key, val[0]))
		}
	}
	return *acn, getIsolatorStr(name, strings.Join(values, ", ")), nil
}

func patchManifest(im *schema.ImageManifest) error {

	if patchName != "" {
		name, err := types.NewACIdentifier(patchName)
		if err != nil {
			return err
		}
		im.Name = *name
	}

	var app *types.App = im.App
	if patchExec != "" {
		if app == nil {
			// if the original manifest was missing an app and
			// patchExec is set let's assume the user is trying to
			// inject one...
			im.App = &types.App{}
			app = im.App
		}
		app.Exec = strings.Split(patchExec, " ")
	}

	if patchUser != "" || patchGroup != "" || patchCaps != "" || patchMounts != "" || patchPorts != "" || patchIsolators != "" {
		// ...but if we still don't have an app and the user is trying
		// to patch one of its other parameters, it's an error
		if app == nil {
			return fmt.Errorf("no app in the supplied manifest and no exec command provided")
		}
	}

	if patchUser != "" {
		app.User = patchUser
	}

	if patchGroup != "" {
		app.Group = patchGroup
	}

	if patchCaps != "" {
		isolator := app.Isolators.GetByName(types.LinuxCapabilitiesRetainSetName)
		if isolator != nil {
			return fmt.Errorf("isolator already exists")
		}

		// Instantiate a Isolator with the content specified by the --capability
		// parameter.

		// TODO: Instead of creating a JSON and then unmarshalling it, the isolator
		// should be instantiated directory. But it requires a constructor, see:
		// https://github.com/appc/spec/issues/268
		capsList := strings.Split(patchCaps, ",")
		caps := fmt.Sprintf(`"set": ["%s"]`, strings.Join(capsList, `", "`))
		isolatorStr := getIsolatorStr(types.LinuxCapabilitiesRetainSetName, caps)
		isolator = &types.Isolator{}
		err := isolator.UnmarshalJSON([]byte(isolatorStr))
		if err != nil {
			return fmt.Errorf("cannot parse capability %q: %v", patchCaps, err)
		}
		app.Isolators = append(app.Isolators, *isolator)
	}

	if patchMounts != "" {
		mounts := strings.Split(patchMounts, ":")
		for _, m := range mounts {
			mountPoint, err := types.MountPointFromString(m)
			if err != nil {
				return fmt.Errorf("cannot parse mount point %q: %v", m, err)
			}
			app.MountPoints = append(app.MountPoints, *mountPoint)
		}
	}

	if patchPorts != "" {
		ports := strings.Split(patchPorts, ":")
		for _, p := range ports {
			port, err := types.PortFromString(p)
			if err != nil {
				return fmt.Errorf("cannot parse port %q: %v", p, err)
			}
			app.Ports = append(app.Ports, *port)
		}
	}

	if patchIsolators != "" {
		isolators := strings.Split(patchIsolators, ":")
		for _, is := range isolators {
			name, isolatorStr, err := isolatorStrFromString(is)
			if err != nil {
				return fmt.Errorf("cannot parse isolator %q: %v", is, err)
			}

			if _, ok := types.ResourceIsolatorNames[name]; !ok {
				return fmt.Errorf("isolator %s is not supported for patching", name)
			}

			isolator := &types.Isolator{}
			if err := isolator.UnmarshalJSON([]byte(isolatorStr)); err != nil {
				return fmt.Errorf("cannot unmarshal isolator %v: %v", isolatorStr, err)
			}
			app.Isolators = append(app.Isolators, *isolator)
		}
	}
	return nil
}

// extractManifest iterates over the tar reader and locate the manifest. Once
// located, the manifest can be printed, replaced or patched.
func extractManifest(tr *tar.Reader, tw *tar.Writer, printManifest bool, newManifest []byte) error {
Tar:
	for {
		hdr, err := tr.Next()
		switch err {
		case io.EOF:
			break Tar
		case nil:
			if filepath.Clean(hdr.Name) == aci.ManifestFile {
				var new_bytes []byte

				bytes, err := ioutil.ReadAll(tr)
				if err != nil {
					return err
				}

				if printManifest && !catPrettyPrint {
					fmt.Println(string(bytes))
				}

				im := &schema.ImageManifest{}
				err = im.UnmarshalJSON(bytes)
				if err != nil {
					return err
				}

				if printManifest && catPrettyPrint {
					output, err := json.MarshalIndent(im, "", "    ")
					if err != nil {
						return err
					}
					fmt.Println(string(output))
				}

				if tw == nil {
					return nil
				}

				if len(newManifest) == 0 {
					err = patchManifest(im)
					if err != nil {
						return err
					}

					new_bytes, err = im.MarshalJSON()
					if err != nil {
						return err
					}
				} else {
					new_bytes = newManifest
				}

				hdr.Size = int64(len(new_bytes))
				err = tw.WriteHeader(hdr)
				if err != nil {
					return err
				}

				_, err = tw.Write(new_bytes)
				if err != nil {
					return err
				}
			} else if tw != nil {
				err := tw.WriteHeader(hdr)
				if err != nil {
					return err
				}
				_, err = io.Copy(tw, tr)
				if err != nil {
					return err
				}
			}
		default:
			return fmt.Errorf("error reading tarball: %v", err)
		}
	}

	return nil
}

func runPatchManifest(args []string) (exit int) {
	var fh *os.File
	var err error

	if patchReplace && patchOverwrite {
		stderr("patch-manifest: Cannot use both --replace and --overwrite")
		return 1
	}
	if !patchReplace && len(args) != 2 {
		stderr("patch-manifest: Must provide input and output files (or use --replace)")
		return 1
	}
	if patchReplace && len(args) != 1 {
		stderr("patch-manifest: Must provide one file")
		return 1
	}
	if patchManifestFile != "" && (patchName != "" || patchExec != "" || patchUser != "" || patchGroup != "" || patchCaps != "" || patchMounts != "") {
		stderr("patch-manifest: --manifest is incompatible with other manifest editing options")
		return 1
	}

	inputFile = args[0]

	// Prepare output writer

	if patchReplace {
		fh, err = ioutil.TempFile(path.Dir(inputFile), ".actool-tmp."+path.Base(inputFile)+"-")
		if err != nil {
			stderr("patch-manifest: Cannot create temporary file: %v", err)
			return 1
		}
	} else {
		outputFile = args[1]

		ext := filepath.Ext(outputFile)
		if ext != schema.ACIExtension {
			stderr("patch-manifest: Extension must be %s (given %s)", schema.ACIExtension, ext)
			return 1
		}

		mode := os.O_CREATE | os.O_WRONLY
		if patchOverwrite {
			mode |= os.O_TRUNC
		} else {
			mode |= os.O_EXCL
		}

		fh, err = os.OpenFile(outputFile, mode, 0644)
		if err != nil {
			if os.IsExist(err) {
				stderr("patch-manifest: Output file exists (try --overwrite)")
			} else {
				stderr("patch-manifest: Unable to open output %s: %v", outputFile, err)
			}
			return 1
		}
	}

	var gw *gzip.Writer
	var w io.WriteCloser = fh
	if !patchNocompress {
		gw = gzip.NewWriter(fh)
		w = gw
	}
	tw := tar.NewWriter(w)

	defer func() {
		tw.Close()
		if !patchNocompress {
			gw.Close()
		}
		fh.Close()
		if exit != 0 && !patchOverwrite {
			os.Remove(fh.Name())
		}
	}()

	// Prepare input reader

	input, err := os.Open(inputFile)
	if err != nil {
		stderr("patch-manifest: Cannot open %s: %v", inputFile, err)
		return 1
	}
	defer input.Close()

	tr, err := aci.NewCompressedTarReader(input)
	if err != nil {
		stderr("patch-manifest: Cannot extract %s: %v", inputFile, err)
		return 1
	}
	defer tr.Close()

	var newManifest []byte

	if patchManifestFile != "" {
		mr, err := os.Open(patchManifestFile)
		if err != nil {
			stderr("patch-manifest: Cannot open %s: %v", patchManifestFile, err)
			return 1
		}
		defer input.Close()

		newManifest, err = ioutil.ReadAll(mr)
		if err != nil {
			stderr("patch-manifest: Cannot read %s: %v", patchManifestFile, err)
			return 1
		}
	}

	err = extractManifest(tr.Reader, tw, false, newManifest)
	if err != nil {
		stderr("patch-manifest: Unable to read %s: %v", inputFile, err)
		return 1
	}

	if patchReplace {
		err = os.Rename(fh.Name(), inputFile)
		if err != nil {
			stderr("patch-manifest: Cannot rename %q to %q: %v", fh.Name, inputFile, err)
			return 1
		}
	}

	return
}

func runCatManifest(args []string) (exit int) {
	if len(args) != 1 {
		stderr("cat-manifest: Must provide one file")
		return 1
	}

	inputFile = args[0]

	input, err := os.Open(inputFile)
	if err != nil {
		stderr("cat-manifest: Cannot open %s: %v", inputFile, err)
		return 1
	}
	defer input.Close()

	tr, err := aci.NewCompressedTarReader(input)
	if err != nil {
		stderr("cat-manifest: Cannot extract %s: %v", inputFile, err)
		return 1
	}
	defer tr.Close()

	err = extractManifest(tr.Reader, nil, true, nil)
	if err != nil {
		stderr("cat-manifest: Unable to read %s: %v", inputFile, err)
		return 1
	}

	return
}
<|endoftext|>


<commit_before>package adapters

type MeasurementSchema struct {
    Humidity float32
    Pressure float32
    Precipitation float32
    Temp float32
    Wind float32
}

type MeasurementArray []MeasurementSchema

func AdaptWeather(sourceName string, wtypeName string, data string) MeasurementArray {
    var fnTable = make(map[string](map[string]func(string)MeasurementArray))

    fnTable["OpenWeatherMap"] = make(map[string]func(string)MeasurementArray)
    fnTable["OpenWeatherMap"]["current"] = OwmAdaptCurrentWeather
    fnTable["OpenWeatherMap"]["forecast"] = func(data string)MeasurementArray {return MeasurementArray{MeasurementSchema{}}}

    fnTable["Weather Underground"] = make(map[string]func(string)MeasurementArray)
    fnTable["Weather Underground"]["current"] = WundergroundAdaptCurrentWeather
    fnTable["Weather Underground"]["forecast"] = func(data string)MeasurementArray {return MeasurementArray{MeasurementSchema{}}}

    fnTable["MyWeather2"] = make(map[string]func(string)MeasurementArray)
    fnTable["MyWeather2"]["current"] = Myweather2AdaptCurrentWeather
    fnTable["MyWeather2"]["forecast"] = func(data string)MeasurementArray {return MeasurementArray{MeasurementSchema{}}}

    return fnTable[sourceName][wtypeName](data)
}
<commit_msg>+AdaptStub<commit_after>package adapters

type MeasurementSchema struct {
    Humidity float32
    Pressure float32
    Precipitation float32
    Temp float32
    Wind float32
}

type MeasurementArray []MeasurementSchema

func AdaptStub (s string) MeasurementArray {return MeasurementArray{MeasurementSchema{}}}

func AdaptWeather(sourceName string, wtypeName string, data string) MeasurementArray {
    var adaptFunc func(string)MeasurementArray
    var fnTable = make(map[string](map[string]func(string)MeasurementArray))

    fnTable["OpenWeatherMap"] = make(map[string]func(string)MeasurementArray)
    fnTable["OpenWeatherMap"]["current"] = OwmAdaptCurrentWeather

    fnTable["Weather Underground"] = make(map[string]func(string)MeasurementArray)
    fnTable["Weather Underground"]["current"] = WundergroundAdaptCurrentWeather

    fnTable["MyWeather2"] = make(map[string]func(string)MeasurementArray)
    fnTable["MyWeather2"]["current"] = Myweather2AdaptCurrentWeather

    adaptFunc = AdaptStub

    _, p_ok := fnTable[sourceName]
    if p_ok == true {
        storedFunc, f_ok := fnTable[sourceName][wtypeName]
        if f_ok == true {
            adaptFunc = storedFunc
        }
    }

    return adaptFunc(data)
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/hmac"
	"crypto/sha1"
	"crypto/sha256"
	"flag"
	"fmt"
	"html"
	"log"
	"net/http"
	"regexp"
)

var urlRe *regexp.Regexp
var secretKey = flag.String("key", "", "The secret key.")
var addr = flag.String("addr", ":9090", "Where to bind.")

func init() {
	urlRe = regexp.MustCompile(`.*?v1/([^/]+)/(.*)`)
}

func sha1Str(msg string) string {
	h := sha1.New()
	h.Write([]byte(msg))
	return fmt.Sprintf("%x", h.Sum(nil))
}

func sha256Mac(msg, key string) string {
	mac := hmac.New(sha256.New, []byte(key))
	mac.Write([]byte(msg))
	return fmt.Sprintf("%x", mac.Sum(nil))
}

func errorResp(w http.ResponseWriter) {
	w.WriteHeader(http.StatusBadRequest)
	w.Write([]byte("Invalid address."))
}

func validateSig(url, sig string) bool {
	if sha1Str(*secretKey+url) == sig || sha256Mac(url, *secretKey) == sig {
		return true
	}
	return false
}

func bounce(w http.ResponseWriter, sig, url string) {
	if !validateSig(url, sig) {
		errorResp(w)
		return
	}

	safeUrl := html.EscapeString(url)
	if safeUrl[:10] == "javascript" {
		errorResp(w)
		return
	}
	err := redirectTemplate.Execute(
		w,
		&struct {
			Url string
		}{safeUrl},
	)
	if err != nil {
		log.Println(err)
	}
}

func home(w http.ResponseWriter) {
	homeHtml := `<!DOCTYPE html>

    <meta charset="utf-8">
    <title>outgoing urls</title>

outgoing urls.

`
	w.Write([]byte(homeHtml))

}

func readReq(w http.ResponseWriter, req *http.Request) {
	if req.URL.RequestURI() == "/" {
		home(w)
		return
	}

	m := urlRe.FindStringSubmatch(req.URL.RequestURI())
	if len(m) < 3 {
		errorResp(w)
		return
	}
	bounce(w, m[1], m[2])
}

func main() {
	flag.Parse()
	if *secretKey == "" {
		log.Fatal("-key must be set.")
	}
	http.ListenAndServe(*addr, http.HandlerFunc(readReq))
}
<commit_msg>Add a scheme to the url, if it doesn't exist.<commit_after>package main

import (
	"crypto/hmac"
	"crypto/sha1"
	"crypto/sha256"
	"flag"
	"fmt"
	"html"
	"log"
	"net/http"
	"regexp"
	"strings"
)

var urlRe *regexp.Regexp
var secretKey = flag.String("key", "", "The secret key.")
var addr = flag.String("addr", ":9090", "Where to bind.")

func init() {
	urlRe = regexp.MustCompile(`.*?v1/([^/]+)/(.*)`)
}

func sha1Str(msg string) string {
	h := sha1.New()
	h.Write([]byte(msg))
	return fmt.Sprintf("%x", h.Sum(nil))
}

func sha256Mac(msg, key string) string {
	mac := hmac.New(sha256.New, []byte(key))
	mac.Write([]byte(msg))
	return fmt.Sprintf("%x", mac.Sum(nil))
}

func errorResp(w http.ResponseWriter) {
	w.WriteHeader(http.StatusBadRequest)
	w.Write([]byte("Invalid address."))
}

func validateSig(url, sig string) bool {
	if sha1Str(*secretKey+url) == sig || sha256Mac(url, *secretKey) == sig {
		return true
	}
	return false
}

func bounce(w http.ResponseWriter, sig, url string) {
	if !validateSig(url, sig) {
		errorResp(w)
		return
	}

	safeUrl := html.EscapeString(url)
	if safeUrl[:10] == "javascript" {
		errorResp(w)
		return
	}
	if !strings.Contains(safeUrl, "://") {
		safeUrl = "http://" + safeUrl
	}

	err := redirectTemplate.Execute(
		w,
		&struct {
			Url string
		}{safeUrl},
	)
	if err != nil {
		log.Println(err)
	}
}

func home(w http.ResponseWriter) {
	homeHtml := `<!DOCTYPE html>

    <meta charset="utf-8">
    <title>outgoing urls</title>

outgoing urls.

`
	w.Write([]byte(homeHtml))

}

func readReq(w http.ResponseWriter, req *http.Request) {
	if req.URL.RequestURI() == "/" {
		home(w)
		return
	}

	m := urlRe.FindStringSubmatch(req.URL.RequestURI())
	if len(m) < 3 {
		errorResp(w)
		return
	}
	bounce(w, m[1], m[2])
}

func main() {
	flag.Parse()
	if *secretKey == "" {
		log.Fatal("-key must be set.")
	}
	http.ListenAndServe(*addr, http.HandlerFunc(readReq))
}
<|endoftext|>


<commit_before>// Daemonizable self-upgrading binaries in Go (golang).
package overseer

import (
	"errors"
	"fmt"
	"log"
	"os"
	"runtime"
	"time"

	"github.com/jpillora/overseer/fetcher"
)

const (
	envSlaveID  = "OVERSEER_SLAVE_ID"
	envIsSlave  = "OVERSEER_IS_SLAVE"
	envNumFDs   = "OVERSEER_NUM_FDS"
	envBinID    = "OVERSEER_BIN_ID"
	envBinPath  = "OVERSEER_BIN_PATH"
	envBinCheck = "OVERSEER_BIN_CHECK"
)

type Config struct {
	//Required will prevent overseer from fallback to running
	//running the program in the main process on failure.
	Required bool
	//Program's main function
	Program func(state State)
	//Program's zero-downtime socket listening address (set this or Addresses)
	Address string
	//Program's zero-downtime socket listening addresses (set this or Address)
	Addresses []string
	//RestartSignal will manually trigger a graceful restart. Defaults to SIGUSR2.
	RestartSignal os.Signal
	//TerminateTimeout controls how long overseer should
	//wait for the program to terminate itself. After this
	//timeout, overseer will issue a SIGKILL.
	TerminateTimeout time.Duration
	//MinFetchInterval defines the smallest duration between Fetch()s.
	//This helps to prevent unwieldy fetch.Interfaces from hogging
	//too many resources. Defaults to 1 second.
	MinFetchInterval time.Duration
	//PreUpgrade runs after a binary has been retreived, user defined checks
	//can be run here and returning an error will cancel the upgrade.
	PreUpgrade func(tempBinaryPath string) error
	//Debug enables all [overseer] logs.
	Debug bool
	//NoWarn disables warning [overseer] logs.
	NoWarn bool
	//NoRestart disables all restarts, this option essentially converts
	//the RestartSignal into a "ShutdownSignal".
	NoRestart bool
	//NoRestartAfterFetch disables automatic restarts after each upgrade.
	//Though manual restarts using the RestartSignal can still be performed.
	NoRestartAfterFetch bool
	//Fetcher will be used to fetch binaries.
	Fetcher fetcher.Interface
}

func validate(c *Config) error {
	//validate
	if c.Program == nil {
		return errors.New("overseer.Config.Program required")
	}
	if c.Address != "" {
		if len(c.Addresses) > 0 {
			return errors.New("overseer.Config.Address and Addresses cant both be set")
		}
		c.Addresses = []string{c.Address}
	} else if len(c.Addresses) > 0 {
		c.Address = c.Addresses[0]
	}
	if c.RestartSignal == nil {
		c.RestartSignal = SIGUSR2
	}
	if c.TerminateTimeout <= 0 {
		c.TerminateTimeout = 30 * time.Second
	}
	if c.MinFetchInterval <= 0 {
		c.MinFetchInterval = 1 * time.Second
	}
	return nil
}

//RunErr allows manual handling of any
//overseer errors.
func RunErr(c Config) error {
	return runErr(&c)
}

//Run executes overseer, if an error is
//encounted, overseer fallsback to running
//the program directly (unless Required is set).
func Run(c Config) {
	err := runErr(&c)
	if err != nil {
		if c.Required {
			log.Fatalf("[overseer] %s", err)
		} else if c.Debug || !c.NoWarn {
			log.Printf("[overseer] disabled. run failed: %s", err)
		}
		c.Program(DisabledState)
		return
	}
	os.Exit(0)
}

var currentProcess interface {
	triggerRestart()
	run() error
}

func runErr(c *Config) error {
	if err := validate(c); err != nil {
		return err
	}
	//sanity check
	if token := os.Getenv(envBinCheck); token != "" {
		fmt.Fprint(os.Stdout, token)
		return nil
	}
	//os not supported
	if !supported {
		return fmt.Errorf("os (%s) not supported", runtime.GOOS)
	}
	//run either in master or slave mode
	if os.Getenv(envIsSlave) == "1" {
		currentProcess = &slave{Config: c}
	} else {
		currentProcess = &master{Config: c}
	}
	return currentProcess.run()
}

//Restart programmatically triggers a graceful restart. If NoRestart
//is enabled, then this will essentially be a graceful shutdown.
func Restart() {
	if currentProcess != nil {
		currentProcess.triggerRestart()
	}
}

//IsSupported returns whether overseer is supported on the current OS.
func IsSupported() bool {
	return supported
}
<commit_msg>Reinclude legacy bin check env var for backwards compatible upgrades<commit_after>// Daemonizable self-upgrading binaries in Go (golang).
package overseer

import (
	"errors"
	"fmt"
	"log"
	"os"
	"runtime"
	"time"

	"github.com/jpillora/overseer/fetcher"
)

const (
	envSlaveID        = "OVERSEER_SLAVE_ID"
	envIsSlave        = "OVERSEER_IS_SLAVE"
	envNumFDs         = "OVERSEER_NUM_FDS"
	envBinID          = "OVERSEER_BIN_ID"
	envBinPath        = "OVERSEER_BIN_PATH"
	envBinCheck       = "OVERSEER_BIN_CHECK"
	envBinCheckLegacy = "GO_UPGRADE_BIN_CHECK"
)

type Config struct {
	//Required will prevent overseer from fallback to running
	//running the program in the main process on failure.
	Required bool
	//Program's main function
	Program func(state State)
	//Program's zero-downtime socket listening address (set this or Addresses)
	Address string
	//Program's zero-downtime socket listening addresses (set this or Address)
	Addresses []string
	//RestartSignal will manually trigger a graceful restart. Defaults to SIGUSR2.
	RestartSignal os.Signal
	//TerminateTimeout controls how long overseer should
	//wait for the program to terminate itself. After this
	//timeout, overseer will issue a SIGKILL.
	TerminateTimeout time.Duration
	//MinFetchInterval defines the smallest duration between Fetch()s.
	//This helps to prevent unwieldy fetch.Interfaces from hogging
	//too many resources. Defaults to 1 second.
	MinFetchInterval time.Duration
	//PreUpgrade runs after a binary has been retreived, user defined checks
	//can be run here and returning an error will cancel the upgrade.
	PreUpgrade func(tempBinaryPath string) error
	//Debug enables all [overseer] logs.
	Debug bool
	//NoWarn disables warning [overseer] logs.
	NoWarn bool
	//NoRestart disables all restarts, this option essentially converts
	//the RestartSignal into a "ShutdownSignal".
	NoRestart bool
	//NoRestartAfterFetch disables automatic restarts after each upgrade.
	//Though manual restarts using the RestartSignal can still be performed.
	NoRestartAfterFetch bool
	//Fetcher will be used to fetch binaries.
	Fetcher fetcher.Interface
}

func validate(c *Config) error {
	//validate
	if c.Program == nil {
		return errors.New("overseer.Config.Program required")
	}
	if c.Address != "" {
		if len(c.Addresses) > 0 {
			return errors.New("overseer.Config.Address and Addresses cant both be set")
		}
		c.Addresses = []string{c.Address}
	} else if len(c.Addresses) > 0 {
		c.Address = c.Addresses[0]
	}
	if c.RestartSignal == nil {
		c.RestartSignal = SIGUSR2
	}
	if c.TerminateTimeout <= 0 {
		c.TerminateTimeout = 30 * time.Second
	}
	if c.MinFetchInterval <= 0 {
		c.MinFetchInterval = 1 * time.Second
	}
	return nil
}

//RunErr allows manual handling of any
//overseer errors.
func RunErr(c Config) error {
	return runErr(&c)
}

//Run executes overseer, if an error is
//encounted, overseer fallsback to running
//the program directly (unless Required is set).
func Run(c Config) {
	err := runErr(&c)
	if err != nil {
		if c.Required {
			log.Fatalf("[overseer] %s", err)
		} else if c.Debug || !c.NoWarn {
			log.Printf("[overseer] disabled. run failed: %s", err)
		}
		c.Program(DisabledState)
		return
	}
	os.Exit(0)
}

var currentProcess interface {
	triggerRestart()
	run() error
}

func runErr(c *Config) error {
	if err := validate(c); err != nil {
		return err
	}
	//sanity check
	if token := os.Getenv(envBinCheck); token != "" {
		fmt.Fprint(os.Stdout, token)
		return nil
	}
	//legacy sanity check using old env var
	if token := os.Getenv(envBinCheckLegacy); token != "" {
		fmt.Fprint(os.Stdout, token)
		return nil
	}
	//os not supported
	if !supported {
		return fmt.Errorf("os (%s) not supported", runtime.GOOS)
	}
	//run either in master or slave mode
	if os.Getenv(envIsSlave) == "1" {
		currentProcess = &slave{Config: c}
	} else {
		currentProcess = &master{Config: c}
	}
	return currentProcess.run()
}

//Restart programmatically triggers a graceful restart. If NoRestart
//is enabled, then this will essentially be a graceful shutdown.
func Restart() {
	if currentProcess != nil {
		currentProcess.triggerRestart()
	}
}

//IsSupported returns whether overseer is supported on the current OS.
func IsSupported() bool {
	return supported
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/metakeule/speccer/webspec/handler"
	utils "github.com/metakeule/utils/http"
	"net/http"
	"os"
	"path/filepath"
)

func my(rw http.ResponseWriter, req *http.Request) {
	rw.Write([]byte(req.URL.String()))
}

var server string

func init() {
	flag.StringVar(&server, "server", "localhost:8282", "host and port where the webserver should run, e.g. localhost:8282")
}

var _ = handler.New

func main() {
	_ = os.Args
	flag.Parse()
	/*
		if len(os.Args) != 2 {
			fmt.Println("no file given")
			os.Exit(1)
		}
		spec, err := ioutil.ReadFile(os.Args[1])
		if err != nil {
			panic(err.Error())
		}
	*/

	/*
		spec1file := "../examples/de/spec_de_DE.json"
		spec2file := "public/js/spec.json"

		var port = "8282"
		//_ = spec

		utils.Mount(
			"/spec1",
			handler.New(
				handler.FileLoader(spec1file),
				handler.FileSaver(spec1file),
			),
		)
		utils.Mount(
			"/spec2",
			handler.New(
				handler.FileLoader(spec2file),
				handler.FileSaver(spec2file),
			),
		)
	*/

	//specfile := "spec.json"
	specfile := "spec"
	if len(os.Args) > 1 {
		//specfile = os.Args[1]
		specfile = os.Args[1]

		//fmt.Println("no file given")
		//os.Exit(1)
	}

	_ = specfile

	pwd, err := os.Getwd()
	if err != nil {
		panic(err.Error())
	}

	//fmt.Printf("%#v\n", pwd)
	// fileserver := http.FileServer(http.Dir(pwd))

	mountpoint := "/spec"

	utils.Mount(
		mountpoint,
		handler.New(
			handler.FileLoader(specfile+".json"),
			handler.OmniSaver(specfile),
		),
	)

	fileDir := filepath.Join(pwd, "file")
	d, e := os.Stat(fileDir)
	if e != nil {
		fmt.Printf("warning: no file directory found\n")
	} else {
		if !d.IsDir() {
			fmt.Printf("warning: %s is no directory\n", fileDir)
		} else {
			fileserver := http.FileServer(http.Dir(fileDir))
			http.Handle("/spec/file/", http.StripPrefix("/spec/file", fileserver))
		}
	}

	/*
		http.Handle("/", handler.New(
			handler.FileLoader(specfile+".json"),
			handler.OmniSaver(specfile),
		))
	*/

	//fmt.Printf("listening on http://%s%s/admin\n", server, mountpoint)
	err = http.ListenAndServe(server, nil)
	//err = http.ListenAndServe(":"+port, handler.New(fmt.Sprintf("%s", spec)))
	if err != nil {
		panic(err.Error())
	}
}
<commit_msg>output the info where the server ist listening<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/metakeule/speccer/webspec/handler"
	utils "github.com/metakeule/utils/http"
	"net/http"
	"os"
	"path/filepath"
)

func my(rw http.ResponseWriter, req *http.Request) {
	rw.Write([]byte(req.URL.String()))
}

var server string

func init() {
	flag.StringVar(&server, "server", "localhost:8282", "host and port where the webserver should run, e.g. localhost:8282")
}

var _ = handler.New

func main() {
	_ = os.Args
	flag.Parse()
	/*
		if len(os.Args) != 2 {
			fmt.Println("no file given")
			os.Exit(1)
		}
		spec, err := ioutil.ReadFile(os.Args[1])
		if err != nil {
			panic(err.Error())
		}
	*/

	/*
		spec1file := "../examples/de/spec_de_DE.json"
		spec2file := "public/js/spec.json"

		var port = "8282"
		//_ = spec

		utils.Mount(
			"/spec1",
			handler.New(
				handler.FileLoader(spec1file),
				handler.FileSaver(spec1file),
			),
		)
		utils.Mount(
			"/spec2",
			handler.New(
				handler.FileLoader(spec2file),
				handler.FileSaver(spec2file),
			),
		)
	*/

	//specfile := "spec.json"
	specfile := "spec"
	if len(os.Args) > 1 {
		//specfile = os.Args[1]
		specfile = os.Args[1]

		//fmt.Println("no file given")
		//os.Exit(1)
	}

	_ = specfile

	pwd, err := os.Getwd()
	if err != nil {
		panic(err.Error())
	}

	//fmt.Printf("%#v\n", pwd)
	// fileserver := http.FileServer(http.Dir(pwd))

	mountpoint := "/spec"

	utils.Mount(
		mountpoint,
		handler.New(
			handler.FileLoader(specfile+".json"),
			handler.OmniSaver(specfile),
		),
	)

	fileDir := filepath.Join(pwd, "file")
	d, e := os.Stat(fileDir)
	if e != nil {
		fmt.Printf("warning: no file directory found\n")
	} else {
		if !d.IsDir() {
			fmt.Printf("warning: %s is no directory\n", fileDir)
		} else {
			fileserver := http.FileServer(http.Dir(fileDir))
			http.Handle("/spec/file/", http.StripPrefix("/spec/file", fileserver))
		}
	}

	fmt.Printf("listening on http://%s%s/admin\n", server, mountpoint)
	err = http.ListenAndServe(server, nil)
	//err = http.ListenAndServe(":"+port, handler.New(fmt.Sprintf("%s", spec)))
	if err != nil {
		panic(err.Error())
	}
}
<|endoftext|>


<commit_before>package TF2RconWrapper

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

var logs []string = []string{
	`"Sk1LL0<2><[U:1:198288660]><Unassigned>" joined team "Red"`,
	`"Sk1LL0<2><[U:1:198288660]><Red>" changed role to "scout"`,
	`"Sk1LL0<2><[U:1:198288660]><Red>" changed role to "soldier"`,
	`"Sk1LL0<2><[U:1:198288660]><Red>" say "hello gringos"`,
	`"Sk1LL0<2><[U:1:198288660]><Red>" say_team "ufo porno"`,
	`"Sk1LL0<2><[U:1:198288660]><Red>" changed role to "sniper"`,
	`"Sk1LL0<2><[U:1:198288660]><Red>" joined team "Blue"`,
	`"Sk1LL0<2><[U:1:198288660]><Blue>" changed role to "medic"`,
}

func TestParse(t *testing.T) {
	for i := range logs {
		m := Parse(logs[i])

		switch i {

		// 0 = changed team
		case 0:
			assert.Equal(t, m.Type, playerChangedTeam)

			assert.Equal(t, m.Data.Team, "Unassigned")
			assert.Equal(t, m.Data.NewTeam, "Red")

			assert.Equal(t, m.Data.Username, "Sk1LL0")
			assert.Equal(t, m.Data.UserId, "2")
			assert.Equal(t, m.Data.SteamId, "[U:1:198288660]")

			// 1 = changed class
		case 1:
			assert.Equal(t, m.Type, playerChangedClass)
			assert.Equal(t, m.Data.Class, "scout")

			// 2 = changed class
		case 2:
			assert.Equal(t, m.Type, playerChangedClass)
			assert.Equal(t, m.Data.Class, "soldier")

			// 3 = global message
		case 3:
			assert.Equal(t, m.Type, playerGlobalMessage)

			assert.Equal(t, m.Data.Team, "Red")
			assert.Equal(t, m.Data.Text, "hello gringos")

			// 4 = team message
		case 4:
			assert.Equal(t, m.Type, playerTeamMessage)
			assert.Equal(t, m.Data.Text, "ufo porno")

			// 5 = changed class
		case 5:
			assert.Equal(t, m.Type, playerChangedClass)
			assert.Equal(t, m.Data.Class, "sniper")

			// 6 = changed team
		case 6:
			assert.Equal(t, m.Type, playerChangedTeam)

			assert.Equal(t, m.Data.Team, "Red")
			assert.Equal(t, m.Data.NewTeam, "Blue")

			// 7 = changed class
		case 7:
			assert.Equal(t, m.Type, playerChangedClass)

			assert.Equal(t, m.Data.Team, "Blue")
			assert.Equal(t, m.Data.Class, "medic")
		}
	}
}
<commit_msg>Fix tests.<commit_after>package TF2RconWrapper

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

var logs []string = []string{
	`"Sk1LL0<2><[U:1:198288660]><Unassigned>" joined team "Red"`,
	`"Sk1LL0<2><[U:1:198288660]><Red>" changed role to "scout"`,
	`"Sk1LL0<2><[U:1:198288660]><Red>" changed role to "soldier"`,
	`"Sk1LL0<2><[U:1:198288660]><Red>" say "hello gringos"`,
	`"Sk1LL0<2><[U:1:198288660]><Red>" say_team "ufo porno"`,
	`"Sk1LL0<2><[U:1:198288660]><Red>" changed role to "sniper"`,
	`"Sk1LL0<2><[U:1:198288660]><Red>" joined team "Blue"`,
	`"Sk1LL0<2><[U:1:198288660]><Blue>" changed role to "medic"`,
}

func TestParse(t *testing.T) {
	for i := range logs {
		m := Parse(logs[i])

		switch i {

		// 0 = changed team
		case 0:
			assert.Equal(t, m.Type, PlayerChangedTeam)

			assert.Equal(t, m.Data.Team, "Unassigned")
			assert.Equal(t, m.Data.NewTeam, "Red")

			assert.Equal(t, m.Data.Username, "Sk1LL0")
			assert.Equal(t, m.Data.UserId, "2")
			assert.Equal(t, m.Data.SteamId, "[U:1:198288660]")

			// 1 = changed class
		case 1:
			assert.Equal(t, m.Type, PlayerChangedClass)
			assert.Equal(t, m.Data.Class, "scout")

			// 2 = changed class
		case 2:
			assert.Equal(t, m.Type, PlayerChangedClass)
			assert.Equal(t, m.Data.Class, "soldier")

			// 3 = global message
		case 3:
			assert.Equal(t, m.Type, PlayerGlobalMessage)

			assert.Equal(t, m.Data.Team, "Red")
			assert.Equal(t, m.Data.Text, "hello gringos")

			// 4 = team message
		case 4:
			assert.Equal(t, m.Type, PlayerTeamMessage)
			assert.Equal(t, m.Data.Text, "ufo porno")

			// 5 = changed class
		case 5:
			assert.Equal(t, m.Type, PlayerChangedClass)
			assert.Equal(t, m.Data.Class, "sniper")

			// 6 = changed team
		case 6:
			assert.Equal(t, m.Type, PlayerChangedTeam)

			assert.Equal(t, m.Data.Team, "Red")
			assert.Equal(t, m.Data.NewTeam, "Blue")

			// 7 = changed class
		case 7:
			assert.Equal(t, m.Type, PlayerChangedClass)

			assert.Equal(t, m.Data.Team, "Blue")
			assert.Equal(t, m.Data.Class, "medic")
		}
	}
}
<|endoftext|>


<commit_before>package handler

import (
	"bufio"
	"fmt"
	"io"
	"os/exec"
	"strings"
	"time"
)

func ExeSync(outputer func([]byte, interface{}), outputerArgs interface{}, name string, arg ...string) error {
	fmt.Printf("Exec: %s %v %s\n", name, strings.Join(arg, " "), time.Now())
	defer func() {
		fmt.Printf("Finish: %s %v %s\n\n", name, strings.Join(arg, " "), time.Now())
	}()
	cmd := exec.Command(name, arg...)
	stdOut, err := cmd.StdoutPipe()
	if err != nil {
		return err
	}
	stdErr, err := cmd.StderrPipe()
	if err != nil {
		return err
	}
	multi := io.MultiReader(stdOut, stdErr)
	sc := bufio.NewScanner(multi)

	if err := cmd.Start(); err != nil {
		return err
	}
	// read command's stdout line by line
	go func() {
		lines := []byte{}
		for sc.Scan() {
			l := append(sc.Bytes(), '\n')
			lines = append(lines, l...)
		}
		outputer(lines, outputerArgs)
	}()
	if err := cmd.Wait(); err != nil {
		panic(err.Error())
	}
	return nil
}
<commit_msg>change output way<commit_after>package handler

import (
	"bufio"
	"fmt"
	"io"
	"os/exec"
	"strings"
	"time"
)

func ExeSync(outputer func([]byte, interface{}), outputerArgs interface{}, name string, arg ...string) error {
	fmt.Printf("Exec: %s %v %s\n", name, strings.Join(arg, " "), time.Now())
	defer func() {
		fmt.Printf("Finish: %s %v %s\n\n", name, strings.Join(arg, " "), time.Now())
	}()
	cmd := exec.Command(name, arg...)
	stdOut, err := cmd.StdoutPipe()
	if err != nil {
		return err
	}
	stdErr, err := cmd.StderrPipe()
	if err != nil {
		return err
	}
	multi := io.MultiReader(stdOut, stdErr)
	sc := bufio.NewScanner(multi)

	if err := cmd.Start(); err != nil {
		return err
	}
	// read command's stdout line by line
	go func() {
		lines := []byte{}
		for sc.Scan() {
			l := append(sc.Bytes(), '\n')
			lines = append(lines, l...)
			if len(lines) > 100 {
				outputer(lines, outputerArgs)
				lines = []byte{}
			}
		}
	}()
	if err := cmd.Wait(); err != nil {
		panic(err.Error())
	}
	return nil
}
<|endoftext|>


<commit_before>package chapter1

import "net/http"

func main() {
	http.Handler("/helloworld", helloWorldHandler)

	log.Fatal(http.ListenAndServe(":8080", nil)
}

func helloWorldHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "Hello World")
}<commit_msg>updated basic example<commit_after>package main

import (
	"fmt"
	"log"
	"net/http"
)

func main() {
	http.HandleFunc("/helloworld", helloWorldHandler)

	log.Fatal(http.ListenAndServe(":8080", nil))
}

func helloWorldHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "Hello World\n")
}
<|endoftext|>


<commit_before>package chessboard

import "fmt"

func PrintChessboard() {
	for i := 0; i < 8; i++ {
		fmt.Print("+")
		for j := 0; j < 8; j++ {
			fmt.Print("-+")
		}
		fmt.Println()
		fmt.Print("|")
		for j := 0; j < 8; j++ {
			fmt.Print(" |")
		}
		fmt.Println()
	}
	fmt.Print("+")
	for i := 0; i < 8; i++ {
		fmt.Print("-+")
	}
	fmt.Println()
}
<commit_msg>Print coordinates<commit_after>package chessboard

import "fmt"

func PrintChessboard() {
	fmt.Print(" ")
	for i := 0; i < 8; i++ {
		fmt.Printf(" %c", 'a'+i)
	}
	fmt.Println()

	for i := 0; i < 8; i++ {
		fmt.Print(" +")
		for j := 0; j < 8; j++ {
			fmt.Print("-+")
		}
		fmt.Println()
		fmt.Printf("%d|", i+1)
		for j := 0; j < 8; j++ {
			fmt.Print(" |")
		}
		fmt.Println(i + 1)
	}
	fmt.Print(" +")
	for i := 0; i < 8; i++ {
		fmt.Print("-+")
	}
	fmt.Println()

	fmt.Print(" ")
	for i := 0; i < 8; i++ {
		fmt.Printf(" %c", 'a'+i)
	}
	fmt.Println()
}
<|endoftext|>


<commit_before>package whetstone_test

import (
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"strconv"

	"github.com/cloudfoundry-incubator/runtime-schema/models/factories"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
	"github.com/onsi/gomega/gexec"
)

var (
	cli    string
	tmpDir string
)

var _ = BeforeSuite(func() {
	tmpDir = os.TempDir()

	var err error
	cli, err = gexec.Build("github.com/pivotal-cf-experimental/lattice-cli/ltc")
	Expect(err).ToNot(HaveOccurred())
})

var _ = AfterSuite(func() {
	gexec.CleanupBuildArtifacts()
})

var _ = Describe("Lattice", func() {
	Context("when desiring a docker-based LRP", func() {

		var (
			appName string
			route   string
		)

		BeforeEach(func() {
			appName = fmt.Sprintf("whetstone-%s", factories.GenerateGuid())
			route = fmt.Sprintf("%s.%s", appName, domain)

			targetLattice(domain, username, password)
		})

		AfterEach(func() {
			removeApp(appName)

			Eventually(errorCheckForRoute(route), timeout, 1).Should(HaveOccurred())
		})

		It("eventually runs a docker app", func() {
			startDockerApp(appName)
			Eventually(errorCheckForRoute(route), timeout, 1).ShouldNot(HaveOccurred())

			logsStream := streamLogs(appName)
			Eventually(logsStream.Out, timeout).Should(gbytes.Say("WHETSTONE TEST APP. Says Hello Whetstone."))

			scaleApp(appName)

			instanceCountChan := make(chan int, numCpu)
			go countInstances(route, instanceCountChan)
			Eventually(instanceCountChan, timeout).Should(Receive(Equal(3)))

			logsStream.Terminate().Wait()
		})
	})

})

func startDockerApp(appName string) {
	command := command(cli, "start", appName, "-i", "docker:///cloudfoundry/lattice-app", "--env", "APP_NAME", "--", "/lattice-app", "--message", "Hello Whetstone", "--quiet")
	session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)

	Expect(err).ToNot(HaveOccurred())
	expectExit(session)

	Expect(session.Out).To(gbytes.Say(appName + " is now running."))
}

func streamLogs(appName string) *gexec.Session {
	command := command(cli, "logs", appName)
	session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
	Expect(err).ToNot(HaveOccurred())

	return session
}

func scaleApp(appName string) {
	command := command(cli, "scale", appName, "--instances", "3")
	session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)

	Expect(err).ToNot(HaveOccurred())
	expectExit(session)
}

func removeApp(appName string) {
	command := command(cli, "remove", appName)
	session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)

	Expect(err).ToNot(HaveOccurred())
	expectExit(session)
}

func targetLattice(domain, username, password string) {
	stdinReader, stdinWriter := io.Pipe()

	command := command(cli, "target", domain)
	command.Stdin = stdinReader

	session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
	Expect(err).ToNot(HaveOccurred())

	if username != "" || password != "" {
		Eventually(session.Out).Should(gbytes.Say("Username: "))
		stdinWriter.Write([]byte(username + "\n"))

		Eventually(session.Out).Should(gbytes.Say("Password: "))
		stdinWriter.Write([]byte(password + "\n"))
	}

	stdinWriter.Close()
	expectExit(session)
}

func command(name string, arg ...string) *exec.Cmd {
	command := exec.Command(name, arg...)

	appName := "APP_NAME=WHETSTONE TEST APP"
	cliHome := fmt.Sprintf("LATTICE_CLI_HOME=%s", tmpDir)
	cliTimeout := fmt.Sprintf("LATTICE_CLI_TIMEOUT=%d", timeout)

	command.Env = []string{cliHome, appName, cliTimeout}
	return command
}

func errorCheckForRoute(route string) func() error {
	return func() error {
		response, err := makeGetRequestToRoute(route)
		if err != nil {
			return err
		}

		io.Copy(ioutil.Discard, response.Body)
		defer response.Body.Close()

		if response.StatusCode != 200 {
			return fmt.Errorf("Status code %d should be 200", response.StatusCode)
		}

		return nil
	}
}

func countInstances(route string, instanceCountChan chan<- int) {
	defer GinkgoRecover()
	instanceIndexRoute := fmt.Sprintf("%s/index", route)
	instancesSeen := make(map[int]bool)

	instanceIndexChan := make(chan int, numCpu)

	for i := 0; i < numCpu; i++ {
		go pollForInstanceIndices(instanceIndexRoute, instanceIndexChan)
	}

	for {
		instanceIndex := <-instanceIndexChan
		instancesSeen[instanceIndex] = true
		instanceCountChan <- len(instancesSeen)
	}
}

func pollForInstanceIndices(route string, instanceIndexChan chan<- int) {
	defer GinkgoRecover()
	for {
		response, err := makeGetRequestToRoute(route)
		Expect(err).To(BeNil())

		responseBody, err := ioutil.ReadAll(response.Body)
		defer response.Body.Close()
		Expect(err).To(BeNil())

		instanceIndex, err := strconv.Atoi(string(responseBody))
		if err != nil {
			continue
		}
		instanceIndexChan <- instanceIndex
	}
}

func makeGetRequestToRoute(route string) (*http.Response, error) {
	routeWithScheme := fmt.Sprintf("http://%s", route)
	resp, err := http.DefaultClient.Get(routeWithScheme)
	if err != nil {
		return nil, err
	}

	return resp, nil
}

func expectExit(session *gexec.Session) {
	Eventually(session, timeout).Should(gexec.Exit(0))
	Expect(string(session.Out.Contents())).To(HaveSuffix("\n"))
}
<commit_msg>test pulling metadata from dockerhub in a second test<commit_after>package whetstone_test

import (
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"strconv"

	"github.com/cloudfoundry-incubator/runtime-schema/models/factories"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
	"github.com/onsi/gomega/gexec"
)

var (
	cli    string
	tmpDir string
)

var _ = BeforeSuite(func() {
	tmpDir = os.TempDir()

	var err error
	cli, err = gexec.Build("github.com/pivotal-cf-experimental/lattice-cli/ltc")
	Expect(err).ToNot(HaveOccurred())
})

var _ = AfterSuite(func() {
	gexec.CleanupBuildArtifacts()
})

var _ = Describe("Lattice", func() {
	Context("when desiring a docker-based LRP", func() {

		var (
			appName string
			route   string
		)

		BeforeEach(func() {
			appName = fmt.Sprintf("whetstone-%s", factories.GenerateGuid())
			route = fmt.Sprintf("%s.%s", appName, domain)

			targetLattice(domain, username, password)
		})

		AfterEach(func() {
			removeApp(appName)

			Eventually(errorCheckForRoute(route), timeout, 1).Should(HaveOccurred())
		})

		It("eventually runs a docker app", func() {
			startDockerApp(appName, "-i", "docker:///cloudfoundry/lattice-app", "--env", "APP_NAME", "--", "/lattice-app", "--message", "Hello Whetstone", "--quiet")

			Eventually(errorCheckForRoute(route), timeout, 1).ShouldNot(HaveOccurred())

			logsStream := streamLogs(appName)
			Eventually(logsStream.Out, timeout).Should(gbytes.Say("WHETSTONE TEST APP. Says Hello Whetstone."))

			scaleApp(appName)

			instanceCountChan := make(chan int, numCpu)
			go countInstances(route, instanceCountChan)
			Eventually(instanceCountChan, timeout).Should(Receive(Equal(3)))

			logsStream.Terminate().Wait()
		})

		It("eventually runs a docker app with metadata from Docker Hub", func() {
			startDockerApp(appName, "-i", "docker:///cloudfoundry/lattice-app")

			Eventually(errorCheckForRoute(route), timeout, 1).ShouldNot(HaveOccurred())
		})
	})
})

func startDockerApp(appName string, args ...string) {
	startArgs := append([]string{"start", appName}, args...)
	command := command(cli, startArgs...)
	session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)

	Expect(err).ToNot(HaveOccurred())
	expectExit(session)

	Expect(session.Out).To(gbytes.Say(appName + " is now running."))
}

func streamLogs(appName string) *gexec.Session {
	command := command(cli, "logs", appName)
	session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
	Expect(err).ToNot(HaveOccurred())

	return session
}

func scaleApp(appName string) {
	command := command(cli, "scale", appName, "--instances", "3")
	session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)

	Expect(err).ToNot(HaveOccurred())
	expectExit(session)
}

func removeApp(appName string) {
	command := command(cli, "remove", appName)
	session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)

	Expect(err).ToNot(HaveOccurred())
	expectExit(session)
}

func targetLattice(domain, username, password string) {
	stdinReader, stdinWriter := io.Pipe()

	command := command(cli, "target", domain)
	command.Stdin = stdinReader

	session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
	Expect(err).ToNot(HaveOccurred())

	if username != "" || password != "" {
		Eventually(session.Out).Should(gbytes.Say("Username: "))
		stdinWriter.Write([]byte(username + "\n"))

		Eventually(session.Out).Should(gbytes.Say("Password: "))
		stdinWriter.Write([]byte(password + "\n"))
	}

	stdinWriter.Close()
	expectExit(session)
}

func command(name string, arg ...string) *exec.Cmd {
	command := exec.Command(name, arg...)

	appName := "APP_NAME=WHETSTONE TEST APP"
	cliHome := fmt.Sprintf("LATTICE_CLI_HOME=%s", tmpDir)
	cliTimeout := fmt.Sprintf("LATTICE_CLI_TIMEOUT=%d", timeout)

	command.Env = []string{cliHome, appName, cliTimeout}
	return command
}

func errorCheckForRoute(route string) func() error {
	return func() error {
		response, err := makeGetRequestToRoute(route)
		if err != nil {
			return err
		}

		io.Copy(ioutil.Discard, response.Body)
		defer response.Body.Close()

		if response.StatusCode != 200 {
			return fmt.Errorf("Status code %d should be 200", response.StatusCode)
		}

		return nil
	}
}

func countInstances(route string, instanceCountChan chan<- int) {
	defer GinkgoRecover()
	instanceIndexRoute := fmt.Sprintf("%s/index", route)
	instancesSeen := make(map[int]bool)

	instanceIndexChan := make(chan int, numCpu)

	for i := 0; i < numCpu; i++ {
		go pollForInstanceIndices(instanceIndexRoute, instanceIndexChan)
	}

	for {
		instanceIndex := <-instanceIndexChan
		instancesSeen[instanceIndex] = true
		instanceCountChan <- len(instancesSeen)
	}
}

func pollForInstanceIndices(route string, instanceIndexChan chan<- int) {
	defer GinkgoRecover()
	for {
		response, err := makeGetRequestToRoute(route)
		Expect(err).To(BeNil())

		responseBody, err := ioutil.ReadAll(response.Body)
		defer response.Body.Close()
		Expect(err).To(BeNil())

		instanceIndex, err := strconv.Atoi(string(responseBody))
		if err != nil {
			continue
		}
		instanceIndexChan <- instanceIndex
	}
}

func makeGetRequestToRoute(route string) (*http.Response, error) {
	routeWithScheme := fmt.Sprintf("http://%s", route)
	resp, err := http.DefaultClient.Get(routeWithScheme)
	if err != nil {
		return nil, err
	}

	return resp, nil
}

func expectExit(session *gexec.Session) {
	Eventually(session, timeout).Should(gexec.Exit(0))
	Expect(string(session.Out.Contents())).To(HaveSuffix("\n"))
}
<|endoftext|>


<commit_before>package expr

import (
	"math"
	"sort"
	"strconv"
	"strings"
	"testing"

	"github.com/grafana/metrictank/api/models"
	"github.com/grafana/metrictank/test"
	schema "gopkg.in/raintank/schema.v1"
)

func getModel(name string, data []schema.Point) models.Series {
	tags := make(map[string]string, 3)
	tagSplits := strings.Split(name, ";")

	tags["name"] = tagSplits[0]

	if len(tagSplits) > 1 {
		tagSplits = tagSplits[1:]
	}

	for _, split := range tagSplits {
		pair := strings.SplitN(split, "=", 2)
		tags[pair[0]] = pair[1]
	}
	return models.Series{
		Target:     name,
		QueryPatt:  name,
		Tags:       tags,
		Datapoints: getCopy(data),
	}
}

// Test error cases
func TestNoTags(t *testing.T) {
	in := []models.Series{
		getModel("name1;tag1=val1", a),
	}
	f := NewGroupByTags()
	gby := f.(*FuncGroupByTags)
	gby.in = NewMock(in)
	gby.aggregator = "sum"
	gby.tags = []string{}

	_, err := f.Exec(make(map[Req][]models.Series))
	if err == nil {
		t.Fatalf("case %q: err should not be nil but was", "TestnoTags")
	}
}

func TestInvalidAggregator(t *testing.T) {
	in := []models.Series{
		getModel("name1;tag1=val1", a),
	}
	f := NewGroupByTags()
	gby := f.(*FuncGroupByTags)
	gby.in = NewMock(in)
	gby.aggregator = "inv"
	gby.tags = []string{}

	_, err := f.Exec(make(map[Req][]models.Series))
	if err == nil {
		t.Fatalf("case %q: err should not be nil but was", "TestnoTags")
	}
}

// Test normal cases
func TestGroupByTagsSingleSeries(t *testing.T) {
	in := []models.Series{
		getModel("name1;tag1=val1", a),
	}
	out := in

	testGroupByTags("SingleSeries", in, out, "sum", []string{"tag1"}, t)
}

func TestGroupByTagsMultipleSeriesSingleResult(t *testing.T) {
	in := []models.Series{
		getModel("name1;tag1=val1;tag2=val2_0", a),
		getModel("name1;tag1=val1;tag2=val2_1", b),
	}
	out := []models.Series{
		getModel("name1;tag1=val1", sumab),
	}

	testGroupByTags("MultipleSeriesSingleResult", in, out, "sum", []string{"tag1"}, t)
}

func TestGroupByTagsMultipleSeriesMultipleResults(t *testing.T) {
	in := []models.Series{
		getModel("name1;tag1=val1;tag2=val2_0", a),
		getModel("name1;tag1=val1;tag2=val2_1", b),
		getModel("name1;tag1=val1_1;tag2=val2_0", c),
		getModel("name1;tag1=val1_1;tag2=val2_1", d),
	}
	out := []models.Series{
		getModel("name1;tag1=val1", sumab),
		getModel("name1;tag1=val1_1", sumcd),
	}

	testGroupByTags("MultipleSeriesMultipleResult", in, out, "sum", []string{"tag1"}, t)
}
func TestGroupByTagsMultipleSeriesMultipleResultsMultipleNames(t *testing.T) {
	in := []models.Series{
		getModel("name1;tag1=val1;tag2=val2_0", a),
		getModel("name1;tag1=val1;tag2=val2_1", b),
		getModel("name2;tag1=val1_1;tag2=val2_0", c),
		getModel("name2;tag1=val1_1;tag2=val2_1", d),
	}
	out := []models.Series{
		getModel("sum;tag1=val1", sumab),
		getModel("sum;tag1=val1_1", sumcd),
	}

	testGroupByTags("MultipleSeriesMultipleResultsMultipleNames", in, out, "sum", []string{"tag1"}, t)
}

func TestGroupByTagsMultipleSeriesMultipleResultsMultipleNamesMoreTags(t *testing.T) {
	in := []models.Series{
		getModel("name1;tag1=val1;tag2=val2_0;tag3=3", a),
		getModel("name1;tag1=val1;tag2=val2_1;tag3=3", b),
		getModel("name2;tag1=val1_1;tag2=val2_0;tag3=3", c),
		getModel("name2;tag1=val1_1;tag2=val2_1;tag3=3", d),
	}
	out := []models.Series{
		getModel("sum;tag1=val1;tag3=3", sumab),
		getModel("sum;tag1=val1_1;tag3=3", sumcd),
	}

	testGroupByTags("MultipleSeriesMultipleResultsMultipleNamesMoreTags", in, out, "sum", []string{"tag1", "tag3"}, t)
}

func TestGroupByTagsMultipleSeriesMultipleResultsGroupByName(t *testing.T) {
	in := []models.Series{
		getModel("name1;tag1=val1;tag2=val2_0", a),
		getModel("name1;tag1=val1;tag2=val2_1", b),
		getModel("name2;tag1=val1_1;tag2=val2_0", c),
		getModel("name2;tag1=val1_1;tag2=val2_1", d),
	}
	out := []models.Series{
		getModel("name1;tag1=val1", sumab),
		getModel("name2;tag1=val1_1", sumcd),
	}

	testGroupByTags("MultipleSeriesMultipleResultsGroupByName", in, out, "sum", []string{"tag1", "name"}, t)
}

func TestGroupByTagsMultipleSeriesMissingTag(t *testing.T) {
	in := []models.Series{
		getModel("name1;tag1=val1;tag2=val2_0", a),
		getModel("name1;tag1=val1;tag2=val2_1", b),
		getModel("name2;tag1=val1_1;tag2=val2_0", c),
		getModel("name2;tag1=val1_1;tag2=val2_1", d),
	}
	out := []models.Series{
		getModel("name1;missingTag=;tag1=val1", sumab),
		getModel("name2;missingTag=;tag1=val1_1", sumcd),
	}

	testGroupByTags("MultipleSeriesMultipleResultsGroupByName", in, out, "sum", []string{"tag1", "name", "missingTag"}, t)
}

func TestGroupByTagsAllAggregators(t *testing.T) {
	aggregators := []struct {
		name             string
		result1, result2 []schema.Point
	}{
		{name: "sum", result1: sumab, result2: sumabc},
		{name: "avg", result1: avgab, result2: avgabc},
		{name: "average", result1: avgab, result2: avgabc},
		{name: "max", result1: maxab, result2: maxabc},
	}

	for _, agg := range aggregators {
		in := []models.Series{
			getModel("name1;tag1=val1;tag2=val2_0", a),
			getModel("name1;tag1=val1;tag2=val2_1", b),
			getModel("name2;tag1=val1_1;tag2=val2_0", a),
			getModel("name2;tag1=val1_1;tag2=val2_1", b),
			getModel("name2;tag1=val1_1;tag2=val2_2", c),
		}
		out := []models.Series{
			getModel("name1;tag1=val1", agg.result1),
			getModel("name2;tag1=val1_1", agg.result2),
		}

		testGroupByTags("AllAggregators:"+agg.name, in, out, agg.name, []string{"tag1", "name"}, t)
	}
}

func testGroupByTags(name string, in []models.Series, out []models.Series, agg string, tags []string, t *testing.T) {
	f := NewGroupByTags()
	gby := f.(*FuncGroupByTags)
	gby.in = NewMock(in)
	gby.aggregator = agg
	gby.tags = tags

	got, err := f.Exec(make(map[Req][]models.Series))
	if err != nil {
		t.Fatalf("case %q: err should be nil. got %q", name, err)
	}
	if len(got) != len(out) {
		t.Fatalf("case %q: GroupByTags output expected to be %d but actually %d", name, len(out), len(got))
	}

	// Make sure got and out are in the same order
	sort.Slice(got, func(i, j int) bool {
		return got[i].Target < got[j].Target
	})
	sort.Slice(out, func(i, j int) bool {
		return out[i].Target < out[j].Target
	})
	for i, g := range got {
		o := out[i]
		if g.Target != o.Target {
			t.Fatalf("case %q: expected target %q, got %q", name, o.Target, g.Target)
		}
		if len(g.Datapoints) != len(o.Datapoints) {
			t.Fatalf("case %q: len output expected %d, got %d", name, len(o.Datapoints), len(g.Datapoints))
		}
		for j, p := range g.Datapoints {
			bothNaN := math.IsNaN(p.Val) && math.IsNaN(o.Datapoints[j].Val)
			if (bothNaN || p.Val == o.Datapoints[j].Val) && p.Ts == o.Datapoints[j].Ts {
				continue
			}
			t.Fatalf("case %q: output point %d - expected %v got %v", name, j, o.Datapoints[j], p)
		}
		if len(g.Tags) != len(o.Tags) {
			t.Fatalf("case %q: len tags expected %d, got %d", name, len(o.Tags), len(g.Tags))
		}
		for k, v := range g.Tags {
			expectedVal, ok := o.Tags[k]

			if !ok {
				t.Fatalf("case %q: Got unknown tag key '%s'", name, k)
			}

			if v != expectedVal {
				t.Fatalf("case %q: Key '%s' had wrong value: expected '%s', got '%s'", name, k, expectedVal, v)
			}
		}
	}
}

func BenchmarkGroupByTags10k_1NoNulls(b *testing.B) {
	benchmarkGroupByTags(b, 1, test.RandFloats10k, test.RandFloats10k)
}
func BenchmarkGroupByTags10k_10NoNulls(b *testing.B) {
	benchmarkGroupByTags(b, 10, test.RandFloats10k, test.RandFloats10k)
}
func BenchmarkGroupByTags10k_100NoNulls(b *testing.B) {
	benchmarkGroupByTags(b, 100, test.RandFloats10k, test.RandFloats10k)
}
func BenchmarkGroupByTags10k_1000NoNulls(b *testing.B) {
	benchmarkGroupByTags(b, 1000, test.RandFloats10k, test.RandFloats10k)
}

func BenchmarkGroupByTags10k_1SomeSeriesHalfNulls(b *testing.B) {
	benchmarkGroupByTags(b, 1, test.RandFloats10k, test.RandFloatsWithNulls10k)
}
func BenchmarkGroupByTags10k_10SomeSeriesHalfNulls(b *testing.B) {
	benchmarkGroupByTags(b, 10, test.RandFloats10k, test.RandFloatsWithNulls10k)
}
func BenchmarkGroupByTags10k_100SomeSeriesHalfNulls(b *testing.B) {
	benchmarkGroupByTags(b, 100, test.RandFloats10k, test.RandFloatsWithNulls10k)
}
func BenchmarkGroupByTags10k_1000SomeSeriesHalfNulls(b *testing.B) {
	benchmarkGroupByTags(b, 1000, test.RandFloats10k, test.RandFloatsWithNulls10k)
}

func BenchmarkGroupByTags10k_1AllSeriesHalfNulls(b *testing.B) {
	benchmarkGroupByTags(b, 1, test.RandFloatsWithNulls10k, test.RandFloatsWithNulls10k)
}
func BenchmarkGroupByTags10k_10AllSeriesHalfNulls(b *testing.B) {
	benchmarkGroupByTags(b, 10, test.RandFloatsWithNulls10k, test.RandFloatsWithNulls10k)
}
func BenchmarkGroupByTags10k_100AllSeriesHalfNulls(b *testing.B) {
	benchmarkGroupByTags(b, 100, test.RandFloatsWithNulls10k, test.RandFloatsWithNulls10k)
}
func BenchmarkGroupByTags10k_1000AllSeriesHalfNulls(b *testing.B) {
	benchmarkGroupByTags(b, 1000, test.RandFloatsWithNulls10k, test.RandFloatsWithNulls10k)
}

func benchmarkGroupByTags(b *testing.B, numSeries int, fn0, fn1 func() []schema.Point) {
	var input []models.Series
	tagValues := []string{"tag1", "tag2", "tag3", "tag4"}
	for i := 0; i < numSeries; i++ {
		tags := make(map[string]string, len(tagValues))

		for t, tag := range tagValues {
			tags[tag] = strconv.Itoa(t)
		}
		series := models.Series{
			Target: strconv.Itoa(i),
		}
		if i%1 == 0 {
			series.Datapoints = fn0()
		} else {
			series.Datapoints = fn1()
		}
		input = append(input, series)
	}
	b.ResetTimer()
	var err error
	for i := 0; i < b.N; i++ {
		f := NewGroupByTags()
		gby := f.(*FuncGroupByTags)
		gby.in = NewMock(input)
		gby.aggregator = "sum"
		gby.tags = []string{"tag1", "tag2"}
		results, err = f.Exec(make(map[Req][]models.Series))
		if err != nil {
			b.Fatalf("%s", err)
		}
	}
	b.SetBytes(int64(numSeries * len(results[0].Datapoints) * 12))
}
<commit_msg>Add more aggregation types to the 'TestAllAggregators' test<commit_after>package expr

import (
	"math"
	"sort"
	"strconv"
	"strings"
	"testing"

	"github.com/grafana/metrictank/api/models"
	"github.com/grafana/metrictank/test"
	schema "gopkg.in/raintank/schema.v1"
)

func getModel(name string, data []schema.Point) models.Series {
	tags := make(map[string]string, 3)
	tagSplits := strings.Split(name, ";")

	tags["name"] = tagSplits[0]

	if len(tagSplits) > 1 {
		tagSplits = tagSplits[1:]
	}

	for _, split := range tagSplits {
		pair := strings.SplitN(split, "=", 2)
		tags[pair[0]] = pair[1]
	}
	return models.Series{
		Target:     name,
		QueryPatt:  name,
		Tags:       tags,
		Datapoints: getCopy(data),
	}
}

// Test error cases
func TestNoTags(t *testing.T) {
	in := []models.Series{
		getModel("name1;tag1=val1", a),
	}
	f := NewGroupByTags()
	gby := f.(*FuncGroupByTags)
	gby.in = NewMock(in)
	gby.aggregator = "sum"
	gby.tags = []string{}

	_, err := f.Exec(make(map[Req][]models.Series))
	if err == nil {
		t.Fatalf("case %q: err should not be nil but was", "TestnoTags")
	}
}

func TestInvalidAggregator(t *testing.T) {
	in := []models.Series{
		getModel("name1;tag1=val1", a),
	}
	f := NewGroupByTags()
	gby := f.(*FuncGroupByTags)
	gby.in = NewMock(in)
	gby.aggregator = "inv"
	gby.tags = []string{}

	_, err := f.Exec(make(map[Req][]models.Series))
	if err == nil {
		t.Fatalf("case %q: err should not be nil but was", "TestnoTags")
	}
}

// Test normal cases
func TestGroupByTagsSingleSeries(t *testing.T) {
	in := []models.Series{
		getModel("name1;tag1=val1", a),
	}
	out := in

	testGroupByTags("SingleSeries", in, out, "sum", []string{"tag1"}, t)
}

func TestGroupByTagsMultipleSeriesSingleResult(t *testing.T) {
	in := []models.Series{
		getModel("name1;tag1=val1;tag2=val2_0", a),
		getModel("name1;tag1=val1;tag2=val2_1", b),
	}
	out := []models.Series{
		getModel("name1;tag1=val1", sumab),
	}

	testGroupByTags("MultipleSeriesSingleResult", in, out, "sum", []string{"tag1"}, t)
}

func TestGroupByTagsMultipleSeriesMultipleResults(t *testing.T) {
	in := []models.Series{
		getModel("name1;tag1=val1;tag2=val2_0", a),
		getModel("name1;tag1=val1;tag2=val2_1", b),
		getModel("name1;tag1=val1_1;tag2=val2_0", c),
		getModel("name1;tag1=val1_1;tag2=val2_1", d),
	}
	out := []models.Series{
		getModel("name1;tag1=val1", sumab),
		getModel("name1;tag1=val1_1", sumcd),
	}

	testGroupByTags("MultipleSeriesMultipleResult", in, out, "sum", []string{"tag1"}, t)
}
func TestGroupByTagsMultipleSeriesMultipleResultsMultipleNames(t *testing.T) {
	in := []models.Series{
		getModel("name1;tag1=val1;tag2=val2_0", a),
		getModel("name1;tag1=val1;tag2=val2_1", b),
		getModel("name2;tag1=val1_1;tag2=val2_0", c),
		getModel("name2;tag1=val1_1;tag2=val2_1", d),
	}
	out := []models.Series{
		getModel("sum;tag1=val1", sumab),
		getModel("sum;tag1=val1_1", sumcd),
	}

	testGroupByTags("MultipleSeriesMultipleResultsMultipleNames", in, out, "sum", []string{"tag1"}, t)
}

func TestGroupByTagsMultipleSeriesMultipleResultsMultipleNamesMoreTags(t *testing.T) {
	in := []models.Series{
		getModel("name1;tag1=val1;tag2=val2_0;tag3=3", a),
		getModel("name1;tag1=val1;tag2=val2_1;tag3=3", b),
		getModel("name2;tag1=val1_1;tag2=val2_0;tag3=3", c),
		getModel("name2;tag1=val1_1;tag2=val2_1;tag3=3", d),
	}
	out := []models.Series{
		getModel("sum;tag1=val1;tag3=3", sumab),
		getModel("sum;tag1=val1_1;tag3=3", sumcd),
	}

	testGroupByTags("MultipleSeriesMultipleResultsMultipleNamesMoreTags", in, out, "sum", []string{"tag1", "tag3"}, t)
}

func TestGroupByTagsMultipleSeriesMultipleResultsGroupByName(t *testing.T) {
	in := []models.Series{
		getModel("name1;tag1=val1;tag2=val2_0", a),
		getModel("name1;tag1=val1;tag2=val2_1", b),
		getModel("name2;tag1=val1_1;tag2=val2_0", c),
		getModel("name2;tag1=val1_1;tag2=val2_1", d),
	}
	out := []models.Series{
		getModel("name1;tag1=val1", sumab),
		getModel("name2;tag1=val1_1", sumcd),
	}

	testGroupByTags("MultipleSeriesMultipleResultsGroupByName", in, out, "sum", []string{"tag1", "name"}, t)
}

func TestGroupByTagsMultipleSeriesMissingTag(t *testing.T) {
	in := []models.Series{
		getModel("name1;tag1=val1;tag2=val2_0", a),
		getModel("name1;tag1=val1;tag2=val2_1", b),
		getModel("name2;tag1=val1_1;tag2=val2_0", c),
		getModel("name2;tag1=val1_1;tag2=val2_1", d),
	}
	out := []models.Series{
		getModel("name1;missingTag=;tag1=val1", sumab),
		getModel("name2;missingTag=;tag1=val1_1", sumcd),
	}

	testGroupByTags("MultipleSeriesMultipleResultsGroupByName", in, out, "sum", []string{"tag1", "name", "missingTag"}, t)
}

func TestGroupByTagsAllAggregators(t *testing.T) {
	aggregators := []struct {
		name             string
		result1, result2 []schema.Point
	}{
		{name: "sum", result1: sumab, result2: sumabc},
		{name: "avg", result1: avgab, result2: avgabc},
		{name: "average", result1: avgab, result2: avgabc},
		{name: "max", result1: maxab, result2: maxabc},
		{name: "median", result1: medianab, result2: medianabc},
		{name: "multiply", result1: multab, result2: multabc},
		{name: "stddev", result1: stddevab, result2: stddevabc},
		{name: "diff", result1: diffab, result2: diffabc},
		{name: "range", result1: rangeab, result2: rangeabc},
	}

	for _, agg := range aggregators {
		in := []models.Series{
			getModel("name1;tag1=val1;tag2=val2_0", a),
			getModel("name1;tag1=val1;tag2=val2_1", b),
			getModel("name2;tag1=val1_1;tag2=val2_0", a),
			getModel("name2;tag1=val1_1;tag2=val2_1", b),
			getModel("name2;tag1=val1_1;tag2=val2_2", c),
		}
		out := []models.Series{
			getModel("name1;tag1=val1", agg.result1),
			getModel("name2;tag1=val1_1", agg.result2),
		}

		testGroupByTags("AllAggregators:"+agg.name, in, out, agg.name, []string{"tag1", "name"}, t)
	}
}

func testGroupByTags(name string, in []models.Series, out []models.Series, agg string, tags []string, t *testing.T) {
	f := NewGroupByTags()
	gby := f.(*FuncGroupByTags)
	gby.in = NewMock(in)
	gby.aggregator = agg
	gby.tags = tags

	got, err := f.Exec(make(map[Req][]models.Series))
	if err != nil {
		t.Fatalf("case %q: err should be nil. got %q", name, err)
	}
	if len(got) != len(out) {
		t.Fatalf("case %q: GroupByTags output expected to be %d but actually %d", name, len(out), len(got))
	}

	// Make sure got and out are in the same order
	sort.Slice(got, func(i, j int) bool {
		return got[i].Target < got[j].Target
	})
	sort.Slice(out, func(i, j int) bool {
		return out[i].Target < out[j].Target
	})
	for i, g := range got {
		o := out[i]
		if g.Target != o.Target {
			t.Fatalf("case %q: expected target %q, got %q", name, o.Target, g.Target)
		}
		if len(g.Datapoints) != len(o.Datapoints) {
			t.Fatalf("case %q: len output expected %d, got %d", name, len(o.Datapoints), len(g.Datapoints))
		}
		for j, p := range g.Datapoints {
			bothNaN := math.IsNaN(p.Val) && math.IsNaN(o.Datapoints[j].Val)
			if (bothNaN || p.Val == o.Datapoints[j].Val) && p.Ts == o.Datapoints[j].Ts {
				continue
			}
			t.Fatalf("case %q: output point %d - expected %v got %v", name, j, o.Datapoints[j], p)
		}
		if len(g.Tags) != len(o.Tags) {
			t.Fatalf("case %q: len tags expected %d, got %d", name, len(o.Tags), len(g.Tags))
		}
		for k, v := range g.Tags {
			expectedVal, ok := o.Tags[k]

			if !ok {
				t.Fatalf("case %q: Got unknown tag key '%s'", name, k)
			}

			if v != expectedVal {
				t.Fatalf("case %q: Key '%s' had wrong value: expected '%s', got '%s'", name, k, expectedVal, v)
			}
		}
	}
}

func BenchmarkGroupByTags10k_1NoNulls(b *testing.B) {
	benchmarkGroupByTags(b, 1, test.RandFloats10k, test.RandFloats10k)
}
func BenchmarkGroupByTags10k_10NoNulls(b *testing.B) {
	benchmarkGroupByTags(b, 10, test.RandFloats10k, test.RandFloats10k)
}
func BenchmarkGroupByTags10k_100NoNulls(b *testing.B) {
	benchmarkGroupByTags(b, 100, test.RandFloats10k, test.RandFloats10k)
}
func BenchmarkGroupByTags10k_1000NoNulls(b *testing.B) {
	benchmarkGroupByTags(b, 1000, test.RandFloats10k, test.RandFloats10k)
}

func BenchmarkGroupByTags10k_1SomeSeriesHalfNulls(b *testing.B) {
	benchmarkGroupByTags(b, 1, test.RandFloats10k, test.RandFloatsWithNulls10k)
}
func BenchmarkGroupByTags10k_10SomeSeriesHalfNulls(b *testing.B) {
	benchmarkGroupByTags(b, 10, test.RandFloats10k, test.RandFloatsWithNulls10k)
}
func BenchmarkGroupByTags10k_100SomeSeriesHalfNulls(b *testing.B) {
	benchmarkGroupByTags(b, 100, test.RandFloats10k, test.RandFloatsWithNulls10k)
}
func BenchmarkGroupByTags10k_1000SomeSeriesHalfNulls(b *testing.B) {
	benchmarkGroupByTags(b, 1000, test.RandFloats10k, test.RandFloatsWithNulls10k)
}

func BenchmarkGroupByTags10k_1AllSeriesHalfNulls(b *testing.B) {
	benchmarkGroupByTags(b, 1, test.RandFloatsWithNulls10k, test.RandFloatsWithNulls10k)
}
func BenchmarkGroupByTags10k_10AllSeriesHalfNulls(b *testing.B) {
	benchmarkGroupByTags(b, 10, test.RandFloatsWithNulls10k, test.RandFloatsWithNulls10k)
}
func BenchmarkGroupByTags10k_100AllSeriesHalfNulls(b *testing.B) {
	benchmarkGroupByTags(b, 100, test.RandFloatsWithNulls10k, test.RandFloatsWithNulls10k)
}
func BenchmarkGroupByTags10k_1000AllSeriesHalfNulls(b *testing.B) {
	benchmarkGroupByTags(b, 1000, test.RandFloatsWithNulls10k, test.RandFloatsWithNulls10k)
}

func benchmarkGroupByTags(b *testing.B, numSeries int, fn0, fn1 func() []schema.Point) {
	var input []models.Series
	tagValues := []string{"tag1", "tag2", "tag3", "tag4"}
	for i := 0; i < numSeries; i++ {
		tags := make(map[string]string, len(tagValues))

		for t, tag := range tagValues {
			tags[tag] = strconv.Itoa(t)
		}
		series := models.Series{
			Target: strconv.Itoa(i),
		}
		if i%1 == 0 {
			series.Datapoints = fn0()
		} else {
			series.Datapoints = fn1()
		}
		input = append(input, series)
	}
	b.ResetTimer()
	var err error
	for i := 0; i < b.N; i++ {
		f := NewGroupByTags()
		gby := f.(*FuncGroupByTags)
		gby.in = NewMock(input)
		gby.aggregator = "sum"
		gby.tags = []string{"tag1", "tag2"}
		results, err = f.Exec(make(map[Req][]models.Series))
		if err != nil {
			b.Fatalf("%s", err)
		}
	}
	b.SetBytes(int64(numSeries * len(results[0].Datapoints) * 12))
}
<|endoftext|>


<commit_before>// This file was auto-generated by pgen.
// Editing is not advised.

package pgen

type Lookup struct {
	Term  Term
	NTerm NTerm
}

type Token interface{}

type Rule []Token

var Table = map[Lookup]Rule{
	{Term: newTerm("id"), NTerm: newNTerm("argdecls")}:      newRule(newTerm("id"), newNTerm("argdecls")),
	{Term: newTerm("switch"), NTerm: newNTerm("args")}:      newRule(newNTerm("single"), newNTerm("args")),
	{Term: newTerm("("), NTerm: newNTerm("args")}:           newRule(newNTerm("single"), newNTerm("args")),
	{Term: newTerm("number"), NTerm: newNTerm("args")}:      newRule(newNTerm("single"), newNTerm("args")),
	{Term: newTerm("string"), NTerm: newNTerm("args")}:      newRule(newNTerm("single"), newNTerm("args")),
	{Term: newTerm("id"), NTerm: newNTerm("args")}:          newRule(newNTerm("single"), newNTerm("args")),
	{Term: newTerm("["), NTerm: newNTerm("args")}:           newRule(newNTerm("single"), newNTerm("args")),
	{Term: newTerm("["), NTerm: newNTerm("array")}:          newRule(newTerm("["), newNTerm("exprs"), newNTerm("optend"), newTerm("]")),
	{Term: newTerm("->"), NTerm: newNTerm("chain")}:         newRule(newTerm("->"), newNTerm("single"), newNTerm("args"), newNTerm("chain")),
	{Term: newTerm("("), NTerm: newNTerm("compound")}:       newRule(newTerm("("), newNTerm("exprs"), newNTerm("optend"), newTerm(")")),
	{Term: newTerm("string"), NTerm: newNTerm("decl")}:      newRule(newNTerm("import")),
	{Term: newTerm("id"), NTerm: newNTerm("decl")}:          newRule(newNTerm("funcdecl")),
	{Term: newTerm("number"), NTerm: newNTerm("expr")}:      newRule(newNTerm("single"), newNTerm("args"), newNTerm("chain")),
	{Term: newTerm("string"), NTerm: newNTerm("expr")}:      newRule(newNTerm("single"), newNTerm("args"), newNTerm("chain")),
	{Term: newTerm("id"), NTerm: newNTerm("expr")}:          newRule(newNTerm("single"), newNTerm("args"), newNTerm("chain")),
	{Term: newTerm("["), NTerm: newNTerm("expr")}:           newRule(newNTerm("single"), newNTerm("args"), newNTerm("chain")),
	{Term: newTerm("switch"), NTerm: newNTerm("expr")}:      newRule(newNTerm("single"), newNTerm("args"), newNTerm("chain")),
	{Term: newTerm("("), NTerm: newNTerm("expr")}:           newRule(newNTerm("single"), newNTerm("args"), newNTerm("chain")),
	{Term: newTerm("id"), NTerm: newNTerm("exprs")}:         newRule(newNTerm("expr"), newTerm(";"), newNTerm("exprs")),
	{Term: newTerm("["), NTerm: newNTerm("exprs")}:          newRule(newNTerm("expr"), newTerm(";"), newNTerm("exprs")),
	{Term: newTerm("switch"), NTerm: newNTerm("exprs")}:     newRule(newNTerm("expr"), newTerm(";"), newNTerm("exprs")),
	{Term: newTerm("("), NTerm: newNTerm("exprs")}:          newRule(newNTerm("expr"), newTerm(";"), newNTerm("exprs")),
	{Term: newTerm("number"), NTerm: newNTerm("exprs")}:     newRule(newNTerm("expr"), newTerm(";"), newNTerm("exprs")),
	{Term: newTerm("string"), NTerm: newNTerm("exprs")}:     newRule(newNTerm("expr"), newTerm(";"), newNTerm("exprs")),
	{Term: newTerm("id"), NTerm: newNTerm("func")}:          newRule(newTerm("id"), newNTerm("subfunc")),
	{Term: newTerm("id"), NTerm: newNTerm("funcdecl")}:      newRule(newTerm("id"), newNTerm("argdecls"), newTerm("=>"), newNTerm("expr"), newTerm(";")),
	{Term: newTerm("string"), NTerm: newNTerm("import")}:    newRule(newTerm("string"), newTerm("=>"), newTerm("id"), newTerm(";")),
	{Term: newTerm(";"), NTerm: newNTerm("optend")}:         newRule(newTerm(";")),
	{Term: newTerm("string"), NTerm: newNTerm("script")}:    newRule(newNTerm("decl"), newNTerm("script")),
	{Term: newTerm("id"), NTerm: newNTerm("script")}:        newRule(newNTerm("decl"), newNTerm("script")),
	{Term: newTerm("["), NTerm: newNTerm("single")}:         newRule(newNTerm("array")),
	{Term: newTerm("switch"), NTerm: newNTerm("single")}:    newRule(newNTerm("switch")),
	{Term: newTerm("("), NTerm: newNTerm("single")}:         newRule(newNTerm("compound")),
	{Term: newTerm("number"), NTerm: newNTerm("single")}:    newRule(newTerm("number")),
	{Term: newTerm("string"), NTerm: newNTerm("single")}:    newRule(newTerm("string")),
	{Term: newTerm("id"), NTerm: newNTerm("single")}:        newRule(newNTerm("func")),
	{Term: newTerm("."), NTerm: newNTerm("subfunc")}:        newRule(newTerm("."), newTerm("id")),
	{Term: newTerm("switch"), NTerm: newNTerm("switch")}:    newRule(newTerm("switch"), newNTerm("expr"), newTerm("{"), newNTerm("switches"), newTerm("}")),
	{Term: newTerm("id"), NTerm: newNTerm("switches")}:      newRule(newNTerm("expr"), newTerm("=>"), newNTerm("expr"), newNTerm("switches")),
	{Term: newTerm("["), NTerm: newNTerm("switches")}:       newRule(newNTerm("expr"), newTerm("=>"), newNTerm("expr"), newNTerm("switches")),
	{Term: newTerm("switch"), NTerm: newNTerm("switches")}:  newRule(newNTerm("expr"), newTerm("=>"), newNTerm("expr"), newNTerm("switches")),
	{Term: newTerm("default"), NTerm: newNTerm("switches")}: newRule(newTerm("default"), newTerm("=>"), newNTerm("expr"), newNTerm("switches")),
	{Term: newTerm("("), NTerm: newNTerm("switches")}:       newRule(newNTerm("expr"), newTerm("=>"), newNTerm("expr"), newNTerm("switches")),
	{Term: newTerm("number"), NTerm: newNTerm("switches")}:  newRule(newNTerm("expr"), newTerm("=>"), newNTerm("expr"), newNTerm("switches")),
	{Term: newTerm("string"), NTerm: newNTerm("switches")}:  newRule(newNTerm("expr"), newTerm("=>"), newNTerm("expr"), newNTerm("switches")),
}
<commit_msg>ast/internal/pgen: Regenerate table.<commit_after>// This file was auto-generated by pgen.
// Editing is not advised.

package pgen

type Lookup struct {
	Term  Term
	NTerm NTerm
}

type Token interface{}

type Rule []Token

var Table = map[Lookup]Rule{
	{Term: newTerm("id"), NTerm: newNTerm("argdecls")}:      newRule(newTerm("id"), newNTerm("argdecls")),
	{Term: newTerm("=>"), NTerm: newNTerm("argdecls")}:      newRule(newTerm("id"), newNTerm("argdecls")),
	{Term: newTerm("["), NTerm: newNTerm("args")}:           newRule(newNTerm("single"), newNTerm("args")),
	{Term: newTerm("switch"), NTerm: newNTerm("args")}:      newRule(newNTerm("single"), newNTerm("args")),
	{Term: newTerm("("), NTerm: newNTerm("args")}:           newRule(newNTerm("single"), newNTerm("args")),
	{Term: newTerm("number"), NTerm: newNTerm("args")}:      newRule(newNTerm("single"), newNTerm("args")),
	{Term: newTerm("string"), NTerm: newNTerm("args")}:      newRule(newNTerm("single"), newNTerm("args")),
	{Term: newTerm("id"), NTerm: newNTerm("args")}:          newRule(newNTerm("single"), newNTerm("args")),
	{Term: newTerm(";"), NTerm: newNTerm("args")}:           newRule(newTerm("->"), newNTerm("single"), newNTerm("args"), newNTerm("chain")),
	{Term: newTerm("string"), NTerm: newNTerm("args")}:      newRule(newTerm("->"), newNTerm("single"), newNTerm("args"), newNTerm("chain")),
	{Term: newTerm("}"), NTerm: newNTerm("args")}:           newRule(newTerm("->"), newNTerm("single"), newNTerm("args"), newNTerm("chain")),
	{Term: newTerm("id"), NTerm: newNTerm("args")}:          newRule(newTerm("->"), newNTerm("single"), newNTerm("args"), newNTerm("chain")),
	{Term: newTerm("default"), NTerm: newNTerm("args")}:     newRule(newTerm("->"), newNTerm("single"), newNTerm("args"), newNTerm("chain")),
	{Term: newTerm("("), NTerm: newNTerm("args")}:           newRule(newTerm("->"), newNTerm("single"), newNTerm("args"), newNTerm("chain")),
	{Term: newTerm("{"), NTerm: newNTerm("args")}:           newRule(newTerm("->"), newNTerm("single"), newNTerm("args"), newNTerm("chain")),
	{Term: newTerm("["), NTerm: newNTerm("args")}:           newRule(newTerm("->"), newNTerm("single"), newNTerm("args"), newNTerm("chain")),
	{Term: newTerm("->"), NTerm: newNTerm("args")}:          newRule(newTerm("->"), newNTerm("single"), newNTerm("args"), newNTerm("chain")),
	{Term: newTerm("number"), NTerm: newNTerm("args")}:      newRule(newTerm("->"), newNTerm("single"), newNTerm("args"), newNTerm("chain")),
	{Term: newTerm("=>"), NTerm: newNTerm("args")}:          newRule(newTerm("->"), newNTerm("single"), newNTerm("args"), newNTerm("chain")),
	{Term: newTerm("switch"), NTerm: newNTerm("args")}:      newRule(newTerm("->"), newNTerm("single"), newNTerm("args"), newNTerm("chain")),
	{Term: newTerm("["), NTerm: newNTerm("array")}:          newRule(newTerm("["), newNTerm("exprs"), newNTerm("optend"), newTerm("]")),
	{Term: newTerm("->"), NTerm: newNTerm("chain")}:         newRule(newTerm("->"), newNTerm("single"), newNTerm("args"), newNTerm("chain")),
	{Term: newTerm("switch"), NTerm: newNTerm("chain")}:     newRule(newTerm("->"), newNTerm("single"), newNTerm("args"), newNTerm("chain")),
	{Term: newTerm("string"), NTerm: newNTerm("chain")}:     newRule(newTerm("->"), newNTerm("single"), newNTerm("args"), newNTerm("chain")),
	{Term: newTerm("=>"), NTerm: newNTerm("chain")}:         newRule(newTerm("->"), newNTerm("single"), newNTerm("args"), newNTerm("chain")),
	{Term: newTerm("id"), NTerm: newNTerm("chain")}:         newRule(newTerm("->"), newNTerm("single"), newNTerm("args"), newNTerm("chain")),
	{Term: newTerm("{"), NTerm: newNTerm("chain")}:          newRule(newTerm("->"), newNTerm("single"), newNTerm("args"), newNTerm("chain")),
	{Term: newTerm("["), NTerm: newNTerm("chain")}:          newRule(newTerm("->"), newNTerm("single"), newNTerm("args"), newNTerm("chain")),
	{Term: newTerm(";"), NTerm: newNTerm("chain")}:          newRule(newTerm("->"), newNTerm("single"), newNTerm("args"), newNTerm("chain")),
	{Term: newTerm("("), NTerm: newNTerm("chain")}:          newRule(newTerm("->"), newNTerm("single"), newNTerm("args"), newNTerm("chain")),
	{Term: newTerm("default"), NTerm: newNTerm("chain")}:    newRule(newTerm("->"), newNTerm("single"), newNTerm("args"), newNTerm("chain")),
	{Term: newTerm("number"), NTerm: newNTerm("chain")}:     newRule(newTerm("->"), newNTerm("single"), newNTerm("args"), newNTerm("chain")),
	{Term: newTerm("}"), NTerm: newNTerm("chain")}:          newRule(newTerm("->"), newNTerm("single"), newNTerm("args"), newNTerm("chain")),
	{Term: newTerm("("), NTerm: newNTerm("compound")}:       newRule(newTerm("("), newNTerm("exprs"), newNTerm("optend"), newTerm(")")),
	{Term: newTerm("string"), NTerm: newNTerm("decl")}:      newRule(newNTerm("import")),
	{Term: newTerm("id"), NTerm: newNTerm("decl")}:          newRule(newNTerm("funcdecl")),
	{Term: newTerm("("), NTerm: newNTerm("expr")}:           newRule(newNTerm("single"), newNTerm("args"), newNTerm("chain")),
	{Term: newTerm("number"), NTerm: newNTerm("expr")}:      newRule(newNTerm("single"), newNTerm("args"), newNTerm("chain")),
	{Term: newTerm("string"), NTerm: newNTerm("expr")}:      newRule(newNTerm("single"), newNTerm("args"), newNTerm("chain")),
	{Term: newTerm("id"), NTerm: newNTerm("expr")}:          newRule(newNTerm("single"), newNTerm("args"), newNTerm("chain")),
	{Term: newTerm("["), NTerm: newNTerm("expr")}:           newRule(newNTerm("single"), newNTerm("args"), newNTerm("chain")),
	{Term: newTerm("switch"), NTerm: newNTerm("expr")}:      newRule(newNTerm("single"), newNTerm("args"), newNTerm("chain")),
	{Term: newTerm("["), NTerm: newNTerm("exprs")}:          newRule(newNTerm("expr"), newTerm(";"), newNTerm("exprs")),
	{Term: newTerm("switch"), NTerm: newNTerm("exprs")}:     newRule(newNTerm("expr"), newTerm(";"), newNTerm("exprs")),
	{Term: newTerm("("), NTerm: newNTerm("exprs")}:          newRule(newNTerm("expr"), newTerm(";"), newNTerm("exprs")),
	{Term: newTerm("number"), NTerm: newNTerm("exprs")}:     newRule(newNTerm("expr"), newTerm(";"), newNTerm("exprs")),
	{Term: newTerm("string"), NTerm: newNTerm("exprs")}:     newRule(newNTerm("expr"), newTerm(";"), newNTerm("exprs")),
	{Term: newTerm("id"), NTerm: newNTerm("exprs")}:         newRule(newNTerm("expr"), newTerm(";"), newNTerm("exprs")),
	{Term: newTerm(";"), NTerm: newNTerm("exprs")}:          newRule(newNTerm("expr"), newTerm(";"), newNTerm("exprs")),
	{Term: newTerm("]"), NTerm: newNTerm("exprs")}:          newRule(newNTerm("expr"), newTerm(";"), newNTerm("exprs")),
	{Term: newTerm(")"), NTerm: newNTerm("exprs")}:          newRule(newNTerm("expr"), newTerm(";"), newNTerm("exprs")),
	{Term: newTerm("id"), NTerm: newNTerm("func")}:          newRule(newTerm("id"), newNTerm("subfunc")),
	{Term: newTerm("id"), NTerm: newNTerm("funcdecl")}:      newRule(newTerm("id"), newNTerm("argdecls"), newTerm("=>"), newNTerm("expr"), newTerm(";")),
	{Term: newTerm("string"), NTerm: newNTerm("import")}:    newRule(newTerm("string"), newTerm("=>"), newTerm("id"), newTerm(";")),
	{Term: newTerm(";"), NTerm: newNTerm("optend")}:         newRule(newTerm(";")),
	{Term: newTerm("]"), NTerm: newNTerm("optend")}:         newRule(newTerm("["), newNTerm("exprs"), newNTerm("optend"), newTerm("]")),
	{Term: newTerm(")"), NTerm: newNTerm("optend")}:         newRule(newTerm("("), newNTerm("exprs"), newNTerm("optend"), newTerm(")")),
	{Term: newTerm("string"), NTerm: newNTerm("script")}:    newRule(newNTerm("decl"), newNTerm("script")),
	{Term: newTerm("id"), NTerm: newNTerm("script")}:        newRule(newNTerm("decl"), newNTerm("script")),
	{Term: newTerm("id"), NTerm: newNTerm("single")}:        newRule(newNTerm("func")),
	{Term: newTerm("["), NTerm: newNTerm("single")}:         newRule(newNTerm("array")),
	{Term: newTerm("switch"), NTerm: newNTerm("single")}:    newRule(newNTerm("switch")),
	{Term: newTerm("("), NTerm: newNTerm("single")}:         newRule(newNTerm("compound")),
	{Term: newTerm("number"), NTerm: newNTerm("single")}:    newRule(newTerm("number")),
	{Term: newTerm("string"), NTerm: newNTerm("single")}:    newRule(newTerm("string")),
	{Term: newTerm("."), NTerm: newNTerm("subfunc")}:        newRule(newTerm("."), newTerm("id")),
	{Term: newTerm("->"), NTerm: newNTerm("subfunc")}:       newRule(newTerm("id"), newNTerm("subfunc")),
	{Term: newTerm("}"), NTerm: newNTerm("subfunc")}:        newRule(newTerm("id"), newNTerm("subfunc")),
	{Term: newTerm("default"), NTerm: newNTerm("subfunc")}:  newRule(newTerm("id"), newNTerm("subfunc")),
	{Term: newTerm("switch"), NTerm: newNTerm("subfunc")}:   newRule(newTerm("id"), newNTerm("subfunc")),
	{Term: newTerm(";"), NTerm: newNTerm("subfunc")}:        newRule(newTerm("id"), newNTerm("subfunc")),
	{Term: newTerm("number"), NTerm: newNTerm("subfunc")}:   newRule(newTerm("id"), newNTerm("subfunc")),
	{Term: newTerm("string"), NTerm: newNTerm("subfunc")}:   newRule(newTerm("id"), newNTerm("subfunc")),
	{Term: newTerm("["), NTerm: newNTerm("subfunc")}:        newRule(newTerm("id"), newNTerm("subfunc")),
	{Term: newTerm("=>"), NTerm: newNTerm("subfunc")}:       newRule(newTerm("id"), newNTerm("subfunc")),
	{Term: newTerm("id"), NTerm: newNTerm("subfunc")}:       newRule(newTerm("id"), newNTerm("subfunc")),
	{Term: newTerm("{"), NTerm: newNTerm("subfunc")}:        newRule(newTerm("id"), newNTerm("subfunc")),
	{Term: newTerm("("), NTerm: newNTerm("subfunc")}:        newRule(newTerm("id"), newNTerm("subfunc")),
	{Term: newTerm("switch"), NTerm: newNTerm("switch")}:    newRule(newTerm("switch"), newNTerm("expr"), newTerm("{"), newNTerm("switches"), newTerm("}")),
	{Term: newTerm("string"), NTerm: newNTerm("switches")}:  newRule(newNTerm("expr"), newTerm("=>"), newNTerm("expr"), newNTerm("switches")),
	{Term: newTerm("default"), NTerm: newNTerm("switches")}: newRule(newTerm("default"), newTerm("=>"), newNTerm("expr"), newNTerm("switches")),
	{Term: newTerm("id"), NTerm: newNTerm("switches")}:      newRule(newNTerm("expr"), newTerm("=>"), newNTerm("expr"), newNTerm("switches")),
	{Term: newTerm("["), NTerm: newNTerm("switches")}:       newRule(newNTerm("expr"), newTerm("=>"), newNTerm("expr"), newNTerm("switches")),
	{Term: newTerm("switch"), NTerm: newNTerm("switches")}:  newRule(newNTerm("expr"), newTerm("=>"), newNTerm("expr"), newNTerm("switches")),
	{Term: newTerm("("), NTerm: newNTerm("switches")}:       newRule(newNTerm("expr"), newTerm("=>"), newNTerm("expr"), newNTerm("switches")),
	{Term: newTerm("number"), NTerm: newNTerm("switches")}:  newRule(newNTerm("expr"), newTerm("=>"), newNTerm("expr"), newNTerm("switches")),
	{Term: newTerm("}"), NTerm: newNTerm("switches")}:       newRule(newNTerm("expr"), newTerm("=>"), newNTerm("expr"), newNTerm("switches")),
}
<|endoftext|>