<commit_before>package vsphere

import (
	"bytes"
	"log"
	"strings"

	"github.com/cblomart/vsphere-graphite/utils"

	"github.com/vmware/govmomi/vim25/types"
)

// Cache will hold some informations in memory
type Cache map[string]interface{}

// Gets an index
func index(vcenter, section, i string) string {
	var buffer bytes.Buffer
	_, err := buffer.WriteString(vcenter)
	if err != nil {
		return ""
	}
	_, err = buffer.WriteString("|")
	if err != nil {
		return ""
	}
	_, err = buffer.WriteString(section)
	if err != nil {
		return ""
	}
	_, err = buffer.WriteString("|")
	if err != nil {
		return ""
	}
	_, err = buffer.WriteString(i)
	if err != nil {
		return ""
	}
	return buffer.String()
}

// Add a value to the cache
func (c *Cache) Add(vcenter, section, i string, v interface{}) {
	if len(vcenter) == 0 || len(section) == 0 || len(i) == 0 || v == nil {
		return
	}
	switch typed := v.(type) {
	case string:
		c.add(vcenter, section, i, &typed)
	case []string:
		if len(typed) > 0 {
			c.add(vcenter, section, i, &typed)
		}
	case int32:
		c.add(vcenter, section, i, v)
	case types.ManagedObjectReference:
		c.add(vcenter, section, i, &(typed.Value))
	case types.ArrayOfManagedObjectReference:
		if len(typed.ManagedObjectReference) > 0 {
			c.add(vcenter, section, i, &(typed.ManagedObjectReference))
		}
	case types.ArrayOfTag:
		if len(typed.Tag) > 0 {
			c.add(vcenter, section, i, &(typed.Tag))
		}
	case types.ArrayOfGuestDiskInfo:
		if len(typed.GuestDiskInfo) > 0 {
			c.add(vcenter, section, i, &(typed.GuestDiskInfo))
		}
	default:
		log.Printf("Adding a unhandled type %T to cache for %s section %s and ref %s\n", v, vcenter, section, i)
	}
}

// add to the cache without type check
func (c *Cache) add(vcenter, section, i string, v interface{}) {
	if v != nil {
		(*c)[index(vcenter, section, i)] = v
	}
}

// get a value from the cache
func (c *Cache) get(vcenter, section, i string) interface{} {
	if v, ok := (*c)[index(vcenter, section, i)]; ok {
		return v
	}
	return nil
}

// GetString gets a string from cache
func (c *Cache) GetString(vcenter, section, i string) *string {
	if v, ok := c.get(vcenter, section, i).(*string); ok {
		return v
	}
	return nil
}

// GetStrings gets an array of strings from cache
func (c *Cache) GetStrings(vcenter, section, i string) *[]string {
	if v, ok := c.get(vcenter, section, i).(*[]string); ok {
		return v
	}
	return nil
}

// GetInt32 get an int32 from cache
func (c *Cache) GetInt32(vcenter, section, i string) *int32 {
	if v, ok := c.get(vcenter, section, i).(*int32); ok {
		return v
	}
	return nil
}

// GetMoref gets a managed object reference from cache
func (c *Cache) GetMoref(vcenter, section, i string) *types.ManagedObjectReference {
	if v, ok := c.get(vcenter, section, i).(*types.ManagedObjectReference); ok {
		return v
	}
	return nil
}

// GetMorefs gets an array of managed references from cache
func (c *Cache) GetMorefs(vcenter, section, i string) *[]types.ManagedObjectReference {
	if v, ok := c.get(vcenter, section, i).(*[]types.ManagedObjectReference); ok {
		return v
	}
	return nil
}

// GetTags gets an array of vsphere tags from cache
func (c *Cache) GetTags(vcenter, section, i string) *[]types.Tag {
	if v, ok := c.get(vcenter, section, i).(*[]types.Tag); ok {
		return v
	}
	return nil
}

// GetDiskInfos gets an array of diskinfos from cache
func (c *Cache) GetDiskInfos(vcenter, section, i string) *[]types.GuestDiskInfo {
	if v, ok := c.get(vcenter, section, i).(*[]types.GuestDiskInfo); ok {
		return v
	}
	return nil
}

// Clean cache of unknows references
func (c *Cache) Clean(vcenter string, section string, refs []string) {
	for e := range *c {
		// get back index parts
		m := strings.Split(e, "|")
		// check that we have tree parts or delete
		if len(m) != 3 {
			delete(*c, e)
		}
		// check vcenter
		if m[0] != vcenter && m[1] != section {
			continue
		}
		// find the value in the range
		found := false
		for _, ref := range refs {
			if m[2] == ref {
				found = true
				break
			}
		}
		// remove if not found
		if !found {
			log.Printf("removing %s from cache\n", e)
			delete(*c, e)
		}
	}
}

// CleanAll cleans all sections of unknown references
// poolpaths and metrics are ignored as they will be cleaned real time
func (c *Cache) CleanAll(vcenter string, refs []string) {
	for e := range *c {
		// get back index parts
		m := strings.Split(e, "|")
		// check that we have tree parts or delete
		if len(m) != 3 {
			delete(*c, e)
		}
		// check vcenter and ignored sections
		if m[0] != vcenter || m[1] == "metrics" || m[1] == "poolpaths" {
			continue
		}
		// find the value in the range
		found := false
		for _, ref := range refs {
			if m[2] == ref {
				found = true
				break
			}
		}
		// remove if not found
		if !found {
			log.Printf("removing %s from cache\n", e)
			delete(*c, e)
		}
	}
}

// Purge purges a section of the cache
func (c *Cache) Purge(vcenter, section string) {
	for e := range *c {
		// get back index parts
		m := strings.Split(e, "|")
		// check that we have tree parts
		if len(m) != 3 {
			continue
		}
		// check vcenter and ignored sections
		if m[0] != vcenter || m[1] != section {
			continue
		}
		delete(*c, e)
	}
}

// lookup items in the cache
func (c *Cache) lookup(vcenter, section string) *map[string]interface{} {
	result := make(map[string]interface{})
	for e := range *c {
		// get back index parts
		m := strings.Split(e, "|")
		// check that we have tree parts
		if len(m) != 3 {
			continue
		}
		// check vcenter and ignored sections
		if m[0] != vcenter || m[1] != section {
			continue
		}
		result[m[2]] = (*c)[e]
	}
	return &result
}

// LookupString looks for items in the cache of type string
func (c *Cache) LookupString(vcenter, section string) *map[string]*string {
	result := make(map[string]*string)
	for key, val := range *c.lookup(vcenter, section) {
		if typed, ok := val.(*string); ok {
			result[key] = typed
		}
	}
	return &result
}

// LookupMorefs looks for items in the cache of type Morefs
func (c *Cache) LookupMorefs(vcenter, section string) *map[string]*[]types.ManagedObjectReference {
	result := make(map[string]*[]types.ManagedObjectReference)
	for key, val := range *c.lookup(vcenter, section) {
		if typed, ok := val.(*[]types.ManagedObjectReference); ok {
			result[key] = typed
		}
	}
	return &result
}

// FindHostAndCluster finds host and cluster of a host or a vm
func (c *Cache) FindHostAndCluster(vcenter, moref string) (string, string) {
	// get host
	if strings.HasPrefix(moref, "vm-") {
		// find host of the vm
		host := c.GetString(vcenter, "hosts", moref)
		if host == nil {
			log.Printf("VM %s has no host.\n", moref)
			return "", ""
		}
		moref = *host
	}
	// find hostname
	hostnameptr := cache.GetString(vcenter, "names", moref)
	hostname := ""
	if hostnameptr != nil {
		hostname = *hostnameptr
	}
	// find cluster
	cluster := cache.GetString(vcenter, "parents", moref)
	if cluster == nil {
		log.Printf("Host %s has no parents.\n", moref)
		return hostname, ""
	}
	if strings.HasPrefix(*cluster, "domain-s") {
		//ignore standalone hosts
		return hostname, ""
	}
	if !strings.HasPrefix(*cluster, "domain-c") {
		log.Printf("Host %s has no suitable parent %s.", moref, *cluster)
		return hostname, ""
	}
	clusternameptr := cache.GetString(vcenter, "names", *cluster)
	if clusternameptr == nil {
		return hostname, ""
	}
	return hostname, *clusternameptr
}

// FindString finds and return a string
func (c *Cache) FindString(vcenter, section, moref string) string {
	ptr := cache.GetString(vcenter, section, moref)
	if ptr == nil {
		return ""
	}
	return *ptr
}

// FindName finds an object in cache and resolves its name
func (c *Cache) FindName(vcenter, section, moref string) string {
	ptr := cache.GetString(vcenter, section, moref)
	if ptr == nil {
		return ""
	}
	return cache.FindString(vcenter, "names", *ptr)
}

// FindNames finds objects in cache and resolves their names
func (c *Cache) FindNames(vcenter, section, moref string) []string {
	ptr := cache.GetMorefs(vcenter, section, moref)
	if ptr == nil {
		return []string{}
	}
	if len(*ptr) == 0 {
		return []string{}
	}
	names := make([]string, len(*ptr))
	for _, mor := range *ptr {
		nptr := cache.GetString(vcenter, "names", mor.Value)
		if nptr == nil {
			continue
		}
		if len(*nptr) == 0 {
			continue
		}
		names = append(names, *nptr)
	}
	return names
}

// FindTags finds objects in cachee and create a tag array
func (c *Cache) FindTags(vcenter, moref string) []string {
	ptr := cache.GetTags(vcenter, "tags", moref)
	if ptr == nil {
		return []string{}
	}
	if len(*ptr) == 0 {
		return []string{}
	}
	tags := make([]string, len(*ptr))
	for _, tag := range *ptr {
		tags = append(tags, tag.Key)
	}
	return tags
}

// FindMetricName find metricname from cache
func (c *Cache) FindMetricName(vcenter string, id int32) string {
	ptr := cache.GetString(vcenter, "metrics", utils.ValToString(id, "", true))
	if ptr == nil {
		return ""
	}
	return *ptr
}
<commit_msg>correct removal<commit_after>package vsphere

import (
	"bytes"
	"log"
	"strings"

	"github.com/cblomart/vsphere-graphite/utils"

	"github.com/vmware/govmomi/vim25/types"
)

// Cache will hold some informations in memory
type Cache map[string]interface{}

// Gets an index
func index(vcenter, section, i string) string {
	var buffer bytes.Buffer
	_, err := buffer.WriteString(vcenter)
	if err != nil {
		return ""
	}
	_, err = buffer.WriteString("|")
	if err != nil {
		return ""
	}
	_, err = buffer.WriteString(section)
	if err != nil {
		return ""
	}
	_, err = buffer.WriteString("|")
	if err != nil {
		return ""
	}
	_, err = buffer.WriteString(i)
	if err != nil {
		return ""
	}
	return buffer.String()
}

// Add a value to the cache
func (c *Cache) Add(vcenter, section, i string, v interface{}) {
	if len(vcenter) == 0 || len(section) == 0 || len(i) == 0 || v == nil {
		return
	}
	switch typed := v.(type) {
	case string:
		c.add(vcenter, section, i, &typed)
	case []string:
		if len(typed) > 0 {
			c.add(vcenter, section, i, &typed)
		}
	case int32:
		c.add(vcenter, section, i, v)
	case types.ManagedObjectReference:
		c.add(vcenter, section, i, &(typed.Value))
	case types.ArrayOfManagedObjectReference:
		if len(typed.ManagedObjectReference) > 0 {
			c.add(vcenter, section, i, &(typed.ManagedObjectReference))
		}
	case types.ArrayOfTag:
		if len(typed.Tag) > 0 {
			c.add(vcenter, section, i, &(typed.Tag))
		}
	case types.ArrayOfGuestDiskInfo:
		if len(typed.GuestDiskInfo) > 0 {
			c.add(vcenter, section, i, &(typed.GuestDiskInfo))
		}
	default:
		log.Printf("Adding a unhandled type %T to cache for %s section %s and ref %s\n", v, vcenter, section, i)
	}
}

// add to the cache without type check
func (c *Cache) add(vcenter, section, i string, v interface{}) {
	if v != nil {
		(*c)[index(vcenter, section, i)] = v
	}
}

// get a value from the cache
func (c *Cache) get(vcenter, section, i string) interface{} {
	if v, ok := (*c)[index(vcenter, section, i)]; ok {
		return v
	}
	return nil
}

