<commit_before>package cmd

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/pkg/browser"
	"github.com/spf13/cobra"
	"github.com/stormforger/cli/api"
	"github.com/stormforger/cli/api/testrun"
)

var (
	// testRunLaunchCmd represents the test run launch command
	testRunLaunchCmd = &cobra.Command{
		Use:   "launch <test-case-ref>",
		Short: "Create and launch a new test run",
		Long: fmt.Sprintf(`Create and launch a new test run based on given test case

<test-case-ref> can be 'organisation-name/test-case-name' or 'test-case-uid'.

Examples
--------
* Launch by organisation and test case name

  forge test-case launch acme-inc/checkout

* Alternatively the test case UID can also be provided

  forge test-case launch xPSX5KXM

Configuration
-------------
You can specify configuration for a test run that will overwrite what is defined
in your JavaScript definition.

* Available cluster sizings:
  * %s

Available cluster regions are available at https://docs.stormforger.com/reference/test-cluster/#cluster-region
`,
			strings.Join(validSizings, "\n  * ")),
		Run: testRunLaunch,
		PersistentPreRun: func(cmd *cobra.Command, args []string) {
			if len(args) < 1 {
				log.Fatal("Missing argument: test case reference")
			}

			if len(args) > 1 {
				log.Fatal("Too many arguments")
			}

			if testRunLaunchOpts.DumpTraffic && testRunLaunchOpts.CheckNFR != "" {
				log.Fatal("--dump-traffic and --nfr-check-file are mutual exclusive")
			}

			if testRunLaunchOpts.ClusterRegion != "" && !stringInSlice(testRunLaunchOpts.ClusterRegion, validRegions) {
				log.Fatalf("%s is not a valid region", testRunLaunchOpts.ClusterRegion)
			}

			if testRunLaunchOpts.ClusterSizing != "" && !stringInSlice(testRunLaunchOpts.ClusterSizing, validSizings) {
				log.Fatalf("%s is not a valid sizing", testRunLaunchOpts.ClusterSizing)
			}
		},
	}

	testRunLaunchOpts struct {
		OpenInBrowser         bool
		Title                 string
		Notes                 string
		ClusterRegion         string
		ClusterSizing         string
		Watch                 bool
		MaxWatchTime          time.Duration
		CheckNFR              string
		DisableGzip           bool
		SkipWait              bool
		DumpTraffic           bool
		SessionValidationMode bool
		Validate              bool
	}

	validRegions = []string{
		"ap-east-1",
		"ap-northeast-1",
		"ap-northeast-2",
		"ap-south-1",
		"ap-southeast-1",
		"ap-southeast-2",
		"ca-central-1",
		"eu-central-1",
		"eu-north-1",
		"eu-west-1",
		"eu-west-2",
		"eu-west-3",
		"sa-east-1",
		"us-east-1",
		"us-east-2",
		"us-west-1",
		"us-west-2",
	}

	validSizings = []string{
		"preflight",
		"tiny",
		"small",
		"medium",
		"large",
		"xlarge",
		"2xlarge",
	}
)

func init() {
	TestCaseCmd.AddCommand(testRunLaunchCmd)

	testRunLaunchCmd.Flags().BoolVar(&testRunLaunchOpts.OpenInBrowser, "open", false, "Open test run in browser")

	testRunLaunchCmd.Flags().StringVarP(&testRunLaunchOpts.Title, "title", "t", "", "Descriptive title of test run")
	testRunLaunchCmd.Flags().StringVarP(&testRunLaunchOpts.Notes, "notes", "n", "", "Longer description (Markdown supported)")

	testRunLaunchCmd.Flags().StringVar(&testRunLaunchOpts.ClusterRegion, "region", "", "Region to start test in")
	testRunLaunchCmd.Flags().StringVar(&testRunLaunchOpts.ClusterSizing, "sizing", "", "Cluster sizing to use")

	testRunLaunchCmd.Flags().BoolVarP(&testRunLaunchOpts.Watch, "watch", "w", false, "Automatically watch newly launched test run")
	testRunLaunchCmd.Flags().DurationVar(&testRunLaunchOpts.MaxWatchTime, "watch-timeout", 0, "Maximum duration in seconds to watch")

	testRunLaunchCmd.Flags().StringVarP(&testRunLaunchOpts.CheckNFR, "nfr-check-file", "", "", "Check test result against NFR definition (implies --watch)")

	// options for debugging
	testRunLaunchCmd.Flags().BoolVarP(&testRunLaunchOpts.DisableGzip, "disable-gzip", "", false, "Globally disable gzip")
	testRunLaunchCmd.Flags().BoolVarP(&testRunLaunchOpts.SkipWait, "skip-wait", "", false, "Ignore defined waits")
	testRunLaunchCmd.Flags().BoolVarP(&testRunLaunchOpts.DumpTraffic, "dump-traffic", "", false, "Create traffic dump")
	testRunLaunchCmd.Flags().BoolVarP(&testRunLaunchOpts.SessionValidationMode, "session-validation-mode", "", false, "Enable session validation mode")
	testRunLaunchCmd.Flags().BoolVarP(&testRunLaunchOpts.Validate, "validate", "", false, "Perform validation run")
}

func testRunLaunch(cmd *cobra.Command, args []string) {
	client := NewClient()

	testCaseUID := lookupTestCase(*client, args[0])

	launchOptions := api.TestRunLaunchOptions{
		Title:                 testRunLaunchOpts.Title,
		Notes:                 testRunLaunchOpts.Notes,
		ClusterRegion:         testRunLaunchOpts.ClusterRegion,
		ClusterSizing:         testRunLaunchOpts.ClusterSizing,
		DisableGzip:           testRunLaunchOpts.DisableGzip,
		SkipWait:              testRunLaunchOpts.SkipWait,
		DumpTraffic:           testRunLaunchOpts.DumpTraffic,
		SessionValidationMode: testRunLaunchOpts.SessionValidationMode,
	}

	if testRunLaunchOpts.Validate {
		launchOptions.SessionValidationMode = true
		launchOptions.ClusterSizing = "preflight"
	}

	status, response, err := client.TestRunCreate(testCaseUID, launchOptions)
	if err != nil {
		log.Fatal(err)
	}

	if !status {
		fmt.Fprintln(os.Stderr, "Could not launch test run!")
		fmt.Println(response)

		os.Exit(1)
	}

	testRun, err := testrun.UnmarshalSingle(strings.NewReader(response))
	if err != nil {
		log.Fatal(err)
	}

	if rootOpts.OutputFormat == "json" {
		fmt.Println(string(response))
	} else {
		// FIXME can we integrate this into testrun.UnmarshalSingle somehow?
		meta, err := api.UnmarshalMeta(strings.NewReader(response))
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf(`Launching test %s
UID: %s
Web URL: %s
`,
			testRun.Scope,
			testRun.ID,
			meta.Links.SelfWeb,
		)

		fmt.Printf("Configuration: %s cluster in %s\n", testRun.TestConfiguration.ClusterSizing, testRun.TestConfiguration.ClusterRegion)

		if testRun.TestConfiguration.DisableGzip {
			fmt.Print("  [\u2713] Disabled GZIP\n")
		}
		if testRun.TestConfiguration.SkipWait {
			fmt.Print("  [\u2713] Skip Waits\n")
		}
		if testRun.TestConfiguration.DumpTrafficFull {
			fmt.Print("  [\u2713] Traffic Dump\n")
		}
		if testRun.TestConfiguration.SessionValidationMode {
			fmt.Print("  [\u2713] Session Validation Mode\n")
		}

		if testRunLaunchOpts.OpenInBrowser {
			fmt.Printf("Opening %s in browser...\n", meta.Links.SelfWeb)
			err = browser.OpenURL(meta.Links.SelfWeb)
			if err != nil {
				log.Fatal(err)
			}
		}
	}

	if testRunLaunchOpts.Watch || testRunLaunchOpts.CheckNFR != "" || testRunLaunchOpts.Validate {
		if rootOpts.OutputFormat != "json" {
			fmt.Println("\nWatching...")
		}

		watchTestRun(testRun.ID, testRunLaunchOpts.MaxWatchTime.Round(time.Second).Seconds(), rootOpts.OutputFormat)

		if testRunLaunchOpts.CheckNFR != "" || testRunLaunchOpts.Validate {
			fmt.Println("Test finished, running non-functional checks...")

			fileName := ""
			var nfrData io.Reader
			if testRunLaunchOpts.CheckNFR != "" {
				fileName = filepath.Base(testRunLaunchOpts.CheckNFR)
				nfrData, err = os.OpenFile(testRunLaunchOpts.CheckNFR, os.O_RDONLY, 0755)
				if err != nil {
					log.Fatal(err)
				}
			} else {
				fileName = "validation.yml"
				nfrData = bytes.NewBufferString(`version: "0.1"
requirements:
- test.completed: true
- checks:
    select: success_rate
    test: ["=", 1]
- http.error_ratio:
    test: ["=", 0]`)
			}

			runNfrCheck(*client, testRun.ID, fileName, nfrData)
		} else {
			result := fetchTestRun(*client, testRun.ID)
			fmt.Println(string(result))
		}
	}
}
<commit_msg>feat: Add --uid-file flag for exposing the test-run id<commit_after>package cmd

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/pkg/browser"
	"github.com/spf13/cobra"
	"github.com/stormforger/cli/api"
	"github.com/stormforger/cli/api/testrun"
)

var (
	// testRunLaunchCmd represents the test run launch command
	testRunLaunchCmd = &cobra.Command{
		Use:   "launch <test-case-ref>",
		Short: "Create and launch a new test run",
		Long: fmt.Sprintf(`Create and launch a new test run based on given test case

<test-case-ref> can be 'organisation-name/test-case-name' or 'test-case-uid'.

Examples
--------
* Launch by organisation and test case name

  forge test-case launch acme-inc/checkout

* Alternatively the test case UID can also be provided

  forge test-case launch xPSX5KXM

Configuration
-------------
You can specify configuration for a test run that will overwrite what is defined
in your JavaScript definition.

* Available cluster sizings:
  * %s

Available cluster regions are available at https://docs.stormforger.com/reference/test-cluster/#cluster-region
`,
			strings.Join(validSizings, "\n  * ")),
		Run: testRunLaunch,
		PersistentPreRun: func(cmd *cobra.Command, args []string) {
			if len(args) < 1 {
				log.Fatal("Missing argument: test case reference")
			}

			if len(args) > 1 {
				log.Fatal("Too many arguments")
			}

			if testRunLaunchOpts.DumpTraffic && testRunLaunchOpts.CheckNFR != "" {
				log.Fatal("--dump-traffic and --nfr-check-file are mutual exclusive")
			}

			if testRunLaunchOpts.ClusterRegion != "" && !stringInSlice(testRunLaunchOpts.ClusterRegion, validRegions) {
				log.Fatalf("%s is not a valid region", testRunLaunchOpts.ClusterRegion)
			}

			if testRunLaunchOpts.ClusterSizing != "" && !stringInSlice(testRunLaunchOpts.ClusterSizing, validSizings) {
				log.Fatalf("%s is not a valid sizing", testRunLaunchOpts.ClusterSizing)
			}
		},
	}

	testRunLaunchOpts struct {
		OpenInBrowser         bool
		Title                 string
		Notes                 string
		ClusterRegion         string
		ClusterSizing         string
		Watch                 bool
		MaxWatchTime          time.Duration
		CheckNFR              string
		DisableGzip           bool
		SkipWait              bool
		DumpTraffic           bool
		SessionValidationMode bool
		Validate              bool
		TestRunIDOutputFile   string
	}

	validRegions = []string{
		"ap-east-1",
		"ap-northeast-1",
		"ap-northeast-2",
		"ap-south-1",
		"ap-southeast-1",
		"ap-southeast-2",
		"ca-central-1",
		"eu-central-1",
		"eu-north-1",
		"eu-west-1",
		"eu-west-2",
		"eu-west-3",
		"sa-east-1",
		"us-east-1",
		"us-east-2",
		"us-west-1",
		"us-west-2",
	}

	validSizings = []string{
		"preflight",
		"tiny",
		"small",
		"medium",
		"large",
		"xlarge",
		"2xlarge",
	}
)

func init() {
	TestCaseCmd.AddCommand(testRunLaunchCmd)

	testRunLaunchCmd.Flags().StringVar(&testRunLaunchOpts.TestRunIDOutputFile, "uid-file", "", "Output file for the test-run id")

	testRunLaunchCmd.Flags().BoolVar(&testRunLaunchOpts.OpenInBrowser, "open", false, "Open test run in browser")

	testRunLaunchCmd.Flags().StringVarP(&testRunLaunchOpts.Title, "title", "t", "", "Descriptive title of test run")
	testRunLaunchCmd.Flags().StringVarP(&testRunLaunchOpts.Notes, "notes", "n", "", "Longer description (Markdown supported)")

	testRunLaunchCmd.Flags().StringVar(&testRunLaunchOpts.ClusterRegion, "region", "", "Region to start test in")
	testRunLaunchCmd.Flags().StringVar(&testRunLaunchOpts.ClusterSizing, "sizing", "", "Cluster sizing to use")

	testRunLaunchCmd.Flags().BoolVarP(&testRunLaunchOpts.Watch, "watch", "w", false, "Automatically watch newly launched test run")
	testRunLaunchCmd.Flags().DurationVar(&testRunLaunchOpts.MaxWatchTime, "watch-timeout", 0, "Maximum duration in seconds to watch")

	testRunLaunchCmd.Flags().StringVarP(&testRunLaunchOpts.CheckNFR, "nfr-check-file", "", "", "Check test result against NFR definition (implies --watch)")

	// options for debugging
	testRunLaunchCmd.Flags().BoolVarP(&testRunLaunchOpts.DisableGzip, "disable-gzip", "", false, "Globally disable gzip")
	testRunLaunchCmd.Flags().BoolVarP(&testRunLaunchOpts.SkipWait, "skip-wait", "", false, "Ignore defined waits")
	testRunLaunchCmd.Flags().BoolVarP(&testRunLaunchOpts.DumpTraffic, "dump-traffic", "", false, "Create traffic dump")
	testRunLaunchCmd.Flags().BoolVarP(&testRunLaunchOpts.SessionValidationMode, "session-validation-mode", "", false, "Enable session validation mode")
	testRunLaunchCmd.Flags().BoolVarP(&testRunLaunchOpts.Validate, "validate", "", false, "Perform validation run")
}

