<commit_before>package main

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/errata-ai/vale/action"
	"github.com/errata-ai/vale/check"
	"github.com/errata-ai/vale/core"
	"github.com/errata-ai/vale/lint"
	"github.com/errata-ai/vale/ui"
	"github.com/urfave/cli"
)

// version is set during the release build process.
var version = "master"

func main() {
	var glob string
	var path string
	var minAlertLevel string
	var backup bool
	var local bool

	config := core.NewConfig()
	app := cli.NewApp()
	app.Name = "vale"
	app.Usage = "A command-line linter for prose."
	app.Version = version
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:        "token",
			Destination: &config.AuthToken,
			Hidden:      true,
		},
		cli.StringFlag{
			Name:        "glob",
			Value:       "*",
			Usage:       `a glob pattern (e.g., --glob='*.{md,txt}')`,
			Destination: &glob,
		},
		cli.StringFlag{
			Name:        "config",
			Usage:       `a file path (e.g., --config='some/file/path/.vale.ini')`,
			Destination: &path,
		},
		cli.StringFlag{
			Name:        "minAlertLevel",
			Usage:       `The lowest alert level to display`,
			Destination: &minAlertLevel,
		},
		cli.StringFlag{
			Name:        "output",
			Value:       "CLI",
			Usage:       `output style ("line" or "JSON")`,
			Destination: &config.Output,
		},
		cli.StringFlag{
			Name:        "ext",
			Value:       ".txt",
			Usage:       `extension to associate with stdin`,
			Destination: &config.InExt,
		},
		cli.BoolFlag{
			Name:        "mode-compat",
			Usage:       `Respect local Vale configurations`,
			Destination: &backup,
			Hidden:      true,
		},
		cli.BoolFlag{
			Name:        "mode-rev-compat",
			Usage:       `Treat --config as local`,
			Destination: &local,
			Hidden:      true,
		},
		cli.BoolFlag{
			Name:        "no-wrap",
			Usage:       "don't wrap CLI output",
			Destination: &config.Wrap,
		},
		cli.BoolFlag{
			Name:        "no-exit",
			Usage:       "don't return a nonzero exit code on lint errors",
			Destination: &config.NoExit,
		},
		cli.BoolFlag{
			Name:        "sort",
			Usage:       "sort files by their name in output",
			Destination: &config.Sorted,
		},
		cli.BoolFlag{
			Name:        "debug",
			Usage:       "print debugging information to stdout",
			Destination: &config.Debug,
		},
		cli.BoolFlag{
			Name:        "normalize",
			Usage:       "replace each path separator with a slash ('/')",
			Destination: &config.Normalize,
		},
		cli.BoolFlag{
			Name:        "ignore-syntax",
			Usage:       "lint all files line-by-line",
			Destination: &config.Simple,
		},
		cli.BoolFlag{
			Name:        "relative",
			Usage:       "return relative paths",
			Destination: &config.Relative,
		},
	}

	app.Commands = []cli.Command{
		{
			Name:    "ls-config",
			Aliases: []string{"dc"},
			Usage:   "List the current configuration options",
			Action: func(c *cli.Context) error {
				config, _ = core.LoadConfig(
					config, path, minAlertLevel, backup, local)
				return action.ListConfig(config)
			},
		},
		{
			Name:  "new-rule",
			Usage: "Generates a template for the given extension point",
			Action: func(c *cli.Context) error {
				return action.GetTemplate(c.Args().First())
			},
		},
		{
			Name:  "new-project",
			Usage: "Creates a vocabulary directory for the new project",
			Action: func(c *cli.Context) error {
				config, _ = core.LoadConfig(
					config, path, minAlertLevel, backup, local)
				return action.AddProject(config, c.Args().First())
			},
			Hidden: true,
		},
		{
			Name:  "remove-project",
			Usage: "Deletes an existing vocabulary directory",
			Action: func(c *cli.Context) error {
				config, _ = core.LoadConfig(
					config, path, minAlertLevel, backup, local)
				return action.RemoveProject(config, c.Args().First())
			},
			Hidden: true,
		},
		{
			Name:  "edit-project",
			Usage: "Renames a project from the current StylesPath.",
			Action: func(c *cli.Context) error {
				config, _ = core.LoadConfig(
					config, path, minAlertLevel, backup, local)
				return action.EditProject(config, c.Args())
			},
			Hidden: true,
		},
		{
			Name:  "ls-projects",
			Usage: "List all current projects",
			Action: func(c *cli.Context) error {
				config, _ = core.LoadConfig(
					config, path, minAlertLevel, backup, local)
				return action.ListDir(config, "Vocab")
			},
			Hidden: true,
		},
		{
			Name:  "get-vocab",
			Usage: "Get a vocab file for a project",
			Action: func(c *cli.Context) error {
				config, _ = core.LoadConfig(
					config, path, minAlertLevel, backup, local)
				return action.GetVocab(config, c.Args())
			},
			Hidden: true,
		},
		{
			Name:  "update-vocab",
			Usage: "Update a vocab file for the given project",
			Action: func(c *cli.Context) error {
				config, _ = core.LoadConfig(
					config, path, minAlertLevel, backup, local)
				return action.UpdateVocab(config, c.Args())
			},
			Hidden: true,
		},
		{
			Name:  "ls-styles",
			Usage: "List all installed styles",
			Action: func(c *cli.Context) error {
				config, _ = core.LoadConfig(
					config, path, minAlertLevel, backup, local)
				return action.ListDir(config, "")
			},
			Hidden: true,
		},
		{
			Name:  "ls-library",
			Usage: "List all available styles",
			Action: func(c *cli.Context) error {
				config, _ = core.LoadConfig(
					config, path, minAlertLevel, backup, local)
				return action.GetLibrary(config)
			},
			Hidden: true,
		},
		{
			Name:  "install",
			Usage: "Install the given style",
			Action: func(c *cli.Context) error {
				config, _ = core.LoadConfig(
					config, path, minAlertLevel, backup, local)
				return action.InstallStyle(config, c.Args())
			},
			Hidden: true,
		},
		{
			Name:  "fetch",
			Usage: "Fetch an external (compressed) resource.",
			Action: func(c *cli.Context) error {
				config, _ = core.LoadConfig(
					config, path, minAlertLevel, backup, local)
				return action.FetchAddon(config, c.Args())
			},
			Hidden: true,
		},
		{
			Name:  "ls-addons",
			Usage: "List all available addons",
			Action: func(c *cli.Context) error {
				config, _ = core.LoadConfig(
					config, path, minAlertLevel, backup, local)
				return action.GetAddons(config)
			},
			Hidden: true,
		},
		{
			Name:  "start-addons",
			Usage: "Start all installed addons",
			Action: func(c *cli.Context) error {
				config, _ = core.LoadConfig(
					config, path, minAlertLevel, backup, local)
				return action.StartAddons(config)
			},
			Hidden: true,
		},
		{
			Name:  "stop-addons",
			Usage: "Stop all running addons",
			Action: func(c *cli.Context) error {
				config, _ = core.LoadConfig(
					config, path, minAlertLevel, backup, local)
				return action.StopAddons(config, c.Args())
			},
			Hidden: true,
		},
	}

	app.Action = func(c *cli.Context) error {
		var linted []*core.File
		var err error
		var hasAlerts bool

		config, err = core.LoadConfig(
			config, path, minAlertLevel, backup, local)
		if err != nil && config.Output == "CLI" {
			fmt.Println("WARNING: Missing or invalid config file.\n\n" +
				"See https://github.com/errata-ai/vale#usage for " +
				"information about creating a config file.")
			return nil
		} else if c.NArg() > 0 || core.Stat() {
			linter := lint.Linter{
				Config: config, CheckManager: check.NewManager(config)}

			if c.NArg() > 0 {
				if core.LooksLikeStdin(c.Args()[0]) {
					linted, err = linter.LintString(c.Args()[0])
				} else {
					linted, err = linter.Lint(c.Args(), glob)
				}
			} else {
				stdin, _ := ioutil.ReadAll(os.Stdin)
				linted, err = linter.LintString(string(stdin))
			}

			// How should we style the output?
			if config.Output == "line" {
				hasAlerts = ui.PrintLineAlerts(linted, config.Relative)
			} else if config.Output == "JSON" {
				hasAlerts = ui.PrintJSONAlerts(linted)
			} else {
				hasAlerts = ui.PrintVerboseAlerts(linted, config.Wrap)
			}

			// Should return a nonzero vale on errors?
			if err == nil && hasAlerts && !config.NoExit {
				err = errors.New("")
			}
			return err
		} else {
			cli.ShowAppHelp(c)
			return nil
		}
	}

	core.ExeDir, _ = filepath.Abs(filepath.Dir(os.Args[0]))
	if app.Run(os.Args) != nil {
		os.Exit(1)
	} else {
		os.Exit(0)
	}
}
<commit_msg>Update incorrect guidance link<commit_after>package main

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/errata-ai/vale/action"
	"github.com/errata-ai/vale/check"
	"github.com/errata-ai/vale/core"
	"github.com/errata-ai/vale/lint"
	"github.com/errata-ai/vale/ui"
	"github.com/urfave/cli"
)

// version is set during the release build process.
var version = "master"

