<commit_before>/*
Copyright 2016 The Fission Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"os"

	"github.com/urfave/cli"
)

func main() {
	app := cli.NewApp()
	app.Name = "fission"
	app.Usage = "Serverless functions for Kubernetes"

	app.Flags = []cli.Flag{
		cli.StringFlag{Name: "server", Usage: "Fission server URL", EnvVar: "FISSION_URL"},
	}

	// trigger method and url flags (used in function and route CLIs)
	htMethodFlag := cli.StringFlag{Name: "method", Usage: "HTTP Method: GET|POST|PUT|DELETE|HEAD; defaults to GET"}
	htUrlFlag := cli.StringFlag{Name: "url", Usage: "URL pattern (See gorilla/mux supported patterns)"}

	// functions
	fnNameFlag := cli.StringFlag{Name: "name", Usage: "function name"}
	fnEnvNameFlag := cli.StringFlag{Name: "env", Usage: "environment name for function"}
	fnCodeFlag := cli.StringFlag{Name: "code", Usage: "local path or URL for source code"}
	fnPackageFlag := cli.StringFlag{Name: "package", Usage: "local path or URL for binary package"}
	fnUidFlag := cli.StringFlag{Name: "uid", Usage: "function uid, optional (use latest if unspecified)"}
	fnPodFlag := cli.StringFlag{Name: "pod", Usage: "function pod name, optional (use latest if unspecified)"}
	fnFollowFlag := cli.BoolFlag{Name: "follow, f", Usage: "specify if the logs should be streamed"}
	fnDetailFlag := cli.BoolFlag{Name: "detail, d", Usage: "display detailed information"}
	fnLogDBTypeFlag := cli.StringFlag{Name: "dbtype", Usage: "log database type, e.g. influxdb (currently only influxdb is supported)"}
	fnSubcommands := []cli.Command{
		{Name: "create", Usage: "Create new function (and optionally, an HTTP route to it)", Flags: []cli.Flag{fnNameFlag, fnEnvNameFlag, fnCodeFlag, fnPackageFlag, htUrlFlag, htMethodFlag}, Action: fnCreate},
		{Name: "get", Usage: "Get function source code", Flags: []cli.Flag{fnNameFlag, fnUidFlag}, Action: fnGet},
		{Name: "edit", Usage: "Edit function source code in $EDITOR", Flags: []cli.Flag{fnNameFlag, fnUidFlag}, Action: fnEdit},
		{Name: "getmeta", Usage: "Get function metadata", Flags: []cli.Flag{fnNameFlag, fnUidFlag}, Action: fnGetMeta},
		{Name: "update", Usage: "Update function source code", Flags: []cli.Flag{fnNameFlag, fnEnvNameFlag, fnCodeFlag, fnPackageFlag}, Action: fnUpdate},
		{Name: "delete", Usage: "Delete function", Flags: []cli.Flag{fnNameFlag, fnUidFlag}, Action: fnDelete},
		{Name: "list", Usage: "List all functions", Flags: []cli.Flag{}, Action: fnList},
		{Name: "logs", Usage: "Display funtion logs", Flags: []cli.Flag{fnNameFlag, fnPodFlag, fnFollowFlag, fnDetailFlag, fnLogDBTypeFlag}, Action: fnLogs},
		{Name: "pods", Usage: "Display funtion pods", Flags: []cli.Flag{fnNameFlag, fnLogDBTypeFlag}, Action: fnPods},
	}

	// httptriggers
	htNameFlag := cli.StringFlag{Name: "name", Usage: "HTTP Trigger name"}
	htFnNameFlag := cli.StringFlag{Name: "function", Usage: "Function name"}
	htFnUidFlag := cli.StringFlag{Name: "uid", Usage: "Function UID (optional; uses latest if unspecified)"}
	htSubcommands := []cli.Command{
		{Name: "create", Aliases: []string{"add"}, Usage: "Create HTTP trigger", Flags: []cli.Flag{htMethodFlag, htUrlFlag, htFnNameFlag, htFnUidFlag}, Action: htCreate},
		{Name: "get", Usage: "Get HTTP trigger", Flags: []cli.Flag{htMethodFlag, htUrlFlag}, Action: htGet},
		{Name: "update", Usage: "Update HTTP trigger", Flags: []cli.Flag{htNameFlag, htFnNameFlag, htFnUidFlag}, Action: htUpdate},
		{Name: "delete", Usage: "Delete HTTP trigger", Flags: []cli.Flag{htNameFlag}, Action: htDelete},
		{Name: "list", Usage: "List HTTP triggers", Flags: []cli.Flag{}, Action: htList},
	}

	// timetriggers
	ttNameFlag := cli.StringFlag{Name: "name", Usage: "Time Trigger name"}
	ttCronFlag := cli.StringFlag{Name: "cron", Usage: "Time Trigger cron spec ('0 30 * * *', '@every 5m', '@hourly')"}
	ttFnNameFlag := cli.StringFlag{Name: "function", Usage: "Function name"}
	ttFnUidFlag := cli.StringFlag{Name: "uid", Usage: "Function UID (optional; uses latest if unspecified)"}
	ttSubcommands := []cli.Command{
		{Name: "create", Aliases: []string{"add"}, Usage: "Create Time trigger", Flags: []cli.Flag{ttNameFlag, ttFnNameFlag, ttFnUidFlag, ttCronFlag}, Action: ttCreate},
		{Name: "get", Usage: "Get Time trigger", Flags: []cli.Flag{}, Action: ttGet},
		{Name: "update", Usage: "Update Time trigger", Flags: []cli.Flag{ttNameFlag, ttCronFlag}, Action: ttUpdate},
		{Name: "delete", Usage: "Delete Time trigger", Flags: []cli.Flag{ttNameFlag}, Action: ttDelete},
		{Name: "list", Usage: "List Time triggers", Flags: []cli.Flag{}, Action: ttList},
	}

	// Message queue trigger
	mqtNameFlag := cli.StringFlag{Name: "name", Usage: "Message queue Trigger name"}
	mqtFnNameFlag := cli.StringFlag{Name: "function", Usage: "Function name"}
	mqtFnUidFlag := cli.StringFlag{Name: "uid", Usage: "Function UID (optional; uses latest if unspecified)"}
	mqtMQTypeFlag := cli.StringFlag{Name: "mqtype", Usage: "Message queue type, e.g. nats-streaming  (optional; uses \"nats-streaming\" if unspecified)"}
	mqtTopicFlag := cli.StringFlag{Name: "topic", Usage: "Message queue Topic the trigger listens on"}
	mqtRespTopicFlag := cli.StringFlag{Name: "resptopic", Usage: "Topic that the function response is sent on (optional; response discarded if unspecified)"}
	mqtSubcommands := []cli.Command{
		{Name: "create", Aliases: []string{"add"}, Usage: "Create Message queue trigger", Flags: []cli.Flag{mqtNameFlag, mqtFnNameFlag, mqtFnUidFlag, mqtMQTypeFlag, mqtTopicFlag, mqtRespTopicFlag}, Action: mqtCreate},
		{Name: "get", Usage: "Get message queue trigger", Flags: []cli.Flag{}, Action: mqtGet},
		{Name: "update", Usage: "Update message queue trigger", Flags: []cli.Flag{mqtNameFlag, mqtTopicFlag, mqtRespTopicFlag}, Action: mqtUpdate},
		{Name: "delete", Usage: "Delete message queue trigger", Flags: []cli.Flag{mqtNameFlag}, Action: mqtDelete},
		{Name: "list", Usage: "List message queue triggers", Flags: []cli.Flag{mqtMQTypeFlag}, Action: mqtList},
	}

	// environments
	envNameFlag := cli.StringFlag{Name: "name", Usage: "Environment name"}
	envImageFlag := cli.StringFlag{Name: "image", Usage: "Environment image URL"}
	envSubcommands := []cli.Command{
		{Name: "create", Aliases: []string{"add"}, Usage: "Add an environment", Flags: []cli.Flag{envNameFlag, envImageFlag}, Action: envCreate},
		{Name: "get", Usage: "Get environment details", Flags: []cli.Flag{envNameFlag}, Action: envGet},
		{Name: "update", Usage: "Update environment", Flags: []cli.Flag{envNameFlag, envImageFlag}, Action: envUpdate},
		{Name: "delete", Usage: "Delete environment", Flags: []cli.Flag{envNameFlag}, Action: envDelete},
		{Name: "list", Usage: "List all environments", Flags: []cli.Flag{}, Action: envList},
	}

	// watches
	wNameFlag := cli.StringFlag{Name: "name", Usage: "Watch name"}
	wFnNameFlag := cli.StringFlag{Name: "function", Usage: "Function name"}
	wFnUidFlag := cli.StringFlag{Name: "uid", Usage: "Function UID (optional; uses latest if unspecified)"}
	wNamespaceFlag := cli.StringFlag{Name: "ns", Usage: "Namespace of resource to watch"}
	wObjTypeFlag := cli.StringFlag{Name: "type", Usage: "Type of resource to watch (Pod, Service, etc.)"}
	wLabelsFlag := cli.StringFlag{Name: "labels", Usage: "Label selector of the form a=b,c=d"}
	wSubCommands := []cli.Command{
		{Name: "create", Aliases: []string{"add"}, Usage: "Create a watch", Flags: []cli.Flag{wFnNameFlag, wFnUidFlag, wNamespaceFlag, wObjTypeFlag, wLabelsFlag}, Action: wCreate},
		{Name: "get", Usage: "Get details about a watch", Flags: []cli.Flag{wNameFlag}, Action: wGet},
		// TODO add update flag when supported
		{Name: "delete", Usage: "Delete watch", Flags: []cli.Flag{wNameFlag}, Action: wDelete},
		{Name: "list", Usage: "List all watches", Flags: []cli.Flag{}, Action: wList},
	}

	app.Commands = []cli.Command{
		{Name: "function", Aliases: []string{"fn"}, Usage: "Create, update and manage functions", Subcommands: fnSubcommands},
		{Name: "httptrigger", Aliases: []string{"ht", "route"}, Usage: "Manage HTTP triggers (routes) for functions", Subcommands: htSubcommands},
		{Name: "timetrigger", Aliases: []string{"tt", "timer"}, Usage: "Manage Time triggers (timers) for functions", Subcommands: ttSubcommands},
		{Name: "mqtrigger", Aliases: []string{"mqt", "messagequeue"}, Usage: "Manage message queue triggers for functions", Subcommands: mqtSubcommands},
		{Name: "environment", Aliases: []string{"env"}, Usage: "Manage environments", Subcommands: envSubcommands},
		{Name: "watch", Aliases: []string{"w"}, Usage: "Manage watches", Subcommands: wSubCommands},

		// Misc commands
		{
			Name:  "get-deployment-yaml",
			Usage: "Get deployment yaml.  Use it as 'fission get-deployment-yaml | kubectl create -f -'",
			Action: func(c *cli.Context) error {
				getDeploymentYaml()
				return nil
			},
		},
	}

	app.Run(os.Args)
}
<commit_msg>fix typo funtion -> function (#252)<commit_after>/*
Copyright 2016 The Fission Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"os"

	"github.com/urfave/cli"
)

func main() {
	app := cli.NewApp()
	app.Name = "fission"
	app.Usage = "Serverless functions for Kubernetes"

	app.Flags = []cli.Flag{
		cli.StringFlag{Name: "server", Usage: "Fission server URL", EnvVar: "FISSION_URL"},
	}

	// trigger method and url flags (used in function and route CLIs)
	htMethodFlag := cli.StringFlag{Name: "method", Usage: "HTTP Method: GET|POST|PUT|DELETE|HEAD; defaults to GET"}
	htUrlFlag := cli.StringFlag{Name: "url", Usage: "URL pattern (See gorilla/mux supported patterns)"}

	// functions
	fnNameFlag := cli.StringFlag{Name: "name", Usage: "function name"}
	fnEnvNameFlag := cli.StringFlag{Name: "env", Usage: "environment name for function"}
	fnCodeFlag := cli.StringFlag{Name: "code", Usage: "local path or URL for source code"}
	fnPackageFlag := cli.StringFlag{Name: "package", Usage: "local path or URL for binary package"}
	fnUidFlag := cli.StringFlag{Name: "uid", Usage: "function uid, optional (use latest if unspecified)"}
	fnPodFlag := cli.StringFlag{Name: "pod", Usage: "function pod name, optional (use latest if unspecified)"}
	fnFollowFlag := cli.BoolFlag{Name: "follow, f", Usage: "specify if the logs should be streamed"}
	fnDetailFlag := cli.BoolFlag{Name: "detail, d", Usage: "display detailed information"}
	fnLogDBTypeFlag := cli.StringFlag{Name: "dbtype", Usage: "log database type, e.g. influxdb (currently only influxdb is supported)"}
	fnSubcommands := []cli.Command{
		{Name: "create", Usage: "Create new function (and optionally, an HTTP route to it)", Flags: []cli.Flag{fnNameFlag, fnEnvNameFlag, fnCodeFlag, fnPackageFlag, htUrlFlag, htMethodFlag}, Action: fnCreate},
		{Name: "get", Usage: "Get function source code", Flags: []cli.Flag{fnNameFlag, fnUidFlag}, Action: fnGet},
		{Name: "edit", Usage: "Edit function source code in $EDITOR", Flags: []cli.Flag{fnNameFlag, fnUidFlag}, Action: fnEdit},
		{Name: "getmeta", Usage: "Get function metadata", Flags: []cli.Flag{fnNameFlag, fnUidFlag}, Action: fnGetMeta},
		{Name: "update", Usage: "Update function source code", Flags: []cli.Flag{fnNameFlag, fnEnvNameFlag, fnCodeFlag, fnPackageFlag}, Action: fnUpdate},
		{Name: "delete", Usage: "Delete function", Flags: []cli.Flag{fnNameFlag, fnUidFlag}, Action: fnDelete},
		{Name: "list", Usage: "List all functions", Flags: []cli.Flag{}, Action: fnList},
		{Name: "logs", Usage: "Display function logs", Flags: []cli.Flag{fnNameFlag, fnPodFlag, fnFollowFlag, fnDetailFlag, fnLogDBTypeFlag}, Action: fnLogs},
		{Name: "pods", Usage: "Display function pods", Flags: []cli.Flag{fnNameFlag, fnLogDBTypeFlag}, Action: fnPods},
	}

	// httptriggers
	htNameFlag := cli.StringFlag{Name: "name", Usage: "HTTP Trigger name"}
	htFnNameFlag := cli.StringFlag{Name: "function", Usage: "Function name"}
	htFnUidFlag := cli.StringFlag{Name: "uid", Usage: "Function UID (optional; uses latest if unspecified)"}
	htSubcommands := []cli.Command{
		{Name: "create", Aliases: []string{"add"}, Usage: "Create HTTP trigger", Flags: []cli.Flag{htMethodFlag, htUrlFlag, htFnNameFlag, htFnUidFlag}, Action: htCreate},
		{Name: "get", Usage: "Get HTTP trigger", Flags: []cli.Flag{htMethodFlag, htUrlFlag}, Action: htGet},
		{Name: "update", Usage: "Update HTTP trigger", Flags: []cli.Flag{htNameFlag, htFnNameFlag, htFnUidFlag}, Action: htUpdate},
		{Name: "delete", Usage: "Delete HTTP trigger", Flags: []cli.Flag{htNameFlag}, Action: htDelete},
		{Name: "list", Usage: "List HTTP triggers", Flags: []cli.Flag{}, Action: htList},
	}

	// timetriggers
	ttNameFlag := cli.StringFlag{Name: "name", Usage: "Time Trigger name"}
	ttCronFlag := cli.StringFlag{Name: "cron", Usage: "Time Trigger cron spec ('0 30 * * *', '@every 5m', '@hourly')"}
	ttFnNameFlag := cli.StringFlag{Name: "function", Usage: "Function name"}
	ttFnUidFlag := cli.StringFlag{Name: "uid", Usage: "Function UID (optional; uses latest if unspecified)"}
	ttSubcommands := []cli.Command{
		{Name: "create", Aliases: []string{"add"}, Usage: "Create Time trigger", Flags: []cli.Flag{ttNameFlag, ttFnNameFlag, ttFnUidFlag, ttCronFlag}, Action: ttCreate},
		{Name: "get", Usage: "Get Time trigger", Flags: []cli.Flag{}, Action: ttGet},
		{Name: "update", Usage: "Update Time trigger", Flags: []cli.Flag{ttNameFlag, ttCronFlag}, Action: ttUpdate},
		{Name: "delete", Usage: "Delete Time trigger", Flags: []cli.Flag{ttNameFlag}, Action: ttDelete},
		{Name: "list", Usage: "List Time triggers", Flags: []cli.Flag{}, Action: ttList},
	}

	// Message queue trigger
	mqtNameFlag := cli.StringFlag{Name: "name", Usage: "Message queue Trigger name"}
	mqtFnNameFlag := cli.StringFlag{Name: "function", Usage: "Function name"}
	mqtFnUidFlag := cli.StringFlag{Name: "uid", Usage: "Function UID (optional; uses latest if unspecified)"}
	mqtMQTypeFlag := cli.StringFlag{Name: "mqtype", Usage: "Message queue type, e.g. nats-streaming  (optional; uses \"nats-streaming\" if unspecified)"}
	mqtTopicFlag := cli.StringFlag{Name: "topic", Usage: "Message queue Topic the trigger listens on"}
	mqtRespTopicFlag := cli.StringFlag{Name: "resptopic", Usage: "Topic that the function response is sent on (optional; response discarded if unspecified)"}
	mqtSubcommands := []cli.Command{
		{Name: "create", Aliases: []string{"add"}, Usage: "Create Message queue trigger", Flags: []cli.Flag{mqtNameFlag, mqtFnNameFlag, mqtFnUidFlag, mqtMQTypeFlag, mqtTopicFlag, mqtRespTopicFlag}, Action: mqtCreate},
		{Name: "get", Usage: "Get message queue trigger", Flags: []cli.Flag{}, Action: mqtGet},
		{Name: "update", Usage: "Update message queue trigger", Flags: []cli.Flag{mqtNameFlag, mqtTopicFlag, mqtRespTopicFlag}, Action: mqtUpdate},
		{Name: "delete", Usage: "Delete message queue trigger", Flags: []cli.Flag{mqtNameFlag}, Action: mqtDelete},
		{Name: "list", Usage: "List message queue triggers", Flags: []cli.Flag{mqtMQTypeFlag}, Action: mqtList},
	}

	// environments
	envNameFlag := cli.StringFlag{Name: "name", Usage: "Environment name"}
	envImageFlag := cli.StringFlag{Name: "image", Usage: "Environment image URL"}
	envSubcommands := []cli.Command{
		{Name: "create", Aliases: []string{"add"}, Usage: "Add an environment", Flags: []cli.Flag{envNameFlag, envImageFlag}, Action: envCreate},
		{Name: "get", Usage: "Get environment details", Flags: []cli.Flag{envNameFlag}, Action: envGet},
		{Name: "update", Usage: "Update environment", Flags: []cli.Flag{envNameFlag, envImageFlag}, Action: envUpdate},
		{Name: "delete", Usage: "Delete environment", Flags: []cli.Flag{envNameFlag}, Action: envDelete},
		{Name: "list", Usage: "List all environments", Flags: []cli.Flag{}, Action: envList},
	}

	// watches
	wNameFlag := cli.StringFlag{Name: "name", Usage: "Watch name"}
	wFnNameFlag := cli.StringFlag{Name: "function", Usage: "Function name"}
	wFnUidFlag := cli.StringFlag{Name: "uid", Usage: "Function UID (optional; uses latest if unspecified)"}
	wNamespaceFlag := cli.StringFlag{Name: "ns", Usage: "Namespace of resource to watch"}
	wObjTypeFlag := cli.StringFlag{Name: "type", Usage: "Type of resource to watch (Pod, Service, etc.)"}
	wLabelsFlag := cli.StringFlag{Name: "labels", Usage: "Label selector of the form a=b,c=d"}
	wSubCommands := []cli.Command{
		{Name: "create", Aliases: []string{"add"}, Usage: "Create a watch", Flags: []cli.Flag{wFnNameFlag, wFnUidFlag, wNamespaceFlag, wObjTypeFlag, wLabelsFlag}, Action: wCreate},
		{Name: "get", Usage: "Get details about a watch", Flags: []cli.Flag{wNameFlag}, Action: wGet},
		// TODO add update flag when supported
		{Name: "delete", Usage: "Delete watch", Flags: []cli.Flag{wNameFlag}, Action: wDelete},
		{Name: "list", Usage: "List all watches", Flags: []cli.Flag{}, Action: wList},
	}

	app.Commands = []cli.Command{
		{Name: "function", Aliases: []string{"fn"}, Usage: "Create, update and manage functions", Subcommands: fnSubcommands},
		{Name: "httptrigger", Aliases: []string{"ht", "route"}, Usage: "Manage HTTP triggers (routes) for functions", Subcommands: htSubcommands},
		{Name: "timetrigger", Aliases: []string{"tt", "timer"}, Usage: "Manage Time triggers (timers) for functions", Subcommands: ttSubcommands},
		{Name: "mqtrigger", Aliases: []string{"mqt", "messagequeue"}, Usage: "Manage message queue triggers for functions", Subcommands: mqtSubcommands},
		{Name: "environment", Aliases: []string{"env"}, Usage: "Manage environments", Subcommands: envSubcommands},
		{Name: "watch", Aliases: []string{"w"}, Usage: "Manage watches", Subcommands: wSubCommands},

		// Misc commands
		{
			Name:  "get-deployment-yaml",
			Usage: "Get deployment yaml.  Use it as 'fission get-deployment-yaml | kubectl create -f -'",
			Action: func(c *cli.Context) error {
				getDeploymentYaml()
				return nil
			},
		},
	}

	app.Run(os.Args)
}
<|endoftext|>


<commit_before>package osversion

/*
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/sysctl.h>

int darwin_get_os(char* str, size_t size) {
    return sysctlbyname("kern.osrelease", str, &size, NULL, 0);
}
*/
import "C"

