<commit_before>package tmpl

import (
	"errors"
	"fmt"
	"html/template"
	"path"
	"path/filepath"
	"reflect"
	"strconv"
	"strings"

	gateway "github.com/gengo/grpc-gateway/protoc-gen-grpc-gateway/descriptor"
	"github.com/gengo/grpc-gateway/protoc-gen-grpc-gateway/httprule"
	descriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
	"sourcegraph.com/sourcegraph/prototools/util"
)

// unixPath takes a path, cleans it, and replaces any windows separators (\\)
// with unix ones (/). This is needed because plugin.CodeGeneratorResponse_File
// is defined as having always unix path separators for the file name.
func unixPath(s string) string {
	s = filepath.Clean(s)
	s = strings.Replace(s, "\\", "/", -1)

	// Duplicate clean for trailing slashes that were previously windows ones.
	return filepath.Clean(s)
}

// stripExt strips the extension off the path and returns it.
func stripExt(s string) string {
	ext := filepath.Ext(s)
	if len(ext) > 0 {
		return s[:len(s)-len(ext)]
	}
	return s
}

var Preload = (&tmplFuncs{}).funcMap()

// cacheItem is a single cache item with a value and a location -- effectively
// it is just used for searching.
type cacheItem struct {
	V interface{}
	L *descriptor.SourceCodeInfo_Location
}

// Functions exposed to templates. The user of the package must first preload
// the FuncMap above for these to be called properly (as they are actually
// closures with context).
type tmplFuncs struct {
	f                   *descriptor.FileDescriptorProto
	outputFile, rootDir string
	protoFile           []*descriptor.FileDescriptorProto
	registry            *gateway.Registry
	apiHost             string

	locCache []cacheItem
}

// funcMap returns the function map for feeding into templates.
func (f *tmplFuncs) funcMap() template.FuncMap {
	return map[string]interface{}{
		"cleanLabel": f.cleanLabel,
		"cleanType":  f.cleanType,
		"fieldType":  f.fieldType,
		"dict":       f.dict,
		"ext":        filepath.Ext,
		"dir": func(s string) string {
			dir, _ := path.Split(s)
			return dir
		},
		"trimExt":       stripExt,
		"sub":           f.sub,
		"filepath":      f.filepath,
		"gatewayMethod": f.gatewayMethod,
		"gatewayPath":   f.gatewayPath,
		"urlToType":     f.urlToType,
		"jsonMessage":   f.jsonMessage,
		"location":      f.location,
		"AllMessages": func(fixNames bool) []*descriptor.DescriptorProto {
			return util.AllMessages(f.f, fixNames)
		},
		"AllEnums": func(fixNames bool) []*descriptor.EnumDescriptorProto {
			return util.AllEnums(f.f, fixNames)
		},
	}
}

// cleanLabel returns the clean (i.e. human-readable / protobuf-style) version
// of a label.
func (f *tmplFuncs) cleanLabel(l *descriptor.FieldDescriptorProto_Label) string {
	switch int32(*l) {
	case 1:
		return "optional"
	case 2:
		return "required"
	case 3:
		return "repeated"
	default:
		panic("unknown label")
	}
}

// cleanType returns the last part of a types name, i.e. for a fully-qualified
// type ".foo.bar.baz" it would return just "baz".
func (f *tmplFuncs) cleanType(path string) string {
	split := strings.Split(path, ".")
	return split[len(split)-1]
}

// fieldType returns the clean (i.e. human-readable / protobuf-style) version
// of a field type.
func (f *tmplFuncs) fieldType(field *descriptor.FieldDescriptorProto) string {
	if field.TypeName != nil {
		return f.cleanType(*field.TypeName)
	}
	return util.FieldTypeName(field.Type)
}

// dict builds a map of paired items, allowing you to invoke a template with
// multiple parameters.
func (f *tmplFuncs) dict(pairs ...interface{}) (map[string]interface{}, error) {
	if len(pairs)%2 != 0 {
		return nil, errors.New("expected pairs")
	}
	m := make(map[string]interface{}, len(pairs)/2)
	for i := 0; i < len(pairs); i += 2 {
		m[pairs[i].(string)] = pairs[i+1]
	}
	return m, nil
}

// sub performs simple x-y subtraction on integers.
func (f *tmplFuncs) sub(x, y int) int { return x - y }

// filepath returns the output filepath (prefixed by the root directory).
func (f *tmplFuncs) filepath() string {
	return path.Join(f.rootDir, f.outputFile)
}

// gatewayMethod returns the grpc-gateway method for a given service method.
func (f *tmplFuncs) gatewayMethod(target *descriptor.MethodDescriptorProto) (*gateway.Method, error) {
	file, err := f.registry.LookupFile(f.f.GetName())
	if err != nil {
		return nil, err
	}
	for _, s := range file.Services {
		for _, m := range s.Methods {
			if m.MethodDescriptorProto == target {
				return m, nil
			}
		}
	}
	return nil, nil
}

// gatewayPath renders the given grpc-gateway HTTP rule template (i.e. the HTTP
// route to be bound). The method parameter is used to insert a link to the
// method type for any HTTP fields (which will be marked clearly in "{text}").
//
// The returned string will always be prefixed by the APIHost string.
func (f *tmplFuncs) gatewayPath(r *httprule.Template, method *descriptor.MethodDescriptorProto) template.HTML {
	var final string
pool:
	for _, pathElem := range r.Pool {
		for _, fieldName := range r.Fields {
			if pathElem != fieldName {
				continue
			}
			u := fmt.Sprintf(`<a href="%s">{%s}</a>`, f.urlToType(method.GetInputType()), pathElem)
			final = path.Join(final, u)
			continue pool
		}
		final = path.Join(final, pathElem)
	}
	return template.HTML(f.apiHost + final)
}

// urlToType returns a URL to the documentation file for the given type. The
// input type path can be either fully-qualified or not, regardless, the URL
// returned will always have a fully-qualified hash.
//
// TODO(slimsag): have the template pass in the relative type instead of nil,
// so that relative symbol paths work.
func (f *tmplFuncs) urlToType(symbolPath string) string {
	if !util.IsFullyQualified(symbolPath) {
		panic("urlToType: not a fully-qualified symbol path")
	}

	// Resolve the package path for the type.
	file := util.NewResolver(f.protoFile).ResolveFile(symbolPath, nil)
	if file == nil {
		return ""
	}
	pkgPath := file.GetName()

	// Remove the package prefix from types, for example:
	//
	//  pkg.html#.pkg.Type.SubType
	//  ->
	//  pkg.html#Type.SubType
	//
	typePath := util.TrimElem(symbolPath, util.CountElem(file.GetPackage()))

	// Prefix the absolute path with the root directory and swap the extension out
	// with the correct one.
	p := stripExt(pkgPath) + path.Ext(f.outputFile)
	p = path.Join(f.rootDir, p)
	return fmt.Sprintf("%s#%s", p, typePath)
}

// resolvePkgPath resolves the named protobuf package, returning its file path.
//
// TODO(slimsag): This function assumes that the package ("package foo;") is
// named identically to its file name ("foo.proto"). Protoc doesn't pass such
// information to us because it hasn't parsed all the files yet -- we will most
// likely have to scan for the package statement in these dependency files
// ourselves.
func (f *tmplFuncs) resolvePkgPath(pkg string) string {
	// Test this proto file itself:
	if stripExt(filepath.Base(*f.f.Name)) == pkg {
		return *f.f.Name
	}

	// Test each dependency:
	for _, p := range f.f.Dependency {
		if stripExt(filepath.Base(p)) == pkg {
			return p
		}
	}
	return ""
}

// location returns the source code info location for the generic AST-like node
// from the descriptor package.
func (f *tmplFuncs) location(x interface{}) *descriptor.SourceCodeInfo_Location {
	// Validate that we got a sane type from the template.
	pkgPath := reflect.Indirect(reflect.ValueOf(x)).Type().PkgPath()
	if pkgPath != "" && pkgPath != "github.com/golang/protobuf/protoc-gen-go/descriptor" {
		panic("expected descriptor type; got " + fmt.Sprintf("%q", pkgPath))
	}

	// If the location cache is empty; we build it now.
	if f.locCache == nil {
		for _, loc := range f.f.SourceCodeInfo.Location {
			f.locCache = append(f.locCache, cacheItem{
				V: f.walkPath(loc.Path),
				L: loc,
			})
		}
	}
	return f.findCachedItem(x)
}

// findCachedItem finds and returns a cached location for x.
func (f *tmplFuncs) findCachedItem(x interface{}) *descriptor.SourceCodeInfo_Location {
	for _, i := range f.locCache {
		if i.V == x {
			return i.L
		}
	}
	return nil
}

// walkPath walks through the root node (the f.f file) descending down the path
// until it is resolved, at which point the value is returned.
func (f *tmplFuncs) walkPath(path []int32) interface{} {
	if len(path) == 0 {
		return f.f
	}
	var (
		walker func(id int, v interface{}) bool
		found  interface{}
		target = int(path[0])
	)
	path = path[1:]
	walker = func(id int, v interface{}) bool {
		if id != target {
			return true
		}
		if len(path) == 0 {
			found = v
			return false
		}
		target = int(path[0])
		path = path[1:]
		f.protoFields(reflect.ValueOf(v), walker)
		return false
	}
	f.protoFields(reflect.ValueOf(f.f), walker)
	return found
}

// protoFields invokes fn with the protobuf tag ID and its in-memory Go value
// given a descriptor node type. It stops invoking fn when it returns false.
func (f *tmplFuncs) protoFields(node reflect.Value, fn func(id int, v interface{}) bool) {
	indirect := reflect.Indirect(node)

	switch indirect.Kind() {
	case reflect.Slice:
		for i := 0; i < indirect.Len(); i++ {
			if !fn(i, indirect.Index(i).Interface()) {
				return
			}
		}

	case reflect.Struct:
		// Iterate each field.
		for i := 0; i < indirect.NumField(); i++ {
			// Parse the protobuf tag for the ID, e.g. the 49 in:
			// "bytes,49,opt,name=foo,def=hello!"
			tag := indirect.Type().Field(i).Tag.Get("protobuf")
			fields := strings.Split(tag, ",")
			if len(fields) < 2 {
				continue // too few fields
			}

			// Parse the tag ID.
			tagID, err := strconv.Atoi(fields[1])
			if err != nil {
				continue
			}
			if !fn(tagID, indirect.Field(i).Interface()) {
				return
			}
		}
	}
}
<commit_msg>tmpl: expose a simple slug creation function to templates<commit_after>package tmpl

import (
	"errors"
	"fmt"
	"html/template"
	"path"
	"path/filepath"
	"reflect"
	"strconv"
	"strings"
	"unicode"

	gateway "github.com/gengo/grpc-gateway/protoc-gen-grpc-gateway/descriptor"
	"github.com/gengo/grpc-gateway/protoc-gen-grpc-gateway/httprule"
	descriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
	"sourcegraph.com/sourcegraph/prototools/util"
)

// unixPath takes a path, cleans it, and replaces any windows separators (\\)
// with unix ones (/). This is needed because plugin.CodeGeneratorResponse_File
// is defined as having always unix path separators for the file name.
func unixPath(s string) string {
	s = filepath.Clean(s)
	s = strings.Replace(s, "\\", "/", -1)

	// Duplicate clean for trailing slashes that were previously windows ones.
	return filepath.Clean(s)
}

// stripExt strips the extension off the path and returns it.
func stripExt(s string) string {
	ext := filepath.Ext(s)
	if len(ext) > 0 {
		return s[:len(s)-len(ext)]
	}
	return s
}

// slug returns a simple slug string by making everything lowercase and
// replacing anything no a unicode letter with a dash separator.
func slug(s string) string {
	s = strings.ToLower(s)
	fields := strings.FieldsFunc(s, func(c rune) bool {
		return !unicode.IsLetter(c)
	})
	return strings.Join(fields, "-")
}

var Preload = (&tmplFuncs{}).funcMap()

// cacheItem is a single cache item with a value and a location -- effectively
// it is just used for searching.
type cacheItem struct {
	V interface{}
	L *descriptor.SourceCodeInfo_Location
}

// Functions exposed to templates. The user of the package must first preload
// the FuncMap above for these to be called properly (as they are actually
// closures with context).
type tmplFuncs struct {
	f                   *descriptor.FileDescriptorProto
	outputFile, rootDir string
	protoFile           []*descriptor.FileDescriptorProto
	registry            *gateway.Registry
	apiHost             string

	locCache []cacheItem
}

// funcMap returns the function map for feeding into templates.
func (f *tmplFuncs) funcMap() template.FuncMap {
	return map[string]interface{}{
		"cleanLabel": f.cleanLabel,
		"cleanType":  f.cleanType,
		"fieldType":  f.fieldType,
		"dict":       f.dict,
		"ext":        filepath.Ext,
		"dir": func(s string) string {
			dir, _ := path.Split(s)
			return dir
		},
		"trimExt":       stripExt,
		"slug":          slug,
		"sub":           f.sub,
		"filepath":      f.filepath,
		"gatewayMethod": f.gatewayMethod,
		"gatewayPath":   f.gatewayPath,
		"urlToType":     f.urlToType,
		"jsonMessage":   f.jsonMessage,
		"location":      f.location,
		"AllMessages": func(fixNames bool) []*descriptor.DescriptorProto {
			return util.AllMessages(f.f, fixNames)
		},
		"AllEnums": func(fixNames bool) []*descriptor.EnumDescriptorProto {
			return util.AllEnums(f.f, fixNames)
		},
	}
}

