<commit_before>package powershell

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"strconv"
	"strings"

	"github.com/hashicorp/packer/packer/tmp"
)

const (
	powerShellFalse = "False"
	powerShellTrue  = "True"
)

func IsTrue(s string) bool {
	return strings.TrimSpace(s) == powerShellTrue
}

func IsFalse(s string) bool {
	return strings.TrimSpace(s) == powerShellFalse
}

type PowerShellCmd struct {
	Stdout io.Writer
	Stderr io.Writer
}

func (ps *PowerShellCmd) Run(fileContents string, params ...string) error {
	_, err := ps.Output(fileContents, params...)
	return err
}

// Output runs the PowerShell command and returns its standard output.
func (ps *PowerShellCmd) Output(fileContents string, params ...string) (string, error) {
	path, err := ps.getPowerShellPath()
	if err != nil {
		return "", fmt.Errorf("Cannot find PowerShell in the path")
	}

	filename, err := saveScript(fileContents)
	if err != nil {
		return "", err
	}

	debug := os.Getenv("PACKER_POWERSHELL_DEBUG") != ""
	verbose := debug || os.Getenv("PACKER_POWERSHELL_VERBOSE") != ""

	if !debug {
		defer os.Remove(filename)
	}

	args := createArgs(filename, params...)

	if verbose {
		log.Printf("Run: %s %s", path, args)
	}

	var stdout, stderr bytes.Buffer
	command := exec.Command(path, args...)
	command.Stdout = &stdout
	command.Stderr = &stderr

	err = command.Run()

	if ps.Stdout != nil {
		stdout.WriteTo(ps.Stdout)
	}

	if ps.Stderr != nil {
		stderr.WriteTo(ps.Stderr)
	}

	stderrString := strings.TrimSpace(stderr.String())

	if _, ok := err.(*exec.ExitError); ok {
		err = fmt.Errorf("PowerShell error: %s", stderrString)
	}

	if len(stderrString) > 0 {
		err = fmt.Errorf("PowerShell error: %s", stderrString)
	}

	stdoutString := strings.TrimSpace(stdout.String())

	if verbose && stdoutString != "" {
		log.Printf("stdout: %s", stdoutString)
	}

	// only write the stderr string if verbose because
	// the error string will already be in the err return value.
	if verbose && stderrString != "" {
		log.Printf("stderr: %s", stderrString)
	}

	return stdoutString, err
}

func IsPowershellAvailable() (bool, string, error) {
	path, err := exec.LookPath("powershell")
	if err != nil {
		return false, "", err
	} else {
		return true, path, err
	}
}

func (ps *PowerShellCmd) getPowerShellPath() (string, error) {
	powershellAvailable, path, err := IsPowershellAvailable()

	if !powershellAvailable {
		log.Fatal("Cannot find PowerShell in the path")
		return "", err
	}

	return path, nil
}

func saveScript(fileContents string) (string, error) {
	file, err := tmp.File("powershell")
	if err != nil {
		return "", err
	}

	_, err = file.Write([]byte(fileContents))
	if err != nil {
		return "", err
	}

	err = file.Close()
	if err != nil {
		return "", err
	}

	newFilename := file.Name() + ".ps1"
	err = os.Rename(file.Name(), newFilename)
	if err != nil {
		return "", err
	}

	return newFilename, nil
}

func createArgs(filename string, params ...string) []string {
	args := make([]string, len(params)+5)
	args[0] = "-ExecutionPolicy"
	args[1] = "Bypass"

	args[2] = "-NoProfile"

	args[3] = "-File"
	args[4] = filename

	for key, value := range params {
		args[key+5] = value
	}

	return args
}

func GetHostAvailableMemory() float64 {

	var script = "(Get-WmiObject Win32_OperatingSystem).FreePhysicalMemory / 1024"

	var ps PowerShellCmd
	output, _ := ps.Output(script)

	freeMB, _ := strconv.ParseFloat(output, 64)

	return freeMB
}

func GetHostName(ip string) (string, error) {

	var script = `
param([string]$ip)
try {
  $HostName = [System.Net.Dns]::GetHostEntry($ip).HostName
  if ($HostName -ne $null) {
    $HostName = $HostName.Split('.')[0]
  }
  $HostName
} catch { }
`

	//
	var ps PowerShellCmd
	cmdOut, err := ps.Output(script, ip)
	if err != nil {
		return "", err
	}

	return cmdOut, nil
}

func IsCurrentUserAnAdministrator() (bool, error) {
	var script = `
$identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$principal = new-object System.Security.Principal.WindowsPrincipal($identity)
$administratorRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator
return $principal.IsInRole($administratorRole)
`

	var ps PowerShellCmd
	cmdOut, err := ps.Output(script)
	if err != nil {
		return false, err
	}

	res := strings.TrimSpace(cmdOut)
	return res == powerShellTrue, nil
}

func ModuleExists(moduleName string) (bool, error) {

	var script = `
param([string]$moduleName)
(Get-Module -Name $moduleName) -ne $null
`
	var ps PowerShellCmd
	cmdOut, err := ps.Output(script)
	if err != nil {
		return false, err
	}

	res := strings.TrimSpace(cmdOut)

	if res == powerShellFalse {
		err := fmt.Errorf("PowerShell %s module is not loaded. Make sure %s feature is on.", moduleName, moduleName)
		return false, err
	}

	return true, nil
}

func HasVirtualMachineVirtualizationExtensions() (bool, error) {

	var script = `
(GET-Command Hyper-V\Set-VMProcessor).parameters.keys -contains "ExposeVirtualizationExtensions"
`

	var ps PowerShellCmd
	cmdOut, err := ps.Output(script)

	if err != nil {
		return false, err
	}

	var hasVirtualMachineVirtualizationExtensions = strings.TrimSpace(cmdOut) == "True"
	return hasVirtualMachineVirtualizationExtensions, err
}

func DoesVirtualMachineExist(vmName string) (bool, error) {

	var script = `
param([string]$vmName)
return (Hyper-V\Get-VM -Name $vmName | ?{$_.Name -eq $vmName}) -ne $null
`

	var ps PowerShellCmd
	cmdOut, err := ps.Output(script, vmName)

	if err != nil {
		return false, err
	}

	var exists = strings.TrimSpace(cmdOut) == "True"
	return exists, err
}

func DoesVirtualMachineSnapshotExist(vmName string, snapshotName string) (bool, error) {

	var script = `
param([string]$vmName, [string]$snapshotName)
return (Hyper-V\Get-VMSnapshot -VMName $vmName | ?{$_.Name -eq $snapshotName}) -ne $null
`

	var ps PowerShellCmd
	cmdOut, err := ps.Output(script, vmName, snapshotName)

	if err != nil {
		return false, err
	}

	var exists = strings.TrimSpace(cmdOut) == "True"
	return exists, err
}

func IsVirtualMachineOn(vmName string) (bool, error) {

	var script = `
param([string]$vmName)
$vm = Hyper-V\Get-VM -Name $vmName -ErrorAction SilentlyContinue
$vm.State -eq [Microsoft.HyperV.PowerShell.VMState]::Running
`

	var ps PowerShellCmd
	cmdOut, err := ps.Output(script, vmName)

	if err != nil {
		return false, err
	}

	var isRunning = strings.TrimSpace(cmdOut) == "True"
	return isRunning, err
}

func GetVirtualMachineGeneration(vmName string) (uint, error) {
	var script = `
param([string]$vmName)
$generation = Hyper-V\Get-Vm -Name $vmName | %{$_.Generation}
if (!$generation){
    $generation = 1
}
return $generation
`
	var ps PowerShellCmd
	cmdOut, err := ps.Output(script, vmName)

	if err != nil {
		return 0, err
	}

	generationUint32, err := strconv.ParseUint(strings.TrimSpace(string(cmdOut)), 10, 32)

	if err != nil {
		return 0, err
	}

	generation := uint(generationUint32)

	return generation, err
}

func SetUnattendedProductKey(path string, productKey string) error {

	var script = `
param([string]$path,[string]$productKey)

$unattend = [xml](Get-Content -Path $path)
$ns = @{ un = 'urn:schemas-microsoft-com:unattend' }

$setupNode = $unattend |
  Select-Xml -XPath '//un:settings[@pass = "specialize"]/un:component[@name = "Microsoft-Windows-Shell-Setup"]' -Namespace $ns |
  Select-Object -ExpandProperty Node

$productKeyNode = $setupNode |
  Select-Xml -XPath '//un:ProductKey' -Namespace $ns |
  Select-Object -ExpandProperty Node

if ($productKeyNode -eq $null) {
    $productKeyNode = $unattend.CreateElement('ProductKey', $ns.un)
    [Void]$setupNode.AppendChild($productKeyNode)
}

$productKeyNode.InnerText = $productKey

$unattend.Save($path)
`

	var ps PowerShellCmd
	err := ps.Run(script, path, productKey)
	return err
}
<commit_msg>common/powershell: fix dropped error<commit_after>package powershell

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"strconv"
	"strings"

	"github.com/hashicorp/packer/packer/tmp"
)

const (
	powerShellFalse = "False"
	powerShellTrue  = "True"
)

func IsTrue(s string) bool {
	return strings.TrimSpace(s) == powerShellTrue
}

func IsFalse(s string) bool {
	return strings.TrimSpace(s) == powerShellFalse
}

type PowerShellCmd struct {
	Stdout io.Writer
	Stderr io.Writer
}

func (ps *PowerShellCmd) Run(fileContents string, params ...string) error {
	_, err := ps.Output(fileContents, params...)
	return err
}

// Output runs the PowerShell command and returns its standard output.
func (ps *PowerShellCmd) Output(fileContents string, params ...string) (string, error) {
	path, err := ps.getPowerShellPath()
	if err != nil {
		return "", fmt.Errorf("Cannot find PowerShell in the path")
	}

	filename, err := saveScript(fileContents)
	if err != nil {
		return "", err
	}

	debug := os.Getenv("PACKER_POWERSHELL_DEBUG") != ""
	verbose := debug || os.Getenv("PACKER_POWERSHELL_VERBOSE") != ""

	if !debug {
		defer os.Remove(filename)
	}

	args := createArgs(filename, params...)

	if verbose {
		log.Printf("Run: %s %s", path, args)
	}

	var stdout, stderr bytes.Buffer
	command := exec.Command(path, args...)
	command.Stdout = &stdout
	command.Stderr = &stderr

	err = command.Run()

	if ps.Stdout != nil {
		stdout.WriteTo(ps.Stdout)
	}

	if ps.Stderr != nil {
		stderr.WriteTo(ps.Stderr)
	}

	stderrString := strings.TrimSpace(stderr.String())

	if _, ok := err.(*exec.ExitError); ok {
		err = fmt.Errorf("PowerShell error: %s", stderrString)
	}

	if len(stderrString) > 0 {
		err = fmt.Errorf("PowerShell error: %s", stderrString)
	}

	stdoutString := strings.TrimSpace(stdout.String())

	if verbose && stdoutString != "" {
		log.Printf("stdout: %s", stdoutString)
	}

	// only write the stderr string if verbose because
	// the error string will already be in the err return value.
	if verbose && stderrString != "" {
		log.Printf("stderr: %s", stderrString)
	}

	return stdoutString, err
}

func IsPowershellAvailable() (bool, string, error) {
	path, err := exec.LookPath("powershell")
	if err != nil {
		return false, "", err
	} else {
		return true, path, err
	}
}

func (ps *PowerShellCmd) getPowerShellPath() (string, error) {
	powershellAvailable, path, err := IsPowershellAvailable()
	if err != nil {
		log.Fatalf("IsPowershellAvailable: %v", err)
	}
	if !powershellAvailable {
		log.Fatal("Cannot find PowerShell in the path")
		return "", err
	}

	return path, nil
}

func saveScript(fileContents string) (string, error) {
	file, err := tmp.File("powershell")
	if err != nil {
		return "", err
	}

	_, err = file.Write([]byte(fileContents))
	if err != nil {
		return "", err
	}

	err = file.Close()
	if err != nil {
		return "", err
	}

	newFilename := file.Name() + ".ps1"
	err = os.Rename(file.Name(), newFilename)
	if err != nil {
		return "", err
	}

	return newFilename, nil
}

func createArgs(filename string, params ...string) []string {
	args := make([]string, len(params)+5)
	args[0] = "-ExecutionPolicy"
	args[1] = "Bypass"

	args[2] = "-NoProfile"

	args[3] = "-File"
	args[4] = filename

	for key, value := range params {
		args[key+5] = value
	}

	return args
}

func GetHostAvailableMemory() float64 {

	var script = "(Get-WmiObject Win32_OperatingSystem).FreePhysicalMemory / 1024"

	var ps PowerShellCmd
	output, _ := ps.Output(script)

	freeMB, _ := strconv.ParseFloat(output, 64)

	return freeMB
}