// GetString gets a string from cache
func (c *Cache) GetString(vcenter, section, i string) *string {
	if v, ok := c.get(vcenter, section, i).(*string); ok {
		return v
	}
	return nil
}

// GetStrings gets an array of strings from cache
func (c *Cache) GetStrings(vcenter, section, i string) *[]string {
	if v, ok := c.get(vcenter, section, i).(*[]string); ok {
		return v
	}
	return nil
}

// GetInt32 get an int32 from cache
func (c *Cache) GetInt32(vcenter, section, i string) *int32 {
	if v, ok := c.get(vcenter, section, i).(*int32); ok {
		return v
	}
	return nil
}

// GetMoref gets a managed object reference from cache
func (c *Cache) GetMoref(vcenter, section, i string) *types.ManagedObjectReference {
	if v, ok := c.get(vcenter, section, i).(*types.ManagedObjectReference); ok {
		return v
	}
	return nil
}

// GetMorefs gets an array of managed references from cache
func (c *Cache) GetMorefs(vcenter, section, i string) *[]types.ManagedObjectReference {
	if v, ok := c.get(vcenter, section, i).(*[]types.ManagedObjectReference); ok {
		return v
	}
	return nil
}

// GetTags gets an array of vsphere tags from cache
func (c *Cache) GetTags(vcenter, section, i string) *[]types.Tag {
	if v, ok := c.get(vcenter, section, i).(*[]types.Tag); ok {
		return v
	}
	return nil
}

// GetDiskInfos gets an array of diskinfos from cache
func (c *Cache) GetDiskInfos(vcenter, section, i string) *[]types.GuestDiskInfo {
	if v, ok := c.get(vcenter, section, i).(*[]types.GuestDiskInfo); ok {
		return v
	}
	return nil
}

// Clean cache of unknows references
func (c *Cache) Clean(vcenter string, section string, refs []string) {
	for e := range *c {
		// get back index parts
		m := strings.Split(e, "|")
		// check that we have tree parts or delete
		if len(m) != 3 {
			delete(*c, e)
		}
		// check vcenter
		if m[0] != vcenter || m[1] != section {
			continue
		}
		// find the value in the range
		found := false
		for _, ref := range refs {
			if m[2] == ref {
				found = true
				break
			}
		}
		// remove if not found
		if !found {
			log.Printf("removing %s from cache\n", e)
			delete(*c, e)
		}
	}
}

// CleanAll cleans all sections of unknown references
// poolpaths and metrics are ignored as they will be cleaned real time
func (c *Cache) CleanAll(vcenter string, refs []string) {
	for e := range *c {
		// get back index parts
		m := strings.Split(e, "|")
		// check that we have tree parts or delete
		if len(m) != 3 {
			delete(*c, e)
		}
		// check vcenter and ignored sections
		if m[0] != vcenter || m[1] == "metrics" || m[1] == "poolpaths" {
			continue
		}
		// find the value in the range
		found := false
		for _, ref := range refs {
			if m[2] == ref {
				found = true
				break
			}
		}
		// remove if not found
		if !found {
			log.Printf("removing %s from cache\n", e)
			delete(*c, e)
		}
	}
}

// Purge purges a section of the cache
func (c *Cache) Purge(vcenter, section string) {
	for e := range *c {
		// get back index parts
		m := strings.Split(e, "|")
		// check that we have tree parts
		if len(m) != 3 {
			continue
		}
		// check vcenter and ignored sections
		if m[0] != vcenter || m[1] != section {
			continue
		}
		delete(*c, e)
	}
}

// lookup items in the cache
func (c *Cache) lookup(vcenter, section string) *map[string]interface{} {
	result := make(map[string]interface{})
	for e := range *c {
		// get back index parts
		m := strings.Split(e, "|")
		// check that we have tree parts
		if len(m) != 3 {
			continue
		}
		// check vcenter and ignored sections
		if m[0] != vcenter || m[1] != section {
			continue
		}
		result[m[2]] = (*c)[e]
	}
	return &result
}

// LookupString looks for items in the cache of type string
func (c *Cache) LookupString(vcenter, section string) *map[string]*string {
	result := make(map[string]*string)
	for key, val := range *c.lookup(vcenter, section) {
		if typed, ok := val.(*string); ok {
			result[key] = typed
		}
	}
	return &result
}

// LookupMorefs looks for items in the cache of type Morefs
func (c *Cache) LookupMorefs(vcenter, section string) *map[string]*[]types.ManagedObjectReference {
	result := make(map[string]*[]types.ManagedObjectReference)
	for key, val := range *c.lookup(vcenter, section) {
		if typed, ok := val.(*[]types.ManagedObjectReference); ok {
			result[key] = typed
		}
	}
	return &result
}

// FindHostAndCluster finds host and cluster of a host or a vm
func (c *Cache) FindHostAndCluster(vcenter, moref string) (string, string) {
	// get host
	if strings.HasPrefix(moref, "vm-") {
		// find host of the vm
		host := c.GetString(vcenter, "hosts", moref)
		if host == nil {
			log.Printf("VM %s has no host.\n", moref)
			return "", ""
		}
		moref = *host
	}
	// find hostname
	hostnameptr := cache.GetString(vcenter, "names", moref)
	hostname := ""
	if hostnameptr != nil {
		hostname = *hostnameptr
	}
	// find cluster
	cluster := cache.GetString(vcenter, "parents", moref)
	if cluster == nil {
		log.Printf("Host %s has no parents.\n", moref)
		return hostname, ""
	}
	if strings.HasPrefix(*cluster, "domain-s") {
		//ignore standalone hosts
		return hostname, ""
	}
	if !strings.HasPrefix(*cluster, "domain-c") {
		log.Printf("Host %s has no suitable parent %s.", moref, *cluster)
		return hostname, ""
	}
	clusternameptr := cache.GetString(vcenter, "names", *cluster)
	if clusternameptr == nil {
		return hostname, ""
	}
	return hostname, *clusternameptr
}

// FindString finds and return a string
func (c *Cache) FindString(vcenter, section, moref string) string {
	ptr := cache.GetString(vcenter, section, moref)
	if ptr == nil {
		return ""
	}
	return *ptr
}

// FindName finds an object in cache and resolves its name
func (c *Cache) FindName(vcenter, section, moref string) string {
	ptr := cache.GetString(vcenter, section, moref)
	if ptr == nil {
		return ""
	}
	return cache.FindString(vcenter, "names", *ptr)
}

// FindNames finds objects in cache and resolves their names
func (c *Cache) FindNames(vcenter, section, moref string) []string {
	ptr := cache.GetMorefs(vcenter, section, moref)
	if ptr == nil {
		return []string{}
	}
	if len(*ptr) == 0 {
		return []string{}
	}
	names := make([]string, len(*ptr))
	for _, mor := range *ptr {
		nptr := cache.GetString(vcenter, "names", mor.Value)
		if nptr == nil {
			continue
		}
		if len(*nptr) == 0 {
			continue
		}
		names = append(names, *nptr)
	}
	return names
}

// FindTags finds objects in cachee and create a tag array
func (c *Cache) FindTags(vcenter, moref string) []string {
	ptr := cache.GetTags(vcenter, "tags", moref)
	if ptr == nil {
		return []string{}
	}
	if len(*ptr) == 0 {
		return []string{}
	}
	tags := make([]string, len(*ptr))
	for _, tag := range *ptr {
		tags = append(tags, tag.Key)
	}
	return tags
}

// FindMetricName find metricname from cache
func (c *Cache) FindMetricName(vcenter string, id int32) string {
	ptr := cache.GetString(vcenter, "metrics", utils.ValToString(id, "", true))
	if ptr == nil {
		return ""
	}
	return *ptr
}
<|endoftext|>


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

package kubernetes

import (
	"context"
	"fmt"

	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/log/level"
	"github.com/prometheus/common/model"
	"github.com/prometheus/prometheus/discovery/targetgroup"
	"github.com/prometheus/prometheus/util/strutil"
	"k8s.io/client-go/pkg/apis/extensions/v1beta1"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/util/workqueue"
)

// Ingress implements discovery of Kubernetes ingresss.
type Ingress struct {
	logger   log.Logger
	informer cache.SharedInformer
	store    cache.Store
	queue    *workqueue.Type
}

// NewIngress returns a new ingress discovery.
func NewIngress(l log.Logger, inf cache.SharedInformer) *Ingress {
	s := &Ingress{logger: l, informer: inf, store: inf.GetStore(), queue: workqueue.NewNamed("ingress")}
	s.informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc: func(o interface{}) {
			eventCount.WithLabelValues("ingress", "add").Inc()
			s.enqueue(o)
		},
		DeleteFunc: func(o interface{}) {
			eventCount.WithLabelValues("ingress", "delete").Inc()
			s.enqueue(o)
		},
		UpdateFunc: func(_, o interface{}) {
			eventCount.WithLabelValues("ingress", "update").Inc()
			s.enqueue(o)
		},
	})
	return s
}

func (e *Ingress) enqueue(obj interface{}) {
	key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
	if err != nil {
		return
	}

	e.queue.Add(key)
}

// Run implements the Discoverer interface.
func (s *Ingress) Run(ctx context.Context, ch chan<- []*targetgroup.Group) {
	defer s.queue.ShutDown()

	if !cache.WaitForCacheSync(ctx.Done(), s.informer.HasSynced) {
		level.Error(s.logger).Log("msg", "ingress informer unable to sync cache")
		return
	}

	go func() {
		for s.process(ctx, ch) {
		}
	}()

	// Block until the target provider is explicitly canceled.
	<-ctx.Done()
}

func (s *Ingress) process(ctx context.Context, ch chan<- []*targetgroup.Group) bool {
	keyObj, quit := s.queue.Get()
	if quit {
		return false
	}
	defer s.queue.Done(keyObj)
	key := keyObj.(string)

	namespace, name, err := cache.SplitMetaNamespaceKey(key)
	if err != nil {
		return true
	}

	o, exists, err := s.store.GetByKey(key)
	if err != nil {
		return true
	}
	if !exists {
		send(ctx, s.logger, RoleIngress, ch, &targetgroup.Group{Source: ingressSourceFromNamespaceAndName(namespace, name)})
		return true
	}
	eps, err := convertToIngress(o)
	if err != nil {
		level.Error(s.logger).Log("msg", "converting to Ingress object failed", "err", err)
		return true
	}
	send(ctx, s.logger, RoleIngress, ch, s.buildIngress(eps))
	return true
}

func convertToIngress(o interface{}) (*v1beta1.Ingress, error) {
	ingress, ok := o.(*v1beta1.Ingress)
	if ok {
		return ingress, nil
	}

	return nil, fmt.Errorf("Received unexpected object: %v", o)
}

func ingressSource(s *v1beta1.Ingress) string {
	return ingressSourceFromNamespaceAndName(s.Namespace, s.Name)
}

func ingressSourceFromNamespaceAndName(namespace, name string) string {
	return "ingress/" + namespace + "/" + name
}

const (
	ingressNameLabel        = metaLabelPrefix + "ingress_name"
	ingressLabelPrefix      = metaLabelPrefix + "ingress_label_"
	ingressAnnotationPrefix = metaLabelPrefix + "ingress_annotation_"
	ingressSchemeLabel      = metaLabelPrefix + "ingress_scheme"
	ingressHostLabel        = metaLabelPrefix + "ingress_host"
	ingressPathLabel        = metaLabelPrefix + "ingress_path"
)

func ingressLabels(ingress *v1beta1.Ingress) model.LabelSet {
	ls := make(model.LabelSet, len(ingress.Labels)+len(ingress.Annotations)+2)
	ls[ingressNameLabel] = lv(ingress.Name)
	ls[namespaceLabel] = lv(ingress.Namespace)

	for k, v := range ingress.Labels {
		ln := strutil.SanitizeLabelName(ingressLabelPrefix + k)
		ls[model.LabelName(ln)] = lv(v)
	}

	for k, v := range ingress.Annotations {
		ln := strutil.SanitizeLabelName(ingressAnnotationPrefix + k)
		ls[model.LabelName(ln)] = lv(v)
	}
	return ls
}

