<commit_before>package iptables

import (
	"errors"
	"fmt"
	"net"
	"os/exec"
	"regexp"
	"strconv"
	"strings"

	"hyper/lib/glog"
)

type Action string
type Table string

const (
	Append Action = "-A"
	Delete Action = "-D"
	Insert Action = "-I"
	Nat    Table  = "nat"
	Filter Table  = "filter"
	Mangle Table  = "mangle"
)

var (
	iptablesPath        string
	supportsXlock       = false
	ErrIptablesNotFound = errors.New("Iptables not found")
)

type Chain struct {
	Name   string
	Bridge string
	Table  Table
}

type ChainError struct {
	Chain  string
	Output []byte
}

func (e *ChainError) Error() string {
	return fmt.Sprintf("Error iptables %s: %s", e.Chain, string(e.Output))
}

func initCheck() error {

	if iptablesPath == "" {
		path, err := exec.LookPath("iptables")
		if err != nil {
			return ErrIptablesNotFound
		}
		iptablesPath = path
		supportsXlock = exec.Command(iptablesPath, "--wait", "-L", "-n").Run() == nil
	}
	return nil
}

func NewChain(name, bridge string, table Table) (*Chain, error) {
	c := &Chain{
		Name:   name,
		Bridge: bridge,
		Table:  table,
	}

	if string(c.Table) == "" {
		c.Table = Filter
	}

	// Add chain if it doesn't exist
	if _, err := Raw("-t", string(c.Table), "-n", "-L", c.Name); err != nil {
		if output, err := Raw("-t", string(c.Table), "-N", c.Name); err != nil {
			return nil, err
		} else if len(output) != 0 {
			return nil, fmt.Errorf("Could not create %s/%s chain: %s", c.Table, c.Name, output)
		}
	}

	switch table {
	case Nat:
		preroute := []string{
			"-m", "addrtype",
			"--dst-type", "LOCAL"}
		if !Exists(Nat, "PREROUTING", preroute...) {
			if err := c.Prerouting(Append, preroute...); err != nil {
				return nil, fmt.Errorf("Failed to inject docker in PREROUTING chain: %s", err)
			}
		}
		output := []string{
			"-m", "addrtype",
			"--dst-type", "LOCAL",
			"!", "--dst", "127.0.0.0/8"}
		if !Exists(Nat, "OUTPUT", output...) {
			if err := c.Output(Append, output...); err != nil {
				return nil, fmt.Errorf("Failed to inject docker in OUTPUT chain: %s", err)
			}
		}
	case Filter:
		link := []string{
			"-o", c.Bridge,
			"-j", c.Name}
		if !Exists(Filter, "FORWARD", link...) {
			insert := append([]string{string(Insert), "FORWARD"}, link...)
			if output, err := Raw(insert...); err != nil {
				return nil, err
			} else if len(output) != 0 {
				return nil, fmt.Errorf("Could not create linking rule to %s/%s: %s", c.Table, c.Name, output)
			}
		}
	}
	return c, nil
}

func RemoveExistingChain(name string, table Table) error {
	c := &Chain{
		Name:  name,
		Table: table,
	}
	if string(c.Table) == "" {
		c.Table = Filter
	}
	return c.Remove()
}

// Add forwarding rule to 'filter' table and corresponding nat rule to 'nat' table
func (c *Chain) Forward(action Action, ip net.IP, port int, proto, destAddr string, destPort int) error {
	daddr := ip.String()
	if ip.IsUnspecified() {
		// iptables interprets "0.0.0.0" as "0.0.0.0/32", whereas we
		// want "0.0.0.0/0". "0/0" is correctly interpreted as "any
		// value" by both iptables and ip6tables.
		daddr = "0/0"
	}
	if output, err := Raw("-t", string(Nat), string(action), c.Name,
		"-p", proto,
		"-d", daddr,
		"--dport", strconv.Itoa(port),
		"!", "-i", c.Bridge,
		"-j", "DNAT",
		"--to-destination", net.JoinHostPort(destAddr, strconv.Itoa(destPort))); err != nil {
		return err
	} else if len(output) != 0 {
		return &ChainError{Chain: "FORWARD", Output: output}
	}

	if output, err := Raw("-t", string(Filter), string(action), c.Name,
		"!", "-i", c.Bridge,
		"-o", c.Bridge,
		"-p", proto,
		"-d", destAddr,
		"--dport", strconv.Itoa(destPort),
		"-j", "ACCEPT"); err != nil {
		return err
	} else if len(output) != 0 {
		return &ChainError{Chain: "FORWARD", Output: output}
	}

	if output, err := Raw("-t", string(Nat), string(action), "POSTROUTING",
		"-p", proto,
		"-s", destAddr,
		"-d", destAddr,
		"--dport", strconv.Itoa(destPort),
		"-j", "MASQUERADE"); err != nil {
		return err
	} else if len(output) != 0 {
		return &ChainError{Chain: "FORWARD", Output: output}
	}

	return nil
}

// Add reciprocal ACCEPT rule for two supplied IP addresses.
// Traffic is allowed from ip1 to ip2 and vice-versa
func (c *Chain) Link(action Action, ip1, ip2 net.IP, port int, proto string) error {
	if output, err := Raw("-t", string(Filter), string(action), c.Name,
		"-i", c.Bridge, "-o", c.Bridge,
		"-p", proto,
		"-s", ip1.String(),
		"-d", ip2.String(),
		"--dport", strconv.Itoa(port),
		"-j", "ACCEPT"); err != nil {
		return err
	} else if len(output) != 0 {
		return fmt.Errorf("Error iptables forward: %s", output)
	}
	if output, err := Raw("-t", string(Filter), string(action), c.Name,
		"-i", c.Bridge, "-o", c.Bridge,
		"-p", proto,
		"-s", ip2.String(),
		"-d", ip1.String(),
		"--sport", strconv.Itoa(port),
		"-j", "ACCEPT"); err != nil {
		return err
	} else if len(output) != 0 {
		return fmt.Errorf("Error iptables forward: %s", output)
	}
	return nil
}

// Add linking rule to nat/PREROUTING chain.
func (c *Chain) Prerouting(action Action, args ...string) error {
	a := []string{"-t", string(Nat), string(action), "PREROUTING"}
	if len(args) > 0 {
		a = append(a, args...)
	}
	if output, err := Raw(append(a, "-j", c.Name)...); err != nil {
		return err
	} else if len(output) != 0 {
		return &ChainError{Chain: "PREROUTING", Output: output}
	}
	return nil
}

// Add linking rule to an OUTPUT chain
func (c *Chain) Output(action Action, args ...string) error {
	a := []string{"-t", string(c.Table), string(action), "OUTPUT"}
	if len(args) > 0 {
		a = append(a, args...)
	}
	if output, err := Raw(append(a, "-j", c.Name)...); err != nil {
		return err
	} else if len(output) != 0 {
		return &ChainError{Chain: "OUTPUT", Output: output}
	}
	return nil
}

func (c *Chain) Remove() error {
	// Ignore errors - This could mean the chains were never set up
	if c.Table == Nat {
		c.Prerouting(Delete, "-m", "addrtype", "--dst-type", "LOCAL")
		c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8")
		c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL") // Created in versions <= 0.1.6

		c.Prerouting(Delete)
		c.Output(Delete)
	}
	Raw("-t", string(c.Table), "-F", c.Name)
	Raw("-t", string(c.Table), "-X", c.Name)
	return nil
}

// Check if a dnat rule exists
func OperatePortMap(action Action, rule []string) error {
	if output, err := Raw(append([]string{
		"-t", string(Nat), string(action), "PREROUTING"}, rule...)...); err != nil {
		return fmt.Errorf("Unable to setup network port map: %s", err)
	} else if len(output) != 0 {
		return &ChainError{Chain: "PREROUTING", Output: output}
	}

	return nil
}

func PortMapExists(rule []string) bool {
	// iptables -C, --check option was added in v.1.4.11
	// http://ftp.netfilter.org/pub/iptables/changes-iptables-1.4.11.txt

	// try -C
	// if exit status is 0 then return true, the rule exists
	if _, err := Raw(append([]string{
		"-t", "nat", "-C", "PREROUTING"}, rule...)...); err == nil {
		return true
	}

	return false
}

func PortMapUsed(rule []string) bool {
	// parse "iptables -S" for the rule (this checks rules in a specific chain
	// in a specific table)
	existingRules, _ := exec.Command("iptables", "-t", "nat", "-S", "PREROUTING").Output()
	ruleString := strings.Join(rule, " ")

	glog.V(3).Infof("MapUsed %s", ruleString)
	// regex to replace ips in rule
	re := regexp.MustCompile(`[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\:[0-9]{1,2}`)

	return strings.Contains(
		re.ReplaceAllString(string(existingRules), "?"),
		re.ReplaceAllString(ruleString, "?"),
	)
}

// Check if a rule exists
func Exists(table Table, chain string, rule ...string) bool {
	if string(table) == "" {
		table = Filter
	}

	// iptables -C, --check option was added in v.1.4.11
	// http://ftp.netfilter.org/pub/iptables/changes-iptables-1.4.11.txt

	// try -C
	// if exit status is 0 then return true, the rule exists
	if _, err := Raw(append([]string{
		"-t", string(table), "-C", chain}, rule...)...); err == nil {
		return true
	}

	// parse "iptables -S" for the rule (this checks rules in a specific chain
	// in a specific table)
	ruleString := strings.Join(rule, " ")
	existingRules, _ := exec.Command("iptables", "-t", string(table), "-S", chain).Output()

	// regex to replace ips in rule
	// because MASQUERADE rule will not be exactly what was passed
	re := regexp.MustCompile(`[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\/[0-9]{1,2}`)

	return strings.Contains(
		re.ReplaceAllString(string(existingRules), "?"),
		re.ReplaceAllString(ruleString, "?"),
	)
}

// Call 'iptables' system command, passing supplied arguments
func Raw(args ...string) ([]byte, error) {

	if err := initCheck(); err != nil {
		return nil, err
	}
	if supportsXlock {
		args = append([]string{"--wait"}, args...)
	}

	glog.V(3).Infof("%s, %v", iptablesPath, args)

	output, err := exec.Command(iptablesPath, args...).CombinedOutput()
	if err != nil {
		return nil, fmt.Errorf("iptables failed: iptables %v: %s (%s)", strings.Join(args, " "), output, err)
	}

	// ignore iptables' message about xtables lock
	if strings.Contains(string(output), "waiting for it to exit") {
		output = []byte("")
	}

	return output, err
}
<commit_msg>network: remove useless code<commit_after>package iptables

import (
	"errors"
	"fmt"
	"os/exec"
	"regexp"
	"strings"

	"hyper/lib/glog"
)

type Action string
type Table string

const (
	Append Action = "-A"
	Delete Action = "-D"
	Insert Action = "-I"
	Nat    Table  = "nat"
	Filter Table  = "filter"
	Mangle Table  = "mangle"
)

var (
	iptablesPath        string
	supportsXlock       = false
	ErrIptablesNotFound = errors.New("Iptables not found")
)

type Chain struct {
	Name   string
	Bridge string
	Table  Table
}

type ChainError struct {
	Chain  string
	Output []byte
}

func (e *ChainError) Error() string {
	return fmt.Sprintf("Error iptables %s: %s", e.Chain, string(e.Output))
}

func initCheck() error {
	if iptablesPath == "" {
		path, err := exec.LookPath("iptables")
		if err != nil {
			return ErrIptablesNotFound
		}
		iptablesPath = path
		supportsXlock = exec.Command(iptablesPath, "--wait", "-L", "-n").Run() == nil
	}
	return nil
}

// Check if a dnat rule exists
func OperatePortMap(action Action, rule []string) error {
	if output, err := Raw(append([]string{
		"-t", string(Nat), string(action), "PREROUTING"}, rule...)...); err != nil {
		return fmt.Errorf("Unable to setup network port map: %s", err)
	} else if len(output) != 0 {
		return &ChainError{Chain: "PREROUTING", Output: output}
	}

	return nil
}

func PortMapExists(rule []string) bool {
	// iptables -C, --check option was added in v.1.4.11
	// http://ftp.netfilter.org/pub/iptables/changes-iptables-1.4.11.txt

	// try -C
	// if exit status is 0 then return true, the rule exists
	if _, err := Raw(append([]string{
		"-t", "nat", "-C", "PREROUTING"}, rule...)...); err == nil {
		return true
	}

	return false
}

func PortMapUsed(rule []string) bool {
	// parse "iptables -S" for the rule (this checks rules in a specific chain
	// in a specific table)
	existingRules, _ := exec.Command("iptables", "-t", "nat", "-S", "PREROUTING").Output()
	ruleString := strings.Join(rule, " ")

	glog.V(3).Infof("MapUsed %s", ruleString)
	// regex to replace ips in rule
	re := regexp.MustCompile(`[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\:[0-9]{1,2}`)

	return strings.Contains(
		re.ReplaceAllString(string(existingRules), "?"),
		re.ReplaceAllString(ruleString, "?"),
	)
}

