<commit_before>// This is the main package for the `packer` application.

//go:generate go run ./scripts/generate-plugins.go
//go:generate go generate ./common/bootcommand/...
package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"os"
	"path/filepath"
	"runtime"
	"sync"
	"syscall"
	"time"

	"github.com/hashicorp/go-uuid"
	"github.com/hashicorp/packer/command"
	"github.com/hashicorp/packer/packer"
	"github.com/hashicorp/packer/packer/plugin"
	"github.com/hashicorp/packer/version"
	"github.com/mitchellh/cli"
	"github.com/mitchellh/panicwrap"
	"github.com/mitchellh/prefixedio"
)

func main() {
	// Call realMain instead of doing the work here so we can use
	// `defer` statements within the function and have them work properly.
	// (defers aren't called with os.Exit)
	os.Exit(realMain())
}

// realMain is executed from main and returns the exit status to exit with.
func realMain() int {
	var wrapConfig panicwrap.WrapConfig

	if !panicwrap.Wrapped(&wrapConfig) {
		// Generate a UUID for this packer run and pass it to the environment.
		// GenerateUUID always returns a nil error (based on rand.Read) so we'll
		// just ignore it.
		UUID, _ := uuid.GenerateUUID()
		os.Setenv("PACKER_RUN_UUID", UUID)

		// Determine where logs should go in general (requested by the user)
		logWriter, err := logOutput()
		if err != nil {
			fmt.Fprintf(os.Stderr, "Couldn't setup log output: %s", err)
			return 1
		}
		if logWriter == nil {
			logWriter = ioutil.Discard
		}

		packer.LogSecretFilter.SetOutput(logWriter)

		//packer.LogSecrets.

		// Disable logging here
		log.SetOutput(ioutil.Discard)

		// We always send logs to a temporary file that we use in case
		// there is a panic. Otherwise, we delete it.
		logTempFile, err := ioutil.TempFile("", "packer-log")
		if err != nil {
			fmt.Fprintf(os.Stderr, "Couldn't setup logging tempfile: %s", err)
			return 1
		}
		defer os.Remove(logTempFile.Name())
		defer logTempFile.Close()

		// Tell the logger to log to this file
		os.Setenv(EnvLog, "")
		os.Setenv(EnvLogFile, "")

		// Setup the prefixed readers that send data properly to
		// stdout/stderr.
		doneCh := make(chan struct{})
		outR, outW := io.Pipe()
		go copyOutput(outR, doneCh)

		// Enable checkpoint for panic reporting
		if config, _ := loadConfig(); config != nil && !config.DisableCheckpoint {
			packer.CheckpointReporter = packer.NewCheckpointReporter(
				config.DisableCheckpointSignature,
			)
		}

		// Create the configuration for panicwrap and wrap our executable
		wrapConfig.Handler = panicHandler(logTempFile)
		wrapConfig.Writer = io.MultiWriter(logTempFile, &packer.LogSecretFilter)
		wrapConfig.Stdout = outW
		wrapConfig.DetectDuration = 500 * time.Millisecond
		wrapConfig.ForwardSignals = []os.Signal{syscall.SIGTERM}
		exitStatus, err := panicwrap.Wrap(&wrapConfig)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Couldn't start Packer: %s", err)
			return 1
		}

		// If >= 0, we're the parent, so just exit
		if exitStatus >= 0 {
			// Close the stdout writer so that our copy process can finish
			outW.Close()

			// Wait for the output copying to finish
			<-doneCh

			return exitStatus
		}

		// We're the child, so just close the tempfile we made in order to
		// save file handles since the tempfile is only used by the parent.
		logTempFile.Close()
	}

	// Call the real main
	return wrappedMain()
}

// wrappedMain is called only when we're wrapped by panicwrap and
// returns the exit status to exit with.
func wrappedMain() int {
	// If there is no explicit number of Go threads to use, then set it
	if os.Getenv("GOMAXPROCS") == "" {
		runtime.GOMAXPROCS(runtime.NumCPU())
	}

	packer.LogSecretFilter.SetOutput(os.Stderr)
	log.SetOutput(&packer.LogSecretFilter)

	log.Printf("[INFO] Packer version: %s", version.FormattedVersion())
	log.Printf("Packer Target OS/Arch: %s %s", runtime.GOOS, runtime.GOARCH)
	log.Printf("Built with Go Version: %s", runtime.Version())

	inPlugin := os.Getenv(plugin.MagicCookieKey) == plugin.MagicCookieValue

	// Prepare stdin for plugin usage by switching it to a pipe
	// But do not switch to pipe in plugin
	if !inPlugin {
		setupStdin()
	}

	config, err := loadConfig()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error loading configuration: \n\n%s\n", err)
		return 1
	}
	log.Printf("Packer config: %+v", config)

	// Fire off the checkpoint.
	go runCheckpoint(config)
	if !config.DisableCheckpoint {
		packer.CheckpointReporter = packer.NewCheckpointReporter(
			config.DisableCheckpointSignature,
		)
	}

	cacheDir := os.Getenv("PACKER_CACHE_DIR")
	if cacheDir == "" {
		cacheDir = "packer_cache"
	}

	cacheDir, err = filepath.Abs(cacheDir)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error preparing cache directory: \n\n%s\n", err)
		return 1
	}

	log.Printf("Setting cache directory: %s", cacheDir)
	cache := &packer.FileCache{CacheDir: cacheDir}

	// Determine if we're in machine-readable mode by mucking around with
	// the arguments...
	args, machineReadable := extractMachineReadable(os.Args[1:])

	defer plugin.CleanupClients()

	// Setup the UI if we're being machine-readable
	var ui packer.Ui = &packer.BasicUi{
		Reader:      os.Stdin,
		Writer:      os.Stdout,
		ErrorWriter: os.Stdout,
	}
	if machineReadable {
		ui = &packer.MachineReadableUi{
			Writer: os.Stdout,
		}

		// Set this so that we don't get colored output in our machine-
		// readable UI.
		if err := os.Setenv("PACKER_NO_COLOR", "1"); err != nil {
			fmt.Fprintf(os.Stderr, "Packer failed to initialize UI: %s\n", err)
			return 1
		}
	}

	// Create the CLI meta
	CommandMeta = &command.Meta{
		CoreConfig: &packer.CoreConfig{
			Components: packer.ComponentFinder{
				Builder:       config.LoadBuilder,
				Hook:          config.LoadHook,
				PostProcessor: config.LoadPostProcessor,
				Provisioner:   config.LoadProvisioner,
			},
			Version: version.Version,
		},
		Cache: cache,
		Ui:    ui,
	}

	cli := &cli.CLI{
		Args:         args,
		Autocomplete: true,
		Commands:     Commands,
		HelpFunc:     excludeHelpFunc(Commands, []string{"plugin"}),
		HelpWriter:   os.Stdout,
		Name:         "packer",
		Version:      version.Version,
	}

	exitCode, err := cli.Run()
	if !inPlugin {
		if err := packer.CheckpointReporter.Finalize(cli.Subcommand(), exitCode, err); err != nil {
			log.Printf("[WARN] (telemetry) Error finalizing report. This is safe to ignore. %s", err.Error())
		}
	}

	if err != nil {
		fmt.Fprintf(os.Stderr, "Error executing CLI: %s\n", err)
		return 1
	}

	return exitCode
}

// excludeHelpFunc filters commands we don't want to show from the list of
// commands displayed in packer's help text.
func excludeHelpFunc(commands map[string]cli.CommandFactory, exclude []string) cli.HelpFunc {
	// Make search slice into a map so we can use use the `if found` idiom
	// instead of a nested loop.
	var excludes = make(map[string]interface{}, len(exclude))
	for _, item := range exclude {
		excludes[item] = nil
	}

	// Create filtered list of commands
	helpCommands := []string{}
	for command := range commands {
		if _, found := excludes[command]; !found {
			helpCommands = append(helpCommands, command)
		}
	}

	return cli.FilteredHelpFunc(helpCommands, cli.BasicHelpFunc("packer"))
}

// extractMachineReadable checks the args for the machine readable
// flag and returns whether or not it is on. It modifies the args
// to remove this flag.
func extractMachineReadable(args []string) ([]string, bool) {
	for i, arg := range args {
		if arg == "-machine-readable" {
			// We found it. Slice it out.
			result := make([]string, len(args)-1)
			copy(result, args[:i])
			copy(result[i:], args[i+1:])
			return result, true
		}
	}

	return args, false
}

func loadConfig() (*config, error) {
	var config config
	config.PluginMinPort = 10000
	config.PluginMaxPort = 25000
	if err := config.Discover(); err != nil {
		return nil, err
	}

	configFilePath := os.Getenv("PACKER_CONFIG")
	if configFilePath != "" {
		log.Printf("'PACKER_CONFIG' set, loading config from environment.")
	} else {
		var err error
		configFilePath, err = packer.ConfigFile()

		if err != nil {
			log.Printf("Error detecting default config file path: %s", err)
		}
	}

	if configFilePath == "" {
		return &config, nil
	}

	log.Printf("Attempting to open config file: %s", configFilePath)
	f, err := os.Open(configFilePath)
	if err != nil {
		if !os.IsNotExist(err) {
			return nil, err
		}

		log.Printf("[WARN] Config file doesn't exist: %s", configFilePath)
		return &config, nil
	}
	defer f.Close()

	if err := decodeConfig(f, &config); err != nil {
		return nil, err
	}

	return &config, nil
}

// copyOutput uses output prefixes to determine whether data on stdout
// should go to stdout or stderr. This is due to panicwrap using stderr
// as the log and error channel.
func copyOutput(r io.Reader, doneCh chan<- struct{}) {
	defer close(doneCh)

	pr, err := prefixedio.NewReader(r)
	if err != nil {
		panic(err)
	}

	stderrR, err := pr.Prefix(ErrorPrefix)
	if err != nil {
		panic(err)
	}
	stdoutR, err := pr.Prefix(OutputPrefix)
	if err != nil {
		panic(err)
	}
	defaultR, err := pr.Prefix("")
	if err != nil {
		panic(err)
	}

	var wg sync.WaitGroup
	wg.Add(3)
	go func() {
		defer wg.Done()
		io.Copy(os.Stderr, stderrR)
	}()
	go func() {
		defer wg.Done()
		io.Copy(os.Stdout, stdoutR)
	}()
	go func() {
		defer wg.Done()
		io.Copy(os.Stdout, defaultR)
	}()

	wg.Wait()
}

func init() {
	// Seed the random number generator
	rand.Seed(time.Now().UTC().UnixNano())
}
<commit_msg>don't use default panic wrap key<commit_after>// This is the main package for the `packer` application.

//go:generate go run ./scripts/generate-plugins.go
//go:generate go generate ./common/bootcommand/...
package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"os"
	"path/filepath"
	"runtime"
	"sync"
	"syscall"
	"time"

	"github.com/hashicorp/go-uuid"
	"github.com/hashicorp/packer/command"
	"github.com/hashicorp/packer/packer"
	"github.com/hashicorp/packer/packer/plugin"
	"github.com/hashicorp/packer/version"
	"github.com/mitchellh/cli"
	"github.com/mitchellh/panicwrap"
	"github.com/mitchellh/prefixedio"
)

func main() {
	// Call realMain instead of doing the work here so we can use
	// `defer` statements within the function and have them work properly.
	// (defers aren't called with os.Exit)
	os.Exit(realMain())
}

// realMain is executed from main and returns the exit status to exit with.
func realMain() int {
	var wrapConfig panicwrap.WrapConfig
	wrapConfig.CookieKey = "PACKER_WRAP_COOKIE"
	wrapConfig.CookieValue = "49C22B1A-3A93-4C98-97FA-E07D18C787B5"

	if !panicwrap.Wrapped(&wrapConfig) {
		// Generate a UUID for this packer run and pass it to the environment.
		// GenerateUUID always returns a nil error (based on rand.Read) so we'll
		// just ignore it.
		UUID, _ := uuid.GenerateUUID()
		os.Setenv("PACKER_RUN_UUID", UUID)

		// Determine where logs should go in general (requested by the user)
		logWriter, err := logOutput()
		if err != nil {
			fmt.Fprintf(os.Stderr, "Couldn't setup log output: %s", err)
			return 1
		}
		if logWriter == nil {
			logWriter = ioutil.Discard
		}

		packer.LogSecretFilter.SetOutput(logWriter)

		//packer.LogSecrets.

		// Disable logging here
		log.SetOutput(ioutil.Discard)

		// We always send logs to a temporary file that we use in case
		// there is a panic. Otherwise, we delete it.
		logTempFile, err := ioutil.TempFile("", "packer-log")
		if err != nil {
			fmt.Fprintf(os.Stderr, "Couldn't setup logging tempfile: %s", err)
			return 1
		}
		defer os.Remove(logTempFile.Name())
		defer logTempFile.Close()

		// Tell the logger to log to this file
		os.Setenv(EnvLog, "")
		os.Setenv(EnvLogFile, "")

		// Setup the prefixed readers that send data properly to
		// stdout/stderr.
		doneCh := make(chan struct{})
		outR, outW := io.Pipe()
		go copyOutput(outR, doneCh)

		// Enable checkpoint for panic reporting
		if config, _ := loadConfig(); config != nil && !config.DisableCheckpoint {
			packer.CheckpointReporter = packer.NewCheckpointReporter(
				config.DisableCheckpointSignature,
			)
		}

		// Create the configuration for panicwrap and wrap our executable
		wrapConfig.Handler = panicHandler(logTempFile)
		wrapConfig.Writer = io.MultiWriter(logTempFile, &packer.LogSecretFilter)
		wrapConfig.Stdout = outW
		wrapConfig.DetectDuration = 500 * time.Millisecond
		wrapConfig.ForwardSignals = []os.Signal{syscall.SIGTERM}
		exitStatus, err := panicwrap.Wrap(&wrapConfig)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Couldn't start Packer: %s", err)
			return 1
		}

		// If >= 0, we're the parent, so just exit
		if exitStatus >= 0 {
			// Close the stdout writer so that our copy process can finish
			outW.Close()

			// Wait for the output copying to finish
			<-doneCh

			return exitStatus
		}

		// We're the child, so just close the tempfile we made in order to
		// save file handles since the tempfile is only used by the parent.
		logTempFile.Close()
	}

	// Call the real main
	return wrappedMain()
}

