<commit_before>package main

import (
	"errors"
	"flag"
	"fmt"
	"net"
	"os"
	"path"
	"strings"
	"time"

	"veyron/examples/tunnel"
	"veyron/examples/tunnel/lib"
	"veyron/lib/signals"
	"veyron2"
	"veyron2/rt"
	"veyron2/vlog"
)

var (
	disablePty = flag.Bool("T", false, "Disable pseudo-terminal allocation.")
	forcePty   = flag.Bool("t", false, "Force allocation of pseudo-terminal.")
	vname      = flag.String("vname", "", "Veyron name (or endpoint) for tunneling service.")

	portforward = flag.String("L", "", "localaddr,remoteaddr Forward local 'localaddr' to 'remoteaddr'")
	lprotocol   = flag.String("local_protocol", "tcp", "Local network protocol for port forwarding")
	rprotocol   = flag.String("remote_protocol", "tcp", "Remote network protocol for port forwarding")

	noshell = flag.Bool("N", false, "Do not execute a shell. Only do port forwarding.")
)

func init() {
	flag.Usage = func() {
		bname := path.Base(os.Args[0])
		fmt.Fprintf(os.Stderr, `%s: Veyron SHell.

This tool is used to run shell commands or an interactive shell on a remote
tunneld service.

To open an interactive shell, use:
  %s --host=<veyron name or endpoint>

To run a shell command, use:
  %s --host=<veyron name or endpoint> <command to run>

The -L flag will forward connections from a local port to a remote address
through the tunneld service. The flag value is localaddr,remoteaddr. E.g.
  -L :14141,www.google.com:80

%s can't be used directly with tools like rsync because veyron addresses don't
look like traditional hostnames, which rsync doesn't understand. For
compatibility with such tools, %s has a special feature that allows passing the
veyron address via the VSH_NAME environment variable.

  $ VSH_NAME=<veyron address> rsync -avh -e %s /foo/* veyron:/foo/

In this example, the "veyron" host will be substituted with $VSH_NAME by %s
and rsync will work as expected.

Full flags:
`, os.Args[0], bname, bname, bname, bname, os.Args[0], bname)
		flag.PrintDefaults()
	}
}

func main() {
	// Work around the fact that os.Exit doesn't run deferred functions.
	os.Exit(realMain())
}

func realMain() int {
	r := rt.Init()
	defer r.Shutdown()

	host, cmd, err := veyronNameAndCommandLine()
	if err != nil {
		flag.Usage()
		fmt.Fprintf(os.Stderr, "\n%v\n", err)
		return 1
	}

	t, err := tunnel.BindTunnel(host)
	if err != nil {
		vlog.Fatalf("BindTunnel(%q) failed: %v", host, err)
	}

	if len(*portforward) > 0 {
		go runPortForwarding(t, host)
	}

	if *noshell {
		<-signals.ShutdownOnSignals()
		return 0
	}

	opts := shellOptions(cmd)

	stream, err := t.Shell(rt.R().TODOContext(), cmd, opts, veyron2.CallTimeout(24*time.Hour))
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %v\n", err)
		return 1
	}
	saved := lib.EnterRawTerminalMode()
	defer lib.RestoreTerminalSettings(saved)
	runIOManager(os.Stdin, os.Stdout, os.Stderr, stream)

	exitMsg := fmt.Sprintf("Connection to %s closed.", host)
	exitStatus, err := stream.Finish()
	if err != nil {
		exitMsg += fmt.Sprintf(" (%v)", err)
	}
	vlog.VI(1).Info(exitMsg)
	// Only show the exit message on stdout for interactive shells.
	// Otherwise, the exit message might get confused with the output
	// of the command that was run.
	if err != nil {
		fmt.Fprintln(os.Stderr, exitMsg)
	} else if len(cmd) == 0 {
		fmt.Println(exitMsg)
	}
	return int(exitStatus)
}

func shellOptions(cmd string) (opts tunnel.ShellOpts) {
	opts.UsePty = (len(cmd) == 0 || *forcePty) && !*disablePty
	opts.Environment = environment()
	ws, err := lib.GetWindowSize()
	if err != nil {
		vlog.VI(1).Infof("GetWindowSize failed: %v", err)
	} else {
		opts.Rows = uint32(ws.Row)
		opts.Cols = uint32(ws.Col)
	}
	return
}

func environment() []string {
	env := []string{}
	for _, name := range []string{"TERM", "COLORTERM"} {
		if value := os.Getenv(name); value != "" {
			env = append(env, fmt.Sprintf("%s=%s", name, value))
		}
	}
	return env
}

// veyronNameAndCommandLine extracts the veyron name and the remote command to
// send to the server. The name can be specified with the --vname flag or as the
// first non-flag argument. The command line is the concatenation of all the
// non-flag arguments, minus the veyron name.
func veyronNameAndCommandLine() (string, string, error) {
	name := *vname
	args := flag.Args()
	if len(name) == 0 {
		if len(args) > 0 {
			name = args[0]
			args = args[1:]
		}
	}
	if len(name) == 0 {
		return "", "", errors.New("veyron name missing")
	}
	// For compatibility with tools like rsync. Because veyron addresses
	// don't look like traditional hostnames, tools that work with rsh and
	// ssh can't work directly with vsh. This trick makes the following
	// possible:
	//   $ VSH_NAME=<veyron address> rsync -avh -e vsh /foo/* veyron:/foo/
	// The "veyron" host will be substituted with <veyron address>.
	if envName := os.Getenv("VSH_NAME"); len(envName) > 0 && name == "veyron" {
		name = envName
	}
	cmd := strings.Join(args, " ")
	return name, cmd, nil
}

func runPortForwarding(t tunnel.Tunnel, host string) {
	// *portforward is localaddr,remoteaddr
	parts := strings.Split(*portforward, ",")
	var laddr, raddr string
	if len(parts) != 2 {
		vlog.Fatalf("-L flag expects 2 values separated by a comma")
	}
	laddr = parts[0]
	raddr = parts[1]

	ln, err := net.Listen(*lprotocol, laddr)
	if err != nil {
		vlog.Fatalf("net.Listen(%q, %q) failed: %v", *lprotocol, laddr, err)
	}
	defer ln.Close()
	vlog.VI(1).Infof("Listening on %q", ln.Addr())
	for {
		conn, err := ln.Accept()
		if err != nil {
			vlog.Infof("Accept failed: %v", err)
			continue
		}
		stream, err := t.Forward(rt.R().TODOContext(), *rprotocol, raddr, veyron2.CallTimeout(24*time.Hour))
		if err != nil {
			vlog.Infof("Tunnel(%q, %q) failed: %v", *rprotocol, raddr, err)
			conn.Close()
			continue
		}
		name := fmt.Sprintf("%v-->%v-->(%v)-->%v", conn.RemoteAddr(), conn.LocalAddr(), host, raddr)
		go func() {
			vlog.VI(1).Infof("TUNNEL START: %v", name)
			errf := lib.Forward(conn, stream)
			err := stream.Finish()
			vlog.VI(1).Infof("TUNNEL END  : %v (%v, %v)", name, errf, err)
		}()
	}
}
<commit_msg>veyron,veyron2: replace Veyron name by Object name everywhere.<commit_after>package main

import (
	"errors"
	"flag"
	"fmt"
	"net"
	"os"
	"path"
	"strings"
	"time"

	"veyron/examples/tunnel"
	"veyron/examples/tunnel/lib"
	"veyron/lib/signals"
	"veyron2"
	"veyron2/rt"
	"veyron2/vlog"
)

var (
	disablePty = flag.Bool("T", false, "Disable pseudo-terminal allocation.")
	forcePty   = flag.Bool("t", false, "Force allocation of pseudo-terminal.")
	oname      = flag.String("oname", "", "Object name (or endpoint) for tunneling service.")

	portforward = flag.String("L", "", "localaddr,remoteaddr Forward local 'localaddr' to 'remoteaddr'")
	lprotocol   = flag.String("local_protocol", "tcp", "Local network protocol for port forwarding")
	rprotocol   = flag.String("remote_protocol", "tcp", "Remote network protocol for port forwarding")

	noshell = flag.Bool("N", false, "Do not execute a shell. Only do port forwarding.")
)

func init() {
	flag.Usage = func() {
		bname := path.Base(os.Args[0])
		fmt.Fprintf(os.Stderr, `%s: Veyron SHell.

This tool is used to run shell commands or an interactive shell on a remote
tunneld service.

To open an interactive shell, use:
  %s --host=<object name or endpoint>

To run a shell command, use:
  %s --host=<object name or endpoint> <command to run>

The -L flag will forward connections from a local port to a remote address
through the tunneld service. The flag value is localaddr,remoteaddr. E.g.
  -L :14141,www.google.com:80

%s can't be used directly with tools like rsync because veyron addresses don't
look like traditional hostnames, which rsync doesn't understand. For
compatibility with such tools, %s has a special feature that allows passing the
veyron address via the VSH_NAME environment variable.

  $ VSH_NAME=<veyron address> rsync -avh -e %s /foo/* veyron:/foo/

In this example, the "veyron" host will be substituted with $VSH_NAME by %s
and rsync will work as expected.

Full flags:
`, os.Args[0], bname, bname, bname, bname, os.Args[0], bname)
		flag.PrintDefaults()
	}
}

func main() {
	// Work around the fact that os.Exit doesn't run deferred functions.
	os.Exit(realMain())
}

func realMain() int {
	r := rt.Init()
	defer r.Shutdown()

	host, cmd, err := veyronNameAndCommandLine()
	if err != nil {
		flag.Usage()
		fmt.Fprintf(os.Stderr, "\n%v\n", err)
		return 1
	}

	t, err := tunnel.BindTunnel(host)
	if err != nil {
		vlog.Fatalf("BindTunnel(%q) failed: %v", host, err)
	}

	if len(*portforward) > 0 {
		go runPortForwarding(t, host)
	}

	if *noshell {
		<-signals.ShutdownOnSignals()
		return 0
	}

	opts := shellOptions(cmd)

	stream, err := t.Shell(rt.R().TODOContext(), cmd, opts, veyron2.CallTimeout(24*time.Hour))
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %v\n", err)
		return 1
	}
	saved := lib.EnterRawTerminalMode()
	defer lib.RestoreTerminalSettings(saved)
	runIOManager(os.Stdin, os.Stdout, os.Stderr, stream)

	exitMsg := fmt.Sprintf("Connection to %s closed.", host)
	exitStatus, err := stream.Finish()
	if err != nil {
		exitMsg += fmt.Sprintf(" (%v)", err)
	}
	vlog.VI(1).Info(exitMsg)
	// Only show the exit message on stdout for interactive shells.
	// Otherwise, the exit message might get confused with the output
	// of the command that was run.
	if err != nil {
		fmt.Fprintln(os.Stderr, exitMsg)
	} else if len(cmd) == 0 {
		fmt.Println(exitMsg)
	}
	return int(exitStatus)
}

func shellOptions(cmd string) (opts tunnel.ShellOpts) {
	opts.UsePty = (len(cmd) == 0 || *forcePty) && !*disablePty
	opts.Environment = environment()
	ws, err := lib.GetWindowSize()
	if err != nil {
		vlog.VI(1).Infof("GetWindowSize failed: %v", err)
	} else {
		opts.Rows = uint32(ws.Row)
		opts.Cols = uint32(ws.Col)
	}
	return
}

func environment() []string {
	env := []string{}
	for _, name := range []string{"TERM", "COLORTERM"} {
		if value := os.Getenv(name); value != "" {
			env = append(env, fmt.Sprintf("%s=%s", name, value))
		}
	}
	return env
}

// veyronNameAndCommandLine extracts the object name and the remote command to
// send to the server. The name can be specified with the --oname flag or as the
// first non-flag argument. The command line is the concatenation of all the
// non-flag arguments, minus the object name.
func veyronNameAndCommandLine() (string, string, error) {
	name := *oname
	args := flag.Args()
	if len(name) == 0 {
		if len(args) > 0 {
			name = args[0]
			args = args[1:]
		}
	}
	if len(name) == 0 {
		return "", "", errors.New("object name missing")
	}
	// For compatibility with tools like rsync. Because veyron addresses
	// don't look like traditional hostnames, tools that work with rsh and
	// ssh can't work directly with vsh. This trick makes the following
	// possible:
	//   $ VSH_NAME=<veyron address> rsync -avh -e vsh /foo/* veyron:/foo/
	// The "veyron" host will be substituted with <veyron address>.
	if envName := os.Getenv("VSH_NAME"); len(envName) > 0 && name == "veyron" {
		name = envName
	}
	cmd := strings.Join(args, " ")
	return name, cmd, nil
}

func runPortForwarding(t tunnel.Tunnel, host string) {
	// *portforward is localaddr,remoteaddr
	parts := strings.Split(*portforward, ",")
	var laddr, raddr string
	if len(parts) != 2 {
		vlog.Fatalf("-L flag expects 2 values separated by a comma")
	}
	laddr = parts[0]
	raddr = parts[1]

	ln, err := net.Listen(*lprotocol, laddr)
	if err != nil {
		vlog.Fatalf("net.Listen(%q, %q) failed: %v", *lprotocol, laddr, err)
	}
	defer ln.Close()
	vlog.VI(1).Infof("Listening on %q", ln.Addr())
	for {
		conn, err := ln.Accept()
		if err != nil {
			vlog.Infof("Accept failed: %v", err)
			continue
		}
		stream, err := t.Forward(rt.R().TODOContext(), *rprotocol, raddr, veyron2.CallTimeout(24*time.Hour))
		if err != nil {
			vlog.Infof("Tunnel(%q, %q) failed: %v", *rprotocol, raddr, err)
			conn.Close()
			continue
		}
		name := fmt.Sprintf("%v-->%v-->(%v)-->%v", conn.RemoteAddr(), conn.LocalAddr(), host, raddr)
		go func() {
			vlog.VI(1).Infof("TUNNEL START: %v", name)
			errf := lib.Forward(conn, stream)
			err := stream.Finish()
			vlog.VI(1).Infof("TUNNEL END  : %v (%v, %v)", name, errf, err)
		}()
	}
}
<|endoftext|>