// Check if a rule exists
func Exists(table Table, chain string, rule ...string) bool {
	if string(table) == "" {
		table = Filter
	}

	// iptables -C, --check option was added in v.1.4.11
	// http://ftp.netfilter.org/pub/iptables/changes-iptables-1.4.11.txt

	// try -C
	// if exit status is 0 then return true, the rule exists
	if _, err := Raw(append([]string{
		"-t", string(table), "-C", chain}, rule...)...); err == nil {
		return true
	}

	// parse "iptables -S" for the rule (this checks rules in a specific chain
	// in a specific table)
	ruleString := strings.Join(rule, " ")
	existingRules, _ := exec.Command("iptables", "-t", string(table), "-S", chain).Output()

	// regex to replace ips in rule
	// because MASQUERADE rule will not be exactly what was passed
	re := regexp.MustCompile(`[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\/[0-9]{1,2}`)

	return strings.Contains(
		re.ReplaceAllString(string(existingRules), "?"),
		re.ReplaceAllString(ruleString, "?"),
	)
}

// Call 'iptables' system command, passing supplied arguments
func Raw(args ...string) ([]byte, error) {
	if err := initCheck(); err != nil {
		return nil, err
	}
	if supportsXlock {
		args = append([]string{"--wait"}, args...)
	}

	glog.V(3).Infof("%s, %v", iptablesPath, args)

	output, err := exec.Command(iptablesPath, args...).CombinedOutput()
	if err != nil {
		return nil, fmt.Errorf("iptables failed: iptables %v: %s (%s)", strings.Join(args, " "), output, err)
	}

	// ignore iptables' message about xtables lock
	if strings.Contains(string(output), "waiting for it to exit") {
		output = []byte("")
	}

	return output, err
}
<|endoftext|>


<commit_before>package local

import (
	"fmt"
	"strings"

	"github.com/creativesoftwarefdn/weaviate/database/schema"
	"github.com/creativesoftwarefdn/weaviate/database/schema/kind"
	"github.com/creativesoftwarefdn/weaviate/models"
	"github.com/fatih/camelcase"
)

// Validate if a class can be added to the schema
func (l *localSchemaManager) validateCanAddClass(knd kind.Kind, class *models.SemanticSchemaClass) error {
	// First check if there is a name clash.
	err := l.validateClassNameUniqueness(class.Class)
	if err != nil {
		return err
	}

	err = l.validateClassNameOrKeywordsCorrect(knd, class.Class, class.Keywords)
	if err != nil {
		return err
	}

	// Check properties
	foundNames := map[string]bool{}
	for _, property := range class.Properties {
		err = l.validatePropertyNameOrKeywordsCorrect(class.Class, property.Name, property.Keywords)
		if err != nil {
			return err
		}

		if foundNames[property.Name] == true {
			return fmt.Errorf("Name '%s' already in use as a property name for class '%s'", property.Name, class.Class)
		}

		foundNames[property.Name] = true

		// Validate data type of property.
		schema := l.GetSchema()
		err, _ := (&schema).FindPropertyDataType(property.AtDataType)
		if err != nil {
			return fmt.Errorf("Data type fo property '%s' is invalid; %v", property.Name, err)
		}
	}

	// all is fine!
	return nil
}

func (l *localSchemaManager) validateClassNameUniqueness(className string) error {
	for _, otherClass := range l.schemaState.SchemaFor(kind.ACTION_KIND).Classes {
		if className == otherClass.Class {
			return fmt.Errorf("Name '%s' already used as a name for an Action class", className)
		}
	}

	for _, otherClass := range l.schemaState.SchemaFor(kind.THING_KIND).Classes {
		if className == otherClass.Class {
			return fmt.Errorf("Name '%s' already used as a name for a Thing class", className)
		}
	}

	return nil
}

// Check that the format of the name is correct
// Check that the name is acceptable according to the contextionary
func (l *localSchemaManager) validateClassNameOrKeywordsCorrect(knd kind.Kind, className string, keywords models.SemanticSchemaKeywords) error {
	err, _ := schema.ValidateClassName(className)
	if err != nil {
		return err
	}

	if len(keywords) > 0 {
		for _, keyword := range keywords {
			word := strings.ToLower(keyword.Keyword)
			if l.contextionary != nil {
				idx := l.contextionary.WordToItemIndex(word)
				if !idx.IsPresent() {
					return fmt.Errorf("Could not find the keyword '%s' for class '%s' in the contextionary", word, className)
				}
			}
		}
	} else {
		camelParts := camelcase.Split(className)
		for _, part := range camelParts {
			word := strings.ToLower(part)
			if l.contextionary != nil {
				idx := l.contextionary.WordToItemIndex(word)
				if !idx.IsPresent() {
					return fmt.Errorf("Could not find the word '%s' from the class name '%s' in the contextionary. Consider using keywords to define the semantic meaning of this class.", word, className)
				}
			}
		}
	}

	return nil
}

// Verify if we can add the passed property to the passed in class.
// We need the total schema state to be able to check that references etc are valid.
func (l *localSchemaManager) validateCanAddProperty(property *models.SemanticSchemaClassProperty, class *models.SemanticSchemaClass) error {
	// Verify format of property.
	err, _ := schema.ValidatePropertyName(property.Name)
	if err != nil {
		return err
	}

	// First check if there is a name clash.
	err = validatePropertyNameUniqueness(property.Name, class)
	if err != nil {
		return err
	}

	err = l.validatePropertyNameOrKeywordsCorrect(class.Class, property.Name, property.Keywords)
	if err != nil {
		return err
	}

	// Validate data type of property.
	schema := l.GetSchema()
	_, err = (&schema).FindPropertyDataType(property.AtDataType)
	if err != nil {
		return fmt.Errorf("Data type fo property '%s' is invalid; %v", property.Name, err)
	}

	// all is fine!
	return nil
}

func validatePropertyNameUniqueness(propertyName string, class *models.SemanticSchemaClass) error {
	for _, otherProperty := range class.Properties {
		if propertyName == otherProperty.Name {
			return fmt.Errorf("Name '%s' already in use as a property name for class '%s'", propertyName, class.Class)
		}
	}

	return nil
}

// Check that the format of the name is correct
// Check that the name is acceptable according to the contextionary
func (l *localSchemaManager) validatePropertyNameOrKeywordsCorrect(className string, propertyName string, keywords models.SemanticSchemaKeywords) error {
	err, _ := schema.ValidatePropertyName(propertyName)
	if err != nil {
		return err
	}

	if len(keywords) > 0 {
		for _, keyword := range keywords {
			word := strings.ToLower(keyword.Keyword)
			if l.contextionary != nil {
				idx := l.contextionary.WordToItemIndex(word)
				if !idx.IsPresent() {
					return fmt.Errorf("Could not find the keyword '%s' for property '%s' in the class '%s' in the contextionary", word, propertyName, className)
				}
			}
		}
	} else {
		camelParts := camelcase.Split(propertyName)
		for _, part := range camelParts {
			word := strings.ToLower(part)
			if l.contextionary != nil {
				idx := l.contextionary.WordToItemIndex(word)
				if !idx.IsPresent() {
					return fmt.Errorf("Could not find the word '%s' from the property '%s' in the class name '%s' in the contextionary. Consider using keywords to define the semantic meaning of this class.", word, propertyName, className)
				}
			}
		}
	}

	return nil
}
<commit_msg>Update call to FindPropertyDataType to reflect change in ret types.<commit_after>package local

import (
	"fmt"
	"strings"

	"github.com/creativesoftwarefdn/weaviate/database/schema"
	"github.com/creativesoftwarefdn/weaviate/database/schema/kind"
	"github.com/creativesoftwarefdn/weaviate/models"
	"github.com/fatih/camelcase"
)

// Validate if a class can be added to the schema
func (l *localSchemaManager) validateCanAddClass(knd kind.Kind, class *models.SemanticSchemaClass) error {
	// First check if there is a name clash.
	err := l.validateClassNameUniqueness(class.Class)
	if err != nil {
		return err
	}

	err = l.validateClassNameOrKeywordsCorrect(knd, class.Class, class.Keywords)
	if err != nil {
		return err
	}

	// Check properties
	foundNames := map[string]bool{}
	for _, property := range class.Properties {
		err = l.validatePropertyNameOrKeywordsCorrect(class.Class, property.Name, property.Keywords)
		if err != nil {
			return err
		}

		if foundNames[property.Name] == true {
			return fmt.Errorf("Name '%s' already in use as a property name for class '%s'", property.Name, class.Class)
		}

		foundNames[property.Name] = true

		// Validate data type of property.
		schema := l.GetSchema()
		_, err := (&schema).FindPropertyDataType(property.AtDataType)
		if err != nil {
			return fmt.Errorf("Data type fo property '%s' is invalid; %v", property.Name, err)
		}
	}

	// all is fine!
	return nil
}

func (l *localSchemaManager) validateClassNameUniqueness(className string) error {
	for _, otherClass := range l.schemaState.SchemaFor(kind.ACTION_KIND).Classes {
		if className == otherClass.Class {
			return fmt.Errorf("Name '%s' already used as a name for an Action class", className)
		}
	}

	for _, otherClass := range l.schemaState.SchemaFor(kind.THING_KIND).Classes {
		if className == otherClass.Class {
			return fmt.Errorf("Name '%s' already used as a name for a Thing class", className)
		}
	}

	return nil
}

// Check that the format of the name is correct
// Check that the name is acceptable according to the contextionary
func (l *localSchemaManager) validateClassNameOrKeywordsCorrect(knd kind.Kind, className string, keywords models.SemanticSchemaKeywords) error {
	err, _ := schema.ValidateClassName(className)
	if err != nil {
		return err
	}

	if len(keywords) > 0 {
		for _, keyword := range keywords {
			word := strings.ToLower(keyword.Keyword)
			if l.contextionary != nil {
				idx := l.contextionary.WordToItemIndex(word)
				if !idx.IsPresent() {
					return fmt.Errorf("Could not find the keyword '%s' for class '%s' in the contextionary", word, className)
				}
			}
		}
	} else {
		camelParts := camelcase.Split(className)
		for _, part := range camelParts {
			word := strings.ToLower(part)
			if l.contextionary != nil {
				idx := l.contextionary.WordToItemIndex(word)
				if !idx.IsPresent() {
					return fmt.Errorf("Could not find the word '%s' from the class name '%s' in the contextionary. Consider using keywords to define the semantic meaning of this class.", word, className)
				}
			}
		}
	}

	return nil
}

// Verify if we can add the passed property to the passed in class.
// We need the total schema state to be able to check that references etc are valid.
func (l *localSchemaManager) validateCanAddProperty(property *models.SemanticSchemaClassProperty, class *models.SemanticSchemaClass) error {
	// Verify format of property.
	err, _ := schema.ValidatePropertyName(property.Name)
	if err != nil {
		return err
	}

	// First check if there is a name clash.
	err = validatePropertyNameUniqueness(property.Name, class)
	if err != nil {
		return err
	}

	err = l.validatePropertyNameOrKeywordsCorrect(class.Class, property.Name, property.Keywords)
	if err != nil {
		return err
	}

	// Validate data type of property.
	schema := l.GetSchema()
	_, err = (&schema).FindPropertyDataType(property.AtDataType)
	if err != nil {
		return fmt.Errorf("Data type fo property '%s' is invalid; %v", property.Name, err)
	}

	// all is fine!
	return nil
}

func validatePropertyNameUniqueness(propertyName string, class *models.SemanticSchemaClass) error {
	for _, otherProperty := range class.Properties {
		if propertyName == otherProperty.Name {
			return fmt.Errorf("Name '%s' already in use as a property name for class '%s'", propertyName, class.Class)
		}
	}

	return nil
}

// Check that the format of the name is correct
// Check that the name is acceptable according to the contextionary
func (l *localSchemaManager) validatePropertyNameOrKeywordsCorrect(className string, propertyName string, keywords models.SemanticSchemaKeywords) error {
	err, _ := schema.ValidatePropertyName(propertyName)
	if err != nil {
		return err
	}

	if len(keywords) > 0 {
		for _, keyword := range keywords {
			word := strings.ToLower(keyword.Keyword)
			if l.contextionary != nil {
				idx := l.contextionary.WordToItemIndex(word)
				if !idx.IsPresent() {
					return fmt.Errorf("Could not find the keyword '%s' for property '%s' in the class '%s' in the contextionary", word, propertyName, className)
				}
			}
		}
	} else {
		camelParts := camelcase.Split(propertyName)
		for _, part := range camelParts {
			word := strings.ToLower(part)
			if l.contextionary != nil {
				idx := l.contextionary.WordToItemIndex(word)
				if !idx.IsPresent() {
					return fmt.Errorf("Could not find the word '%s' from the property '%s' in the class name '%s' in the contextionary. Consider using keywords to define the semantic meaning of this class.", word, propertyName, className)
				}
			}
		}
	}

	return nil
}
<|endoftext|>


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