// wrappedMain is called only when we're wrapped by panicwrap and
// returns the exit status to exit with.
func wrappedMain() int {
	// If there is no explicit number of Go threads to use, then set it
	if os.Getenv("GOMAXPROCS") == "" {
		runtime.GOMAXPROCS(runtime.NumCPU())
	}

	packer.LogSecretFilter.SetOutput(os.Stderr)
	log.SetOutput(&packer.LogSecretFilter)

	log.Printf("[INFO] Packer version: %s", version.FormattedVersion())
	log.Printf("Packer Target OS/Arch: %s %s", runtime.GOOS, runtime.GOARCH)
	log.Printf("Built with Go Version: %s", runtime.Version())

	inPlugin := os.Getenv(plugin.MagicCookieKey) == plugin.MagicCookieValue

	// Prepare stdin for plugin usage by switching it to a pipe
	// But do not switch to pipe in plugin
	if !inPlugin {
		setupStdin()
	}

	config, err := loadConfig()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error loading configuration: \n\n%s\n", err)
		return 1
	}
	log.Printf("Packer config: %+v", config)

	// Fire off the checkpoint.
	go runCheckpoint(config)
	if !config.DisableCheckpoint {
		packer.CheckpointReporter = packer.NewCheckpointReporter(
			config.DisableCheckpointSignature,
		)
	}

	cacheDir := os.Getenv("PACKER_CACHE_DIR")
	if cacheDir == "" {
		cacheDir = "packer_cache"
	}

	cacheDir, err = filepath.Abs(cacheDir)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error preparing cache directory: \n\n%s\n", err)
		return 1
	}

	log.Printf("Setting cache directory: %s", cacheDir)
	cache := &packer.FileCache{CacheDir: cacheDir}

	// Determine if we're in machine-readable mode by mucking around with
	// the arguments...
	args, machineReadable := extractMachineReadable(os.Args[1:])

	defer plugin.CleanupClients()

	// Setup the UI if we're being machine-readable
	var ui packer.Ui = &packer.BasicUi{
		Reader:      os.Stdin,
		Writer:      os.Stdout,
		ErrorWriter: os.Stdout,
	}
	if machineReadable {
		ui = &packer.MachineReadableUi{
			Writer: os.Stdout,
		}

		// Set this so that we don't get colored output in our machine-
		// readable UI.
		if err := os.Setenv("PACKER_NO_COLOR", "1"); err != nil {
			fmt.Fprintf(os.Stderr, "Packer failed to initialize UI: %s\n", err)
			return 1
		}
	}

	// Create the CLI meta
	CommandMeta = &command.Meta{
		CoreConfig: &packer.CoreConfig{
			Components: packer.ComponentFinder{
				Builder:       config.LoadBuilder,
				Hook:          config.LoadHook,
				PostProcessor: config.LoadPostProcessor,
				Provisioner:   config.LoadProvisioner,
			},
			Version: version.Version,
		},
		Cache: cache,
		Ui:    ui,
	}

	cli := &cli.CLI{
		Args:         args,
		Autocomplete: true,
		Commands:     Commands,
		HelpFunc:     excludeHelpFunc(Commands, []string{"plugin"}),
		HelpWriter:   os.Stdout,
		Name:         "packer",
		Version:      version.Version,
	}

	exitCode, err := cli.Run()
	if !inPlugin {
		if err := packer.CheckpointReporter.Finalize(cli.Subcommand(), exitCode, err); err != nil {
			log.Printf("[WARN] (telemetry) Error finalizing report. This is safe to ignore. %s", err.Error())
		}
	}

	if err != nil {
		fmt.Fprintf(os.Stderr, "Error executing CLI: %s\n", err)
		return 1
	}

	return exitCode
}

// excludeHelpFunc filters commands we don't want to show from the list of
// commands displayed in packer's help text.
func excludeHelpFunc(commands map[string]cli.CommandFactory, exclude []string) cli.HelpFunc {
	// Make search slice into a map so we can use use the `if found` idiom
	// instead of a nested loop.
	var excludes = make(map[string]interface{}, len(exclude))
	for _, item := range exclude {
		excludes[item] = nil
	}

	// Create filtered list of commands
	helpCommands := []string{}
	for command := range commands {
		if _, found := excludes[command]; !found {
			helpCommands = append(helpCommands, command)
		}
	}

	return cli.FilteredHelpFunc(helpCommands, cli.BasicHelpFunc("packer"))
}

// extractMachineReadable checks the args for the machine readable
// flag and returns whether or not it is on. It modifies the args
// to remove this flag.
func extractMachineReadable(args []string) ([]string, bool) {
	for i, arg := range args {
		if arg == "-machine-readable" {
			// We found it. Slice it out.
			result := make([]string, len(args)-1)
			copy(result, args[:i])
			copy(result[i:], args[i+1:])
			return result, true
		}
	}

	return args, false
}

func loadConfig() (*config, error) {
	var config config
	config.PluginMinPort = 10000
	config.PluginMaxPort = 25000
	if err := config.Discover(); err != nil {
		return nil, err
	}

	configFilePath := os.Getenv("PACKER_CONFIG")
	if configFilePath != "" {
		log.Printf("'PACKER_CONFIG' set, loading config from environment.")
	} else {
		var err error
		configFilePath, err = packer.ConfigFile()

		if err != nil {
			log.Printf("Error detecting default config file path: %s", err)
		}
	}

	if configFilePath == "" {
		return &config, nil
	}

	log.Printf("Attempting to open config file: %s", configFilePath)
	f, err := os.Open(configFilePath)
	if err != nil {
		if !os.IsNotExist(err) {
			return nil, err
		}

		log.Printf("[WARN] Config file doesn't exist: %s", configFilePath)
		return &config, nil
	}
	defer f.Close()

	if err := decodeConfig(f, &config); err != nil {
		return nil, err
	}

	return &config, nil
}

// copyOutput uses output prefixes to determine whether data on stdout
// should go to stdout or stderr. This is due to panicwrap using stderr
// as the log and error channel.
func copyOutput(r io.Reader, doneCh chan<- struct{}) {
	defer close(doneCh)

	pr, err := prefixedio.NewReader(r)
	if err != nil {
		panic(err)
	}

	stderrR, err := pr.Prefix(ErrorPrefix)
	if err != nil {
		panic(err)
	}
	stdoutR, err := pr.Prefix(OutputPrefix)
	if err != nil {
		panic(err)
	}
	defaultR, err := pr.Prefix("")
	if err != nil {
		panic(err)
	}

	var wg sync.WaitGroup
	wg.Add(3)
	go func() {
		defer wg.Done()
		io.Copy(os.Stderr, stderrR)
	}()
	go func() {
		defer wg.Done()
		io.Copy(os.Stdout, stdoutR)
	}()
	go func() {
		defer wg.Done()
		io.Copy(os.Stdout, defaultR)
	}()

	wg.Wait()
}

func init() {
	// Seed the random number generator
	rand.Seed(time.Now().UTC().UnixNano())
}
<|endoftext|>


<commit_before>package main

import "fmt"
import "github.com/stianeikeland/go-rpio"

func main() {
	fmt.Println("foo")

	err := rpio.Open()
	defer rpio.Close()
}
<commit_msg>Set pin 18 high on request to open door<commit_after>package main

import "io"
import "log"
import "net/http"
import "time"

import "github.com/stianeikeland/go-rpio"

type gpioHandler struct {
  pin rpio.Pin
}

func GpioHandler(pin rpio.Pin) http.Handler {
  return &gpioHandler{pin}
}

func (f *gpioHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  io.WriteString(w, "hello, gpio!\n")

  timer := time.NewTimer(time.Second * 5)
  go func() {
    f.pin.Output()
    f.pin.High()
    <-timer.C
    f.pin.Low()
  }()
}

func main() {
  err := rpio.Open()
  defer rpio.Close()

  if err != nil {
    log.Fatal(err)
  }

  http.Handle("/hodoor", GpioHandler(rpio.Pin(18)))
  log.Fatal(http.ListenAndServe(":8080", nil))
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"flag"
	"fmt"
	"github.com/42wim/matterircd/config"
	"github.com/42wim/mm-go-irckit"
	"github.com/sirupsen/logrus"
	"net"
	"os"
	"strings"
)

var (
	flagRestrict, flagDefaultTeam, flagDefaultServer, flagTLSBind, flagTLSDir *string
	flagInsecure, flagSkipTLSVerify                                           *bool
	version                                                                   = "0.17.2"
	githash                                                                   string
	logger                                                                    *logrus.Entry
	cfg                                                                       config.Config
)

func main() {
	ourlog := logrus.New()
	ourlog.Formatter = &logrus.TextFormatter{FullTimestamp: true}
	logger = ourlog.WithFields(logrus.Fields{"module": "matterircd"})
	config.Logger = logger

	// config related. instantiate a new config.Config to store flags
	cfg = config.Config{}
	flagConfig := flag.String("conf", "", "config file")

	// bools for showing version/enabling debug
	flagVersion := flag.Bool("version", false, "show version")
	flag.BoolVar(&cfg.Debug, "debug", false, "enable debug logging")

	// bind related cfg
	flag.StringVar(&cfg.Bind, "bind", "127.0.0.1:6667", "interface:port to bind to.")

	// mattermost related cfg
	flag.StringVar(&cfg.Restrict, "restrict", "", "only allow connection to specified mattermost server/instances. Space delimited")
	flag.StringVar(&cfg.DefaultTeam, "mmteam", "", "specify default mattermost team")
	flag.StringVar(&cfg.DefaultServer, "mmserver", "", "specify default mattermost server/instance")
	flag.BoolVar(&cfg.Insecure, "mminsecure", false, "use http connection to mattermost")

	// TLS related cfg
	flag.BoolVar(&cfg.SkipTLSVerify, "mmskiptlsverify", false, "skip verification of mattermost certificate chain and hostname")
	flag.StringVar(&cfg.TLSBind, "tlsbind", "", "interface:port to bind to. (e.g 127.0.0.1:6697)")
	flag.StringVar(&cfg.TLSDir, "tlsdir", ".", "directory to look for key.pem and cert.pem.")
	flag.Parse()

	// if -config was set, load the config file (overrides args)
	if *flagConfig != "" {
		cfg = *config.LoadConfig(*flagConfig, cfg)
	}

	if cfg.Debug {
		logger.Info("enabling debug")
		ourlog.Level = logrus.DebugLevel
		irckit.SetLogLevel("debug")
	}
	if *flagVersion {
		fmt.Printf("version: %s %s\n", version, githash)
		return
	}

	irckit.SetLogger(logger)

	logger.Infof("Running version %s %s", version, githash)
	if strings.Contains(version, "-dev") {
		logger.Infof("WARNING: THIS IS A DEVELOPMENT VERSION. Things may break.")
	}

	if cfg.TLSBind != "" {
		go func() {
			logger.Infof("Listening on %s (TLS)", cfg.TLSBind)
			socket := tlsbind()
			defer socket.Close()
			start(socket)
		}()
	}

	// backwards compatible
	if cfg.Bind == "127.0.0.1:6667" && cfg.BindInterface != "" && cfg.BindPort != 0 {
		cfg.Bind = fmt.Sprintf("%s:%d", cfg.BindInterface, cfg.BindPort)
	}

	if cfg.Bind != "" {
		go func() {
			socket, err := net.Listen("tcp", cfg.Bind)
			if err != nil {
				logger.Errorf("Can not listen on %s: %v", cfg.Bind, err)
			}
			logger.Infof("Listening on %s", cfg.Bind)
			defer socket.Close()
			start(socket)
		}()
	}
	select {}
}

func tlsbind() net.Listener {
	cert, err := tls.LoadX509KeyPair(cfg.TLSDir+"/cert.pem", cfg.TLSDir+"/key.pem")
	if err != nil {
		logger.Errorf("could not load TLS, incorrect directory? Error: %s", err)
		os.Exit(1)
	}
	config := tls.Config{Certificates: []tls.Certificate{cert}}
	listenerTLS, err := tls.Listen("tcp", cfg.TLSBind, &config)
	if err != nil {
		logger.Errorf("Can not listen on %s: %v\n", cfg.TLSBind, err)
		os.Exit(1)
	}
	logger.Info("TLS listening on ", cfg.TLSBind)
	return listenerTLS
}