<commit_before>package fire

import (
	"net/http"
	"strings"

	"github.com/256dpi/jsonapi"
	"github.com/256dpi/stack"
)

// A Group manages access to multiple controllers and their interconnections.
type Group struct {
	controllers map[string]*Controller

	// The function gets invoked by the controller with occurring fatal errors.
	Reporter func(error)

	// The function that should be used to log debugging information.
	Logger func(string)
}

// NewGroup creates and returns a new group.
func NewGroup() *Group {
	return &Group{
		controllers: make(map[string]*Controller),
	}
}

// Add will add a controller to the group.
func (g *Group) Add(controllers ...*Controller) {
	for _, controller := range controllers {
		// prepare controller
		controller.prepare()

		// get name
		name := controller.Model.Meta().PluralName

		// create entry in controller map
		g.controllers[name] = controller
	}
}

// List will return a list of all added controllers.
func (g *Group) List() []*Controller {
	list := make([]*Controller, 0, len(g.controllers))

	for _, c := range g.controllers {
		list = append(list, c)
	}

	return list
}

// Find will return the controller with the matching plural name if found.
func (g *Group) Find(pluralName string) *Controller {
	return g.controllers[pluralName]
}

// Endpoint will return an http handler that serves requests for this controller
// group. The specified prefix is used to parse the requests and generate urls
// for the resources.
func (g *Group) Endpoint(prefix string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// continue any previous aborts
		defer stack.Resume(func(err error) {
			// directly write potential bearer errors
			if jsonapiError, ok := err.(*jsonapi.Error); ok {
				jsonapi.WriteError(w, jsonapiError)
				return
			}

			// otherwise report critical errors
			if g.Reporter != nil {
				g.Reporter(err)
			}

			// ignore errors caused by writing critical errors
			jsonapi.WriteError(w, jsonapi.InternalServerError(""))
		})

		// trim and split path
		s := strings.Split(strings.Trim(strings.TrimPrefix(r.URL.Path, prefix), "/"), "/")

		// try to call the controllers general handler
		if len(s) > 0 {
			if controller, ok := g.controllers[s[0]]; ok {
				controller.generalHandler(g, prefix, w, r)
				return
			}
		}

		// write not found error
		jsonapi.WriteError(w, jsonapi.NotFound("resource not found"))
	})
}
<commit_msg>added note<commit_after>package fire

import (
	"net/http"
	"strings"

	"github.com/256dpi/jsonapi"
	"github.com/256dpi/stack"
)

// TODO: Pull in CORS from wood an make it a first class citizen.

// A Group manages access to multiple controllers and their interconnections.
type Group struct {
	controllers map[string]*Controller

	// The function gets invoked by the controller with occurring fatal errors.
	Reporter func(error)

	// The function that should be used to log debugging information.
	Logger func(string)
}

// NewGroup creates and returns a new group.
func NewGroup() *Group {
	return &Group{
		controllers: make(map[string]*Controller),
	}
}

// Add will add a controller to the group.
func (g *Group) Add(controllers ...*Controller) {
	for _, controller := range controllers {
		// prepare controller
		controller.prepare()

		// get name
		name := controller.Model.Meta().PluralName

		// create entry in controller map
		g.controllers[name] = controller
	}
}

// List will return a list of all added controllers.
func (g *Group) List() []*Controller {
	list := make([]*Controller, 0, len(g.controllers))

	for _, c := range g.controllers {
		list = append(list, c)
	}

	return list
}

// Find will return the controller with the matching plural name if found.
func (g *Group) Find(pluralName string) *Controller {
	return g.controllers[pluralName]
}

// Endpoint will return an http handler that serves requests for this controller
// group. The specified prefix is used to parse the requests and generate urls
// for the resources.
func (g *Group) Endpoint(prefix string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// continue any previous aborts
		defer stack.Resume(func(err error) {
			// directly write potential bearer errors
			if jsonapiError, ok := err.(*jsonapi.Error); ok {
				jsonapi.WriteError(w, jsonapiError)
				return
			}

			// otherwise report critical errors
			if g.Reporter != nil {
				g.Reporter(err)
			}

			// ignore errors caused by writing critical errors
			jsonapi.WriteError(w, jsonapi.InternalServerError(""))
		})

		// trim and split path
		s := strings.Split(strings.Trim(strings.TrimPrefix(r.URL.Path, prefix), "/"), "/")

		// try to call the controllers general handler
		if len(s) > 0 {
			if controller, ok := g.controllers[s[0]]; ok {
				controller.generalHandler(g, prefix, w, r)
				return
			}
		}

		// write not found error
		jsonapi.WriteError(w, jsonapi.NotFound("resource not found"))
	})
}
<|endoftext|>


