<commit_before>package main

import (
	"fmt"
	"github.com/codegangsta/cli"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

func main() {
	app := cli.NewApp()
	app.Name = "exercism"
	app.Usage = "A command line tool to interact with http://exercism.io"
	app.Version = VERSION
	app.Commands = []cli.Command{
		{
			Name:      "demo",
			ShortName: "d",
			Usage:     "Fetch first assignment for each language from exercism.io",
			Action: func(c *cli.Context) {
				config, err := ConfigFromFile(HomeDir())
				if err != nil {
					demoDir, err := DemoDirectory()
					if err != nil {
						fmt.Println(err)
						return
					}
					config = Config{
						Hostname:          "http://exercism.io",
						ApiKey:            "",
						ExercismDirectory: demoDir,
					}
				}
				assignments, err := FetchAssignments(config,
					FetchEndpoints["demo"])
				if err != nil {
					fmt.Println(err)
					return
				}

				for _, a := range assignments {
					err := SaveAssignment(config.ExercismDirectory, a)
					if err != nil {
						fmt.Println(err)
					}
				}
			},
		},
		{
			Name:      "fetch",
			ShortName: "f",
			Usage:     "Fetch current assignment from exercism.io",
			Action: func(c *cli.Context) {
				config, err := ConfigFromFile(HomeDir())
				if err != nil {
					fmt.Println("Are you sure you are logged in? Please login again.")
					return
				}
				assignments, err := FetchAssignments(config,
					FetchEndpoints["current"])
				if err != nil {
					fmt.Println(err)
					return
				}

				for _, a := range assignments {
					err := SaveAssignment(config.ExercismDirectory, a)
					if err != nil {
						fmt.Println(err)
					}
				}
			},
		},
		{
			Name:      "login",
			ShortName: "l",
			Usage:     "Save exercism.io api credentials",
			Action: func(c *cli.Context) {
				ConfigToFile(HomeDir(), askForConfigInfo())
			},
		},
		{
			Name:      "logout",
			ShortName: "o",
			Usage:     "Clear exercism.io api credentials",
			Action: func(c *cli.Context) {
				Logout(HomeDir())
			},
		},
		{
			Name:      "peek",
			ShortName: "p",
			Usage:     "Fetch upcoming assignment from exercism.io",
			Action: func(c *cli.Context) {
				config, err := ConfigFromFile(HomeDir())
				if err != nil {
					fmt.Println("Are you sure you are logged in? Please login again.")
					return
				}
				assignments, err := FetchAssignments(config,
					FetchEndpoints["next"])
				if err != nil {
					fmt.Println(err)
					return
				}

				for _, a := range assignments {
					err := SaveAssignment(config.ExercismDirectory, a)
					if err != nil {
						fmt.Println(err)
					}
				}
			},
		},
		{
			Name:      "submit",
			ShortName: "s",
			Usage:     "Submit code to exercism.io on your current assignment",
			Action: func(c *cli.Context) {
				config, err := ConfigFromFile(HomeDir())
				if err != nil {
					fmt.Println("Are you sure you are logged in? Please login again.")
					return
				}

				if len(c.Args()) == 0 {
					fmt.Println("Please enter a file name")
					return
				}

				filename := c.Args()[0]

				// Make filename relative to config.ExercismDirectory.
				absPath, err := absolutePath(filename)
				if err != nil {
					fmt.Printf("Couldn't find %v: %v\n", filename, err)
					return
				}
				exDir := config.ExercismDirectory + string(filepath.Separator)
				if !strings.HasPrefix(absPath, exDir) {
					fmt.Printf("%v is not under your exercism project path (%v)\n", absPath, exDir)
					return
				}
				filename = absPath[len(exDir):]

				if IsTest(filename) {
					fmt.Println("It looks like this is a test, please enter an example file name.")
					return
				}

				code, err := ioutil.ReadFile(absPath)
				if err != nil {
					fmt.Printf("Error reading %v: %v\n", absPath, err)
					return
				}

				response, err := SubmitAssignment(config, filename, code)
				if err != nil {
					fmt.Printf("There was an issue with your submission: %v\n", err)
					return
				}

				fmt.Printf("For feedback on your submission visit %s%s.\n",
					config.Hostname, response.SubmissionPath)

			},
		},
		{
			Name:      "whoami",
			ShortName: "w",
			Usage:     "Get the github username that you are logged in as",
			Action: func(c *cli.Context) {
				config, err := ConfigFromFile(HomeDir())
				if err != nil {
					fmt.Println("Are you sure you are logged in? Please login again.")
					return
				}

				fmt.Println(config.GithubUsername)
			},
		},
	}
	app.Run(os.Args)
}

func askForConfigInfo() (c Config) {
	var un, key, dir string

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

	fmt.Print("Your GitHub username: ")
	_, err = fmt.Scanln(&un)
	if err != nil {
		panic(err)
	}

	fmt.Print("Your exercism.io API key: ")
	_, err = fmt.Scanln(&key)
	if err != nil {
		panic(err)
	}

	fmt.Println("What is your exercism exercises project path?")
	fmt.Printf("Press Enter to select the default (%s):\n", currentDir)
	fmt.Print("> ")
	_, err = fmt.Scanln(&dir)
	if err != nil && err.Error() != "unexpected newline" {
		panic(err)
	}
	dir, err = absolutePath(dir)
	if err != nil {
		panic(err)
	}

	if dir == "" {
		dir = currentDir
	}

	return Config{un, key, ReplaceTilde(dir), "http://exercism.io"}
}

func absolutePath(path string) (string, error) {
	path, err := filepath.Abs(path)
	if err != nil {
		return "", err
	}
	return filepath.EvalSymlinks(path)
}
<commit_msg>Print current exercises<commit_after>package main

import (
	"fmt"
	"github.com/codegangsta/cli"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

func main() {
	app := cli.NewApp()
	app.Name = "exercism"
	app.Usage = "A command line tool to interact with http://exercism.io"
	app.Version = VERSION
	app.Commands = []cli.Command{
		{
			Name:      "current",
			ShortName: "c",
			Usage:     "Show the current assignments",
			Action: func(c *cli.Context) {
				config, err := ConfigFromFile(HomeDir())
				if err != nil {
					fmt.Println("Are you sure you are logged in? Please login again.")
					return
				}
				currentAssignments, err := FetchAssignments(config, FetchEndpoints["current"])
				if err != nil {
					fmt.Println(err)
					return
				}

				fmt.Println("Current Assignments")

				for _, a := range currentAssignments {
					fmt.Printf("%v: %v\n", strings.Title(a.Track), a.Slug)
				}
			},
		},
		{
			Name:      "demo",
			ShortName: "d",
			Usage:     "Fetch first assignment for each language from exercism.io",
			Action: func(c *cli.Context) {
				config, err := ConfigFromFile(HomeDir())
				if err != nil {
					demoDir, err := DemoDirectory()
					if err != nil {
						fmt.Println(err)
						return
					}
					config = Config{
						Hostname:          "http://exercism.io",
						ApiKey:            "",
						ExercismDirectory: demoDir,
					}
				}
				assignments, err := FetchAssignments(config,
					FetchEndpoints["demo"])
				if err != nil {
					fmt.Println(err)
					return
				}

				for _, a := range assignments {
					err := SaveAssignment(config.ExercismDirectory, a)
					if err != nil {
						fmt.Println(err)
					}
				}
			},
		},
		{
			Name:      "fetch",
			ShortName: "f",
			Usage:     "Fetch current assignment from exercism.io",
			Action: func(c *cli.Context) {
				config, err := ConfigFromFile(HomeDir())
				if err != nil {
					fmt.Println("Are you sure you are logged in? Please login again.")
					return
				}
				assignments, err := FetchAssignments(config,
					FetchEndpoints["current"])
				if err != nil {
					fmt.Println(err)
					return
				}

				for _, a := range assignments {
					err := SaveAssignment(config.ExercismDirectory, a)
					if err != nil {
						fmt.Println(err)
					}
				}
			},
		},
		{
			Name:      "login",
			ShortName: "l",
			Usage:     "Save exercism.io api credentials",
			Action: func(c *cli.Context) {
				ConfigToFile(HomeDir(), askForConfigInfo())
			},
		},
		{
			Name:      "logout",
			ShortName: "o",
			Usage:     "Clear exercism.io api credentials",
			Action: func(c *cli.Context) {
				Logout(HomeDir())
			},
		},
		{
			Name:      "peek",
			ShortName: "p",
			Usage:     "Fetch upcoming assignment from exercism.io",
			Action: func(c *cli.Context) {
				config, err := ConfigFromFile(HomeDir())
				if err != nil {
					fmt.Println("Are you sure you are logged in? Please login again.")
					return
				}
				assignments, err := FetchAssignments(config,
					FetchEndpoints["next"])
				if err != nil {
					fmt.Println(err)
					return
				}

				for _, a := range assignments {
					err := SaveAssignment(config.ExercismDirectory, a)
					if err != nil {
						fmt.Println(err)
					}
				}
			},
		},
		{
			Name:      "submit",
			ShortName: "s",
			Usage:     "Submit code to exercism.io on your current assignment",
			Action: func(c *cli.Context) {
				config, err := ConfigFromFile(HomeDir())
				if err != nil {
					fmt.Println("Are you sure you are logged in? Please login again.")
					return
				}

				if len(c.Args()) == 0 {
					fmt.Println("Please enter a file name")
					return
				}

				filename := c.Args()[0]

				// Make filename relative to config.ExercismDirectory.
				absPath, err := absolutePath(filename)
				if err != nil {
					fmt.Printf("Couldn't find %v: %v\n", filename, err)
					return
				}
				exDir := config.ExercismDirectory + string(filepath.Separator)
				if !strings.HasPrefix(absPath, exDir) {
					fmt.Printf("%v is not under your exercism project path (%v)\n", absPath, exDir)
					return
				}
				filename = absPath[len(exDir):]

				if IsTest(filename) {
					fmt.Println("It looks like this is a test, please enter an example file name.")
					return
				}

				code, err := ioutil.ReadFile(absPath)
				if err != nil {
					fmt.Printf("Error reading %v: %v\n", absPath, err)
					return
				}

				response, err := SubmitAssignment(config, filename, code)
				if err != nil {
					fmt.Printf("There was an issue with your submission: %v\n", err)
					return
				}

				fmt.Printf("For feedback on your submission visit %s%s.\n",
					config.Hostname, response.SubmissionPath)

			},
		},
		{
			Name:      "whoami",
			ShortName: "w",
			Usage:     "Get the github username that you are logged in as",
			Action: func(c *cli.Context) {
				config, err := ConfigFromFile(HomeDir())
				if err != nil {
					fmt.Println("Are you sure you are logged in? Please login again.")
					return
				}

				fmt.Println(config.GithubUsername)
			},
		},
	}
	app.Run(os.Args)
}

func askForConfigInfo() (c Config) {
	var un, key, dir string

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

	fmt.Print("Your GitHub username: ")
	_, err = fmt.Scanln(&un)
	if err != nil {
		panic(err)
	}

	fmt.Print("Your exercism.io API key: ")
	_, err = fmt.Scanln(&key)
	if err != nil {
		panic(err)
	}

	fmt.Println("What is your exercism exercises project path?")
	fmt.Printf("Press Enter to select the default (%s):\n", currentDir)
	fmt.Print("> ")
	_, err = fmt.Scanln(&dir)
	if err != nil && err.Error() != "unexpected newline" {
		panic(err)
	}
	dir, err = absolutePath(dir)
	if err != nil {
		panic(err)
	}

	if dir == "" {
		dir = currentDir
	}

	return Config{un, key, ReplaceTilde(dir), "http://exercism.io"}
}

func absolutePath(path string) (string, error) {
	path, err := filepath.Abs(path)
	if err != nil {
		return "", err
	}
	return filepath.EvalSymlinks(path)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/fsouza/go-dockerclient"
	"os"
)

func main() {
	endpoint := "unix:///var/run/docker.sock"
	client, _ := docker.NewClient(endpoint)

	// Create container
	container, err := client.CreateContainer(docker.CreateContainerOptions{
		Config: &docker.Config{
			Image:        "fgrehm/devstep:v0.0.1",
			Cmd:          []string{"/.devstep/bin/build-project", "/workspace"},
			OpenStdin:    true,
			StdinOnce:    true,
			AttachStdin:  true,
			AttachStdout: true,
			AttachStderr: true,
			Tty:          true,
		},
	})

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

	defer func() {
		client.RemoveContainer(docker.RemoveContainerOptions{
			ID:    container.ID,
			Force: true,
		})

		//client.RemoveImage("new-devstep/testing-build:latest")
	}()

	if err := runBuild(client, container); err != nil {
		fmt.Println("Error during build!")
		fmt.Println(err)
		os.Exit(1)
	}
	if err := commit(client, container.ID); err != nil {
		fmt.Println("Error during commit!")
		fmt.Println(err)
		os.Exit(1)
	}
}

func runBuild(client *docker.Client, container *docker.Container) (err error) {
	// Start container
	// pwd, err := os.Getwd()
	// if err != nil {
	// 	fmt.Println(err)
	// 	os.Exit(1)
	// }

	exitedChannel := make(chan struct{})

	// This goroutine will listen to Docker events and will signal that is has
	// stopped at the exitedChannel
	go listenForContainerExit(client, container.ID, exitedChannel)

	// Attach to the container on a separate goroutine
	go attachToContainer(client, container.ID)

	binds := []string{
		"/home/fabio/projects/oss/devstep-cli:/workspace",
		"/tmp/devstep/cache:/.devstep/cache",
	}

	err = client.StartContainer(container.ID, &docker.HostConfig{
		Binds: binds,
	})

	<-exitedChannel

	// TODO: Check for exit status

	return err
}

func listenForContainerExit(client *docker.Client, containerID string, exitedChannel chan struct{}) error {
	listenerChannel := make(chan *docker.APIEvents)
	client.AddEventListener(listenerChannel)

	for {
		event := <-listenerChannel
		if event.ID == containerID && event.Status == "die" {
			exitedChannel <- struct{}{}
		}
	}
}

func attachToContainer(client *docker.Client, containerID string) {
	client.AttachToContainer(docker.AttachToContainerOptions{
		Container:    containerID,
		OutputStream: os.Stdout,
		ErrorStream:  os.Stderr,
		Stdout:       true,
		Stderr:       true,
		Stream:       true,
		RawTerminal:  true,
	})
}

func commit(client *docker.Client, containerID string) error {
	_, err := client.CommitContainer(docker.CommitContainerOptions{
		Container:  containerID,
		Repository: "new-devstep/testing-build",
		Tag:        "latest",
		Run: &docker.Config{
			Cmd: []string{"/.devstep/bin/hack"},
		},
	})
	return err
}
<commit_msg>This is no longer needed<commit_after>package main

import (
	"fmt"
	"github.com/fsouza/go-dockerclient"
	"os"
)

func main() {
	endpoint := "unix:///var/run/docker.sock"
	client, _ := docker.NewClient(endpoint)

	// Create container
	container, err := client.CreateContainer(docker.CreateContainerOptions{
		Config: &docker.Config{
			Image:        "fgrehm/devstep:v0.0.1",
			Cmd:          []string{"/.devstep/bin/build-project", "/workspace"},
			OpenStdin:    true,
			StdinOnce:    true,
			AttachStdin:  true,
			AttachStdout: true,
			AttachStderr: true,
			Tty:          true,
		},
	})

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

	defer func() {
		client.RemoveContainer(docker.RemoveContainerOptions{
			ID:    container.ID,
			Force: true,
		})

		//client.RemoveImage("new-devstep/testing-build:latest")
	}()

	if err := runBuild(client, container); err != nil {
		fmt.Println("Error during build!")
		fmt.Println(err)
		os.Exit(1)
	}
	if err := commit(client, container.ID); err != nil {
		fmt.Println("Error during commit!")
		fmt.Println(err)
		os.Exit(1)
	}
}

func runBuild(client *docker.Client, container *docker.Container) (err error) {
	// Start container
	// pwd, err := os.Getwd()
	// if err != nil {
	// 	fmt.Println(err)
	// 	os.Exit(1)
	// }

	binds := []string{
		"/home/fabio/projects/oss/devstep-cli:/workspace",
		"/tmp/devstep/cache:/.devstep/cache",
	}

	// Keep an eye on the build...
	err = client.StartContainer(container.ID, &docker.HostConfig{
		Binds: binds,
	})

	if err != nil {
		return err
	}

	// Keep an eye on the build...
	attachToContainer(client, container.ID)

	// TODO: Check for exit status

	return err
}

func listenForContainerExit(client *docker.Client, containerID string, exitedChannel chan struct{}) error {
	listenerChannel := make(chan *docker.APIEvents)
	client.AddEventListener(listenerChannel)

	for {
		event := <-listenerChannel
		if event.ID == containerID && event.Status == "die" {
			exitedChannel <- struct{}{}
		}
	}
}

func attachToContainer(client *docker.Client, containerID string) {
	client.AttachToContainer(docker.AttachToContainerOptions{
		Container:    containerID,
		OutputStream: os.Stdout,
		ErrorStream:  os.Stderr,
		Stdout:       true,
		Stderr:       true,
		Stream:       true,
		RawTerminal:  true,
	})
}

func commit(client *docker.Client, containerID string) error {
	_, err := client.CommitContainer(docker.CommitContainerOptions{
		Container:  containerID,
		Repository: "new-devstep/testing-build",
		Tag:        "latest",
		Run: &docker.Config{
			Cmd: []string{"/.devstep/bin/hack"},
		},
	})
	return err
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"

	"github.com/otoolep/hraftd/http"
	"github.com/otoolep/hraftd/store"
)

const (
	DefaultHTTPAddr = ":11000"
	DefaultRaftAddr = ":12000"
)

// Parameters
var httpAddr string
var raftAddr string
var joinAddr string

func init() {
	flag.StringVar(&httpAddr, "haddr", DefaultHTTPAddr, "Set the HTTP bind address")
	flag.StringVar(&raftAddr, "raddr", DefaultRaftAddr, "Set Raft bind address")
	flag.StringVar(&joinAddr, "join", "", "Set join address, if any")
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [options] <raft-data-path> \n", os.Args[0])
		flag.PrintDefaults()
	}
}

func main() {
	flag.Parse()

	if flag.NArg() == 0 {
		fmt.Fprintf(os.Stderr, "No Raft storage directory specified\n")
		os.Exit(1)
	}

	// Ensure Raft storage exists.
	raftDir := flag.Arg(0)
	if raftDir == "" {
		fmt.Fprintf(os.Stderr, "No Raft storage directory specified\n")
		os.Exit(1)
	}
	os.MkdirAll(raftDir, 0700)

	s := store.New()
	s.RaftDir = raftDir
	s.RaftBind = raftAddr
	if err := s.Open(joinAddr == ""); err != nil {
		log.Fatalf("failed to open store: %s", err.Error())
	}

	h := httpd.New(httpAddr, s)
	if err := h.Start(); err != nil {
		log.Fatalf("failed to start HTTP service: %s", err.Error())
	}

	// If join was specified, make the join request.
	if joinAddr != "" {
		if err := join(joinAddr, raftAddr); err != nil {
			log.Fatalf("failed to join node at %s: %s", joinAddr, err.Error())
		}
	}

	log.Println("hraft started successfully")

	select {}
}

func join(joinAddr, raftAddr string) error {
	b, err := json.Marshal(map[string]string{"addr": raftAddr})
	if err != nil {
		return err
	}
	resp, err := http.Post(fmt.Sprintf("http://%s/join", joinAddr), "application-type/json", bytes.NewReader(b))
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	return nil
}
<commit_msg>Tweak comments<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"

	"github.com/otoolep/hraftd/http"
	"github.com/otoolep/hraftd/store"
)