import (
	"errors"
	"strconv"
	"strings"
	"unsafe"

	"github.com/blang/semver"
)

func GetString() (string, error) {
	bufferSize := C.size_t(256)
	str := (*C.char)(C.malloc(bufferSize))
	defer C.free(unsafe.Pointer(str))

	err := C.darwin_get_os(str, bufferSize)
	if err == -1 {
		return "", errors.New("Error running sysctl")
	}
	return C.GoString(str), nil
}

func GetSemanticVersion() (semver.Version, error) {
	str, err := GetString()
	if err != nil {
		return semver.Version{}, err
	}

	return semver.Make(str)
}

func GetHumanReadable() (string, error) {

	versions := []string{
		"",
		"",
		"",
		"",
		"",
		"OS X 10.1.{patch} Puma",
		"OS X 10.2.{patch} Jaguar",
		"OS X 10.3.{patch} Panther",
		"OS X 10.4.{patch} Tiger",
		"OS X 10.5.{patch} Leopard",
		"OS X 10.6.{patch} Snow Leopard",
		"OS X 10.7.{patch} Lion",
		"OS X 10.8.{patch} Mountain Lion",
		"OS X 10.9.{patch} Mavericks",
		"OS X 10.10.{patch} Yosemite",
		"OS X 10.11.{patch} El Capitan",
	}

	version, err := GetSemanticVersion()
	if err != nil {
		return "", err
	}

	return strings.Replace(versions[version.Major],
		"{patch}",
		strconv.FormatUint(version.Patch, 10),
		1), nil
}
<commit_msg>Added OSX Cheetah<commit_after>package osversion

/*
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/sysctl.h>

int darwin_get_os(char* str, size_t size) {
    return sysctlbyname("kern.osrelease", str, &size, NULL, 0);
}
*/
import "C"

import (
	"errors"
	"strconv"
	"strings"
	"unsafe"

	"github.com/blang/semver"
)

func GetString() (string, error) {
	bufferSize := C.size_t(256)
	str := (*C.char)(C.malloc(bufferSize))
	defer C.free(unsafe.Pointer(str))

	err := C.darwin_get_os(str, bufferSize)
	if err == -1 {
		return "", errors.New("Error running sysctl")
	}
	return C.GoString(str), nil
}

func GetSemanticVersion() (semver.Version, error) {
	str, err := GetString()
	if err != nil {
		return semver.Version{}, err
	}

	return semver.Make(str)
}

func GetHumanReadable() (string, error) {
	versions := []string{
		"OS X 10.0.{patch} Cheetah",
		"OS X 10.1.{patch} Puma",
		"OS X 10.2.{patch} Jaguar",
		"OS X 10.3.{patch} Panther",
		"OS X 10.4.{patch} Tiger",
		"OS X 10.5.{patch} Leopard",
		"OS X 10.6.{patch} Snow Leopard",
		"OS X 10.7.{patch} Lion",
		"OS X 10.8.{patch} Mountain Lion",
		"OS X 10.9.{patch} Mavericks",
		"OS X 10.10.{patch} Yosemite",
		"OS X 10.11.{patch} El Capitan",
	}

	version, err := GetSemanticVersion()
	if err != nil {
		return "", err
	}

	return strings.Replace(versions[version.Major - 4],
		"{patch}",
		strconv.FormatUint(version.Patch, 10),
		1), nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"os"
	"os/signal"
	"strings"
	"syscall"

	"github.com/coreos/fleet/Godeps/_workspace/src/github.com/rakyll/globalconf"

	"github.com/coreos/fleet/agent"
	"github.com/coreos/fleet/config"
	"github.com/coreos/fleet/log"
	"github.com/coreos/fleet/registry"
	"github.com/coreos/fleet/server"
	"github.com/coreos/fleet/version"
)