<commit_before>/*
Copyright 2014 Rohith All rights reserved.

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 marathon

import (
	"fmt"
	"time"
)

// Group is a marathon application group
type Group struct {
	ID           string         `json:"id"`
	Apps         []*Application `json:"apps"`
	Dependencies []string       `json:"dependencies"`
	Groups       []*Group       `json:"groups"`
}

// Groups is a collection of marathon application groups
type Groups struct {
	ID           string         `json:"id"`
	Apps         []*Application `json:"apps"`
	Dependencies []string       `json:"dependencies"`
	Groups       []*Group       `json:"groups"`
}

// GetGroupOpts contains a payload for Group and Groups method
//		embed:		Embeds nested resources that match the supplied path.
// 					You can specify this parameter multiple times with different values
type GetGroupOpts struct {
	Embed []string `url:"embed,omitempty"`
}

// DeleteGroupOpts contains a payload for DeleteGroup method
//		force:		overrides a currently running deployment.
type DeleteGroupOpts struct {
	Force bool `url:"force,omitempty"`
}

// UpdateGroupOpts contains a payload for UpdateGroup method
//		force:		overrides a currently running deployment.
type UpdateGroupOpts struct {
	Force bool `url:"force,omitempty"`
}

// NewApplicationGroup create a new application group
//		name:			the name of the group
func NewApplicationGroup(name string) *Group {
	return &Group{
		ID:           name,
		Apps:         make([]*Application, 0),
		Dependencies: make([]string, 0),
		Groups:       make([]*Group, 0),
	}
}

// Name sets the name of the group
// 		name:	the name of the group
func (r *Group) Name(name string) *Group {
	r.ID = validateID(name)
	return r
}

// App add a application to the group in question
// 		application:	a pointer to the Application
func (r *Group) App(application *Application) *Group {
	if r.Apps == nil {
		r.Apps = make([]*Application, 0)
	}
	r.Apps = append(r.Apps, application)
	return r
}

// Groups retrieves a list of all the groups from marathon
func (r *marathonClient) Groups() (*Groups, error) {
	groups := new(Groups)
	if err := r.apiGet(marathonAPIGroups, "", groups); err != nil {
		return nil, err
	}
	return groups, nil
}

// Group retrieves the configuration of a specific group from marathon
//		name:			the identifier for the group
func (r *marathonClient) Group(name string) (*Group, error) {
	group := new(Group)
	if err := r.apiGet(fmt.Sprintf("%s/%s", marathonAPIGroups, trimRootPath(name)), nil, group); err != nil {
		return nil, err
	}
	return group, nil
}

// GroupsBy retrieves a list of all the groups from marathon by embed options
//		opts:		GetGroupOpts request payload
func (r *marathonClient) GroupsBy(opts *GetGroupOpts) (*Groups, error) {
	path, err := addOptions(marathonAPIGroups, opts)
	if err != nil {
		return nil, err
	}
	groups := new(Groups)
	if err := r.apiGet(path, "", groups); err != nil {
		return nil, err
	}
	return groups, nil
}

// GroupBy retrieves the configuration of a specific group from marathon
//		name:			the identifier for the group
//		opts:			GetGroupOpts request payload
func (r *marathonClient) GroupBy(name string, opts *GetGroupOpts) (*Group, error) {
	path, err := addOptions(fmt.Sprintf("%s/%s", marathonAPIGroups, trimRootPath(name)), opts)
	if err != nil {
		return nil, err
	}
	group := new(Group)
	if err := r.apiGet(path, nil, group); err != nil {
		return nil, err
	}
	return group, nil
}

// HasGroup checks if the group exists in marathon
// 		name:			the identifier for the group
func (r *marathonClient) HasGroup(name string) (bool, error) {
	path := fmt.Sprintf("%s/%s", marathonAPIGroups, trimRootPath(name))
	err := r.apiCall("GET", path, "", nil)
	if err != nil {
		if apiErr, ok := err.(*APIError); ok && apiErr.ErrCode == ErrCodeNotFound {
			return false, nil
		}
		return false, err
	}
	return true, nil
}

// CreateGroup creates a new group in marathon
//		group:			a pointer the Group structure defining the group
func (r *marathonClient) CreateGroup(group *Group) error {
	return r.apiPost(marathonAPIGroups, group, nil)
}

// WaitOnGroup waits for all the applications in a group to be deployed
// 		group:			the identifier for the group
//		timeout: 		a duration of time to wait before considering it failed (all tasks in all apps running defined as deployed)
func (r *marathonClient) WaitOnGroup(name string, timeout time.Duration) error {
	err := deadline(timeout, func(stop_channel chan bool) error {
		var flick atomicSwitch
		go func() {
			<-stop_channel
			close(stop_channel)
			flick.SwitchOn()
		}()
		for !flick.IsSwitched() {
			if group, err := r.Group(name); err != nil {
				continue
			} else {
				allRunning := true
				// for each of the application, check if the tasks and running
				for _, appID := range group.Apps {
					// Arrrgghhh!! .. so we can't use application instances from the Application struct like with app wait on as it
					// appears the instance count is not set straight away!! .. it defaults to zero and changes probably at the
					// dependencies gets deployed. Which is probably how it internally handles dependencies ..
					// step: grab the application
					application, err := r.Application(appID.ID)
					if err != nil {
						allRunning = false
						break
					}

					if application.Tasks == nil {
						allRunning = false
					} else if len(application.Tasks) != *appID.Instances {
						allRunning = false
					} else if application.TasksRunning != *appID.Instances {
						allRunning = false
					} else if len(application.DeploymentIDs()) > 0 {
						allRunning = false
					}
				}
				// has anyone toggle the flag?
				if allRunning {
					return nil
				}
			}
			time.Sleep(r.config.PollingWaitTime)
		}
		return nil
	})

	return err
}

// DeleteGroup deletes a group from marathon
//		name:			the identifier for the group
//		force:			used to force the delete operation in case of blocked deployment
func (r *marathonClient) DeleteGroup(name string, force bool) (*DeploymentID, error) {
	version := new(DeploymentID)
	path := fmt.Sprintf("%s/%s", marathonAPIGroups, trimRootPath(name))
	path = buildPathWithForceParam(path, force)
	if err := r.apiDelete(path, nil, version); err != nil {
		return nil, err
	}

	return version, nil
}

// UpdateGroup updates the parameters of a groups
//		name:			the identifier for the group
//		group:  		the group structure with the new params
//		force:			used to force the update operation in case of blocked deployment
func (r *marathonClient) UpdateGroup(name string, group *Group, force bool) (*DeploymentID, error) {
	deploymentID := new(DeploymentID)
	path := fmt.Sprintf("%s/%s", marathonAPIGroups, trimRootPath(name))
	path = buildPathWithForceParam(path, force)
	if err := r.apiPut(path, group, deploymentID); err != nil {
		return nil, err
	}

	return deploymentID, nil
}
<commit_msg>Fix build group path with force param (#284)<commit_after>/*
Copyright 2014 Rohith All rights reserved.

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 marathon

import (
	"fmt"
	"time"
)

// Group is a marathon application group
type Group struct {
	ID           string         `json:"id"`
	Apps         []*Application `json:"apps"`
	Dependencies []string       `json:"dependencies"`
	Groups       []*Group       `json:"groups"`
}

// Groups is a collection of marathon application groups
type Groups struct {
	ID           string         `json:"id"`
	Apps         []*Application `json:"apps"`
	Dependencies []string       `json:"dependencies"`
	Groups       []*Group       `json:"groups"`
}

// GetGroupOpts contains a payload for Group and Groups method
//		embed:		Embeds nested resources that match the supplied path.
// 					You can specify this parameter multiple times with different values
type GetGroupOpts struct {
	Embed []string `url:"embed,omitempty"`
}

// DeleteGroupOpts contains a payload for DeleteGroup method
//		force:		overrides a currently running deployment.
type DeleteGroupOpts struct {
	Force bool `url:"force,omitempty"`
}

// UpdateGroupOpts contains a payload for UpdateGroup method
//		force:		overrides a currently running deployment.
type UpdateGroupOpts struct {
	Force bool `url:"force,omitempty"`
}

// NewApplicationGroup create a new application group
//		name:			the name of the group
func NewApplicationGroup(name string) *Group {
	return &Group{
		ID:           name,
		Apps:         make([]*Application, 0),
		Dependencies: make([]string, 0),
		Groups:       make([]*Group, 0),
	}
}

// Name sets the name of the group
// 		name:	the name of the group
func (r *Group) Name(name string) *Group {
	r.ID = validateID(name)
	return r
}

// App add a application to the group in question
// 		application:	a pointer to the Application
func (r *Group) App(application *Application) *Group {
	if r.Apps == nil {
		r.Apps = make([]*Application, 0)
	}
	r.Apps = append(r.Apps, application)
	return r
}

// Groups retrieves a list of all the groups from marathon
func (r *marathonClient) Groups() (*Groups, error) {
	groups := new(Groups)
	if err := r.apiGet(marathonAPIGroups, "", groups); err != nil {
		return nil, err
	}
	return groups, nil
}

// Group retrieves the configuration of a specific group from marathon
//		name:			the identifier for the group
func (r *marathonClient) Group(name string) (*Group, error) {
	group := new(Group)
	if err := r.apiGet(fmt.Sprintf("%s/%s", marathonAPIGroups, trimRootPath(name)), nil, group); err != nil {
		return nil, err
	}
	return group, nil
}

// GroupsBy retrieves a list of all the groups from marathon by embed options
//		opts:		GetGroupOpts request payload
func (r *marathonClient) GroupsBy(opts *GetGroupOpts) (*Groups, error) {
	path, err := addOptions(marathonAPIGroups, opts)
	if err != nil {
		return nil, err
	}
	groups := new(Groups)
	if err := r.apiGet(path, "", groups); err != nil {
		return nil, err
	}
	return groups, nil
}

// GroupBy retrieves the configuration of a specific group from marathon
//		name:			the identifier for the group
//		opts:			GetGroupOpts request payload
func (r *marathonClient) GroupBy(name string, opts *GetGroupOpts) (*Group, error) {
	path, err := addOptions(fmt.Sprintf("%s/%s", marathonAPIGroups, trimRootPath(name)), opts)
	if err != nil {
		return nil, err
	}
	group := new(Group)
	if err := r.apiGet(path, nil, group); err != nil {
		return nil, err
	}
	return group, nil
}

// HasGroup checks if the group exists in marathon
// 		name:			the identifier for the group
func (r *marathonClient) HasGroup(name string) (bool, error) {
	path := fmt.Sprintf("%s/%s", marathonAPIGroups, trimRootPath(name))
	err := r.apiCall("GET", path, "", nil)
	if err != nil {
		if apiErr, ok := err.(*APIError); ok && apiErr.ErrCode == ErrCodeNotFound {
			return false, nil
		}
		return false, err
	}
	return true, nil
}

// CreateGroup creates a new group in marathon
//		group:			a pointer the Group structure defining the group
func (r *marathonClient) CreateGroup(group *Group) error {
	return r.apiPost(marathonAPIGroups, group, nil)
}

// WaitOnGroup waits for all the applications in a group to be deployed
// 		group:			the identifier for the group
//		timeout: 		a duration of time to wait before considering it failed (all tasks in all apps running defined as deployed)
func (r *marathonClient) WaitOnGroup(name string, timeout time.Duration) error {
	err := deadline(timeout, func(stop_channel chan bool) error {
		var flick atomicSwitch
		go func() {
			<-stop_channel
			close(stop_channel)
			flick.SwitchOn()
		}()
		for !flick.IsSwitched() {
			if group, err := r.Group(name); err != nil {
				continue
			} else {
				allRunning := true
				// for each of the application, check if the tasks and running
				for _, appID := range group.Apps {
					// Arrrgghhh!! .. so we can't use application instances from the Application struct like with app wait on as it
					// appears the instance count is not set straight away!! .. it defaults to zero and changes probably at the
					// dependencies gets deployed. Which is probably how it internally handles dependencies ..
					// step: grab the application
					application, err := r.Application(appID.ID)
					if err != nil {
						allRunning = false
						break
					}

					if application.Tasks == nil {
						allRunning = false
					} else if len(application.Tasks) != *appID.Instances {
						allRunning = false
					} else if application.TasksRunning != *appID.Instances {
						allRunning = false
					} else if len(application.DeploymentIDs()) > 0 {
						allRunning = false
					}
				}
				// has anyone toggle the flag?
				if allRunning {
					return nil
				}
			}
			time.Sleep(r.config.PollingWaitTime)
		}
		return nil
	})

	return err
}

// DeleteGroup deletes a group from marathon
//		name:			the identifier for the group
//		force:			used to force the delete operation in case of blocked deployment
func (r *marathonClient) DeleteGroup(name string, force bool) (*DeploymentID, error) {
	version := new(DeploymentID)
	path := fmt.Sprintf("%s/%s", marathonAPIGroups, trimRootPath(name))
	if force {
		path += "?force=true"
	}
	if err := r.apiDelete(path, nil, version); err != nil {
		return nil, err
	}

	return version, nil
}

// UpdateGroup updates the parameters of a groups
//		name:			the identifier for the group
//		group:  		the group structure with the new params
//		force:			used to force the update operation in case of blocked deployment
func (r *marathonClient) UpdateGroup(name string, group *Group, force bool) (*DeploymentID, error) {
	deploymentID := new(DeploymentID)
	path := fmt.Sprintf("%s/%s", marathonAPIGroups, trimRootPath(name))
	if force {
		path += "?force=true"
	}
	if err := r.apiPut(path, group, deploymentID); err != nil {
		return nil, err
	}

	return deploymentID, nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"net/http"
	"os"
	"time"

	"github.com/ae6rt/decap/web/credentials"
	"github.com/ae6rt/decap/web/deferrals"
	"github.com/ae6rt/decap/web/lock"
	"github.com/ae6rt/decap/web/scmclients"
	"github.com/ae6rt/decap/web/storage"
	"github.com/julienschmidt/httprouter"
)

var (
	awsKey                 = flag.String("aws-access-key", "", "Default decap AWS access key.  /etc/secrets/aws-key in the cluster overrides this.")
	awsSecret              = flag.String("aws-secret-key", "", "Default decap AWS access secret.  /etc/secrets/aws-secret in the cluster overrides this.")
	awsRegion              = flag.String("aws-region", "us-west-1", "Default decap AWS region.  /etc/secrets/aws-region in the cluster overrides this.")
	githubClientID         = flag.String("github-client-id", "", "Default Github ClientID for quering Github repos.  /etc/secrets/github-client-id in the cluster overrides this.")
	githubClientSecret     = flag.String("github-client-secret", "", "Default Github Client Secret for quering Github repos.  /etc/secrets/github-client-secret in the cluster overrides this.")
	buildScriptsRepo       = flag.String("build-scripts-repo", "https://github.com/ae6rt/decap-build-scripts.git", "Git repo where userland build scripts are held.")
	buildScriptsRepoBranch = flag.String("build-scripts-repo-branch", "master", "Branch or revision to use on git repo where userland build scripts are held.")
	noPodWatcher           = flag.Bool("no-podwatcher", false, "Do not start k8s podwatcher, for dev mode purposes.")
	versionFlag            = flag.Bool("version", false, "Print version info and exit.")
)

var (
	// Log is the global logger.
	Log          = log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile)
	buildVersion string
	buildCommit  string
	buildDate    string
	buildGoSDK   string
)

func main() {
	flag.Parse()
	Log.Printf("Version: %s, Commit: %s, Date: %s, Go SDK: %s\n", buildVersion, buildCommit, buildDate, buildGoSDK)
	if *versionFlag {
		os.Exit(0)
	}

	*awsKey = kubeSecret("/etc/secrets/aws-key", *awsKey)
	*awsSecret = kubeSecret("/etc/secrets/aws-secret", *awsSecret)
	*awsRegion = kubeSecret("/etc/secrets/aws-region", *awsRegion)
	*githubClientID = kubeSecret("/etc/secrets/github-client-id", *githubClientID)
	*githubClientSecret = kubeSecret("/etc/secrets/github-client-secret", *githubClientSecret)

	// Create collaborating services.

	k8sClient, err := NewKubernetesClient()
	if err != nil {
		Log.Fatalf("Cannot create Kubernetes client: %v\n", err)
	}
	deferralService := deferrals.NewDefault(Log)

	lockService := lock.NewDefault(k8sClient)

	buildScripts := BuildScripts{URL: *buildScriptsRepo, Branch: *buildScriptsRepoBranch}

	projectManager := NewDefaultProjectManager(buildScripts)

	buildManager := NewBuildManager(k8sClient, projectManager, lockService, deferralService, Log)

	awsCredential := credentials.AWSCredential{AccessKey: *awsKey, AccessSecret: *awsSecret, Region: *awsRegion}

	buildStore := storage.NewAWS(awsCredential, Log)

	scmManagers := map[string]scmclients.SCMClient{
		"github": scmclients.NewGithub("https://api.github.com", *githubClientID, *githubClientSecret),
	}

	// Setup HTTP routing

	router := httprouter.New()

	// This serves up the front end webapp.
	router.ServeFiles("/decap/*filepath", http.Dir("./static"))

	// Set options for frontend webapp.
	router.OPTIONS("/api/v1/*filepath", HandleOptions)

	// Report backend version.
	router.GET("/api/v1/version", VersionHandler)

	// Report managed projects
	router.GET("/api/v1/projects", ProjectsHandler)

	// Report on branches of a given project
	router.GET("/api/v1/projects/:team/:project/refs", ProjectRefsHandler(scmManagers))

	// Report on historical builds for a given project
	router.GET("/api/v1/builds/:team/:project", BuildsHandler(buildStore))

	// Terminates a running build
	router.DELETE("/api/v1/builds/:id", StopBuildHandler(buildManager, Log))

	// Execute a build on demand
	router.POST("/api/v1/builds/:team/:project", ExecuteBuildHandler(buildManager, Log))

	// Report on teams.  (I think this is a project - msp)
	router.GET("/api/v1/teams", TeamsHandler)

	// Report on currenly deferred builds.
	router.GET("/api/v1/deferred", DeferredBuildsHandler(buildManager))

	// Remove a build from the deferred builds queue.
	router.POST("/api/v1/deferred", DeferredBuildsHandler(buildManager))

	//	Return gzipped console log, or console log in plain text if Accept: text/plain is set
	router.GET("/api/v1/logs/:id", LogHandler(buildStore))

	// ArtifactsHandler returns build artifacts gzipped tarball, or file listing in tarball if Accept: text/plain is set
	router.GET("/api/v1/artifacts/:id", ArtifactsHandler(buildStore))

	// Return current state of the build queue
	router.GET("/api/v1/shutdown", ShutdownHandler(buildManager))

	// ShutdownHandler stops the build queue from accepting new build requests.
	router.POST("/api/v1/shutdown/:state", ShutdownHandler(buildManager))

	// LogLevelHandler toggles debug logging.
	router.POST("/api/v1/loglevel/:level", LogLevelHandler)

	// The interface for external SCM systems to post VCS events through post-commit hooks.
	router.POST("/hooks/:repomanager", HooksHandler(projectManager, buildManager, Log))

	projects, err := projectManager.Assemble()
	if err != nil {
		Log.Printf("Cannot clone build scripts repository: %v\n", err)
	}

	go func() {
		c := time.Tick(1 * time.Minute)
		buildManager.LaunchDeferred(c)
	}()
	go projectMux(projects)
	go logLevelMux(LogDefault)
	go shutdownMux(BuildQueueOpen)
	if !*noPodWatcher {
		go buildManager.PodWatcher()
	}

	Log.Println("decap ready on port 9090...")
	Log.Fatal(http.ListenAndServe(":9090", corsWrapper(router)))
}
<commit_msg>[issue/41] minor main() cleanup<commit_after>package main

import (
	"flag"
	"log"
	"net/http"
	"os"
	"time"

	"github.com/ae6rt/decap/web/credentials"
	"github.com/ae6rt/decap/web/deferrals"
	"github.com/ae6rt/decap/web/lock"
	"github.com/ae6rt/decap/web/scmclients"
	"github.com/ae6rt/decap/web/storage"
	"github.com/julienschmidt/httprouter"
)

var (
	awsKey                 = flag.String("aws-access-key", "", "Default decap AWS access key.  /etc/secrets/aws-key in the cluster overrides this.")
	awsSecret              = flag.String("aws-secret-key", "", "Default decap AWS access secret.  /etc/secrets/aws-secret in the cluster overrides this.")
	awsRegion              = flag.String("aws-region", "us-west-1", "Default decap AWS region.  /etc/secrets/aws-region in the cluster overrides this.")
	githubClientID         = flag.String("github-client-id", "", "Default Github ClientID for quering Github repos.  /etc/secrets/github-client-id in the cluster overrides this.")
	githubClientSecret     = flag.String("github-client-secret", "", "Default Github Client Secret for quering Github repos.  /etc/secrets/github-client-secret in the cluster overrides this.")
	buildScriptsRepo       = flag.String("build-scripts-repo", "https://github.com/ae6rt/decap-build-scripts.git", "Git repo where userland build scripts are held.")
	buildScriptsRepoBranch = flag.String("build-scripts-repo-branch", "master", "Branch or revision to use on git repo where userland build scripts are held.")
	noPodWatcher           = flag.Bool("no-podwatcher", false, "Do not start k8s podwatcher, for dev mode purposes.")
	versionFlag            = flag.Bool("version", false, "Print version info and exit.")
)

var (
	// Log is the global logger.
	Log          = log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile)
	buildVersion string
	buildCommit  string
	buildDate    string
	buildGoSDK   string
)

func main() {
	flag.Parse()
	Log.Printf("Version: %s, Commit: %s, Date: %s, Go SDK: %s\n", buildVersion, buildCommit, buildDate, buildGoSDK)
	if *versionFlag {
		os.Exit(0)
	}

	*awsKey = kubeSecret("/etc/secrets/aws-key", *awsKey)
	*awsSecret = kubeSecret("/etc/secrets/aws-secret", *awsSecret)
	*awsRegion = kubeSecret("/etc/secrets/aws-region", *awsRegion)
	*githubClientID = kubeSecret("/etc/secrets/github-client-id", *githubClientID)
	*githubClientSecret = kubeSecret("/etc/secrets/github-client-secret", *githubClientSecret)

	// Create collaborating services.

	k8sClient, err := NewKubernetesClient()
	if err != nil {
		Log.Fatalf("Cannot create Kubernetes client: %v\n", err)
	}
	deferralService := deferrals.NewDefault(Log)

	lockService := lock.NewDefault(k8sClient)

	projectManager := NewDefaultProjectManager(BuildScripts{URL: *buildScriptsRepo, Branch: *buildScriptsRepoBranch})

	buildManager := NewBuildManager(k8sClient, projectManager, lockService, deferralService, Log)

	buildStore := storage.NewAWS(credentials.AWSCredential{AccessKey: *awsKey, AccessSecret: *awsSecret, Region: *awsRegion}, Log)

	scmManagers := map[string]scmclients.SCMClient{
		"github": scmclients.NewGithub("https://api.github.com", *githubClientID, *githubClientSecret),
	}

	// Setup HTTP routing

	router := httprouter.New()

	// This serves up the front end webapp.
	router.ServeFiles("/decap/*filepath", http.Dir("./static"))

	// Set options for frontend webapp.
	router.OPTIONS("/api/v1/*filepath", HandleOptions)

	// Report backend version.
	router.GET("/api/v1/version", VersionHandler)

	// Report managed projects
	router.GET("/api/v1/projects", ProjectsHandler)

	// Report on branches of a given project
	router.GET("/api/v1/projects/:team/:project/refs", ProjectRefsHandler(scmManagers))

	// Report on historical builds for a given project
	router.GET("/api/v1/builds/:team/:project", BuildsHandler(buildStore))

	// Terminates a running build
	router.DELETE("/api/v1/builds/:id", StopBuildHandler(buildManager, Log))

	// Execute a build on demand
	router.POST("/api/v1/builds/:team/:project", ExecuteBuildHandler(buildManager, Log))

	// Report on teams.  (I think this is a project - msp)
	router.GET("/api/v1/teams", TeamsHandler)

	// Report on currenly deferred builds.
	router.GET("/api/v1/deferred", DeferredBuildsHandler(buildManager))

	// Remove a build from the deferred builds queue.
	router.POST("/api/v1/deferred", DeferredBuildsHandler(buildManager))

	//	Return gzipped console log, or console log in plain text if Accept: text/plain is set
	router.GET("/api/v1/logs/:id", LogHandler(buildStore))

	// ArtifactsHandler returns build artifacts gzipped tarball, or file listing in tarball if Accept: text/plain is set
	router.GET("/api/v1/artifacts/:id", ArtifactsHandler(buildStore))

	// Return current state of the build queue
	router.GET("/api/v1/shutdown", ShutdownHandler(buildManager))

	// ShutdownHandler stops the build queue from accepting new build requests.
	router.POST("/api/v1/shutdown/:state", ShutdownHandler(buildManager))

	// LogLevelHandler toggles debug logging.
	router.POST("/api/v1/loglevel/:level", LogLevelHandler)

	// The interface for external SCM systems to post VCS events through post-commit hooks.
	router.POST("/hooks/:repomanager", HooksHandler(projectManager, buildManager, Log))

	projects, err := projectManager.Assemble()
	if err != nil {
		Log.Printf("Cannot clone build scripts repository: %v\n", err)
	}

	go func() {
		c := time.Tick(1 * time.Minute)
		buildManager.LaunchDeferred(c)
	}()
	go projectMux(projects)
	go logLevelMux(LogDefault)
	go shutdownMux(BuildQueueOpen)
	if !*noPodWatcher {
		go buildManager.PodWatcher()
	}

	Log.Println("decap ready on port 9090...")
	Log.Fatal(http.ListenAndServe(":9090", corsWrapper(router)))
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net"
	"net/rpc"
	"net/rpc/jsonrpc"
	"reflect"
	"testing"

	log "github.com/Sirupsen/logrus"

	"github.com/Cepave/open-falcon-backend/common/model"
)

type NqmAgent int

func (t *NqmAgent) Task(request model.NqmTaskRequest, response *model.NqmTaskResponse) (err error) {
	response.NeedPing = true

	nqmAgent := &model.NqmAgent{
		Id:           1,
		Name:         "agent_for_test",
		IspId:        2,
		IspName:      "IspName_for_test",
		ProvinceId:   3,
		ProvinceName: "ProvinceName_for_test",
		CityId:       4,
		CityName:     "CityName_for_test",
	}
	response.Agent = nqmAgent

	targets := []model.NqmTarget{
		{Id: 11, Host: "11.11.11.11", IspId: 12, ProvinceId: 13, CityId: 14},
		{Id: 21, Host: "22.22.22.22", IspId: 22, ProvinceId: 23, CityId: 24},
		{Id: 31, Host: "33.33.33.33", IspId: 32, ProvinceId: 33, CityId: 34},
	}
	response.Targets = targets

	response.Measurements = map[string]model.MeasurementsProperty{
		"fping":   {true, []string{"fping", "-p", "20", "-i", "10", "-C", "4", "-q", "-a"}, 300},
		"tcpping": {false, []string{"tcpping", "-i", "0.01", "-c", "4"}, 300},
		"tcpconn": {false, []string{"tcpconn"}, 300},
	}
	return nil
}

func initJsonRpcServer(addr string) {
	rpc.Register(new(NqmAgent))

	l, e := net.Listen("tcp", addr)
	if e != nil {
		panic(fmt.Errorf("Cannot listen to address %v. Error %v", addr, e))
	} else {
		log.Println("RPC server is listening at", addr)
	}

	go func() {
		for {
			conn, err := l.Accept()
			if err != nil {
				log.Println("listener accept fail:", err)
				break
			}
			go jsonrpc.ServeConn(conn)
		}
	}()
}

func initJsonRpcClient(srvAddr string) {
	rpcClient.RpcServer = srvAddr
	req = model.NqmTaskRequest{
		Hostname:     "nqm-agent",
		IpAddress:    "1.2.3.4",
		ConnectionId: "arg-arg-arg",
	}
}

func TestTask(t *testing.T) {
	GetGeneralConfig().hbsResp.Store(model.NqmTaskResponse{})
	initJsonRpcServer("127.0.0.1:65534")
	initJsonRpcClient("127.0.0.1:65534")

	query()
	hbsResp := GetGeneralConfig().hbsResp.Load().(model.NqmTaskResponse)
	cmd, targets, agent, _, err := Task(new(Fping))
	if err != nil {
		t.Error(err)
	}

	expecteds := []model.NqmTarget{
		{Id: 11, Host: "11.11.11.11", IspId: 12, ProvinceId: 13, CityId: 14},
		{Id: 21, Host: "22.22.22.22", IspId: 22, ProvinceId: 23, CityId: 24},
		{Id: 31, Host: "33.33.33.33", IspId: 32, ProvinceId: 33, CityId: 34},
	}

	expectedAgent := *hbsResp.Agent
	expectedCommand := []string{
		"fping", "-p", "20", "-i", "10", "-C", "4", "-q", "-a",
		"11.11.11.11", "22.22.22.22", "33.33.33.33",
	}

	for i, v := range targets {
		if !reflect.DeepEqual(expecteds[i], v) {
			t.Error(expecteds[i], v)
		}
		t.Log(expecteds[i], v)
	}
	if !reflect.DeepEqual(expectedAgent, agent) {
		t.Error(expectedAgent, agent)
	}
	t.Log(agent)

	if &expectedAgent == &agent {
		t.Errorf("%p == %p", &expectedAgent, &agent)
	}
	t.Logf("%p != %p", &agent, &expectedAgent)

	if !reflect.DeepEqual(expectedCommand, cmd) {
		t.Error(expectedCommand, cmd)
	}
	t.Log(cmd)
}
<commit_msg>[OWL-687][nqm-agent] Relative changes in the test<commit_after>package main

import (
	"fmt"
	"net"
	"net/rpc"
	"net/rpc/jsonrpc"
	"reflect"
	"testing"

	log "github.com/Sirupsen/logrus"

	"github.com/Cepave/open-falcon-backend/common/model"
)

type NqmAgent int

func (t *NqmAgent) Task(request model.NqmTaskRequest, response *model.NqmTaskResponse) (err error) {
	response.NeedPing = true

	nqmAgent := &model.NqmAgent{
		Id:           1,
		Name:         "agent_for_test",
		IspId:        2,
		IspName:      "IspName_for_test",
		ProvinceId:   3,
		ProvinceName: "ProvinceName_for_test",
		CityId:       4,
		CityName:     "CityName_for_test",
	}
	response.Agent = nqmAgent

	targets := []model.NqmTarget{
		{Id: 11, Host: "11.11.11.11", IspId: 12, ProvinceId: 13, CityId: 14},
		{Id: 21, Host: "22.22.22.22", IspId: 22, ProvinceId: 23, CityId: 24},
		{Id: 31, Host: "33.33.33.33", IspId: 32, ProvinceId: 33, CityId: 34},
	}
	response.Targets = targets

	response.Measurements = map[string]model.MeasurementsProperty{
		"fping":   {true, []string{"fping", "-p", "20", "-i", "10", "-C", "4", "-q", "-a"}, 300},
		"tcpping": {false, []string{"tcpping", "-i", "0.01", "-c", "4"}, 300},
		"tcpconn": {false, []string{"tcpconn"}, 300},
	}
	return nil
}

func initJsonRpcServer(addr string) {
	rpc.Register(new(NqmAgent))

	l, e := net.Listen("tcp", addr)
	if e != nil {
		panic(fmt.Errorf("Cannot listen to address %v. Error %v", addr, e))
	} else {
		log.Println("RPC server is listening at", addr)
	}

	go func() {
		for {
			conn, err := l.Accept()
			if err != nil {
				log.Println("listener accept fail:", err)
				break
			}
			go jsonrpc.ServeConn(conn)
		}
	}()
}

func initJsonRpcClient(srvAddr string) {
	rpcServer = srvAddr
	req = model.NqmTaskRequest{
		Hostname:     "nqm-agent",
		IpAddress:    "1.2.3.4",
		ConnectionId: "arg-arg-arg",
	}
	rpcClient = initConn(rpcServer, rpcTimeout)
}

func TestTask(t *testing.T) {
	GetGeneralConfig().hbsResp.Store(model.NqmTaskResponse{})
	initJsonRpcServer("127.0.0.1:65534")
	initJsonRpcClient("127.0.0.1:65534")

	query()
	hbsResp := GetGeneralConfig().hbsResp.Load().(model.NqmTaskResponse)
	cmd, targets, agent, _, err := Task(new(Fping))
	if err != nil {
		t.Error(err)
	}

	expecteds := []model.NqmTarget{
		{Id: 11, Host: "11.11.11.11", IspId: 12, ProvinceId: 13, CityId: 14},
		{Id: 21, Host: "22.22.22.22", IspId: 22, ProvinceId: 23, CityId: 24},
		{Id: 31, Host: "33.33.33.33", IspId: 32, ProvinceId: 33, CityId: 34},
	}

	expectedAgent := *hbsResp.Agent
	expectedCommand := []string{
		"fping", "-p", "20", "-i", "10", "-C", "4", "-q", "-a",
		"11.11.11.11", "22.22.22.22", "33.33.33.33",
	}

	for i, v := range targets {
		if !reflect.DeepEqual(expecteds[i], v) {
			t.Error(expecteds[i], v)
		}
		t.Log(expecteds[i], v)
	}
	if !reflect.DeepEqual(expectedAgent, agent) {
		t.Error(expectedAgent, agent)
	}
	t.Log(agent)

	if &expectedAgent == &agent {
		t.Errorf("%p == %p", &expectedAgent, &agent)
	}
	t.Logf("%p != %p", &agent, &expectedAgent)

	if !reflect.DeepEqual(expectedCommand, cmd) {
		t.Error(expectedCommand, cmd)
	}
	t.Log(cmd)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net"

	"github.com/davecgh/go-spew/spew"
	"github.com/lightningnetwork/lnd/autopilot"
	"github.com/lightningnetwork/lnd/lnwire"
	"github.com/roasbeef/btcd/btcec"
	"github.com/roasbeef/btcd/wire"
	"github.com/roasbeef/btcutil"
)

// chanController is an implementation of the autopilot.ChannelController
// interface that's backed by a running lnd instance.
type chanController struct {
	server *server
}

// OpenChannel opens a channel to a target peer, with a capacity of the
// specified amount. This function should un-block immediately after the
// funding transaction that marks the channel open has been broadcast.
func (c *chanController) OpenChannel(target *btcec.PublicKey,
	amt btcutil.Amount, addrs []net.Addr) error {

	// We can't establish a channel if no addresses were provided for the
	// peer.
	if len(addrs) == 0 {
		return fmt.Errorf("Unable to create channel w/o an active " +
			"address")
	}

	// First, we'll check if we're already connected to the target peer. If
	// not, then we'll need to establish a connection.
	if _, err := c.server.FindPeer(target); err != nil {
		// TODO(roasbeef): try teach addr

		atplLog.Tracef("Connecting to %x to auto-create channel: ",
			target.SerializeCompressed())

		lnAddr := &lnwire.NetAddress{
			IdentityKey: target,
			ChainNet:    activeNetParams.Net,
		}

		// We'll attempt to successively connect to each of the
		// advertised IP addresses until we've either exhausted the
		// advertised IP addresses, or have made a connection.
		var connected bool
		for _, addr := range addrs {
			// If the address doesn't already have a port, then
			// we'll assume the current default port.
			tcpAddr, ok := addr.(*net.TCPAddr)
			if !ok {
				return fmt.Errorf("TCP address required instead "+
					"have %T", addr)
			}
			if tcpAddr.Port == 0 {
				tcpAddr.Port = defaultPeerPort
			}

			lnAddr.Address = tcpAddr

			// TODO(roasbeef): make perm connection in server after
			// chan open?
			err := c.server.ConnectToPeer(lnAddr, false)
			if err != nil {
				// If we weren't able to connect to the peer,
				// then we'll move onto the next.
				continue
			}

			connected = true
			break
		}

		// If we weren't able to establish a connection at all, then
		// we'll error out.
		if !connected {
			return fmt.Errorf("Unable to connect to %x",
				target.SerializeCompressed())
		}
	}

	// With the connection established, we'll now establish our connection
	// to the target peer, waiting for the first update before we exit.
	feePerWeight, err := c.server.cc.feeEstimator.EstimateFeePerWeight(3)
	if err != nil {
		return err
	}
	updateStream, errChan := c.server.OpenChannel(-1, target, amt, 0,
		feePerWeight)

	select {
	case err := <-errChan:
		return err
	case <-updateStream:
		return nil
	case <-c.server.quit:
		return nil
	}
}

func (c *chanController) CloseChannel(chanPoint *wire.OutPoint) error {
	return nil
}
func (c *chanController) SpliceIn(chanPoint *wire.OutPoint,
	amt btcutil.Amount) (*autopilot.Channel, error) {
	return nil, nil
}
func (c *chanController) SpliceOut(chanPoint *wire.OutPoint,
	amt btcutil.Amount) (*autopilot.Channel, error) {
	return nil, nil
}

// A compile time assertion to ensure chanController meets the
// autopilot.ChannelController interface.
var _ autopilot.ChannelController = (*chanController)(nil)

// initAutoPilot initializes a new autopilot.Agent instance based on the passed
// configuration struct. All interfaces needed to drive the pilot will be
// registered and launched.
func initAutoPilot(svr *server, cfg *autoPilotConfig) (*autopilot.Agent, error) {
	atplLog.Infof("Instantiating autopilot with cfg: %v", spew.Sdump(cfg))

	// First, we'll create the preferential attachment heuristic,
	// initialized with the passed auto pilot configuration parameters.
	//
	// TODO(roasbeef): switch here to dispatch specified heuristic
	minChanSize := svr.cc.wallet.Cfg.DefaultConstraints.DustLimit * 5
	prefAttachment := autopilot.NewConstrainedPrefAttachment(
		minChanSize, maxFundingAmount,
		uint16(cfg.MaxChannels), cfg.Allocation,
	)

	// With the heuristic itself created, we can now populate the remainder
	// of the items that the autopilot agent needs to perform its duties.
	self := svr.identityPriv.PubKey()
	pilotCfg := autopilot.Config{
		Self:           self,
		Heuristic:      prefAttachment,
		ChanController: &chanController{svr},
		WalletBalance: func() (btcutil.Amount, error) {
			return svr.cc.wallet.ConfirmedBalance(1, true)
		},
		Graph: autopilot.ChannelGraphFromDatabase(svr.chanDB.ChannelGraph()),
	}

	// Next, we'll fetch the current state of open channels from the
	// database to use as initial state for the auto-pilot agent.
	activeChannels, err := svr.chanDB.FetchAllChannels()
	if err != nil {
		return nil, err
	}
	initialChanState := make([]autopilot.Channel, len(activeChannels))
	for i, channel := range activeChannels {
		initialChanState[i] = autopilot.Channel{
			ChanID:   channel.ShortChanID,
			Capacity: channel.Capacity,
			Node:     autopilot.NewNodeID(channel.IdentityPub),
		}
	}

	// Now that we have all the initial dependencies, we can create the
	// auto-pilot instance itself.
	pilot, err := autopilot.New(pilotCfg, initialChanState)
	if err != nil {
		return nil, err
	}

	// Finally, we'll need to subscribe to two things: incoming
	// transactions that modify the wallet's balance, and also any graph
	// topology updates.
	txnSubscription, err := svr.cc.wallet.SubscribeTransactions()
	if err != nil {
		return nil, err
	}
	graphSubscription, err := svr.chanRouter.SubscribeTopology()
	if err != nil {
		return nil, err
	}

	// We'll launch a goroutine to provide the agent with notifications
	// whenever the balance of the wallet changes.
	svr.wg.Add(1)
	go func() {
		defer txnSubscription.Cancel()
		defer svr.wg.Done()

		for {
			select {
			case txnUpdate := <-txnSubscription.ConfirmedTransactions():
				pilot.OnBalanceChange(txnUpdate.Value)
			case <-svr.quit:
				return
			}
		}

	}()

	// We'll also launch a goroutine to provide the agent with
	// notifications for when the graph topology controlled by the node
	// changes.
	svr.wg.Add(1)
	go func() {
		defer graphSubscription.Cancel()
		defer svr.wg.Done()

		for {
			select {
			case topChange, ok := <-graphSubscription.TopologyChanges:
				// If the router is shutting down, then we will
				// as well.
				if !ok {
					return
				}

				for _, edgeUpdate := range topChange.ChannelEdgeUpdates {
					// If this isn't an advertisement by
					// the backing lnd node, then we'll
					// continue as we only want to add
					// channels that we've created
					// ourselves.
					if !edgeUpdate.AdvertisingNode.IsEqual(self) {
						continue
					}

					// If this is indeed a channel we
					// opened, then we'll convert it to the
					// autopilot.Channel format, and notify
					// the pilot of the new channel.
					chanNode := autopilot.NewNodeID(
						edgeUpdate.ConnectingNode,
					)
					chanID := lnwire.NewShortChanIDFromInt(
						edgeUpdate.ChanID,
					)
					edge := autopilot.Channel{
						ChanID:   chanID,
						Capacity: edgeUpdate.Capacity,
						Node:     chanNode,
					}
					pilot.OnChannelOpen(edge)
				}

				// For each closed closed channel, we'll obtain
				// the chanID of the closed channel and send it
				// to the pilot.
				for _, chanClose := range topChange.ClosedChannels {
					chanID := lnwire.NewShortChanIDFromInt(
						chanClose.ChanID,
					)

					pilot.OnChannelClose(chanID)
				}

			case <-svr.quit:
				return
			}
		}
	}()

	return pilot, nil
}
<commit_msg>pilot: pass private=false to OpenChannel<commit_after>package main

import (
	"fmt"
	"net"

	"github.com/davecgh/go-spew/spew"
	"github.com/lightningnetwork/lnd/autopilot"
	"github.com/lightningnetwork/lnd/lnwire"
	"github.com/roasbeef/btcd/btcec"
	"github.com/roasbeef/btcd/wire"
	"github.com/roasbeef/btcutil"
)

// chanController is an implementation of the autopilot.ChannelController
// interface that's backed by a running lnd instance.
type chanController struct {
	server *server
}

// OpenChannel opens a channel to a target peer, with a capacity of the
// specified amount. This function should un-block immediately after the
// funding transaction that marks the channel open has been broadcast.
func (c *chanController) OpenChannel(target *btcec.PublicKey,
	amt btcutil.Amount, addrs []net.Addr) error {

	// We can't establish a channel if no addresses were provided for the
	// peer.
	if len(addrs) == 0 {
		return fmt.Errorf("Unable to create channel w/o an active " +
			"address")
	}

	// First, we'll check if we're already connected to the target peer. If
	// not, then we'll need to establish a connection.
	if _, err := c.server.FindPeer(target); err != nil {
		// TODO(roasbeef): try teach addr

		atplLog.Tracef("Connecting to %x to auto-create channel: ",
			target.SerializeCompressed())

		lnAddr := &lnwire.NetAddress{
			IdentityKey: target,
			ChainNet:    activeNetParams.Net,
		}

		// We'll attempt to successively connect to each of the
		// advertised IP addresses until we've either exhausted the
		// advertised IP addresses, or have made a connection.
		var connected bool
		for _, addr := range addrs {
			// If the address doesn't already have a port, then
			// we'll assume the current default port.
			tcpAddr, ok := addr.(*net.TCPAddr)
			if !ok {
				return fmt.Errorf("TCP address required instead "+
					"have %T", addr)
			}
			if tcpAddr.Port == 0 {
				tcpAddr.Port = defaultPeerPort
			}

			lnAddr.Address = tcpAddr

			// TODO(roasbeef): make perm connection in server after
			// chan open?
			err := c.server.ConnectToPeer(lnAddr, false)
			if err != nil {
				// If we weren't able to connect to the peer,
				// then we'll move onto the next.
				continue
			}

			connected = true
			break
		}

		// If we weren't able to establish a connection at all, then
		// we'll error out.
		if !connected {
			return fmt.Errorf("Unable to connect to %x",
				target.SerializeCompressed())
		}
	}

	// With the connection established, we'll now establish our connection
	// to the target peer, waiting for the first update before we exit.
	feePerWeight, err := c.server.cc.feeEstimator.EstimateFeePerWeight(3)
	if err != nil {
		return err
	}
	updateStream, errChan := c.server.OpenChannel(-1, target, amt, 0,
		feePerWeight, false)

	select {
	case err := <-errChan:
		return err
	case <-updateStream:
		return nil
	case <-c.server.quit:
		return nil
	}
}

func (c *chanController) CloseChannel(chanPoint *wire.OutPoint) error {
	return nil
}
func (c *chanController) SpliceIn(chanPoint *wire.OutPoint,
	amt btcutil.Amount) (*autopilot.Channel, error) {
	return nil, nil
}
func (c *chanController) SpliceOut(chanPoint *wire.OutPoint,
	amt btcutil.Amount) (*autopilot.Channel, error) {
	return nil, nil
}

// A compile time assertion to ensure chanController meets the
// autopilot.ChannelController interface.
var _ autopilot.ChannelController = (*chanController)(nil)

// initAutoPilot initializes a new autopilot.Agent instance based on the passed
// configuration struct. All interfaces needed to drive the pilot will be
// registered and launched.
func initAutoPilot(svr *server, cfg *autoPilotConfig) (*autopilot.Agent, error) {
	atplLog.Infof("Instantiating autopilot with cfg: %v", spew.Sdump(cfg))

	// First, we'll create the preferential attachment heuristic,
	// initialized with the passed auto pilot configuration parameters.
	//
	// TODO(roasbeef): switch here to dispatch specified heuristic
	minChanSize := svr.cc.wallet.Cfg.DefaultConstraints.DustLimit * 5
	prefAttachment := autopilot.NewConstrainedPrefAttachment(
		minChanSize, maxFundingAmount,
		uint16(cfg.MaxChannels), cfg.Allocation,
	)

	// With the heuristic itself created, we can now populate the remainder
	// of the items that the autopilot agent needs to perform its duties.
	self := svr.identityPriv.PubKey()
	pilotCfg := autopilot.Config{
		Self:           self,
		Heuristic:      prefAttachment,
		ChanController: &chanController{svr},
		WalletBalance: func() (btcutil.Amount, error) {
			return svr.cc.wallet.ConfirmedBalance(1, true)
		},
		Graph: autopilot.ChannelGraphFromDatabase(svr.chanDB.ChannelGraph()),
	}

	// Next, we'll fetch the current state of open channels from the
	// database to use as initial state for the auto-pilot agent.
	activeChannels, err := svr.chanDB.FetchAllChannels()
	if err != nil {
		return nil, err
	}
	initialChanState := make([]autopilot.Channel, len(activeChannels))
	for i, channel := range activeChannels {
		initialChanState[i] = autopilot.Channel{
			ChanID:   channel.ShortChanID,
			Capacity: channel.Capacity,
			Node:     autopilot.NewNodeID(channel.IdentityPub),
		}
	}

	// Now that we have all the initial dependencies, we can create the
	// auto-pilot instance itself.
	pilot, err := autopilot.New(pilotCfg, initialChanState)
	if err != nil {
		return nil, err
	}

	// Finally, we'll need to subscribe to two things: incoming
	// transactions that modify the wallet's balance, and also any graph
	// topology updates.
	txnSubscription, err := svr.cc.wallet.SubscribeTransactions()
	if err != nil {
		return nil, err
	}
	graphSubscription, err := svr.chanRouter.SubscribeTopology()
	if err != nil {
		return nil, err
	}

	// We'll launch a goroutine to provide the agent with notifications
	// whenever the balance of the wallet changes.
	svr.wg.Add(1)
	go func() {
		defer txnSubscription.Cancel()
		defer svr.wg.Done()

		for {
			select {
			case txnUpdate := <-txnSubscription.ConfirmedTransactions():
				pilot.OnBalanceChange(txnUpdate.Value)
			case <-svr.quit:
				return
			}
		}

	}()

	// We'll also launch a goroutine to provide the agent with
	// notifications for when the graph topology controlled by the node
	// changes.
	svr.wg.Add(1)
	go func() {
		defer graphSubscription.Cancel()
		defer svr.wg.Done()

		for {
			select {
			case topChange, ok := <-graphSubscription.TopologyChanges:
				// If the router is shutting down, then we will
				// as well.
				if !ok {
					return
				}

				for _, edgeUpdate := range topChange.ChannelEdgeUpdates {
					// If this isn't an advertisement by
					// the backing lnd node, then we'll
					// continue as we only want to add
					// channels that we've created
					// ourselves.
					if !edgeUpdate.AdvertisingNode.IsEqual(self) {
						continue
					}

					// If this is indeed a channel we
					// opened, then we'll convert it to the
					// autopilot.Channel format, and notify
					// the pilot of the new channel.
					chanNode := autopilot.NewNodeID(
						edgeUpdate.ConnectingNode,
					)
					chanID := lnwire.NewShortChanIDFromInt(
						edgeUpdate.ChanID,
					)
					edge := autopilot.Channel{
						ChanID:   chanID,
						Capacity: edgeUpdate.Capacity,
						Node:     chanNode,
					}
					pilot.OnChannelOpen(edge)
				}

				// For each closed closed channel, we'll obtain
				// the chanID of the closed channel and send it
				// to the pilot.
				for _, chanClose := range topChange.ClosedChannels {
					chanID := lnwire.NewShortChanIDFromInt(
						chanClose.ChanID,
					)

					pilot.OnChannelClose(chanID)
				}

			case <-svr.quit:
				return
			}
		}
	}()

	return pilot, nil
}
<|endoftext|>


<commit_before>// Copyright 2013, zhangpeihao All rights reserved.

package rtmp

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"github.com/zhangpeihao/goamf"
	"net"
	"time"
)

const (
	OUTBOUND_CONN_STATUS_CLOSE            = uint(0)
	OUTBOUND_CONN_STATUS_HANDSHAKE_OK     = uint(1)
	OUTBOUND_CONN_STATUS_CONNECT          = uint(2)
	OUTBOUND_CONN_STATUS_CONNECT_OK       = uint(3)
	OUTBOUND_CONN_STATUS_CREATE_STREAM    = uint(4)
	OUTBOUND_CONN_STATUS_CREATE_STREAM_OK = uint(5)
)

// A handler for outbound connection
type OutboundConnHandler interface {
	ConnHandler
	// When connection status changed
	OnStatus()
	// On stream created
	OnStreamCreated(stream OutboundStream)
}

type OutboundConn interface {
	// Connect an appliction on FMS after handshake.
	Connect(extendedParameters ...interface{}) (err error)
	// Create a stream
	CreateStream() (err error)
	// Close a connection
	Close()
	// URL to connect
	URL() string
	// Connection status
	Status() (uint, error)
	// Send a message
	Send(message *Message) error
	// Calls a command or method on Flash Media Server
	// or on an application server running Flash Remoting.
	Call(customParameters ...interface{}) (err error)
	// Get network connect instance
	Conn() Conn
}

// High-level interface
//
// A RTMP connection(based on TCP) to RTMP server(FMS or crtmpserver).
// In one connection, we can create many chunk streams.
type outboundConn struct {
	url              string
	rtmpURL          RtmpURL
	status           uint
	err              error
	handler          OutboundConnHandler
	conn             Conn
	transactions     map[uint32]string
	streams          map[uint32]OutboundStream
	maxChannelNumber int
}

// Connect to FMS server, and finish handshake process
func Dial(url string, handler OutboundConnHandler, maxChannelNumber int) (OutboundConn, error) {
	rtmpURL, err := ParseURL(url)
	if err != nil {
		return nil, err
	}
	if rtmpURL.protocol != "rtmp" {
		return nil, errors.New(fmt.Sprintf("Unsupport protocol %s", rtmpURL.protocol))
	}
	c, err := net.Dial("tcp", fmt.Sprintf("%s:%d", rtmpURL.host, rtmpURL.port))
	if err != nil {
		return nil, err
	}

	ipConn, ok := c.(*net.TCPConn)
	if ok {
		ipConn.SetWriteBuffer(128 * 1024)
	}
	br := bufio.NewReader(c)
	bw := bufio.NewWriter(c)
	timeout := time.Duration(10) * time.Second
	err = Handshake(c, br, bw, timeout)
	//err = HandshakeSample(c, br, bw, timeout)
	if err == nil {
		fmt.Println("Handshake OK")

		obConn := &outboundConn{
			url:              url,
			rtmpURL:          rtmpURL,
			handler:          handler,
			status:           OUTBOUND_CONN_STATUS_HANDSHAKE_OK,
			transactions:     make(map[uint32]string),
			streams:          make(map[uint32]OutboundStream),
			maxChannelNumber: maxChannelNumber,
		}
		obConn.conn = NewConn(c, br, bw, obConn, obConn.maxChannelNumber)
		return obConn, nil
	}

	return nil, err
}

// Connect to FMS server, and finish handshake process
func NewOutbounConn(c net.Conn, url string, handler OutboundConnHandler, maxChannelNumber int) (OutboundConn, error) {
	rtmpURL, err := ParseURL(url)
	if err != nil {
		return nil, err
	}
	if rtmpURL.protocol != "rtmp" {
		return nil, errors.New(fmt.Sprintf("Unsupport protocol %s", rtmpURL.protocol))
	}
	/*
		ipConn, ok := c.(*net.TCPConn)
		if ok {
			ipConn.SetWriteBuffer(128 * 1024)
		}
	*/
	br := bufio.NewReader(c)
	bw := bufio.NewWriter(c)
	obConn := &outboundConn{
		url:              url,
		rtmpURL:          rtmpURL,
		handler:          handler,
		status:           OUTBOUND_CONN_STATUS_HANDSHAKE_OK,
		transactions:     make(map[uint32]string),
		streams:          make(map[uint32]OutboundStream),
		maxChannelNumber: maxChannelNumber,
	}
	obConn.conn = NewConn(c, br, bw, obConn, obConn.maxChannelNumber)
	return obConn, nil
}

