<commit_before>package webserver

// wiki.go - manage the wikis served by this quiki

import (
	"errors"
	"log"
	"net/http"
	"path"
	"strings"

	"github.com/cooper/quiki/monitor"
	"github.com/cooper/quiki/wiki"
	"github.com/cooper/quiki/wikifier"
)

type wikiInfo struct {
	name     string // wiki shortname
	title    string // wiki title from @name in the wiki config
	logo     string
	host     string
	template wikiTemplate
	*wiki.Wiki
}

// all wikis served by this quiki
var wikis map[string]*wikiInfo

// initialize all the wikis in the configuration
func initWikis() error {

	// find wikis
	found, err := conf.Get("server.wiki")
	if err != nil {
		return err
	}
	wikiMap, ok := found.(*wikifier.Map)
	if !ok {
		return errors.New("server.wiki is not a map")
	}

	wikiNames := wikiMap.Keys()
	if len(wikiNames) == 0 {
		return errors.New("no wikis configured")
	}

	// set up each wiki
	wikis = make(map[string]*wikiInfo, len(wikiNames))
	for _, wikiName := range wikiNames {
		configPfx := "server.wiki." + wikiName

		// not enabled
		enable, _ := conf.GetBool(configPfx + ".enable")
		if !enable {
			continue
		}

		// host to accept (optional)
		wikiHost, _ := conf.GetStr(configPfx + ".host")

		// get wiki config path and password
		wikiConfPath, _ := conf.GetStr(configPfx + ".config")
		privConfPath, _ := conf.GetStr(configPfx + ".private")

		if wikiConfPath == "" {
			// config not specified, so use server.dir.wiki and wiki.conf
			dirWiki, err := conf.GetStr("server.dir.wiki")
			if err != nil {
				return err
			}
			wikiConfPath = dirWiki + "/" + wikiName + "/wiki.conf"
		}

		// create wiki
		w, err := wiki.NewWiki(wikiConfPath, privConfPath)
		if err != nil {
			return err
		}
		wi := &wikiInfo{Wiki: w, host: wikiHost, name: wikiName}

		// pregenerate
		w.Pregenerate()

		// monitor for changes
		go monitor.WatchWiki(w)

		// set up the wiki for webserver
		if err := setupWiki(wi); err != nil {
			return err
		}

		wikis[wikiName] = wi
	}

	// still no wikis?
	if len(wikis) == 0 {
		return errors.New("none of the configured wikis are enabled")
	}

	return nil
}

// initialize a wiki
func setupWiki(wi *wikiInfo) error {

	// if not configured, use default template
	templateNameOrPath := wi.Opt.Template
	if templateNameOrPath == "" {
		templateNameOrPath = "default"
	}

	// find the template
	var template wikiTemplate
	var err error
	if strings.Contains(templateNameOrPath, "/") {
		// if a path is given, try to load the template at this exact path
		template, err = loadTemplate(path.Base(templateNameOrPath), templateNameOrPath)
	} else {
		// otherwise, search template directories
		template, err = findTemplate(templateNameOrPath)
	}

	// couldn't find it, or an error occurred in loading it
	if err != nil {
		return err
	}
	wi.template = template

	// generate logo according to template
	logoInfo := wi.template.manifest.Logo
	logoName := wi.Opt.Logo
	if logoName != "" && (logoInfo.Width != 0 || logoInfo.Height != 0) {
		si := wiki.SizedImageFromName(logoName)
		si.Width = logoInfo.Width
		si.Height = logoInfo.Height
		res := wi.DisplaySizedImageGenerate(si, true)
		if di, ok := res.(wiki.DisplayImage); ok {
			log.Printf("[%s] generated logo: %s", wi.name, di.File)
			wi.logo = wi.Opt.Root.Image + "/" + di.File
		}
	}

	type wikiHandler struct {
		rootType string
		root     string
		handler  func(*wikiInfo, string, http.ResponseWriter, *http.Request)
	}

	wikiRoots := []wikiHandler{
		wikiHandler{
			rootType: "page",
			root:     wi.Opt.Root.Page,
			handler:  handlePage,
		},
		wikiHandler{
			rootType: "image",
			root:     wi.Opt.Root.Image,
			handler:  handleImage,
		},
		wikiHandler{
			rootType: "category",
			root:     wi.Opt.Root.Category,
			handler:  handleCategoryPosts,
		},
	}

	// setup handlers
	wikiRoot := wi.Opt.Root.Wiki
	for _, item := range wikiRoots {
		rootType, root, handler := item.rootType, item.root, item.handler

		// if it doesn't already have the wiki root as the prefix, add it
		if !strings.HasPrefix(root, wikiRoot) {
			log.Printf(
				"@root.%s (%s) is configured outside of @root.wiki (%s); assuming %s%s",
				rootType, root, wikiRoot, wikiRoot, root,
			)
			root = wikiRoot + root
		}

		root += "/"

		// add the real handler
		wi := wi // copy pointer so the handler below always refer to this one
		mux.HandleFunc(wi.host+root, func(w http.ResponseWriter, r *http.Request) {

			// determine the path relative to the root
			relPath := strings.TrimPrefix(r.URL.Path, root)
			if relPath == "" && rootType != "wiki" {
				http.NotFound(w, r)
				return
			}

			handler(wi, relPath, w, r)
		})

		log.Printf("[%s] registered %s root: %s", wi.name, rootType, wi.host+root)
	}

	// file server
	rootFile := wi.Opt.Root.File
	dirWiki := wi.Opt.Dir.Wiki
	if rootFile != "" && dirWiki != "" {
		rootFile += "/"
		fileServer := http.FileServer(http.Dir(dirWiki))
		mux.Handle(wi.host+rootFile, http.StripPrefix(rootFile, fileServer))
		log.Printf("[%s] registered file root: %s (%s)", wi.name, wi.host+rootFile, dirWiki)
	}

	// store the wiki info
	wi.title = wi.Opt.Name
	return nil
}
<commit_msg>explanation<commit_after>package webserver

// wiki.go - manage the wikis served by this quiki

import (
	"errors"
	"log"
	"net/http"
	"path"
	"strings"

	"github.com/cooper/quiki/monitor"
	"github.com/cooper/quiki/wiki"
	"github.com/cooper/quiki/wikifier"
)

type wikiInfo struct {
	name     string // wiki shortname
	title    string // wiki title from @name in the wiki config
	logo     string
	host     string
	template wikiTemplate
	*wiki.Wiki
}

// all wikis served by this quiki
var wikis map[string]*wikiInfo

// initialize all the wikis in the configuration
func initWikis() error {

	// find wikis
	found, err := conf.Get("server.wiki")
	if err != nil {
		return err
	}
	wikiMap, ok := found.(*wikifier.Map)
	if !ok {
		return errors.New("server.wiki is not a map")
	}

	wikiNames := wikiMap.Keys()
	if len(wikiNames) == 0 {
		return errors.New("no wikis configured")
	}

	// set up each wiki
	wikis = make(map[string]*wikiInfo, len(wikiNames))
	for _, wikiName := range wikiNames {
		configPfx := "server.wiki." + wikiName

		// not enabled
		enable, _ := conf.GetBool(configPfx + ".enable")
		if !enable {
			continue
		}

		// host to accept (optional)
		wikiHost, _ := conf.GetStr(configPfx + ".host")

		// get wiki config path and password
		wikiConfPath, _ := conf.GetStr(configPfx + ".config")
		privConfPath, _ := conf.GetStr(configPfx + ".private")

		if wikiConfPath == "" {
			// config not specified, so use server.dir.wiki and wiki.conf
			dirWiki, err := conf.GetStr("server.dir.wiki")
			if err != nil {
				return err
			}
			wikiConfPath = dirWiki + "/" + wikiName + "/wiki.conf"
		}

		// create wiki
		w, err := wiki.NewWiki(wikiConfPath, privConfPath)
		if err != nil {
			return err
		}
		wi := &wikiInfo{Wiki: w, host: wikiHost, name: wikiName}

		// pregenerate
		w.Pregenerate()

		// monitor for changes
		go monitor.WatchWiki(w)

		// set up the wiki for webserver
		if err := setupWiki(wi); err != nil {
			return err
		}

		wikis[wikiName] = wi
	}

	// still no wikis?
	if len(wikis) == 0 {
		return errors.New("none of the configured wikis are enabled")
	}

	return nil
}

// initialize a wiki
func setupWiki(wi *wikiInfo) error {

	// if not configured, use default template
	templateNameOrPath := wi.Opt.Template
	if templateNameOrPath == "" {
		templateNameOrPath = "default"
	}

	// find the template
	var template wikiTemplate
	var err error
	if strings.Contains(templateNameOrPath, "/") {
		// if a path is given, try to load the template at this exact path
		template, err = loadTemplate(path.Base(templateNameOrPath), templateNameOrPath)
	} else {
		// otherwise, search template directories
		template, err = findTemplate(templateNameOrPath)
	}

	// couldn't find it, or an error occurred in loading it
	if err != nil {
		return err
	}
	wi.template = template

	// generate logo according to template
	logoInfo := wi.template.manifest.Logo
	logoName := wi.Opt.Logo
	if logoName != "" && (logoInfo.Width != 0 || logoInfo.Height != 0) {
		si := wiki.SizedImageFromName(logoName)
		si.Width = logoInfo.Width
		si.Height = logoInfo.Height
		res := wi.DisplaySizedImageGenerate(si, true)
		if di, ok := res.(wiki.DisplayImage); ok {
			log.Printf("[%s] generated logo: %s", wi.name, di.File)
			wi.logo = wi.Opt.Root.Image + "/" + di.File
		} else {
			log.Printf("[%s] generate logo failed: %v", di)
		}
	}

	type wikiHandler struct {
		rootType string
		root     string
		handler  func(*wikiInfo, string, http.ResponseWriter, *http.Request)
	}

	wikiRoots := []wikiHandler{
		wikiHandler{
			rootType: "page",
			root:     wi.Opt.Root.Page,
			handler:  handlePage,
		},
		wikiHandler{
			rootType: "image",
			root:     wi.Opt.Root.Image,
			handler:  handleImage,
		},
		wikiHandler{
			rootType: "category",
			root:     wi.Opt.Root.Category,
			handler:  handleCategoryPosts,
		},
	}

	// setup handlers
	wikiRoot := wi.Opt.Root.Wiki
	for _, item := range wikiRoots {
		rootType, root, handler := item.rootType, item.root, item.handler

		// if it doesn't already have the wiki root as the prefix, add it
		if !strings.HasPrefix(root, wikiRoot) {
			log.Printf(
				"@root.%s (%s) is configured outside of @root.wiki (%s); assuming %s%s",
				rootType, root, wikiRoot, wikiRoot, root,
			)
			root = wikiRoot + root
		}

		root += "/"

		// add the real handler
		wi := wi // copy pointer so the handler below always refer to this one
		mux.HandleFunc(wi.host+root, func(w http.ResponseWriter, r *http.Request) {

			// determine the path relative to the root
			relPath := strings.TrimPrefix(r.URL.Path, root)
			if relPath == "" && rootType != "wiki" {
				http.NotFound(w, r)
				return
			}

			handler(wi, relPath, w, r)
		})

		log.Printf("[%s] registered %s root: %s", wi.name, rootType, wi.host+root)
	}

	// file server
	rootFile := wi.Opt.Root.File
	dirWiki := wi.Opt.Dir.Wiki
	if rootFile != "" && dirWiki != "" {
		rootFile += "/"
		fileServer := http.FileServer(http.Dir(dirWiki))
		mux.Handle(wi.host+rootFile, http.StripPrefix(rootFile, fileServer))
		log.Printf("[%s] registered file root: %s (%s)", wi.name, wi.host+rootFile, dirWiki)
	}

	// store the wiki info
	wi.title = wi.Opt.Name
	return nil
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"log"
	"os"
	"path/filepath"

	"github.com/Originate/exosphere/src/application"
	"github.com/Originate/exosphere/src/config"
	"github.com/Originate/exosphere/src/docker/composebuilder"
	"github.com/Originate/exosphere/src/types"
	"github.com/Originate/exosphere/src/util"
	"github.com/spf13/cobra"
)

var deployProfileFlag string
var deployServicesFlag bool