func start(socket net.Listener) {
	for {
		conn, err := socket.Accept()
		if err != nil {
			logger.Errorf("Failed to accept connection: %v", err)
			return
		}

		go func() {
			irccfg := &irckit.MmCfg{Insecure: cfg.Insecure, SkipTLSVerify: cfg.SkipTLSVerify,
				SlackSettings: cfg.Slack, MattermostSettings: cfg.Mattermost}
			newsrv := irckit.ServerConfig{Name: "matterircd", Version: version}.Server()
			logger.Infof("New connection: %s", conn.RemoteAddr())
			err = newsrv.Connect(irckit.NewUserMM(conn, newsrv, irccfg))
			if err != nil {
				logger.Errorf("Failed to join: %v", err)
				return
			}
		}()
	}
}
<commit_msg>Bump version<commit_after>package main

import (
	"crypto/tls"
	"flag"
	"fmt"
	"github.com/42wim/matterircd/config"
	"github.com/42wim/mm-go-irckit"
	"github.com/sirupsen/logrus"
	"net"
	"os"
	"strings"
)

var (
	flagRestrict, flagDefaultTeam, flagDefaultServer, flagTLSBind, flagTLSDir *string
	flagInsecure, flagSkipTLSVerify                                           *bool
	version                                                                   = "0.17.3-dev"
	githash                                                                   string
	logger                                                                    *logrus.Entry
	cfg                                                                       config.Config
)

func main() {
	ourlog := logrus.New()
	ourlog.Formatter = &logrus.TextFormatter{FullTimestamp: true}
	logger = ourlog.WithFields(logrus.Fields{"module": "matterircd"})
	config.Logger = logger

	// config related. instantiate a new config.Config to store flags
	cfg = config.Config{}
	flagConfig := flag.String("conf", "", "config file")

	// bools for showing version/enabling debug
	flagVersion := flag.Bool("version", false, "show version")
	flag.BoolVar(&cfg.Debug, "debug", false, "enable debug logging")

	// bind related cfg
	flag.StringVar(&cfg.Bind, "bind", "127.0.0.1:6667", "interface:port to bind to.")

	// mattermost related cfg
	flag.StringVar(&cfg.Restrict, "restrict", "", "only allow connection to specified mattermost server/instances. Space delimited")
	flag.StringVar(&cfg.DefaultTeam, "mmteam", "", "specify default mattermost team")
	flag.StringVar(&cfg.DefaultServer, "mmserver", "", "specify default mattermost server/instance")
	flag.BoolVar(&cfg.Insecure, "mminsecure", false, "use http connection to mattermost")

	// TLS related cfg
	flag.BoolVar(&cfg.SkipTLSVerify, "mmskiptlsverify", false, "skip verification of mattermost certificate chain and hostname")
	flag.StringVar(&cfg.TLSBind, "tlsbind", "", "interface:port to bind to. (e.g 127.0.0.1:6697)")
	flag.StringVar(&cfg.TLSDir, "tlsdir", ".", "directory to look for key.pem and cert.pem.")
	flag.Parse()

	// if -config was set, load the config file (overrides args)
	if *flagConfig != "" {
		cfg = *config.LoadConfig(*flagConfig, cfg)
	}

	if cfg.Debug {
		logger.Info("enabling debug")
		ourlog.Level = logrus.DebugLevel
		irckit.SetLogLevel("debug")
	}
	if *flagVersion {
		fmt.Printf("version: %s %s\n", version, githash)
		return
	}

	irckit.SetLogger(logger)

	logger.Infof("Running version %s %s", version, githash)
	if strings.Contains(version, "-dev") {
		logger.Infof("WARNING: THIS IS A DEVELOPMENT VERSION. Things may break.")
	}

	if cfg.TLSBind != "" {
		go func() {
			logger.Infof("Listening on %s (TLS)", cfg.TLSBind)
			socket := tlsbind()
			defer socket.Close()
			start(socket)
		}()
	}

	// backwards compatible
	if cfg.Bind == "127.0.0.1:6667" && cfg.BindInterface != "" && cfg.BindPort != 0 {
		cfg.Bind = fmt.Sprintf("%s:%d", cfg.BindInterface, cfg.BindPort)
	}

	if cfg.Bind != "" {
		go func() {
			socket, err := net.Listen("tcp", cfg.Bind)
			if err != nil {
				logger.Errorf("Can not listen on %s: %v", cfg.Bind, err)
			}
			logger.Infof("Listening on %s", cfg.Bind)
			defer socket.Close()
			start(socket)
		}()
	}
	select {}
}

func tlsbind() net.Listener {
	cert, err := tls.LoadX509KeyPair(cfg.TLSDir+"/cert.pem", cfg.TLSDir+"/key.pem")
	if err != nil {
		logger.Errorf("could not load TLS, incorrect directory? Error: %s", err)
		os.Exit(1)
	}
	config := tls.Config{Certificates: []tls.Certificate{cert}}
	listenerTLS, err := tls.Listen("tcp", cfg.TLSBind, &config)
	if err != nil {
		logger.Errorf("Can not listen on %s: %v\n", cfg.TLSBind, err)
		os.Exit(1)
	}
	logger.Info("TLS listening on ", cfg.TLSBind)
	return listenerTLS
}

func start(socket net.Listener) {
	for {
		conn, err := socket.Accept()
		if err != nil {
			logger.Errorf("Failed to accept connection: %v", err)
			return
		}

		go func() {
			irccfg := &irckit.MmCfg{Insecure: cfg.Insecure, SkipTLSVerify: cfg.SkipTLSVerify,
				SlackSettings: cfg.Slack, MattermostSettings: cfg.Mattermost}
			newsrv := irckit.ServerConfig{Name: "matterircd", Version: version}.Server()
			logger.Infof("New connection: %s", conn.RemoteAddr())
			err = newsrv.Connect(irckit.NewUserMM(conn, newsrv, irccfg))
			if err != nil {
				logger.Errorf("Failed to join: %v", err)
				return
			}
		}()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"

	"github.com/Smartling/api-sdk-go"
	"github.com/docopt/docopt-go"
	"github.com/reconquest/hierr-go"
)

var version = "1.0"

var usage = `smartling - manage translation files using Smartling.

TBD.

Usage:
  smartling -h | --help
  smartling [options] projects list
  smartling [options] projects info <project>
  smartling [options] files list <project> [<uri>] [-s] [--format=]
  smartling [options] files pull <project> [<uri>] [-l=] [-d=]
  smartling [options] files status <project> [<uri>] [--format=]

Commands:
  projects                 Used to access various project sub-commands.
   list                    Lists projects for current account.
   get <project>           Get project details about specific project.
                            Accepts project ID as <project> parameter.
  files                     Used to access various files sub-commands.
   status <project> <uri>  Shows file translation status.
    --format <format>      Specifies format to use for file status output.
                            [default: $FILE_STATUS_FORMAT]
   list <project> <uri>    Lists files from specified project.
    -s --short             Output only file URI.
    --format <format>      Specifies format to use for file list output.
                            [default: $FILE_LIST_FORMAT]
   pull <project> <uri>    Pulls specified files from server.
    -d --directory <dir>   Download all files to specified directory.
    --format <format>      Can be used to format path to downloaded files.
                            Note, that single file can be translated in
                            different locales, so format should include locale
                            to create several file paths.
                            [default: $FILE_PULL_FORMAT]

All <uri> arguments support globbing with following patterns:
  > ** — matches any number of any chars;
  > *  — matches any number of chars except '/';
  > ?  — matches any single char except '/';
  > [xyz]   — matches 'x', 'y' or 'z' charachers;
  > [!xyz]  — matches not 'x', 'y' or 'z' charachers;
  > {a,b,c} — matches alternatives a, b or c;

Options:
  -h --help               Show this help.
  -c --config <file>      Config file in YAML format.
                           [default: $HOME/.config/smartling/config.yml]
  -p --project <project>  Project ID to operate on.
                           This option ovverides config value "project_id".
                           This option is not used by "projects" command.
  -a --account <account>  Account ID to operate on.
                           This option ovverides config value "account_id".
  --user <user>           User ID which will be used for authentication.
                           This option ovverides config value "user_id".
  --secret <secret>       Token Secret which will be used for authentication.
                           This option ovverides config value "secret".
  -s --short              Use short list output, usually outputs only first
                           column, e.g. file URI in case of files list.
  -l --locale <locale>    Sets locale to filter by or operate upon. Depends on
                           command.
  -d --directory <dir>    Sets directory to operate on, usually, to store or to
                           read files.  Depends on command.  [default: .]
`

const (
	defaultFilesListFormat  = `{{.FileURI}}\t{{.LastUploaded}}\t{{.FileType}}\n`
	defaultFileStatusFormat = `{{.FileURI}}\t{{.Locale}}\t{{.Status}}\t{{.Progress}}\n`
	defaultFilePullFormat   = `{{name .FileURI}}@{{.Locale}}{{ext .FileURI}}`
)

func main() {

	usage := os.Expand(usage, func(key string) string {
		switch key {
		case "HOME":
			return os.Getenv(key)

		case "FILE_LIST_FORMAT":
			return defaultFilesListFormat

		case "FILE_PULL_FORMAT":
			return defaultFilePullFormat

		case "FILE_STATUS_FORMAT":
			return defaultFileStatusFormat
		}

		return key
	})

	args, err := docopt.Parse(usage, nil, true, "smartling "+version, false)
	if err != nil {
		panic(err)
	}

	config, err := loadConfig(args)
	if err != nil {
		fmt.Println(err)

		os.Exit(1)
	}

	switch {
	case args["projects"].(bool):
		err = projects(config, args)

	case args["files"].(bool):
		err = files(config, args)
	}

	if err != nil {
		fmt.Println(err)
	}
}

func loadConfig(args map[string]interface{}) (Config, error) {
	path := args["--config"].(string)

	config, err := NewConfig(path)
	if err != nil {
		return config, NewError(
			hierr.Errorf(err, `failed to load configuration file "%s".`, path),
			`Check configuretion file contents according to documentation.`,
		)
	}

	if args["--user"] != nil {
		config.UserID = args["--user"].(string)
	}

	if args["--secret"] != nil {
		config.Secret = args["--secret"].(string)
	}

	if args["--account"] != nil {
		config.AccountID = args["--account"].(string)
	}

	if config.UserID == "" {
		return config, MissingConfigValueError{
			ConfigPath: config.path,
			ValueName:  "user ID",
			OptionName: "user",
			KeyName:    "user_id",
		}
	}

	if config.Secret == "" {
		return config, MissingConfigValueError{
			ConfigPath: config.path,
			ValueName:  "token secret",
			OptionName: "secret",
			KeyName:    "secret",
		}
	}

	return config, nil
}

func projects(config Config, args map[string]interface{}) error {
	client := smartling.NewClient(config.UserID, config.Secret)

	if config.AccountID == "" {
		return MissingConfigValueError{
			ConfigPath: config.path,
			ValueName:  "account ID",
			OptionName: "account",
			KeyName:    "account_id",
		}
	}

	switch {
	case args["list"].(bool):
		return doProjectsList(client, config, args)

	case args["info"].(bool):
		return doProjectsGet(client, config, args)
	}

	return nil
}

func files(config Config, args map[string]interface{}) error {
	client := smartling.NewClient(config.UserID, config.Secret)

	switch {
	case args["list"].(bool):
		return doFilesList(client, config, args)

	case args["pull"].(bool):
		return doFilesPull(client, config, args)

	case args["status"].(bool):
		return doFilesStatus(client, config, args)

	}

	return nil
}
<commit_msg>fix #7: change environment lookup to native code<commit_after>package main

import (
	"fmt"
	"os"
	"os/user"

	"github.com/Smartling/api-sdk-go"
	"github.com/docopt/docopt-go"
	"github.com/reconquest/hierr-go"
)

var version = "1.0"

var usage = `smartling - manage translation files using Smartling.

TBD.

Usage:
  smartling -h | --help
  smartling [options] projects list
  smartling [options] projects info <project>
  smartling [options] files list <project> [<uri>] [-s] [--format=]
  smartling [options] files pull <project> [<uri>] [-l=] [-d=]
  smartling [options] files status <project> [<uri>] [--format=]

Commands:
  projects                 Used to access various project sub-commands.
   list                    Lists projects for current account.
   get <project>           Get project details about specific project.
                            Accepts project ID as <project> parameter.
  files                     Used to access various files sub-commands.
   status <project> <uri>  Shows file translation status.
    --format <format>      Specifies format to use for file status output.
                            [default: $FILE_STATUS_FORMAT]
   list <project> <uri>    Lists files from specified project.
    -s --short             Output only file URI.
    --format <format>      Specifies format to use for file list output.
                            [default: $FILE_LIST_FORMAT]
   pull <project> <uri>    Pulls specified files from server.
    -d --directory <dir>   Download all files to specified directory.
    --format <format>      Can be used to format path to downloaded files.
                            Note, that single file can be translated in
                            different locales, so format should include locale
                            to create several file paths.
                            [default: $FILE_PULL_FORMAT]

All <uri> arguments support globbing with following patterns:
  > ** — matches any number of any chars;
  > *  — matches any number of chars except '/';
  > ?  — matches any single char except '/';
  > [xyz]   — matches 'x', 'y' or 'z' charachers;
  > [!xyz]  — matches not 'x', 'y' or 'z' charachers;
  > {a,b,c} — matches alternatives a, b or c;

Options:
  -h --help               Show this help.
  -c --config <file>      Config file in YAML format.
                           [default: $HOME/.config/smartling/config.yml]
  -p --project <project>  Project ID to operate on.
                           This option ovverides config value "project_id".
                           This option is not used by "projects" command.
  -a --account <account>  Account ID to operate on.
                           This option ovverides config value "account_id".
  --user <user>           User ID which will be used for authentication.
                           This option ovverides config value "user_id".
  --secret <secret>       Token Secret which will be used for authentication.
                           This option ovverides config value "secret".
  -s --short              Use short list output, usually outputs only first
                           column, e.g. file URI in case of files list.
  -l --locale <locale>    Sets locale to filter by or operate upon. Depends on
                           command.
  -d --directory <dir>    Sets directory to operate on, usually, to store or to
                           read files.  Depends on command.  [default: .]
`

const (
	defaultFilesListFormat  = `{{.FileURI}}\t{{.LastUploaded}}\t{{.FileType}}\n`
	defaultFileStatusFormat = `{{.FileURI}}\t{{.Locale}}\t{{.Status}}\t{{.Progress}}\n`
	defaultFilePullFormat   = `{{name .FileURI}}@{{.Locale}}{{ext .FileURI}}`
)

func main() {
	currentUser, err := user.Current()
	if err != nil {
		panic(err)
	}

	usage := os.Expand(usage, func(key string) string {
		switch key {
		case "HOME":
			return currentUser.HomeDir

		case "FILE_LIST_FORMAT":
			return defaultFilesListFormat

		case "FILE_PULL_FORMAT":
			return defaultFilePullFormat

		case "FILE_STATUS_FORMAT":
			return defaultFileStatusFormat
		}

		return key
	})

	args, err := docopt.Parse(usage, nil, true, "smartling "+version, false)
	if err != nil {
		panic(err)
	}

	config, err := loadConfig(args)
	if err != nil {
		fmt.Println(err)

		os.Exit(1)
	}

	switch {
	case args["projects"].(bool):
		err = projects(config, args)

	case args["files"].(bool):
		err = files(config, args)
	}

	if err != nil {
		fmt.Println(err)
	}
}

func loadConfig(args map[string]interface{}) (Config, error) {
	path := args["--config"].(string)

	config, err := NewConfig(path)
	if err != nil {
		return config, NewError(
			hierr.Errorf(err, `failed to load configuration file "%s".`, path),
			`Check configuretion file contents according to documentation.`,
		)
	}

	if args["--user"] != nil {
		config.UserID = args["--user"].(string)
	}

	if args["--secret"] != nil {
		config.Secret = args["--secret"].(string)
	}

	if args["--account"] != nil {
		config.AccountID = args["--account"].(string)
	}

	if config.UserID == "" {
		return config, MissingConfigValueError{
			ConfigPath: config.path,
			ValueName:  "user ID",
			OptionName: "user",
			KeyName:    "user_id",
		}
	}

	if config.Secret == "" {
		return config, MissingConfigValueError{
			ConfigPath: config.path,
			ValueName:  "token secret",
			OptionName: "secret",
			KeyName:    "secret",
		}
	}

	return config, nil
}

func projects(config Config, args map[string]interface{}) error {
	client := smartling.NewClient(config.UserID, config.Secret)

	if config.AccountID == "" {
		return MissingConfigValueError{
			ConfigPath: config.path,
			ValueName:  "account ID",
			OptionName: "account",
			KeyName:    "account_id",
		}
	}

	switch {
	case args["list"].(bool):
		return doProjectsList(client, config, args)

	case args["info"].(bool):
		return doProjectsGet(client, config, args)
	}

	return nil
}

func files(config Config, args map[string]interface{}) error {
	client := smartling.NewClient(config.UserID, config.Secret)

	switch {
	case args["list"].(bool):
		return doFilesList(client, config, args)

	case args["pull"].(bool):
		return doFilesPull(client, config, args)

	case args["status"].(bool):
		return doFilesStatus(client, config, args)

	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/wsxiaoys/terminal/color"
	"net/http"
	"net/url"
	"os"
	"strings"
)

type CheckResult struct {
	url    string
	status bool
	err    bool
}

func main() {
	var quiet bool = false
	var result bool = true

	flag.BoolVar(&quiet, "quiet", false, "Disables any output and limits the status to the exit code")
	flag.Parse()

	// First, let's make sure that all passed arguments are actually valid URLs.
	for _, url_ := range flag.Args() {
		if !isUrl(url_) {
			fmt.Errorf("%s is not a valid URL\n", url_)
			os.Exit(1)
			return
		}
	}

	resultChan := make(chan CheckResult, len(flag.Args()))

	for _, url_ := range flag.Args() {
		go testUrl(url_, resultChan)
	}

	for i := 0; i < len(flag.Args()); i++ {
		checkResult := <-resultChan
		if checkResult.err || !checkResult.status {
			result = false
		}
		if !quiet {
			if checkResult.err {
				color.Printf("@{r}ERROR@{|}  %s\n", checkResult.url)
			} else {
				if checkResult.status {
					color.Printf("@{g}OK@{|}     %s\n", checkResult.url)
				} else {
					color.Printf("@{r}FAILED@{|} %s\n", checkResult.url)
				}
			}
		}
	}

	if !result {
		os.Exit(-1)
	} else {
		os.Exit(0)
	}
}

// Checks if a given string represents a URL that we can work with.
func isUrl(u string) bool {
	result, err := url.Parse(u)
	if err != nil {
		return false
	}
	return result.Scheme == "http" || result.Scheme == "https"
}

// This method tests if the given URL responds in a gzipped manner.
func testUrl(url string, result chan CheckResult) {
	client := http.Client{}
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		fmt.Errorf("Failed to generate new request")
		result <- CheckResult{url, false, true}
		return
	}
	req.Header.Add("Accept-Encoding", "gzip,deflate")
	resp, err := client.Do(req)
	if err != nil {
		fmt.Errorf("Request failed: %s", err)
		result <- CheckResult{url, false, true}
		return
	}
	encoding := resp.Header.Get("Content-Encoding")
	if strings.Contains(encoding, "gzip") || strings.Contains(encoding, "deflate") {
		result <- CheckResult{url, true, false}
		return
	}
	result <- CheckResult{url, false, false}
}
<commit_msg>Misunderstood fmt.Errorf and fixed that.<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/wsxiaoys/terminal/color"
	"log"
	"net/http"
	"net/url"
	"os"
	"strings"
)

