<commit_before>/*
Copyright 2016 The Kubernetes 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 templates

import (
	"bytes"
	"fmt"
	"strings"
	"text/template"
	"unicode"

	"k8s.io/kubectl/pkg/util/term"

	"github.com/spf13/cobra"
	flag "github.com/spf13/pflag"
)

type FlagExposer interface {
	ExposeFlags(cmd *cobra.Command, flags ...string) FlagExposer
}

func ActsAsRootCommand(cmd *cobra.Command, filters []string, groups ...CommandGroup) FlagExposer {
	if cmd == nil {
		panic("nil root command")
	}
	templater := &templater{
		RootCmd:       cmd,
		UsageTemplate: MainUsageTemplate(),
		HelpTemplate:  MainHelpTemplate(),
		CommandGroups: groups,
		Filtered:      filters,
	}
	cmd.SetUsageFunc(templater.UsageFunc())
	cmd.SetHelpFunc(templater.HelpFunc())
	return templater
}

func UseOptionsTemplates(cmd *cobra.Command) {
	templater := &templater{
		UsageTemplate: OptionsUsageTemplate(),
		HelpTemplate:  OptionsHelpTemplate(),
	}
	cmd.SetUsageFunc(templater.UsageFunc())
	cmd.SetHelpFunc(templater.HelpFunc())
}

type templater struct {
	UsageTemplate string
	HelpTemplate  string
	RootCmd       *cobra.Command
	CommandGroups
	Filtered []string
}

func (templater *templater) ExposeFlags(cmd *cobra.Command, flags ...string) FlagExposer {
	cmd.SetUsageFunc(templater.UsageFunc(flags...))
	return templater
}

func (templater *templater) HelpFunc() func(*cobra.Command, []string) {
	return func(c *cobra.Command, s []string) {
		t := template.New("help")
		t.Funcs(templater.templateFuncs())
		template.Must(t.Parse(templater.HelpTemplate))
		out := term.NewResponsiveWriter(c.OutOrStdout())
		err := t.Execute(out, c)
		if err != nil {
			c.Println(err)
		}
	}
}

func (templater *templater) UsageFunc(exposedFlags ...string) func(*cobra.Command) error {
	return func(c *cobra.Command) error {
		t := template.New("usage")
		t.Funcs(templater.templateFuncs(exposedFlags...))
		template.Must(t.Parse(templater.UsageTemplate))
		out := term.NewResponsiveWriter(c.OutOrStderr())
		return t.Execute(out, c)
	}
}

func (templater *templater) templateFuncs(exposedFlags ...string) template.FuncMap {
	return template.FuncMap{
		"trim":                strings.TrimSpace,
		"trimRight":           func(s string) string { return strings.TrimRightFunc(s, unicode.IsSpace) },
		"trimLeft":            func(s string) string { return strings.TrimLeftFunc(s, unicode.IsSpace) },
		"gt":                  cobra.Gt,
		"eq":                  cobra.Eq,
		"rpad":                rpad,
		"appendIfNotPresent":  appendIfNotPresent,
		"flagsNotIntersected": flagsNotIntersected,
		"visibleFlags":        visibleFlags,
		"flagsUsages":         flagsUsages,
		"cmdGroups":           templater.cmdGroups,
		"cmdGroupsString":     templater.cmdGroupsString,
		"rootCmd":             templater.rootCmdName,
		"isRootCmd":           templater.isRootCmd,
		"optionsCmdFor":       templater.optionsCmdFor,
		"usageLine":           templater.usageLine,
		"exposed": func(c *cobra.Command) *flag.FlagSet {
			exposed := flag.NewFlagSet("exposed", flag.ContinueOnError)
			if len(exposedFlags) > 0 {
				for _, name := range exposedFlags {
					if flag := c.Flags().Lookup(name); flag != nil {
						exposed.AddFlag(flag)
					}
				}
			}
			return exposed
		},
	}
}

func (templater *templater) cmdGroups(c *cobra.Command, all []*cobra.Command) []CommandGroup {
	if len(templater.CommandGroups) > 0 && c == templater.RootCmd {
		all = filter(all, templater.Filtered...)
		return AddAdditionalCommands(templater.CommandGroups, "Other Commands:", all)
	}
	all = filter(all, "options")
	return []CommandGroup{
		{
			Message:  "Available Commands:",
			Commands: all,
		},
	}
}

func (t *templater) cmdGroupsString(c *cobra.Command) string {
	groups := []string{}
	for _, cmdGroup := range t.cmdGroups(c, c.Commands()) {
		cmds := []string{cmdGroup.Message}
		for _, cmd := range cmdGroup.Commands {
			if cmd.IsAvailableCommand() {
				cmds = append(cmds, "  "+rpad(cmd.Name(), cmd.NamePadding())+" "+cmd.Short)
			}
		}
		groups = append(groups, strings.Join(cmds, "\n"))
	}
	return strings.Join(groups, "\n\n")
}

func (t *templater) rootCmdName(c *cobra.Command) string {
	return t.rootCmd(c).CommandPath()
}

func (t *templater) isRootCmd(c *cobra.Command) bool {
	return t.rootCmd(c) == c
}

func (t *templater) parents(c *cobra.Command) []*cobra.Command {
	parents := []*cobra.Command{c}
	for current := c; !t.isRootCmd(current) && current.HasParent(); {
		current = current.Parent()
		parents = append(parents, current)
	}
	return parents
}

func (t *templater) rootCmd(c *cobra.Command) *cobra.Command {
	if c != nil && !c.HasParent() {
		return c
	}
	if t.RootCmd == nil {
		panic("nil root cmd")
	}
	return t.RootCmd
}

func (t *templater) optionsCmdFor(c *cobra.Command) string {
	if !c.Runnable() {
		return ""
	}
	rootCmdStructure := t.parents(c)
	for i := len(rootCmdStructure) - 1; i >= 0; i-- {
		cmd := rootCmdStructure[i]
		if _, _, err := cmd.Find([]string{"options"}); err == nil {
			return cmd.CommandPath() + " options"
		}
	}
	return ""
}

func (t *templater) usageLine(c *cobra.Command) string {
	usage := c.UseLine()
	suffix := "[options]"
	if c.HasFlags() && !strings.Contains(usage, suffix) {
		usage += " " + suffix
	}
	return usage
}

func flagsUsages(f *flag.FlagSet) string {
	x := new(bytes.Buffer)

	f.VisitAll(func(flag *flag.Flag) {
		if flag.Hidden {
			return
		}
		format := "--%s=%s: %s\n"

		if flag.Value.Type() == "string" {
			format = "--%s='%s': %s\n"
		}

		if len(flag.Shorthand) > 0 {
			format = "  -%s, " + format
		} else {
			format = "   %s   " + format
		}

		fmt.Fprintf(x, format, flag.Shorthand, flag.Name, flag.DefValue, flag.Usage)
	})

	return x.String()
}

func rpad(s string, padding int) string {
	template := fmt.Sprintf("%%-%ds", padding)
	return fmt.Sprintf(template, s)
}

func appendIfNotPresent(s, stringToAppend string) string {
	if strings.Contains(s, stringToAppend) {
		return s
	}
	return s + " " + stringToAppend
}

func flagsNotIntersected(l *flag.FlagSet, r *flag.FlagSet) *flag.FlagSet {
	f := flag.NewFlagSet("notIntersected", flag.ContinueOnError)
	l.VisitAll(func(flag *flag.Flag) {
		if r.Lookup(flag.Name) == nil {
			f.AddFlag(flag)
		}
	})
	return f
}

func visibleFlags(l *flag.FlagSet) *flag.FlagSet {
	hidden := "help"
	f := flag.NewFlagSet("visible", flag.ContinueOnError)
	l.VisitAll(func(flag *flag.Flag) {
		if flag.Name != hidden {
			f.AddFlag(flag)
		}
	})
	return f
}

func filter(cmds []*cobra.Command, names ...string) []*cobra.Command {
	out := []*cobra.Command{}
	for _, c := range cmds {
		if c.Hidden {
			continue
		}
		skip := false
		for _, name := range names {
			if name == c.Name() {
				skip = true
				break
			}
		}
		if skip {
			continue
		}
		out = append(out, c)
	}
	return out
}
<commit_msg>kubectl: remove usage info from bad flag error msg<commit_after>/*
Copyright 2016 The Kubernetes 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 templates

import (
	"bytes"
	"fmt"
	"strings"
	"text/template"
	"unicode"

	"k8s.io/kubectl/pkg/util/term"

	"github.com/spf13/cobra"
	flag "github.com/spf13/pflag"
)

type FlagExposer interface {
	ExposeFlags(cmd *cobra.Command, flags ...string) FlagExposer
}

func ActsAsRootCommand(cmd *cobra.Command, filters []string, groups ...CommandGroup) FlagExposer {
	if cmd == nil {
		panic("nil root command")
	}
	templater := &templater{
		RootCmd:       cmd,
		UsageTemplate: MainUsageTemplate(),
		HelpTemplate:  MainHelpTemplate(),
		CommandGroups: groups,
		Filtered:      filters,
	}
	cmd.SetFlagErrorFunc(templater.FlagErrorFunc())
	cmd.SetUsageFunc(templater.UsageFunc())
	cmd.SetHelpFunc(templater.HelpFunc())
	return templater
}

func UseOptionsTemplates(cmd *cobra.Command) {
	templater := &templater{
		UsageTemplate: OptionsUsageTemplate(),
		HelpTemplate:  OptionsHelpTemplate(),
	}
	cmd.SetUsageFunc(templater.UsageFunc())
	cmd.SetHelpFunc(templater.HelpFunc())
}

type templater struct {
	UsageTemplate string
	HelpTemplate  string
	RootCmd       *cobra.Command
	CommandGroups
	Filtered []string
}

func (templater *templater) FlagErrorFunc(exposedFlags ...string) func(*cobra.Command, error) error {
	return func(c *cobra.Command, err error) error {
		c.SilenceUsage = true
		switch c.CalledAs() {
		case "options":
			return fmt.Errorf("%s\nRun '%s' without flags.", err, c.CommandPath())
		default:
			return fmt.Errorf("%s\nSee '%s --help' for usage.", err, c.CommandPath())
		}
	}
}

func (templater *templater) ExposeFlags(cmd *cobra.Command, flags ...string) FlagExposer {
	cmd.SetUsageFunc(templater.UsageFunc(flags...))
	return templater
}

func (templater *templater) HelpFunc() func(*cobra.Command, []string) {
	return func(c *cobra.Command, s []string) {
		t := template.New("help")
		t.Funcs(templater.templateFuncs())
		template.Must(t.Parse(templater.HelpTemplate))
		out := term.NewResponsiveWriter(c.OutOrStdout())
		err := t.Execute(out, c)
		if err != nil {
			c.Println(err)
		}
	}
}

func (templater *templater) UsageFunc(exposedFlags ...string) func(*cobra.Command) error {
	return func(c *cobra.Command) error {
		t := template.New("usage")
		t.Funcs(templater.templateFuncs(exposedFlags...))
		template.Must(t.Parse(templater.UsageTemplate))
		out := term.NewResponsiveWriter(c.OutOrStderr())
		return t.Execute(out, c)
	}
}

func (templater *templater) templateFuncs(exposedFlags ...string) template.FuncMap {
	return template.FuncMap{
		"trim":                strings.TrimSpace,
		"trimRight":           func(s string) string { return strings.TrimRightFunc(s, unicode.IsSpace) },
		"trimLeft":            func(s string) string { return strings.TrimLeftFunc(s, unicode.IsSpace) },
		"gt":                  cobra.Gt,
		"eq":                  cobra.Eq,
		"rpad":                rpad,
		"appendIfNotPresent":  appendIfNotPresent,
		"flagsNotIntersected": flagsNotIntersected,
		"visibleFlags":        visibleFlags,
		"flagsUsages":         flagsUsages,
		"cmdGroups":           templater.cmdGroups,
		"cmdGroupsString":     templater.cmdGroupsString,
		"rootCmd":             templater.rootCmdName,
		"isRootCmd":           templater.isRootCmd,
		"optionsCmdFor":       templater.optionsCmdFor,
		"usageLine":           templater.usageLine,
		"exposed": func(c *cobra.Command) *flag.FlagSet {
			exposed := flag.NewFlagSet("exposed", flag.ContinueOnError)
			if len(exposedFlags) > 0 {
				for _, name := range exposedFlags {
					if flag := c.Flags().Lookup(name); flag != nil {
						exposed.AddFlag(flag)
					}
				}
			}
			return exposed
		},
	}
}

func (templater *templater) cmdGroups(c *cobra.Command, all []*cobra.Command) []CommandGroup {
	if len(templater.CommandGroups) > 0 && c == templater.RootCmd {
		all = filter(all, templater.Filtered...)
		return AddAdditionalCommands(templater.CommandGroups, "Other Commands:", all)
	}
	all = filter(all, "options")
	return []CommandGroup{
		{
			Message:  "Available Commands:",
			Commands: all,
		},
	}
}

func (t *templater) cmdGroupsString(c *cobra.Command) string {
	groups := []string{}
	for _, cmdGroup := range t.cmdGroups(c, c.Commands()) {
		cmds := []string{cmdGroup.Message}
		for _, cmd := range cmdGroup.Commands {
			if cmd.IsAvailableCommand() {
				cmds = append(cmds, "  "+rpad(cmd.Name(), cmd.NamePadding())+" "+cmd.Short)
			}
		}
		groups = append(groups, strings.Join(cmds, "\n"))
	}
	return strings.Join(groups, "\n\n")
}

func (t *templater) rootCmdName(c *cobra.Command) string {
	return t.rootCmd(c).CommandPath()
}

func (t *templater) isRootCmd(c *cobra.Command) bool {
	return t.rootCmd(c) == c
}

func (t *templater) parents(c *cobra.Command) []*cobra.Command {
	parents := []*cobra.Command{c}
	for current := c; !t.isRootCmd(current) && current.HasParent(); {
		current = current.Parent()
		parents = append(parents, current)
	}
	return parents
}

func (t *templater) rootCmd(c *cobra.Command) *cobra.Command {
	if c != nil && !c.HasParent() {
		return c
	}
	if t.RootCmd == nil {
		panic("nil root cmd")
	}
	return t.RootCmd
}

func (t *templater) optionsCmdFor(c *cobra.Command) string {
	if !c.Runnable() {
		return ""
	}
	rootCmdStructure := t.parents(c)
	for i := len(rootCmdStructure) - 1; i >= 0; i-- {
		cmd := rootCmdStructure[i]
		if _, _, err := cmd.Find([]string{"options"}); err == nil {
			return cmd.CommandPath() + " options"
		}
	}
	return ""
}

func (t *templater) usageLine(c *cobra.Command) string {
	usage := c.UseLine()
	suffix := "[options]"
	if c.HasFlags() && !strings.Contains(usage, suffix) {
		usage += " " + suffix
	}
	return usage
}

func flagsUsages(f *flag.FlagSet) string {
	x := new(bytes.Buffer)

	f.VisitAll(func(flag *flag.Flag) {
		if flag.Hidden {
			return
		}
		format := "--%s=%s: %s\n"

		if flag.Value.Type() == "string" {
			format = "--%s='%s': %s\n"
		}

		if len(flag.Shorthand) > 0 {
			format = "  -%s, " + format
		} else {
			format = "   %s   " + format
		}

		fmt.Fprintf(x, format, flag.Shorthand, flag.Name, flag.DefValue, flag.Usage)
	})

	return x.String()
}

func rpad(s string, padding int) string {
	template := fmt.Sprintf("%%-%ds", padding)
	return fmt.Sprintf(template, s)
}

func appendIfNotPresent(s, stringToAppend string) string {
	if strings.Contains(s, stringToAppend) {
		return s
	}
	return s + " " + stringToAppend
}

func flagsNotIntersected(l *flag.FlagSet, r *flag.FlagSet) *flag.FlagSet {
	f := flag.NewFlagSet("notIntersected", flag.ContinueOnError)
	l.VisitAll(func(flag *flag.Flag) {
		if r.Lookup(flag.Name) == nil {
			f.AddFlag(flag)
		}
	})
	return f
}

func visibleFlags(l *flag.FlagSet) *flag.FlagSet {
	hidden := "help"
	f := flag.NewFlagSet("visible", flag.ContinueOnError)
	l.VisitAll(func(flag *flag.Flag) {
		if flag.Name != hidden {
			f.AddFlag(flag)
		}
	})
	return f
}

func filter(cmds []*cobra.Command, names ...string) []*cobra.Command {
	out := []*cobra.Command{}
	for _, c := range cmds {
		if c.Hidden {
			continue
		}
		skip := false
		for _, name := range names {
			if name == c.Name() {
				skip = true
				break
			}
		}
		if skip {
			continue
		}
		out = append(out, c)
	}
	return out
}
<|endoftext|>


<commit_before>package acpb

import (
	"fmt"
	"github.com/golang/protobuf/proto" // protobuf is now here.
	"github.com/unix4fun/ac/acutl"
	"os"
	"runtime"
	"testing"
)

type pkhandler func(*AcPublicKeyMessageRequest) (acMsgResponse *AcPublicKeyMessageResponse, err error)

type Test struct {
	// input
	inType AcPublicKeyMessageRequestAcPKReqMsgType
	in     *AcPublicKeyMessageRequest // input
	// Expected output
	oType         AcPublicKeyMessageResponseAcPKRespMsgType
	oBada         bool
	oErrorCode    int32
	oBlob         []byte
	oPubKeys      []*AcPublicKey
	oHaveErrorMsg bool
}

var PKGENTests = []Test{
	// TEST #1 : OK
	{AcPublicKeyMessageRequest_PK_GEN,
		&AcPublicKeyMessageRequest{
			Nick:   proto.String("nick1"),
			Host:   proto.String("prout@hostname"),
			Server: proto.String("freenode.net"),
		}, AcPublicKeyMessageResponse_PKR_GEN, true, 0, nil, nil, false,
	},
	// TEST #2 : FAIL -1
	{AcPublicKeyMessageRequest_PK_GEN,
		&AcPublicKeyMessageRequest{
			Nick:   nil,
			Host:   proto.String("prout@hostname"),
			Server: proto.String("freenode.net"),
		}, AcPublicKeyMessageResponse_PKR_GEN, false, -1, nil, nil, true,
	},

	// TEST #3 : FAIL -1
	{AcPublicKeyMessageRequest_PK_GEN,
		&AcPublicKeyMessageRequest{
			Nick:   proto.String("mynick"),
			Host:   proto.String("eau@host.com"),
			Server: nil,
		}, AcPublicKeyMessageResponse_PKR_GEN, false, -1, nil, nil, true,
	},

	// TEST #4 : OK
	{AcPublicKeyMessageRequest_PK_GEN,
		&AcPublicKeyMessageRequest{
			Nick:   proto.String("eau"),
			Host:   proto.String("h2o@unix4funnnn.net"),
			Server: proto.String("freeenode.net"),
		}, AcPublicKeyMessageResponse_PKR_GEN, true, 0, nil, nil, false,
	},

	// TEST #4 : FAIL -1
	{AcPublicKeyMessageRequest_PK_GEN,
		&AcPublicKeyMessageRequest{
			Nick:   nil,
			Host:   nil,
			Server: nil,
		}, AcPublicKeyMessageResponse_PKR_GEN, false, -1, nil, nil, true,
	},
}

var PKADDTests = []Test{
	// TEST #1 : OK
	{AcPublicKeyMessageRequest_PK_ADD,
		&AcPublicKeyMessageRequest{
			Nick:   proto.String("nick1"),
			Host:   proto.String("prout@hostname"),
			Server: proto.String("freenode.net"),
			Blob:   []byte("DSix7zIaLXjaSrzBNkm3dtqdHqWLk2wnyVt/y+wNq01n5Avc6RaXdcrcDxAAAP//7okNxA=="),
		}, AcPublicKeyMessageResponse_PKR_ADD, true, 0, nil, nil, false,
	},

	// TEST #2 : Invalid base64 -2
	{AcPublicKeyMessageRequest_PK_ADD,
		&AcPublicKeyMessageRequest{
			Nick:   proto.String("nick3"),
			Host:   proto.String("prout@hostname"),
			Server: proto.String("freenode.net"),
			Blob:   []byte("DSix7zIaLXjaSrzBNkm3dtqdHqWLk2wnyVt/y+wNq01n5Avc6RaXdcrcDxAAAP//7okNxA"),
		}, AcPublicKeyMessageResponse_PKR_ADD, false, -2, nil, nil, true,
	},

	// TEST #3 : null blob -1
	{AcPublicKeyMessageRequest_PK_ADD,
		&AcPublicKeyMessageRequest{
			Nick:   proto.String("nick4"),
			Host:   proto.String("prout@hostname"),
			Server: proto.String("freenode.net"),
			Blob:   nil,
		}, AcPublicKeyMessageResponse_PKR_ADD, false, -1, nil, nil, true,
	},

	// TEST #4 : null nick
	{AcPublicKeyMessageRequest_PK_ADD,
		&AcPublicKeyMessageRequest{
			Nick:   nil,
			Host:   proto.String("prout@hostname"),
			Server: proto.String("freenode.net"),
			Blob:   []byte("DSix7zIaLXjaSrzBNkm3dtqdHqWLk2wnyVt/y+wNq01n5Avc6RaXdcrcDxAAAP//7okNxA=="),
		}, AcPublicKeyMessageResponse_PKR_ADD, false, -1, nil, nil, true,
	},
}

func TraceFunc2() string {
	pc := make([]uintptr, 10) // at least 1 entry needed
	runtime.Callers(2, pc)
	f := runtime.FuncForPC(pc[0])
	file, line := f.FileLine(pc[0])
	return fmt.Sprintf("[+] %s\n \\_ %s:%d\n", f.Name(), file, line)
}

func TraceFunc() *runtime.Func {
	pc := make([]uintptr, 10) // at least 1 entry needed
	runtime.Callers(2, pc)
	return runtime.FuncForPC(pc[0])
}

func TraceHdr(f *runtime.Func) string {
	file, line := f.FileLine(f.Entry())
	return fmt.Sprintf("[+] %s\n \\_ %s:%d\n", f.Name(), file, line)
}

/*
 * TESTs needed
 * PKGEN -> PKR_GEN
 */