package main_test

import (
	main "cmd/go"
	"runtime"
	"testing"
)

func TestNoteReading(t *testing.T) {
	tg := testgo(t)
	defer tg.cleanup()
	tg.tempFile("hello.go", `package main; func main() { print("hello, world\n") }`)
	const buildID = "TestNoteReading-Build-ID"
	tg.run("build", "-ldflags", "-buildid="+buildID, "-o", tg.path("hello.exe"), tg.path("hello.go"))
	id, err := main.ReadBuildIDFromBinary(tg.path("hello.exe"))
	if err != nil {
		t.Fatalf("reading build ID from hello binary: %v", err)
	}
	if id != buildID {
		t.Fatalf("buildID in hello binary = %q, want %q", id, buildID)
	}

	if runtime.GOOS == "linux" && (runtime.GOARCH == "ppc64le" || runtime.GOARCH == "ppc64") {
		t.Skipf("skipping - golang.org/issue/11184")
	}

	if runtime.GOOS == "linux" && (runtime.GOARCH == "mips64le" || runtime.GOARCH == "mips64") {
		t.Skipf("skipping - external linking not supported, golang.org/issue/12560")
	}

	switch runtime.GOOS {
	case "plan9":
		// no external linking
		t.Logf("no external linking - skipping linkmode=external test")

	default:
		tg.run("build", "-ldflags", "-buildid="+buildID+" -linkmode=external", "-o", tg.path("hello.exe"), tg.path("hello.go"))
		id, err := main.ReadBuildIDFromBinary(tg.path("hello.exe"))
		if err != nil {
			t.Fatalf("reading build ID from hello binary (linkmode=external): %v", err)
		}
		if id != buildID {
			t.Fatalf("buildID in hello binary = %q, want %q (linkmode=external)", id, buildID)
		}
	}
}
<commit_msg>cmd/go: Skip note reading test with linkmode external on openbsd/arm<commit_after>// Copyright 2015 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main_test

import (
	main "cmd/go"
	"runtime"
	"testing"
)

func TestNoteReading(t *testing.T) {
	tg := testgo(t)
	defer tg.cleanup()
	tg.tempFile("hello.go", `package main; func main() { print("hello, world\n") }`)
	const buildID = "TestNoteReading-Build-ID"
	tg.run("build", "-ldflags", "-buildid="+buildID, "-o", tg.path("hello.exe"), tg.path("hello.go"))
	id, err := main.ReadBuildIDFromBinary(tg.path("hello.exe"))
	if err != nil {
		t.Fatalf("reading build ID from hello binary: %v", err)
	}
	if id != buildID {
		t.Fatalf("buildID in hello binary = %q, want %q", id, buildID)
	}

	switch {
	case runtime.GOOS == "linux" && (runtime.GOARCH == "ppc64le" || runtime.GOARCH == "ppc64"):
		t.Skipf("skipping - external linking not supported, golang.org/issue/11184")
	case runtime.GOOS == "linux" && (runtime.GOARCH == "mips64le" || runtime.GOARCH == "mips64"):
		t.Skipf("skipping - external linking not supported, golang.org/issue/12560")
	case runtime.GOOS == "openbsd" && runtime.GOARCH == "arm":
		t.Skipf("skipping - external linking not supported, golang.org/issue/10619")
	case runtime.GOOS == "plan9":
		t.Skipf("skipping - external linking not supported")
	}

	tg.run("build", "-ldflags", "-buildid="+buildID+" -linkmode=external", "-o", tg.path("hello.exe"), tg.path("hello.go"))
	id, err = main.ReadBuildIDFromBinary(tg.path("hello.exe"))
	if err != nil {
		t.Fatalf("reading build ID from hello binary (linkmode=external): %v", err)
	}
	if id != buildID {
		t.Fatalf("buildID in hello binary = %q, want %q (linkmode=external)", id, buildID)
	}
}
<|endoftext|>


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

package main_test

import (
	"go/build"
	"runtime"
	"testing"

	"cmd/internal/buildid"
)

func TestNoteReading(t *testing.T) {
	// cmd/internal/buildid already has tests that the basic reading works.
	// This test is essentially checking that -ldflags=-buildid=XXX works,
	// both in internal and external linking mode.
	tg := testgo(t)
	defer tg.cleanup()
	tg.tempFile("hello.go", `package main; func main() { print("hello, world\n") }`)
	const buildID = "TestNoteReading-Build-ID"
	tg.run("build", "-ldflags", "-buildid="+buildID, "-o", tg.path("hello.exe"), tg.path("hello.go"))
	id, err := buildid.ReadFile(tg.path("hello.exe"))
	if err != nil {
		t.Fatalf("reading build ID from hello binary: %v", err)
	}
	if id != buildID {
		t.Fatalf("buildID in hello binary = %q, want %q", id, buildID)
	}

	switch {
	case !build.Default.CgoEnabled:
		t.Skipf("skipping - no cgo, so assuming external linking not available")
	case runtime.GOOS == "linux" && (runtime.GOARCH == "ppc64le" || runtime.GOARCH == "ppc64"):
		t.Skipf("skipping - external linking not supported, golang.org/issue/11184")
	case runtime.GOOS == "openbsd" && runtime.GOARCH == "arm":
		t.Skipf("skipping - external linking not supported, golang.org/issue/10619")
	case runtime.GOOS == "plan9":
		t.Skipf("skipping - external linking not supported")
	}

	tg.run("build", "-ldflags", "-buildid="+buildID+" -linkmode=external", "-o", tg.path("hello2.exe"), tg.path("hello.go"))
	id, err = buildid.ReadFile(tg.path("hello2.exe"))
	if err != nil {
		t.Fatalf("reading build ID from hello binary (linkmode=external): %v", err)
	}
	if id != buildID {
		t.Fatalf("buildID in hello binary = %q, want %q (linkmode=external)", id, buildID)
	}

	switch runtime.GOOS {
	case "dragonfly", "freebsd", "linux", "netbsd", "openbsd":
		// Test while forcing use of the gold linker, since in the past
		// we've had trouble reading the notes generated by gold.
		err := tg.doRun([]string{"build", "-ldflags", "-buildid=" + buildID + " -linkmode=external -extldflags=-fuse-ld=gold", "-o", tg.path("hello3.exe"), tg.path("hello.go")})
		if err != nil && (tg.grepCountBoth("invalid linker") > 0 || tg.grepCountBoth("gold") > 0) {
			// It's not an error if gold isn't there.
			t.Log("skipping gold test")
			break
		}
		id, err = buildid.ReadFile(tg.path("hello3.exe"))
		if err != nil {
			t.Fatalf("reading build ID from hello binary (linkmode=external -extldflags=-fuse-ld=gold): %v", err)
		}
		if id != buildID {
			t.Fatalf("buildID in hello binary = %q, want %q (linkmode=external -extldflags=-fuse-ld=gold)", id, buildID)
		}
	}
}
<commit_msg>cmd/go: skip gold-specific part of TestNoteReading if gold is unavailable<commit_after>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main_test

import (
	"go/build"
	"runtime"
	"testing"

	"cmd/internal/buildid"
)