const (
	DefaultConfigFile = "/etc/fleet/fleet.conf"
)

func main() {
	userset := flag.NewFlagSet("fleet", flag.ExitOnError)
	printVersion := userset.Bool("version", false, "Print the version and exit")
	cfgPath := userset.String("config", "", fmt.Sprintf("Path to config file. Fleet will look for a config at %s by default.", DefaultConfigFile))

	err := userset.Parse(os.Args[1:])
	if err == flag.ErrHelp {
		userset.Usage()
		os.Exit(1)
	}

	if *printVersion {
		fmt.Println("fleet version", version.Version)
		os.Exit(0)
	}

	cfgset := flag.NewFlagSet("fleet", flag.ExitOnError)
	cfgset.Int("verbosity", 0, "Logging level")
	cfgset.Var(&stringSlice{}, "etcd_servers", "List of etcd endpoints")
	cfgset.String("etcd_keyfile", "", "SSL key file used to secure etcd communication")
	cfgset.String("etcd_certfile", "", "SSL certification file used to secure etcd communication")
	cfgset.String("etcd_cafile", "", "SSL Certificate Authority file used to secure etcd communication")
	cfgset.String("etcd_key_prefix", registry.DefaultKeyPrefix, "Keyspace for fleet data in etcd")
	cfgset.Float64("etcd_request_timeout", 1.0, "Amount of time in seconds to allow a single etcd request before considering it failed.")
	cfgset.Float64("engine_reconcile_interval", 2.0, "Interval at which the engine should reconcile the cluster schedule in etcd.")
	cfgset.String("public_ip", "", "IP address that fleet machine should publish")
	cfgset.String("metadata", "", "List of key-value metadata to assign to the fleet machine")
	cfgset.String("agent_ttl", agent.DefaultTTL, "TTL in seconds of fleet machine state in etcd")
	cfgset.Bool("verify_units", false, "DEPRECATED - This option is ignored")
	cfgset.String("authorized_keys_file", "", "DEPRECATED - This option is ignored")

	globalconf.Register("", cfgset)
	cfg, err := getConfig(cfgset, *cfgPath)
	if err != nil {
		log.Fatalf(err.Error())
	}

	log.V(1).Infof("Creating Server")
	srv, err := server.New(*cfg)
	if err != nil {
		log.Fatalf("Failed creating Server: %v", err.Error())
	}
	srv.Run()

	reconfigure := func() {
		log.Infof("Reloading configuration from %s", *cfgPath)

		cfg, err := getConfig(cfgset, *cfgPath)
		if err != nil {
			log.Fatalf(err.Error())
		}

		log.Infof("Restarting server components")
		srv.Stop()

		srv, err = server.New(*cfg)
		if err != nil {
			log.Fatalf(err.Error())
		}
		srv.Run()
	}

	shutdown := func() {
		log.Infof("Gracefully shutting down")
		srv.Stop()
		srv.Purge()
		os.Exit(0)
	}

	writeState := func() {
		log.Infof("Dumping server state")

		encoded, err := json.Marshal(srv)
		if err != nil {
			log.Errorf("Failed to dump server state: %v", err)
			return
		}

		if _, err := os.Stdout.Write(encoded); err != nil {
			log.Errorf("Failed to dump server state: %v", err)
			return
		}

		os.Stdout.Write([]byte("\n"))

		log.V(1).Infof("Finished dumping server state")
	}

	signals := map[os.Signal]func(){
		syscall.SIGHUP:  reconfigure,
		syscall.SIGTERM: shutdown,
		syscall.SIGINT:  shutdown,
		syscall.SIGUSR1: writeState,
	}

	listenForSignals(signals)
}

func getConfig(flagset *flag.FlagSet, userCfgFile string) (*config.Config, error) {
	opts := globalconf.Options{EnvPrefix: "FLEET_"}

	if userCfgFile != "" {
		// Fail hard if a user-provided config is not usable
		fi, err := os.Stat(userCfgFile)
		if err != nil {
			log.Fatalf("Unable to use config file %s: %v", userCfgFile, err)
		}
		if fi.IsDir() {
			log.Fatalf("Provided config %s is a directory, not a file", userCfgFile)
		}

		log.Infof("Using provided config file %s", userCfgFile)
		opts.Filename = userCfgFile

	} else if _, err := os.Stat(DefaultConfigFile); err == nil {
		log.Infof("Using default config file %s", DefaultConfigFile)
		opts.Filename = DefaultConfigFile
	} else {
		log.Infof("No provided or default config file found - proceeding without")
	}

	gconf, err := globalconf.NewWithOptions(&opts)
	if err != nil {
		return nil, err
	}

	gconf.ParseSet("", flagset)

	cfg := config.Config{
		Verbosity:               (*flagset.Lookup("verbosity")).Value.(flag.Getter).Get().(int),
		EtcdServers:             (*flagset.Lookup("etcd_servers")).Value.(flag.Getter).Get().(stringSlice),
		EtcdKeyPrefix:           (*flagset.Lookup("etcd_key_prefix")).Value.(flag.Getter).Get().(string),
		EtcdKeyFile:             (*flagset.Lookup("etcd_keyfile")).Value.(flag.Getter).Get().(string),
		EtcdCertFile:            (*flagset.Lookup("etcd_certfile")).Value.(flag.Getter).Get().(string),
		EtcdCAFile:              (*flagset.Lookup("etcd_cafile")).Value.(flag.Getter).Get().(string),
		EtcdRequestTimeout:      (*flagset.Lookup("etcd_request_timeout")).Value.(flag.Getter).Get().(float64),
		EngineReconcileInterval: (*flagset.Lookup("engine_reconcile_interval")).Value.(flag.Getter).Get().(float64),
		PublicIP:                (*flagset.Lookup("public_ip")).Value.(flag.Getter).Get().(string),
		RawMetadata:             (*flagset.Lookup("metadata")).Value.(flag.Getter).Get().(string),
		AgentTTL:                (*flagset.Lookup("agent_ttl")).Value.(flag.Getter).Get().(string),
		VerifyUnits:             (*flagset.Lookup("verify_units")).Value.(flag.Getter).Get().(bool),
		AuthorizedKeysFile:      (*flagset.Lookup("authorized_keys_file")).Value.(flag.Getter).Get().(string),
	}

	if cfg.VerifyUnits {
		log.Error("Config option verify_units is no longer supported - ignoring")
	}
	if len(cfg.AuthorizedKeysFile) > 0 {
		log.Error("Config option authorized_keys_file is no longer supported - ignoring")
	}

	log.SetVerbosity(cfg.Verbosity)
	return &cfg, nil
}

func listenForSignals(sigmap map[os.Signal]func()) {
	sigchan := make(chan os.Signal, 1)

	for k := range sigmap {
		signal.Notify(sigchan, k)
	}

	for true {
		sig := <-sigchan
		handler, ok := sigmap[sig]
		if ok {
			handler()
		}
	}
}

type stringSlice []string

func (f *stringSlice) Set(value string) error {
	for _, item := range strings.Split(value, ",") {
		item = strings.TrimLeft(item, " [\"")
		item = strings.TrimRight(item, " \"]")
		*f = append(*f, item)
	}

	return nil
}

func (f *stringSlice) String() string {
	return fmt.Sprintf("%v", *f)
}

func (f *stringSlice) Value() []string {
	return *f
}

func (f *stringSlice) Get() interface{} {
	return *f
}
<commit_msg>fleetd: log version at start-up<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"os"
	"os/signal"
	"strings"
	"syscall"

	"github.com/coreos/fleet/Godeps/_workspace/src/github.com/rakyll/globalconf"

	"github.com/coreos/fleet/agent"
	"github.com/coreos/fleet/config"
	"github.com/coreos/fleet/log"
	"github.com/coreos/fleet/registry"
	"github.com/coreos/fleet/server"
	"github.com/coreos/fleet/version"
)

const (
	DefaultConfigFile = "/etc/fleet/fleet.conf"
)

func main() {
	userset := flag.NewFlagSet("fleet", flag.ExitOnError)
	printVersion := userset.Bool("version", false, "Print the version and exit")
	cfgPath := userset.String("config", "", fmt.Sprintf("Path to config file. Fleet will look for a config at %s by default.", DefaultConfigFile))

	err := userset.Parse(os.Args[1:])
	if err == flag.ErrHelp {
		userset.Usage()
		os.Exit(1)
	}

	if *printVersion {
		fmt.Println("fleet version", version.Version)
		os.Exit(0)
	}

	log.Infof("Starting fleet version %v", version.Version)

	cfgset := flag.NewFlagSet("fleet", flag.ExitOnError)
	cfgset.Int("verbosity", 0, "Logging level")
	cfgset.Var(&stringSlice{}, "etcd_servers", "List of etcd endpoints")
	cfgset.String("etcd_keyfile", "", "SSL key file used to secure etcd communication")
	cfgset.String("etcd_certfile", "", "SSL certification file used to secure etcd communication")
	cfgset.String("etcd_cafile", "", "SSL Certificate Authority file used to secure etcd communication")
	cfgset.String("etcd_key_prefix", registry.DefaultKeyPrefix, "Keyspace for fleet data in etcd")
	cfgset.Float64("etcd_request_timeout", 1.0, "Amount of time in seconds to allow a single etcd request before considering it failed.")
	cfgset.Float64("engine_reconcile_interval", 2.0, "Interval at which the engine should reconcile the cluster schedule in etcd.")
	cfgset.String("public_ip", "", "IP address that fleet machine should publish")
	cfgset.String("metadata", "", "List of key-value metadata to assign to the fleet machine")
	cfgset.String("agent_ttl", agent.DefaultTTL, "TTL in seconds of fleet machine state in etcd")
	cfgset.Bool("verify_units", false, "DEPRECATED - This option is ignored")
	cfgset.String("authorized_keys_file", "", "DEPRECATED - This option is ignored")

	globalconf.Register("", cfgset)
	cfg, err := getConfig(cfgset, *cfgPath)
	if err != nil {
		log.Fatalf(err.Error())
	}

	log.V(1).Infof("Creating Server")
	srv, err := server.New(*cfg)
	if err != nil {
		log.Fatalf("Failed creating Server: %v", err.Error())
	}
	srv.Run()

	reconfigure := func() {
		log.Infof("Reloading configuration from %s", *cfgPath)

		cfg, err := getConfig(cfgset, *cfgPath)
		if err != nil {
			log.Fatalf(err.Error())
		}

		log.Infof("Restarting server components")
		srv.Stop()

		srv, err = server.New(*cfg)
		if err != nil {
			log.Fatalf(err.Error())
		}
		srv.Run()
	}

	shutdown := func() {
		log.Infof("Gracefully shutting down")
		srv.Stop()
		srv.Purge()
		os.Exit(0)
	}

	writeState := func() {
		log.Infof("Dumping server state")

		encoded, err := json.Marshal(srv)
		if err != nil {
			log.Errorf("Failed to dump server state: %v", err)
			return
		}

		if _, err := os.Stdout.Write(encoded); err != nil {
			log.Errorf("Failed to dump server state: %v", err)
			return
		}

		os.Stdout.Write([]byte("\n"))

		log.V(1).Infof("Finished dumping server state")
	}

	signals := map[os.Signal]func(){
		syscall.SIGHUP:  reconfigure,
		syscall.SIGTERM: shutdown,
		syscall.SIGINT:  shutdown,
		syscall.SIGUSR1: writeState,
	}

	listenForSignals(signals)
}