// Connect an appliction on FMS after handshake.
func (obConn *outboundConn) Connect(extendedParameters ...interface{}) (err error) {
	defer func() {
		if r := recover(); r != nil {
			err = r.(error)
			if obConn.err == nil {
				obConn.err = err
			}
		}
	}()
	// Create connect command
	buf := new(bytes.Buffer)
	// Command name
	_, err = amf.WriteString(buf, "connect")
	CheckError(err, "Connect() Write name: connect")
	transactionID := obConn.conn.NewTransactionID()
	obConn.transactions[transactionID] = "connect"
	_, err = amf.WriteDouble(buf, float64(transactionID))
	CheckError(err, "Connect() Write transaction ID")
	_, err = amf.WriteObjectMarker(buf)
	CheckError(err, "Connect() Write object marker")

	_, err = amf.WriteObjectName(buf, "app")
	CheckError(err, "Connect() Write app name")
	_, err = amf.WriteString(buf, obConn.rtmpURL.App())
	CheckError(err, "Connect() Write app value")

	_, err = amf.WriteObjectName(buf, "flashver")
	CheckError(err, "Connect() Write flashver name")
	_, err = amf.WriteString(buf, FLASH_PLAYER_VERSION_STRING)
	CheckError(err, "Connect() Write flashver value")

	//	_, err = amf.WriteObjectName(buf, "swfUrl")
	//	CheckError(err, "Connect() Write swfUrl name")
	//	_, err = amf.WriteString(buf, SWF_URL_STRING)
	//	CheckError(err, "Connect() Write swfUrl value")

	_, err = amf.WriteObjectName(buf, "tcUrl")
	CheckError(err, "Connect() Write tcUrl name")
	_, err = amf.WriteString(buf, obConn.url)
	CheckError(err, "Connect() Write tcUrl value")

	_, err = amf.WriteObjectName(buf, "fpad")
	CheckError(err, "Connect() Write fpad name")
	_, err = amf.WriteBoolean(buf, false)
	CheckError(err, "Connect() Write fpad value")

	_, err = amf.WriteObjectName(buf, "capabilities")
	CheckError(err, "Connect() Write capabilities name")
	_, err = amf.WriteDouble(buf, DEFAULT_CAPABILITIES)
	CheckError(err, "Connect() Write capabilities value")

	_, err = amf.WriteObjectName(buf, "audioCodecs")
	CheckError(err, "Connect() Write audioCodecs name")
	_, err = amf.WriteDouble(buf, DEFAULT_AUDIO_CODECS)
	CheckError(err, "Connect() Write audioCodecs value")

	_, err = amf.WriteObjectName(buf, "vedioCodecs")
	CheckError(err, "Connect() Write vedioCodecs name")
	_, err = amf.WriteDouble(buf, DEFAULT_VIDEO_CODECS)
	CheckError(err, "Connect() Write vedioCodecs value")

	_, err = amf.WriteObjectName(buf, "vedioFunction")
	CheckError(err, "Connect() Write vedioFunction name")
	_, err = amf.WriteDouble(buf, float64(1))
	CheckError(err, "Connect() Write vedioFunction value")

	//	_, err = amf.WriteObjectName(buf, "pageUrl")
	//	CheckError(err, "Connect() Write pageUrl name")
	//	_, err = amf.WriteString(buf, PAGE_URL_STRING)
	//	CheckError(err, "Connect() Write pageUrl value")

	//_, err = amf.WriteObjectName(buf, "objectEncoding")
	//CheckError(err, "Connect() Write objectEncoding name")
	//_, err = amf.WriteDouble(buf, float64(amf.AMF0))
	//CheckError(err, "Connect() Write objectEncoding value")

	_, err = amf.WriteObjectEndMarker(buf)
	CheckError(err, "Connect() Write ObjectEndMarker")

	// extended parameters
	for _, param := range extendedParameters {
		_, err = amf.WriteValue(buf, param)
		CheckError(err, "Connect() Write extended parameters")
	}

	connectMessage := Message{
		ChunkStreamID: CS_ID_COMMAND,
		Type:          COMMAND_AMF0,
		Size:          uint32(buf.Len()),
		Buf:           buf,
	}
	connectMessage.Dump("connect")
	obConn.status = OUTBOUND_CONN_STATUS_CONNECT
	return obConn.conn.Send(&connectMessage)
}