// cleanLabel returns the clean (i.e. human-readable / protobuf-style) version
// of a label.
func (f *tmplFuncs) cleanLabel(l *descriptor.FieldDescriptorProto_Label) string {
	switch int32(*l) {
	case 1:
		return "optional"
	case 2:
		return "required"
	case 3:
		return "repeated"
	default:
		panic("unknown label")
	}
}

// cleanType returns the last part of a types name, i.e. for a fully-qualified
// type ".foo.bar.baz" it would return just "baz".
func (f *tmplFuncs) cleanType(path string) string {
	split := strings.Split(path, ".")
	return split[len(split)-1]
}

// fieldType returns the clean (i.e. human-readable / protobuf-style) version
// of a field type.
func (f *tmplFuncs) fieldType(field *descriptor.FieldDescriptorProto) string {
	if field.TypeName != nil {
		return f.cleanType(*field.TypeName)
	}
	return util.FieldTypeName(field.Type)
}

// dict builds a map of paired items, allowing you to invoke a template with
// multiple parameters.
func (f *tmplFuncs) dict(pairs ...interface{}) (map[string]interface{}, error) {
	if len(pairs)%2 != 0 {
		return nil, errors.New("expected pairs")
	}
	m := make(map[string]interface{}, len(pairs)/2)
	for i := 0; i < len(pairs); i += 2 {
		m[pairs[i].(string)] = pairs[i+1]
	}
	return m, nil
}

// sub performs simple x-y subtraction on integers.
func (f *tmplFuncs) sub(x, y int) int { return x - y }

// filepath returns the output filepath (prefixed by the root directory).
func (f *tmplFuncs) filepath() string {
	return path.Join(f.rootDir, f.outputFile)
}

// gatewayMethod returns the grpc-gateway method for a given service method.
func (f *tmplFuncs) gatewayMethod(target *descriptor.MethodDescriptorProto) (*gateway.Method, error) {
	file, err := f.registry.LookupFile(f.f.GetName())
	if err != nil {
		return nil, err
	}
	for _, s := range file.Services {
		for _, m := range s.Methods {
			if m.MethodDescriptorProto == target {
				return m, nil
			}
		}
	}
	return nil, nil
}

// gatewayPath renders the given grpc-gateway HTTP rule template (i.e. the HTTP
// route to be bound). The method parameter is used to insert a link to the
// method type for any HTTP fields (which will be marked clearly in "{text}").
//
// The returned string will always be prefixed by the APIHost string.
func (f *tmplFuncs) gatewayPath(r *httprule.Template, method *descriptor.MethodDescriptorProto) template.HTML {
	var final string
pool:
	for _, pathElem := range r.Pool {
		for _, fieldName := range r.Fields {
			if pathElem != fieldName {
				continue
			}
			u := fmt.Sprintf(`<a href="%s">{%s}</a>`, f.urlToType(method.GetInputType()), pathElem)
			final = path.Join(final, u)
			continue pool
		}
		final = path.Join(final, pathElem)
	}
	return template.HTML(f.apiHost + final)
}

// urlToType returns a URL to the documentation file for the given type. The
// input type path can be either fully-qualified or not, regardless, the URL
// returned will always have a fully-qualified hash.
//
// TODO(slimsag): have the template pass in the relative type instead of nil,
// so that relative symbol paths work.
func (f *tmplFuncs) urlToType(symbolPath string) string {
	if !util.IsFullyQualified(symbolPath) {
		panic("urlToType: not a fully-qualified symbol path")
	}

	// Resolve the package path for the type.
	file := util.NewResolver(f.protoFile).ResolveFile(symbolPath, nil)
	if file == nil {
		return ""
	}
	pkgPath := file.GetName()

	// Remove the package prefix from types, for example:
	//
	//  pkg.html#.pkg.Type.SubType
	//  ->
	//  pkg.html#Type.SubType
	//
	typePath := util.TrimElem(symbolPath, util.CountElem(file.GetPackage()))

	// Prefix the absolute path with the root directory and swap the extension out
	// with the correct one.
	p := stripExt(pkgPath) + path.Ext(f.outputFile)
	p = path.Join(f.rootDir, p)
	return fmt.Sprintf("%s#%s", p, typePath)
}

// resolvePkgPath resolves the named protobuf package, returning its file path.
//
// TODO(slimsag): This function assumes that the package ("package foo;") is
// named identically to its file name ("foo.proto"). Protoc doesn't pass such
// information to us because it hasn't parsed all the files yet -- we will most
// likely have to scan for the package statement in these dependency files
// ourselves.
func (f *tmplFuncs) resolvePkgPath(pkg string) string {
	// Test this proto file itself:
	if stripExt(filepath.Base(*f.f.Name)) == pkg {
		return *f.f.Name
	}

	// Test each dependency:
	for _, p := range f.f.Dependency {
		if stripExt(filepath.Base(p)) == pkg {
			return p
		}
	}
	return ""
}

// location returns the source code info location for the generic AST-like node
// from the descriptor package.
func (f *tmplFuncs) location(x interface{}) *descriptor.SourceCodeInfo_Location {
	// Validate that we got a sane type from the template.
	pkgPath := reflect.Indirect(reflect.ValueOf(x)).Type().PkgPath()
	if pkgPath != "" && pkgPath != "github.com/golang/protobuf/protoc-gen-go/descriptor" {
		panic("expected descriptor type; got " + fmt.Sprintf("%q", pkgPath))
	}

	// If the location cache is empty; we build it now.
	if f.locCache == nil {
		for _, loc := range f.f.SourceCodeInfo.Location {
			f.locCache = append(f.locCache, cacheItem{
				V: f.walkPath(loc.Path),
				L: loc,
			})
		}
	}
	return f.findCachedItem(x)
}

// findCachedItem finds and returns a cached location for x.
func (f *tmplFuncs) findCachedItem(x interface{}) *descriptor.SourceCodeInfo_Location {
	for _, i := range f.locCache {
		if i.V == x {
			return i.L
		}
	}
	return nil
}

// walkPath walks through the root node (the f.f file) descending down the path
// until it is resolved, at which point the value is returned.
func (f *tmplFuncs) walkPath(path []int32) interface{} {
	if len(path) == 0 {
		return f.f
	}
	var (
		walker func(id int, v interface{}) bool
		found  interface{}
		target = int(path[0])
	)
	path = path[1:]
	walker = func(id int, v interface{}) bool {
		if id != target {
			return true
		}
		if len(path) == 0 {
			found = v
			return false
		}
		target = int(path[0])
		path = path[1:]
		f.protoFields(reflect.ValueOf(v), walker)
		return false
	}
	f.protoFields(reflect.ValueOf(f.f), walker)
	return found
}

// protoFields invokes fn with the protobuf tag ID and its in-memory Go value
// given a descriptor node type. It stops invoking fn when it returns false.
func (f *tmplFuncs) protoFields(node reflect.Value, fn func(id int, v interface{}) bool) {
	indirect := reflect.Indirect(node)

	switch indirect.Kind() {
	case reflect.Slice:
		for i := 0; i < indirect.Len(); i++ {
			if !fn(i, indirect.Index(i).Interface()) {
				return
			}
		}

	case reflect.Struct:
		// Iterate each field.
		for i := 0; i < indirect.NumField(); i++ {
			// Parse the protobuf tag for the ID, e.g. the 49 in:
			// "bytes,49,opt,name=foo,def=hello!"
			tag := indirect.Type().Field(i).Tag.Get("protobuf")
			fields := strings.Split(tag, ",")
			if len(fields) < 2 {
				continue // too few fields
			}

			// Parse the tag ID.
			tagID, err := strconv.Atoi(fields[1])
			if err != nil {
				continue
			}
			if !fn(tagID, indirect.Field(i).Interface()) {
				return
			}
		}
	}
}
<|endoftext|>


<commit_before>package k8s

import (
	"testing"

	ktesting "k8s.io/client-go/testing"

	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/client-go/kubernetes/fake"
	"k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/pkg/apis/apps/v1beta1"
)

func mockUnmountedK8sClient() *fake.Clientset {
	k8sMock := &fake.Clientset{}
	k8sMock.AddReactor("list", "secrets", func(a ktesting.Action) (bool, runtime.Object, error) {
		return true, &v1.SecretList{
			Items: []v1.Secret{
				{
					Data: map[string][]byte{
						"name": []byte("test-service"),
					},
				},
				{
					Data: map[string][]byte{
						"now":        []byte("something"),
						"completely": []byte("different"),
					},
				},
			},
		}, nil
	})
	k8sMock.AddReactor("get", "deployments", func(action ktesting.Action) (bool, runtime.Object, error) {
		return true, &v1beta1.Deployment{
			Spec: v1beta1.DeploymentSpec{
				Template: v1.PodTemplateSpec{
					Spec: v1.PodSpec{
						Volumes: []v1.Volume{},
						Containers: []v1.Container{
							{
								Name:         "test-service",
								VolumeMounts: []v1.VolumeMount{},
							},
						},
					},
				},
			},
		}, nil
	})
	k8sMock.AddReactor("get", "secrets", func(action ktesting.Action) (bool, runtime.Object, error) {
		return true, &v1.Secret{
			ObjectMeta: meta_v1.ObjectMeta{
				Name: "test-secret",
			},
			Data: map[string][]byte{},
		}, nil
	})
	k8sMock.AddReactor("Update", "deployments", func(action ktesting.Action) (bool, runtime.Object, error) {
		return true, &v1beta1.Deployment{
			Spec: v1beta1.DeploymentSpec{
				Template: v1.PodTemplateSpec{
					Spec: v1.PodSpec{
						Volumes: []v1.Volume{},
						Containers: []v1.Container{
							{
								Name:         "fh-sync-server",
								VolumeMounts: []v1.VolumeMount{},
							},
						},
					},
				},
			},
		}, nil
	})
	return k8sMock
}

func mockMountedK8sClient() *fake.Clientset {
	k8sMock := &fake.Clientset{}
	k8sMock.AddReactor("list", "secrets", func(a ktesting.Action) (bool, runtime.Object, error) {
		return true, &v1.SecretList{
			Items: []v1.Secret{
				{
					Data: map[string][]byte{
						"name": []byte("test-service"),
					},
				},
				{
					Data: map[string][]byte{
						"now":        []byte("something"),
						"completely": []byte("different"),
					},
				},
			},
		}, nil
	})
	k8sMock.AddReactor("get", "deployments", func(action ktesting.Action) (bool, runtime.Object, error) {
		return true, &v1beta1.Deployment{
			Spec: v1beta1.DeploymentSpec{
				Template: v1.PodTemplateSpec{
					Spec: v1.PodSpec{
						Volumes: []v1.Volume{
							{
								Name: "test-secret",
							},
						},
						Containers: []v1.Container{
							{
								Name: "test-service",
								VolumeMounts: []v1.VolumeMount{
									{
										Name: "test-secret",
									},
								},
							},
						},
					},
				},
			},
		}, nil
	})
	k8sMock.AddReactor("get", "secrets", func(action ktesting.Action) (bool, runtime.Object, error) {
		return true, &v1.Secret{
			ObjectMeta: meta_v1.ObjectMeta{
				Name: "test-secret",
			},
			Data: map[string][]byte{},
		}, nil
	})
	k8sMock.AddReactor("Update", "deployments", func(action ktesting.Action) (bool, runtime.Object, error) {
		return true, &v1beta1.Deployment{
			Spec: v1beta1.DeploymentSpec{
				Template: v1.PodTemplateSpec{
					Spec: v1.PodSpec{
						Volumes: []v1.Volume{},
						Containers: []v1.Container{
							{
								Name:         "fh-sync-server",
								VolumeMounts: []v1.VolumeMount{},
							},
						},
					},
				},
			},
		}, nil
	})
	return k8sMock
}