func pathsFromIngressRule(rv *v1beta1.IngressRuleValue) []string {
	if rv.HTTP == nil {
		return []string{"/"}
	}
	paths := make([]string, len(rv.HTTP.Paths))
	for n, p := range rv.HTTP.Paths {
		path := p.Path
		if path == "" {
			path = "/"
		}
		paths[n] = path
	}
	return paths
}

func (s *Ingress) buildIngress(ingress *v1beta1.Ingress) *targetgroup.Group {
	tg := &targetgroup.Group{
		Source: ingressSource(ingress),
	}
	tg.Labels = ingressLabels(ingress)

	tlsHosts := make(map[string]struct{})
	if ingress.Spec.TLS != nil {
		for _, tls := range ingress.Spec.TLS {
			for _, host := range tls.Hosts {
				tlsHosts[host] = struct{}{}
			}
		}
	}

	for _, rule := range ingress.Spec.Rules {
		paths := pathsFromIngressRule(&rule.IngressRuleValue)

		schema := "http"
		_, isTLS := tlsHosts[rule.Host]
		if isTLS {
			schema = "https"
		}

		for _, path := range paths {
			tg.Targets = append(tg.Targets, model.LabelSet{
				model.AddressLabel: lv(rule.Host),
				ingressSchemeLabel: lv(schema),
				ingressHostLabel:   lv(rule.Host),
				ingressPathLabel:   lv(path),
			})
		}
	}

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

package kubernetes

import (
	"context"
	"fmt"

	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/log/level"
	"github.com/prometheus/common/model"
	"github.com/prometheus/prometheus/discovery/targetgroup"
	"github.com/prometheus/prometheus/util/strutil"
	"k8s.io/client-go/pkg/apis/extensions/v1beta1"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/util/workqueue"
)

// Ingress implements discovery of Kubernetes ingresss.
type Ingress struct {
	logger   log.Logger
	informer cache.SharedInformer
	store    cache.Store
	queue    *workqueue.Type
}

// NewIngress returns a new ingress discovery.
func NewIngress(l log.Logger, inf cache.SharedInformer) *Ingress {
	s := &Ingress{logger: l, informer: inf, store: inf.GetStore(), queue: workqueue.NewNamed("ingress")}
	s.informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc: func(o interface{}) {
			eventCount.WithLabelValues("ingress", "add").Inc()
			s.enqueue(o)
		},
		DeleteFunc: func(o interface{}) {
			eventCount.WithLabelValues("ingress", "delete").Inc()
			s.enqueue(o)
		},
		UpdateFunc: func(_, o interface{}) {
			eventCount.WithLabelValues("ingress", "update").Inc()
			s.enqueue(o)
		},
	})
	return s
}

func (e *Ingress) enqueue(obj interface{}) {
	key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
	if err != nil {
		return
	}

	e.queue.Add(key)
}

// Run implements the Discoverer interface.
func (s *Ingress) Run(ctx context.Context, ch chan<- []*targetgroup.Group) {
	defer s.queue.ShutDown()

	if !cache.WaitForCacheSync(ctx.Done(), s.informer.HasSynced) {
		level.Error(s.logger).Log("msg", "ingress informer unable to sync cache")
		return
	}

	go func() {
		for s.process(ctx, ch) {
		}
	}()

	// Block until the target provider is explicitly canceled.
	<-ctx.Done()
}

func (s *Ingress) process(ctx context.Context, ch chan<- []*targetgroup.Group) bool {
	keyObj, quit := s.queue.Get()
	if quit {
		return false
	}
	defer s.queue.Done(keyObj)
	key := keyObj.(string)

	namespace, name, err := cache.SplitMetaNamespaceKey(key)
	if err != nil {
		return true
	}

	o, exists, err := s.store.GetByKey(key)
	if err != nil {
		return true
	}
	if !exists {
		send(ctx, s.logger, RoleIngress, ch, &targetgroup.Group{Source: ingressSourceFromNamespaceAndName(namespace, name)})
		return true
	}
	eps, err := convertToIngress(o)
	if err != nil {
		level.Error(s.logger).Log("msg", "converting to Ingress object failed", "err", err)
		return true
	}
	send(ctx, s.logger, RoleIngress, ch, s.buildIngress(eps))
	return true
}

func convertToIngress(o interface{}) (*v1beta1.Ingress, error) {
	ingress, ok := o.(*v1beta1.Ingress)
	if ok {
		return ingress, nil
	}

	return nil, fmt.Errorf("Received unexpected object: %v", o)
}

func ingressSource(s *v1beta1.Ingress) string {
	return ingressSourceFromNamespaceAndName(s.Namespace, s.Name)
}

func ingressSourceFromNamespaceAndName(namespace, name string) string {
	return "ingress/" + namespace + "/" + name
}

const (
	ingressNameLabel        = metaLabelPrefix + "ingress_name"
	ingressLabelPrefix      = metaLabelPrefix + "ingress_label_"
	ingressAnnotationPrefix = metaLabelPrefix + "ingress_annotation_"
	ingressSchemeLabel      = metaLabelPrefix + "ingress_scheme"
	ingressHostLabel        = metaLabelPrefix + "ingress_host"
	ingressPathLabel        = metaLabelPrefix + "ingress_path"
)

func ingressLabels(ingress *v1beta1.Ingress) model.LabelSet {
	ls := make(model.LabelSet, len(ingress.Labels)+len(ingress.Annotations)+2)
	ls[ingressNameLabel] = lv(ingress.Name)
	ls[namespaceLabel] = lv(ingress.Namespace)

	for k, v := range ingress.Labels {
		ln := strutil.SanitizeLabelName(ingressLabelPrefix + k)
		ls[model.LabelName(ln)] = lv(v)
	}

	for k, v := range ingress.Annotations {
		ln := strutil.SanitizeLabelName(ingressAnnotationPrefix + k)
		ls[model.LabelName(ln)] = lv(v)
	}
	return ls
}

func pathsFromIngressRule(rv *v1beta1.IngressRuleValue) []string {
	if rv.HTTP == nil {
		return []string{"/"}
	}
	paths := make([]string, len(rv.HTTP.Paths))
	for n, p := range rv.HTTP.Paths {
		path := p.Path
		if path == "" {
			path = "/"
		}
		paths[n] = path
	}
	return paths
}

func (s *Ingress) buildIngress(ingress *v1beta1.Ingress) *targetgroup.Group {
	tg := &targetgroup.Group{
		Source: ingressSource(ingress),
	}
	tg.Labels = ingressLabels(ingress)

	tlsHosts := make(map[string]struct{})
	for _, tls := range ingress.Spec.TLS {
		for _, host := range tls.Hosts {
			tlsHosts[host] = struct{}{}
		}
	}

	for _, rule := range ingress.Spec.Rules {
		paths := pathsFromIngressRule(&rule.IngressRuleValue)

		schema := "http"
		_, isTLS := tlsHosts[rule.Host]
		if isTLS {
			schema = "https"
		}

		for _, path := range paths {
			tg.Targets = append(tg.Targets, model.LabelSet{
				model.AddressLabel: lv(rule.Host),
				ingressSchemeLabel: lv(schema),
				ingressHostLabel:   lv(rule.Host),
				ingressPathLabel:   lv(path),
			})
		}
	}

	return tg
}
<|endoftext|>


<commit_before>package tuplespace

import "svn/bachelorProject/tupleSpaceFramework/constants"

// WaitingClient is used as a structure for clients who performed an
// unsuccessful Get or Query operation, in the sense that it didn't initially
// found a tuple in the tuple space. This structure stores the necessary
// information about the client, so that a tuple can be send to it once a
// matching tuple arrives.
type WaitingClient struct {
	template     Template      // Template that the waiting client is using to search for a tuple.
	responseChan chan<- *Tuple // Channel where the response can be send through.
	operation    string        // String that will denote the type of operation the client is trying to carry out.
}

// CreateWaitingClient will create the waiting client with the template that
// should be used for tuple matching, response channel for the matched tuple to
// be send to. The remove value will be used to determine if the client
// performed a Get or Query operation.
func CreateWaitingClient(temp Template, tupleChan chan<- *Tuple, remove bool) WaitingClient {
	var o string
	if remove {
		o = constants.GetRequest
	} else {
		o = constants.QueryRequest
	}

	waitingClient := WaitingClient{template: temp, responseChan: tupleChan, operation: o}
	return waitingClient
}

// GetTemplate will return the template of the waiting client.
func (waitingClient *WaitingClient) GetTemplate() Template {
	return waitingClient.template
}

// GetResponseChan will return the response channel of the waiting client.
func (waitingClient *WaitingClient) GetResponseChan() chan<- *Tuple {
	return waitingClient.responseChan
}

// GetOperation will return the operation of the waiting client.
func (waitingClient *WaitingClient) GetOperation() string {
	return waitingClient.operation
}
<commit_msg>Delete waitingClient.go<commit_after><|endoftext|>


<commit_before>package tictactoe

import (
	"github.com/jkomoros/boardgame"
)

const (
	X     = "X"
	O     = "O"
	Empty = ""
)

//+autoreader reader
type playerToken struct {
	Value string
}

//Designed to be used with stack.ComponentValues()
func playerTokenValues(in []boardgame.SubState) []*playerToken {
	result := make([]*playerToken, len(in))
	for i := 0; i < len(in); i++ {
		c := in[i]
		if c == nil {
			result[i] = nil
			continue
		}
		result[i] = c.(*playerToken)
	}
	return result
}

const (
	ColorUnknown = iota
	ColorRed
	ColorGreen
)

type EnumManager struct {
	myInt int
}

type Enum struct {
	Name    string
	Manager *EnumManager
}

func NewEnumManager() *EnumManager {
	return &EnumManager{}
}

func (e *EnumManager) NewEnum(name string, values map[int]string) *Enum {
	return &Enum{name, e}
}

var enumManager = NewEnumManager()

var ColorEnum = enumManager.NewEnum("Color", map[int]string{
	ColorUnknown: "Unknown",
	ColorRed:     "Red",
	ColorGreen:   "Green",
})
<commit_msg>Oops, remove scratch that was added to tictactoe accidentally. Part of #457.<commit_after>package tictactoe

import (
	"github.com/jkomoros/boardgame"
)

const (
	X     = "X"
	O     = "O"
	Empty = ""
)

//+autoreader reader
type playerToken struct {
	Value string
}

//Designed to be used with stack.ComponentValues()
func playerTokenValues(in []boardgame.SubState) []*playerToken {
	result := make([]*playerToken, len(in))
	for i := 0; i < len(in); i++ {
		c := in[i]
		if c == nil {
			result[i] = nil
			continue
		}
		result[i] = c.(*playerToken)
	}
	return result
}
<|endoftext|>


<commit_before>package handlers

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

	"github.com/Sirupsen/logrus"
	"github.com/alexellis/faas/gateway/metrics"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/filters"
	"github.com/docker/docker/client"
	"github.com/gorilla/mux"
	"github.com/prometheus/client_golang/prometheus"
)

// MakeProxy creates a proxy for HTTP web requests which can be routed to a function.
func MakeProxy(metrics metrics.MetricOptions, wildcard bool, c *client.Client, logger *logrus.Logger) http.HandlerFunc {
	proxyClient := http.Client{
		Transport: &http.Transport{
			Proxy: http.ProxyFromEnvironment,
			DialContext: (&net.Dialer{
				Timeout:   3 * time.Second,
				KeepAlive: 0,
			}).DialContext,
			MaxIdleConns:          1,
			DisableKeepAlives:     true,
			IdleConnTimeout:       120 * time.Millisecond,
			ExpectContinueTimeout: 1500 * time.Millisecond,
		},
	}

	return func(w http.ResponseWriter, r *http.Request) {

		if r.Method == "POST" {
			logger.Infoln(r.Header)
			header := r.Header["X-Function"]
			logger.Infoln(header)

			if wildcard == true {
				vars := mux.Vars(r)
				name := vars["name"]
				fmt.Println("invoke by name")
				lookupInvoke(w, r, metrics, name, c, logger, &proxyClient)
				defer r.Body.Close()

			} else if len(header) > 0 {
				lookupInvoke(w, r, metrics, header[0], c, logger, &proxyClient)
				defer r.Body.Close()
			} else {
				w.WriteHeader(http.StatusBadRequest)
				w.Write([]byte("Provide a named /function URL or an x-function header."))
				defer r.Body.Close()
			}
		}
	}
}

