<commit_before>package slinga

import (
	"bytes"
	"errors"
	log "github.com/Sirupsen/logrus"
	"strings"
	"text/template"
)

/*
	Core engine for Slinga processing and evaluation
*/

// TemplateData defines struct that holds all params and gets passed to template engine for evaluating template string
type TemplateData struct {
	Labels    map[string]string
	User      User
	Discovery map[string]interface{}
}

// ResolveUsage evaluates all recorded Dependencies ("<user> needs <service> with <labels>") and calculates allocations
func (usage *ServiceUsageState) ResolveUsage(users *GlobalUsers) error {
	for serviceName, dependencies := range usage.Dependencies.Dependencies {
		for _, d := range dependencies {
			user := users.Users[d.UserID]

			// take user labels
			labels := user.getLabelSet()

			// combine them with dependency labels
			labels = labels.addLabels(d.getLabelSet())

			// see if it needs to be traced (addl debug output on console)
			tracing.setEnable(d.Trace)

			// resolve usage via applying policy
			resKey, err := usage.resolveWithLabels(user, serviceName, labels, usage.ComponentInstanceMap, 0)

			// disable tracing
			tracing.setEnable(false)

			// see if there is an error
			if err != nil {
				return err
			}

			// record high-level service resolution
			d.ResolvesTo = resKey
		}
	}
	return nil
}

// Evaluate "<user> needs <service>" statement
func (usage *ServiceUsageState) resolveWithLabels(user User, serviceName string, labels LabelSet, cim map[string]interface{}, depth int) (string, error) {

	// Resolving allocations for service
	debug.WithFields(log.Fields{
		"service": serviceName,
		"user":    user.Name,
		"labels":  labels,
	}).Info("Resolving allocations for service")

	tracing.Printf(depth, "[Dependency]")
	tracing.Printf(depth, "User: %s (ID = %s)", user.Name, user.ID)
	tracing.Printf(depth+1, "Labels: %s", labels)
	tracing.Printf(depth, "Service: %s", serviceName)

	// Policy
	policy := usage.Policy

	// Locate the service
	service, err := policy.getService(serviceName)
	if err != nil {
		tracing.Printf(depth+1, "Error while trying to look up service %s (%v)", serviceName, err)
		return "", err
	}

	// Process service and transform labels
	labels = labels.applyTransform(service.Labels)
	tracing.Printf(depth+1, "New labels = %s", labels)

	// Match the context
	context, err := policy.getMatchedContext(*service, user, labels, depth)
	if err != nil {
		tracing.Printf(depth+1, "Error while matching context for service %s (%v)", serviceName, err)
		return "", err
	}
	// If no matching context is found, let's just exit
	if context == nil {
		tracing.Printf(depth+1, "No context matched for service %s", serviceName)
		return "", nil
	}

	// Process context and transform labels
	labels = labels.applyTransform(context.Labels)
	tracing.Printf(depth, "Context: %s", context.Name)
	tracing.Printf(depth+1, "New labels = %s", labels)

	// Match the allocation
	allocation, err := policy.getMatchedAllocation(*service, user, *context, labels, depth)
	if err != nil {
		tracing.Printf(depth, "Error while matching allocation for service %s, context %s (%v)", serviceName, context.Name, err)
		return "", err
	}
	// If no matching allocation is found, let's just exit
	if allocation == nil {
		tracing.Printf(depth, "No allocation matched for service %s, context %s", serviceName, context.Name)
		return "", nil
	}

	// Process allocation and transform labels
	labels = labels.applyTransform(allocation.Labels)
	tracing.Printf(depth, "Allocation: %s", allocation.NameResolved)
	tracing.Printf(depth+1, "New labels = %s", labels)

	// Now, sort all components in topological order
	componentsOrdered, err := service.getComponentsSortedTopologically()
	if err != nil {
		return "", err
	}

	// Resolve every component
	for _, component := range componentsOrdered {
		// Process component and transform labels
		componentLabels := labels.applyTransform(component.Labels)

		// Is it a code?
		var codeParams interface{}
		var discoveryParams interface{}

		componentKey := usage.createServiceUsageKey(service, context, allocation, component)
		discoveryParams, err = component.processTemplateParams(component.Discovery, componentKey, componentLabels, user, cim, "discovery", depth)
		if err != nil {
			return "", err
		}

		// Create new map with resolution keys for component
		cimComponent := make(map[string]interface{})
		cim[component.Name] = cimComponent

		if component.Code != nil {
			// Evaluate code params
			debug.WithFields(log.Fields{
				"service":    service.Name,
				"component":  component.Name,
				"context":    context.Name,
				"allocation": allocation.NameResolved,
			}).Info("Processing dependency on code execution")

			// TODO: WAT?? no HelmName please
			cimComponent["instance"] = HelmName(componentKey)
			if discoveryParamsMap, ok := discoveryParams.(map[interface{}]interface{}); ok {
				for k, v := range discoveryParamsMap {
					cimComponent[k.(string)] = v
				}
			}

			codeParams, err = component.processTemplateParams(component.Code.Params, componentKey, componentLabels, user, cim, "code", depth)
			if err != nil {
				return "", err
			}
		} else if component.Service != "" {
			debug.WithFields(log.Fields{
				"service":          service.Name,
				"component":        component.Name,
				"context":          context.Name,
				"allocation":       allocation.NameResolved,
				"dependsOnService": component.Service,
			}).Info("Processing dependency on another service")

			tracing.Println()

			// resolve dependency recursively
			resolvedKey, err := usage.resolveWithLabels(user, component.Service, componentLabels, cimComponent, depth+1)
			if err != nil {
				return "", err
			}

			// if a dependency has not been matched
			if len(resolvedKey) <= 0 {
				// TODO: if a dependency cannot be fulfilled, we need to handle it correctly. i.e. usages should be recorded in different context and not applied
				debug.WithFields(log.Fields{
					"service":          service.Name,
					"component":        component.Name,
					"context":          context.Name,
					"allocation":       allocation.NameResolved,
					"dependsOnService": component.Service,
				}).Warning("Cannot fulfill dependency on another service")
				return "", nil
			}
		} else {
			debug.WithFields(log.Fields{
				"service":   service.Name,
				"component": component.Name,
			}).Fatal("Invalid component (not code and not service")
		}

		// Record usage of a given component
		usage.recordUsage(componentKey, user, componentLabels, codeParams, discoveryParams)
	}

	// Record usage of a given service
	serviceKey := usage.createServiceUsageKey(service, context, allocation, nil)
	usage.recordUsage(serviceKey, user, labels, nil, nil)

	return context.Name + "#" + allocation.NameResolved, nil
}

// Topologically sort components and return true if there is a cycle detected
func (service *Service) dfsComponentSort(u *ServiceComponent, colors map[string]int) bool {
	colors[u.Name] = 1

	for _, vName := range u.Dependencies {
		v, exists := service.getComponentsMap()[vName]
		if !exists {
			debug.WithFields(log.Fields{
				"service":   service.Name,
				"component": vName,
			}).Fatal("Service dependency points to non-existing component")
		}
		if vColor, ok := colors[v.Name]; !ok {
			// not visited yet -> visit and exit if a cycle was found
			if service.dfsComponentSort(v, colors) {
				return true
			}
		} else if vColor == 1 {
			return true
		}
	}

	service.componentsOrdered = append(service.componentsOrdered, u)
	colors[u.Name] = 2
	return false
}

// Sorts all components in a topological way
func (service *Service) getComponentsSortedTopologically() ([]*ServiceComponent, error) {
	if service.componentsOrdered == nil {
		// Initiate colors
		colors := make(map[string]int)

		// Dfs
		var cycle = false
		for _, c := range service.Components {
			if _, ok := colors[c.Name]; !ok {
				if service.dfsComponentSort(c, colors) {
					cycle = true
					break
				}
			}
		}

		if cycle {
			return nil, errors.New("Component cycle detected in service " + service.Name)
		}
	}

	return service.componentsOrdered, nil
}

// Helper to get a service
func (policy *Policy) getService(serviceName string) (*Service, error) {
	// Locate the service
	service := policy.Services[serviceName]
	if service == nil {
		return nil, errors.New("Service " + serviceName + " not found")
	}
	return service, nil
}

// Helper to get a matched context
func (policy *Policy) getMatchedContext(service Service, user User, labels LabelSet, depth int) (*Context, error) {
	// Locate the list of contexts for service
	contexts, ok := policy.Contexts[service.Name]
	if !ok || len(contexts) <= 0 {
		tracing.Printf(depth+1, "No contexts found")
		return nil, errors.New("No contexts found for " + service.Name)
	}

	// See which context matches
	var contextMatched *Context
	for _, c := range contexts {
		m := c.matches(labels)
		tracing.Printf(depth+1, "[%t] Testing context '%s': (criteria = %+v)", m, c.Name, c.Criteria)
		if m {
			contextMatched = c
			break
		}
	}

	if contextMatched != nil {
		debug.WithFields(log.Fields{
			"service": service.Name,
			"context": contextMatched.Name,
			"user":    user.Name,
		}).Info("Matched context")
	} else {
		debug.WithFields(log.Fields{
			"service": service.Name,
			"user":    user.Name,
		}).Info("No context matched")
	}
	return contextMatched, nil
}

// Helper to get a matched allocation
func (policy *Policy) getMatchedAllocation(service Service, user User, context Context, labels LabelSet, depth int) (*Allocation, error) {
	if len(context.Allocations) <= 0 {
		tracing.Printf(depth+1, "No allocations found")
		return nil, errors.New("No allocations found for " + service.Name)
	}

	// See which allocation matches
	var allocationMatched *Allocation
	for _, a := range context.Allocations {
		m := a.matches(labels)
		tracing.Printf(depth+1, "[%t] Testing allocation '%s': (criteria = %+v)", m, a.Name, a.Criteria)
		if m {
			allocationMatched = a
			break
		}
	}

	// Check errors and resolve allocation name (it can be dynamic, depending on user labels)
	if allocationMatched != nil {
		err := allocationMatched.resolveName(user)
		if err != nil {
			debug.WithFields(log.Fields{
				"service":    service.Name,
				"context":    context.Name,
				"allocation": allocationMatched.Name,
				"user":       user.Name,
			}).Fatal("Cannot resolve name for an allocation")
		}
		debug.WithFields(log.Fields{
			"service":            service.Name,
			"context":            context.Name,
			"allocation":         allocationMatched.Name,
			"allocationResolved": allocationMatched.NameResolved,
			"user":               user.Name,
		}).Info("Matched allocation")
	} else {
		debug.WithFields(log.Fields{
			"service": service.Name,
			"context": context.Name,
			"user":    user.Name,
		}).Info("No allocation matched")
	}

	return allocationMatched, nil
}

type ProcessingError struct {
	Reason string
}

func (err ProcessingError) Error() string {
	return err.Reason
}