func testRunLaunch(cmd *cobra.Command, args []string) {
	client := NewClient()

	testCaseUID := lookupTestCase(*client, args[0])

	launchOptions := api.TestRunLaunchOptions{
		Title:                 testRunLaunchOpts.Title,
		Notes:                 testRunLaunchOpts.Notes,
		ClusterRegion:         testRunLaunchOpts.ClusterRegion,
		ClusterSizing:         testRunLaunchOpts.ClusterSizing,
		DisableGzip:           testRunLaunchOpts.DisableGzip,
		SkipWait:              testRunLaunchOpts.SkipWait,
		DumpTraffic:           testRunLaunchOpts.DumpTraffic,
		SessionValidationMode: testRunLaunchOpts.SessionValidationMode,
	}

	if testRunLaunchOpts.Validate {
		launchOptions.SessionValidationMode = true
		launchOptions.ClusterSizing = "preflight"
	}

	status, response, err := client.TestRunCreate(testCaseUID, launchOptions)
	if err != nil {
		log.Fatal(err)
	}

	if !status {
		fmt.Fprintln(os.Stderr, "Could not launch test run!")
		fmt.Println(response)

		os.Exit(1)
	}

	testRun, err := testrun.UnmarshalSingle(strings.NewReader(response))
	if err != nil {
		log.Fatal(err)
	}

	if testRunLaunchOpts.TestRunIDOutputFile != "" {
		f := testRunLaunchOpts.TestRunIDOutputFile
		err := ioutil.WriteFile(f, []byte(testRun.ID), 0644)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to write file: %v\n", err)
			os.Exit(1)
		}
	}

	if rootOpts.OutputFormat == "json" {
		fmt.Println(string(response))
	} else {
		// FIXME can we integrate this into testrun.UnmarshalSingle somehow?
		meta, err := api.UnmarshalMeta(strings.NewReader(response))
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf(`Launching test %s
UID: %s
Web URL: %s
`,
			testRun.Scope,
			testRun.ID,
			meta.Links.SelfWeb,
		)

		fmt.Printf("Configuration: %s cluster in %s\n", testRun.TestConfiguration.ClusterSizing, testRun.TestConfiguration.ClusterRegion)

		if testRun.TestConfiguration.DisableGzip {
			fmt.Print("  [\u2713] Disabled GZIP\n")
		}
		if testRun.TestConfiguration.SkipWait {
			fmt.Print("  [\u2713] Skip Waits\n")
		}
		if testRun.TestConfiguration.DumpTrafficFull {
			fmt.Print("  [\u2713] Traffic Dump\n")
		}
		if testRun.TestConfiguration.SessionValidationMode {
			fmt.Print("  [\u2713] Session Validation Mode\n")
		}

		if testRunLaunchOpts.OpenInBrowser {
			fmt.Printf("Opening %s in browser...\n", meta.Links.SelfWeb)
			err = browser.OpenURL(meta.Links.SelfWeb)
			if err != nil {
				log.Fatal(err)
			}
		}
	}

	if testRunLaunchOpts.Watch || testRunLaunchOpts.CheckNFR != "" || testRunLaunchOpts.Validate {
		if rootOpts.OutputFormat != "json" {
			fmt.Println("\nWatching...")
		}

		watchTestRun(testRun.ID, testRunLaunchOpts.MaxWatchTime.Round(time.Second).Seconds(), rootOpts.OutputFormat)

		if testRunLaunchOpts.CheckNFR != "" || testRunLaunchOpts.Validate {
			fmt.Println("Test finished, running non-functional checks...")

			fileName := ""
			var nfrData io.Reader
			if testRunLaunchOpts.CheckNFR != "" {
				fileName = filepath.Base(testRunLaunchOpts.CheckNFR)
				nfrData, err = os.OpenFile(testRunLaunchOpts.CheckNFR, os.O_RDONLY, 0755)
				if err != nil {
					log.Fatal(err)
				}
			} else {
				fileName = "validation.yml"
				nfrData = bytes.NewBufferString(`version: "0.1"
requirements:
- test.completed: true
- checks:
    select: success_rate
    test: ["=", 1]
- http.error_ratio:
    test: ["=", 0]`)
			}

			runNfrCheck(*client, testRun.ID, fileName, nfrData)
		} else {
			result := fetchTestRun(*client, testRun.ID)
			fmt.Println(string(result))
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"

	"github.com/hackebrot/turtle"
	"github.com/spf13/cobra"
)

func newCategoryCmd(w io.Writer) *cobra.Command {
	return &cobra.Command{
		Use:   "category",
		Short: "Print all emojis of the category",
		Long:  "Print all emojis of the category",
		RunE: func(cmd *cobra.Command, args []string) error {
			return runCategory(w, args[0])
		},
		Args: func(cmd *cobra.Command, args []string) error {
			if len(args) != 1 {
				return fmt.Errorf("require one category")
			}
			return nil
		},
	}
}

func runCategory(w io.Writer, c string) error {

	emojis := turtle.Category(c)

	if emojis == nil {
		return fmt.Errorf("cannot find emojis of category %q", c)
	}

	j, err := NewJSONWriter(w, WithIndent(prefix, indent))

	if err != nil {
		return fmt.Errorf("error creating JSONWriter: %v", err)
	}

	return j.WriteEmojis(emojis)
}
<commit_msg>Update Use and Args for category cmd<commit_after>package main

import (
	"fmt"
	"io"

	"github.com/hackebrot/turtle"
	"github.com/spf13/cobra"
)

func newCategoryCmd(w io.Writer) *cobra.Command {
	return &cobra.Command{
		Use:   "category [CATEGORY]",
		Short: "Print all emojis of the category",
		Long:  "Print all emojis of the category",
		Args:  cobra.ExactArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {
			return runCategory(w, args[0])
		},
	}
}

func runCategory(w io.Writer, c string) error {

	emojis := turtle.Category(c)

	if emojis == nil {
		return fmt.Errorf("cannot find emojis of category %q", c)
	}

	j, err := NewJSONWriter(w, WithIndent(prefix, indent))

	if err != nil {
		return fmt.Errorf("error creating JSONWriter: %v", err)
	}

	return j.WriteEmojis(emojis)
}
<|endoftext|>


<commit_before>// 每日收盤後產生符合選股條件的報告.
//
/*
Install:

	go install github.com/toomore/gogrs/cmd/twsereport

Usage:

	twsereport [flags]

The flags are:

	-twse
		上市股票代碼，可使用 ',' 分隔多組代碼，例：2618,2329
	-twsecate
		上市股票類別，可使用 ',' 分隔多組代碼，例：11,15
	-ncpu
		指定 CPU 數量，預設為實際 CPU 數量

*/
package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/toomore/gogrs/tradingdays"
	"github.com/toomore/gogrs/twse"
)

type checkGroupList []checkGroup

func (c *checkGroupList) Add(f checkGroup) {
	if (*c)[0] == nil {
		(*c)[0] = f
	} else {
		*c = append(*c, f)
	}
}

var wg sync.WaitGroup
var twseNo = flag.String("twse", "", "上市股票代碼，可使用 ',' 分隔多組代碼，例：2618,2329")
var twseCate = flag.String("twsecate", "", "上市股票類別，可使用 ',' 分隔多組代碼，例：11,15")
var ncpu = flag.Int("ncpu", runtime.NumCPU(), "指定 CPU 數量，預設為實際 CPU 數量")
var ckList = make(checkGroupList, 1)

func init() {
	runtime.GOMAXPROCS(*ncpu)
}

func main() {
	flag.Parse()
	var datalist []*twse.Data
	var catelist []twse.StockInfo
	var twselist []string
	var catenolist []string

	if *twseCate != "" {
		l := &twse.Lists{Date: tradingdays.FindRecentlyOpened(time.Now())}

		for _, v := range strings.Split(*twseCate, ",") {
			catelist = l.GetCategoryList(v)
			for _, s := range catelist {
				catenolist = append(catenolist, s.No)
			}
		}
	}

	if *twseNo != "" {
		twselist = strings.Split(*twseNo, ",")
	}
	datalist = make([]*twse.Data, len(twselist)+len(catenolist))

	for i, no := range append(twselist, catenolist...) {
		datalist[i] = twse.NewTWSE(no, tradingdays.FindRecentlyOpened(time.Now()))
	}

Run:
	if len(datalist) > 0 {
		for _, check := range ckList {
			fmt.Printf("----- %v -----\n", check)
			wg.Add(len(datalist))
			for _, stock := range datalist {
				go func(check checkGroup, stock *twse.Data) {
					runtime.Gosched()
					if check.CheckFunc(stock) {
						fmt.Printf("[%s] %s %s $%.2f %d\n",
							check,
							stock.No, stock.Name,
							stock.GetPriceList()[len(stock.GetPriceList())-1],
							stock.GetVolumeList()[len(stock.GetVolumeList())-1],
						)
					}
				}(check, stock)
			}
			wg.Wait()
		}
	} else {
		flag.PrintDefaults()
	}
	fmt.Println("\n再次計算(Y/n)")
	in := bufio.NewReader(os.Stdin)
	stdString, _ := in.ReadString('\n')
	if strings.Split(stdString, "\n")[0] == "Y" {
		goto Run
	}
}
<commit_msg>Add func prettyprint.<commit_after>// 每日收盤後產生符合選股條件的報告.
//
/*
Install:

	go install github.com/toomore/gogrs/cmd/twsereport

Usage:

	twsereport [flags]

The flags are:

	-twse
		上市股票代碼，可使用 ',' 分隔多組代碼，例：2618,2329
	-twsecate
		上市股票類別，可使用 ',' 分隔多組代碼，例：11,15
	-ncpu
		指定 CPU 數量，預設為實際 CPU 數量

*/
package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/toomore/gogrs/tradingdays"
	"github.com/toomore/gogrs/twse"
)

type checkGroupList []checkGroup

func (c *checkGroupList) Add(f checkGroup) {
	if (*c)[0] == nil {
		(*c)[0] = f
	} else {
		*c = append(*c, f)
	}
}

var wg sync.WaitGroup
var twseNo = flag.String("twse", "", "上市股票代碼，可使用 ',' 分隔多組代碼，例：2618,2329")
var twseCate = flag.String("twsecate", "", "上市股票類別，可使用 ',' 分隔多組代碼，例：11,15")
var ncpu = flag.Int("ncpu", runtime.NumCPU(), "指定 CPU 數量，預設為實際 CPU 數量")
var ckList = make(checkGroupList, 1)

func init() {
	runtime.GOMAXPROCS(*ncpu)
}

func prettyprint(stock *twse.Data, check checkGroup) string {
	return fmt.Sprintf("[%s] %s %s $%.2f %d",
		check,
		stock.No, stock.Name,
		stock.GetPriceList()[len(stock.GetPriceList())-1],
		stock.GetVolumeList()[len(stock.GetVolumeList())-1],
	)
}