var deployCmd = &cobra.Command{
	Use:   "deploy",
	Short: "Deploys Exosphere application to the cloud",
	Long:  "Deploys Exosphere application to the cloud",
	Run: func(cmd *cobra.Command, args []string) {
		if printHelpIfNecessary(cmd, args) {
			return
		}
		fmt.Println("We are about to deploy an application!")
		appDir, err := os.Getwd()
		if err != nil {
			panic(err)
		}
		homeDir, err := util.GetHomeDirectory()
		if err != nil {
			panic(err)
		}
		appConfig, err := types.NewAppConfig(appDir)
		if err != nil {
			log.Fatalf("Cannot read application configuration: %s", err)
		}
		serviceConfigs, err := config.GetServiceConfigs(appDir, appConfig)
		if err != nil {
			log.Fatalf("Failed to read service configurations: %s", err)
		}
		awsConfig, err := getAwsConfig(deployProfileFlag)
		if err != nil {
			log.Fatalf("Failed to read secrest configurations: %s", err)
		}

		logger := util.NewLogger([]string{"exo-deploy"}, []string{}, "exo-deploy", os.Stdout)
		terraformDir := filepath.Join(appDir, "terraform")
		deployConfig := types.DeployConfig{
			AppConfig:                appConfig,
			ServiceConfigs:           serviceConfigs,
			AppDir:                   appDir,
			HomeDir:                  homeDir,
			DockerComposeProjectName: composebuilder.GetDockerComposeProjectName(appDir),
			Logger:             logger,
			TerraformDir:       terraformDir,
			SecretsPath:        filepath.Join(terraformDir, "secrets.tfvars"),
			AwsConfig:          awsConfig,
			DeployServicesOnly: deployServicesFlag,

			// git commit hash of the Terraform modules in Originate/exosphere we are using
			TerraformModulesRef: "e650982f",
		}

		err = application.StartDeploy(deployConfig)
		if err != nil {
			log.Fatalf("Deploy failed: %s", err)
		}
	},
}

func init() {
	RootCmd.AddCommand(deployCmd)
	deployCmd.PersistentFlags().StringVarP(&deployProfileFlag, "profile", "p", "default", "AWS profile to use")
	deployCmd.PersistentFlags().BoolVarP(&deployServicesFlag, "update-services", "", false, "Deploy changes to service images and env vars only")
}
<commit_msg>terraform: update module hash<commit_after>package cmd

import (
	"fmt"
	"log"
	"os"
	"path/filepath"

	"github.com/Originate/exosphere/src/application"
	"github.com/Originate/exosphere/src/config"
	"github.com/Originate/exosphere/src/docker/composebuilder"
	"github.com/Originate/exosphere/src/types"
	"github.com/Originate/exosphere/src/util"
	"github.com/spf13/cobra"
)

var deployProfileFlag string
var deployServicesFlag bool

var deployCmd = &cobra.Command{
	Use:   "deploy",
	Short: "Deploys Exosphere application to the cloud",
	Long:  "Deploys Exosphere application to the cloud",
	Run: func(cmd *cobra.Command, args []string) {
		if printHelpIfNecessary(cmd, args) {
			return
		}
		fmt.Println("We are about to deploy an application!")
		appDir, err := os.Getwd()
		if err != nil {
			panic(err)
		}
		homeDir, err := util.GetHomeDirectory()
		if err != nil {
			panic(err)
		}
		appConfig, err := types.NewAppConfig(appDir)
		if err != nil {
			log.Fatalf("Cannot read application configuration: %s", err)
		}
		serviceConfigs, err := config.GetServiceConfigs(appDir, appConfig)
		if err != nil {
			log.Fatalf("Failed to read service configurations: %s", err)
		}
		awsConfig, err := getAwsConfig(deployProfileFlag)
		if err != nil {
			log.Fatalf("Failed to read secrest configurations: %s", err)
		}

		logger := util.NewLogger([]string{"exo-deploy"}, []string{}, "exo-deploy", os.Stdout)
		terraformDir := filepath.Join(appDir, "terraform")
		deployConfig := types.DeployConfig{
			AppConfig:                appConfig,
			ServiceConfigs:           serviceConfigs,
			AppDir:                   appDir,
			HomeDir:                  homeDir,
			DockerComposeProjectName: composebuilder.GetDockerComposeProjectName(appDir),
			Logger:             logger,
			TerraformDir:       terraformDir,
			SecretsPath:        filepath.Join(terraformDir, "secrets.tfvars"),
			AwsConfig:          awsConfig,
			DeployServicesOnly: deployServicesFlag,

			// git commit hash of the Terraform modules in Originate/exosphere we are using
			TerraformModulesRef: "d5c193c5",
		}

		err = application.StartDeploy(deployConfig)
		if err != nil {
			log.Fatalf("Deploy failed: %s", err)
		}
	},
}

func init() {
	RootCmd.AddCommand(deployCmd)
	deployCmd.PersistentFlags().StringVarP(&deployProfileFlag, "profile", "p", "default", "AWS profile to use")
	deployCmd.PersistentFlags().BoolVarP(&deployServicesFlag, "update-services", "", false, "Deploy changes to service images and env vars only")
}
<|endoftext|>


<commit_before>package main

import (
  "strings"
  "log"
  // "fmt"
//  "regexp"
  "bytes"
  "encoding/json"
  "os"
  "time"
  "strconv"
  "regexp"
  "github.com/lookify/town/cluster"
  dockerapi "github.com/fsouza/go-dockerclient"
)

var (
  scaleNumRegexp, _ = regexp.Compile("\\$\\{SCALE_NUM:(.+)\\}")
//  SCALE_TOTAL_REG, _ = regexp.Compile("\\$\\{SCALE_NUM:(.+)\\}")
  hostsRegexp, _ = regexp.Compile("\\$\\{(.+)_HOSTS\\}")
)

// Town describe cluster and docker clients.
type Town struct {
  cluster *cluster.Cluster
  docker *dockerapi.Client // TODO change to multiple clients
}

// NewTown create new town with default values
func NewTown() *Town {
  return &Town{
    cluster: nil,
    docker: nil,
  }
}

// ReadFile - read town configuration fail in current direcotry or /etc/town (ext .yml)
func (t *Town) ReadFile(name string) {
  var pathLocs = [...]string{
    name + ".yml",
    "/etc/town/" + name + ".yml",
  }

  for _, path := range pathLocs {
    if _, err := os.Stat(path); err == nil {
      t.cluster = cluster.NewCluster(path)
      t.cluster.ReadFile()
      return
    }
  }

  log.Println("ERROR: Could not find file ", name, ".yml")
}

// Connect - connect to docker hosts.
func (t *Town) Connect() {
  endpoint := t.cluster.Application.Docker.Hosts[0] // at the moment use only first
  log.Println("Using Docker API endpont: ", endpoint)
  docker, err := dockerapi.NewClient( endpoint )
  if err != nil {
    log.Println("Can't connect to the docker")
  }
  t.docker = docker
}

// Provision running containers.
func (t *Town) Provision(checkChanged bool) {
  // update containers
  bool pull = true
  if len(t.cluster.Application.Docker.Repository) == 1 && t.cluster.Application.Docker.Repository[0] == "local" {
    pull = false
  }

  if pull {
    for _, node := range t.cluster.Nodes {
      var buf bytes.Buffer
      var image = strings.Split(node.Container.Image, ":")

      opts := dockerapi.PullImageOptions{
          Repository: image[0],
          // Registry:     "docker.tsuru.io",
          // Tag:          "latest",
          OutputStream: &buf,
      }

      if len(image) > 1 {
        opts.Tag = image[1]
      }
      err := t.docker.PullImage(opts, dockerapi.AuthConfiguration{});
      if err != nil {
        log.Println("Could not pull image ", image)
      }
    }
  }

  allContainers, err := t.docker.ListContainers(dockerapi.ListContainersOptions{
    All: true,
  })
  if err == nil {
    for _, listing := range allContainers {
      container, err := t.docker.InspectContainer(listing.ID)
      if err == nil {
        name := container.Name[1:]
        node, index := t.cluster.FindNodeByName(name)
        if node != nil && index > 0 {
          if node.Container.Exist == nil {
            node.Container.Exist = []cluster.ExistContainer{}
          }
          runningContainer := cluster.NewExistContainer(listing.ID, name, index, container.State.Running)
          runningContainer.Pid = container.State.Pid
          runningContainer.User = container.Config.User
          if checkChanged {
            node.Container.Changed = t.isChangedImage(node, container)
          } else {
            node.Container.Changed = true
          }
          node.Container.Exist = append(node.Container.Exist, runningContainer)
        }
      } else {
        log.Println("[ERROR] Unable to inspect container:", listing.ID[:12], err)
      }
    }

    if checkChanged {
      t.cluster.AddChangeDependant()
    }
  } else {
    log.Println("[ERROR] Can't start provision")
  }
}

// Info - print current cluster information.
func (t *Town) Info() {
  for i := len(t.cluster.Nodes) - 1; i >= 0; i-- {
    node := t.cluster.Nodes[i]
    log.Print("Node ", node.Container.Name, " image ")
	  if node.Container.Changed {
      log.Print("(Changed)")
    }
	  log.Println(": ", node.Container.Image)
    for _, container := range node.Container.Exist {
      log.Print("      ", container.Name, "\t")
  	  if container.Running {
  	    log.Println("Running")
  	  } else {
  	    log.Print("Stoped")
  	  }
    }
  }
}

/**
 * Check node and running container for changes.
 * TODO: add cache to docker call.
 **/
func (t *Town) isChangedImage(node *cluster.Node, container *dockerapi.Container) bool {
  var imageName = container.Image
  image , error := t.docker.InspectImage(imageName)
  if error == nil {
    secondImage , secondError := t.docker.InspectImage(node.Container.Image)
    if secondError == nil {
      return secondImage.Created.After(image.Created)
    }
  }
  log.Println("[ERROR] Could not inspect image ", node.Container.Name)
  return false
}

// StopContainers - stop all containers or only containers with changed images.
func (t *Town) StopContainers(checkChanged bool) {
  log.Println("Stop...")
  //for node := range t.cluster.nodes {
  for i := len(t.cluster.Nodes) - 1; i >= 0; i-- {
    node := t.cluster.Nodes[i]
    if (!checkChanged || node.Container.Changed) && len(node.Container.Exist) > 0 {
      for _, container := range node.Container.Exist {
        if container.Running {
          err := t.docker.StopContainer(container.ID, 10)
          if err == nil {
            log.Println("   -  ", container.Name)
          } else {
            log.Println("   -  ", container.Name, " failed ", err)
          }
        }
      }
    }
  }
  log.Println("=============================")
}

// RemoveContainers - remove container form local repository.
func (t *Town) RemoveContainers(checkChanged bool) {
  log.Println("Remove...")
  //for node := range t.cluster.nodes {
  for i := len(t.cluster.Nodes) - 1; i >= 0; i-- {
    node := t.cluster.Nodes[i]
    if (!checkChanged || node.Container.Changed) && len(node.Container.Exist) > 0 {
      for _, container := range node.Container.Exist {
        err := t.docker.RemoveContainer(dockerapi.RemoveContainerOptions{
          ID: container.ID,
          RemoveVolumes: false,
        })
        if err == nil {
          log.Println("   -  ", container.Name)
        } else {
          log.Println("   -  ", container.Name, " failed ", err)
        }
      }
    }
  }
  log.Println("=============================")
}

// CreateContainer - create container.
func (t *Town) CreateContainer(node *cluster.Node, index int) (string, string, string) {
  containerName := node.Container.Name + "-" + strconv.Itoa(index)

  log.Println("   -  ", containerName)

  node.Container.Hostname = containerName // ?? Help !!!!

  env :=  make([]string, 0, cap(node.Container.Environment))
  for _, e := range node.Container.Environment {
    env = append(env, t.exec(e, index))
  }

  volumes := make(map[string]struct{})
  binds := make([]string, 0, cap(node.Container.Volumes))
  if len(node.Container.Volumes) > 0 {
    for _, volume := range node.Container.Volumes {
      volume = t.exec(volume, index)
      vol := strings.Split(volume, ":")
      if len(vol) > 1 {
        volumes[vol[1]] = struct{}{}
      } else {
        volumes[vol[0]] = struct{}{}
      }
      binds = append(binds, volume)
    }
  }

  dockerConfig := dockerapi.Config{
      Image: node.Container.Image,
      Hostname: node.Container.Hostname,
      PortSpecs: node.Container.Ports,
      Env: env,
      Volumes: volumes,

      AttachStdout: false,
      AttachStdin: false,
      AttachStderr: false,

      Tty: false,

      //Cmd: []
  }

  if len(node.Container.Command) > 0 {
    cmd := t.exec(node.Container.Command, index)
    dockerConfig.Cmd = []string{ cmd }
  }

  // just info
  //for _, l := range node.status.links {
  //  log.Println("     * ", l)
  //}

  // create links
  links := t.cluster.GetLinks(node)

  portBindings := map[dockerapi.Port][]dockerapi.PortBinding{}
  // create ports
  for _, ports := range node.Container.Ports {

    port := strings.Split(ports, ":")
    var p dockerapi.Port

    if len(port) > 1 {
      p = dockerapi.Port(port[1] + "/tcp")
    } else {
      p = dockerapi.Port(port[0] + "/tcp")
    }

    if portBindings[p] == nil {
      portBindings[p] = [] dockerapi.PortBinding {}
    }

    portBindings[p] = append(portBindings[p], dockerapi.PortBinding{
      HostIP: "",
      HostPort: port[0],
    })
  }

  hostConfig := dockerapi.HostConfig{
    Binds: binds,
    Links: links, //, [],
    PortBindings: portBindings,
    NetworkMode: "bridge",
    PublishAllPorts: false,
    Privileged: node.Container.Privileged,
  }

  opts := dockerapi.CreateContainerOptions{Name: containerName, Config: &dockerConfig, HostConfig: &hostConfig}
  container, err := t.docker.CreateContainer(opts)
  if err == nil {
    runningContainer := cluster.NewExistContainer(container.ID, name, index, true)
    node.Container.Exist = append(node.Container.Exist, runningContainer)
    // runningContainer.Pid = container.State.Pid
    // runningContainer.User = container.Config.User
    // if checkChanged {
    //   node.Container.Changed = t.isChangedImage(node, container)
    // } else {
    //   node.Container.Changed = true
    // }

    retry := 5
    for retry > 0 {
      error := t.docker.StartContainer(container.ID, &hostConfig)
      if error != nil {
        // log.Println("start error: ", error);

        out, err := json.Marshal(container)
        if err != nil {
            panic (err)
        }
        // fmt.Println(string(out))

        retry--;
        if retry == 0 {
          log.Println(" Start failed after 5 retries: ", string(out))
        }
        // log.Println("retry: ", retry);
      } else {
        inspect, inspectError := t.docker.InspectContainer(container.ID)
        if inspectError == nil {
          //links = append(links, inspect.NetworkSettings.IPAddress + "  " + containerName)
          //ids = append(ids, container.ID)
          return container.ID, inspect.NetworkSettings.IPAddress + "  " + containerName, containerName
        }

        log.Println("Inpect ", container.ID, " error ", inspectError)

        //retry = 0
        break;
      }
    }
  } else {
    log.Println("Create container ", containerName, " error: ", err);
  }

  return "", "", ""
}