func TestNoteReading(t *testing.T) {
	// cmd/internal/buildid already has tests that the basic reading works.
	// This test is essentially checking that -ldflags=-buildid=XXX works,
	// both in internal and external linking mode.
	tg := testgo(t)
	defer tg.cleanup()
	tg.tempFile("hello.go", `package main; func main() { print("hello, world\n") }`)
	const buildID = "TestNoteReading-Build-ID"
	tg.run("build", "-ldflags", "-buildid="+buildID, "-o", tg.path("hello.exe"), tg.path("hello.go"))
	id, err := buildid.ReadFile(tg.path("hello.exe"))
	if err != nil {
		t.Fatalf("reading build ID from hello binary: %v", err)
	}
	if id != buildID {
		t.Fatalf("buildID in hello binary = %q, want %q", id, buildID)
	}

	switch {
	case !build.Default.CgoEnabled:
		t.Skipf("skipping - no cgo, so assuming external linking not available")
	case runtime.GOOS == "linux" && (runtime.GOARCH == "ppc64le" || runtime.GOARCH == "ppc64"):
		t.Skipf("skipping - external linking not supported, golang.org/issue/11184")
	case runtime.GOOS == "openbsd" && runtime.GOARCH == "arm":
		t.Skipf("skipping - external linking not supported, golang.org/issue/10619")
	case runtime.GOOS == "plan9":
		t.Skipf("skipping - external linking not supported")
	}

	tg.run("build", "-ldflags", "-buildid="+buildID+" -linkmode=external", "-o", tg.path("hello2.exe"), tg.path("hello.go"))
	id, err = buildid.ReadFile(tg.path("hello2.exe"))
	if err != nil {
		t.Fatalf("reading build ID from hello binary (linkmode=external): %v", err)
	}
	if id != buildID {
		t.Fatalf("buildID in hello binary = %q, want %q (linkmode=external)", id, buildID)
	}

	switch runtime.GOOS {
	case "dragonfly", "freebsd", "linux", "netbsd", "openbsd":
		// Test while forcing use of the gold linker, since in the past
		// we've had trouble reading the notes generated by gold.
		err := tg.doRun([]string{"build", "-ldflags", "-buildid=" + buildID + " -linkmode=external -extldflags=-fuse-ld=gold", "-o", tg.path("hello3.exe"), tg.path("hello.go")})
		if err != nil {
			if tg.grepCountBoth("(invalid linker|gold|cannot find 'ld')") > 0 {
				// It's not an error if gold isn't there. gcc claims it "cannot find 'ld'" if
				// ld.gold is missing, see issue #22340.
				t.Log("skipping gold test")
				break
			}
			t.Fatalf("building hello binary: %v", err)
		}
		id, err = buildid.ReadFile(tg.path("hello3.exe"))
		if err != nil {
			t.Fatalf("reading build ID from hello binary (linkmode=external -extldflags=-fuse-ld=gold): %v", err)
		}
		if id != buildID {
			t.Fatalf("buildID in hello binary = %q, want %q (linkmode=external -extldflags=-fuse-ld=gold)", id, buildID)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	".."
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/BurntSushi/toml"
	"github.com/bitly/go-nsq"
	log "github.com/Sirupsen/logrus"
	"net/http"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"
)

var (
	ip = flag.String("ip", "4.3.2.1", "Node ip address")
	configFile = flag.String("config", "haaas.conf", "Configuration file")
	version = flag.Bool("version", false, "Print current version")
	verbose = flag.Bool("verbose", false, "Log in verbose mode")
	config = nsq.NewConfig()
	properties *haaasd.Config
	daemon      *haaasd.Daemon
	producer    *nsq.Producer
	syslog        *haaasd.Syslog
	reloadChan = make(chan haaasd.ReloadEvent)
)

func main() {
	log.SetFormatter(&log.TextFormatter{})
	flag.Parse()

	if *version {
		println(haaasd.AppVersion)
		os.Exit(0)
	}

	if *verbose {
		log.SetLevel(log.DebugLevel)
		log.WithField("loglevel", "debug").Info("Change loglevel")
	}

	loadProperties()

	daemon = haaasd.NewDaemon(properties)
	syslog = haaasd.NewSyslog(properties)
	syslog.Init()
	log.WithFields(log.Fields{
		"status": properties.Status,
		"id":    properties.NodeId(),
	}).Info("Starting haaasd")

	producer, _ = nsq.NewProducer(properties.ProducerAddr, config)

	initProducer()
	time.Sleep(1 * time.Second)

	var wg sync.WaitGroup
	// Start http API
	restApi := haaasd.NewRestApi(properties)
	go func() {
		defer wg.Done()
		wg.Add(1)
		err := restApi.Start()
		if err != nil {
			log.Fatal("Cannot start api")
		}
	}()

	// Start slave consumer
	go func() {
		defer wg.Done()
		wg.Add(1)
		consumer, _ := nsq.NewConsumer(fmt.Sprintf("commit_requested_%s", properties.ClusterId), properties.NodeId(), config)
		consumer.AddHandler(nsq.HandlerFunc(onCommitRequested))
		err := consumer.ConnectToNSQLookupd(properties.LookupdAddr)
		if err != nil {
			log.Panic("Could not connect")
		}
	}()

	// Start master consumer
	go func() {
		defer wg.Done()
		wg.Add(1)
		consumer, _ := nsq.NewConsumer(fmt.Sprintf("commit_slave_completed_%s", properties.ClusterId), properties.NodeId(), config)
		consumer.AddHandler(nsq.HandlerFunc(onCommitSlaveRequested))
		err := consumer.ConnectToNSQLookupd(properties.LookupdAddr)
		if err != nil {
			log.Panic("Could not connect")
		}
	}()

	// Start complete consumer
	go func() {
		defer wg.Done()
		wg.Add(1)
		consumer, _ := nsq.NewConsumer(fmt.Sprintf("commit_completed_%s", properties.ClusterId), properties.NodeId(), config)
		consumer.AddHandler(nsq.HandlerFunc(onCommitCompleted))
	}()

	// Start reload pipeline
	stopChan := make(chan interface{}, 1)
	go func() {
		defer wg.Done()
		wg.Add(1)
		for {
			select {
			case reload := <-reloadChan:
				reload.Execute()
			case  <- stopChan:
				return
			}
		}
	}()

	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
	select {
	case signal := <-sigChan:
		log.Printf("Got signal: %v\n", signal)
	}
	restApi.Stop()

	log.Printf("Waiting on server to stop\n")
	wg.Wait()
}

func initProducer() {
	// Create required topics
	topics := []string{"commit_slave_completed", "commit_completed", "commit_failed"}
	channels := []string{"slave", "master"}
	topicChan := make(chan string, len(topics))
	for i := range topics {
		topicChan <- topics[i]
	}
	left := len(topics)
	for left > 0 {
		topic := <-topicChan
		log.WithField("topic", topic).Info("Creating topic")
		url := fmt.Sprintf("%s/topic/create?topic=%s_%s", properties.ProducerRestAddr, topic, properties.ClusterId)
		resp, err := http.PostForm(url, nil)
		if err != nil || resp.StatusCode != 200 {
			topicChan <- topic
			continue
		}
		for channel := range channels {
			log.WithField("channel", channels[channel]).Info("Creating channel")
			url := fmt.Sprintf("%s/channel/create?topic=%s_%s&channel=%s-%s", properties.ProducerRestAddr, topic, properties.ClusterId, properties.ClusterId, channels[channel])
			resp, err := http.PostForm(url, nil)
			if err != nil || resp.StatusCode != 200 {
				topicChan <- topic
				continue
			}
		}

		log.WithField("topic", topic).Info("Topic created")
		left--

	}
}

// loadProperties load properties file
func loadProperties() {
	properties = haaasd.DefaultConfig()
	if _, err := toml.DecodeFile(*configFile, properties); err != nil {
		log.Fatal(err)
		os.Exit(1)
	}
	properties.IpAddr = *ip
	len := len(properties.HapHome)
	if properties.HapHome[len - 1] == '/' {
		properties.HapHome = properties.HapHome[:len - 1]
	}
}

func filteredHandler(event string, message *nsq.Message, target string, f haaasd.HandlerFunc) error {
	defer message.Finish()
	match, err := daemon.Is(target)
	if err != nil {
		return err
	}

	if match {
		log.WithField("event", event).Debug("Handle event")
		data, err := bodyToData(message.Body)
		if err != nil {
			log.WithError(err).Error("Unable to read data")
			return err
		}

		switch event {
		case "commit_requested":
			reloadChan <- haaasd.ReloadEvent{F: f, Message: data}
		case "commit_slave_completed":
			reloadChan <- haaasd.ReloadEvent{F: f, Message: data}
		case "commit_completed":
			logAndForget(data)
		}

	} else {
		log.WithField("event", event).Debug("Ignore event")
	}

	return nil
}

func onCommitRequested(message *nsq.Message) error {
	return filteredHandler("commit_requested", message, "slave", reloadSlave)
}
func onCommitSlaveRequested(message *nsq.Message) error {
	return filteredHandler("commit_slave_completed", message, "master", reloadMaster)
}
func onCommitCompleted(message *nsq.Message) error {
	return filteredHandler("commit_completed", message, "slave", logAndForget)
}

// logAndForget is a generic function to just log event
func logAndForget(data *haaasd.EventMessage) error {
	log.WithFields(log.Fields{
		"correlationId": data.Correlationid,
		"application" : data.Application,
		"platform": data.Platform,
	}).Debug("Commit completed")
	return nil
}

func reloadSlave(data *haaasd.EventMessage) error {
	hap := haaasd.NewHaproxy("slave", properties, data.Application, data.Platform, data.HapVersion)

	status, err := hap.ApplyConfiguration(data)
	if err == nil {
		if status != haaasd.UNCHANGED {
			syslog.Restart()
		}
		publishMessage("commit_slave_completed_", data)
	} else {
		log.WithFields(log.Fields{
			"correlationId": data.Correlationid,
			"application" : data.Application,
			"platform": data.Platform,
		}).WithError(err).Error("Commit failed")
		publishMessage("commit_failed_", map[string]string{"application": data.Application, "platform": data.Platform, "correlationid": data.Correlationid})
	}
	return nil
}

func reloadMaster(data *haaasd.EventMessage) error {
	hap := haaasd.NewHaproxy("master", properties, data.Application, data.Platform, data.HapVersion)
	status, err := hap.ApplyConfiguration(data)
	if err == nil {
		if status != haaasd.UNCHANGED {
			syslog.Restart()
		}
		publishMessage("commit_completed_", map[string]string{"application": data.Application, "platform": data.Platform, "correlationid": data.Correlationid})
	} else {
		log.WithFields(log.Fields{
			"correlationId": data.Correlationid,
			"application" : data.Application,
			"platform": data.Platform,
		}).WithError(err).Error("Commit failed")
		publishMessage("commit_failed_", map[string]string{"application": data.Application, "platform": data.Platform, "correlationid": data.Correlationid})
	}
	return nil
}

// Unmarshal json to EventMessage
func bodyToData(jsonStream []byte) (*haaasd.EventMessage, error) {
	dec := json.NewDecoder(bytes.NewReader(jsonStream))
	var message haaasd.EventMessage
	err := dec.Decode(&message)
	return &message, err
}

func publishMessage(topic_prefix string, data interface{}) error {
	jsonMsg, _ := json.Marshal(data)
	topic := topic_prefix + properties.ClusterId
	log.WithField("topic", topic).WithField("payload", string(jsonMsg)).Debug("Publish")
	return producer.Publish(topic, []byte(jsonMsg))
}
<commit_msg>Add request_completed consumer<commit_after>package main

import (
	".."
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/BurntSushi/toml"
	"github.com/bitly/go-nsq"
	log "github.com/Sirupsen/logrus"
	"net/http"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"
)

var (
	ip = flag.String("ip", "4.3.2.1", "Node ip address")
	configFile = flag.String("config", "haaas.conf", "Configuration file")
	version = flag.Bool("version", false, "Print current version")
	verbose = flag.Bool("verbose", false, "Log in verbose mode")
	config = nsq.NewConfig()
	properties *haaasd.Config
	daemon      *haaasd.Daemon
	producer    *nsq.Producer
	syslog        *haaasd.Syslog
	reloadChan = make(chan haaasd.ReloadEvent)
)

func main() {
	log.SetFormatter(&log.TextFormatter{})
	flag.Parse()

	if *version {
		println(haaasd.AppVersion)
		os.Exit(0)
	}

	if *verbose {
		log.SetLevel(log.DebugLevel)
		log.WithField("loglevel", "debug").Info("Change loglevel")
	}

	loadProperties()

	daemon = haaasd.NewDaemon(properties)
	syslog = haaasd.NewSyslog(properties)
	syslog.Init()
	log.WithFields(log.Fields{
		"status": properties.Status,
		"id":    properties.NodeId(),
	}).Info("Starting haaasd")

	producer, _ = nsq.NewProducer(properties.ProducerAddr, config)

	initProducer()
	time.Sleep(1 * time.Second)

	var wg sync.WaitGroup
	// Start http API
	restApi := haaasd.NewRestApi(properties)
	go func() {
		defer wg.Done()
		wg.Add(1)
		err := restApi.Start()
		if err != nil {
			log.Fatal("Cannot start api")
		}
	}()

	// Start slave consumer
	go func() {
		defer wg.Done()
		wg.Add(1)
		consumer, _ := nsq.NewConsumer(fmt.Sprintf("commit_requested_%s", properties.ClusterId), properties.NodeId(), config)
		consumer.AddHandler(nsq.HandlerFunc(onCommitRequested))
		err := consumer.ConnectToNSQLookupd(properties.LookupdAddr)
		if err != nil {
			log.Panic("Could not connect")
		}
	}()

	// Start master consumer
	go func() {
		defer wg.Done()
		wg.Add(1)
		consumer, _ := nsq.NewConsumer(fmt.Sprintf("commit_slave_completed_%s", properties.ClusterId), properties.NodeId(), config)
		consumer.AddHandler(nsq.HandlerFunc(onCommitSlaveRequested))
		err := consumer.ConnectToNSQLookupd(properties.LookupdAddr)
		if err != nil {
			log.Panic("Could not connect")
		}
	}()

	// Start complete consumer
	go func() {
		defer wg.Done()
		wg.Add(1)
		consumer, _ := nsq.NewConsumer(fmt.Sprintf("commit_completed_%s", properties.ClusterId), properties.NodeId(), config)
		consumer.AddHandler(nsq.HandlerFunc(onCommitCompleted))
		err := consumer.ConnectToNSQLookupd(properties.LookupdAddr)
		if err != nil {
			log.Panic("Could not connect")
		}
	}()

	// Start reload pipeline
	stopChan := make(chan interface{}, 1)
	go func() {
		defer wg.Done()
		wg.Add(1)
		for {
			select {
			case reload := <-reloadChan:
				reload.Execute()
			case  <- stopChan:
				return
			}
		}
	}()

	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
	select {
	case signal := <-sigChan:
		log.Printf("Got signal: %v\n", signal)
	}
	restApi.Stop()

	log.Printf("Waiting on server to stop\n")
	wg.Wait()
}

func initProducer() {
	// Create required topics
	topics := []string{"commit_slave_completed", "commit_completed", "commit_failed"}
	channels := []string{"slave", "master"}
	topicChan := make(chan string, len(topics))
	for i := range topics {
		topicChan <- topics[i]
	}
	left := len(topics)
	for left > 0 {
		topic := <-topicChan
		log.WithField("topic", topic).Info("Creating topic")
		url := fmt.Sprintf("%s/topic/create?topic=%s_%s", properties.ProducerRestAddr, topic, properties.ClusterId)
		resp, err := http.PostForm(url, nil)
		if err != nil || resp.StatusCode != 200 {
			topicChan <- topic
			continue
		}
		for channel := range channels {
			log.WithField("channel", channels[channel]).Info("Creating channel")
			url := fmt.Sprintf("%s/channel/create?topic=%s_%s&channel=%s-%s", properties.ProducerRestAddr, topic, properties.ClusterId, properties.ClusterId, channels[channel])
			resp, err := http.PostForm(url, nil)
			if err != nil || resp.StatusCode != 200 {
				topicChan <- topic
				continue
			}
		}

		log.WithField("topic", topic).Info("Topic created")
		left--

	}
}

// loadProperties load properties file
func loadProperties() {
	properties = haaasd.DefaultConfig()
	if _, err := toml.DecodeFile(*configFile, properties); err != nil {
		log.Fatal(err)
		os.Exit(1)
	}
	properties.IpAddr = *ip
	len := len(properties.HapHome)
	if properties.HapHome[len - 1] == '/' {
		properties.HapHome = properties.HapHome[:len - 1]
	}
}

func filteredHandler(event string, message *nsq.Message, target string, f haaasd.HandlerFunc) error {
	defer message.Finish()
	match, err := daemon.Is(target)
	if err != nil {
		return err
	}

	if match {
		log.WithField("event", event).Debug("Handle event")
		data, err := bodyToData(message.Body)
		if err != nil {
			log.WithError(err).Error("Unable to read data")
			return err
		}

		switch event {
		case "commit_requested":
			reloadChan <- haaasd.ReloadEvent{F: f, Message: data}
		case "commit_slave_completed":
			reloadChan <- haaasd.ReloadEvent{F: f, Message: data}
		case "commit_completed":
			logAndForget(data)
		}

	} else {
		log.WithField("event", event).Debug("Ignore event")
	}

	return nil
}

func onCommitRequested(message *nsq.Message) error {
	return filteredHandler("commit_requested", message, "slave", reloadSlave)
}
func onCommitSlaveRequested(message *nsq.Message) error {
	return filteredHandler("commit_slave_completed", message, "master", reloadMaster)
}
func onCommitCompleted(message *nsq.Message) error {
	return filteredHandler("commit_completed", message, "slave", logAndForget)
}

// logAndForget is a generic function to just log event
func logAndForget(data *haaasd.EventMessage) error {
	log.WithFields(log.Fields{
		"correlationId": data.Correlationid,
		"application" : data.Application,
		"platform": data.Platform,
	}).Debug("Commit completed")
	return nil
}

func reloadSlave(data *haaasd.EventMessage) error {
	hap := haaasd.NewHaproxy("slave", properties, data.Application, data.Platform, data.HapVersion)

	status, err := hap.ApplyConfiguration(data)
	if err == nil {
		if status != haaasd.UNCHANGED {
			syslog.Restart()
		}
		publishMessage("commit_slave_completed_", data)
	} else {
		log.WithFields(log.Fields{
			"correlationId": data.Correlationid,
			"application" : data.Application,
			"platform": data.Platform,
		}).WithError(err).Error("Commit failed")
		publishMessage("commit_failed_", map[string]string{"application": data.Application, "platform": data.Platform, "correlationid": data.Correlationid})
	}
	return nil
}

func reloadMaster(data *haaasd.EventMessage) error {
	hap := haaasd.NewHaproxy("master", properties, data.Application, data.Platform, data.HapVersion)
	status, err := hap.ApplyConfiguration(data)
	if err == nil {
		if status != haaasd.UNCHANGED {
			syslog.Restart()
		}
		publishMessage("commit_completed_", map[string]string{"application": data.Application, "platform": data.Platform, "correlationid": data.Correlationid})
	} else {
		log.WithFields(log.Fields{
			"correlationId": data.Correlationid,
			"application" : data.Application,
			"platform": data.Platform,
		}).WithError(err).Error("Commit failed")
		publishMessage("commit_failed_", map[string]string{"application": data.Application, "platform": data.Platform, "correlationid": data.Correlationid})
	}
	return nil
}

// Unmarshal json to EventMessage
func bodyToData(jsonStream []byte) (*haaasd.EventMessage, error) {
	dec := json.NewDecoder(bytes.NewReader(jsonStream))
	var message haaasd.EventMessage
	err := dec.Decode(&message)
	return &message, err
}

func publishMessage(topic_prefix string, data interface{}) error {
	jsonMsg, _ := json.Marshal(data)
	topic := topic_prefix + properties.ClusterId
	log.WithField("topic", topic).WithField("payload", string(jsonMsg)).Debug("Publish")
	return producer.Publish(topic, []byte(jsonMsg))
}
<|endoftext|>


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

package dao

import (
	"fmt"

	"github.com/astaxie/beego/orm"
	_ "github.com/go-sql-driver/mysql" //register mysql driver
	"github.com/vmware/harbor/src/common/utils"
)

type mysql struct {
	host     string
	port     string
	usr      string
	pwd      string
	database string
}

// NewMySQL returns an instance of mysql
func NewMySQL(host, port, usr, pwd, database string) Database {
	return &mysql{
		host:     host,
		port:     port,
		usr:      usr,
		pwd:      pwd,
		database: database,
	}
}

// Register registers MySQL as the underlying database used
func (m *mysql) Register(alias ...string) error {

	if err := utils.TestTCPConn(m.host+":"+m.port, 60, 2); err != nil {
		return err
	}

	if err := orm.RegisterDriver("mysql", orm.DRMySQL); err != nil {
		return err
	}

	an := "default"
	if len(alias) != 0 {
		an = alias[0]
	}
	conn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s", m.usr,
		m.pwd, m.host, m.port, m.database)
	return orm.RegisterDataBase(an, "mysql", conn)
}

// Name returns the name of MySQL
func (m *mysql) Name() string {
	return "MySQL"
}

// String returns the details of database
func (m *mysql) String() string {
	return fmt.Sprintf("type-%s host-%s port-%s user-%s database-%s",
		m.Name(), m.host, m.port, m.usr, m.database)
}
<commit_msg>Fix intermittent `broken pipe` issue in log<commit_after>// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package dao

import (
	"fmt"
	"time"

	"github.com/astaxie/beego/orm"
	_ "github.com/go-sql-driver/mysql" //register mysql driver
	"github.com/vmware/harbor/src/common/utils"
)

type mysql struct {
	host     string
	port     string
	usr      string
	pwd      string
	database string
}

// NewMySQL returns an instance of mysql
func NewMySQL(host, port, usr, pwd, database string) Database {
	return &mysql{
		host:     host,
		port:     port,
		usr:      usr,
		pwd:      pwd,
		database: database,
	}
}

// Register registers MySQL as the underlying database used
func (m *mysql) Register(alias ...string) error {

	if err := utils.TestTCPConn(m.host+":"+m.port, 60, 2); err != nil {
		return err
	}

	if err := orm.RegisterDriver("mysql", orm.DRMySQL); err != nil {
		return err
	}

	an := "default"
	if len(alias) != 0 {
		an = alias[0]
	}
	conn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s", m.usr,
		m.pwd, m.host, m.port, m.database)
	if err := orm.RegisterDataBase(an, "mysql", conn); err != nil {
		return err
	}
	db, _ := orm.GetDB(an)
	db.SetConnMaxLifetime(5 * time.Minute)
	return nil
}