func main() {
	flag.Parse()
	var datalist []*twse.Data
	var catelist []twse.StockInfo
	var twselist []string
	var catenolist []string

	if *twseCate != "" {
		l := &twse.Lists{Date: tradingdays.FindRecentlyOpened(time.Now())}

		for _, v := range strings.Split(*twseCate, ",") {
			catelist = l.GetCategoryList(v)
			for _, s := range catelist {
				catenolist = append(catenolist, s.No)
			}
		}
	}

	if *twseNo != "" {
		twselist = strings.Split(*twseNo, ",")
	}
	datalist = make([]*twse.Data, len(twselist)+len(catenolist))

	for i, no := range append(twselist, catenolist...) {
		datalist[i] = twse.NewTWSE(no, tradingdays.FindRecentlyOpened(time.Now()))
	}

Run:
	if len(datalist) > 0 {
		for _, check := range ckList {
			fmt.Printf("----- %v -----\n", check)
			wg.Add(len(datalist))
			for _, stock := range datalist {
				go func(check checkGroup, stock *twse.Data) {
					runtime.Gosched()
					if check.CheckFunc(stock) {
						fmt.Println(prettyprint(stock, check))
					}
				}(check, stock)
			}
			wg.Wait()
		}
	} else {
		flag.PrintDefaults()
	}
	fmt.Println("\n再次計算(Y/n)")
	in := bufio.NewReader(os.Stdin)
	stdString, _ := in.ReadString('\n')
	if strings.Split(stdString, "\n")[0] == "Y" {
		goto Run
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"os/signal"
	"regexp"
	"strings"
	"syscall"
	"time"

	"github.com/gentlemanautomaton/volmgmt/fileattr"
	"github.com/gentlemanautomaton/volmgmt/usn"
)

func usage(errmsg string) {
	fmt.Fprintf(os.Stderr, "%s\n\n", errmsg)
	flag.Usage()
	os.Exit(2)
}

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "usage: %s [-t type[,type...]] [-i regexp] [-e regexp] <volume>\n", os.Args[0])
		flag.PrintDefaults()
	}

	var (
		reasonStr  string
		reason     usn.Reason
		includeStr string
		include    *regexp.Regexp
		excludeStr string
		exclude    *regexp.Regexp
	)

	flag.StringVar(&reasonStr, "t", "*", "journal record types to include (comma-separated)")
	flag.StringVar(&includeStr, "i", "", "regular expression for file match (inclusion)")
	flag.StringVar(&excludeStr, "e", "", "regular expression for file match (exclusion)")
	flag.Parse()

	if flag.NArg() == 0 {
		usage("No volume specified.")
	}
	if flag.NArg() > 1 {
		usage("Only a single volume may be specified.")
	}

	path := flag.Arg(0)

	reason, err := usn.ParseReason(reasonStr)
	if err != nil {
		usage(fmt.Sprintf("%v", err))
	}

	include = compileRegex(includeStr)
	exclude = compileRegex(excludeStr)

	journal, err := usn.NewJournal(path)
	if err != nil {
		fmt.Printf("Unable to create monitor: %v\n", err)
		os.Exit(2)
	}
	defer journal.Close()

	data, err := journal.Query()
	if err != nil {
		fmt.Printf("Unable to access USN Journal: %v\n", err)
		os.Exit(2)
	}

	location, err := time.LoadLocation("Local")
	if err != nil {
		fmt.Printf("Unable to load local timezone information: %v\n", err)
		os.Exit(2)
	}

	monitor := journal.Monitor()
	defer monitor.Close()

	feed := monitor.Listen(64) // Register the feed before starting the monitor

	err = monitor.Run(data.NextUSN, time.Millisecond*100, reason)
	if err != nil {
		fmt.Printf("Unable to monitor USN Journal: %v\n", err)
		os.Exit(2)
	}

	done := make(chan struct{})
	go run(feed, location, include, exclude, done)

	ch := make(chan os.Signal)
	signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)

	select {
	case <-ch:
	case <-done:
	}
}

func run(feed <-chan usn.Record, location *time.Location, include, exclude *regexp.Regexp, done chan struct{}) {
	defer close(done)

	for record := range feed {
		if include != nil && !include.MatchString(record.FileName) {
			continue
		}

		if exclude != nil && exclude.MatchString(record.FileName) {
			continue
		}

		id := record.FileReferenceNumber.String()
		when := record.TimeStamp.In(location).Format("2006-01-02 15:04:05.000000 MST")
		attr := record.FileAttributes.Join("", fileattr.FormatCode)
		action := strings.ToUpper(record.Reason.Join("|", usn.ReasonFormatShort))

		fmt.Printf("%s  %d.%d  %-5s  %20s  \"%s\"  %s  %s\n", when, record.MajorVersion, record.MinorVersion, record.SourceInfo, id, record.Path, attr, action)
	}
}

func compileRegex(re string) *regexp.Regexp {
	if re == "" {
		return nil
	}

	// Force case-insensitive matching
	if !strings.HasPrefix(re, "(?i)") {
		re = "(?i)" + re
	}

	c, err := regexp.Compile(re)
	if err != nil {
		usage(fmt.Sprintf("Unable to compile regular expression \"%s\": %v\n", re, err))
	}
	return c
}
<commit_msg>usnjournal: regular expression matching now works on full file paths<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"os/signal"
	"regexp"
	"strings"
	"syscall"
	"time"

	"github.com/gentlemanautomaton/volmgmt/fileattr"
	"github.com/gentlemanautomaton/volmgmt/usn"
)

func usage(errmsg string) {
	fmt.Fprintf(os.Stderr, "%s\n\n", errmsg)
	flag.Usage()
	os.Exit(2)
}

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "usage: %s [-t type[,type...]] [-i regexp] [-e regexp] <volume>\n", os.Args[0])
		flag.PrintDefaults()
	}

	var (
		reasonStr  string
		reason     usn.Reason
		includeStr string
		include    *regexp.Regexp
		excludeStr string
		exclude    *regexp.Regexp
	)

	flag.StringVar(&reasonStr, "t", "*", "journal record types to include (comma-separated)")
	flag.StringVar(&includeStr, "i", "", "regular expression for file match (inclusion)")
	flag.StringVar(&excludeStr, "e", "", "regular expression for file match (exclusion)")
	flag.Parse()

	if flag.NArg() == 0 {
		usage("No volume specified.")
	}
	if flag.NArg() > 1 {
		usage("Only a single volume may be specified.")
	}

	path := flag.Arg(0)

	reason, err := usn.ParseReason(reasonStr)
	if err != nil {
		usage(fmt.Sprintf("%v", err))
	}

	include = compileRegex(includeStr)
	exclude = compileRegex(excludeStr)

	journal, err := usn.NewJournal(path)
	if err != nil {
		fmt.Printf("Unable to create monitor: %v\n", err)
		os.Exit(2)
	}
	defer journal.Close()

	data, err := journal.Query()
	if err != nil {
		fmt.Printf("Unable to access USN Journal: %v\n", err)
		os.Exit(2)
	}

	location, err := time.LoadLocation("Local")
	if err != nil {
		fmt.Printf("Unable to load local timezone information: %v\n", err)
		os.Exit(2)
	}

	monitor := journal.Monitor()
	defer monitor.Close()

	feed := monitor.Listen(64) // Register the feed before starting the monitor

	err = monitor.Run(data.NextUSN, time.Millisecond*100, reason)
	if err != nil {
		fmt.Printf("Unable to monitor USN Journal: %v\n", err)
		os.Exit(2)
	}

	done := make(chan struct{})
	go run(feed, location, include, exclude, done)

	ch := make(chan os.Signal)
	signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)

	select {
	case <-ch:
	case <-done:
	}
}

func run(feed <-chan usn.Record, location *time.Location, include, exclude *regexp.Regexp, done chan struct{}) {
	defer close(done)

	for record := range feed {
		if include != nil {
			if record.Path == "" {
				if !include.MatchString(record.FileName) {
					continue
				}
			} else {
				if !include.MatchString(record.Path) {
					continue
				}
			}
		}

		if exclude != nil {
			if record.Path == "" {
				if exclude.MatchString(record.FileName) {
					continue
				}
			} else {
				if exclude.MatchString(record.Path) {
					continue
				}
			}
		}

		id := record.FileReferenceNumber.String()
		when := record.TimeStamp.In(location).Format("2006-01-02 15:04:05.000000 MST")
		attr := record.FileAttributes.Join("", fileattr.FormatCode)
		action := strings.ToUpper(record.Reason.Join("|", usn.ReasonFormatShort))

		fmt.Printf("%s  %d.%d  %-5s  %20s  \"%s\"  %s  %s\n", when, record.MajorVersion, record.MinorVersion, record.SourceInfo, id, record.Path, attr, action)
	}
}

func compileRegex(re string) *regexp.Regexp {
	if re == "" {
		return nil
	}

	// Force case-insensitive matching
	if !strings.HasPrefix(re, "(?i)") {
		re = "(?i)" + re
	}

	c, err := regexp.Compile(re)
	if err != nil {
		usage(fmt.Sprintf("Unable to compile regular expression \"%s\": %v\n", re, err))
	}
	return c
}
<|endoftext|>


<commit_before>/*
 * This file is part of the KubeVirt project
 *
 * 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.
 *
 * Copyright 2017, 2018 Red Hat, Inc.
 *
 */

package main

import (
	"os"

	"kubevirt.io/kubevirt/pkg/virtctl"
)

func main() {
	virtctl.Execute()
	os.Exit(0)
}
<commit_msg>virtctl: Allow Kubernetes auth plugins<commit_after>/*
 * This file is part of the KubeVirt project
 *
 * 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.
 *
 * Copyright 2017, 2018 Red Hat, Inc.
 *
 */

package main

import (
	"os"

	"kubevirt.io/kubevirt/pkg/virtctl"

	// Import to initialize client auth plugins.
	_ "k8s.io/client-go/plugin/pkg/client/auth"
)

func main() {
	virtctl.Execute()
	os.Exit(0)
}
<|endoftext|>


<commit_before>// waitsms waits for SMSs to be received by the modem, and dumps them to stdout.
//
// This provides an example of using indications, as well as a test
// that the library works with the modem.
//
// The modem device provided must support nofications, or no SMSs will be seen.
// (the notification port is typically USB2, hence the default)
package main

import (
	"context"
	"flag"
	"io"
	"log"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/warthog618/sms/ms/message"
	"github.com/warthog618/sms/ms/sar"

	"github.com/warthog618/sms/encoding/tpdu"

	"github.com/warthog618/sms/ms/pdumode"

	"github.com/warthog618/modem/gsm"
	"github.com/warthog618/modem/serial"
	"github.com/warthog618/modem/trace"
)

func main() {
	dev := flag.String("d", "/dev/ttyUSB2", "path to modem device")
	baud := flag.Int("b", 115200, "baud rate")
	period := flag.Duration("p", 10*time.Minute, "period to wait")
	timeout := flag.Duration("t", 400*time.Millisecond, "command timeout period")
	verbose := flag.Bool("v", false, "log modem interactions")
	hex := flag.Bool("x", false, "hex dump modem responses")
	flag.Parse()
	m, err := serial.New(*dev, *baud)
	if err != nil {
		log.Println(err)
		return
	}
	defer m.Close()
	var mio io.ReadWriter = m
	if *hex {
		mio = trace.New(m, log.New(os.Stdout, "", log.LstdFlags), trace.ReadFormat("r: %v"))
	} else if *verbose {
		mio = trace.New(m, log.New(os.Stdout, "", log.LstdFlags))
	}
	g := gsm.New(mio)
	g.SetPDUMode()
	ctx, cancel := context.WithTimeout(context.Background(), *timeout)
	err = g.Init(ctx)
	cancel()
	if err != nil {
		log.Println(err)
		return
	}
	cmt, err := g.AddIndication("+CMT:", 1)
	if err != nil {
		log.Println(err)
		return
	}
	ctx, cancel = context.WithTimeout(context.Background(), *timeout)
	if _, err = g.Command(ctx, "+CNMI=1,2,2,1,0"); err != nil {
		log.Println(err)
		cancel()
		return
	}
	cancel()
	ctx, cancel = context.WithTimeout(context.Background(), *period)
	go func() {
		for {
			select {
			case <-time.After(time.Minute):
				tctx, tcancel := context.WithTimeout(ctx, *timeout)
				i, err := g.Command(tctx, "+CSQ")
				if err != nil {
					log.Println(err)
				} else {
					log.Printf("Signal quality: %v\n", i)
				}
				tcancel()
			case <-ctx.Done():
				return
			}
		}
	}()
	defer cancel()
	pd := pdumode.Decoder{}
	asyncError := func(error) {
		log.Printf("reassembly error: %v", err)
	}
	udd, err := tpdu.NewUDDecoder()
	if err != nil {
		log.Fatalf("err: %v\n", err)
	}
	udd.AddAllCharsets()
	c := sar.NewCollector(time.Hour, asyncError)
	reassembler := message.NewReassembler(udd, c)
	defer reassembler.Close()
	for {
		select {
		case <-ctx.Done():
			log.Println("exiting...")
			return
		case i := <-cmt:
			actx, acancel := context.WithTimeout(ctx, *timeout)
			g.Command(actx, "+CNMA")
			acancel()
			lstr := strings.Split(i[0], ",")
			l, err := strconv.Atoi(lstr[len(lstr)-1])
			if err != nil {
				log.Printf("err: %v\n", err)
			}
			_, pdu, err := pd.DecodeString(i[1])
			if err != nil {
				log.Printf("err: %v\n", err)
			}
			if int(l) != len(pdu) {
				log.Printf("length mismatch - expected %d, got %d", l, len(pdu))
			}
			m, err := reassembler.Reassemble(pdu)
			if err != nil {
				log.Printf("err: %v\n", err)
			}
			if m != nil {
				log.Printf("%s: %s\n", m.Number, m.Msg)
			}
		}
	}
}
<commit_msg>Refactor waitsms to reduce complexity and make the code clearer<commit_after>// waitsms waits for SMSs to be received by the modem, and dumps them to stdout.
//
// This provides an example of using indications, as well as a test
// that the library works with the modem.
//
// The modem device provided must support nofications, or no SMSs will be seen.
// (the notification port is typically USB2, hence the default)
package main

import (
	"context"
	"flag"
	"io"
	"log"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/warthog618/sms/ms/message"
	"github.com/warthog618/sms/ms/sar"

	"github.com/warthog618/sms/encoding/tpdu"

	"github.com/warthog618/sms/ms/pdumode"

	"github.com/warthog618/modem/gsm"
	"github.com/warthog618/modem/serial"
	"github.com/warthog618/modem/trace"
)