// CreateContainers - create list of containers.
func (t *Town) CreateContainers(checkChanged bool) {
  log.Println("Create...")
  for _, node := range t.cluster.Nodes {

    if !checkChanged || node.Container.Changed {
      ids := make([]string, 0, node.Container.Scale )

      hosts := make([]string, 0, node.Container.Scale)

      log.Println(node.Container.Name, "  image: ", node.Container.Image)
      for i := 1; i <= node.Container.Scale; i++ {

        _, host, containerName := t.CreateContainer(node, i) //id

        if len(node.Container.Exec.Post) > 0 {
          t.bashCommand(containerName, node.Container.Exec.Post)
        }

        ids = append(ids, containerName)
        hosts = append(hosts, host)
      }

      if len(ids) > 1 {
        for index, id := range ids {
          var buffer bytes.Buffer

          buffer.WriteString("echo -e '")
          for i := 0; i < len(hosts); i++ {
            if i != index {
              buffer.WriteString("\n")
              buffer.WriteString(hosts[i])
            }
          }
          buffer.WriteString("' >> /etc/hosts; touch /tmp/host-generated")
          t.bashCommand(id, buffer.String() )
        }
      }

      time.Sleep(1000 * time.Millisecond)
    } else if len(node.Container.Exist) < node.Container.Scale {
      log.Println(node.Container.Name, "  image: ", node.Container.Image, "  ", node.Container.Scale)
      var create =  make([]bool, node.Container.Scale)
      for i := 0; i < node.Container.Scale; i++ {
        create[i] = true
      }
      for _, container := range node.Container.Exist {
        if container.Running {
          create[container.Index - 1] = false;
        }
      }

      for i := 0; i < node.Container.Scale; i++ {
        if create[i] {
          // TODO create or start
          _, _, containerName := t.CreateContainer(node, i + 1)
          // TODO add hosts
          if len(node.Container.Exec.Post) > 0 {
            t.bashCommand(containerName, node.Container.Exec.Post)
          }
        }
      }
    }
  }
}

// bashCommand - execute bash command inside container.
func (t *Town) bashCommand(id string, command string)  {
  config := dockerapi.CreateExecOptions{
    Container:    id,
    AttachStdin:  false,
    AttachStdout: false,
    AttachStderr: false,
    Tty:          false,
    Cmd:          []string{"bash", "-c", command},
  }
  execObj, err := t.docker.CreateExec(config)
  if err == nil {
    startConfig := dockerapi.StartExecOptions{
      Detach: false,
    }
    err = t.docker.StartExec(execObj.ID, startConfig)
    if err != nil {
      log.Println("Container ", id, " command failed with error: ", err, "\n", command)
    }
  } else {
    log.Println("Container ", id, " command failed with error: ", err, "\n", command)
  }
}

func (t *Town) exec(text string, scale int) string {
  replace := strings.Replace(text, "${SCALE_NUM}", strconv.Itoa(scale), -1)
  match := scaleNumRegexp.FindAllStringSubmatch(replace, -1)
  hostMatch := hostsRegexp.FindAllStringSubmatch(replace, -1)
  if len(match) > 0 {
    if len(match[0]) > 1 {
      nums := strings.Split(match[0][1], ",")
      if len(nums) > (scale - 1) {
        replace = strings.Replace(replace, match[0][0], nums[scale - 1], -1)
      }
    }
  }
  if len(hostMatch) > 0 {
    if len(hostMatch[0]) > 1 {
      //nums := strings.Split(, ",")
      name := strings.ToLower(hostMatch[0][1])
      node := t.cluster.FindNodeByID(name)

      var buffer bytes.Buffer
      for i := 1; i <= node.Container.Scale; i++ {
        buffer.WriteString(name)
        buffer.WriteString("-")
        buffer.WriteString(strconv.Itoa( i ))
        if i != node.Container.Scale {
          buffer.WriteString(",")
        }
      }
      replace = strings.Replace(replace, hostMatch[0][0], buffer.String(), -1)
    }
  }
  return replace
}
<commit_msg>add check for only local repositiory<commit_after>package main

import (
  "strings"
  "log"
  // "fmt"
//  "regexp"
  "bytes"
  "encoding/json"
  "os"
  "time"
  "strconv"
  "regexp"
  "github.com/lookify/town/cluster"
  dockerapi "github.com/fsouza/go-dockerclient"
)

var (
  scaleNumRegexp, _ = regexp.Compile("\\$\\{SCALE_NUM:(.+)\\}")
//  SCALE_TOTAL_REG, _ = regexp.Compile("\\$\\{SCALE_NUM:(.+)\\}")
  hostsRegexp, _ = regexp.Compile("\\$\\{(.+)_HOSTS\\}")
)

// Town describe cluster and docker clients.
type Town struct {
  cluster *cluster.Cluster
  docker *dockerapi.Client // TODO change to multiple clients
}

// NewTown create new town with default values
func NewTown() *Town {
  return &Town{
    cluster: nil,
    docker: nil,
  }
}

// ReadFile - read town configuration fail in current direcotry or /etc/town (ext .yml)
func (t *Town) ReadFile(name string) {
  var pathLocs = [...]string{
    name + ".yml",
    "/etc/town/" + name + ".yml",
  }

  for _, path := range pathLocs {
    if _, err := os.Stat(path); err == nil {
      t.cluster = cluster.NewCluster(path)
      t.cluster.ReadFile()
      return
    }
  }

  log.Println("ERROR: Could not find file ", name, ".yml")
}

// Connect - connect to docker hosts.
func (t *Town) Connect() {
  endpoint := t.cluster.Application.Docker.Hosts[0] // at the moment use only first
  log.Println("Using Docker API endpont: ", endpoint)
  docker, err := dockerapi.NewClient( endpoint )
  if err != nil {
    log.Println("Can't connect to the docker")
  }
  t.docker = docker
}

// Provision running containers.
func (t *Town) Provision(checkChanged bool) {
  // update containers
  pull := true
  repository := t.cluster.Application.Docker.Repository
  if len(repository) == 1 && repository[0] == "local" {
    pull = false
  }

  if pull {
    for _, node := range t.cluster.Nodes {
      var buf bytes.Buffer
      var image = strings.Split(node.Container.Image, ":")

      opts := dockerapi.PullImageOptions{
          Repository: image[0],
          // Registry:     "docker.tsuru.io",
          // Tag:          "latest",
          OutputStream: &buf,
      }

      if len(image) > 1 {
        opts.Tag = image[1]
      }
      err := t.docker.PullImage(opts, dockerapi.AuthConfiguration{});
      if err != nil {
        log.Println("Could not pull image ", image)
      }
    }
  }

  allContainers, err := t.docker.ListContainers(dockerapi.ListContainersOptions{
    All: true,
  })
  if err == nil {
    for _, listing := range allContainers {
      container, err := t.docker.InspectContainer(listing.ID)
      if err == nil {
        name := container.Name[1:]
        node, index := t.cluster.FindNodeByName(name)
        if node != nil && index > 0 {
          if node.Container.Exist == nil {
            node.Container.Exist = []cluster.ExistContainer{}
          }
          runningContainer := cluster.NewExistContainer(listing.ID, name, index, container.State.Running)
          runningContainer.Pid = container.State.Pid
          runningContainer.User = container.Config.User
          if checkChanged {
            node.Container.Changed = t.isChangedImage(node, container)
          } else {
            node.Container.Changed = true
          }
          node.Container.Exist = append(node.Container.Exist, runningContainer)
        }
      } else {
        log.Println("[ERROR] Unable to inspect container:", listing.ID[:12], err)
      }
    }

    if checkChanged {
      t.cluster.AddChangeDependant()
    }
  } else {
    log.Println("[ERROR] Can't start provision")
  }
}

// Info - print current cluster information.
func (t *Town) Info() {
  for i := len(t.cluster.Nodes) - 1; i >= 0; i-- {
    node := t.cluster.Nodes[i]
    log.Print("Node ", node.Container.Name, " image ")
	  if node.Container.Changed {
      log.Print("(Changed)")
    }
	  log.Println(": ", node.Container.Image)
    for _, container := range node.Container.Exist {
      log.Print("      ", container.Name, "\t")
  	  if container.Running {
  	    log.Println("Running")
  	  } else {
  	    log.Print("Stoped")
  	  }
    }
  }
}

/**
 * Check node and running container for changes.
 * TODO: add cache to docker call.
 **/
func (t *Town) isChangedImage(node *cluster.Node, container *dockerapi.Container) bool {
  var imageName = container.Image
  image , error := t.docker.InspectImage(imageName)
  if error == nil {
    secondImage , secondError := t.docker.InspectImage(node.Container.Image)
    if secondError == nil {
      return secondImage.Created.After(image.Created)
    }
  }
  log.Println("[ERROR] Could not inspect image ", node.Container.Name)
  return false
}

// StopContainers - stop all containers or only containers with changed images.
func (t *Town) StopContainers(checkChanged bool) {
  log.Println("Stop...")
  //for node := range t.cluster.nodes {
  for i := len(t.cluster.Nodes) - 1; i >= 0; i-- {
    node := t.cluster.Nodes[i]
    if (!checkChanged || node.Container.Changed) && len(node.Container.Exist) > 0 {
      for _, container := range node.Container.Exist {
        if container.Running {
          err := t.docker.StopContainer(container.ID, 10)
          if err == nil {
            log.Println("   -  ", container.Name)
          } else {
            log.Println("   -  ", container.Name, " failed ", err)
          }
        }
      }
    }
  }
  log.Println("=============================")
}

// RemoveContainers - remove container form local repository.
func (t *Town) RemoveContainers(checkChanged bool) {
  log.Println("Remove...")
  //for node := range t.cluster.nodes {
  for i := len(t.cluster.Nodes) - 1; i >= 0; i-- {
    node := t.cluster.Nodes[i]
    if (!checkChanged || node.Container.Changed) && len(node.Container.Exist) > 0 {
      for _, container := range node.Container.Exist {
        err := t.docker.RemoveContainer(dockerapi.RemoveContainerOptions{
          ID: container.ID,
          RemoveVolumes: false,
        })
        if err == nil {
          log.Println("   -  ", container.Name)
        } else {
          log.Println("   -  ", container.Name, " failed ", err)
        }
      }
    }
  }
  log.Println("=============================")
}