// Close a connection
func (obConn *outboundConn) Close() {
	for _, stream := range obConn.streams {
		stream.Close()
	}
	time.Sleep(time.Second)
	obConn.status = OUTBOUND_CONN_STATUS_CLOSE
	obConn.conn.Close()
}

// URL to connect
func (obConn *outboundConn) URL() string {
	return obConn.url
}

// Connection status
func (obConn *outboundConn) Status() (uint, error) {
	return obConn.status, obConn.err
}

// Callback when recieved message. Audio & Video data
func (obConn *outboundConn) Received(message *Message) {
	stream, found := obConn.streams[message.StreamID]
	if found {
		if !stream.Received(message) {
			obConn.handler.Received(message)
		}
	} else {
		obConn.handler.Received(message)
	}
}

// Callback when recieved message.
func (obConn *outboundConn) ReceivedCommand(command *Command) {
	command.Dump()
	switch command.Name {
	case "_result":
		transaction, found := obConn.transactions[command.TransactionID]
		if found {
			switch transaction {
			case "connect":
				if command.Objects != nil && len(command.Objects) >= 2 {
					information, ok := command.Objects[1].(amf.Object)
					if ok {
						code, ok := information["code"]
						if ok && code == RESULT_CONNECT_OK {
							// Connect OK
							time.Sleep(time.Duration(200) * time.Millisecond)
							obConn.conn.SetWindowAcknowledgementSize()
							obConn.status = OUTBOUND_CONN_STATUS_CONNECT_OK
							obConn.handler.OnStatus()
							obConn.status = OUTBOUND_CONN_STATUS_CREATE_STREAM
							obConn.CreateStream()
						}
					}
				}
			case "createStream":
				if command.Objects != nil && len(command.Objects) >= 2 {
					streamID, ok := command.Objects[1].(float64)
					if ok {
						newChunkStream, err := obConn.conn.CreateMediaChunkStream()
						if err != nil {
							fmt.Println("outboundConn::ReceivedCommand() CreateMediaChunkStream err:", err)
							return
						}
						stream := &outboundStream{
							id:            uint32(streamID),
							conn:          obConn,
							chunkStreamID: newChunkStream.ID,
						}
						obConn.streams[stream.ID()] = stream
						obConn.status = OUTBOUND_CONN_STATUS_CREATE_STREAM_OK
						obConn.handler.OnStatus()
						obConn.handler.OnStreamCreated(stream)
					}
				}
			}
			delete(obConn.transactions, command.TransactionID)
		}
	case "_error":
		transaction, found := obConn.transactions[command.TransactionID]
		if found {
			fmt.Printf("Command(%d) %s error\n", command.TransactionID, transaction)
		} else {
			fmt.Printf("Command(%d) not been found\n", command.TransactionID)
		}
	case "onBWCheck":
	}
}