func main() {
	dev := flag.String("d", "/dev/ttyUSB2", "path to modem device")
	baud := flag.Int("b", 115200, "baud rate")
	period := flag.Duration("p", 10*time.Minute, "period to wait")
	timeout := flag.Duration("t", 400*time.Millisecond, "command timeout period")
	verbose := flag.Bool("v", false, "log modem interactions")
	hex := flag.Bool("x", false, "hex dump modem responses")
	flag.Parse()
	m, err := serial.New(*dev, *baud)
	if err != nil {
		log.Println(err)
		return
	}
	defer m.Close()
	var mio io.ReadWriter = m
	if *hex {
		mio = trace.New(m, log.New(os.Stdout, "", log.LstdFlags), trace.ReadFormat("r: %v"))
	} else if *verbose {
		mio = trace.New(m, log.New(os.Stdout, "", log.LstdFlags))
	}
	g := gsm.New(mio)
	g.SetPDUMode()
	ctx, cancel := context.WithTimeout(context.Background(), *timeout)
	err = g.Init(ctx)
	cancel()
	if err != nil {
		log.Println(err)
		return
	}
	ctx, cancel = context.WithTimeout(context.Background(), *period)
	defer cancel()
	go pollSignalQuality(ctx, g, timeout)
	waitForSMSs(ctx, g, timeout)
}

// pollSignalQuality polls the modem to read signal quality every minute.
// This is run in parallel to waitForSMS to demonstrate separate goroutines
// interacting with the modem.
func pollSignalQuality(ctx context.Context, g *gsm.GSM, timeout *time.Duration) {
	for {
		select {
		case <-time.After(time.Minute):
			tctx, tcancel := context.WithTimeout(ctx, *timeout)
			i, err := g.Command(tctx, "+CSQ")
			if err != nil {
				log.Println(err)
			} else {
				log.Printf("Signal quality: %v\n", i)
			}
			tcancel()
		case <-ctx.Done():
			return
		}
	}
}

// waitForSMSs adds an indication to the modem and prints any received SMSs.
// It will continue to wait until the provided context is done.
// It reassembles multi-part SMSs into a complete message prior to display.
func waitForSMSs(ctx context.Context, g *gsm.GSM, timeout *time.Duration) {
	cmt, err := g.AddIndication("+CMT:", 1)
	if err != nil {
		log.Println(err)
		return
	}
	cctx, cancel := context.WithTimeout(ctx, *timeout)
	// tell the modem to forward SMSs to us.
	if _, err = g.Command(cctx, "+CNMI=1,2,2,1,0"); err != nil {
		log.Println(err)
		cancel()
		return
	}
	cancel()
	pd := pdumode.Decoder{}
	asyncError := func(err error) {
		log.Printf("reassembly error: %v", err)
	}
	udd, err := tpdu.NewUDDecoder()
	if err != nil {
		log.Fatalf("err: %v\n", err)
	}
	udd.AddAllCharsets()
	c := sar.NewCollector(time.Hour, asyncError)
	reassembler := message.NewReassembler(udd, c)
	defer reassembler.Close()
	for {
		select {
		case <-ctx.Done():
			log.Println("exiting...")
			return
		case i := <-cmt:
			actx, acancel := context.WithTimeout(ctx, *timeout)
			g.Command(actx, "+CNMA")
			acancel()
			lstr := strings.Split(i[0], ",")
			l, err := strconv.Atoi(lstr[len(lstr)-1])
			if err != nil {
				log.Printf("err: %v\n", err)
			}
			_, pdu, err := pd.DecodeString(i[1])
			if err != nil {
				log.Printf("err: %v\n", err)
			}
			if int(l) != len(pdu) {
				log.Printf("length mismatch - expected %d, got %d", l, len(pdu))
			}
			m, err := reassembler.Reassemble(pdu)
			if err != nil {
				log.Printf("err: %v\n", err)
			}
			if m != nil {
				log.Printf("%s: %s\n", m.Number, m.Msg)
			}
		}
	}
}
<|endoftext|>


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

package main

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"strings"
	"testing"

	"github.com/u-root/u-root/pkg/testutil"
)

type WifiTestCase struct {
	name   string
	args   []string
	expect string
}

var testcases = []WifiTestCase{
	{
		name:   "No Flags, No Args",
		args:   nil,
		expect: "Usage",
	},
	{
		name:   "Flags, No Args",
		args:   []string{"-i=123"},
		expect: "Usage",
	},
}

func run(c *exec.Cmd) (string, string, error) {
	var o, e bytes.Buffer
	c.Stdout, c.Stderr = &o, &e
	err := c.Run()
	return o.String(), e.String(), err
}

func TestWifi(t *testing.T) {
	// Set up
	tmpDir, execPath := testutil.CompileInTempDir(t)
	defer os.RemoveAll(tmpDir)

	// Tests
	for _, test := range testcases {
		c := exec.Command(execPath, test.args...)
		_, e, _ := run(c)
		if !strings.Contains(e, test.expect) {
			t.Logf("TEST %v", test.name)
			execStatement := fmt.Sprintf("exec(wifi %s)", strings.Trim(fmt.Sprint(test.args), "[]"))
			t.Errorf("%s\ngot:%s\nwant:%s", execStatement, e, test.expect)
		}
	}
}
<commit_msg>Adjust Error Test case because 0 args is no longer an error<commit_after>// Copyright 2017 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"strings"
	"testing"

	"github.com/u-root/u-root/pkg/testutil"
)

type WifiTestCase struct {
	name   string
	args   []string
	expect string
}

var testcases = []WifiTestCase{
	{
		name:   "More elements than needed",
		args:   []string{"a", "a", "a", "a"},
		expect: "Usage",
	},
	{
		name:   "Flags, More elements than needed",
		args:   []string{"-i=123", "a", "a", "a", "a"},
		expect: "Usage",
	},
}

func run(c *exec.Cmd) (string, string, error) {
	var o, e bytes.Buffer
	c.Stdout, c.Stderr = &o, &e
	err := c.Run()
	return o.String(), e.String(), err
}

func TestWifiErrors(t *testing.T) {
	// Set up
	tmpDir, execPath := testutil.CompileInTempDir(t)
	defer os.RemoveAll(tmpDir)

	// Tests
	for _, test := range testcases {
		c := exec.Command(execPath, test.args...)
		_, e, _ := run(c)
		if !strings.Contains(e, test.expect) {
			t.Logf("TEST %v", test.name)
			execStatement := fmt.Sprintf("exec(wifi %s)", strings.Trim(fmt.Sprint(test.args), "[]"))
			t.Errorf("%s\ngot:%s\nwant:%s", execStatement, e, test.expect)
		}
	}
}
<|endoftext|>


<commit_before>// +build !race
package command

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

	"github.com/mitchellh/cli"
)

var (
	basehcl = `
disable_mlock = true

listener "tcp" {
  address = "127.0.0.1:8200"
  tls_disable = "true"
}
`

	consulhcl = `
backend "consul" {
    prefix = "foo/"
	advertise_addr = "http://127.0.0.1:8200"
}
`
	haconsulhcl = `
ha_backend "consul" {
    prefix = "bar/"
	advertise_addr = "http://127.0.0.1:8200"
}
`

	badhaconsulhcl = `
ha_backend "file" {
    path = "/dev/null"
}
`
)

// The following tests have a go-metrics/exp manager race condition
func TestServer_CommonHA(t *testing.T) {
	ui := new(cli.MockUi)
	c := &ServerCommand{
		Meta: Meta{
			Ui: ui,
		},
	}

	tmpfile, err := ioutil.TempFile("", "")
	if err != nil {
		t.Fatalf("error creating temp dir: %v", err)
	}

	tmpfile.WriteString(basehcl + consulhcl)
	tmpfile.Close()
	defer os.Remove(tmpfile.Name())

	args := []string{"-config", tmpfile.Name(), "-verify-only", "true"}

	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	if !strings.Contains(ui.OutputWriter.String(), "(HA available)") {
		t.Fatalf("did not find HA available: %s", ui.OutputWriter.String())
	}
}

func TestServer_GoodSeparateHA(t *testing.T) {
	ui := new(cli.MockUi)
	c := &ServerCommand{
		Meta: Meta{
			Ui: ui,
		},
	}

	tmpfile, err := ioutil.TempFile("", "")
	if err != nil {
		t.Fatalf("error creating temp dir: %v", err)
	}

	tmpfile.WriteString(basehcl + consulhcl + haconsulhcl)
	tmpfile.Close()
	defer os.Remove(tmpfile.Name())

	args := []string{"-config", tmpfile.Name(), "-verify-only", "true"}

	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	if !strings.Contains(ui.OutputWriter.String(), "HA Backend:") {
		t.Fatalf("did not find HA Backend: %s", ui.OutputWriter.String())
	}
}

func TestServer_BadSeparateHA(t *testing.T) {
	ui := new(cli.MockUi)
	c := &ServerCommand{
		Meta: Meta{
			Ui: ui,
		},
	}

	tmpfile, err := ioutil.TempFile("", "")
	if err != nil {
		t.Fatalf("error creating temp dir: %v", err)
	}

	tmpfile.WriteString(basehcl + consulhcl + badhaconsulhcl)
	tmpfile.Close()
	defer os.Remove(tmpfile.Name())

	args := []string{"-config", tmpfile.Name()}

	if code := c.Run(args); code == 0 {
		t.Fatalf("bad: should have gotten an error on a bad HA config")
	}
}
<commit_msg>Fix build tag<commit_after>// +build !race

package command

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

	"github.com/mitchellh/cli"
)

var (
	basehcl = `
disable_mlock = true

listener "tcp" {
  address = "127.0.0.1:8200"
  tls_disable = "true"
}
`

	consulhcl = `
backend "consul" {
    prefix = "foo/"
	advertise_addr = "http://127.0.0.1:8200"
}
`
	haconsulhcl = `
ha_backend "consul" {
    prefix = "bar/"
	advertise_addr = "http://127.0.0.1:8200"
}
`

	badhaconsulhcl = `
ha_backend "file" {
    path = "/dev/null"
}
`
)

// The following tests have a go-metrics/exp manager race condition
func TestServer_CommonHA(t *testing.T) {
	ui := new(cli.MockUi)
	c := &ServerCommand{
		Meta: Meta{
			Ui: ui,
		},
	}

	tmpfile, err := ioutil.TempFile("", "")
	if err != nil {
		t.Fatalf("error creating temp dir: %v", err)
	}

	tmpfile.WriteString(basehcl + consulhcl)
	tmpfile.Close()
	defer os.Remove(tmpfile.Name())

	args := []string{"-config", tmpfile.Name(), "-verify-only", "true"}

	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	if !strings.Contains(ui.OutputWriter.String(), "(HA available)") {
		t.Fatalf("did not find HA available: %s", ui.OutputWriter.String())
	}
}

func TestServer_GoodSeparateHA(t *testing.T) {
	ui := new(cli.MockUi)
	c := &ServerCommand{
		Meta: Meta{
			Ui: ui,
		},
	}

	tmpfile, err := ioutil.TempFile("", "")
	if err != nil {
		t.Fatalf("error creating temp dir: %v", err)
	}

	tmpfile.WriteString(basehcl + consulhcl + haconsulhcl)
	tmpfile.Close()
	defer os.Remove(tmpfile.Name())

	args := []string{"-config", tmpfile.Name(), "-verify-only", "true"}

	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	if !strings.Contains(ui.OutputWriter.String(), "HA Backend:") {
		t.Fatalf("did not find HA Backend: %s", ui.OutputWriter.String())
	}
}

func TestServer_BadSeparateHA(t *testing.T) {
	ui := new(cli.MockUi)
	c := &ServerCommand{
		Meta: Meta{
			Ui: ui,
		},
	}

	tmpfile, err := ioutil.TempFile("", "")
	if err != nil {
		t.Fatalf("error creating temp dir: %v", err)
	}

	tmpfile.WriteString(basehcl + consulhcl + badhaconsulhcl)
	tmpfile.Close()
	defer os.Remove(tmpfile.Name())

	args := []string{"-config", tmpfile.Name()}

	if code := c.Run(args); code == 0 {
		t.Fatalf("bad: should have gotten an error on a bad HA config")
	}
}
<|endoftext|>


<commit_before>package workflow

import (
	"context"
	"fmt"
	"strings"

	"github.com/fsamin/go-dump"

	"github.com/ovh/cds/sdk"
	"github.com/ovh/cds/sdk/interpolate"
	"github.com/ovh/cds/sdk/telemetry"
)

func getNodeJobRunParameters(j sdk.Job, run *sdk.WorkflowNodeRun, stage *sdk.Stage) ([]sdk.Parameter, *sdk.MultiError) {
	params := run.BuildParameters
	tmp := map[string]string{
		"cds.stage": stage.Name,
		"cds.job":   j.Action.Name,
	}
	errm := &sdk.MultiError{}

	for k, v := range tmp {
		s, err := interpolate.Do(v, tmp)
		if err != nil {
			errm.Append(err)
			continue
		}
		sdk.AddParameter(&params, k, sdk.StringParameter, s)
	}

	if errm.IsEmpty() {
		return params, nil
	}
	return params, errm
}