// CreateContainer - create container.
func (t *Town) CreateContainer(node *cluster.Node, index int) (string, string, string) {
  containerName := node.Container.Name + "-" + strconv.Itoa(index)

  log.Println("   -  ", containerName)

  node.Container.Hostname = containerName // ?? Help !!!!

  env :=  make([]string, 0, cap(node.Container.Environment))
  for _, e := range node.Container.Environment {
    env = append(env, t.exec(e, index))
  }

  volumes := make(map[string]struct{})
  binds := make([]string, 0, cap(node.Container.Volumes))
  if len(node.Container.Volumes) > 0 {
    for _, volume := range node.Container.Volumes {
      volume = t.exec(volume, index)
      vol := strings.Split(volume, ":")
      if len(vol) > 1 {
        volumes[vol[1]] = struct{}{}
      } else {
        volumes[vol[0]] = struct{}{}
      }
      binds = append(binds, volume)
    }
  }

  dockerConfig := dockerapi.Config{
      Image: node.Container.Image,
      Hostname: node.Container.Hostname,
      PortSpecs: node.Container.Ports,
      Env: env,
      Volumes: volumes,

      AttachStdout: false,
      AttachStdin: false,
      AttachStderr: false,

      Tty: false,

      //Cmd: []
  }

  if len(node.Container.Command) > 0 {
    cmd := t.exec(node.Container.Command, index)
    dockerConfig.Cmd = []string{ cmd }
  }

  // just info
  //for _, l := range node.status.links {
  //  log.Println("     * ", l)
  //}

  // create links
  links := t.cluster.GetLinks(node)

  portBindings := map[dockerapi.Port][]dockerapi.PortBinding{}
  // create ports
  for _, ports := range node.Container.Ports {

    port := strings.Split(ports, ":")
    var p dockerapi.Port

    if len(port) > 1 {
      p = dockerapi.Port(port[1] + "/tcp")
    } else {
      p = dockerapi.Port(port[0] + "/tcp")
    }

    if portBindings[p] == nil {
      portBindings[p] = [] dockerapi.PortBinding {}
    }

    portBindings[p] = append(portBindings[p], dockerapi.PortBinding{
      HostIP: "",
      HostPort: port[0],
    })
  }

  hostConfig := dockerapi.HostConfig{
    Binds: binds,
    Links: links, //, [],
    PortBindings: portBindings,
    NetworkMode: "bridge",
    PublishAllPorts: false,
    Privileged: node.Container.Privileged,
  }

  opts := dockerapi.CreateContainerOptions{Name: containerName, Config: &dockerConfig, HostConfig: &hostConfig}
  container, err := t.docker.CreateContainer(opts)
  if err == nil {
    runningContainer := cluster.NewExistContainer(container.ID, name, index, true)
    node.Container.Exist = append(node.Container.Exist, runningContainer)
    // runningContainer.Pid = container.State.Pid
    // runningContainer.User = container.Config.User
    // if checkChanged {
    //   node.Container.Changed = t.isChangedImage(node, container)
    // } else {
    //   node.Container.Changed = true
    // }

    retry := 5
    for retry > 0 {
      error := t.docker.StartContainer(container.ID, &hostConfig)
      if error != nil {
        // log.Println("start error: ", error);

        out, err := json.Marshal(container)
        if err != nil {
            panic (err)
        }
        // fmt.Println(string(out))

        retry--;
        if retry == 0 {
          log.Println(" Start failed after 5 retries: ", string(out))
        }
        // log.Println("retry: ", retry);
      } else {
        inspect, inspectError := t.docker.InspectContainer(container.ID)
        if inspectError == nil {
          //links = append(links, inspect.NetworkSettings.IPAddress + "  " + containerName)
          //ids = append(ids, container.ID)
          return container.ID, inspect.NetworkSettings.IPAddress + "  " + containerName, containerName
        }

        log.Println("Inpect ", container.ID, " error ", inspectError)

        //retry = 0
        break;
      }
    }
  } else {
    log.Println("Create container ", containerName, " error: ", err);
  }

  return "", "", ""
}

// CreateContainers - create list of containers.
func (t *Town) CreateContainers(checkChanged bool) {
  log.Println("Create...")
  for _, node := range t.cluster.Nodes {

    if !checkChanged || node.Container.Changed {
      ids := make([]string, 0, node.Container.Scale )

      hosts := make([]string, 0, node.Container.Scale)

      log.Println(node.Container.Name, "  image: ", node.Container.Image)
      for i := 1; i <= node.Container.Scale; i++ {

        _, host, containerName := t.CreateContainer(node, i) //id

        if len(node.Container.Exec.Post) > 0 {
          t.bashCommand(containerName, node.Container.Exec.Post)
        }

        ids = append(ids, containerName)
        hosts = append(hosts, host)
      }

      if len(ids) > 1 {
        for index, id := range ids {
          var buffer bytes.Buffer

          buffer.WriteString("echo -e '")
          for i := 0; i < len(hosts); i++ {
            if i != index {
              buffer.WriteString("\n")
              buffer.WriteString(hosts[i])
            }
          }
          buffer.WriteString("' >> /etc/hosts; touch /tmp/host-generated")
          t.bashCommand(id, buffer.String() )
        }
      }

      time.Sleep(1000 * time.Millisecond)
    } else if len(node.Container.Exist) < node.Container.Scale {
      log.Println(node.Container.Name, "  image: ", node.Container.Image, "  ", node.Container.Scale)
      var create =  make([]bool, node.Container.Scale)
      for i := 0; i < node.Container.Scale; i++ {
        create[i] = true
      }
      for _, container := range node.Container.Exist {
        if container.Running {
          create[container.Index - 1] = false;
        }
      }

      for i := 0; i < node.Container.Scale; i++ {
        if create[i] {
          // TODO create or start
          _, _, containerName := t.CreateContainer(node, i + 1)
          // TODO add hosts
          if len(node.Container.Exec.Post) > 0 {
            t.bashCommand(containerName, node.Container.Exec.Post)
          }
        }
      }
    }
  }
}

// bashCommand - execute bash command inside container.
func (t *Town) bashCommand(id string, command string)  {
  config := dockerapi.CreateExecOptions{
    Container:    id,
    AttachStdin:  false,
    AttachStdout: false,
    AttachStderr: false,
    Tty:          false,
    Cmd:          []string{"bash", "-c", command},
  }
  execObj, err := t.docker.CreateExec(config)
  if err == nil {
    startConfig := dockerapi.StartExecOptions{
      Detach: false,
    }
    err = t.docker.StartExec(execObj.ID, startConfig)
    if err != nil {
      log.Println("Container ", id, " command failed with error: ", err, "\n", command)
    }
  } else {
    log.Println("Container ", id, " command failed with error: ", err, "\n", command)
  }
}

func (t *Town) exec(text string, scale int) string {
  replace := strings.Replace(text, "${SCALE_NUM}", strconv.Itoa(scale), -1)
  match := scaleNumRegexp.FindAllStringSubmatch(replace, -1)
  hostMatch := hostsRegexp.FindAllStringSubmatch(replace, -1)
  if len(match) > 0 {
    if len(match[0]) > 1 {
      nums := strings.Split(match[0][1], ",")
      if len(nums) > (scale - 1) {
        replace = strings.Replace(replace, match[0][0], nums[scale - 1], -1)
      }
    }
  }
  if len(hostMatch) > 0 {
    if len(hostMatch[0]) > 1 {
      //nums := strings.Split(, ",")
      name := strings.ToLower(hostMatch[0][1])
      node := t.cluster.FindNodeByID(name)

      var buffer bytes.Buffer
      for i := 1; i <= node.Container.Scale; i++ {
        buffer.WriteString(name)
        buffer.WriteString("-")
        buffer.WriteString(strconv.Itoa( i ))
        if i != node.Container.Scale {
          buffer.WriteString(",")
        }
      }
      replace = strings.Replace(replace, hostMatch[0][0], buffer.String(), -1)
    }
  }
  return replace
}
<|endoftext|>


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

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"os"
	"os/exec"
	"regexp"
	"strings"
)

// A vcsCmd describes how to use a version control system
// like Mercurial, Git, or Subversion.
type vcsCmd struct {
	name string
	cmd  string // name of binary to invoke command

	createCmd   string // command to download a fresh copy of a repository
	downloadCmd string // command to download updates into an existing repository

	tagCmd     []tagCmd // commands to list tags
	tagDefault string   // default tag to use
	tagSyncCmd string   // command to sync to specific tag
}

// A tagCmd describes a command to list available tags
// that can be passed to tagSyncCmd.
type tagCmd struct {
	cmd     string // command to list tags
	pattern string // regexp to extract tags from list
}

// vcsList lists the known version control systems
var vcsList = []*vcsCmd{
	vcsHg,
	vcsGit,
	vcsSvn,
	vcsBzr,
}

// vcsByCmd returns the version control system for the given
// command name (hg, git, svn, bzr).
func vcsByCmd(cmd string) *vcsCmd {
	for _, vcs := range vcsList {
		if vcs.cmd == cmd {
			return vcs
		}
	}
	return nil
}

// vcsHg describes how to use Mercurial.
var vcsHg = &vcsCmd{
	name: "Mercurial",
	cmd:  "hg",

	createCmd:   "clone -U {repo} {dir}",
	downloadCmd: "pull",

	// We allow both tag and branch names as 'tags'
	// for selecting a version.  This lets people have
	// a go.release.r60 branch and a go.1 branch
	// and make changes in both, without constantly
	// editing .hgtags.
	tagCmd: []tagCmd{
		{"tags", `^(\S+)`},
		{"branches", `^(\S+)`},
	},
	tagDefault: "default",
	tagSyncCmd: "update -r {tag}",
}

// vcsGit describes how to use Git.
var vcsGit = &vcsCmd{
	name: "Git",
	cmd:  "git",

	createCmd:   "clone {repo} {dir}",
	downloadCmd: "fetch",

	tagCmd:     []tagCmd{{"tag", `^(\S+)$`}},
	tagDefault: "master",
	tagSyncCmd: "checkout {tag}",
}

// vcsBzr describes how to use Bazaar.
var vcsBzr = &vcsCmd{
	name: "Bazaar",
	cmd:  "bzr",

	createCmd:   "branch {repo} {dir}",
	downloadCmd: "pull --overwrite", // TODO: REALLY?

	tagCmd:     []tagCmd{{"tags", `^(\S+)`}},
	tagDefault: "revno:-1",
	tagSyncCmd: "update -r {tag}",
}

// vcsSvn describes how to use Subversion.
var vcsSvn = &vcsCmd{
	name: "Subversion",
	cmd:  "svn",

	createCmd:   "checkout {repo} {dir}",
	downloadCmd: "update",

	// There is no tag command in subversion.
	// The branch information is all in the path names.
}

func (v *vcsCmd) String() string {
	return v.name
}

// run runs the command line cmd in the given directory.
// keyval is a list of key, value pairs.  run expands
// instances of {key} in cmd into value, but only after
// splitting cmd into individual arguments.
// If an error occurs, run prints the command line and the
// command's combined stdout+stderr to standard error.
// Otherwise run discards the command's output.
func (v *vcsCmd) run(dir string, cmd string, keyval ...string) error {
	_, err := v.run1(dir, false, cmd, keyval)
	return err
}

// runOutput is like run but returns the output of the command.
func (v *vcsCmd) runOutput(dir string, cmd string, keyval ...string) ([]byte, error) {
	return v.run1(dir, true, cmd, keyval)
}

// run1 is the generalized implementation of run and runOutput.
func (v *vcsCmd) run1(dir string, output bool, cmdline string, keyval []string) ([]byte, error) {
	m := make(map[string]string)
	for i := 0; i < len(keyval); i += 2 {
		m[keyval[i]] = keyval[i+1]
	}
	args := strings.Fields(cmdline)
	for i, arg := range args {
		args[i] = expand(m, arg)
	}

	cmd := exec.Command(v.cmd, args...)
	cmd.Dir = dir
	if buildX {
		fmt.Printf("cd %s\n", dir)
		fmt.Printf("%s %s\n", v.cmd, strings.Join(args, " "))
	}
	var buf bytes.Buffer
	cmd.Stdout = &buf
	cmd.Stderr = &buf
	out := buf.Bytes()
	err := cmd.Run()
	if err != nil {
		fmt.Fprintf(os.Stderr, "# cd %s; %s %s\n", dir, v.cmd, strings.Join(args, " "))
		os.Stderr.Write(out)
		return nil, err
	}
	return out, nil
}

// create creates a new copy of repo in dir.
// The parent of dir must exist; dir must not.
func (v *vcsCmd) create(dir, repo string) error {
	return v.run(".", v.createCmd, "dir", dir, "repo", repo)
}

// download downloads any new changes for the repo in dir.
func (v *vcsCmd) download(dir string) error {
	return v.run(dir, v.downloadCmd)
}

// tags returns the list of available tags for the repo in dir.
func (v *vcsCmd) tags(dir string) ([]string, error) {
	var tags []string
	for _, tc := range v.tagCmd {
		out, err := v.runOutput(dir, tc.cmd)
		if err != nil {
			return nil, err
		}
		re := regexp.MustCompile(`(?m-s)` + tc.pattern)
		tags = append(tags, re.FindAllString(string(out), -1)...)
	}
	return tags, nil
}

// tagSync syncs the repo in dir to the named tag,
// which either is a tag returned by tags or is v.tagDefault.
func (v *vcsCmd) tagSync(dir, tag string) error {
	if v.tagSyncCmd == "" {
		return nil
	}
	return v.run(dir, v.tagSyncCmd, "tag", tag)
}

// A vcsPath is describes how to convert an import path into a
// version control system and repository name.
type vcsPath struct {
	prefix string                              // prefix this description applies to
	re     string                              // pattern for import path
	repo   string                              // repository to use (expand with match of re)
	vcs    string                              // version control system to use (expand with match of re)
	check  func(match map[string]string) error // additional checks

	regexp *regexp.Regexp // cached compiled form of re
}