func TestMount(t *testing.T) {
	cases := []struct {
		Name      string
		K8sClient func() *fake.Clientset
		Namespace string
		Service   string
		Secret    string
		Validate  func(t *testing.T, mountRes error)
	}{
		{
			Name:      "Valid mount request",
			K8sClient: mockUnmountedK8sClient,
			Namespace: "test-namespace",
			Service:   "test-service",
			Secret:    "test-secret",
			Validate: func(t *testing.T, mountRes error) {
				if mountRes != nil {
					t.Fatalf("Did not expect error mounting in testCase: Valid mount request, got: %v", mountRes)
				}
			},
		},
		{
			Name:      "Bad service name to mount request",
			K8sClient: mockUnmountedK8sClient,
			Namespace: "test-namespace",
			Service:   "test-bad-service",
			Secret:    "test-secret",
			Validate: func(t *testing.T, mountRes error) {
				if mountRes == nil {
					t.Fatalf("expected error when providing bad clientService name, but got none")
				}
				if mountRes.Error() != "k8s.mm.Mount -> could not find container in deployment with name: test-bad-service" {
					t.Fatalf("Expected 'k8s.mm.Mount -> could not find container in deployment with name: test-bad-service' but got: %s", mountRes.Error())
				}
			},
		},
		{
			Name:      "Bad secret name to mount request",
			K8sClient: mockUnmountedK8sClient,
			Namespace: "test-namespace",
			Service:   "test-service",
			Secret:    "test-bad-secret",
			Validate: func(t *testing.T, mountRes error) {
				if mountRes == nil {
					t.Fatalf("expected error when providing bad secret name, but got none")
				}
				if mountRes.Error() != "k8s.mm.Mount -> could not find secret: test-bad-secret" {
					t.Fatalf("Expected 'k8s.mm.Mount -> could not find secret: test-bad-secret' but got: '%s'", mountRes.Error())
				}
			},
		},
	}

	for _, testCase := range cases {
		mb := NewMounterBuilder(testCase.Namespace).WithK8s(testCase.K8sClient()).Build()
		err := mb.Mount(testCase.Secret, testCase.Service)
		testCase.Validate(t, err)
	}
}

func TestUnmount(t *testing.T) {
	cases := []struct {
		Name      string
		K8sClient func() *fake.Clientset
		Namespace string
		Service   string
		Secret    string
		Validate  func(t *testing.T, unmountRes error)
	}{
		{
			Name:      "Valid unmount request",
			K8sClient: mockMountedK8sClient,
			Namespace: "test-namespace",
			Service:   "test-service",
			Secret:    "test-secret",
			Validate: func(t *testing.T, unmountRes error) {
				if unmountRes != nil {
					t.Fatalf("Did not expect error mounting in testCase: Valid mount request, got: %v", unmountRes)
				}
			},
		},
		{
			Name:      "Bad service name to mount request",
			K8sClient: mockMountedK8sClient,
			Namespace: "test-namespace",
			Service:   "test-bad-service",
			Secret:    "test-secret",
			Validate: func(t *testing.T, unmountRes error) {
				if unmountRes == nil {
					t.Fatalf("expected error when providing bad clientService name, but got none")
				}
				if unmountRes.Error() != "k8s.mm.Mount -> could not find container in deployment with name: test-bad-service" {
					t.Fatalf("Expected 'k8s.mm.Mount -> could not find container in deployment with name: test-bad-service' but got: %s", unmountRes.Error())
				}
			},
		},
		{
			Name:      "Bad secret name to unmount request",
			K8sClient: mockMountedK8sClient,
			Namespace: "test-namespace",
			Service:   "test-service",
			Secret:    "test-bad-secret",
			Validate: func(t *testing.T, unmountRes error) {
				if unmountRes == nil {
					t.Fatalf("expected error when providing bad secret name, but got none")
				}
				if unmountRes.Error() != "k8s.mm.Unmount -> could not find secret: test-bad-secret" {
					t.Fatalf("Expected 'k8s.mm.Unmount -> could not find secret: test-bad-secret' but got: '%s'", unmountRes.Error())
				}
			},
		},
	}

	for _, testCase := range cases {
		mb := NewMounterBuilder(testCase.Namespace).WithK8s(testCase.K8sClient()).Build()
		err := mb.Unmount(testCase.Secret, testCase.Service)
		testCase.Validate(t, err)
	}
}
<commit_msg>reduce duplication in test<commit_after>package k8s

import (
	"testing"

	ktesting "k8s.io/client-go/testing"

	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/client-go/kubernetes/fake"
	"k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/pkg/apis/apps/v1beta1"
)

func mockK8sClient() *fake.Clientset {
	k8sMock := &fake.Clientset{}
	k8sMock.AddReactor("list", "secrets", func(a ktesting.Action) (bool, runtime.Object, error) {
		return true, &v1.SecretList{
			Items: []v1.Secret{
				{
					Data: map[string][]byte{
						"name": []byte("test-service"),
					},
				},
				{
					Data: map[string][]byte{
						"now":        []byte("something"),
						"completely": []byte("different"),
					},
				},
			},
		}, nil
	})
	k8sMock.AddReactor("get", "secrets", func(action ktesting.Action) (bool, runtime.Object, error) {
		return true, &v1.Secret{
			ObjectMeta: meta_v1.ObjectMeta{
				Name: "test-secret",
			},
			Data: map[string][]byte{},
		}, nil
	})
	k8sMock.AddReactor("Update", "deployments", func(action ktesting.Action) (bool, runtime.Object, error) {
		return true, &v1beta1.Deployment{
			Spec: v1beta1.DeploymentSpec{
				Template: v1.PodTemplateSpec{
					Spec: v1.PodSpec{
						Volumes: []v1.Volume{},
						Containers: []v1.Container{
							{
								Name:         "fh-sync-server",
								VolumeMounts: []v1.VolumeMount{},
							},
						},
					},
				},
			},
		}, nil
	})
	return k8sMock
}

func mockUnmountedK8sClient() *fake.Clientset {
	k8sMock := mockK8sClient()
	k8sMock.AddReactor("get", "deployments", func(action ktesting.Action) (bool, runtime.Object, error) {
		return true, &v1beta1.Deployment{
			Spec: v1beta1.DeploymentSpec{
				Template: v1.PodTemplateSpec{
					Spec: v1.PodSpec{
						Volumes: []v1.Volume{},
						Containers: []v1.Container{
							{
								Name:         "test-service",
								VolumeMounts: []v1.VolumeMount{},
							},
						},
					},
				},
			},
		}, nil
	})

	return k8sMock
}

func mockMountedK8sClient() *fake.Clientset {
	k8sMock := mockK8sClient()
	k8sMock.AddReactor("get", "deployments", func(action ktesting.Action) (bool, runtime.Object, error) {
		return true, &v1beta1.Deployment{
			Spec: v1beta1.DeploymentSpec{
				Template: v1.PodTemplateSpec{
					Spec: v1.PodSpec{
						Volumes: []v1.Volume{
							{
								Name: "test-secret",
							},
						},
						Containers: []v1.Container{
							{
								Name: "test-service",
								VolumeMounts: []v1.VolumeMount{
									{
										Name: "test-secret",
									},
								},
							},
						},
					},
				},
			},
		}, nil
	})
	return k8sMock
}

func TestMount(t *testing.T) {
	cases := []struct {
		Name      string
		K8sClient func() *fake.Clientset
		Namespace string
		Service   string
		Secret    string
		Validate  func(t *testing.T, mountRes error)
	}{
		{
			Name:      "Valid mount request",
			K8sClient: mockUnmountedK8sClient,
			Namespace: "test-namespace",
			Service:   "test-service",
			Secret:    "test-secret",
			Validate: func(t *testing.T, mountRes error) {
				if mountRes != nil {
					t.Fatalf("Did not expect error mounting in testCase: Valid mount request, got: %v", mountRes)
				}
			},
		},
		{
			Name:      "Bad service name to mount request",
			K8sClient: mockUnmountedK8sClient,
			Namespace: "test-namespace",
			Service:   "test-bad-service",
			Secret:    "test-secret",
			Validate: func(t *testing.T, mountRes error) {
				if mountRes == nil {
					t.Fatalf("expected error when providing bad clientService name, but got none")
				}
				if mountRes.Error() != "k8s.mm.Mount -> could not find container in deployment with name: test-bad-service" {
					t.Fatalf("Expected 'k8s.mm.Mount -> could not find container in deployment with name: test-bad-service' but got: %s", mountRes.Error())
				}
			},
		},
		{
			Name:      "Bad secret name to mount request",
			K8sClient: mockUnmountedK8sClient,
			Namespace: "test-namespace",
			Service:   "test-service",
			Secret:    "test-bad-secret",
			Validate: func(t *testing.T, mountRes error) {
				if mountRes == nil {
					t.Fatalf("expected error when providing bad secret name, but got none")
				}
				if mountRes.Error() != "k8s.mm.Mount -> could not find secret: test-bad-secret" {
					t.Fatalf("Expected 'k8s.mm.Mount -> could not find secret: test-bad-secret' but got: '%s'", mountRes.Error())
				}
			},
		},
	}

	for _, testCase := range cases {
		mb := NewMounterBuilder(testCase.Namespace).WithK8s(testCase.K8sClient()).Build()
		err := mb.Mount(testCase.Secret, testCase.Service)
		testCase.Validate(t, err)
	}
}

func TestUnmount(t *testing.T) {
	cases := []struct {
		Name      string
		K8sClient func() *fake.Clientset
		Namespace string
		Service   string
		Secret    string
		Validate  func(t *testing.T, unmountRes error)
	}{
		{
			Name:      "Valid unmount request",
			K8sClient: mockMountedK8sClient,
			Namespace: "test-namespace",
			Service:   "test-service",
			Secret:    "test-secret",
			Validate: func(t *testing.T, unmountRes error) {
				if unmountRes != nil {
					t.Fatalf("Did not expect error mounting in testCase: Valid mount request, got: %v", unmountRes)
				}
			},
		},
		{
			Name:      "Bad service name to mount request",
			K8sClient: mockMountedK8sClient,
			Namespace: "test-namespace",
			Service:   "test-bad-service",
			Secret:    "test-secret",
			Validate: func(t *testing.T, unmountRes error) {
				if unmountRes == nil {
					t.Fatalf("expected error when providing bad clientService name, but got none")
				}
				if unmountRes.Error() != "k8s.mm.Mount -> could not find container in deployment with name: test-bad-service" {
					t.Fatalf("Expected 'k8s.mm.Mount -> could not find container in deployment with name: test-bad-service' but got: %s", unmountRes.Error())
				}
			},
		},
		{
			Name:      "Bad secret name to unmount request",
			K8sClient: mockMountedK8sClient,
			Namespace: "test-namespace",
			Service:   "test-service",
			Secret:    "test-bad-secret",
			Validate: func(t *testing.T, unmountRes error) {
				if unmountRes == nil {
					t.Fatalf("expected error when providing bad secret name, but got none")
				}
				if unmountRes.Error() != "k8s.mm.Unmount -> could not find secret: test-bad-secret" {
					t.Fatalf("Expected 'k8s.mm.Unmount -> could not find secret: test-bad-secret' but got: '%s'", unmountRes.Error())
				}
			},
		},
	}

	for _, testCase := range cases {
		mb := NewMounterBuilder(testCase.Namespace).WithK8s(testCase.K8sClient()).Build()
		err := mb.Unmount(testCase.Secret, testCase.Service)
		testCase.Validate(t, err)
	}
}
<|endoftext|>


<commit_before>package opentracing

import (
	"context"
	"io"
	"net/http"
	"time"

	"github.com/hellofresh/gcloud-opentracing"
	"github.com/hellofresh/janus/pkg/config"
	"github.com/hellofresh/janus/pkg/errors"
	"github.com/opentracing/opentracing-go"
	log "github.com/sirupsen/logrus"
	jaegercfg "github.com/uber/jaeger-client-go/config"
	"github.com/uber/jaeger-lib/metrics"
)

const (
	gcloudTracing = "googleCloud"
	jaegerTracing = "jaeger"
)

// Tracing is the tracing functionality
type Tracing struct {
	config config.Tracing
	tracer opentracing.Tracer
	closer io.Closer
}

type noopCloser struct{}

func (n noopCloser) Close() error { return nil }

// New creates a new instance of Tracing
func New(config config.Tracing) *Tracing {
	return &Tracing{config: config}
}

// Setup a tracer based on the configuration provided
func (t *Tracing) Setup() {
	var err error

	log.Debug("Initializing distributed tracing")
	switch t.config.Provider {
	case gcloudTracing:
		log.Debug("Using google cloud platform (stackdriver trace) as tracing system")
		t.tracer, t.closer, err = t.buildGCloud(t.config.GoogleCloudTracing)
	case jaegerTracing:
		log.Debug("Using Jaeger as tracing system")
		t.tracer, t.closer, err = t.buildJaeger(t.config.ServiceName, t.config.JaegerTracing)
	default:
		log.Debug("No tracer selected")
		t.tracer, t.closer, err = &opentracing.NoopTracer{}, noopCloser{}, nil
	}

	if err != nil {
		log.WithError(err).WithField("provider", t.config.Provider).Warnf("Could not initialize tracing")
		return
	}

	opentracing.SetGlobalTracer(t.tracer)
}

// Close tracer
func (t *Tracing) Close() {
	if t.closer != nil {
		t.closer.Close()
	}
}

func (t *Tracing) buildGCloud(config config.GoogleCloudTracing) (opentracing.Tracer, io.Closer, error) {
	tracer, err := gcloudtracer.NewTracer(
		context.Background(),
		gcloudtracer.WithLogger(log.StandardLogger()),
		gcloudtracer.WithProject(config.ProjectID),
		gcloudtracer.WithJWTCredentials(gcloudtracer.JWTCredentials{
			Email:        config.Email,
			PrivateKey:   []byte(config.PrivateKey),
			PrivateKeyID: config.PrivateKeyID,
		}),
	)

	return tracer, noopCloser{}, err
}