func writeHead(service string, metrics metrics.MetricOptions, code int, w http.ResponseWriter) {
	w.WriteHeader(code)

	metrics.GatewayFunctionInvocation.With(prometheus.Labels{"function_name": service, "code": strconv.Itoa(code)}).Inc()

	// metrics.GatewayFunctionInvocation.WithLabelValues(service).Add(1)
}

func trackTime(then time.Time, metrics metrics.MetricOptions, name string) {
	since := time.Since(then)
	metrics.GatewayFunctionsHistogram.WithLabelValues(name).Observe(since.Seconds())
}

func lookupInvoke(w http.ResponseWriter, r *http.Request, metrics metrics.MetricOptions, name string, c *client.Client, logger *logrus.Logger, proxyClient *http.Client) {
	exists, err := lookupSwarmService(name, c)

	if err != nil || exists == false {
		if err != nil {
			logger.Fatalln(err)
		}
		writeHead(name, metrics, http.StatusInternalServerError, w)
		w.Write([]byte("Error resolving service."))
		defer r.Body.Close()
		return
	}

	if exists {
		defer trackTime(time.Now(), metrics, name)
		requestBody, _ := ioutil.ReadAll(r.Body)
		invokeService(w, r, metrics, name, requestBody, logger, proxyClient)
	}
}

func lookupSwarmService(serviceName string, c *client.Client) (bool, error) {
	fmt.Printf("Resolving: '%s'\n", serviceName)
	serviceFilter := filters.NewArgs()
	serviceFilter.Add("name", serviceName)
	services, err := c.ServiceList(context.Background(), types.ServiceListOptions{Filters: serviceFilter})

	return len(services) > 0, err
}

func copyHeaders(destination *http.Header, source *http.Header) {
	for k, vv := range *source {
		vvClone := make([]string, len(vv))
		copy(vvClone, vv)
		(*destination)[k] = vvClone
	}
}

func invokeService(w http.ResponseWriter, r *http.Request, metrics metrics.MetricOptions, service string, requestBody []byte, logger *logrus.Logger, proxyClient *http.Client) {
	stamp := strconv.FormatInt(time.Now().Unix(), 10)

	defer func(when time.Time) {
		seconds := time.Since(when).Seconds()

		fmt.Printf("[%s] took %f seconds\n", stamp, seconds)
		metrics.GatewayFunctionsHistogram.WithLabelValues(service).Observe(seconds)
	}(time.Now())

	watchdogPort := 8080
	addr, lookupErr := net.LookupIP(service)
	var url string
	if len(addr) > 0 && lookupErr == nil {
		url = fmt.Sprintf("http://%s:%d/", addr[0].String(), watchdogPort)
	} else {
		url = fmt.Sprintf("http://%s:%d/", service, watchdogPort)
	}

	contentType := r.Header.Get("Content-Type")
	fmt.Printf("[%s] Forwarding request [%s] to: %s\n", stamp, contentType, url)

	request, err := http.NewRequest("POST", url, bytes.NewReader(requestBody))

	copyHeaders(&request.Header, &r.Header)

	defer request.Body.Close()

	response, err := proxyClient.Do(request)
	if err != nil {
		logger.Infoln(err)
		writeHead(service, metrics, http.StatusInternalServerError, w)
		buf := bytes.NewBufferString("Can't reach service: " + service)
		w.Write(buf.Bytes())
		return
	}

	responseBody, readErr := ioutil.ReadAll(response.Body)
	if readErr != nil {
		fmt.Println(readErr)

		writeHead(service, metrics, http.StatusInternalServerError, w)
		buf := bytes.NewBufferString("Error reading response from service: " + service)
		w.Write(buf.Bytes())
		return
	}

	clientHeader := w.Header()
	copyHeaders(&clientHeader, &response.Header)

	// Match header for strict services
	w.Header().Set("Content-Type", r.Header.Get("Content-Type"))

	writeHead(service, metrics, http.StatusOK, w)
	w.Write(responseBody)
}
<commit_msg>Enable DNS-RR override for task lookup.<commit_after>package handlers

import (
	"bytes"
	"context"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net"
	"net/http"
	"strconv"
	"time"

	"os"

	"github.com/Sirupsen/logrus"
	"github.com/alexellis/faas/gateway/metrics"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/filters"
	"github.com/docker/docker/client"
	"github.com/gorilla/mux"
	"github.com/prometheus/client_golang/prometheus"
)

// MakeProxy creates a proxy for HTTP web requests which can be routed to a function.
func MakeProxy(metrics metrics.MetricOptions, wildcard bool, c *client.Client, logger *logrus.Logger) http.HandlerFunc {
	proxyClient := http.Client{
		Transport: &http.Transport{
			Proxy: http.ProxyFromEnvironment,
			DialContext: (&net.Dialer{
				Timeout:   3 * time.Second,
				KeepAlive: 0,
			}).DialContext,
			MaxIdleConns:          1,
			DisableKeepAlives:     true,
			IdleConnTimeout:       120 * time.Millisecond,
			ExpectContinueTimeout: 1500 * time.Millisecond,
		},
	}

	return func(w http.ResponseWriter, r *http.Request) {

		if r.Method == "POST" {
			logger.Infoln(r.Header)
			header := r.Header["X-Function"]
			logger.Infoln(header)

			if wildcard == true {
				vars := mux.Vars(r)
				name := vars["name"]
				fmt.Println("invoke by name")
				lookupInvoke(w, r, metrics, name, c, logger, &proxyClient)
				defer r.Body.Close()

			} else if len(header) > 0 {
				lookupInvoke(w, r, metrics, header[0], c, logger, &proxyClient)
				defer r.Body.Close()
			} else {
				w.WriteHeader(http.StatusBadRequest)
				w.Write([]byte("Provide a named /function URL or an x-function header."))
				defer r.Body.Close()
			}
		}
	}
}

func writeHead(service string, metrics metrics.MetricOptions, code int, w http.ResponseWriter) {
	w.WriteHeader(code)

	metrics.GatewayFunctionInvocation.With(prometheus.Labels{"function_name": service, "code": strconv.Itoa(code)}).Inc()

	// metrics.GatewayFunctionInvocation.WithLabelValues(service).Add(1)
}

func trackTime(then time.Time, metrics metrics.MetricOptions, name string) {
	since := time.Since(then)
	metrics.GatewayFunctionsHistogram.WithLabelValues(name).Observe(since.Seconds())
}

func lookupInvoke(w http.ResponseWriter, r *http.Request, metrics metrics.MetricOptions, name string, c *client.Client, logger *logrus.Logger, proxyClient *http.Client) {
	exists, err := lookupSwarmService(name, c)

	if err != nil || exists == false {
		if err != nil {
			logger.Fatalln(err)
		}
		writeHead(name, metrics, http.StatusInternalServerError, w)
		w.Write([]byte("Error resolving service."))
		defer r.Body.Close()
		return
	}

	if exists {
		defer trackTime(time.Now(), metrics, name)
		requestBody, _ := ioutil.ReadAll(r.Body)
		invokeService(w, r, metrics, name, requestBody, logger, proxyClient)
	}
}

func lookupSwarmService(serviceName string, c *client.Client) (bool, error) {
	fmt.Printf("Resolving: '%s'\n", serviceName)
	serviceFilter := filters.NewArgs()
	serviceFilter.Add("name", serviceName)
	services, err := c.ServiceList(context.Background(), types.ServiceListOptions{Filters: serviceFilter})

	return len(services) > 0, err
}

func copyHeaders(destination *http.Header, source *http.Header) {
	for k, vv := range *source {
		vvClone := make([]string, len(vv))
		copy(vvClone, vv)
		(*destination)[k] = vvClone
	}
}

func randomInt(min, max int) int {
	rand.Seed(time.Now().Unix())
	return rand.Intn(max-min) + min
}

func invokeService(w http.ResponseWriter, r *http.Request, metrics metrics.MetricOptions, service string, requestBody []byte, logger *logrus.Logger, proxyClient *http.Client) {
	stamp := strconv.FormatInt(time.Now().Unix(), 10)

	defer func(when time.Time) {
		seconds := time.Since(when).Seconds()

		fmt.Printf("[%s] took %f seconds\n", stamp, seconds)
		metrics.GatewayFunctionsHistogram.WithLabelValues(service).Observe(seconds)
	}(time.Now())

	var dnsrr bool
	if os.Getenv("dnsrr") == "true" {
		dnsrr = true
	}

	watchdogPort := 8080

	addr := service
	// Use DNS-RR via tasks.servicename if enabled as override, otherwise VIP.
	if dnsrr {
		entries, lookupErr := net.LookupIP(fmt.Sprintf("tasks.%s", service))
		if lookupErr == nil && len(entries) > 0 {
			index := randomInt(0, len(entries))
			addr = entries[index].String()
		}
	}
	url := fmt.Sprintf("http://%s:%d/", addr, watchdogPort)

	contentType := r.Header.Get("Content-Type")
	fmt.Printf("[%s] Forwarding request [%s] to: %s\n", stamp, contentType, url)

	request, err := http.NewRequest("POST", url, bytes.NewReader(requestBody))

	copyHeaders(&request.Header, &r.Header)

	defer request.Body.Close()

	response, err := proxyClient.Do(request)
	if err != nil {
		logger.Infoln(err)
		writeHead(service, metrics, http.StatusInternalServerError, w)
		buf := bytes.NewBufferString("Can't reach service: " + service)
		w.Write(buf.Bytes())
		return
	}

	responseBody, readErr := ioutil.ReadAll(response.Body)
	if readErr != nil {
		fmt.Println(readErr)

		writeHead(service, metrics, http.StatusInternalServerError, w)
		buf := bytes.NewBufferString("Error reading response from service: " + service)
		w.Write(buf.Bytes())
		return
	}

	clientHeader := w.Header()
	copyHeaders(&clientHeader, &response.Header)

	// TODO: copyHeaders removes the need for this line - test removal.
	// Match header for strict services
	w.Header().Set("Content-Type", r.Header.Get("Content-Type"))

	writeHead(service, metrics, http.StatusOK, w)
	w.Write(responseBody)
}
<|endoftext|>


<commit_before>package warmup

import (
	"fmt"
	"net/http"
)

var jobs chan *Warmup
var finished chan struct{}

const workers = 3

func init() {
	jobs = make(chan *Warmup)
	finished = make(chan struct{})

	for w := 1; w <= workers; w++ {
		go worker(w, jobs, finished)
	}
}

func worker(id int, jobs <-chan *Warmup, finished chan<- struct{}) {
	for w := range jobs {
		if w.URL != "" {
			_, err := http.Get(w.URL)
			if err != nil {
				fmt.Println("[", id, "] Warmup error: ", err)
			}
		}
	}
	finished <- struct{}{}
}

// Warmup defines an action to take to "warm up" a service after launch
type Warmup struct {
	// A URL that can be used to warm up this service, will result in a GET request
	URL string `json:"URL,omitempty"`
}

func Run(service string, w *Warmup) {
	if w == nil {
		return
	}
	if w.URL != "" {
		fmt.Printf("Warming up %v: %v\n", service, w.URL)
	}
	jobs <- w
}

func Wait() {
	close(jobs)
	for a := 1; a <= workers; a++ {
		<-finished
	}
}
<commit_msg>Clean up lint warnings in warmup package<commit_after>package warmup

import (
	"fmt"
	"net/http"
)

var jobs chan *Warmup
var finished chan struct{}

const workers = 3

func init() {
	jobs = make(chan *Warmup)
	finished = make(chan struct{})

	for w := 1; w <= workers; w++ {
		go worker(w, jobs, finished)
	}
}

func worker(id int, jobs <-chan *Warmup, finished chan<- struct{}) {
	for w := range jobs {
		if w.URL != "" {
			_, err := http.Get(w.URL)
			if err != nil {
				fmt.Println("[", id, "] Warmup error: ", err)
			}
		}
	}
	finished <- struct{}{}
}

// Warmup defines an action to take to "warm up" a service after launch
type Warmup struct {
	// A URL that can be used to warm up this service, will result in a GET request
	URL string `json:"URL,omitempty"`
}

// Run executes a warmup operation for a service
func Run(service string, w *Warmup) {
	if w == nil {
		return
	}
	if w.URL != "" {
		fmt.Printf("Warming up %v: %v\n", service, w.URL)
	}
	jobs <- w
}