type CheckResult struct {
	url    string
	status bool
	err    bool
}

func main() {
	var quiet bool = false
	var result bool = true

	flag.Usage = func() {
		fmt.Printf("Usage: testgzip [options] URL+\n\nOptions:\n")
		flag.PrintDefaults()
	}
	flag.BoolVar(&quiet, "quiet", false, "Disables any output and limits the status to the exit code")
	flag.Parse()

	if 0 == len(flag.Args()) {
		fmt.Println("You have to provide at least one URL to be tested.")
		os.Exit(2)
	}

	// First, let's make sure that all passed arguments are actually valid URLs.
	for _, url_ := range flag.Args() {
		if !isUrl(url_) {
			fmt.Printf("%s is not a valid URL\n", url_)
			os.Exit(1)
			return
		}
	}

	resultChan := make(chan CheckResult, len(flag.Args()))

	for _, url_ := range flag.Args() {
		go testUrl(url_, resultChan)
	}

	for i := 0; i < len(flag.Args()); i++ {
		checkResult := <-resultChan
		if checkResult.err || !checkResult.status {
			result = false
		}
		if !quiet {
			if checkResult.err {
				color.Printf("@{r}ERROR@{|}  %s\n", checkResult.url)
			} else {
				if checkResult.status {
					color.Printf("@{g}OK@{|}     %s\n", checkResult.url)
				} else {
					color.Printf("@{r}FAILED@{|} %s\n", checkResult.url)
				}
			}
		}
	}

	if !result {
		os.Exit(-1)
	} else {
		os.Exit(0)
	}
}

// Checks if a given string represents a URL that we can work with.
func isUrl(u string) bool {
	result, err := url.Parse(u)
	if err != nil {
		return false
	}
	return result.Scheme == "http" || result.Scheme == "https"
}

// This method tests if the given URL responds in a gzipped manner.
func testUrl(url string, result chan CheckResult) {
	client := http.Client{}
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		log.Println("Failed to generate new request")
		result <- CheckResult{url, false, true}
		return
	}
	req.Header.Add("Accept-Encoding", "gzip,deflate")
	resp, err := client.Do(req)
	if err != nil {
		log.Println("Request failed: %s", err)
		result <- CheckResult{url, false, true}
		return
	}
	encoding := resp.Header.Get("Content-Encoding")
	if strings.Contains(encoding, "gzip") || strings.Contains(encoding, "deflate") {
		result <- CheckResult{url, true, false}
		return
	}
	result <- CheckResult{url, false, false}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/Shopify/sarama"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"time"
)

type ConsumerConfig struct {
	Broker    string `json:"broker"`
	Partition int    `json:"partition"`
	Topic     string `json:"topic"`
	Offset    string `json:"offset"`
}

type Config struct {
	Consumers []ConsumerConfig `json:"consumers"`
}

func readConfig() *Config {
	file, err := ioutil.ReadFile("./config.json")
	if err != nil {
		panic("Cannot open config.json")
	}
	config := new(Config)
	err = json.Unmarshal(file, &config)
	if err != nil {
		fmt.Println("error:", err)
	}

	return config
}

func startConsumers(config *Config, c chan *sarama.ConsumerMessage) {
	for _, consumerConfig := range config.Consumers {
		topic, broker, partition := consumerConfig.Topic, consumerConfig.Broker, consumerConfig.Partition
		var offset int64 = -1
		switch consumerConfig.Offset {
		case "oldest":
			offset = -2
		case "newest":
			offset = -1
		default:
			panic("Invalid value for consumer offset")
		}

		go consume(c, topic, broker, partition, offset)
	}
}

func ServeWithChannel(c chan *sarama.ConsumerMessage) func(w http.ResponseWriter, req *http.Request) {
	return func(w http.ResponseWriter, req *http.Request) {
		messages := ""
		timer := time.NewTimer(time.Millisecond * 10)
		for {
			select {
			case consumerMessage := <-c:
				// fmt.Printf("Received message with value: [%v]\n", string(consumerMessage.Value))
				messages +=
					"{\"topic\": \"" + consumerMessage.Topic +
						"\", \"partition\": \"" + strconv.FormatInt(int64(consumerMessage.Partition), 10) +
						"\", \"offset\": \"" + strconv.FormatInt(consumerMessage.Offset, 10) +
						"\", \"key\": \"" + strings.Replace(string(consumerMessage.Key), `"`, `\"`, -1) +
						"\", \"value\": \"" + strings.Replace(string(consumerMessage.Value), `"`, `\"`, -1) + "\"}\n"
			case <-timer.C:
				w.Header().Set("Access-Control-Allow-Origin", "*")
				io.WriteString(w, messages)
				return
			}
		}
	}
}

func Die(w http.ResponseWriter, req *http.Request) {
	os.Exit(1)
}

func main() {
	config := readConfig()
	c := make(chan *sarama.ConsumerMessage)

	startConsumers(config, c)

	var addr = flag.String("addr", ":41234", "host:port I'm serving on (default :41234)")

	signals := make(chan os.Signal, 1)
	signal.Notify(signals, os.Interrupt)

	flag.Parse()
	http.Handle("/", http.HandlerFunc(ServeWithChannel(c)))
	http.Handle("/die", http.HandlerFunc(Die))
	err := http.ListenAndServe(*addr, nil)
	if err != nil {
		log.Fatal("ListenAndServe:", err)
	}
}
<commit_msg>Cap of 10 messages per poll.<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/Shopify/sarama"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"time"
)

type ConsumerConfig struct {
	Broker    string `json:"broker"`
	Partition int    `json:"partition"`
	Topic     string `json:"topic"`
	Offset    string `json:"offset"`
}

type Config struct {
	Consumers []ConsumerConfig `json:"consumers"`
}

func readConfig() *Config {
	file, err := ioutil.ReadFile("./config.json")
	if err != nil {
		panic("Cannot open config.json")
	}
	config := new(Config)
	err = json.Unmarshal(file, &config)
	if err != nil {
		fmt.Println("error:", err)
	}

	return config
}

func startConsumers(config *Config, c chan *sarama.ConsumerMessage) {
	for _, consumerConfig := range config.Consumers {
		topic, broker, partition := consumerConfig.Topic, consumerConfig.Broker, consumerConfig.Partition
		var offset int64 = -1
		switch consumerConfig.Offset {
		case "oldest":
			offset = -2
		case "newest":
			offset = -1
		default:
			panic("Invalid value for consumer offset")
		}

		go consume(c, topic, broker, partition, offset)
	}
}

func ServeWithChannel(c chan *sarama.ConsumerMessage) func(w http.ResponseWriter, req *http.Request) {
	return func(w http.ResponseWriter, req *http.Request) {
		messages := ""
		timer := time.NewTimer(time.Millisecond * 10)
		for {
			select {
			case consumerMessage := <-c:
				// fmt.Printf("Received message with value: [%v]\n", string(consumerMessage.Value))
				messages +=
					"{\"topic\": \"" + consumerMessage.Topic +
						"\", \"partition\": \"" + strconv.FormatInt(int64(consumerMessage.Partition), 10) +
						"\", \"offset\": \"" + strconv.FormatInt(consumerMessage.Offset, 10) +
						"\", \"key\": \"" + strings.Replace(string(consumerMessage.Key), `"`, `\"`, -1) +
						"\", \"value\": \"" + strings.Replace(string(consumerMessage.Value), `"`, `\"`, -1) + "\"}\n"

				if len(messages) >= 10 {
					w.Header().Set("Access-Control-Allow-Origin", "*")
					io.WriteString(w, messages)
					return
				}
			case <-timer.C:
				w.Header().Set("Access-Control-Allow-Origin", "*")
				io.WriteString(w, messages)
				return
			}
		}
	}
}