// Connection closed
func (obConn *outboundConn) Closed() {
	obConn.status = OUTBOUND_CONN_STATUS_CLOSE
	obConn.handler.OnStatus()
}

// Create a stream
func (obConn *outboundConn) CreateStream() (err error) {
	defer func() {
		if r := recover(); r != nil {
			err = r.(error)
			if obConn.err == nil {
				obConn.err = err
			}
		}
	}()
	// Create createStream command
	transactionID := obConn.conn.NewTransactionID()
	cmd := &Command{
		IsFlex:        false,
		Name:          "createStream",
		TransactionID: transactionID,
		Objects:       make([]interface{}, 1),
	}
	cmd.Objects[0] = nil
	buf := new(bytes.Buffer)
	err = cmd.Write(buf)
	CheckError(err, "createStream() Create command")
	obConn.transactions[transactionID] = "createStream"

	message := Message{
		ChunkStreamID: CS_ID_COMMAND,
		Type:          COMMAND_AMF0,
		Size:          uint32(buf.Len()),
		Buf:           buf,
	}
	message.Dump("createStream")
	return obConn.conn.Send(&message)
}

// Send a message
func (obConn *outboundConn) Send(message *Message) error {
	return obConn.conn.Send(message)
}

// Calls a command or method on Flash Media Server
// or on an application server running Flash Remoting.
func (obConn *outboundConn) Call(customParameters ...interface{}) (err error) {
	return errors.New("Unimplemented")
}