func makeTests(tests []Test, fn pkhandler, t *testing.T) {
	for i, v := range tests {
		reqType := v.inType
		v.in.Type = &reqType

		fmt.Printf("=> TEST %d <=\n", i)
		r, e := oneTest(fn, v.in)

		// are we expecting an error message
		if e != nil && v.oHaveErrorMsg == false {
			t.Errorf("[%d] Expected: %t Error: [%s]", i, v.oHaveErrorMsg, e.Error())
		}

		// type REPLY ?
		if r.GetType() != v.oType {
			t.Errorf("[%d] type exp: %v res: [%v]", i, v.oType, r.GetType())
		}
		// did it succeed or not
		if r.GetBada() != v.oBada {
			t.Errorf("[%d] exp: %t res: %t", i, v.oBada, r.GetBada())
		}
		// is it the expected error code?
		if r.GetErrorCode() != v.oErrorCode {
			t.Errorf("[%d] exp: %d res: %d", i, v.oErrorCode, r.GetErrorCode())
		}
	}
}

func oneTest(fn pkhandler, in *AcPublicKeyMessageRequest) (out *AcPublicKeyMessageResponse, err error) {
	out, err = fn(in)
	return
}

func TestPKGEN(t *testing.T) {
	acutl.LogInit(os.Stderr)
	// TEST PKGEN
	fmt.Printf("\n== PKGEN TESTs ==\n")
	makeTests(PKGENTests, PKGEN_Handler, t)
	// TEST PKADD
	fmt.Printf("\n== PKADD TESTs ==\n")
	makeTests(PKADDTests, PKADD_Handler, t)
}
<commit_msg>adding more unit tests slowly...<commit_after>package acpb

import (
	"fmt"
	"github.com/golang/protobuf/proto" // protobuf is now here.
	"github.com/unix4fun/ac/acutl"
	"os"
	"runtime"
	"testing"
)

type pkhandler func(*AcPublicKeyMessageRequest) (acMsgResponse *AcPublicKeyMessageResponse, err error)

type Test struct {
	// input
	inType AcPublicKeyMessageRequestAcPKReqMsgType
	in     *AcPublicKeyMessageRequest // input
	// Expected output
	oType         AcPublicKeyMessageResponseAcPKRespMsgType
	oBada         bool
	oErrorCode    int32
	oBlob         []byte
	oPubKeys      []*AcPublicKey
	oHaveErrorMsg bool
}