// Name returns the name of MySQL
func (m *mysql) Name() string {
	return "MySQL"
}

// String returns the details of database
func (m *mysql) String() string {
	return fmt.Sprintf("type-%s host-%s port-%s user-%s database-%s",
		m.Name(), m.host, m.port, m.usr, m.database)
}
<|endoftext|>


<commit_before>package paths

import (
	"crypto/md5"
	"crypto/rand"
	"fmt"
	"strings"
)

// Paths
type Paths struct {
	LocalBasePath  string
	RemoteBasePath string
	RemoteBaseURL  string
}

// LocalOriginalPath returns local path for original image
func (p *Paths) LocalOriginalPath(namespace string, md5 string) string {
	return fmt.Sprintf("%s/%s", p.LocalBasePath, p.originalPath(namespace, md5))
}

// LocalImageDirectory returns location for locally cached images
func (p *Paths) LocalImageDirectory(namespace string, md5 string) string {
	return fmt.Sprintf("%s/%s", p.LocalBasePath, p.imageDirectory(namespace, md5))
}

// RemoteImageDirectory returns location for directory for images and info
func (p *Paths) RemoteImageDirectory(namespace string, md5 string) string {
	return fmt.Sprintf("%s/%s", p.RemoteBasePath, p.imageDirectory(namespace, md5))
}

//  LocalImagePath returns local path for resized image
func (p *Paths) LocalImagePath(namespace string, md5 string, imageName string) string {
	return fmt.Sprintf("%s/%s", p.LocalBasePath, p.imagePath(namespace, md5, imageName))
}

func (p *Paths) RemoteImagePath(namespace string, md5 string, imageName string) string {
	return fmt.Sprintf("%s/%s", p.RemoteBasePath, p.imagePath(namespace, md5, imageName))
}

// RemoteOriginalPath returns local path for original image
func (p *Paths) RemoteOriginalPath(namespace string, md5 string) string {
	return fmt.Sprintf("%s/%s", p.RemoteBasePath, p.originalPath(namespace, md5))
}

func (p *Paths) RemoteOriginalURL(namespace string, md5 string) string {
	return fmt.Sprintf("%s/%s", p.RemoteBaseURL, p.RemoteOriginalPath(namespace, md5))
}

func (p *Paths) LocalInfoPath(namespace string, md5 string) string {
	return fmt.Sprintf("%s/%s", p.LocalBasePath, p.infoPath(namespace, md5))
}

func (p *Paths) RemoteInfoPath(namespace string, md5 string) string {
	return fmt.Sprintf("%s/%s", p.RemoteBasePath, p.infoPath(namespace, md5))
}

func (p *Paths) TempImagePath(url string) string {
	data := []byte(url)
	name := fmt.Sprintf("%x", md5.Sum(data))
	return fmt.Sprintf("%s/tmp/%s", p.LocalBasePath, name)
}

func (p *Paths) RandomTempPath() string {
	b := make([]byte, 16)
	rand.Read(b)
	name := fmt.Sprintf("%x", b)
	return fmt.Sprintf("%s/tmp/%s", p.LocalBasePath, name)
}

// originalPath
func (p *Paths) originalPath(namespace string, md5 string) string {
	return fmt.Sprintf("%s/original", p.imageDirectory(namespace, md5))
}

// imageDirectory returns relative directory starting at image root
func (p *Paths) imageDirectory(namespace string, md5 string) string {
	partitions := []string{md5[0:3], md5[3:6], md5[6:9], md5[9:32]}
	return fmt.Sprintf("%s/%s", namespace, strings.Join(partitions, "/"))
}

func (p *Paths) infoPath(namespace string, md5 string) string {
	return fmt.Sprintf("%s/info.json", p.imageDirectory(namespace, md5))
}

//  imagePath returns relative path to resized image
func (p *Paths) imagePath(namespace string, md5 string, imageName string) string {
	return fmt.Sprintf("%s/%s", p.imageDirectory(namespace, md5), imageName)
}
<commit_msg>Construct paths using `filepath.Join` instead of `fmt.Sprintf`<commit_after>package paths

import (
	"crypto/md5"
	"crypto/rand"
	"fmt"
	"strings"
	"path/filepath"
	"net/url"
)

// Paths
type Paths struct {
	LocalBasePath  string
	RemoteBasePath string
	RemoteBaseURL  string
}

// LocalOriginalPath returns local path for original image
func (p *Paths) LocalOriginalPath(namespace string, md5 string) string {
	return filepath.Join(p.LocalBasePath, p.originalPath(namespace, md5))
}

// LocalImageDirectory returns location for locally cached images
func (p *Paths) LocalImageDirectory(namespace string, md5 string) string {
	return filepath.Join(p.LocalBasePath, p.imageDirectory(namespace, md5))
}

// RemoteImageDirectory returns location for directory for images and info
func (p *Paths) RemoteImageDirectory(namespace string, md5 string) string {
	return filepath.Join(p.RemoteBasePath, p.imageDirectory(namespace, md5))
}

//  LocalImagePath returns local path for resized image
func (p *Paths) LocalImagePath(namespace string, md5 string, imageName string) string {
	return filepath.Join(p.LocalBasePath, p.imagePath(namespace, md5, imageName))
}

func (p *Paths) RemoteImagePath(namespace string, md5 string, imageName string) string {
	return filepath.Join(p.RemoteBasePath, p.imagePath(namespace, md5, imageName))
}

// RemoteOriginalPath returns local path for original image
func (p *Paths) RemoteOriginalPath(namespace string, md5 string) string {
	return filepath.Join(p.RemoteBasePath, p.originalPath(namespace, md5))
}

func (p *Paths) RemoteOriginalURL(namespace string, md5 string) string {
	u, _ := url.Parse(p.RemoteBaseURL)
	u.Path = filepath.Join(u.Path, p.RemoteOriginalPath(namespace, md5))
	return u.String()
}

func (p *Paths) LocalInfoPath(namespace string, md5 string) string {
	return filepath.Join(p.LocalBasePath, p.infoPath(namespace, md5))
}

func (p *Paths) RemoteInfoPath(namespace string, md5 string) string {
	return filepath.Join(p.RemoteBasePath, p.infoPath(namespace, md5))
}

func (p *Paths) TempImagePath(url string) string {
	data := []byte(url)
	name := fmt.Sprintf("%x", md5.Sum(data))
	return filepath.Join(p.LocalBasePath, "tmp", name)
}

func (p *Paths) RandomTempPath() string {
	b := make([]byte, 16)
	rand.Read(b)
	name := fmt.Sprintf("%x", b)
	return filepath.Join(p.LocalBasePath, "tmp", name)
}

// originalPath
func (p *Paths) originalPath(namespace string, md5 string) string {
	return filepath.Join(p.imageDirectory(namespace, md5), "original")
}

// imageDirectory returns relative directory starting at image root
func (p *Paths) imageDirectory(namespace string, md5 string) string {
	partitions := []string{md5[0:3], md5[3:6], md5[6:9], md5[9:32]}
	return filepath.Join(namespace, strings.Join(partitions, "/"))
}

func (p *Paths) infoPath(namespace string, md5 string) string {
	return filepath.Join(p.imageDirectory(namespace, md5), "info.json")
}

//  imagePath returns relative path to resized image
func (p *Paths) imagePath(namespace string, md5 string, imageName string) string {
	return filepath.Join(p.imageDirectory(namespace, md5), imageName)
}
<|endoftext|>


<commit_before>package service_test

import (
	"cf/api"
	. "cf/commands/service"
	"cf/models"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	testapi "testhelpers/api"
	testassert "testhelpers/assert"
	testcmd "testhelpers/commands"
	testconfig "testhelpers/configuration"
	testreq "testhelpers/requirements"
	testterm "testhelpers/terminal"
)