// Command line defaults
const (
	DefaultHTTPAddr = ":11000"
	DefaultRaftAddr = ":12000"
)

// Command line parameters
var httpAddr string
var raftAddr string
var joinAddr string

func init() {
	flag.StringVar(&httpAddr, "haddr", DefaultHTTPAddr, "Set the HTTP bind address")
	flag.StringVar(&raftAddr, "raddr", DefaultRaftAddr, "Set Raft bind address")
	flag.StringVar(&joinAddr, "join", "", "Set join address, if any")
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [options] <raft-data-path> \n", os.Args[0])
		flag.PrintDefaults()
	}
}

func main() {
	flag.Parse()

	if flag.NArg() == 0 {
		fmt.Fprintf(os.Stderr, "No Raft storage directory specified\n")
		os.Exit(1)
	}

	// Ensure Raft storage exists.
	raftDir := flag.Arg(0)
	if raftDir == "" {
		fmt.Fprintf(os.Stderr, "No Raft storage directory specified\n")
		os.Exit(1)
	}
	os.MkdirAll(raftDir, 0700)

	s := store.New()
	s.RaftDir = raftDir
	s.RaftBind = raftAddr
	if err := s.Open(joinAddr == ""); err != nil {
		log.Fatalf("failed to open store: %s", err.Error())
	}

	h := httpd.New(httpAddr, s)
	if err := h.Start(); err != nil {
		log.Fatalf("failed to start HTTP service: %s", err.Error())
	}

	// If join was specified, make the join request.
	if joinAddr != "" {
		if err := join(joinAddr, raftAddr); err != nil {
			log.Fatalf("failed to join node at %s: %s", joinAddr, err.Error())
		}
	}

	log.Println("hraft started successfully")

	// Block forever.
	select {}
}

func join(joinAddr, raftAddr string) error {
	b, err := json.Marshal(map[string]string{"addr": raftAddr})
	if err != nil {
		return err
	}
	resp, err := http.Post(fmt.Sprintf("http://%s/join", joinAddr), "application-type/json", bytes.NewReader(b))
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	return nil
}
<|endoftext|>


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

package main

import (
	"database/sql"
	"flag"
	"net/http"
	"os"
	"os/signal"
	"path/filepath"
	"syscall"

	"github.com/prometheus/common/log"
	"github.com/prometheus/common/route"

	"github.com/prometheus/alertmanager/config"
	"github.com/prometheus/alertmanager/notify"
	"github.com/prometheus/alertmanager/provider"
	"github.com/prometheus/alertmanager/template"
)

var (
	configFile    = flag.String("config.file", "config.yml", "The configuration file")
	dataDir       = flag.String("data.dir", "data/", "The data directory")
	listenAddress = flag.String("web.listen-address", ":9093", "Address to listen on for the web interface and API.")
)

func main() {
	flag.Parse()

	db, err := sql.Open("ql", filepath.Join(*dataDir, "am.db"))
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	alerts, err := provider.NewSQLAlerts(db)
	if err != nil {
		log.Fatal(err)
	}
	notifies, err := provider.NewSQLNotifyInfo(db)
	if err != nil {
		log.Fatal(err)
	}
	silences, err := provider.NewSQLSilences(db)
	if err != nil {
		log.Fatal(err)
	}

	var (
		inhibitor *Inhibitor
		tmpl      *template.Template
		disp      *Dispatcher
	)
	defer disp.Stop()

	build := func(nconf []*config.NotificationConfig) notify.Notifier {
		var (
			router  = notify.Router{}
			fanouts = notify.Build(nconf, tmpl)
		)
		for name, fo := range fanouts {
			for i, n := range fo {
				n = notify.Retry(n)
				n = notify.Log(n, log.With("step", "retry"))
				n = notify.Dedup(notifies, n)
				n = notify.Log(n, log.With("step", "dedup"))

				fo[i] = n
			}
			router[name] = fo
		}
		var n notify.Notifier = router

		n = notify.Log(n, log.With("step", "route"))
		n = notify.Mute(silences, n)
		n = notify.Log(n, log.With("step", "silence"))
		n = notify.Mute(inhibitor, n)
		n = notify.Log(n, log.With("step", "inhibit"))

		return n
	}

	reload := func() (err error) {
		log.With("file", *configFile).Infof("Loading configuration file")
		defer func() {
			if err != nil {
				log.With("file", *configFile).Errorf("Loading configuration file failed")
			}
		}()

		conf, err := config.LoadFile(*configFile)
		if err != nil {
			return err
		}

		tmpl, err = template.FromGlobs(conf.Templates...)
		if err != nil {
			return err
		}

		disp.Stop()

		inhibitor = NewInhibitor(alerts, conf.InhibitRules)
		disp = NewDispatcher(alerts, conf.Routes, build(conf.NotificationConfigs))

		go disp.Run()

		return nil
	}

	if err := reload(); err != nil {
		os.Exit(1)
	}

	router := route.New()

	RegisterWeb(router)
	RegisterAPI(router.WithPrefix("/api"), alerts, silences)

	go http.ListenAndServe(*listenAddress, router)

	var (
		hup  = make(chan os.Signal)
		term = make(chan os.Signal)
	)
	signal.Notify(hup, syscall.SIGHUP)
	signal.Notify(term, os.Interrupt, syscall.SIGTERM)

	go func() {
		for range hup {
			reload()
		}
	}()

	<-term

	log.Infoln("Received SIGTERM, exiting gracefully...")
}
<commit_msg>Output error on config load failure<commit_after>// Copyright 2015 Prometheus Team
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"database/sql"
	"flag"
	"net/http"
	"os"
	"os/signal"
	"path/filepath"
	"syscall"

	"github.com/prometheus/common/log"
	"github.com/prometheus/common/route"

	"github.com/prometheus/alertmanager/config"
	"github.com/prometheus/alertmanager/notify"
	"github.com/prometheus/alertmanager/provider"
	"github.com/prometheus/alertmanager/template"
)

var (
	configFile    = flag.String("config.file", "config.yml", "The configuration file")
	dataDir       = flag.String("data.dir", "data/", "The data directory")
	listenAddress = flag.String("web.listen-address", ":9093", "Address to listen on for the web interface and API.")
)