// Wait blocks until all Warmup operations are complete
func Wait() {
	close(jobs)
	for a := 1; a <= workers; a++ {
		<-finished
	}
}
<|endoftext|>


<commit_before>package release

import (
	"bytes"
	"sort"
)

const maxVersions = 5

type EC2Manifest struct {
	Name     string        `json:"name"`
	Versions []*EC2Version `json:"versions"`
}

// Add adds an image to the manifest.
//
// If the version is already in the manifest, the given image either
// replaces any existing image with the same region, or is appended to
// the existing list of images for that version.
//
// If the version is not already in the manifest a new version is added
// containing the image.
//
// The number of versions in the manifest is capped at the value of maxVersions.
func (m *EC2Manifest) Add(version string, image *EC2Image) {
	versions := make(sortVersions, 0, len(m.Versions)+1)
	for _, v := range m.Versions {
		if v.version() == version {
			images := make([]*EC2Image, len(v.Images))
			added := false
			for n, i := range v.Images {
				if i.Region == image.Region {
					// replace existing image
					images[n] = image
					added = true
					continue
				}
				images[n] = i
			}
			if !added {
				images = append(images, image)
			}
			v.Images = images
			return
		}
		versions = append(versions, v)
	}
	versions = append(versions, &EC2Version{
		Version: version,
		Images:  []*EC2Image{image},
	})
	sort.Sort(sort.Reverse(versions))
	m.Versions = make([]*EC2Version, 0, maxVersions)
	for i := 0; i < len(versions) && i < maxVersions; i++ {
		m.Versions = append(m.Versions, versions[i].(*EC2Version))
	}
}

type EC2Version struct {
	Version string      `json:"version"`
	Images  []*EC2Image `json:"images"`
}

func (v *EC2Version) version() string {
	return v.Version
}

type EC2Image struct {
	ID                   string `json:"id"`
	Name                 string `json:"name"`
	Region               string `json:"region"`
	OwnerID              string `json:"owner_id"`
	RootDeviceType       string `json:"root_device_type"`
	RootDeviceName       string `json:"root_device_name"`
	RootDeviceSnapshotID string `json:"root_device_snapshot_id"`
	VirtualizationType   string `json:"virtualization_type"`
	Hypervisor           string `json:"hypervisor"`
}

type Versioner interface {
	version() string
}

type sortVersions []Versioner

func (s sortVersions) Len() int {
	return len(s)
}

func (s sortVersions) Less(i, j int) bool {
	return bytes.Compare([]byte(s[i].version()), []byte(s[j].version())) < 0
}

func (s sortVersions) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

type VagrantManifest struct {
	Name     string            `json:"name"`
	Versions []*VagrantVersion `json:"versions"`
}

// Add adds a provider to the manifest.
//
// If the version is already in the manifest, the given provider either
// replaces any existing provider with the same name, or is appended to
// the existing list of providers for that version.
//
// If the version is not already in the manifest a new version is added
// containing the provider.
//
// The number of versions in the manifest is capped at the value of maxVersions.
func (m *VagrantManifest) Add(version string, provider *VagrantProvider) {
	versions := make(sortVersions, 0, len(m.Versions)+1)
	for _, v := range m.Versions {
		if v.version() == version {
			providers := make([]*VagrantProvider, len(v.Providers))
			added := false
			for i, p := range v.Providers {
				if p.Name == provider.Name {
					// replace existing provider
					providers[i] = provider
					added = true
					continue
				}
				providers[i] = p
			}
			if !added {
				providers = append(providers, provider)
			}
			v.Providers = providers
			return
		}
		versions = append(versions, v)
	}
	versions = append(versions, &VagrantVersion{
		Version:   version,
		Providers: []*VagrantProvider{provider},
	})
	sort.Sort(sort.Reverse(versions))
	m.Versions = make([]*VagrantVersion, 0, maxVersions)
	for i := 0; i < len(versions) && i < maxVersions; i++ {
		m.Versions = append(m.Versions, versions[i].(*VagrantVersion))
	}
}

type VagrantVersion struct {
	Version   string             `json:"version"`
	Providers []*VagrantProvider `json:"providers"`
}

func (v *VagrantVersion) version() string {
	return v.Version
}

type VagrantProvider struct {
	Name         string `json:"name"`
	URL          string `json:"url"`
	ChecksumType string `json:"checksum_type"`
	Checksum     string `json:"checksum"`
}
<commit_msg>util/release: Strip leading "v" from version in vagrant manifest<commit_after>package release

import (
	"bytes"
	"sort"
	"strings"
)

const maxVersions = 5

type EC2Manifest struct {
	Name     string        `json:"name"`
	Versions []*EC2Version `json:"versions"`
}

// Add adds an image to the manifest.
//
// If the version is already in the manifest, the given image either
// replaces any existing image with the same region, or is appended to
// the existing list of images for that version.
//
// If the version is not already in the manifest a new version is added
// containing the image.
//
// The number of versions in the manifest is capped at the value of maxVersions.
func (m *EC2Manifest) Add(version string, image *EC2Image) {
	versions := make(sortVersions, 0, len(m.Versions)+1)
	for _, v := range m.Versions {
		if v.version() == version {
			images := make([]*EC2Image, len(v.Images))
			added := false
			for n, i := range v.Images {
				if i.Region == image.Region {
					// replace existing image
					images[n] = image
					added = true
					continue
				}
				images[n] = i
			}
			if !added {
				images = append(images, image)
			}
			v.Images = images
			return
		}
		versions = append(versions, v)
	}
	versions = append(versions, &EC2Version{
		Version: version,
		Images:  []*EC2Image{image},
	})
	sort.Sort(sort.Reverse(versions))
	m.Versions = make([]*EC2Version, 0, maxVersions)
	for i := 0; i < len(versions) && i < maxVersions; i++ {
		m.Versions = append(m.Versions, versions[i].(*EC2Version))
	}
}

type EC2Version struct {
	Version string      `json:"version"`
	Images  []*EC2Image `json:"images"`
}

func (v *EC2Version) version() string {
	return v.Version
}

type EC2Image struct {
	ID                   string `json:"id"`
	Name                 string `json:"name"`
	Region               string `json:"region"`
	OwnerID              string `json:"owner_id"`
	RootDeviceType       string `json:"root_device_type"`
	RootDeviceName       string `json:"root_device_name"`
	RootDeviceSnapshotID string `json:"root_device_snapshot_id"`
	VirtualizationType   string `json:"virtualization_type"`
	Hypervisor           string `json:"hypervisor"`
}

type Versioner interface {
	version() string
}

type sortVersions []Versioner

func (s sortVersions) Len() int {
	return len(s)
}

func (s sortVersions) Less(i, j int) bool {
	return bytes.Compare([]byte(s[i].version()), []byte(s[j].version())) < 0
}

func (s sortVersions) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

type VagrantManifest struct {
	Name     string            `json:"name"`
	Versions []*VagrantVersion `json:"versions"`
}

// Add adds a provider to the manifest.
//
// If the version is already in the manifest, the given provider either
// replaces any existing provider with the same name, or is appended to
// the existing list of providers for that version.
//
// If the version is not already in the manifest a new version is added
// containing the provider.
//
// The number of versions in the manifest is capped at the value of maxVersions.
func (m *VagrantManifest) Add(version string, provider *VagrantProvider) {
	// strip the leading "v" from the version as Vagrant only supports
	// versions like X.Y.Z, see https://github.com/flynn/flynn/issues/2230
	version = strings.TrimPrefix(version, "v")

	versions := make(sortVersions, 0, len(m.Versions)+1)
	for _, v := range m.Versions {
		if v.version() == version {
			providers := make([]*VagrantProvider, len(v.Providers))
			added := false
			for i, p := range v.Providers {
				if p.Name == provider.Name {
					// replace existing provider
					providers[i] = provider
					added = true
					continue
				}
				providers[i] = p
			}
			if !added {
				providers = append(providers, provider)
			}
			v.Providers = providers
			return
		}
		versions = append(versions, v)
	}
	versions = append(versions, &VagrantVersion{
		Version:   version,
		Providers: []*VagrantProvider{provider},
	})
	sort.Sort(sort.Reverse(versions))
	m.Versions = make([]*VagrantVersion, 0, maxVersions)
	for i := 0; i < len(versions) && i < maxVersions; i++ {
		m.Versions = append(m.Versions, versions[i].(*VagrantVersion))
	}
}

type VagrantVersion struct {
	Version   string             `json:"version"`
	Providers []*VagrantProvider `json:"providers"`
}

func (v *VagrantVersion) version() string {
	return v.Version
}

type VagrantProvider struct {
	Name         string `json:"name"`
	URL          string `json:"url"`
	ChecksumType string `json:"checksum_type"`
	Checksum     string `json:"checksum"`
}
<|endoftext|>


<commit_before>package lahman_test

import (
	"fmt"
	"testing"

	"github.com/frenata/marcel/lahman"
)

var tail string = "zuninmi01,2014,1,SEA,AL,131,438,51,87,20,2,22,60,0,3,17,158,1,17,0,4,12"

// Test reading from the CSV database
func Test_ParseCSV(t *testing.T) {
	r, err := lahman.ReadCSV("data/Batting.csv")
	if err != nil {
		t.Log(err)
		t.Fatal("error reading file")
	}

	player, _ := r.Read() // dispose of first line
	player, _ = r.Read()  // dispose of second line
	player, _ = r.Read()

	b, err := lahman.NewBatterCSV(player)
	checkLineTwo(b, err, t)
}

// test csv read when given bad file name
func Test_BadFile(t *testing.T) {
	_, err := lahman.ReadCSV("data/bad.csv")
	if err == nil {
		t.Fatal("failed to report error on bad file")
	}
}

// test read full file, check last line
func Test_ReadFull(t *testing.T) {
	batters, err := lahman.ReadAll("data/Batting.csv")
	if err != nil {
		t.Fatal("error reading full file")
	}
	fmt.Println(batters[len(batters)-1])
}
<commit_msg>added tests for last player in full file read<commit_after>package lahman_test

import (
	"testing"

	"github.com/frenata/marcel/lahman"
)

var tail string = "zuninmi01,2014,1,SEA,AL,131,438,51,87,20,2,22,60,0,3,17,158,1,17,0,4,12"

// Test reading from the CSV database
func Test_ParseCSV(t *testing.T) {
	r, err := lahman.ReadCSV("data/Batting.csv")
	if err != nil {
		t.Log(err)
		t.Fatal("error reading file")
	}

	player, _ := r.Read() // dispose of first line
	player, _ = r.Read()  // dispose of second line
	player, _ = r.Read()

	b, err := lahman.NewBatterCSV(player)
	checkLineTwo(b, err, t)
}

// test csv read when given bad file name
func Test_BadFile(t *testing.T) {
	_, err := lahman.ReadCSV("data/bad.csv")
	if err == nil {
		t.Fatal("failed to report error on bad file")
	}
}

// test read full file, check last line
func Test_ReadFull(t *testing.T) {
	batters, err := lahman.ReadAll("data/Batting.csv")
	if err != nil {
		t.Fatal("error reading full file")
	}
	//	fmt.Println(batters[len(batters)-1])

	b := batters[len(batters)-1]
	// add checks for last batter
	switch {
	case err != nil:
		t.Log(err)
		t.Fatal("Parsing error")
	case b.ID != "zuninmi01":
		t.Log("ID: ", b.ID)
		t.Fatal("Incorrectly parsed ID")
		fallthrough
	case b.Team != "SEA":
		t.Log("Team: ", b.Team)
		t.Fatal("Incorrectly parsed Team")
		fallthrough
	case b.HR != 22:
		t.Log("Homeruns: ", b.HR)
		t.Fatal("Incorrectly parsed HR")
		fallthrough
	case b.SF != 4:
		t.Log("SF: ", b.SF)
		t.Fatal("Incorrectly parsed SF")
	}
}
<|endoftext|>


<commit_before>// Copyright 2022 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 vulncheck detects uses of known vulnerabilities
in Go programs. The two main APIs of vulncheck, Source
and Binary, allow vulnerability detection in Go source
code and binaries, respectively.

vulncheck identifies vulnerability uses in Go programs
at the level of call graph, package import graph, and module
requires graph. For instance, vulncheck identifies which
vulnerable functions and methods are transitively called
from the program entry points. vulncheck also detects
transitively imported packages and required modules that
contain known vulnerable functions and methods.