func (component *ServiceComponent) processTemplateParams(template interface{}, componentKey string, labels LabelSet, user User, cim map[string]interface{}, templateType string, depth int) (interface{}, error) {
	if template == nil {
		return nil, nil
	}
	tracing.Printf(depth+1, "Component: %s (%s)", component.Name, templateType)

	cimCopy := make(map[string]interface{})
	// TODO SPARTAAA!
	cimCopy["instance"] = HelmName(componentKey)
	for k, v := range cim {
		cimCopy[k] = v
	}

	templateData := TemplateData{
		Labels:    labels.Labels,
		Discovery: cimCopy,
		User:      user}

	var evalParamsInterface func(params interface{}) (interface{}, error)
	evalParamsInterface = func(params interface{}) (interface{}, error) {
		if params == nil {
			return "", nil
		} else if paramsMap, ok := params.(map[interface{}]interface{}); ok {
			resultMap := make(map[interface{}]interface{})

			for key, value := range paramsMap {
				evaluatedValue, err := evalParamsInterface(value)
				if err != nil {
					return nil, err
				}
				resultMap[key] = evaluatedValue
			}

			return resultMap, nil
		} else if paramsStr, ok := params.(string); ok {
			evaluatedValue, err := evaluateCodeParamTemplate(paramsStr, templateData)
			tracing.Printf(depth+2, "Parameter '%s': %s", paramsStr, evaluatedValue)
			if err != nil {
				return nil, err
			}
			return evaluatedValue, nil
		} else if paramsInt, ok := params.(int); ok {
			return paramsInt, nil
		} else if paramsBool, ok := params.(bool); ok {
			return paramsBool, nil
		}

		return nil, ProcessingError{"There should be map[string]interface{} or string"}
	}

	return evalParamsInterface(template)
}

func evaluateCodeParamTemplate(templateStr string, templateData TemplateData) (string, error) {
	tmpl, err := template.New("").Parse(templateStr)
	if err != nil {
		return "", errors.New("Invalid template " + templateStr)
	}

	var doc bytes.Buffer
	err = tmpl.Execute(&doc, templateData)

	if err != nil {
		return "", errors.New("Cannot evaluate template " + templateStr)
	}

	result := doc.String()
	if strings.Contains(result, "<no value>") {
		return "", errors.New("Cannot evaluate template " + templateStr)
	}

	// TODO: remove this ugly shit later (should not reference Helm in generic engine)
	return HelmName(doc.String()), nil
}
<commit_msg>Replace unneeded error type with errors.New<commit_after>package slinga

import (
	"bytes"
	"errors"
	log "github.com/Sirupsen/logrus"
	"strings"
	"text/template"
)

/*
	Core engine for Slinga processing and evaluation
*/

// TemplateData defines struct that holds all params and gets passed to template engine for evaluating template string
type TemplateData struct {
	Labels    map[string]string
	User      User
	Discovery map[string]interface{}
}

// ResolveUsage evaluates all recorded Dependencies ("<user> needs <service> with <labels>") and calculates allocations
func (usage *ServiceUsageState) ResolveUsage(users *GlobalUsers) error {
	for serviceName, dependencies := range usage.Dependencies.Dependencies {
		for _, d := range dependencies {
			user := users.Users[d.UserID]

			// take user labels
			labels := user.getLabelSet()

			// combine them with dependency labels
			labels = labels.addLabels(d.getLabelSet())

			// see if it needs to be traced (addl debug output on console)
			tracing.setEnable(d.Trace)

			// resolve usage via applying policy
			resKey, err := usage.resolveWithLabels(user, serviceName, labels, usage.ComponentInstanceMap, 0)

			// disable tracing
			tracing.setEnable(false)

			// see if there is an error
			if err != nil {
				return err
			}

			// record high-level service resolution
			d.ResolvesTo = resKey
		}
	}
	return nil
}

// Evaluate "<user> needs <service>" statement
func (usage *ServiceUsageState) resolveWithLabels(user User, serviceName string, labels LabelSet, cim map[string]interface{}, depth int) (string, error) {

	// Resolving allocations for service
	debug.WithFields(log.Fields{
		"service": serviceName,
		"user":    user.Name,
		"labels":  labels,
	}).Info("Resolving allocations for service")

	tracing.Printf(depth, "[Dependency]")
	tracing.Printf(depth, "User: %s (ID = %s)", user.Name, user.ID)
	tracing.Printf(depth+1, "Labels: %s", labels)
	tracing.Printf(depth, "Service: %s", serviceName)

	// Policy
	policy := usage.Policy

	// Locate the service
	service, err := policy.getService(serviceName)
	if err != nil {
		tracing.Printf(depth+1, "Error while trying to look up service %s (%v)", serviceName, err)
		return "", err
	}

	// Process service and transform labels
	labels = labels.applyTransform(service.Labels)
	tracing.Printf(depth+1, "New labels = %s", labels)

	// Match the context
	context, err := policy.getMatchedContext(*service, user, labels, depth)
	if err != nil {
		tracing.Printf(depth+1, "Error while matching context for service %s (%v)", serviceName, err)
		return "", err
	}
	// If no matching context is found, let's just exit
	if context == nil {
		tracing.Printf(depth+1, "No context matched for service %s", serviceName)
		return "", nil
	}

	// Process context and transform labels
	labels = labels.applyTransform(context.Labels)
	tracing.Printf(depth, "Context: %s", context.Name)
	tracing.Printf(depth+1, "New labels = %s", labels)

	// Match the allocation
	allocation, err := policy.getMatchedAllocation(*service, user, *context, labels, depth)
	if err != nil {
		tracing.Printf(depth, "Error while matching allocation for service %s, context %s (%v)", serviceName, context.Name, err)
		return "", err
	}
	// If no matching allocation is found, let's just exit
	if allocation == nil {
		tracing.Printf(depth, "No allocation matched for service %s, context %s", serviceName, context.Name)
		return "", nil
	}

	// Process allocation and transform labels
	labels = labels.applyTransform(allocation.Labels)
	tracing.Printf(depth, "Allocation: %s", allocation.NameResolved)
	tracing.Printf(depth+1, "New labels = %s", labels)

	// Now, sort all components in topological order
	componentsOrdered, err := service.getComponentsSortedTopologically()
	if err != nil {
		return "", err
	}

	// Resolve every component
	for _, component := range componentsOrdered {
		// Process component and transform labels
		componentLabels := labels.applyTransform(component.Labels)

		// Is it a code?
		var codeParams interface{}
		var discoveryParams interface{}

		componentKey := usage.createServiceUsageKey(service, context, allocation, component)
		discoveryParams, err = component.processTemplateParams(component.Discovery, componentKey, componentLabels, user, cim, "discovery", depth)
		if err != nil {
			return "", err
		}

		// Create new map with resolution keys for component
		cimComponent := make(map[string]interface{})
		cim[component.Name] = cimComponent

		if component.Code != nil {
			// Evaluate code params
			debug.WithFields(log.Fields{
				"service":    service.Name,
				"component":  component.Name,
				"context":    context.Name,
				"allocation": allocation.NameResolved,
			}).Info("Processing dependency on code execution")

			// TODO: WAT?? no HelmName please
			cimComponent["instance"] = HelmName(componentKey)
			if discoveryParamsMap, ok := discoveryParams.(map[interface{}]interface{}); ok {
				for k, v := range discoveryParamsMap {
					cimComponent[k.(string)] = v
				}
			}

			codeParams, err = component.processTemplateParams(component.Code.Params, componentKey, componentLabels, user, cim, "code", depth)
			if err != nil {
				return "", err
			}
		} else if component.Service != "" {
			debug.WithFields(log.Fields{
				"service":          service.Name,
				"component":        component.Name,
				"context":          context.Name,
				"allocation":       allocation.NameResolved,
				"dependsOnService": component.Service,
			}).Info("Processing dependency on another service")

			tracing.Println()

			// resolve dependency recursively
			resolvedKey, err := usage.resolveWithLabels(user, component.Service, componentLabels, cimComponent, depth+1)
			if err != nil {
				return "", err
			}

			// if a dependency has not been matched
			if len(resolvedKey) <= 0 {
				// TODO: if a dependency cannot be fulfilled, we need to handle it correctly. i.e. usages should be recorded in different context and not applied
				debug.WithFields(log.Fields{
					"service":          service.Name,
					"component":        component.Name,
					"context":          context.Name,
					"allocation":       allocation.NameResolved,
					"dependsOnService": component.Service,
				}).Warning("Cannot fulfill dependency on another service")
				return "", nil
			}
		} else {
			debug.WithFields(log.Fields{
				"service":   service.Name,
				"component": component.Name,
			}).Fatal("Invalid component (not code and not service")
		}

		// Record usage of a given component
		usage.recordUsage(componentKey, user, componentLabels, codeParams, discoveryParams)
	}

	// Record usage of a given service
	serviceKey := usage.createServiceUsageKey(service, context, allocation, nil)
	usage.recordUsage(serviceKey, user, labels, nil, nil)

	return context.Name + "#" + allocation.NameResolved, nil
}

// Topologically sort components and return true if there is a cycle detected
func (service *Service) dfsComponentSort(u *ServiceComponent, colors map[string]int) bool {
	colors[u.Name] = 1

	for _, vName := range u.Dependencies {
		v, exists := service.getComponentsMap()[vName]
		if !exists {
			debug.WithFields(log.Fields{
				"service":   service.Name,
				"component": vName,
			}).Fatal("Service dependency points to non-existing component")
		}
		if vColor, ok := colors[v.Name]; !ok {
			// not visited yet -> visit and exit if a cycle was found
			if service.dfsComponentSort(v, colors) {
				return true
			}
		} else if vColor == 1 {
			return true
		}
	}

	service.componentsOrdered = append(service.componentsOrdered, u)
	colors[u.Name] = 2
	return false
}

// Sorts all components in a topological way
func (service *Service) getComponentsSortedTopologically() ([]*ServiceComponent, error) {
	if service.componentsOrdered == nil {
		// Initiate colors
		colors := make(map[string]int)

		// Dfs
		var cycle = false
		for _, c := range service.Components {
			if _, ok := colors[c.Name]; !ok {
				if service.dfsComponentSort(c, colors) {
					cycle = true
					break
				}
			}
		}

		if cycle {
			return nil, errors.New("Component cycle detected in service " + service.Name)
		}
	}

	return service.componentsOrdered, nil
}

// Helper to get a service
func (policy *Policy) getService(serviceName string) (*Service, error) {
	// Locate the service
	service := policy.Services[serviceName]
	if service == nil {
		return nil, errors.New("Service " + serviceName + " not found")
	}
	return service, nil
}

// Helper to get a matched context
func (policy *Policy) getMatchedContext(service Service, user User, labels LabelSet, depth int) (*Context, error) {
	// Locate the list of contexts for service
	contexts, ok := policy.Contexts[service.Name]
	if !ok || len(contexts) <= 0 {
		tracing.Printf(depth+1, "No contexts found")
		return nil, errors.New("No contexts found for " + service.Name)
	}

	// See which context matches
	var contextMatched *Context
	for _, c := range contexts {
		m := c.matches(labels)
		tracing.Printf(depth+1, "[%t] Testing context '%s': (criteria = %+v)", m, c.Name, c.Criteria)
		if m {
			contextMatched = c
			break
		}
	}

	if contextMatched != nil {
		debug.WithFields(log.Fields{
			"service": service.Name,
			"context": contextMatched.Name,
			"user":    user.Name,
		}).Info("Matched context")
	} else {
		debug.WithFields(log.Fields{
			"service": service.Name,
			"user":    user.Name,
		}).Info("No context matched")
	}
	return contextMatched, nil
}