func main() {
	var glob string
	var path string
	var minAlertLevel string
	var backup bool
	var local bool

	config := core.NewConfig()
	app := cli.NewApp()
	app.Name = "vale"
	app.Usage = "A command-line linter for prose."
	app.Version = version
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:        "token",
			Destination: &config.AuthToken,
			Hidden:      true,
		},
		cli.StringFlag{
			Name:        "glob",
			Value:       "*",
			Usage:       `a glob pattern (e.g., --glob='*.{md,txt}')`,
			Destination: &glob,
		},
		cli.StringFlag{
			Name:        "config",
			Usage:       `a file path (e.g., --config='some/file/path/.vale.ini')`,
			Destination: &path,
		},
		cli.StringFlag{
			Name:        "minAlertLevel",
			Usage:       `The lowest alert level to display`,
			Destination: &minAlertLevel,
		},
		cli.StringFlag{
			Name:        "output",
			Value:       "CLI",
			Usage:       `output style ("line" or "JSON")`,
			Destination: &config.Output,
		},
		cli.StringFlag{
			Name:        "ext",
			Value:       ".txt",
			Usage:       `extension to associate with stdin`,
			Destination: &config.InExt,
		},
		cli.BoolFlag{
			Name:        "mode-compat",
			Usage:       `Respect local Vale configurations`,
			Destination: &backup,
			Hidden:      true,
		},
		cli.BoolFlag{
			Name:        "mode-rev-compat",
			Usage:       `Treat --config as local`,
			Destination: &local,
			Hidden:      true,
		},
		cli.BoolFlag{
			Name:        "no-wrap",
			Usage:       "don't wrap CLI output",
			Destination: &config.Wrap,
		},
		cli.BoolFlag{
			Name:        "no-exit",
			Usage:       "don't return a nonzero exit code on lint errors",
			Destination: &config.NoExit,
		},
		cli.BoolFlag{
			Name:        "sort",
			Usage:       "sort files by their name in output",
			Destination: &config.Sorted,
		},
		cli.BoolFlag{
			Name:        "debug",
			Usage:       "print debugging information to stdout",
			Destination: &config.Debug,
		},
		cli.BoolFlag{
			Name:        "normalize",
			Usage:       "replace each path separator with a slash ('/')",
			Destination: &config.Normalize,
		},
		cli.BoolFlag{
			Name:        "ignore-syntax",
			Usage:       "lint all files line-by-line",
			Destination: &config.Simple,
		},
		cli.BoolFlag{
			Name:        "relative",
			Usage:       "return relative paths",
			Destination: &config.Relative,
		},
	}

	app.Commands = []cli.Command{
		{
			Name:    "ls-config",
			Aliases: []string{"dc"},
			Usage:   "List the current configuration options",
			Action: func(c *cli.Context) error {
				config, _ = core.LoadConfig(
					config, path, minAlertLevel, backup, local)
				return action.ListConfig(config)
			},
		},
		{
			Name:  "new-rule",
			Usage: "Generates a template for the given extension point",
			Action: func(c *cli.Context) error {
				return action.GetTemplate(c.Args().First())
			},
		},
		{
			Name:  "new-project",
			Usage: "Creates a vocabulary directory for the new project",
			Action: func(c *cli.Context) error {
				config, _ = core.LoadConfig(
					config, path, minAlertLevel, backup, local)
				return action.AddProject(config, c.Args().First())
			},
			Hidden: true,
		},
		{
			Name:  "remove-project",
			Usage: "Deletes an existing vocabulary directory",
			Action: func(c *cli.Context) error {
				config, _ = core.LoadConfig(
					config, path, minAlertLevel, backup, local)
				return action.RemoveProject(config, c.Args().First())
			},
			Hidden: true,
		},
		{
			Name:  "edit-project",
			Usage: "Renames a project from the current StylesPath.",
			Action: func(c *cli.Context) error {
				config, _ = core.LoadConfig(
					config, path, minAlertLevel, backup, local)
				return action.EditProject(config, c.Args())
			},
			Hidden: true,
		},
		{
			Name:  "ls-projects",
			Usage: "List all current projects",
			Action: func(c *cli.Context) error {
				config, _ = core.LoadConfig(
					config, path, minAlertLevel, backup, local)
				return action.ListDir(config, "Vocab")
			},
			Hidden: true,
		},
		{
			Name:  "get-vocab",
			Usage: "Get a vocab file for a project",
			Action: func(c *cli.Context) error {
				config, _ = core.LoadConfig(
					config, path, minAlertLevel, backup, local)
				return action.GetVocab(config, c.Args())
			},
			Hidden: true,
		},
		{
			Name:  "update-vocab",
			Usage: "Update a vocab file for the given project",
			Action: func(c *cli.Context) error {
				config, _ = core.LoadConfig(
					config, path, minAlertLevel, backup, local)
				return action.UpdateVocab(config, c.Args())
			},
			Hidden: true,
		},
		{
			Name:  "ls-styles",
			Usage: "List all installed styles",
			Action: func(c *cli.Context) error {
				config, _ = core.LoadConfig(
					config, path, minAlertLevel, backup, local)
				return action.ListDir(config, "")
			},
			Hidden: true,
		},
		{
			Name:  "ls-library",
			Usage: "List all available styles",
			Action: func(c *cli.Context) error {
				config, _ = core.LoadConfig(
					config, path, minAlertLevel, backup, local)
				return action.GetLibrary(config)
			},
			Hidden: true,
		},
		{
			Name:  "install",
			Usage: "Install the given style",
			Action: func(c *cli.Context) error {
				config, _ = core.LoadConfig(
					config, path, minAlertLevel, backup, local)
				return action.InstallStyle(config, c.Args())
			},
			Hidden: true,
		},
		{
			Name:  "fetch",
			Usage: "Fetch an external (compressed) resource.",
			Action: func(c *cli.Context) error {
				config, _ = core.LoadConfig(
					config, path, minAlertLevel, backup, local)
				return action.FetchAddon(config, c.Args())
			},
			Hidden: true,
		},
		{
			Name:  "ls-addons",
			Usage: "List all available addons",
			Action: func(c *cli.Context) error {
				config, _ = core.LoadConfig(
					config, path, minAlertLevel, backup, local)
				return action.GetAddons(config)
			},
			Hidden: true,
		},
		{
			Name:  "start-addons",
			Usage: "Start all installed addons",
			Action: func(c *cli.Context) error {
				config, _ = core.LoadConfig(
					config, path, minAlertLevel, backup, local)
				return action.StartAddons(config)
			},
			Hidden: true,
		},
		{
			Name:  "stop-addons",
			Usage: "Stop all running addons",
			Action: func(c *cli.Context) error {
				config, _ = core.LoadConfig(
					config, path, minAlertLevel, backup, local)
				return action.StopAddons(config, c.Args())
			},
			Hidden: true,
		},
	}

	app.Action = func(c *cli.Context) error {
		var linted []*core.File
		var err error
		var hasAlerts bool

		config, err = core.LoadConfig(
			config, path, minAlertLevel, backup, local)
		if err != nil && config.Output == "CLI" {
			fmt.Println("WARNING: Missing or invalid config file.\n\n" +
				"See https://errata-ai.gitbook.io/vale/#usage for " +
				"information about creating a config file.")
			return nil
		} else if c.NArg() > 0 || core.Stat() {
			linter := lint.Linter{
				Config: config, CheckManager: check.NewManager(config)}

			if c.NArg() > 0 {
				if core.LooksLikeStdin(c.Args()[0]) {
					linted, err = linter.LintString(c.Args()[0])
				} else {
					linted, err = linter.Lint(c.Args(), glob)
				}
			} else {
				stdin, _ := ioutil.ReadAll(os.Stdin)
				linted, err = linter.LintString(string(stdin))
			}

			// How should we style the output?
			if config.Output == "line" {
				hasAlerts = ui.PrintLineAlerts(linted, config.Relative)
			} else if config.Output == "JSON" {
				hasAlerts = ui.PrintJSONAlerts(linted)
			} else {
				hasAlerts = ui.PrintVerboseAlerts(linted, config.Wrap)
			}

			// Should return a nonzero vale on errors?
			if err == nil && hasAlerts && !config.NoExit {
				err = errors.New("")
			}
			return err
		} else {
			cli.ShowAppHelp(c)
			return nil
		}
	}

	core.ExeDir, _ = filepath.Abs(filepath.Dir(os.Args[0]))
	if app.Run(os.Args) != nil {
		os.Exit(1)
	} else {
		os.Exit(0)
	}
}
<|endoftext|>