// Get network connect instance
func (obConn *outboundConn) Conn() Conn {
	return obConn.conn
}
<commit_msg>Fixed bug for video<commit_after>// Copyright 2013, zhangpeihao All rights reserved.

package rtmp

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"github.com/zhangpeihao/goamf"
	"net"
	"time"
)

const (
	OUTBOUND_CONN_STATUS_CLOSE            = uint(0)
	OUTBOUND_CONN_STATUS_HANDSHAKE_OK     = uint(1)
	OUTBOUND_CONN_STATUS_CONNECT          = uint(2)
	OUTBOUND_CONN_STATUS_CONNECT_OK       = uint(3)
	OUTBOUND_CONN_STATUS_CREATE_STREAM    = uint(4)
	OUTBOUND_CONN_STATUS_CREATE_STREAM_OK = uint(5)
)

// A handler for outbound connection
type OutboundConnHandler interface {
	ConnHandler
	// When connection status changed
	OnStatus()
	// On stream created
	OnStreamCreated(stream OutboundStream)
}

type OutboundConn interface {
	// Connect an appliction on FMS after handshake.
	Connect(extendedParameters ...interface{}) (err error)
	// Create a stream
	CreateStream() (err error)
	// Close a connection
	Close()
	// URL to connect
	URL() string
	// Connection status
	Status() (uint, error)
	// Send a message
	Send(message *Message) error
	// Calls a command or method on Flash Media Server
	// or on an application server running Flash Remoting.
	Call(customParameters ...interface{}) (err error)
	// Get network connect instance
	Conn() Conn
}

// High-level interface
//
// A RTMP connection(based on TCP) to RTMP server(FMS or crtmpserver).
// In one connection, we can create many chunk streams.
type outboundConn struct {
	url              string
	rtmpURL          RtmpURL
	status           uint
	err              error
	handler          OutboundConnHandler
	conn             Conn
	transactions     map[uint32]string
	streams          map[uint32]OutboundStream
	maxChannelNumber int
}

// Connect to FMS server, and finish handshake process
func Dial(url string, handler OutboundConnHandler, maxChannelNumber int) (OutboundConn, error) {
	rtmpURL, err := ParseURL(url)
	if err != nil {
		return nil, err
	}
	if rtmpURL.protocol != "rtmp" {
		return nil, errors.New(fmt.Sprintf("Unsupport protocol %s", rtmpURL.protocol))
	}
	c, err := net.Dial("tcp", fmt.Sprintf("%s:%d", rtmpURL.host, rtmpURL.port))
	if err != nil {
		return nil, err
	}

	ipConn, ok := c.(*net.TCPConn)
	if ok {
		ipConn.SetWriteBuffer(128 * 1024)
	}
	br := bufio.NewReader(c)
	bw := bufio.NewWriter(c)
	timeout := time.Duration(10) * time.Second
	err = Handshake(c, br, bw, timeout)
	//err = HandshakeSample(c, br, bw, timeout)
	if err == nil {
		fmt.Println("Handshake OK")

		obConn := &outboundConn{
			url:              url,
			rtmpURL:          rtmpURL,
			handler:          handler,
			status:           OUTBOUND_CONN_STATUS_HANDSHAKE_OK,
			transactions:     make(map[uint32]string),
			streams:          make(map[uint32]OutboundStream),
			maxChannelNumber: maxChannelNumber,
		}
		obConn.conn = NewConn(c, br, bw, obConn, obConn.maxChannelNumber)
		return obConn, nil
	}

	return nil, err
}

// Connect to FMS server, and finish handshake process
func NewOutbounConn(c net.Conn, url string, handler OutboundConnHandler, maxChannelNumber int) (OutboundConn, error) {
	rtmpURL, err := ParseURL(url)
	if err != nil {
		return nil, err
	}
	if rtmpURL.protocol != "rtmp" {
		return nil, errors.New(fmt.Sprintf("Unsupport protocol %s", rtmpURL.protocol))
	}
	/*
		ipConn, ok := c.(*net.TCPConn)
		if ok {
			ipConn.SetWriteBuffer(128 * 1024)
		}
	*/
	br := bufio.NewReader(c)
	bw := bufio.NewWriter(c)
	obConn := &outboundConn{
		url:              url,
		rtmpURL:          rtmpURL,
		handler:          handler,
		status:           OUTBOUND_CONN_STATUS_HANDSHAKE_OK,
		transactions:     make(map[uint32]string),
		streams:          make(map[uint32]OutboundStream),
		maxChannelNumber: maxChannelNumber,
	}
	obConn.conn = NewConn(c, br, bw, obConn, obConn.maxChannelNumber)
	return obConn, nil
}

// Connect an appliction on FMS after handshake.
func (obConn *outboundConn) Connect(extendedParameters ...interface{}) (err error) {
	defer func() {
		if r := recover(); r != nil {
			err = r.(error)
			if obConn.err == nil {
				obConn.err = err
			}
		}
	}()
	// Create connect command
	buf := new(bytes.Buffer)
	// Command name
	_, err = amf.WriteString(buf, "connect")
	CheckError(err, "Connect() Write name: connect")
	transactionID := obConn.conn.NewTransactionID()
	obConn.transactions[transactionID] = "connect"
	_, err = amf.WriteDouble(buf, float64(transactionID))
	CheckError(err, "Connect() Write transaction ID")
	_, err = amf.WriteObjectMarker(buf)
	CheckError(err, "Connect() Write object marker")

	_, err = amf.WriteObjectName(buf, "app")
	CheckError(err, "Connect() Write app name")
	_, err = amf.WriteString(buf, obConn.rtmpURL.App())
	CheckError(err, "Connect() Write app value")

	_, err = amf.WriteObjectName(buf, "flashVer")
	CheckError(err, "Connect() Write flashver name")
	_, err = amf.WriteString(buf, FLASH_PLAYER_VERSION_STRING)
	CheckError(err, "Connect() Write flashver value")

	//	_, err = amf.WriteObjectName(buf, "swfUrl")
	//	CheckError(err, "Connect() Write swfUrl name")
	//	_, err = amf.WriteString(buf, SWF_URL_STRING)
	//	CheckError(err, "Connect() Write swfUrl value")

	_, err = amf.WriteObjectName(buf, "tcUrl")
	CheckError(err, "Connect() Write tcUrl name")
	_, err = amf.WriteString(buf, obConn.url)
	CheckError(err, "Connect() Write tcUrl value")

	_, err = amf.WriteObjectName(buf, "fpad")
	CheckError(err, "Connect() Write fpad name")
	_, err = amf.WriteBoolean(buf, false)
	CheckError(err, "Connect() Write fpad value")

	_, err = amf.WriteObjectName(buf, "capabilities")
	CheckError(err, "Connect() Write capabilities name")
	_, err = amf.WriteDouble(buf, DEFAULT_CAPABILITIES)
	CheckError(err, "Connect() Write capabilities value")

	_, err = amf.WriteObjectName(buf, "audioCodecs")
	CheckError(err, "Connect() Write audioCodecs name")
	_, err = amf.WriteDouble(buf, DEFAULT_AUDIO_CODECS)
	CheckError(err, "Connect() Write audioCodecs value")

	_, err = amf.WriteObjectName(buf, "videoCodecs")
	CheckError(err, "Connect() Write videoCodecs name")
	_, err = amf.WriteDouble(buf, DEFAULT_VIDEO_CODECS)
	CheckError(err, "Connect() Write videoCodecs value")

	_, err = amf.WriteObjectName(buf, "videoFunction")
	CheckError(err, "Connect() Write videoFunction name")
	_, err = amf.WriteDouble(buf, float64(1))
	CheckError(err, "Connect() Write videoFunction value")

	//	_, err = amf.WriteObjectName(buf, "pageUrl")
	//	CheckError(err, "Connect() Write pageUrl name")
	//	_, err = amf.WriteString(buf, PAGE_URL_STRING)
	//	CheckError(err, "Connect() Write pageUrl value")

	//_, err = amf.WriteObjectName(buf, "objectEncoding")
	//CheckError(err, "Connect() Write objectEncoding name")
	//_, err = amf.WriteDouble(buf, float64(amf.AMF0))
	//CheckError(err, "Connect() Write objectEncoding value")

	_, err = amf.WriteObjectEndMarker(buf)
	CheckError(err, "Connect() Write ObjectEndMarker")

	// extended parameters
	for _, param := range extendedParameters {
		_, err = amf.WriteValue(buf, param)
		CheckError(err, "Connect() Write extended parameters")
	}

	connectMessage := Message{
		ChunkStreamID: CS_ID_COMMAND,
		Type:          COMMAND_AMF0,
		Size:          uint32(buf.Len()),
		Buf:           buf,
	}
	connectMessage.Dump("connect")
	obConn.status = OUTBOUND_CONN_STATUS_CONNECT
	return obConn.conn.Send(&connectMessage)
}

// Close a connection
func (obConn *outboundConn) Close() {
	for _, stream := range obConn.streams {
		stream.Close()
	}
	time.Sleep(time.Second)
	obConn.status = OUTBOUND_CONN_STATUS_CLOSE
	obConn.conn.Close()
}

// URL to connect
func (obConn *outboundConn) URL() string {
	return obConn.url
}

// Connection status
func (obConn *outboundConn) Status() (uint, error) {
	return obConn.status, obConn.err
}

// Callback when recieved message. Audio & Video data
func (obConn *outboundConn) Received(message *Message) {
	stream, found := obConn.streams[message.StreamID]
	if found {
		if !stream.Received(message) {
			obConn.handler.Received(message)
		}
	} else {
		obConn.handler.Received(message)
	}
}

// Callback when recieved message.
func (obConn *outboundConn) ReceivedCommand(command *Command) {
	command.Dump()
	switch command.Name {
	case "_result":
		transaction, found := obConn.transactions[command.TransactionID]
		if found {
			switch transaction {
			case "connect":
				if command.Objects != nil && len(command.Objects) >= 2 {
					information, ok := command.Objects[1].(amf.Object)
					if ok {
						code, ok := information["code"]
						if ok && code == RESULT_CONNECT_OK {
							// Connect OK
							//time.Sleep(time.Duration(200) * time.Millisecond)
							obConn.conn.SetWindowAcknowledgementSize()
							obConn.status = OUTBOUND_CONN_STATUS_CONNECT_OK
							obConn.handler.OnStatus()
							obConn.status = OUTBOUND_CONN_STATUS_CREATE_STREAM
							obConn.CreateStream()
						}
					}
				}
			case "createStream":
				if command.Objects != nil && len(command.Objects) >= 2 {
					streamID, ok := command.Objects[1].(float64)
					if ok {
						newChunkStream, err := obConn.conn.CreateMediaChunkStream()
						if err != nil {
							fmt.Println("outboundConn::ReceivedCommand() CreateMediaChunkStream err:", err)
							return
						}
						stream := &outboundStream{
							id:            uint32(streamID),
							conn:          obConn,
							chunkStreamID: newChunkStream.ID,
						}
						obConn.streams[stream.ID()] = stream
						obConn.status = OUTBOUND_CONN_STATUS_CREATE_STREAM_OK
						obConn.handler.OnStatus()
						obConn.handler.OnStreamCreated(stream)
					}
				}
			}
			delete(obConn.transactions, command.TransactionID)
		}
	case "_error":
		transaction, found := obConn.transactions[command.TransactionID]
		if found {
			fmt.Printf("Command(%d) %s error\n", command.TransactionID, transaction)
		} else {
			fmt.Printf("Command(%d) not been found\n", command.TransactionID)
		}
	case "onBWCheck":
	}
}

// Connection closed
func (obConn *outboundConn) Closed() {
	obConn.status = OUTBOUND_CONN_STATUS_CLOSE
	obConn.handler.OnStatus()
}

// Create a stream
func (obConn *outboundConn) CreateStream() (err error) {
	defer func() {
		if r := recover(); r != nil {
			err = r.(error)
			if obConn.err == nil {
				obConn.err = err
			}
		}
	}()
	// Create createStream command
	transactionID := obConn.conn.NewTransactionID()
	cmd := &Command{
		IsFlex:        false,
		Name:          "createStream",
		TransactionID: transactionID,
		Objects:       make([]interface{}, 1),
	}
	cmd.Objects[0] = nil
	buf := new(bytes.Buffer)
	err = cmd.Write(buf)
	CheckError(err, "createStream() Create command")
	obConn.transactions[transactionID] = "createStream"

	message := Message{
		ChunkStreamID: CS_ID_COMMAND,
		Type:          COMMAND_AMF0,
		Size:          uint32(buf.Len()),
		Buf:           buf,
	}
	message.Dump("createStream")
	return obConn.conn.Send(&message)
}

// Send a message
func (obConn *outboundConn) Send(message *Message) error {
	return obConn.conn.Send(message)
}