func main() {
	flag.Parse()

	db, err := sql.Open("ql", filepath.Join(*dataDir, "am.db"))
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	alerts, err := provider.NewSQLAlerts(db)
	if err != nil {
		log.Fatal(err)
	}
	notifies, err := provider.NewSQLNotifyInfo(db)
	if err != nil {
		log.Fatal(err)
	}
	silences, err := provider.NewSQLSilences(db)
	if err != nil {
		log.Fatal(err)
	}

	var (
		inhibitor *Inhibitor
		tmpl      *template.Template
		disp      *Dispatcher
	)
	defer disp.Stop()

	build := func(nconf []*config.NotificationConfig) notify.Notifier {
		var (
			router  = notify.Router{}
			fanouts = notify.Build(nconf, tmpl)
		)
		for name, fo := range fanouts {
			for i, n := range fo {
				n = notify.Retry(n)
				n = notify.Log(n, log.With("step", "retry"))
				n = notify.Dedup(notifies, n)
				n = notify.Log(n, log.With("step", "dedup"))

				fo[i] = n
			}
			router[name] = fo
		}
		var n notify.Notifier = router

		n = notify.Log(n, log.With("step", "route"))
		n = notify.Mute(silences, n)
		n = notify.Log(n, log.With("step", "silence"))
		n = notify.Mute(inhibitor, n)
		n = notify.Log(n, log.With("step", "inhibit"))

		return n
	}

	reload := func() (err error) {
		log.With("file", *configFile).Infof("Loading configuration file")
		defer func() {
			if err != nil {
				log.With("file", *configFile).Errorf("Loading configuration file failed: %s", err)
			}
		}()

		conf, err := config.LoadFile(*configFile)
		if err != nil {
			return err
		}

		tmpl, err = template.FromGlobs(conf.Templates...)
		if err != nil {
			return err
		}

		disp.Stop()

		inhibitor = NewInhibitor(alerts, conf.InhibitRules)
		disp = NewDispatcher(alerts, conf.Routes, build(conf.NotificationConfigs))

		go disp.Run()

		return nil
	}

	if err := reload(); err != nil {
		os.Exit(1)
	}

	router := route.New()

	RegisterWeb(router)
	RegisterAPI(router.WithPrefix("/api"), alerts, silences)

	go http.ListenAndServe(*listenAddress, router)

	var (
		hup  = make(chan os.Signal)
		term = make(chan os.Signal)
	)
	signal.Notify(hup, syscall.SIGHUP)
	signal.Notify(term, os.Interrupt, syscall.SIGTERM)

	go func() {
		for range hup {
			reload()
		}
	}()

	<-term

	log.Infoln("Received SIGTERM, exiting gracefully...")
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"math/rand"
	"os"
	"strings"
	"text/tabwriter"
	"time"
)

type Inventory struct {
	Size       int
	Categories int
	Vector     []int
	Pickeds    []PickedGroup
	GroupSize  int

	Items map[int]ItemList
	Kinds []int
}

type PickedGroup []int

type ItemList []string

type Item struct {
	Kind   int
	Value  string
	Idx    int
	Person int
}

var BaseInventory map[int]ItemList

const (
	KindNationality = iota
	KindHouseColor
	KindPet
	KindJob
	KindBeverage
	KindWeapon
	KindTransport
	KindRoom
)

var KindName map[int]string

var Kinds = []int{
	KindNationality, KindHouseColor, KindPet, KindJob,
	KindBeverage, KindWeapon, KindTransport, KindRoom,
}

func (il *ItemList) Shuffle() {
	for i := range *il {
		j := rand.Intn(i + 1)
		(*il)[i], (*il)[j] = (*il)[j], (*il)[i]
	}
}

func init() {
	KindName = map[int]string{
		KindNationality: "nationality",
		KindHouseColor:  "house-color",
		KindPet:         "pet",
		KindJob:         "job",
		KindBeverage:    "beverage",
		KindWeapon:      "weapon",
		KindTransport:   "transport",
		KindRoom:        "room",
	}
	BaseInventory = map[int]ItemList{
		KindNationality: {
			"french",
			"english",
			"norvegian",
			"american",
			"portuguese",
			"spannish",
			"german",
			"scottish",
		},
		KindHouseColor: {
			"red",
			"yellow",
			"pink",
			"orange",
			"purple",
			"magenta",
			"blue",
			"green",
		},
		KindPet: {
			"dog",
			"cat",
			"horse",
			"poney",
			"fish",
			"whale",
			"beaver",
			"bird",
			"shark",
			"snake",
		},
		KindJob: {
			"teacher",
			"architect",
			"nurse",
			"scientist",
			"student",
			"cop",
			"designer",
			"docker",
		},
		KindBeverage: {
			"beer",
			"wine",
			"water",
			"long-island-ice-tea",
			"coca-cola",
			"dr-pepper",
			"blue-lagoon",
			"fanta",
		},
		KindWeapon: {
			"sword",
			"gun",
			"bazooka",
			"grenade",
			"bomb",
			"assault-rifle",
			"shotgun",
			"knife",
			"lasergun",
		},
		KindTransport: {
			"bus",
			"train",
			"car",
			"bike",
			"plane",
			"roller",
			"motorbike",
			"hoverboard",
		},
		KindRoom: {
			"kitchen",
			"bedroom",
			"lobby",
			"living-room",
			"veranda",
			"garden",
			"pool",
			"restroom",
			"bathroom",
		},
	}
}

func NewInventory(size, categories, groupSize int) *Inventory {
	inventory := Inventory{
		Size:       size,
		Categories: categories,
		Items:      make(map[int]ItemList, 0),
		Kinds:      make([]int, categories),
		GroupSize:  groupSize,
	}

	inventory.Vector = make([]int, inventory.Length())
	inventory.Pickeds = make([]PickedGroup, 0)

	for i := range Kinds {
		j := rand.Intn(i + 1)
		Kinds[i], Kinds[j] = Kinds[j], Kinds[i]
	}

	for i := 0; i < categories; i++ {
		inventory.Kinds[i] = Kinds[i]
	}

	for _, kind := range inventory.Kinds {
		itemList := BaseInventory[kind]
		itemList.Shuffle()
		inventory.Items[kind] = ItemList{}
		for i := 0; i < inventory.Size; i++ {
			inventory.Items[kind] = append(inventory.Items[kind], itemList[i])
		}
	}

	return &inventory
}

func (i *Inventory) Show() {
	w := tabwriter.NewWriter(os.Stdout, 10, 1, 3, ' ', 0)
	defer w.Flush()
	header := ""
	for j := 0; j < i.Size; j++ {
		header += fmt.Sprintf("\t%d", j+1)
	}
	fmt.Fprintf(w, "%s\n", header)
	for y, kind := range i.Kinds {
		row := ""
		for j := 0; j < i.Size; j++ {
			row += fmt.Sprintf("%d: %s\t", i.Vector[y*i.Size+j], i.Items[kind][j])
		}
		fmt.Fprintf(w, "%s\t%s\n", KindName[kind], row)
	}
	fmt.Fprintln(w, "\n")
}

func (i *Inventory) Length() int {
	return i.Size * i.Categories
}

func (i *Inventory) EOF() bool {
	return (len(i.Pickeds)+1)*i.GroupSize >= i.Length()
}

func (i *Inventory) PickAvailableGroup(maxLevel int) []int {
	// FIXME: pick more groups of the same person
	// FIXME: pick groups of one item on full left or full right
	length := i.Length()
	picked := make([]int, i.GroupSize)

	for j := 0; j < i.GroupSize; j++ {
		max := rand.Intn(length) + 1
		k := 0
		l := 0
		idx := -1
		for k < max {
			for {
				if i.Vector[(k+l)%length] == maxLevel-1 {
					idx = (k + l) % length
					break
				}
				l++
			}
			k++
		}
		picked[j] = idx
		i.Vector[idx]++
	}
	i.Pickeds = append(i.Pickeds, picked)
	return picked
}

func (i *Inventory) At(idx int) Item {
	item := Item{
		Kind:   i.Kinds[idx/i.Size],
		Idx:    idx,
		Person: idx % i.Size,
	}
	item.Value = i.Items[item.Kind][item.Person]
	return item
}
func (i *Item) Name() string {
	return fmt.Sprintf("%s:%s", KindName[i.Kind], i.Value)
}

func (i *Inventory) GroupString(group PickedGroup) string {
	items := []Item{}

	fullNames := []string{}
	for _, idx := range group {
		item := i.At(idx)
		items = append(items, item)
		fullNames = append(fullNames, item.Name())
	}

	sameKind := true
	samePerson := true

	currentKind := items[0].Kind
	currentPerson := items[0].Person
	for j := 1; j < i.GroupSize; j++ {
		if currentKind != items[j].Kind {
			sameKind = false
		}
		if currentPerson != items[j].Person {
			samePerson = false
		}
	}

	if samePerson {
		return strings.Join(fullNames, " == ")
	}

	switch len(group) {
	case 1:
		if currentPerson == 0 {
			return fmt.Sprintf("%s is on the left", items[0].Name())
		}
		if currentPerson == i.Size {
			return fmt.Sprintf("%s is on the right", items[0].Name())
		}
		if i.Size%2 == 1 && currentPerson == (i.Size-1)/2 {
			return fmt.Sprintf("%s is in the middle", items[0].Name())
		}
		panic("not implemented")
	case 2:
		if items[0].Person == items[1].Person-1 {
			return fmt.Sprintf("%s is direct on the left of %s", items[0].Name(), items[1].Name())
		}
		if items[0].Person == items[1].Person+1 {
			return fmt.Sprintf("%s is direct on the right of %s", items[0].Name(), items[1].Name())
		}
		if items[0].Person < items[1].Person {
			return fmt.Sprintf("%s is on the left of %s", items[0].Name(), items[1].Name())
		}
		if items[0].Person > items[1].Person {
			return fmt.Sprintf("%s is on the left of %s", items[0].Name(), items[1].Name())
		}
	default:
		panic("not implemented")
	}

	return fmt.Sprintf("%v %v %v", sameKind, samePerson, items)
}

func (i *Inventory) Missings() []Item {
	missings := []Item{}
	for idx := 0; idx < i.Length(); idx++ {
		if i.Vector[idx] == 0 {
			missings = append(missings, i.At(idx))
		}
	}
	return missings
}

func main() {
	rand.Seed(time.Now().UnixNano())
	inventory := NewInventory(5, 5, 2)

	inventory.Show()
	// pick at least each item one time
	for !inventory.EOF() {
		inventory.PickAvailableGroup(1)
	}

	// pick again some items
	for i := 0; i < 3; i++ {
		inventory.PickAvailableGroup(2)
	}

	inventory.Show()

	for _, group := range inventory.Pickeds {
		fmt.Printf("- %s\n", inventory.GroupString(group))
	}

	fmt.Println("")

	for _, item := range inventory.Missings() {
		fmt.Printf("- where is %s ?\n", item.Name())
	}
}
<commit_msg>Picking items at absolute locations<commit_after>package main

import (
	"fmt"
	"math/rand"
	"os"
	"strings"
	"text/tabwriter"
	"time"
)

type Inventory struct {
	Size       int
	Categories int
	Vector     []int
	Pickeds    []PickedGroup
	GroupSize  int

	Items map[int]ItemList
	Kinds []int
}

type PickedGroup []int

type ItemList []string

type Item struct {
	Kind   int
	Value  string
	Idx    int
	Person int
}

var BaseInventory map[int]ItemList

const (
	KindNationality = iota
	KindHouseColor
	KindPet
	KindJob
	KindBeverage
	KindWeapon
	KindTransport
	KindRoom
)

var KindName map[int]string

var Kinds = []int{
	KindNationality, KindHouseColor, KindPet, KindJob,
	KindBeverage, KindWeapon, KindTransport, KindRoom,
}

func (il *ItemList) Shuffle() {
	for i := range *il {
		j := rand.Intn(i + 1)
		(*il)[i], (*il)[j] = (*il)[j], (*il)[i]
	}
}

func init() {
	KindName = map[int]string{
		KindNationality: "nationality",
		KindHouseColor:  "house-color",
		KindPet:         "pet",
		KindJob:         "job",
		KindBeverage:    "beverage",
		KindWeapon:      "weapon",
		KindTransport:   "transport",
		KindRoom:        "room",
	}
	BaseInventory = map[int]ItemList{
		KindNationality: {
			"french",
			"english",
			"norvegian",
			"american",
			"portuguese",
			"spannish",
			"german",
			"scottish",
		},
		KindHouseColor: {
			"red",
			"yellow",
			"pink",
			"orange",
			"purple",
			"magenta",
			"blue",
			"green",
		},
		KindPet: {
			"dog",
			"cat",
			"horse",
			"poney",
			"fish",
			"whale",
			"beaver",
			"bird",
			"shark",
			"snake",
		},
		KindJob: {
			"teacher",
			"architect",
			"nurse",
			"scientist",
			"student",
			"cop",
			"designer",
			"docker",
		},
		KindBeverage: {
			"beer",
			"wine",
			"water",
			"long-island-ice-tea",
			"coca-cola",
			"dr-pepper",
			"blue-lagoon",
			"fanta",
		},
		KindWeapon: {
			"sword",
			"gun",
			"bazooka",
			"grenade",
			"bomb",
			"assault-rifle",
			"shotgun",
			"knife",
			"lasergun",
		},
		KindTransport: {
			"bus",
			"train",
			"car",
			"bike",
			"plane",
			"roller",
			"motorbike",
			"hoverboard",
		},
		KindRoom: {
			"kitchen",
			"bedroom",
			"lobby",
			"living-room",
			"veranda",
			"garden",
			"pool",
			"restroom",
			"bathroom",
		},
	}
}

func NewInventory(size, categories, groupSize int) *Inventory {
	inventory := Inventory{
		Size:       size,
		Categories: categories,
		Items:      make(map[int]ItemList, 0),
		Kinds:      make([]int, categories),
		GroupSize:  groupSize,
	}

	inventory.Vector = make([]int, inventory.Length())
	inventory.Pickeds = make([]PickedGroup, 0)

	for i := range Kinds {
		j := rand.Intn(i + 1)
		Kinds[i], Kinds[j] = Kinds[j], Kinds[i]
	}

	for i := 0; i < categories; i++ {
		inventory.Kinds[i] = Kinds[i]
	}

	for _, kind := range inventory.Kinds {
		itemList := BaseInventory[kind]
		itemList.Shuffle()
		inventory.Items[kind] = ItemList{}
		for i := 0; i < inventory.Size; i++ {
			inventory.Items[kind] = append(inventory.Items[kind], itemList[i])
		}
	}

	return &inventory
}