func GetHostName(ip string) (string, error) {

	var script = `
param([string]$ip)
try {
  $HostName = [System.Net.Dns]::GetHostEntry($ip).HostName
  if ($HostName -ne $null) {
    $HostName = $HostName.Split('.')[0]
  }
  $HostName
} catch { }
`

	//
	var ps PowerShellCmd
	cmdOut, err := ps.Output(script, ip)
	if err != nil {
		return "", err
	}

	return cmdOut, nil
}

func IsCurrentUserAnAdministrator() (bool, error) {
	var script = `
$identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$principal = new-object System.Security.Principal.WindowsPrincipal($identity)
$administratorRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator
return $principal.IsInRole($administratorRole)
`

	var ps PowerShellCmd
	cmdOut, err := ps.Output(script)
	if err != nil {
		return false, err
	}

	res := strings.TrimSpace(cmdOut)
	return res == powerShellTrue, nil
}

func ModuleExists(moduleName string) (bool, error) {

	var script = `
param([string]$moduleName)
(Get-Module -Name $moduleName) -ne $null
`
	var ps PowerShellCmd
	cmdOut, err := ps.Output(script)
	if err != nil {
		return false, err
	}

	res := strings.TrimSpace(cmdOut)

	if res == powerShellFalse {
		err := fmt.Errorf("PowerShell %s module is not loaded. Make sure %s feature is on.", moduleName, moduleName)
		return false, err
	}

	return true, nil
}

func HasVirtualMachineVirtualizationExtensions() (bool, error) {

	var script = `
(GET-Command Hyper-V\Set-VMProcessor).parameters.keys -contains "ExposeVirtualizationExtensions"
`

	var ps PowerShellCmd
	cmdOut, err := ps.Output(script)

	if err != nil {
		return false, err
	}

	var hasVirtualMachineVirtualizationExtensions = strings.TrimSpace(cmdOut) == "True"
	return hasVirtualMachineVirtualizationExtensions, err
}

func DoesVirtualMachineExist(vmName string) (bool, error) {

	var script = `
param([string]$vmName)
return (Hyper-V\Get-VM -Name $vmName | ?{$_.Name -eq $vmName}) -ne $null
`

	var ps PowerShellCmd
	cmdOut, err := ps.Output(script, vmName)

	if err != nil {
		return false, err
	}

	var exists = strings.TrimSpace(cmdOut) == "True"
	return exists, err
}

func DoesVirtualMachineSnapshotExist(vmName string, snapshotName string) (bool, error) {

	var script = `
param([string]$vmName, [string]$snapshotName)
return (Hyper-V\Get-VMSnapshot -VMName $vmName | ?{$_.Name -eq $snapshotName}) -ne $null
`

	var ps PowerShellCmd
	cmdOut, err := ps.Output(script, vmName, snapshotName)

	if err != nil {
		return false, err
	}

	var exists = strings.TrimSpace(cmdOut) == "True"
	return exists, err
}

func IsVirtualMachineOn(vmName string) (bool, error) {

	var script = `
param([string]$vmName)
$vm = Hyper-V\Get-VM -Name $vmName -ErrorAction SilentlyContinue
$vm.State -eq [Microsoft.HyperV.PowerShell.VMState]::Running
`

	var ps PowerShellCmd
	cmdOut, err := ps.Output(script, vmName)

	if err != nil {
		return false, err
	}

	var isRunning = strings.TrimSpace(cmdOut) == "True"
	return isRunning, err
}

func GetVirtualMachineGeneration(vmName string) (uint, error) {
	var script = `
param([string]$vmName)
$generation = Hyper-V\Get-Vm -Name $vmName | %{$_.Generation}
if (!$generation){
    $generation = 1
}
return $generation
`
	var ps PowerShellCmd
	cmdOut, err := ps.Output(script, vmName)

	if err != nil {
		return 0, err
	}

	generationUint32, err := strconv.ParseUint(strings.TrimSpace(string(cmdOut)), 10, 32)

	if err != nil {
		return 0, err
	}

	generation := uint(generationUint32)

	return generation, err
}

func SetUnattendedProductKey(path string, productKey string) error {

	var script = `
param([string]$path,[string]$productKey)

$unattend = [xml](Get-Content -Path $path)
$ns = @{ un = 'urn:schemas-microsoft-com:unattend' }

$setupNode = $unattend |
  Select-Xml -XPath '//un:settings[@pass = "specialize"]/un:component[@name = "Microsoft-Windows-Shell-Setup"]' -Namespace $ns |
  Select-Object -ExpandProperty Node

$productKeyNode = $setupNode |
  Select-Xml -XPath '//un:ProductKey' -Namespace $ns |
  Select-Object -ExpandProperty Node

if ($productKeyNode -eq $null) {
    $productKeyNode = $unattend.CreateElement('ProductKey', $ns.un)
    [Void]$setupNode.AppendChild($productKeyNode)
}

$productKeyNode.InnerText = $productKey

$unattend.Save($path)
`

	var ps PowerShellCmd
	err := ps.Run(script, path, productKey)
	return err
}
<|endoftext|>


<commit_before>package readon

import (
	"code.google.com/p/go-html-transform/h5"
	"code.google.com/p/go-html-transform/html/transform"
	"code.google.com/p/go.net/html"
	"fmt"
	"io"
	"strings"
)

func Readon(reader io.Reader) (string, error) {
	tree, _ := h5.New(reader)
	t := transform.New(tree)
	removeScripts(t)
	removeUnlikely(t)
	removeCss(t)
	removeBr(t)
	removeTags(t, []string{"form", "h1", "object", "iframe"})
	// TODO(pascalj): replace double BR with P
	return t.String(), nil
}

func removeScripts(t *transform.Transformer) {
	t.Apply(transform.Replace(), "script")
	t.Apply(transform.Replace(), "noscript")
}

func removeCss(t *transform.Transformer) {
	t.Apply(transform.Replace(), "link")
	t.Apply(transform.ModifyAttrib("style", ""), "[style]")
}

func removeUnlikely(t *transform.Transformer) {
	unlikelyClasses := ".combx, .comment, .community, .disqus, .extra, .foot, .header, .menu, .remark, .rss, .shoutbox, .sidebar, .sponsor, .ad-break, .agegate, .pagination, .pager, .popup, .tweet, .twitter"
	unlikelyIds := "#combx, #comment, #community, #disqus, #extra, #foot, #header, #menu, #remark, #rss, #shoutbox, #sidebar, #sponsor, #ad-break, #agegate, #pagination, #pager, #popup, #tweet, #twitter"
	applyGroup(unlikelyClasses, func(sel string) { t.Apply(transform.Replace(), sel) })
	applyGroup(unlikelyIds, func(sel string) { t.Apply(transform.Replace(), sel) })
}

func removeBr(t *transform.Transformer) {
	t.Apply(transform.Replace(), "br")
}

func removeTags(t *transform.Transformer, tags []string) {
	for _, tag := range tags {
		t.Apply(transform.Replace(), tag)
	}
}

func linkDensity(tree *h5.Tree) float32 {
	var textLength, linkLength int
	textLength = len(innerText(tree))
	h5.WalkNodes(tree.Top(), func(node *html.Node) {
		if node.Type == html.ElementNode && node.Data == "a" {
			subTree := h5.NewTree(node)
			linkLength = linkLength + len(innerText(&subTree))
		}
	})
	return float32(linkLength) / float32(textLength)
}

func innerText(tree *h5.Tree) string {
	var content string
	tree.Walk(func(node *html.Node) {
		if node.Type == html.TextNode {
			content = content + node.Data
		}
	})
	return content
}

func count(tree *h5.Tree, tag string) int {
	var total int
	tree.Walk(func(node *html.Node) {
		if node.Type == html.ElementNode && node.Data == tag {
			total = total + 1
		}
	})
	return total
}

func applyGroup(group string, applyFunc func(string)) {
	for _, sel := range strings.Split(group, ",") {
		applyFunc(strings.Trim(sel, " \t"))
	}
}
<commit_msg>Add calculation for the top candidate.<commit_after>package readon

import (
	"code.google.com/p/go-html-transform/h5"
	"code.google.com/p/go-html-transform/html/transform"
	"code.google.com/p/go.net/html"
	"io"
	"math"
	"strings"
)

func Readon(reader io.Reader) (string, error) {
	tree, _ := h5.New(reader)
	t := transform.New(tree)
	removeScripts(t)
	removeUnlikely(t)
	removeCss(t)
	// TODO(pascalj): replace double BR with P
	removeBr(t)
	removeTags(t, []string{"form", "h1", "object", "iframe"})
	topTag := topCancidate(tree)
	return innerText(topTag), nil
}

func removeScripts(t *transform.Transformer) {
	t.Apply(transform.Replace(), "script")
	t.Apply(transform.Replace(), "noscript")
}

func removeCss(t *transform.Transformer) {
	t.Apply(transform.Replace(), "link")
	t.Apply(transform.ModifyAttrib("style", ""), "[style]")
}

func removeUnlikely(t *transform.Transformer) {
	unlikelyClasses := ".combx, .comment, .community, .disqus, .extra, .foot, .header, .menu, .remark, .rss, .shoutbox, .sidebar, .sponsor, .ad-break, .agegate, .pagination, .pager, .popup, .tweet, .twitter"
	unlikelyIds := "#combx, #comment, #community, #disqus, #extra, #foot, #header, #menu, #remark, #rss, #shoutbox, #sidebar, #sponsor, #ad-break, #agegate, #pagination, #pager, #popup, #tweet, #twitter"
	applyGroup(unlikelyClasses, func(sel string) { t.Apply(transform.Replace(), sel) })
	applyGroup(unlikelyIds, func(sel string) { t.Apply(transform.Replace(), sel) })
}

func removeBr(t *transform.Transformer) {
	t.Apply(transform.Replace(), "br")
}

func removeTags(t *transform.Transformer, tags []string) {
	for _, tag := range tags {
		t.Apply(transform.Replace(), tag)
	}
}

func topCancidate(tree *h5.Tree) *html.Node {
	ratings := rateDocument(tree.Top())
	var topCancidate *html.Node

	// Weight the links by linkDensity (less links is better) and get the top candidate
	for node, score := range ratings {
		if topCancidate == nil || score > ratings[topCancidate] {
			topCancidate = node
		}
	}

	return topCancidate
}

func rateDocument(node *html.Node) map[*html.Node]int {
	ratings := make(map[*html.Node]int)
	h5.WalkNodes(node, func(node *html.Node) {

		// Only consider paragraphs
		if node.Type != html.ElementNode || node.Data != "p" {
			return
		}
		score := rateNode(node)
		if node.Parent != nil {
			if _, containsNode := ratings[node.Parent]; !containsNode {
				ratings[node.Parent] = 0
			}
			ratings[node.Parent] += int(float32(score) * (1.0 - linkDensity(node.Parent)))
		}
		if node.Parent != nil && node.Parent.Parent != nil {
			if _, containsNode := ratings[node.Parent.Parent]; !containsNode {
				ratings[node.Parent.Parent] = 0
			}
			ratings[node.Parent.Parent] += int(float32(score)*(1.0-linkDensity(node.Parent.Parent))) / 2
		}
	})
	return ratings
}

func rateNode(node *html.Node) int {
	score := 1
	innerText := innerText(node)

	// Don't even consider short paragraphs
	if len(innerText) <= 25 {
		return 0
	}

	// Add a point for ervery comma
	score += strings.Count(innerText, ",")

	// Add up to three points for each 100 chars
	if hChars := int(math.Floor(float64(len(innerText)) / 100)); hChars <= 3 {
		score += hChars
	} else {
		score += 3
	}
	return score
}

func linkDensity(node *html.Node) float32 {
	var textLength, linkLength int
	textLength = len(innerText(node))
	h5.WalkNodes(node, func(node *html.Node) {
		if node.Type == html.ElementNode && node.Data == "a" {
			linkLength = linkLength + len(innerText(node))
		}
	})
	return float32(linkLength) / float32(textLength)
}

func innerText(node *html.Node) string {
	var content string
	h5.WalkNodes(node, func(node *html.Node) {
		if node.Type == html.TextNode {
			content = content + node.Data
		}
	})
	return content
}

func count(node *html.Node, tag string) int {
	var total int
	h5.WalkNodes(node, func(node *html.Node) {
		if node.Type == html.ElementNode && node.Data == tag {
			total = total + 1
		}
	})
	return total
}

func applyGroup(group string, applyFunc func(string)) {
	for _, sel := range strings.Split(group, ",") {
		applyFunc(strings.Trim(sel, " \t"))
	}
}
<|endoftext|>


<commit_before>package cmd