func (t *Tracing) buildJaeger(componentName string, c config.JaegerTracing) (opentracing.Tracer, io.Closer, error) {
	bufferFLushInterval, err := time.ParseDuration(c.BufferFlushInterval)
	if err != nil {
		return nil, noopCloser{}, errors.Wrap(err, "could not parse buffer flush interval for jaeger")
	}

	cfg := jaegercfg.Configuration{
		Sampler: &jaegercfg.SamplerConfig{
			Type:  c.SamplingType,
			Param: c.SamplingParam,
		},
		Reporter: &jaegercfg.ReporterConfig{
			LogSpans:            c.LogSpans,
			BufferFlushInterval: bufferFLushInterval,
			LocalAgentHostPort:  c.SamplingServerURL,
			QueueSize:           c.QueueSize,
		},
	}

	return cfg.New(
		componentName,
		jaegercfg.Logger(jaegerLoggerAdapter{log.StandardLogger()}),
		jaegercfg.Metrics(metrics.NullFactory),
	)
}

// FromContext creates a span from a context that contains a parent span
func FromContext(ctx context.Context, name string) opentracing.Span {
	span, _ := opentracing.StartSpanFromContext(ctx, name)
	return span
}

// ToContext sets a span to a context
func ToContext(r *http.Request, span opentracing.Span) *http.Request {
	return r.WithContext(opentracing.ContextWithSpan(r.Context(), span))
}
<commit_msg>Fixed typo<commit_after>package opentracing

import (
	"context"
	"io"
	"net/http"
	"time"

	"github.com/hellofresh/gcloud-opentracing"
	"github.com/hellofresh/janus/pkg/config"
	"github.com/hellofresh/janus/pkg/errors"
	"github.com/opentracing/opentracing-go"
	log "github.com/sirupsen/logrus"
	jaegercfg "github.com/uber/jaeger-client-go/config"
	"github.com/uber/jaeger-lib/metrics"
)

const (
	gcloudTracing = "googleCloud"
	jaegerTracing = "jaeger"
)

// Tracing is the tracing functionality
type Tracing struct {
	config config.Tracing
	tracer opentracing.Tracer
	closer io.Closer
}

type noopCloser struct{}

func (n noopCloser) Close() error { return nil }

// New creates a new instance of Tracing
func New(config config.Tracing) *Tracing {
	return &Tracing{config: config}
}

// Setup a tracer based on the configuration provided
func (t *Tracing) Setup() {
	var err error

	log.Debug("Initializing distributed tracing")
	switch t.config.Provider {
	case gcloudTracing:
		log.Debug("Using google cloud platform (stackdriver trace) as tracing system")
		t.tracer, t.closer, err = t.buildGCloud(t.config.GoogleCloudTracing)
	case jaegerTracing:
		log.Debug("Using Jaeger as tracing system")
		t.tracer, t.closer, err = t.buildJaeger(t.config.ServiceName, t.config.JaegerTracing)
	default:
		log.Debug("No tracer selected")
		t.tracer, t.closer, err = &opentracing.NoopTracer{}, noopCloser{}, nil
	}

	if err != nil {
		log.WithError(err).WithField("provider", t.config.Provider).Warn("Could not initialize tracing")
		return
	}

	opentracing.SetGlobalTracer(t.tracer)
}

// Close tracer
func (t *Tracing) Close() {
	if t.closer != nil {
		t.closer.Close()
	}
}

func (t *Tracing) buildGCloud(config config.GoogleCloudTracing) (opentracing.Tracer, io.Closer, error) {
	tracer, err := gcloudtracer.NewTracer(
		context.Background(),
		gcloudtracer.WithLogger(log.StandardLogger()),
		gcloudtracer.WithProject(config.ProjectID),
		gcloudtracer.WithJWTCredentials(gcloudtracer.JWTCredentials{
			Email:        config.Email,
			PrivateKey:   []byte(config.PrivateKey),
			PrivateKeyID: config.PrivateKeyID,
		}),
	)

	return tracer, noopCloser{}, err
}

func (t *Tracing) buildJaeger(componentName string, c config.JaegerTracing) (opentracing.Tracer, io.Closer, error) {
	bufferFLushInterval, err := time.ParseDuration(c.BufferFlushInterval)
	if err != nil {
		return nil, noopCloser{}, errors.Wrap(err, "could not parse buffer flush interval for jaeger")
	}

	cfg := jaegercfg.Configuration{
		Sampler: &jaegercfg.SamplerConfig{
			Type:  c.SamplingType,
			Param: c.SamplingParam,
		},
		Reporter: &jaegercfg.ReporterConfig{
			LogSpans:            c.LogSpans,
			BufferFlushInterval: bufferFLushInterval,
			LocalAgentHostPort:  c.SamplingServerURL,
			QueueSize:           c.QueueSize,
		},
	}

	return cfg.New(
		componentName,
		jaegercfg.Logger(jaegerLoggerAdapter{log.StandardLogger()}),
		jaegercfg.Metrics(metrics.NullFactory),
	)
}

// FromContext creates a span from a context that contains a parent span
func FromContext(ctx context.Context, name string) opentracing.Span {
	span, _ := opentracing.StartSpanFromContext(ctx, name)
	return span
}

// ToContext sets a span to a context
func ToContext(r *http.Request, span opentracing.Span) *http.Request {
	return r.WithContext(opentracing.ContextWithSpan(r.Context(), span))
}
<|endoftext|>


<commit_before>// Copyright 2014 Wandoujia Inc. All Rights Reserved.
// Licensed under the MIT (MIT-LICENSE.txt) license.

package router
<commit_msg>Remove, useless slots_test.go<commit_after><|endoftext|>