A broader overview of vulncheck can be found at https://go.dev/security/vulncheck.

# Inputs

Source accepts a list of vulncheck.Package objects, which
are a trimmed version of packages.Package objects to reduce
memory consumption. Binary accepts a path to a Go binary file
that must have been compiled with Go 1.18 or greater. Otherwise,
the list of modules used by the binary is unavailable and
vulncheck hence might miss vulnerabilities present in the binary.

Both Source and Binary require information about known
vulnerabilities in the form of a vulnerability database
https://golang.org/x/vuln/client#Client. The vulnerabilities
are modeled using the shared https://golang.org/x/vuln/osv format.

# Results

The result of vulncheck are slices of the call graph, package
imports graph, and module requires graph leading to the use
of an identified vulnerability. Parts of these graphs not
related to any vulnerabilities are omitted.

# Vulnerability Witnesses

CallStacks and ImportChains APIs search the returned slices
for user-friendly representative call stacks and import chains.
Clients of vulncheck can use these stacks and chains as a
witness of a vulnerability use during, for instance, security
review.

# Limitations

Please see https://go.dev/security/vulncheck#limitations.
*/
package vulncheck
<commit_msg>vulncheck: update doc.go<commit_after>// Copyright 2022 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 vulncheck detects uses of known vulnerabilities
in Go programs.

vulncheck identifies vulnerability uses in Go programs at the level of call
graph, package import graph, and module requires graph. For instance, vulncheck
identifies which vulnerable functions and methods are transitively called from
the program entry points. vulncheck also detects transitively imported packages
and required modules that contain known vulnerable functions and methods.

A broader overview of vulncheck can be found at
https://go.dev/security/vuln/vulncheck.

# Usage

The two main APIs of vulncheck, Source and Binary, allow vulnerability
detection in Go source code and binaries, respectively.

Source accepts a list of [Package] objects, which are a trimmed version of
[golang.org/x/tools/go/packages.Package] objects to reduce memory consumption.
Binary accepts a path to a Go binary file that must have been compiled with Go
1.18 or greater. Earlier versions omit the list of modules used by the binary,
which vulncheck needs to find vulnerabilities.

Both Source and Binary require information about known vulnerabilities in the
form of a vulnerability database, specifically a
[golang.org/x/vuln/client.Client]. The vulnerabilities are modeled using the
[golang.org/x/vuln/osv] format.

# Results

The results of vulncheck are slices of the call graph, package imports graph,
and module requires graph leading to the use of an identified vulnerability.
Parts of these graphs not related to any vulnerabilities are omitted.

# Vulnerability Witnesses

[CallStacks] and [ImportChains] APIs search the returned slices for
user-friendly representative call stacks and import chains.  Clients of
vulncheck can use these stacks and chains as a witness of a vulnerability use
during, for instance, security review.

# Limitations

Please see the [documented limitations].

[documented limitations]: https://go.dev/security/vulncheck#limitations.
*/
package vulncheck
<|endoftext|>


<commit_before>// Copyright 2015 The Vanadium 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 test

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

	"v.io/x/devtools/internal/collect"
	"v.io/x/devtools/internal/test"
	"v.io/x/devtools/internal/tool"
	"v.io/x/devtools/internal/util"
)

var (
	jenkinsHost = "http://localhost:8001/jenkins"
	netrcFile   = filepath.Join(os.Getenv("HOME"), ".netrc")
)

// requireEnv makes sure that the given environment variables are set.
func requireEnv(names []string) error {
	for _, name := range names {
		if os.Getenv(name) == "" {
			return fmt.Errorf("environment variable %q is not set", name)
		}
	}
	return nil
}

// vanadiumPresubmitPoll polls vanadium projects for new patchsets for
// which to run presubmit tests.
func vanadiumPresubmitPoll(ctx *tool.Context, testName string, _ ...Opt) (_ *test.Result, e error) {
	root, err := util.V23Root()
	if err != nil {
		return nil, err
	}

	// Initialize the test.
	cleanup, err := initTest(ctx, testName, nil)
	if err != nil {
		return nil, internalTestError{err, "Init"}
	}
	defer collect.Error(func() error { return cleanup() }, &e)

	// Use the "presubmit query" command to poll for new changes.
	logfile := filepath.Join(root, ".presubmit_log")
	args := []string{}
	if ctx.Verbose() {
		args = append(args, "-v")
	}
	args = append(args,
		"-host", jenkinsHost,
		"-netrc", netrcFile,
		"query",
		"-log-file", logfile,
		"-manifest", "tools",
	)
	if err := ctx.Run().Command("presubmit", args...); err != nil {
		return nil, err
	}

	return &test.Result{Status: test.Passed}, nil
}

// vanadiumPresubmitTest runs presubmit tests for a given project specified
// in TEST environment variable.
func vanadiumPresubmitTest(ctx *tool.Context, testName string, _ ...Opt) (_ *test.Result, e error) {
	if err := requireEnv([]string{"BUILD_NUMBER", "REFS", "PROJECTS", "TEST", "WORKSPACE"}); err != nil {
		return nil, err
	}

	// Initialize the test.
	cleanup, err := initTest(ctx, testName, nil)
	if err != nil {
		return nil, internalTestError{err, "Init"}
	}
	defer collect.Error(func() error { return cleanup() }, &e)

	// Use the "presubmit test" command to run the presubmit test.
	args := []string{}
	if ctx.Verbose() {
		args = append(args, "-v")
	}
	name := os.Getenv("TEST")
	args = append(args,
		"-host", jenkinsHost,
		"-netrc", netrcFile,
		"test",
		"-build-number", os.Getenv("BUILD_NUMBER"),
		"-manifest", "tools",
		"-projects", os.Getenv("PROJECTS"),
		"-refs", os.Getenv("REFS"),
		"-test", name,
	)
	if err := ctx.Run().Command("presubmit", args...); err != nil {
		return nil, err
	}

	// Remove any test result files that are empty.
	testResultFiles, err := findTestResultFiles(ctx, name)
	if err != nil {
		return nil, err
	}
	for _, file := range testResultFiles {
		fileInfo, err := os.Stat(file)
		if err != nil {
			return nil, err
		}
		if fileInfo.Size() == 0 {
			if err := ctx.Run().RemoveAll(file); err != nil {
				return nil, err
			}
		}
	}

	return &test.Result{Status: test.Passed}, nil
}

// vanadiumPresubmitResult runs "presubmit result" command to process and post test resutls.
func vanadiumPresubmitResult(ctx *tool.Context, testName string, _ ...Opt) (_ *test.Result, e error) {
	if err := requireEnv([]string{"BUILD_NUMBER", "REFS", "PROJECTS", "WORKSPACE"}); err != nil {
		return nil, err
	}

	// Initialize the test.
	cleanup, err := initTest(ctx, testName, nil)
	if err != nil {
		return nil, internalTestError{err, "Init"}
	}
	defer collect.Error(func() error { return cleanup() }, &e)

	// Run "presubmit result".
	args := []string{}
	if ctx.Verbose() {
		args = append(args, "-v")
	}
	args = append(args,
		"-host", jenkinsHost,
		"-netrc", netrcFile,
		"result",
		"-build-number", os.Getenv("BUILD_NUMBER"),
		"-manifest", "tools",
		"-refs", os.Getenv("REFS"),
		"-projects", os.Getenv("PROJECTS"),
	)
	if err := ctx.Run().Command("presubmit", args...); err != nil {
		return nil, err
	}

	return &test.Result{Status: test.Passed}, nil
}
<commit_msg>TBR v23/devtools: fixing the presubmit test targets<commit_after>// Copyright 2015 The Vanadium 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 test

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

	"v.io/x/devtools/internal/collect"
	"v.io/x/devtools/internal/test"
	"v.io/x/devtools/internal/tool"
	"v.io/x/devtools/internal/util"
)

var (
	jenkinsHost = "http://localhost:8001/jenkins"
)

// requireEnv makes sure that the given environment variables are set.
func requireEnv(names []string) error {
	for _, name := range names {
		if os.Getenv(name) == "" {
			return fmt.Errorf("environment variable %q is not set", name)
		}
	}
	return nil
}

// vanadiumPresubmitPoll polls vanadium projects for new patchsets for
// which to run presubmit tests.
func vanadiumPresubmitPoll(ctx *tool.Context, testName string, _ ...Opt) (_ *test.Result, e error) {
	root, err := util.V23Root()
	if err != nil {
		return nil, err
	}

	// Initialize the test.
	cleanup, err := initTest(ctx, testName, nil)
	if err != nil {
		return nil, internalTestError{err, "Init"}
	}
	defer collect.Error(func() error { return cleanup() }, &e)

	// Use the "presubmit query" command to poll for new changes.
	logfile := filepath.Join(root, ".presubmit_log")
	args := []string{}
	if ctx.Verbose() {
		args = append(args, "-v")
	}
	args = append(args,
		"-host", jenkinsHost,
		"query",
		"-log-file", logfile,
		"-manifest", "tools",
	)
	if err := ctx.Run().Command("presubmit", args...); err != nil {
		return nil, err
	}

	return &test.Result{Status: test.Passed}, nil
}

// vanadiumPresubmitTest runs presubmit tests for a given project specified
// in TEST environment variable.
func vanadiumPresubmitTest(ctx *tool.Context, testName string, _ ...Opt) (_ *test.Result, e error) {
	if err := requireEnv([]string{"BUILD_NUMBER", "REFS", "PROJECTS", "TEST", "WORKSPACE"}); err != nil {
		return nil, err
	}

	// Initialize the test.
	cleanup, err := initTest(ctx, testName, nil)
	if err != nil {
		return nil, internalTestError{err, "Init"}
	}
	defer collect.Error(func() error { return cleanup() }, &e)

	// Use the "presubmit test" command to run the presubmit test.
	args := []string{}
	if ctx.Verbose() {
		args = append(args, "-v")
	}
	name := os.Getenv("TEST")
	args = append(args,
		"-host", jenkinsHost,
		"test",
		"-build-number", os.Getenv("BUILD_NUMBER"),
		"-manifest", "tools",
		"-projects", os.Getenv("PROJECTS"),
		"-refs", os.Getenv("REFS"),
		"-test", name,
	)
	if err := ctx.Run().Command("presubmit", args...); err != nil {
		return nil, err
	}

	// Remove any test result files that are empty.
	testResultFiles, err := findTestResultFiles(ctx, name)
	if err != nil {
		return nil, err
	}
	for _, file := range testResultFiles {
		fileInfo, err := os.Stat(file)
		if err != nil {
			return nil, err
		}
		if fileInfo.Size() == 0 {
			if err := ctx.Run().RemoveAll(file); err != nil {
				return nil, err
			}
		}
	}

	return &test.Result{Status: test.Passed}, nil
}

// vanadiumPresubmitResult runs "presubmit result" command to process and post test resutls.
func vanadiumPresubmitResult(ctx *tool.Context, testName string, _ ...Opt) (_ *test.Result, e error) {
	if err := requireEnv([]string{"BUILD_NUMBER", "REFS", "PROJECTS", "WORKSPACE"}); err != nil {
		return nil, err
	}

	// Initialize the test.
	cleanup, err := initTest(ctx, testName, nil)
	if err != nil {
		return nil, internalTestError{err, "Init"}
	}
	defer collect.Error(func() error { return cleanup() }, &e)

	// Run "presubmit result".
	args := []string{}
	if ctx.Verbose() {
		args = append(args, "-v")
	}
	args = append(args,
		"-host", jenkinsHost,
		"result",
		"-build-number", os.Getenv("BUILD_NUMBER"),
		"-manifest", "tools",
		"-refs", os.Getenv("REFS"),
		"-projects", os.Getenv("PROJECTS"),
	)
	if err := ctx.Run().Command("presubmit", args...); err != nil {
		return nil, err
	}

	return &test.Result{Status: test.Passed}, nil
}
<|endoftext|>


<commit_before>package examples

import (
	"flag"
	"os"
	"os/signal"

	"github.com/slf4go/logger"
)

var (
	appQuit chan bool
	Token   string
)

func init() {
	logger.SetLevel(logger.LogAll)

	flag.StringVar(&Token, "token", "", "Token for the bot")
	flag.Parse()

	appQuit = make(chan bool)
	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, os.Interrupt)
	go func() { <-signalChan; appQuit <- true }()
}