/*
 Copyright 2017-2018 Crunchy Data Solutions, Inc.
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

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

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

import (
	"encoding/json"
	"fmt"
	log "github.com/Sirupsen/logrus"
	msgs "github.com/crunchydata/postgres-operator/apiservermsgs"
	"github.com/crunchydata/postgres-operator/pgo/util"
	commonutil "github.com/crunchydata/postgres-operator/util"
	"github.com/spf13/cobra"
	"net/http"
	"os"
	"strconv"
)

var ReplicaCount int
var ScaleDownTarget string

var scaleCmd = &cobra.Command{
	Use:   "scale",
	Short: "Scale a PostgreSQL cluster",
	Long: `The scale command allows you to adjust a Cluster's replica configuration. For example:

	pgo scale mycluster --replica-count=1

	To list targetable replicas:
	pgo scale mycluster --query

	To scale down a specific replica:
	pgo scale mycluster --scale-down-target=mycluster-replica-xxxx`,
	Run: func(cmd *cobra.Command, args []string) {
		log.Debug("scale called")

		if len(args) == 0 {
			fmt.Println(`Error: You must specify the clusters to scale.`)
		} else {
			if Query {
				queryCluster(args)
			} else if ScaleDownTarget != "" {
				if util.AskForConfirmation(NoPrompt, "") {
				} else {
					fmt.Println("Aborting...")
					os.Exit(2)
				}
				scaleDownCluster(args[0])
			} else {
				if ReplicaCount < 1 {
					fmt.Println("Error: --replica-count is required to be greater than 0, the default is 1 if not specified")
					return
				}
				if util.AskForConfirmation(NoPrompt, "") {
				} else {
					fmt.Println("Aborting...")
					os.Exit(2)
				}
				scaleCluster(args)
			}
		}
	},
}

func init() {
	RootCmd.AddCommand(scaleCmd)

	scaleCmd.Flags().StringVarP(&ScaleDownTarget, "scale-down-target", "", "", "The name of a replica to delete.")
	scaleCmd.Flags().StringVarP(&ServiceType, "service-type", "", "", "The service type to use in the replica Service. If not set, the default in pgo.yaml will be used.")
	scaleCmd.Flags().StringVarP(&CCPImageTag, "ccp-image-tag", "c", "", "The CCPImageTag to use for cluster creation. If specified, overrides the .pgo.yaml setting.")
	scaleCmd.Flags().BoolVarP(&Query, "query", "", false, "Prints the list of targetable replica candidates.")
	scaleCmd.Flags().BoolVarP(&DeleteData, "delete-data", "", false, "Causes the data for the scaled down replica to be removed permanently.")
	scaleCmd.Flags().BoolVarP(&NoPrompt, "no-prompt", "n", false, "No command line confirmation.")
	scaleCmd.Flags().StringVarP(&Target, "target", "", "", "The replica target which the scaling will occur on. Only applies when --replica-count=-1.")
	scaleCmd.Flags().IntVarP(&ReplicaCount, "replica-count", "r", 1, "The replica count to apply to the clusters.")
	scaleCmd.Flags().StringVarP(&ContainerResources, "resources-config", "", "", "he name of a container resource configuration in pgo.yaml that holds CPU and memory requests and limits.")
	scaleCmd.Flags().StringVarP(&StorageConfig, "storage-config", "", "", "The name of a Storage config in pgo.yaml to use for the replica storage.")
	scaleCmd.Flags().StringVarP(&NodeLabel, "node-label", "", "", "The node label (key) to use in placing the primary database. If not set, any node is used.")

}

func scaleCluster(args []string) {

	var url string

	for _, arg := range args {
		log.Debugf(" %s ReplicaCount is %d\n", arg, ReplicaCount)
		url = APIServerURL + "/clusters/scale/" + arg + "?replica-count=" + strconv.Itoa(ReplicaCount) + "&resources-config=" + ContainerResources + "&storage-config=" + StorageConfig + "&node-label=" + NodeLabel + "&version=" + msgs.PGO_VERSION + "&ccp-image-tag=" + CCPImageTag + "&service-type=" + ServiceType
		log.Debug(url)

		req, err := http.NewRequest("GET", url, nil)
		if err != nil {
			fmt.Println("Error: NewRequest: ", err)
			return
		}

		req.Header.Set("Content-Type", "application/json")
		req.SetBasicAuth(BasicAuthUsername, BasicAuthPassword)

		resp, err := httpclient.Do(req)
		if err != nil {
			fmt.Println("Error: Do: ", err)
			return
		}
		log.Debugf("%v\n", resp)
		StatusCheck(resp)

		defer resp.Body.Close()

		var response msgs.ClusterScaleResponse
		if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
			log.Printf("%v\n", resp.Body)
			fmt.Println("Error: ", err)
			log.Println(err)
			return
		}

		if response.Status.Code == msgs.Ok {
			for _, v := range response.Results {
				fmt.Println(v)
			}
		} else {
			fmt.Println("Error: " + response.Status.Msg)
		}

	}
}

func queryCluster(args []string) {

	var url string
	for _, arg := range args {
		url = APIServerURL + "/scale/" + arg + "?version=" + msgs.PGO_VERSION
		log.Debug(url)

		req, err := http.NewRequest("GET", url, nil)
		if err != nil {
			fmt.Println("Error: NewRequest: ", err)
			return
		}

		req.Header.Set("Content-Type", "application/json")
		req.SetBasicAuth(BasicAuthUsername, BasicAuthPassword)

		resp, err := httpclient.Do(req)
		if err != nil {
			fmt.Println("Error: Do: ", err)
			return
		}
		log.Debugf("%v\n", resp)
		StatusCheck(resp)

		defer resp.Body.Close()

		var response msgs.ScaleQueryResponse
		if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
			log.Printf("%v\n", resp.Body)
			fmt.Println("Error: ", err)
			log.Println(err)
			return
		}

		fmt.Println("jeff here we are")
		if response.Status.Code == msgs.Ok {
			if len(response.Targets) > 0 {
				fmt.Println("Replica targets include:")
				for i := 0; i < len(response.Targets); i++ {
					printScaleTarget(response.Targets[i])
				}
			}

			for _, v := range response.Results {
				fmt.Println(v)
			}
		} else {
			fmt.Println("Error: " + response.Status.Msg)
		}

	}
}

func scaleDownCluster(clusterName string) {

	var url string
	url = APIServerURL + "/scaledown/" + clusterName + "?version=" + msgs.PGO_VERSION + "&" + commonutil.LABEL_REPLICA_NAME + "=" + ScaleDownTarget + "&" + commonutil.LABEL_DELETE_DATA + "=" + strconv.FormatBool(DeleteData)
	log.Debug(url)

	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		fmt.Println("Error: NewRequest: ", err)
		return
	}

	req.Header.Set("Content-Type", "application/json")
	req.SetBasicAuth(BasicAuthUsername, BasicAuthPassword)

	resp, err := httpclient.Do(req)
	if err != nil {
		fmt.Println("Error: Do: ", err)
		return
	}
	log.Debugf("%v\n", resp)
	StatusCheck(resp)

	defer resp.Body.Close()

	var response msgs.ScaleDownResponse
	if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
		log.Printf("%v\n", resp.Body)
		fmt.Println("Error: ", err)
		log.Println(err)
		return
	}

	if response.Status.Code == msgs.Ok {
		for _, v := range response.Results {
			fmt.Println(v)
		}
	} else {
		fmt.Println("Error: " + response.Status.Msg)
	}

}

func printScaleTarget(target msgs.ScaleQueryTargetSpec) {
	fmt.Printf("\t%s (%s) (%s)\n", target.Name, target.ReadyStatus, target.Node)
}
<commit_msg>cleanup scale command output<commit_after>package cmd

/*
 Copyright 2017-2018 Crunchy Data Solutions, Inc.
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

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

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

import (
	"encoding/json"
	"fmt"
	log "github.com/Sirupsen/logrus"
	msgs "github.com/crunchydata/postgres-operator/apiservermsgs"
	"github.com/crunchydata/postgres-operator/pgo/util"
	commonutil "github.com/crunchydata/postgres-operator/util"
	"github.com/spf13/cobra"
	"net/http"
	"os"
	"strconv"
)

var ReplicaCount int
var ScaleDownTarget string

var scaleCmd = &cobra.Command{
	Use:   "scale",
	Short: "Scale a PostgreSQL cluster",
	Long: `The scale command allows you to adjust a Cluster's replica configuration. For example:

	pgo scale mycluster --replica-count=1

	To list targetable replicas:
	pgo scale mycluster --query

	To scale down a specific replica:
	pgo scale mycluster --scale-down-target=mycluster-replica-xxxx`,
	Run: func(cmd *cobra.Command, args []string) {
		log.Debug("scale called")

		if len(args) == 0 {
			fmt.Println(`Error: You must specify the clusters to scale.`)
		} else {
			if Query {
				queryCluster(args)
			} else if ScaleDownTarget != "" {
				if util.AskForConfirmation(NoPrompt, "") {
				} else {
					fmt.Println("Aborting...")
					os.Exit(2)
				}
				scaleDownCluster(args[0])
			} else {
				if ReplicaCount < 1 {
					fmt.Println("Error: --replica-count is required to be greater than 0, the default is 1 if not specified")
					return
				}
				if util.AskForConfirmation(NoPrompt, "") {
				} else {
					fmt.Println("Aborting...")
					os.Exit(2)
				}
				scaleCluster(args)
			}
		}
	},
}

func init() {
	RootCmd.AddCommand(scaleCmd)

	scaleCmd.Flags().StringVarP(&ScaleDownTarget, "scale-down-target", "", "", "The name of a replica to delete.")
	scaleCmd.Flags().StringVarP(&ServiceType, "service-type", "", "", "The service type to use in the replica Service. If not set, the default in pgo.yaml will be used.")
	scaleCmd.Flags().StringVarP(&CCPImageTag, "ccp-image-tag", "c", "", "The CCPImageTag to use for cluster creation. If specified, overrides the .pgo.yaml setting.")
	scaleCmd.Flags().BoolVarP(&Query, "query", "", false, "Prints the list of targetable replica candidates.")
	scaleCmd.Flags().BoolVarP(&DeleteData, "delete-data", "", false, "Causes the data for the scaled down replica to be removed permanently.")
	scaleCmd.Flags().BoolVarP(&NoPrompt, "no-prompt", "n", false, "No command line confirmation.")
	scaleCmd.Flags().StringVarP(&Target, "target", "", "", "The replica target which the scaling will occur on. Only applies when --replica-count=-1.")
	scaleCmd.Flags().IntVarP(&ReplicaCount, "replica-count", "r", 1, "The replica count to apply to the clusters.")
	scaleCmd.Flags().StringVarP(&ContainerResources, "resources-config", "", "", "he name of a container resource configuration in pgo.yaml that holds CPU and memory requests and limits.")
	scaleCmd.Flags().StringVarP(&StorageConfig, "storage-config", "", "", "The name of a Storage config in pgo.yaml to use for the replica storage.")
	scaleCmd.Flags().StringVarP(&NodeLabel, "node-label", "", "", "The node label (key) to use in placing the primary database. If not set, any node is used.")

}

func scaleCluster(args []string) {

	var url string

	for _, arg := range args {
		log.Debugf(" %s ReplicaCount is %d\n", arg, ReplicaCount)
		url = APIServerURL + "/clusters/scale/" + arg + "?replica-count=" + strconv.Itoa(ReplicaCount) + "&resources-config=" + ContainerResources + "&storage-config=" + StorageConfig + "&node-label=" + NodeLabel + "&version=" + msgs.PGO_VERSION + "&ccp-image-tag=" + CCPImageTag + "&service-type=" + ServiceType
		log.Debug(url)

		req, err := http.NewRequest("GET", url, nil)
		if err != nil {
			fmt.Println("Error: NewRequest: ", err)
			return
		}

		req.Header.Set("Content-Type", "application/json")
		req.SetBasicAuth(BasicAuthUsername, BasicAuthPassword)

		resp, err := httpclient.Do(req)
		if err != nil {
			fmt.Println("Error: Do: ", err)
			return
		}
		log.Debugf("%v\n", resp)
		StatusCheck(resp)

		defer resp.Body.Close()

		var response msgs.ClusterScaleResponse
		if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
			log.Printf("%v\n", resp.Body)
			fmt.Println("Error: ", err)
			log.Println(err)
			return
		}

		if response.Status.Code == msgs.Ok {
			for _, v := range response.Results {
				fmt.Println(v)
			}
		} else {
			fmt.Println("Error: " + response.Status.Msg)
		}

	}
}

func queryCluster(args []string) {

	var url string
	for _, arg := range args {
		url = APIServerURL + "/scale/" + arg + "?version=" + msgs.PGO_VERSION
		log.Debug(url)

		req, err := http.NewRequest("GET", url, nil)
		if err != nil {
			fmt.Println("Error: NewRequest: ", err)
			return
		}

		req.Header.Set("Content-Type", "application/json")
		req.SetBasicAuth(BasicAuthUsername, BasicAuthPassword)

		resp, err := httpclient.Do(req)
		if err != nil {
			fmt.Println("Error: Do: ", err)
			return
		}
		log.Debugf("%v\n", resp)
		StatusCheck(resp)

		defer resp.Body.Close()

		var response msgs.ScaleQueryResponse
		if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
			log.Printf("%v\n", resp.Body)
			fmt.Println("Error: ", err)
			log.Println(err)
			return
		}

		if response.Status.Code == msgs.Ok {
			if len(response.Targets) > 0 {
				fmt.Println("Replica targets include:")
				for i := 0; i < len(response.Targets); i++ {
					printScaleTarget(response.Targets[i])
				}
			}

			for _, v := range response.Results {
				fmt.Println(v)
			}
		} else {
			fmt.Println("Error: " + response.Status.Msg)
		}

	}
}

func scaleDownCluster(clusterName string) {

	var url string
	url = APIServerURL + "/scaledown/" + clusterName + "?version=" + msgs.PGO_VERSION + "&" + commonutil.LABEL_REPLICA_NAME + "=" + ScaleDownTarget + "&" + commonutil.LABEL_DELETE_DATA + "=" + strconv.FormatBool(DeleteData)
	log.Debug(url)

	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		fmt.Println("Error: NewRequest: ", err)
		return
	}

	req.Header.Set("Content-Type", "application/json")
	req.SetBasicAuth(BasicAuthUsername, BasicAuthPassword)

	resp, err := httpclient.Do(req)
	if err != nil {
		fmt.Println("Error: Do: ", err)
		return
	}
	log.Debugf("%v\n", resp)
	StatusCheck(resp)

	defer resp.Body.Close()

	var response msgs.ScaleDownResponse
	if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
		log.Printf("%v\n", resp.Body)
		fmt.Println("Error: ", err)
		log.Println(err)
		return
	}

	if response.Status.Code == msgs.Ok {
		for _, v := range response.Results {
			fmt.Println(v)
		}
	} else {
		fmt.Println("Error: " + response.Status.Msg)
	}

}

func printScaleTarget(target msgs.ScaleQueryTargetSpec) {
	fmt.Printf("\t%s (%s) (%s)\n", target.Name, target.ReadyStatus, target.Node)
}
<|endoftext|>


<commit_before>package git

/*
#include <git2.h>
*/
import "C"
import (
	"errors"
	"runtime"
	"unsafe"
)