// vcsForImportPath analyzes importPath to determine the
// version control system, and code repository to use.
// On return, repo is the repository URL and root is the
// import path corresponding to the root of the repository
// (thus root is a prefix of importPath).
func vcsForImportPath(importPath string) (vcs *vcsCmd, repo, root string, err error) {
	for _, srv := range vcsPaths {
		if !strings.HasPrefix(importPath, srv.prefix) {
			continue
		}
		m := srv.regexp.FindStringSubmatch(importPath)
		if m == nil {
			if srv.prefix != "" {
				return nil, "", "", fmt.Errorf("invalid %s import path %q", srv.prefix, importPath)
			}
			continue
		}

		// Build map of named subexpression matches for expand.
		match := map[string]string{
			"prefix": srv.prefix,
			"import": importPath,
		}
		for i, name := range srv.regexp.SubexpNames() {
			if name != "" && match[name] == "" {
				match[name] = m[i]
			}
		}
		if srv.vcs != "" {
			match["vcs"] = expand(match, srv.vcs)
		}
		if srv.repo != "" {
			match["repo"] = expand(match, srv.repo)
		}
		if srv.check != nil {
			if err := srv.check(match); err != nil {
				return nil, "", "", err
			}
		}
		vcs := vcsByCmd(match["vcs"])
		if vcs == nil {
			return nil, "", "", fmt.Errorf("unknown version control system %q", match["vcs"])
		}
		return vcs, match["repo"], match["root"], nil
	}
	return nil, "", "", fmt.Errorf("unrecognized import path %q", importPath)
}

// expand rewrites s to replace {k} with match[k] for each key k in match.
func expand(match map[string]string, s string) string {
	for k, v := range match {
		s = strings.Replace(s, "{"+k+"}", v, -1)
	}
	return s
}

// vcsPaths lists the known vcs paths.
var vcsPaths = []*vcsPath{
	// Google Code - new syntax
	{
		prefix: "code.google.com/",
		re:     `^(?P<root>code\.google\.com/p/(?P<project>[a-z0-9\-]+)(\.(?P<subrepo>[a-z0-9\-]+))?)(/[A-Za-z0-9_.\-]+)*$`,
		repo:   "https://{root}",
		check:  googleCodeVCS,
	},

	// Google Code - old syntax
	{
		re:    `^(?P<project>[a-z0-9_\-.]+)\.googlecode\.com/(git|hg|svn)(?P<path>/.*)?$`,
		check: oldGoogleCode,
	},

	// Github
	{
		prefix: "github.com/",
		re:     `^(?P<root>github\.com/[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+)(/[A-Za-z0-9_.\-]+)*$`,
		vcs:    "git",
		repo:   "https://{root}",
		check:  noVCSSuffix,
	},

	// Bitbucket
	{
		prefix: "bitbucket.org/",
		re:     `^(?P<root>bitbucket\.org/(?P<bitname>[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+))(/[A-Za-z0-9_.\-]+)*$`,
		repo:   "https://{root}",
		check:  bitbucketVCS,
	},

	// Launchpad
	{
		prefix: "launchpad.net/",
		re:     `^(?P<root>launchpad\.net/([A-Za-z0-9_.\-]+(/[A-Za-z0-9_.\-]+)?|~[A-Za-z0-9_.\-]+/(\+junk|[A-Za-z0-9_.\-]+)/[A-Za-z0-9_.\-]+))(/[A-Za-z0-9_.\-]+)*$`,
		vcs:    "bzr",
		repo:   "https://{root}",
	},

	// General syntax for any server.
	{
		re: `^(?P<root>(?P<repo>([a-z0-9.\-]+\.)+[a-z0-9.\-]+(:[0-9]+)?/[A-Za-z0-9_.\-/]*?)\.(?P<vcs>bzr|git|hg|svn))(/[A-Za-z0-9_.\-]+)*$`,
	},
}

func init() {
	// fill in cached regexps.
	// Doing this eagerly discovers invalid regexp syntax
	// without having to run a command that needs that regexp.
	for _, srv := range vcsPaths {
		srv.regexp = regexp.MustCompile(srv.re)
	}
}

// noVCSSuffix checks that the repository name does not
// end in .foo for any version control system foo.
// The usual culprit is ".git".
func noVCSSuffix(match map[string]string) error {
	repo := match["repo"]
	for _, vcs := range vcsList {
		if strings.HasSuffix(repo, "."+vcs.cmd) {
			return fmt.Errorf("invalid version control suffix in %s path", match["prefix"])
		}
	}
	return nil
}

var googleCheckout = regexp.MustCompile(`id="checkoutcmd">(hg|git|svn)`)

// googleCodeVCS determines the version control system for
// a code.google.com repository, by scraping the project's
// /source/checkout page.
func googleCodeVCS(match map[string]string) error {
	if err := noVCSSuffix(match); err != nil {
		return err
	}
	data, err := httpGET(expand(match, "https://code.google.com/p/{project}/source/checkout?repo={subrepo}"))
	if err != nil {
		return err
	}

	if m := googleCheckout.FindSubmatch(data); m != nil {
		if vcs := vcsByCmd(string(m[1])); vcs != nil {
			// Subversion requires the old URLs.
			// TODO: Test.
			if vcs == vcsSvn {
				if match["subrepo"] != "" {
					return fmt.Errorf("sub-repositories not supported in Google Code Subversion projects")
				}
				match["repo"] = expand(match, "https://{project}.googlecode.com/svn")
			}
			match["vcs"] = vcs.cmd
			return nil
		}
	}

	return fmt.Errorf("unable to detect version control system for code.google.com/ path")
}

// oldGoogleCode is invoked for old-style foo.googlecode.com paths.
// It prints an error giving the equivalent new path.
func oldGoogleCode(match map[string]string) error {
	return fmt.Errorf("invalid Google Code import path: use %s instead",
		expand(match, "code.google.com/p/{project}{path}"))
}

// bitbucketVCS determines the version control system for a
// BitBucket repository, by using the BitBucket API.
func bitbucketVCS(match map[string]string) error {
	if err := noVCSSuffix(match); err != nil {
		return err
	}

	var resp struct {
		SCM string `json:"scm"`
	}
	url := expand(match, "https://api.bitbucket.org/1.0/repositories/{bitname}")
	data, err := httpGET(url)
	if err != nil {
		return err
	}
	if err := json.Unmarshal(data, &resp); err != nil {
		return fmt.Errorf("decoding %s: %v", url, err)
	}

	if vcsByCmd(resp.SCM) != nil {
		match["vcs"] = resp.SCM
		if resp.SCM == "git" {
			match["repo"] += ".git"
		}
		return nil
	}

	return fmt.Errorf("unable to detect version control system for bitbucket.org/ path")
}
<commit_msg>cmd/go: solve ambiguity of get lp.net/project/foo<commit_after>// Copyright 2012 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"os"
	"os/exec"
	"regexp"
	"strings"
)

// A vcsCmd describes how to use a version control system
// like Mercurial, Git, or Subversion.
type vcsCmd struct {
	name string
	cmd  string // name of binary to invoke command

	createCmd   string // command to download a fresh copy of a repository
	downloadCmd string // command to download updates into an existing repository

	tagCmd     []tagCmd // commands to list tags
	tagDefault string   // default tag to use
	tagSyncCmd string   // command to sync to specific tag
}

// A tagCmd describes a command to list available tags
// that can be passed to tagSyncCmd.
type tagCmd struct {
	cmd     string // command to list tags
	pattern string // regexp to extract tags from list
}

// vcsList lists the known version control systems
var vcsList = []*vcsCmd{
	vcsHg,
	vcsGit,
	vcsSvn,
	vcsBzr,
}

// vcsByCmd returns the version control system for the given
// command name (hg, git, svn, bzr).
func vcsByCmd(cmd string) *vcsCmd {
	for _, vcs := range vcsList {
		if vcs.cmd == cmd {
			return vcs
		}
	}
	return nil
}

// vcsHg describes how to use Mercurial.
var vcsHg = &vcsCmd{
	name: "Mercurial",
	cmd:  "hg",

	createCmd:   "clone -U {repo} {dir}",
	downloadCmd: "pull",

	// We allow both tag and branch names as 'tags'
	// for selecting a version.  This lets people have
	// a go.release.r60 branch and a go.1 branch
	// and make changes in both, without constantly
	// editing .hgtags.
	tagCmd: []tagCmd{
		{"tags", `^(\S+)`},
		{"branches", `^(\S+)`},
	},
	tagDefault: "default",
	tagSyncCmd: "update -r {tag}",
}

// vcsGit describes how to use Git.
var vcsGit = &vcsCmd{
	name: "Git",
	cmd:  "git",

	createCmd:   "clone {repo} {dir}",
	downloadCmd: "fetch",

	tagCmd:     []tagCmd{{"tag", `^(\S+)$`}},
	tagDefault: "master",
	tagSyncCmd: "checkout {tag}",
}

// vcsBzr describes how to use Bazaar.
var vcsBzr = &vcsCmd{
	name: "Bazaar",
	cmd:  "bzr",

	createCmd: "branch {repo} {dir}",

	// Without --overwrite bzr will not pull tags that changed.
	// Replace by --overwrite-tags after http://pad.lv/681792 goes in.
	downloadCmd: "pull --overwrite",

	tagCmd:     []tagCmd{{"tags", `^(\S+)`}},
	tagDefault: "revno:-1",
	tagSyncCmd: "update -r {tag}",
}

// vcsSvn describes how to use Subversion.
var vcsSvn = &vcsCmd{
	name: "Subversion",
	cmd:  "svn",

	createCmd:   "checkout {repo} {dir}",
	downloadCmd: "update",

	// There is no tag command in subversion.
	// The branch information is all in the path names.
}

func (v *vcsCmd) String() string {
	return v.name
}

// run runs the command line cmd in the given directory.
// keyval is a list of key, value pairs.  run expands
// instances of {key} in cmd into value, but only after
// splitting cmd into individual arguments.
// If an error occurs, run prints the command line and the
// command's combined stdout+stderr to standard error.
// Otherwise run discards the command's output.
func (v *vcsCmd) run(dir string, cmd string, keyval ...string) error {
	_, err := v.run1(dir, false, cmd, keyval)
	return err
}

// runOutput is like run but returns the output of the command.
func (v *vcsCmd) runOutput(dir string, cmd string, keyval ...string) ([]byte, error) {
	return v.run1(dir, true, cmd, keyval)
}

// run1 is the generalized implementation of run and runOutput.
func (v *vcsCmd) run1(dir string, output bool, cmdline string, keyval []string) ([]byte, error) {
	m := make(map[string]string)
	for i := 0; i < len(keyval); i += 2 {
		m[keyval[i]] = keyval[i+1]
	}
	args := strings.Fields(cmdline)
	for i, arg := range args {
		args[i] = expand(m, arg)
	}

	cmd := exec.Command(v.cmd, args...)
	cmd.Dir = dir
	if buildX {
		fmt.Printf("cd %s\n", dir)
		fmt.Printf("%s %s\n", v.cmd, strings.Join(args, " "))
	}
	var buf bytes.Buffer
	cmd.Stdout = &buf
	cmd.Stderr = &buf
	out := buf.Bytes()
	err := cmd.Run()
	if err != nil {
		fmt.Fprintf(os.Stderr, "# cd %s; %s %s\n", dir, v.cmd, strings.Join(args, " "))
		os.Stderr.Write(out)
		return nil, err
	}
	return out, nil
}

// create creates a new copy of repo in dir.
// The parent of dir must exist; dir must not.
func (v *vcsCmd) create(dir, repo string) error {
	return v.run(".", v.createCmd, "dir", dir, "repo", repo)
}

// download downloads any new changes for the repo in dir.
func (v *vcsCmd) download(dir string) error {
	return v.run(dir, v.downloadCmd)
}

// tags returns the list of available tags for the repo in dir.
func (v *vcsCmd) tags(dir string) ([]string, error) {
	var tags []string
	for _, tc := range v.tagCmd {
		out, err := v.runOutput(dir, tc.cmd)
		if err != nil {
			return nil, err
		}
		re := regexp.MustCompile(`(?m-s)` + tc.pattern)
		tags = append(tags, re.FindAllString(string(out), -1)...)
	}
	return tags, nil
}

// tagSync syncs the repo in dir to the named tag,
// which either is a tag returned by tags or is v.tagDefault.
func (v *vcsCmd) tagSync(dir, tag string) error {
	if v.tagSyncCmd == "" {
		return nil
	}
	return v.run(dir, v.tagSyncCmd, "tag", tag)
}

// A vcsPath describes how to convert an import path into a
// version control system and repository name.
type vcsPath struct {
	prefix string                              // prefix this description applies to
	re     string                              // pattern for import path
	repo   string                              // repository to use (expand with match of re)
	vcs    string                              // version control system to use (expand with match of re)
	check  func(match map[string]string) error // additional checks

	regexp *regexp.Regexp // cached compiled form of re
}