func getConfig(flagset *flag.FlagSet, userCfgFile string) (*config.Config, error) {
	opts := globalconf.Options{EnvPrefix: "FLEET_"}

	if userCfgFile != "" {
		// Fail hard if a user-provided config is not usable
		fi, err := os.Stat(userCfgFile)
		if err != nil {
			log.Fatalf("Unable to use config file %s: %v", userCfgFile, err)
		}
		if fi.IsDir() {
			log.Fatalf("Provided config %s is a directory, not a file", userCfgFile)
		}

		log.Infof("Using provided config file %s", userCfgFile)
		opts.Filename = userCfgFile

	} else if _, err := os.Stat(DefaultConfigFile); err == nil {
		log.Infof("Using default config file %s", DefaultConfigFile)
		opts.Filename = DefaultConfigFile
	} else {
		log.Infof("No provided or default config file found - proceeding without")
	}

	gconf, err := globalconf.NewWithOptions(&opts)
	if err != nil {
		return nil, err
	}

	gconf.ParseSet("", flagset)

	cfg := config.Config{
		Verbosity:               (*flagset.Lookup("verbosity")).Value.(flag.Getter).Get().(int),
		EtcdServers:             (*flagset.Lookup("etcd_servers")).Value.(flag.Getter).Get().(stringSlice),
		EtcdKeyPrefix:           (*flagset.Lookup("etcd_key_prefix")).Value.(flag.Getter).Get().(string),
		EtcdKeyFile:             (*flagset.Lookup("etcd_keyfile")).Value.(flag.Getter).Get().(string),
		EtcdCertFile:            (*flagset.Lookup("etcd_certfile")).Value.(flag.Getter).Get().(string),
		EtcdCAFile:              (*flagset.Lookup("etcd_cafile")).Value.(flag.Getter).Get().(string),
		EtcdRequestTimeout:      (*flagset.Lookup("etcd_request_timeout")).Value.(flag.Getter).Get().(float64),
		EngineReconcileInterval: (*flagset.Lookup("engine_reconcile_interval")).Value.(flag.Getter).Get().(float64),
		PublicIP:                (*flagset.Lookup("public_ip")).Value.(flag.Getter).Get().(string),
		RawMetadata:             (*flagset.Lookup("metadata")).Value.(flag.Getter).Get().(string),
		AgentTTL:                (*flagset.Lookup("agent_ttl")).Value.(flag.Getter).Get().(string),
		VerifyUnits:             (*flagset.Lookup("verify_units")).Value.(flag.Getter).Get().(bool),
		AuthorizedKeysFile:      (*flagset.Lookup("authorized_keys_file")).Value.(flag.Getter).Get().(string),
	}

	if cfg.VerifyUnits {
		log.Error("Config option verify_units is no longer supported - ignoring")
	}
	if len(cfg.AuthorizedKeysFile) > 0 {
		log.Error("Config option authorized_keys_file is no longer supported - ignoring")
	}

	log.SetVerbosity(cfg.Verbosity)
	return &cfg, nil
}

func listenForSignals(sigmap map[os.Signal]func()) {
	sigchan := make(chan os.Signal, 1)

	for k := range sigmap {
		signal.Notify(sigchan, k)
	}

	for true {
		sig := <-sigchan
		handler, ok := sigmap[sig]
		if ok {
			handler()
		}
	}
}

type stringSlice []string

func (f *stringSlice) Set(value string) error {
	for _, item := range strings.Split(value, ",") {
		item = strings.TrimLeft(item, " [\"")
		item = strings.TrimRight(item, " \"]")
		*f = append(*f, item)
	}

	return nil
}

func (f *stringSlice) String() string {
	return fmt.Sprintf("%v", *f)
}

func (f *stringSlice) Value() []string {
	return *f
}

func (f *stringSlice) Get() interface{} {
	return *f
}
<|endoftext|>


<commit_before>package flow

import "github.com/github/hub/git"

func flowFeatureStart(featureName string) (err error) {
	branchName := "feature/" + featureName

	cmdGit := [][]string{}

	cmdGit1 := []string{"checkout", "develop"}
	cmdGit2 := []string{"checkout", "-b", branchName}

	cmdGit = append(cmdGit, cmdGit1, cmdGit2)

	err = launchCmdGit(cmdGit)

	return
}

func flowFeatureFinish(featureName string) (err error) {
	branchName := "feature/" + featureName

	cmdGit := [][]string{}

	cmdGit1 := []string{"checkout", "develop"}
	cmdGit2 := []string{"merge", branchName}
	cmdGit3 := []string{"branch", "-d", branchName}

	cmdGit = append(cmdGit, cmdGit1, cmdGit2, cmdGit3)

	err = launchCmdGit(cmdGit)

	return
}

func launchCmdGit(cmdGit [][]string) (err error) {
	for i := range cmdGit {
		err = git.Spawn(cmdGit[i]...)

		if err != nil {
			break
		}
	}

	return
}
<commit_msg>Public function<commit_after>package flow

import "github.com/github/hub/git"

func FlowFeatureStart(featureName string) (err error) {
	branchName := "feature/" + featureName

	cmdGit := [][]string{}

	cmdGit1 := []string{"checkout", "develop"}
	cmdGit2 := []string{"checkout", "-b", branchName}

	cmdGit = append(cmdGit, cmdGit1, cmdGit2)

	err = launchCmdGit(cmdGit)

	return
}

func FlowFeatureFinish(featureName string) (err error) {
	branchName := "feature/" + featureName

	cmdGit := [][]string{}

	cmdGit1 := []string{"checkout", "develop"}
	cmdGit2 := []string{"merge", branchName}
	cmdGit3 := []string{"branch", "-d", branchName}

	cmdGit = append(cmdGit, cmdGit1, cmdGit2, cmdGit3)

	err = launchCmdGit(cmdGit)

	return
}

func launchCmdGit(cmdGit [][]string) (err error) {
	for i := range cmdGit {
		err = git.Spawn(cmdGit[i]...)

		if err != nil {
			break
		}
	}

	return
}
<|endoftext|>


<commit_before>package fluxd

import (
	"bytes"
	"errors"
	"github.com/bitly/go-nsq"
	"log"
	"net/http"
	"sync"
	"time"
)

var (
	// DefaultConfig is the default configuration used for NSQ communications by each binding if one is not specified.
	DefaultConfig *nsq.Config
)

func init() {
	DefaultConfig = nsq.NewConfig()
	_ = DefaultConfig.Set("max_in_flight", 50)
}

// Server represents a collection of bindings
// to NSQLookupd instances.
type Server struct {
	// Config used for NSQ connections; defaults to DefaultConfig
	NSQConfig *nsq.Config
	// Address(es) of nsqlookupd(s)
	Lookupdaddrs []string
	// If true, requests are redirected to StdOut instead of
	// to the http endpoint. Useful for testing.
	UseStdout bool
	bindings  []*Binding
	batchbin  []*BatchBinding
}

// Stop ends all of the server processes gracefully. It does not block.
func (s *Server) Stop() {
	for _, b := range s.bindings {
		go func(b *Binding) {
			b.cons.Stop()
			return
		}(b)
	}
	for _, bb := range s.batchbin {
		go func(bb *BatchBinding) {
			bb.cons.Stop()
			bb.stchan <- struct{}{}
			return
		}(bb)
	}
}

// Bind adds a binding to the server. Bind must be called before Run.
func (s *Server) Bind(b *Binding) { s.bindings = append(s.bindings, b) }

// BindBatch adds a batched binding to the server. Bind must be called before Run.
func (s *Server) BindBatch(b *BatchBinding) { s.batchbin = append(s.batchbin, b) }

// use server config and addrs to configure a binding
// and start the consumer, client, and handler
func (s *Server) startrun(b *Binding) error {
	var err error
	err = b.Endpoint.Init()
	if err != nil {
		return err
	}

	b.cons, err = nsq.NewConsumer(b.Topic, b.Channel, s.NSQConfig)
	if err != nil {
		return err
	}

	if !s.UseStdout {
		// default client
		b.dcl = &http.Client{}
		if b.Workers <= 0 {
			b.Workers = 1
		}
	} else {
		// use stdout
		b.dcl = stdoutcl{}
		b.Workers = 1
	}
	b.cons.AddConcurrentHandlers(nsq.HandlerFunc(b.handle), b.Workers)
	err = b.cons.ConnectToNSQLookupds(s.Lookupdaddrs)
	return err
}

func (s *Server) startbatch(b *BatchBinding) error {
	var err error
	err = b.Endpoint.Init()
	if err != nil {
		return err
	}

	b.cons, err = nsq.NewConsumer(b.Topic, b.Channel, s.NSQConfig)
	if err != nil {
		return err
	}

	if !s.UseStdout {
		b.dcl = &http.Client{}
		if b.Workers < 1 {
			b.Workers = 1
		}
	} else {
		b.dcl = stdoutcl{}
		b.Workers = 1
	}
	if b.MaxMsg <= 0 {
		b.MaxMsg = 50
	}
	if b.BatchTime == 0 {
		b.BatchTime = 250 * time.Millisecond
	}

	b.outbuf = bytes.NewBuffer(nil)
	b.outbuf.Grow(2048)
	b.accum = make(chan *bytes.Buffer, 128)
	b.stchan = make(chan struct{}, 1)

	b.wg = new(sync.WaitGroup)
	b.wg.Add(1)
	go batchloop(b, b.stchan)

	b.cons.AddConcurrentHandlers(nsq.HandlerFunc(b.handle), b.Workers)
	err = b.cons.ConnectToNSQLookupds(s.Lookupdaddrs)
	return err
}

func batchloop(b *BatchBinding, stchan chan struct{}) {
	var inbuf *bytes.Buffer
	var nmsg int
	var ok bool
	b.outbuf.Write(b.Endpoint.BatchPrefix())
	for {
		select {
		case <-time.After(b.BatchTime):
			// can't send if zero
			if nmsg == 0 {
				continue
			}
			// postfix
			b.outbuf.Write(b.Endpoint.BatchPostfix())
			// do request
			res, err := b.dcl.Do(b.Endpoint.Req(b.outbuf))
			if err != nil {
				log.Printf("HTTP Client error: %s", err.Error())
				log.Printf("Failed to send body: %q", b.outbuf.String())
				b.outbuf.Reset()
				nmsg = 0
				b.outbuf.Write(b.Endpoint.BatchPrefix())
				continue
			}
			// validate
			err = b.Endpoint.Validate(res)
			if err != nil {
				log.Printf("Response invalidated: %s", err.Error())
				log.Printf("The following body failed: %q", b.outbuf.String())
				b.outbuf.Reset()
				nmsg = 0
				b.outbuf.Write(b.Endpoint.BatchPrefix())
				continue
			}
			// everything worked ok
			b.outbuf.Reset()
			nmsg = 0
			b.outbuf.Write(b.Endpoint.BatchPrefix())
			continue

		case inbuf, ok = <-b.accum:
			if !ok {
				goto exit
			}
			// write concatenator
			if nmsg != 0 {
				b.outbuf.Write(b.Endpoint.Concat())
			}
			// write prefix, msg, postfix
			b.outbuf.Write(b.Endpoint.EntryPrefix())
			inbuf.WriteTo(b.outbuf)
			b.outbuf.Write(b.Endpoint.EntryPostfix())
			// free inbuf
			putBuf(inbuf)
			// increment msg; see if we have hit maxmsg
			nmsg++
			if nmsg >= b.MaxMsg {
				// BATCH SEND

				// batch postfix
				b.outbuf.Write(b.Endpoint.BatchPostfix())
				//send everything
				res, err := b.dcl.Do(b.Endpoint.Req(b.outbuf))
				if err != nil {
					log.Printf("HTTP Client error: %s", err.Error())
					log.Printf("Failed to send body: %q", b.outbuf.String())
					b.outbuf.Reset()
					nmsg = 0
					b.outbuf.Write(b.Endpoint.BatchPrefix())
					continue
				}
				err = b.Endpoint.Validate(res)
				if err != nil {
					log.Printf("Response invalidated: %s", err.Error())
					log.Printf("The following body failed: %q", b.outbuf.String())
					b.outbuf.Reset()
					nmsg = 0
					b.outbuf.Write(b.Endpoint.BatchPrefix())
					continue
				}
				// everything worked okay
				b.outbuf.Reset()
				nmsg = 0
				b.outbuf.Write(b.Endpoint.BatchPrefix())
				continue
			}
		case <-stchan:
			goto exit
		}
	}
exit:
	b.wg.Done()
	return
}

// Run blocks until all bindings exit gracefully, usually after a call to Stop.
// Run immediately returns an error if the server is not configured correctly.
func (s *Server) Run() error {
	var err error
	if s.NSQConfig == nil {
		s.NSQConfig = DefaultConfig
	}
	err = s.NSQConfig.Validate()
	if err != nil {
		return err
	}

	if len(s.bindings) == 0 && len(s.batchbin) == 0 {
		return errors.New("No bindings registered.")
	}

	for _, b := range s.bindings {
		err = s.startrun(b)
		if err != nil {
			return err
		}
	}

	for _, b := range s.batchbin {
		err = s.startbatch(b)
		if err != nil {
			return err
		}
	}
	// block until graceful stop
	for _, b := range s.bindings {
		<-b.cons.StopChan
	}
	for _, bb := range s.batchbin {
		<-bb.cons.StopChan
		bb.wg.Wait()
	}
	return nil
}

// Binding types connect NSQ channels, flux/msg schemas, and database endpoints.
type Binding struct {
	// Topic is the NSQ topic to listen on
	Topic string
	// Channel is the NSQ channel to listen on
	Channel string
	// Endpoint is the database and decode logic used for this topic & channel
	Endpoint DB
	// Workers sets the number of concurrent goroutines serving this binding; defaults to 1
	Workers int
	dcl     dclient       //used for database communication
	cons    *nsq.Consumer //used for nsq communication
}