// Helper to get a matched allocation
func (policy *Policy) getMatchedAllocation(service Service, user User, context Context, labels LabelSet, depth int) (*Allocation, error) {
	if len(context.Allocations) <= 0 {
		tracing.Printf(depth+1, "No allocations found")
		return nil, errors.New("No allocations found for " + service.Name)
	}

	// See which allocation matches
	var allocationMatched *Allocation
	for _, a := range context.Allocations {
		m := a.matches(labels)
		tracing.Printf(depth+1, "[%t] Testing allocation '%s': (criteria = %+v)", m, a.Name, a.Criteria)
		if m {
			allocationMatched = a
			break
		}
	}

	// Check errors and resolve allocation name (it can be dynamic, depending on user labels)
	if allocationMatched != nil {
		err := allocationMatched.resolveName(user)
		if err != nil {
			debug.WithFields(log.Fields{
				"service":    service.Name,
				"context":    context.Name,
				"allocation": allocationMatched.Name,
				"user":       user.Name,
			}).Fatal("Cannot resolve name for an allocation")
		}
		debug.WithFields(log.Fields{
			"service":            service.Name,
			"context":            context.Name,
			"allocation":         allocationMatched.Name,
			"allocationResolved": allocationMatched.NameResolved,
			"user":               user.Name,
		}).Info("Matched allocation")
	} else {
		debug.WithFields(log.Fields{
			"service": service.Name,
			"context": context.Name,
			"user":    user.Name,
		}).Info("No allocation matched")
	}

	return allocationMatched, nil
}

func (component *ServiceComponent) processTemplateParams(template interface{}, componentKey string, labels LabelSet, user User, cim map[string]interface{}, templateType string, depth int) (interface{}, error) {
	if template == nil {
		return nil, nil
	}
	tracing.Printf(depth+1, "Component: %s (%s)", component.Name, templateType)

	cimCopy := make(map[string]interface{})
	// TODO SPARTAAA!
	cimCopy["instance"] = HelmName(componentKey)
	for k, v := range cim {
		cimCopy[k] = v
	}

	templateData := TemplateData{
		Labels:    labels.Labels,
		Discovery: cimCopy,
		User:      user}

	var evalParamsInterface func(params interface{}) (interface{}, error)
	evalParamsInterface = func(params interface{}) (interface{}, error) {
		if params == nil {
			return "", nil
		} else if paramsMap, ok := params.(map[interface{}]interface{}); ok {
			resultMap := make(map[interface{}]interface{})

			for key, value := range paramsMap {
				evaluatedValue, err := evalParamsInterface(value)
				if err != nil {
					return nil, err
				}
				resultMap[key] = evaluatedValue
			}

			return resultMap, nil
		} else if paramsStr, ok := params.(string); ok {
			evaluatedValue, err := evaluateCodeParamTemplate(paramsStr, templateData)
			tracing.Printf(depth+2, "Parameter '%s': %s", paramsStr, evaluatedValue)
			if err != nil {
				return nil, err
			}
			return evaluatedValue, nil
		} else if paramsInt, ok := params.(int); ok {
			return paramsInt, nil
		} else if paramsBool, ok := params.(bool); ok {
			return paramsBool, nil
		}

		return nil, errors.New("There should be map[string]interface{} or string")
	}

	return evalParamsInterface(template)
}

func evaluateCodeParamTemplate(templateStr string, templateData TemplateData) (string, error) {
	tmpl, err := template.New("").Parse(templateStr)
	if err != nil {
		return "", errors.New("Invalid template " + templateStr)
	}

	var doc bytes.Buffer
	err = tmpl.Execute(&doc, templateData)

	if err != nil {
		return "", errors.New("Cannot evaluate template " + templateStr)
	}

	result := doc.String()
	if strings.Contains(result, "<no value>") {
		return "", errors.New("Cannot evaluate template " + templateStr)
	}

	// TODO: remove this ugly shit later (should not reference Helm in generic engine)
	return HelmName(doc.String()), nil
}
<|endoftext|>


<commit_before>// Copyright 2013 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 testutil

import (
	"fmt"
	"net/http"
	"os"
	"regexp"
	"runtime"
	"sort"
	"strings"
	"testing"
	"time"
)

/*
CheckLeakedGoroutine verifies tests do not leave any leaky
goroutines. It returns true when there are goroutines still
running(leaking) after all tests.

	import "github.com/coreos/etcd/pkg/testutil"

	func TestMain(m *testing.M) {
		v := m.Run()
		if v == 0 && testutil.CheckLeakedGoroutine() {
			os.Exit(1)
		}
		os.Exit(v)
	}

	func TestSample(t *testing.T) {
		defer testutil.AfterTest(t)
		...
	}

*/
func CheckLeakedGoroutine() bool {
	if testing.Short() {
		// not counting goroutines for leakage in -short mode
		return false
	}
	gs := interestingGoroutines()
	if len(gs) == 0 {
		return false
	}

	stackCount := make(map[string]int)
	re := regexp.MustCompile(`\(0[0-9a-fx, ]*\)`)
	for _, g := range gs {
		// strip out pointer arguments in first function of stack dump
		normalized := string(re.ReplaceAll([]byte(g), []byte("(...)")))
		stackCount[normalized]++
	}

	fmt.Fprintf(os.Stderr, "Too many goroutines running after all test(s).\n")
	for stack, count := range stackCount {
		fmt.Fprintf(os.Stderr, "%d instances of:\n%s\n", count, stack)
	}
	return true
}

func AfterTest(t *testing.T) {
	http.DefaultTransport.(*http.Transport).CloseIdleConnections()
	if testing.Short() {
		return
	}
	var bad string
	badSubstring := map[string]string{
		").writeLoop(":                                 "a Transport",
		"created by net/http/httptest.(*Server).Start": "an httptest.Server",
		"timeoutHandler":                               "a TimeoutHandler",
		"net.(*netFD).connect(":                        "a timing out dial",
		").noteClientGone(":                            "a closenotifier sender",
		").readLoop(":                                  "a Transport",
	}

	var stacks string
	for i := 0; i < 6; i++ {
		bad = ""
		stacks = strings.Join(interestingGoroutines(), "\n\n")
		for substr, what := range badSubstring {
			if strings.Contains(stacks, substr) {
				bad = what
			}
		}
		if bad == "" {
			return
		}
		// Bad stuff found, but goroutines might just still be
		// shutting down, so give it some time.
		time.Sleep(50 * time.Millisecond)
	}
	t.Errorf("Test appears to have leaked %s:\n%s", bad, stacks)
}

func interestingGoroutines() (gs []string) {
	buf := make([]byte, 2<<20)
	buf = buf[:runtime.Stack(buf, true)]
	for _, g := range strings.Split(string(buf), "\n\n") {
		sl := strings.SplitN(g, "\n", 2)
		if len(sl) != 2 {
			continue
		}
		stack := strings.TrimSpace(sl[1])
		if stack == "" ||
			strings.Contains(stack, "runtime/panic.go") ||
			strings.Contains(stack, "created by testing.RunTests") ||
			strings.Contains(stack, "testing.Main(") ||
			strings.Contains(stack, "runtime.goexit") ||
			strings.Contains(stack, "github.com/coreos/etcd/pkg/testutil.interestingGoroutines") ||
			strings.Contains(stack, "github.com/coreos/etcd/pkg/logutil.(*MergeLogger).outputLoop") ||
			strings.Contains(stack, "github.com/golang/glog.(*loggingT).flushDaemon") ||
			strings.Contains(stack, "created by runtime.gc") ||
			strings.Contains(stack, "runtime.MHeap_Scavenger") {
			continue
		}
		gs = append(gs, stack)
	}
	sort.Strings(gs)
	return
}
<commit_msg>testutil: whitelist thread created by go cover<commit_after>// Copyright 2013 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 testutil

import (
	"fmt"
	"net/http"
	"os"
	"regexp"
	"runtime"
	"sort"
	"strings"
	"testing"
	"time"
)

/*
CheckLeakedGoroutine verifies tests do not leave any leaky
goroutines. It returns true when there are goroutines still
running(leaking) after all tests.

	import "github.com/coreos/etcd/pkg/testutil"

	func TestMain(m *testing.M) {
		v := m.Run()
		if v == 0 && testutil.CheckLeakedGoroutine() {
			os.Exit(1)
		}
		os.Exit(v)
	}

	func TestSample(t *testing.T) {
		defer testutil.AfterTest(t)
		...
	}

*/
func CheckLeakedGoroutine() bool {
	if testing.Short() {
		// not counting goroutines for leakage in -short mode
		return false
	}
	gs := interestingGoroutines()
	if len(gs) == 0 {
		return false
	}

	stackCount := make(map[string]int)
	re := regexp.MustCompile(`\(0[0-9a-fx, ]*\)`)
	for _, g := range gs {
		// strip out pointer arguments in first function of stack dump
		normalized := string(re.ReplaceAll([]byte(g), []byte("(...)")))
		stackCount[normalized]++
	}

	fmt.Fprintf(os.Stderr, "Too many goroutines running after all test(s).\n")
	for stack, count := range stackCount {
		fmt.Fprintf(os.Stderr, "%d instances of:\n%s\n", count, stack)
	}
	return true
}

func AfterTest(t *testing.T) {
	http.DefaultTransport.(*http.Transport).CloseIdleConnections()
	if testing.Short() {
		return
	}
	var bad string
	badSubstring := map[string]string{
		").writeLoop(":                                 "a Transport",
		"created by net/http/httptest.(*Server).Start": "an httptest.Server",
		"timeoutHandler":                               "a TimeoutHandler",
		"net.(*netFD).connect(":                        "a timing out dial",
		").noteClientGone(":                            "a closenotifier sender",
		").readLoop(":                                  "a Transport",
	}

	var stacks string
	for i := 0; i < 6; i++ {
		bad = ""
		stacks = strings.Join(interestingGoroutines(), "\n\n")
		for substr, what := range badSubstring {
			if strings.Contains(stacks, substr) {
				bad = what
			}
		}
		if bad == "" {
			return
		}
		// Bad stuff found, but goroutines might just still be
		// shutting down, so give it some time.
		time.Sleep(50 * time.Millisecond)
	}
	t.Errorf("Test appears to have leaked %s:\n%s", bad, stacks)
}

func interestingGoroutines() (gs []string) {
	buf := make([]byte, 2<<20)
	buf = buf[:runtime.Stack(buf, true)]
	for _, g := range strings.Split(string(buf), "\n\n") {
		sl := strings.SplitN(g, "\n", 2)
		if len(sl) != 2 {
			continue
		}
		stack := strings.TrimSpace(sl[1])
		if stack == "" ||
			strings.Contains(stack, "created by os/signal.init") ||
			strings.Contains(stack, "runtime/panic.go") ||
			strings.Contains(stack, "created by testing.RunTests") ||
			strings.Contains(stack, "testing.Main(") ||
			strings.Contains(stack, "runtime.goexit") ||
			strings.Contains(stack, "github.com/coreos/etcd/pkg/testutil.interestingGoroutines") ||
			strings.Contains(stack, "github.com/coreos/etcd/pkg/logutil.(*MergeLogger).outputLoop") ||
			strings.Contains(stack, "github.com/golang/glog.(*loggingT).flushDaemon") ||
			strings.Contains(stack, "created by runtime.gc") ||
			strings.Contains(stack, "runtime.MHeap_Scavenger") {
			continue
		}
		gs = append(gs, stack)
	}
	sort.Strings(gs)
	return
}
<|endoftext|>


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

// The srv package provides definitions and functions used to implement
// a 9P2000 file client.
package clnt

import "fmt"
import "log"
import "net"
import "plan9/p"
import "sync"
import "syscall"