// RebaseOperationType is the type of rebase operation
type RebaseOperationType uint

const (
	// RebaseOperationPick The given commit is to be cherry-picked.  The client should commit the changes and continue if there are no conflicts.
	RebaseOperationPick RebaseOperationType = C.GIT_REBASE_OPERATION_PICK
	// RebaseOperationEdit The given commit is to be cherry-picked, but the client should stop to allow the user to edit the changes before committing them.
	RebaseOperationEdit RebaseOperationType = C.GIT_REBASE_OPERATION_EDIT
	// RebaseOperationSquash The given commit is to be squashed into the previous commit.  The commit message will be merged with the previous message.
	RebaseOperationSquash RebaseOperationType = C.GIT_REBASE_OPERATION_SQUASH
	// RebaseOperationFixup No commit will be cherry-picked.  The client should run the given command and (if successful) continue.
	RebaseOperationFixup RebaseOperationType = C.GIT_REBASE_OPERATION_FIXUP
	// RebaseOperationExec No commit will be cherry-picked.  The client should run the given command and (if successful) continue.
	RebaseOperationExec RebaseOperationType = C.GIT_REBASE_OPERATION_EXEC
)

// Special value indicating that there is no currently active operation
var RebaseNoOperation uint = ^uint(0)

// Error returned if there is no current rebase operation
var ErrRebaseNoOperation = errors.New("o current rebase operation")

// RebaseOperation describes a single instruction/operation to be performed during the rebase.
type RebaseOperation struct {
	Type RebaseOperationType
	Id   *Oid
	Exec string
}

func newRebaseOperationFromC(c *C.git_rebase_operation) *RebaseOperation {
	operation := &RebaseOperation{}
	operation.Type = RebaseOperationType(c._type)
	operation.Id = newOidFromC(&c.id)
	operation.Exec = C.GoString(c.exec)

	return operation
}

// RebaseOptions are used to tell the rebase machinery how to operate
type RebaseOptions struct {
	Version         uint
	Quiet           int
	InMemory        int
	RewriteNotesRef string
	MergeOptions    MergeOptions
	CheckoutOptions CheckoutOpts
}

// DefaultRebaseOptions returns a RebaseOptions with default values.
func DefaultRebaseOptions() (RebaseOptions, error) {
	opts := C.git_rebase_options{}

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ecode := C.git_rebase_init_options(&opts, C.GIT_REBASE_OPTIONS_VERSION)
	if ecode < 0 {
		return RebaseOptions{}, MakeGitError(ecode)
	}
	return rebaseOptionsFromC(&opts), nil
}

func rebaseOptionsFromC(opts *C.git_rebase_options) RebaseOptions {
	return RebaseOptions{
		Version:         uint(opts.version),
		Quiet:           int(opts.quiet),
		InMemory:        int(opts.inmemory),
		RewriteNotesRef: C.GoString(opts.rewrite_notes_ref),
		MergeOptions:    mergeOptionsFromC(&opts.merge_options),
		CheckoutOptions: checkoutOptionsFromC(&opts.checkout_options),
	}
}

func (ro *RebaseOptions) toC() *C.git_rebase_options {
	if ro == nil {
		return nil
	}
	return &C.git_rebase_options{
		version:           C.uint(ro.Version),
		quiet:             C.int(ro.Quiet),
		inmemory:          C.int(ro.InMemory),
		rewrite_notes_ref: mapEmptyStringToNull(ro.RewriteNotesRef),
		merge_options:     *ro.MergeOptions.toC(),
		checkout_options:  *ro.CheckoutOptions.toC(),
	}
}

func mapEmptyStringToNull(ref string) *C.char {
	if ref == "" {
		return nil
	}
	return C.CString(ref)
}

// Rebase is the struct representing a Rebase object.
type Rebase struct {
	ptr *C.git_rebase
	r   *Repository
}

// InitRebase initializes a rebase operation to rebase the changes in branch relative to upstream onto another branch.
func (r *Repository) InitRebase(branch *AnnotatedCommit, upstream *AnnotatedCommit, onto *AnnotatedCommit, opts *RebaseOptions) (*Rebase, error) {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	if branch == nil {
		branch = &AnnotatedCommit{ptr: nil}
	}

	if upstream == nil {
		upstream = &AnnotatedCommit{ptr: nil}
	}

	if onto == nil {
		onto = &AnnotatedCommit{ptr: nil}
	}

	var ptr *C.git_rebase
	err := C.git_rebase_init(&ptr, r.ptr, branch.ptr, upstream.ptr, onto.ptr, opts.toC())
	runtime.KeepAlive(branch)
	runtime.KeepAlive(upstream)
	runtime.KeepAlive(onto)
	if err < 0 {
		return nil, MakeGitError(err)
	}

	return newRebaseFromC(ptr), nil
}

// OpenRebase opens an existing rebase that was previously started by either an invocation of InitRebase or by another client.
func (r *Repository) OpenRebase(opts *RebaseOptions) (*Rebase, error) {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	var ptr *C.git_rebase
	err := C.git_rebase_open(&ptr, r.ptr, opts.toC())
	runtime.KeepAlive(r)
	if err < 0 {
		return nil, MakeGitError(err)
	}

	return newRebaseFromC(ptr), nil
}

// OperationAt gets the rebase operation specified by the given index.
func (rebase *Rebase) OperationAt(index uint) *RebaseOperation {
	operation := C.git_rebase_operation_byindex(rebase.ptr, C.size_t(index))

	return newRebaseOperationFromC(operation)
}

// CurrentOperationIndex gets the index of the rebase operation that is
// currently being applied. There is also an error returned for API
// compatibility.
func (rebase *Rebase) CurrentOperationIndex() (uint, error) {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	var err error
	operationIndex := uint(C.git_rebase_operation_current(rebase.ptr))
	runtime.KeepAlive(rebase)
	if operationIndex == RebaseNoOperation {
		err = ErrRebaseNoOperation
	}

	return uint(operationIndex), err
}

// OperationCount gets the count of rebase operations that are to be applied.
func (rebase *Rebase) OperationCount() uint {
	ret := uint(C.git_rebase_operation_entrycount(rebase.ptr))
	runtime.KeepAlive(rebase)
	return ret
}

// Next performs the next rebase operation and returns the information about it.
// If the operation is one that applies a patch (which is any operation except RebaseOperationExec)
// then the patch will be applied and the index and working directory will be updated with the changes.
// If there are conflicts, you will need to address those before committing the changes.
func (rebase *Rebase) Next() (*RebaseOperation, error) {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	var ptr *C.git_rebase_operation
	err := C.git_rebase_next(&ptr, rebase.ptr)
	runtime.KeepAlive(rebase)
	if err < 0 {
		return nil, MakeGitError(err)
	}

	return newRebaseOperationFromC(ptr), nil
}

// Commit commits the current patch.
// You must have resolved any conflicts that were introduced during the patch application from the Next() invocation.
func (rebase *Rebase) Commit(ID *Oid, author, committer *Signature, message string) error {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	authorSig, err := author.toC()
	if err != nil {
		return err
	}
	defer C.git_signature_free(authorSig)

	committerSig, err := committer.toC()
	if err != nil {
		return err
	}
	defer C.git_signature_free(committerSig)

	cmsg := C.CString(message)
	defer C.free(unsafe.Pointer(cmsg))

	cerr := C.git_rebase_commit(ID.toC(), rebase.ptr, authorSig, committerSig, nil, cmsg)
	runtime.KeepAlive(ID)
	runtime.KeepAlive(rebase)
	if cerr < 0 {
		return MakeGitError(cerr)
	}

	return nil
}

// Finish finishes a rebase that is currently in progress once all patches have been applied.
func (rebase *Rebase) Finish() error {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	err := C.git_rebase_finish(rebase.ptr, nil)
	runtime.KeepAlive(rebase)
	if err < 0 {
		return MakeGitError(err)
	}

	return nil
}

// Abort aborts a rebase that is currently in progress, resetting the repository and working directory to their state before rebase began.
func (rebase *Rebase) Abort() error {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	err := C.git_rebase_abort(rebase.ptr)
	runtime.KeepAlive(rebase)
	if err < 0 {
		return MakeGitError(err)
	}
	return nil
}

// Free frees the Rebase object.
func (rebase *Rebase) Free() {
	runtime.SetFinalizer(rebase, nil)
	C.git_rebase_free(rebase.ptr)
}

func newRebaseFromC(ptr *C.git_rebase) *Rebase {
	rebase := &Rebase{ptr: ptr}
	runtime.SetFinalizer(rebase, (*Rebase).Free)
	return rebase
}
<commit_msg>Fix typo in the error message<commit_after>package git

/*
#include <git2.h>
*/
import "C"
import (
	"errors"
	"runtime"
	"unsafe"
)

// RebaseOperationType is the type of rebase operation
type RebaseOperationType uint

const (
	// RebaseOperationPick The given commit is to be cherry-picked.  The client should commit the changes and continue if there are no conflicts.
	RebaseOperationPick RebaseOperationType = C.GIT_REBASE_OPERATION_PICK
	// RebaseOperationEdit The given commit is to be cherry-picked, but the client should stop to allow the user to edit the changes before committing them.
	RebaseOperationEdit RebaseOperationType = C.GIT_REBASE_OPERATION_EDIT
	// RebaseOperationSquash The given commit is to be squashed into the previous commit.  The commit message will be merged with the previous message.
	RebaseOperationSquash RebaseOperationType = C.GIT_REBASE_OPERATION_SQUASH
	// RebaseOperationFixup No commit will be cherry-picked.  The client should run the given command and (if successful) continue.
	RebaseOperationFixup RebaseOperationType = C.GIT_REBASE_OPERATION_FIXUP
	// RebaseOperationExec No commit will be cherry-picked.  The client should run the given command and (if successful) continue.
	RebaseOperationExec RebaseOperationType = C.GIT_REBASE_OPERATION_EXEC
)

// Special value indicating that there is no currently active operation
var RebaseNoOperation uint = ^uint(0)

// Error returned if there is no current rebase operation
var ErrRebaseNoOperation = errors.New("no current rebase operation")

// RebaseOperation describes a single instruction/operation to be performed during the rebase.
type RebaseOperation struct {
	Type RebaseOperationType
	Id   *Oid
	Exec string
}

func newRebaseOperationFromC(c *C.git_rebase_operation) *RebaseOperation {
	operation := &RebaseOperation{}
	operation.Type = RebaseOperationType(c._type)
	operation.Id = newOidFromC(&c.id)
	operation.Exec = C.GoString(c.exec)

	return operation
}