<commit_before>/*
Copyright 2021 The Skaffold 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 v2

import (
	"bytes"
	"encoding/json"
	"fmt"
	"os"
	"sync"

	//nolint:golint,staticcheck
	"github.com/golang/protobuf/jsonpb"
	"github.com/golang/protobuf/ptypes"
	"github.com/golang/protobuf/ptypes/timestamp"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants"
	sErrors "github.com/GoogleContainerTools/skaffold/pkg/skaffold/errors"
	proto "github.com/GoogleContainerTools/skaffold/proto/v2"
)

const (
	NotStarted = "NotStarted"
	InProgress = "InProgress"
	Complete   = "Complete"
	Failed     = "Failed"
	Info       = "Information"
	Started    = "Started"
	Succeeded  = "Succeeded"
	Terminated = "Terminated"
	Canceled   = "Canceled"
)

var handler = newHandler()

func newHandler() *eventHandler {
	h := &eventHandler{
		eventChan: make(chan *proto.Event),
	}
	go func() {
		for {
			ev, open := <-h.eventChan
			if !open {
				break
			}
			h.handleExec(ev)
		}
	}()
	return h
}

type eventHandler struct {
	eventLog            []proto.Event
	logLock             sync.Mutex
	applicationLogs     []proto.Event
	applicationLogsLock sync.Mutex
	skaffoldLogs        []proto.Event
	skaffoldLogsLock    sync.Mutex
	cfg                 Config

	iteration               int
	state                   proto.State
	stateLock               sync.Mutex
	eventChan               chan *proto.Event
	eventListeners          []*listener
	applicationLogListeners []*listener
	skaffoldLogListeners    []*listener
}

type listener struct {
	callback func(*proto.Event) error
	errors   chan error
	closed   bool
}

func GetIteration() int {
	return handler.iteration
}

func GetState() (*proto.State, error) {
	state := handler.getState()
	return &state, nil
}

func ForEachEvent(callback func(*proto.Event) error) error {
	return handler.forEachEvent(callback)
}

func ForEachApplicationLog(callback func(*proto.Event) error) error {
	return handler.forEachApplicationLog(callback)
}

func ForEachSkaffoldLog(callback func(*proto.Event) error) error {
	return handler.forEachSkaffoldLog(callback)
}

func Handle(event *proto.Event) error {
	if event != nil {
		handler.handle(event)
	}
	return nil
}

func (ev *eventHandler) getState() proto.State {
	ev.stateLock.Lock()
	// Deep copy
	buf, _ := json.Marshal(ev.state)
	ev.stateLock.Unlock()

	var state proto.State
	json.Unmarshal(buf, &state)

	return state
}

func (ev *eventHandler) log(event *proto.Event, listeners *[]*listener, log *[]proto.Event, lock sync.Locker) {
	lock.Lock()

	for _, listener := range *listeners {
		if listener.closed {
			continue
		}

		if err := listener.callback(event); err != nil {
			listener.errors <- err
			listener.closed = true
		}
	}
	*log = append(*log, *event)

	lock.Unlock()
}

func (ev *eventHandler) logEvent(event *proto.Event) {
	ev.log(event, &ev.eventListeners, &ev.eventLog, &ev.logLock)
}

func (ev *eventHandler) logApplicationLog(event *proto.Event) {
	ev.log(event, &ev.applicationLogListeners, &ev.applicationLogs, &ev.applicationLogsLock)
}

func (ev *eventHandler) logSkaffoldLog(event *proto.Event) {
	ev.log(event, &ev.skaffoldLogListeners, &ev.skaffoldLogs, &ev.skaffoldLogsLock)
}

func (ev *eventHandler) forEach(listeners *[]*listener, log *[]proto.Event, lock sync.Locker, callback func(*proto.Event) error) error {
	listener := &listener{
		callback: callback,
		errors:   make(chan error),
	}

	lock.Lock()

	oldEvents := make([]proto.Event, len(*log))
	copy(oldEvents, *log)
	*listeners = append(*listeners, listener)

	lock.Unlock()

	for i := range oldEvents {
		if err := callback(&oldEvents[i]); err != nil {
			// listener should maybe be closed
			return err
		}
	}

	return <-listener.errors
}

func (ev *eventHandler) forEachEvent(callback func(*proto.Event) error) error {
	return ev.forEach(&ev.eventListeners, &ev.eventLog, &ev.logLock, callback)
}

func (ev *eventHandler) forEachApplicationLog(callback func(*proto.Event) error) error {
	return ev.forEach(&ev.applicationLogListeners, &ev.applicationLogs, &ev.applicationLogsLock, callback)
}

func (ev *eventHandler) forEachSkaffoldLog(callback func(*proto.Event) error) error {
	return ev.forEach(&ev.skaffoldLogListeners, &ev.skaffoldLogs, &ev.skaffoldLogsLock, callback)
}

func emptyState(cfg Config) proto.State {
	builds := map[string]string{}
	for _, p := range cfg.GetPipelines() {
		for _, a := range p.Build.Artifacts {
			builds[a.ImageName] = NotStarted
		}
	}
	metadata := initializeMetadata(cfg.GetPipelines(), cfg.GetKubeContext())
	return emptyStateWithArtifacts(builds, metadata, cfg.AutoBuild(), cfg.AutoDeploy(), cfg.AutoSync())
}

func emptyStateWithArtifacts(builds map[string]string, metadata *proto.Metadata, autoBuild, autoDeploy, autoSync bool) proto.State {
	return proto.State{
		BuildState: &proto.BuildState{
			Artifacts:   builds,
			AutoTrigger: autoBuild,
			StatusCode:  proto.StatusCode_OK,
		},
		TestState: &proto.TestState{
			Status:     NotStarted,
			StatusCode: proto.StatusCode_OK,
		},
		DeployState: &proto.DeployState{
			Status:      NotStarted,
			AutoTrigger: autoDeploy,
			StatusCode:  proto.StatusCode_OK,
		},
		StatusCheckState: emptyStatusCheckState(),
		ForwardedPorts:   make(map[int32]*proto.PortForwardEvent),
		FileSyncState: &proto.FileSyncState{
			Status:      NotStarted,
			AutoTrigger: autoSync,
		},
		Metadata: metadata,
	}
}

// ResetStateOnBuild resets the build, test, deploy and sync state
func ResetStateOnBuild() {
	builds := map[string]string{}
	for k := range handler.getState().BuildState.Artifacts {
		builds[k] = NotStarted
	}
	autoBuild, autoDeploy, autoSync := handler.getState().BuildState.AutoTrigger, handler.getState().DeployState.AutoTrigger, handler.getState().FileSyncState.AutoTrigger
	newState := emptyStateWithArtifacts(builds, handler.getState().Metadata, autoBuild, autoDeploy, autoSync)
	handler.setState(newState)
}

// ResetStateOnTest resets the test, deploy, sync and status check state
func ResetStateOnTest() {
	newState := handler.getState()
	newState.TestState.Status = NotStarted
	handler.setState(newState)
}

// ResetStateOnDeploy resets the deploy, sync and status check state
func ResetStateOnDeploy() {
	newState := handler.getState()
	newState.DeployState.Status = NotStarted
	newState.DeployState.StatusCode = proto.StatusCode_OK
	newState.StatusCheckState = emptyStatusCheckState()
	newState.ForwardedPorts = map[int32]*proto.PortForwardEvent{}
	newState.DebuggingContainers = nil
	handler.setState(newState)
}

func UpdateStateAutoBuildTrigger(t bool) {
	newState := handler.getState()
	newState.BuildState.AutoTrigger = t
	handler.setState(newState)
}

func UpdateStateAutoDeployTrigger(t bool) {
	newState := handler.getState()
	newState.DeployState.AutoTrigger = t
	handler.setState(newState)
}

func UpdateStateAutoSyncTrigger(t bool) {
	newState := handler.getState()
	newState.FileSyncState.AutoTrigger = t
	handler.setState(newState)
}

func emptyStatusCheckState() *proto.StatusCheckState {
	return &proto.StatusCheckState{
		Status:     NotStarted,
		Resources:  map[string]string{},
		StatusCode: proto.StatusCode_OK,
	}
}

// InitializeState instantiates the global state of the skaffold runner, as well as the event log.
func InitializeState(cfg Config) {
	handler.cfg = cfg
	handler.setState(emptyState(cfg))
}

func AutoTriggerDiff(phase constants.Phase, val bool) (bool, error) {
	switch phase {
	case constants.Build:
		return val != handler.getState().BuildState.AutoTrigger, nil
	case constants.Sync:
		return val != handler.getState().FileSyncState.AutoTrigger, nil
	case constants.Deploy:
		return val != handler.getState().DeployState.AutoTrigger, nil
	default:
		return false, fmt.Errorf("unknown Phase %v not found in handler state", phase)
	}
}

func TaskInProgress(task constants.Phase, description string) {
	if task == constants.DevLoop {
		handler.iteration++
	}

	handler.handleTaskEvent(&proto.TaskEvent{
		Id:          fmt.Sprintf("%s-%d", task, handler.iteration),
		Task:        string(task),
		Description: description,
		Iteration:   int32(handler.iteration),
		Status:      InProgress,
	})
}

func TaskFailed(task constants.Phase, err error) {
	ae := sErrors.ActionableErrV2(handler.cfg, task, err)
	handler.handleTaskEvent(&proto.TaskEvent{
		Id:            fmt.Sprintf("%s-%d", task, handler.iteration),
		Task:          string(task),
		Iteration:     int32(handler.iteration),
		Status:        Failed,
		ActionableErr: ae,
	})
}

func TaskSucceeded(task constants.Phase) {
	handler.handleTaskEvent(&proto.TaskEvent{
		Id:        fmt.Sprintf("%s-%d", task, handler.iteration),
		Task:      string(task),
		Iteration: int32(handler.iteration),
		Status:    Succeeded,
	})
}

func (ev *eventHandler) setState(state proto.State) {
	ev.stateLock.Lock()
	ev.state = state
	ev.stateLock.Unlock()
}

func (ev *eventHandler) handle(event *proto.Event) {
	go func(t *timestamp.Timestamp) {
		event.Timestamp = t
		ev.eventChan <- event
		if _, ok := event.GetEventType().(*proto.Event_TerminationEvent); ok {
			// close the event channel indicating there are no more events to all the
			// receivers
			close(ev.eventChan)
		}
	}(ptypes.TimestampNow())
}

func (ev *eventHandler) handleTaskEvent(e *proto.TaskEvent) {
	ev.handle(&proto.Event{
		EventType: &proto.Event_TaskEvent{
			TaskEvent: e,
		},
	})
}

func (ev *eventHandler) handleExec(event *proto.Event) {
	switch e := event.GetEventType().(type) {
	case *proto.Event_ApplicationLogEvent:
		ev.logApplicationLog(event)
		return
	case *proto.Event_SkaffoldLogEvent:
		ev.logSkaffoldLog(event)
		return
	case *proto.Event_BuildSubtaskEvent:
		be := e.BuildSubtaskEvent
		if be.Step == Build {
			ev.stateLock.Lock()
			ev.state.BuildState.Artifacts[be.Artifact] = be.Status
			ev.stateLock.Unlock()
		}
	case *proto.Event_TestEvent:
		te := e.TestEvent
		ev.stateLock.Lock()
		ev.state.TestState.Status = te.Status
		ev.stateLock.Unlock()
	case *proto.Event_DeploySubtaskEvent:
		de := e.DeploySubtaskEvent
		ev.stateLock.Lock()
		ev.state.DeployState.Status = de.Status
		ev.stateLock.Unlock()
	case *proto.Event_PortEvent:
		pe := e.PortEvent
		ev.stateLock.Lock()
		ev.state.ForwardedPorts[pe.LocalPort] = pe
		ev.stateLock.Unlock()
	case *proto.Event_StatusCheckSubtaskEvent:
		se := e.StatusCheckSubtaskEvent
		ev.stateLock.Lock()
		ev.state.StatusCheckState.Resources[se.Resource] = se.Status
		ev.stateLock.Unlock()
	case *proto.Event_FileSyncEvent:
		fse := e.FileSyncEvent
		ev.stateLock.Lock()
		ev.state.FileSyncState.Status = fse.Status
		ev.stateLock.Unlock()
	case *proto.Event_DebuggingContainerEvent:
		de := e.DebuggingContainerEvent
		ev.stateLock.Lock()
		switch de.Status {
		case Started:
			ev.state.DebuggingContainers = append(ev.state.DebuggingContainers, de)
		case Terminated:
			n := 0
			for _, x := range ev.state.DebuggingContainers {
				if x.Namespace != de.Namespace || x.PodName != de.PodName || x.ContainerName != de.ContainerName {
					ev.state.DebuggingContainers[n] = x
					n++
				}
			}
			ev.state.DebuggingContainers = ev.state.DebuggingContainers[:n]
		}
		ev.stateLock.Unlock()
	}
	ev.logEvent(event)
}

// SaveEventsToFile saves the current event log to the filepath provided
func SaveEventsToFile(fp string) error {
	handler.logLock.Lock()
	f, err := os.OpenFile(fp, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
	if err != nil {
		return fmt.Errorf("opening %s: %w", fp, err)
	}
	defer f.Close()
	marshaller := jsonpb.Marshaler{}
	for _, ev := range handler.eventLog {
		contents := bytes.NewBuffer([]byte{})
		if err := marshaller.Marshal(contents, &ev); err != nil {
			return fmt.Errorf("marshalling event: %w", err)
		}
		if _, err := f.WriteString(contents.String() + "\n"); err != nil {
			return fmt.Errorf("writing string: %w", err)
		}
	}
	handler.logLock.Unlock()
	return nil
}
<commit_msg>Clear `ApplicationLogEvent` and `SkaffoldLogEvent` buffers on new dev iteration (#5993)<commit_after>/*
Copyright 2021 The Skaffold 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 v2

import (
	"bytes"
	"encoding/json"
	"fmt"
	"os"
	"sync"

	//nolint:golint,staticcheck
	"github.com/golang/protobuf/jsonpb"
	"github.com/golang/protobuf/ptypes"
	"github.com/golang/protobuf/ptypes/timestamp"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants"
	sErrors "github.com/GoogleContainerTools/skaffold/pkg/skaffold/errors"
	proto "github.com/GoogleContainerTools/skaffold/proto/v2"
)

const (
	NotStarted = "NotStarted"
	InProgress = "InProgress"
	Complete   = "Complete"
	Failed     = "Failed"
	Info       = "Information"
	Started    = "Started"
	Succeeded  = "Succeeded"
	Terminated = "Terminated"
	Canceled   = "Canceled"
)

var handler = newHandler()

func newHandler() *eventHandler {
	h := &eventHandler{
		eventChan: make(chan *proto.Event),
	}
	go func() {
		for {
			ev, open := <-h.eventChan
			if !open {
				break
			}
			h.handleExec(ev)
		}
	}()
	return h
}

type eventHandler struct {
	eventLog            []proto.Event
	logLock             sync.Mutex
	applicationLogs     []proto.Event
	applicationLogsLock sync.Mutex
	skaffoldLogs        []proto.Event
	skaffoldLogsLock    sync.Mutex
	cfg                 Config

	iteration               int
	state                   proto.State
	stateLock               sync.Mutex
	eventChan               chan *proto.Event
	eventListeners          []*listener
	applicationLogListeners []*listener
	skaffoldLogListeners    []*listener
}

type listener struct {
	callback func(*proto.Event) error
	errors   chan error
	closed   bool
}

func GetIteration() int {
	return handler.iteration
}

func GetState() (*proto.State, error) {
	state := handler.getState()
	return &state, nil
}

func ForEachEvent(callback func(*proto.Event) error) error {
	return handler.forEachEvent(callback)
}

func ForEachApplicationLog(callback func(*proto.Event) error) error {
	return handler.forEachApplicationLog(callback)
}

func ForEachSkaffoldLog(callback func(*proto.Event) error) error {
	return handler.forEachSkaffoldLog(callback)
}

func Handle(event *proto.Event) error {
	if event != nil {
		handler.handle(event)
	}
	return nil
}

func (ev *eventHandler) getState() proto.State {
	ev.stateLock.Lock()
	// Deep copy
	buf, _ := json.Marshal(ev.state)
	ev.stateLock.Unlock()

	var state proto.State
	json.Unmarshal(buf, &state)

	return state
}

func (ev *eventHandler) log(event *proto.Event, listeners *[]*listener, log *[]proto.Event, lock sync.Locker) {
	lock.Lock()

	for _, listener := range *listeners {
		if listener.closed {
			continue
		}

		if err := listener.callback(event); err != nil {
			listener.errors <- err
			listener.closed = true
		}
	}
	*log = append(*log, *event)

	lock.Unlock()
}

func (ev *eventHandler) logEvent(event *proto.Event) {
	ev.log(event, &ev.eventListeners, &ev.eventLog, &ev.logLock)
}

func (ev *eventHandler) logApplicationLog(event *proto.Event) {
	ev.log(event, &ev.applicationLogListeners, &ev.applicationLogs, &ev.applicationLogsLock)
}

func (ev *eventHandler) logSkaffoldLog(event *proto.Event) {
	ev.log(event, &ev.skaffoldLogListeners, &ev.skaffoldLogs, &ev.skaffoldLogsLock)
}

func (ev *eventHandler) forEach(listeners *[]*listener, log *[]proto.Event, lock sync.Locker, callback func(*proto.Event) error) error {
	listener := &listener{
		callback: callback,
		errors:   make(chan error),
	}

	lock.Lock()

	oldEvents := make([]proto.Event, len(*log))
	copy(oldEvents, *log)
	*listeners = append(*listeners, listener)

	lock.Unlock()

	for i := range oldEvents {
		if err := callback(&oldEvents[i]); err != nil {
			// listener should maybe be closed
			return err
		}
	}

	return <-listener.errors
}

func (ev *eventHandler) forEachEvent(callback func(*proto.Event) error) error {
	return ev.forEach(&ev.eventListeners, &ev.eventLog, &ev.logLock, callback)
}

func (ev *eventHandler) forEachApplicationLog(callback func(*proto.Event) error) error {
	return ev.forEach(&ev.applicationLogListeners, &ev.applicationLogs, &ev.applicationLogsLock, callback)
}

func (ev *eventHandler) forEachSkaffoldLog(callback func(*proto.Event) error) error {
	return ev.forEach(&ev.skaffoldLogListeners, &ev.skaffoldLogs, &ev.skaffoldLogsLock, callback)
}

func emptyState(cfg Config) proto.State {
	builds := map[string]string{}
	for _, p := range cfg.GetPipelines() {
		for _, a := range p.Build.Artifacts {
			builds[a.ImageName] = NotStarted
		}
	}
	metadata := initializeMetadata(cfg.GetPipelines(), cfg.GetKubeContext())
	return emptyStateWithArtifacts(builds, metadata, cfg.AutoBuild(), cfg.AutoDeploy(), cfg.AutoSync())
}

func emptyStateWithArtifacts(builds map[string]string, metadata *proto.Metadata, autoBuild, autoDeploy, autoSync bool) proto.State {
	return proto.State{
		BuildState: &proto.BuildState{
			Artifacts:   builds,
			AutoTrigger: autoBuild,
			StatusCode:  proto.StatusCode_OK,
		},
		TestState: &proto.TestState{
			Status:     NotStarted,
			StatusCode: proto.StatusCode_OK,
		},
		DeployState: &proto.DeployState{
			Status:      NotStarted,
			AutoTrigger: autoDeploy,
			StatusCode:  proto.StatusCode_OK,
		},
		StatusCheckState: emptyStatusCheckState(),
		ForwardedPorts:   make(map[int32]*proto.PortForwardEvent),
		FileSyncState: &proto.FileSyncState{
			Status:      NotStarted,
			AutoTrigger: autoSync,
		},
		Metadata: metadata,
	}
}

// ResetStateOnBuild resets the build, test, deploy and sync state
func ResetStateOnBuild() {
	builds := map[string]string{}
	for k := range handler.getState().BuildState.Artifacts {
		builds[k] = NotStarted
	}
	autoBuild, autoDeploy, autoSync := handler.getState().BuildState.AutoTrigger, handler.getState().DeployState.AutoTrigger, handler.getState().FileSyncState.AutoTrigger
	newState := emptyStateWithArtifacts(builds, handler.getState().Metadata, autoBuild, autoDeploy, autoSync)
	handler.setState(newState)
}

// ResetStateOnTest resets the test, deploy, sync and status check state
func ResetStateOnTest() {
	newState := handler.getState()
	newState.TestState.Status = NotStarted
	handler.setState(newState)
}

// ResetStateOnDeploy resets the deploy, sync and status check state
func ResetStateOnDeploy() {
	newState := handler.getState()
	newState.DeployState.Status = NotStarted
	newState.DeployState.StatusCode = proto.StatusCode_OK
	newState.StatusCheckState = emptyStatusCheckState()
	newState.ForwardedPorts = map[int32]*proto.PortForwardEvent{}
	newState.DebuggingContainers = nil
	handler.setState(newState)
}

func UpdateStateAutoBuildTrigger(t bool) {
	newState := handler.getState()
	newState.BuildState.AutoTrigger = t
	handler.setState(newState)
}

func UpdateStateAutoDeployTrigger(t bool) {
	newState := handler.getState()
	newState.DeployState.AutoTrigger = t
	handler.setState(newState)
}

func UpdateStateAutoSyncTrigger(t bool) {
	newState := handler.getState()
	newState.FileSyncState.AutoTrigger = t
	handler.setState(newState)
}

func emptyStatusCheckState() *proto.StatusCheckState {
	return &proto.StatusCheckState{
		Status:     NotStarted,
		Resources:  map[string]string{},
		StatusCode: proto.StatusCode_OK,
	}
}

// InitializeState instantiates the global state of the skaffold runner, as well as the event log.
func InitializeState(cfg Config) {
	handler.cfg = cfg
	handler.setState(emptyState(cfg))
}

func AutoTriggerDiff(phase constants.Phase, val bool) (bool, error) {
	switch phase {
	case constants.Build:
		return val != handler.getState().BuildState.AutoTrigger, nil
	case constants.Sync:
		return val != handler.getState().FileSyncState.AutoTrigger, nil
	case constants.Deploy:
		return val != handler.getState().DeployState.AutoTrigger, nil
	default:
		return false, fmt.Errorf("unknown Phase %v not found in handler state", phase)
	}
}

func TaskInProgress(task constants.Phase, description string) {
	// Special casing to increment iteration and clear application and skaffold logs
	if task == constants.DevLoop {
		handler.iteration++

		handler.applicationLogs = []proto.Event{}
		handler.skaffoldLogs = []proto.Event{}
	}

	handler.handleTaskEvent(&proto.TaskEvent{
		Id:          fmt.Sprintf("%s-%d", task, handler.iteration),
		Task:        string(task),
		Description: description,
		Iteration:   int32(handler.iteration),
		Status:      InProgress,
	})
}

func TaskFailed(task constants.Phase, err error) {
	ae := sErrors.ActionableErrV2(handler.cfg, task, err)
	handler.handleTaskEvent(&proto.TaskEvent{
		Id:            fmt.Sprintf("%s-%d", task, handler.iteration),
		Task:          string(task),
		Iteration:     int32(handler.iteration),
		Status:        Failed,
		ActionableErr: ae,
	})
}

func TaskSucceeded(task constants.Phase) {
	handler.handleTaskEvent(&proto.TaskEvent{
		Id:        fmt.Sprintf("%s-%d", task, handler.iteration),
		Task:      string(task),
		Iteration: int32(handler.iteration),
		Status:    Succeeded,
	})
}

func (ev *eventHandler) setState(state proto.State) {
	ev.stateLock.Lock()
	ev.state = state
	ev.stateLock.Unlock()
}

func (ev *eventHandler) handle(event *proto.Event) {
	go func(t *timestamp.Timestamp) {
		event.Timestamp = t
		ev.eventChan <- event
		if _, ok := event.GetEventType().(*proto.Event_TerminationEvent); ok {
			// close the event channel indicating there are no more events to all the
			// receivers
			close(ev.eventChan)
		}
	}(ptypes.TimestampNow())
}

func (ev *eventHandler) handleTaskEvent(e *proto.TaskEvent) {
	ev.handle(&proto.Event{
		EventType: &proto.Event_TaskEvent{
			TaskEvent: e,
		},
	})
}

func (ev *eventHandler) handleExec(event *proto.Event) {
	switch e := event.GetEventType().(type) {
	case *proto.Event_ApplicationLogEvent:
		ev.logApplicationLog(event)
		return
	case *proto.Event_SkaffoldLogEvent:
		ev.logSkaffoldLog(event)
		return
	case *proto.Event_BuildSubtaskEvent:
		be := e.BuildSubtaskEvent
		if be.Step == Build {
			ev.stateLock.Lock()
			ev.state.BuildState.Artifacts[be.Artifact] = be.Status
			ev.stateLock.Unlock()
		}
	case *proto.Event_TestEvent:
		te := e.TestEvent
		ev.stateLock.Lock()
		ev.state.TestState.Status = te.Status
		ev.stateLock.Unlock()
	case *proto.Event_DeploySubtaskEvent:
		de := e.DeploySubtaskEvent
		ev.stateLock.Lock()
		ev.state.DeployState.Status = de.Status
		ev.stateLock.Unlock()
	case *proto.Event_PortEvent:
		pe := e.PortEvent
		ev.stateLock.Lock()
		ev.state.ForwardedPorts[pe.LocalPort] = pe
		ev.stateLock.Unlock()
	case *proto.Event_StatusCheckSubtaskEvent:
		se := e.StatusCheckSubtaskEvent
		ev.stateLock.Lock()
		ev.state.StatusCheckState.Resources[se.Resource] = se.Status
		ev.stateLock.Unlock()
	case *proto.Event_FileSyncEvent:
		fse := e.FileSyncEvent
		ev.stateLock.Lock()
		ev.state.FileSyncState.Status = fse.Status
		ev.stateLock.Unlock()
	case *proto.Event_DebuggingContainerEvent:
		de := e.DebuggingContainerEvent
		ev.stateLock.Lock()
		switch de.Status {
		case Started:
			ev.state.DebuggingContainers = append(ev.state.DebuggingContainers, de)
		case Terminated:
			n := 0
			for _, x := range ev.state.DebuggingContainers {
				if x.Namespace != de.Namespace || x.PodName != de.PodName || x.ContainerName != de.ContainerName {
					ev.state.DebuggingContainers[n] = x
					n++
				}
			}
			ev.state.DebuggingContainers = ev.state.DebuggingContainers[:n]
		}
		ev.stateLock.Unlock()
	}
	ev.logEvent(event)
}

// SaveEventsToFile saves the current event log to the filepath provided
func SaveEventsToFile(fp string) error {
	handler.logLock.Lock()
	f, err := os.OpenFile(fp, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
	if err != nil {
		return fmt.Errorf("opening %s: %w", fp, err)
	}
	defer f.Close()
	marshaller := jsonpb.Marshaler{}
	for _, ev := range handler.eventLog {
		contents := bytes.NewBuffer([]byte{})
		if err := marshaller.Marshal(contents, &ev); err != nil {
			return fmt.Errorf("marshalling event: %w", err)
		}
		if _, err := f.WriteString(contents.String() + "\n"); err != nil {
			return fmt.Errorf("writing string: %w", err)
		}
	}
	handler.logLock.Unlock()
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2018 Google LLC

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 (
	"bufio"
	"fmt"
	"io"
	"sync"
	"sync/atomic"
	"time"

	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"k8s.io/api/core/v1"
	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
	restclient "k8s.io/client-go/rest"
)

const defaultRetry int = 5

var colors = []int{
	31, // red
	32, // green
	33, // yellow
	34, // blue
	35, // magenta
	36, // cyan
	37, // lightGray
	90, // darkGray
	91, // lightRed
	92, // lightGreen
	93, // lightYellow
	94, // lightBlue
	95, // lightPurple
	96, // lightCyan
	97, // white
}

// LogAggregator aggregates the logs for all the deployed pods.
type LogAggregator struct {
	muted          int32
	creationTime   time.Time
	output         io.Writer
	retries        int
	nextColorIndex int
	lockColor      sync.Mutex
}

// NewLogAggregator creates a new LogAggregator for a given output.
func NewLogAggregator(out io.Writer) *LogAggregator {
	return &LogAggregator{
		creationTime: time.Now(),
		output:       out,
		retries:      defaultRetry,
	}
}

const streamRetryDelay = 1 * time.Second

// TODO(@r2d4): Figure out how to mock this out. fake.NewSimpleClient
// won't mock out restclient.Request and will just return a nil stream.
var getStream = func(r *restclient.Request) (io.ReadCloser, error) {
	return r.Stream()
}

func (a *LogAggregator) StreamLogs(client corev1.CoreV1Interface, image string) {
	for i := 0; i < a.retries; i++ {
		if err := a.streamLogs(client, image); err != nil {
			logrus.Infof("Error getting logs %s", err)
		}
		time.Sleep(streamRetryDelay)
	}
}

func (a *LogAggregator) SetCreationTime(t time.Time) {
	a.creationTime = t
}

// nolint: interfacer
func (a *LogAggregator) streamLogs(client corev1.CoreV1Interface, image string) error {
	pods, err := client.Pods("").List(meta_v1.ListOptions{
		IncludeUninitialized: true,
	})
	if err != nil {
		return errors.Wrap(err, "getting pods")
	}

	found := false

	logrus.Infof("Looking for logs to stream for %s", image)
	for _, p := range pods.Items {
		for _, c := range p.Spec.Containers {
			logrus.Debugf("Found container %s with image %s", c.Name, c.Image)
			if c.Image != image {
				continue
			}

			logrus.Infof("Trying to stream logs from pod: %s container: %s", p.Name, c.Name)
			pods := client.Pods(p.Namespace)
			if err := WaitForPodReady(pods, p.Name); err != nil {
				return errors.Wrap(err, "waiting for pod ready")
			}
			req := pods.GetLogs(p.Name, &v1.PodLogOptions{
				Follow:    true,
				Container: c.Name,
				SinceTime: &meta_v1.Time{
					Time: a.creationTime,
				},
			})
			rc, err := getStream(req)
			if err != nil {
				return errors.Wrap(err, "setting up container log stream")
			}
			defer rc.Close()

			color := a.nextColor()

			header := fmt.Sprintf("\033[1;%dm[%s %s]\033[0m", color, p.Name, c.Name)
			if err := a.streamRequest(header, rc); err != nil {
				return errors.Wrap(err, "streaming request")
			}

			found = true
		}
	}

	if !found {
		return fmt.Errorf("Image %s not found", image)
	}

	return nil
}

func (a *LogAggregator) nextColor() int {
	a.lockColor.Lock()
	color := colors[a.nextColorIndex]
	a.nextColorIndex++
	a.lockColor.Unlock()

	return color
}

func (a *LogAggregator) streamRequest(header string, rc io.Reader) error {
	r := bufio.NewReader(rc)
	for {
		// Read up to newline
		line, err := r.ReadBytes('\n')
		if err == io.EOF {
			break
		}
		if err != nil {
			return errors.Wrap(err, "reading bytes from log stream")
		}

		if a.IsMuted() {
			continue
		}

		if _, err := fmt.Fprintf(a.output, "%s %s", header, line); err != nil {
			return errors.Wrap(err, "writing to out")
		}
	}
	logrus.Infof("%s exited", header)
	return nil
}

// Mute mutes the logs.
func (a *LogAggregator) Mute() {
	atomic.StoreInt32(&a.muted, 1)
}

// Unmute unmute the logs.
func (a *LogAggregator) Unmute() {
	atomic.StoreInt32(&a.muted, 0)
}

// IsMuted says if the logs are to be muted.
func (a *LogAggregator) IsMuted() bool {
	return atomic.LoadInt32(&a.muted) == 1
}
<commit_msg>Stop running out of colors<commit_after>/*
Copyright 2018 Google LLC

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 (
	"bufio"
	"fmt"
	"io"
	"sync"
	"sync/atomic"
	"time"

	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"k8s.io/api/core/v1"
	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
	restclient "k8s.io/client-go/rest"
)

const defaultRetry int = 5

var colors = []int{
	31, // red
	32, // green
	33, // yellow
	34, // blue
	35, // magenta
	36, // cyan
	37, // lightGray
	90, // darkGray
	91, // lightRed
	92, // lightGreen
	93, // lightYellow
	94, // lightBlue
	95, // lightPurple
	96, // lightCyan
	97, // white
}

// LogAggregator aggregates the logs for all the deployed pods.
type LogAggregator struct {
	muted          int32
	creationTime   time.Time
	output         io.Writer
	retries        int
	nextColorIndex int
	lockColor      sync.Mutex
}

// NewLogAggregator creates a new LogAggregator for a given output.
func NewLogAggregator(out io.Writer) *LogAggregator {
	return &LogAggregator{
		creationTime: time.Now(),
		output:       out,
		retries:      defaultRetry,
	}
}

const streamRetryDelay = 1 * time.Second

// TODO(@r2d4): Figure out how to mock this out. fake.NewSimpleClient
// won't mock out restclient.Request and will just return a nil stream.
var getStream = func(r *restclient.Request) (io.ReadCloser, error) {
	return r.Stream()
}

func (a *LogAggregator) StreamLogs(client corev1.CoreV1Interface, image string) {
	for i := 0; i < a.retries; i++ {
		if err := a.streamLogs(client, image); err != nil {
			logrus.Infof("Error getting logs %s", err)
		}
		time.Sleep(streamRetryDelay)
	}
}

func (a *LogAggregator) SetCreationTime(t time.Time) {
	a.creationTime = t
}

// nolint: interfacer
func (a *LogAggregator) streamLogs(client corev1.CoreV1Interface, image string) error {
	pods, err := client.Pods("").List(meta_v1.ListOptions{
		IncludeUninitialized: true,
	})
	if err != nil {
		return errors.Wrap(err, "getting pods")
	}

	found := false

	logrus.Infof("Looking for logs to stream for %s", image)
	for _, p := range pods.Items {
		for _, c := range p.Spec.Containers {
			logrus.Debugf("Found container %s with image %s", c.Name, c.Image)
			if c.Image != image {
				continue
			}

			logrus.Infof("Trying to stream logs from pod: %s container: %s", p.Name, c.Name)
			pods := client.Pods(p.Namespace)
			if err := WaitForPodReady(pods, p.Name); err != nil {
				return errors.Wrap(err, "waiting for pod ready")
			}
			req := pods.GetLogs(p.Name, &v1.PodLogOptions{
				Follow:    true,
				Container: c.Name,
				SinceTime: &meta_v1.Time{
					Time: a.creationTime,
				},
			})
			rc, err := getStream(req)
			if err != nil {
				return errors.Wrap(err, "setting up container log stream")
			}
			defer rc.Close()

			color := a.nextColor()

			header := fmt.Sprintf("\033[1;%dm[%s %s]\033[0m", color, p.Name, c.Name)
			if err := a.streamRequest(header, rc); err != nil {
				return errors.Wrap(err, "streaming request")
			}

			found = true
		}
	}

	if !found {
		return fmt.Errorf("Image %s not found", image)
	}

	return nil
}

func (a *LogAggregator) nextColor() int {
	a.lockColor.Lock()
	color := colors[a.nextColorIndex]
	a.nextColorIndex = (a.nextColorIndex + 1) % len(colors)
	a.lockColor.Unlock()

	return color
}

func (a *LogAggregator) streamRequest(header string, rc io.Reader) error {
	r := bufio.NewReader(rc)
	for {
		// Read up to newline
		line, err := r.ReadBytes('\n')
		if err == io.EOF {
			break
		}
		if err != nil {
			return errors.Wrap(err, "reading bytes from log stream")
		}

		if a.IsMuted() {
			continue
		}

		if _, err := fmt.Fprintf(a.output, "%s %s", header, line); err != nil {
			return errors.Wrap(err, "writing to out")
		}
	}
	logrus.Infof("%s exited", header)
	return nil
}

// Mute mutes the logs.
func (a *LogAggregator) Mute() {
	atomic.StoreInt32(&a.muted, 1)
}

// Unmute unmute the logs.
func (a *LogAggregator) Unmute() {
	atomic.StoreInt32(&a.muted, 0)
}

// IsMuted says if the logs are to be muted.
func (a *LogAggregator) IsMuted() bool {
	return atomic.LoadInt32(&a.muted) == 1
}
<|endoftext|>


<commit_before>package stats

import (
	"net/http"
	"strconv"
	"sync"
	"time"

	"github.com/labstack/echo"
)

type (
	Stats struct {
		Uptime       time.Time      `json:"uptime"`
		RequestCount uint64         `json:"requestCount"`
		Statuses     map[string]int `json:"statuses"`
		mutex        sync.RWMutex
	}
)

func NewStats() *Stats {
	return &Stats{
		Uptime:   time.Now(),
		Statuses: map[string]int{},
	}
}

// Process is the middleware function.
func (s *Stats) Process(next echo.HandlerFunc) echo.HandlerFunc {
	return func(c echo.Context) error {
		if err := next(c); err != nil {
			c.Error(err)
		}
		s.mutex.Lock()
		defer s.mutex.Unlock()
		s.RequestCount++
		status := strconv.Itoa(c.Response().Status)
		s.Statuses[status]++
		return nil
	}
}

// Handle is the endpoint to get stats.
func (s *Stats) Handle(c echo.Context) error {
	s.mutex.RLock()
	defer s.mutex.RUnlock()
	return c.JSON(http.StatusOK, s)
}

// ServerHeader middleware adds a `Server` header to the response.
func ServerHeader(next echo.HandlerFunc) echo.HandlerFunc {
	return func(c echo.Context) error {
		c.Response().Header().Set(echo.HeaderServer, "Echo/3.0")
		return next(c)
	}
}
<commit_msg>Updated stats module<commit_after>package stats

import (
	"net/http"
	"strconv"
	"sync"
	"time"

	"github.com/labstack/echo"
	"github.com/robjporter/go-functions/browser"
)

type (
	Stats struct {
		Uptime         time.Time      `json:"uptime"`
		RequestCount   uint64         `json:"requestCount"`
		Statuses       map[string]int `json:"statuses"`
		Browsers       map[string]int `json:"browsers"`
		BrowserVersion map[string]int `json:"browserversion"`
		OS             map[string]int `json:"os"`
		Device         map[string]int `json:"device"`
		OSVersion      map[string]int `json:"osversion"`
		mutex          sync.RWMutex
	}
)

func NewStats() *Stats {
	return &Stats{
		Uptime:         time.Now(),
		Statuses:       map[string]int{},
		Browsers:       map[string]int{},
		BrowserVersion: map[string]int{},
		OS:             map[string]int{},
		Device:         map[string]int{},
		OSVersion:      map[string]int{},
	}
}

// Process is the middleware function.
func (s *Stats) Process(next echo.HandlerFunc) echo.HandlerFunc {
	return func(c echo.Context) error {
		if err := next(c); err != nil {
			c.Error(err)
		}
		s.mutex.Lock()
		defer s.mutex.Unlock()
		s.RequestCount++
		status := strconv.Itoa(c.Response().Status)
		browser, browserversion, devicename, osname, osversion := getBrowser(c.Request().UserAgent())
		s.Statuses[status]++
		s.Browsers[browser]++
		s.BrowserVersion[browser+" "+browserversion]++
		s.OS[osname]++
		s.OSVersion[osname+" "+osversion]++
		s.Device[devicename]++
		return nil
	}
}

func getBrowser(agent string) (string, string, string, string, string) {
	ua1 := browser.Parse(agent)
	return ua1.Browser.Name, ua1.Browser.Version, ua1.Device.Name, ua1.OS.Name, ua1.OS.Version
}

// Handle is the endpoint to get stats.
func (s *Stats) Handle(c echo.Context) error {
	s.mutex.RLock()
	defer s.mutex.RUnlock()
	return c.JSON(http.StatusOK, s)
}
<|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 zlib package implements reading and writing of zlib
// format compressed files, as specified in RFC 1950.
package zlib

import (
	"bufio"
	"compress/flate"
	"hash"
	"hash/adler32"
	"io"
	"os"
)

const zlibDeflate = 8

var ChecksumError os.Error = os.ErrorString("zlib checksum error")
var HeaderError os.Error = os.ErrorString("invalid zlib header")
var UnsupportedError os.Error = os.ErrorString("unsupported zlib format")

type reader struct {
	r        flate.Reader
	inflater io.ReadCloser
	digest   hash.Hash32
	err      os.Error
	scratch  [4]byte
}

// NewInflater creates a new io.ReadCloser that satisfies reads by decompressing data read from r.
// The implementation buffers input and may read more data than necessary from r.
// It is the caller's responsibility to call Close on the ReadCloser when done.
func NewInflater(r io.Reader) (io.ReadCloser, os.Error) {
	z := new(reader)
	if fr, ok := r.(flate.Reader); ok {
		z.r = fr
	} else {
		z.r = bufio.NewReader(r)
	}
	_, err := io.ReadFull(z.r, z.scratch[0:2])
	if err != nil {
		return nil, err
	}
	h := uint(z.scratch[0])<<8 | uint(z.scratch[1])
	if (z.scratch[0]&0x0f != zlibDeflate) || (h%31 != 0) {
		return nil, HeaderError
	}
	if z.scratch[1]&0x20 != 0 {
		// BUG(nigeltao): The zlib package does not implement the FDICT flag.
		return nil, UnsupportedError
	}
	z.digest = adler32.New()
	z.inflater = flate.NewInflater(z.r)
	return z, nil
}

func (z *reader) Read(p []byte) (n int, err os.Error) {
	if z.err != nil {
		return 0, z.err
	}
	if len(p) == 0 {
		return 0, nil
	}

	n, err = z.inflater.Read(p)
	z.digest.Write(p[0:n])
	if n != 0 || err != os.EOF {
		z.err = err
		return
	}

	// Finished file; check checksum.
	if _, err := io.ReadFull(z.r, z.scratch[0:4]); err != nil {
		z.err = err
		return 0, err
	}
	// ZLIB (RFC 1950) is big-endian, unlike GZIP (RFC 1952).
	checksum := uint32(z.scratch[0])<<24 | uint32(z.scratch[1])<<16 | uint32(z.scratch[2])<<8 | uint32(z.scratch[3])
	if checksum != z.digest.Sum32() {
		z.err = ChecksumError
		return 0, z.err
	}
	return
}

// Calling Close does not close the wrapped io.Reader originally passed to NewInflater.
func (z *reader) Close() os.Error {
	if z.err != nil {
		return z.err
	}
	z.err = z.inflater.Close()
	return z.err
}
<commit_msg>compress/zlib: add example to doc comment<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 zlib package implements reading and writing of zlib
format compressed data, as specified in RFC 1950.

The implementation provides filters that uncompress during reading
and compress during writing.  For example, to write compressed data
to a buffer:

	var b bytes.Buffer
	w, err := zlib.NewDeflater(&b)
	w.Write(strings.Bytes("hello, world\n"))
	w.Close()

and to read that data back:

	r, err := zlib.NewInflater(&b)
	io.Copy(os.Stdout, r)
	r.Close()
*/
package zlib