// The Clnt type represents a 9P2000 client. The client is connected to
// a 9P2000 file server and its methods can be used to access and manipulate
// the files exported by the server.
type Clnt struct {
	sync.Mutex;
	Finished	bool;	// client is no longer connected to server
	Debuglevel	int;	// =0 don't print anything, >0 print Fcalls, >1 print raw packets
	Msize		uint32;	// Maximum size of the 9P messages
	Dotu		bool;	// If true, 9P2000.u protocol is spoken
	Root		*Fid;	// Fid that points to the rood directory

	conn		net.Conn;
	tagpool		*pool;
	fidpool		*pool;
	reqout		chan *req;
	done		chan bool;
	reqfirst	*req;
	reqlast		*req;
	err		*p.Error;
}

// A Fid type represents a file on the server. Fids are used for the
// low level methods that correspond directly to the 9P2000 message requests
type Fid struct {
	sync.Mutex;
	Clnt	*Clnt;	// Client the fid belongs to
	Iounit	uint32;
	p.Qid;		// The Qid description for the file
	Mode	uint8;	// Open mode (one of p.O* values) (if file is open)
	Fid	uint32;	// Fid number
	p.User;		// The user the fid belongs to
}

// The file is similar to the Fid, but is used in the high-level client
// interface.
type File struct {
	fid	*Fid;
	offset	uint64;
}

type pool struct {
	sync.Mutex;
	need	int;
	nchan	chan uint32;
	maxid	uint32;
	imap	[]byte;
}

type req struct {
	sync.Mutex;
	clnt		*Clnt;
	tc		*p.Fcall;
	rc		*p.Fcall;
	err		*p.Error;
	done		chan *req;
	prev, next	*req;
}

func (clnt *Clnt) rpcnb(r *req) *p.Error {
	var tag uint16;

	if clnt.Finished {
		return &p.Error{"Client no longer connected", 0}
	}
	if r.tc.Type == p.Tversion {
		tag = p.NOTAG
	} else {
		tag = uint16(clnt.tagpool.getId())
	}

	p.SetTag(r.tc, tag);
	clnt.Lock();
	if clnt.err != nil {
		clnt.Unlock();
		return clnt.err;
	}

	if clnt.reqlast != nil {
		clnt.reqlast.next = r
	} else {
		clnt.reqfirst = r
	}

	r.prev = clnt.reqlast;
	clnt.reqlast = r;
	clnt.Unlock();

	clnt.reqout <- r;
	return nil;
}

func (clnt *Clnt) rpc(tc *p.Fcall) (*p.Fcall, *p.Error) {
	r := new(req);
	r.tc = tc;
	r.done = make(chan *req);
	err := clnt.rpcnb(r);
	if err != nil {
		return nil, err
	}

	<-r.done;
	return r.rc, r.err;
}

func (clnt *Clnt) recv() {
	var err *p.Error;

	buf := make([]byte, clnt.Msize);
	pos := 0;
	for {
		print("recv\n")
		if len(buf) < int(clnt.Msize) {
			b := make([]byte, clnt.Msize);
			copy(b, buf[0:pos]);
			buf = b;
		}

		n, oerr := clnt.conn.Read(buf[pos:len(buf)]);
		if oerr != nil || n == 0 {
			err = &p.Error{oerr.String(), syscall.EIO};
			goto closed;
		}

		pos += n;
		for pos > 4 {
			sz, _ := p.Gint32(buf);
			if pos < int(sz) {
				break
			}

			fc, err, fcsize := p.Unpack(buf, clnt.Dotu);
			clnt.Lock();
			if err != nil {
				clnt.err = err;
				clnt.conn.Close();
				clnt.Unlock();
				goto closed;
			}

			if clnt.Debuglevel > 0 {
				if clnt.Debuglevel > 1 {
					log.Stderr("}-} " + fmt.Sprint(fc.Pkt))
				}

				log.Stderr("}}} " + fc.String());
			}

			var r *req = nil;
			for r = clnt.reqfirst; r != nil; r = r.next {
				if r.tc.Tag == fc.Tag {
					break
				}
			}

			if r == nil {
				clnt.err = &p.Error{"unexpected response", syscall.EINVAL};
				clnt.conn.Close();
				clnt.Unlock();
				goto closed;
			}

			r.rc = fc;
			if r.prev != nil {
				r.prev.next = r.next
			} else {
				clnt.reqfirst = r.next
			}

			if r.next != nil {
				r.next.prev = r.prev
			} else {
				clnt.reqlast = r.prev
			}
			clnt.Unlock();

			if r.tc.Type != r.rc.Type-1 {
				if r.rc.Type != p.Rerror {
					r.err = &p.Error{"invalid response id", syscall.EINVAL}
				} else {
					if r.err != nil {
						r.err = &p.Error{r.rc.Error, int(r.rc.Errornum)}
					}
				}
			}

			if r.tc.Tag != p.NOTAG {
				clnt.tagpool.putId(uint32(r.tc.Tag))
			}

			if r.done != nil {
				r.done <- r
			}

			pos -= fcsize;
			buf = buf[0:fcsize];
		}
	}

closed:
	clnt.done <- true;

	/* send error to all pending requests */
	clnt.Lock();
	r := clnt.reqfirst;
	clnt.reqfirst = nil;
	clnt.reqlast = nil;
	err = clnt.err;
	clnt.Unlock();
	for ; r != nil; r = r.next {
		r.err = err;
		if r.done != nil {
			r.done <- r
		}
	}
}

func (clnt *Clnt) send() {
	for {
		select {
		case <-clnt.done:
			clnt.Finished = true;
			return

		case req := <-clnt.reqout:
			if clnt.Debuglevel > 0 {
				if clnt.Debuglevel > 1 {
					log.Stderr("{-{ " + fmt.Sprint(req.tc.Pkt))
				}

				log.Stderr("{{{ " + req.tc.String());
			}

			for buf := req.tc.Pkt; len(buf) > 0; {
				n, err := clnt.conn.Write(buf);
				if err != nil {
					/* just close the socket, will get signal on clnt.done */
					clnt.conn.Close();
					break;
				}

				buf = buf[n:len(buf)];
			}
		}
	}
}

// Creates and initializes a new Clnt object. Doesn't send any data
// on the wire.
func NewClnt(c net.Conn, msize uint32, dotu bool) *Clnt {
	clnt := new(Clnt);
	clnt.conn = c;
	clnt.Msize = msize;
	clnt.Dotu = dotu;
	clnt.tagpool = newPool(uint32(p.NOTAG));
	clnt.fidpool = newPool(p.NOFID);
	clnt.reqout = make(chan *req);
	clnt.done = make(chan bool);

	go clnt.recv();
	go clnt.send();

	return clnt;
}

// Establishes a new socket connection to the 9P server and creates
// a client object for it. Negotiates the dialect and msize for the
// connection. Returns a Clnt object, or Error.
func Connect(ntype, addr string, msize uint32, dotu bool) (*Clnt, *p.Error) {
	c, e := net.Dial(ntype, "", addr);
	if e != nil {
		return nil, &p.Error{e.String(), syscall.EIO}
	}

	clnt := NewClnt(c, msize, dotu);
	ver := "9P2000";
	if clnt.Dotu {
		ver = "9P2000.u"
	}

	tc := p.NewFcall(clnt.Msize);
	err := p.PackTversion(tc, clnt.Msize, ver);
	if err != nil {
		return nil, err
	}

	rc, err := clnt.rpc(tc);
	if err != nil {
		return nil, err
	}

	if rc.Msize < clnt.Msize {
		clnt.Msize = rc.Msize
	}

	clnt.Dotu = rc.Version == "9P2000.u" && clnt.Dotu;
	return clnt, nil;
}

// Creates a new Fid object for the client
func (clnt *Clnt) FidAlloc() *Fid {
	fid := new(Fid);
	fid.Fid = clnt.fidpool.getId();
	fid.Clnt = clnt;

	return fid;
}
<commit_msg>oops, debugging print was left in<commit_after>// Copyright 2009 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// The srv package provides definitions and functions used to implement
// a 9P2000 file client.
package clnt

import "fmt"
import "log"
import "net"
import "plan9/p"
import "sync"
import "syscall"

// The Clnt type represents a 9P2000 client. The client is connected to
// a 9P2000 file server and its methods can be used to access and manipulate
// the files exported by the server.
type Clnt struct {
	sync.Mutex;
	Finished	bool;	// client is no longer connected to server
	Debuglevel	int;	// =0 don't print anything, >0 print Fcalls, >1 print raw packets
	Msize		uint32;	// Maximum size of the 9P messages
	Dotu		bool;	// If true, 9P2000.u protocol is spoken
	Root		*Fid;	// Fid that points to the rood directory

	conn		net.Conn;
	tagpool		*pool;
	fidpool		*pool;
	reqout		chan *req;
	done		chan bool;
	reqfirst	*req;
	reqlast		*req;
	err		*p.Error;
}

// A Fid type represents a file on the server. Fids are used for the
// low level methods that correspond directly to the 9P2000 message requests
type Fid struct {
	sync.Mutex;
	Clnt	*Clnt;	// Client the fid belongs to
	Iounit	uint32;
	p.Qid;		// The Qid description for the file
	Mode	uint8;	// Open mode (one of p.O* values) (if file is open)
	Fid	uint32;	// Fid number
	p.User;		// The user the fid belongs to
}

// The file is similar to the Fid, but is used in the high-level client
// interface.
type File struct {
	fid	*Fid;
	offset	uint64;
}

type pool struct {
	sync.Mutex;
	need	int;
	nchan	chan uint32;
	maxid	uint32;
	imap	[]byte;
}

type req struct {
	sync.Mutex;
	clnt		*Clnt;
	tc		*p.Fcall;
	rc		*p.Fcall;
	err		*p.Error;
	done		chan *req;
	prev, next	*req;
}

func (clnt *Clnt) rpcnb(r *req) *p.Error {
	var tag uint16;

	if clnt.Finished {
		return &p.Error{"Client no longer connected", 0}
	}
	if r.tc.Type == p.Tversion {
		tag = p.NOTAG
	} else {
		tag = uint16(clnt.tagpool.getId())
	}

	p.SetTag(r.tc, tag);
	clnt.Lock();
	if clnt.err != nil {
		clnt.Unlock();
		return clnt.err;
	}

	if clnt.reqlast != nil {
		clnt.reqlast.next = r
	} else {
		clnt.reqfirst = r
	}

	r.prev = clnt.reqlast;
	clnt.reqlast = r;
	clnt.Unlock();

	clnt.reqout <- r;
	return nil;
}

func (clnt *Clnt) rpc(tc *p.Fcall) (*p.Fcall, *p.Error) {
	r := new(req);
	r.tc = tc;
	r.done = make(chan *req);
	err := clnt.rpcnb(r);
	if err != nil {
		return nil, err
	}

	<-r.done;
	return r.rc, r.err;
}