// Calls a command or method on Flash Media Server
// or on an application server running Flash Remoting.
func (obConn *outboundConn) Call(customParameters ...interface{}) (err error) {
	return errors.New("Unimplemented")
}

// Get network connect instance
func (obConn *outboundConn) Conn() Conn {
	return obConn.conn
}
<|endoftext|>


<commit_before>package gaeamqp

import (
	"fmt"
	"net"
	"time"

	"github.com/streadway/amqp"

	"appengine"
	"appengine/socket"
)

const (
	defaultHeartbeat         = 10 * time.Second
	defaultConnectionTimeout = 30 * time.Second
)

func dial(c appengine.Context, url string) (*amqp.Connection, error) {
	return amqp.DialConfig(url, amqp.Config{
		Heartbeat: defaultHeartbeat,
		Dial: func(network, addr string) (net.Conn, error) {
			conn, err := socket.DialTimeout(c, network, addr, defaultConnectionTimeout)
			if err != nil {
				return nil, err
			}

			if err := conn.SetReadDeadline(time.Now().Add(defaultConnectionTimeout)); err != nil {
				return nil, err
			}

			return conn, nil
		},
	})
}

type context interface {
	appengine.Context

	GetAMQPAuthority() string
}

func Publish(c context, message string) (err error) {
	conn, err := dial(c, fmt.Sprintf("amqp://%s/", c.GetAMQPAuthority()))
	if err != nil {
		c.Errorf("Failed to connect to RabbitMQ")
		return err
	}
	defer conn.Close()

	ch, err := conn.Channel()
	if err != nil {
		c.Errorf("Failed to open a channel")
		return err
	}
	defer ch.Close()

	q, err := ch.QueueDeclare(
		"hello",
		false,
		true,
		false,
		false,
		nil,
	)
	if err != nil {
		c.Errorf("Failed to declare a queue")
		return err
	}

	err = ch.Publish(
		"",
		q.Name,
		false,
		false,
		amqp.Publishing{
			ContentType: "text/plain",
			Body:        []byte(message),
		})
	if err != nil {
		c.Errorf("Failed to publish a message")
		return err
	}

	return nil
}
<commit_msg>Simplified GAEAMQP.<commit_after>package gaeamqp

import (
	"net"
	"time"

	"github.com/streadway/amqp"

	"appengine"
	"appengine/socket"
)

const (
	defaultHeartbeat         = 10 * time.Second
	defaultConnectionTimeout = 30 * time.Second
)

func Dial(c appengine.Context, url string) (*amqp.Connection, error) {
	return amqp.DialConfig(url, amqp.Config{
		Heartbeat: defaultHeartbeat,
		Dial: func(network, addr string) (net.Conn, error) {
			conn, err := socket.DialTimeout(c, network, addr, defaultConnectionTimeout)
			if err != nil {
				return nil, err
			}

			if err := conn.SetReadDeadline(time.Now().Add(defaultConnectionTimeout)); err != nil {
				return nil, err
			}

			return conn, nil
		},
	})
}
<|endoftext|>


<commit_before>// +build gotask

package main

import (
	"fmt"
	"github.com/jingweno/gotask/tasking"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
)

// NAME
//    install-deps - install dependencies with go get
//
// DESCRIPTION
//    Install dependencies with go get.
func TaskInstallDeps(t *tasking.T) {
	deps := []string{
		"github.com/laher/goxc",
	}

	for _, dep := range deps {
		t.Logf("Installing %s\n", dep)
		err := t.Exec("go get", dep)
		if err != nil {
			t.Fatalf("Can't download dependency %s", err)
		}
	}
}

// NAME
//    package - cross compile gh and package it
//
// DESCRIPTION
//    Cross compile gh and package it into PWD/target
func TaskPackage(t *tasking.T) {
	gopath, err := ioutil.TempDir("", "gh-build")
	os.Setenv("GOPATH", gopath)
	t.Logf("GOPATH=%s\n", gopath)

	path := fmt.Sprintf("%s%c%s", filepath.Join(gopath, "bin"), os.PathListSeparator, os.Getenv("PATH"))
	os.Setenv("PATH", path)
	t.Logf("PATH=%s\n", path)

	t.Logf("Packaging for %s...\n", runtime.GOOS)

	t.Log("Installing dependencies...")
	TaskInstallDeps(t)

	pwd, err := os.Getwd()
	if err != nil {
		t.Fatal(err)
	}
	ghPath := filepath.Join(gopath, "src", "github.com", "jingweno", "gh")
	t.Logf("Copying source from %s to %s\n", pwd, ghPath)
	err = copyDir(pwd, ghPath)
	if err != nil {
		t.Fatal(err)
	}
	err = os.Chdir(ghPath)
	if err != nil {
		t.Fatal(err)
	}

	t.Log("Cross-compiling...")
	godepPath := filepath.Join(ghPath, "Godeps", "_workspace")
	gopath = fmt.Sprintf("%s%c%s", gopath, os.PathListSeparator, godepPath)
	os.Setenv("GOPATH", gopath)
	TaskCrossCompile(t)

	source := filepath.Join(ghPath, "target")
	target := filepath.Join(pwd, "target")
	t.Logf("Copying build artifacts from %s to %s...\n", source, target)
	_, err = os.Stat(target)
	if err != nil {
		err = os.Mkdir(target, 0777)
		if err != nil {
			t.Fatal(err)
		}
	}
	err = copyBuildArtifacts(source, target)
	if err != nil {
		t.Fatal(err)
	}
}

// NAME
//    cross-compile - cross-compiles gh for current platform.
//
// DESCRIPTION
//    Cross-compiles gh for current platform. Build artifacts will be in target/VERSION
func TaskCrossCompile(t *tasking.T) {
	t.Logf("Cross-compiling gh for %s...\n", runtime.GOOS)
	t.Logf("GOPATH=%s\n", os.Getenv("GOPATH"))
	err := t.Exec("goxc", "-wd=.", "-os="+runtime.GOOS, "-c="+runtime.GOOS)
	if err != nil {
		t.Fatalf("Can't cross-compile gh: %s\n", err)
	}
}

func copyBuildArtifacts(srcDir, destDir string) error {
	artifacts := findBuildArtifacts(srcDir)
	for _, artifact := range artifacts {
		target := filepath.Join(destDir, filepath.Base(artifact))
		fmt.Printf("Copying %s to %s\n", artifact, target)
		err := copyFile(artifact, target)
		if err != nil {
			return err
		}
	}

	return nil
}

func copyFile(source, dest string) error {
	sf, err := os.Open(source)
	if err != nil {
		return err
	}
	defer sf.Close()

	df, err := os.Create(dest)
	if err != nil {
		return err
	}
	defer df.Close()

	_, err = io.Copy(df, sf)

	if err == nil {
		si, err := os.Stat(source)
		if err != nil {
			err = os.Chmod(dest, si.Mode())
		}
	}

	return err
}

func copyDir(source, dest string) (err error) {
	fi, err := os.Stat(source)
	if err != nil {
		return err
	}

	if !fi.IsDir() {
		return fmt.Errorf("Source is not a directory")
	}

	_, err = os.Open(dest)
	if !os.IsNotExist(err) {
		return fmt.Errorf("Destination already exists")
	}

	err = os.MkdirAll(dest, fi.Mode())
	if err != nil {
		return err
	}

	entries, err := ioutil.ReadDir(source)
	for _, entry := range entries {
		sfp := filepath.Join(source, entry.Name())
		dfp := filepath.Join(dest, entry.Name())
		if entry.IsDir() {
			err = copyDir(sfp, dfp)
			if err != nil {
				return err
			}
		} else {
			err = copyFile(sfp, dfp)
			if err != nil {
				return err
			}
		}
	}

	return
}

func findBuildArtifacts(root string) (artifacts []string) {
	filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
		ext := filepath.Ext(path)
		if ext == ".deb" || ext == ".zip" || ext == ".gz" {
			artifacts = append(artifacts, path)
		}

		return nil
	})

	return
}
<commit_msg>Build homebrew bottle<commit_after>// +build gotask

package main

import (
	"fmt"
	"github.com/jingweno/gotask/tasking"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
)

// NAME
//    install-deps - install dependencies with go get
//
// DESCRIPTION
//    Install dependencies with go get.
func TaskInstallDeps(t *tasking.T) {
	deps := []string{
		"github.com/laher/goxc",
	}

	for _, dep := range deps {
		t.Logf("Installing %s\n", dep)
		err := t.Exec("go get", dep)
		if err != nil {
			t.Fatalf("Can't download dependency %s", err)
		}
	}
}

// NAME
//    package - cross compile gh and package it
//
// DESCRIPTION
//    Cross compile gh and package it into PWD/target
func TaskPackage(t *tasking.T) {
	gopath, err := ioutil.TempDir("", "gh-build")
	os.Setenv("GOPATH", gopath)
	t.Logf("GOPATH=%s\n", gopath)

	path := fmt.Sprintf("%s%c%s", filepath.Join(gopath, "bin"), os.PathListSeparator, os.Getenv("PATH"))
	os.Setenv("PATH", path)
	t.Logf("PATH=%s\n", path)

	t.Logf("Packaging for %s...\n", runtime.GOOS)

	t.Log("Installing dependencies...")
	TaskInstallDeps(t)

	pwd, err := os.Getwd()
	if err != nil {
		t.Fatal(err)
	}

	ghPath := filepath.Join(gopath, "src", "github.com", "jingweno", "gh")
	t.Logf("Copying source from %s to %s\n", pwd, ghPath)
	err = copyDir(pwd, ghPath)
	if err != nil {
		t.Fatal(err)
	}
	err = os.Chdir(ghPath)
	if err != nil {
		t.Fatal(err)
	}

	t.Log("Cross-compiling...")
	godepPath := filepath.Join(ghPath, "Godeps", "_workspace")
	gopath = fmt.Sprintf("%s%c%s", gopath, os.PathListSeparator, godepPath)
	os.Setenv("GOPATH", gopath)
	TaskCrossCompile(t)

	source := filepath.Join(ghPath, "target")
	target := filepath.Join(pwd, "target")
	t.Logf("Copying build artifacts from %s to %s...\n", source, target)
	_, err = os.Stat(target)
	if err != nil {
		err = os.Mkdir(target, 0777)
		if err != nil {
			t.Fatal(err)
		}
	}
	err = copyBuildArtifacts(source, target)
	if err != nil {
		t.Fatal(err)
	}

	buildHomebrewBottle(t, target)
}

func buildHomebrewBottle(t *tasking.T, target string) {
	err := t.Exec("brew", "list", "gh")
	if err == nil {
		err := t.Exec("brew", "uninstall", "gh")
		if err != nil {
			t.Fatal(err)
		}
	}

	err = t.Exec("brew", "install", "--build-from-source", "--build-bottle", "gh")
	if err != nil {
		t.Fatal(err)
	}

	err = os.Chdir(target)
	if err != nil {
		t.Fatal(err)
	}

	err = t.Exec("brew", "bottle", "gh")
	if err != nil {
		t.Fatal(err)
	}
}

// NAME
//    cross-compile - cross-compiles gh for current platform.
//
// DESCRIPTION
//    Cross-compiles gh for current platform. Build artifacts will be in target/VERSION
func TaskCrossCompile(t *tasking.T) {
	t.Logf("Cross-compiling gh for %s...\n", runtime.GOOS)
	t.Logf("GOPATH=%s\n", os.Getenv("GOPATH"))
	err := t.Exec("goxc", "-wd=.", "-os="+runtime.GOOS, "-c="+runtime.GOOS)
	if err != nil {
		t.Fatalf("Can't cross-compile gh: %s\n", err)
	}
}

func copyBuildArtifacts(srcDir, destDir string) error {
	artifacts := findBuildArtifacts(srcDir)
	for _, artifact := range artifacts {
		target := filepath.Join(destDir, filepath.Base(artifact))
		fmt.Printf("Copying %s to %s\n", artifact, target)
		err := copyFile(artifact, target)
		if err != nil {
			return err
		}
	}

	return nil
}

func copyFile(source, dest string) error {
	sf, err := os.Open(source)
	if err != nil {
		return err
	}
	defer sf.Close()

	df, err := os.Create(dest)
	if err != nil {
		return err
	}
	defer df.Close()

	_, err = io.Copy(df, sf)

	if err == nil {
		si, err := os.Stat(source)
		if err != nil {
			err = os.Chmod(dest, si.Mode())
		}
	}

	return err
}

func copyDir(source, dest string) (err error) {
	fi, err := os.Stat(source)
	if err != nil {
		return err
	}

	if !fi.IsDir() {
		return fmt.Errorf("Source is not a directory")
	}

	_, err = os.Open(dest)
	if !os.IsNotExist(err) {
		return fmt.Errorf("Destination already exists")
	}

	err = os.MkdirAll(dest, fi.Mode())
	if err != nil {
		return err
	}

	entries, err := ioutil.ReadDir(source)
	for _, entry := range entries {
		sfp := filepath.Join(source, entry.Name())
		dfp := filepath.Join(dest, entry.Name())
		if entry.IsDir() {
			err = copyDir(sfp, dfp)
			if err != nil {
				return err
			}
		} else {
			err = copyFile(sfp, dfp)
			if err != nil {
				return err
			}
		}
	}

	return
}

func findBuildArtifacts(root string) (artifacts []string) {
	filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
		ext := filepath.Ext(path)
		if ext == ".deb" || ext == ".zip" || ext == ".gz" {
			artifacts = append(artifacts, path)
		}

		return nil
	})

	return
}
<|endoftext|>