func WaitForExit() {
	<-appQuit
}
<commit_msg>Example now terminates properly if a second interrupt is sent<commit_after>package examples

import (
	"flag"
	"os"
	"os/signal"

	"github.com/slf4go/logger"
)

var (
	appQuit chan bool
	Token   string
)

func init() {
	logger.SetLevel(logger.LogAll)

	flag.StringVar(&Token, "token", "", "Token for the bot")
	flag.Parse()

	appQuit = make(chan bool)
	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, os.Interrupt)
	go func() {
		<-signalChan
		appQuit <- true

		go func() {
			<-signalChan
			os.Exit(1)
		}()
	}()
}

func WaitForExit() {
	<-appQuit
}
<|endoftext|>


<commit_before>package walkngo

import (
	"bytes"
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"io"
	"strings"

	"github.com/raff/walkngo/printer"
)

//
// GoWalker is the context for the AST visitor
//
type GoWalker struct {
	p      printer.Printer
	parent ast.Node
	flush  bool
	buffer bytes.Buffer
	writer io.Writer
	debug  bool
}

func NewWalker(p printer.Printer, out io.Writer, debug bool) *GoWalker {
	w := GoWalker{p: p, flush: true, writer: out, debug: debug}
	p.SetWriter(&w.buffer)
	return &w
}

func (w *GoWalker) SetWriter(writer io.Writer) (old io.Writer) {
	w.Flush()

	old, w.writer = w.writer, writer
	return
}

func (w *GoWalker) WalkFile(filename string) error {
	fset := token.NewFileSet() // positions are relative to fset

	f, err := parser.ParseFile(fset, filename, nil, 0)
	if err != nil {
		return err
	}

	w.p.Print(fmt.Sprintf("//source: %s\n", filename))

	ast.Walk(w, f)
	return nil
}

//
// Implement the Visitor interface for GoWalker
//
func (w *GoWalker) Visit(node ast.Node) (ret ast.Visitor) {
	if node == nil {
		return
	}

	if w.debug {
		w.p.Print(fmt.Sprintf("/* Node: %#v */\n", node))
	}

	pparent := w.parent
	w.parent = node

	switch n := node.(type) {
	case *ast.File:
		w.p.PrintPackage(n.Name.String())
		for _, d := range n.Decls {
			w.Visit(d)
		}

	case *ast.ImportSpec:
		w.p.PrintImport(w.parseExpr(n.Name), n.Path.Value)

	case *ast.TypeSpec:
		w.p.PrintType(n.Name.String(), w.parseExpr(n.Type))

	case *ast.ValueSpec:
		vtype := (pparent.(*ast.GenDecl)).Tok.String()
		w.p.PrintValue(vtype, w.parseExpr(n.Type), w.parseNames(n.Names), w.parseExprList(n.Values), len(n.Names) > 1, len(n.Values) > 1)

	case *ast.GenDecl:
		w.p.Print("\n")
		for _, s := range n.Specs {
			w.Visit(s)
		}

	case *ast.FuncDecl:
		w.p.Print("\n")
		w.p.PrintFunc(w.parseFieldList(n.Recv, printer.RECEIVER),
			n.Name.String(),
			w.parseFieldList(n.Type.Params, printer.PARAM),
			w.parseFieldList(n.Type.Results, printer.RESULT))
		w.Visit(n.Body)
		w.p.Print("\n")

	case *ast.BlockStmt:
		w.p.PrintBlockStart()
		for _, i := range n.List {
			w.Visit(i)
		}
		w.p.PrintBlockEnd()

	case *ast.IfStmt:
		if !w.p.IsSameLine() {
			w.p.Print("\n")
		}
		w.p.PrintIf(w.BufferVisit(n.Init), w.parseExpr(n.Cond))
		w.p.SameLine()
		w.Visit(n.Body)
		if n.Else != nil {
			w.p.SameLine()
			w.p.PrintElse()
			w.p.SameLine()
			w.Visit(n.Else)
		}
		w.p.Print("\n")

	case *ast.ForStmt:
		w.p.Print("\n")
		w.p.PrintFor(w.BufferVisit(n.Init), w.parseExpr(n.Cond), w.BufferVisit(n.Post))
		w.Visit(n.Body)
		w.p.Print("\n")

	case *ast.SwitchStmt:
		w.p.Print("\n")
		w.p.PrintSwitch(w.BufferVisit(n.Init), w.parseExpr(n.Tag))
		w.Visit(n.Body)
		w.p.Print("\n")

	case *ast.TypeSwitchStmt:
		w.p.Print("\n")
		w.p.PrintSwitch(w.BufferVisit(n.Init), w.BufferVisit(n.Assign))
		w.Visit(n.Body)
		w.p.Print("\n")

	case *ast.CaseClause:
		w.p.PrintCase(w.parseExprList(n.List))
		w.p.UpdateLevel(printer.UP)
		for _, i := range n.Body {
			w.Visit(i)
		}
		w.p.PrintEndCase()
		w.p.UpdateLevel(printer.DOWN)

	case *ast.RangeStmt:
		w.p.Print("\n")
		w.p.PrintRange(w.parseExpr(n.Key), w.parseExpr(n.Value), w.parseExpr(n.X))
		w.Visit(n.Body)
		w.p.Print("\n")

	case *ast.BranchStmt:
		w.p.PrintStmt(n.Tok.String(), w.parseExpr(n.Label))

	case *ast.DeferStmt:
		w.p.PrintStmt("defer", w.parseExpr(n.Call))

	case *ast.GoStmt:
		w.p.PrintStmt("go", w.parseExpr(n.Call))

	case *ast.ReturnStmt:
		w.p.PrintReturn(w.parseExprList(n.Results), len(n.Results) > 1)

	case *ast.ExprStmt:
		w.p.PrintStmt("", w.parseExpr(n.X))

	case *ast.DeclStmt:
		w.Visit(n.Decl)

	case *ast.AssignStmt:
		w.p.PrintAssignment(w.parseExprList(n.Lhs), n.Tok.String(), w.parseExprList(n.Rhs), len(n.Lhs) > 1, len(n.Rhs) > 1)

	case *ast.IncDecStmt:
		w.p.PrintStmt("", w.parseExpr(n.X)+n.Tok.String())

	case *ast.SendStmt:
		w.p.PrintSend(w.parseExpr(n.Chan), w.parseExpr(n.Value))

	case *ast.EmptyStmt:
		w.p.PrintEmpty()

	default:
		w.p.Print(fmt.Sprintf("/* Node: %#v */\n", n))
		ret = w
	}

	w.Flush()

	w.parent = pparent
	return
}

func (w *GoWalker) Flush() {
	if w.flush && w.buffer.Len() > 0 {
		w.buffer.WriteTo(w.writer)
		w.buffer.Reset()
	}
}

func (w *GoWalker) BufferVisit(node ast.Node) (ret string) {
	w.Flush()

	prev := w.flush
	w.flush = false

	w.Visit(node)

	w.flush = prev

	ret = w.buffer.String()
	w.buffer.Reset()

	if prev == true {
		ret = strings.TrimSpace(ret)
	}

	return
}

func (w *GoWalker) parseExpr(expr interface{}) string {
	if expr == nil {
		return ""
	}

	if w.debug {
		w.p.Print(fmt.Sprintf("/* Expr: %#v */\n", expr))
	}

	switch expr := expr.(type) {

	// a name or a predefined constant
	case *ast.Ident:
		if expr == nil {
			return ""
		}
		return w.p.FormatIdent(expr.Name)

		// *thing
	case *ast.StarExpr:
		return w.p.FormatStar(w.parseExpr(expr.X))

		// [len]type
	case *ast.ArrayType:
		return w.p.FormatArray(w.parseExpr(expr.Len), w.parseExpr(expr.Elt))

		// [key]value
	case *ast.MapType:
		return w.p.FormatMap(w.parseExpr(expr.Key), w.parseExpr(expr.Value))

		// interface{ things }
	case *ast.InterfaceType:
		w.p.UpdateLevel(printer.UP)
		ret := w.p.FormatInterface(w.parseFieldList(expr.Methods, printer.METHOD))
		w.p.UpdateLevel(printer.DOWN)
		return ret

		// struct{ things }
	case *ast.StructType:
		w.p.UpdateLevel(printer.UP)
		ret := w.p.FormatStruct(w.parseFieldList(expr.Fields, printer.FIELD))
		w.p.UpdateLevel(printer.DOWN)
		return ret

		// <-chan type
	case *ast.ChanType:
		chdir := printer.CHAN_BIDI
		if expr.Dir == ast.SEND {
			chdir = printer.CHAN_SEND
		} else if expr.Dir == ast.RECV {
			chdir = printer.CHAN_RECV
		}
		return w.p.FormatChan(chdir, w.parseExpr(expr.Value))

		// (params) (results)
	case *ast.FuncType:
		return w.p.FormatFuncType(
			w.parseFieldList(expr.Params, printer.PARAM),
			w.parseFieldList(expr.Results, printer.RESULT))

		// "thing", 0, 1.2, 'x', etc.
	case *ast.BasicLit:
		return w.p.FormatLiteral(expr.Value)

		// type{list}
	case *ast.CompositeLit:
		return w.p.FormatCompositeLit(w.parseExpr(expr.Type), w.parseExprList(expr.Elts))

		// ...type
	case *ast.Ellipsis:
		return w.p.FormatEllipsis(w.parseExpr(expr.Elt))

		// -3
	case *ast.UnaryExpr:
		return w.p.FormatUnary(expr.Op.String(), w.parseExpr(expr.X))

		// 3 + 2
	case *ast.BinaryExpr:
		return w.p.FormatBinary(w.parseExpr(expr.X), expr.Op.String(), w.parseExpr(expr.Y))

		// array[index]
	case *ast.IndexExpr:
		return w.p.FormatArrayIndex(w.parseExpr(expr.X), w.parseExpr(expr.Index))

		// key: value
	case *ast.KeyValueExpr:
		return w.p.FormatKeyValue(w.parseExpr(expr.Key), w.parseExpr(expr.Value))

		// x[low:hi:max]
	case *ast.SliceExpr:
		return w.p.FormatSlice(w.parseExpr(expr.X), w.parseExpr(expr.Low), w.parseExpr(expr.High), w.parseExpr(expr.Max))

		// package.member
	case *ast.SelectorExpr:
		ident, isObj := expr.X.(*ast.Ident)
		if isObj {
			isObj = ident.Obj != nil
		}
		return w.p.FormatSelector(w.parseExpr(expr.X), w.parseExpr(expr.Sel), isObj)

		// funcname(args)
	case *ast.CallExpr:
		_, funclit := expr.Fun.(*ast.FuncLit)
		return w.p.FormatCall(w.parseExpr(expr.Fun), w.parseExprList(expr.Args)+ifTrue("...", expr.Ellipsis > 0), funclit)

		// name.(type)
	case *ast.TypeAssertExpr:
		return w.p.FormatTypeAssert(w.parseExpr(expr.X), w.exprOr(expr.Type, "type"))

		// (expr)
	case *ast.ParenExpr:
		return w.p.FormatParen(w.parseExpr(expr.X))

		// func(params) (ret) { body }
	case *ast.FuncLit:
		return w.p.FormatFuncLit(w.parseExpr(expr.Type), w.BufferVisit(expr.Body))
	}

	return fmt.Sprintf("/* Expr: %#v */", expr)
}

func (w *GoWalker) parseExprList(l []ast.Expr) string {
	exprs := []string{}
	for _, e := range l {
		exprs = append(exprs, w.parseExpr(e))
	}
	return strings.Join(exprs, ", ")
}

func (w *GoWalker) parseFieldList(l *ast.FieldList, ftype printer.FieldType) string {
	buffer := bytes.NewBufferString("")

	if l != nil {
		for _, f := range l.List {
			field := printer.Pair{w.parseNames(f.Names), w.parseExpr(f.Type)}
			buffer.WriteString(w.p.FormatPair(field, ftype))
		}
	}

	return w.p.Chop(buffer.String())
}

func (w *GoWalker) parseNames(v []*ast.Ident) string {
	names := make([]string, len(v))

	for i, n := range v {
		names[i] = n.Name
	}

	return strings.Join(names, ", ")
}

func (w *GoWalker) exprOr(expr ast.Expr, v string) string {
	if expr != nil {
		return w.parseExpr(expr)
	} else {
		return v
	}
}