<commit_before>/**
 * Copyright 2017 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package main

import (
	"crypto/tls"
	"fmt"
	"net/http"
	_ "net/http/pprof"
	"net/url"
	"os"
	"os/signal"
	"time"

	"github.com/go-kit/kit/log/level"
	"github.com/xmidt-org/webpa-common/service/servicecfg"

	"github.com/spf13/pflag"
	"github.com/spf13/viper"
	"github.com/xmidt-org/webpa-common/concurrent"
	"github.com/xmidt-org/webpa-common/logging"
	"github.com/xmidt-org/webpa-common/server"
	"github.com/xmidt-org/webpa-common/webhook"
	"github.com/xmidt-org/webpa-common/webhook/aws"
)

const (
	applicationName    = "caduceus"
	DEFAULT_KEY_ID     = "current"
	applicationVersion = "0.1.5"
)

// caduceus is the driver function for Caduceus.  It performs everything main() would do,
// except for obtaining the command-line arguments (which are passed to it).

func caduceus(arguments []string) int {
	beginCaduceus := time.Now()

	var (
		f = pflag.NewFlagSet(applicationName, pflag.ContinueOnError)
		v = viper.New()

		logger, metricsRegistry, webPA, err = server.Initialize(applicationName, arguments, f, v, Metrics, webhook.Metrics, aws.Metrics)
	)

	if err != nil {
		fmt.Fprintf(os.Stderr, "Unable to initialize Viper environment: %s\n", err)
		return 1
	}

	var (
		infoLog  = logging.Info(logger)
		errorLog = logging.Error(logger)
		debugLog = logging.Debug(logger)
	)

	infoLog.Log("configurationFile", v.ConfigFileUsed())

	caduceusConfig := new(CaduceusConfig)
	err = v.Unmarshal(caduceusConfig)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Unable to unmarshal configuration data into struct: %s\n", err)
		return 1
	}

	tr := &http.Transport{
		TLSClientConfig:       &tls.Config{InsecureSkipVerify: true},
		MaxIdleConnsPerHost:   caduceusConfig.Sender.NumWorkersPerSender,
		ResponseHeaderTimeout: caduceusConfig.Sender.ResponseHeaderTimeout,
		IdleConnTimeout:       caduceusConfig.Sender.IdleConnTimeout,
	}

	caduceusSenderWrapper, err := SenderWrapperFactory{
		NumWorkersPerSender: caduceusConfig.Sender.NumWorkersPerSender,
		QueueSizePerSender:  caduceusConfig.Sender.QueueSizePerSender,
		CutOffPeriod:        caduceusConfig.Sender.CutOffPeriod,
		Linger:              caduceusConfig.Sender.Linger,
		DeliveryRetries:     caduceusConfig.Sender.DeliveryRetries,
		DeliveryInterval:    caduceusConfig.Sender.DeliveryInterval,
		MetricsRegistry:     metricsRegistry,
		Logger:              logger,
		Sender: (&http.Client{
			Transport: tr,
			Timeout:   caduceusConfig.Sender.ClientTimeout,
		}).Do,
	}.New()

	if err != nil {
		fmt.Fprintf(os.Stderr, "Unable to initialize new caduceus sender wrapper: %s\n", err)
		return 1
	}

	serverWrapper := &ServerHandler{
		Logger: logger,
		caduceusHandler: &CaduceusHandler{
			senderWrapper: caduceusSenderWrapper,
			Logger:        logger,
		},
		errorRequests:            metricsRegistry.NewCounter(ErrorRequestBodyCounter),
		emptyRequests:            metricsRegistry.NewCounter(EmptyRequestBodyCounter),
		invalidCount:             metricsRegistry.NewCounter(DropsDueToInvalidPayload),
		incomingQueueDepthMetric: metricsRegistry.NewGauge(IncomingQueueDepth),
		maxOutstanding:           0,
	}

	webhookFactory, err := webhook.NewFactory(v)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error creating new webhook factory: %s\n", err)
		return 1
	}
	webhookRegistry, webhookHandler := webhookFactory.NewRegistryAndHandler(metricsRegistry)
	webhookFactory.SetExternalUpdate(caduceusSenderWrapper.Update)

	primaryHandler, err := NewPrimaryHandler(logger, v, serverWrapper, &webhookRegistry)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Validator error: %v\n", err)
		return 1
	}

	scheme := v.GetString("scheme")
	if len(scheme) < 1 {
		scheme = "https"
	}

	selfURL := &url.URL{
		Scheme: scheme,
		Host:   v.GetString("fqdn") + v.GetString("primary.address"),
	}

	webhookFactory.Initialize(primaryHandler, selfURL, v.GetString("soa.provider"), webhookHandler, logger, metricsRegistry, nil)

	_, runnable, done := webPA.Prepare(logger, nil, metricsRegistry, primaryHandler)

	waitGroup, shutdown, err := concurrent.Execute(runnable)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Unable to start device manager: %s\n", err)
		return 1
	}

	var messageKey = logging.MessageKey()

	if webhookFactory != nil {
		// wait for DNS to propagate before subscribing to SNS
		if err = webhookFactory.DnsReady(); err == nil {
			debugLog.Log(messageKey, "Calling webhookFactory.PrepareAndStart. Server is ready to take on subscription confirmations")
			webhookFactory.PrepareAndStart()
		} else {
			errorLog.Log(messageKey, "Server was not ready within a time constraint. SNS confirmation could not happen",
				logging.ErrorKey(), err)
		}
	}

	//
	// Now, initialize the service discovery infrastructure
	//
	if false == v.IsSet("service") {
		logger.Log(level.Key(), level.InfoValue(), logging.MessageKey(), "no service discovery configured")
	} else {
		e, err := servicecfg.NewEnvironment(logger, v.Sub("service"))
		if err != nil {
			logger.Log(level.Key(), level.ErrorValue(), logging.MessageKey(), "Unable to initialize service discovery environment", logging.ErrorKey(), err)
			return 4
		}

		defer e.Close()
		logger.Log(level.Key(), level.InfoValue(), "configurationFile", v.ConfigFileUsed())
		e.Register()
	}

	// Attempt to obtain the current listener list from current system without having to wait for listener reregistration.
	debugLog.Log(messageKey, "Attempting to obtain current listener list from source", "source",
		v.GetString("start.apiPath"))
	beginObtainList := time.Now()
	startChan := make(chan webhook.Result, 1)
	webhookFactory.Start.GetCurrentSystemsHooks(startChan)
	var webhookStartResults webhook.Result = <-startChan
	if webhookStartResults.Error != nil {
		errorLog.Log(logging.ErrorKey(), webhookStartResults.Error)
	} else {
		// todo: add message
		webhookFactory.SetList(webhook.NewList(webhookStartResults.Hooks))
		caduceusSenderWrapper.Update(webhookStartResults.Hooks)
	}

	debugLog.Log(messageKey, "Current listener retrieval.", "elapsedTime", time.Since(beginObtainList))
	infoLog.Log(messageKey, "Caduceus is up and running!", "elapsedTime", time.Since(beginCaduceus))

	signals := make(chan os.Signal, 10)
	signal.Notify(signals)
	for exit := false; !exit; {
		select {
		case s := <-signals:
			if s != os.Kill && s != os.Interrupt {
				logger.Log(level.Key(), level.InfoValue(), logging.MessageKey(), "ignoring signal", "signal", s)
			} else {
				logger.Log(level.Key(), level.ErrorValue(), logging.MessageKey(), "exiting due to signal", "signal", s)
				exit = true
			}
		case <-done:
			logger.Log(level.Key(), level.ErrorValue(), logging.MessageKey(), "one or more servers exited")
			exit = true
		}
	}

	close(shutdown)
	waitGroup.Wait()

	// shutdown the sender wrapper gently so that all queued messages get serviced
	caduceusSenderWrapper.Shutdown(true)
	return 0
}

func main() {
	os.Exit(caduceus(os.Args))
}
<commit_msg>[skip ci] bump version from 0.1.5 to 0.2.0<commit_after>/**
 * Copyright 2017 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package main

import (
	"crypto/tls"
	"fmt"
	"net/http"
	_ "net/http/pprof"
	"net/url"
	"os"
	"os/signal"
	"time"

	"github.com/go-kit/kit/log/level"
	"github.com/xmidt-org/webpa-common/service/servicecfg"

	"github.com/spf13/pflag"
	"github.com/spf13/viper"
	"github.com/xmidt-org/webpa-common/concurrent"
	"github.com/xmidt-org/webpa-common/logging"
	"github.com/xmidt-org/webpa-common/server"
	"github.com/xmidt-org/webpa-common/webhook"
	"github.com/xmidt-org/webpa-common/webhook/aws"
)

const (
	applicationName    = "caduceus"
	DEFAULT_KEY_ID     = "current"
	applicationVersion = "0.2.0"
)

// caduceus is the driver function for Caduceus.  It performs everything main() would do,
// except for obtaining the command-line arguments (which are passed to it).

func caduceus(arguments []string) int {
	beginCaduceus := time.Now()

	var (
		f = pflag.NewFlagSet(applicationName, pflag.ContinueOnError)
		v = viper.New()

		logger, metricsRegistry, webPA, err = server.Initialize(applicationName, arguments, f, v, Metrics, webhook.Metrics, aws.Metrics)
	)

	if err != nil {
		fmt.Fprintf(os.Stderr, "Unable to initialize Viper environment: %s\n", err)
		return 1
	}

	var (
		infoLog  = logging.Info(logger)
		errorLog = logging.Error(logger)
		debugLog = logging.Debug(logger)
	)

	infoLog.Log("configurationFile", v.ConfigFileUsed())

	caduceusConfig := new(CaduceusConfig)
	err = v.Unmarshal(caduceusConfig)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Unable to unmarshal configuration data into struct: %s\n", err)
		return 1
	}

	tr := &http.Transport{
		TLSClientConfig:       &tls.Config{InsecureSkipVerify: true},
		MaxIdleConnsPerHost:   caduceusConfig.Sender.NumWorkersPerSender,
		ResponseHeaderTimeout: caduceusConfig.Sender.ResponseHeaderTimeout,
		IdleConnTimeout:       caduceusConfig.Sender.IdleConnTimeout,
	}

	caduceusSenderWrapper, err := SenderWrapperFactory{
		NumWorkersPerSender: caduceusConfig.Sender.NumWorkersPerSender,
		QueueSizePerSender:  caduceusConfig.Sender.QueueSizePerSender,
		CutOffPeriod:        caduceusConfig.Sender.CutOffPeriod,
		Linger:              caduceusConfig.Sender.Linger,
		DeliveryRetries:     caduceusConfig.Sender.DeliveryRetries,
		DeliveryInterval:    caduceusConfig.Sender.DeliveryInterval,
		MetricsRegistry:     metricsRegistry,
		Logger:              logger,
		Sender: (&http.Client{
			Transport: tr,
			Timeout:   caduceusConfig.Sender.ClientTimeout,
		}).Do,
	}.New()

	if err != nil {
		fmt.Fprintf(os.Stderr, "Unable to initialize new caduceus sender wrapper: %s\n", err)
		return 1
	}

	serverWrapper := &ServerHandler{
		Logger: logger,
		caduceusHandler: &CaduceusHandler{
			senderWrapper: caduceusSenderWrapper,
			Logger:        logger,
		},
		errorRequests:            metricsRegistry.NewCounter(ErrorRequestBodyCounter),
		emptyRequests:            metricsRegistry.NewCounter(EmptyRequestBodyCounter),
		invalidCount:             metricsRegistry.NewCounter(DropsDueToInvalidPayload),
		incomingQueueDepthMetric: metricsRegistry.NewGauge(IncomingQueueDepth),
		maxOutstanding:           0,
	}

	webhookFactory, err := webhook.NewFactory(v)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error creating new webhook factory: %s\n", err)
		return 1
	}
	webhookRegistry, webhookHandler := webhookFactory.NewRegistryAndHandler(metricsRegistry)
	webhookFactory.SetExternalUpdate(caduceusSenderWrapper.Update)

	primaryHandler, err := NewPrimaryHandler(logger, v, serverWrapper, &webhookRegistry)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Validator error: %v\n", err)
		return 1
	}

	scheme := v.GetString("scheme")
	if len(scheme) < 1 {
		scheme = "https"
	}

	selfURL := &url.URL{
		Scheme: scheme,
		Host:   v.GetString("fqdn") + v.GetString("primary.address"),
	}

	webhookFactory.Initialize(primaryHandler, selfURL, v.GetString("soa.provider"), webhookHandler, logger, metricsRegistry, nil)

	_, runnable, done := webPA.Prepare(logger, nil, metricsRegistry, primaryHandler)

	waitGroup, shutdown, err := concurrent.Execute(runnable)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Unable to start device manager: %s\n", err)
		return 1
	}

	var messageKey = logging.MessageKey()

	if webhookFactory != nil {
		// wait for DNS to propagate before subscribing to SNS
		if err = webhookFactory.DnsReady(); err == nil {
			debugLog.Log(messageKey, "Calling webhookFactory.PrepareAndStart. Server is ready to take on subscription confirmations")
			webhookFactory.PrepareAndStart()
		} else {
			errorLog.Log(messageKey, "Server was not ready within a time constraint. SNS confirmation could not happen",
				logging.ErrorKey(), err)
		}
	}

	//
	// Now, initialize the service discovery infrastructure
	//
	if false == v.IsSet("service") {
		logger.Log(level.Key(), level.InfoValue(), logging.MessageKey(), "no service discovery configured")
	} else {
		e, err := servicecfg.NewEnvironment(logger, v.Sub("service"))
		if err != nil {
			logger.Log(level.Key(), level.ErrorValue(), logging.MessageKey(), "Unable to initialize service discovery environment", logging.ErrorKey(), err)
			return 4
		}

		defer e.Close()
		logger.Log(level.Key(), level.InfoValue(), "configurationFile", v.ConfigFileUsed())
		e.Register()
	}

	// Attempt to obtain the current listener list from current system without having to wait for listener reregistration.
	debugLog.Log(messageKey, "Attempting to obtain current listener list from source", "source",
		v.GetString("start.apiPath"))
	beginObtainList := time.Now()
	startChan := make(chan webhook.Result, 1)
	webhookFactory.Start.GetCurrentSystemsHooks(startChan)
	var webhookStartResults webhook.Result = <-startChan
	if webhookStartResults.Error != nil {
		errorLog.Log(logging.ErrorKey(), webhookStartResults.Error)
	} else {
		// todo: add message
		webhookFactory.SetList(webhook.NewList(webhookStartResults.Hooks))
		caduceusSenderWrapper.Update(webhookStartResults.Hooks)
	}

	debugLog.Log(messageKey, "Current listener retrieval.", "elapsedTime", time.Since(beginObtainList))
	infoLog.Log(messageKey, "Caduceus is up and running!", "elapsedTime", time.Since(beginCaduceus))

	signals := make(chan os.Signal, 10)
	signal.Notify(signals)
	for exit := false; !exit; {
		select {
		case s := <-signals:
			if s != os.Kill && s != os.Interrupt {
				logger.Log(level.Key(), level.InfoValue(), logging.MessageKey(), "ignoring signal", "signal", s)
			} else {
				logger.Log(level.Key(), level.ErrorValue(), logging.MessageKey(), "exiting due to signal", "signal", s)
				exit = true
			}
		case <-done:
			logger.Log(level.Key(), level.ErrorValue(), logging.MessageKey(), "one or more servers exited")
			exit = true
		}
	}

	close(shutdown)
	waitGroup.Wait()

	// shutdown the sender wrapper gently so that all queued messages get serviced
	caduceusSenderWrapper.Shutdown(true)
	return 0
}

func main() {
	os.Exit(caduceus(os.Args))
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"runtime"

	"github.com/ajvb/kala/cmd"

	log "github.com/sirupsen/logrus"
)

func init() {
	log.SetLevel(log.InfoLevel)
}

// The current version of kala
var Version = "0.1"

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())
	cmd.App.Version = Version
	cmd.App.Run(os.Args)
}
<commit_msg>Removed GOMAXPROCS setting as this matches the number of cores as of go v1.5<commit_after>package main

import (
	"os"

	"github.com/ajvb/kala/cmd"

	log "github.com/sirupsen/logrus"
)

func init() {
	log.SetLevel(log.InfoLevel)
}

// The current version of kala
var Version = "0.1"

func main() {
	cmd.App.Version = Version
	cmd.App.Run(os.Args)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"
	"strconv"
	"time"

	"github.com/StackExchange/dnscontrol/commands"
	_ "github.com/StackExchange/dnscontrol/providers/_all"
)

//go:generate go run build/generate/generate.go build/generate/featureMatrix.go

func main() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)
	os.Exit(commands.Run(versionString()))
}

// Version management. 2 Goals:
// 1. Someone who just does "go get" has at least some information.
// 2. If built with build/build.go, more specific build information gets put in.
// Update the number here manually each release, so at least we have a range for go-get people.
var (
	SHA       = ""
	Version   = "0.2.3"
	BuildTime = ""
)

// printVersion prints the version banner.
func versionString() string {
	var version string
	if SHA != "" {
		version = fmt.Sprintf("%s (%s)", Version, SHA)
	} else {
		version = fmt.Sprintf("%s-dev", Version) //no SHA. '0.x.y-dev' indicates it is run from source without build script.
	}
	if BuildTime != "" {
		i, err := strconv.ParseInt(BuildTime, 10, 64)
		if err == nil {
			tm := time.Unix(i, 0)
			version += fmt.Sprintf(" built %s", tm.Format(time.RFC822))
		}
	}
	return fmt.Sprintf("dnscontrol %s", version)
}
<commit_msg>Release v2.4<commit_after>package main

import (
	"fmt"
	"log"
	"os"
	"strconv"
	"time"

	"github.com/StackExchange/dnscontrol/commands"
	_ "github.com/StackExchange/dnscontrol/providers/_all"
)

//go:generate go run build/generate/generate.go build/generate/featureMatrix.go

func main() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)
	os.Exit(commands.Run(versionString()))
}

// Version management. 2 Goals:
// 1. Someone who just does "go get" has at least some information.
// 2. If built with build/build.go, more specific build information gets put in.
// Update the number here manually each release, so at least we have a range for go-get people.
var (
	SHA       = ""
	Version   = "0.2.4"
	BuildTime = ""
)

// printVersion prints the version banner.
func versionString() string {
	var version string
	if SHA != "" {
		version = fmt.Sprintf("%s (%s)", Version, SHA)
	} else {
		version = fmt.Sprintf("%s-dev", Version) //no SHA. '0.x.y-dev' indicates it is run from source without build script.
	}
	if BuildTime != "" {
		i, err := strconv.ParseInt(BuildTime, 10, 64)
		if err == nil {
			tm := time.Unix(i, 0)
			version += fmt.Sprintf(" built %s", tm.Format(time.RFC822))
		}
	}
	return fmt.Sprintf("dnscontrol %s", version)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"flag"
	"github.com/henderjon/omnilogger/counter"
	"github.com/henderjon/omnilogger/shutdown"
	"io"
	"log"
	"net/http"
	"os"
	"sync"
	"time"
)

const (
	Kilobyte        = 1024
	Megabyte        = Kilobyte * Kilobyte
	Gigabyte        = Kilobyte * Megabyte
	defaultCapacity = 64 * Kilobyte
	defaultInterval = 10 * time.Minute
	defaultPrefix   = "log-omnilogs-"
	defaultPerms    = 0644
)

var (
	port          string                      // the port on which to listen
	pswd          string                      // a simple means of authentication
	requestBuffer int                         // the size of the incoming request buffer (channel)
	size          int                         // how many lines per log file
	scale         bool                        // how many bytes per log file
	numWorkers    int                         // how many bytes per log file
	logDir        string                      // the dir for the log file(s)
	help          bool                        // I forgot my options
	wg            sync.WaitGroup              // ensure that our goroutines finish before shut down
	capacity      = defaultCapacity           // the default capacity of the buffers
	closeInterval = defaultInterval           // how often to close our file and open a new one
	byteCount     = counter.NewCounter()      // keep track of how many bytes total have been received
	hitCount      = counter.NewCounter()      // keep track of how many bytes total have been received
	helpLogger    = log.New(os.Stderr, "", 0) // log to stderr without the timestamps
)

func init() {
	flag.StringVar(&port, "port", "8080", "The port used for the server.")
	flag.StringVar(&pswd, "auth", "", "If not empty, this is matched against the Authorization header (e.g. Authorization: Bearer my-password).")
	flag.IntVar(&requestBuffer, "buf", 500, "The size of the incoming request buffer. A zero (0) will disable buffering.")
	flag.IntVar(&numWorkers, "workers", 2, "The number of workers/buffers... most likely doesn't need changing and should probably not exceed the number of CPUs on the machine.")
	flag.IntVar(&size, "size", 64, "The size (in kilobytes) at which to split the log file(s).")
	flag.BoolVar(&scale, "m", false, "If set, -size will be in megabytes.")
	flag.StringVar(&logDir, "dir", "", "A dir to use for log files. The first arg after '--' is used as a filename prefix. (e.g. '% omnilogger -dir /path/to/log-dir -- file-prefix-')")
	flag.BoolVar(&help, "h", false, "Show this message.")
	flag.Parse()

	capacity = size * Kilobyte
	if scale {
		capacity = size * Megabyte
	}
	// if capacity == 0 -> stdout

	if help {
		helpLogger.Println("")
		helpLogger.Println("Omnilogger is an HTTP server that ingests log data from multiple sources to a common destination.")
		helpLogger.Println("")
		flag.PrintDefaults()
		helpLogger.Println("")
		os.Exit(0)
	}
}

func main() {
	workers := make([]*worker, numWorkers)
	inbound := make(chan []byte, requestBuffer)
	shutdownCh := make(shutdown.ShutdownChan)

	for t := 0; t < numWorkers; t += 1 {
		workers[t] = &worker{}
		go workers[t].coalesce(inbound, &nameWriter{logDir, flag.Arg(0)}, shutdownCh)
	}

	go shutdown.Watch(shutdownCh, destructor)

	fs := http.FileServer(http.Dir("public"))
	http.Handle("/", http.StripPrefix("/", fs))
	http.Handle("/log", Adapt(parseRequest(inbound, &wg), parseCustomHeader, checkAuth(pswd), ensurePost(), checkShutdown(shutdownCh)))
	if e := http.ListenAndServe(":"+port, nil); e != nil {
		log.Fatal(e)
	}
}

// destructor is the func that gets called if we catch a shutdown signal. It waits
// for all goroutines to finish and then prints a final status message
func destructor() {
	wg.Wait()
	helpLogger.Printf(".collected %dm from %d hits in %s", byteCount.Current(Megabyte), hitCount.Current(0), byteCount.Since())
}

// worker wraps a bytes.Buffer in order to attach the coalesce func
type worker struct {
	bytes.Buffer
}

// coalesce runs in it's own goroutine and ranges over a channel and writing the
// data to an io.Writer. All our goroutines send data on this channel and this
// func coalesces them in to one stream.
func (w *worker) coalesce(inbound chan []byte, fw io.Writer, shutdownCh chan struct{}) {
	wg.Add(1)
	for {
		select {
		case b := <-inbound: // pull data out of the channel
			if w.Len() > capacity {
				go fw.Write(w.Bytes())
				w.Reset()
			}

			n, _ := w.Write(b)

			byteCount.IncrBy(uint64(n))
			hitCount.IncrBy(uint64(1))

		case <-time.After(closeInterval): // after 10 minutes of inactivity close the file
			go fw.Write(w.Bytes())
			w.Reset()

		case <-shutdownCh: // if we're shutting down, make sure we flush to disk first
			fw.Write(w.Bytes())
			wg.Done()
			return
		}
	}
}
<commit_msg>a test<commit_after>package main

import (
	"bytes"
	"flag"
	"github.com/henderjon/omnilogger/counter"
	"github.com/henderjon/omnilogger/shutdown"
	"io"
	"log"
	"net/http"
	"os"
	"sync"
	"time"
)

const (
	Kilobyte        = 1024
	Megabyte        = Kilobyte * Kilobyte
	Gigabyte        = Kilobyte * Megabyte
	defaultCapacity = 64 * Kilobyte
	defaultInterval = 10 * time.Minute
	defaultPrefix   = "log-omnilogs-"
	defaultPerms    = 0644
)

var (
	port          string                      // the port on which to listen
	pswd          string                      // a simple means of authentication
	requestBuffer int                         // the size of the incoming request buffer (channel)
	size          int                         // how many lines per log file
	scale         bool                        // how many bytes per log file
	numWorkers    int                         // how many bytes per log file
	logDir        string                      // the dir for the log file(s)
	help          bool                        // I forgot my options
	wg            sync.WaitGroup              // ensure that our goroutines finish before shut down
	capacity      = defaultCapacity           // the default capacity of the buffers
	closeInterval = defaultInterval           // how often to close our file and open a new one
	byteCount     = counter.NewCounter()      // keep track of how many bytes total have been received
	hitCount      = counter.NewCounter()      // keep track of how many bytes total have been received
	helpLogger    = log.New(os.Stderr, "", 0) // log to stderr without the timestamps
)

func init() {
	flag.StringVar(&port, "port", "8080", "The port used for the server.")
	flag.StringVar(&pswd, "auth", "", "If not empty, this is matched against the Authorization header (e.g. Authorization: Bearer my-password).")
	flag.IntVar(&requestBuffer, "buf", 500, "The size of the incoming request buffer. A zero (0) will disable buffering.")
	flag.IntVar(&numWorkers, "workers", 2, "The number of workers/buffers... most likely doesn't need changing and should probably not exceed the number of CPUs on the machine.")
	flag.IntVar(&size, "size", 64, "The size (in kilobytes) at which to split the log file(s).")
	flag.BoolVar(&scale, "m", false, "If set, -size will be in megabytes.")
	flag.StringVar(&logDir, "dir", "", "A dir to use for log files. The first arg after '--' is used as a filename prefix. (e.g. '% omnilogger -dir /path/to/log-dir -- file-prefix-')")
	flag.BoolVar(&help, "h", false, "Show this message.")
	flag.Parse()

	capacity = size * Kilobyte
	if scale {
		capacity = size * Megabyte
	}
	// if capacity == 0 -> stdout

	if help {
		helpLogger.Println("")
		helpLogger.Println("Omnilogger is an HTTP server that ingests log data from multiple sources to a common destination.")
		helpLogger.Println("")
		flag.PrintDefaults()
		helpLogger.Println("")
		os.Exit(0)
	}
}

func main() {
	workers := make([]*worker, numWorkers)
	inbound := make(chan []byte, requestBuffer)
	shutdownCh := make(shutdown.ShutdownChan)

	for t := 0; t < numWorkers; t += 1 {
		workers[t] = &worker{}
		// workers[t].Grow(capacity)
		go workers[t].coalesce(inbound, &nameWriter{logDir, flag.Arg(0)}, shutdownCh)
	}

	go shutdown.Watch(shutdownCh, destructor)

	fs := http.FileServer(http.Dir("public"))
	http.Handle("/", http.StripPrefix("/", fs))
	http.Handle("/log", Adapt(parseRequest(inbound, &wg), parseCustomHeader, checkAuth(pswd), ensurePost(), checkShutdown(shutdownCh)))
	if e := http.ListenAndServe(":"+port, nil); e != nil {
		log.Fatal(e)
	}
}

// destructor is the func that gets called if we catch a shutdown signal. It waits
// for all goroutines to finish and then prints a final status message
func destructor() {
	wg.Wait()
	helpLogger.Printf(".collected %dm from %d hits in %s", byteCount.Current(Megabyte), hitCount.Current(0), byteCount.Since())
}

// worker wraps a bytes.Buffer in order to attach the coalesce func
type worker struct {
	bytes.Buffer
}

// coalesce runs in it's own goroutine and ranges over a channel and writing the
// data to an io.Writer. All our goroutines send data on this channel and this
// func coalesces them in to one stream.
func (w *worker) coalesce(inbound chan []byte, fw io.Writer, shutdownCh chan struct{}) {
	wg.Add(1)
	for {
		select {
		case b := <-inbound: // pull data out of the channel
			if w.Len() > capacity {
				go fw.Write(w.Bytes())
				w.Reset()
			}

			n, _ := w.Write(b)

			byteCount.IncrBy(uint64(n))
			hitCount.IncrBy(uint64(1))

		case <-time.After(closeInterval): // after 10 minutes of inactivity close the file
			go fw.Write(w.Bytes())
			w.Reset()

		case <-shutdownCh: // if we're shutting down, make sure we flush to disk first
			fw.Write(w.Bytes())
			wg.Done()
			return
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"unicode"

	"github.com/rjeczalik/notify"

	"bigoh.co.uk/language-tool/formats"
	"bigoh.co.uk/language-tool/replacer"
	"bigoh.co.uk/language-tool/strings"
)

func main() {
	rootFolder := "/Users/neilmoore67/tmp/foo"

	replacer := replacer.NewMakerRegistry(rootFolder)

	runeSets := [][]rune{
		[]rune(strings.Emoji),
		strings.AllRunes(unicode.Katakana),
		strings.AllRunes(unicode.Khmer),
		strings.AllRunes(unicode.Nl),
		strings.AllRunes(unicode.Lo),
		strings.AllRunes(unicode.Sc),
		strings.AllRunes(unicode.Greek),
		strings.AllRunes(unicode.Other_Grapheme_Extend),
		strings.AllRunes(unicode.Avestan),
	}

	formatSet := []formats.Format{
		formats.Folder{},
		formats.Text{},
	}

	for _, runes := range runeSets {
		for _, format := range formatSet {
			replacer.Add(format, strings.NewRandomPicker(10, runes))
		}
	}

	done := make(chan bool)

	c := make(chan notify.EventInfo, 1)
	if err := notify.Watch(rootFolder, c, notify.Rename, notify.Remove); err != nil {
		log.Fatal(err)
	}
	defer notify.Stop(c)
	go func() {
		for {
			select {
			case event := <-c:
				replacer.Replace(event.Path())
			}
		}
	}()

	<-done
}
<commit_msg>Fix problem with events getting lost due to limited buffer<commit_after>package main

import (
	"log"
	"unicode"

	"github.com/rjeczalik/notify"

	"bigoh.co.uk/language-tool/formats"
	"bigoh.co.uk/language-tool/replacer"
	"bigoh.co.uk/language-tool/strings"
)

func main() {
	rootFolder := "/Users/neilmoore67/tmp/foo"

	replacer := replacer.NewMakerRegistry(rootFolder)

	runeSets := [][]rune{
		[]rune(strings.Emoji),
		strings.AllRunes(unicode.Katakana),
		strings.AllRunes(unicode.Khmer),
		strings.AllRunes(unicode.Nl),
		strings.AllRunes(unicode.Lo),
		strings.AllRunes(unicode.Sc),
		strings.AllRunes(unicode.Greek),
		strings.AllRunes(unicode.Other_Grapheme_Extend),
		strings.AllRunes(unicode.Avestan),
	}

	formatSet := []formats.Format{
		formats.Folder{},
		formats.Text{},
	}

	for _, runes := range runeSets {
		for _, format := range formatSet {
			replacer.Add(format, strings.NewRandomPicker(10, runes))
		}
	}

	done := make(chan bool)

	c := make(chan notify.EventInfo, 1000)
	if err := notify.Watch(rootFolder, c, notify.Rename, notify.Remove); err != nil {
		log.Fatal(err)
	}
	defer notify.Stop(c)
	go func() {
		for {
			select {
			case event := <-c:
				replacer.Replace(event.Path())
			}
		}
	}()

	<-done
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"os/user"
	"strconv"
	"strings"
)

func main() {
	// Get our user and group list
	un := os.Getenv("USER")
	if un == "" {
		os.Exit(3)
	}
	gl := strings.Split(os.Getenv("GROUP"), ",")
	if len(gl) < 1 {
		os.Exit(4)
	}

	// Get user struct, primary gid, and gid (string) array
	u, err := user.Lookup(un)
	if err != nil {
		os.Exit(2)
	}
	gid, err := strconv.ParseInt(u.Gid, 10, 64)
	if err != nil {
		os.Exit(2)
	}
	gs, err := u.GroupIds()
	if err != nil {
		os.Exit(2)
	}

	// Convert gid strings to gid ints
	var gids []int64
	for _, v := range gs {
		g, err := strconv.ParseInt(v, 10, 64)
		if err == nil {
			gids = append(gids, g)
		}
	}

	// Check each of the supplied group names/gids
	for _, v := range gl {
		ggid, err := strconv.ParseInt(v, 10, 64)
		if err == nil {
			// Group is a gid
			if ggid == gid {
				// gid matches primary group
				os.Exit(0)
			} else {
				for _, v := range gids {
					if ggid == v {
						// gid matches secondary group
						os.Exit(0)
					}
				}
			}
		} else {
			// Group is a group name
			fmt.Printf("v: %s\n", v)
			g, err := user.LookupGroup(v)
			if err != nil {
				// Group is invalid
				continue
			}
			ggid, err := strconv.ParseInt(g.Gid, 10, 64)
			if err != nil {
				// Group doesn't exist or gid isn't numeric
				continue
			}

			if ggid == gid {
				// gid matches primary group
				os.Exit(0)
			} else {
				for _, v := range gids {
					if ggid == v {
						// gid matches secondary group
						os.Exit(0)
					}
				}
			}
		}
	}

	os.Exit(1)
}
<commit_msg>Added extra corner case<commit_after>package main

import (
	"fmt"
	"os"
	"os/user"
	"strconv"
	"strings"
)

func main() {
	// Get our user and group list
	un := os.Getenv("USER")
	if un == "" {
		os.Exit(3)
	}
	gl := strings.Split(os.Getenv("GROUP"), ",")
	if len(gl) == 0 || gl[0] == "" {
		os.Exit(4)
	}

	// Get user struct, primary gid, and gid (string) array
	u, err := user.Lookup(un)
	if err != nil {
		os.Exit(2)
	}
	gid, err := strconv.ParseInt(u.Gid, 10, 64)
	if err != nil {
		os.Exit(2)
	}
	gs, err := u.GroupIds()
	if err != nil {
		os.Exit(2)
	}

	// Convert gid strings to gid ints
	var gids []int64
	for _, v := range gs {
		g, err := strconv.ParseInt(v, 10, 64)
		if err == nil {
			gids = append(gids, g)
		}
	}

	// Check each of the supplied group names/gids
	for _, v := range gl {
		ggid, err := strconv.ParseInt(v, 10, 64)
		if err == nil {
			// Group is a gid
			if ggid == gid {
				// gid matches primary group
				os.Exit(0)
			} else {
				for _, v := range gids {
					if ggid == v {
						// gid matches secondary group
						os.Exit(0)
					}
				}
			}
		} else {
			// Group is a group name
			fmt.Printf("v: %s\n", v)
			g, err := user.LookupGroup(v)
			if err != nil {
				// Group is invalid
				continue
			}
			ggid, err := strconv.ParseInt(g.Gid, 10, 64)
			if err != nil {
				// Group doesn't exist or gid isn't numeric
				continue
			}

			if ggid == gid {
				// gid matches primary group
				os.Exit(0)
			} else {
				for _, v := range gids {
					if ggid == v {
						// gid matches secondary group
						os.Exit(0)
					}
				}
			}
		}
	}

	os.Exit(1)
}
<|endoftext|>


<commit_before>package valida

import "strconv"

func digits(in string, noChars int) ([]int, int, bool) {
	empty := []int{}

	lenIn := len(in)
	if lenIn != noChars {
		return empty, 0, false
	}

	all := make([]int, 0, lenIn)
	zeroes := 0
	for _, char := range in {
		d, err := strconv.Atoi(string(char))
		if err != nil {
			return empty, 0, false
		}
		if d == 0 {
			zeroes++
		}
		all = append(all, d)
	}

	if zeroes == lenIn {
		return empty, 0, false
	}

	digits := all[:len(all)-1]
	last := all[len(all)-1]
	return digits, last, true
}
<commit_msg>add isZero<commit_after>package valida

import "strconv"

func digits(in string, noChars int) ([]int, int, bool) {
	empty := []int{}

	lenIn := len(in)
	if lenIn != noChars {
		return empty, 0, false
	}

	all := make([]int, 0, lenIn)
	for _, char := range in {
		d, err := strconv.Atoi(string(char))
		if err != nil {
			return empty, 0, false
		}
		all = append(all, d)
	}

	if isZero(all) {
		return empty, 0, false
	}

	digits := all[:len(all)-1]
	last := all[len(all)-1]
	return digits, last, true
}

func isZero(all []int) bool {
	n := 0
	for _, d := range all {
		if d == 0 {
			n++
		}
	}
	return n == len(all)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net"
	"os"

	flags "github.com/jessevdk/go-flags"
)

type hostEntry struct {
	hostname string
	ip       net.IP
	// Aliases are read from the /etc/hosts file, but not mapped to Route53
	aliases []string
}

var opts struct {
	File     string    `short:"f" long:"file" description:"Input file in /etc/hosts format" default:"/etc/hosts" value-name:"HOSTFILE"`
	Networks []CIDRNet `long:"network" description:"Filter by CIDR network" value-name:"x.x.x.x/len"`
	Domain   string    `short:"d" long:"domain" description:"Domain to update records in" required:"true"`
}

func parseOpts() {
	parser := flags.NewParser(&opts, flags.Default)

	if _, err := parser.Parse(); err != nil {
		if flagsErr, ok := err.(*flags.Error); ok && flagsErr.Type == flags.ErrHelp {
			os.Exit(0)
		} else {
			os.Exit(1)
		}
	}
}

func compareHosts(hosts []hostEntry, r53hosts []hostEntry) {

}

func main() {
	parseOpts()
	hosts := readHosts(opts.File)
	hosts = filterHosts(hosts, opts.Networks)

	r53 := newRoute53()
	r53_hosts, err := r53.getRoute53Hosts(opts.Domain)
	if err != nil {
		log.Fatal(fmt.Errorf("error when retrieving zones: %v", err))
	}

	compareHosts(hosts, r53_hosts)
}
<commit_msg>Rename r53_hosts to r53Hosts<commit_after>package main

import (
	"fmt"
	"log"
	"net"
	"os"

	flags "github.com/jessevdk/go-flags"
)

type hostEntry struct {
	hostname string
	ip       net.IP
	// Aliases are read from the /etc/hosts file, but not mapped to Route53
	aliases []string
}

var opts struct {
	File     string    `short:"f" long:"file" description:"Input file in /etc/hosts format" default:"/etc/hosts" value-name:"HOSTFILE"`
	Networks []CIDRNet `long:"network" description:"Filter by CIDR network" value-name:"x.x.x.x/len"`
	Domain   string    `short:"d" long:"domain" description:"Domain to update records in" required:"true"`
}

func parseOpts() {
	parser := flags.NewParser(&opts, flags.Default)

	if _, err := parser.Parse(); err != nil {
		if flagsErr, ok := err.(*flags.Error); ok && flagsErr.Type == flags.ErrHelp {
			os.Exit(0)
		} else {
			os.Exit(1)
		}
	}
}

func compareHosts(hosts []hostEntry, r53hosts []hostEntry) {

}

func main() {
	parseOpts()
	hosts := readHosts(opts.File)
	hosts = filterHosts(hosts, opts.Networks)

	r53 := newRoute53()
	r53Hosts, err := r53.getHosts(opts.Domain)
	if err != nil {
		log.Fatal(fmt.Errorf("error when retrieving zones: %v", err))
	}
	r53Hosts = filterHosts(r53Hosts, opts.Networks)
}
<|endoftext|>


<commit_before>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// A simple drum machine app.
//
// Note: This demo is an early preview of Go 1.5. In order to build this
// program as an Android APK using the gomobile tool, you need to install
// Go 1.5 from the source.
//
// Clone the source from the tip under $HOME/go directory. On Windows,
// you may like to clone the repo to your user folder, %USERPROFILE%\go.
//
//   $ git clone https://go.googlesource.com/go $HOME/go
//
// Go 1.5 requires Go 1.4. Read more about this requirement at
// http://golang.org/s/go15bootstrap.
// Set GOROOT_BOOTSTRAP to the GOROOT of your existing 1.4 installation or
// follow the steps below to checkout go1.4 from the source and build.
//
//   $ git clone https://go.googlesource.com/go $HOME/go1.4
//   $ cd $HOME/go1.4
//   $ git checkout go1.4.1
//   $ cd src && ./make.bash
//
// If you clone Go 1.4 to a different destination, set GOROOT_BOOTSTRAP
// environmental variable accordingly.
//
// Build Go 1.5 and add Go 1.5 bin to your path.
//
//   $ cd $HOME/go/src && ./make.bash
//   $ export PATH=$PATH:$HOME/go/bin
//
// Set a GOPATH if no GOPATH is set, add $GOPATH/bin to your path.
//
//   $ export GOPATH=$HOME
//   $ export PATH=$PATH:$GOPATH/bin
//
// Get the gomobile tool and initialize.
//
//   $ go get golang.org/x/mobile/cmd/gomobile
//   $ gomobile init
//
// It may take a while to initialize gomobile, please wait.
//
// Get the drum machine example and use gomobile to build or install it on your device.
//
//   $ go get -d github.com/rakyll/drummachine
//   $ gomobile build github.com/rakyll/drummachine # will build an APK
//
//   # plug your Android device to your computer or start an Android emulator.
//   # if you have adb installed on your machine, use gomobile install to
//   # build and deploy the APK to an Android target.
//   $ gomobile install github.com/rakyll/drummachine
//
// Switch to your device or emulator to start the Drum Machine application from
// the launcher.
// You can also run the application on your desktop by running the command
// below. (Note: It currently doesn't work on Windows.)
//   $ go install github.com/rakyll/drummachine && drummachine
package main

import (
	"fmt"
	"image"
	"io"
	"log"
	"time"

	_ "image/jpeg"

	"golang.org/x/mobile/app"
	"golang.org/x/mobile/audio"
	"golang.org/x/mobile/event"
	"golang.org/x/mobile/f32"
	"golang.org/x/mobile/gl"
	"golang.org/x/mobile/sprite"
	"golang.org/x/mobile/sprite/clock"
	"golang.org/x/mobile/sprite/glsprite"
)

var (
	startClock = time.Now()
	lastClock  = clock.Time(-1)
	eng        = glsprite.Engine()

	texs []sprite.SubTex

	board *sprite.Node
)

var (
	buttons [4][4]bool
	pattern [16][16]bool

	samples [16]io.Closer
	players [16]*audio.Player
)

func main() {
	// TODO(jbd): Handle touch to turn on/off the beats.
	app.Run(app.Callbacks{
		Start: start,
		Stop:  stop,
		Draw:  draw,
		Touch: touch,
	})
}

// TODO(jbd): Add multitouch.

func touch(t event.Touch) {
	x, y := float32(t.Loc.X), float32(t.Loc.Y)
	i := int((x - offsetX) / (buttonW + 10))
	j := int((y - offsetY) / (buttonH + 10))
	if i < 0 || i > 3 || j < 0 || j > 3 {
		return
	}
	if t.Type == event.TouchStart {
		buttons[i][j] = true
	}
	go func() {
		time.Sleep(400 * time.Millisecond)
		buttons[i][j] = false
	}()
}

func start() {
	for i := 0; i < len(samples); i++ {
		src, err := app.Open(fmt.Sprintf("sample%d.wav", i))
		if err != nil {
			log.Fatal(err)
		}
		samples[i] = src
		p, err := audio.NewPlayer(src, 0, 0)
		if err != nil {
			log.Fatal(err)
		}
		players[i] = p
	}

	// player goroutine
	go func() {
		for {
			for i := 0; i < 4; i++ {
				for j := 0; j < 4; j++ {
					if buttons[i][j] {
						players[i*4+j].Play()
					}
				}
			}
			// bpm=140
			time.Sleep(time.Minute / 140)
		}
	}()
}

func stop() {
	for _, p := range players {
		p.Close()
	}
	for _, s := range samples {
		s.Close()
	}
}

// TODO(jbd): Dynamically calculate the width and the height
// depending on the size of the screen.
const (
	offsetX = 20
	offsetY = 20
	buttonW = 50
	buttonH = 50
)

func draw() {
	if texs == nil {
		texs = loadTextures()
	}

	now := clock.Time(time.Since(startClock) * 60 / time.Second)
	if now == lastClock {
		// TODO: figure out how to limit draw callbacks to 60Hz instead of
		// burning the CPU as fast as possible.
		// TODO: (relatedly??) sync to vblank?
		return
	}
	lastClock = now
	gl.ClearColor(1, 1, 1, 1)
	gl.Clear(gl.COLOR_BUFFER_BIT)

	cfg := app.GetConfig()
	board = &sprite.Node{}
	eng.Register(board)
	eng.SetTransform(board, f32.Affine{
		{1, 0, 0},
		{0, 1, 0},
	})

	n := newNode()
	eng.SetSubTex(n, texs[texBG])
	eng.SetTransform(n, f32.Affine{
		{float32(cfg.Width), 0, 0},
		{0, float32(cfg.Height), 0},
	})
	for i := 0; i < 4; i++ {
		for j := 0; j < 4; j++ {
			drawButton(i, j)
		}
	}

	drawBrandModel()
	eng.Render(board, now)
}

func drawButton(i, j int) {
	n := newNode()
	if buttons[i][j] {
		eng.SetSubTex(n, texs[texButtonOn])
	} else {
		eng.SetSubTex(n, texs[texButtonOff])
	}
	eng.SetTransform(n, f32.Affine{
		{buttonW, 0, float32(offsetX + i*(buttonW+10))},
		{0, buttonH, float32(offsetY + j*(buttonW+10))},
	})
}

func drawBrandModel() {
	n := newNode()
	eng.SetSubTex(n, texs[texBrand])
	eng.SetTransform(n, f32.Affine{
		{113, 0, offsetX},
		{0, 44, 260},
	})
}

func newNode() *sprite.Node {
	n := &sprite.Node{}
	eng.Register(n)
	board.AppendChild(n)
	return n
}

const (
	texBG = iota
	texButtonOn
	texButtonOff
	texBrand
	texModel
	texOthers
)

func loadTextures() []sprite.SubTex {
	a, err := app.Open("sprite.jpg")
	if err != nil {
		log.Fatal(err)
	}
	defer a.Close()

	img, _, err := image.Decode(a)
	if err != nil {
		log.Fatal(err)
	}
	t, err := eng.LoadTexture(img)
	if err != nil {
		log.Fatal(err)
	}

	return []sprite.SubTex{
		texBG:        sprite.SubTex{t, image.Rect(0, 0, 24, 860)},
		texButtonOff: sprite.SubTex{t, image.Rect(94, 242, 94+150, 242+151)},
		texButtonOn:  sprite.SubTex{t, image.Rect(94, 413, 94+150, 413+151)},
		texBrand:     sprite.SubTex{t, image.Rect(94, 31, 94+227, 31+88)},
		texModel:     sprite.SubTex{t, image.Rect(162, 120, 162+140, 120+90)},
		texOthers:    sprite.SubTex{t, image.Rect(162, 120, 162+140, 120+90)},
	}
}
<commit_msg>add pattern recorder.<commit_after>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// A simple drum machine app.
//
// Note: This demo is an early preview of Go 1.5. In order to build this
// program as an Android APK using the gomobile tool, you need to install
// Go 1.5 from the source.
//
// Clone the source from the tip under $HOME/go directory. On Windows,
// you may like to clone the repo to your user folder, %USERPROFILE%\go.
//
//   $ git clone https://go.googlesource.com/go $HOME/go
//
// Go 1.5 requires Go 1.4. Read more about this requirement at
// http://golang.org/s/go15bootstrap.
// Set GOROOT_BOOTSTRAP to the GOROOT of your existing 1.4 installation or
// follow the steps below to checkout go1.4 from the source and build.
//
//   $ git clone https://go.googlesource.com/go $HOME/go1.4
//   $ cd $HOME/go1.4
//   $ git checkout go1.4.1
//   $ cd src && ./make.bash
//
// If you clone Go 1.4 to a different destination, set GOROOT_BOOTSTRAP
// environmental variable accordingly.
//
// Build Go 1.5 and add Go 1.5 bin to your path.
//
//   $ cd $HOME/go/src && ./make.bash
//   $ export PATH=$PATH:$HOME/go/bin
//
// Set a GOPATH if no GOPATH is set, add $GOPATH/bin to your path.
//
//   $ export GOPATH=$HOME
//   $ export PATH=$PATH:$GOPATH/bin
//
// Get the gomobile tool and initialize.
//
//   $ go get golang.org/x/mobile/cmd/gomobile
//   $ gomobile init
//
// It may take a while to initialize gomobile, please wait.
//
// Get the drum machine example and use gomobile to build or install it on your device.
//
//   $ go get -d github.com/rakyll/drummachine
//   $ gomobile build github.com/rakyll/drummachine # will build an APK
//
//   # plug your Android device to your computer or start an Android emulator.
//   # if you have adb installed on your machine, use gomobile install to
//   # build and deploy the APK to an Android target.
//   $ gomobile install github.com/rakyll/drummachine
//
// Switch to your device or emulator to start the Drum Machine application from
// the launcher.
// You can also run the application on your desktop by running the command
// below. (Note: It currently doesn't work on Windows.)
//   $ go install github.com/rakyll/drummachine && drummachine
package main

import (
	"fmt"
	"image"
	"io"
	"log"
	"time"

	_ "image/jpeg"

	"golang.org/x/mobile/app"
	"golang.org/x/mobile/audio"
	"golang.org/x/mobile/event"
	"golang.org/x/mobile/f32"
	"golang.org/x/mobile/gl"
	"golang.org/x/mobile/sprite"
	"golang.org/x/mobile/sprite/clock"
	"golang.org/x/mobile/sprite/glsprite"
)

var (
	startClock = time.Now()
	lastClock  = clock.Time(-1)
	eng        = glsprite.Engine()

	texs []sprite.SubTex

	board *sprite.Node
)

var (
	buttons [4][4]bool
	pattern [8][16]bool
	index   int

	samples [16]io.Closer
	players [16]*audio.Player
)

func main() {
	// TODO(jbd): Handle touch to turn on/off the beats.
	app.Run(app.Callbacks{
		Start: start,
		Stop:  stop,
		Draw:  draw,
		Touch: touch,
	})
}

// TODO(jbd): Add multitouch.

func touch(t event.Touch) {
	x, y := float32(t.Loc.X), float32(t.Loc.Y)
	i := int((x - offsetX) / (buttonW + 10))
	j := int((y - offsetY) / (buttonH + 10))
	if i < 0 || i > 3 || j < 0 || j > 3 {
		return
	}
	if t.Type == event.TouchStart {
		glow(i, j)
		pattern[index][i*4+j] = true
	}
}

func start() {
	for i := 0; i < len(samples); i++ {
		src, err := app.Open(fmt.Sprintf("sample%d.wav", i))
		if err != nil {
			log.Fatal(err)
		}
		samples[i] = src
		p, err := audio.NewPlayer(src, 0, 0)
		if err != nil {
			log.Fatal(err)
		}
		players[i] = p
	}

	// player goroutine
	go func() {
		for {
			for i := 0; i < 4; i++ {
				for j := 0; j < 4; j++ {
					if pattern[index][i*4+j] {
						glow(i, j)
					}
				}
			}
			// bpm=140
			time.Sleep(time.Minute / 140)
			index = (index + 1) % len(pattern)
		}
	}()
}

// TODO(jbd): racy glow, do we have to care?

func glow(i, j int) {
	p := players[i*4+j]
	buttons[i][j] = true
	p.Seek(0)
	p.Play()
	go func() {
		time.Sleep(300 * time.Millisecond)
		buttons[i][j] = false
	}()
}

func stop() {
	for _, p := range players {
		p.Close()
	}
	for _, s := range samples {
		s.Close()
	}
}

// TODO(jbd): Dynamically calculate the width and the height
// depending on the size of the screen.
const (
	offsetX = 20
	offsetY = 20
	buttonW = 50
	buttonH = 50
)

func draw() {
	if texs == nil {
		texs = loadTextures()
	}

	now := clock.Time(time.Since(startClock) * 60 / time.Second)
	if now == lastClock {
		// TODO: figure out how to limit draw callbacks to 60Hz instead of
		// burning the CPU as fast as possible.
		// TODO: (relatedly??) sync to vblank?
		return
	}
	lastClock = now
	gl.ClearColor(1, 1, 1, 1)
	gl.Clear(gl.COLOR_BUFFER_BIT)

	cfg := app.GetConfig()
	board = &sprite.Node{}
	eng.Register(board)
	eng.SetTransform(board, f32.Affine{
		{1, 0, 0},
		{0, 1, 0},
	})

	n := newNode()
	eng.SetSubTex(n, texs[texBG])
	eng.SetTransform(n, f32.Affine{
		{float32(cfg.Width), 0, 0},
		{0, float32(cfg.Height), 0},
	})
	for i := 0; i < 4; i++ {
		for j := 0; j < 4; j++ {
			drawButton(i, j)
		}
	}

	drawBrandModel()
	eng.Render(board, now)
}

func drawButton(i, j int) {
	n := newNode()
	if buttons[i][j] {
		eng.SetSubTex(n, texs[texButtonOn])
	} else {
		eng.SetSubTex(n, texs[texButtonOff])
	}
	eng.SetTransform(n, f32.Affine{
		{buttonW, 0, float32(offsetX + i*(buttonW+10))},
		{0, buttonH, float32(offsetY + j*(buttonW+10))},
	})
}

func drawBrandModel() {
	n := newNode()
	eng.SetSubTex(n, texs[texBrand])
	eng.SetTransform(n, f32.Affine{
		{113, 0, offsetX},
		{0, 44, 260},
	})
}

func newNode() *sprite.Node {
	n := &sprite.Node{}
	eng.Register(n)
	board.AppendChild(n)
	return n
}

const (
	texBG = iota
	texButtonOn
	texButtonOff
	texBrand
	texModel
	texOthers
)

func loadTextures() []sprite.SubTex {
	a, err := app.Open("sprite.jpg")
	if err != nil {
		log.Fatal(err)
	}
	defer a.Close()

	img, _, err := image.Decode(a)
	if err != nil {
		log.Fatal(err)
	}
	t, err := eng.LoadTexture(img)
	if err != nil {
		log.Fatal(err)
	}

	return []sprite.SubTex{
		texBG:        sprite.SubTex{t, image.Rect(0, 0, 24, 860)},
		texButtonOff: sprite.SubTex{t, image.Rect(94, 242, 94+150, 242+151)},
		texButtonOn:  sprite.SubTex{t, image.Rect(94, 413, 94+150, 413+151)},
		texBrand:     sprite.SubTex{t, image.Rect(94, 31, 94+227, 31+88)},
		texModel:     sprite.SubTex{t, image.Rect(162, 120, 162+140, 120+90)},
		texOthers:    sprite.SubTex{t, image.Rect(162, 120, 162+140, 120+90)},
	}
}
<|endoftext|>


<commit_before>/*
Package json-store reads/writes data to json flat files

Given a top level directory, `Get`, `Put`, and `Del` json encoded flat files of
a given name.
*/
package jsonstore