// vcsForImportPath analyzes importPath to determine the
// version control system, and code repository to use.
// On return, repo is the repository URL and root is the
// import path corresponding to the root of the repository
// (thus root is a prefix of importPath).
func vcsForImportPath(importPath string) (vcs *vcsCmd, repo, root string, err error) {
	for _, srv := range vcsPaths {
		if !strings.HasPrefix(importPath, srv.prefix) {
			continue
		}
		m := srv.regexp.FindStringSubmatch(importPath)
		if m == nil {
			if srv.prefix != "" {
				return nil, "", "", fmt.Errorf("invalid %s import path %q", srv.prefix, importPath)
			}
			continue
		}

		// Build map of named subexpression matches for expand.
		match := map[string]string{
			"prefix": srv.prefix,
			"import": importPath,
		}
		for i, name := range srv.regexp.SubexpNames() {
			if name != "" && match[name] == "" {
				match[name] = m[i]
			}
		}
		if srv.vcs != "" {
			match["vcs"] = expand(match, srv.vcs)
		}
		if srv.repo != "" {
			match["repo"] = expand(match, srv.repo)
		}
		if srv.check != nil {
			if err := srv.check(match); err != nil {
				return nil, "", "", err
			}
		}
		vcs := vcsByCmd(match["vcs"])
		if vcs == nil {
			return nil, "", "", fmt.Errorf("unknown version control system %q", match["vcs"])
		}
		return vcs, match["repo"], match["root"], nil
	}
	return nil, "", "", fmt.Errorf("unrecognized import path %q", importPath)
}

// expand rewrites s to replace {k} with match[k] for each key k in match.
func expand(match map[string]string, s string) string {
	for k, v := range match {
		s = strings.Replace(s, "{"+k+"}", v, -1)
	}
	return s
}

// vcsPaths lists the known vcs paths.
var vcsPaths = []*vcsPath{
	// Google Code - new syntax
	{
		prefix: "code.google.com/",
		re:     `^(?P<root>code\.google\.com/p/(?P<project>[a-z0-9\-]+)(\.(?P<subrepo>[a-z0-9\-]+))?)(/[A-Za-z0-9_.\-]+)*$`,
		repo:   "https://{root}",
		check:  googleCodeVCS,
	},

	// Google Code - old syntax
	{
		re:    `^(?P<project>[a-z0-9_\-.]+)\.googlecode\.com/(git|hg|svn)(?P<path>/.*)?$`,
		check: oldGoogleCode,
	},

	// Github
	{
		prefix: "github.com/",
		re:     `^(?P<root>github\.com/[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+)(/[A-Za-z0-9_.\-]+)*$`,
		vcs:    "git",
		repo:   "https://{root}",
		check:  noVCSSuffix,
	},

	// Bitbucket
	{
		prefix: "bitbucket.org/",
		re:     `^(?P<root>bitbucket\.org/(?P<bitname>[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+))(/[A-Za-z0-9_.\-]+)*$`,
		repo:   "https://{root}",
		check:  bitbucketVCS,
	},

	// Launchpad
	{
		prefix: "launchpad.net/",
		re:     `^(?P<root>launchpad\.net/((?P<project>[A-Za-z0-9_.\-]+)(?P<series>/[A-Za-z0-9_.\-]+)?|~[A-Za-z0-9_.\-]+/(\+junk|[A-Za-z0-9_.\-]+)/[A-Za-z0-9_.\-]+))(/[A-Za-z0-9_.\-]+)*$`,
		vcs:    "bzr",
		repo:   "https://{root}",
		check:  launchpadVCS,
	},

	// General syntax for any server.
	{
		re: `^(?P<root>(?P<repo>([a-z0-9.\-]+\.)+[a-z0-9.\-]+(:[0-9]+)?/[A-Za-z0-9_.\-/]*?)\.(?P<vcs>bzr|git|hg|svn))(/[A-Za-z0-9_.\-]+)*$`,
	},
}

func init() {
	// fill in cached regexps.
	// Doing this eagerly discovers invalid regexp syntax
	// without having to run a command that needs that regexp.
	for _, srv := range vcsPaths {
		srv.regexp = regexp.MustCompile(srv.re)
	}
}

// noVCSSuffix checks that the repository name does not
// end in .foo for any version control system foo.
// The usual culprit is ".git".
func noVCSSuffix(match map[string]string) error {
	repo := match["repo"]
	for _, vcs := range vcsList {
		if strings.HasSuffix(repo, "."+vcs.cmd) {
			return fmt.Errorf("invalid version control suffix in %s path", match["prefix"])
		}
	}
	return nil
}

var googleCheckout = regexp.MustCompile(`id="checkoutcmd">(hg|git|svn)`)

// googleCodeVCS determines the version control system for
// a code.google.com repository, by scraping the project's
// /source/checkout page.
func googleCodeVCS(match map[string]string) error {
	if err := noVCSSuffix(match); err != nil {
		return err
	}
	data, err := httpGET(expand(match, "https://code.google.com/p/{project}/source/checkout?repo={subrepo}"))
	if err != nil {
		return err
	}

	if m := googleCheckout.FindSubmatch(data); m != nil {
		if vcs := vcsByCmd(string(m[1])); vcs != nil {
			// Subversion requires the old URLs.
			// TODO: Test.
			if vcs == vcsSvn {
				if match["subrepo"] != "" {
					return fmt.Errorf("sub-repositories not supported in Google Code Subversion projects")
				}
				match["repo"] = expand(match, "https://{project}.googlecode.com/svn")
			}
			match["vcs"] = vcs.cmd
			return nil
		}
	}

	return fmt.Errorf("unable to detect version control system for code.google.com/ path")
}

// oldGoogleCode is invoked for old-style foo.googlecode.com paths.
// It prints an error giving the equivalent new path.
func oldGoogleCode(match map[string]string) error {
	return fmt.Errorf("invalid Google Code import path: use %s instead",
		expand(match, "code.google.com/p/{project}{path}"))
}

// bitbucketVCS determines the version control system for a
// BitBucket repository, by using the BitBucket API.
func bitbucketVCS(match map[string]string) error {
	if err := noVCSSuffix(match); err != nil {
		return err
	}

	var resp struct {
		SCM string `json:"scm"`
	}
	url := expand(match, "https://api.bitbucket.org/1.0/repositories/{bitname}")
	data, err := httpGET(url)
	if err != nil {
		return err
	}
	if err := json.Unmarshal(data, &resp); err != nil {
		return fmt.Errorf("decoding %s: %v", url, err)
	}

	if vcsByCmd(resp.SCM) != nil {
		match["vcs"] = resp.SCM
		if resp.SCM == "git" {
			match["repo"] += ".git"
		}
		return nil
	}

	return fmt.Errorf("unable to detect version control system for bitbucket.org/ path")
}

// launchpadVCS solves the ambiguity for "lp.net/project/foo". In this case,
// "foo" could be a series name registered in Launchpad with its own branch,
// and it could also be the name of a directory within the main project
// branch one level up.
func launchpadVCS(match map[string]string) error {
	if match["project"] == "" || match["series"] == "" {
		return nil
	}
	_, err := httpGET(expand(match, "https://code.launchpad.net/{project}{series}/.bzr/branch-format"))
	if err != nil {
		match["root"] = expand(match, "launchpad.net/{project}")
		match["repo"] = expand(match, "https://{root}")
	}
	return nil
}
<|endoftext|>


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

// +build ignore

/*

Ld is the portable code for a modified version of the Plan 9 linker.  The original is documented at

	http://plan9.bell-labs.com/magic/man2html/1/8l

It reads object files (.5, .6, or .8 files) and writes a binary named for the
architecture (5.out, 6.out, 8.out) by default (if $GOOS is windows, a .exe suffix
will be appended).

Major changes include:
	- support for ELF, Mach-O and PE binary files
	- support for segmented stacks (this feature is implemented here, not in the compilers).

Original options are listed on the manual page linked above.

Usage:
	go tool 6l [flags] mainObj
Substitute 6l with 8l or 5l as appropriate.

Options new in this version:

	-d
		Elide the dynamic linking header.  With this option, the binary
		is statically linked and does not refer to a dynamic linker.  Without this option
		(the default), the binary's contents are identical but it is loaded with a dynamic
		linker. This flag cannot be used when $GOOS is windows.
	-H darwin     (only in 6l/8l)
		Write Apple Mach-O binaries (default when $GOOS is darwin)
	-H linux
		Write Linux ELF binaries (default when $GOOS is linux)
	-H freebsd
		Write FreeBSD ELF binaries (default when $GOOS is freebsd)
	-H netbsd
		Write NetBSD ELF binaries (default when $GOOS is netbsd)
	-H openbsd    (only in 6l/8l)
		Write OpenBSD ELF binaries (default when $GOOS is openbsd)
	-H windows    (only in 6l/8l)
		Write Windows PE32+ Console binaries (default when $GOOS is windows)
	-H windowsgui (only in 6l/8l)
		Write Windows PE32+ GUI binaries
	-I interpreter
		Set the ELF dynamic linker to use.
	-L dir1 -L dir2
		Search for libraries (package files) in dir1, dir2, etc.
		The default is the single location $GOROOT/pkg/$GOOS_$GOARCH.
	-r dir1:dir2:...
		Set the dynamic linker search path when using ELF.
	-V
		Print the linker version.
	-X symbol value
		Set the value of an otherwise uninitialized string variable.
		The symbol name should be of the form importpath.name,
		as displayed in the symbol table printed by "go tool nm".
	-race
		Link with race detection libraries.
	-B value
		Add a NT_GNU_BUILD_ID note when using ELF.  The value
		should start with 0x and be an even number of hex digits.
	-Z
		Zero stack on function entry. This is expensive but it might
		be useful in cases where you are suffering from false positives
		during garbage collection and are willing to trade the CPU time
		for getting rid of the false positives.
		NOTE: it only eliminates false positives caused by other function
		calls, not false positives caused by dead temporaries stored in
		the current function call.
	-linkmode argument
		Set the linkmode.  The argument must be one of
		internal, external, or auto.  The default is auto.
		This sets the linking mode as described in
		../cgo/doc.go.
	-tmpdir dir
		Set the location to use for any temporary files.  The
		default is a newly created directory that is removed
		after the linker completes.  Temporary files are only
		used in external linking mode.
	-extld name
		Set the name of the external linker to use in external
		linking mode.  The default is "gcc".
	-extldflags flags
		Set space-separated trailing flags to pass to the
		external linker in external linking mode.  The default
		is to not pass any additional trailing flags.
*/
package main
<commit_msg>cmd/ld: document -s flag.<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build ignore

/*

Ld is the portable code for a modified version of the Plan 9 linker.  The original is documented at

	http://plan9.bell-labs.com/magic/man2html/1/8l

It reads object files (.5, .6, or .8 files) and writes a binary named for the
architecture (5.out, 6.out, 8.out) by default (if $GOOS is windows, a .exe suffix
will be appended).

Major changes include:
	- support for ELF, Mach-O and PE binary files
	- support for segmented stacks (this feature is implemented here, not in the compilers).

Original options are listed on the manual page linked above.

Usage:
	go tool 6l [flags] mainObj
Substitute 6l with 8l or 5l as appropriate.

Options new in this version:

	-d
		Elide the dynamic linking header.  With this option, the binary
		is statically linked and does not refer to a dynamic linker.  Without this option
		(the default), the binary's contents are identical but it is loaded with a dynamic
		linker. This flag cannot be used when $GOOS is windows.
	-H darwin     (only in 6l/8l)
		Write Apple Mach-O binaries (default when $GOOS is darwin)
	-H linux
		Write Linux ELF binaries (default when $GOOS is linux)
	-H freebsd
		Write FreeBSD ELF binaries (default when $GOOS is freebsd)
	-H netbsd
		Write NetBSD ELF binaries (default when $GOOS is netbsd)
	-H openbsd    (only in 6l/8l)
		Write OpenBSD ELF binaries (default when $GOOS is openbsd)
	-H windows    (only in 6l/8l)
		Write Windows PE32+ Console binaries (default when $GOOS is windows)
	-H windowsgui (only in 6l/8l)
		Write Windows PE32+ GUI binaries
	-I interpreter
		Set the ELF dynamic linker to use.
	-L dir1 -L dir2
		Search for libraries (package files) in dir1, dir2, etc.
		The default is the single location $GOROOT/pkg/$GOOS_$GOARCH.
	-r dir1:dir2:...
		Set the dynamic linker search path when using ELF.
	-s
		Omit the symbol table and debug information.
	-V
		Print the linker version.
	-X symbol value
		Set the value of an otherwise uninitialized string variable.
		The symbol name should be of the form importpath.name,
		as displayed in the symbol table printed by "go tool nm".
	-race
		Link with race detection libraries.
	-B value
		Add a NT_GNU_BUILD_ID note when using ELF.  The value
		should start with 0x and be an even number of hex digits.
	-Z
		Zero stack on function entry. This is expensive but it might
		be useful in cases where you are suffering from false positives
		during garbage collection and are willing to trade the CPU time
		for getting rid of the false positives.
		NOTE: it only eliminates false positives caused by other function
		calls, not false positives caused by dead temporaries stored in
		the current function call.
	-linkmode argument
		Set the linkmode.  The argument must be one of
		internal, external, or auto.  The default is auto.
		This sets the linking mode as described in
		../cgo/doc.go.
	-tmpdir dir
		Set the location to use for any temporary files.  The
		default is a newly created directory that is removed
		after the linker completes.  Temporary files are only
		used in external linking mode.
	-extld name
		Set the name of the external linker to use in external
		linking mode.  The default is "gcc".
	-extldflags flags
		Set space-separated trailing flags to pass to the
		external linker in external linking mode.  The default
		is to not pass any additional trailing flags.
*/
package main
<|endoftext|>