var PKGENTests = []Test{
	// TEST #1 : OK
	{AcPublicKeyMessageRequest_PK_GEN,
		&AcPublicKeyMessageRequest{
			Nick:   proto.String("nick1"),
			Host:   proto.String("prout@hostname"),
			Server: proto.String("freenode.net"),
		}, AcPublicKeyMessageResponse_PKR_GEN, true, 0, nil, nil, false,
	},
	// TEST #2 : FAIL -1
	{AcPublicKeyMessageRequest_PK_GEN,
		&AcPublicKeyMessageRequest{
			Nick:   nil,
			Host:   proto.String("prout@hostname"),
			Server: proto.String("freenode.net"),
		}, AcPublicKeyMessageResponse_PKR_GEN, false, -1, nil, nil, true,
	},

	// TEST #3 : FAIL -1
	{AcPublicKeyMessageRequest_PK_GEN,
		&AcPublicKeyMessageRequest{
			Nick:   proto.String("mynick"),
			Host:   proto.String("eau@host.com"),
			Server: nil,
		}, AcPublicKeyMessageResponse_PKR_GEN, false, -1, nil, nil, true,
	},

	// TEST #4 : OK
	{AcPublicKeyMessageRequest_PK_GEN,
		&AcPublicKeyMessageRequest{
			Nick:   proto.String("eau"),
			Host:   proto.String("h2o@unix4funnnn.net"),
			Server: proto.String("freeenode.net"),
		}, AcPublicKeyMessageResponse_PKR_GEN, true, 0, nil, nil, false,
	},

	// TEST #4 : FAIL -1
	{AcPublicKeyMessageRequest_PK_GEN,
		&AcPublicKeyMessageRequest{
			Nick:   nil,
			Host:   nil,
			Server: nil,
		}, AcPublicKeyMessageResponse_PKR_GEN, false, -1, nil, nil, true,
	},
}

var PKADDTests = []Test{
	// TEST #1 : OK
	{AcPublicKeyMessageRequest_PK_ADD,
		&AcPublicKeyMessageRequest{
			Nick:   proto.String("nick1"),
			Host:   proto.String("prout@hostname"),
			Server: proto.String("freenode.net"),
			Blob:   []byte("DSix7zIaLXjaSrzBNkm3dtqdHqWLk2wnyVt/y+wNq01n5Avc6RaXdcrcDxAAAP//7okNxA=="),
		}, AcPublicKeyMessageResponse_PKR_ADD, true, 0, nil, nil, false,
	},

	// TEST #2 : Invalid base64 -2
	{AcPublicKeyMessageRequest_PK_ADD,
		&AcPublicKeyMessageRequest{
			Nick:   proto.String("nick3"),
			Host:   proto.String("prout@hostname"),
			Server: proto.String("freenode.net"),
			Blob:   []byte("DSix7zIaLXjaSrzBNkm3dtqdHqWLk2wnyVt/y+wNq01n5Avc6RaXdcrcDxAAAP//7okNxA"),
		}, AcPublicKeyMessageResponse_PKR_ADD, false, -2, nil, nil, true,
	},

	// TEST #3 : null blob -1
	{AcPublicKeyMessageRequest_PK_ADD,
		&AcPublicKeyMessageRequest{
			Nick:   proto.String("nick4"),
			Host:   proto.String("prout@hostname"),
			Server: proto.String("freenode.net"),
			Blob:   nil,
		}, AcPublicKeyMessageResponse_PKR_ADD, false, -1, nil, nil, true,
	},

	// TEST #4 : null nick
	{AcPublicKeyMessageRequest_PK_ADD,
		&AcPublicKeyMessageRequest{
			Nick:   nil,
			Host:   proto.String("prout@hostname"),
			Server: proto.String("freenode.net"),
			Blob:   []byte("DSix7zIaLXjaSrzBNkm3dtqdHqWLk2wnyVt/y+wNq01n5Avc6RaXdcrcDxAAAP//7okNxA=="),
		}, AcPublicKeyMessageResponse_PKR_ADD, false, -1, nil, nil, true,
	},

	// TEST #5 : null nick
	{AcPublicKeyMessageRequest_PK_ADD,
		&AcPublicKeyMessageRequest{
			Nick:   proto.String("nanother"),
			Host:   proto.String("prout@hostname"),
			Server: proto.String("freenode.net"),
			Blob:   []byte("AAAAAAAADSix7zIaLXjaSrzBNkm3dtqdHqWLk2wnyVt/y+wNq01n5Avc6RaXdcrcDxAAAP//7okNxAAAAAAAAAAAAAAAAA=="),
		}, AcPublicKeyMessageResponse_PKR_ADD, false, -2, nil, nil, true,
	},

	// TEST #6 : OK
	{AcPublicKeyMessageRequest_PK_ADD,
		&AcPublicKeyMessageRequest{
			Nick:   proto.String("nick1"),
			Host:   proto.String("prout@hostname"),
			Server: proto.String("freenode.net"),
			Blob:   []byte("DSix7zIaLXjaSrzBNkm3dtqdHqWLk2wnyVt/y+wNq01n5Avc6RaXdcrcDxAAAP//7okNxA=="),
		}, AcPublicKeyMessageResponse_PKR_ADD, true, 0, nil, nil, false,
	},

}// End of PKADD TESTs

var PKLISTTests = []Test{
	// TEST #1 : OK
	{AcPublicKeyMessageRequest_PK_LIST,
		&AcPublicKeyMessageRequest{
			Nick:   proto.String("nick1"),
			Server: proto.String("freenode.net"),
		}, AcPublicKeyMessageResponse_PKR_LIST, false, -2, nil, nil, true,
	},

	// TEST #2 : OK
	{AcPublicKeyMessageRequest_PK_LIST,
		&AcPublicKeyMessageRequest{
			Nick:   proto.String("nick2"),
			Server: nil,
		}, AcPublicKeyMessageResponse_PKR_LIST, false, -1, nil, nil, true,
	},
}

func TraceFunc2() string {
	pc := make([]uintptr, 10) // at least 1 entry needed
	runtime.Callers(2, pc)
	f := runtime.FuncForPC(pc[0])
	file, line := f.FileLine(pc[0])
	return fmt.Sprintf("[+] %s\n \\_ %s:%d\n", f.Name(), file, line)
}

func TraceFunc() *runtime.Func {
	pc := make([]uintptr, 10) // at least 1 entry needed
	runtime.Callers(2, pc)
	return runtime.FuncForPC(pc[0])
}

func TraceHdr(f *runtime.Func) string {
	file, line := f.FileLine(f.Entry())
	return fmt.Sprintf("[+] %s\n \\_ %s:%d\n", f.Name(), file, line)
}

/*
 * TESTs needed
 * PKGEN -> PKR_GEN
 */

func makeTests(tests []Test, fn pkhandler, t *testing.T) {
	for i, v := range tests {
		reqType := v.inType
		v.in.Type = &reqType

		fmt.Printf("=> TEST %d <=\n", i)
		r, e := oneTest(fn, v.in)

		// are we expecting an error message
		if e != nil && v.oHaveErrorMsg == false {
			t.Errorf("[%d] Expected: %t Error: [%s]", i, v.oHaveErrorMsg, e.Error())
		}

		// type REPLY ?
		if r.GetType() != v.oType {
			t.Errorf("[%d] type exp: %v res: [%v]", i, v.oType, r.GetType())
		}
		// did it succeed or not
		if r.GetBada() != v.oBada {
			t.Errorf("[%d] exp: %t res: %t", i, v.oBada, r.GetBada())
		}
		// is it the expected error code?
		if r.GetErrorCode() != v.oErrorCode {
			t.Errorf("[%d] exp: %d res: %d", i, v.oErrorCode, r.GetErrorCode())
		}
	}
}

func oneTest(fn pkhandler, in *AcPublicKeyMessageRequest) (out *AcPublicKeyMessageResponse, err error) {
	out, err = fn(in)
	return
}

func TestPK(t *testing.T) {
	acutl.LogInit(os.Stderr)
	// TEST PKGEN
	fmt.Printf("\n== PKGEN TESTs ==\n")
	makeTests(PKGENTests, PKGEN_Handler, t)

	// TEST PKADD
	fmt.Printf("\n== PKADD TESTs ==\n")
	makeTests(PKADDTests, PKADD_Handler, t)

	// TEST PKLIST
	fmt.Printf("\n== PKLIST TESTs ==\n")
	makeTests(PKLISTTests, PKLIST_Handler, t)
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

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

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

// Import docs from a git branch and format them for gh-pages.
// usage: go run _tools/release_docs/main.go _tools/release_docs/api-reference-process.go --branch release-1.0 --output-dir v1.0
package main

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"net/url"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"
	"strings"
)

var (
	branch       = flag.String("branch", "", "The git branch from which to pull docs. (e.g. release-1.0, master).")
	outputDir    = flag.String("output-dir", "", "The directory in which to save results.")
	version      = flag.String("version", "", "The release version. It should be the same as the version segment in the URL, e.g., when importing docs that will be hosted at k8s.io/v1.0/, the version flag should be \"v1.0\".")
	remote       = flag.String("remote", "upstream", "Optional: The name of the remote repo from which to pull docs.")
	apiReference = flag.Bool("apiReference", true, "Optional: Whether update api reference")

	subdirs = []string{"docs", "examples"}
)

func fileExistsInBranch(path string) bool {
	out, err := exec.Command("git", "ls-tree", fmt.Sprintf("%s/%s", *remote, *branch), path).Output()
	return err == nil && len(out) != 0
}

func fixURL(filename string, u *url.URL) bool {
	if u.Host != "" || u.Path == "" {
		return false
	}

	target := filepath.Join(filepath.Dir(filename), u.Path)
	if fi, err := os.Stat(target); os.IsNotExist(err) {
		// We're linking to something we didn't copy over. Send
		// it through the redirector.
		rel, err := filepath.Rel(*outputDir, target)
		if err != nil {
			return false
		}

		if fileExistsInBranch(rel) {
			u.Path = filepath.Join("HEAD", rel)
			u.Host = "releases.k8s.io"
			u.Scheme = "https"
			return true
		}
	} else if fi.IsDir() {
		// If there's no README.md in the directory, redirect to github
		// for the directory view.
		files, err := filepath.Glob(target + "/*")
		if err != nil {
			return false
		}

		hasReadme := false
		for _, f := range files {
			if strings.ToLower(filepath.Base(f)) == "readme.md" {
				hasReadme = true
			}
		}
		if !hasReadme {
			rel, err := filepath.Rel(*outputDir, target)
			if err != nil {
				return false
			}
			u.Path = filepath.Join("HEAD", rel)
			u.Host = "releases.k8s.io"
			u.Scheme = "https"
			return true
		}
	} else if strings.HasSuffix(u.Path, ".md") {
		u.Path = u.Path[:len(u.Path)-3] + ".html"
		return true
	}

	return false
}

func processFile(prefix, filename string) error {
	fileBytes, err := ioutil.ReadFile(filename)
	if err != nil {
		return err
	}

	title := getTitle(fileBytes)
	if len(title) == 0 {
		title = filename[len(prefix)+1 : len(filename)-len(".md")]
	}

	output := rewriteLinks(filename, fileBytes)
	output = rewriteCodeBlocks(output)

	f, err := os.Create(filename)
	if err != nil {
		return err
	}

	_, err = f.WriteString(fmt.Sprintf("---\nlayout: docwithnav\ntitle: %q\n---\n", title))
	if err != nil {
		return err
	}

	_, err = f.Write(output)
	return err
}

var (
	// Finds markdown links of the form [foo](bar "alt-text").
	linkRE = regexp.MustCompile(`\[([^]]*)\]\(([^)]*)\)`)
	// Finds markdown refence style link definitions
	refRE = regexp.MustCompile(`(?m)^\[([^]]*)\]:\s+(.*)$`)
	// Splits the link target into link target and alt-text.
	altTextRE = regexp.MustCompile(`(.*)( ".*")`)
)

func rewriteLinks(filename string, fileBytes []byte) []byte {
	getParts := func(re *regexp.Regexp, in []byte) (text, link, caption string, changed bool) {
		match := re.FindSubmatch(in)
		text = string(match[1])
		link = strings.TrimSpace(string(match[2]))

		if parts := altTextRE.FindStringSubmatch(link); parts != nil {
			link = parts[1]
			caption = parts[2]
		}

		u, err := url.Parse(link)
		if err != nil || !fixURL(filename, u) {
			return "", "", "", false
		}

		return text, u.String(), caption, true
	}

	for _, conversion := range []struct {
		re     *regexp.Regexp
		format string
	}{
		{linkRE, "[%s](%s)"},
		{refRE, "[%s]: %s"},
	} {

		fileBytes = conversion.re.ReplaceAllFunc(fileBytes, func(in []byte) (out []byte) {
			text, link, caption, changed := getParts(conversion.re, in)
			if !changed {
				return in
			}

			return []byte(fmt.Sprintf(conversion.format, text, link+caption))
		})

	}
	return fileBytes
}

var (
	// Allow more than 3 tick because people write this stuff.
	ticticticRE    = regexp.MustCompile("^`{3,}\\s*(.*)$")
	notTicticticRE = regexp.MustCompile("^```(.*)```")
	languageFixups = map[string]string{
		"shell": "sh",
	}
)