func Die(w http.ResponseWriter, req *http.Request) {
	os.Exit(1)
}

func main() {
	config := readConfig()
	c := make(chan *sarama.ConsumerMessage)

	startConsumers(config, c)

	var addr = flag.String("addr", ":41234", "host:port I'm serving on (default :41234)")

	signals := make(chan os.Signal, 1)
	signal.Notify(signals, os.Interrupt)

	flag.Parse()
	http.Handle("/", http.HandlerFunc(ServeWithChannel(c)))
	http.Handle("/die", http.HandlerFunc(Die))
	err := http.ListenAndServe(*addr, nil)
	if err != nil {
		log.Fatal("ListenAndServe:", err)
	}
}
<|endoftext|>


<commit_before>// cmail is a command that runs another command and sends stdout and stderr
// to a specified email address at certain intervals.
package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/BurntSushi/cmd"
)

var (
	flagSendMail      = "sendmail"
	flagTo            = os.Getenv("EMAIL")
	flagPeriod        = "1h"
	flagNoPeriod      = false
	flagNoPass        = false
	flagInc           = false
	flagSubjectPrefix = "[cmail] "

	fullProgram string
)

func init() {
	flag.StringVar(&flagSendMail, "sendmail", flagSendMail,
		"The command to use to send mail. The email content and headers\n"+
			"will be sent to stdin.")
	flag.StringVar(&flagTo, "to", flagTo,
		"The email address to send mail to. By default, this is set to the\n"+
			"value of the $EMAIL environment variable.")
	flag.StringVar(&flagSubjectPrefix, "subj", flagSubjectPrefix,
		"A subject prefix to use for all emails.")
	flag.StringVar(&flagPeriod, "period", flagPeriod,
		"The amount of time to wait between sending data gathered from\n"+
			"stdin. Value should be a duration defined by Go's\n"+
			"time.ParseDuration. e.g., '300ms', '1.5h', '1m'.")
	flag.BoolVar(&flagNoPass, "no-pass", flagNoPass,
		"If set, stdout/stderr will not be passed thru.")
	flag.BoolVar(&flagInc, "inc", flagInc,
		"If set, emails will contain incremental changes as opposed to\n"+
			"each email containing all data. Will also use less memory.")
	flag.BoolVar(&flagNoPeriod, "no-period", flagNoPeriod,
		"If set, only one email will be sent when the command finishes.")

	log.SetFlags(0)
	flag.Usage = usage
	flag.Parse()

	flagTo = strings.TrimSpace(flagTo)

	runtime.GOMAXPROCS(runtime.NumCPU())
}

func main() {
	if len(flagTo) == 0 {
		log.Println("I don't know who to send email to. Please use the\n" +
			"'-to' flag or set the EMAIL environment variable.\n")
		flag.Usage()
		os.Exit(1)
	}

	period, err := time.ParseDuration(flagPeriod)
	assert(err, "Invalid period '%s': %s.", flagPeriod, err)

	sigged := make(chan os.Signal)
	signal.Notify(sigged, os.Interrupt, os.Kill)

	var program *exec.Cmd
	var inlines <-chan string
	if flag.NArg() == 0 {
		fullProgram = "stdin"

		inlines = gobble(bufio.NewReader(os.Stdin))
	} else {
		program = exec.Command(flag.Arg(0), flag.Args()[1:]...)
		fullProgram = strings.Join(flag.Args(), " ")
		if len(fullProgram) > 200 {
			fullProgram = fullProgram[0:201]
		}

		stdout, err := program.StdoutPipe()
		assert(err, "Could not get stdout pipe: %s.", err)

		stderr, err := program.StderrPipe()
		assert(err, "Could not get stderr pipe: %s.", err)

		err = program.Start()
		assert(err, "Could not start program '%s': %s.", fullProgram, err)

		// Start goroutines for reading stdout and stderr, then mux them.
		stdoutLines := gobble(bufio.NewReader(stdout))
		stderrLines := gobble(bufio.NewReader(stderr))
		inlines = muxer(stdoutLines, stderrLines)
	}

	// Start the goroutine responsible for sending emails.
	// The send is also responsible for quitting the program.
	// (When all emails remaining have been sent.)
	send := sender()

	// Keep track of all lines emitted to stdout/stderr.
	// If the duration passes, stop and send whatever we have.
	// If the user interrupts the program, stop and send whatever we have.
	// If EOF is read on both stdout and stderr, send what we have.
	// We exit the program by closing the `send` channel, which will force
	// any remaining emails left to be sent.
	outlines := make([]string, 0)
	addMsg := func(msg string) {
		outlines = append(outlines, []string{"\n", "\n", msg + "\n"}...)
	}
	killed := false // set if user interrupted
	for {
		select {
		case <-time.After(period):
			if !flagNoPeriod {
				send <- outlines
				outlines = outlines[:0]
			}
		case <-sigged:
			if program != nil {
				program.Process.Kill()
				killed = true
				// continue reading stdout/stderr until program really quits.
			} else { // reading stdin, so send what we've got now.
				send <- outlines
				close(send)
				select {}
			}
		case line, ok := <-inlines:
			if !ok {
				// Program completed successfully!
				if killed {
					addMsg("Program interrupted.")
				} else {
					addMsg("Program completed successfully.")
				}
				send <- outlines
				close(send)
				select {}
			}
			outlines = append(outlines, line)
		}
	}
}

// muxer takes a list of incoming string channels, and muxes them all into
// the result channel. The channel returned is closed if and only if all
// channels in 'ins' have been closed.
func muxer(ins ...<-chan string) <-chan string {
	wg := new(sync.WaitGroup)
	combined := make(chan string, 500)
	for _, in := range ins {
		wg.Add(1)
		in := in
		go func() {
			for line := range in {
				combined <- line
			}
			wg.Done()
		}()
	}
	go func() {
		wg.Wait()
		close(combined)
	}()
	return combined
}

// sender receives chunks of lines on the result channel, and sends those
// chunks of lines via email.
//
// sender is the only goroutine that should exit the program in normal
// operation, which happens when there are no more chunks of lines to read.
func sender() chan<- []string {
	toSend := make([]string, 500)
	send := make(chan []string)
	go func() {
		for newLines := range send {
			switch {
			case flagInc:
				if len(newLines) == 0 {
					emailLines([]string{"Nothing to report."})
				} else {
					emailLines(newLines)
				}
			default:
				toSend = append(toSend, newLines...)
				emailLines(toSend)
			}
		}
		os.Exit(0)
	}()
	return send
}

// gobble reads lines from any buffered reader, sends the lines on the result
// channel, and closes the channel when EOF is read.
//
// gobble will quit the program with an error message if the input source
// cannot be read.
func gobble(buf *bufio.Reader) <-chan string {
	lines := make(chan string)
	go func() {
		for {
			line, err := buf.ReadString('\n')
			if err != nil && err != io.EOF {
				fatal("Could not read line: %s", err)
			}
			if !flagNoPass {
				fmt.Print(line)
			}
			lines <- line
			if err == io.EOF {
				close(lines)
				break
			}
		}
	}()
	return lines
}

// emailLines sends a chunk of lines via email.
func emailLines(lines []string) {
	var c *cmd.Command
	subj := fmt.Sprintf("%s%s", flagSubjectPrefix, fullProgram)

	if flagSendMail == "mailx" {
		c = cmd.New(flagSendMail, "-s", subj, flagTo)
		fmt.Print(c.BufStdin, strings.Join(lines, ""))
	} else {
		c = cmd.New(flagSendMail, "-t")
		date := time.Now().Format("Mon, 02 Jan 2006 15:04:05 -0700")
		fmt.Fprintf(c.BufStdin,
			`Subject: %s
From: %s
To: %s
Date: %s

%s`, subj, flagTo, flagTo, date, strings.Join(lines, ""))
	}

	if err := c.Run(); err != nil {
		log.Printf("Error sending mail '%s -t': %s.", flagSendMail, err)
	}
}

func usage() {
	log.Printf("Usage: %s [flags] command [args]\n\n", path.Base(os.Args[0]))
	log.Printf("cmail sends data read from `command` periodically, and/or\n" +
		"when EOF is reached.\n\n")

	flag.VisitAll(func(fl *flag.Flag) {
		var def string
		if len(fl.DefValue) > 0 {
			def = fmt.Sprintf(" (default: %s)", fl.DefValue)
		}
		log.Printf("-%s%s\n", fl.Name, def)
		log.Printf("    %s\n", strings.Replace(fl.Usage, "\n", "\n    ", -1))
	})
}

func assert(err error, format string, v ...interface{}) {
	if err != nil {
		fatal(format, v...)
	}
}

func fatal(format string, v ...interface{}) {
	log.Fatalf(format, v...)
}
<commit_msg>kill me now<commit_after>// cmail is a command that runs another command and sends stdout and stderr
// to a specified email address at certain intervals.
package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/BurntSushi/cmd"
)

var (
	flagSendMail      = "sendmail"
	flagTo            = os.Getenv("EMAIL")
	flagPeriod        = "1h"
	flagNoPeriod      = false
	flagNoPass        = false
	flagInc           = false
	flagSubjectPrefix = "[cmail] "

	fullProgram string
)

func init() {
	flag.StringVar(&flagSendMail, "sendmail", flagSendMail,
		"The command to use to send mail. The email content and headers\n"+
			"will be sent to stdin.")
	flag.StringVar(&flagTo, "to", flagTo,
		"The email address to send mail to. By default, this is set to the\n"+
			"value of the $EMAIL environment variable.")
	flag.StringVar(&flagSubjectPrefix, "subj", flagSubjectPrefix,
		"A subject prefix to use for all emails.")
	flag.StringVar(&flagPeriod, "period", flagPeriod,
		"The amount of time to wait between sending data gathered from\n"+
			"stdin. Value should be a duration defined by Go's\n"+
			"time.ParseDuration. e.g., '300ms', '1.5h', '1m'.")
	flag.BoolVar(&flagNoPass, "no-pass", flagNoPass,
		"If set, stdout/stderr will not be passed thru.")
	flag.BoolVar(&flagInc, "inc", flagInc,
		"If set, emails will contain incremental changes as opposed to\n"+
			"each email containing all data. Will also use less memory.")
	flag.BoolVar(&flagNoPeriod, "no-period", flagNoPeriod,
		"If set, only one email will be sent when the command finishes.")

	log.SetFlags(0)
	flag.Usage = usage
	flag.Parse()

	flagTo = strings.TrimSpace(flagTo)

	runtime.GOMAXPROCS(runtime.NumCPU())
}

func main() {
	if len(flagTo) == 0 {
		log.Println("I don't know who to send email to. Please use the\n" +
			"'-to' flag or set the EMAIL environment variable.\n")
		flag.Usage()
		os.Exit(1)
	}

	period, err := time.ParseDuration(flagPeriod)
	assert(err, "Invalid period '%s': %s.", flagPeriod, err)

	sigged := make(chan os.Signal)
	signal.Notify(sigged, os.Interrupt, os.Kill)

	var program *exec.Cmd
	var inlines <-chan string
	if flag.NArg() == 0 {
		fullProgram = "stdin"

		inlines = gobble(bufio.NewReader(os.Stdin))
	} else {
		program = exec.Command(flag.Arg(0), flag.Args()[1:]...)
		fullProgram = strings.Join(flag.Args(), " ")
		if len(fullProgram) > 200 {
			fullProgram = fullProgram[0:201]
		}

		stdout, err := program.StdoutPipe()
		assert(err, "Could not get stdout pipe: %s.", err)

		stderr, err := program.StderrPipe()
		assert(err, "Could not get stderr pipe: %s.", err)

		err = program.Start()
		assert(err, "Could not start program '%s': %s.", fullProgram, err)

		// Start goroutines for reading stdout and stderr, then mux them.
		stdoutLines := gobble(bufio.NewReader(stdout))
		stderrLines := gobble(bufio.NewReader(stderr))
		inlines = muxer(stdoutLines, stderrLines)
	}

	// Start the goroutine responsible for sending emails.
	// The send is also responsible for quitting the program.
	// (When all emails remaining have been sent.)
	send := sender()

	// Keep track of all lines emitted to stdout/stderr.
	// If the duration passes, stop and send whatever we have.
	// If the user interrupts the program, stop and send whatever we have.
	// If EOF is read on both stdout and stderr, send what we have.
	// We exit the program by closing the `send` channel, which will force
	// any remaining emails left to be sent.
	outlines := make([]string, 0)
	addMsg := func(msg string) {
		outlines = append(outlines, []string{"\n", "\n", msg + "\n"}...)
	}
	killed := false // set if user interrupted
	for {
		select {
		case <-time.After(period):
			if !flagNoPeriod {
				send <- outlines
				outlines = outlines[:0]
			}
		case <-sigged:
			if program != nil {
				program.Process.Kill()
				killed = true
				// continue reading stdout/stderr until program really quits.
			} else { // reading stdin, so send what we've got now.
				send <- outlines
				close(send)
				select {}
			}
		case line, ok := <-inlines:
			if !ok {
				// Program completed successfully!
				if killed {
					addMsg("Program interrupted.")
				} else {
					addMsg("Program completed successfully.")
				}
				send <- outlines
				close(send)
				select {}
			}
			outlines = append(outlines, line)
		}
	}
}