<commit_before>package slack

import (
	"fmt"
	"time"
)

// XXX: Need to implement
type UserPrefs struct {
	// "highlight_words":"",
	// "user_colors":"",
	// "color_names_in_list":true,
	// "growls_enabled":true,
	// "tz":"Europe\/London",
	// "push_dm_alert":true,
	// "push_mention_alert":true,
	// "push_everything":true,
	// "push_idle_wait":2,
	// "push_sound":"b2.mp3",
	// "push_loud_channels":"",
	// "push_mention_channels":"",
	// "push_loud_channels_set":"",
	// "email_alerts":"instant",
	// "email_alerts_sleep_until":0,
	// "email_misc":false,
	// "email_weekly":true,
	// "welcome_message_hidden":false,
	// "all_channels_loud":true,
	// "loud_channels":"",
	// "never_channels":"",
	// "loud_channels_set":"",
	// "show_member_presence":true,
	// "search_sort":"timestamp",
	// "expand_inline_imgs":true,
	// "expand_internal_inline_imgs":true,
	// "expand_snippets":false,
	// "posts_formatting_guide":true,
	// "seen_welcome_2":true,
	// "seen_ssb_prompt":false,
	// "search_only_my_channels":false,
	// "emoji_mode":"default",
	// "has_invited":true,
	// "has_uploaded":false,
	// "has_created_channel":true,
	// "search_exclude_channels":"",
	// "messages_theme":"default",
	// "webapp_spellcheck":true,
	// "no_joined_overlays":false,
	// "no_created_overlays":true,
	// "dropbox_enabled":false,
	// "seen_user_menu_tip_card":true,
	// "seen_team_menu_tip_card":true,
	// "seen_channel_menu_tip_card":true,
	// "seen_message_input_tip_card":true,
	// "seen_channels_tip_card":true,
	// "seen_domain_invite_reminder":false,
	// "seen_member_invite_reminder":false,
	// "seen_flexpane_tip_card":true,
	// "seen_search_input_tip_card":true,
	// "mute_sounds":false,
	// "arrow_history":false,
	// "tab_ui_return_selects":true,
	// "obey_inline_img_limit":true,
	// "new_msg_snd":"knock_brush.mp3",
	// "collapsible":false,
	// "collapsible_by_click":true,
	// "require_at":false,
	// "mac_ssb_bounce":"",
	// "mac_ssb_bullet":true,
	// "win_ssb_bullet":true,
	// "expand_non_media_attachments":true,
	// "show_typing":true,
	// "pagekeys_handled":true,
	// "last_snippet_type":"",
	// "display_real_names_override":0,
	// "time24":false,
	// "enter_is_special_in_tbt":false,
	// "graphic_emoticons":false,
	// "convert_emoticons":true,
	// "autoplay_chat_sounds":true,
	// "ss_emojis":true,
	// "sidebar_behavior":"",
	// "mark_msgs_read_immediately":true,
	// "start_scroll_at_oldest":true,
	// "snippet_editor_wrap_long_lines":false,
	// "ls_disabled":false,
	// "sidebar_theme":"default",
	// "sidebar_theme_custom_values":"",
	// "f_key_search":false,
	// "k_key_omnibox":true,
	// "speak_growls":false,
	// "mac_speak_voice":"com.apple.speech.synthesis.voice.Alex",
	// "mac_speak_speed":250,
	// "comma_key_prefs":false,
	// "at_channel_suppressed_channels":"",
	// "push_at_channel_suppressed_channels":"",
	// "prompted_for_email_disabling":false,
	// "full_text_extracts":false,
	// "no_text_in_notifications":false,
	// "muted_channels":"",
	// "no_macssb1_banner":false,
	// "privacy_policy_seen":true,
	// "search_exclude_bots":false,
	// "fuzzy_matching":false
}

// UserDetails contains user details coming in the initial response from StartRTM
type UserDetails struct {
	Id             string    `json:"id"`
	Name           string    `json:"name"`
	Created        JSONTime  `json:"created"`
	ManualPresence string    `json:"manual_presence"`
	Prefs          UserPrefs `json:"prefs"`
}

// JSONTime exists so that we can have a String method converting the date
type JSONTime int64

// String converts the unix timestamp into a string
func (t JSONTime) String() string {
	tm := time.Unix(int64(t), 0)
	return fmt.Sprintf("\"%s\"", tm.Format("Mon Jan _2"))
}

// Team contains details about a team
type Team struct {
	Id     string `json:"id"`
	Name   string `json:"name"`
	Domain string `json:"name"`
}

// Icons XXX: needs further investigation
type Icons struct {
	Image48 string `json:"image_48"`
}

// Bot contains information about a bot
type Bot struct {
	Id      string `json:"id"`
	Name    string `json:"name"`
	Deleted bool   `json:"deleted"`
	Icons   Icons  `json:"icons"`
}

// Info contains various details about Users, Channels, Bots and the authenticated user
// It is returned by StartRTM
type Info struct {
	Url      string      `json:"url,omitempty"`
	User     UserDetails `json:"self,omitempty"`
	Team     Team        `json:"team,omitempty"`
	Users    []User      `json:"users,omitempty"`
	Channels []Channel   `json:"channels,omitempty"`
	Bots     []Bot       `json:"bots,omitempty"`
	IMs      []IM        `json:"ims,omitempty"`
}

type infoResponseFull struct {
	Info
	SlackWSResponse
}

// GetBotById returns a bot given a bot id
func (info Info) GetBotById(botId string) *Bot {
	for _, bot := range info.Bots {
		if bot.Id == botId {
			return &bot
		}
	}
	return nil
}

// GetUserById returns a user given a user id
func (info Info) GetUserById(userId string) *User {
	for _, user := range info.Users {
		if user.Id == userId {
			return &user
		}
	}
	return nil
}

// GetChannelById returns a channel given a channel id
func (info Info) GetChannelById(channelId string) *Channel {
	for _, channel := range info.Channels {
		if channel.Id == channelId {
			return &channel
		}
	}
	return nil
}
<commit_msg>info: info responses contain a list of groups<commit_after>package slack

import (
	"fmt"
	"time"
)

// XXX: Need to implement
type UserPrefs struct {
	// "highlight_words":"",
	// "user_colors":"",
	// "color_names_in_list":true,
	// "growls_enabled":true,
	// "tz":"Europe\/London",
	// "push_dm_alert":true,
	// "push_mention_alert":true,
	// "push_everything":true,
	// "push_idle_wait":2,
	// "push_sound":"b2.mp3",
	// "push_loud_channels":"",
	// "push_mention_channels":"",
	// "push_loud_channels_set":"",
	// "email_alerts":"instant",
	// "email_alerts_sleep_until":0,
	// "email_misc":false,
	// "email_weekly":true,
	// "welcome_message_hidden":false,
	// "all_channels_loud":true,
	// "loud_channels":"",
	// "never_channels":"",
	// "loud_channels_set":"",
	// "show_member_presence":true,
	// "search_sort":"timestamp",
	// "expand_inline_imgs":true,
	// "expand_internal_inline_imgs":true,
	// "expand_snippets":false,
	// "posts_formatting_guide":true,
	// "seen_welcome_2":true,
	// "seen_ssb_prompt":false,
	// "search_only_my_channels":false,
	// "emoji_mode":"default",
	// "has_invited":true,
	// "has_uploaded":false,
	// "has_created_channel":true,
	// "search_exclude_channels":"",
	// "messages_theme":"default",
	// "webapp_spellcheck":true,
	// "no_joined_overlays":false,
	// "no_created_overlays":true,
	// "dropbox_enabled":false,
	// "seen_user_menu_tip_card":true,
	// "seen_team_menu_tip_card":true,
	// "seen_channel_menu_tip_card":true,
	// "seen_message_input_tip_card":true,
	// "seen_channels_tip_card":true,
	// "seen_domain_invite_reminder":false,
	// "seen_member_invite_reminder":false,
	// "seen_flexpane_tip_card":true,
	// "seen_search_input_tip_card":true,
	// "mute_sounds":false,
	// "arrow_history":false,
	// "tab_ui_return_selects":true,
	// "obey_inline_img_limit":true,
	// "new_msg_snd":"knock_brush.mp3",
	// "collapsible":false,
	// "collapsible_by_click":true,
	// "require_at":false,
	// "mac_ssb_bounce":"",
	// "mac_ssb_bullet":true,
	// "win_ssb_bullet":true,
	// "expand_non_media_attachments":true,
	// "show_typing":true,
	// "pagekeys_handled":true,
	// "last_snippet_type":"",
	// "display_real_names_override":0,
	// "time24":false,
	// "enter_is_special_in_tbt":false,
	// "graphic_emoticons":false,
	// "convert_emoticons":true,
	// "autoplay_chat_sounds":true,
	// "ss_emojis":true,
	// "sidebar_behavior":"",
	// "mark_msgs_read_immediately":true,
	// "start_scroll_at_oldest":true,
	// "snippet_editor_wrap_long_lines":false,
	// "ls_disabled":false,
	// "sidebar_theme":"default",
	// "sidebar_theme_custom_values":"",
	// "f_key_search":false,
	// "k_key_omnibox":true,
	// "speak_growls":false,
	// "mac_speak_voice":"com.apple.speech.synthesis.voice.Alex",
	// "mac_speak_speed":250,
	// "comma_key_prefs":false,
	// "at_channel_suppressed_channels":"",
	// "push_at_channel_suppressed_channels":"",
	// "prompted_for_email_disabling":false,
	// "full_text_extracts":false,
	// "no_text_in_notifications":false,
	// "muted_channels":"",
	// "no_macssb1_banner":false,
	// "privacy_policy_seen":true,
	// "search_exclude_bots":false,
	// "fuzzy_matching":false
}

// UserDetails contains user details coming in the initial response from StartRTM
type UserDetails struct {
	Id             string    `json:"id"`
	Name           string    `json:"name"`
	Created        JSONTime  `json:"created"`
	ManualPresence string    `json:"manual_presence"`
	Prefs          UserPrefs `json:"prefs"`
}

// JSONTime exists so that we can have a String method converting the date
type JSONTime int64

// String converts the unix timestamp into a string
func (t JSONTime) String() string {
	tm := time.Unix(int64(t), 0)
	return fmt.Sprintf("\"%s\"", tm.Format("Mon Jan _2"))
}

// Team contains details about a team
type Team struct {
	Id     string `json:"id"`
	Name   string `json:"name"`
	Domain string `json:"name"`
}

// Icons XXX: needs further investigation
type Icons struct {
	Image48 string `json:"image_48"`
}

// Bot contains information about a bot
type Bot struct {
	Id      string `json:"id"`
	Name    string `json:"name"`
	Deleted bool   `json:"deleted"`
	Icons   Icons  `json:"icons"`
}

// Info contains various details about Users, Channels, Bots and the authenticated user
// It is returned by StartRTM
type Info struct {
	Url      string      `json:"url,omitempty"`
	User     UserDetails `json:"self,omitempty"`
	Team     Team        `json:"team,omitempty"`
	Users    []User      `json:"users,omitempty"`
	Channels []Channel   `json:"channels,omitempty"`
	Groups   []Group     `json:"channels,omitempty"`
	Bots     []Bot       `json:"bots,omitempty"`
	IMs      []IM        `json:"ims,omitempty"`
}

type infoResponseFull struct {
	Info
	SlackWSResponse
}

// GetBotById returns a bot given a bot id
func (info Info) GetBotById(botId string) *Bot {
	for _, bot := range info.Bots {
		if bot.Id == botId {
			return &bot
		}
	}
	return nil
}

// GetUserById returns a user given a user id
func (info Info) GetUserById(userId string) *User {
	for _, user := range info.Users {
		if user.Id == userId {
			return &user
		}
	}
	return nil
}