import (
	"bufio"
	"compress/flate"
	"hash"
	"hash/adler32"
	"io"
	"os"
)

const zlibDeflate = 8

var ChecksumError os.Error = os.ErrorString("zlib checksum error")
var HeaderError os.Error = os.ErrorString("invalid zlib header")
var UnsupportedError os.Error = os.ErrorString("unsupported zlib format")

type reader struct {
	r        flate.Reader
	inflater io.ReadCloser
	digest   hash.Hash32
	err      os.Error
	scratch  [4]byte
}

// NewInflater creates a new io.ReadCloser that satisfies reads by decompressing data read from r.
// The implementation buffers input and may read more data than necessary from r.
// It is the caller's responsibility to call Close on the ReadCloser when done.
func NewInflater(r io.Reader) (io.ReadCloser, os.Error) {
	z := new(reader)
	if fr, ok := r.(flate.Reader); ok {
		z.r = fr
	} else {
		z.r = bufio.NewReader(r)
	}
	_, err := io.ReadFull(z.r, z.scratch[0:2])
	if err != nil {
		return nil, err
	}
	h := uint(z.scratch[0])<<8 | uint(z.scratch[1])
	if (z.scratch[0]&0x0f != zlibDeflate) || (h%31 != 0) {
		return nil, HeaderError
	}
	if z.scratch[1]&0x20 != 0 {
		// BUG(nigeltao): The zlib package does not implement the FDICT flag.
		return nil, UnsupportedError
	}
	z.digest = adler32.New()
	z.inflater = flate.NewInflater(z.r)
	return z, nil
}