//
// ifTrue retruns the input value if the condition is true, an empty string otherwise
//
func ifTrue(val string, cond bool) (ret string) {
	if cond {
		ret = val
	}
	return
}
<commit_msg>Changed parseFieldList so that now each field has a type (instead of doing the "Go" way where there is a type and a list of fields). This makes it easy to process each field individually at the "printer" level (and got "Go" we could potentially "collapse" the types there too).<commit_after>package walkngo

import (
	"bytes"
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"io"
	"strings"

	"github.com/raff/walkngo/printer"
)

//
// GoWalker is the context for the AST visitor
//
type GoWalker struct {
	p      printer.Printer
	parent ast.Node
	flush  bool
	buffer bytes.Buffer
	writer io.Writer
	debug  bool
}

func NewWalker(p printer.Printer, out io.Writer, debug bool) *GoWalker {
	w := GoWalker{p: p, flush: true, writer: out, debug: debug}
	p.SetWriter(&w.buffer)
	return &w
}

func (w *GoWalker) SetWriter(writer io.Writer) (old io.Writer) {
	w.Flush()

	old, w.writer = w.writer, writer
	return
}

func (w *GoWalker) WalkFile(filename string) error {
	fset := token.NewFileSet() // positions are relative to fset

	f, err := parser.ParseFile(fset, filename, nil, 0)
	if err != nil {
		return err
	}

	w.p.Print(fmt.Sprintf("//source: %s\n", filename))

	ast.Walk(w, f)
	return nil
}

//
// Implement the Visitor interface for GoWalker
//
func (w *GoWalker) Visit(node ast.Node) (ret ast.Visitor) {
	if node == nil {
		return
	}

	if w.debug {
		w.p.Print(fmt.Sprintf("/* Node: %#v */\n", node))
	}

	pparent := w.parent
	w.parent = node

	switch n := node.(type) {
	case *ast.File:
		w.p.PrintPackage(n.Name.String())
		for _, d := range n.Decls {
			w.Visit(d)
		}

	case *ast.ImportSpec:
		w.p.PrintImport(w.parseExpr(n.Name), n.Path.Value)

	case *ast.TypeSpec:
		w.p.PrintType(n.Name.String(), w.parseExpr(n.Type))

	case *ast.ValueSpec:
		vtype := (pparent.(*ast.GenDecl)).Tok.String()
		w.p.PrintValue(vtype, w.parseExpr(n.Type), w.parseNames(n.Names), w.parseExprList(n.Values), len(n.Names) > 1, len(n.Values) > 1)

	case *ast.GenDecl:
		w.p.Print("\n")
		for _, s := range n.Specs {
			w.Visit(s)
		}

	case *ast.FuncDecl:
		w.p.Print("\n")
		w.p.PrintFunc(w.parseFieldList(n.Recv, printer.RECEIVER),
			n.Name.String(),
			w.parseFieldList(n.Type.Params, printer.PARAM),
			w.parseFieldList(n.Type.Results, printer.RESULT))
		w.Visit(n.Body)
		w.p.Print("\n")

	case *ast.BlockStmt:
		w.p.PrintBlockStart()
		for _, i := range n.List {
			w.Visit(i)
		}
		w.p.PrintBlockEnd()

	case *ast.IfStmt:
		if !w.p.IsSameLine() {
			w.p.Print("\n")
		}
		w.p.PrintIf(w.BufferVisit(n.Init), w.parseExpr(n.Cond))
		w.p.SameLine()
		w.Visit(n.Body)
		if n.Else != nil {
			w.p.SameLine()
			w.p.PrintElse()
			w.p.SameLine()
			w.Visit(n.Else)
		}
		w.p.Print("\n")

	case *ast.ForStmt:
		w.p.Print("\n")
		w.p.PrintFor(w.BufferVisit(n.Init), w.parseExpr(n.Cond), w.BufferVisit(n.Post))
		w.Visit(n.Body)
		w.p.Print("\n")

	case *ast.SwitchStmt:
		w.p.Print("\n")
		w.p.PrintSwitch(w.BufferVisit(n.Init), w.parseExpr(n.Tag))
		w.Visit(n.Body)
		w.p.Print("\n")

	case *ast.TypeSwitchStmt:
		w.p.Print("\n")
		w.p.PrintSwitch(w.BufferVisit(n.Init), w.BufferVisit(n.Assign))
		w.Visit(n.Body)
		w.p.Print("\n")

	case *ast.CaseClause:
		w.p.PrintCase(w.parseExprList(n.List))
		w.p.UpdateLevel(printer.UP)
		for _, i := range n.Body {
			w.Visit(i)
		}
		w.p.PrintEndCase()
		w.p.UpdateLevel(printer.DOWN)

	case *ast.RangeStmt:
		w.p.Print("\n")
		w.p.PrintRange(w.parseExpr(n.Key), w.parseExpr(n.Value), w.parseExpr(n.X))
		w.Visit(n.Body)
		w.p.Print("\n")

	case *ast.BranchStmt:
		w.p.PrintStmt(n.Tok.String(), w.parseExpr(n.Label))

	case *ast.DeferStmt:
		w.p.PrintStmt("defer", w.parseExpr(n.Call))

	case *ast.GoStmt:
		w.p.PrintStmt("go", w.parseExpr(n.Call))

	case *ast.ReturnStmt:
		w.p.PrintReturn(w.parseExprList(n.Results), len(n.Results) > 1)

	case *ast.ExprStmt:
		w.p.PrintStmt("", w.parseExpr(n.X))

	case *ast.DeclStmt:
		w.Visit(n.Decl)

	case *ast.AssignStmt:
		w.p.PrintAssignment(w.parseExprList(n.Lhs), n.Tok.String(), w.parseExprList(n.Rhs), len(n.Lhs) > 1, len(n.Rhs) > 1)

	case *ast.IncDecStmt:
		w.p.PrintStmt("", w.parseExpr(n.X)+n.Tok.String())

	case *ast.SendStmt:
		w.p.PrintSend(w.parseExpr(n.Chan), w.parseExpr(n.Value))

	case *ast.EmptyStmt:
		w.p.PrintEmpty()

	default:
		w.p.Print(fmt.Sprintf("/* Node: %#v */\n", n))
		ret = w
	}

	w.Flush()

	w.parent = pparent
	return
}

func (w *GoWalker) Flush() {
	if w.flush && w.buffer.Len() > 0 {
		w.buffer.WriteTo(w.writer)
		w.buffer.Reset()
	}
}

func (w *GoWalker) BufferVisit(node ast.Node) (ret string) {
	w.Flush()

	prev := w.flush
	w.flush = false

	w.Visit(node)

	w.flush = prev

	ret = w.buffer.String()
	w.buffer.Reset()

	if prev == true {
		ret = strings.TrimSpace(ret)
	}

	return
}

func (w *GoWalker) parseExpr(expr interface{}) string {
	if expr == nil {
		return ""
	}

	if w.debug {
		w.p.Print(fmt.Sprintf("/* Expr: %#v */\n", expr))
	}

	switch expr := expr.(type) {

	// a name or a predefined constant
	case *ast.Ident:
		if expr == nil {
			return ""
		}
		return w.p.FormatIdent(expr.Name)

		// *thing
	case *ast.StarExpr:
		return w.p.FormatStar(w.parseExpr(expr.X))

		// [len]type
	case *ast.ArrayType:
		return w.p.FormatArray(w.parseExpr(expr.Len), w.parseExpr(expr.Elt))

		// [key]value
	case *ast.MapType:
		return w.p.FormatMap(w.parseExpr(expr.Key), w.parseExpr(expr.Value))

		// interface{ things }
	case *ast.InterfaceType:
		w.p.UpdateLevel(printer.UP)
		ret := w.p.FormatInterface(w.parseFieldList(expr.Methods, printer.METHOD))
		w.p.UpdateLevel(printer.DOWN)
		return ret

		// struct{ things }
	case *ast.StructType:
		w.p.UpdateLevel(printer.UP)
		ret := w.p.FormatStruct(w.parseFieldList(expr.Fields, printer.FIELD))
		w.p.UpdateLevel(printer.DOWN)
		return ret

		// <-chan type
	case *ast.ChanType:
		chdir := printer.CHAN_BIDI
		if expr.Dir == ast.SEND {
			chdir = printer.CHAN_SEND
		} else if expr.Dir == ast.RECV {
			chdir = printer.CHAN_RECV
		}
		return w.p.FormatChan(chdir, w.parseExpr(expr.Value))

		// (params) (results)
	case *ast.FuncType:
		return w.p.FormatFuncType(
			w.parseFieldList(expr.Params, printer.PARAM),
			w.parseFieldList(expr.Results, printer.RESULT))

		// "thing", 0, 1.2, 'x', etc.
	case *ast.BasicLit:
		return w.p.FormatLiteral(expr.Value)

		// type{list}
	case *ast.CompositeLit:
		return w.p.FormatCompositeLit(w.parseExpr(expr.Type), w.parseExprList(expr.Elts))

		// ...type
	case *ast.Ellipsis:
		return w.p.FormatEllipsis(w.parseExpr(expr.Elt))

		// -3
	case *ast.UnaryExpr:
		return w.p.FormatUnary(expr.Op.String(), w.parseExpr(expr.X))

		// 3 + 2
	case *ast.BinaryExpr:
		return w.p.FormatBinary(w.parseExpr(expr.X), expr.Op.String(), w.parseExpr(expr.Y))

		// array[index]
	case *ast.IndexExpr:
		return w.p.FormatArrayIndex(w.parseExpr(expr.X), w.parseExpr(expr.Index))

		// key: value
	case *ast.KeyValueExpr:
		return w.p.FormatKeyValue(w.parseExpr(expr.Key), w.parseExpr(expr.Value))

		// x[low:hi:max]
	case *ast.SliceExpr:
		return w.p.FormatSlice(w.parseExpr(expr.X), w.parseExpr(expr.Low), w.parseExpr(expr.High), w.parseExpr(expr.Max))

		// package.member
	case *ast.SelectorExpr:
		ident, isObj := expr.X.(*ast.Ident)
		if isObj {
			isObj = ident.Obj != nil
		}
		return w.p.FormatSelector(w.parseExpr(expr.X), w.parseExpr(expr.Sel), isObj)

		// funcname(args)
	case *ast.CallExpr:
		_, funclit := expr.Fun.(*ast.FuncLit)
		return w.p.FormatCall(w.parseExpr(expr.Fun), w.parseExprList(expr.Args)+ifTrue("...", expr.Ellipsis > 0), funclit)

		// name.(type)
	case *ast.TypeAssertExpr:
		return w.p.FormatTypeAssert(w.parseExpr(expr.X), w.exprOr(expr.Type, "type"))

		// (expr)
	case *ast.ParenExpr:
		return w.p.FormatParen(w.parseExpr(expr.X))

		// func(params) (ret) { body }
	case *ast.FuncLit:
		return w.p.FormatFuncLit(w.parseExpr(expr.Type), w.BufferVisit(expr.Body))
	}

	return fmt.Sprintf("/* Expr: %#v */", expr)
}

func (w *GoWalker) parseExprList(l []ast.Expr) string {
	exprs := []string{}
	for _, e := range l {
		exprs = append(exprs, w.parseExpr(e))
	}
	return strings.Join(exprs, ", ")
}

func (w *GoWalker) parseFieldList(l *ast.FieldList, ftype printer.FieldType) string {
	buffer := bytes.NewBufferString("")

	if l != nil {
		for _, f := range l.List {
			ptype := w.parseExpr(f.Type)

			for _, n := range f.Names {
				field := printer.Pair{n.Name, ptype}
				buffer.WriteString(w.p.FormatPair(field, ftype))
			}
		}
	}

	return w.p.Chop(buffer.String())
}

func (w *GoWalker) parseNames(v []*ast.Ident) string {
	names := make([]string, len(v))

	for i, n := range v {
		names[i] = n.Name
	}

	return strings.Join(names, ", ")
}

func (w *GoWalker) exprOr(expr ast.Expr, v string) string {
	if expr != nil {
		return w.parseExpr(expr)
	} else {
		return v
	}
}

//
// ifTrue retruns the input value if the condition is true, an empty string otherwise
//
func ifTrue(val string, cond bool) (ret string) {
	if cond {
		ret = val
	}
	return
}
<|endoftext|>