// getBuildParameterFromNodeContext returns the parameters compute from  node context (project, application,  pipeline, pyaload)
func getBuildParameterFromNodeContext(proj sdk.Project, w sdk.Workflow, runContext nodeRunContext, pipelineParameters []sdk.Parameter, payload interface{}, hookEvent *sdk.WorkflowNodeRunHookEvent) ([]sdk.Parameter, error) {
	tmpProj := sdk.ParametersFromProjectVariables(proj)
	vars := make(map[string]string, len(tmpProj))
	for k, v := range tmpProj {
		vars[k] = v
	}

	tmpProj = sdk.ParametersFromProjectKeys(proj)
	for k, v := range tmpProj {
		vars[k] = v
	}

	// COMPUTE APPLICATION VARIABLE
	if runContext.Application.ID != 0 {
		vars["cds.application"] = runContext.Application.Name
		tmp := sdk.ParametersFromApplicationVariables(runContext.Application)
		for k, v := range tmp {
			vars[k] = v
		}

		tmp = sdk.ParametersFromApplicationKeys(runContext.Application)
		for k, v := range tmp {
			vars[k] = v
		}
	}

	// COMPUTE ENVIRONMENT VARIABLE
	if runContext.Environment.ID != 0 {
		vars["cds.environment"] = runContext.Environment.Name
		tmp := sdk.ParametersFromEnvironmentVariables(runContext.Environment)
		for k, v := range tmp {
			vars[k] = v
		}
		tmp = sdk.ParametersFromEnvironmentKeys(runContext.Environment)
		for k, v := range tmp {
			vars[k] = v
		}
	}

	// COmpute integration from workflow
	for _, integ := range runContext.WorkflowProjectIntegrations {
		if !sdk.AllowIntegrationInVariable(integ.ProjectIntegration.Model) {
			continue
		}
		prefix := sdk.GetIntegrationVariablePrefix(integ.ProjectIntegration.Model)
		vars["cds.integration."+prefix] = integ.ProjectIntegration.Name
		tmp := sdk.ParametersFromIntegration(prefix, integ.ProjectIntegration.Config)
		for k, v := range tmp {
			vars[k] = v
		}

		tmpWkfConf := sdk.ParametersFromIntegration(prefix, integ.Config)
		for k, v := range tmpWkfConf {
			vars[k] = v
		}
	}

	// COMPUTE INTEGRATION VARIABLE FROM NODE CONTEXT
	for _, integ := range runContext.ProjectIntegrations {
		prefix := sdk.GetIntegrationVariablePrefix(integ.Model)
		vars["cds.integration."+prefix] = integ.Name
		tmp := sdk.ParametersFromIntegration(prefix, integ.Config)
		for k, v := range tmp {
			vars[k] = v
		}

		if integ.Model.Deployment {
			// COMPUTE DEPLOYMENT STRATEGIES VARIABLE
			if runContext.Application.ID != 0 {
				for pfName, pfConfig := range runContext.Application.DeploymentStrategies {
					if pfName == integ.Name {
						tmp := sdk.ParametersFromIntegration(prefix, pfConfig)
						for k, v := range tmp {
							vars[k] = v
						}
					}
				}
			}
		}
	}

	// COMPUTE PIPELINE PARAMETER
	tmpPip := sdk.ParametersFromPipelineParameters(pipelineParameters)
	for k, v := range tmpPip {
		vars[k] = v
	}

	// COMPUTE PAYLOAD
	e := dump.NewDefaultEncoder()

	e.Formatters = []dump.KeyFormatterFunc{dump.WithDefaultLowerCaseFormatter()}
	e.ExtraFields.DetailedMap = false
	e.ExtraFields.DetailedStruct = false
	e.ExtraFields.Len = false
	e.ExtraFields.Type = false
	tmpVars, errdump := e.ToStringMap(payload)
	if errdump != nil {
		return nil, sdk.WrapError(errdump, "do-dump error")
	}

	//Merge the dumped payload with vars
	vars = sdk.ParametersMapMerge(vars, tmpVars)

	vars["cds.project"] = w.ProjectKey
	vars["cds.workflow"] = w.Name
	vars["cds.pipeline"] = runContext.Pipeline.Name

	// COMPUTE VCS STRATEGY VARIABLE
	if runContext.Application.RepositoryStrategy.ConnectionType != "" {
		vars["git.connection.type"] = runContext.Application.RepositoryStrategy.ConnectionType
		if runContext.Application.RepositoryStrategy.SSHKey != "" {
			vars["git.ssh.key"] = runContext.Application.RepositoryStrategy.SSHKey
		}
		if runContext.Application.RepositoryStrategy.PGPKey != "" {
			vars["git.pgp.key"] = runContext.Application.RepositoryStrategy.PGPKey
		}
		if runContext.Application.RepositoryStrategy.User != "" {
			vars["git.http.user"] = runContext.Application.RepositoryStrategy.User
		}
		if runContext.Application.VCSServer != "" {
			vars["git.server"] = runContext.Application.VCSServer
		}
	} else {
		// remove vcs strategy variable
		delete(vars, "git.ssh.key")
		delete(vars, "git.pgp.key")
		delete(vars, "git.http.user")
	}

	if hookEvent != nil {
		vars["parent.project"] = hookEvent.ParentWorkflow.Key
		vars["parent.run"] = fmt.Sprintf("%d", hookEvent.ParentWorkflow.Run)
		vars["parent.workflow"] = hookEvent.ParentWorkflow.Name
		vars["parent.outgoinghook"] = hookEvent.WorkflowNodeHookUUID
	}

	params := make([]sdk.Parameter, 0)
	for k, v := range vars {
		sdk.AddParameter(&params, k, sdk.StringParameter, v)
	}

	return params, nil
}

func getParentParameters(w *sdk.WorkflowRun, nodeRuns []*sdk.WorkflowNodeRun) ([]sdk.Parameter, error) {
	params := make([]sdk.Parameter, 0, len(nodeRuns))
	for _, parentNodeRun := range nodeRuns {
		var nodeName string

		node := w.Workflow.WorkflowData.NodeByID(parentNodeRun.WorkflowNodeID)
		if node == nil {
			return nil, sdk.WithStack(fmt.Errorf("unable to find node %d in workflow", parentNodeRun.WorkflowNodeID))
		}
		nodeName = node.Name

		parentParams := make([]sdk.Parameter, 0, len(parentNodeRun.BuildParameters))
		for _, param := range parentNodeRun.BuildParameters {
			prefix := "workflow." + nodeName + "."
			if param.Name == "" || param.Name == "cds.semver" || param.Name == "cds.release.version" ||
				strings.HasPrefix(param.Name, "cds.proj") ||
				strings.HasPrefix(param.Name, "cds.version") || strings.HasPrefix(param.Name, "cds.run.number") ||
				strings.HasPrefix(param.Name, "cds.workflow") || strings.HasPrefix(param.Name, "job.requirement") {
				continue
			}

			if strings.HasPrefix(param.Name, "workflow.") {
				parentParams = append(parentParams, param)
				continue
			}

			// We inherite git variables is there is more than one repositories in the whole workflow
			if strings.HasPrefix(param.Name, "git.") {
				parentParams = append(parentParams, param)

				// Create parent param
				param.Name = prefix + param.Name
				parentParams = append(parentParams, param)
				continue
			}
			if strings.HasPrefix(param.Name, "gerrit.") {
				parentParams = append(parentParams, param)
				continue
			}

			if param.Name == "payload" || strings.HasPrefix(param.Name, "cds.triggered") || strings.HasPrefix(param.Name, "cds.release") {
				// keep p.Name as is
			} else if strings.HasPrefix(param.Name, "cds.") {
				param.Name = strings.Replace(param.Name, "cds.", prefix, 1)
			}
			parentParams = append(parentParams, param)
		}
		params = append(params, parentParams...)
	}
	return params, nil
}