func (i *Inventory) Show() {
	w := tabwriter.NewWriter(os.Stdout, 10, 1, 3, ' ', 0)
	defer w.Flush()
	header := ""
	for j := 0; j < i.Size; j++ {
		header += fmt.Sprintf("\t%d", j+1)
	}
	fmt.Fprintf(w, "%s\n", header)
	for y, kind := range i.Kinds {
		row := ""
		for j := 0; j < i.Size; j++ {
			row += fmt.Sprintf("%d: %s\t", i.Vector[y*i.Size+j], i.Items[kind][j])
		}
		fmt.Fprintf(w, "%s\t%s\n", KindName[kind], row)
	}
	fmt.Fprintln(w, "\n")
}

func (i *Inventory) Length() int {
	return i.Size * i.Categories
}

func (i *Inventory) EOF() bool {
	return (len(i.Pickeds)+1)*i.GroupSize >= i.Length()
}

func (i *Inventory) PickAvailableGroup(maxLevel int) []int {
	// FIXME: pick more groups of the same person
	// FIXME: pick groups of one item on full left or full right
	length := i.Length()
	picked := make([]int, i.GroupSize)

	for j := 0; j < i.GroupSize; j++ {
		max := rand.Intn(length) + 1
		k := 0
		l := 0
		idx := -1
		for k < max {
			for {
				if i.Vector[(k+l)%length] == maxLevel-1 {
					idx = (k + l) % length
					break
				}
				l++
			}
			k++
		}
		picked[j] = idx
		i.Vector[idx]++
	}
	i.Pickeds = append(i.Pickeds, picked)
	return picked
}

func (i *Inventory) At(idx int) Item {
	item := Item{
		Kind:   i.Kinds[idx/i.Size],
		Idx:    idx,
		Person: idx % i.Size,
	}
	item.Value = i.Items[item.Kind][item.Person]
	return item
}
func (i *Item) Name() string {
	return fmt.Sprintf("%s:%s", KindName[i.Kind], i.Value)
}

func (i *Inventory) GroupString(group PickedGroup) string {
	items := []Item{}

	fullNames := []string{}
	for _, idx := range group {
		item := i.At(idx)
		items = append(items, item)
		fullNames = append(fullNames, item.Name())
	}

	if len(group) == 1 {
		currentPerson := items[0].Person
		if currentPerson == 0 {
			return fmt.Sprintf("%s is on the far left", items[0].Name())
		}
		if currentPerson == i.Size-1 {
			return fmt.Sprintf("%s is on the far right", items[0].Name())
		}
		if i.Size%2 == 1 && currentPerson == (i.Size-1)/2 {
			return fmt.Sprintf("%s is in the middle", items[0].Name())
		}
		panic("not implemented")
	}

	sameKind := true
	samePerson := true

	currentKind := items[0].Kind
	currentPerson := items[0].Person
	for j := 1; j < i.GroupSize; j++ {
		if currentKind != items[j].Kind {
			sameKind = false
		}
		if currentPerson != items[j].Person {
			samePerson = false
		}
	}

	if samePerson {
		return strings.Join(fullNames, " == ")
	}

	switch len(group) {
	case 1:
		panic("should never happen")
	case 2:
		if items[0].Person == items[1].Person-1 {
			return fmt.Sprintf("%s is direct on the left of %s", items[0].Name(), items[1].Name())
		}
		if items[0].Person == items[1].Person+1 {
			return fmt.Sprintf("%s is direct on the right of %s", items[0].Name(), items[1].Name())
		}
		if items[0].Person < items[1].Person {
			return fmt.Sprintf("%s is on the left of %s", items[0].Name(), items[1].Name())
		}
		if items[0].Person > items[1].Person {
			return fmt.Sprintf("%s is on the left of %s", items[0].Name(), items[1].Name())
		}
	default:
		panic("not implemented")
	}

	return fmt.Sprintf("%v %v %v", sameKind, samePerson, items)
}

func (i *Inventory) Missings() []Item {
	missings := []Item{}
	for idx := 0; idx < i.Length(); idx++ {
		if i.Vector[idx] == 0 {
			missings = append(missings, i.At(idx))
		}
	}
	return missings
}

func (i *Inventory) PickItemAtExtremity(targetCounter int) []int {
	picked := make([]int, 1)
	idx := -1

	// limits are arbitrary choosen to avoid infinite loop
	// best effort for now
	for ; targetCounter < 10; targetCounter++ {
		for try := 0; try < 100; try++ {
			direction := rand.Intn(3)
			kind := rand.Intn(i.Categories)
			switch direction {
			case 0:
				idx = kind * i.Size
			case 1:
				idx = kind*i.Size + i.Size - 1
			case 2:
				if i.Size%2 == 1 {
					idx = kind*i.Size + (i.Size-1)/2
				} else {
					continue
				}
			default:
				panic("should never happen")
			}
			if i.Vector[idx] == targetCounter {
				picked[0] = idx
				i.Vector[idx]++
				i.Pickeds = append(i.Pickeds, picked)
				return picked
			}
		}
	}
	panic("should never happen")
	return picked
}