func rewriteCodeBlocks(fileBytes []byte) []byte {
	lines := strings.Split(string(fileBytes), "\n")
	inside := false
	highlight := false
	output := []string{}
	for i := range lines {
		trimmed := []byte(strings.TrimLeft(lines[i], " "))
		if !ticticticRE.Match(trimmed) || notTicticticRE.Match(trimmed) {
			output = append(output, lines[i])
			continue
		}
		if !inside {
			out := ticticticRE.FindSubmatch(trimmed)
			lang := strings.ToLower(string(out[1]))
			// Can't syntax highlight unknown language.
			if fixedLang := languageFixups[lang]; fixedLang != "" {
				lang = fixedLang
			}
			if lang != "" {
				// The "redcarpet" markdown renderer will accept ```lang, but
				// "kramdown" will not.  They both accept this, format, and we
				// need a hook to fixup language codes anyway (until we have a
				// munger in master).
				output = append(output, fmt.Sprintf("{%% highlight %s %%}", lang))
				highlight = true
			} else {
				output = append(output, lines[i])
			}
			output = append(output, `{% raw %}`)
		} else {
			output = append(output, `{% endraw %}`)
			if highlight {
				output = append(output, `{% endhighlight %}`)
				highlight = false
			} else {
				output = append(output, lines[i])
			}
		}
		inside = !inside
	}
	return []byte(strings.Join(output, "\n") + "\n")
}

var (
	// matches "# headers" and "## headers"
	atxTitleRE = regexp.MustCompile(`(?m)^\s*##?\s+(?P<title>.*)$`)
	// matches
	//  Headers
	//  =======
	// and
	//  Headers
	//  --
	setextTitleRE = regexp.MustCompile("(?m)^(?P<title>.+)\n((=+)|(-+))$")
	ignoredRE     = regexp.MustCompile(`[*_\\]`)
)

// removeLinks removes markdown links from the input leaving only the
// display text.
func removeLinks(input []byte) []byte {
	indices := linkRE.FindAllSubmatchIndex(input, -1)
	if len(indices) == 0 {
		return input
	}

	out := make([]byte, 0, len(input))
	cur := 0
	for _, index := range indices {
		linkStart, linkEnd, textStart, textEnd := index[0], index[1], index[2], index[3]
		// append bytes between previous match and this one
		out = append(out, input[cur:linkStart]...)
		// extract and append link text
		out = append(out, input[textStart:textEnd]...)
		// update cur
		cur = linkEnd
	}
	// pick up the remaining and return it
	return append(out, input[cur:len(input)]...)
}

// findTitleMatch returns the start of the match and the "title" subgroup of
// bytes. If the regexp doesn't match, it will return -1 and nil.
func findTitleMatch(titleRE *regexp.Regexp, input []byte) (start int, title []byte) {
	indices := titleRE.FindSubmatchIndex(input)
	if len(indices) == 0 {
		return -1, nil
	}

	for i, name := range titleRE.SubexpNames() {
		if name == "title" {
			start, end := indices[2*i], indices[2*i+1]
			return indices[0], input[start:end]
		}
	}

	// there was no grouped named title
	return -1, nil
}

func getTitle(fileBytes []byte) string {
	atxStart, atxMatch := findTitleMatch(atxTitleRE, fileBytes)
	setextStart, setextMatch := findTitleMatch(setextTitleRE, fileBytes)

	var title []byte

	switch {
	case atxStart == -1 && setextStart == -1:
		return ""
	case atxStart == -1:
		title = setextMatch
	case setextStart == -1:
		title = atxMatch
	case setextStart < atxStart:
		title = setextMatch
	default:
		title = atxMatch
	}

	// Handle the case where there's a link in the header.
	title = removeLinks(title)

	// Take out all markdown stylings.
	return string(ignoredRE.ReplaceAll(title, nil))
}

func runGitUpdate(remote string) error {
	cmd := exec.Command("git", "fetch", remote)
	out, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("git fetch failed: %v\n%s", err, out)
	}
	return err
}

func copyFiles(remoteRepo, directory, branch string) error {
	if err := runGitUpdate(remoteRepo); err != nil {
		return err
	}

	if !strings.HasSuffix(directory, "/") {
		directory += "/"
	}
	prefix := fmt.Sprintf("--prefix=%s", directory)
	tagRef := fmt.Sprintf("%s/%s", remoteRepo, branch)
	gitArgs := append([]string{"archive", "--format=tar", prefix, tagRef}, subdirs...)
	gitCmd := exec.Command("git", gitArgs...)
	tarCmd := exec.Command("tar", "-x")

	var err error
	tarCmd.Stdin, err = gitCmd.StdoutPipe()
	if err != nil {
		return err
	}

	gitStderr, err := gitCmd.StderrPipe()
	if err != nil {
		return err
	}
	gitErrs := bytes.Buffer{}

	fmt.Printf("Copying docs and examples from %s to %s\n", tagRef, directory)

	if err = tarCmd.Start(); err != nil {
		return fmt.Errorf("tar command failed: %v", err)
	}

	if err = gitCmd.Run(); err != nil {
		gitErrs.ReadFrom(gitStderr)
		return fmt.Errorf("git command failed: %v\n%s", err, gitErrs.String())
	}

	return tarCmd.Wait()
}

func copySingleFile(src, dst string) (err error) {
	_, err = os.Stat(dst)
	if err != nil && !os.IsNotExist(err) {
		return err
	}

	in, err := os.Open(src)
	if err != nil {
		return err
	}
	defer in.Close()

	out, err := os.Create(dst)
	if err != nil {
		return err
	}

	defer func() {
		cerr := out.Close()
		if err == nil {
			err = cerr
		}
	}()

	_, err = io.Copy(out, in)
	return
}

func main() {
	flag.Parse()
	if len(*branch) == 0 {
		fmt.Println("You must specify a branch with --branch.")
		os.Exit(1)
	}
	if len(*outputDir) == 0 {
		fmt.Println("You must specify an output dir with --output-dir.")
		os.Exit(1)
	}
	if len(*version) == 0 {
		fmt.Println("You must specify the release version with --version.")
		os.Exit(1)
	}

	if err := checkCWD(); err != nil {
		fmt.Printf("Could not find the kubernetes root: %v\n", err)
		os.Exit(1)
	}

	if err := copyFiles(*remote, *outputDir, *branch); err != nil {
		fmt.Printf("Error copying files: %v\n", err)
		os.Exit(1)
	}

	for _, subDir := range subdirs {
		prefix := path.Join(*outputDir, subDir)
		err := filepath.Walk(prefix, func(path string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}

			if !info.IsDir() && strings.HasSuffix(info.Name(), ".md") {
				fmt.Printf("Processing %s\n", path)
				if err = processFile(prefix, path); err != nil {
					return err
				}

				if strings.ToLower(info.Name()) == "readme.md" {
					newpath := path[0:len(path)-len("readme.md")] + "index.md"
					fmt.Printf("Copying %s to %s\n", path, newpath)
					if err = copySingleFile(path, newpath); err != nil {
						return err
					}
				}
			}

			if *apiReference && !info.IsDir() && (info.Name() == "definitions.html" || info.Name() == "operations.html") {
				fmt.Printf("Processing %s\n", path)
				err := processHTML(path, *version, *outputDir)
				if err != nil {
					return err
				}
			}
			return nil

		})

		if err != nil {
			fmt.Printf("Error while processing markdown and html files: %v\n", err)
			os.Exit(1)
		}
	}
}

func checkCWD() error {
	dir, err := exec.Command("git", "rev-parse", "--show-toplevel").Output()
	if err != nil {
		return err
	}
	return os.Chdir(strings.TrimSpace(string(dir)))
}
<commit_msg>minor fix in comment<commit_after>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

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

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

// Import docs from a git branch and format them for gh-pages.
// usage: go run _tools/release_docs/main.go _tools/release_docs/api-reference-process.go --branch release-1.0 --output-dir v1.0 --version v1.0
package main

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"net/url"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"
	"strings"
)

var (
	branch       = flag.String("branch", "", "The git branch from which to pull docs. (e.g. release-1.0, master).")
	outputDir    = flag.String("output-dir", "", "The directory in which to save results.")
	version      = flag.String("version", "", "The release version. It should be the same as the version segment in the URL, e.g., when importing docs that will be hosted at k8s.io/v1.0/, the version flag should be \"v1.0\".")
	remote       = flag.String("remote", "upstream", "Optional: The name of the remote repo from which to pull docs.")
	apiReference = flag.Bool("apiReference", true, "Optional: Whether update api reference")

	subdirs = []string{"docs", "examples"}
)

func fileExistsInBranch(path string) bool {
	out, err := exec.Command("git", "ls-tree", fmt.Sprintf("%s/%s", *remote, *branch), path).Output()
	return err == nil && len(out) != 0
}

func fixURL(filename string, u *url.URL) bool {
	if u.Host != "" || u.Path == "" {
		return false
	}

	target := filepath.Join(filepath.Dir(filename), u.Path)
	if fi, err := os.Stat(target); os.IsNotExist(err) {
		// We're linking to something we didn't copy over. Send
		// it through the redirector.
		rel, err := filepath.Rel(*outputDir, target)
		if err != nil {
			return false
		}

		if fileExistsInBranch(rel) {
			u.Path = filepath.Join("HEAD", rel)
			u.Host = "releases.k8s.io"
			u.Scheme = "https"
			return true
		}
	} else if fi.IsDir() {
		// If there's no README.md in the directory, redirect to github
		// for the directory view.
		files, err := filepath.Glob(target + "/*")
		if err != nil {
			return false
		}

		hasReadme := false
		for _, f := range files {
			if strings.ToLower(filepath.Base(f)) == "readme.md" {
				hasReadme = true
			}
		}
		if !hasReadme {
			rel, err := filepath.Rel(*outputDir, target)
			if err != nil {
				return false
			}
			u.Path = filepath.Join("HEAD", rel)
			u.Host = "releases.k8s.io"
			u.Scheme = "https"
			return true
		}
	} else if strings.HasSuffix(u.Path, ".md") {
		u.Path = u.Path[:len(u.Path)-3] + ".html"
		return true
	}

	return false
}

func processFile(prefix, filename string) error {
	fileBytes, err := ioutil.ReadFile(filename)
	if err != nil {
		return err
	}

	title := getTitle(fileBytes)
	if len(title) == 0 {
		title = filename[len(prefix)+1 : len(filename)-len(".md")]
	}

	output := rewriteLinks(filename, fileBytes)
	output = rewriteCodeBlocks(output)

	f, err := os.Create(filename)
	if err != nil {
		return err
	}

	_, err = f.WriteString(fmt.Sprintf("---\nlayout: docwithnav\ntitle: %q\n---\n", title))
	if err != nil {
		return err
	}

	_, err = f.Write(output)
	return err
}

var (
	// Finds markdown links of the form [foo](bar "alt-text").
	linkRE = regexp.MustCompile(`\[([^]]*)\]\(([^)]*)\)`)
	// Finds markdown refence style link definitions
	refRE = regexp.MustCompile(`(?m)^\[([^]]*)\]:\s+(.*)$`)
	// Splits the link target into link target and alt-text.
	altTextRE = regexp.MustCompile(`(.*)( ".*")`)
)

func rewriteLinks(filename string, fileBytes []byte) []byte {
	getParts := func(re *regexp.Regexp, in []byte) (text, link, caption string, changed bool) {
		match := re.FindSubmatch(in)
		text = string(match[1])
		link = strings.TrimSpace(string(match[2]))

		if parts := altTextRE.FindStringSubmatch(link); parts != nil {
			link = parts[1]
			caption = parts[2]
		}

		u, err := url.Parse(link)
		if err != nil || !fixURL(filename, u) {
			return "", "", "", false
		}

		return text, u.String(), caption, true
	}

	for _, conversion := range []struct {
		re     *regexp.Regexp
		format string
	}{
		{linkRE, "[%s](%s)"},
		{refRE, "[%s]: %s"},
	} {

		fileBytes = conversion.re.ReplaceAllFunc(fileBytes, func(in []byte) (out []byte) {
			text, link, caption, changed := getParts(conversion.re, in)
			if !changed {
				return in
			}

			return []byte(fmt.Sprintf(conversion.format, text, link+caption))
		})

	}
	return fileBytes
}

var (
	// Allow more than 3 tick because people write this stuff.
	ticticticRE    = regexp.MustCompile("^`{3,}\\s*(.*)$")
	notTicticticRE = regexp.MustCompile("^```(.*)```")
	languageFixups = map[string]string{
		"shell": "sh",
	}
)