import (
	"encoding/json"
	"io/ioutil"
	"os"
)

// default dir/file permission
const PERM = 0755

// holds the name of the directory to which the files are written
type connection struct {
	dir string
}

// NewConnection creates our root data dir
func NewConnection(dir string) (*connection, error) {
	if dir[:len(dir)-1] == "/" {
		dir = dir[:len(dir)-2]
	}

	if err := os.MkdirAll(dir, PERM); err != nil {
		return nil, err
	}

	return &connection{dir}, nil
}

// Get retrieves the contents of the given file and unmarshals it to the given interface
func (c *connection) Get(key string, v interface{}) error {
	contents, err := ioutil.ReadFile(mkkey(c.dir, key))
	if err != nil {
		return err
	}

	err = json.Unmarshal(contents, v)
	if err != nil {
		return err
	}
	return nil
}

// Put marshals and writes the contents of the given interface to the given file
func (c *connection) Put(key string, v interface{}) error {
	contents, err := json.Marshal(v)
	if err != nil {
		return err
	}

	err = ioutil.WriteFile(mkkey(c.dir, key), contents, PERM)
	if err != nil {
		return err
	}
	return nil
}

// Del deletes the given file
func (c *connection) Del(key string) error {
	err := os.Remove(mkkey(c.dir, key))
	if err != nil {
		return err
	}
	return nil
}