func (clnt *Clnt) recv() {
	var err *p.Error;

	buf := make([]byte, clnt.Msize);
	pos := 0;
	for {
		if len(buf) < int(clnt.Msize) {
			b := make([]byte, clnt.Msize);
			copy(b, buf[0:pos]);
			buf = b;
		}

		n, oerr := clnt.conn.Read(buf[pos:len(buf)]);
		if oerr != nil || n == 0 {
			err = &p.Error{oerr.String(), syscall.EIO};
			goto closed;
		}

		pos += n;
		for pos > 4 {
			sz, _ := p.Gint32(buf);
			if pos < int(sz) {
				break
			}

			fc, err, fcsize := p.Unpack(buf, clnt.Dotu);
			clnt.Lock();
			if err != nil {
				clnt.err = err;
				clnt.conn.Close();
				clnt.Unlock();
				goto closed;
			}

			if clnt.Debuglevel > 0 {
				if clnt.Debuglevel > 1 {
					log.Stderr("}-} " + fmt.Sprint(fc.Pkt))
				}

				log.Stderr("}}} " + fc.String());
			}

			var r *req = nil;
			for r = clnt.reqfirst; r != nil; r = r.next {
				if r.tc.Tag == fc.Tag {
					break
				}
			}

			if r == nil {
				clnt.err = &p.Error{"unexpected response", syscall.EINVAL};
				clnt.conn.Close();
				clnt.Unlock();
				goto closed;
			}

			r.rc = fc;
			if r.prev != nil {
				r.prev.next = r.next
			} else {
				clnt.reqfirst = r.next
			}

			if r.next != nil {
				r.next.prev = r.prev
			} else {
				clnt.reqlast = r.prev
			}
			clnt.Unlock();

			if r.tc.Type != r.rc.Type-1 {
				if r.rc.Type != p.Rerror {
					r.err = &p.Error{"invalid response id", syscall.EINVAL}
				} else {
					if r.err != nil {
						r.err = &p.Error{r.rc.Error, int(r.rc.Errornum)}
					}
				}
			}

			if r.tc.Tag != p.NOTAG {
				clnt.tagpool.putId(uint32(r.tc.Tag))
			}

			if r.done != nil {
				r.done <- r
			}

			pos -= fcsize;
			buf = buf[0:fcsize];
		}
	}

closed:
	clnt.done <- true;

	/* send error to all pending requests */
	clnt.Lock();
	r := clnt.reqfirst;
	clnt.reqfirst = nil;
	clnt.reqlast = nil;
	err = clnt.err;
	clnt.Unlock();
	for ; r != nil; r = r.next {
		r.err = err;
		if r.done != nil {
			r.done <- r
		}
	}
}

func (clnt *Clnt) send() {
	for {
		select {
		case <-clnt.done:
			clnt.Finished = true;
			return

		case req := <-clnt.reqout:
			if clnt.Debuglevel > 0 {
				if clnt.Debuglevel > 1 {
					log.Stderr("{-{ " + fmt.Sprint(req.tc.Pkt))
				}

				log.Stderr("{{{ " + req.tc.String());
			}

			for buf := req.tc.Pkt; len(buf) > 0; {
				n, err := clnt.conn.Write(buf);
				if err != nil {
					/* just close the socket, will get signal on clnt.done */
					clnt.conn.Close();
					break;
				}

				buf = buf[n:len(buf)];
			}
		}
	}
}

// Creates and initializes a new Clnt object. Doesn't send any data
// on the wire.
func NewClnt(c net.Conn, msize uint32, dotu bool) *Clnt {
	clnt := new(Clnt);
	clnt.conn = c;
	clnt.Msize = msize;
	clnt.Dotu = dotu;
	clnt.tagpool = newPool(uint32(p.NOTAG));
	clnt.fidpool = newPool(p.NOFID);
	clnt.reqout = make(chan *req);
	clnt.done = make(chan bool);

	go clnt.recv();
	go clnt.send();

	return clnt;
}

// Establishes a new socket connection to the 9P server and creates
// a client object for it. Negotiates the dialect and msize for the
// connection. Returns a Clnt object, or Error.
func Connect(ntype, addr string, msize uint32, dotu bool) (*Clnt, *p.Error) {
	c, e := net.Dial(ntype, "", addr);
	if e != nil {
		return nil, &p.Error{e.String(), syscall.EIO}
	}

	clnt := NewClnt(c, msize, dotu);
	ver := "9P2000";
	if clnt.Dotu {
		ver = "9P2000.u"
	}

	tc := p.NewFcall(clnt.Msize);
	err := p.PackTversion(tc, clnt.Msize, ver);
	if err != nil {
		return nil, err
	}

	rc, err := clnt.rpc(tc);
	if err != nil {
		return nil, err
	}

	if rc.Msize < clnt.Msize {
		clnt.Msize = rc.Msize
	}

	clnt.Dotu = rc.Version == "9P2000.u" && clnt.Dotu;
	return clnt, nil;
}

// Creates a new Fid object for the client
func (clnt *Clnt) FidAlloc() *Fid {
	fid := new(Fid);
	fid.Fid = clnt.fidpool.getId();
	fid.Clnt = clnt;

	return fid;
}
<|endoftext|>


<commit_before>// vSphere template
// The purpose of this is to abstract away all the values in a vSphere Salt Profile to be used
// elsewhere in pepper.

package vsphere

import (
	"os"
	"path/filepath"
	"text/template"

	"github.com/nextgearcapital/pepper/pkg/log"

	"github.com/spf13/viper"
)

// ProfileConfig :
type ProfileConfig struct {
	Platform     string   `mapstructure:"platform"`
	Environment  string   `mapstructure:"environment"`
	InstanceType string   `mapstructure:"instance_type"`
	Role         string   `mapstructure:"role"`
	Datacenter   string   `mapstructure:"datacenter"`
	Provider     string   `mapstructure:"provider"`
	Template     string   `mapstructure:"template"`
	CPU          int      `mapstructure:"cpu"`
	Memory       int      `mapstructure:"memory"`
	DiskSize     int      `mapstructure:"disksize"`
	DHCP         bool     `mapstructure:"dhcp"`
	Network      string   `mapstructure:"network"`
	IPAddress    string   `mapstructure:"ip_address"`
	Gateway      string   `mapstructure:"gateway"`
	Subnet       string   `mapstructure:"subnet"`
	Domain       string   `mapstructure:"domain"`
	DNSServers   []string `mapstructure:"dns_servers"`
	Cluster      string   `mapstructure:"cluster"`
	Folder       string   `mapstructure:"folder"`
	Datastore    string   `mapstructure:"datastore"`
	IsCoreOS     bool     `mapstructure:"is_coreos"`
	ConfigData   string   `mapstructure:"config_data"`
}

var (
	configPath   = "/etc/pepper/config.d/vmware"
	saltProfiles = "/etc/salt/cloud.profiles.d"
)

var (
	// Platform :
	Platform string
	// Environment :
	Environment string
	// InstanceType :
	InstanceType string
	// Template :
	Template string
	// IPAddress :
	IPAddress string
	// Role :
	Role string
	// Datacenter :
	Datacenter string
	// CPU :
	CPU int
	// Memory :
	Memory int
	// DiskSize :
	DiskSize int
)

// Prepare :
func (profile *ProfileConfig) Prepare() error {
	if err := os.MkdirAll(configPath, 0644); err != nil {
		return err
	}

	profile.Platform = Platform
	profile.Environment = Environment
	profile.InstanceType = InstanceType
	profile.Template = Template
	profile.IPAddress = IPAddress
	profile.Role = Role
	profile.Datacenter = Datacenter

	switch InstanceType {
	case "nano":
		profile.CPU = 1
		profile.Memory = 512
		profile.DiskSize = 20
	case "micro":
		profile.CPU = 1
		profile.Memory = 1024
		profile.DiskSize = 20
	case "small":
		profile.CPU = 1
		profile.Memory = 2048
		profile.DiskSize = 40
	case "medium":
		profile.CPU = 2
		profile.Memory = 4096
		profile.DiskSize = 60
	case "large":
		profile.CPU = 2
		profile.Memory = 8192
		profile.DiskSize = 80
	case "xlarge":
		profile.CPU = 4
		profile.Memory = 16384
		profile.DiskSize = 100
	case "ultra":
		profile.CPU = 8
		profile.Memory = 32768
		profile.DiskSize = 160
	case "mega":
		profile.CPU = 16
		profile.Memory = 65536
		profile.DiskSize = 200
	}

	viper.SetConfigName(Environment)
	viper.SetConfigType("yaml")
	viper.AddConfigPath(configPath)

	if err := viper.ReadInConfig(); err != nil {
		log.Err("Couldn't read the config! Did you put it in /etc/pepper/config.d/vmware?")
		return err
	}

	if err := viper.Unmarshal(profile); err != nil {
		return err
	}
	return nil
}