// RebaseOptions are used to tell the rebase machinery how to operate
type RebaseOptions struct {
	Version         uint
	Quiet           int
	InMemory        int
	RewriteNotesRef string
	MergeOptions    MergeOptions
	CheckoutOptions CheckoutOpts
}

// DefaultRebaseOptions returns a RebaseOptions with default values.
func DefaultRebaseOptions() (RebaseOptions, error) {
	opts := C.git_rebase_options{}

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ecode := C.git_rebase_init_options(&opts, C.GIT_REBASE_OPTIONS_VERSION)
	if ecode < 0 {
		return RebaseOptions{}, MakeGitError(ecode)
	}
	return rebaseOptionsFromC(&opts), nil
}

func rebaseOptionsFromC(opts *C.git_rebase_options) RebaseOptions {
	return RebaseOptions{
		Version:         uint(opts.version),
		Quiet:           int(opts.quiet),
		InMemory:        int(opts.inmemory),
		RewriteNotesRef: C.GoString(opts.rewrite_notes_ref),
		MergeOptions:    mergeOptionsFromC(&opts.merge_options),
		CheckoutOptions: checkoutOptionsFromC(&opts.checkout_options),
	}
}

func (ro *RebaseOptions) toC() *C.git_rebase_options {
	if ro == nil {
		return nil
	}
	return &C.git_rebase_options{
		version:           C.uint(ro.Version),
		quiet:             C.int(ro.Quiet),
		inmemory:          C.int(ro.InMemory),
		rewrite_notes_ref: mapEmptyStringToNull(ro.RewriteNotesRef),
		merge_options:     *ro.MergeOptions.toC(),
		checkout_options:  *ro.CheckoutOptions.toC(),
	}
}

func mapEmptyStringToNull(ref string) *C.char {
	if ref == "" {
		return nil
	}
	return C.CString(ref)
}

// Rebase is the struct representing a Rebase object.
type Rebase struct {
	ptr *C.git_rebase
	r   *Repository
}

// InitRebase initializes a rebase operation to rebase the changes in branch relative to upstream onto another branch.
func (r *Repository) InitRebase(branch *AnnotatedCommit, upstream *AnnotatedCommit, onto *AnnotatedCommit, opts *RebaseOptions) (*Rebase, error) {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	if branch == nil {
		branch = &AnnotatedCommit{ptr: nil}
	}

	if upstream == nil {
		upstream = &AnnotatedCommit{ptr: nil}
	}

	if onto == nil {
		onto = &AnnotatedCommit{ptr: nil}
	}

	var ptr *C.git_rebase
	err := C.git_rebase_init(&ptr, r.ptr, branch.ptr, upstream.ptr, onto.ptr, opts.toC())
	runtime.KeepAlive(branch)
	runtime.KeepAlive(upstream)
	runtime.KeepAlive(onto)
	if err < 0 {
		return nil, MakeGitError(err)
	}

	return newRebaseFromC(ptr), nil
}

// OpenRebase opens an existing rebase that was previously started by either an invocation of InitRebase or by another client.
func (r *Repository) OpenRebase(opts *RebaseOptions) (*Rebase, error) {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	var ptr *C.git_rebase
	err := C.git_rebase_open(&ptr, r.ptr, opts.toC())
	runtime.KeepAlive(r)
	if err < 0 {
		return nil, MakeGitError(err)
	}

	return newRebaseFromC(ptr), nil
}

// OperationAt gets the rebase operation specified by the given index.
func (rebase *Rebase) OperationAt(index uint) *RebaseOperation {
	operation := C.git_rebase_operation_byindex(rebase.ptr, C.size_t(index))

	return newRebaseOperationFromC(operation)
}

// CurrentOperationIndex gets the index of the rebase operation that is
// currently being applied. There is also an error returned for API
// compatibility.
func (rebase *Rebase) CurrentOperationIndex() (uint, error) {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	var err error
	operationIndex := uint(C.git_rebase_operation_current(rebase.ptr))
	runtime.KeepAlive(rebase)
	if operationIndex == RebaseNoOperation {
		err = ErrRebaseNoOperation
	}

	return uint(operationIndex), err
}

// OperationCount gets the count of rebase operations that are to be applied.
func (rebase *Rebase) OperationCount() uint {
	ret := uint(C.git_rebase_operation_entrycount(rebase.ptr))
	runtime.KeepAlive(rebase)
	return ret
}

// Next performs the next rebase operation and returns the information about it.
// If the operation is one that applies a patch (which is any operation except RebaseOperationExec)
// then the patch will be applied and the index and working directory will be updated with the changes.
// If there are conflicts, you will need to address those before committing the changes.
func (rebase *Rebase) Next() (*RebaseOperation, error) {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	var ptr *C.git_rebase_operation
	err := C.git_rebase_next(&ptr, rebase.ptr)
	runtime.KeepAlive(rebase)
	if err < 0 {
		return nil, MakeGitError(err)
	}

	return newRebaseOperationFromC(ptr), nil
}

// Commit commits the current patch.
// You must have resolved any conflicts that were introduced during the patch application from the Next() invocation.
func (rebase *Rebase) Commit(ID *Oid, author, committer *Signature, message string) error {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	authorSig, err := author.toC()
	if err != nil {
		return err
	}
	defer C.git_signature_free(authorSig)

	committerSig, err := committer.toC()
	if err != nil {
		return err
	}
	defer C.git_signature_free(committerSig)

	cmsg := C.CString(message)
	defer C.free(unsafe.Pointer(cmsg))

	cerr := C.git_rebase_commit(ID.toC(), rebase.ptr, authorSig, committerSig, nil, cmsg)
	runtime.KeepAlive(ID)
	runtime.KeepAlive(rebase)
	if cerr < 0 {
		return MakeGitError(cerr)
	}

	return nil
}

// Finish finishes a rebase that is currently in progress once all patches have been applied.
func (rebase *Rebase) Finish() error {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	err := C.git_rebase_finish(rebase.ptr, nil)
	runtime.KeepAlive(rebase)
	if err < 0 {
		return MakeGitError(err)
	}

	return nil
}

// Abort aborts a rebase that is currently in progress, resetting the repository and working directory to their state before rebase began.
func (rebase *Rebase) Abort() error {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	err := C.git_rebase_abort(rebase.ptr)
	runtime.KeepAlive(rebase)
	if err < 0 {
		return MakeGitError(err)
	}
	return nil
}

// Free frees the Rebase object.
func (rebase *Rebase) Free() {
	runtime.SetFinalizer(rebase, nil)
	C.git_rebase_free(rebase.ptr)
}

func newRebaseFromC(ptr *C.git_rebase) *Rebase {
	rebase := &Rebase{ptr: ptr}
	runtime.SetFinalizer(rebase, (*Rebase).Free)
	return rebase
}
<|endoftext|>


<commit_before>package storage

import "github.com/play-with-docker/play-with-docker/pwd/types"

const notFound = "NotFound"

func NotFound(e error) bool {
	return e.Error() == notFound
}

type StorageApi interface {
	SessionGet(string) (*types.Session, error)
	SessionPut(*types.Session) error
	SessionCount() (int, error)
	SessionDelete(string) error
	SessionGetAll() (map[string]*types.Session, error)

	InstanceGet(sessionId, name string) (*types.Instance, error)
	InstanceFindByIP(session, ip string) (*types.Instance, error)
	InstanceCreate(sessionId string, instance *types.Instance) error
	InstanceDelete(sessionId, instanceName string) error
	InstanceDeleteWindows(sessionId, instanceId string) error
	InstanceCount() (int, error)
	InstanceGetAllWindows() ([]*types.WindowsInstance, error)
	InstanceCreateWindows(*types.WindowsInstance) error
}
<commit_msg>Allow to create new storage NotFound errors<commit_after>package storage

import (
	"fmt"

	"github.com/play-with-docker/play-with-docker/pwd/types"
)

const notFound = "NotFound"

func NotFound(e error) bool {
	return e.Error() == notFound
}

func NewNotFoundError() error {
	return fmt.Errorf("%s", notFound)
}

type StorageApi interface {
	SessionGet(string) (*types.Session, error)
	SessionPut(*types.Session) error
	SessionCount() (int, error)
	SessionDelete(string) error
	SessionGetAll() (map[string]*types.Session, error)

	InstanceGet(sessionId, name string) (*types.Instance, error)
	InstanceFindByIP(session, ip string) (*types.Instance, error)
	InstanceCreate(sessionId string, instance *types.Instance) error
	InstanceDelete(sessionId, instanceName string) error
	InstanceDeleteWindows(sessionId, instanceId string) error
	InstanceCount() (int, error)
	InstanceGetAllWindows() ([]*types.WindowsInstance, error)
	InstanceCreateWindows(*types.WindowsInstance) error
}
<|endoftext|>


<commit_before>package dnsimple

import (
	"errors"
	"fmt"
)

type Record struct {
	Id         int    `json:"id,omitempty"`
	Content    string `json:"content,omitempty"`
	Name       string `json:"name,omitempty"`
	TTL        int    `json:"ttl,omitempty"`
	RecordType string `json:"record_type,omitempty"`
	Priority   int    `json:"prio,omitempty"`
	DomainId   int    `json:"domain_id,omitempty"`
	CreatedAt  string `json:"created_at,omitempty"`
	UpdatedAt  string `json:"updated_at,omitempty"`
}

type recordWrapper struct {
	Record Record `json:"record"`
}

func recordURL(domain interface{}, record *Record) string {
	str := fmt.Sprintf("https://dnsimple.com/domains/%s/records", domainIdentifier(domain))
	if record != nil {
		str += fmt.Sprintf("/%d", record.Id)
	}
	return str
}

func (client *DNSimpleClient) Records(domain interface{}) ([]Record, error) {
	wrappedRecords := []recordWrapper{}

	if err := client.get(recordURL(domain, nil), &wrappedRecords); err != nil {
		return []Record{}, err
	}

	records := []Record{}
	for _, record := range wrappedRecords {
		records = append(records, record.Record)
	}

	return records, nil
}

func (client *DNSimpleClient) Record(domain interface{}, name string) (Record, error) {
	reqStr := fmt.Sprintf("%s?name=%s", recordURL(domain, nil), name)

	wrappedRecords := []recordWrapper{}
	if err := client.get(reqStr, &wrappedRecords); err != nil {
		return Record{}, err
	}

	if len(wrappedRecords) == 0 {
		return Record{}, errors.New("Domain not found")
	}

	return wrappedRecords[0].Record, nil
}

func (client *DNSimpleClient) CreateRecord(domain interface{}, record Record) (Record, error) {
	// pre-validate the Record?
	wrappedRecord := recordWrapper{Record: record}
	returnedRecord := recordWrapper{}

	status, err := client.post(recordURL(domain, nil), wrappedRecord, &returnedRecord)
	if err != nil {
		return Record{}, err
	}

	if status == 400 {
		return Record{}, errors.New("Invalid Record")
	}

	return returnedRecord.Record, nil
}

func (record *Record) Update(client *DNSimpleClient, recordAttributes Record) (Record, error) {
	// pre-validate the Record?
	// name, content, ttl, prio - only things allowed
	wrappedRecord := recordWrapper{Record: Record{
		Name:     recordAttributes.Name,
		Content:  recordAttributes.Content,
		TTL:      recordAttributes.TTL,
		Priority: recordAttributes.Priority}}

	returnedRecord := recordWrapper{}

	status, err := client.put(recordURL(record.DomainId, record), wrappedRecord, &returnedRecord)
	if err != nil {
		return Record{}, err
	}

	if status == 400 {
		return Record{}, errors.New("Invalid Record")
	}

	return returnedRecord.Record, nil
}

func (record *Record) Delete(client *DNSimpleClient) error {
	_, status, err := client.sendRequest("DELETE", recordURL(record.DomainId, record), nil)
	if err != nil {
		return err
	}

	if status == 200 {
		return nil
	}
	return errors.New("Failed to delete domain")
}

func (record *Record) UpdateIP(client *DNSimpleClient, IP string) error {
	newRecord := Record{Content: IP}
	_, err := record.Update(client, newRecord)
	return err
}
<commit_msg>remove Record() in favor of passing params to Records(), matches API<commit_after>package dnsimple

import (
	"errors"
	"fmt"
	"net/url"
)

type Record struct {
	Id         int    `json:"id,omitempty"`
	Content    string `json:"content,omitempty"`
	Name       string `json:"name,omitempty"`
	TTL        int    `json:"ttl,omitempty"`
	RecordType string `json:"record_type,omitempty"`
	Priority   int    `json:"prio,omitempty"`
	DomainId   int    `json:"domain_id,omitempty"`
	CreatedAt  string `json:"created_at,omitempty"`
	UpdatedAt  string `json:"updated_at,omitempty"`
}