var _ = Describe("unbind-service command", func() {
	var (
		app models.Application
		serviceInstance models.ServiceInstance
		requirementsFactory *testreq.FakeReqFactory
		serviceBindingRepo *testapi.FakeServiceBindingRepo
	)

	BeforeEach(func() {
		app.Name = "my-app"
		app.Guid = "my-app-guid"

		serviceInstance.Name = "my-service"
		serviceInstance.Guid = "my-service-guid"

		requirementsFactory = &testreq.FakeReqFactory{}
		requirementsFactory.Application = app
		requirementsFactory.ServiceInstance = serviceInstance

		serviceBindingRepo = &testapi.FakeServiceBindingRepo{}
	})

	Context("when not logged in", func() {
		It("fails requirements when not logged in", func() {
			cmd := NewUnbindService(&testterm.FakeUI{}, testconfig.NewRepository(), serviceBindingRepo)
			testcmd.RunCommand(cmd, testcmd.NewContext("unbind-service", []string{"my-service", "my-app"}), requirementsFactory)
			Expect(testcmd.CommandDidPassRequirements).To(BeFalse())
		})
	})

	Context("when logged in", func() {
		BeforeEach(func() {
		    requirementsFactory.LoginSuccess = true
		})

		Context("when the service instance exists", func() {
			It("unbinds a service from an app", func() {
				ui := callUnbindService([]string{"my-app", "my-service"}, requirementsFactory, serviceBindingRepo)

				Expect(requirementsFactory.ApplicationName).To(Equal("my-app"))
				Expect(requirementsFactory.ServiceInstanceName).To(Equal("my-service"))

				testassert.SliceContains(ui.Outputs, testassert.Lines{
					{"Unbinding app", "my-service", "my-app", "my-org", "my-space", "my-user"},
					{"OK"},
				})
				Expect(serviceBindingRepo.DeleteServiceInstance).To(Equal(serviceInstance))
				Expect(serviceBindingRepo.DeleteApplicationGuid).To(Equal("my-app-guid"))
			})
		})

		Context("when the service instance does not exist", func() {
			BeforeEach(func() {
				serviceBindingRepo.DeleteBindingNotFound = true
			})

			It("warns the user the the service instance does not exist", func() {
				ui := callUnbindService([]string{"my-app", "my-service"}, requirementsFactory, serviceBindingRepo)

				Expect(requirementsFactory.ApplicationName).To(Equal("my-app"))
				Expect(requirementsFactory.ServiceInstanceName).To(Equal("my-service"))

				testassert.SliceContains(ui.Outputs, testassert.Lines{
					{"Unbinding app", "my-service", "my-app"},
					{"OK"},
					{"my-service", "my-app", "did not exist"},
				})
				Expect(serviceBindingRepo.DeleteServiceInstance).To(Equal(serviceInstance))
				Expect(serviceBindingRepo.DeleteApplicationGuid).To(Equal("my-app-guid"))
			})
		})

		It("when no parameters are given the command fails with usage", func() {
			ui := callUnbindService([]string{"my-service"}, requirementsFactory, serviceBindingRepo)
			Expect(ui.FailedWithUsage).To(BeTrue())

			ui = callUnbindService([]string{"my-app"}, requirementsFactory, serviceBindingRepo)
			Expect(ui.FailedWithUsage).To(BeTrue())

			ui = callUnbindService([]string{"my-app", "my-service"}, requirementsFactory, serviceBindingRepo)
			Expect(ui.FailedWithUsage).To(BeFalse())
		})
	})
})

func callUnbindService(args []string, reqFactory *testreq.FakeReqFactory, serviceBindingRepo api.ServiceBindingRepository) (fakeUI *testterm.FakeUI) {
	fakeUI = &testterm.FakeUI{}
	ctxt := testcmd.NewContext("unbind-service", args)

	config := testconfig.NewRepositoryWithDefaults()

	cmd := NewUnbindService(fakeUI, config, serviceBindingRepo)
	testcmd.RunCommand(cmd, ctxt, reqFactory)
	return
}
<commit_msg>cleanup tests for create-service<commit_after>package service_test

import (
	"cf/api"
	. "cf/commands/service"
	"cf/models"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	testapi "testhelpers/api"
	testassert "testhelpers/assert"
	testcmd "testhelpers/commands"
	testconfig "testhelpers/configuration"
	testreq "testhelpers/requirements"
	testterm "testhelpers/terminal"
)

var _ = Describe("unbind-service command", func() {
	var (
		app                 models.Application
		serviceInstance     models.ServiceInstance
		requirementsFactory *testreq.FakeReqFactory
		serviceBindingRepo  *testapi.FakeServiceBindingRepo
	)

	BeforeEach(func() {
		app.Name = "my-app"
		app.Guid = "my-app-guid"

		serviceInstance.Name = "my-service"
		serviceInstance.Guid = "my-service-guid"

		requirementsFactory = &testreq.FakeReqFactory{}
		requirementsFactory.Application = app
		requirementsFactory.ServiceInstance = serviceInstance

		serviceBindingRepo = &testapi.FakeServiceBindingRepo{}
	})

	Context("when not logged in", func() {
		It("fails requirements when not logged in", func() {
			cmd := NewUnbindService(&testterm.FakeUI{}, testconfig.NewRepository(), serviceBindingRepo)
			testcmd.RunCommand(cmd, testcmd.NewContext("unbind-service", []string{"my-service", "my-app"}), requirementsFactory)
			Expect(testcmd.CommandDidPassRequirements).To(BeFalse())
		})
	})

	Context("when logged in", func() {
		BeforeEach(func() {
			requirementsFactory.LoginSuccess = true
		})

		Context("when the service instance exists", func() {
			It("unbinds a service from an app", func() {
				ui := callUnbindService([]string{"my-app", "my-service"}, requirementsFactory, serviceBindingRepo)

				Expect(requirementsFactory.ApplicationName).To(Equal("my-app"))
				Expect(requirementsFactory.ServiceInstanceName).To(Equal("my-service"))

				testassert.SliceContains(ui.Outputs, testassert.Lines{
					{"Unbinding app", "my-service", "my-app", "my-org", "my-space", "my-user"},
					{"OK"},
				})
				Expect(serviceBindingRepo.DeleteServiceInstance).To(Equal(serviceInstance))
				Expect(serviceBindingRepo.DeleteApplicationGuid).To(Equal("my-app-guid"))
			})
		})

		Context("when the service instance does not exist", func() {
			BeforeEach(func() {
				serviceBindingRepo.DeleteBindingNotFound = true
			})

			It("warns the user the the service instance does not exist", func() {
				ui := callUnbindService([]string{"my-app", "my-service"}, requirementsFactory, serviceBindingRepo)

				Expect(requirementsFactory.ApplicationName).To(Equal("my-app"))
				Expect(requirementsFactory.ServiceInstanceName).To(Equal("my-service"))

				testassert.SliceContains(ui.Outputs, testassert.Lines{
					{"Unbinding app", "my-service", "my-app"},
					{"OK"},
					{"my-service", "my-app", "did not exist"},
				})
				Expect(serviceBindingRepo.DeleteServiceInstance).To(Equal(serviceInstance))
				Expect(serviceBindingRepo.DeleteApplicationGuid).To(Equal("my-app-guid"))
			})
		})

		It("when no parameters are given the command fails with usage", func() {
			ui := callUnbindService([]string{"my-service"}, requirementsFactory, serviceBindingRepo)
			Expect(ui.FailedWithUsage).To(BeTrue())

			ui = callUnbindService([]string{"my-app"}, requirementsFactory, serviceBindingRepo)
			Expect(ui.FailedWithUsage).To(BeTrue())

			ui = callUnbindService([]string{"my-app", "my-service"}, requirementsFactory, serviceBindingRepo)
			Expect(ui.FailedWithUsage).To(BeFalse())
		})
	})
})

func callUnbindService(args []string, reqFactory *testreq.FakeReqFactory, serviceBindingRepo api.ServiceBindingRepository) (fakeUI *testterm.FakeUI) {
	fakeUI = &testterm.FakeUI{}
	ctxt := testcmd.NewContext("unbind-service", args)

	config := testconfig.NewRepositoryWithDefaults()

	cmd := NewUnbindService(fakeUI, config, serviceBindingRepo)
	testcmd.RunCommand(cmd, ctxt, reqFactory)
	return
}
<|endoftext|>


<commit_before>// Copyright 2016 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package dokan

import (
	"strconv"
	"time"

	"github.com/keybase/kbfs/dokan/winacl"
	"golang.org/x/net/context"
)

// Config is the configuration used for a mount.
type Config struct {
	// Path is the path to mount, e.g. `L:`. Must be set.
	Path string
	// FileSystem is the filesystem implementation. Must be set.
	FileSystem FileSystem
	// MountFlags for this filesystem instance. Is optional.
	MountFlags MountFlag
	// DllPath is the optional full path to dokan1.dll.
	// Empty causes dokan1.dll to be loaded from the system directory.
	// Only the first load of a dll determines the path -
	// further instances in the same process will use
	// the same instance regardless of path.
	DllPath string
}

// FileSystem is the inteface for filesystems in Dokan.
type FileSystem interface {
	// WithContext returns a context for a new request. If the CancelFunc
	// is not null, it is called after the request is done. The most minimal
	// implementation is
	// `func (*T)WithContext(c context.Context) { return c, nil }`.
	WithContext(context.Context) (context.Context, context.CancelFunc)

	// CreateFile is called to open and create files.
	CreateFile(ctx context.Context, fi *FileInfo, data *CreateData) (file File, isDirectory bool, err error)

	// GetDiskFreeSpace returns information about disk free space.
	// Called quite often by Explorer.
	GetDiskFreeSpace(ctx context.Context) (FreeSpace, error)

	// GetVolumeInformation returns information about the volume.
	GetVolumeInformation(ctx context.Context) (VolumeInformation, error)

	// MoveFile corresponds to rename.
	MoveFile(ctx context.Context, source *FileInfo, targetPath string, replaceExisting bool) error

	// ErrorPrint is called when dokan needs notify the program of an error message.
	// A sensible approach is to print the error.
	ErrorPrint(error)
}

// MountFlag is the type for Dokan mount flags.
type MountFlag uint32

// Flags for mounting the filesystem. See Dokan documentation for these.
const (
	CDebug         = MountFlag(kbfsLibdokanDebug)
	CStderr        = MountFlag(kbfsLibdokanStderr)
	Removable      = MountFlag(kbfsLibdokanRemovable)
	MountManager   = MountFlag(kbfsLibdokanMountManager)
	CurrentSession = MountFlag(kbfsLibdokanCurrentSession)
	// UseFindFilesWithPattern enables FindFiles calls to be with a search
	// pattern string. Otherwise the string will be empty in all calls.
	UseFindFilesWithPattern = MountFlag(kbfsLibdokanUseFindFilesWithPattern)
)

// CreateData contains all the info needed to create a file.
type CreateData struct {
	DesiredAccess     uint32
	FileAttributes    FileAttribute
	ShareAccess       uint32
	CreateDisposition CreateDisposition
	CreateOptions     uint32
}

// CreateDisposition marks whether to create or open a file. Not a bitmask.
type CreateDisposition uint32

// File creation flags for CreateFile. This is not a bitmask.
const (
	FileSupersede   = CreateDisposition(0)
	FileOpen        = CreateDisposition(1)
	FileCreate      = CreateDisposition(2)
	FileOpenIf      = CreateDisposition(3)
	FileOverwrite   = CreateDisposition(4)
	FileOverwriteIf = CreateDisposition(5)
)

// CreateOptions flags. These are bitmask flags.
const (
	FileDirectoryFile    = 0x1
	FileNonDirectoryFile = 0x40
	FileOpenReparsePoint = 0x00200000
)

// FileAttribute is the type of a directory entry in Stat.
type FileAttribute uint32

// File attribute bit masks - same as syscall but provided for all platforms.
const (
	FileAttributeReadonly     = FileAttribute(0x00000001)
	FileAttributeHidden       = FileAttribute(0x00000002)
	FileAttributeSystem       = FileAttribute(0x00000004)
	FileAttributeDirectory    = FileAttribute(0x00000010)
	FileAttributeArchive      = FileAttribute(0x00000020)
	FileAttributeNormal       = FileAttribute(0x00000080)
	FileAttributeReparsePoint = FileAttribute(0x00000400)
	IOReparseTagSymlink       = 0xA000000C
)