// DellAll deletes the top level data dir
func (c *connection) DelAll() error {
	err := os.RemoveAll(c.dir)
	if err != nil {
		return err
	}
	return nil
}

// mkkey creates the full path to the file given the prefix and the key
func mkkey(prefix, key string) string {
	return prefix + "/" + key
}
<commit_msg>export top level connection<commit_after>/*
Package json-store reads/writes data to json flat files

Given a top level directory, `Get`, `Put`, and `Del` json encoded flat files of
a given name.
*/
package jsonstore

import (
	"encoding/json"
	"io/ioutil"
	"os"
)

// default dir/file permission
const PERM = 0755

// holds the name of the directory to which the files are written
type Connection struct {
	dir string
}

// NewConnection creates our root data dir
func NewConnection(dir string) (*Connection, error) {
	if dir[:len(dir)-1] == "/" {
		dir = dir[:len(dir)-2]
	}

	if err := os.MkdirAll(dir, PERM); err != nil {
		return nil, err
	}

	return &Connection{dir}, nil
}

// Get retrieves the contents of the given file and unmarshals it to the given interface
func (c *Connection) Get(key string, v interface{}) error {
	contents, err := ioutil.ReadFile(mkkey(c.dir, key))
	if err != nil {
		return err
	}

	err = json.Unmarshal(contents, v)
	if err != nil {
		return err
	}
	return nil
}