// GetChannelById returns a channel given a channel id
func (info Info) GetChannelById(channelId string) *Channel {
	for _, channel := range info.Channels {
		if channel.Id == channelId {
			return &channel
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/siddontang/ledisdb/client/go/ledis"
	"math/rand"
	"sync"
	"sync/atomic"
	"time"
)

var ip = flag.String("ip", "127.0.0.1", "redis/ledis/ssdb server ip")
var port = flag.Int("port", 6380, "redis/ledis/ssdb server port")
var number = flag.Int("n", 1000, "request number")
var clients = flag.Int("c", 50, "number of clients")
var reverse = flag.Bool("rev", false, "enable zset rev benchmark")
var round = flag.Int("r", 1, "benchmark round number")
var del = flag.Bool("del", true, "enable del benchmark")
var valueSize = flag.Int("vsize", 100, "kv value size")
var wg sync.WaitGroup

var client *ledis.Client

var loop int = 0

func waitBench(cmd string, args ...interface{}) {
	c := client.Get()
	defer c.Close()

	_, err := c.Do(cmd, args...)
	if err != nil {
		fmt.Printf("do %s error %s", cmd, err.Error())
		return
	}
}

func bench(cmd string, f func()) {
	wg.Add(*clients)

	t1 := time.Now()
	for i := 0; i < *clients; i++ {
		go func() {
			for j := 0; j < loop; j++ {
				f()
			}
			wg.Done()
		}()
	}

	wg.Wait()

	t2 := time.Now()

	fmt.Printf("%s: %0.2f op/s\n", cmd, (float64(*number) / t2.Sub(t1).Seconds()))
}

var kvSetBase int64 = 0
var kvGetBase int64 = 0
var kvIncrBase int64 = 0
var kvDelBase int64 = 0

func benchSet() {
	f := func() {
		value := make([]byte, *valueSize)
		n := atomic.AddInt64(&kvSetBase, 1)
		waitBench("set", n, value)
	}

	bench("set", f)
}

func benchGet() {
	f := func() {
		n := atomic.AddInt64(&kvGetBase, 1)
		waitBench("get", n)
	}

	bench("get", f)
}

func benchRandGet() {
	f := func() {
		n := rand.Int()
		waitBench("get", n)
	}

	bench("randget", f)
}

func benchDel() {
	f := func() {
		n := atomic.AddInt64(&kvDelBase, 1)
		waitBench("del", n)
	}

	bench("del", f)
}

func benchPushList() {
	f := func() {
		value := make([]byte, 100)
		waitBench("rpush", "mytestlist", value)
	}

	bench("rpush", f)
}

func benchRangeList10() {
	f := func() {
		waitBench("lrange", "mytestlist", 0, 10)
	}

	bench("lrange10", f)
}

func benchRangeList50() {
	f := func() {
		waitBench("lrange", "mytestlist", 0, 50)
	}

	bench("lrange50", f)
}

func benchRangeList100() {
	f := func() {
		waitBench("lrange", "mytestlist", 0, 100)
	}

	bench("lrange100", f)
}

func benchPopList() {
	f := func() {
		waitBench("lpop", "mytestlist")
	}

	bench("lpop", f)
}

var hashSetBase int64 = 0
var hashIncrBase int64 = 0
var hashGetBase int64 = 0
var hashDelBase int64 = 0

func benchHset() {
	f := func() {
		value := make([]byte, 100)

		n := atomic.AddInt64(&hashSetBase, 1)
		waitBench("hset", "myhashkey", n, value)
	}

	bench("hset", f)
}

func benchHGet() {
	f := func() {
		n := atomic.AddInt64(&hashGetBase, 1)
		waitBench("hget", "myhashkey", n)
	}

	bench("hget", f)
}

func benchHRandGet() {
	f := func() {
		n := rand.Int()
		waitBench("hget", "myhashkey", n)
	}

	bench("hrandget", f)
}

func benchHDel() {
	f := func() {
		n := atomic.AddInt64(&hashDelBase, 1)
		waitBench("hdel", "myhashkey", n)
	}

	bench("hdel", f)
}

var zsetAddBase int64 = 0
var zsetDelBase int64 = 0
var zsetIncrBase int64 = 0

func benchZAdd() {
	f := func() {
		member := make([]byte, 16)
		n := atomic.AddInt64(&zsetAddBase, 1)
		waitBench("zadd", "myzsetkey", n, member)
	}

	bench("zadd", f)
}

func benchZDel() {
	f := func() {
		n := atomic.AddInt64(&zsetDelBase, 1)
		waitBench("zrem", "myzsetkey", n)
	}

	bench("zrem", f)
}

func benchZIncr() {
	f := func() {
		n := atomic.AddInt64(&zsetIncrBase, 1)
		waitBench("zincrby", "myzsetkey", 1, n)
	}

	bench("zincrby", f)
}

func benchZRangeByScore() {
	f := func() {
		waitBench("zrangebyscore", "myzsetkey", 0, rand.Int(), "withscores", "limit", rand.Int()%100, 100)
	}

	bench("zrangebyscore", f)
}

func benchZRangeByRank() {
	f := func() {
		waitBench("zrange", "myzsetkey", 0, rand.Int()%100)
	}

	bench("zrange", f)
}

func benchZRevRangeByScore() {
	f := func() {
		waitBench("zrevrangebyscore", "myzsetkey", 0, rand.Int(), "withscores", "limit", rand.Int()%100, 100)
	}

	bench("zrevrangebyscore", f)
}

func benchZRevRangeByRank() {
	f := func() {
		waitBench("zrevrange", "myzsetkey", 0, rand.Int()%100)
	}

	bench("zrevrange", f)
}

func main() {
	flag.Parse()

	if *number <= 0 {
		panic("invalid number")
		return
	}

	if *clients <= 0 || *number < *clients {
		panic("invalid client number")
		return
	}

	loop = *number / *clients

	addr := fmt.Sprintf("%s:%d", *ip, *port)

	cfg := new(ledis.Config)
	cfg.Addr = addr
	cfg.MaxIdleConns = *clients
	client = ledis.NewClient(cfg)

	if *round <= 0 {
		*round = 1
	}

	for i := 0; i < *round; i++ {
		benchSet()
		benchGet()
		benchRandGet()

		if *del == true {
			benchDel()
		}

		benchPushList()
		benchRangeList10()
		benchRangeList50()
		benchRangeList100()

		if *del == true {
			benchPopList()
		}

		benchHset()
		benchHGet()
		benchHRandGet()

		if *del == true {
			benchHDel()
		}

		benchZAdd()
		benchZIncr()
		benchZRangeByRank()
		benchZRangeByScore()

		//rev is too slow in leveldb, rocksdb or other
		//maybe disable for huge data benchmark
		if *reverse == true {
			benchZRevRangeByRank()
			benchZRevRangeByScore()
		}

		if *del == true {
			benchZDel()
		}

		println("")
	}
}
<commit_msg>benchmark add GOMAXPROCS<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/siddontang/ledisdb/client/go/ledis"
	"math/rand"
	"runtime"
	"sync"
	"sync/atomic"
	"time"
)

var ip = flag.String("ip", "127.0.0.1", "redis/ledis/ssdb server ip")
var port = flag.Int("port", 6380, "redis/ledis/ssdb server port")
var number = flag.Int("n", 1000, "request number")
var clients = flag.Int("c", 50, "number of clients")
var reverse = flag.Bool("rev", false, "enable zset rev benchmark")
var round = flag.Int("r", 1, "benchmark round number")
var del = flag.Bool("del", true, "enable del benchmark")
var valueSize = flag.Int("vsize", 100, "kv value size")
var wg sync.WaitGroup

var client *ledis.Client

var loop int = 0

func waitBench(cmd string, args ...interface{}) {
	c := client.Get()
	defer c.Close()

	_, err := c.Do(cmd, args...)
	if err != nil {
		fmt.Printf("do %s error %s", cmd, err.Error())
		return
	}
}

func bench(cmd string, f func()) {
	wg.Add(*clients)

	t1 := time.Now()
	for i := 0; i < *clients; i++ {
		go func() {
			for j := 0; j < loop; j++ {
				f()
			}
			wg.Done()
		}()
	}

	wg.Wait()

	t2 := time.Now()

	fmt.Printf("%s: %0.2f op/s\n", cmd, (float64(*number) / t2.Sub(t1).Seconds()))
}

var kvSetBase int64 = 0
var kvGetBase int64 = 0
var kvIncrBase int64 = 0
var kvDelBase int64 = 0

func benchSet() {
	f := func() {
		value := make([]byte, *valueSize)
		n := atomic.AddInt64(&kvSetBase, 1)
		waitBench("set", n, value)
	}

	bench("set", f)
}

func benchGet() {
	f := func() {
		n := atomic.AddInt64(&kvGetBase, 1)
		waitBench("get", n)
	}

	bench("get", f)
}

func benchRandGet() {
	f := func() {
		n := rand.Int()
		waitBench("get", n)
	}

	bench("randget", f)
}

func benchDel() {
	f := func() {
		n := atomic.AddInt64(&kvDelBase, 1)
		waitBench("del", n)
	}

	bench("del", f)
}

func benchPushList() {
	f := func() {
		value := make([]byte, 100)
		waitBench("rpush", "mytestlist", value)
	}

	bench("rpush", f)
}

func benchRangeList10() {
	f := func() {
		waitBench("lrange", "mytestlist", 0, 10)
	}

	bench("lrange10", f)
}

func benchRangeList50() {
	f := func() {
		waitBench("lrange", "mytestlist", 0, 50)
	}

	bench("lrange50", f)
}

func benchRangeList100() {
	f := func() {
		waitBench("lrange", "mytestlist", 0, 100)
	}

	bench("lrange100", f)
}

func benchPopList() {
	f := func() {
		waitBench("lpop", "mytestlist")
	}

	bench("lpop", f)
}

var hashSetBase int64 = 0
var hashIncrBase int64 = 0
var hashGetBase int64 = 0
var hashDelBase int64 = 0

func benchHset() {
	f := func() {
		value := make([]byte, 100)

		n := atomic.AddInt64(&hashSetBase, 1)
		waitBench("hset", "myhashkey", n, value)
	}

	bench("hset", f)
}

func benchHGet() {
	f := func() {
		n := atomic.AddInt64(&hashGetBase, 1)
		waitBench("hget", "myhashkey", n)
	}

	bench("hget", f)
}

func benchHRandGet() {
	f := func() {
		n := rand.Int()
		waitBench("hget", "myhashkey", n)
	}

	bench("hrandget", f)
}

func benchHDel() {
	f := func() {
		n := atomic.AddInt64(&hashDelBase, 1)
		waitBench("hdel", "myhashkey", n)
	}

	bench("hdel", f)
}

var zsetAddBase int64 = 0
var zsetDelBase int64 = 0
var zsetIncrBase int64 = 0

func benchZAdd() {
	f := func() {
		member := make([]byte, 16)
		n := atomic.AddInt64(&zsetAddBase, 1)
		waitBench("zadd", "myzsetkey", n, member)
	}

	bench("zadd", f)
}

func benchZDel() {
	f := func() {
		n := atomic.AddInt64(&zsetDelBase, 1)
		waitBench("zrem", "myzsetkey", n)
	}

	bench("zrem", f)
}

func benchZIncr() {
	f := func() {
		n := atomic.AddInt64(&zsetIncrBase, 1)
		waitBench("zincrby", "myzsetkey", 1, n)
	}

	bench("zincrby", f)
}

func benchZRangeByScore() {
	f := func() {
		waitBench("zrangebyscore", "myzsetkey", 0, rand.Int(), "withscores", "limit", rand.Int()%100, 100)
	}

	bench("zrangebyscore", f)
}

func benchZRangeByRank() {
	f := func() {
		waitBench("zrange", "myzsetkey", 0, rand.Int()%100)
	}

	bench("zrange", f)
}

func benchZRevRangeByScore() {
	f := func() {
		waitBench("zrevrangebyscore", "myzsetkey", 0, rand.Int(), "withscores", "limit", rand.Int()%100, 100)
	}

	bench("zrevrangebyscore", f)
}

func benchZRevRangeByRank() {
	f := func() {
		waitBench("zrevrange", "myzsetkey", 0, rand.Int()%100)
	}

	bench("zrevrange", f)
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	flag.Parse()

	if *number <= 0 {
		panic("invalid number")
		return
	}

	if *clients <= 0 || *number < *clients {
		panic("invalid client number")
		return
	}

	loop = *number / *clients

	addr := fmt.Sprintf("%s:%d", *ip, *port)

	cfg := new(ledis.Config)
	cfg.Addr = addr
	cfg.MaxIdleConns = *clients
	client = ledis.NewClient(cfg)

	if *round <= 0 {
		*round = 1
	}

	for i := 0; i < *round; i++ {
		benchSet()
		benchGet()
		benchRandGet()

		if *del == true {
			benchDel()
		}

		benchPushList()
		benchRangeList10()
		benchRangeList50()
		benchRangeList100()

		if *del == true {
			benchPopList()
		}

		benchHset()
		benchHGet()
		benchHRandGet()

		if *del == true {
			benchHDel()
		}

		benchZAdd()
		benchZIncr()
		benchZRangeByRank()
		benchZRangeByScore()

		//rev is too slow in leveldb, rocksdb or other
		//maybe disable for huge data benchmark
		if *reverse == true {
			benchZRevRangeByRank()
			benchZRevRangeByScore()
		}

		if *del == true {
			benchZDel()
		}

		println("")
	}
}
<|endoftext|>