// File is the interface for files and directories.
type File interface {
	// ReadFile implements read for dokan.
	ReadFile(ctx context.Context, fi *FileInfo, bs []byte, offset int64) (int, error)
	// WriteFile implements write for dokan.
	WriteFile(ctx context.Context, fi *FileInfo, bs []byte, offset int64) (int, error)
	// FlushFileBuffers corresponds to fsync.
	FlushFileBuffers(ctx context.Context, fi *FileInfo) error

	// GetFileInformation - corresponds to stat.
	GetFileInformation(ctx context.Context, fi *FileInfo) (*Stat, error)

	// FindFiles is the readdir. The function is a callback that should be called
	// with each file. The same NamedStat may be reused for subsequent calls.
	//
	// Pattern will be an empty string unless UseFindFilesWithPattern is enabled - then
	// it may be a pattern like `*.png` to match. All implementations must be prepared
	// to handle empty strings as patterns.
	FindFiles(ctx context.Context, fi *FileInfo, pattern string, fillStatCallback func(*NamedStat) error) error

	// SetFileTime sets the file time. Test times with .IsZero
	// whether they should be set.
	SetFileTime(ctx context.Context, fi *FileInfo, creation time.Time, lastAccess time.Time, lastWrite time.Time) error
	// SetFileAttributes is for setting file attributes.
	SetFileAttributes(ctx context.Context, fi *FileInfo, fileAttributes FileAttribute) error

	// SetEndOfFile truncates the file. May be used to extend a file with zeros.
	SetEndOfFile(ctx context.Context, fi *FileInfo, length int64) error
	// SetAllocationSize see FILE_ALLOCATION_INFORMATION on MSDN.
	// For simple semantics if length > filesize then ignore else truncate(length).
	SetAllocationSize(ctx context.Context, fi *FileInfo, length int64) error

	LockFile(ctx context.Context, fi *FileInfo, offset int64, length int64) error
	UnlockFile(ctx context.Context, fi *FileInfo, offset int64, length int64) error

	GetFileSecurity(ctx context.Context, fi *FileInfo, si winacl.SecurityInformation, sd *winacl.SecurityDescriptor) error
	SetFileSecurity(ctx context.Context, fi *FileInfo, si winacl.SecurityInformation, sd *winacl.SecurityDescriptor) error

	// CanDeleteFile and CanDeleteDirectory should check whether the file/directory
	// can be deleted. The actual deletion should be done by checking
	// FileInfo.IsDeleteOnClose in Cleanup.
	CanDeleteFile(ctx context.Context, fi *FileInfo) error
	CanDeleteDirectory(ctx context.Context, fi *FileInfo) error
	// Cleanup is called after the last handle from userspace is closed.
	// Cleanup must perform actual deletions marked from CanDelete*
	// by checking FileInfo.IsDeleteOnClose if the filesystem supports
	// deletions.
	Cleanup(ctx context.Context, fi *FileInfo)
	// CloseFile is called when closing a handle to the file.
	CloseFile(ctx context.Context, fi *FileInfo)
}

// FreeSpace - semantics as with WINAPI GetDiskFreeSpaceEx
type FreeSpace struct {
	FreeBytesAvailable, TotalNumberOfBytes, TotalNumberOfFreeBytes uint64
}

// VolumeInformation - see WINAPI GetVolumeInformation for hints
type VolumeInformation struct {
	VolumeName             string
	VolumeSerialNumber     uint32
	MaximumComponentLength uint32
	FileSystemFlags        FileSystemFlags
	FileSystemName         string
}

// FileSystemFlags holds flags for filesystem features.
type FileSystemFlags uint32

// Various FileSystemFlags constants, see winapi documentation for details.
const (
	FileCasePreservedNames         = FileSystemFlags(0x2)
	FileCaseSensitiveSearch        = FileSystemFlags(0x1)
	FileFileCompression            = FileSystemFlags(0x10)
	FileNamedStreams               = FileSystemFlags(0x40000)
	FilePersistentAcls             = FileSystemFlags(0x8)
	FileReadOnlyVolume             = FileSystemFlags(0x80000)
	FileSequentalWriteOnce         = FileSystemFlags(0x100000)
	FileSupportsEncryption         = FileSystemFlags(0x20000)
	FileSupportsExtendedAttributes = FileSystemFlags(0x800000)
	FileSupportsHardLinks          = FileSystemFlags(0x400000)
	FileSupportObjectIDs           = FileSystemFlags(0x10000)
	FileSupportsOpenByFileID       = FileSystemFlags(0x1000000)
	FileSupportsRemoteStorage      = FileSystemFlags(0x100)
	FileSupportsReparsePoints      = FileSystemFlags(0x80)
	FileSupportsSparseFiles        = FileSystemFlags(0x40)
	FileSupportsTransactions       = FileSystemFlags(0x200000)
	FileSupportsUsnJournal         = FileSystemFlags(0x2000000)
	FileUnicodeOnDisk              = FileSystemFlags(0x4)
	FileVolumeIsCompressed         = FileSystemFlags(0x8000)
	FileVolumeQuotas               = FileSystemFlags(0x20)
)

// Stat is for GetFileInformation and friends.
type Stat struct {
	// FileAttributes bitmask holds the file attributes.
	FileAttributes FileAttribute
	// Timestamps for the file
	Creation, LastAccess, LastWrite time.Time
	// VolumeSerialNumber is the serial number of the volume (0 is fine)
	VolumeSerialNumber uint32
	// FileSize is the size of the file in bytes
	FileSize int64
	// NumberOfLinks can be omitted, if zero set to 1.
	NumberOfLinks uint32
	// FileIndex is a 64 bit (nearly) unique ID of the file
	FileIndex uint64
	// ReparsePointTag is for WIN32_FIND_DATA dwReserved0 for reparse point tags, typically it can be omitted.
	ReparsePointTag uint32
}

// NamedStat is used to for stat responses that require file names.
// If the name is longer than a DOS-name, insert the corresponding
// DOS-name to ShortName.
type NamedStat struct {
	Name      string
	ShortName string
	Stat
}

// NtStatus is a type implementing error interface that corresponds
// to NTSTATUS. It can be used to set the exact error/status code
// from the filesystem.
type NtStatus uint32

func (n NtStatus) Error() string {
	return "NTSTATUS=" + strconv.FormatUint(uint64(n), 16)
}

const (
	// ErrAccessDenied - access denied (EPERM)
	ErrAccessDenied = NtStatus(0xC0000022)
	// ErrObjectNameNotFound - filename does not exist (ENOENT)
	ErrObjectNameNotFound = NtStatus(0xC0000034)
	// ErrObjectNameCollision - a pathname already exists (EEXIST)
	ErrObjectNameCollision = NtStatus(0xC0000035)
	// ErrObjectPathNotFound - a pathname does not exist (ENOENT)
	ErrObjectPathNotFound = NtStatus(0xC000003A)
	// ErrNotSupported - not supported.
	ErrNotSupported = NtStatus(0xC00000BB)
	// ErrFileIsADirectory - file is a directory.
	ErrFileIsADirectory = NtStatus(0xC00000BA)
	// ErrDirectoryNotEmpty - wanted an empty dir - it is not empty.
	ErrDirectoryNotEmpty = NtStatus(0xC0000101)
	// ErrFileAlreadyExists - file already exists - fatal.
	ErrFileAlreadyExists = NtStatus(0xC0000035)
	// ErrNotSameDevice - MoveFile is denied, please use copy+delete.
	ErrNotSameDevice = NtStatus(0xC00000D4)
	// StatusBufferOverflow - buffer space too short for return value.
	StatusBufferOverflow = NtStatus(0x80000005)
	// StatusObjectNameExists - already exists, may be non-fatal...
	StatusObjectNameExists = NtStatus(0x40000000)
)
<commit_msg>dokan: Reorder Stat fields for smaller struct size<commit_after>// Copyright 2016 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package dokan

import (
	"strconv"
	"time"

	"github.com/keybase/kbfs/dokan/winacl"
	"golang.org/x/net/context"
)

// Config is the configuration used for a mount.
type Config struct {
	// Path is the path to mount, e.g. `L:`. Must be set.
	Path string
	// FileSystem is the filesystem implementation. Must be set.
	FileSystem FileSystem
	// MountFlags for this filesystem instance. Is optional.
	MountFlags MountFlag
	// DllPath is the optional full path to dokan1.dll.
	// Empty causes dokan1.dll to be loaded from the system directory.
	// Only the first load of a dll determines the path -
	// further instances in the same process will use
	// the same instance regardless of path.
	DllPath string
}

// FileSystem is the inteface for filesystems in Dokan.
type FileSystem interface {
	// WithContext returns a context for a new request. If the CancelFunc
	// is not null, it is called after the request is done. The most minimal
	// implementation is
	// `func (*T)WithContext(c context.Context) { return c, nil }`.
	WithContext(context.Context) (context.Context, context.CancelFunc)

	// CreateFile is called to open and create files.
	CreateFile(ctx context.Context, fi *FileInfo, data *CreateData) (file File, isDirectory bool, err error)

	// GetDiskFreeSpace returns information about disk free space.
	// Called quite often by Explorer.
	GetDiskFreeSpace(ctx context.Context) (FreeSpace, error)

	// GetVolumeInformation returns information about the volume.
	GetVolumeInformation(ctx context.Context) (VolumeInformation, error)

	// MoveFile corresponds to rename.
	MoveFile(ctx context.Context, source *FileInfo, targetPath string, replaceExisting bool) error

	// ErrorPrint is called when dokan needs notify the program of an error message.
	// A sensible approach is to print the error.
	ErrorPrint(error)
}

// MountFlag is the type for Dokan mount flags.
type MountFlag uint32

// Flags for mounting the filesystem. See Dokan documentation for these.
const (
	CDebug         = MountFlag(kbfsLibdokanDebug)
	CStderr        = MountFlag(kbfsLibdokanStderr)
	Removable      = MountFlag(kbfsLibdokanRemovable)
	MountManager   = MountFlag(kbfsLibdokanMountManager)
	CurrentSession = MountFlag(kbfsLibdokanCurrentSession)
	// UseFindFilesWithPattern enables FindFiles calls to be with a search
	// pattern string. Otherwise the string will be empty in all calls.
	UseFindFilesWithPattern = MountFlag(kbfsLibdokanUseFindFilesWithPattern)
)

// CreateData contains all the info needed to create a file.
type CreateData struct {
	DesiredAccess     uint32
	FileAttributes    FileAttribute
	ShareAccess       uint32
	CreateDisposition CreateDisposition
	CreateOptions     uint32
}

// CreateDisposition marks whether to create or open a file. Not a bitmask.
type CreateDisposition uint32

// File creation flags for CreateFile. This is not a bitmask.
const (
	FileSupersede   = CreateDisposition(0)
	FileOpen        = CreateDisposition(1)
	FileCreate      = CreateDisposition(2)
	FileOpenIf      = CreateDisposition(3)
	FileOverwrite   = CreateDisposition(4)
	FileOverwriteIf = CreateDisposition(5)
)

// CreateOptions flags. These are bitmask flags.
const (
	FileDirectoryFile    = 0x1
	FileNonDirectoryFile = 0x40
	FileOpenReparsePoint = 0x00200000
)

// FileAttribute is the type of a directory entry in Stat.
type FileAttribute uint32

// File attribute bit masks - same as syscall but provided for all platforms.
const (
	FileAttributeReadonly     = FileAttribute(0x00000001)
	FileAttributeHidden       = FileAttribute(0x00000002)
	FileAttributeSystem       = FileAttribute(0x00000004)
	FileAttributeDirectory    = FileAttribute(0x00000010)
	FileAttributeArchive      = FileAttribute(0x00000020)
	FileAttributeNormal       = FileAttribute(0x00000080)
	FileAttributeReparsePoint = FileAttribute(0x00000400)
	IOReparseTagSymlink       = 0xA000000C
)

// File is the interface for files and directories.
type File interface {
	// ReadFile implements read for dokan.
	ReadFile(ctx context.Context, fi *FileInfo, bs []byte, offset int64) (int, error)
	// WriteFile implements write for dokan.
	WriteFile(ctx context.Context, fi *FileInfo, bs []byte, offset int64) (int, error)
	// FlushFileBuffers corresponds to fsync.
	FlushFileBuffers(ctx context.Context, fi *FileInfo) error

	// GetFileInformation - corresponds to stat.
	GetFileInformation(ctx context.Context, fi *FileInfo) (*Stat, error)

	// FindFiles is the readdir. The function is a callback that should be called
	// with each file. The same NamedStat may be reused for subsequent calls.
	//
	// Pattern will be an empty string unless UseFindFilesWithPattern is enabled - then
	// it may be a pattern like `*.png` to match. All implementations must be prepared
	// to handle empty strings as patterns.
	FindFiles(ctx context.Context, fi *FileInfo, pattern string, fillStatCallback func(*NamedStat) error) error

	// SetFileTime sets the file time. Test times with .IsZero
	// whether they should be set.
	SetFileTime(ctx context.Context, fi *FileInfo, creation time.Time, lastAccess time.Time, lastWrite time.Time) error
	// SetFileAttributes is for setting file attributes.
	SetFileAttributes(ctx context.Context, fi *FileInfo, fileAttributes FileAttribute) error

	// SetEndOfFile truncates the file. May be used to extend a file with zeros.
	SetEndOfFile(ctx context.Context, fi *FileInfo, length int64) error
	// SetAllocationSize see FILE_ALLOCATION_INFORMATION on MSDN.
	// For simple semantics if length > filesize then ignore else truncate(length).
	SetAllocationSize(ctx context.Context, fi *FileInfo, length int64) error

	LockFile(ctx context.Context, fi *FileInfo, offset int64, length int64) error
	UnlockFile(ctx context.Context, fi *FileInfo, offset int64, length int64) error

	GetFileSecurity(ctx context.Context, fi *FileInfo, si winacl.SecurityInformation, sd *winacl.SecurityDescriptor) error
	SetFileSecurity(ctx context.Context, fi *FileInfo, si winacl.SecurityInformation, sd *winacl.SecurityDescriptor) error

	// CanDeleteFile and CanDeleteDirectory should check whether the file/directory
	// can be deleted. The actual deletion should be done by checking
	// FileInfo.IsDeleteOnClose in Cleanup.
	CanDeleteFile(ctx context.Context, fi *FileInfo) error
	CanDeleteDirectory(ctx context.Context, fi *FileInfo) error
	// Cleanup is called after the last handle from userspace is closed.
	// Cleanup must perform actual deletions marked from CanDelete*
	// by checking FileInfo.IsDeleteOnClose if the filesystem supports
	// deletions.
	Cleanup(ctx context.Context, fi *FileInfo)
	// CloseFile is called when closing a handle to the file.
	CloseFile(ctx context.Context, fi *FileInfo)
}