// muxer takes a list of incoming string channels, and muxes them all into
// the result channel. The channel returned is closed if and only if all
// channels in 'ins' have been closed.
func muxer(ins ...<-chan string) <-chan string {
	wg := new(sync.WaitGroup)
	combined := make(chan string, 500)
	for _, in := range ins {
		wg.Add(1)
		in := in
		go func() {
			for line := range in {
				combined <- line
			}
			wg.Done()
		}()
	}
	go func() {
		wg.Wait()
		close(combined)
	}()
	return combined
}

// sender receives chunks of lines on the result channel, and sends those
// chunks of lines via email.
//
// sender is the only goroutine that should exit the program in normal
// operation, which happens when there are no more chunks of lines to read.
func sender() chan<- []string {
	toSend := make([]string, 500)
	send := make(chan []string)
	go func() {
		for newLines := range send {
			switch {
			case flagInc:
				if len(newLines) == 0 {
					emailLines([]string{"Nothing to report."})
				} else {
					emailLines(newLines)
				}
			default:
				toSend = append(toSend, newLines...)
				emailLines(toSend)
			}
		}
		os.Exit(0)
	}()
	return send
}

// gobble reads lines from any buffered reader, sends the lines on the result
// channel, and closes the channel when EOF is read.
//
// gobble will quit the program with an error message if the input source
// cannot be read.
func gobble(buf *bufio.Reader) <-chan string {
	lines := make(chan string)
	go func() {
		for {
			line, err := buf.ReadString('\n')
			if err != nil && err != io.EOF {
				fatal("Could not read line: %s", err)
			}
			if !flagNoPass {
				fmt.Print(line)
			}
			lines <- line
			if err == io.EOF {
				close(lines)
				break
			}
		}
	}()
	return lines
}

// emailLines sends a chunk of lines via email.
func emailLines(lines []string) {
	var c *cmd.Command
	subj := fmt.Sprintf("%s%s", flagSubjectPrefix, fullProgram)

	if flagSendMail == "mailx" {
		c = cmd.New(flagSendMail, "-s", subj, flagTo)
		fmt.Fprint(c.BufStdin, strings.Join(lines, ""))
	} else {
		c = cmd.New(flagSendMail, "-t")
		date := time.Now().Format("Mon, 02 Jan 2006 15:04:05 -0700")
		fmt.Fprintf(c.BufStdin,
			`Subject: %s
From: %s
To: %s
Date: %s

%s`, subj, flagTo, flagTo, date, strings.Join(lines, ""))
	}

	if err := c.Run(); err != nil {
		log.Printf("Error sending mail '%s -t': %s.", flagSendMail, err)
	}
}

func usage() {
	log.Printf("Usage: %s [flags] command [args]\n\n", path.Base(os.Args[0]))
	log.Printf("cmail sends data read from `command` periodically, and/or\n" +
		"when EOF is reached.\n\n")

	flag.VisitAll(func(fl *flag.Flag) {
		var def string
		if len(fl.DefValue) > 0 {
			def = fmt.Sprintf(" (default: %s)", fl.DefValue)
		}
		log.Printf("-%s%s\n", fl.Name, def)
		log.Printf("    %s\n", strings.Replace(fl.Usage, "\n", "\n    ", -1))
	})
}

func assert(err error, format string, v ...interface{}) {
	if err != nil {
		fatal(format, v...)
	}
}

func fatal(format string, v ...interface{}) {
	log.Fatalf(format, v...)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"sync"
	"time"

	"golang.org/x/net/context"

	etcd "github.com/coreos/etcd/client"
	"github.com/docker/engine-api/client"
	"github.com/docker/engine-api/types"
	"github.com/docker/engine-api/types/filters"
)

func main() {
	var wg sync.WaitGroup

	wg.Add(1)

	go func() {

		ctx, cancel := context.WithCancel(context.Background())
		defer cancel()

		cli, err := client.NewEnvClient()
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println("Docker API connection OK.")

		cfg := etcd.Config{
			Endpoints: []string{"http://192.168.99.100:2379"},
			Transport: etcd.DefaultTransport,
			// set timeout per request to fail fast when the target endpoint is unavailable
			HeaderTimeoutPerRequest: time.Second,
		}
		c, err := etcd.New(cfg)
		if err != nil {
			log.Fatal(err)
		}
		kapi := etcd.NewKeysAPI(c)
		fmt.Println("etcd API connection OK.")

		filters := filters.NewArgs()
		filters.Add("type", "network")
		// filters.Add("network", "hae_default")

		options := types.EventsOptions{Filters: filters}
		reader, err := cli.Events(ctx, options)
		if err != nil {
			log.Fatal(err)
		}
		defer reader.Close()

		fmt.Println("Listening for events...")

		scanner := bufio.NewScanner(reader)
		for scanner.Scan() {

			fmt.Println(scanner.Text())

			updateConfig(cli, ctx, kapi)

			// data := new(events.Message)
			// json.Unmarshal(scanner.Bytes(), data)

			// containerID := data.Actor.Attributes["container"]
			// fmt.Printf("Network state changed for %v\n", containerID)
			//
			// containerInfo, err := cli.ContainerInspect(ctx, containerID)
			// if err != nil {
			// 	fmt.Fprintln(os.Stderr, "Inspect error", err)
			// }
			//
			// fmt.Println("## Infos ##")
			// fmt.Println("  name: ", containerInfo.Name)
			// fmt.Println("  labels: ", containerInfo.Config.Labels)

		}
		if err := scanner.Err(); err != nil {
			fmt.Fprintln(os.Stderr, "There was an error with the scanner", err)
		}

	}()

	wg.Wait()
}

func updateConfig(cli *client.Client, ctx context.Context, kapi etcd.KeysAPI) {

	filters := filters.NewArgs()
	filters.Add("label", "com.docker.compose.service=proxy")

	containers, err := cli.ContainerList(ctx, types.ContainerListOptions{Filter: filters})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(containers)

	// clear content
	kapi.Delete(ctx, "/subproxies", &etcd.DeleteOptions{Recursive: true})

	for _, container := range containers {
		kapi.Set(ctx, "/subproxies"+container.Names[0], "mokay", nil)
	}
}
<commit_msg>Check of the label 'proxy.domain_names' to get the domain names associated to the services<commit_after>package main

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"sync"
	"time"

	"golang.org/x/net/context"

	etcd "github.com/coreos/etcd/client"
	"github.com/docker/engine-api/client"
	"github.com/docker/engine-api/types"
	"github.com/docker/engine-api/types/filters"
)

func main() {
	var wg sync.WaitGroup

	wg.Add(1)

	go func() {

		ctx, cancel := context.WithCancel(context.Background())
		defer cancel()

		cli, err := client.NewEnvClient()
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println("Docker API connection OK.")

		cfg := etcd.Config{
			Endpoints: []string{"http://192.168.99.100:2379"},
			Transport: etcd.DefaultTransport,
			// set timeout per request to fail fast when the target endpoint is unavailable
			HeaderTimeoutPerRequest: time.Second,
		}
		c, err := etcd.New(cfg)
		if err != nil {
			log.Fatal(err)
		}
		kapi := etcd.NewKeysAPI(c)
		fmt.Println("etcd API connection OK.")

		filters := filters.NewArgs()
		filters.Add("type", "network")
		// filters.Add("network", "hae_default")

		options := types.EventsOptions{Filters: filters}
		reader, err := cli.Events(ctx, options)
		if err != nil {
			log.Fatal(err)
		}
		defer reader.Close()

		fmt.Println("Listening for events...")

		scanner := bufio.NewScanner(reader)
		for scanner.Scan() {

			fmt.Println(scanner.Text())

			updateConfig(cli, ctx, kapi)

			// data := new(events.Message)
			// json.Unmarshal(scanner.Bytes(), data)

			// containerID := data.Actor.Attributes["container"]
			// fmt.Printf("Network state changed for %v\n", containerID)
			//
			// containerInfo, err := cli.ContainerInspect(ctx, containerID)
			// if err != nil {
			// 	fmt.Fprintln(os.Stderr, "Inspect error", err)
			// }
			//
			// fmt.Println("## Infos ##")
			// fmt.Println("  name: ", containerInfo.Name)
			// fmt.Println("  labels: ", containerInfo.Config.Labels)

		}
		if err := scanner.Err(); err != nil {
			fmt.Fprintln(os.Stderr, "There was an error with the scanner", err)
		}

	}()

	wg.Wait()
}