// Put marshals and writes the contents of the given interface to the given file
func (c *Connection) Put(key string, v interface{}) error {
	contents, err := json.Marshal(v)
	if err != nil {
		return err
	}

	err = ioutil.WriteFile(mkkey(c.dir, key), contents, PERM)
	if err != nil {
		return err
	}
	return nil
}

// Del deletes the given file
func (c *Connection) Del(key string) error {
	err := os.Remove(mkkey(c.dir, key))
	if err != nil {
		return err
	}
	return nil
}

// DellAll deletes the top level data dir
func (c *Connection) DelAll() error {
	err := os.RemoveAll(c.dir)
	if err != nil {
		return err
	}
	return nil
}

// mkkey creates the full path to the file given the prefix and the key
func mkkey(prefix, key string) string {
	return prefix + "/" + key
}
<|endoftext|>


<commit_before>// Copyright 2017 Martin Planer. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"flag"
	"fmt"
	"net/http"
	"os"
	"os/signal"
	"path/filepath"
	"sync"
	"syscall"
	"time"

	"github.com/martinplaner/felix/internal/felix"
	"github.com/martinplaner/felix/internal/felix/bolt"
	"github.com/martinplaner/felix/internal/felix/html"
	"github.com/martinplaner/felix/internal/felix/rss"
	"golang.org/x/net/context"
)