func getNodeRunBuildParameters(ctx context.Context, proj sdk.Project, wr *sdk.WorkflowRun, run *sdk.WorkflowNodeRun, runContext nodeRunContext) ([]sdk.Parameter, error) {
	ctx, end := telemetry.Span(ctx, "workflow.getNodeRunBuildParameters",
		telemetry.Tag(telemetry.TagWorkflow, wr.Workflow.Name),
		telemetry.Tag(telemetry.TagWorkflowRun, wr.Number),
		telemetry.Tag(telemetry.TagWorkflowNodeRun, run.ID),
	)
	defer end()

	// GET PARAMETER FROM NODE CONTEXT
	params, errparam := getBuildParameterFromNodeContext(proj, wr.Workflow, runContext, run.PipelineParameters, run.Payload, run.HookEvent)
	if errparam != nil {
		return nil, sdk.WrapError(errparam, "unable to compute node build parameters")
	}

	errm := &sdk.MultiError{}
	//override default parameters value
	tmp := sdk.ParametersToMap(params)
	if wr.Version != nil {
		tmp["cds.version"] = *wr.Version
	} else {
		tmp["cds.version"] = fmt.Sprintf("%d", run.Number)
	}
	tmp["cds.run"] = fmt.Sprintf("%d.%d", run.Number, run.SubNumber)
	tmp["cds.run.number"] = fmt.Sprintf("%d", run.Number)
	tmp["cds.run.subnumber"] = fmt.Sprintf("%d", run.SubNumber)

	if wr.Workflow.TemplateInstance != nil {
		tmp["cds.template.version"] = fmt.Sprintf("%d", wr.Workflow.TemplateInstance.WorkflowTemplateVersion)
	}

	_, next := telemetry.Span(ctx, "workflow.interpolate")
	params = make([]sdk.Parameter, 0, len(tmp))
	for k, v := range tmp {
		s, err := interpolate.Do(v, tmp)
		if err != nil {
			errm.Append(err)
			continue
		}
		sdk.AddParameter(&params, k, sdk.StringParameter, s)
	}
	next()

	if errm.IsEmpty() {
		return params, nil
	}

	return params, errm
}
<commit_msg>fix(api): use final status for pipeline parent parameter (#6164)<commit_after>package workflow

import (
	"context"
	"fmt"
	"strings"

	"github.com/fsamin/go-dump"

	"github.com/ovh/cds/sdk"
	"github.com/ovh/cds/sdk/interpolate"
	"github.com/ovh/cds/sdk/telemetry"
)

func getNodeJobRunParameters(j sdk.Job, run *sdk.WorkflowNodeRun, stage *sdk.Stage) ([]sdk.Parameter, *sdk.MultiError) {
	params := run.BuildParameters
	tmp := map[string]string{
		"cds.stage": stage.Name,
		"cds.job":   j.Action.Name,
	}
	errm := &sdk.MultiError{}

	for k, v := range tmp {
		s, err := interpolate.Do(v, tmp)
		if err != nil {
			errm.Append(err)
			continue
		}
		sdk.AddParameter(&params, k, sdk.StringParameter, s)
	}

	if errm.IsEmpty() {
		return params, nil
	}
	return params, errm
}

// getBuildParameterFromNodeContext returns the parameters compute from  node context (project, application,  pipeline, pyaload)
func getBuildParameterFromNodeContext(proj sdk.Project, w sdk.Workflow, runContext nodeRunContext, pipelineParameters []sdk.Parameter, payload interface{}, hookEvent *sdk.WorkflowNodeRunHookEvent) ([]sdk.Parameter, error) {
	tmpProj := sdk.ParametersFromProjectVariables(proj)
	vars := make(map[string]string, len(tmpProj))
	for k, v := range tmpProj {
		vars[k] = v
	}

	tmpProj = sdk.ParametersFromProjectKeys(proj)
	for k, v := range tmpProj {
		vars[k] = v
	}

	// COMPUTE APPLICATION VARIABLE
	if runContext.Application.ID != 0 {
		vars["cds.application"] = runContext.Application.Name
		tmp := sdk.ParametersFromApplicationVariables(runContext.Application)
		for k, v := range tmp {
			vars[k] = v
		}

		tmp = sdk.ParametersFromApplicationKeys(runContext.Application)
		for k, v := range tmp {
			vars[k] = v
		}
	}

	// COMPUTE ENVIRONMENT VARIABLE
	if runContext.Environment.ID != 0 {
		vars["cds.environment"] = runContext.Environment.Name
		tmp := sdk.ParametersFromEnvironmentVariables(runContext.Environment)
		for k, v := range tmp {
			vars[k] = v
		}
		tmp = sdk.ParametersFromEnvironmentKeys(runContext.Environment)
		for k, v := range tmp {
			vars[k] = v
		}
	}

	// COmpute integration from workflow
	for _, integ := range runContext.WorkflowProjectIntegrations {
		if !sdk.AllowIntegrationInVariable(integ.ProjectIntegration.Model) {
			continue
		}
		prefix := sdk.GetIntegrationVariablePrefix(integ.ProjectIntegration.Model)
		vars["cds.integration."+prefix] = integ.ProjectIntegration.Name
		tmp := sdk.ParametersFromIntegration(prefix, integ.ProjectIntegration.Config)
		for k, v := range tmp {
			vars[k] = v
		}

		tmpWkfConf := sdk.ParametersFromIntegration(prefix, integ.Config)
		for k, v := range tmpWkfConf {
			vars[k] = v
		}
	}

	// COMPUTE INTEGRATION VARIABLE FROM NODE CONTEXT
	for _, integ := range runContext.ProjectIntegrations {
		prefix := sdk.GetIntegrationVariablePrefix(integ.Model)
		vars["cds.integration."+prefix] = integ.Name
		tmp := sdk.ParametersFromIntegration(prefix, integ.Config)
		for k, v := range tmp {
			vars[k] = v
		}

		if integ.Model.Deployment {
			// COMPUTE DEPLOYMENT STRATEGIES VARIABLE
			if runContext.Application.ID != 0 {
				for pfName, pfConfig := range runContext.Application.DeploymentStrategies {
					if pfName == integ.Name {
						tmp := sdk.ParametersFromIntegration(prefix, pfConfig)
						for k, v := range tmp {
							vars[k] = v
						}
					}
				}
			}
		}
	}

	// COMPUTE PIPELINE PARAMETER
	tmpPip := sdk.ParametersFromPipelineParameters(pipelineParameters)
	for k, v := range tmpPip {
		vars[k] = v
	}

	// COMPUTE PAYLOAD
	e := dump.NewDefaultEncoder()

	e.Formatters = []dump.KeyFormatterFunc{dump.WithDefaultLowerCaseFormatter()}
	e.ExtraFields.DetailedMap = false
	e.ExtraFields.DetailedStruct = false
	e.ExtraFields.Len = false
	e.ExtraFields.Type = false
	tmpVars, errdump := e.ToStringMap(payload)
	if errdump != nil {
		return nil, sdk.WrapError(errdump, "do-dump error")
	}

	//Merge the dumped payload with vars
	vars = sdk.ParametersMapMerge(vars, tmpVars)

	vars["cds.project"] = w.ProjectKey
	vars["cds.workflow"] = w.Name
	vars["cds.pipeline"] = runContext.Pipeline.Name

	// COMPUTE VCS STRATEGY VARIABLE
	if runContext.Application.RepositoryStrategy.ConnectionType != "" {
		vars["git.connection.type"] = runContext.Application.RepositoryStrategy.ConnectionType
		if runContext.Application.RepositoryStrategy.SSHKey != "" {
			vars["git.ssh.key"] = runContext.Application.RepositoryStrategy.SSHKey
		}
		if runContext.Application.RepositoryStrategy.PGPKey != "" {
			vars["git.pgp.key"] = runContext.Application.RepositoryStrategy.PGPKey
		}
		if runContext.Application.RepositoryStrategy.User != "" {
			vars["git.http.user"] = runContext.Application.RepositoryStrategy.User
		}
		if runContext.Application.VCSServer != "" {
			vars["git.server"] = runContext.Application.VCSServer
		}
	} else {
		// remove vcs strategy variable
		delete(vars, "git.ssh.key")
		delete(vars, "git.pgp.key")
		delete(vars, "git.http.user")
	}

	if hookEvent != nil {
		vars["parent.project"] = hookEvent.ParentWorkflow.Key
		vars["parent.run"] = fmt.Sprintf("%d", hookEvent.ParentWorkflow.Run)
		vars["parent.workflow"] = hookEvent.ParentWorkflow.Name
		vars["parent.outgoinghook"] = hookEvent.WorkflowNodeHookUUID
	}

	params := make([]sdk.Parameter, 0)
	for k, v := range vars {
		sdk.AddParameter(&params, k, sdk.StringParameter, v)
	}

	return params, nil
}

func getParentParameters(w *sdk.WorkflowRun, nodeRuns []*sdk.WorkflowNodeRun) ([]sdk.Parameter, error) {
	params := make([]sdk.Parameter, 0, len(nodeRuns))
	for _, parentNodeRun := range nodeRuns {
		var nodeName string

		node := w.Workflow.WorkflowData.NodeByID(parentNodeRun.WorkflowNodeID)
		if node == nil {
			return nil, sdk.WithStack(fmt.Errorf("unable to find node %d in workflow", parentNodeRun.WorkflowNodeID))
		}
		nodeName = node.Name
		prefix := "workflow." + nodeName + "."

		parentParams := make([]sdk.Parameter, 0, len(parentNodeRun.BuildParameters))
		for _, param := range parentNodeRun.BuildParameters {
			if param.Name == "" || param.Name == "cds.semver" || param.Name == "cds.release.version" ||
				strings.HasPrefix(param.Name, "cds.proj") ||
				strings.HasPrefix(param.Name, "cds.version") || strings.HasPrefix(param.Name, "cds.run.number") ||
				strings.HasPrefix(param.Name, "cds.workflow") || strings.HasPrefix(param.Name, "job.requirement") {
				continue
			}

			if strings.HasPrefix(param.Name, "workflow.") {
				parentParams = append(parentParams, param)
				continue
			}

			// We inherite git variables is there is more than one repositories in the whole workflow
			if strings.HasPrefix(param.Name, "git.") {
				parentParams = append(parentParams, param)

				// Create parent param
				param.Name = prefix + param.Name
				parentParams = append(parentParams, param)
				continue
			}
			if strings.HasPrefix(param.Name, "gerrit.") {
				parentParams = append(parentParams, param)
				continue
			}

			if param.Name == "payload" || strings.HasPrefix(param.Name, "cds.triggered") || strings.HasPrefix(param.Name, "cds.release") {
				// keep p.Name as is
			} else if param.Name == "cds.status" {
				// do not use input status value for parent param
				continue
			} else if strings.HasPrefix(param.Name, "cds.") {
				param.Name = strings.Replace(param.Name, "cds.", prefix, 1)
			}
			parentParams = append(parentParams, param)
		}

		// inject parent final status as parameter
		parentParams = append(parentParams, sdk.Parameter{
			Name:  prefix + "status",
			Type:  sdk.StringParameter,
			Value: parentNodeRun.Status,
		})

		params = append(params, parentParams...)
	}
	return params, nil
}

func getNodeRunBuildParameters(ctx context.Context, proj sdk.Project, wr *sdk.WorkflowRun, run *sdk.WorkflowNodeRun, runContext nodeRunContext) ([]sdk.Parameter, error) {
	ctx, end := telemetry.Span(ctx, "workflow.getNodeRunBuildParameters",
		telemetry.Tag(telemetry.TagWorkflow, wr.Workflow.Name),
		telemetry.Tag(telemetry.TagWorkflowRun, wr.Number),
		telemetry.Tag(telemetry.TagWorkflowNodeRun, run.ID),
	)
	defer end()

	// GET PARAMETER FROM NODE CONTEXT
	params, errparam := getBuildParameterFromNodeContext(proj, wr.Workflow, runContext, run.PipelineParameters, run.Payload, run.HookEvent)
	if errparam != nil {
		return nil, sdk.WrapError(errparam, "unable to compute node build parameters")
	}

	errm := &sdk.MultiError{}
	//override default parameters value
	tmp := sdk.ParametersToMap(params)
	if wr.Version != nil {
		tmp["cds.version"] = *wr.Version
	} else {
		tmp["cds.version"] = fmt.Sprintf("%d", run.Number)
	}
	tmp["cds.run"] = fmt.Sprintf("%d.%d", run.Number, run.SubNumber)
	tmp["cds.run.number"] = fmt.Sprintf("%d", run.Number)
	tmp["cds.run.subnumber"] = fmt.Sprintf("%d", run.SubNumber)

	if wr.Workflow.TemplateInstance != nil {
		tmp["cds.template.version"] = fmt.Sprintf("%d", wr.Workflow.TemplateInstance.WorkflowTemplateVersion)
	}

	_, next := telemetry.Span(ctx, "workflow.interpolate")
	params = make([]sdk.Parameter, 0, len(tmp))
	for k, v := range tmp {
		s, err := interpolate.Do(v, tmp)
		if err != nil {
			errm.Append(err)
			continue
		}
		sdk.AddParameter(&params, k, sdk.StringParameter, s)
	}
	next()

	if errm.IsEmpty() {
		return params, nil
	}

	return params, errm
}
<|endoftext|>


<commit_before>// Package alloc provides a light-weight memory allocation mechanism.
package alloc

import (
	"io"
)

const (
	defaultOffset = 16
)

type BytesWriter func([]byte) int

// Buffer is a recyclable allocation of a byte array. Buffer.Release() recycles
// the buffer into an internal buffer pool, in order to recreate a buffer more
// quickly.
type Buffer struct {
	head []byte
	pool Pool

	start int
	end   int
}

func CreateBuffer(container []byte, parent Pool) *Buffer {
	b := new(Buffer)
	b.head = container
	b.pool = parent
	b.start = defaultOffset
	b.end = defaultOffset
	return b
}

// Release recycles the buffer into an internal buffer pool.
func (b *Buffer) Release() {
	if b == nil || b.head == nil {
		return
	}
	if b.pool != nil {
		b.pool.Free(b)
	}
	b.head = nil
	b.pool = nil
}

// Clear clears the content of the buffer, results an empty buffer with
// Len() = 0.
func (b *Buffer) Clear() {
	b.start = defaultOffset
	b.end = defaultOffset
}

// Reset resets this Buffer into its original state.
func (b *Buffer) Reset() {
	b.start = defaultOffset
	b.end = len(b.head)
}

// AppendBytes appends one or more bytes to the end of the buffer.
func (b *Buffer) AppendBytes(bytes ...byte) {
	b.Append(bytes)
}

// Append appends a byte array to the end of the buffer.
func (b *Buffer) Append(data []byte) {
	nBytes := copy(b.head[b.end:], data)
	b.end += nBytes
}

func (b *Buffer) AppendFunc(writer BytesWriter) {
	nBytes := writer(b.head[b.end:])
	b.end += nBytes
}

// Prepend prepends bytes in front of the buffer. Caller must ensure total bytes prepended is
// no more than 16 bytes.
func (b *Buffer) Prepend(data []byte) {
	b.SliceBack(len(data))
	copy(b.head[b.start:], data)
}

func (b *Buffer) PrependBytes(data ...byte) {
	b.Prepend(data)
}

func (b *Buffer) PrependFunc(offset int, writer BytesWriter) {
	b.SliceBack(offset)
	writer(b.head[b.start:])
}

func (b *Buffer) Byte(index int) byte {
	return b.head[b.start+index]
}

func (b *Buffer) SetByte(index int, value byte) {
	b.head[b.start+index] = value
}

// Bytes returns the content bytes of this Buffer.
func (b *Buffer) Bytes() []byte {
	return b.head[b.start:b.end]
}

func (b *Buffer) BytesRange(from, to int) []byte {
	if from < 0 {
		from += b.Len()
	}
	if to < 0 {
		to += b.Len()
	}
	return b.head[b.start+from : b.start+to]
}

func (b *Buffer) BytesFrom(from int) []byte {
	if from < 0 {
		from += b.Len()
	}
	return b.head[b.start+from : b.end]
}

func (b *Buffer) BytesTo(to int) []byte {
	if to < 0 {
		to += b.Len()
	}
	return b.head[b.start : b.start+to]
}

// Slice cuts the buffer at the given position.
func (b *Buffer) Slice(from, to int) {
	if from < 0 {
		from += b.Len()
	}
	if to < 0 {
		to += b.Len()
	}
	if to < from {
		panic("Invalid slice")
	}
	b.end = b.start + to
	b.start += from
}

// SliceFrom cuts the buffer at the given position.
func (b *Buffer) SliceFrom(from int) {
	if from < 0 {
		from += b.Len()
	}
	b.start += from
}

// SliceBack extends the Buffer to its front by offset bytes.
// Caller must ensure cumulated offset is no more than 16.
func (b *Buffer) SliceBack(offset int) {
	b.start -= offset
	if b.start < 0 {
		panic("Negative buffer offset.")
	}
}

// Len returns the length of the buffer content.
func (b *Buffer) Len() int {
	if b == nil {
		return 0
	}
	return b.end - b.start
}

func (b *Buffer) IsEmpty() bool {
	return b.Len() == 0
}

// IsFull returns true if the buffer has no more room to grow.
func (b *Buffer) IsFull() bool {
	return b.end == len(b.head)
}

// Write implements Write method in io.Writer.
func (b *Buffer) Write(data []byte) (int, error) {
	nBytes := copy(b.head[b.end:], data)
	b.end += nBytes
	return nBytes, nil
}

// Read implements io.Reader.Read().
func (b *Buffer) Read(data []byte) (int, error) {
	if b.Len() == 0 {
		return 0, io.EOF
	}
	nBytes := copy(data, b.head[b.start:b.end])
	if nBytes == b.Len() {
		b.Clear()
	} else {
		b.start += nBytes
	}
	return nBytes, nil
}

func (b *Buffer) FillFrom(reader io.Reader) (int, error) {
	nBytes, err := reader.Read(b.head[b.end:])
	b.end += nBytes
	return nBytes, err
}

func (b *Buffer) FillFullFrom(reader io.Reader, amount int) (int, error) {
	nBytes, err := io.ReadFull(reader, b.head[b.end:b.end+amount])
	b.end += nBytes
	return nBytes, err
}

func (b *Buffer) String() string {
	return string(b.head[b.start:b.end])
}

// NewBuffer creates a Buffer with 8K bytes of arbitrary content.
func NewBuffer() *Buffer {
	return mediumPool.Allocate()
}

func NewSmallBuffer() *Buffer {
	return smallPool.Allocate()
}

func NewLocalBuffer(size int) *Buffer {
	return CreateBuffer(make([]byte, size), nil)
}
<commit_msg>comments<commit_after>// Package alloc provides a light-weight memory allocation mechanism.
package alloc

import (
	"io"
)

const (
	defaultOffset = 16
)

// A Writer that writes contents into the given buffer.
type BytesWriter func([]byte) int

// Buffer is a recyclable allocation of a byte array. Buffer.Release() recycles
// the buffer into an internal buffer pool, in order to recreate a buffer more
// quickly.
type Buffer struct {
	head []byte
	pool Pool

	start int
	end   int
}

func CreateBuffer(container []byte, parent Pool) *Buffer {
	b := new(Buffer)
	b.head = container
	b.pool = parent
	b.start = defaultOffset
	b.end = defaultOffset
	return b
}

// Release recycles the buffer into an internal buffer pool.
func (b *Buffer) Release() {
	if b == nil || b.head == nil {
		return
	}
	if b.pool != nil {
		b.pool.Free(b)
	}
	b.head = nil
	b.pool = nil
}

// Clear clears the content of the buffer, results an empty buffer with
// Len() = 0.
func (b *Buffer) Clear() {
	b.start = defaultOffset
	b.end = defaultOffset
}

// Reset resets this Buffer into its original state.
func (b *Buffer) Reset() {
	b.start = defaultOffset
	b.end = len(b.head)
}

// AppendBytes appends one or more bytes to the end of the buffer.
func (b *Buffer) AppendBytes(bytes ...byte) {
	b.Append(bytes)
}

// Append appends a byte array to the end of the buffer.
func (b *Buffer) Append(data []byte) {
	nBytes := copy(b.head[b.end:], data)
	b.end += nBytes
}

func (b *Buffer) AppendFunc(writer BytesWriter) {
	nBytes := writer(b.head[b.end:])
	b.end += nBytes
}

// Prepend prepends bytes in front of the buffer. Caller must ensure total bytes prepended is
// no more than 16 bytes.
func (b *Buffer) Prepend(data []byte) {
	b.SliceBack(len(data))
	copy(b.head[b.start:], data)
}

func (b *Buffer) PrependBytes(data ...byte) {
	b.Prepend(data)
}

func (b *Buffer) PrependFunc(offset int, writer BytesWriter) {
	b.SliceBack(offset)
	writer(b.head[b.start:])
}

func (b *Buffer) Byte(index int) byte {
	return b.head[b.start+index]
}

func (b *Buffer) SetByte(index int, value byte) {
	b.head[b.start+index] = value
}

// Bytes returns the content bytes of this Buffer.
func (b *Buffer) Bytes() []byte {
	return b.head[b.start:b.end]
}

func (b *Buffer) BytesRange(from, to int) []byte {
	if from < 0 {
		from += b.Len()
	}
	if to < 0 {
		to += b.Len()
	}
	return b.head[b.start+from : b.start+to]
}

func (b *Buffer) BytesFrom(from int) []byte {
	if from < 0 {
		from += b.Len()
	}
	return b.head[b.start+from : b.end]
}

func (b *Buffer) BytesTo(to int) []byte {
	if to < 0 {
		to += b.Len()
	}
	return b.head[b.start : b.start+to]
}

// Slice cuts the buffer at the given position.
func (b *Buffer) Slice(from, to int) {
	if from < 0 {
		from += b.Len()
	}
	if to < 0 {
		to += b.Len()
	}
	if to < from {
		panic("Invalid slice")
	}
	b.end = b.start + to
	b.start += from
}

// SliceFrom cuts the buffer at the given position.
func (b *Buffer) SliceFrom(from int) {
	if from < 0 {
		from += b.Len()
	}
	b.start += from
}

// SliceBack extends the Buffer to its front by offset bytes.
// Caller must ensure cumulated offset is no more than 16.
func (b *Buffer) SliceBack(offset int) {
	b.start -= offset
	if b.start < 0 {
		panic("Negative buffer offset.")
	}
}

// Len returns the length of the buffer content.
func (b *Buffer) Len() int {
	if b == nil {
		return 0
	}
	return b.end - b.start
}

func (b *Buffer) IsEmpty() bool {
	return b.Len() == 0
}

// IsFull returns true if the buffer has no more room to grow.
func (b *Buffer) IsFull() bool {
	return b.end == len(b.head)
}

// Write implements Write method in io.Writer.
func (b *Buffer) Write(data []byte) (int, error) {
	nBytes := copy(b.head[b.end:], data)
	b.end += nBytes
	return nBytes, nil
}

// Read implements io.Reader.Read().
func (b *Buffer) Read(data []byte) (int, error) {
	if b.Len() == 0 {
		return 0, io.EOF
	}
	nBytes := copy(data, b.head[b.start:b.end])
	if nBytes == b.Len() {
		b.Clear()
	} else {
		b.start += nBytes
	}
	return nBytes, nil
}

func (b *Buffer) FillFrom(reader io.Reader) (int, error) {
	nBytes, err := reader.Read(b.head[b.end:])
	b.end += nBytes
	return nBytes, err
}

func (b *Buffer) FillFullFrom(reader io.Reader, amount int) (int, error) {
	nBytes, err := io.ReadFull(reader, b.head[b.end:b.end+amount])
	b.end += nBytes
	return nBytes, err
}

func (b *Buffer) String() string {
	return string(b.head[b.start:b.end])
}

// NewBuffer creates a Buffer with 8K bytes of arbitrary content.
func NewBuffer() *Buffer {
	return mediumPool.Allocate()
}

func NewSmallBuffer() *Buffer {
	return smallPool.Allocate()
}

func NewLocalBuffer(size int) *Buffer {
	return CreateBuffer(make([]byte, size), nil)
}
<|endoftext|>


<commit_before>package slack

import (
	"encoding/json"
)

// InteractionType type of interactions
type InteractionType string

// ActionType type represents the type of action (attachment, block, etc.)
type actionType string

// action is an interface that should be implemented by all callback action types
type action interface {
	actionType() actionType
}

// Types of interactions that can be received.
const (
	InteractionTypeDialogCancellation = InteractionType("dialog_cancellation")
	InteractionTypeDialogSubmission   = InteractionType("dialog_submission")
	InteractionTypeDialogSuggestion   = InteractionType("dialog_suggestion")
	InteractionTypeInteractionMessage = InteractionType("interactive_message")
	InteractionTypeMessageAction      = InteractionType("message_action")
)

// InteractionCallback is sent from slack when a user interactions with a button or dialog.
type InteractionCallback struct {
	Type            InteractionType `json:"type"`
	Token           string          `json:"token"`
	CallbackID      string          `json:"callback_id"`
	ResponseURL     string          `json:"response_url"`
	TriggerID       string          `json:"trigger_id"`
	ActionTs        string          `json:"action_ts"`
	Team            Team            `json:"team"`
	Channel         Channel         `json:"channel"`
	User            User            `json:"user"`
	OriginalMessage Message         `json:"original_message"`
	Message         Message         `json:"message"`
	Name            string          `json:"name"`
	Value           string          `json:"value"`
	MessageTs       string          `json:"message_ts"`
	AttachmentID    string          `json:"attachment_id"`
	ActionCallback  ActionCallbacks `json:"actions"`
	DialogSubmissionCallback
}

// ActionCallback is a convenience struct defined to allow dynamic unmarshalling of
// the "actions" value in Slack's JSON response, which varies depending on block type
type ActionCallbacks struct {
	AttachmentActions []*AttachmentAction
	BlockActions      []*BlockAction
}

// UnmarshalJSON implements the Marshaller interface in order to delegate
// marshalling and allow for proper type assertion when decoding the response
func (a *ActionCallbacks) UnmarshalJSON(data []byte) error {
	var raw []json.RawMessage
	err := json.Unmarshal(data, &raw)
	if err != nil {
		return err
	}

	for _, r := range raw {
		var obj map[string]interface{}
		err := json.Unmarshal(r, &obj)
		if err != nil {
			return err
		}

		if _, ok := obj["block_id"].(string); ok {
			action, err := unmarshalAction(r, &BlockAction{})
			if err != nil {
				return err
			}

			a.BlockActions = append(a.BlockActions, action.(*BlockAction))
			return nil
		}

		action, err := unmarshalAction(r, &AttachmentAction{})
		if err != nil {
			return err
		}
		a.AttachmentActions = append(a.AttachmentActions, action.(*AttachmentAction))
	}

	return nil
}

func unmarshalAction(r json.RawMessage, callbackAction action) (action, error) {
	err := json.Unmarshal(r, callbackAction)
	if err != nil {
		return nil, err
	}
	return callbackAction, nil
}
<commit_msg>Add new interaction type for block actions<commit_after>package slack

import (
	"encoding/json"
)

// InteractionType type of interactions
type InteractionType string

// ActionType type represents the type of action (attachment, block, etc.)
type actionType string

// action is an interface that should be implemented by all callback action types
type action interface {
	actionType() actionType
}

// Types of interactions that can be received.
const (
	InteractionTypeDialogCancellation = InteractionType("dialog_cancellation")
	InteractionTypeDialogSubmission   = InteractionType("dialog_submission")
	InteractionTypeDialogSuggestion   = InteractionType("dialog_suggestion")
	InteractionTypeInteractionMessage = InteractionType("interactive_message")
	InteractionTypeMessageAction      = InteractionType("message_action")
	InteractionTypeBlockActions       = InteractionType("block_actions")
)

// InteractionCallback is sent from slack when a user interactions with a button or dialog.
type InteractionCallback struct {
	Type            InteractionType `json:"type"`
	Token           string          `json:"token"`
	CallbackID      string          `json:"callback_id"`
	ResponseURL     string          `json:"response_url"`
	TriggerID       string          `json:"trigger_id"`
	ActionTs        string          `json:"action_ts"`
	Team            Team            `json:"team"`
	Channel         Channel         `json:"channel"`
	User            User            `json:"user"`
	OriginalMessage Message         `json:"original_message"`
	Message         Message         `json:"message"`
	Name            string          `json:"name"`
	Value           string          `json:"value"`
	MessageTs       string          `json:"message_ts"`
	AttachmentID    string          `json:"attachment_id"`
	ActionCallback  ActionCallbacks `json:"actions"`
	DialogSubmissionCallback
}

// ActionCallback is a convenience struct defined to allow dynamic unmarshalling of
// the "actions" value in Slack's JSON response, which varies depending on block type
type ActionCallbacks struct {
	AttachmentActions []*AttachmentAction
	BlockActions      []*BlockAction
}

// UnmarshalJSON implements the Marshaller interface in order to delegate
// marshalling and allow for proper type assertion when decoding the response
func (a *ActionCallbacks) UnmarshalJSON(data []byte) error {
	var raw []json.RawMessage
	err := json.Unmarshal(data, &raw)
	if err != nil {
		return err
	}

	for _, r := range raw {
		var obj map[string]interface{}
		err := json.Unmarshal(r, &obj)
		if err != nil {
			return err
		}

		if _, ok := obj["block_id"].(string); ok {
			action, err := unmarshalAction(r, &BlockAction{})
			if err != nil {
				return err
			}

			a.BlockActions = append(a.BlockActions, action.(*BlockAction))
			return nil
		}

		action, err := unmarshalAction(r, &AttachmentAction{})
		if err != nil {
			return err
		}
		a.AttachmentActions = append(a.AttachmentActions, action.(*AttachmentAction))
	}

	return nil
}

func unmarshalAction(r json.RawMessage, callbackAction action) (action, error) {
	err := json.Unmarshal(r, callbackAction)
	if err != nil {
		return nil, err
	}
	return callbackAction, nil
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"flag"
	"labix.org/v2/mgo"
	. "replay"
	"runtime"
	"sync/atomic"
	"time"
)

func panicOnError(err error) {
	if err != nil {
		panic(err)
	}
}

var (
	maxOps        int
	numSkipOps    int
	opsFilename   string
	sampleRate    float64
	socketTimeout int64
	startTime     int64
	style         string
	url           string
	verbose       bool
	workers       int
	stderr        string
	stdout        string
	logger        *Logger
)

const (
	// Set one minute timeout on mongo socket connections (nanoseconds) by default
	defaultMgoSocketTimeout = 60000000000
)

func init() {
	flag.StringVar(&opsFilename,
		"ops_filename",
		"",
		"The file for the serialized ops, generated by the record scripts.")
	flag.StringVar(&url,
		"url",
		"",
		"The database server's url, in the format of <host>[:<port>]")
	flag.StringVar(&style,
		"style",
		"",
		"How to replay the the ops. You can choose: \n"+
			"	stress: repaly ops at fast as possible\n"+
			"	real: repaly ops in accordance to ops' timestamps")
	flag.IntVar(&workers,
		"workers",
		10,
		"Number of workers that sends ops to database.")
	flag.IntVar(&maxOps,
		"maxOps",
		0,
		"[Optional] Maximal amount of ops to be replayed from the "+
			"ops_filename file. By setting it to `0`, replayer will "+
			"replay all the ops.")
	flag.IntVar(&numSkipOps,
		"numSkipOps",
		0,
		"[Optional] Skip first N ops. Useful for when the total ops in ops_filename"+
			" exceeds available memory and you're running in stress mode.")
	flag.Int64Var(&socketTimeout,
		"socketTimeout",
		defaultMgoSocketTimeout,
		"Mongo socket timeout in nanoseconds. Defaults to 60 seconds.")
	flag.Float64Var(&sampleRate,
		"sample_rate",
		0.1,
		"sample ops for latency")
	flag.BoolVar(&verbose,
		"verbose",
		false,
		"[Optional] Print op errors and other verbose information to stdout.")
	flag.Int64Var(&startTime,
		"start_time",
		0,
		"[Optional] Provide a unix timestamp (i.e. 1396456709419)"+
			"indicating the first op that you want to run. Otherwise, play from the top.")
	flag.StringVar(&stderr,
		"stderr",
		"",
		"error/warning log messages will go to stderr")
	flag.StringVar(&stdout,
		"stdout",
		"",
		"regular log messages will go to stderr")
}

func parseFlags() error {
	flag.Parse()
	if style != "stress" && style != "real" {
		return errors.New("Cannot recognize the style: " + style)
	}
	if workers <= 0 {
		return errors.New("`workers` should be a positive number")
	}
	if maxOps == 0 {
		maxOps = 4294967295
	}
	var err error
	if logger, err = NewLogger(stdout, stderr); err != nil {
		return err
	}
	return nil
}

func RetryOnSocketFailure(block func() error, session *mgo.Session) error {
	err := block()
	if err == nil {
		return nil
	}

	switch err.(type) {
	case *mgo.QueryError:
		return err
	case *mgo.LastError:
		return err
	}

	if err == mgo.ErrNotFound {
		return err
	}

	// Otherwise it's probably a socket error so we refresh the connection,
	// and try again
	session.Refresh()
	logger.Error("retrying mongo query after error: ", err)
	return block()
}

func makeOpsChan(style string, opsFilename string, logger *Logger) (chan *Op, error) {
	// Prepare to dispatch ops
	var (
		reader OpsReader
		err    error
	)

	if style == "stress" {
		err, reader = NewFileByLineOpsReader(opsFilename, logger)
		if err != nil {
			return nil, err
		}

		if startTime > 0 {
			if _, err = reader.SetStartTime(startTime); err != nil {
				return nil, err
			}
			return nil, err
		}
		if numSkipOps > 0 {
			if err := reader.SkipOps(numSkipOps); err != nil {
				return nil, err
			}
		}
		return NewBestEffortOpsDispatcher(reader, maxOps, logger), nil
	}

	// TODO NewCyclicOpsReader: do we really want to make it cyclic?
	reader = NewCyclicOpsReader(func() OpsReader {
		err, reader := NewFileByLineOpsReader(opsFilename, logger)
		panicOnError(err)
		return reader
	}, logger)

	if startTime > 0 {
		if _, err := reader.SetStartTime(startTime); err != nil {
			return nil, err
		}
	}
	if numSkipOps > 0 {
		if err := reader.SkipOps(numSkipOps); err != nil {
			return nil, err
		}
	}
	return NewByTimeOpsDispatcher(reader, maxOps, logger), nil
}

func main() {
	// Will enable system threads to make sure all cpus can be well utilized.
	runtime.GOMAXPROCS(runtime.NumCPU())
	err := parseFlags()
	panicOnError(err)
	defer logger.Close()

	opsChan, err := makeOpsChan(style, opsFilename, logger)
	panicOnError(err)

	latencyChan := make(chan Latency, workers)

	// Set up workers to do the job
	exit := make(chan int)
	opsExecuted := int64(0)
	fetch := func(id int, statsCollector IStatsCollector) {
		logger.Infof("Worker #%d report for duty\n", id)

		session, err := mgo.Dial(url)
		panicOnError(err)
		session.SetSocketTimeout(time.Duration(socketTimeout))

		defer session.Close()
		exec := OpsExecutorWithStats(session, statsCollector)
		for {
			op := <-opsChan
			if op == nil {
				break
			}
			block := func() error {
				err := exec.Execute(op)
				return err
			}
			err := RetryOnSocketFailure(block, session)
			if verbose == true && err != nil {
				logger.Error(err)
			}
			atomic.AddInt64(&opsExecuted, 1)
		}
		exit <- 1
		logger.Infof("Worker #%d done!\n", id)
	}
	statsCollectorList := make([]*StatsCollector, workers)
	for i := 0; i < workers; i++ {
		statsCollectorList[i] = NewStatsCollector()
		statsCollectorList[i].SampleLatencies(sampleRate, latencyChan)
		go fetch(i, statsCollectorList[i])
	}

	// Periodically report execution status
	go func() {
		statsAnalyzer := NewStatsAnalyzer(statsCollectorList, &opsExecuted,
			latencyChan, int(sampleRate*float64(maxOps)))
		toFloat := func(nano int64) float64 {
			return float64(nano) / float64(1e6)
		}
		report := func() {
			status := statsAnalyzer.GetStatus()
			logger.Infof("Executed %d ops, %.2f ops/sec", opsExecuted,
				status.OpsPerSec)
			for _, opType := range AllOpTypes {
				allTime := status.AllTimeLatencies[opType]
				sinceLast := status.SinceLastLatencies[opType]
				logger.Infof("  Op type: %s, count: %d, ops/sec: %.2f",
					opType, status.Counts[opType],
					status.TypeOpsSec[opType]*float64(workers))
				template := "   %s: P50: %.2fms, P70: %.2fms, P90: %.2fms, " +
					"P95 %.2fms, P99 %.2fms, Max %.2fms\n"
				logger.Infof(template, "Total", toFloat(allTime[P50]),
					toFloat(allTime[P70]), toFloat(allTime[P90]),
					toFloat(allTime[P95]), toFloat(allTime[P99]),
					toFloat(allTime[P100]))
				logger.Infof(template, "Last ", toFloat(sinceLast[P50]),
					toFloat(sinceLast[P70]), toFloat(sinceLast[P90]),
					toFloat(sinceLast[P95]), toFloat(sinceLast[P99]),
					toFloat(sinceLast[P100]))
			}
		}
		defer report()

		for opsExecuted < int64(maxOps) {
			time.Sleep(5 * time.Second)
			report()
		}
	}()

	// Wait for workers
	received := 0
	for received < workers {
		<-exit
		received += 1
	}
}
<commit_msg>make retryOnSocketFailure private<commit_after>package main

import (
	"errors"
	"flag"
	"labix.org/v2/mgo"
	. "replay"
	"runtime"
	"sync/atomic"
	"time"
)

func panicOnError(err error) {
	if err != nil {
		panic(err)
	}
}

var (
	maxOps        int
	numSkipOps    int
	opsFilename   string
	sampleRate    float64
	socketTimeout int64
	startTime     int64
	style         string
	url           string
	verbose       bool
	workers       int
	stderr        string
	stdout        string
	logger        *Logger
)

const (
	// Set one minute timeout on mongo socket connections (nanoseconds) by default
	defaultMgoSocketTimeout = 60000000000
)

func init() {
	flag.StringVar(&opsFilename,
		"ops_filename",
		"",
		"The file for the serialized ops, generated by the record scripts.")
	flag.StringVar(&url,
		"url",
		"",
		"The database server's url, in the format of <host>[:<port>]")
	flag.StringVar(&style,
		"style",
		"",
		"How to replay the the ops. You can choose: \n"+
			"	stress: repaly ops at fast as possible\n"+
			"	real: repaly ops in accordance to ops' timestamps")
	flag.IntVar(&workers,
		"workers",
		10,
		"Number of workers that sends ops to database.")
	flag.IntVar(&maxOps,
		"maxOps",
		0,
		"[Optional] Maximal amount of ops to be replayed from the "+
			"ops_filename file. By setting it to `0`, replayer will "+
			"replay all the ops.")
	flag.IntVar(&numSkipOps,
		"numSkipOps",
		0,
		"[Optional] Skip first N ops. Useful for when the total ops in ops_filename"+
			" exceeds available memory and you're running in stress mode.")
	flag.Int64Var(&socketTimeout,
		"socketTimeout",
		defaultMgoSocketTimeout,
		"Mongo socket timeout in nanoseconds. Defaults to 60 seconds.")
	flag.Float64Var(&sampleRate,
		"sample_rate",
		0.1,
		"sample ops for latency")
	flag.BoolVar(&verbose,
		"verbose",
		false,
		"[Optional] Print op errors and other verbose information to stdout.")
	flag.Int64Var(&startTime,
		"start_time",
		0,
		"[Optional] Provide a unix timestamp (i.e. 1396456709419)"+
			"indicating the first op that you want to run. Otherwise, play from the top.")
	flag.StringVar(&stderr,
		"stderr",
		"",
		"error/warning log messages will go to stderr")
	flag.StringVar(&stdout,
		"stdout",
		"",
		"regular log messages will go to stderr")
}

func parseFlags() error {
	flag.Parse()
	if style != "stress" && style != "real" {
		return errors.New("Cannot recognize the style: " + style)
	}
	if workers <= 0 {
		return errors.New("`workers` should be a positive number")
	}
	if maxOps == 0 {
		maxOps = 4294967295
	}
	var err error
	if logger, err = NewLogger(stdout, stderr); err != nil {
		return err
	}
	return nil
}

func retryOnSocketFailure(block func() error, session *mgo.Session) error {
	err := block()
	if err == nil {
		return nil
	}

	switch err.(type) {
	case *mgo.QueryError:
		return err
	case *mgo.LastError:
		return err
	}

	if err == mgo.ErrNotFound {
		return err
	}

	// Otherwise it's probably a socket error so we refresh the connection,
	// and try again
	session.Refresh()
	logger.Error("retrying mongo query after error: ", err)
	return block()
}

func makeOpsChan(style string, opsFilename string, logger *Logger) (chan *Op, error) {
	// Prepare to dispatch ops
	var (
		reader OpsReader
		err    error
	)

	if style == "stress" {
		err, reader = NewFileByLineOpsReader(opsFilename, logger)
		if err != nil {
			return nil, err
		}

		if startTime > 0 {
			if _, err = reader.SetStartTime(startTime); err != nil {
				return nil, err
			}
			return nil, err
		}
		if numSkipOps > 0 {
			if err := reader.SkipOps(numSkipOps); err != nil {
				return nil, err
			}
		}
		return NewBestEffortOpsDispatcher(reader, maxOps, logger), nil
	}

	// TODO NewCyclicOpsReader: do we really want to make it cyclic?
	reader = NewCyclicOpsReader(func() OpsReader {
		err, reader := NewFileByLineOpsReader(opsFilename, logger)
		panicOnError(err)
		return reader
	}, logger)

	if startTime > 0 {
		if _, err := reader.SetStartTime(startTime); err != nil {
			return nil, err
		}
	}
	if numSkipOps > 0 {
		if err := reader.SkipOps(numSkipOps); err != nil {
			return nil, err
		}
	}
	return NewByTimeOpsDispatcher(reader, maxOps, logger), nil
}

func main() {
	// Will enable system threads to make sure all cpus can be well utilized.
	runtime.GOMAXPROCS(runtime.NumCPU())
	err := parseFlags()
	panicOnError(err)
	defer logger.Close()

	opsChan, err := makeOpsChan(style, opsFilename, logger)
	panicOnError(err)

	latencyChan := make(chan Latency, workers)

	// Set up workers to do the job
	exit := make(chan int)
	opsExecuted := int64(0)
	fetch := func(id int, statsCollector IStatsCollector) {
		logger.Infof("Worker #%d report for duty\n", id)

		session, err := mgo.Dial(url)
		panicOnError(err)
		session.SetSocketTimeout(time.Duration(socketTimeout))

		defer session.Close()
		exec := OpsExecutorWithStats(session, statsCollector)
		for {
			op := <-opsChan
			if op == nil {
				break
			}
			block := func() error {
				err := exec.Execute(op)
				return err
			}
			err := retryOnSocketFailure(block, session)
			if verbose == true && err != nil {
				logger.Error(err)
			}
			atomic.AddInt64(&opsExecuted, 1)
		}
		exit <- 1
		logger.Infof("Worker #%d done!\n", id)
	}
	statsCollectorList := make([]*StatsCollector, workers)
	for i := 0; i < workers; i++ {
		statsCollectorList[i] = NewStatsCollector()
		statsCollectorList[i].SampleLatencies(sampleRate, latencyChan)
		go fetch(i, statsCollectorList[i])
	}

	// Periodically report execution status
	go func() {
		statsAnalyzer := NewStatsAnalyzer(statsCollectorList, &opsExecuted,
			latencyChan, int(sampleRate*float64(maxOps)))
		toFloat := func(nano int64) float64 {
			return float64(nano) / float64(1e6)
		}
		report := func() {
			status := statsAnalyzer.GetStatus()
			logger.Infof("Executed %d ops, %.2f ops/sec", opsExecuted,
				status.OpsPerSec)
			for _, opType := range AllOpTypes {
				allTime := status.AllTimeLatencies[opType]
				sinceLast := status.SinceLastLatencies[opType]
				logger.Infof("  Op type: %s, count: %d, ops/sec: %.2f",
					opType, status.Counts[opType],
					status.TypeOpsSec[opType]*float64(workers))
				template := "   %s: P50: %.2fms, P70: %.2fms, P90: %.2fms, " +
					"P95 %.2fms, P99 %.2fms, Max %.2fms\n"
				logger.Infof(template, "Total", toFloat(allTime[P50]),
					toFloat(allTime[P70]), toFloat(allTime[P90]),
					toFloat(allTime[P95]), toFloat(allTime[P99]),
					toFloat(allTime[P100]))
				logger.Infof(template, "Last ", toFloat(sinceLast[P50]),
					toFloat(sinceLast[P70]), toFloat(sinceLast[P90]),
					toFloat(sinceLast[P95]), toFloat(sinceLast[P99]),
					toFloat(sinceLast[P100]))
			}
		}
		defer report()

		for opsExecuted < int64(maxOps) {
			time.Sleep(5 * time.Second)
			report()
		}
	}()

	// Wait for workers
	received := 0
	for received < workers {
		<-exit
		received += 1
	}
}
<|endoftext|>