func updateConfig(cli *client.Client, ctx context.Context, kapi etcd.KeysAPI) {

	filters := filters.NewArgs()
	// filters.Add("label", "com.docker.compose.project=hae")

	containers, err := cli.ContainerList(ctx, types.ContainerListOptions{Filter: filters})
	if err != nil {
		log.Fatal(err)
	}
	// fmt.Println(containers)

	// clear content
	kapi.Delete(ctx, "/subproxies", &etcd.DeleteOptions{Recursive: true})

	for _, container := range containers {
		if hosts, ok := container.Labels["proxy.domain_names"]; ok {
			fmt.Println(hosts)
			kapi.Set(ctx, "/subproxies"+container.Names[0]+"/hosts", hosts, nil)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 bs authors. 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"
	"os"
	"os/signal"
	"syscall"

	"github.com/google/gops/agent"
	"github.com/tsuru/bs/bslog"
	"github.com/tsuru/bs/config"
	"github.com/tsuru/bs/log"
	"github.com/tsuru/bs/metric"
	_ "github.com/tsuru/bs/metric/logstash"
	"github.com/tsuru/bs/status"
)

const (
	version = "v1.13-rc5"
)

var printVersion bool

type StopWaiter interface {
	Stop()
	Wait()
}

func init() {
	flag.BoolVar(&printVersion, "version", false, "Print version and exit")
}

func startSignalHandler(callback func(os.Signal), signals ...os.Signal) {
	sigChan := make(chan os.Signal, 4)
	go func() {
		if signal, ok := <-sigChan; ok {
			callback(signal)
		}
	}()
	signal.Notify(sigChan, signals...)
}

func main() {
	err := agent.Listen(&agent.Options{
		NoShutdownCleanup: true,
	})
	if err != nil {
		bslog.Fatalf("Unable to initialize gops agent: %s\n", err)
	}
	defer agent.Close()
	flag.Parse()
	if printVersion {
		fmt.Printf("bs version %s\n", version)
		return
	}
	lf := log.LogForwarder{
		BindAddress:     config.Config.SyslogListenAddress,
		DockerEndpoint:  config.Config.DockerEndpoint,
		EnabledBackends: config.Config.LogBackends,
	}
	err = lf.Start()
	if err != nil {
		bslog.Fatalf("Unable to initialize log forwarder: %s\n", err)
	}
	mRunner := metric.NewRunner(config.Config.DockerEndpoint, config.Config.MetricsInterval,
		config.Config.MetricsBackend)
	mRunner.EnableBasicMetrics = config.Config.MetricsEnableBasic
	mRunner.EnableConnMetrics = config.Config.MetricsEnableConn
	mRunner.EnableHostMetrics = config.Config.MetricsEnableHost
	err = mRunner.Start()
	if err != nil {
		bslog.Warnf("Unable to initialize metrics runner: %s\n", err)
	}
	reporter, err := status.NewReporter(&status.ReporterConfig{
		TsuruEndpoint:  config.Config.TsuruEndpoint,
		TsuruToken:     config.Config.TsuruToken,
		DockerEndpoint: config.Config.DockerEndpoint,
		Interval:       config.Config.StatusInterval,
	})
	if err != nil {
		bslog.Warnf("Unable to initialize status reporter: %s\n", err)
	}
	monitorEl := []StopWaiter{&lf, mRunner}
	if reporter != nil {
		monitorEl = append(monitorEl, reporter)
	}
	var signaled bool
	startSignalHandler(func(signal os.Signal) {
		signaled = true
		for _, m := range monitorEl {
			go m.Stop()
		}
	}, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
	for _, m := range monitorEl {
		m.Wait()
	}
	if !signaled {
		bslog.Fatalf("Exiting bs because no service could be initialized.")
	}
}
<commit_msg>bump to v1.13-rc6<commit_after>// Copyright 2016 bs authors. 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"
	"os"
	"os/signal"
	"syscall"

	"github.com/google/gops/agent"
	"github.com/tsuru/bs/bslog"
	"github.com/tsuru/bs/config"
	"github.com/tsuru/bs/log"
	"github.com/tsuru/bs/metric"
	_ "github.com/tsuru/bs/metric/logstash"
	"github.com/tsuru/bs/status"
)

const (
	version = "v1.13-rc6"
)

var printVersion bool

type StopWaiter interface {
	Stop()
	Wait()
}

func init() {
	flag.BoolVar(&printVersion, "version", false, "Print version and exit")
}

func startSignalHandler(callback func(os.Signal), signals ...os.Signal) {
	sigChan := make(chan os.Signal, 4)
	go func() {
		if signal, ok := <-sigChan; ok {
			callback(signal)
		}
	}()
	signal.Notify(sigChan, signals...)
}

func main() {
	err := agent.Listen(&agent.Options{
		NoShutdownCleanup: true,
	})
	if err != nil {
		bslog.Fatalf("Unable to initialize gops agent: %s\n", err)
	}
	defer agent.Close()
	flag.Parse()
	if printVersion {
		fmt.Printf("bs version %s\n", version)
		return
	}
	lf := log.LogForwarder{
		BindAddress:     config.Config.SyslogListenAddress,
		DockerEndpoint:  config.Config.DockerEndpoint,
		EnabledBackends: config.Config.LogBackends,
	}
	err = lf.Start()
	if err != nil {
		bslog.Fatalf("Unable to initialize log forwarder: %s\n", err)
	}
	mRunner := metric.NewRunner(config.Config.DockerEndpoint, config.Config.MetricsInterval,
		config.Config.MetricsBackend)
	mRunner.EnableBasicMetrics = config.Config.MetricsEnableBasic
	mRunner.EnableConnMetrics = config.Config.MetricsEnableConn
	mRunner.EnableHostMetrics = config.Config.MetricsEnableHost
	err = mRunner.Start()
	if err != nil {
		bslog.Warnf("Unable to initialize metrics runner: %s\n", err)
	}
	reporter, err := status.NewReporter(&status.ReporterConfig{
		TsuruEndpoint:  config.Config.TsuruEndpoint,
		TsuruToken:     config.Config.TsuruToken,
		DockerEndpoint: config.Config.DockerEndpoint,
		Interval:       config.Config.StatusInterval,
	})
	if err != nil {
		bslog.Warnf("Unable to initialize status reporter: %s\n", err)
	}
	monitorEl := []StopWaiter{&lf, mRunner}
	if reporter != nil {
		monitorEl = append(monitorEl, reporter)
	}
	var signaled bool
	startSignalHandler(func(signal os.Signal) {
		signaled = true
		for _, m := range monitorEl {
			go m.Stop()
		}
	}, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
	for _, m := range monitorEl {
		m.Wait()
	}
	if !signaled {
		bslog.Fatalf("Exiting bs because no service could be initialized.")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"./libaudit-go"
	"log"
	"os"
	"os/signal"
	"io/ioutil"
	"syscall"
	"time"
)

var done chan bool
var debug bool

func main() {
	// Request NetlinkSocket
	s, err := netlinkAudit.GetNetlinkSocket()
	if err != nil {
		log.Println(err)
		log.Fatalln("Error while availing socket! Exiting!")
	}
	defer s.Close()
	debug = false

	// Enable Audit
	err = netlinkAudit.AuditSetEnabled(s)
	if err != nil {
		log.Fatal("Error while enabling Audit !", err)
	}

	// Check if Audit is enabled
	err = netlinkAudit.AuditIsEnabled(s)

	// TODO: do all this inside the library?
	if debug == true {
		log.Println(netlinkAudit.ParsedResult)
	}
	if err == nil && netlinkAudit.ParsedResult.Enabled == 1 {
		log.Println("Enabled Audit!!")
	} else {
		log.Fatalln("Audit Not Enabled! Exiting")
	}

	// Set the maximum number of messages
	// that the kernel will send per second
	err = netlinkAudit.AuditSetRateLimit(s, 600)
	if err != nil {
		log.Fatalln("Error Setting Rate Limit!!", err)
	}

	// Set max limit audit message queue
	err = netlinkAudit.AuditSetBacklogLimit(s, 420)
	if err != nil {
		log.Fatalln("Error Setting Backlog Limit!!", err)
	}

	// Register current pid with audit
    err = netlinkAudit.AuditSetPid(s, uint32(syscall.Getpid()))
	if err == nil {
		log.Println("Set pid successful!!")
	}

	// Load all rules
	content, err := ioutil.ReadFile("audit.rules.json")
	if err != nil {
		log.Print("Error:", err)
		os.Exit(0)
	}

	// Set audit rules
	err = netlinkAudit.SetRules(s, content)
	// err = netlinkAudit.DeleteAllRules(s)
	if err != nil {
		log.Fatalln("Setting Rules Unsuccessful! Exiting")
	}

	done := make(chan bool, 1)
	msg := make(chan string)
	errchan := make(chan error)
	exit :=  make(chan os.Signal, 1)

	// Go rutine to monitor events and send them to channels
	go netlinkAudit.Getreply(s, done, msg, errchan)

	// Go rutine to extract events from channels
	go func() {
		f, err := os.OpenFile("/tmp/log", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0660)
		if err != nil {
			log.Fatalln("Error Creating File!!")
		}
		defer f.Close()

		for {
			select {
				case ev := <-msg:
					log.Println(ev + "\n")
					_, err := f.WriteString(ev + "\n")
					if err != nil {
						log.Println("Writing Error!!")
					}
				case ev := <-errchan:
					log.Println(ev)
			}
		}
	}()

	// Notify when control-c is pressed
	signal.Notify(exit, os.Interrupt)
	signal.Notify(exit, syscall.SIGTERM)
	go func() {
        <-exit
        done <- true
		close(done)
        os.Exit(1)
    }()

    for {
    	time.Sleep(time.Second * 5)
    }

	//Important point is that NLMSG_ERROR is also an acknowledgement from Kernel.
	//If the first 4 bytes of Data part are zero then it means the message is acknowledged
}
<commit_msg>move auditIsenabled checks to libauditgo<commit_after>package main

import (
	"./libaudit-go"
	"log"
	"os"
	"os/signal"
	"io/ioutil"
	"syscall"
	"time"
)

var done chan bool
var debug bool

func main() {
	// Request NetlinkSocket
	s, err := netlinkAudit.GetNetlinkSocket()
	if err != nil {
		log.Println(err)
		log.Fatalln("Error while availing socket! Exiting!")
	}
	defer s.Close()
	debug = false

	// Enable Audit
	err = netlinkAudit.AuditSetEnabled(s)
	if err != nil {
		log.Fatal("Error while enabling Audit !", err)
	}

	// Check if Audit is enabled
	status, err := netlinkAudit.AuditIsEnabled(s)

	if err == nil && status == 1 {
		log.Println("Enabled Audit!!")
	} else  if err == nil && status == 0 {
		log.Fatalln("Audit Not Enabled!")
	} else {
		log.Fatalln("Error while fetching status!",)
	}

	// Set the maximum number of messages
	// that the kernel will send per second
	err = netlinkAudit.AuditSetRateLimit(s, 600)
	if err != nil {
		log.Fatalln("Error Setting Rate Limit!!", err)
	}

	// Set max limit audit message queue
	err = netlinkAudit.AuditSetBacklogLimit(s, 420)
	if err != nil {
		log.Fatalln("Error Setting Backlog Limit!!", err)
	}

	// Register current pid with audit
    err = netlinkAudit.AuditSetPid(s, uint32(syscall.Getpid()))
	if err == nil {
		log.Println("Set pid successful!!")
	}

	// Load all rules
	content, err := ioutil.ReadFile("audit.rules.json")
	if err != nil {
		log.Print("Error:", err)
		os.Exit(0)
	}

	// Set audit rules
	err = netlinkAudit.SetRules(s, content)
	// err = netlinkAudit.DeleteAllRules(s)
	if err != nil {
		log.Fatalln("Setting Rules Unsuccessful! Exiting")
	}

	done := make(chan bool, 1)
	msg := make(chan string)
	errchan := make(chan error)
	exit :=  make(chan os.Signal, 1)

	// Go rutine to monitor events and send them to channels
	go netlinkAudit.Getreply(s, done, msg, errchan)

	// Go rutine to extract events from channels
	go func() {
		f, err := os.OpenFile("/tmp/log", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0660)
		if err != nil {
			log.Fatalln("Error Creating File!!")
		}
		defer f.Close()

		for {
			select {
				case ev := <-msg:
					log.Println(ev + "\n")
					_, err := f.WriteString(ev + "\n")
					if err != nil {
						log.Println("Writing Error!!")
					}
				case ev := <-errchan:
					log.Println(ev)
			}
		}
	}()

	// Notify when control-c is pressed
	signal.Notify(exit, os.Interrupt)
	signal.Notify(exit, syscall.SIGTERM)
	go func() {
        <-exit
        done <- true
		close(done)
        os.Exit(1)
    }()

    for {
    	time.Sleep(time.Second * 5)
    }

	//Important point is that NLMSG_ERROR is also an acknowledgement from Kernel.
	//If the first 4 bytes of Data part are zero then it means the message is acknowledged
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	stathat "github.com/stathat/go"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"time"
)

func KelvinToFahr(kelvin float64) float64 {
	return (kelvin-273.15)*1.8 + 32
}

type TickList struct {
	List []*Tick
}

type Tick struct {
	Kelvin float64 `json:"temp"`
	Fahr   float64
	Dt     int64
	Date   time.Time
}

func NewTickList(stationID string) (list *TickList, err error) {
	url := "http://api.openweathermap.org/data/2.1/history/station/" +
		stationID + "?type=tick"
	resp, err := http.Get(url)
	if err != nil {
		return
	}
	buf, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}
	err = json.Unmarshal(buf, &list)
	if err != nil {
		return
	}
	list.init()
	return
}

func (tick *Tick) init() {
	tick.Fahr = KelvinToFahr(tick.Kelvin)
	tick.Date = time.Unix(tick.Dt, 0)
}

func (list *TickList) init() {
	for _, tick := range list.List {
		tick.init()
	}
}

func (list *TickList) Len() int {
	return len(list.List)
}

func (list *TickList) Tick(i int) *Tick {
	return list.List[i]
}

func (tick *Tick) Print() {
	fmt.Printf("% 3.1f %v\n", tick.Fahr, tick.Date)
}

func (list *TickList) Print() {
	if len(list.List) < 0 {
		fmt.Println("no data from station!")
		return
	}
	fmt.Printf("%5v %v\n", "Temp", "Time")
	for _, tick := range list.List {
		tick.Print()
	}
}

type UsageError struct {
	error
}

func errMain() (err error) {
	help := false
	flag.BoolVar(&help, "help", false, "print this help message")
	verbose := false
	flag.BoolVar(&verbose, "verbose", false, "print actions to stdout")
	email := ""
	flag.StringVar(&email, "email", "",
		"email address registered with StatHat")
	id := ""
	flag.StringVar(&id, "station", "", "station id. ex: 1348")
	name := ""
	flag.StringVar(&name, "stat", "",
		`stat name. defaults to "weather-" + station. ex: weather-temp-KBGR`)
	interval := time.Minute * 10
	flag.DurationVar(&interval, "interval", time.Minute*10,
		"time since last run")
	flag.Parse()
	if help {
		flag.Usage()
		return nil
	}
	if email == "" {
		return UsageError{errors.New("email required")}
	}
	if id == "" {
		return UsageError{errors.New("station id required")}
	}
	if name == "" {
		name = "weather-" + id
	}

	prev := time.Now().Add(-interval)

	list, err := NewTickList(id)
	if err != nil {
		return err
	}

	if list.Len() < 1 {
		// BUG(sk): likely there's useful data in the json on error
		return errors.New("no weather data returned")
	}

	recent := list.Tick(list.Len() - 1)
	if recent.Date.Before(prev) {
		if verbose {
			fmt.Println("already reported latest data")
		}
		return nil
	}

	name = "weather-temp" + name
	if verbose {
		fmt.Println(name, email, recent.Fahr)
	}
	// the return value is a farce
	_ = stathat.PostEZValue(name, email, recent.Fahr)
	// if you make anything print in here, convert the whole thing to use
	// log instead of fmt.
	finished := stathat.WaitUntilFinished(time.Second * 5)
	if !finished {
		return errors.New("stathat timed out")
	}

	return nil
}

func main() {
	err := errMain()
	if err == nil {
		os.Exit(0)
	}
	if _, ok := err.(UsageError); ok {
		flag.Usage()
	}
	fmt.Fprintf(os.Stderr, "%v: Error: %v\n", filepath.Base(os.Args[0]), err)
	os.Exit(1)
}
<commit_msg>Add more information to error when no weather data is returned<commit_after>package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	stathat "github.com/stathat/go"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"time"
)

func KelvinToFahr(kelvin float64) float64 {
	return (kelvin-273.15)*1.8 + 32
}

type TickList struct {
	Message string
	Code    string `json:"cod"`
	List    []*Tick
}

type Tick struct {
	Kelvin float64 `json:"temp"`
	Fahr   float64
	Dt     int64
	Date   time.Time
}

func NewTickList(stationID string) (list *TickList, err error) {
	url := "http://api.openweathermap.org/data/2.1/history/station/" +
		stationID + "?type=tick"
	resp, err := http.Get(url)
	if err != nil {
		return
	}
	buf, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}
	err = json.Unmarshal(buf, &list)
	if err != nil {
		return
	}
	list.init()
	return
}

func (tick *Tick) init() {
	tick.Fahr = KelvinToFahr(tick.Kelvin)
	tick.Date = time.Unix(tick.Dt, 0)
}

func (list *TickList) init() {
	for _, tick := range list.List {
		tick.init()
	}
}

func (list *TickList) Len() int {
	return len(list.List)
}

func (list *TickList) Tick(i int) *Tick {
	return list.List[i]
}

func (tick *Tick) Print() {
	fmt.Printf("% 3.1f %v\n", tick.Fahr, tick.Date)
}

func (list *TickList) Print() {
	if len(list.List) < 0 {
		fmt.Println("no data from station!")
		return
	}
	fmt.Printf("%5v %v\n", "Temp", "Time")
	for _, tick := range list.List {
		tick.Print()
	}
}

type UsageError struct {
	error
}

func errMain() (err error) {
	help := false
	flag.BoolVar(&help, "help", false, "print this help message")
	verbose := false
	flag.BoolVar(&verbose, "verbose", false, "print actions to stdout")
	email := ""
	flag.StringVar(&email, "email", "",
		"email address registered with StatHat")
	id := ""
	flag.StringVar(&id, "station", "", "station id. ex: 1348")
	name := ""
	flag.StringVar(&name, "stat", "",
		`stat name. defaults to "weather-" + station. ex: weather-temp-KBGR`)
	interval := time.Minute * 10
	flag.DurationVar(&interval, "interval", time.Minute*10,
		"time since last run")
	flag.Parse()
	if help {
		flag.Usage()
		return nil
	}
	if email == "" {
		return UsageError{errors.New("email required")}
	}
	if id == "" {
		return UsageError{errors.New("station id required")}
	}
	if name == "" {
		name = "weather-" + id
	}

	prev := time.Now().Add(-interval)

	list, err := NewTickList(id)
	if err != nil {
		return err
	}

	if list.Len() < 1 {
		return fmt.Errorf("no weather data returned: %v: %v", list.Code,
			list.Message)
	}

	recent := list.Tick(list.Len() - 1)
	if recent.Date.Before(prev) {
		if verbose {
			fmt.Println("already reported latest data")
		}
		return nil
	}

	name = "weather-temp" + name
	if verbose {
		fmt.Println(name, email, recent.Fahr)
	}
	// the return value is a farce
	_ = stathat.PostEZValue(name, email, recent.Fahr)
	// if you make anything print in here, convert the whole thing to use
	// log instead of fmt.
	finished := stathat.WaitUntilFinished(time.Second * 5)
	if !finished {
		return errors.New("stathat timed out")
	}

	return nil
}

func main() {
	err := errMain()
	if err == nil {
		os.Exit(0)
	}
	if _, ok := err.(UsageError); ok {
		flag.Usage()
	}
	fmt.Fprintf(os.Stderr, "%v: Error: %v\n", filepath.Base(os.Args[0]), err)
	os.Exit(1)
}
<|endoftext|>


<commit_before>package cache

import (
	"container/list"
	"sync"
	"time"
)

type CacheStrategy int

const (
	CacheStrategyRandom    = CacheStrategy(1)
	CacheStrategyOldest    = CacheStrategy(2)
	CacheStrategyOldestLRU = CacheStrategy(3)
	CacheStrategyLFU       = CacheStrategy(4)
)

type Cache struct {
	l        *list.List
	contents map[string]*list.Element
	mutex    *sync.Mutex
	dead     bool
	hits     int64
	misses   int64
	options  CacheOptions
}

type CachedItem struct {
	key     string
	value   interface{}
	running bool
	used    int
}

type CacheOptions struct {
	MaxEntries     int //If this is set to 0, you must have an expiration time set.
	Upper          int
	CacheStrategy  CacheStrategy
	ExpirationTime time.Duration
	JobInvertal    time.Duration
	SafeRange      int
}

func NewCache(c CacheOptions) *Cache {
	newc := &Cache{}
	newc.options = c
	newc.contents = make(map[string]*list.Element)
	newc.mutex = &sync.Mutex{}
	newc.l = list.New()
	newc.dead = true
	if newc.options.MaxEntries > newc.options.Upper && newc.options.Upper > 0 {
		newc.options.Upper = newc.options.MaxEntries
	}
	return newc
}

func (c *Cache) Set(key string, value interface{}) {
	c.lock()
	defer c.unlock()
	if test := c.contents[key]; test != nil {
		//TODO fix expiration in this scenario?
		c.deleteItem(test)
		if value == nil {
			return
		}
	}
	if c.l.Len()+1 >= c.options.Upper && c.options.Upper > 0 {
		c.burnEntryByStrategy()
	}
	newitem := &CachedItem{}
	newitem.value = value
	newitem.key = key
	e := c.l.PushFront(newitem)
	c.contents[key] = e
	if c.options.ExpirationTime > 0 && !c.dead {
		go c.expireIn(c.options.ExpirationTime, e)
	}
}

func (c *Cache) Get(key string) interface{} {
	c.lock()
	defer c.unlock()
	k, ok := c.contents[key]
	if ok {
		c.hits++
		if c.options.CacheStrategy == CacheStrategyOldestLRU || c.options.CacheStrategy == CacheStrategyLFU {
			c.l.MoveToFront(k)
			k.Value.(*CachedItem).used++
		}
		return k.Value.(*CachedItem).value
	} else {
		c.misses++
		return nil
	}
}

func (c *Cache) Start() {
	c.dead = false
	if c.options.JobInvertal > 0 {
		go c.runner()
	}
	for _, b := range c.contents {
		if !b.Value.(*CachedItem).running && c.options.ExpirationTime > 0 {
			go c.expireIn(c.options.ExpirationTime, b)
		}
	}
}

func (c *Cache) Stop() {
	c.dead = true
}

func (c *Cache) Hits() int64 {
	return c.hits
}

func (c *Cache) Misses() int64 {
	return c.misses
}

func (c *Cache) RemoveItem(key string) {
	c.lock()
	defer c.unlock()
	c.deleteItem(c.contents[key])
}

func (c *Cache) Trim(num int) {
	c.lock()
	defer c.unlock()
	for num > 0 && c.l.Len() > 0 {
		num--
		c.burnEntryByStrategy()
	}
}

func (c *Cache) Bump(key string) {
	k, ok := c.contents[key]
	if ok {
		c.l.MoveToFront(k)
	}
}

//For now we, can't use this internally since it is mutex'd
func (c *Cache) Len() int {
	c.lock()
	defer c.unlock()
	return c.l.Len()
}

//private functions
func (c *Cache) burnEntryByStrategy() {
	if c.options.CacheStrategy == CacheStrategyOldest || c.options.CacheStrategy == CacheStrategyOldestLRU {
		c.burnEntryByOldest()
	} else if c.options.CacheStrategy == CacheStrategyLFU {
		c.burnEntryByLFU()
	} else {
		c.burnEntryByRandom()
	}
}

func (c *Cache) burnEntryByRandom() {
	for _, a := range c.contents {
		c.deleteItem(a)
		break
	}
}

func (c *Cache) burnEntryByOldest() {
	i := c.l.Back()
	if i != nil {
		c.deleteItem(i)
	}
}

func (c *Cache) burnEntryByLFU() {
	counter := 0
	var found bool
	for {
		for e := c.l.Back(); e != nil; e = e.Prev() {
			if e.Value.(*CachedItem).used == counter {
				c.deleteItem(e)
				found = true
				break
			}
		}
		if found {
			break
		} else {
			counter++
		}
	}
}

func (c *Cache) lock() {
	c.mutex.Lock()
}

func (c *Cache) unlock() {
	c.mutex.Unlock()
}

func (c *Cache) runner() {
	for {
		time.Sleep(c.options.JobInvertal)
		if c.dead {
			break
		}
		c.lock()
		if c.options.MaxEntries > 0 {
			for len(c.contents) > c.options.MaxEntries-c.options.SafeRange {
				c.burnEntryByStrategy()
			}
		}
		c.unlock()
	}
}

func (c *Cache) expireIn(t time.Duration, i *list.Element) {
	i.Value.(*CachedItem).running = true
	time.Sleep(t)
	if c.dead {
		i.Value.(*CachedItem).running = false
		return
	}
	c.lock()
	defer c.unlock()
	c.deleteItem(i)
}

func (c *Cache) deleteItem(i *list.Element) {
	c.l.Remove(i)
	delete(c.contents, i.Value.(*CachedItem).key)
}
<commit_msg>handle expiration on key deletion<commit_after>package cache

import (
	"container/list"
	"sync"
	"time"
)

type CacheStrategy int

const (
	CacheStrategyRandom    = CacheStrategy(1)
	CacheStrategyOldest    = CacheStrategy(2)
	CacheStrategyOldestLRU = CacheStrategy(3)
	CacheStrategyLFU       = CacheStrategy(4)
)

type Cache struct {
	l        *list.List
	contents map[string]*list.Element
	mutex    *sync.Mutex
	dead     bool
	hits     int64
	misses   int64
	options  CacheOptions
}

type CachedItem struct {
	key     string
	value   interface{}
	running bool
	used    int
}

type CacheOptions struct {
	MaxEntries     int //If this is set to 0, you must have an expiration time set.
	Upper          int
	CacheStrategy  CacheStrategy
	ExpirationTime time.Duration
	JobInvertal    time.Duration
	SafeRange      int
}

func NewCache(c CacheOptions) *Cache {
	newc := &Cache{}
	newc.options = c
	newc.contents = make(map[string]*list.Element)
	newc.mutex = &sync.Mutex{}
	newc.l = list.New()
	newc.dead = true
	if newc.options.MaxEntries > newc.options.Upper && newc.options.Upper > 0 {
		newc.options.Upper = newc.options.MaxEntries
	}
	return newc
}

func (c *Cache) Set(key string, value interface{}) {
	c.lock()
	defer c.unlock()
	if test := c.contents[key]; test != nil {
		//TODO fix expiration in this scenario?
		c.deleteItem(test)
		if value == nil {
			return
		}
	}
	if c.l.Len()+1 >= c.options.Upper && c.options.Upper > 0 {
		c.burnEntryByStrategy()
	}
	newitem := &CachedItem{}
	newitem.value = value
	newitem.key = key
	e := c.l.PushFront(newitem)
	c.contents[key] = e
	if c.options.ExpirationTime > 0 && !c.dead {
		go c.expireIn(c.options.ExpirationTime, e)
	}
}

func (c *Cache) Get(key string) interface{} {
	c.lock()
	defer c.unlock()
	k, ok := c.contents[key]
	if ok {
		c.hits++
		if c.options.CacheStrategy == CacheStrategyOldestLRU || c.options.CacheStrategy == CacheStrategyLFU {
			c.l.MoveToFront(k)
			k.Value.(*CachedItem).used++
		}
		return k.Value.(*CachedItem).value
	} else {
		c.misses++
		return nil
	}
}

func (c *Cache) Start() {
	c.lock()
	defer c.unlock()
	c.dead = false
	if c.options.JobInvertal > 0 {
		go c.runner()
	}
	for _, b := range c.contents {
		if !b.Value.(*CachedItem).running && c.options.ExpirationTime > 0 {
			go c.expireIn(c.options.ExpirationTime, b)
		}
	}
}

func (c *Cache) Stop() {
	c.lock()
	defer c.unlock()
	c.dead = true
	for _, k := range c.contents {
		k.Value.(*CachedItem).running = false
	}
}

func (c *Cache) Hits() int64 {
	return c.hits
}

func (c *Cache) Misses() int64 {
	return c.misses
}

func (c *Cache) RemoveItem(key string) {
	c.lock()
	defer c.unlock()
	c.deleteItem(c.contents[key])
}

func (c *Cache) Trim(num int) {
	c.lock()
	defer c.unlock()
	for num > 0 && c.l.Len() > 0 {
		num--
		c.burnEntryByStrategy()
	}
}

func (c *Cache) Bump(key string) {
	k, ok := c.contents[key]
	if ok {
		c.l.MoveToFront(k)
	}
}

//For now we, can't use this internally since it is mutex'd
func (c *Cache) Len() int {
	c.lock()
	defer c.unlock()
	return c.l.Len()
}

//private functions
func (c *Cache) burnEntryByStrategy() {
	if c.options.CacheStrategy == CacheStrategyOldest || c.options.CacheStrategy == CacheStrategyOldestLRU {
		c.burnEntryByOldest()
	} else if c.options.CacheStrategy == CacheStrategyLFU {
		c.burnEntryByLFU()
	} else {
		c.burnEntryByRandom()
	}
}

func (c *Cache) burnEntryByRandom() {
	for _, a := range c.contents {
		c.deleteItem(a)
		break
	}
}

func (c *Cache) burnEntryByOldest() {
	i := c.l.Back()
	if i != nil {
		c.deleteItem(i)
	}
}

func (c *Cache) burnEntryByLFU() {
	counter := 0
	var found bool
	for {
		for e := c.l.Back(); e != nil; e = e.Prev() {
			if e.Value.(*CachedItem).used == counter {
				c.deleteItem(e)
				found = true
				break
			}
		}
		if found {
			break
		} else {
			counter++
		}
	}
}

func (c *Cache) lock() {
	c.mutex.Lock()
}

func (c *Cache) unlock() {
	c.mutex.Unlock()
}

func (c *Cache) runner() {
	for {
		time.Sleep(c.options.JobInvertal)
		if c.dead {
			break
		}
		c.lock()
		if c.options.MaxEntries > 0 {
			for len(c.contents) > c.options.MaxEntries-c.options.SafeRange {
				c.burnEntryByStrategy()
			}
		}
		c.unlock()
	}
}

func (c *Cache) expireIn(t time.Duration, i *list.Element) {
	i.Value.(*CachedItem).running = true
	time.Sleep(t)
	if c.dead || !i.Value.(*CachedItem).running {
		i.Value.(*CachedItem).running = false
		return
	}
	c.lock()
	defer c.unlock()
	c.deleteItem(i)
}

func (c *Cache) deleteItem(i *list.Element) {
	i.Value.(*CachedItem).running = false
	c.l.Remove(i)
	delete(c.contents, i.Value.(*CachedItem).key)
}
<|endoftext|>


<commit_before>

<commit_msg>initial<commit_after>// Copyright 2016 Jacob Taylor jacob@ablox.io
// License: Apache2 - http://www.apache.org/licenses/LICENSE-2.0
package main

import (
    "fmt"
)

func main() {
    fmt.Printf("replicat online....")
    defer fmt.Printf("End of line\n")
}
<|endoftext|>