var (
	// Version is the version string in compliance with Semantic Versioning 2.x
	Version = "undefined"
	// BuildDate is the date and time of build (UTC)
	BuildDate = "undefined"
	// GitSummary is the output of `output of git describe --tags --dirty --always`
	GitSummary = "undefined"
)

var (
	log           = felix.NewLogger()
	source        = felix.NewHTTPSource(http.DefaultClient)
	newItems      = make(chan felix.Item)
	newLinks      = make(chan felix.Link)
	filteredItems = make(chan felix.Item)
	filteredLinks = make(chan felix.Link)

	// TODO: remove this hack
	titles []string
)

func main() {
	printVersion()

	configfile := flag.String("config", "config.yml", "location of the config file")
	datadir := flag.String("datadir", ".", "dir for auxilliary data")
	flag.Parse()

	// Initialize config and shared components

	config, err := felix.ConfigFromFile(*configfile)
	if err != nil {
		log.Fatal("could not read config file", "configfile", *configfile)
	}
	log.Info("read config from file.", "configfile", *configfile)

	datastorefile := filepath.Join(*datadir, "felix.db")
	db, err := bolt.NewDatastore(datastorefile)
	if err != nil {
		log.Fatal("could not create datastore", "file", datastorefile)
	} else {
		log.Info("initialized datastore", "datastorefile", datastorefile)
	}
	defer db.Close()

	// Configure fetchers and filters

	feedFetchers := initFeedFetchers(config, db)
	itemFilters := initItemFilters(config)
	linkFilters := initLinkFilters(config)

	quit := make(chan struct{})
	var wgFeeds sync.WaitGroup
	var wgItems sync.WaitGroup

	sig := make(chan os.Signal)
	signal.Notify(sig, os.Interrupt, syscall.SIGTERM)

	// Start up components

	for _, f := range feedFetchers {
		log.Info("starting new feed fetcher")
		wgFeeds.Add(1)
		go func(f *felix.Fetcher) {
			f.Start(quit)
			wgFeeds.Done()
		}(f)
	}

	go felix.FilterItems(newItems, filteredItems, itemFilters...)
	go runPageFetchers(config, db, &wgItems)
	go felix.FilterLinks(newLinks, filteredLinks, linkFilters...)

	wgFeeds.Add(1)
	go func() {
		periodicCleanup(db, config.CleanupInterval, config.CleanupMaxAge, quit)
		wgFeeds.Done()
	}()

	http.Handle("/", felix.FeedHandler(db))
	http.Handle("/filters", felix.StringHandler(felix.FilterString(itemFilters, linkFilters)))

	// TODO: make host and port configurable
	server := &http.Server{Addr: fmt.Sprintf("%s:%d", "", 6554)}

	go func() {
		log.Info("starting http server", "addr", server.Addr)
		if err := server.ListenAndServe(); err != http.ErrServerClosed {
			log.Error("http server error", "err", err)
		}
	}()

	// Shutdown handler
	go func() {
		<-sig
		log.Info("got shutdown signal, finishing up")
		close(quit)
		wgFeeds.Wait()
		close(newItems)
		wgItems.Wait()
		close(newLinks)
	}()

	// Insert filtered links into datastore
	for link := range filteredLinks {
		log.Info("found new link", "url", link.URL)
		if _, err := db.StoreLink(link); err != nil {
			log.Error("could not store link", "err", err, "link", link)
		}
	}

	if err := server.Shutdown(context.Background()); err != nil {
		log.Error("could not shutdown http server", "err", err)
	} else {
		log.Info("stopped http server")
	}

	log.Info("shutdown complete")
}

// run periodic datastore cleanup routine to remove entries older than maxAge
func periodicCleanup(db felix.Datastore, interval time.Duration, maxAge time.Duration, quit <-chan struct{}) {
L:
	for {
		select {
		case <-time.After(interval):
			if err := db.Cleanup(maxAge); err != nil {
				log.Error("could not cleanup datastore", "err", err)
			}
		case <-quit:
			break L
		}
	}
}

func initFeedFetchers(config felix.Config, data felix.Datastore) []*felix.Fetcher {
	var feedFetchers []*felix.Fetcher
	for _, fc := range config.Feeds {
		switch fc.Type {

		case "rss":
			fetchInterval := fc.FetchInterval
			if fetchInterval == 0 {
				fetchInterval = config.FetchInterval
			}

			nextFetch := felix.PeriodicAttempter(data, fetchInterval)
			f := felix.NewFetcher(fc.URL, source, rss.ItemScanner, nextFetch, newItems, newLinks)
			f.SetLogger(log)
			feedFetchers = append(feedFetchers, f)

		default:
			log.Fatal("unknown feed type", "type", fc.Type)
		}
	}
	return feedFetchers
}

func initItemFilters(config felix.Config) []felix.ItemFilter {
	var itemFilters []felix.ItemFilter
	for _, f := range config.ItemFilters {
		switch f.Type {

		case "title":
			var fc felix.ItemTitleFilterConfig
			if err := f.Unmarshal(&fc); err != nil {
				log.Fatal("could not decode filter config", "err", err, "type", f.Type)
			}

			itemFilters = append(itemFilters, felix.ItemTitleFilter(fc.Titles...))
			titles = fc.Titles

		default:
			log.Fatal("unsupported item filter type", "type", f.Type)
		}
	}
	return itemFilters
}