type recordWrapper struct {
	Record Record `json:"record"`
}

func recordURL(domain interface{}, record *Record) string {
	str := fmt.Sprintf("https://dnsimple.com/domains/%s/records", domainIdentifier(domain))
	if record != nil {
		str += fmt.Sprintf("/%d", record.Id)
	}
	return str
}

func (client *DNSimpleClient) Records(domain interface{}, name, recordType string) ([]Record, error) {
	reqStr := recordURL(domain, nil)
	v := url.Values{}

	if name != "" {
		v.Add("name", name)
	}

	if recordType != "" {
		v.Add("type", recordType)
	}

	reqStr += "?" + v.Encode()

	wrappedRecords := []recordWrapper{}

	if err := client.get(reqStr, &wrappedRecords); err != nil {
		return []Record{}, err
	}

	records := []Record{}
	for _, record := range wrappedRecords {
		records = append(records, record.Record)
	}

	return records, nil
}

func (client *DNSimpleClient) CreateRecord(domain interface{}, record Record) (Record, error) {
	// pre-validate the Record?
	wrappedRecord := recordWrapper{Record: record}
	returnedRecord := recordWrapper{}

	status, err := client.post(recordURL(domain, nil), wrappedRecord, &returnedRecord)
	if err != nil {
		return Record{}, err
	}

	if status == 400 {
		return Record{}, errors.New("Invalid Record")
	}

	return returnedRecord.Record, nil
}

func (record *Record) Update(client *DNSimpleClient, recordAttributes Record) (Record, error) {
	// pre-validate the Record?
	// name, content, ttl, prio - only things allowed
	wrappedRecord := recordWrapper{Record: Record{
		Name:     recordAttributes.Name,
		Content:  recordAttributes.Content,
		TTL:      recordAttributes.TTL,
		Priority: recordAttributes.Priority}}

	returnedRecord := recordWrapper{}

	status, err := client.put(recordURL(record.DomainId, record), wrappedRecord, &returnedRecord)
	if err != nil {
		return Record{}, err
	}

	if status == 400 {
		return Record{}, errors.New("Invalid Record")
	}

	return returnedRecord.Record, nil
}

func (record *Record) Delete(client *DNSimpleClient) error {
	_, status, err := client.sendRequest("DELETE", recordURL(record.DomainId, record), nil)
	if err != nil {
		return err
	}

	if status == 200 {
		return nil
	}
	return errors.New("Failed to delete domain")
}

func (record *Record) UpdateIP(client *DNSimpleClient, IP string) error {
	newRecord := Record{Content: IP}
	_, err := record.Update(client, newRecord)
	return err
}
<|endoftext|>


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

package web

import (
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"sort"
	"sync"
	"time"

	"github.com/zenoss/glog"

	"github.com/control-center/serviced/coordinator/client"
	"github.com/control-center/serviced/utils"
	"github.com/control-center/serviced/zzk"
	"github.com/control-center/serviced/zzk/registry"
)

var (
	vregistry = vhostRegistry{lookup: make(map[string]*vhostInfo), vhostWatch: make(map[string]chan<- bool)}
)

type vhostInfo struct {
	sync.RWMutex
	endpoints []vhostEndpointInfo
	counter   int
}

func newVhostInfo() *vhostInfo {
	return &vhostInfo{endpoints: make([]vhostEndpointInfo, 0)}
}

func (vi *vhostInfo) GetNext() (vhostEndpointInfo, error) {
	vi.Lock()
	defer vi.Unlock()
	if len(vi.endpoints) == 0 {
		return vhostEndpointInfo{}, errors.New("no vhost endpoints available")
	}
	vep := vi.endpoints[vi.counter%len(vi.endpoints)]
	vi.counter++
	return vep, nil
}

type vhostEndpointInfo struct {
	hostIP    string
	epPort    uint16
	privateIP string
	serviceID string
}

func createvhostEndpointInfo(vep *registry.VhostEndpoint) vhostEndpointInfo {
	return vhostEndpointInfo{
		hostIP:    vep.HostIP,
		epPort:    vep.ContainerPort,
		privateIP: vep.ContainerIP,
		serviceID: vep.ServiceID,
	}
}

//vhostRegistry keeps track of all current known vhosts and vhost endpoints.
type vhostRegistry struct {
	sync.RWMutex
	lookup     map[string]*vhostInfo  //vhost name to all availabe endpoints
	vhostWatch map[string]chan<- bool //watches to ZK vhost dir  e.g. zenoss5x. Channel is to cancel watch
}

//get returns a vhostInfo, bool is true or false if vhost is found
func (vr *vhostRegistry) get(vhost string) (*vhostInfo, bool) {
	vr.RLock()
	defer vr.RUnlock()
	vhInfo, found := vr.lookup[vhost]
	if !found {
		glog.V(4).Infof("vhost %v not found in map %v", vhost, vr.lookup)
	}
	return vhInfo, found
}

//setEndpoints sets/replaces all the endpoints available for a vhost
func (vr *vhostRegistry) setVhostInfo(vhost string, vhInfo *vhostInfo) {
	vr.Lock()
	defer vr.Unlock()
	vr.lookup[vhost] = vhInfo
	glog.Infof("setVhostInfo adding VHost %v with backend: %#v", vhost, vhInfo)
}

func areEqual(s1, s2 []string) bool {

	if s1 == nil || s2 == nil {
		return false
	}
	if len(s1) != len(s2) {
		return false
	}
	for i, v := range s1 {
		if v != s2[i] {
			return false
		}
	}
	return true
}

func (sc *ServiceConfig) syncVhosts(shutdown <-chan interface{}) error {
	glog.Info("watchVhosts starting")

	glog.V(2).Infof("getting pool based connection")
	// vhosts are at the root level (not pool aware)
	poolBasedConn, err := zzk.GetLocalConnection("/")
	if err != nil {
		glog.Errorf("watchVhosts - Error getting pool based zk connection: %v", err)
		return err
	}

	glog.V(2).Infof("creating vhostRegistry")
	vhostRegistry, err := registry.VHostRegistry(poolBasedConn)
	if err != nil {
		glog.Errorf("watchVhosts - Error getting vhost registry: %v", err)
		return err
	}

	processVhosts := func(conn client.Connection, parentPath string, childIDs ...string) {
		glog.V(1).Infof("processVhosts STARTING for parentPath:%s childIDs:%v", parentPath, childIDs)

		currentVhosts := make(map[string]struct{})
		//watch any new vhost nodes
		for _, vhostID := range childIDs {
			vhostPath := fmt.Sprintf("%s/%s", parentPath, vhostID)
			currentVhosts[vhostPath] = struct{}{}
			if _, found := vregistry.vhostWatch[vhostPath]; !found {
				glog.Infof("processing vhost watch: %s", vhostPath)
				cancelChan := make(chan bool)
				vregistry.vhostWatch[vhostPath] = cancelChan
				go func(vhostID string) {
					defer delete(vregistry.vhostWatch, vhostPath)
					glog.Infof("starting vhost watch: %s", vhostPath)
					var lastChildIDs []string
					processVhost := func(conn client.Connection, parentPath string, childIDs ...string) {

						glog.V(1).Infof("watching:%s %+v", parentPath, childIDs)
						if !sort.StringsAreSorted(childIDs) {
							sort.Strings(childIDs)
						}
						if areEqual(lastChildIDs, childIDs) {
							glog.V(1).Infof("not processing children because they are the same as last ones: %v = %v ", lastChildIDs, childIDs)
							return
						}
						glog.V(1).Infof("processing vhost parent %v; children %v", parentPath, childIDs)
						vr, err := registry.VHostRegistry(conn)
						if err != nil {
							glog.Errorf("processVhost - Error getting vhost registry: %v", err)
							return
						}

						errors := false
						vhostEndpoints := newVhostInfo()
						for _, child := range childIDs {
							vhEndpoint, err := vr.GetItem(conn, parentPath+"/"+child)
							if err != nil {
								errors = true
								glog.Errorf("processVhost - Error getting vhost for %v/%v: %v", parentPath, child, err)
								continue
							}
							glog.V(1).Infof("Processing vhost %s/%s: %#v", parentPath, child, vhEndpoint)
							vepInfo := createvhostEndpointInfo(vhEndpoint)
							vhostEndpoints.endpoints = append(vhostEndpoints.endpoints, vepInfo)
						}
						vregistry.setVhostInfo(vhostID, vhostEndpoints)
						if !errors {
							lastChildIDs = childIDs
						}
					}
					// loop if error. If watch is cancelled will not return error. Blocking call
					for {
						err := vhostRegistry.WatchKey(conn, vhostID, cancelChan, processVhost, vhostWatchError)
						if err == nil {
							glog.Infof("VHostRegisty Watch %s Stopped", vhostID)
							return
						}
						glog.Infof("VHostRegisty Watch %s Restarting due to %v", vhostID, err)
						time.Sleep(500 * time.Millisecond)
					}

				}(vhostID)
			} else {
				glog.V(2).Infof("vhost %s already being watched", vhostPath)
			}
		}

		//cancel watching any vhosts nodes that are no longer
		for previousVhost, cancel := range vregistry.vhostWatch {
			if _, found := currentVhosts[previousVhost]; !found {
				glog.Infof("Cancelling vhost watch for %s}", previousVhost)
				delete(vregistry.vhostWatch, previousVhost)
				cancel <- true
				close(cancel)
			}
		}
	}
	cancelChan := make(chan bool)
	for {
		glog.Info("Running vhostRegistry.WatchRegistry")

		watchStopped := make(chan error)

		go func() {
			watchStopped <- vhostRegistry.WatchRegistry(poolBasedConn, cancelChan, processVhosts, vhostWatchError)
		}()
		select {
		case <-shutdown:
			close(cancelChan)
			for vhost, ch := range vregistry.vhostWatch {
				glog.V(1).Infof("Shutdown closing watch for %v", vhost)
				close(ch)
			}
			return nil
		case err := <-watchStopped:
			if err != nil {
				glog.Infof("VHostRegisty Watch Restarting due to %v", err)
				time.Sleep(500 * time.Millisecond)
			}

		}
	}
}

func vhostWatchError(path string, err error) {
	glog.Warningf("processing vhostWatchError on %s: %v", path, err)
}

// Lookup the appropriate virtual host and forward the request to it.
// serviceIDs is the list of services on which the vhost is enabled
func (sc *ServiceConfig) vhosthandler(w http.ResponseWriter, r *http.Request, vhostname string, serviceIDs map[string]struct{}) {
	start := time.Now()
	glog.V(1).Infof("vhosthandler handling: %+v", r)

	defer func() {
		glog.V(1).Infof("Time to process %s vhost request %v: %v", vhostname, r.URL, time.Since(start))
	}()

	vhInfo, found := vregistry.get(vhostname)
	if !found {
		http.Error(w, fmt.Sprintf("service associated with vhost %v is not running", vhostname), http.StatusNotFound)
		return
	}
	// round robin through available endpoints
	vhEP, err := vhInfo.GetNext()
	if err != nil {
		glog.V(4).Infof("no endpoint found for vhost %s: %v", vhostname, err)
		http.Error(w, fmt.Sprintf("no available service for vhost %v ", vhostname), http.StatusNotFound)
		return
	}
	// check that the endpoint's service id is in the list of vhosts that are enabled.
	// This happens if more than one tenant has the same vhost. One tenant is off and the one that is running has
	// has disabled this vhost.
	if _, found := serviceIDs[vhEP.serviceID]; !found {
		glog.V(4).Infof("vhost not enabled %s: %v", vhostname, err)
		http.Error(w, fmt.Sprintf("vhost %s not available", vhostname), http.StatusNotFound)
		return
	}

	rp := getReverseProxy(vhEP.hostIP, sc.muxPort, vhEP.privateIP, vhEP.epPort, sc.muxTLS && (sc.muxPort > 0))
	glog.V(1).Infof("Time to set up %s vhost proxy for %v: %v", vhostname, r.URL, time.Since(start))

	// Set up the X-Forwarded-Proto header so that downstream servers know
	// the request originated as HTTPS.
	if _, ok := r.Header["X-Forwarded-Proto"]; !ok {
		r.Header.Set("X-Forwarded-Proto", "https")
	}

	rp.ServeHTTP(w, r)
	return
}

var reverseProxies map[string]*httputil.ReverseProxy
var reverseProxiesLock sync.Mutex
var localAddrs map[string]struct{}

func init() {
	var err error
	reverseProxies = make(map[string]*httputil.ReverseProxy)
	hostAddrs, err := utils.GetIPv4Addresses()
	if err != nil {
		panic(err)
	}
	localAddrs = make(map[string]struct{})
	for _, host := range hostAddrs {
		localAddrs[host] = struct{}{}
	}
}