// Generate :
func (profile *ProfileConfig) Generate() error {
	compiled, err := template.New("vsphere_profile").Parse(vsphereTemplate)
	if err != nil {
		return err
	}

	profileName := profile.Platform + "-" + profile.Environment + "-" + profile.InstanceType + ".conf"
	f, err := os.OpenFile(filepath.Join(saltProfiles, profileName), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
	if err != nil {
		return err
	}

	if err := compiled.Execute(f, profile); err != nil {
		return err
	}
	return nil
}

const vsphereTemplate = `
{{- .Platform}}-{{.Environment}}-{{.InstanceType}}:
  provider: {{.Provider}}
  clonefrom: {{.Template}}
  num_cpus: {{.CPU}}
  memory: {{.Memory}}
  devices:
    cd:
      CD/DVD drive 1:
        device_type: client_device
        mode: atapi
    disk:
      Hard disk 1:
        size: {{.DiskSize}}
    network:
      Network adapter 1:
        name: {{.Network}}
        adapter_type: vmxnet3
        switch_type: distributed
		{{- if eq .DHCP false}}
        ip: {{.IPAddress}}
        gateway: {{.Gateway}}
        subnet_mask: {{.Subnet}}
	    {{- end}}
    scsi:
      SCSI controller 0:
        type: lsilogic
  {{- if .Domain}}
  domain: {{.Domain}}
  {{- end}}
  {{- if .DNSServers}}
  dns_servers:
  {{- range $value := .DNSServers}}
  - {{$value}}
  {{- end}}
  {{- end}}
  cluster: {{.Cluster}}
  folder: {{.Folder}}
  datastore: {{.Datastore}}
  template: False
  power_on: True
  deploy: False
  minion:
    grains:
      env: {{.Environment}}
      {{- if .Datacenter}}
      datacenter: {{.Datacenter}}
      {{- end}}
      {{- if .Role}}
      role: {{.Role}}
      {{- end}}
  extra_config:
    cpu.hotadd: 'yes'
    mem.hotadd: 'yes'
	{{- if .IsCoreOS}}
	guestinfo.coreos.config.data: {{.ConfigData}}
	guestinfo.coreos.config.data.encoding: base64
	{{- end}}
`
<commit_msg>More stupid tabs<commit_after>// vSphere template
// The purpose of this is to abstract away all the values in a vSphere Salt Profile to be used
// elsewhere in pepper.

package vsphere

import (
	"os"
	"path/filepath"
	"text/template"

	"github.com/nextgearcapital/pepper/pkg/log"

	"github.com/spf13/viper"
)

// ProfileConfig :
type ProfileConfig struct {
	Platform     string   `mapstructure:"platform"`
	Environment  string   `mapstructure:"environment"`
	InstanceType string   `mapstructure:"instance_type"`
	Role         string   `mapstructure:"role"`
	Datacenter   string   `mapstructure:"datacenter"`
	Provider     string   `mapstructure:"provider"`
	Template     string   `mapstructure:"template"`
	CPU          int      `mapstructure:"cpu"`
	Memory       int      `mapstructure:"memory"`
	DiskSize     int      `mapstructure:"disksize"`
	DHCP         bool     `mapstructure:"dhcp"`
	Network      string   `mapstructure:"network"`
	IPAddress    string   `mapstructure:"ip_address"`
	Gateway      string   `mapstructure:"gateway"`
	Subnet       string   `mapstructure:"subnet"`
	Domain       string   `mapstructure:"domain"`
	DNSServers   []string `mapstructure:"dns_servers"`
	Cluster      string   `mapstructure:"cluster"`
	Folder       string   `mapstructure:"folder"`
	Datastore    string   `mapstructure:"datastore"`
	IsCoreOS     bool     `mapstructure:"is_coreos"`
	ConfigData   string   `mapstructure:"config_data"`
}

var (
	configPath   = "/etc/pepper/config.d/vmware"
	saltProfiles = "/etc/salt/cloud.profiles.d"
)

var (
	// Platform :
	Platform string
	// Environment :
	Environment string
	// InstanceType :
	InstanceType string
	// Template :
	Template string
	// IPAddress :
	IPAddress string
	// Role :
	Role string
	// Datacenter :
	Datacenter string
	// CPU :
	CPU int
	// Memory :
	Memory int
	// DiskSize :
	DiskSize int
)

// Prepare :
func (profile *ProfileConfig) Prepare() error {
	if err := os.MkdirAll(configPath, 0644); err != nil {
		return err
	}

	profile.Platform = Platform
	profile.Environment = Environment
	profile.InstanceType = InstanceType
	profile.Template = Template
	profile.IPAddress = IPAddress
	profile.Role = Role
	profile.Datacenter = Datacenter

	switch InstanceType {
	case "nano":
		profile.CPU = 1
		profile.Memory = 512
		profile.DiskSize = 20
	case "micro":
		profile.CPU = 1
		profile.Memory = 1024
		profile.DiskSize = 20
	case "small":
		profile.CPU = 1
		profile.Memory = 2048
		profile.DiskSize = 40
	case "medium":
		profile.CPU = 2
		profile.Memory = 4096
		profile.DiskSize = 60
	case "large":
		profile.CPU = 2
		profile.Memory = 8192
		profile.DiskSize = 80
	case "xlarge":
		profile.CPU = 4
		profile.Memory = 16384
		profile.DiskSize = 100
	case "ultra":
		profile.CPU = 8
		profile.Memory = 32768
		profile.DiskSize = 160
	case "mega":
		profile.CPU = 16
		profile.Memory = 65536
		profile.DiskSize = 200
	}

	viper.SetConfigName(Environment)
	viper.SetConfigType("yaml")
	viper.AddConfigPath(configPath)

	if err := viper.ReadInConfig(); err != nil {
		log.Err("Couldn't read the config! Did you put it in /etc/pepper/config.d/vmware?")
		return err
	}

	if err := viper.Unmarshal(profile); err != nil {
		return err
	}
	return nil
}

// Generate :
func (profile *ProfileConfig) Generate() error {
	compiled, err := template.New("vsphere_profile").Parse(vsphereTemplate)
	if err != nil {
		return err
	}

	profileName := profile.Platform + "-" + profile.Environment + "-" + profile.InstanceType + ".conf"
	f, err := os.OpenFile(filepath.Join(saltProfiles, profileName), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
	if err != nil {
		return err
	}

	if err := compiled.Execute(f, profile); err != nil {
		return err
	}
	return nil
}

const vsphereTemplate = `
{{- .Platform}}-{{.Environment}}-{{.InstanceType}}:
  provider: {{.Provider}}
  clonefrom: {{.Template}}
  num_cpus: {{.CPU}}
  memory: {{.Memory}}
  devices:
    cd:
      CD/DVD drive 1:
        device_type: client_device
        mode: atapi
    disk:
      Hard disk 1:
        size: {{.DiskSize}}
    network:
      Network adapter 1:
        name: {{.Network}}
        adapter_type: vmxnet3
        switch_type: distributed
        {{- if eq .DHCP false}}
        ip: {{.IPAddress}}
        gateway: {{.Gateway}}
        subnet_mask: {{.Subnet}}
        {{- end}}
    scsi:
      SCSI controller 0:
        type: lsilogic
  {{- if .Domain}}
  domain: {{.Domain}}
  {{- end}}
  {{- if .DNSServers}}
  dns_servers:
  {{- range $value := .DNSServers}}
  - {{$value}}
  {{- end}}
  {{- end}}
  cluster: {{.Cluster}}
  folder: {{.Folder}}
  datastore: {{.Datastore}}
  template: False
  power_on: True
  deploy: False
  minion:
    grains:
      env: {{.Environment}}
      {{- if .Datacenter}}
      datacenter: {{.Datacenter}}
      {{- end}}
      {{- if .Role}}
      role: {{.Role}}
      {{- end}}
  extra_config:
    cpu.hotadd: 'yes'
    mem.hotadd: 'yes'
    {{- if .IsCoreOS}}
    guestinfo.coreos.config.data: {{.ConfigData}}
    guestinfo.coreos.config.data.encoding: base64
    {{- end}}
`
<|endoftext|>


<commit_before>package main

import (
	"encoding/base64"
	"encoding/json"
	"flag"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/zenazn/goji"
	"github.com/zenazn/goji/web"
)

const fifo string = "omxcontrol"

var commands *strings.Replacer = strings.NewReplacer(
	"play", "p",
	"pause", "p",
	"subs", "m",
	"quit", "q",
)

var videosPath string

type Page struct {
	Title string
}

type Video struct {
	File string `json:"file"`
	Hash string `json:"hash"`
}

func home(c web.C, w http.ResponseWriter, r *http.Request) {
	p := &Page{Title: "gomxremote"}
	tmpl, err := FSString(false, "/views/index.html")
	if err != nil {
		fmt.Println(err.Error())
	}

	t, _ := template.New("index").Parse(tmpl)
	t.Execute(w, p)
}

func videoFiles(c web.C, w http.ResponseWriter, r *http.Request) {
	var files []*Video
	var root = videosPath
	_ = filepath.Walk(root, func(path string, f os.FileInfo, _ error) error {
		if f.IsDir() == false {
			if filepath.Ext(path) == ".mkv" || filepath.Ext(path) == ".mp4" || filepath.Ext(path) == ".avi" {
				files = append(files, &Video{File: filepath.Base(path), Hash: base64.StdEncoding.EncodeToString([]byte(path))})
			}
		}
		return nil
	})
	encoder := json.NewEncoder(w)
	encoder.Encode(files)
}

func startVideo(c web.C, w http.ResponseWriter, r *http.Request) {
	filename, _ := base64.StdEncoding.DecodeString(c.URLParams["name"])
	string_filename := string(filename[:])
	escapePathReplacer := strings.NewReplacer(
		"[", "\\[",
		"]", "\\]",
		"(", "\\(",
		")", "\\)",
		"'", "\\'",
		" ", "\\ ",
		"*", "\\*",
		"?", "\\?",
	)
	escapedPath := escapePathReplacer.Replace(string_filename)

	fifo_cmd := exec.Command("mkfifo", fifo)
	fifo_cmd.Run()

	cmd := exec.Command("bash", "-c", "omxplayer -o hdmi "+escapedPath+" < "+fifo)
	err := cmd.Start()
	if err != nil {
		log.Fatal(err)
	}

	startErr := exec.Command("bash", "-c", "echo . > "+fifo).Run()
	if startErr != nil {
		http.Error(w, err.Error(), 500)
		return
	}
	err = cmd.Wait()

	fmt.Fprintf(w, "%s", string_filename)
}

func togglePlayVideo(c web.C, w http.ResponseWriter, r *http.Request) {

	err := sendCommand("play")
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}
	fmt.Fprintf(w, "1")
}

func stopVideo(c web.C, w http.ResponseWriter, r *http.Request) {

	err := sendCommand("quit")
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}
	os.Remove(fifo)

	fmt.Fprintf(w, "1")
}

func sendCommand(command string) error {
	commandString := "echo -n " + commands.Replace(command) + " > " + fifo
	cmd := exec.Command("bash", "-c", commandString)
	err := cmd.Run()
	return err
}

func main() {

	flag.StringVar(&videosPath, "media", ".", "path to look for videos in")

	goji.Get("/", home)
	goji.Get("/files", videoFiles)

	goji.Post("/file/:name/start", startVideo)
	goji.Post("/file/:name/play", togglePlayVideo)
	goji.Post("/file/:name/pause", togglePlayVideo)
	goji.Post("/file/:name/stop", stopVideo)

	goji.Handle("/assets/*", http.FileServer(FS(false)))

	goji.Serve()
}
<commit_msg>next sub toggle probably works<commit_after>package main

import (
	"encoding/base64"
	"encoding/json"
	"flag"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/zenazn/goji"
	"github.com/zenazn/goji/web"
)

const fifo string = "omxcontrol"

var commands *strings.Replacer = strings.NewReplacer(
	"play", "p",
	"pause", "p",
	"subs", "m",
	"quit", "q",
)

var videosPath string

type Page struct {
	Title string
}

type Video struct {
	File string `json:"file"`
	Hash string `json:"hash"`
}

func home(c web.C, w http.ResponseWriter, r *http.Request) {
	p := &Page{Title: "gomxremote"}
	tmpl, err := FSString(false, "/views/index.html")
	if err != nil {
		fmt.Println(err.Error())
	}

	t, _ := template.New("index").Parse(tmpl)
	t.Execute(w, p)
}

func videoFiles(c web.C, w http.ResponseWriter, r *http.Request) {
	var files []*Video
	var root = videosPath
	_ = filepath.Walk(root, func(path string, f os.FileInfo, _ error) error {
		if f.IsDir() == false {
			if filepath.Ext(path) == ".mkv" || filepath.Ext(path) == ".mp4" || filepath.Ext(path) == ".avi" {
				files = append(files, &Video{File: filepath.Base(path), Hash: base64.StdEncoding.EncodeToString([]byte(path))})
			}
		}
		return nil
	})
	encoder := json.NewEncoder(w)
	encoder.Encode(files)
}

func startVideo(c web.C, w http.ResponseWriter, r *http.Request) {
	filename, _ := base64.StdEncoding.DecodeString(c.URLParams["name"])
	string_filename := string(filename[:])
	escapePathReplacer := strings.NewReplacer(
		"[", "\\[",
		"]", "\\]",
		"(", "\\(",
		")", "\\)",
		"'", "\\'",
		" ", "\\ ",
		"*", "\\*",
		"?", "\\?",
	)
	escapedPath := escapePathReplacer.Replace(string_filename)

	fifo_cmd := exec.Command("mkfifo", fifo)
	fifo_cmd.Run()

	cmd := exec.Command("bash", "-c", "omxplayer -o hdmi "+escapedPath+" < "+fifo)
	err := cmd.Start()
	if err != nil {
		log.Fatal(err)
	}

	startErr := exec.Command("bash", "-c", "echo . > "+fifo).Run()
	if startErr != nil {
		http.Error(w, err.Error(), 500)
		return
	}
	err = cmd.Wait()

	fmt.Fprintf(w, "%s", string_filename)
}

func togglePlayVideo(c web.C, w http.ResponseWriter, r *http.Request) {

	err := sendCommand("play")
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}
	fmt.Fprintf(w, "1")
}

func stopVideo(c web.C, w http.ResponseWriter, r *http.Request) {

	err := sendCommand("quit")
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}
	os.Remove(fifo)

	fmt.Fprintf(w, "1")
}