// FreeSpace - semantics as with WINAPI GetDiskFreeSpaceEx
type FreeSpace struct {
	FreeBytesAvailable, TotalNumberOfBytes, TotalNumberOfFreeBytes uint64
}

// VolumeInformation - see WINAPI GetVolumeInformation for hints
type VolumeInformation struct {
	VolumeName             string
	VolumeSerialNumber     uint32
	MaximumComponentLength uint32
	FileSystemFlags        FileSystemFlags
	FileSystemName         string
}

// FileSystemFlags holds flags for filesystem features.
type FileSystemFlags uint32

// Various FileSystemFlags constants, see winapi documentation for details.
const (
	FileCasePreservedNames         = FileSystemFlags(0x2)
	FileCaseSensitiveSearch        = FileSystemFlags(0x1)
	FileFileCompression            = FileSystemFlags(0x10)
	FileNamedStreams               = FileSystemFlags(0x40000)
	FilePersistentAcls             = FileSystemFlags(0x8)
	FileReadOnlyVolume             = FileSystemFlags(0x80000)
	FileSequentalWriteOnce         = FileSystemFlags(0x100000)
	FileSupportsEncryption         = FileSystemFlags(0x20000)
	FileSupportsExtendedAttributes = FileSystemFlags(0x800000)
	FileSupportsHardLinks          = FileSystemFlags(0x400000)
	FileSupportObjectIDs           = FileSystemFlags(0x10000)
	FileSupportsOpenByFileID       = FileSystemFlags(0x1000000)
	FileSupportsRemoteStorage      = FileSystemFlags(0x100)
	FileSupportsReparsePoints      = FileSystemFlags(0x80)
	FileSupportsSparseFiles        = FileSystemFlags(0x40)
	FileSupportsTransactions       = FileSystemFlags(0x200000)
	FileSupportsUsnJournal         = FileSystemFlags(0x2000000)
	FileUnicodeOnDisk              = FileSystemFlags(0x4)
	FileVolumeIsCompressed         = FileSystemFlags(0x8000)
	FileVolumeQuotas               = FileSystemFlags(0x20)
)

// Stat is for GetFileInformation and friends.
type Stat struct {
	// Timestamps for the file
	Creation, LastAccess, LastWrite time.Time
	// FileSize is the size of the file in bytes
	FileSize int64
	// FileIndex is a 64 bit (nearly) unique ID of the file
	FileIndex uint64
	// FileAttributes bitmask holds the file attributes.
	FileAttributes FileAttribute
	// VolumeSerialNumber is the serial number of the volume (0 is fine)
	VolumeSerialNumber uint32
	// NumberOfLinks can be omitted, if zero set to 1.
	NumberOfLinks uint32
	// ReparsePointTag is for WIN32_FIND_DATA dwReserved0 for reparse point tags, typically it can be omitted.
	ReparsePointTag uint32
}

// NamedStat is used to for stat responses that require file names.
// If the name is longer than a DOS-name, insert the corresponding
// DOS-name to ShortName.
type NamedStat struct {
	Name      string
	ShortName string
	Stat
}

// NtStatus is a type implementing error interface that corresponds
// to NTSTATUS. It can be used to set the exact error/status code
// from the filesystem.
type NtStatus uint32

func (n NtStatus) Error() string {
	return "NTSTATUS=" + strconv.FormatUint(uint64(n), 16)
}

const (
	// ErrAccessDenied - access denied (EPERM)
	ErrAccessDenied = NtStatus(0xC0000022)
	// ErrObjectNameNotFound - filename does not exist (ENOENT)
	ErrObjectNameNotFound = NtStatus(0xC0000034)
	// ErrObjectNameCollision - a pathname already exists (EEXIST)
	ErrObjectNameCollision = NtStatus(0xC0000035)
	// ErrObjectPathNotFound - a pathname does not exist (ENOENT)
	ErrObjectPathNotFound = NtStatus(0xC000003A)
	// ErrNotSupported - not supported.
	ErrNotSupported = NtStatus(0xC00000BB)
	// ErrFileIsADirectory - file is a directory.
	ErrFileIsADirectory = NtStatus(0xC00000BA)
	// ErrDirectoryNotEmpty - wanted an empty dir - it is not empty.
	ErrDirectoryNotEmpty = NtStatus(0xC0000101)
	// ErrFileAlreadyExists - file already exists - fatal.
	ErrFileAlreadyExists = NtStatus(0xC0000035)
	// ErrNotSameDevice - MoveFile is denied, please use copy+delete.
	ErrNotSameDevice = NtStatus(0xC00000D4)
	// StatusBufferOverflow - buffer space too short for return value.
	StatusBufferOverflow = NtStatus(0x80000005)
	// StatusObjectNameExists - already exists, may be non-fatal...
	StatusObjectNameExists = NtStatus(0x40000000)
)
<|endoftext|>


<commit_before>// vagrant implements the packer.PostProcessor interface and adds a
// post-processor that turns artifacts of known builders into Vagrant
// boxes.
package vagrant

import (
	"fmt"
	"github.com/mitchellh/mapstructure"
	"github.com/mitchellh/packer/packer"
	"log"
	"text/template"
)

var builtins = map[string]string{
	"mitchellh.amazonebs":  "aws",
	"mitchellh.virtualbox": "virtualbox",
	"mitchellh.vmware":     "vmware",
}

type Config struct {
	OutputPath string `mapstructure:"output"`
}

type PostProcessor struct {
	config     Config
	premade    map[string]packer.PostProcessor
	rawConfigs []interface{}
}

func (p *PostProcessor) Configure(raws ...interface{}) error {
	// Store the raw configs for usage later
	p.rawConfigs = raws

	for _, raw := range raws {
		err := mapstructure.Decode(raw, &p.config)
		if err != nil {
			return err
		}
	}

	ppExtraConfig := make(map[string]interface{})
	if p.config.OutputPath == "" {
		p.config.OutputPath = "packer_{{ .BuildName }}_{{.Provider}}.box"
		ppExtraConfig["output"] = p.config.OutputPath
	}

	_, err := template.New("output").Parse(p.config.OutputPath)
	if err != nil {
		return fmt.Errorf("output invalid template: %s", err)
	}

	// Store the extra configuration for post-processors
	p.rawConfigs = append(p.rawConfigs, ppExtraConfig)

	// TODO(mitchellh): Properly handle multiple raw configs
	var mapConfig map[string]interface{}
	if err := mapstructure.Decode(raws[0], &mapConfig); err != nil {
		return err
	}

	p.premade = make(map[string]packer.PostProcessor)
	errors := make([]error, 0)
	for k, raw := range mapConfig {
		pp := keyToPostProcessor(k)
		if pp == nil {
			continue
		}

		// Create the proper list of configurations
		ppConfigs := make([]interface{}, 0, len(p.rawConfigs)+1)
		copy(ppConfigs, p.rawConfigs)
		ppConfigs = append(ppConfigs, raw)

		if err := pp.Configure(ppConfigs...); err != nil {
			errors = append(errors, err)
		}

		p.premade[k] = pp
	}

	if len(errors) > 0 {
		return &packer.MultiError{errors}
	}

	return nil
}

func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) {
	ppName, ok := builtins[artifact.BuilderId()]
	if !ok {
		return nil, false, fmt.Errorf("Unknown artifact type, can't build box: %s", artifact.BuilderId())
	}

	// Use the premade PostProcessor if we have one. Otherwise, we
	// create it and configure it here.
	pp, ok := p.premade[ppName]
	if !ok {
		log.Printf("Premade post-processor for '%s' not found. Creating.", ppName)
		pp = keyToPostProcessor(ppName)
		if pp == nil {
			return nil, false, fmt.Errorf("Vagrant box post-processor not found: %s", ppName)
		}

		if err := pp.Configure(p.rawConfigs...); err != nil {
			return nil, false, err
		}
	}

	ui.Say(fmt.Sprintf("Creating Vagrant box for '%s' provider", ppName))
	return pp.PostProcess(ui, artifact)
}

func keyToPostProcessor(key string) packer.PostProcessor {
	switch key {
	case "aws":
		return new(AWSBoxPostProcessor)
	case "virtualbox":
		return new(VBoxBoxPostProcessor)
	case "vmware":
		return new(VMwareBoxPostProcessor)
	default:
		return nil
	}
}
<commit_msg>post-processor/vagrant: fix output ConfigTemplate validation [GH-324]<commit_after>// vagrant implements the packer.PostProcessor interface and adds a
// post-processor that turns artifacts of known builders into Vagrant
// boxes.
package vagrant

import (
	"fmt"
	"github.com/mitchellh/mapstructure"
	"github.com/mitchellh/packer/common"
	"github.com/mitchellh/packer/packer"
	"log"
)

var builtins = map[string]string{
	"mitchellh.amazonebs":  "aws",
	"mitchellh.virtualbox": "virtualbox",
	"mitchellh.vmware":     "vmware",
}

type Config struct {
	common.PackerConfig `mapstructure:",squash"`

	OutputPath string `mapstructure:"output"`
}

type PostProcessor struct {
	config     Config
	premade    map[string]packer.PostProcessor
	rawConfigs []interface{}
}

func (p *PostProcessor) Configure(raws ...interface{}) error {
	// Store the raw configs for usage later
	p.rawConfigs = raws

	md, err := common.DecodeConfig(&p.config, raws...)
	if err != nil {
		return err
	}

	tpl, err := packer.NewConfigTemplate()
	if err != nil {
		return err
	}
	tpl.UserVars = p.config.PackerUserVars

	// Accumulate any errors
	errs := common.CheckUnusedConfig(md)

	ppExtraConfig := make(map[string]interface{})
	if p.config.OutputPath == "" {
		p.config.OutputPath = "packer_{{ .BuildName }}_{{.Provider}}.box"
		ppExtraConfig["output"] = p.config.OutputPath
	}

	//	_, err := template.New("output").Parse(p.config.OutputPath)
	if err := tpl.Validate(p.config.OutputPath); err != nil {
		errs = packer.MultiErrorAppend(
			errs, fmt.Errorf("Error parsing output template: %s", err))
		return errs
	}

	// Store the extra configuration for post-processors
	p.rawConfigs = append(p.rawConfigs, ppExtraConfig)

	// TODO(mitchellh): Properly handle multiple raw configs
	var mapConfig map[string]interface{}
	if err := mapstructure.Decode(raws[0], &mapConfig); err != nil {
		return err
	}

	p.premade = make(map[string]packer.PostProcessor)
	errors := make([]error, 0)
	for k, raw := range mapConfig {
		pp := keyToPostProcessor(k)
		if pp == nil {
			continue
		}

		// Create the proper list of configurations
		ppConfigs := make([]interface{}, 0, len(p.rawConfigs)+1)
		copy(ppConfigs, p.rawConfigs)
		ppConfigs = append(ppConfigs, raw)

		if err := pp.Configure(ppConfigs...); err != nil {
			errors = append(errors, err)
		}

		p.premade[k] = pp
	}

	if len(errors) > 0 {
		return &packer.MultiError{errors}
	}

	return nil
}

func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) {
	ppName, ok := builtins[artifact.BuilderId()]
	if !ok {
		return nil, false, fmt.Errorf("Unknown artifact type, can't build box: %s", artifact.BuilderId())
	}

	// Use the premade PostProcessor if we have one. Otherwise, we
	// create it and configure it here.
	pp, ok := p.premade[ppName]
	if !ok {
		log.Printf("Premade post-processor for '%s' not found. Creating.", ppName)
		pp = keyToPostProcessor(ppName)
		if pp == nil {
			return nil, false, fmt.Errorf("Vagrant box post-processor not found: %s", ppName)
		}

		if err := pp.Configure(p.rawConfigs...); err != nil {
			return nil, false, err
		}
	}

	ui.Say(fmt.Sprintf("Creating Vagrant box for '%s' provider", ppName))
	return pp.PostProcess(ui, artifact)
}

func keyToPostProcessor(key string) packer.PostProcessor {
	switch key {
	case "aws":
		return new(AWSBoxPostProcessor)
	case "virtualbox":
		return new(VBoxBoxPostProcessor)
	case "vmware":
		return new(VMwareBoxPostProcessor)
	default:
		return nil
	}
}
<|endoftext|>