func getReverseProxy(hostIP string, muxPort int, privateIP string, privatePort uint16, useTLS bool) *httputil.ReverseProxy {

	var remoteAddr string

	reverseProxiesLock.Lock()
	defer reverseProxiesLock.Unlock()

	_, isLocalContainer := localAddrs[hostIP]
	if isLocalContainer {
		remoteAddr = fmt.Sprintf("%s:%d", privateIP, privatePort)
	} else {
		remoteAddr = fmt.Sprintf("%s:%d", hostIP, muxPort)
	}

	key := fmt.Sprintf("%s,%d,%s,%d,%v", remoteAddr, muxPort, privateIP, privatePort, useTLS)
	proxy, ok := reverseProxies[key]
	if ok {
		return proxy
	}

	rpurl := url.URL{Scheme: "http", Host: remoteAddr}

	glog.V(1).Infof("vhosthandler reverse proxy to: %v", rpurl)

	transport := &http.Transport{Proxy: http.ProxyFromEnvironment}
	transport.Dial = func(network, addr string) (remote net.Conn, err error) {
		if useTLS && !isLocalContainer { // Only do TLS if connecting to a TCPMux
			config := tls.Config{InsecureSkipVerify: true}
			glog.V(1).Infof("vhost about to dial %s", remoteAddr)
			remote, err = tls.Dial("tcp4", remoteAddr, &config)
		} else {
			glog.V(1).Info("vhost about to dial %s", remoteAddr)
			remote, err = net.Dial("tcp4", remoteAddr)
		}
		if err != nil {
			return nil, err
		}

		if muxPort > 0 && !isLocalContainer {
			//TODO: move this check to happen sooner
			if len(privateIP) == 0 {
				return nil, fmt.Errorf("missing endpoint")
			}
			muxAddr := fmt.Sprintf("%s:%d\n", privateIP, privatePort)
			glog.V(1).Infof("vhost muxing to %s", muxAddr)
			io.WriteString(remote, muxAddr)

		}
		return remote, nil
	}
	rp := httputil.NewSingleHostReverseProxy(&rpurl)
	rp.Transport = transport
	rp.FlushInterval = time.Millisecond * 10

	reverseProxies[key] = rp
	return rp

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

package web

import (
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"sort"
	"sync"
	"time"

	"github.com/zenoss/glog"

	"github.com/control-center/serviced/coordinator/client"
	"github.com/control-center/serviced/utils"
	"github.com/control-center/serviced/zzk"
	"github.com/control-center/serviced/zzk/registry"
)

var (
	vregistry = vhostRegistry{lookup: make(map[string]*vhostInfo), vhostWatch: make(map[string]chan<- bool)}
)

type vhostInfo struct {
	sync.RWMutex
	endpoints []vhostEndpointInfo
	counter   int
}

func newVhostInfo() *vhostInfo {
	return &vhostInfo{endpoints: make([]vhostEndpointInfo, 0)}
}

func (vi *vhostInfo) GetNext() (vhostEndpointInfo, error) {
	vi.Lock()
	defer vi.Unlock()
	if len(vi.endpoints) == 0 {
		return vhostEndpointInfo{}, errors.New("no vhost endpoints available")
	}
	vep := vi.endpoints[vi.counter%len(vi.endpoints)]
	vi.counter++
	return vep, nil
}

type vhostEndpointInfo struct {
	hostIP    string
	epPort    uint16
	privateIP string
	serviceID string
}

func createvhostEndpointInfo(vep *registry.VhostEndpoint) vhostEndpointInfo {
	return vhostEndpointInfo{
		hostIP:    vep.HostIP,
		epPort:    vep.ContainerPort,
		privateIP: vep.ContainerIP,
		serviceID: vep.ServiceID,
	}
}

//vhostRegistry keeps track of all current known vhosts and vhost endpoints.
type vhostRegistry struct {
	sync.RWMutex
	lookup     map[string]*vhostInfo  //vhost name to all availabe endpoints
	vhostWatch map[string]chan<- bool //watches to ZK vhost dir  e.g. zenoss5x. Channel is to cancel watch
}

func (vr *vhostRegistry) getWatch(vhost string) (chan<- bool, bool) {
	vr.RLock()
	defer vr.RUnlock()
	channel, found := vr.vhostWatch[vhost]
	return channel, found
}

func (vr *vhostRegistry) setWatch(vhost string, cancel chan<- bool) {
	vr.RLock()
	defer vr.RUnlock()
	vr.vhostWatch[vhost] = cancel
}

func (vr *vhostRegistry) deleteWatch(vhost string) {
	vr.Lock()
	defer vr.Unlock()
	delete(vr.vhostWatch, vhost)
}

//get returns a vhostInfo, bool is true or false if vhost is found
func (vr *vhostRegistry) get(vhost string) (*vhostInfo, bool) {
	vr.RLock()
	defer vr.RUnlock()
	vhInfo, found := vr.lookup[vhost]
	if !found {
		glog.V(4).Infof("vhost %v not found in map %v", vhost, vr.lookup)
	}
	return vhInfo, found
}

//setEndpoints sets/replaces all the endpoints available for a vhost
func (vr *vhostRegistry) setVhostInfo(vhost string, vhInfo *vhostInfo) {
	vr.Lock()
	defer vr.Unlock()
	vr.lookup[vhost] = vhInfo
	glog.Infof("setVhostInfo adding VHost %v with backend: %#v", vhost, vhInfo)
}

func areEqual(s1, s2 []string) bool {

	if s1 == nil || s2 == nil {
		return false
	}
	if len(s1) != len(s2) {
		return false
	}
	for i, v := range s1 {
		if v != s2[i] {
			return false
		}
	}
	return true
}

func (sc *ServiceConfig) syncVhosts(shutdown <-chan interface{}) error {
	glog.Info("watchVhosts starting")

	glog.V(2).Infof("getting pool based connection")
	// vhosts are at the root level (not pool aware)
	poolBasedConn, err := zzk.GetLocalConnection("/")
	if err != nil {
		glog.Errorf("watchVhosts - Error getting pool based zk connection: %v", err)
		return err
	}

	glog.V(2).Infof("creating vhostRegistry")
	vhostRegistry, err := registry.VHostRegistry(poolBasedConn)
	if err != nil {
		glog.Errorf("watchVhosts - Error getting vhost registry: %v", err)
		return err
	}

	processVhosts := func(conn client.Connection, parentPath string, childIDs ...string) {
		glog.V(1).Infof("processVhosts STARTING for parentPath:%s childIDs:%v", parentPath, childIDs)

		currentVhosts := make(map[string]struct{})
		//watch any new vhost nodes
		for _, vhostID := range childIDs {
			vhostPath := fmt.Sprintf("%s/%s", parentPath, vhostID)
			currentVhosts[vhostPath] = struct{}{}
			if _, found := vregistry.getWatch(vhostPath); !found {
				glog.Infof("processing vhost watch: %s", vhostPath)
				cancelChan := make(chan bool)
				vregistry.setWatch(vhostPath, cancelChan)
				go func(vhostID string) {
					defer vregistry.deleteWatch(vhostPath)
					glog.Infof("starting vhost watch: %s", vhostPath)
					var lastChildIDs []string
					processVhost := func(conn client.Connection, parentPath string, childIDs ...string) {

						glog.V(1).Infof("watching:%s %+v", parentPath, childIDs)
						if !sort.StringsAreSorted(childIDs) {
							sort.Strings(childIDs)
						}
						if areEqual(lastChildIDs, childIDs) {
							glog.V(1).Infof("not processing children because they are the same as last ones: %v = %v ", lastChildIDs, childIDs)
							return
						}
						glog.V(1).Infof("processing vhost parent %v; children %v", parentPath, childIDs)
						vr, err := registry.VHostRegistry(conn)
						if err != nil {
							glog.Errorf("processVhost - Error getting vhost registry: %v", err)
							return
						}

						errors := false
						vhostEndpoints := newVhostInfo()
						for _, child := range childIDs {
							vhEndpoint, err := vr.GetItem(conn, parentPath+"/"+child)
							if err != nil {
								errors = true
								glog.Errorf("processVhost - Error getting vhost for %v/%v: %v", parentPath, child, err)
								continue
							}
							glog.V(1).Infof("Processing vhost %s/%s: %#v", parentPath, child, vhEndpoint)
							vepInfo := createvhostEndpointInfo(vhEndpoint)
							vhostEndpoints.endpoints = append(vhostEndpoints.endpoints, vepInfo)
						}
						vregistry.setVhostInfo(vhostID, vhostEndpoints)
						if !errors {
							lastChildIDs = childIDs
						}
					}
					// loop if error. If watch is cancelled will not return error. Blocking call
					for {
						err := vhostRegistry.WatchKey(conn, vhostID, cancelChan, processVhost, vhostWatchError)
						if err == nil {
							glog.Infof("VHostRegistry Watch %s Stopped", vhostID)
							return
						}
						glog.Infof("VHostRegistry Watch %s Restarting due to %v", vhostID, err)
						time.Sleep(500 * time.Millisecond)
					}

				}(vhostID)
			} else {
				glog.V(2).Infof("vhost %s already being watched", vhostPath)
			}
		}

		//cancel watching any vhosts nodes that are no longer
		for previousVhost, cancel := range vregistry.vhostWatch {
			if _, found := currentVhosts[previousVhost]; !found {
				glog.Infof("Cancelling vhost watch for %s}", previousVhost)
				delete(vregistry.vhostWatch, previousVhost)
				cancel <- true
				close(cancel)
			}
		}
	}
	cancelChan := make(chan bool)
	for {
		glog.Info("Running vhostRegistry.WatchRegistry")

		watchStopped := make(chan error)

		go func() {
			watchStopped <- vhostRegistry.WatchRegistry(poolBasedConn, cancelChan, processVhosts, vhostWatchError)
		}()
		select {
		case <-shutdown:
			close(cancelChan)
			for vhost, ch := range vregistry.vhostWatch {
				glog.V(1).Infof("Shutdown closing watch for %v", vhost)
				close(ch)
			}
			return nil
		case err := <-watchStopped:
			if err != nil {
				glog.Infof("VHostRegistry Watch Restarting due to %v", err)
				time.Sleep(500 * time.Millisecond)
			}

		}
	}
}

func vhostWatchError(path string, err error) {
	glog.Warningf("processing vhostWatchError on %s: %v", path, err)
}

// Lookup the appropriate virtual host and forward the request to it.
// serviceIDs is the list of services on which the vhost is enabled
func (sc *ServiceConfig) vhosthandler(w http.ResponseWriter, r *http.Request, vhostname string, serviceIDs map[string]struct{}) {
	start := time.Now()
	glog.V(1).Infof("vhosthandler handling: %+v", r)

	defer func() {
		glog.V(1).Infof("Time to process %s vhost request %v: %v", vhostname, r.URL, time.Since(start))
	}()

	vhInfo, found := vregistry.get(vhostname)
	if !found {
		http.Error(w, fmt.Sprintf("service associated with vhost %v is not running", vhostname), http.StatusNotFound)
		return
	}
	// round robin through available endpoints
	vhEP, err := vhInfo.GetNext()
	if err != nil {
		glog.V(4).Infof("no endpoint found for vhost %s: %v", vhostname, err)
		http.Error(w, fmt.Sprintf("no available service for vhost %v ", vhostname), http.StatusNotFound)
		return
	}
	// check that the endpoint's service id is in the list of vhosts that are enabled.
	// This happens if more than one tenant has the same vhost. One tenant is off and the one that is running has
	// has disabled this vhost.
	if _, found := serviceIDs[vhEP.serviceID]; !found {
		glog.V(4).Infof("vhost not enabled %s: %v", vhostname, err)
		http.Error(w, fmt.Sprintf("vhost %s not available", vhostname), http.StatusNotFound)
		return
	}

	rp := getReverseProxy(vhEP.hostIP, sc.muxPort, vhEP.privateIP, vhEP.epPort, sc.muxTLS && (sc.muxPort > 0))
	glog.V(1).Infof("Time to set up %s vhost proxy for %v: %v", vhostname, r.URL, time.Since(start))

	// Set up the X-Forwarded-Proto header so that downstream servers know
	// the request originated as HTTPS.
	if _, ok := r.Header["X-Forwarded-Proto"]; !ok {
		r.Header.Set("X-Forwarded-Proto", "https")
	}

	rp.ServeHTTP(w, r)
	return
}

var reverseProxies map[string]*httputil.ReverseProxy
var reverseProxiesLock sync.Mutex
var localAddrs map[string]struct{}

func init() {
	var err error
	reverseProxies = make(map[string]*httputil.ReverseProxy)
	hostAddrs, err := utils.GetIPv4Addresses()
	if err != nil {
		panic(err)
	}
	localAddrs = make(map[string]struct{})
	for _, host := range hostAddrs {
		localAddrs[host] = struct{}{}
	}
}

func getReverseProxy(hostIP string, muxPort int, privateIP string, privatePort uint16, useTLS bool) *httputil.ReverseProxy {

	var remoteAddr string

	reverseProxiesLock.Lock()
	defer reverseProxiesLock.Unlock()

	_, isLocalContainer := localAddrs[hostIP]
	if isLocalContainer {
		remoteAddr = fmt.Sprintf("%s:%d", privateIP, privatePort)
	} else {
		remoteAddr = fmt.Sprintf("%s:%d", hostIP, muxPort)
	}

	key := fmt.Sprintf("%s,%d,%s,%d,%v", remoteAddr, muxPort, privateIP, privatePort, useTLS)
	proxy, ok := reverseProxies[key]
	if ok {
		return proxy
	}

	rpurl := url.URL{Scheme: "http", Host: remoteAddr}

	glog.V(1).Infof("vhosthandler reverse proxy to: %v", rpurl)

	transport := &http.Transport{Proxy: http.ProxyFromEnvironment}
	transport.Dial = func(network, addr string) (remote net.Conn, err error) {
		if useTLS && !isLocalContainer { // Only do TLS if connecting to a TCPMux
			config := tls.Config{InsecureSkipVerify: true}
			glog.V(1).Infof("vhost about to dial %s", remoteAddr)
			remote, err = tls.Dial("tcp4", remoteAddr, &config)
		} else {
			glog.V(1).Info("vhost about to dial %s", remoteAddr)
			remote, err = net.Dial("tcp4", remoteAddr)
		}
		if err != nil {
			return nil, err
		}

		if muxPort > 0 && !isLocalContainer {
			//TODO: move this check to happen sooner
			if len(privateIP) == 0 {
				return nil, fmt.Errorf("missing endpoint")
			}
			muxAddr := fmt.Sprintf("%s:%d\n", privateIP, privatePort)
			glog.V(1).Infof("vhost muxing to %s", muxAddr)
			io.WriteString(remote, muxAddr)

		}
		return remote, nil
	}
	rp := httputil.NewSingleHostReverseProxy(&rpurl)
	rp.Transport = transport
	rp.FlushInterval = time.Millisecond * 10

	reverseProxies[key] = rp
	return rp

}
<|endoftext|>


<commit_before>package agent

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"os"
	"strconv"
	"strings"
	"sync"

	"github.com/armon/circbuf"
	consulapi "github.com/hashicorp/consul/api"
	consultemplate "github.com/marouenj/consul-template/core"
	"github.com/marouenj/consul/watch"
)