func rewriteCodeBlocks(fileBytes []byte) []byte {
	lines := strings.Split(string(fileBytes), "\n")
	inside := false
	highlight := false
	output := []string{}
	for i := range lines {
		trimmed := []byte(strings.TrimLeft(lines[i], " "))
		if !ticticticRE.Match(trimmed) || notTicticticRE.Match(trimmed) {
			output = append(output, lines[i])
			continue
		}
		if !inside {
			out := ticticticRE.FindSubmatch(trimmed)
			lang := strings.ToLower(string(out[1]))
			// Can't syntax highlight unknown language.
			if fixedLang := languageFixups[lang]; fixedLang != "" {
				lang = fixedLang
			}
			if lang != "" {
				// The "redcarpet" markdown renderer will accept ```lang, but
				// "kramdown" will not.  They both accept this, format, and we
				// need a hook to fixup language codes anyway (until we have a
				// munger in master).
				output = append(output, fmt.Sprintf("{%% highlight %s %%}", lang))
				highlight = true
			} else {
				output = append(output, lines[i])
			}
			output = append(output, `{% raw %}`)
		} else {
			output = append(output, `{% endraw %}`)
			if highlight {
				output = append(output, `{% endhighlight %}`)
				highlight = false
			} else {
				output = append(output, lines[i])
			}
		}
		inside = !inside
	}
	return []byte(strings.Join(output, "\n") + "\n")
}

var (
	// matches "# headers" and "## headers"
	atxTitleRE = regexp.MustCompile(`(?m)^\s*##?\s+(?P<title>.*)$`)
	// matches
	//  Headers
	//  =======
	// and
	//  Headers
	//  --
	setextTitleRE = regexp.MustCompile("(?m)^(?P<title>.+)\n((=+)|(-+))$")
	ignoredRE     = regexp.MustCompile(`[*_\\]`)
)

// removeLinks removes markdown links from the input leaving only the
// display text.
func removeLinks(input []byte) []byte {
	indices := linkRE.FindAllSubmatchIndex(input, -1)
	if len(indices) == 0 {
		return input
	}

	out := make([]byte, 0, len(input))
	cur := 0
	for _, index := range indices {
		linkStart, linkEnd, textStart, textEnd := index[0], index[1], index[2], index[3]
		// append bytes between previous match and this one
		out = append(out, input[cur:linkStart]...)
		// extract and append link text
		out = append(out, input[textStart:textEnd]...)
		// update cur
		cur = linkEnd
	}
	// pick up the remaining and return it
	return append(out, input[cur:len(input)]...)
}

// findTitleMatch returns the start of the match and the "title" subgroup of
// bytes. If the regexp doesn't match, it will return -1 and nil.
func findTitleMatch(titleRE *regexp.Regexp, input []byte) (start int, title []byte) {
	indices := titleRE.FindSubmatchIndex(input)
	if len(indices) == 0 {
		return -1, nil
	}

	for i, name := range titleRE.SubexpNames() {
		if name == "title" {
			start, end := indices[2*i], indices[2*i+1]
			return indices[0], input[start:end]
		}
	}

	// there was no grouped named title
	return -1, nil
}

func getTitle(fileBytes []byte) string {
	atxStart, atxMatch := findTitleMatch(atxTitleRE, fileBytes)
	setextStart, setextMatch := findTitleMatch(setextTitleRE, fileBytes)

	var title []byte

	switch {
	case atxStart == -1 && setextStart == -1:
		return ""
	case atxStart == -1:
		title = setextMatch
	case setextStart == -1:
		title = atxMatch
	case setextStart < atxStart:
		title = setextMatch
	default:
		title = atxMatch
	}

	// Handle the case where there's a link in the header.
	title = removeLinks(title)

	// Take out all markdown stylings.
	return string(ignoredRE.ReplaceAll(title, nil))
}

func runGitUpdate(remote string) error {
	cmd := exec.Command("git", "fetch", remote)
	out, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("git fetch failed: %v\n%s", err, out)
	}
	return err
}

func copyFiles(remoteRepo, directory, branch string) error {
	if err := runGitUpdate(remoteRepo); err != nil {
		return err
	}

	if !strings.HasSuffix(directory, "/") {
		directory += "/"
	}
	prefix := fmt.Sprintf("--prefix=%s", directory)
	tagRef := fmt.Sprintf("%s/%s", remoteRepo, branch)
	gitArgs := append([]string{"archive", "--format=tar", prefix, tagRef}, subdirs...)
	gitCmd := exec.Command("git", gitArgs...)
	tarCmd := exec.Command("tar", "-x")

	var err error
	tarCmd.Stdin, err = gitCmd.StdoutPipe()
	if err != nil {
		return err
	}

	gitStderr, err := gitCmd.StderrPipe()
	if err != nil {
		return err
	}
	gitErrs := bytes.Buffer{}

	fmt.Printf("Copying docs and examples from %s to %s\n", tagRef, directory)

	if err = tarCmd.Start(); err != nil {
		return fmt.Errorf("tar command failed: %v", err)
	}

	if err = gitCmd.Run(); err != nil {
		gitErrs.ReadFrom(gitStderr)
		return fmt.Errorf("git command failed: %v\n%s", err, gitErrs.String())
	}

	return tarCmd.Wait()
}

func copySingleFile(src, dst string) (err error) {
	_, err = os.Stat(dst)
	if err != nil && !os.IsNotExist(err) {
		return err
	}

	in, err := os.Open(src)
	if err != nil {
		return err
	}
	defer in.Close()

	out, err := os.Create(dst)
	if err != nil {
		return err
	}

	defer func() {
		cerr := out.Close()
		if err == nil {
			err = cerr
		}
	}()

	_, err = io.Copy(out, in)
	return
}

func main() {
	flag.Parse()
	if len(*branch) == 0 {
		fmt.Println("You must specify a branch with --branch.")
		os.Exit(1)
	}
	if len(*outputDir) == 0 {
		fmt.Println("You must specify an output dir with --output-dir.")
		os.Exit(1)
	}
	if len(*version) == 0 {
		fmt.Println("You must specify the release version with --version.")
		os.Exit(1)
	}

	if err := checkCWD(); err != nil {
		fmt.Printf("Could not find the kubernetes root: %v\n", err)
		os.Exit(1)
	}

	if err := copyFiles(*remote, *outputDir, *branch); err != nil {
		fmt.Printf("Error copying files: %v\n", err)
		os.Exit(1)
	}

	for _, subDir := range subdirs {
		prefix := path.Join(*outputDir, subDir)
		err := filepath.Walk(prefix, func(path string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}

			if !info.IsDir() && strings.HasSuffix(info.Name(), ".md") {
				fmt.Printf("Processing %s\n", path)
				if err = processFile(prefix, path); err != nil {
					return err
				}

				if strings.ToLower(info.Name()) == "readme.md" {
					newpath := path[0:len(path)-len("readme.md")] + "index.md"
					fmt.Printf("Copying %s to %s\n", path, newpath)
					if err = copySingleFile(path, newpath); err != nil {
						return err
					}
				}
			}

			if *apiReference && !info.IsDir() && (info.Name() == "definitions.html" || info.Name() == "operations.html") {
				fmt.Printf("Processing %s\n", path)
				err := processHTML(path, *version, *outputDir)
				if err != nil {
					return err
				}
			}
			return nil

		})

		if err != nil {
			fmt.Printf("Error while processing markdown and html files: %v\n", err)
			os.Exit(1)
		}
	}
}

func checkCWD() error {
	dir, err := exec.Command("git", "rev-parse", "--show-toplevel").Output()
	if err != nil {
		return err
	}
	return os.Chdir(strings.TrimSpace(string(dir)))
}
<|endoftext|>


<commit_before><commit_msg>Testing success upload file<commit_after><|endoftext|>


<commit_before><commit_msg>`log.Fatal` is not recovered...<commit_after><|endoftext|>


<commit_before><commit_msg>server: added --server-username and --server-password to optionally require basic auth for Kopia server<commit_after><|endoftext|>