// BatchBinding types connect NSQ channels, flux/msg schemas, and database endpoints,
// but they use database request batching.
type BatchBinding struct {
	//Topic is the NSQ topic to listen on
	Topic string

	// Channel is the NSQ channel to listen on
	Channel string

	// Endpoint is the database and decode logic
	Endpoint BatchDB

	// Workers sets the number of concurent goroutines serving this binding; defaults to 1
	Workers int

	// MaxMsg sets the maximum number of messages collected before upload; defaults to 50
	MaxMsg int

	// BatchTime sets the maximum time spend waiting to collect messages before upload; defaults to 250ms
	BatchTime time.Duration

	dcl    dclient            // client
	cons   *nsq.Consumer      // consumer
	outbuf *bytes.Buffer      // for request body
	accum  chan *bytes.Buffer // for accumulating responses
	stchan chan struct{}      // stop channel
	wg     *sync.WaitGroup    // for monitoring workers
}

// implements the nsq.HandleFunc interface
func (b *Binding) handle(msg *nsq.Message) error {
	return dbHandle(b.Endpoint, msg.Body, b.dcl)
}

// implements the nsq.HandleFunc interface
func (b *BatchBinding) handle(msg *nsq.Message) error {
	buf := getBuf()
	err := b.Endpoint.Translate(msg.Body, buf)
	if err != nil {
		putBuf(buf)
		return err
	}
	b.accum <- buf
	return nil
}
<commit_msg>Fix Run()/Stop() data race<commit_after>package fluxd

import (
	"bytes"
	"errors"
	"github.com/bitly/go-nsq"
	"log"
	"net/http"
	"sync"
	"time"
)

var (
	// DefaultConfig is the default configuration used for NSQ communications by each binding if one is not specified.
	DefaultConfig *nsq.Config
)

func init() {
	DefaultConfig = nsq.NewConfig()
	_ = DefaultConfig.Set("max_in_flight", 50)
}

// Server represents a collection of bindings
// to NSQLookupd instances.
type Server struct {
	// Config used for NSQ connections; defaults to DefaultConfig
	NSQConfig *nsq.Config
	// Address(es) of nsqlookupd(s)
	Lookupdaddrs []string
	// If true, requests are redirected to StdOut instead of
	// to the http endpoint. Useful for testing.
	UseStdout bool
	bindings  []*Binding
	batchbin  []*BatchBinding
	lock      *sync.Mutex //lock everything on initialization
	running   bool
}

// Stop ends all of the server processes gracefully. It does not block.
func (s *Server) Stop() {
	s.lock.Lock()
	defer s.lock.Unlock()
	for _, b := range s.bindings {
		go func(b *Binding) {
			b.cons.Stop()
			return
		}(b)
	}
	for _, bb := range s.batchbin {
		go func(bb *BatchBinding) {
			bb.cons.Stop()
			bb.stchan <- struct{}{}
			return
		}(bb)
	}
}

// Bind adds a binding to the server. Bind must be called before Run.
func (s *Server) Bind(b *Binding) {
	if s.lock == nil {
		s.lock = new(sync.Mutex)
	}
	if b == nil {
		panic("Cannot add nil binding.")
	}
	s.bindings = append(s.bindings, b)
}

// BindBatch adds a batched binding to the server. Bind must be called before Run.
func (s *Server) BindBatch(b *BatchBinding) {
	if s.lock == nil {
		s.lock = new(sync.Mutex)
	}
	if b == nil {
		panic("Cannot add nil binding.")
	}
	s.batchbin = append(s.batchbin, b)
}

// use server config and addrs to configure a binding
// and start the consumer, client, and handler
func (s *Server) startrun(b *Binding) error {
	var err error
	b.lock = new(sync.Mutex)
	b.lock.Lock()
	defer b.lock.Unlock()
	err = b.Endpoint.Init()
	if err != nil {
		return err
	}

	b.cons, err = nsq.NewConsumer(b.Topic, b.Channel, s.NSQConfig)
	if err != nil {
		return err
	}

	if !s.UseStdout {
		// default client
		b.dcl = &http.Client{}
		if b.Workers <= 0 {
			b.Workers = 1
		}
	} else {
		// use stdout
		b.dcl = stdoutcl{}
		b.Workers = 1
	}
	b.cons.AddConcurrentHandlers(nsq.HandlerFunc(b.handle), b.Workers)
	err = b.cons.ConnectToNSQLookupds(s.Lookupdaddrs)
	return err
}

func (s *Server) startbatch(b *BatchBinding) error {
	var err error
	b.lock = new(sync.Mutex)
	b.lock.Lock()
	defer b.lock.Unlock()
	err = b.Endpoint.Init()
	if err != nil {
		return err
	}

	b.cons, err = nsq.NewConsumer(b.Topic, b.Channel, s.NSQConfig)
	if err != nil {
		return err
	}

	if !s.UseStdout {
		b.dcl = &http.Client{}
		if b.Workers < 1 {
			b.Workers = 1
		}
	} else {
		b.dcl = stdoutcl{}
		b.Workers = 1
	}
	if b.MaxMsg <= 0 {
		b.MaxMsg = 50
	}
	if b.BatchTime == 0 {
		b.BatchTime = 250 * time.Millisecond
	}

	b.outbuf = bytes.NewBuffer(nil)
	b.outbuf.Grow(2048)
	b.accum = make(chan *bytes.Buffer, 128)
	b.stchan = make(chan struct{}, 1)

	b.wg = new(sync.WaitGroup)
	b.wg.Add(1)
	go batchloop(b, b.stchan)

	b.cons.AddConcurrentHandlers(nsq.HandlerFunc(b.handle), b.Workers)
	err = b.cons.ConnectToNSQLookupds(s.Lookupdaddrs)
	return err
}

func batchloop(b *BatchBinding, stchan chan struct{}) {
	var inbuf *bytes.Buffer
	var nmsg int
	var ok bool
	b.outbuf.Write(b.Endpoint.BatchPrefix())
	for {
		select {
		case <-time.After(b.BatchTime):
			// can't send if zero
			if nmsg == 0 {
				continue
			}
			// postfix
			b.outbuf.Write(b.Endpoint.BatchPostfix())
			// do request
			res, err := b.dcl.Do(b.Endpoint.Req(b.outbuf))
			if err != nil {
				log.Printf("HTTP Client error: %s", err.Error())
				log.Printf("Failed to send body: %q", b.outbuf.String())
				b.outbuf.Reset()
				nmsg = 0
				b.outbuf.Write(b.Endpoint.BatchPrefix())
				continue
			}
			// validate
			err = b.Endpoint.Validate(res)
			if err != nil {
				log.Printf("Response invalidated: %s", err.Error())
				log.Printf("The following body failed: %q", b.outbuf.String())
				b.outbuf.Reset()
				nmsg = 0
				b.outbuf.Write(b.Endpoint.BatchPrefix())
				continue
			}
			// everything worked ok
			b.outbuf.Reset()
			nmsg = 0
			b.outbuf.Write(b.Endpoint.BatchPrefix())
			continue

		case inbuf, ok = <-b.accum:
			if !ok {
				goto exit
			}
			// write concatenator
			if nmsg != 0 {
				b.outbuf.Write(b.Endpoint.Concat())
			}
			// write prefix, msg, postfix
			b.outbuf.Write(b.Endpoint.EntryPrefix())
			inbuf.WriteTo(b.outbuf)
			b.outbuf.Write(b.Endpoint.EntryPostfix())
			// free inbuf
			putBuf(inbuf)
			// increment msg; see if we have hit maxmsg
			nmsg++
			if nmsg >= b.MaxMsg {
				// BATCH SEND

				// batch postfix
				b.outbuf.Write(b.Endpoint.BatchPostfix())
				//send everything
				res, err := b.dcl.Do(b.Endpoint.Req(b.outbuf))
				if err != nil {
					log.Printf("HTTP Client error: %s", err.Error())
					log.Printf("Failed to send body: %q", b.outbuf.String())
					b.outbuf.Reset()
					nmsg = 0
					b.outbuf.Write(b.Endpoint.BatchPrefix())
					continue
				}
				err = b.Endpoint.Validate(res)
				if err != nil {
					log.Printf("Response invalidated: %s", err.Error())
					log.Printf("The following body failed: %q", b.outbuf.String())
					b.outbuf.Reset()
					nmsg = 0
					b.outbuf.Write(b.Endpoint.BatchPrefix())
					continue
				}
				// everything worked okay
				b.outbuf.Reset()
				nmsg = 0
				b.outbuf.Write(b.Endpoint.BatchPrefix())
				continue
			}
		case <-stchan:
			goto exit
		}
	}
exit:
	b.wg.Done()
	return
}

// Run blocks until all bindings exit gracefully, usually after a call to Stop.
// Run immediately returns an error if the server is not configured correctly.
func (s *Server) Run() error {
	if len(s.bindings) == 0 && len(s.batchbin) == 0 {
		return errors.New("No bindings registered.")
	}
	s.lock.Lock()

	var err error
	if s.NSQConfig == nil {
		s.NSQConfig = DefaultConfig
	}
	err = s.NSQConfig.Validate()
	if err != nil {
		s.lock.Unlock()
		return err
	}

	for _, b := range s.bindings {
		err = s.startrun(b)
		if err != nil {
			s.lock.Unlock()
			return err
		}
	}

	for _, b := range s.batchbin {
		err = s.startbatch(b)
		if err != nil {
			s.lock.Unlock()
			return err
		}
	}
	s.lock.Unlock()

	// block until graceful stop
	for _, b := range s.bindings {
		<-b.cons.StopChan
	}
	for _, bb := range s.batchbin {
		<-bb.cons.StopChan
		bb.wg.Wait()
	}
	return nil
}

// Binding types connect NSQ channels, flux/msg schemas, and database endpoints.
type Binding struct {
	// Topic is the NSQ topic to listen on
	Topic string
	// Channel is the NSQ channel to listen on
	Channel string
	// Endpoint is the database and decode logic used for this topic & channel
	Endpoint DB
	// Workers sets the number of concurrent goroutines serving this binding; defaults to 1
	Workers int
	dcl     dclient       //used for database communication
	cons    *nsq.Consumer //used for nsq communication
	lock    *sync.Mutex   //lock everything on initialization
}

// BatchBinding types connect NSQ channels, flux/msg schemas, and database endpoints,
// but they use database request batching.
type BatchBinding struct {
	//Topic is the NSQ topic to listen on
	Topic string

	// Channel is the NSQ channel to listen on
	Channel string

	// Endpoint is the database and decode logic
	Endpoint BatchDB

	// Workers sets the number of concurent goroutines serving this binding; defaults to 1
	Workers int

	// MaxMsg sets the maximum number of messages collected before upload; defaults to 50
	MaxMsg int

	// BatchTime sets the maximum time spend waiting to collect messages before upload; defaults to 250ms
	BatchTime time.Duration

	dcl    dclient            // client
	cons   *nsq.Consumer      // consumer
	outbuf *bytes.Buffer      // for request body
	accum  chan *bytes.Buffer // for accumulating responses
	stchan chan struct{}      // stop channel
	wg     *sync.WaitGroup    // for monitoring workers
	lock   *sync.Mutex        //Lock everything on initialization
}

// implements the nsq.HandleFunc interface
func (b *Binding) handle(msg *nsq.Message) error {
	return dbHandle(b.Endpoint, msg.Body, b.dcl)
}

// implements the nsq.HandleFunc interface
func (b *BatchBinding) handle(msg *nsq.Message) error {
	buf := getBuf()
	err := b.Endpoint.Translate(msg.Body, buf)
	if err != nil {
		putBuf(buf)
		return err
	}
	b.accum <- buf
	return nil
}
<|endoftext|>


<commit_before>package mapreduce

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path"
	"sort"
	"strings"
	"sync"
	"time"

	"testing/iotest"

	"github.com/pachyderm/pfs/lib/btrfs"
	"github.com/pachyderm/pfs/lib/route"
	"github.com/pachyderm/pfs/lib/shell"
	"github.com/samalba/dockerclient"
)

var retries int = 5

func doRequest(client *dockerclient.DockerClient, method string, path string, r io.Reader, headers map[string]string) ([]byte, error) {
	req, err := http.NewRequest(method, client.URL.String()+path, r)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Content-Type", "application/json")
	if headers != nil {
		for header, value := range headers {
			req.Header.Add(header, value)
		}
	}
	resp, err := client.HTTPClient.Do(req)
	if err != nil {
		if !strings.Contains(err.Error(), "connection refused") && client.TLSConfig == nil {
			return nil, fmt.Errorf("%v. Are you trying to connect to a TLS-enabled daemon without TLS?", err)
		}
		return nil, err
	}
	defer resp.Body.Close()
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode == 404 {
		return nil, dockerclient.ErrNotFound
	}
	if resp.StatusCode >= 400 {
		log.Print(data)
		return nil, dockerclient.Error{StatusCode: resp.StatusCode, Status: resp.Status}
	}
	return data, nil
}