func toggleSubsVideo(c web.C, w http.ResponseWriter, r *http.Request) {

	err := sendCommand("subs")
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}
	fmt.Fprintf(w, "1")
}

func sendCommand(command string) error {
	commandString := "echo -n " + commands.Replace(command) + " > " + fifo
	cmd := exec.Command("bash", "-c", commandString)
	err := cmd.Run()
	return err
}

func main() {

	flag.StringVar(&videosPath, "media", ".", "path to look for videos in")

	goji.Get("/", home)
	goji.Get("/files", videoFiles)

	goji.Post("/file/:name/start", startVideo)
	goji.Post("/file/:name/play", togglePlayVideo)
	goji.Post("/file/:name/pause", togglePlayVideo)
	goji.Post("/file/:name/stop", stopVideo)
	goji.Post("/file/:name/subs", toggleSubsVideo)

	goji.Handle("/assets/*", http.FileServer(FS(false)))

	goji.Serve()
}
<|endoftext|>


<commit_before>package util

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"time"

	// Below is a port of the exponential backoff implementation from
	// google-http-java-client.
	"github.com/cenkalti/backoff"
	"github.com/skia-dev/glog"
)

const (
	// TIMEOUT is the http timeout when making requests.
	TIMEOUT = time.Duration(time.Minute)

	// Exponential backoff defaults.
	INITIAL_INTERVAL     = 500 * time.Millisecond
	RANDOMIZATION_FACTOR = 0.5
	BACKOFF_MULTIPLIER   = 1.5
	MAX_INTERVAL         = 60 * time.Second
	MAX_ELAPSED_TIME     = 5 * time.Minute
)

// DialTimeout is a dialer that sets a timeout.
func DialTimeout(network, addr string) (net.Conn, error) {
	return net.DialTimeout(network, addr, TIMEOUT)
}

// NewTimeoutClient creates a new http.Client with a timeout.
func NewTimeoutClient() *http.Client {
	return &http.Client{
		Transport: &http.Transport{
			Dial: DialTimeout,
		},
	}
}

type BackOffConfig struct {
	initialInterval     time.Duration
	maxInterval         time.Duration
	maxElapsedTime      time.Duration
	randomizationFactor float64
	backOffMultiplier   float64
}

// NewBackOffTransport creates a BackOffTransport with default values. Look at
// NewConfiguredBackOffTransport for an example of how the values impact behavior.
func NewBackOffTransport() http.RoundTripper {
	config := &BackOffConfig{
		initialInterval:     INITIAL_INTERVAL,
		maxInterval:         MAX_INTERVAL,
		maxElapsedTime:      MAX_ELAPSED_TIME,
		randomizationFactor: RANDOMIZATION_FACTOR,
		backOffMultiplier:   BACKOFF_MULTIPLIER,
	}
	return NewConfiguredBackOffTransport(config)
}

type BackOffTransport struct {
	http.Transport
	backOffConfig *BackOffConfig
}

// NewBackOffTransport creates a BackOffTransport with the specified config.
//
// Example: The default retry_interval is .5 seconds, default randomization_factor
// is 0.5, default multiplier is 1.5 and the default max_interval is 1 minute. For
// 10 tries the sequence will be (values in seconds) and assuming we go over the
// max_elapsed_time on the 10th try:
//
//  request#     retry_interval     randomized_interval
//  1             0.5                [0.25,   0.75]
//  2             0.75               [0.375,  1.125]
//  3             1.125              [0.562,  1.687]
//  4             1.687              [0.8435, 2.53]
//  5             2.53               [1.265,  3.795]
//  6             3.795              [1.897,  5.692]
//  7             5.692              [2.846,  8.538]
//  8             8.538              [4.269, 12.807]
//  9            12.807              [6.403, 19.210]
//  10           19.210              backoff.Stop
func NewConfiguredBackOffTransport(config *BackOffConfig) http.RoundTripper {
	return &BackOffTransport{
		Transport:     http.Transport{Dial: DialTimeout},
		backOffConfig: config,
	}
}

// RoundTrip implements the RoundTripper interface.
func (t *BackOffTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	// Initialize the exponential backoff client.
	backOffClient := &backoff.ExponentialBackOff{
		InitialInterval:     t.backOffConfig.initialInterval,
		RandomizationFactor: t.backOffConfig.randomizationFactor,
		Multiplier:          t.backOffConfig.backOffMultiplier,
		MaxInterval:         t.backOffConfig.maxInterval,
		MaxElapsedTime:      t.backOffConfig.maxElapsedTime,
		Clock:               backoff.SystemClock,
	}
	// Make a copy of the request's Body so that we can reuse it if the request
	// needs to be backed off and retried.
	bodyBuf := bytes.Buffer{}
	if req.Body != nil {
		bodyBuf.ReadFrom(req.Body)
	}

	var resp *http.Response
	var err error
	roundTripOp := func() error {
		if req.Body != nil {
			req.Body = ioutil.NopCloser(bytes.NewBufferString(bodyBuf.String()))
		}
		resp, err = t.Transport.RoundTrip(req)
		if err != nil {
			return fmt.Errorf("Error while making the round trip: %s", err)
		}
		if resp != nil && resp.StatusCode >= 500 && resp.StatusCode <= 599 {
			return fmt.Errorf("Got statuscode %d while making the HTTP %s request to %s", resp.StatusCode, req.Method, req.URL)
		}
		return nil
	}
	notifyFunc := func(err error, wait time.Duration) {
		glog.Warningf("Got error: %s. Retrying HTTP request after sleeping for %s", err, wait)
	}

	if err := backoff.RetryNotify(roundTripOp, backOffClient, notifyFunc); err != nil {
		return nil, fmt.Errorf("HTTP request failed inspite of exponential backoff: %s", err)
	}
	return resp, nil
}

// ReportError formats an HTTP error response and also logs the detailed error message.
func ReportError(w http.ResponseWriter, r *http.Request, err error, message string) {
	glog.Errorln(message, err)
	w.Header().Set("Content-Type", "text/plain")
	http.Error(w, fmt.Sprintf("%s %s", message, err), 500)
}
<commit_msg>Stop backing off if status code is not a server error and it is not != 200<commit_after>package util

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"time"

	// Below is a port of the exponential backoff implementation from
	// google-http-java-client.
	"github.com/cenkalti/backoff"
	"github.com/skia-dev/glog"
)

const (
	// TIMEOUT is the http timeout when making requests.
	TIMEOUT = time.Duration(time.Minute)

	// Exponential backoff defaults.
	INITIAL_INTERVAL     = 500 * time.Millisecond
	RANDOMIZATION_FACTOR = 0.5
	BACKOFF_MULTIPLIER   = 1.5
	MAX_INTERVAL         = 60 * time.Second
	MAX_ELAPSED_TIME     = 5 * time.Minute
)

// DialTimeout is a dialer that sets a timeout.
func DialTimeout(network, addr string) (net.Conn, error) {
	return net.DialTimeout(network, addr, TIMEOUT)
}

// NewTimeoutClient creates a new http.Client with a timeout.
func NewTimeoutClient() *http.Client {
	return &http.Client{
		Transport: &http.Transport{
			Dial: DialTimeout,
		},
	}
}

type BackOffConfig struct {
	initialInterval     time.Duration
	maxInterval         time.Duration
	maxElapsedTime      time.Duration
	randomizationFactor float64
	backOffMultiplier   float64
}

// NewBackOffTransport creates a BackOffTransport with default values. Look at
// NewConfiguredBackOffTransport for an example of how the values impact behavior.
func NewBackOffTransport() http.RoundTripper {
	config := &BackOffConfig{
		initialInterval:     INITIAL_INTERVAL,
		maxInterval:         MAX_INTERVAL,
		maxElapsedTime:      MAX_ELAPSED_TIME,
		randomizationFactor: RANDOMIZATION_FACTOR,
		backOffMultiplier:   BACKOFF_MULTIPLIER,
	}
	return NewConfiguredBackOffTransport(config)
}

type BackOffTransport struct {
	http.Transport
	backOffConfig *BackOffConfig
}

// NewBackOffTransport creates a BackOffTransport with the specified config.
//
// Example: The default retry_interval is .5 seconds, default randomization_factor
// is 0.5, default multiplier is 1.5 and the default max_interval is 1 minute. For
// 10 tries the sequence will be (values in seconds) and assuming we go over the
// max_elapsed_time on the 10th try:
//
//  request#     retry_interval     randomized_interval
//  1             0.5                [0.25,   0.75]
//  2             0.75               [0.375,  1.125]
//  3             1.125              [0.562,  1.687]
//  4             1.687              [0.8435, 2.53]
//  5             2.53               [1.265,  3.795]
//  6             3.795              [1.897,  5.692]
//  7             5.692              [2.846,  8.538]
//  8             8.538              [4.269, 12.807]
//  9            12.807              [6.403, 19.210]
//  10           19.210              backoff.Stop
func NewConfiguredBackOffTransport(config *BackOffConfig) http.RoundTripper {
	return &BackOffTransport{
		Transport:     http.Transport{Dial: DialTimeout},
		backOffConfig: config,
	}
}

// RoundTrip implements the RoundTripper interface.
func (t *BackOffTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	// Initialize the exponential backoff client.
	backOffClient := &backoff.ExponentialBackOff{
		InitialInterval:     t.backOffConfig.initialInterval,
		RandomizationFactor: t.backOffConfig.randomizationFactor,
		Multiplier:          t.backOffConfig.backOffMultiplier,
		MaxInterval:         t.backOffConfig.maxInterval,
		MaxElapsedTime:      t.backOffConfig.maxElapsedTime,
		Clock:               backoff.SystemClock,
	}
	// Make a copy of the request's Body so that we can reuse it if the request
	// needs to be backed off and retried.
	bodyBuf := bytes.Buffer{}
	if req.Body != nil {
		bodyBuf.ReadFrom(req.Body)
	}

	var resp *http.Response
	var err error
	roundTripOp := func() error {
		if req.Body != nil {
			req.Body = ioutil.NopCloser(bytes.NewBufferString(bodyBuf.String()))
		}
		resp, err = t.Transport.RoundTrip(req)
		if err != nil {
			return fmt.Errorf("Error while making the round trip: %s", err)
		}
		if resp != nil {
			if resp.StatusCode >= 500 && resp.StatusCode <= 599 {
				return fmt.Errorf("Got server error statuscode %d while making the HTTP %s request to %s", resp.StatusCode, req.Method, req.URL)
			} else if resp.StatusCode != 200 {
				// Stop backing off if there are non server errors.
				backOffClient.MaxElapsedTime = backoff.Stop
				return fmt.Errorf("Got non server error statuscode %d while making the HTTP %s request to %s", resp.StatusCode, req.Method, req.URL)
			}
		}
		return nil
	}
	notifyFunc := func(err error, wait time.Duration) {
		glog.Warningf("Got error: %s. Retrying HTTP request after sleeping for %s", err, wait)
	}

	if err := backoff.RetryNotify(roundTripOp, backOffClient, notifyFunc); err != nil {
		return nil, fmt.Errorf("HTTP request failed inspite of exponential backoff: %s", err)
	}
	return resp, nil
}

// ReportError formats an HTTP error response and also logs the detailed error message.
func ReportError(w http.ResponseWriter, r *http.Request, err error, message string) {
	glog.Errorln(message, err)
	w.Header().Set("Content-Type", "text/plain")
	http.Error(w, fmt.Sprintf("%s %s", message, err), 500)
}
<|endoftext|>