<commit_before><commit_msg>add existing env vars to hook envs (#184)<commit_after><|endoftext|>


<commit_before><commit_msg>removing duplicate .Set() on metric, added back parser lines for B/Bytes<commit_after><|endoftext|>


<commit_before><commit_msg>アアー アアアア アー<commit_after><|endoftext|>


<commit_before>package xmpp

import (
	"crypto/tls"
	"encoding/xml"
	"errors"
	"fmt"
	"log"
)

// Config structure used to create a new XMPP client connection.
type ClientConfig struct {
	// Don't upgrade the connection to TLS, even if the server supports it. If
	// the server *requires* TLS then this option is ignored.
	NoTLS bool

	// Skip verification of the server's certificate chain. Probably only
	// useful during development.
	InsecureSkipVerify bool
}

// Create a client XMPP over the stream.
func NewClientXMPP(stream *Stream, jid JID, password string, config *ClientConfig) (*XMPP, error) {

	if config == nil {
		config = &ClientConfig{}
	}

	for {

		if err := startClient(stream, jid); err != nil {
			return nil, err
		}

		// Read features.
		f := new(features)
		if err := stream.Decode(f); err != nil {
			return nil, err
		}

		// TLS?
		if f.StartTLS != nil && (f.StartTLS.Required != nil || !config.NoTLS) {
			log.Println("Start TLS")
			if err := startTLS(stream, config); err != nil {
				return nil, err
			}
			continue // Restart
		}

		// Authentication
		if f.Mechanisms != nil {
			log.Println("Authenticating")
			if err := authenticate(stream, f.Mechanisms.Mechanisms, jid.Node, password); err != nil {
				return nil, err
			}
			continue // Restart
		}

		// Bind resource.
		if f.Bind != nil {
			log.Println("Binding resource.")
			boundJID, err := bindResource(stream, jid)
			if err != nil {
				return nil, err
			}
			jid = boundJID
		}

		break
	}

	return newXMPP(jid, stream), nil
}

func startClient(stream *Stream, jid JID) error {

	start := xml.StartElement{
		xml.Name{"stream", "stream"},
		[]xml.Attr{
			xml.Attr{xml.Name{"", "xmlns"}, "jabber:client"},
			xml.Attr{xml.Name{"xmlns", "stream"}, "http://etherx.jabber.org/streams"},
			xml.Attr{xml.Name{"", "from"}, jid.Full()},
			xml.Attr{xml.Name{"", "to"}, jid.Domain},
			xml.Attr{xml.Name{"", "version"}, "1.0"},
		},
	}

	if err := stream.SendStart(&start); err != nil {
		return err
	}

	if _, err := stream.Next(&xml.Name{nsStream, "stream"}); err != nil {
		return err
	}

	return nil
}

func startTLS(stream *Stream, config *ClientConfig) error {

	if err := stream.Send(&tlsStart{}); err != nil {
		return err
	}

	p := tlsProceed{}
	if err := stream.Decode(&p); err != nil {
		return err
	}

	tlsConfig := tls.Config{InsecureSkipVerify: config.InsecureSkipVerify}
	return stream.UpgradeTLS(&tlsConfig)
}

type tlsStart struct {
	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-tls starttls"`
}

type tlsProceed struct {
	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-tls proceed"`
}

func authenticate(stream *Stream, mechanisms []string, user, password string) error {
	for _, handler := range authHandlers {
		if !stringSliceContains(mechanisms, handler.Mechanism) {
			continue
		}
		if err := handler.Fn(stream, user, password); err == nil {
			log.Printf("Authentication (%s) successful", handler.Mechanism)
			return nil
		}
	}
	return errors.New("No supported SASL mechanism found.")
}

type authHandler struct {
	Mechanism string
	Fn func(*Stream, string, string) error
}

var authHandlers = []authHandler{
		authHandler{"PLAIN", authenticatePlain},
	}

func authenticatePlain(stream *Stream, user, password string) error {

	auth := saslAuth{Mechanism: "PLAIN", Message: saslEncodePlain(user, password)}
	if err := stream.Send(&auth); err != nil {
		return err
	}

	se, err := stream.Next(nil)
	if err != nil {
		return err
	}
	switch se.Name.Local {
	case "success":
		if err := stream.Skip(); err != nil {
			return err
		}
	case "failure":
		f := new(saslFailure)
		stream.DecodeElement(f, se)
		return errors.New(fmt.Sprintf("Authentication failed: %s", f.Reason.Local))
	}

	return nil
}

type saslAuth struct {
	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl auth"`
	Mechanism string `xml:"mechanism,attr"`
	Message string `xml:",chardata"`
}

func bindResource(stream *Stream, jid JID) (JID, error) {

	req := Iq{Id: "foo", Type: "set"}
	if jid.Resource == "" {
		req.PayloadEncode(bindIq{})
	} else {
		req.PayloadEncode(bindIq{Resource: jid.Resource})
	}
	if err := stream.Send(req); err != nil {
		return JID{}, err
	}

	resp := Iq{}
	err := stream.Decode(&resp)
	if err != nil {
		return JID{}, err
	}
	bindResp := bindIq{}
	resp.PayloadDecode(&bindResp)

	boundJID, err := ParseJID(bindResp.JID)
	return boundJID, nil
}

type bindIq struct {
	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-bind bind"`
	Resource string `xml:"resource,omitempty"`
	JID string `xml:"jid,omitempty"`
}

func stringSliceContains(l []string, m string) bool {
	for _, i := range l {
		if i == m {
			return true
		}
	}
	return false
}

type features struct {
	XMLName xml.Name `xml:"http://etherx.jabber.org/streams features"`
	StartTLS *tlsStartTLS `xml:"starttls"`
	Mechanisms *mechanisms `xml:"mechanisms"`
	Bind *bind `xml:"bind"`
}

type bind struct {
	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-bind bind"`
	Required *required `xml:"required"`
}

type mechanisms struct {
	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl mechanisms"`
	Mechanisms []string `xml:"mechanism"`
}

type tlsStartTLS struct {
	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-tls starttls"`
	Required *required `xml:"required"`
}

type required struct {}

type saslFailure struct {
	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl failure"`
	Reason xml.Name `xml:",any"`
}

// BUG(matt): Don't use "foo" as the <iq/> id during resource binding.
<commit_msg>Rename auth struct field.<commit_after>package xmpp

import (
	"crypto/tls"
	"encoding/xml"
	"errors"
	"fmt"
	"log"
)

// Config structure used to create a new XMPP client connection.
type ClientConfig struct {
	// Don't upgrade the connection to TLS, even if the server supports it. If
	// the server *requires* TLS then this option is ignored.
	NoTLS bool

	// Skip verification of the server's certificate chain. Probably only
	// useful during development.
	InsecureSkipVerify bool
}

// Create a client XMPP over the stream.
func NewClientXMPP(stream *Stream, jid JID, password string, config *ClientConfig) (*XMPP, error) {

	if config == nil {
		config = &ClientConfig{}
	}

	for {

		if err := startClient(stream, jid); err != nil {
			return nil, err
		}

		// Read features.
		f := new(features)
		if err := stream.Decode(f); err != nil {
			return nil, err
		}

		// TLS?
		if f.StartTLS != nil && (f.StartTLS.Required != nil || !config.NoTLS) {
			log.Println("Start TLS")
			if err := startTLS(stream, config); err != nil {
				return nil, err
			}
			continue // Restart
		}

		// Authentication
		if f.Mechanisms != nil {
			log.Println("Authenticating")
			if err := authenticate(stream, f.Mechanisms.Mechanisms, jid.Node, password); err != nil {
				return nil, err
			}
			continue // Restart
		}

		// Bind resource.
		if f.Bind != nil {
			log.Println("Binding resource.")
			boundJID, err := bindResource(stream, jid)
			if err != nil {
				return nil, err
			}
			jid = boundJID
		}

		break
	}

	return newXMPP(jid, stream), nil
}

func startClient(stream *Stream, jid JID) error {

	start := xml.StartElement{
		xml.Name{"stream", "stream"},
		[]xml.Attr{
			xml.Attr{xml.Name{"", "xmlns"}, "jabber:client"},
			xml.Attr{xml.Name{"xmlns", "stream"}, "http://etherx.jabber.org/streams"},
			xml.Attr{xml.Name{"", "from"}, jid.Full()},
			xml.Attr{xml.Name{"", "to"}, jid.Domain},
			xml.Attr{xml.Name{"", "version"}, "1.0"},
		},
	}

	if err := stream.SendStart(&start); err != nil {
		return err
	}

	if _, err := stream.Next(&xml.Name{nsStream, "stream"}); err != nil {
		return err
	}

	return nil
}

func startTLS(stream *Stream, config *ClientConfig) error {

	if err := stream.Send(&tlsStart{}); err != nil {
		return err
	}

	p := tlsProceed{}
	if err := stream.Decode(&p); err != nil {
		return err
	}

	tlsConfig := tls.Config{InsecureSkipVerify: config.InsecureSkipVerify}
	return stream.UpgradeTLS(&tlsConfig)
}

type tlsStart struct {
	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-tls starttls"`
}

type tlsProceed struct {
	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-tls proceed"`
}

func authenticate(stream *Stream, mechanisms []string, user, password string) error {
	for _, handler := range authHandlers {
		if !stringSliceContains(mechanisms, handler.Mechanism) {
			continue
		}
		if err := handler.Fn(stream, user, password); err == nil {
			log.Printf("Authentication (%s) successful", handler.Mechanism)
			return nil
		}
	}
	return errors.New("No supported SASL mechanism found.")
}

type authHandler struct {
	Mechanism string
	Fn func(*Stream, string, string) error
}

var authHandlers = []authHandler{
		authHandler{"PLAIN", authenticatePlain},
	}

func authenticatePlain(stream *Stream, user, password string) error {
	auth := saslAuth{Mechanism: "PLAIN", Text: saslEncodePlain(user, password)}
	if err := stream.Send(&auth); err != nil {
		return err
	}

	se, err := stream.Next(nil)
	if err != nil {
		return err
	}
	switch se.Name.Local {
	case "success":
		if err := stream.Skip(); err != nil {
			return err
		}
	case "failure":
		f := new(saslFailure)
		stream.DecodeElement(f, se)
		return errors.New(fmt.Sprintf("Authentication failed: %s", f.Reason.Local))
	}

	return nil
}

type saslAuth struct {
	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl auth"`
	Mechanism string `xml:"mechanism,attr"`
	Text string `xml:",chardata"`
}

func bindResource(stream *Stream, jid JID) (JID, error) {

	req := Iq{Id: "foo", Type: "set"}
	if jid.Resource == "" {
		req.PayloadEncode(bindIq{})
	} else {
		req.PayloadEncode(bindIq{Resource: jid.Resource})
	}
	if err := stream.Send(req); err != nil {
		return JID{}, err
	}

	resp := Iq{}
	err := stream.Decode(&resp)
	if err != nil {
		return JID{}, err
	}
	bindResp := bindIq{}
	resp.PayloadDecode(&bindResp)

	boundJID, err := ParseJID(bindResp.JID)
	return boundJID, nil
}

type bindIq struct {
	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-bind bind"`
	Resource string `xml:"resource,omitempty"`
	JID string `xml:"jid,omitempty"`
}

func stringSliceContains(l []string, m string) bool {
	for _, i := range l {
		if i == m {
			return true
		}
	}
	return false
}

type features struct {
	XMLName xml.Name `xml:"http://etherx.jabber.org/streams features"`
	StartTLS *tlsStartTLS `xml:"starttls"`
	Mechanisms *mechanisms `xml:"mechanisms"`
	Bind *bind `xml:"bind"`
}

type bind struct {
	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-bind bind"`
	Required *required `xml:"required"`
}

type mechanisms struct {
	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl mechanisms"`
	Mechanisms []string `xml:"mechanism"`
}

type tlsStartTLS struct {
	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-tls starttls"`
	Required *required `xml:"required"`
}

type required struct {}

type saslFailure struct {
	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl failure"`
	Reason xml.Name `xml:",any"`
}

// BUG(matt): Don't use "foo" as the <iq/> id during resource binding.
<|endoftext|>


<commit_before>package xmpp

import (
	"bytes"
	"encoding/xml"
	"fmt"
)

const (
	IQTypeGet    = "get"
	IQTypeSet    = "set"
	IQTypeResult = "result"
	IQTypeError  = "error"

	MessageTypeNormal = "normal"
	MessageTypeChat   = "chat"
	MessageTypeError  = "error"
)

// XMPP <iq/> stanza.
type Iq struct {
	XMLName xml.Name `xml:"iq"`
	Id      string   `xml:"id,attr"`
	Type    string   `xml:"type,attr"`
	To      string   `xml:"to,attr,omitempty"`
	From    string   `xml:"from,attr,omitempty"`
	Payload string   `xml:",innerxml"`
	Error   *Error   `xml:"error"`
}

// Encode the value to an XML string and set as the payload. See xml.Marshal
// for how the value is encoded.
func (iq *Iq) PayloadEncode(v interface{}) error {
	bytes, err := xml.Marshal(v)
	if err != nil {
		return err
	}
	iq.Payload = string(bytes)
	return nil
}

// Decode the payload (an XML string) into the given value. See xml.Unmarshal
// for how the value is decoded.
func (iq *Iq) PayloadDecode(v interface{}) error {
	return xml.Unmarshal([]byte(iq.Payload), v)
}

// Return the name of the payload element.
func (iq *Iq) PayloadName() (name xml.Name) {
	dec := xml.NewDecoder(bytes.NewBufferString(iq.Payload))
	tok, err := dec.Token()
	if err != nil {
		return
	}
	start, ok := tok.(xml.StartElement)
	if !ok {
		return
	}
	return start.Name
}

// Create a response Iq. The Id is kept, To and From are reversed, Type is set
// to the given value.
func (iq *Iq) Response(type_ string) *Iq {
	return &Iq{Id: iq.Id, Type: type_, From: iq.To, To: iq.From}
}

// XMPP <message/> stanza.
type Message struct {
	XMLName xml.Name      `xml:"message"`
	Id      string        `xml:"id,attr,omitempty"`
	Type    string        `xml:"type,attr,omitempty"`
	To      string        `xml:"to,attr,omitempty"`
	From    string        `xml:"from,attr,omitempty"`
	Subject string        `xml:"subject,omitempty"`
	Body    []MessageBody `xml:"body,omitempty"`
	Thread  string        `xml:"thread,omitempty"`
	Error   *Error        `xml:"error"`
	Lang    string        `xml:"xml:lang,attr,omitempty"`

	Confir *Confirm `xml:"confirm"` // XEP-0070

	Active    *Active    `xml:"active"`    // XEP-0085
	Composing *Composing `xml:"composing"` // XEP-0085
	Paused    *Paused    `xml:"paused"`    // XEP-0085
	Inactive  *Inactive  `xml:"inactive"`  // XEP-0085
	Gone      *Gone      `xml:"gone"`      // XEP-0085
}

type MessageBody struct {
	Lang  string `xml:"xml:lang,attr,omitempty"`
	Value string `xml:",innerxml"`
}

// XMPP <presence/> stanza.
type Presence struct {
	XMLName xml.Name `xml:"presence"`
	Id      string   `xml:"id,attr,omitempty"`
	Type    string   `xml:"type,attr,omitempty"`
	To      string   `xml:"to,attr,omitempty"`
	From    string   `xml:"from,attr,omitempty"`
	Show    string   `xml:"show"`            // away, chat, dnd, xa
	Status  string   `xml:"status"`          // sb []clientText
	Photo   string   `xml:"photo,omitempty"` // Avatar
	Nick    string   `xml:"nick,omitempty"`  // Nickname
}

// XMPP <error/>. May occur as a top-level stanza or embedded in another
// stanza, e.g. an <iq type="error"/>.
type Error struct {
	XMLName xml.Name `xml:"error"`
	Code    string   `xml:"code,attr,omitempty"`
	Type    string   `xml:"type,attr"`
	Payload string   `xml:",innerxml"`
}

func (e Error) Error() string {
	if text := e.Text(); text == "" {
		return fmt.Sprintf("[%s] %s", e.Type, e.Condition().Local)
	} else {
		return fmt.Sprintf("[%s] %s, %s", e.Type, e.Condition().Local, text)
	}
	panic("unreachable")
}

type errorText struct {
	XMLName xml.Name
	Text    string `xml:",chardata"`
}

// Create a new Error instance using the args as the payload.
func NewError(errorType string, condition ErrorCondition, text string) *Error {

	// Build payload.
	buf := new(bytes.Buffer)
	writeXMLStartElement(buf, &xml.StartElement{
		Name: xml.Name{"", condition.Local},
		Attr: []xml.Attr{
			{xml.Name{"", "xmlns"}, condition.Space},
		},
	})
	writeXMLEndElement(buf, &xml.EndElement{Name: xml.Name{"", condition.Local}})
	enc := xml.NewEncoder(buf)
	if text != "" {
		enc.Encode(errorText{xml.Name{condition.Space, "text"}, text})
	}

	return &Error{Type: errorType, Payload: string(buf.Bytes())}
}

func NewErrorWithCode(code, errorType string, condition ErrorCondition, text string) *Error {
	err := NewError(errorType, condition, text)
	err.Code = code
	return err
}

// Return the error text from the payload, or "" if not present.
func (e Error) Text() string {
	dec := xml.NewDecoder(bytes.NewBufferString(e.Payload))
	next := startElementIter(dec)
	for start := next(); start != nil; {
		if start.Name.Local == "text" {
			text := errorText{}
			dec.DecodeElement(&text, start)
			return text.Text
		}
		dec.Skip()
		start = next()
	}
	return ""
}

// Return the error condition from the payload.
func (e Error) Condition() ErrorCondition {
	dec := xml.NewDecoder(bytes.NewBufferString(e.Payload))
	next := startElementIter(dec)
	for start := next(); start != nil; {
		if start.Name.Local != "text" && (start.Name.Space == nsErrorStanzas || start.Name.Space == nsErrorStreams) {
			return ErrorCondition(start.Name)
		}
		dec.Skip()
		start = next()
	}
	return ErrorCondition{}
}

// Error condition.
type ErrorCondition xml.Name

// Stanza errors.
var (
	ErrorFeatureNotImplemented = ErrorCondition{nsErrorStanzas, "feature-not-implemented"}
	ErrorRemoteServerNotFound  = ErrorCondition{nsErrorStanzas, "remote-server-not-found"}
	ErrorServiceUnavailable    = ErrorCondition{nsErrorStanzas, "service-unavailable"}
	ErrorNotAuthorized         = ErrorCondition{nsErrorStanzas, "not-authorized"}
	ErrorConflict              = ErrorCondition{nsErrorStanzas, "conflict"}
	ErrorNotAcceptable         = ErrorCondition{nsErrorStanzas, "not-acceptable"}
	ErrorForbidden             = ErrorCondition{nsErrorStanzas, "forbidden"}
)
<commit_msg>Fix a small bug introduced in lang support.<commit_after>package xmpp

import (
	"bytes"
	"encoding/xml"
	"fmt"
)

const (
	IQTypeGet    = "get"
	IQTypeSet    = "set"
	IQTypeResult = "result"
	IQTypeError  = "error"

	MessageTypeNormal = "normal"
	MessageTypeChat   = "chat"
	MessageTypeError  = "error"
)

// XMPP <iq/> stanza.
type Iq struct {
	XMLName xml.Name `xml:"iq"`
	Id      string   `xml:"id,attr"`
	Type    string   `xml:"type,attr"`
	To      string   `xml:"to,attr,omitempty"`
	From    string   `xml:"from,attr,omitempty"`
	Payload string   `xml:",innerxml"`
	Error   *Error   `xml:"error"`
}

// Encode the value to an XML string and set as the payload. See xml.Marshal
// for how the value is encoded.
func (iq *Iq) PayloadEncode(v interface{}) error {
	bytes, err := xml.Marshal(v)
	if err != nil {
		return err
	}
	iq.Payload = string(bytes)
	return nil
}

// Decode the payload (an XML string) into the given value. See xml.Unmarshal
// for how the value is decoded.
func (iq *Iq) PayloadDecode(v interface{}) error {
	return xml.Unmarshal([]byte(iq.Payload), v)
}

// Return the name of the payload element.
func (iq *Iq) PayloadName() (name xml.Name) {
	dec := xml.NewDecoder(bytes.NewBufferString(iq.Payload))
	tok, err := dec.Token()
	if err != nil {
		return
	}
	start, ok := tok.(xml.StartElement)
	if !ok {
		return
	}
	return start.Name
}

// Create a response Iq. The Id is kept, To and From are reversed, Type is set
// to the given value.
func (iq *Iq) Response(type_ string) *Iq {
	return &Iq{Id: iq.Id, Type: type_, From: iq.To, To: iq.From}
}

// XMPP <message/> stanza.
type Message struct {
	XMLName xml.Name      `xml:"message"`
	Id      string        `xml:"id,attr,omitempty"`
	Type    string        `xml:"type,attr,omitempty"`
	To      string        `xml:"to,attr,omitempty"`
	From    string        `xml:"from,attr,omitempty"`
	Subject string        `xml:"subject,omitempty"`
	Body    []MessageBody `xml:"body,omitempty"`
	Thread  string        `xml:"thread,omitempty"`
	Error   *Error        `xml:"error"`
	Lang    string        `xml:"xml:lang,attr,omitempty"`

	Confir *Confirm `xml:"confirm"` // XEP-0070

	Active    *Active    `xml:"active"`    // XEP-0085
	Composing *Composing `xml:"composing"` // XEP-0085
	Paused    *Paused    `xml:"paused"`    // XEP-0085
	Inactive  *Inactive  `xml:"inactive"`  // XEP-0085
	Gone      *Gone      `xml:"gone"`      // XEP-0085
}

type MessageBody struct {
	Lang  string `xml:"xml:lang,attr,omitempty"`
	Value string `xml:",chardata"`
}

// XMPP <presence/> stanza.
type Presence struct {
	XMLName xml.Name `xml:"presence"`
	Id      string   `xml:"id,attr,omitempty"`
	Type    string   `xml:"type,attr,omitempty"`
	To      string   `xml:"to,attr,omitempty"`
	From    string   `xml:"from,attr,omitempty"`
	Show    string   `xml:"show"`            // away, chat, dnd, xa
	Status  string   `xml:"status"`          // sb []clientText
	Photo   string   `xml:"photo,omitempty"` // Avatar
	Nick    string   `xml:"nick,omitempty"`  // Nickname
}

// XMPP <error/>. May occur as a top-level stanza or embedded in another
// stanza, e.g. an <iq type="error"/>.
type Error struct {
	XMLName xml.Name `xml:"error"`
	Code    string   `xml:"code,attr,omitempty"`
	Type    string   `xml:"type,attr"`
	Payload string   `xml:",innerxml"`
}

func (e Error) Error() string {
	if text := e.Text(); text == "" {
		return fmt.Sprintf("[%s] %s", e.Type, e.Condition().Local)
	} else {
		return fmt.Sprintf("[%s] %s, %s", e.Type, e.Condition().Local, text)
	}
	panic("unreachable")
}

type errorText struct {
	XMLName xml.Name
	Text    string `xml:",chardata"`
}

// Create a new Error instance using the args as the payload.
func NewError(errorType string, condition ErrorCondition, text string) *Error {

	// Build payload.
	buf := new(bytes.Buffer)
	writeXMLStartElement(buf, &xml.StartElement{
		Name: xml.Name{"", condition.Local},
		Attr: []xml.Attr{
			{xml.Name{"", "xmlns"}, condition.Space},
		},
	})
	writeXMLEndElement(buf, &xml.EndElement{Name: xml.Name{"", condition.Local}})
	enc := xml.NewEncoder(buf)
	if text != "" {
		enc.Encode(errorText{xml.Name{condition.Space, "text"}, text})
	}

	return &Error{Type: errorType, Payload: string(buf.Bytes())}
}

func NewErrorWithCode(code, errorType string, condition ErrorCondition, text string) *Error {
	err := NewError(errorType, condition, text)
	err.Code = code
	return err
}

// Return the error text from the payload, or "" if not present.
func (e Error) Text() string {
	dec := xml.NewDecoder(bytes.NewBufferString(e.Payload))
	next := startElementIter(dec)
	for start := next(); start != nil; {
		if start.Name.Local == "text" {
			text := errorText{}
			dec.DecodeElement(&text, start)
			return text.Text
		}
		dec.Skip()
		start = next()
	}
	return ""
}

// Return the error condition from the payload.
func (e Error) Condition() ErrorCondition {
	dec := xml.NewDecoder(bytes.NewBufferString(e.Payload))
	next := startElementIter(dec)
	for start := next(); start != nil; {
		if start.Name.Local != "text" && (start.Name.Space == nsErrorStanzas || start.Name.Space == nsErrorStreams) {
			return ErrorCondition(start.Name)
		}
		dec.Skip()
		start = next()
	}
	return ErrorCondition{}
}

// Error condition.
type ErrorCondition xml.Name

// Stanza errors.
var (
	ErrorFeatureNotImplemented = ErrorCondition{nsErrorStanzas, "feature-not-implemented"}
	ErrorRemoteServerNotFound  = ErrorCondition{nsErrorStanzas, "remote-server-not-found"}
	ErrorServiceUnavailable    = ErrorCondition{nsErrorStanzas, "service-unavailable"}
	ErrorNotAuthorized         = ErrorCondition{nsErrorStanzas, "not-authorized"}
	ErrorConflict              = ErrorCondition{nsErrorStanzas, "conflict"}
	ErrorNotAcceptable         = ErrorCondition{nsErrorStanzas, "not-acceptable"}
	ErrorForbidden             = ErrorCondition{nsErrorStanzas, "forbidden"}
)
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"io"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"strings"
	"syscall"
	"time"
)