// StartContainer pulls image and starts a container from it with command. It
// returns the container id or an error.
func spinupContainer(image string, command []string) (string, error) {
	log.Print("spinupContainer", " ", image, " ", command)
	docker, err := dockerclient.NewDockerClient("unix:///var/run/docker.sock", nil)
	if err != nil {
		log.Print(err)
		return "", err
	}
	if err := docker.PullImage(image, nil); err != nil {
		//return "", err this is erroring due to failing to parse response json
	}

	containerConfig := &dockerclient.ContainerConfig{Image: image, Cmd: command}

	containerId, err := docker.CreateContainer(containerConfig, "")
	if err != nil {
		log.Print(err)
		return "", nil
	}

	if err := docker.StartContainer(containerId, &dockerclient.HostConfig{}); err != nil {
		log.Print(err)
		return "", err
	}

	return containerId, nil
}

func buildContainerFromGit(url, tag string) error {
	name := path.Join("/tmp", btrfs.RandSeq(5))
	c := exec.Command("git", "clone", url, name, "--depth", "1")
	if err := shell.RunStderr(c); err != nil {
		return err
	}
	defer os.RemoveAll(name)

	c = exec.Command("git", "archive", path.Join(name, "image"))
	return shell.CallCont(c, func(r io.ReadCloser) error {
		docker, err := dockerclient.NewDockerClient("unix:///var/run/docker.sock", nil)
		uri := fmt.Sprintf("/build?t=%s", tag)

		res, err := doRequest(docker, "POST", uri, r, nil)
		log.Print("Response to build:\n", res)
		return err
	})
}

func ipAddr(containerId string) (string, error) {
	docker, err := dockerclient.NewDockerClient("unix:///var/run/docker.sock", nil)
	if err != nil {
		return "", err
	}
	containerInfo, err := docker.InspectContainer(containerId)
	if err != nil {
		return "", err
	}

	return containerInfo.NetworkSettings.IpAddress, nil
}

func retry(f func() error, retries int, pause time.Duration) error {
	var err error
	for i := 0; i < retries; i++ {
		err = f()
		if err == nil {
			break
		} else {
			time.Sleep(pause)
		}
	}
	return err
}

// contains checks if set contains val. It assums that set has already been
// sorted.
func contains(set []string, val string) bool {
	index := sort.SearchStrings(set, val)
	return index < len(set) && set[index] == val
}

type Job struct {
	Type    string   `json:"type"`
	Input   string   `json:"input"`
	Image   string   `json:"image"`
	Command []string `json:"command"`
}

type materializeInfo struct {
	In, Out, Branch, Commit string
}

func PrepJob(job Job, jobPath string, m materializeInfo) error {
	if err := btrfs.MkdirAll(path.Join(m.Out, m.Branch, jobPath)); err != nil {
		return err
	}
	return nil
}

func Map(job Job, jobPath string, m materializeInfo, host string) error {
	err := PrepJob(job, path.Base(jobPath), m)
	if err != nil {
		return err
	}

	if job.Type != "map" {
		return fmt.Errorf("runMap called on a job of type \"%s\". Should be \"map\".", job.Type)
	}

	files := make(chan string)

	var wg sync.WaitGroup
	defer wg.Wait()

	defer close(files)
	for i := 0; i < 300; i++ {
		wg.Add(1)
		go func(wg *sync.WaitGroup) {
			defer wg.Done()
			for name := range files {
				inFile, err := btrfs.Open(path.Join(m.In, m.Commit, job.Input, name))
				if err != nil {
					log.Print(err)
					return
				}
				defer inFile.Close()

				var resp *http.Response
				err = retry(func() error {
					log.Print("Posting: ", name)
					resp, err = http.Post("http://"+path.Join(host, name), "application/text", inFile)
					return err
				}, retries, 200*time.Millisecond)
				if err != nil {
					log.Print(err)
					return
				}
				defer resp.Body.Close()

				outFile, err := btrfs.Create(path.Join(m.Out, m.Branch, jobPath, name))
				if err != nil {
					log.Print(err)
					return
				}
				defer outFile.Close()
				if _, err := io.Copy(outFile, resp.Body); err != nil {
					log.Print(err)
					return
				}
			}
		}(&wg)
	}
	err = btrfs.LazyWalk(path.Join(m.In, m.Commit, job.Input),
		func(name string) error {
			files <- name
			return nil
		})
	if err != nil {
		return err
	}
	return nil
}

func Reduce(job Job, jobPath string, m materializeInfo, host string, shard, modulos uint64) error {
	if (route.HashResource(path.Join("/job", jobPath)) % modulos) != shard {
		// This resource isn't supposed to be located on this machine.
		return nil
	}
	log.Print("Reduce: ", job, " ", jobPath, " ")
	if job.Type != "reduce" {
		return fmt.Errorf("Reduce called on a job of type \"%s\". Should be \"reduce\".", job.Type)
	}

	// Notice we're just passing "host" here. Multicast will fill in the host
	// field so we don't actually need to specify it.
	var _reader io.ReadCloser
	err := retry(func() error {
		req, err := http.NewRequest("GET", "http://host/"+path.Join(job.Input, "file", "*")+"?commit="+m.Commit, nil)
		if err != nil {
			return err
		}
		_reader, err = route.Multicast(req, "/pfs/master")
		return err
	}, retries, time.Minute)
	if err != nil {
		return err
	}
	defer _reader.Close()
	reader := iotest.NewReadLogger("Reduce", _reader)

	var resp *http.Response
	err = retry(func() error {
		resp, err = http.Post("http://"+path.Join(host, job.Input), "application/text", reader)
		return err
	}, retries, 200*time.Millisecond)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		return fmt.Errorf("Received error %s", resp.Status)
	}

	outFile, err := btrfs.Create(path.Join(m.Out, m.Branch, jobPath))
	if err != nil {
		return err
	}
	defer outFile.Close()
	if _, err := io.Copy(outFile, resp.Body); err != nil {
		return err
	}
	return nil
}

type jobCond struct {
	sync.Cond
	Done bool
}

var jobs map[string]*jobCond = make(map[string]*jobCond)
var jobsAccess sync.Mutex

// jobCond returns the name of the condition variable for job.
func condKey(in_repo, commit, job string) string {
	return path.Join(in_repo, commit, job)
}

func ensureCond(name string) {
	jobsAccess.Lock()
	defer jobsAccess.Unlock()
	if _, ok := jobs[name]; !ok {
		jobs[name] = &jobCond{sync.Cond{L: &sync.Mutex{}}, false}
	}
}

func broadcast(in_repo, commit, job string) {
	name := condKey(in_repo, commit, job)
	ensureCond(name)
	jobs[name].L.Lock()
	jobs[name].Done = true
	jobs[name].Broadcast()
	jobs[name].L.Unlock()
}

func WaitJob(in_repo, commit, job string) {
	name := condKey(in_repo, commit, job)
	ensureCond(name)
	jobs[name].L.Lock()
	for !jobs[name].Done {
		jobs[name].Wait()
	}
	jobs[name].L.Unlock()
}

// Materialize parses the jobs found in `in_repo`/`commit`/`jobDir` runs them
// with `in_repo/commit` as input, outputs the results to `out_repo`/`branch`
// and commits them as `out_repo`/`commit`
func Materialize(in_repo, branch, commit, out_repo, jobDir string, shard, modulos uint64) error {
	log.Printf("Materialize: %s %s %s %s %s.", in_repo, branch, commit, out_repo, jobDir)
	// We make sure that this function always commits so that we know the comp
	// repo stays in sync with the data repo.
	defer func() {
		if err := btrfs.Commit(out_repo, commit, branch); err != nil {
			log.Print("btrfs.Commit error in Materialize: ", err)
		}
	}()
	// First check if the jobs dir actually exists.
	exists, err := btrfs.FileExists(path.Join(in_repo, commit, jobDir))
	if err != nil {
		return err
	}
	if !exists {
		// Perfectly valid to have no jobs dir, it just means we have no work
		// to do.
		return nil
	}

	docker, err := dockerclient.NewDockerClient("unix:///var/run/docker.sock", nil)
	if err != nil {
		return err
	}
	newFiles, err := btrfs.NewFiles(in_repo, commit)
	if err != nil {
		return err
	}
	sort.Strings(newFiles)

	jobsPath := path.Join(in_repo, commit, jobDir)
	jobs, err := btrfs.ReadDir(jobsPath)
	if err != nil {
		return err
	}
	var wg sync.WaitGroup
	defer wg.Wait()
	for _, jobInfo := range jobs {
		wg.Add(1)
		go func(jobInfo os.FileInfo) {
			defer wg.Done()
			defer broadcast(in_repo, commit, jobInfo.Name())
			jobFile, err := btrfs.Open(path.Join(jobsPath, jobInfo.Name()))
			if err != nil {
				log.Print(err)
				return
			}
			defer jobFile.Close()
			decoder := json.NewDecoder(jobFile)
			job := Job{}
			if err = decoder.Decode(&job); err != nil {
				log.Print(err)
				return
			}
			log.Print("Job: ", job)
			m := materializeInfo{in_repo, out_repo, branch, commit}

			containerId, err := spinupContainer(job.Image, job.Command)
			if err != nil {
				log.Print(err)
				return
			}
			defer docker.StopContainer(containerId, 5)

			containerAddr, err := ipAddr(containerId)
			if err != nil {
				log.Print(err)
				return
			}

			if job.Type == "map" {
				err := Map(job, jobInfo.Name(), m, containerAddr)
				if err != nil {
					log.Print(err)
					return
				}
			} else if job.Type == "reduce" {
				err := Reduce(job, jobInfo.Name(), m, containerAddr, shard, modulos)
				if err != nil {
					log.Print(err)
					return
				}
			} else {
				log.Printf("Job %s has unrecognized type: %s.", jobInfo.Name(), job.Type)
				return
			}
		}(jobInfo)
	}
	return nil
}
<commit_msg>Confess my sins.<commit_after>package mapreduce

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path"
	"sort"
	"strings"
	"sync"
	"time"

	"testing/iotest"

	"github.com/pachyderm/pfs/lib/btrfs"
	"github.com/pachyderm/pfs/lib/route"
	"github.com/pachyderm/pfs/lib/shell"
	"github.com/samalba/dockerclient"
)

var retries int = 5

/* This is a terrible hack, but dockerclient doesn't expose a `Build` method
* and it doesn't make it easy to extend it so I had to copy paste this piece of
* code. Please for the love of god remove this as soon as possible. */
func doRequest(client *dockerclient.DockerClient, method string, path string, r io.Reader, headers map[string]string) ([]byte, error) {
	req, err := http.NewRequest(method, client.URL.String()+path, r)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Content-Type", "application/json")
	if headers != nil {
		for header, value := range headers {
			req.Header.Add(header, value)
		}
	}
	resp, err := client.HTTPClient.Do(req)
	if err != nil {
		if !strings.Contains(err.Error(), "connection refused") && client.TLSConfig == nil {
			return nil, fmt.Errorf("%v. Are you trying to connect to a TLS-enabled daemon without TLS?", err)
		}
		return nil, err
	}
	defer resp.Body.Close()
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode == 404 {
		return nil, dockerclient.ErrNotFound
	}
	if resp.StatusCode >= 400 {
		log.Print(data)
		return nil, dockerclient.Error{StatusCode: resp.StatusCode, Status: resp.Status}
	}
	return data, nil
}

// StartContainer pulls image and starts a container from it with command. It
// returns the container id or an error.
func spinupContainer(image string, command []string) (string, error) {
	log.Print("spinupContainer", " ", image, " ", command)
	docker, err := dockerclient.NewDockerClient("unix:///var/run/docker.sock", nil)
	if err != nil {
		log.Print(err)
		return "", err
	}
	if err := docker.PullImage(image, nil); err != nil {
		//return "", err this is erroring due to failing to parse response json
	}

	containerConfig := &dockerclient.ContainerConfig{Image: image, Cmd: command}

	containerId, err := docker.CreateContainer(containerConfig, "")
	if err != nil {
		log.Print(err)
		return "", nil
	}

	if err := docker.StartContainer(containerId, &dockerclient.HostConfig{}); err != nil {
		log.Print(err)
		return "", err
	}

	return containerId, nil
}

func buildContainerFromGit(url, tag string) error {
	name := path.Join("/tmp", btrfs.RandSeq(5))
	c := exec.Command("git", "clone", url, name, "--depth", "1")
	if err := shell.RunStderr(c); err != nil {
		return err
	}
	defer os.RemoveAll(name)

	c = exec.Command("git", "archive", path.Join(name, "image"))
	return shell.CallCont(c, func(r io.ReadCloser) error {
		docker, err := dockerclient.NewDockerClient("unix:///var/run/docker.sock", nil)
		uri := fmt.Sprintf("/build?t=%s", tag)

		res, err := doRequest(docker, "POST", uri, r, nil)
		log.Print("Response to build:\n", res)
		return err
	})
}