func initLinkFilters(config felix.Config) []felix.LinkFilter {
	var linkFilters []felix.LinkFilter
	for _, f := range config.LinkFilters {
		switch f.Type {

		case "domain":
			var fc felix.LinkDomainFilterConfig
			if err := f.Unmarshal(&fc); err != nil {
				log.Fatal("could not decode filter config", "err", err, "type", f.Type)
			}

			linkFilters = append(linkFilters, felix.LinkDomainFilter(fc.Domains...))

		case "regex":
			var fc felix.LinkURLRegexFilterConfig
			if err := f.Unmarshal(&fc); err != nil {
				log.Fatal("could not decode filter config", "err", err, "type", f.Type)
			}

			lurf, err := felix.LinkURLRegexFilter(fc.Exprs...)
			if err != nil {
				log.Fatal("could not create filter", "err", err, "type", f.Type)
			}

			linkFilters = append(linkFilters, lurf)

		case "filenameastitle":
			var fc felix.LinkFilenameAsTitleFilterConfig
			if err := f.Unmarshal(&fc); err != nil {
				log.Fatal("could not decode filter config", "err", err, "type", f.Type)
			}

			linkFilters = append(linkFilters, felix.LinkFilenameAsTitleFilter(fc.TrimExt))

		default:
			log.Fatal("unsupported link filter type", "type", f.Type)
		}
	}
	return linkFilters
}

// runPageFetchers restarts old page fetchers found in the datastore
// as well as new ones on demand when new items are found
func runPageFetchers(config felix.Config, db felix.Datastore, wg *sync.WaitGroup) {
	// TODO: refactor this mess.. erm.. component -.-
	quit := make(chan struct{})
	// Restore old item fetchers / scrapers
	oldItems, err := db.GetItems(config.CleanupMaxAge)
	if err != nil {
		log.Error("could not get items", "err", err, "cleanupMaxAge", config.CleanupMaxAge)
	} else {
		for _, item := range oldItems {
			// TODO: Make maxTries configurable
			log.Info("restarting item fetcher", "url", item.URL)
			nextFetch := felix.FibAttempter(db, config.FetchInterval, 7)
			f := felix.NewFetcher(item.URL, source, html.LinkScanner, nextFetch, newItems, newLinks)
			f.SetLogger(log)

			wg.Add(1)
			go func() {
				f.Start(quit)
				wg.Done()
			}()
		}
	}

	for item := range filteredItems {
		didExist, err := db.StoreItem(item)

		if err != nil {
			log.Error("could not store item", "err", err, "item", item)
			continue
		}

		if didExist {
			// Item did already exist. Skipping...
			continue
		}

		log.Info("starting new item fetcher", "url", item.URL)
		nextFetch := felix.FibAttempter(db, config.FetchInterval, 7)
		f := felix.NewFetcher(item.URL, source, html.LinkScanner, nextFetch, newItems, newLinks)
		f.SetLogger(log)

		wg.Add(1)
		go func() {
			f.Start(quit)
			wg.Done()
		}()
	}

	close(quit)
}

func printVersion() {
	fmt.Printf("%s version:%s git:%s build:%s\n", os.Args[0], Version, GitSummary, BuildDate)
}
<commit_msg>Removed debug log message<commit_after>// Copyright 2017 Martin Planer. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"flag"
	"fmt"
	"net/http"
	"os"
	"os/signal"
	"path/filepath"
	"sync"
	"syscall"
	"time"

	"github.com/martinplaner/felix/internal/felix"
	"github.com/martinplaner/felix/internal/felix/bolt"
	"github.com/martinplaner/felix/internal/felix/html"
	"github.com/martinplaner/felix/internal/felix/rss"
	"golang.org/x/net/context"
)

var (
	// Version is the version string in compliance with Semantic Versioning 2.x
	Version = "undefined"
	// BuildDate is the date and time of build (UTC)
	BuildDate = "undefined"
	// GitSummary is the output of `output of git describe --tags --dirty --always`
	GitSummary = "undefined"
)

var (
	log           = felix.NewLogger()
	source        = felix.NewHTTPSource(http.DefaultClient)
	newItems      = make(chan felix.Item)
	newLinks      = make(chan felix.Link)
	filteredItems = make(chan felix.Item)
	filteredLinks = make(chan felix.Link)

	// TODO: remove this hack
	titles []string
)

func main() {
	printVersion()

	configfile := flag.String("config", "config.yml", "location of the config file")
	datadir := flag.String("datadir", ".", "dir for auxilliary data")
	flag.Parse()

	// Initialize config and shared components

	config, err := felix.ConfigFromFile(*configfile)
	if err != nil {
		log.Fatal("could not read config file", "configfile", *configfile)
	}
	log.Info("read config from file.", "configfile", *configfile)

	datastorefile := filepath.Join(*datadir, "felix.db")
	db, err := bolt.NewDatastore(datastorefile)
	if err != nil {
		log.Fatal("could not create datastore", "file", datastorefile)
	} else {
		log.Info("initialized datastore", "datastorefile", datastorefile)
	}
	defer db.Close()

	// Configure fetchers and filters

	feedFetchers := initFeedFetchers(config, db)
	itemFilters := initItemFilters(config)
	linkFilters := initLinkFilters(config)

	quit := make(chan struct{})
	var wgFeeds sync.WaitGroup
	var wgItems sync.WaitGroup

	sig := make(chan os.Signal)
	signal.Notify(sig, os.Interrupt, syscall.SIGTERM)

	// Start up components

	for _, f := range feedFetchers {
		wgFeeds.Add(1)
		go func(f *felix.Fetcher) {
			f.Start(quit)
			wgFeeds.Done()
		}(f)
	}

	go felix.FilterItems(newItems, filteredItems, itemFilters...)
	go runPageFetchers(config, db, &wgItems)
	go felix.FilterLinks(newLinks, filteredLinks, linkFilters...)

	wgFeeds.Add(1)
	go func() {
		periodicCleanup(db, config.CleanupInterval, config.CleanupMaxAge, quit)
		wgFeeds.Done()
	}()

	http.Handle("/", felix.FeedHandler(db))
	http.Handle("/filters", felix.StringHandler(felix.FilterString(itemFilters, linkFilters)))

	// TODO: make host and port configurable
	server := &http.Server{Addr: fmt.Sprintf("%s:%d", "", 6554)}

	go func() {
		log.Info("starting http server", "addr", server.Addr)
		if err := server.ListenAndServe(); err != http.ErrServerClosed {
			log.Error("http server error", "err", err)
		}
	}()

	// Shutdown handler
	go func() {
		<-sig
		log.Info("got shutdown signal, finishing up")
		close(quit)
		wgFeeds.Wait()
		close(newItems)
		wgItems.Wait()
		close(newLinks)
	}()

	// Insert filtered links into datastore
	for link := range filteredLinks {
		log.Info("found new link", "url", link.URL)
		if _, err := db.StoreLink(link); err != nil {
			log.Error("could not store link", "err", err, "link", link)
		}
	}

	if err := server.Shutdown(context.Background()); err != nil {
		log.Error("could not shutdown http server", "err", err)
	} else {
		log.Info("stopped http server")
	}

	log.Info("shutdown complete")
}

// run periodic datastore cleanup routine to remove entries older than maxAge
func periodicCleanup(db felix.Datastore, interval time.Duration, maxAge time.Duration, quit <-chan struct{}) {
L:
	for {
		select {
		case <-time.After(interval):
			if err := db.Cleanup(maxAge); err != nil {
				log.Error("could not cleanup datastore", "err", err)
			}
		case <-quit:
			break L
		}
	}
}

func initFeedFetchers(config felix.Config, data felix.Datastore) []*felix.Fetcher {
	var feedFetchers []*felix.Fetcher
	for _, fc := range config.Feeds {
		switch fc.Type {

		case "rss":
			fetchInterval := fc.FetchInterval
			if fetchInterval == 0 {
				fetchInterval = config.FetchInterval
			}

			nextFetch := felix.PeriodicAttempter(data, fetchInterval)
			f := felix.NewFetcher(fc.URL, source, rss.ItemScanner, nextFetch, newItems, newLinks)
			f.SetLogger(log)
			feedFetchers = append(feedFetchers, f)

		default:
			log.Fatal("unknown feed type", "type", fc.Type)
		}
	}
	return feedFetchers
}

func initItemFilters(config felix.Config) []felix.ItemFilter {
	var itemFilters []felix.ItemFilter
	for _, f := range config.ItemFilters {
		switch f.Type {

		case "title":
			var fc felix.ItemTitleFilterConfig
			if err := f.Unmarshal(&fc); err != nil {
				log.Fatal("could not decode filter config", "err", err, "type", f.Type)
			}

			itemFilters = append(itemFilters, felix.ItemTitleFilter(fc.Titles...))
			titles = fc.Titles

		default:
			log.Fatal("unsupported item filter type", "type", f.Type)
		}
	}
	return itemFilters
}

func initLinkFilters(config felix.Config) []felix.LinkFilter {
	var linkFilters []felix.LinkFilter
	for _, f := range config.LinkFilters {
		switch f.Type {

		case "domain":
			var fc felix.LinkDomainFilterConfig
			if err := f.Unmarshal(&fc); err != nil {
				log.Fatal("could not decode filter config", "err", err, "type", f.Type)
			}

			linkFilters = append(linkFilters, felix.LinkDomainFilter(fc.Domains...))

		case "regex":
			var fc felix.LinkURLRegexFilterConfig
			if err := f.Unmarshal(&fc); err != nil {
				log.Fatal("could not decode filter config", "err", err, "type", f.Type)
			}

			lurf, err := felix.LinkURLRegexFilter(fc.Exprs...)
			if err != nil {
				log.Fatal("could not create filter", "err", err, "type", f.Type)
			}

			linkFilters = append(linkFilters, lurf)

		case "filenameastitle":
			var fc felix.LinkFilenameAsTitleFilterConfig
			if err := f.Unmarshal(&fc); err != nil {
				log.Fatal("could not decode filter config", "err", err, "type", f.Type)
			}

			linkFilters = append(linkFilters, felix.LinkFilenameAsTitleFilter(fc.TrimExt))

		default:
			log.Fatal("unsupported link filter type", "type", f.Type)
		}
	}
	return linkFilters
}

// runPageFetchers restarts old page fetchers found in the datastore
// as well as new ones on demand when new items are found
func runPageFetchers(config felix.Config, db felix.Datastore, wg *sync.WaitGroup) {
	// TODO: refactor this mess.. erm.. component -.-
	quit := make(chan struct{})
	// Restore old item fetchers / scrapers
	oldItems, err := db.GetItems(config.CleanupMaxAge)
	if err != nil {
		log.Error("could not get items", "err", err, "cleanupMaxAge", config.CleanupMaxAge)
	} else {
		for _, item := range oldItems {
			// TODO: Make maxTries configurable
			log.Info("restarting item fetcher", "url", item.URL)
			nextFetch := felix.FibAttempter(db, config.FetchInterval, 7)
			f := felix.NewFetcher(item.URL, source, html.LinkScanner, nextFetch, newItems, newLinks)
			f.SetLogger(log)

			wg.Add(1)
			go func() {
				f.Start(quit)
				wg.Done()
			}()
		}
	}

	for item := range filteredItems {
		didExist, err := db.StoreItem(item)

		if err != nil {
			log.Error("could not store item", "err", err, "item", item)
			continue
		}

		if didExist {
			// Item did already exist. Skipping...
			continue
		}

		log.Info("starting new item fetcher", "url", item.URL)
		nextFetch := felix.FibAttempter(db, config.FetchInterval, 7)
		f := felix.NewFetcher(item.URL, source, html.LinkScanner, nextFetch, newItems, newLinks)
		f.SetLogger(log)

		wg.Add(1)
		go func() {
			f.Start(quit)
			wg.Done()
		}()
	}

	close(quit)
}

func printVersion() {
	fmt.Printf("%s version:%s git:%s build:%s\n", os.Args[0], Version, GitSummary, BuildDate)
}
<|endoftext|>