// testKill is a channel that is used during testing only to trigger an immediate cleanup and return
// from the alternate function.
var testKill chan struct{}

// alternate runs a command with alternating parameters inserted in place of the placeholder. Each
// time a USR1 signal is received, a new command is run with the next parameter, and a TERM signal
// is sent to the previous command after the overlap duration has elapsed. The alternate logs are
// written to stderr, and the command logs are written to cmdStdout and cmdStderr.
func alternate(command string, placeholder string, params []string, overlap time.Duration,
	stderr, cmdStdout, cmdStderr io.Writer) {

	log.SetPrefix("alternate | ")
	log.SetOutput(stderr)
	log.SetFlags(0)

	log.Printf("Starting with command %q, placeholder %q, params = %q, overlap = %v\n",
		command, placeholder, params, overlap)

	// When a command exits, cmdExit receives the parameter this command was run with.
	cmdExit := make(chan string)

	// When the overlap duration has elapsed, overlapEnd receives an empty struct.
	overlapEnd := make(chan struct{})

	next := make(chan os.Signal, 1)
	// Buffer a fake signal to run the first command.
	next <- syscall.Signal(0)
	// Then listen to USR1 signals dispatched by the user.
	signal.Notify(next, syscall.SIGUSR1)

	term := make(chan os.Signal)
	// Listen to both TERM signal (termination signal sent programmatically by e.g. supervisord)
	// and INT signal (termination signal sent when the user presses Ctrl-C in the terminal).
	signal.Notify(term, syscall.SIGTERM, syscall.SIGINT)

	s := newState(params)

	for {
		select {
		case <-testKill:
			log.Println("testKill channel received a value, sending KILL signal to all commands " +
				"and exiting alternate")
			signalAllCmds(params, s, syscall.SIGKILL)
			return

		case <-term:
			log.Println("Received TERM or INT signal, sending TERM signal to all commands, will " +
				"exit after all commands have exited")
			signalAllCmds(params, s, syscall.SIGTERM)

		case param := <-cmdExit:
			log.Printf("Command with parameter %q exited\n", param)
			s.unsetCmd(param)
			if !s.hasCmds() {
				log.Println("All commands have exited, exiting alternate")
				return
			}

		case <-overlapEnd:
			finishRotation(s)

		case sig := <-next:
			first := sig != syscall.SIGUSR1
			nextParam := s.nextParam()

			if !first {
				log.Printf("Received signal USR1, rotating to next parameter %q", nextParam)
			}

			if s.nextCmd() != nil {
				log.Printf("A command with parameter %q is already running, cannot run again",
					nextParam)
				break
			}

			cmdStr := strings.Replace(command, placeholder, nextParam, 1)
			nextCmd := cmd(cmdStr, cmdStdout, cmdStderr)
			s.setCmd(nextParam, nextCmd)

			log.Printf("Running command with parameter %q\n", nextParam)
			if err := run(nextCmd, nextParam, cmdExit); err != nil {
				log.Printf("Failed to run the command with parameter %q, error: %v\n",
					err.Error())
				s.unsetCmd(nextParam)
				break
			}

			if first {
				// There is no previous command to terminate.
				s.rotate()
				break
			}

			if overlap == 0 {
				finishRotation(s)
			} else {
				log.Printf("Waiting %v before sending TERM signal to command with parameter %q\n",
					overlap, s.currentParam())
				go func() {
					time.Sleep(overlap)
					overlapEnd <- struct{}{}
				}()
			}
		}
	}
}

func finishRotation(s *state) {
	if s.nextCmd() == nil {
		// The next command is not running. Cancel the rotation without terminating the current
		// command.
		return
	}
	if c := s.currentCmd(); c != nil {
		currentParam := s.currentParam()
		log.Printf("Sending TERM signal to command with parameter %q\n", currentParam)
		if err := signalCmd(c, syscall.SIGTERM); err != nil {
			log.Printf("Failed to send TERM signal to command with parameter %q, error: %v\n",
				currentParam, err)
		}
	}
	s.rotate()
}

// signalCmd sends a signal to a command.
func signalCmd(c *exec.Cmd, s os.Signal) error {
	if c == nil {
		return errors.New("signalCmd error: cmd is nil")
	}
	p := c.Process
	if p == nil {
		return errors.New("signalCmd error: cmd.Process is nil")
	}
	p.Signal(s)
	return nil
}

// signalAllCmds sends a signal to all commands in the state.
func signalAllCmds(params []string, s *state, sig os.Signal) {
	for _, param := range params {
		if c := s.cmd(param); c != nil {
			log.Printf("Sending signal to command with parameter %q\n", param)
			signalCmd(c, sig)
		}
	}
}

// cmd returns a command built from the given string. The command will print to the given stdout and
// stderr.
func cmd(s string, stdout, stderr io.Writer) *exec.Cmd {
	f := strings.Fields(s)
	c := exec.Command(f[0], f[1:]...)
	c.Stdout = stdout
	c.Stderr = stderr
	return c
}