func ipAddr(containerId string) (string, error) {
	docker, err := dockerclient.NewDockerClient("unix:///var/run/docker.sock", nil)
	if err != nil {
		return "", err
	}
	containerInfo, err := docker.InspectContainer(containerId)
	if err != nil {
		return "", err
	}

	return containerInfo.NetworkSettings.IpAddress, nil
}

func retry(f func() error, retries int, pause time.Duration) error {
	var err error
	for i := 0; i < retries; i++ {
		err = f()
		if err == nil {
			break
		} else {
			time.Sleep(pause)
		}
	}
	return err
}

// contains checks if set contains val. It assums that set has already been
// sorted.
func contains(set []string, val string) bool {
	index := sort.SearchStrings(set, val)
	return index < len(set) && set[index] == val
}

type Job struct {
	Type    string   `json:"type"`
	Input   string   `json:"input"`
	Image   string   `json:"image"`
	Command []string `json:"command"`
}

type materializeInfo struct {
	In, Out, Branch, Commit string
}

func PrepJob(job Job, jobPath string, m materializeInfo) error {
	if err := btrfs.MkdirAll(path.Join(m.Out, m.Branch, jobPath)); err != nil {
		return err
	}
	return nil
}

func Map(job Job, jobPath string, m materializeInfo, host string) error {
	err := PrepJob(job, path.Base(jobPath), m)
	if err != nil {
		return err
	}

	if job.Type != "map" {
		return fmt.Errorf("runMap called on a job of type \"%s\". Should be \"map\".", job.Type)
	}

	files := make(chan string)

	var wg sync.WaitGroup
	defer wg.Wait()

	defer close(files)
	for i := 0; i < 300; i++ {
		wg.Add(1)
		go func(wg *sync.WaitGroup) {
			defer wg.Done()
			for name := range files {
				inFile, err := btrfs.Open(path.Join(m.In, m.Commit, job.Input, name))
				if err != nil {
					log.Print(err)
					return
				}
				defer inFile.Close()

				var resp *http.Response
				err = retry(func() error {
					log.Print("Posting: ", name)
					resp, err = http.Post("http://"+path.Join(host, name), "application/text", inFile)
					return err
				}, retries, 200*time.Millisecond)
				if err != nil {
					log.Print(err)
					return
				}
				defer resp.Body.Close()

				outFile, err := btrfs.Create(path.Join(m.Out, m.Branch, jobPath, name))
				if err != nil {
					log.Print(err)
					return
				}
				defer outFile.Close()
				if _, err := io.Copy(outFile, resp.Body); err != nil {
					log.Print(err)
					return
				}
			}
		}(&wg)
	}
	err = btrfs.LazyWalk(path.Join(m.In, m.Commit, job.Input),
		func(name string) error {
			files <- name
			return nil
		})
	if err != nil {
		return err
	}
	return nil
}

func Reduce(job Job, jobPath string, m materializeInfo, host string, shard, modulos uint64) error {
	if (route.HashResource(path.Join("/job", jobPath)) % modulos) != shard {
		// This resource isn't supposed to be located on this machine.
		return nil
	}
	log.Print("Reduce: ", job, " ", jobPath, " ")
	if job.Type != "reduce" {
		return fmt.Errorf("Reduce called on a job of type \"%s\". Should be \"reduce\".", job.Type)
	}

	// Notice we're just passing "host" here. Multicast will fill in the host
	// field so we don't actually need to specify it.
	var _reader io.ReadCloser
	err := retry(func() error {
		req, err := http.NewRequest("GET", "http://host/"+path.Join(job.Input, "file", "*")+"?commit="+m.Commit, nil)
		if err != nil {
			return err
		}
		_reader, err = route.Multicast(req, "/pfs/master")
		return err
	}, retries, time.Minute)
	if err != nil {
		return err
	}
	defer _reader.Close()
	reader := iotest.NewReadLogger("Reduce", _reader)

	var resp *http.Response
	err = retry(func() error {
		resp, err = http.Post("http://"+path.Join(host, job.Input), "application/text", reader)
		return err
	}, retries, 200*time.Millisecond)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		return fmt.Errorf("Received error %s", resp.Status)
	}

	outFile, err := btrfs.Create(path.Join(m.Out, m.Branch, jobPath))
	if err != nil {
		return err
	}
	defer outFile.Close()
	if _, err := io.Copy(outFile, resp.Body); err != nil {
		return err
	}
	return nil
}

type jobCond struct {
	sync.Cond
	Done bool
}

var jobs map[string]*jobCond = make(map[string]*jobCond)
var jobsAccess sync.Mutex

// jobCond returns the name of the condition variable for job.
func condKey(in_repo, commit, job string) string {
	return path.Join(in_repo, commit, job)
}

func ensureCond(name string) {
	jobsAccess.Lock()
	defer jobsAccess.Unlock()
	if _, ok := jobs[name]; !ok {
		jobs[name] = &jobCond{sync.Cond{L: &sync.Mutex{}}, false}
	}
}

func broadcast(in_repo, commit, job string) {
	name := condKey(in_repo, commit, job)
	ensureCond(name)
	jobs[name].L.Lock()
	jobs[name].Done = true
	jobs[name].Broadcast()
	jobs[name].L.Unlock()
}

func WaitJob(in_repo, commit, job string) {
	name := condKey(in_repo, commit, job)
	ensureCond(name)
	jobs[name].L.Lock()
	for !jobs[name].Done {
		jobs[name].Wait()
	}
	jobs[name].L.Unlock()
}

// Materialize parses the jobs found in `in_repo`/`commit`/`jobDir` runs them
// with `in_repo/commit` as input, outputs the results to `out_repo`/`branch`
// and commits them as `out_repo`/`commit`
func Materialize(in_repo, branch, commit, out_repo, jobDir string, shard, modulos uint64) error {
	log.Printf("Materialize: %s %s %s %s %s.", in_repo, branch, commit, out_repo, jobDir)
	// We make sure that this function always commits so that we know the comp
	// repo stays in sync with the data repo.
	defer func() {
		if err := btrfs.Commit(out_repo, commit, branch); err != nil {
			log.Print("btrfs.Commit error in Materialize: ", err)
		}
	}()
	// First check if the jobs dir actually exists.
	exists, err := btrfs.FileExists(path.Join(in_repo, commit, jobDir))
	if err != nil {
		return err
	}
	if !exists {
		// Perfectly valid to have no jobs dir, it just means we have no work
		// to do.
		return nil
	}

	docker, err := dockerclient.NewDockerClient("unix:///var/run/docker.sock", nil)
	if err != nil {
		return err
	}
	newFiles, err := btrfs.NewFiles(in_repo, commit)
	if err != nil {
		return err
	}
	sort.Strings(newFiles)

	jobsPath := path.Join(in_repo, commit, jobDir)
	jobs, err := btrfs.ReadDir(jobsPath)
	if err != nil {
		return err
	}
	var wg sync.WaitGroup
	defer wg.Wait()
	for _, jobInfo := range jobs {
		wg.Add(1)
		go func(jobInfo os.FileInfo) {
			defer wg.Done()
			defer broadcast(in_repo, commit, jobInfo.Name())
			jobFile, err := btrfs.Open(path.Join(jobsPath, jobInfo.Name()))
			if err != nil {
				log.Print(err)
				return
			}
			defer jobFile.Close()
			decoder := json.NewDecoder(jobFile)
			job := Job{}
			if err = decoder.Decode(&job); err != nil {
				log.Print(err)
				return
			}
			log.Print("Job: ", job)
			m := materializeInfo{in_repo, out_repo, branch, commit}

			containerId, err := spinupContainer(job.Image, job.Command)
			if err != nil {
				log.Print(err)
				return
			}
			defer docker.StopContainer(containerId, 5)

			containerAddr, err := ipAddr(containerId)
			if err != nil {
				log.Print(err)
				return
			}

			if job.Type == "map" {
				err := Map(job, jobInfo.Name(), m, containerAddr)
				if err != nil {
					log.Print(err)
					return
				}
			} else if job.Type == "reduce" {
				err := Reduce(job, jobInfo.Name(), m, containerAddr, shard, modulos)
				if err != nil {
					log.Print(err)
					return
				}
			} else {
				log.Printf("Job %s has unrecognized type: %s.", jobInfo.Name(), job.Type)
				return
			}
		}(jobInfo)
	}
	return nil
}
<|endoftext|>


<commit_before>package quicktemplate

import (
	"io"
)

type htmlEscapeWriter struct {
	w io.Writer
}

func (w *htmlEscapeWriter) Write(b []byte) (int, error) {
	write := w.w.Write
	j := 0
	for i, c := range b {
		switch c {
		case '<':
			write(b[j:i])
			write(strLT)
			j = i + 1
		case '>':
			write(b[j:i])
			write(strGT)
			j = i + 1
		case '"':
			write(b[j:i])
			write(strQuot)
			j = i + 1
		case '\'':
			write(b[j:i])
			write(strApos)
			j = i + 1
		case '&':
			write(b[j:i])
			write(strAmp)
			j = i + 1
		}
	}
	if n, err := write(b[j:]); err != nil {
		return j + n, err
	}
	return len(b), nil
}

var (
	strLT   = []byte("<")
	strGT   = []byte(">")
	strQuot = []byte(""")
	strApos = []byte("'")
	strAmp  = []byte("&")
)
<commit_msg>optimize html escaping of strings without special chars<commit_after>package quicktemplate

import (
	"bytes"
	"io"
)

type htmlEscapeWriter struct {
	w io.Writer
}

func (w *htmlEscapeWriter) Write(b []byte) (int, error) {
	if bytes.IndexByte(b, '<') < 0 &&
		bytes.IndexByte(b, '>') < 0 &&
		bytes.IndexByte(b, '"') < 0 &&
		bytes.IndexByte(b, '\'') < 0 &&
		bytes.IndexByte(b, '&') < 0 {

		// fast path - nothing to escape
		return w.w.Write(b)
	}

	// slow path
	write := w.w.Write
	j := 0
	for i, c := range b {
		switch c {
		case '<':
			write(b[j:i])
			write(strLT)
			j = i + 1
		case '>':
			write(b[j:i])
			write(strGT)
			j = i + 1
		case '"':
			write(b[j:i])
			write(strQuot)
			j = i + 1
		case '\'':
			write(b[j:i])
			write(strApos)
			j = i + 1
		case '&':
			write(b[j:i])
			write(strAmp)
			j = i + 1
		}
	}
	if n, err := write(b[j:]); err != nil {
		return j + n, err
	}
	return len(b), nil
}

var (
	strLT   = []byte("<")
	strGT   = []byte(">")
	strQuot = []byte(""")
	strApos = []byte("'")
	strAmp  = []byte("&")
)
<|endoftext|>


<commit_before>package http

import (
	"fmt"
	"time"

	"github.com/alauda/bergamot/log"

	"gopkg.in/kataras/iris.v6"
	"gopkg.in/kataras/iris.v6/adaptors/cors"
	"gopkg.in/kataras/iris.v6/adaptors/httprouter"
)

// Router interface for http endpoints
type Router interface {
	AddRoutes(router *iris.Router, server *Server)
}

// Middleware adding middleware
type Middleware interface {
	Serve(ctx *iris.Context)
}

// Config configuration for the HTTP server
type Config struct {
	Host           string
	Port           string
	AddLog         bool
	AddHealthCheck bool
	Component      string
}

// Server Full HTTP server
type Server struct {
	config      Config
	start       time.Time
	log         log.Logger
	iris        *iris.Framework
	versions    map[int]*iris.Router
	middlewares map[string][]Middleware
}

// NewServer constructor function for the HTTP server
func NewServer(config Config, log log.Logger) *Server {
	return &Server{
		config:      config,
		log:         log,
		iris:        iris.New(),
		versions:    map[int]*iris.Router{},
		middlewares: map[string][]Middleware{MiddlewareTypeAll: []Middleware{}},
	}
}

// Init will setup any necessary data
func (h *Server) Init() *Server {
	h.iris.Adapt(
		// Logging all errors
		iris.DevLogger(),
		// adding router
		httprouter.New(),

		// Cors wrapper to the entire application, allow all origins.
		cors.New(cors.Options{AllowedOrigins: []string{"*"}}),
	)

	if h.config.AddHealthCheck {
		// adding health check
		h.iris.Any("/", h.Healthcheck)
		h.iris.Any("/_ping", h.Healthcheck)
	}

	if h.config.AddLog {
		// Adding request logger middleware
		h.iris.Use(h)
		// default error when requesting unexistent route
		h.iris.OnError(iris.StatusNotFound, func(ctx *iris.Context) {
			// print method and stuff
			h.Serve(ctx)
		})
	}

	return h
}