const (
	// Limit the size of a watch handlers's output to the
	// last WatchBufSize. Prevents an enormous buffer
	// from being captured
	WatchBufSize = 4 * 1024 // 4KB
	upAction     = "up"
	downAction   = "down"
)

var actions = map[string]bool{
	upAction:   true,
	downAction: true,
}

// verifyWatchHandler does the pre-check for our handler configuration
func verifyWatchHandler(params interface{}) error {
	if params == nil {
		return fmt.Errorf("Must provide watch handler")
	}
	_, ok := params.(string)
	if !ok {
		return fmt.Errorf("Watch handler must be a string")
	}
	return nil
}

// makeWatchHandler returns a handler for the given watch
func makeWatchHandler(logOutput io.Writer, params interface{}) watch.HandlerFunc {
	script := params.(string)
	logger := log.New(logOutput, "", log.LstdFlags)
	fn := func(idx uint64, data interface{}) {
		// Create the command
		cmd, err := ExecScript(script)
		if err != nil {
			logger.Printf("[ERR] agent: Failed to setup watch: %v", err)
			return
		}
		cmd.Env = append(os.Environ(),
			"CONSUL_INDEX="+strconv.FormatUint(idx, 10),
		)

		// Collect the output
		output, _ := circbuf.NewBuffer(WatchBufSize)
		cmd.Stdout = output
		cmd.Stderr = output

		// Setup the input
		var inp bytes.Buffer
		enc := json.NewEncoder(&inp)
		if err := enc.Encode(data); err != nil {
			logger.Printf("[ERR] agent: Failed to encode data for watch '%s': %v", script, err)
			return
		}
		cmd.Stdin = &inp

		// Run the handler
		if err := cmd.Run(); err != nil {
			logger.Printf("[ERR] agent: Failed to invoke watch handler '%s': %v", script, err)
		}

		// Get the output, add a message about truncation
		outputStr := string(output.Bytes())
		if output.TotalWritten() > output.Size() {
			outputStr = fmt.Sprintf("Captured %d of %d bytes\n...\n%s",
				output.Size(), output.TotalWritten(), outputStr)
		}

		// Log the output
		logger.Printf("[DEBUG] agent: watch handler '%s' output: %s", script, outputStr)
	}
	return fn
}

// makeWatchHandler returns a handler that would control a consul-template instance
func makeWatchHandlerForArchetype(logOutput io.Writer, agent *Agent, archetypeIndex int) watch.HandlerFunc {
	logger := log.New(logOutput, "", log.LstdFlags)

	// get the key
	archetype := agent.config.Archetypes[archetypeIndex]
	key := strings.Join([]string{"archetype", "watch", archetype.PoolName, archetype.ID}, "/")

	// http address
	address := []string{agent.config.Addresses.HTTP, strconv.Itoa(agent.config.Ports.HTTP)}

	// get the client config
	cliConfig := consulapi.DefaultConfig()
	cliConfig.Address = strings.Join(address, ":")

	// get the client
	client, _ := consulapi.NewClient(cliConfig)

	// get the kv client
	kv := client.KV()

	// get the consul-template config
	config := consultemplate.DefaultConfig()
	config.Consul = strings.Join(address, ":")
	config.ConfigTemplates = append(config.ConfigTemplates, &agent.config.Archetypes[0].Template)
	// check the existing value on archetype/watch/{archetype_name}/{archetype_id}
	pair, _, err := kv.Get(key, nil)
	if err != nil {
		panic(err)
	}
	if pair == nil || string(pair.Value[:]) != upAction {
		// there's no service running (run start command on the first occasion)
		config.ConfigTemplates[0].First = true
	}

	// get the runner
	runner := agent.config.Runners[archetypeIndex]

	// define the selector
	var once sync.Once
	var selector func()

	fn := func(idx uint64, data interface{}) {
		logger.Printf("[INFO] agent: Calling watch handler on key %s", key)

		pair, _, err := kv.Get(key, nil)
		if err != nil {
			panic(err)
		}

		if pair == nil {
			return
		}
		val := string(pair.Value[:])

		if !actions[val] {
			return
		}

		// runner can be safely initialized
		// this bloc is executed once
		if runner == nil {
			logger.Printf("[INFO] agent: Initializing runner for key %s", key)
			// if val != upAction {
			// 	config.ConfigTemplates[0].First = true
			// }
			runner, err = consultemplate.NewRunner(config, false, false)
			if err != nil {
				logger.Printf("[ERR] runner: Failed to launch: %v", err)
			}
			// define selector
			selector = func() {
			Outer:
				for {
					select {
					case err := <-runner.ErrCh:
						logger.Printf("%v", err)
					case <-runner.DoneCh:
						break Outer
					}
				}
			}

			if val == upAction {
				// runner.Up = true
				// runner.Init()
				go runner.Start()
			} else if val == downAction {
				go func() {
					once.Do(selector)
					once = sync.Once{}
				}()
			}
		}

		if val == upAction {
			go runner.Start()
			go func() {
				once.Do(selector)
				once = sync.Once{}
			}()
		} else if val == downAction {
			if err = runner.Stop(); err != nil {
				logger.Printf("%v", err)
			}
		}
	}
	return fn
}
<commit_msg>delete verified comments<commit_after>package agent

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"os"
	"strconv"
	"strings"
	"sync"

	"github.com/armon/circbuf"
	consulapi "github.com/hashicorp/consul/api"
	consultemplate "github.com/marouenj/consul-template/core"
	"github.com/marouenj/consul/watch"
)

const (
	// Limit the size of a watch handlers's output to the
	// last WatchBufSize. Prevents an enormous buffer
	// from being captured
	WatchBufSize = 4 * 1024 // 4KB
	upAction     = "up"
	downAction   = "down"
)

var actions = map[string]bool{
	upAction:   true,
	downAction: true,
}

// verifyWatchHandler does the pre-check for our handler configuration
func verifyWatchHandler(params interface{}) error {
	if params == nil {
		return fmt.Errorf("Must provide watch handler")
	}
	_, ok := params.(string)
	if !ok {
		return fmt.Errorf("Watch handler must be a string")
	}
	return nil
}

// makeWatchHandler returns a handler for the given watch
func makeWatchHandler(logOutput io.Writer, params interface{}) watch.HandlerFunc {
	script := params.(string)
	logger := log.New(logOutput, "", log.LstdFlags)
	fn := func(idx uint64, data interface{}) {
		// Create the command
		cmd, err := ExecScript(script)
		if err != nil {
			logger.Printf("[ERR] agent: Failed to setup watch: %v", err)
			return
		}
		cmd.Env = append(os.Environ(),
			"CONSUL_INDEX="+strconv.FormatUint(idx, 10),
		)

		// Collect the output
		output, _ := circbuf.NewBuffer(WatchBufSize)
		cmd.Stdout = output
		cmd.Stderr = output

		// Setup the input
		var inp bytes.Buffer
		enc := json.NewEncoder(&inp)
		if err := enc.Encode(data); err != nil {
			logger.Printf("[ERR] agent: Failed to encode data for watch '%s': %v", script, err)
			return
		}
		cmd.Stdin = &inp

		// Run the handler
		if err := cmd.Run(); err != nil {
			logger.Printf("[ERR] agent: Failed to invoke watch handler '%s': %v", script, err)
		}

		// Get the output, add a message about truncation
		outputStr := string(output.Bytes())
		if output.TotalWritten() > output.Size() {
			outputStr = fmt.Sprintf("Captured %d of %d bytes\n...\n%s",
				output.Size(), output.TotalWritten(), outputStr)
		}

		// Log the output
		logger.Printf("[DEBUG] agent: watch handler '%s' output: %s", script, outputStr)
	}
	return fn
}

// makeWatchHandler returns a handler that would control a consul-template instance
func makeWatchHandlerForArchetype(logOutput io.Writer, agent *Agent, archetypeIndex int) watch.HandlerFunc {
	logger := log.New(logOutput, "", log.LstdFlags)

	// get the key
	archetype := agent.config.Archetypes[archetypeIndex]
	key := strings.Join([]string{"archetype", "watch", archetype.PoolName, archetype.ID}, "/")

	// http address
	address := []string{agent.config.Addresses.HTTP, strconv.Itoa(agent.config.Ports.HTTP)}

	// get the client config
	cliConfig := consulapi.DefaultConfig()
	cliConfig.Address = strings.Join(address, ":")

	// get the client
	client, _ := consulapi.NewClient(cliConfig)

	// get the kv client
	kv := client.KV()

	// get the consul-template config
	config := consultemplate.DefaultConfig()
	config.Consul = strings.Join(address, ":")
	config.ConfigTemplates = append(config.ConfigTemplates, &agent.config.Archetypes[0].Template)
	// check the existing value on archetype/watch/{archetype_name}/{archetype_id}
	pair, _, err := kv.Get(key, nil)
	if err != nil {
		panic(err)
	}
	if pair == nil || string(pair.Value[:]) != upAction {
		// there's no service running (run start command on the first occasion)
		config.ConfigTemplates[0].First = true
	}

	// get the runner
	runner := agent.config.Runners[archetypeIndex]

	// define the selector
	var once sync.Once
	var selector func()

	fn := func(idx uint64, data interface{}) {
		logger.Printf("[INFO] agent: Calling watch handler on key %s", key)

		pair, _, err := kv.Get(key, nil)
		if err != nil {
			panic(err)
		}

		if pair == nil {
			return
		}
		val := string(pair.Value[:])

		if !actions[val] {
			return
		}

		// runner can be safely initialized
		// this bloc is executed once
		if runner == nil {
			logger.Printf("[INFO] agent: Initializing runner for key %s", key)
			runner, err = consultemplate.NewRunner(config, false, false)
			if err != nil {
				logger.Printf("[ERR] runner: Failed to launch: %v", err)
			}
			// define selector
			selector = func() {
			Outer:
				for {
					select {
					case err := <-runner.ErrCh:
						logger.Printf("%v", err)
					case <-runner.DoneCh:
						break Outer
					}
				}
			}

			if val == upAction {
				go runner.Start()
			} else if val == downAction {
				go func() {
					once.Do(selector)
					once = sync.Once{}
				}()
			}
		}

		if val == upAction {
			go runner.Start()
			go func() {
				once.Do(selector)
				once = sync.Once{}
			}()
		} else if val == downAction {
			if err = runner.Stop(); err != nil {
				logger.Printf("%v", err)
			}
		}
	}
	return fn
}
<|endoftext|>