func main() {
	rand.Seed(time.Now().UnixNano())
	inventory := NewInventory(5, 5, 2)

	// inventory.Show()
	// pick at least each item one time
	for !inventory.EOF() {
		inventory.PickAvailableGroup(1)
	}

	// pick again some items
	for i := 0; i < 3; i++ {
		inventory.PickAvailableGroup(2)
	}

	// pick groups of 1 item on an extremity
	for i := 0; i < 3; i++ {
		inventory.PickItemAtExtremity(1)
	}

	inventory.Show()

	for _, group := range inventory.Pickeds {
		fmt.Printf("- %s\n", inventory.GroupString(group))
	}

	fmt.Println("")

	for _, item := range inventory.Missings() {
		fmt.Printf("- where is %s ?\n", item.Name())
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/md5"
	"flag"
	"fmt"
	"github.com/nfnt/resize"
	"github.com/vharitonsky/iniflags"
	"html/template"
	"image"
	"image/gif"
	"image/jpeg"
	"image/png"
	"io"
	"log"
	"net/http"
	"os"
	"regexp"
	"strconv"
	"strings"
	"time"
)

var (
	dataDir = flag.String("data", "data", "Path to data files relative to current working dir.")
	port    = flag.Int("port", 8080, "Webserver port number.")
	remote  = flag.String("remote", "http://gravatar.com/avatar", "Use this gravatar-compatible avatar service if "+
		"no avatar is found, use 'none' for no remote.")
	dflt = flag.String("default", "remote:monsterid", "Default avatar. Use 'remote' to use the default of the remote\n"+
		"    service, or 'remote:<option>' to use a builtin default. For example: 'remote:monsterid'. This is passes as\n"+
		"    '?d=monsterid' to the remote service. See https://nl.gravatar.com/site/implement/images/.\n"+
		"    If no remote and no local default is configured, resources/mm is used as default.")
)

var (
	defaultImage  = "resources/mm"
	remoteUrl     = ""
	remoteDefault = ""
)

type Avatar struct {
	size int
	data []byte
	// below are used in header fields
	cacheControl string
	lastModified string
}

type Request struct {
	hash string
	size int
}

// scales the avatar (altering it!)
func scale(avatar *Avatar, size int) error {
	image, format, err := image.Decode(bytes.NewBuffer(avatar.data))
	if err != nil {
		return err
	}
	actualSize := image.Bounds().Dx() // assume square
	if size == actualSize {
		return nil
	}
	log.Printf("Resizing image from %vx%v to %vx%v", actualSize, actualSize, size, size)
	resized := resize.Resize(uint(size), uint(size), image, resize.Bicubic)
	b := new(bytes.Buffer)
	switch format {
	case "jpeg":
		jpeg.Encode(b, resized, nil)
	case "gif":
		gif.Encode(b, resized, nil)
	case "png":
		png.Encode(b, resized)
	}
	avatar.data = b.Bytes()
	return nil
}

func readImage(reader io.Reader) *Avatar {
	b := new(bytes.Buffer)
	if _, e := io.Copy(b, reader); e != nil {
		log.Printf("Could not read image", e)
		return nil
	}
	return &Avatar{size: -1, data: b.Bytes()}
}

func readFromFile(filename string, size int) *Avatar {
	file, err := os.Open(filename)
	if err != nil {
		log.Printf("Error reading file: %v", err)
		return nil
	}
	defer file.Close()
	avatar := readImage(file)
	err = scale(avatar, size)
	if err != nil {
		log.Printf("Could not scale image")
		return nil // don't return the image, if we can't scale it it is probably corrupt
	}

	avatar.cacheControl = "max-age=300"
	if info, e := os.Stat(filename); e == nil {
		timestamp := info.ModTime().UTC()
		avatar.lastModified = timestamp.Format(http.TimeFormat)
	} else {
		avatar.lastModified = "Sat, 1 Jan 2000 12:00:00 GMT"
	}
	return avatar
}

func retrieveFromLocal(request Request) *Avatar {
	return readFromFile(createAvatarPath(request.hash), request.size)
}

// Retrieves the avatar from the remote service, returning nil if there is no avatar or it could not be retrieved
func retrieveFromRemote(request Request) *Avatar {
	if remoteUrl == "" {
		return nil
	}
	options := fmt.Sprintf("s=%d", request.size)
	if remoteDefault != "" {
		options += "&d=" + remoteDefault
	}
	remote := remoteUrl + "/" + request.hash + "?" + options
	log.Printf("Retrieving from: %s", remote)
	resp, err2 := http.Get(remote)
	if err2 != nil {
		log.Printf("Remote lookup of %s failed with error: %s", remote, err2)
		return nil
	}
	if resp.StatusCode == 404 {
		log.Printf("Avatar not found on remote")
		return nil
	}
	avatar := readImage(resp.Body)
	avatar.size = request.size // assume image is scaled by remote service
	avatar.cacheControl = resp.Header.Get("Cache-Control")
	avatar.lastModified = resp.Header.Get("Last-Modified")
	return avatar
}

func createAvatarPath(hash string) string {
	return fmt.Sprintf("%s/avatars/%s", *dataDir, hash)
}

func setHeaderField(w http.ResponseWriter, key string, value string) {
	if value != "" {
		w.Header().Set(key, value)
	}
}

func writeAvatarResult(w http.ResponseWriter, avatar *Avatar) {
	setHeaderField(w, "Last-Modified", avatar.lastModified)
	setHeaderField(w, "Cache-Control", avatar.cacheControl)
	b := bytes.NewBuffer(avatar.data)
	_, err := io.Copy(w, b)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func retrieveImage(request Request, w http.ResponseWriter, r *http.Request) *Avatar {
	avatar := retrieveFromLocal(request)
	if avatar == nil {
		avatar = retrieveFromRemote(request)
	}
	if avatar == nil {
		avatar = readFromFile(defaultImage, request.size)
	}
	if avatar == nil {
		avatar = readFromFile("resources/mm", request.size)
	}
	return avatar
}

func loadImage(request Request, w http.ResponseWriter, r *http.Request) {
	log.Printf("Loading image: %v", request)
	avatar := retrieveImage(request, w, r)
	if avatar == nil {
		http.NotFound(w, r)
	} else {
		writeAvatarResult(w, avatar)
	}
}

func avatarHandler(w http.ResponseWriter, r *http.Request, title string) {
	r.ParseForm()
	sizeParam := r.FormValue("s")
	size := 80
	if sizeParam != "" {
		if s, err := strconv.Atoi(sizeParam); err == nil {
			size = s
			if size > 512 {
				size = 512
			}
			if size < 8 {
				size = 8
			}
		}
	}
	loadImage(Request{hash: title, size: size}, w, r)
}

var templates = template.Must(template.ParseFiles("resources/upload.html", "resources/save.html", "resources/index.html"))

func renderTemplate(w http.ResponseWriter, tmpl string, data interface{}) {
	err := templates.ExecuteTemplate(w, tmpl+".html", data)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func uploadHandler(w http.ResponseWriter, r *http.Request, title string) {
	renderTemplate(w, "upload", map[string]string{})
}

func createHash(email string) string {
	h := md5.New()
	io.WriteString(h, strings.TrimSpace(strings.ToLower(email)))
	return fmt.Sprintf("%x", h.Sum(nil))
}

func saveHandler(w http.ResponseWriter, r *http.Request, title string) {
	email := r.FormValue("email")
	log.Printf("Saving image for email address: %v", email)
	file, _, err := r.FormFile("datafile")
	if err != nil {
		log.Print("Error: ", err)
		fmt.Fprintf(w, "
Please chooce a file to upload
")
		return
	}

	hash := createHash(email)
	filename := createAvatarPath(hash)

	f, err := os.Create(filename)
	if err != nil {
		log.Print("Error: ", err)
		fmt.Fprintf(w, "
Error while uploading file
")
		return
	}
	defer f.Close()
	io.Copy(f, file)

	renderTemplate(w, "save", map[string]string{"Avatar": fmt.Sprintf("/avatar/%s", hash)})
}

func mainHandler(w http.ResponseWriter, r *http.Request, title string) {
	hostname, err := os.Hostname()
	if err != nil {
		hostname = "localhost"
	}
	portName := ""
	if *port != 80 {
		portName = fmt.Sprintf(":%d", *port)
	}
	url := "http://" + hostname + portName + "/avatar/"
	renderTemplate(w, "index", map[string]string{"AvatarLink": url, "HostName": hostname})
}

// Creates a http request handler
// pattern is a regular expression that validates the URL. The first matching group is passes to the handler as title
func makeHandler(fn func(http.ResponseWriter, *http.Request, string), pattern string) http.HandlerFunc {
	regex := regexp.MustCompile(pattern)
	return func(w http.ResponseWriter, r *http.Request) {
		//		log.Printf("Request: %v", r)
		m := regex.FindStringSubmatch(r.URL.Path)
		if m == nil {
			log.Print("Invalid request: ", r.URL)
			http.NotFound(w, r)
			return
		}
		log.Printf("Handling request %v %v from %v", r.Method, r.URL, strings.Split(r.RemoteAddr, ":")[0])
		start := time.Now()
		fn(w, r, m[1])
		log.Printf("Handled request %v in %v", r.URL, time.Since(start))
	}
}

var fallbackDefaultPattern = regexp.MustCompile("^remote:([a-zA-Z]+)$")

func main() {
	iniflags.Parse()

	log.Printf("data dir = %s\n", *dataDir)
	address := fmt.Sprintf(":%d", *port)

	if *remote == "none" {
		remoteUrl = ""
	} else {
		remoteUrl = *remote
		log.Printf("Missig avatars will be redirected to %s", remoteUrl)
	}

	if *dflt == "fallback" {
		log.Printf("Default image will be provided by the remote service if configured")
		remoteDefault = ""
	} else if builtin := fallbackDefaultPattern.FindStringSubmatch(*dflt); builtin != nil {
		remoteDefault = builtin[1]
		log.Printf("Default image will be provided by the remote service using '?d=%s' if a remote is configured", remoteDefault)
	} else {
		defaultImage = *dflt
		remoteDefault = "404"
		log.Printf("Using %s as default image", defaultImage)
	}

	log.Printf("Listening on %s\n", address)
	http.HandleFunc("/", makeHandler(mainHandler, "^/()$"))
	http.HandleFunc("/avatar/", makeHandler(avatarHandler, "^/avatar/([a-zA-Z0-9]+)$"))
	http.HandleFunc("/upload/", makeHandler(uploadHandler, "^/(upload)/$"))
	http.HandleFunc("/save/", makeHandler(saveHandler, "^/(save)/$"))
	x := http.ListenAndServe(address, nil)
	fmt.Println("Result: ", x)
}
<commit_msg>Logging<commit_after>package main

import (
	"bytes"
	"crypto/md5"
	"flag"
	"fmt"
	"github.com/nfnt/resize"
	"github.com/vharitonsky/iniflags"
	"html/template"
	"image"
	"image/gif"
	"image/jpeg"
	"image/png"
	"io"
	"log"
	"net/http"
	"os"
	"regexp"
	"strconv"
	"strings"
	"time"
)

var (
	dataDir = flag.String("data", "data", "Path to data files relative to current working dir.")
	port    = flag.Int("port", 8080, "Webserver port number.")
	remote  = flag.String("remote", "http://gravatar.com/avatar", "Use this gravatar-compatible avatar service if "+
		"no avatar is found, use 'none' for no remote.")
	dflt = flag.String("default", "remote:monsterid", "Default avatar. Use 'remote' to use the default of the remote\n"+
		"    service, or 'remote:<option>' to use a builtin default. For example: 'remote:monsterid'. This is passes as\n"+
		"    '?d=monsterid' to the remote service. See https://nl.gravatar.com/site/implement/images/.\n"+
		"    If no remote and no local default is configured, resources/mm is used as default.")
)

var (
	defaultImage  = "resources/mm"
	remoteUrl     = ""
	remoteDefault = ""
)

type Avatar struct {
	size int
	data []byte
	// below are used in header fields
	cacheControl string
	lastModified string
}

type Request struct {
	hash string
	size int
}

// scales the avatar (altering it!)
func scale(avatar *Avatar, size int) error {
	image, format, err := image.Decode(bytes.NewBuffer(avatar.data))
	if err != nil {
		return err
	}
	actualSize := image.Bounds().Dx() // assume square
	if size == actualSize {
		return nil
	}
	log.Printf("Resizing image from %vx%v to %vx%v", actualSize, actualSize, size, size)
	resized := resize.Resize(uint(size), uint(size), image, resize.Bicubic)
	b := new(bytes.Buffer)
	switch format {
	case "jpeg":
		jpeg.Encode(b, resized, nil)
	case "gif":
		gif.Encode(b, resized, nil)
	case "png":
		png.Encode(b, resized)
	}
	avatar.data = b.Bytes()
	return nil
}

func readImage(reader io.Reader) *Avatar {
	b := new(bytes.Buffer)
	if _, e := io.Copy(b, reader); e != nil {
		log.Printf("Could not read image", e)
		return nil
	}
	return &Avatar{size: -1, data: b.Bytes()}
}

func readFromFile(filename string, size int) *Avatar {
	file, err := os.Open(filename)
	if err != nil {
		log.Printf("Error reading file: %v", err)
		return nil
	}
	defer file.Close()
	avatar := readImage(file)
	err = scale(avatar, size)
	if err != nil {
		log.Printf("Could not scale image")
		return nil // don't return the image, if we can't scale it it is probably corrupt
	}

	avatar.cacheControl = "max-age=300"
	if info, e := os.Stat(filename); e == nil {
		timestamp := info.ModTime().UTC()
		avatar.lastModified = timestamp.Format(http.TimeFormat)
	} else {
		avatar.lastModified = "Sat, 1 Jan 2000 12:00:00 GMT"
	}
	return avatar
}

func retrieveFromLocal(request Request) *Avatar {
	return readFromFile(createAvatarPath(request.hash), request.size)
}

// Retrieves the avatar from the remote service, returning nil if there is no avatar or it could not be retrieved
func retrieveFromRemote(request Request) *Avatar {
	if remoteUrl == "" {
		return nil
	}
	options := fmt.Sprintf("s=%d", request.size)
	if remoteDefault != "" {
		options += "&d=" + remoteDefault
	}
	remote := remoteUrl + "/" + request.hash + "?" + options
	log.Printf("Retrieving from: %s", remote)
	resp, err2 := http.Get(remote)
	if err2 != nil {
		log.Printf("Remote lookup of %s failed with error: %s", remote, err2)
		return nil
	}
	if resp.StatusCode == 404 {
		log.Printf("Avatar not found on remote")
		return nil
	}
	avatar := readImage(resp.Body)
	avatar.size = request.size // assume image is scaled by remote service
	avatar.cacheControl = resp.Header.Get("Cache-Control")
	avatar.lastModified = resp.Header.Get("Last-Modified")
	return avatar
}

func createAvatarPath(hash string) string {
	return fmt.Sprintf("%s/avatars/%s", *dataDir, hash)
}

func setHeaderField(w http.ResponseWriter, key string, value string) {
	if value != "" {
		w.Header().Set(key, value)
	}
}

func writeAvatarResult(w http.ResponseWriter, avatar *Avatar) {
	setHeaderField(w, "Last-Modified", avatar.lastModified)
	setHeaderField(w, "Cache-Control", avatar.cacheControl)
	b := bytes.NewBuffer(avatar.data)
	_, err := io.Copy(w, b)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func retrieveImage(request Request, w http.ResponseWriter, r *http.Request) *Avatar {
	avatar := retrieveFromLocal(request)
	if avatar == nil {
		avatar = retrieveFromRemote(request)
	}
	if avatar == nil {
		avatar = readFromFile(defaultImage, request.size)
	}
	if avatar == nil {
		avatar = readFromFile("resources/mm", request.size)
	}
	return avatar
}

func loadImage(request Request, w http.ResponseWriter, r *http.Request) {
	log.Printf("Loading image: %v", request)
	avatar := retrieveImage(request, w, r)
	if avatar == nil {
		http.NotFound(w, r)
	} else {
		writeAvatarResult(w, avatar)
	}
}

func avatarHandler(w http.ResponseWriter, r *http.Request, title string) {
	r.ParseForm()
	sizeParam := r.FormValue("s")
	size := 80
	if sizeParam != "" {
		if s, err := strconv.Atoi(sizeParam); err == nil {
			size = s
			if size > 512 {
				size = 512
			}
			if size < 8 {
				size = 8
			}
		}
	}
	loadImage(Request{hash: title, size: size}, w, r)
}

var templates = template.Must(template.ParseFiles("resources/upload.html", "resources/save.html", "resources/index.html"))

func renderTemplate(w http.ResponseWriter, tmpl string, data interface{}) {
	err := templates.ExecuteTemplate(w, tmpl+".html", data)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func uploadHandler(w http.ResponseWriter, r *http.Request, title string) {
	renderTemplate(w, "upload", map[string]string{})
}

func createHash(email string) string {
	h := md5.New()
	io.WriteString(h, strings.TrimSpace(strings.ToLower(email)))
	return fmt.Sprintf("%x", h.Sum(nil))
}

func saveHandler(w http.ResponseWriter, r *http.Request, title string) {
	email := r.FormValue("email")
	log.Printf("Saving image for email address: %v", email)
	file, _, err := r.FormFile("datafile")
	if err != nil {
		log.Print("Error: ", err)
		fmt.Fprintf(w, "
Please chooce a file to upload
")
		return
	}

	hash := createHash(email)
	filename := createAvatarPath(hash)

	f, err := os.Create(filename)
	if err != nil {
		log.Print("Error: ", err)
		fmt.Fprintf(w, "
Error while uploading file
")
		return
	}
	defer f.Close()
	io.Copy(f, file)

	renderTemplate(w, "save", map[string]string{"Avatar": fmt.Sprintf("/avatar/%s", hash)})
}

func mainHandler(w http.ResponseWriter, r *http.Request, title string) {
	hostname, err := os.Hostname()
	if err != nil {
		hostname = "localhost"
	}
	portName := ""
	if *port != 80 {
		portName = fmt.Sprintf(":%d", *port)
	}
	url := "http://" + hostname + portName + "/avatar/"
	renderTemplate(w, "index", map[string]string{"AvatarLink": url, "HostName": hostname})
}

// Creates a http request handler
// pattern is a regular expression that validates the URL. The first matching group is passes to the handler as title
func makeHandler(fn func(http.ResponseWriter, *http.Request, string), pattern string) http.HandlerFunc {
	regex := regexp.MustCompile(pattern)
	return func(w http.ResponseWriter, r *http.Request) {
		//		log.Printf("Request: %v", r)
		m := regex.FindStringSubmatch(r.URL.Path)
		if m == nil {
			log.Print("Invalid request: ", r.URL)
			http.NotFound(w, r)
			return
		}
		log.Printf("Handling request %v %v from %v", r.Method, r.URL, strings.Split(r.RemoteAddr, ":")[0])
		start := time.Now()
		fn(w, r, m[1])
		log.Printf("Handled request %v %v in %v", r.Method, r.URL, time.Since(start))
	}
}

var fallbackDefaultPattern = regexp.MustCompile("^remote:([a-zA-Z]+)$")

func main() {
	iniflags.Parse()

	log.Printf("data dir = %s\n", *dataDir)
	address := fmt.Sprintf(":%d", *port)

	if *remote == "none" {
		remoteUrl = ""
	} else {
		remoteUrl = *remote
		log.Printf("Missig avatars will be redirected to %s", remoteUrl)
	}

	if *dflt == "fallback" {
		log.Printf("Default image will be provided by the remote service if configured")
		remoteDefault = ""
	} else if builtin := fallbackDefaultPattern.FindStringSubmatch(*dflt); builtin != nil {
		remoteDefault = builtin[1]
		log.Printf("Default image will be provided by the remote service using '?d=%s' if a remote is configured", remoteDefault)
	} else {
		defaultImage = *dflt
		remoteDefault = "404"
		log.Printf("Using %s as default image", defaultImage)
	}

	log.Printf("Listening on %s\n", address)
	http.HandleFunc("/", makeHandler(mainHandler, "^/()$"))
	http.HandleFunc("/avatar/", makeHandler(avatarHandler, "^/avatar/([a-zA-Z0-9]+)$"))
	http.HandleFunc("/upload/", makeHandler(uploadHandler, "^/(upload)/$"))
	http.HandleFunc("/save/", makeHandler(saveHandler, "^/(save)/$"))
	x := http.ListenAndServe(address, nil)
	fmt.Println("Result: ", x)
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"flag"
	"fmt"
	"hash"
	"io"
	"os"
	"path/filepath"
	"strings"
)

var flags struct {
	hash       string
	printOnly  bool
	skipHidden bool
}

func init() {
	flag.StringVar(&flags.hash, "hash", "sha512", "Hash to detect duplicated files (md5, sha1, sha256, sha512 - default)")
	flag.BoolVar(&flags.printOnly, "print-only", false, "Only print files instead of removing")
	flag.BoolVar(&flags.skipHidden, "skip-hidden", true, "Skip hidden files/directories (starting with dot)")
}

func main() {
	flag.Parse()
	if err := run(); err != nil {
		os.Stderr.WriteString(err.Error() + "\n")
		os.Exit(1)
	}
}

func run() error {
	hasher := hasherByName(flags.hash)
	if hasher == nil {
		return fmt.Errorf("unknown hash: " + flags.hash + ", supported are: md5, sha1, sha256, sha512")
	}

	walker := deduper(hasher, flags.printOnly)
	for _, path := range flag.Args() {
		path, err := filepath.Abs(path)
		if err != nil {
			return err
		}

		if err := walkFiles(path, flags.skipHidden, walker); err != nil {
			return err
		}
	}
	return nil
}

func hasherByName(name string) hash.Hash {
	switch name {
	case "md5":
		return md5.New()
	case "sha1":
		return sha1.New()
	case "sha256":
		return sha256.New()
	case "sha512":
		return sha512.New()
	default:
		return nil
	}
}

func walkFiles(path string, skipHidden bool, cb func(path string) error) error {
	return filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if skipHidden && strings.HasPrefix(info.Name(), ".") {
			if info.IsDir() {
				return filepath.SkipDir
			}
			return nil
		}

		if info.Mode().IsRegular() {
			return cb(path)
		}
		return nil
	})
}

func deduper(hasher hash.Hash, printOnly bool) func(path string) error {
	seen := map[string]struct{}{}
	dupe := map[string]string{}

	hashBytes := make([]byte, 0, hasher.Size())

	return func(path string) error {
		hasher.Reset()
		hashBytes = hashBytes[:0]

		if _, ok := seen[path]; ok {
			return nil
		}
		seen[path] = struct{}{}

		f, err := os.Open(path)
		if err != nil {
			return err
		}
		defer f.Close()

		if _, err := io.Copy(hasher, f); err != nil {
			return err
		}

		hashBytes = hasher.Sum(hashBytes)
		original, isDupe := dupe[string(hashBytes)]

		if !isDupe {
			dupe[string(hashBytes)] = path
			return nil
		}

		remove := path
		if len(filepath.Base(original)) > len(filepath.Base(path)) {
			remove = original
			dupe[string(hashBytes)] = path
		}

		fmt.Printf("%s\n", remove)

		if printOnly {
			return nil
		}
		return os.Remove(remove)
	}
}
<commit_msg>Cmd doc<commit_after>/*

Command rmdupes removes duplicated files by content hash.

Usage:

	rmdupes --print-only --skip-hidden=false path/to/dir

*/
package main

import (
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"flag"
	"fmt"
	"hash"
	"io"
	"os"
	"path/filepath"
	"strings"
)

var flags struct {
	hash       string
	printOnly  bool
	skipHidden bool
}

func init() {
	flag.StringVar(&flags.hash, "hash", "sha512", "Hash to detect duplicated files (md5, sha1, sha256, sha512 - default)")
	flag.BoolVar(&flags.printOnly, "print-only", false, "Only print files instead of removing")
	flag.BoolVar(&flags.skipHidden, "skip-hidden", true, "Skip hidden files/directories (starting with dot)")
}

func main() {
	flag.Parse()
	if err := run(); err != nil {
		os.Stderr.WriteString(err.Error() + "\n")
		os.Exit(1)
	}
}

func run() error {
	hasher := hasherByName(flags.hash)
	if hasher == nil {
		return fmt.Errorf("unknown hash: " + flags.hash + ", supported are: md5, sha1, sha256, sha512")
	}

	walker := deduper(hasher, flags.printOnly)
	for _, path := range flag.Args() {
		path, err := filepath.Abs(path)
		if err != nil {
			return err
		}

		if err := walkFiles(path, flags.skipHidden, walker); err != nil {
			return err
		}
	}
	return nil
}

func hasherByName(name string) hash.Hash {
	switch name {
	case "md5":
		return md5.New()
	case "sha1":
		return sha1.New()
	case "sha256":
		return sha256.New()
	case "sha512":
		return sha512.New()
	default:
		return nil
	}
}

func walkFiles(path string, skipHidden bool, cb func(path string) error) error {
	return filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if skipHidden && strings.HasPrefix(info.Name(), ".") {
			if info.IsDir() {
				return filepath.SkipDir
			}
			return nil
		}

		if info.Mode().IsRegular() {
			return cb(path)
		}
		return nil
	})
}

func deduper(hasher hash.Hash, printOnly bool) func(path string) error {
	seen := map[string]struct{}{}
	dupe := map[string]string{}

	hashBytes := make([]byte, 0, hasher.Size())

	return func(path string) error {
		hasher.Reset()
		hashBytes = hashBytes[:0]

		if _, ok := seen[path]; ok {
			return nil
		}
		seen[path] = struct{}{}

		f, err := os.Open(path)
		if err != nil {
			return err
		}
		defer f.Close()

		if _, err := io.Copy(hasher, f); err != nil {
			return err
		}

		hashBytes = hasher.Sum(hashBytes)
		original, isDupe := dupe[string(hashBytes)]

		if !isDupe {
			dupe[string(hashBytes)] = path
			return nil
		}

		remove := path
		if len(filepath.Base(original)) > len(filepath.Base(path)) {
			remove = original
			dupe[string(hashBytes)] = path
		}

		fmt.Printf("%s\n", remove)

		if printOnly {
			return nil
		}
		return os.Remove(remove)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"
	"os"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/Sirupsen/logrus/hooks/sentry"
	"github.com/robfig/cron"

	"github.com/oursky/skygear/asset"
	"github.com/oursky/skygear/authtoken"
	"github.com/oursky/skygear/handler"
	"github.com/oursky/skygear/hook"
	"github.com/oursky/skygear/plugin"
	_ "github.com/oursky/skygear/plugin/exec"
	_ "github.com/oursky/skygear/plugin/zmq"
	"github.com/oursky/skygear/provider"
	"github.com/oursky/skygear/pubsub"
	"github.com/oursky/skygear/push"
	"github.com/oursky/skygear/router"
	"github.com/oursky/skygear/skydb"
	_ "github.com/oursky/skygear/skydb/fs"
	_ "github.com/oursky/skygear/skydb/pq"
	"github.com/oursky/skygear/subscription"
)

func usage() {
	fmt.Println("Usage: skygear [<config file>]")
}

func main() {
	var configPath string
	if len(os.Args) < 2 {
		configPath = os.Getenv("OD_CONFIG")
		if configPath == "" {
			usage()
			return
		}
	} else {
		configPath = os.Args[1]
	}

	config := Configuration{}
	if err := ReadFileInto(&config, configPath); err != nil {
		fmt.Println(err.Error())
		return
	}

	initLogger(config)

	connOpener := func() (skydb.Conn, error) {
		return skydb.Open(config.DB.ImplName, config.App.Name, config.DB.Option)
	}
	conn, connError := connOpener()
	if connError != nil {
		log.Fatalf("Failed to start skygear: %v", connError)
	}
	conn.Close()

	pushSender := initPushSender(config, connOpener)

	internalHub := pubsub.NewHub()
	initSubscription(config, connOpener, internalHub, pushSender)
	initDevice(config, connOpener)

	notificationPreprocessor := notificationPreprocessor{
		NotificationSender: pushSender,
	}

	store := initAssetStore(config)
	assetStorePreprocessor := assetStorePreprocessor{
		Store: store,
	}

	naiveAPIKeyPreprocessor := apiKeyValidatonPreprocessor{
		Key:     config.App.APIKey,
		AppName: config.App.Name,
	}

	tokenStore := authtoken.InitTokenStore(config.TokenStore.ImplName, config.TokenStore.Path)
	tokenStorePreprocessor := tokenStorePreprocessor{
		Store: tokenStore,
	}

	authenticator := userAuthenticator{
		APIKey:  config.App.APIKey,
		AppName: config.App.Name,
	}

	dbConnPreprocessor := connPreprocessor{
		AppName:  config.App.Name,
		DBOpener: skydb.Open,
		DBImpl:   config.DB.ImplName,
		Option:   config.DB.Option,
	}

	assetGetPreprocessors := []router.Processor{
		dbConnPreprocessor.Preprocess,
		assetStorePreprocessor.Preprocess,
	}
	assetUploadPreprocessors := []router.Processor{
		tokenStorePreprocessor.Preprocess,
		authenticator.Preprocess,
		dbConnPreprocessor.Preprocess,
		assetStorePreprocessor.Preprocess,
	}
	fileGateway := router.NewGateway(`files/(.+)`)
	fileGateway.GET(handler.AssetGetURLHandler, assetGetPreprocessors...)
	fileGateway.PUT(handler.AssetUploadURLHandler, assetUploadPreprocessors...)
	http.Handle("/files/", router.LoggingMiddleware(fileGateway, true))

	r := router.NewRouter()
	r.Map("", handler.HomeHandler)

	providerRegistry := provider.NewRegistry()
	providerRegistryPreprocessor := providerRegistryPreprocessor{
		Registry: providerRegistry,
	}

	authPreprocessors := []router.Processor{
		naiveAPIKeyPreprocessor.Preprocess,
		dbConnPreprocessor.Preprocess,
		tokenStorePreprocessor.Preprocess,
		providerRegistryPreprocessor.Preprocess,
	}
	r.Map("auth:signup", handler.SignupHandler, authPreprocessors...)
	r.Map("auth:login", handler.LoginHandler, authPreprocessors...)
	r.Map("auth:logout", handler.LogoutHandler,
		tokenStorePreprocessor.Preprocess,
		authenticator.Preprocess,
		providerRegistryPreprocessor.Preprocess,
	)
	r.Map("auth:password", handler.PasswordHandler,
		dbConnPreprocessor.Preprocess,
		tokenStorePreprocessor.Preprocess,
		authenticator.Preprocess,
	)

	hookRegistry := hook.NewRegistry()
	hookRegistryPreprocessor := hookRegistryPreprocessor{
		Registry: hookRegistry,
	}

	recordReadPreprocessors := []router.Processor{
		tokenStorePreprocessor.Preprocess,
		authenticator.Preprocess,
		dbConnPreprocessor.Preprocess,
		assetStorePreprocessor.Preprocess,
		injectUserIfPresent,
		injectDatabase,
	}
	recordWritePreprocessors := []router.Processor{
		hookRegistryPreprocessor.Preprocess,
		tokenStorePreprocessor.Preprocess,
		authenticator.Preprocess,
		dbConnPreprocessor.Preprocess,
		assetStorePreprocessor.Preprocess,
		injectUserIfPresent,
		injectDatabase,
		requireUserForWrite,
	}
	r.Map("record:fetch", handler.RecordFetchHandler, recordReadPreprocessors...)
	r.Map("record:query", handler.RecordQueryHandler, recordReadPreprocessors...)
	r.Map("record:save", handler.RecordSaveHandler, recordWritePreprocessors...)
	r.Map("record:delete", handler.RecordDeleteHandler, recordWritePreprocessors...)

	r.Map("device:register",
		handler.DeviceRegisterHandler,
		tokenStorePreprocessor.Preprocess,
		authenticator.Preprocess,
		dbConnPreprocessor.Preprocess,
		injectUserIfPresent,
	)

	// subscription shares the same set of preprocessor as record read at the moment
	r.Map("subscription:fetch_all", handler.SubscriptionFetchAllHandler, recordReadPreprocessors...)
	r.Map("subscription:fetch", handler.SubscriptionFetchHandler, recordReadPreprocessors...)
	r.Map("subscription:save", handler.SubscriptionSaveHandler, recordReadPreprocessors...)
	r.Map("subscription:delete", handler.SubscriptionDeleteHandler, recordReadPreprocessors...)

	// relation shares the same setof preprocessor
	r.Map("relation:query", handler.RelationQueryHandler, recordReadPreprocessors...)
	r.Map("relation:add", handler.RelationAddHandler, recordReadPreprocessors...)
	r.Map("relation:remove", handler.RelationRemoveHandler, recordReadPreprocessors...)

	userReadPreprocessors := []router.Processor{
		tokenStorePreprocessor.Preprocess,
		authenticator.Preprocess,
		dbConnPreprocessor.Preprocess,
		injectUserIfPresent,
		injectDatabase,
	}
	userWritePreprocessors := []router.Processor{
		tokenStorePreprocessor.Preprocess,
		authenticator.Preprocess,
		dbConnPreprocessor.Preprocess,
		injectUserIfPresent,
		injectDatabase,
		requireUserForWrite,
	}
	r.Map("user:query", handler.UserQueryHandler, userReadPreprocessors...)
	r.Map("user:update", handler.UserUpdateHandler, userWritePreprocessors...)
	r.Map("user:link", handler.UserLinkHandler,
		tokenStorePreprocessor.Preprocess,
		authenticator.Preprocess,
		dbConnPreprocessor.Preprocess,
		providerRegistryPreprocessor.Preprocess,
		injectUserIfPresent,
		injectDatabase,
		requireUserForWrite,
	)

	notificationPreprocessors := []router.Processor{
		naiveAPIKeyPreprocessor.Preprocess,
		dbConnPreprocessor.Preprocess,
		injectDatabase,
		notificationPreprocessor.Preprocess,
	}

	r.Map("push:user", handler.PushToUserHandler, notificationPreprocessors...)
	r.Map("push:device", handler.PushToDeviceHandler, notificationPreprocessors...)

	plugins := []plugin.Plugin{}
	for _, pluginConfig := range config.Plugin {
		p := plugin.NewPlugin(pluginConfig.Transport, pluginConfig.Path, pluginConfig.Args)

		plugins = append(plugins, p)
	}

	c := cron.New()
	initContext := plugin.InitContext{
		Router:           r,
		HookRegistry:     hookRegistry,
		ProviderRegistry: providerRegistry,
		Scheduler:        c,
	}

	for _, plug := range plugins {
		plug.Init(&initContext)
	}
	c.Start()

	pubSubPreprocessors := []router.Processor{
		naiveAPIKeyPreprocessor.Preprocess,
	}

	pubSub := pubsub.NewWsPubsub(nil)
	pubSubGateway := router.NewGateway(`pubSub`)
	pubSubGateway.GET(handler.NewPubSubHandler(pubSub), pubSubPreprocessors...)

	internalPubSub := pubsub.NewWsPubsub(internalHub)
	internalPubSubGateway := router.NewGateway(`internalpubSub`)
	internalPubSubGateway.GET(handler.NewPubSubHandler(internalPubSub), pubSubPreprocessors...)

	http.Handle("/", router.LoggingMiddleware(r, false))
	http.Handle("/pubsub", router.LoggingMiddleware(pubSubGateway, false))
	http.Handle("/_/pubsub", router.LoggingMiddleware(internalPubSubGateway, false))

	log.Printf("Listening on %v...", config.HTTP.Host)
	err := http.ListenAndServe(config.HTTP.Host, nil)
	if err != nil {
		log.Printf("Failed: %v", err)
	}
}

func initAssetStore(config Configuration) asset.Store {
	var store asset.Store
	switch config.AssetStore.ImplName {
	default:
		panic("unrecgonized asset store implementation: " + config.AssetStore.ImplName)
	case "fs":
		store = asset.NewFileStore(
			config.AssetStore.Path,
			config.AssetURLSigner.URLPrefix,
			config.AssetURLSigner.Secret)
	case "s3":
		s3Store, err := asset.NewS3Store(
			config.AssetStore.AccessToken,
			config.AssetStore.SecretToken,
			config.AssetStore.Reigon,
			config.AssetStore.Bucket,
		)
		if err != nil {
			panic("failed to initialize asset.S3Store: " + err.Error())
		}
		store = s3Store
	}
	return store
}

func initDevice(config Configuration, connOpener func() (skydb.Conn, error)) {
	// TODO: Create a device service to check APNs to remove obsolete devices.
	// The current implementaion deletes pubsub devices if the last registered
	// time is more than 1 day old.
	conn, err := connOpener()
	if err != nil {
		log.Warnf("Failed to delete outdated devices: %v", err)
	}

	conn.DeleteEmptyDevicesByTime(time.Now().AddDate(0, 0, -1))
}

func initPushSender(config Configuration, connOpener func() (skydb.Conn, error)) push.Sender {
	routeSender := push.NewRouteSender()
	if config.APNS.Enable {
		routeSender.Route("aps", initAPNSPusher(config, connOpener))
	}
	if config.GCM.Enable {
		routeSender.Route("gcm", initGCMPusher(config))
	}

	if routeSender.Len() == 0 {
		return nil
	}
	return routeSender
}

func initAPNSPusher(config Configuration, connOpener func() (skydb.Conn, error)) *push.APNSPusher {
	apnsPushSender, err := push.NewAPNSPusher(connOpener, push.GatewayType(config.APNS.Env), config.APNS.Cert, config.APNS.Key)
	if err != nil {
		log.Fatalf("Failed to set up push sender: %v", err)
	}
	go apnsPushSender.Run()
	go apnsPushSender.RunFeedback()

	return apnsPushSender
}

func initGCMPusher(config Configuration) *push.GCMPusher {
	return &push.GCMPusher{APIKey: config.GCM.APIKey}
}

func initSubscription(config Configuration, connOpener func() (skydb.Conn, error), hub *pubsub.Hub, pushSender push.Sender) {
	notifiers := []subscription.Notifier{subscription.NewHubNotifier(hub)}
	if pushSender != nil {
		notifiers = append(notifiers, subscription.NewPushNotifier(pushSender))
	}

	subscriptionService := &subscription.Service{
		ConnOpener: connOpener,
		Notifier:   subscription.NewMultiNotifier(notifiers...),
	}
	log.Infoln("Subscription Service listening...")
	go subscriptionService.Run()
}

func initLogger(config Configuration) {
	// Setup Logging
	log.SetOutput(os.Stderr)
	level, err := log.ParseLevel(config.LOG.Level)
	if err != nil {
		log.Warnf("log: error parsing config: %v", err)
		log.Warnln("log: fall back to `debug`")
		level = log.DebugLevel
	}
	log.SetLevel(level)

	if config.LogHook.SentryDSN != "" {
		initSentry(config)
	}
}

func higherLogLevels(minLevel log.Level) []log.Level {
	levels := []log.Level{
		log.PanicLevel,
		log.FatalLevel,
		log.ErrorLevel,
		log.WarnLevel,
		log.InfoLevel,
		log.DebugLevel,
	}

	output := make([]log.Level, 0, len(levels))
	for _, level := range levels {
		if level <= minLevel {
			output = append(output, level)
		}
	}
	return output
}

func initSentry(config Configuration) {
	level, err := log.ParseLevel(config.LogHook.SentryLevel)
	if err != nil {
		log.Fatalf("log-hook: error parsing sentry-level: %v", err)
		return
	}

	levels := higherLogLevels(level)

	hook, err := logrus_sentry.NewSentryHook(config.LogHook.SentryDSN, levels)
	hook.Timeout = 1 * time.Second
	if err != nil {
		log.Errorf("Failed to initialize Sentry: %v", err)
		return
	}
	log.Infof("Logging to Sentry: %v", levels)
	log.AddHook(hook)
}
<commit_msg>Hotfix device type ios cannot send push notification<commit_after>package main

import (
	"fmt"
	"net/http"
	"os"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/Sirupsen/logrus/hooks/sentry"
	"github.com/robfig/cron"

	"github.com/oursky/skygear/asset"
	"github.com/oursky/skygear/authtoken"
	"github.com/oursky/skygear/handler"
	"github.com/oursky/skygear/hook"
	"github.com/oursky/skygear/plugin"
	_ "github.com/oursky/skygear/plugin/exec"
	_ "github.com/oursky/skygear/plugin/zmq"
	"github.com/oursky/skygear/provider"
	"github.com/oursky/skygear/pubsub"
	"github.com/oursky/skygear/push"
	"github.com/oursky/skygear/router"
	"github.com/oursky/skygear/skydb"
	_ "github.com/oursky/skygear/skydb/fs"
	_ "github.com/oursky/skygear/skydb/pq"
	"github.com/oursky/skygear/subscription"
)

func usage() {
	fmt.Println("Usage: skygear [<config file>]")
}

func main() {
	var configPath string
	if len(os.Args) < 2 {
		configPath = os.Getenv("OD_CONFIG")
		if configPath == "" {
			usage()
			return
		}
	} else {
		configPath = os.Args[1]
	}

	config := Configuration{}
	if err := ReadFileInto(&config, configPath); err != nil {
		fmt.Println(err.Error())
		return
	}

	initLogger(config)

	connOpener := func() (skydb.Conn, error) {
		return skydb.Open(config.DB.ImplName, config.App.Name, config.DB.Option)
	}
	conn, connError := connOpener()
	if connError != nil {
		log.Fatalf("Failed to start skygear: %v", connError)
	}
	conn.Close()

	pushSender := initPushSender(config, connOpener)

	internalHub := pubsub.NewHub()
	initSubscription(config, connOpener, internalHub, pushSender)
	initDevice(config, connOpener)

	notificationPreprocessor := notificationPreprocessor{
		NotificationSender: pushSender,
	}

	store := initAssetStore(config)
	assetStorePreprocessor := assetStorePreprocessor{
		Store: store,
	}

	naiveAPIKeyPreprocessor := apiKeyValidatonPreprocessor{
		Key:     config.App.APIKey,
		AppName: config.App.Name,
	}

	tokenStore := authtoken.InitTokenStore(config.TokenStore.ImplName, config.TokenStore.Path)
	tokenStorePreprocessor := tokenStorePreprocessor{
		Store: tokenStore,
	}

	authenticator := userAuthenticator{
		APIKey:  config.App.APIKey,
		AppName: config.App.Name,
	}

	dbConnPreprocessor := connPreprocessor{
		AppName:  config.App.Name,
		DBOpener: skydb.Open,
		DBImpl:   config.DB.ImplName,
		Option:   config.DB.Option,
	}

	assetGetPreprocessors := []router.Processor{
		dbConnPreprocessor.Preprocess,
		assetStorePreprocessor.Preprocess,
	}
	assetUploadPreprocessors := []router.Processor{
		tokenStorePreprocessor.Preprocess,
		authenticator.Preprocess,
		dbConnPreprocessor.Preprocess,
		assetStorePreprocessor.Preprocess,
	}
	fileGateway := router.NewGateway(`files/(.+)`)
	fileGateway.GET(handler.AssetGetURLHandler, assetGetPreprocessors...)
	fileGateway.PUT(handler.AssetUploadURLHandler, assetUploadPreprocessors...)
	http.Handle("/files/", router.LoggingMiddleware(fileGateway, true))

	r := router.NewRouter()
	r.Map("", handler.HomeHandler)

	providerRegistry := provider.NewRegistry()
	providerRegistryPreprocessor := providerRegistryPreprocessor{
		Registry: providerRegistry,
	}

	authPreprocessors := []router.Processor{
		naiveAPIKeyPreprocessor.Preprocess,
		dbConnPreprocessor.Preprocess,
		tokenStorePreprocessor.Preprocess,
		providerRegistryPreprocessor.Preprocess,
	}
	r.Map("auth:signup", handler.SignupHandler, authPreprocessors...)
	r.Map("auth:login", handler.LoginHandler, authPreprocessors...)
	r.Map("auth:logout", handler.LogoutHandler,
		tokenStorePreprocessor.Preprocess,
		authenticator.Preprocess,
		providerRegistryPreprocessor.Preprocess,
	)
	r.Map("auth:password", handler.PasswordHandler,
		dbConnPreprocessor.Preprocess,
		tokenStorePreprocessor.Preprocess,
		authenticator.Preprocess,
	)

	hookRegistry := hook.NewRegistry()
	hookRegistryPreprocessor := hookRegistryPreprocessor{
		Registry: hookRegistry,
	}

	recordReadPreprocessors := []router.Processor{
		tokenStorePreprocessor.Preprocess,
		authenticator.Preprocess,
		dbConnPreprocessor.Preprocess,
		assetStorePreprocessor.Preprocess,
		injectUserIfPresent,
		injectDatabase,
	}
	recordWritePreprocessors := []router.Processor{
		hookRegistryPreprocessor.Preprocess,
		tokenStorePreprocessor.Preprocess,
		authenticator.Preprocess,
		dbConnPreprocessor.Preprocess,
		assetStorePreprocessor.Preprocess,
		injectUserIfPresent,
		injectDatabase,
		requireUserForWrite,
	}
	r.Map("record:fetch", handler.RecordFetchHandler, recordReadPreprocessors...)
	r.Map("record:query", handler.RecordQueryHandler, recordReadPreprocessors...)
	r.Map("record:save", handler.RecordSaveHandler, recordWritePreprocessors...)
	r.Map("record:delete", handler.RecordDeleteHandler, recordWritePreprocessors...)

	r.Map("device:register",
		handler.DeviceRegisterHandler,
		tokenStorePreprocessor.Preprocess,
		authenticator.Preprocess,
		dbConnPreprocessor.Preprocess,
		injectUserIfPresent,
	)

	// subscription shares the same set of preprocessor as record read at the moment
	r.Map("subscription:fetch_all", handler.SubscriptionFetchAllHandler, recordReadPreprocessors...)
	r.Map("subscription:fetch", handler.SubscriptionFetchHandler, recordReadPreprocessors...)
	r.Map("subscription:save", handler.SubscriptionSaveHandler, recordReadPreprocessors...)
	r.Map("subscription:delete", handler.SubscriptionDeleteHandler, recordReadPreprocessors...)

	// relation shares the same setof preprocessor
	r.Map("relation:query", handler.RelationQueryHandler, recordReadPreprocessors...)
	r.Map("relation:add", handler.RelationAddHandler, recordReadPreprocessors...)
	r.Map("relation:remove", handler.RelationRemoveHandler, recordReadPreprocessors...)

	userReadPreprocessors := []router.Processor{
		tokenStorePreprocessor.Preprocess,
		authenticator.Preprocess,
		dbConnPreprocessor.Preprocess,
		injectUserIfPresent,
		injectDatabase,
	}
	userWritePreprocessors := []router.Processor{
		tokenStorePreprocessor.Preprocess,
		authenticator.Preprocess,
		dbConnPreprocessor.Preprocess,
		injectUserIfPresent,
		injectDatabase,
		requireUserForWrite,
	}
	r.Map("user:query", handler.UserQueryHandler, userReadPreprocessors...)
	r.Map("user:update", handler.UserUpdateHandler, userWritePreprocessors...)
	r.Map("user:link", handler.UserLinkHandler,
		tokenStorePreprocessor.Preprocess,
		authenticator.Preprocess,
		dbConnPreprocessor.Preprocess,
		providerRegistryPreprocessor.Preprocess,
		injectUserIfPresent,
		injectDatabase,
		requireUserForWrite,
	)

	notificationPreprocessors := []router.Processor{
		naiveAPIKeyPreprocessor.Preprocess,
		dbConnPreprocessor.Preprocess,
		injectDatabase,
		notificationPreprocessor.Preprocess,
	}

	r.Map("push:user", handler.PushToUserHandler, notificationPreprocessors...)
	r.Map("push:device", handler.PushToDeviceHandler, notificationPreprocessors...)

	plugins := []plugin.Plugin{}
	for _, pluginConfig := range config.Plugin {
		p := plugin.NewPlugin(pluginConfig.Transport, pluginConfig.Path, pluginConfig.Args)

		plugins = append(plugins, p)
	}

	c := cron.New()
	initContext := plugin.InitContext{
		Router:           r,
		HookRegistry:     hookRegistry,
		ProviderRegistry: providerRegistry,
		Scheduler:        c,
	}

	for _, plug := range plugins {
		plug.Init(&initContext)
	}
	c.Start()

	pubSubPreprocessors := []router.Processor{
		naiveAPIKeyPreprocessor.Preprocess,
	}

	pubSub := pubsub.NewWsPubsub(nil)
	pubSubGateway := router.NewGateway(`pubSub`)
	pubSubGateway.GET(handler.NewPubSubHandler(pubSub), pubSubPreprocessors...)

	internalPubSub := pubsub.NewWsPubsub(internalHub)
	internalPubSubGateway := router.NewGateway(`internalpubSub`)
	internalPubSubGateway.GET(handler.NewPubSubHandler(internalPubSub), pubSubPreprocessors...)

	http.Handle("/", router.LoggingMiddleware(r, false))
	http.Handle("/pubsub", router.LoggingMiddleware(pubSubGateway, false))
	http.Handle("/_/pubsub", router.LoggingMiddleware(internalPubSubGateway, false))

	log.Printf("Listening on %v...", config.HTTP.Host)
	err := http.ListenAndServe(config.HTTP.Host, nil)
	if err != nil {
		log.Printf("Failed: %v", err)
	}
}

func initAssetStore(config Configuration) asset.Store {
	var store asset.Store
	switch config.AssetStore.ImplName {
	default:
		panic("unrecgonized asset store implementation: " + config.AssetStore.ImplName)
	case "fs":
		store = asset.NewFileStore(
			config.AssetStore.Path,
			config.AssetURLSigner.URLPrefix,
			config.AssetURLSigner.Secret)
	case "s3":
		s3Store, err := asset.NewS3Store(
			config.AssetStore.AccessToken,
			config.AssetStore.SecretToken,
			config.AssetStore.Reigon,
			config.AssetStore.Bucket,
		)
		if err != nil {
			panic("failed to initialize asset.S3Store: " + err.Error())
		}
		store = s3Store
	}
	return store
}

func initDevice(config Configuration, connOpener func() (skydb.Conn, error)) {
	// TODO: Create a device service to check APNs to remove obsolete devices.
	// The current implementaion deletes pubsub devices if the last registered
	// time is more than 1 day old.
	conn, err := connOpener()
	if err != nil {
		log.Warnf("Failed to delete outdated devices: %v", err)
	}

	conn.DeleteEmptyDevicesByTime(time.Now().AddDate(0, 0, -1))
}

func initPushSender(config Configuration, connOpener func() (skydb.Conn, error)) push.Sender {
	routeSender := push.NewRouteSender()
	if config.APNS.Enable {
		apns := initAPNSPusher(config, connOpener)
		routeSender.Route("aps", apns)
		routeSender.Route("ios", apns)
	}
	if config.GCM.Enable {
		gcm := initGCMPusher(config)
		routeSender.Route("gcm", gcm)
		routeSender.Route("android", gcm)
	}

	if routeSender.Len() == 0 {
		return nil
	}
	return routeSender
}

func initAPNSPusher(config Configuration, connOpener func() (skydb.Conn, error)) *push.APNSPusher {
	apnsPushSender, err := push.NewAPNSPusher(connOpener, push.GatewayType(config.APNS.Env), config.APNS.Cert, config.APNS.Key)
	if err != nil {
		log.Fatalf("Failed to set up push sender: %v", err)
	}
	go apnsPushSender.Run()
	go apnsPushSender.RunFeedback()

	return apnsPushSender
}

func initGCMPusher(config Configuration) *push.GCMPusher {
	return &push.GCMPusher{APIKey: config.GCM.APIKey}
}

func initSubscription(config Configuration, connOpener func() (skydb.Conn, error), hub *pubsub.Hub, pushSender push.Sender) {
	notifiers := []subscription.Notifier{subscription.NewHubNotifier(hub)}
	if pushSender != nil {
		notifiers = append(notifiers, subscription.NewPushNotifier(pushSender))
	}

	subscriptionService := &subscription.Service{
		ConnOpener: connOpener,
		Notifier:   subscription.NewMultiNotifier(notifiers...),
	}
	log.Infoln("Subscription Service listening...")
	go subscriptionService.Run()
}

func initLogger(config Configuration) {
	// Setup Logging
	log.SetOutput(os.Stderr)
	level, err := log.ParseLevel(config.LOG.Level)
	if err != nil {
		log.Warnf("log: error parsing config: %v", err)
		log.Warnln("log: fall back to `debug`")
		level = log.DebugLevel
	}
	log.SetLevel(level)

	if config.LogHook.SentryDSN != "" {
		initSentry(config)
	}
}

func higherLogLevels(minLevel log.Level) []log.Level {
	levels := []log.Level{
		log.PanicLevel,
		log.FatalLevel,
		log.ErrorLevel,
		log.WarnLevel,
		log.InfoLevel,
		log.DebugLevel,
	}

	output := make([]log.Level, 0, len(levels))
	for _, level := range levels {
		if level <= minLevel {
			output = append(output, level)
		}
	}
	return output
}

func initSentry(config Configuration) {
	level, err := log.ParseLevel(config.LogHook.SentryLevel)
	if err != nil {
		log.Fatalf("log-hook: error parsing sentry-level: %v", err)
		return
	}

	levels := higherLogLevels(level)

	hook, err := logrus_sentry.NewSentryHook(config.LogHook.SentryDSN, levels)
	hook.Timeout = 1 * time.Second
	if err != nil {
		log.Errorf("Failed to initialize Sentry: %v", err)
		return
	}
	log.Infof("Logging to Sentry: %v", levels)
	log.AddHook(hook)
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"errors"
	"fmt"
	"github.com/orchardup/orchard/cli"
	"github.com/orchardup/orchard/github.com/docopt/docopt.go"
	"github.com/orchardup/orchard/proxy"
	"github.com/orchardup/orchard/tlsconfig"
	"io/ioutil"
	"net"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"strings"
	"syscall"
	"text/tabwriter"
)

func main() {
	usage := `Orchard.

Usage:
  orchard hosts
  orchard hosts create NAME
  orchard hosts rm NAME
  orchard docker [COMMAND...]
  orchard proxy

Options:
  -h --help   Show this screen.
  --version   Show version.`

	args, err := docopt.Parse(usage, nil, true, "Orchard 2.0.0", true)
	if err != nil {
		fmt.Println("Error parsing arguments: %s\n", err)
		return
	}

	if args["hosts"] == true {
		if err := Hosts(args); err != nil {
			fmt.Println(err)
		}
	} else if args["docker"] == true || args["proxy"] == true {
		socketPath := "/tmp/orchard.sock"

		p, err := MakeProxy(socketPath, "default")
		if err != nil {
			fmt.Printf("Error starting proxy: %v\n", err)
			return
		}

		go p.Start()
		defer p.Stop()

		if err := <-p.ErrorChannel; err != nil {
			fmt.Printf("Error starting proxy: %v\n", err)
			return
		}

		if args["docker"] == true {
			err := CallDocker(args["COMMAND"].([]string), []string{"DOCKER_HOST=unix://" + socketPath})
			if err != nil {
				fmt.Println(err)
			}
		} else {
			fmt.Println("Started proxy at unix://" + socketPath)

			c := make(chan os.Signal)
			signal.Notify(c, syscall.SIGINT, syscall.SIGKILL)
			<-c

			fmt.Println("\nStopping proxy")
		}
	}
}

func MakeProxy(socketPath string, hostName string) (*proxy.Proxy, error) {
	// httpClient, err := authenticator.Authenticate()
	// if err != nil {
	// 	return nil, err
	// }

	// host, err := httpClient.GetHost(hostName)
	// if err != nil {
	// 	return nil, err
	// }
	// destination := host.IPv4_Address+":443"

	destination := "107.170.41.173:4243"
	certData, err := ioutil.ReadFile("client-cert.pem")
	if err != nil {
		return nil, err
	}
	keyData, err := ioutil.ReadFile("client-key.pem")
	if err != nil {
		return nil, err
	}

	config, err := tlsconfig.GetTLSConfig(certData, keyData)
	if err != nil {
		return nil, err
	}

	return proxy.New(
		func() (net.Listener, error) { return net.Listen("unix", socketPath) },
		func() (net.Conn, error) { return tls.Dial("tcp", destination, config) },
	), nil
}

func CallDocker(args []string, env []string) error {
	dockerPath := GetDockerPath()
	if dockerPath == "" {
		return errors.New("Can't find `docker` executable in $PATH.\nYou might need to install it: http://docs.docker.io/en/latest/installation/#installation-list")
	}

	cmd := exec.Command(dockerPath, args...)
	cmd.Env = env
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func GetDockerPath() string {
	for _, dir := range strings.Split(os.Getenv("PATH"), ":") {
		dockerPath := path.Join(dir, "docker")
		_, err := os.Stat(dockerPath)
		if err == nil {
			return dockerPath
		}
	}
	return ""
}

func Hosts(args map[string]interface{}) error {
	httpClient, err := authenticator.Authenticate()
	if err != nil {
		fmt.Printf("Error authenticating:\n%s\n", err)
	}

	if args["create"] == true {
		host, err := httpClient.CreateHost(args["NAME"].(string))
		if err != nil {
			return err
		}
		fmt.Printf("Created %s with IP address %s\n", host.Name, host.IPv4_Address)
	} else if args["rm"] == true {
		err := httpClient.DeleteHost(args["NAME"].(string))
		if err != nil {
			return err
		}
		fmt.Printf("Removed %s\n", args["NAME"].(string))
	} else {
		hosts, err := httpClient.GetHosts()
		if err != nil {
			return err
		}

		writer := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
		fmt.Fprintln(writer, "NAME\tIP")
		for _, host := range hosts {
			fmt.Fprintf(writer, "%s\t%s\n", host.Name, host.IPv4_Address)
		}
		writer.Flush()
	}

	return nil
}
<commit_msg>Get host name from command-line arg if present<commit_after>package main

import (
	"crypto/tls"
	"errors"
	"fmt"
	"github.com/orchardup/orchard/cli"
	"github.com/orchardup/orchard/github.com/docopt/docopt.go"
	"github.com/orchardup/orchard/proxy"
	"github.com/orchardup/orchard/tlsconfig"
	"io/ioutil"
	"net"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"strings"
	"syscall"
	"text/tabwriter"
)

func main() {
	usage := `Orchard.

Usage:
  orchard hosts
  orchard hosts create NAME
  orchard hosts rm NAME
  orchard [options] docker [COMMAND...]
  orchard [options] proxy

Options:
  -h --help               Show this screen.
  --version               Show version.
  -H NAME, --host=NAME    Name of host to connect to (instead of 'default')`

	args, err := docopt.Parse(usage, nil, true, "Orchard 2.0.0", true)
	if err != nil {
		fmt.Println("Error parsing arguments: %s\n", err)
		return
	}

	if args["hosts"] == true {
		if err := Hosts(args); err != nil {
			fmt.Println(err)
		}
	} else if args["docker"] == true || args["proxy"] == true {
		hostName := "default"
		if args["--host"] != nil {
			hostName = args["--host"].(string)
		}

		socketPath := "/tmp/orchard.sock"

		p, err := MakeProxy(socketPath, hostName)
		if err != nil {
			fmt.Printf("Error starting proxy: %v\n", err)
			return
		}

		go p.Start()
		defer p.Stop()

		if err := <-p.ErrorChannel; err != nil {
			fmt.Printf("Error starting proxy: %v\n", err)
			return
		}

		if args["docker"] == true {
			err := CallDocker(args["COMMAND"].([]string), []string{"DOCKER_HOST=unix://" + socketPath})
			if err != nil {
				fmt.Println(err)
			}
		} else {
			fmt.Println("Started proxy at unix://" + socketPath)

			c := make(chan os.Signal)
			signal.Notify(c, syscall.SIGINT, syscall.SIGKILL)
			<-c

			fmt.Println("\nStopping proxy")
		}
	}
}

func MakeProxy(socketPath string, hostName string) (*proxy.Proxy, error) {
	httpClient, err := authenticator.Authenticate()
	if err != nil {
		return nil, err
	}

	host, err := httpClient.GetHost(hostName)
	if err != nil {
		return nil, err
	}
	destination := host.IPv4_Address + ":4243"

	fmt.Printf("Connecting to %s...\n", destination)

	certData, err := ioutil.ReadFile("client-cert.pem")
	if err != nil {
		return nil, err
	}
	keyData, err := ioutil.ReadFile("client-key.pem")
	if err != nil {
		return nil, err
	}

	config, err := tlsconfig.GetTLSConfig(certData, keyData)
	if err != nil {
		return nil, err
	}

	return proxy.New(
		func() (net.Listener, error) { return net.Listen("unix", socketPath) },
		func() (net.Conn, error) { return tls.Dial("tcp", destination, config) },
	), nil
}

func CallDocker(args []string, env []string) error {
	dockerPath := GetDockerPath()
	if dockerPath == "" {
		return errors.New("Can't find `docker` executable in $PATH.\nYou might need to install it: http://docs.docker.io/en/latest/installation/#installation-list")
	}

	cmd := exec.Command(dockerPath, args...)
	cmd.Env = env
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func GetDockerPath() string {
	for _, dir := range strings.Split(os.Getenv("PATH"), ":") {
		dockerPath := path.Join(dir, "docker")
		_, err := os.Stat(dockerPath)
		if err == nil {
			return dockerPath
		}
	}
	return ""
}

func Hosts(args map[string]interface{}) error {
	httpClient, err := authenticator.Authenticate()
	if err != nil {
		fmt.Printf("Error authenticating:\n%s\n", err)
	}

	if args["create"] == true {
		host, err := httpClient.CreateHost(args["NAME"].(string))
		if err != nil {
			return err
		}
		fmt.Printf("Created %s with IP address %s\n", host.Name, host.IPv4_Address)
	} else if args["rm"] == true {
		err := httpClient.DeleteHost(args["NAME"].(string))
		if err != nil {
			return err
		}
		fmt.Printf("Removed %s\n", args["NAME"].(string))
	} else {
		hosts, err := httpClient.GetHosts()
		if err != nil {
			return err
		}

		writer := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
		fmt.Fprintln(writer, "NAME\tIP")
		for _, host := range hosts {
			fmt.Fprintf(writer, "%s\t%s\n", host.Name, host.IPv4_Address)
		}
		writer.Flush()
	}

	return nil
}
<|endoftext|>