// AddVersion Adds a version number to the API route
func (h *Server) AddVersion(version int) *Server {
	if _, ok := h.versions[version]; !ok {
		// adds /v1 or /v2 route
		h.versions[version] = h.iris.Party(fmt.Sprintf("/v%d", version))
	}
	return h
}

// AddEndpoint ands a handler for the given relative path
// should be executed before the Start method and after the Init method
func (h *Server) AddEndpoint(relativePath string, handler Router) *Server {
	router := h.iris.Party(relativePath)
	handler.AddRoutes(router, h)

	return h
}

// AddVersionEndpoint add a root endpoint to a version specific API
// Used like AddEndpoint but will add on a specific version instead.
// If the version was not created previously will then be created automatically
func (h *Server) AddVersionEndpoint(version int, relativePath string, handler Router) *Server {
	h.AddVersion(version)
	handler.AddRoutes(h.versions[version].Party(relativePath), h)
	return h
}

// Serve will log all the requests
func (h *Server) Serve(ctx *iris.Context) {
	// logging all requests
	h.log.Infof("---- [%s] %s  - args: %s ", ctx.Method(), ctx.Path(), ctx.ParamsSentence())
	ctx.Next()
}

// Healthcheck healthcheck endpoint
func (h *Server) Healthcheck(ctx *iris.Context) {
	ctx.WriteString(fmt.Sprintf("%s:%s", h.config.Component, time.Since(h.start)))
}

// GetApp returns the iris app, used for testing
func (h *Server) GetApp() *iris.Framework {
	return h.iris
}

// Start will start serving the http server
// this method will block while serving http
func (h *Server) Start() {
	h.start = time.Now()
	h.iris.Listen(h.config.Host + ":" + h.config.Port)
}

const (
	// MiddlewareTypeAll special type
	MiddlewareTypeAll = "*"
)

// AddMiddleware adds a middleware for the given types
func (h *Server) AddMiddleware(mw Middleware, kinds ...string) *Server {
	var (
		collection []Middleware
		ok         bool
	)
	kinds = append(kinds, MiddlewareTypeAll)
	for _, k := range kinds {
		if collection, ok = h.middlewares[k]; !ok {
			collection = make([]Middleware, 0, 2)
		}
		collection = append(collection, mw)
		h.middlewares[k] = collection
	}
	return h
}

// AddMiddlewares adds a middleware for the given types
func (h *Server) AddMiddlewares(mws []Middleware, kinds ...string) *Server {
	for _, mw := range mws {
		h.AddMiddleware(mw, kinds...)
	}
	return h
}

// GetMiddlewares get all midlewares of a kind
func (h *Server) GetMiddlewares(kind string) []Middleware {
	return h.middlewares[kind]
}

// GetMiddlewareHandlerFun returns all the handler functions of a middleware kind
func (h *Server) GetMiddlewareHandlerFun(kind string) []iris.HandlerFunc {
	var funcs []iris.HandlerFunc
	mws := h.GetMiddlewares(kind)
	funcs = make([]iris.HandlerFunc, len(mws), len(mws)+1)
	for i, mw := range mws {
		funcs[i] = mw.Serve
	}
	return funcs
}

// GetMiddlewaresDecorated gets all the handler functions of a collection of kinds and decorate the target function
func (h *Server) GetMiddlewaresDecorated(handlerFunc iris.HandlerFunc, kinds ...string) []iris.HandlerFunc {
	var mws []iris.HandlerFunc
	for _, k := range kinds {
		mws = append(mws, h.GetMiddlewareHandlerFun(k)...)
	}
	mws = append(mws, handlerFunc)
	return mws
}
<commit_msg>feature: Adding some public decorator functions<commit_after>package http

import (
	"fmt"
	"time"

	"github.com/alauda/bergamot/log"

	"gopkg.in/kataras/iris.v6"
	"gopkg.in/kataras/iris.v6/adaptors/cors"
	"gopkg.in/kataras/iris.v6/adaptors/httprouter"
)

// Router interface for http endpoints
type Router interface {
	AddRoutes(router *iris.Router, server *Server)
}

// Middleware adding middleware
type Middleware interface {
	Serve(ctx *iris.Context)
}

// Config configuration for the HTTP server
type Config struct {
	Host           string
	Port           string
	AddLog         bool
	AddHealthCheck bool
	Component      string
}

// Server Full HTTP server
type Server struct {
	config      Config
	start       time.Time
	log         log.Logger
	iris        *iris.Framework
	versions    map[int]*iris.Router
	middlewares map[string][]Middleware
}

// NewServer constructor function for the HTTP server
func NewServer(config Config, log log.Logger) *Server {
	return &Server{
		config:      config,
		log:         log,
		iris:        iris.New(),
		versions:    map[int]*iris.Router{},
		middlewares: map[string][]Middleware{MiddlewareTypeAll: []Middleware{}},
	}
}

// Init will setup any necessary data
func (h *Server) Init() *Server {
	h.iris.Adapt(
		// Logging all errors
		iris.DevLogger(),
		// adding router
		httprouter.New(),

		// Cors wrapper to the entire application, allow all origins.
		cors.New(cors.Options{AllowedOrigins: []string{"*"}}),
	)

	if h.config.AddHealthCheck {
		// adding health check
		h.iris.Any("/", h.Healthcheck)
		h.iris.Any("/_ping", h.Healthcheck)
	}

	if h.config.AddLog {
		// Adding request logger middleware
		h.iris.Use(h)
		// default error when requesting unexistent route
		h.iris.OnError(iris.StatusNotFound, func(ctx *iris.Context) {
			// print method and stuff
			h.Serve(ctx)
		})
	}

	return h
}

// AddVersion Adds a version number to the API route
func (h *Server) AddVersion(version int) *Server {
	if _, ok := h.versions[version]; !ok {
		// adds /v1 or /v2 route
		h.versions[version] = h.iris.Party(fmt.Sprintf("/v%d", version))
	}
	return h
}

// AddEndpoint ands a handler for the given relative path
// should be executed before the Start method and after the Init method
func (h *Server) AddEndpoint(relativePath string, handler Router) *Server {
	router := h.iris.Party(relativePath)
	handler.AddRoutes(router, h)

	return h
}

// AddVersionEndpoint add a root endpoint to a version specific API
// Used like AddEndpoint but will add on a specific version instead.
// If the version was not created previously will then be created automatically
func (h *Server) AddVersionEndpoint(version int, relativePath string, handler Router) *Server {
	h.AddVersion(version)
	handler.AddRoutes(h.versions[version].Party(relativePath), h)
	return h
}

// Serve will log all the requests
func (h *Server) Serve(ctx *iris.Context) {
	// logging all requests
	h.log.Infof("---- [%s] %s  - args: %s ", ctx.Method(), ctx.Path(), ctx.ParamsSentence())
	ctx.Next()
}

// Healthcheck healthcheck endpoint
func (h *Server) Healthcheck(ctx *iris.Context) {
	ctx.WriteString(fmt.Sprintf("%s:%s", h.config.Component, time.Since(h.start)))
}

// GetApp returns the iris app, used for testing
func (h *Server) GetApp() *iris.Framework {
	return h.iris
}

// Start will start serving the http server
// this method will block while serving http
func (h *Server) Start() {
	h.start = time.Now()
	h.iris.Listen(h.config.Host + ":" + h.config.Port)
}

const (
	// MiddlewareTypeAll special type
	MiddlewareTypeAll = "*"
)

// AddMiddleware adds a middleware for the given types
func (h *Server) AddMiddleware(mw Middleware, kinds ...string) *Server {
	var (
		collection []Middleware
		ok         bool
	)
	kinds = append(kinds, MiddlewareTypeAll)
	for _, k := range kinds {
		if collection, ok = h.middlewares[k]; !ok {
			collection = make([]Middleware, 0, 2)
		}
		collection = append(collection, mw)
		h.middlewares[k] = collection
	}
	return h
}

// AddMiddlewares adds a middleware for the given types
func (h *Server) AddMiddlewares(mws []Middleware, kinds ...string) *Server {
	for _, mw := range mws {
		h.AddMiddleware(mw, kinds...)
	}
	return h
}

// GetMiddlewares get all midlewares of a kind
func (h *Server) GetMiddlewares(kind string) []Middleware {
	return h.middlewares[kind]
}

// GetMiddlewareHandlerFun returns all the handler functions of a middleware kind
func (h *Server) GetMiddlewareHandlerFun(kind string) []iris.HandlerFunc {
	return GetMiddlewareHandlerFunc(h.GetMiddlewares(kind)...)
}

// GetMiddlewaresDecorated gets all the handler functions of a collection of kinds and decorate the target function
func (h *Server) GetMiddlewaresDecorated(handlerFunc iris.HandlerFunc, kinds ...string) []iris.HandlerFunc {
	var mws []iris.HandlerFunc
	for _, k := range kinds {
		mws = append(mws, h.GetMiddlewareHandlerFun(k)...)
	}
	mws = append(mws, handlerFunc)
	return mws
}

// GetMiddlewareHandlerFunc get only the functions from middlewares
func GetMiddlewareHandlerFunc(mws ...Middleware) []iris.HandlerFunc {
	var funcs []iris.HandlerFunc
	funcs = make([]iris.HandlerFunc, len(mws), len(mws)+1)
	for i, mw := range mws {
		funcs[i] = mw.Serve
	}
	return funcs
}

// DecorateHandlerFunc prepend all the given middlewares
func DecorateHandlerFunc(handlerFunc iris.HandlerFunc, mws ...Middleware) []iris.HandlerFunc {
	return append(GetMiddlewareHandlerFunc(mws...), handlerFunc)
}
<|endoftext|>


<commit_before>package httpimpl

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"log"
	"net/http"
)

func AddLockApiEndpoints(e *gin.Engine, lockApi LockApi) {
	api := e.Group("/api")
	api.POST("/lock", makeLockHandler(lockApi))
	api.POST("/unlock", makeUnlockHandler(lockApi))
}

type LockRequest struct {
	Name string `form:"name" json:"name" binding:"required"`
}

func makeLockHandler(lockApi LockApi) func(c *gin.Context) {
	return func(c *gin.Context) {
		var lockRequest LockRequest
		if err := c.Bind(&lockRequest); err == nil {
			log.Printf("Attempt to lock: %q", lockRequest.Name)
			commitChan := lockApi.Lock(lockRequest.Name)
			if commitChan != nil {
				<-commitChan // FIXME: add timeout!
				c.JSON(http.StatusOK, gin.H{})
			} else {
				c.JSON(http.StatusConflict, gin.H{})
			}
		} else {
			c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("%v", err)})
		}
	}
}

func makeUnlockHandler(lockApi LockApi) func(c *gin.Context) {
	return func(c *gin.Context) {
		var lockRequest LockRequest
		if err := c.Bind(&lockRequest); err == nil {
			log.Printf("Attempt to unlock: %q", lockRequest.Name)
			commitChan := lockApi.Unlock(lockRequest.Name)
			if commitChan != nil {
				<-commitChan // FIXME: add timeout!
				c.JSON(http.StatusOK, gin.H{})
			} else {
				c.JSON(http.StatusConflict, gin.H{})
			}
		} else {
			c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("%v", err)})
		}
	}
}
<commit_msg>Remove some logging<commit_after>package httpimpl

import (
	"fmt"
	"net/http"

	"github.com/gin-gonic/gin"
)

func AddLockApiEndpoints(e *gin.Engine, lockApi LockApi) {
	api := e.Group("/api")
	api.POST("/lock", makeLockHandler(lockApi))
	api.POST("/unlock", makeUnlockHandler(lockApi))
}

type LockRequest struct {
	Name string `form:"name" json:"name" binding:"required"`
}

func makeLockHandler(lockApi LockApi) func(c *gin.Context) {
	return func(c *gin.Context) {
		var lockRequest LockRequest
		if err := c.Bind(&lockRequest); err == nil {
			commitChan := lockApi.Lock(lockRequest.Name)
			if commitChan != nil {
				<-commitChan // FIXME: add timeout!
				c.JSON(http.StatusOK, gin.H{})
			} else {
				c.JSON(http.StatusConflict, gin.H{})
			}
		} else {
			c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("%v", err)})
		}
	}
}

func makeUnlockHandler(lockApi LockApi) func(c *gin.Context) {
	return func(c *gin.Context) {
		var lockRequest LockRequest
		if err := c.Bind(&lockRequest); err == nil {
			commitChan := lockApi.Unlock(lockRequest.Name)
			if commitChan != nil {
				<-commitChan // FIXME: add timeout!
				c.JSON(http.StatusOK, gin.H{})
			} else {
				c.JSON(http.StatusConflict, gin.H{})
			}
		} else {
			c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("%v", err)})
		}
	}
}
<|endoftext|>