// run runs a command without blocking. After the command exits, it sends exitMsg on the exit
// channel.
func run(c *exec.Cmd, exitMsg string, exit chan string) error {
	if err := c.Start(); err != nil {
		return err
	}
	go func() {
		c.Wait()
		exit <- exitMsg
	}()
	return nil
}
<commit_msg>Cleanup comments<commit_after>package main

import (
	"errors"
	"io"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"strings"
	"syscall"
	"time"
)

// testKill is a channel that is used during testing only to trigger an immediate cleanup and return
// from the alternate function.
var testKill chan struct{}

// alternate runs a command with alternating parameters inserted in place of the placeholder. Each
// time a USR1 signal is received, a new command is run with the next parameter, and a TERM signal
// is sent to the previous command after the overlap duration has elapsed. The alternate logs are
// written to stderr, and the command logs are written to cmdStdout and cmdStderr.
func alternate(command string, placeholder string, params []string, overlap time.Duration,
	stderr, cmdStdout, cmdStderr io.Writer) {

	log.SetPrefix("alternate | ")
	log.SetOutput(stderr)
	log.SetFlags(0)

	log.Printf("Starting with command %q, placeholder %q, params = %q, overlap = %v\n",
		command, placeholder, params, overlap)

	// When a command exits, cmdExit receives the parameter this command was run with.
	cmdExit := make(chan string)

	// When the overlap duration has elapsed, overlapEnd receives an empty struct.
	overlapEnd := make(chan struct{})

	// Listen to USR1 signals dispatched by the user, with a fake signal buffered to run the first
	// command.
	next := make(chan os.Signal, 1)
	next <- syscall.Signal(0)
	signal.Notify(next, syscall.SIGUSR1)

	// Listen to both TERM signal (termination signal sent programmatically by e.g. supervisord)
	// and INT signal (termination signal sent when the user presses Ctrl-C in the terminal).
	term := make(chan os.Signal)
	signal.Notify(term, syscall.SIGTERM, syscall.SIGINT)

	s := newState(params)

	for {
		select {
		case <-testKill:
			log.Println("testKill channel received a value, sending KILL signal to all commands " +
				"and exiting alternate")
			signalAllCmds(params, s, syscall.SIGKILL)
			return

		case <-term:
			log.Println("Received TERM or INT signal, sending TERM signal to all commands, will " +
				"exit after all commands have exited")
			signalAllCmds(params, s, syscall.SIGTERM)

		case param := <-cmdExit:
			log.Printf("Command with parameter %q exited\n", param)
			s.unsetCmd(param)
			if !s.hasCmds() {
				log.Println("All commands have exited, exiting alternate")
				return
			}

		case <-overlapEnd:
			finishRotation(s)

		case sig := <-next:
			first := sig != syscall.SIGUSR1
			nextParam := s.nextParam()

			if !first {
				log.Printf("Received signal USR1, rotating to next parameter %q", nextParam)
			}

			if s.nextCmd() != nil {
				log.Printf("A command with parameter %q is already running, cannot run again",
					nextParam)
				break
			}

			cmdStr := strings.Replace(command, placeholder, nextParam, 1)
			nextCmd := cmd(cmdStr, cmdStdout, cmdStderr)
			s.setCmd(nextParam, nextCmd)

			log.Printf("Running command with parameter %q\n", nextParam)
			if err := run(nextCmd, nextParam, cmdExit); err != nil {
				log.Printf("Failed to run the command with parameter %q, error: %v\n",
					err.Error())
				s.unsetCmd(nextParam)
				break
			}

			if first {
				// There is no previous command to terminate.
				s.rotate()
				break
			}

			if overlap == 0 {
				finishRotation(s)
			} else {
				log.Printf("Waiting %v before sending TERM signal to command with parameter %q\n",
					overlap, s.currentParam())
				go func() {
					time.Sleep(overlap)
					overlapEnd <- struct{}{}
				}()
			}
		}
	}
}

func finishRotation(s *state) {
	if s.nextCmd() == nil {
		// The next command is not running. Cancel the rotation without terminating the current
		// command.
		return
	}
	if c := s.currentCmd(); c != nil {
		currentParam := s.currentParam()
		log.Printf("Sending TERM signal to command with parameter %q\n", currentParam)
		if err := signalCmd(c, syscall.SIGTERM); err != nil {
			log.Printf("Failed to send TERM signal to command with parameter %q, error: %v\n",
				currentParam, err)
		}
	}
	s.rotate()
}

// signalCmd sends a signal to a command.
func signalCmd(c *exec.Cmd, s os.Signal) error {
	if c == nil {
		return errors.New("signalCmd error: cmd is nil")
	}
	p := c.Process
	if p == nil {
		return errors.New("signalCmd error: cmd.Process is nil")
	}
	p.Signal(s)
	return nil
}

// signalAllCmds sends a signal to all commands in the state.
func signalAllCmds(params []string, s *state, sig os.Signal) {
	for _, param := range params {
		if c := s.cmd(param); c != nil {
			log.Printf("Sending signal to command with parameter %q\n", param)
			signalCmd(c, sig)
		}
	}
}

// cmd returns a command built from the given string. The command will print to the given stdout and
// stderr.
func cmd(s string, stdout, stderr io.Writer) *exec.Cmd {
	f := strings.Fields(s)
	c := exec.Command(f[0], f[1:]...)
	c.Stdout = stdout
	c.Stderr = stderr
	return c
}

// run runs a command without blocking. After the command exits, it sends exitMsg on the exit
// channel.
func run(c *exec.Cmd, exitMsg string, exit chan string) error {
	if err := c.Start(); err != nil {
		return err
	}
	go func() {
		c.Wait()
		exit <- exitMsg
	}()
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"io"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"strings"
	"syscall"
	"time"
)

// testExit is a channel that is used during testing only to trigger a return from the alternate
// function.
var testExit chan struct{}

// alternate runs a command with alternating values inserted in place of the placeholder. Each time
// a SIGUSR1 is received, a new command is run with the next value, and the previous command is sent
// a SIGINT after the overlap duration has elapsed. The alternate logs are written to stderr, and
// the command logs are written to cmdStdout and cmdStderr;
func alternate(command string, placeholder string, values []string, overlap time.Duration,
	stderr, cmdStdout, cmdStderr io.Writer) {

	log.SetPrefix("alternate | ")
	log.SetOutput(stderr)
	log.SetFlags(0)

	log.Printf("Starting with command = '%s', placeholder '%s', values = %v, overlap = %vs\n",
		command, placeholder, values, overlap.Seconds())

	// next receives a signal when the next command should be run.
	next := make(chan os.Signal, 1)
	signal.Notify(next, syscall.SIGUSR1)
	// Run the first command.
	next <- syscall.Signal(0)

	// overlapEnd receives an empty struct when the overlap duration has elapsed.
	overlapEnd := make(chan struct{})

	// cmdExit receives the value associated with a command when the command exits.
	cmdExit := make(chan string)

	m := newManager(values)

	for {
		select {
		case <-testExit:
			log.Println("testExit channel triggered, exiting alternate")
			return

		case value := <-cmdExit:
			log.Printf("Command with value '%s' exited\n", value)
			m.unsetCmd(value)
			if !m.hasCmds() {
				log.Println("No running commands, exiting")
				return
			}

		case signal := <-next:
			nextValue := m.nextValue()

			if signal == syscall.SIGUSR1 {
				log.Printf("Received USR1, trying to move to next value: '%s'", nextValue)
			}

			if m.nextCmd() != nil {
				log.Printf("Command with value '%s' still running, cannot run again\n", nextValue)
				break
			}

			s := strings.Replace(command, placeholder, nextValue, 1)
			nextCmd := cmd(s, cmdStdout, cmdStderr)
			m.setCmd(nextValue, nextCmd)

			if err := run(nextCmd, cmdExit, nextValue); err != nil {
				log.Printf("Command with value '%s' failed to run, error: '%s'\n", err.Error())
				m.unsetCmd(nextValue)
				break
			}

			if m.first() {
				m.next()
				break
			}

			if overlap == 0 {
				if m.nextCmd() != nil && interruptCmd(m.currentCmd()) {
					m.next()
				}
			} else {
				go func() {
					time.Sleep(overlap)
					overlapEnd <- struct{}{}
				}()
			}

		case <-overlapEnd:
			if m.nextCmd() != nil && interruptCmd(m.currentCmd()) {
				m.next()
			}
		}
	}
}

func interruptCmd(c *exec.Cmd) bool {
	if c == nil {
		return false
	}
	p := c.Process
	if p == nil {
		return false
	}
	p.Signal(os.Interrupt)
	return true
}

func cmd(s string, stdout, stderr io.Writer) *exec.Cmd {
	c := exec.Command("sh", "-c", s)
	c.Stdout = stdout
	c.Stderr = stderr
	return c
}

func run(c *exec.Cmd, exit chan string, v string) error {
	log.Printf("Running command with value '%s'\n", v)
	if err := c.Start(); err != nil {
		return err
	}
	go func() {
		c.Wait()
		exit <- v
	}()
	return nil
}
<commit_msg>Refactor cmd interruption<commit_after>package main

import (
	"errors"
	"io"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"strings"
	"syscall"
	"time"
)

// testExit is a channel that is used during testing only to trigger a return from the alternate
// function.
var testExit chan struct{}

// alternate runs a command with alternating values inserted in place of the placeholder. Each time
// a SIGUSR1 is received, a new command is run with the next value, and the previous command is sent
// a SIGINT after the overlap duration has elapsed. The alternate logs are written to stderr, and
// the command logs are written to cmdStdout and cmdStderr;
func alternate(command string, placeholder string, values []string, overlap time.Duration,
	stderr, cmdStdout, cmdStderr io.Writer) {

	log.SetPrefix("alternate | ")
	log.SetOutput(stderr)
	log.SetFlags(0)

	log.Printf("Starting with command = '%s', placeholder '%s', values = %v, overlap = %vs\n",
		command, placeholder, values, overlap.Seconds())

	// next receives a signal when the next command should be run.
	next := make(chan os.Signal, 1)
	signal.Notify(next, syscall.SIGUSR1)
	// Run the first command.
	next <- syscall.Signal(0)

	// overlapEnd receives an empty struct when the overlap duration has elapsed.
	overlapEnd := make(chan struct{})

	// cmdExit receives the value associated with a command when the command exits.
	cmdExit := make(chan string)

	m := newManager(values)

	for {
		select {
		case <-testExit:
			log.Println("testExit channel triggered, exiting alternate")
			return

		case value := <-cmdExit:
			log.Printf("Command with value '%s' exited\n", value)
			m.unsetCmd(value)
			if !m.hasCmds() {
				log.Println("No running commands, exiting")
				return
			}

		case signal := <-next:
			nextValue := m.nextValue()

			if signal == syscall.SIGUSR1 {
				log.Printf("Received USR1, trying to move to next value: '%s'", nextValue)
			}

			if m.nextCmd() != nil {
				log.Printf("Command with value '%s' still running, cannot run again\n", nextValue)
				break
			}

			s := strings.Replace(command, placeholder, nextValue, 1)
			nextCmd := cmd(s, cmdStdout, cmdStderr)
			m.setCmd(nextValue, nextCmd)

			if err := run(nextCmd, cmdExit, nextValue); err != nil {
				log.Printf("Command with value '%s' failed to run, error: '%s'\n", err.Error())
				m.unsetCmd(nextValue)
				break
			}

			if m.first() {
				m.next()
				break
			}

			if overlap == 0 {
				interruptCurrentCmd(m)
			} else {
				go func() {
					time.Sleep(overlap)
					overlapEnd <- struct{}{}
				}()
			}

		case <-overlapEnd:
			interruptCurrentCmd(m)
		}
	}
}

func interruptCurrentCmd(m *manager) {
	if m.nextCmd() == nil {
		return
	}
	if err := interruptCmd(m.currentCmd()); err == nil {
		m.next()
	}
}

func interruptCmd(c *exec.Cmd) error {
	if c == nil {
		return errors.New("interruptCmd error: cmd is nil")
	}
	p := c.Process
	if p == nil {
		return errors.New("interruptCmd error: cmd.Process is nil")
	}
	p.Signal(os.Interrupt)
	return nil
}

func cmd(s string, stdout, stderr io.Writer) *exec.Cmd {
	c := exec.Command("sh", "-c", s)
	c.Stdout = stdout
	c.Stderr = stderr
	return c
}

func run(c *exec.Cmd, exit chan string, v string) error {
	log.Printf("Running command with value '%s'\n", v)
	if err := c.Start(); err != nil {
		return err
	}
	go func() {
		c.Wait()
		exit <- v
	}()
	return nil
}
<|endoftext|>