func (z *reader) Read(p []byte) (n int, err os.Error) {
	if z.err != nil {
		return 0, z.err
	}
	if len(p) == 0 {
		return 0, nil
	}

	n, err = z.inflater.Read(p)
	z.digest.Write(p[0:n])
	if n != 0 || err != os.EOF {
		z.err = err
		return
	}

	// Finished file; check checksum.
	if _, err := io.ReadFull(z.r, z.scratch[0:4]); err != nil {
		z.err = err
		return 0, err
	}
	// ZLIB (RFC 1950) is big-endian, unlike GZIP (RFC 1952).
	checksum := uint32(z.scratch[0])<<24 | uint32(z.scratch[1])<<16 | uint32(z.scratch[2])<<8 | uint32(z.scratch[3])
	if checksum != z.digest.Sum32() {
		z.err = ChecksumError
		return 0, z.err
	}
	return
}

// Calling Close does not close the wrapped io.Reader originally passed to NewInflater.
func (z *reader) Close() os.Error {
	if z.err != nil {
		return z.err
	}
	z.err = z.inflater.Close()
	return z.err
}
<|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 http_test

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

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 net.newPollServer") ||
			strings.Contains(stack, "created by net.startServer") ||
			strings.Contains(stack, "created by testing.RunTests") ||
			strings.Contains(stack, "closeWriteAndWait") ||
			strings.Contains(stack, "testing.Main(") {
			continue
		}
		gs = append(gs, stack)
	}
	sort.Strings(gs)
	return
}