<commit_before>package sites

import (
	"fmt"
	"github.com/fubarhouse/golang-drush/alias"
	"github.com/fubarhouse/golang-drush/aliases"
	"strings"
)

// SiteList struct with an attached key for attaching methods.
type SiteList struct {
	value []string
	key   string
}

// NewSiteList instantiates an empty SiteList struct.
func NewSiteList() *SiteList {
	// Create an empty SiteList object.
	return &SiteList{}
}

// SetKey sets the key field to an input for a SiteList struct.
func (list *SiteList) SetKey(key string) {
	// The key is used as a filter based off the output of 'drush sa'
	list.key = key
}

// Rewrite rewrites a set of values from a SiteList
func (list *SiteList) Rewrite(oldString string, newString string) {
	aliasesList := aliases.NewAliasList()
	aliasesList.Generate(list.key)
	aliasesFiltered := aliases.NewAliasList()
	for _, thisAlias := range list.GetList() {
		//alias = strings.Replace(alias,deleteString, "", -1)
		if strings.Contains(thisAlias, oldString) {
			thisAlias = strings.Replace(thisAlias, oldString, newString, -1)
			newAlias := alias.NewAlias(thisAlias, "", thisAlias)
			aliasesFiltered.Add(newAlias)
		} else {
			newAlias := alias.NewAlias(thisAlias, "", thisAlias)
			aliasesFiltered.Add(newAlias)
		}
	}
	list.value = aliasesFiltered.GetNames()
}

// Remove will remove an entry from a SiteList based on an input
func (list *SiteList) Remove(remove string) {
	// Removes a set of values from a SiteList
	aliasesList := aliases.NewAliasList()
	aliasesList.Generate(list.key)
	aliasesFiltered := aliases.NewAliasList()
	for _, thisAlias := range list.GetList() {
		//alias = strings.Replace(alias,deleteString, "", -1)
		if !strings.Contains(thisAlias, remove) {
			newAlias := alias.NewAlias(thisAlias, "", thisAlias)
			aliasesFiltered.Add(newAlias)
		}
	}
	list.value = aliasesFiltered.GetNames()
}

// FilterBy will filter by an input for an entry from a SiteList
func (list *SiteList) FilterBy(filter string) {
	// Filters a sataset by a set of values from a SiteList
	aliasesList := aliases.NewAliasList()
	aliasesList.Generate(list.key)
	aliasesFiltered := aliases.NewAliasList()
	for _, thisAlias := range list.GetList() {
		//alias = strings.Replace(alias,deleteString, "", -1)
		if strings.Contains(thisAlias, filter) {
			newAlias := alias.NewAlias(thisAlias, "", thisAlias)
			aliasesFiltered.Add(newAlias)
		}
	}
	list.value = aliasesFiltered.GetNames()
}

// SetList adds a set of aliases to a SiteList.
func (list *SiteList) SetList() {
	aliases := aliases.NewAliasList()
	aliases.Generate(list.key)
	for _, alias := range aliases.GetNames() {
		list.value = append(list.value, alias)
	}
}

// GetList returns the dataset in the SiteList object.
func (list *SiteList) GetList() []string {
	return list.value
}

// Count returns the quantity of items in the SiteList object.
func (list *SiteList) Count() int {
	return len(list.value)
}
<commit_msg>Remove unused imports<commit_after>package sites

import (
	"github.com/fubarhouse/golang-drush/alias"
	"github.com/fubarhouse/golang-drush/aliases"
	"strings"
)

// SiteList struct with an attached key for attaching methods.
type SiteList struct {
	value []string
	key   string
}

// NewSiteList instantiates an empty SiteList struct.
func NewSiteList() *SiteList {
	// Create an empty SiteList object.
	return &SiteList{}
}

// SetKey sets the key field to an input for a SiteList struct.
func (list *SiteList) SetKey(key string) {
	// The key is used as a filter based off the output of 'drush sa'
	list.key = key
}

// Rewrite rewrites a set of values from a SiteList
func (list *SiteList) Rewrite(oldString string, newString string) {
	aliasesList := aliases.NewAliasList()
	aliasesList.Generate(list.key)
	aliasesFiltered := aliases.NewAliasList()
	for _, thisAlias := range list.GetList() {
		//alias = strings.Replace(alias,deleteString, "", -1)
		if strings.Contains(thisAlias, oldString) {
			thisAlias = strings.Replace(thisAlias, oldString, newString, -1)
			newAlias := alias.NewAlias(thisAlias, "", thisAlias)
			aliasesFiltered.Add(newAlias)
		} else {
			newAlias := alias.NewAlias(thisAlias, "", thisAlias)
			aliasesFiltered.Add(newAlias)
		}
	}
	list.value = aliasesFiltered.GetNames()
}

// Remove will remove an entry from a SiteList based on an input
func (list *SiteList) Remove(remove string) {
	// Removes a set of values from a SiteList
	aliasesList := aliases.NewAliasList()
	aliasesList.Generate(list.key)
	aliasesFiltered := aliases.NewAliasList()
	for _, thisAlias := range list.GetList() {
		//alias = strings.Replace(alias,deleteString, "", -1)
		if !strings.Contains(thisAlias, remove) {
			newAlias := alias.NewAlias(thisAlias, "", thisAlias)
			aliasesFiltered.Add(newAlias)
		}
	}
	list.value = aliasesFiltered.GetNames()
}

// FilterBy will filter by an input for an entry from a SiteList
func (list *SiteList) FilterBy(filter string) {
	// Filters a sataset by a set of values from a SiteList
	aliasesList := aliases.NewAliasList()
	aliasesList.Generate(list.key)
	aliasesFiltered := aliases.NewAliasList()
	for _, thisAlias := range list.GetList() {
		//alias = strings.Replace(alias,deleteString, "", -1)
		if strings.Contains(thisAlias, filter) {
			newAlias := alias.NewAlias(thisAlias, "", thisAlias)
			aliasesFiltered.Add(newAlias)
		}
	}
	list.value = aliasesFiltered.GetNames()
}

// SetList adds a set of aliases to a SiteList.
func (list *SiteList) SetList() {
	aliases := aliases.NewAliasList()
	aliases.Generate(list.key)
	for _, alias := range aliases.GetNames() {
		list.value = append(list.value, alias)
	}
}

// GetList returns the dataset in the SiteList object.
func (list *SiteList) GetList() []string {
	return list.value
}

// Count returns the quantity of items in the SiteList object.
func (list *SiteList) Count() int {
	return len(list.value)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"testing"

	. "github.com/VonC/godbg"
	. "github.com/smartystreets/goconvey/convey"
)

func TestGoPanic(t *testing.T) {
	Convey("Test main", t, func() {
		fmt.Println("test main")
		files, err := ioutil.ReadDir("./tests")
		if err != nil {
			Pdbgf("Unable to access tests folder\n'%v'\n", err)
			t.Fail()
		}
		for _, file := range files {
			if file.Name() != "exceptionstack2" {
				continue
			}
			Pdbgf(file.Name())
			if in, err = os.Open("tests/" + file.Name()); err == nil {
				Pdbgf("ok open")
				main()
			} else {
				Pdbgf("Unable to access open file '%v'\n'%v'\n", file.Name(), err)
				t.Fail()
			}
		}
	})
}
<commit_msg>Focus on test1<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"testing"

	. "github.com/VonC/godbg"
	. "github.com/smartystreets/goconvey/convey"
)

func TestGoPanic(t *testing.T) {
	Convey("Test main", t, func() {
		fmt.Println("test main")
		files, err := ioutil.ReadDir("./tests")
		if err != nil {
			Pdbgf("Unable to access tests folder\n'%v'\n", err)
			t.Fail()
		}
		for _, file := range files {
			if file.Name() != "exceptionstack1" {
				continue
			}
			Pdbgf(file.Name())
			if in, err = os.Open("tests/" + file.Name()); err == nil {
				Pdbgf("ok open")
				main()
			} else {
				Pdbgf("Unable to access open file '%v'\n'%v'\n", file.Name(), err)
				t.Fail()
			}
		}
	})
}
<|endoftext|>


<commit_before>package main

import (
	"os/exec"
	"strings"
	"testing"
)

func TestNoti(t *testing.T) {
	if _, err := exec.LookPath("noti"); err != nil {
		t.Skip("noti binary missing from PATH")
	}

	t.Run("show version", func(t *testing.T) {
		data, err := exec.Command("noti", "--version").Output()
		if err != nil {
			t.Error(err)
		}
		out := string(data)

		if !strings.Contains(out, "noti version") {
			t.Error("Missing 'noti version'")
		}
		if !strings.Contains(out, "Latest:") {
			t.Error("Missing name of latest version")
		}
		if !strings.Contains(out, "Download: https://github.com/variadico/noti/releases") {
			t.Error("Missing latest download link")
		}
	})

	t.Run("dry run", func(t *testing.T) {
		cmd := exec.Command("noti", "--verbose", "-b=0")
		cmd.Env = []string{}

		data, err := cmd.Output()
		if err != nil {
			t.Error(err)
		}
		out := string(data)

		if !strings.Contains(out, "0 notifications queued") {
			t.Error("Unexpected queued notifications")
			t.Error(out)
		}
	})
}
<commit_msg>Add pwatch test<commit_after>package main

import (
	"context"
	"fmt"
	"os/exec"
	"strings"
	"testing"
	"time"
)

func TestNoti(t *testing.T) {
	if _, err := exec.LookPath("noti"); err != nil {
		t.Skip("noti binary missing from PATH")
	}

	t.Run("show version", func(t *testing.T) {
		data, err := exec.Command("noti", "--version").Output()
		if err != nil {
			t.Error(err)
		}
		out := string(data)

		if !strings.Contains(out, "noti version") {
			t.Error("Missing 'noti version'")
		}
		if !strings.Contains(out, "Latest:") {
			t.Error("Missing name of latest version")
		}
		if !strings.Contains(out, "Download: https://github.com/variadico/noti/releases") {
			t.Error("Missing latest download link")
		}
	})

	t.Run("dry run", func(t *testing.T) {
		cmd := exec.Command("noti", "--verbose", "-b=0")
		cmd.Env = []string{}

		data, err := cmd.Output()
		if err != nil {
			t.Error(err)
		}
		out := string(data)

		if !strings.Contains(out, "0 notifications queued") {
			t.Error("Unexpected queued notifications")
			t.Error(out)
		}
	})

	t.Run("pwatch", func(t *testing.T) {
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()

		sleep := exec.CommandContext(ctx, "sleep", "2")
		if err := sleep.Start(); err != nil {
			t.Error(err)
		}
		go sleep.Wait()

		var sleepPID string
		for i := 0; i < 3; i++ {
			if sleep.Process == nil {
				time.Sleep(10 * time.Millisecond)
				continue
			}
			sleepPID = fmt.Sprint(sleep.Process.Pid)
		}
		if sleepPID == "" {
			t.Fatal("couldn't get sleep pid")
		}

		cmd := exec.CommandContext(ctx, "noti", "--verbose", "-b=0", "--pwatch", sleepPID)
		cmd.Env = []string{}

		if out, err := cmd.Output(); err != nil {
			t.Errorf("noti: %s", err)
			t.Error(string(out))
		}
	})
}
<|endoftext|>