// Verify the other tests didn't leave any goroutines running.
// This is in a file named z_last_test.go so it sorts at the end.
func TestGoroutinesRunning(t *testing.T) {
	if testing.Short() {
		t.Skip("not counting goroutines for leakage in -short mode")
	}
	gs := interestingGoroutines()

	n := 0
	stackCount := make(map[string]int)
	for _, g := range gs {
		stackCount[g]++
		n++
	}

	t.Logf("num goroutines = %d", n)
	if n > 0 {
		t.Error("Too many goroutines.")
		for stack, count := range stackCount {
			t.Logf("%d instances of:\n%s", count, stack)
		}
	}
}

func afterTest(t *testing.T) {
	http.DefaultTransport.(*http.Transport).CloseIdleConnections()
	if testing.Short() {
		return
	}
	var bad string
	badSubstring := map[string]string{
		").readLoop(":                                  "a Transport",
		").writeLoop(":                                 "a Transport",
		"created by net/http/httptest.(*Server).Start": "an httptest.Server",
		"timeoutHandler":                               "a TimeoutHandler",
		"net.(*netFD).connect(":                        "a timing out dial",
	}
	var stacks string
	for i := 0; i < 4; 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(250 * time.Millisecond)
	}
	t.Errorf("Test appears to have leaked %s:\n%s", bad, stacks)
}
<commit_msg>net/http: ignore more uninteresting goroutines<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 http_test

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

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 net.newPollServer") ||
			strings.Contains(stack, "created by net.startServer") ||
			strings.Contains(stack, "created by testing.RunTests") ||
			strings.Contains(stack, "closeWriteAndWait") ||
			strings.Contains(stack, "testing.Main(") ||
			// These only show up with GOTRACEBACK=2; Issue 5005 (comment 28)
			strings.Contains(stack, "runtime.goexit") ||
			strings.Contains(stack, "created by runtime.gc") ||
			strings.Contains(stack, "runtime.MHeap_Scavenger") {
			continue
		}
		gs = append(gs, stack)
	}
	sort.Strings(gs)
	return
}

// Verify the other tests didn't leave any goroutines running.
// This is in a file named z_last_test.go so it sorts at the end.
func TestGoroutinesRunning(t *testing.T) {
	if testing.Short() {
		t.Skip("not counting goroutines for leakage in -short mode")
	}
	gs := interestingGoroutines()

	n := 0
	stackCount := make(map[string]int)
	for _, g := range gs {
		stackCount[g]++
		n++
	}

	t.Logf("num goroutines = %d", n)
	if n > 0 {
		t.Error("Too many goroutines.")
		for stack, count := range stackCount {
			t.Logf("%d instances of:\n%s", count, stack)
		}
	}
}

func afterTest(t *testing.T) {
	http.DefaultTransport.(*http.Transport).CloseIdleConnections()
	if testing.Short() {
		return
	}
	var bad string
	badSubstring := map[string]string{
		").readLoop(":                                  "a Transport",
		").writeLoop(":                                 "a Transport",
		"created by net/http/httptest.(*Server).Start": "an httptest.Server",
		"timeoutHandler":                               "a TimeoutHandler",
		"net.(*netFD).connect(":                        "a timing out dial",
	}
	var stacks string
	for i := 0; i < 4; 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(250 * time.Millisecond)
	}
	t.Errorf("Test appears to have leaked %s:\n%s", bad, stacks)
}
<|endoftext|>


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

package strings_test

import (
	"fmt"
	"strings"
)

// Fields are: ["foo" "bar" "baz"]
func ExampleFields() {
	fmt.Printf("Fields are: %q", strings.Fields("  foo bar  baz   "))
}

// true
// false
// true
// true
func ExampleContains() {
	fmt.Println(strings.Contains("seafood", "foo"))
	fmt.Println(strings.Contains("seafood", "bar"))
	fmt.Println(strings.Contains("seafood", ""))
	fmt.Println(strings.Contains("", ""))
}

// false
// true
// false
// false
func ExampleContainsAny() {
	fmt.Println(strings.ContainsAny("team", "i"))
	fmt.Println(strings.ContainsAny("failure", "u & i"))
	fmt.Println(strings.ContainsAny("foo", ""))
	fmt.Println(strings.ContainsAny("", ""))

}

// 3
// 5
func ExampleCount() {
	fmt.Println(strings.Count("cheese", "e"))
	fmt.Println(strings.Count("five", "")) // before & after each rune
}

// true
func ExampleEqualFold() {
	fmt.Println(strings.EqualFold("Go", "go"))
}

// 4
// -1
func ExampleIndex() {
	fmt.Println(strings.Index("chicken", "ken"))
	fmt.Println(strings.Index("chicken", "dmr"))
}

// 4
// -1
func ExampleRune() {
	fmt.Println(strings.IndexRune("chicken", 'k'))
	fmt.Println(strings.IndexRune("chicken", 'd'))
}

// 0
// 3
// -1
func ExampleLastIndex() {
	fmt.Println(strings.Index("go gopher", "go"))
	fmt.Println(strings.LastIndex("go gopher", "go"))
	fmt.Println(strings.LastIndex("go gopher", "rodent"))
}

// foo, bar, baz
func ExampleJoin() {
	s := []string{"foo", "bar", "baz"}
	fmt.Println(strings.Join(s, ", "))
}

// banana
func ExampleRepeat() {
	fmt.Println("ba" + strings.Repeat("na", 2))
}

// oinky oinky oink
// moo moo moo
func ExampleReplace() {
	fmt.Println(strings.Replace("oink oink oink", "k", "ky", 2))
	fmt.Println(strings.Replace("oink oink oink", "oink", "moo", -1))
}

// ["a" "b" "c"]
// ["" "man " "plan " "canal panama"]
// [" " "x" "y" "z" " "]
func ExampleSplit() {
	fmt.Printf("%q\n", strings.Split("a,b,c", ","))
	fmt.Printf("%q\n", strings.Split("a man a plan a canal panama", "a "))
	fmt.Printf("%q\n", strings.Split(" xyz ", ""))
}

// ["a" "b,c"]
// [] (nil = true)
func ExampleSplitN() {
	fmt.Printf("%q\n", strings.SplitN("a,b,c", ",", 2))
	z := strings.SplitN("a,b,c", ",", 0)
	fmt.Printf("%q (nil = %v)\n", z, z == nil)
}

// ["a," "b," "c"]
func ExampleSplitAfter() {
	fmt.Printf("%q\n", strings.SplitAfter("a,b,c", ","))
}

// ["a," "b,c"]
func ExampleSplitAfterN() {
	fmt.Printf("%q\n", strings.SplitAfterN("a,b,c", ",", 2))
}

// Her Royal Highness
func ExampleTitle() {
	fmt.Println(strings.Title("her royal highness"))
}

// LOUD NOISES
// ХЛЕБ
func ExampleToTitle() {
	fmt.Println(strings.ToTitle("loud noises"))
	fmt.Println(strings.ToTitle("хлеб"))
}

// [Achtung]
func ExampleTrim() {
	fmt.Printf("[%s]", strings.Trim(" !!! Achtung !!! ", "! "))
}

// 'Gjnf oevyyvt naq gur fyvgul tbcure...
func ExampleMap() {
	rot13 := func(r rune) rune {
		switch {
		case r >= 'A' && r <= 'Z':
			return 'A' + (r-'A'+13)%26
		case r >= 'a' && r <= 'z':
			return 'a' + (r-'a'+13)%26
		}
		return r
	}
	fmt.Println(strings.Map(rot13, "'Twas brillig and the slithy gopher..."))
}

// a lone gopher
func ExampleTrimSpace() {
	fmt.Println(strings.TrimSpace(" \t\n a lone gopher \n\t\r\n"))
}

// This is <b>HTML</b>!
func ExampleNewReplacer() {
	r := strings.NewReplacer("<", "<", ">", ">")
	fmt.Println(r.Replace("This is HTML!"))
}

// GOPHER
func ExampleToUpper() {
	fmt.Println(strings.ToUpper("Gopher"))
}

// gopher
func ExampleToLower() {
	fmt.Println(strings.ToLower("Gopher"))
}
<commit_msg>strings: add Bernardo O'Higgins example<commit_after>// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package strings_test

import (
	"fmt"
	"strings"
)

// Fields are: ["foo" "bar" "baz"]
func ExampleFields() {
	fmt.Printf("Fields are: %q", strings.Fields("  foo bar  baz   "))
}

// true
// false
// true
// true
func ExampleContains() {
	fmt.Println(strings.Contains("seafood", "foo"))
	fmt.Println(strings.Contains("seafood", "bar"))
	fmt.Println(strings.Contains("seafood", ""))
	fmt.Println(strings.Contains("", ""))
}

// false
// true
// false
// false
func ExampleContainsAny() {
	fmt.Println(strings.ContainsAny("team", "i"))
	fmt.Println(strings.ContainsAny("failure", "u & i"))
	fmt.Println(strings.ContainsAny("foo", ""))
	fmt.Println(strings.ContainsAny("", ""))

}

// 3
// 5
func ExampleCount() {
	fmt.Println(strings.Count("cheese", "e"))
	fmt.Println(strings.Count("five", "")) // before & after each rune
}

// true
func ExampleEqualFold() {
	fmt.Println(strings.EqualFold("Go", "go"))
}

// 4
// -1
func ExampleIndex() {
	fmt.Println(strings.Index("chicken", "ken"))
	fmt.Println(strings.Index("chicken", "dmr"))
}

// 4
// -1
func ExampleRune() {
	fmt.Println(strings.IndexRune("chicken", 'k'))
	fmt.Println(strings.IndexRune("chicken", 'd'))
}

// 0
// 3
// -1
func ExampleLastIndex() {
	fmt.Println(strings.Index("go gopher", "go"))
	fmt.Println(strings.LastIndex("go gopher", "go"))
	fmt.Println(strings.LastIndex("go gopher", "rodent"))
}

// foo, bar, baz
func ExampleJoin() {
	s := []string{"foo", "bar", "baz"}
	fmt.Println(strings.Join(s, ", "))
}

// banana
func ExampleRepeat() {
	fmt.Println("ba" + strings.Repeat("na", 2))
}

// oinky oinky oink
// moo moo moo
func ExampleReplace() {
	fmt.Println(strings.Replace("oink oink oink", "k", "ky", 2))
	fmt.Println(strings.Replace("oink oink oink", "oink", "moo", -1))
}

// ["a" "b" "c"]
// ["" "man " "plan " "canal panama"]
// [" " "x" "y" "z" " "]
// [""]
func ExampleSplit() {
	fmt.Printf("%q\n", strings.Split("a,b,c", ","))
	fmt.Printf("%q\n", strings.Split("a man a plan a canal panama", "a "))
	fmt.Printf("%q\n", strings.Split(" xyz ", ""))
	fmt.Printf("%q\n", strings.Split("", "Bernardo O'Higgins"))
}

// ["a" "b,c"]
// [] (nil = true)
func ExampleSplitN() {
	fmt.Printf("%q\n", strings.SplitN("a,b,c", ",", 2))
	z := strings.SplitN("a,b,c", ",", 0)
	fmt.Printf("%q (nil = %v)\n", z, z == nil)
}

// ["a," "b," "c"]
func ExampleSplitAfter() {
	fmt.Printf("%q\n", strings.SplitAfter("a,b,c", ","))
}

// ["a," "b,c"]
func ExampleSplitAfterN() {
	fmt.Printf("%q\n", strings.SplitAfterN("a,b,c", ",", 2))
}

// Her Royal Highness
func ExampleTitle() {
	fmt.Println(strings.Title("her royal highness"))
}

// LOUD NOISES
// ХЛЕБ
func ExampleToTitle() {
	fmt.Println(strings.ToTitle("loud noises"))
	fmt.Println(strings.ToTitle("хлеб"))
}

// [Achtung]
func ExampleTrim() {
	fmt.Printf("[%s]", strings.Trim(" !!! Achtung !!! ", "! "))
}

// 'Gjnf oevyyvt naq gur fyvgul tbcure...
func ExampleMap() {
	rot13 := func(r rune) rune {
		switch {
		case r >= 'A' && r <= 'Z':
			return 'A' + (r-'A'+13)%26
		case r >= 'a' && r <= 'z':
			return 'a' + (r-'a'+13)%26
		}
		return r
	}
	fmt.Println(strings.Map(rot13, "'Twas brillig and the slithy gopher..."))
}

// a lone gopher
func ExampleTrimSpace() {
	fmt.Println(strings.TrimSpace(" \t\n a lone gopher \n\t\r\n"))
}

// This is <b>HTML</b>!
func ExampleNewReplacer() {
	r := strings.NewReplacer("<", "<", ">", ">")
	fmt.Println(r.Replace("This is HTML!"))
}

// GOPHER
func ExampleToUpper() {
	fmt.Println(strings.ToUpper("Gopher"))
}

// gopher
func ExampleToLower() {
	fmt.Println(strings.ToLower("Gopher"))
}
<|endoftext|>