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

import (
	"bufio"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"
	"text/template"
)

// exists returns whether the given file or directory exists or not
func exists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}

func copyFile(src, dst string) error {
	r, err := os.Open(src)
	if err != nil {
		return err
	}
	defer r.Close()

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

	// do the actual work
	_, err = io.Copy(w, r)
	return err
}

// deployScaffold creates the directory structure for a new Go project, copies
// any required non-template files into it, and returns the path to the root
// dir of the newly-created project
func deployScaffold(t Target) (string, error) {
	gopath := os.Getenv("GOPATH")
	if gopath == "" {
		return "", errors.New("$GOPATH is not set")
	}

	if ex, err := exists(gopath); err != nil {
		return "", err
	} else if !ex {
		return "", fmt.Errorf("GOPATH does not exist at: %s", gopath)
	}

	fmt.Printf("GOPATH is: %s\n", gopath)
	// the "root" dir is at: $GOPATH/src/github.com/zulily/fizzbuzz
	root := path.Join(gopath, "src", t.Repository, t.Namespace, t.Project)

	if ex, err := exists(root); err != nil {
		return "", err
	} else if ex {
		return "", fmt.Errorf("%s already exists", root)
	}

	fmt.Printf("Creating a new project at: %s\n", root)

	if err := os.MkdirAll(path.Join(root, "build"), 0755); err != nil {
		return "", err
	}

	if err := copyFile("build/Dockerfile", path.Join(root, "build", "Dockerfile")); err != nil {
		return "", err
	}

	return root, nil
}

// deployTemplate parses and executues a template to a new file under the
// specified `root` dir. Template files are assumed to end with ".template".
// A templated file named `foo.template` will be placed at `root/foo`.
func deployTemplate(root, tmpl string, target Target) error {
	fm := template.FuncMap{
		"ToUpper": strings.ToUpper,
	}
	fname := tmpl[:len(tmpl)-len(".template")]
	fmt.Printf("Creating new: %s\n", fname)
	t := template.Must(template.New(tmpl).Funcs(fm).ParseFiles(tmpl))

	if f, err := os.Create(path.Join(root, fname)); err != nil {
		return err
	} else {
		defer f.Close()
		return t.Execute(f, target)
	}
}

// Target represents a Go build target; typically a binary
type Target struct {

	// Respository is the name of the source control repository (e.g. github.com)
	Repository string

	// Namespace is the name of the organization/group in the repository (e.g. zulily)
	Namespace string

	// Project is the name of the binary or package (e.g. fizzbuzz)
	Project string
}

var opts struct {
	verbose bool
	Target
}

func main() {

	flag.StringVar(&opts.Repository, "repository", "", "the name of the git repository (e.g. github.com)")
	flag.StringVar(&opts.Namespace, "namespace", "", "the name of the organization/group in the repository (e.g. zulily)")
	flag.StringVar(&opts.Project, "project", "", "the name of the project (e.g. fizzbuzz)")
	flag.BoolVar(&opts.verbose, "verbose", false, "toggles verbose output")
	flag.Parse()
	scanner := bufio.NewScanner(os.Stdin)

	if opts.Repository == "" {
		fmt.Printf("Enter the name of git repository (e.g. github.com): ")
		if scanner.Scan() {
			opts.Repository = scanner.Text()
		}
	}

	if opts.Namespace == "" {
		fmt.Printf("Enter the namespace in the repository (e.g. zulily): ")
		if scanner.Scan() {
			opts.Namespace = scanner.Text()
		}
	}

	if opts.Project == "" {
		fmt.Printf("Enter the name of the project (e.g. fizzbuzz): ")
		if scanner.Scan() {
			opts.Project = scanner.Text()
		}
	}

	root, err := deployScaffold(opts.Target)
	if err != nil {
		panic(err)
	}

	if files, err := filepath.Glob("*.template"); err == nil {
		for _, templ := range files {
			if err = deployTemplate(root, templ, opts.Target); err != nil {
				panic(err)
			}
		}
	} else {
		panic(err)
	}

	out := ioutil.Discard
	if opts.verbose {
		out = os.Stdout
	}

	fmt.Println("Initializing git repo")
	c := exec.Command("git", "init")
	c.Dir = root
	c.Stdout, c.Stderr = out, out
	if err = c.Run(); err != nil {
		panic(err)
	}

	fmt.Println("Initializing godeps")
	c = exec.Command("make", "godep")
	c.Dir = root
	c.Stdout, c.Stderr = out, out
	if err = c.Run(); err != nil {
		panic(err)
	}

	fmt.Println("Done")
}
<commit_msg>Allows boilerplating of existing projects w/ user confirmation<commit_after>/*
 * Copyright (C) 2015 zulily, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package main

import (
	"bufio"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"
	"text/template"
)

// exists returns whether the given file or directory exists or not
func exists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}

func copyFile(src, dst string) error {
	r, err := os.Open(src)
	if err != nil {
		return err
	}
	defer r.Close()

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

	// do the actual work
	_, err = io.Copy(w, r)
	return err
}

// deployScaffold creates the directory structure for a new Go project, copies
// any required non-template files into it, and returns the path to the root
// dir of the newly-created project
func deployScaffold(t Target) (string, error) {
	gopath := os.Getenv("GOPATH")
	if gopath == "" {
		return "", errors.New("$GOPATH is not set")
	}

	if ex, err := exists(gopath); err != nil {
		return "", err
	} else if !ex {
		return "", fmt.Errorf("GOPATH does not exist at: %s", gopath)
	}

	fmt.Printf("GOPATH is: %s\n", gopath)
	// the "root" dir is at: $GOPATH/src/github.com/zulily/fizzbuzz
	root := path.Join(gopath, "src", t.Repository, t.Namespace, t.Project)

	if ex, err := exists(root); err != nil {
		return "", err
	} else if ex {
		fmt.Printf("%s already exists. Overwrite existing files? [y/n]: ", root)
		reader := bufio.NewReader(os.Stdin)
		if text, err := reader.ReadString('\n'); err != nil {
			return "", err
		} else if !strings.EqualFold(strings.TrimSpace(text), "y") {
			return "", fmt.Errorf("%s already exists", root)
		}
	}

	fmt.Printf("Boilerplating the project at: %s\n", root)

	if err := os.MkdirAll(path.Join(root, "build"), 0755); err != nil {
		return "", err
	}

	if err := copyFile("build/Dockerfile", path.Join(root, "build", "Dockerfile")); err != nil {
		return "", err
	}

	return root, nil
}

// deployTemplate parses and executues a template to a new file under the
// specified `root` dir. Template files are assumed to end with ".template".
// A templated file named `foo.template` will be placed at `root/foo`.
func deployTemplate(root, tmpl string, target Target) error {
	fm := template.FuncMap{
		"ToUpper": strings.ToUpper,
	}
	fname := tmpl[:len(tmpl)-len(".template")]
	fmt.Printf("Creating new: %s\n", fname)
	t := template.Must(template.New(tmpl).Funcs(fm).ParseFiles(tmpl))

	if f, err := os.Create(path.Join(root, fname)); err != nil {
		return err
	} else {
		defer f.Close()
		return t.Execute(f, target)
	}
}

// Target represents a Go build target; typically a binary
type Target struct {

	// Respository is the name of the source control repository (e.g. github.com)
	Repository string

	// Namespace is the name of the organization/group in the repository (e.g. zulily)
	Namespace string

	// Project is the name of the binary or package (e.g. fizzbuzz)
	Project string
}

var opts struct {
	verbose bool
	Target
}

func main() {

	flag.StringVar(&opts.Repository, "repository", "", "the name of the git repository (e.g. github.com)")
	flag.StringVar(&opts.Namespace, "namespace", "", "the name of the organization/group in the repository (e.g. zulily)")
	flag.StringVar(&opts.Project, "project", "", "the name of the project (e.g. fizzbuzz)")
	flag.BoolVar(&opts.verbose, "verbose", false, "toggles verbose output")
	flag.Parse()
	scanner := bufio.NewScanner(os.Stdin)

	if opts.Repository == "" {
		fmt.Printf("Enter the name of git repository (e.g. github.com): ")
		if scanner.Scan() {
			opts.Repository = scanner.Text()
		}
	}

	if opts.Namespace == "" {
		fmt.Printf("Enter the namespace in the repository (e.g. zulily): ")
		if scanner.Scan() {
			opts.Namespace = scanner.Text()
		}
	}

	if opts.Project == "" {
		fmt.Printf("Enter the name of the project (e.g. fizzbuzz): ")
		if scanner.Scan() {
			opts.Project = scanner.Text()
		}
	}

	root, err := deployScaffold(opts.Target)
	if err != nil {
		panic(err)
	}

	if files, err := filepath.Glob("*.template"); err == nil {
		for _, templ := range files {
			if err = deployTemplate(root, templ, opts.Target); err != nil {
				panic(err)
			}
		}
	} else {
		panic(err)
	}

	out := ioutil.Discard
	if opts.verbose {
		out = os.Stdout
	}

	fmt.Println("Initializing git repo")
	c := exec.Command("git", "init")
	c.Dir = root
	c.Stdout, c.Stderr = out, out
	if err = c.Run(); err != nil {
		panic(err)
	}

	fmt.Println("Initializing godeps")
	c = exec.Command("make", "godep")
	c.Dir = root
	c.Stdout, c.Stderr = out, out
	if err = c.Run(); err != nil {
		panic(err)
	}

	fmt.Println("Done")
}
<|endoftext|>


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

// Package opencensus contains Go support for OpenCensus.
package opencensus // import "go.opencensus.io"

// Version is the current release version of OpenCensus in use.
func Version() string {
	return "0.23.0"
}
<commit_msg>Bump version to next expected release (#1257)<commit_after>// Copyright 2017, OpenCensus Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package opencensus contains Go support for OpenCensus.
package opencensus // import "go.opencensus.io"

// Version is the current release version of OpenCensus in use.
func Version() string {
	return "0.24.0"
}
<|endoftext|>


<commit_before>package sirius

type Extension interface {
	Run(Message) (error, MessageAction)
}

type CfgExtension interface {
	Run(Message, ExtensionConfig) (error, MessageAction)
}

type cfg_int int
type cfg_bool bool
type cfg_float float64
type cfg_list []string

type EID string

type ExtensionLoader interface {
	Load(EID) Extension
}

type ExtensionConfig map[string]interface{}

type InvalidConfig struct {
	Key string
	msg string
}

func (ic InvalidConfig) Error() string {
	return ic.msg
}

func (cfg ExtensionConfig) read(key string, def interface{}) interface{} {
	if val, ok := cfg[key]; ok {
		return val
	}

	return def
}

func (cfg ExtensionConfig) int(key string, def int) cfg_int {
	if val, ok := cfg[key]; ok {
		switch b := val.(type) {
		case int:
			return cfg_int(b)
		}
	}

	return cfg_int(def)
}

func (cfg ExtensionConfig) bool(key string, def bool) cfg_bool {
	if val, ok := cfg[key]; ok {
		switch b := val.(type) {
		case bool:
			return cfg_bool(b)
		case int:
			// Require explicit 0 or 1
			if b == 0 {
				return cfg_bool(false)
			} else if b == 1 {
				return cfg_bool(true)
			}
		}
	}

	return cfg_bool(def)
}

func (cfg ExtensionConfig) float(key string, def float64) cfg_float {
	if val, ok := cfg[key]; ok {
		switch f := val.(type) {
		case float32:
			return cfg_float(f)
		case float64:
			return cfg_float(f)
		}
	}

	return cfg_float(def)
}

func (cfg ExtensionConfig) list(key string, def []string) cfg_list {
	if val, ok := cfg[key]; ok {
		switch l := val.(type) {
		case []string:
			return cfg_list(l)
		}

	}

	return def
}
<commit_msg>Don’t use reserved words as method names<commit_after>package sirius

type Extension interface {
	Run(Message) (error, MessageAction)
}

type CfgExtension interface {
	Run(Message, ExtensionConfig) (error, MessageAction)
}

type cfg_int int
type cfg_bool bool
type cfg_float float64
type cfg_list []string

type EID string

type ExtensionLoader interface {
	Load(EID) Extension
}

type ExtensionConfig map[string]interface{}

type InvalidConfig struct {
	Key string
	msg string
}

func (ic InvalidConfig) Error() string {
	return ic.msg
}

func (cfg ExtensionConfig) read(key string, def interface{}) interface{} {
	if val, ok := cfg[key]; ok {
		return val
	}

	return def
}

func (cfg ExtensionConfig) integer(key string, def int) cfg_int {
	if val, ok := cfg[key]; ok {
		switch b := val.(type) {
		case int:
			return cfg_int(b)
		}
	}

	return cfg_int(def)
}

func (cfg ExtensionConfig) boolean(key string, def bool) cfg_bool {
	if val, ok := cfg[key]; ok {
		switch b := val.(type) {
		case bool:
			return cfg_bool(b)
		case int:
			// Require explicit 0 or 1
			if b == 0 {
				return cfg_bool(false)
			} else if b == 1 {
				return cfg_bool(true)
			}
		}
	}

	return cfg_bool(def)
}

func (cfg ExtensionConfig) float(key string, def float64) cfg_float {
	if val, ok := cfg[key]; ok {
		switch f := val.(type) {
		case float32:
			return cfg_float(f)
		case float64:
			return cfg_float(f)
		}
	}

	return cfg_float(def)
}

func (cfg ExtensionConfig) list(key string, def []string) cfg_list {
	if val, ok := cfg[key]; ok {
		switch l := val.(type) {
		case []string:
			return cfg_list(l)
		}

	}

	return def
}
<|endoftext|>


<commit_before>package sirius

type Extension interface {
	Run(Message) (error, MessageAction)
}

type cfg_int int
type cfg_bool bool
type cfg_float float64
type cfg_list []string

type EID string
type ExtensionLoader interface {
	Load(EID) Extension
}

type ExtensionConfig map[string]interface{}

type InvalidConfig struct {
	Key string
	msg string
}

func (ic InvalidConfig) Error() string {
	return ic.msg
}

func (cfg ExtensionConfig) read(key string, def interface{}) interface{} {
	if val, ok := cfg[key]; ok {
		return val
	}

	return def
}

func (cfg ExtensionConfig) int(key string, def int) cfg_int {
	if val, ok := cfg[key]; ok {
		switch b := val.(type) {
		case int:
			return cfg_int(b)
		}
	}

	return cfg_int(def)
}

func (cfg ExtensionConfig) bool(key string, def bool) cfg_bool {
	if val, ok := cfg[key]; ok {
		switch b := val.(type) {
		case bool:
			return cfg_bool(b)
		case int:
			// Require explicit 0 or 1
			if b == 0 {
				return cfg_bool(false)
			} else if b == 1 {
				return cfg_bool(true)
			}
		}
	}

	return cfg_bool(def)
}

func (cfg ExtensionConfig) float(key string, def float64) cfg_float {
	if val, ok := cfg[key]; ok {
		switch f := val.(type) {
		case float32:
			return cfg_float(f)
		case float64:
			return cfg_float(f)
		}
	}

	return cfg_float(def)
}

func (cfg ExtensionConfig) list(key string, def []string) cfg_list {
	if val, ok := cfg[key]; ok {
		switch l := val.(type) {
		case []string:
			return cfg_list(l)
		}

	}

	return def
}
<commit_msg>Add whitespace<commit_after>package sirius

type Extension interface {
	Run(Message) (error, MessageAction)
}

type cfg_int int
type cfg_bool bool
type cfg_float float64
type cfg_list []string

type EID string

type ExtensionLoader interface {
	Load(EID) Extension
}

type ExtensionConfig map[string]interface{}

type InvalidConfig struct {
	Key string
	msg string
}

func (ic InvalidConfig) Error() string {
	return ic.msg
}

func (cfg ExtensionConfig) read(key string, def interface{}) interface{} {
	if val, ok := cfg[key]; ok {
		return val
	}

	return def
}

func (cfg ExtensionConfig) int(key string, def int) cfg_int {
	if val, ok := cfg[key]; ok {
		switch b := val.(type) {
		case int:
			return cfg_int(b)
		}
	}

	return cfg_int(def)
}

func (cfg ExtensionConfig) bool(key string, def bool) cfg_bool {
	if val, ok := cfg[key]; ok {
		switch b := val.(type) {
		case bool:
			return cfg_bool(b)
		case int:
			// Require explicit 0 or 1
			if b == 0 {
				return cfg_bool(false)
			} else if b == 1 {
				return cfg_bool(true)
			}
		}
	}

	return cfg_bool(def)
}

func (cfg ExtensionConfig) float(key string, def float64) cfg_float {
	if val, ok := cfg[key]; ok {
		switch f := val.(type) {
		case float32:
			return cfg_float(f)
		case float64:
			return cfg_float(f)
		}
	}

	return cfg_float(def)
}

func (cfg ExtensionConfig) list(key string, def []string) cfg_list {
	if val, ok := cfg[key]; ok {
		switch l := val.(type) {
		case []string:
			return cfg_list(l)
		}

	}

	return def
}
<|endoftext|>


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

package backend

import (
	"context"
	"encoding/json"
	"net/http"
	"time"

	"github.com/coreos/etcdlabs/pkg/ratelimit"
	humanize "github.com/dustin/go-humanize"
)

var (
	// limit for manual fetch
	fetchMetricsRequestIntervalLimit = 10 * time.Second
	fetchMetricsLimiter              ratelimit.RequestLimiter
)

// TesterStatus wraps metrics.TesterStatus.
type TesterStatus struct {
	Name          string
	TotalCase     int64
	CurrentCase   int64
	CurrentFailed int64
}

// MetricsResponse translates client's GET response in frontend-friendly format.
type MetricsResponse struct {
	Success    bool
	Result     string
	LastUpdate string
	Statuses   []TesterStatus
}

// fetchMetricsRequestHandler handles fetch metrics requests.
func fetchMetricsRequestHandler(ctx context.Context, w http.ResponseWriter, req *http.Request) error {
	switch req.Method {
	case "GET":
		mresp := MetricsResponse{Success: true}
		if rmsg, ok := fetchMetricsLimiter.Check(); !ok {
			mresp.Success = false
			mresp.Result = "fetch metrics request " + rmsg
			mresp.LastUpdate = humanize.Time(globalMetrics.GetLastUpdate())
			return json.NewEncoder(w).Encode(mresp)
		}
		fetchMetricsLimiter.Advance()

		if globalMetrics == nil {
			mresp.Success = false
			mresp.Result = "metrics is disabled"
			return json.NewEncoder(w).Encode(mresp)
		}

		if err := globalMetrics.Sync(); err != nil {
			mresp.Success = false
			mresp.Result = "fetch metrics request " + err.Error()
		} else {
			mresp.Success = true
		}
		for _, status := range globalMetrics.Get() {
			mresp.Statuses = append(mresp.Statuses, TesterStatus{
				Name:          status.Name,
				TotalCase:     status.TotalCase,
				CurrentCase:   status.CurrentCase,
				CurrentFailed: status.CurrentFailed,
			})
		}

		mresp.LastUpdate = humanize.Time(globalMetrics.GetLastUpdate())
		return json.NewEncoder(w).Encode(mresp)

	default:
		http.Error(w, "Method Not Allowed", 405)
	}

	return nil
}
<commit_msg>backend: serve stale status data when rate-limit exceeded<commit_after>// Copyright 2016 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package backend

import (
	"context"
	"encoding/json"
	"net/http"
	"time"

	"github.com/coreos/etcdlabs/pkg/ratelimit"
	humanize "github.com/dustin/go-humanize"
)

var (
	// limit for manual fetch
	fetchMetricsRequestIntervalLimit = 10 * time.Second
	fetchMetricsLimiter              ratelimit.RequestLimiter
)

// TesterStatus wraps metrics.TesterStatus.
type TesterStatus struct {
	Name          string
	TotalCase     int64
	CurrentCase   int64
	CurrentFailed int64
}

// MetricsResponse translates client's GET response in frontend-friendly format.
type MetricsResponse struct {
	Success    bool
	Result     string
	LastUpdate string
	Statuses   []TesterStatus
}

// fetchMetricsRequestHandler handles fetch metrics requests.
func fetchMetricsRequestHandler(ctx context.Context, w http.ResponseWriter, req *http.Request) error {
	switch req.Method {
	case "GET":
		mresp := MetricsResponse{Success: true}
		if rmsg, ok := fetchMetricsLimiter.Check(); !ok { // rate limit exceeded
			mresp.Success = false
			mresp.Result = "fetch metrics request " + rmsg
			mresp.LastUpdate = humanize.Time(globalMetrics.GetLastUpdate())
			for _, status := range globalMetrics.Get() { // serve stale status
				mresp.Statuses = append(mresp.Statuses, TesterStatus{
					Name:          status.Name,
					TotalCase:     status.TotalCase,
					CurrentCase:   status.CurrentCase,
					CurrentFailed: status.CurrentFailed,
				})
			}
			return json.NewEncoder(w).Encode(mresp)
		}
		fetchMetricsLimiter.Advance()

		if globalMetrics == nil {
			mresp.Success = false
			mresp.Result = "metrics is disabled"
			return json.NewEncoder(w).Encode(mresp)
		}

		if err := globalMetrics.Sync(); err != nil { // connection error
			mresp.Success = false
			mresp.Result = "fetch metrics request " + err.Error()
		} else {
			mresp.Success = true
		}
		for _, status := range globalMetrics.Get() {
			mresp.Statuses = append(mresp.Statuses, TesterStatus{
				Name:          status.Name,
				TotalCase:     status.TotalCase,
				CurrentCase:   status.CurrentCase,
				CurrentFailed: status.CurrentFailed,
			})
		}

		mresp.LastUpdate = humanize.Time(globalMetrics.GetLastUpdate())
		return json.NewEncoder(w).Encode(mresp)

	default:
		http.Error(w, "Method Not Allowed", 405)
	}

	return nil
}
<|endoftext|>


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

package cipher_test

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"io"
	"os"
)

func ExampleNewCBCDecrypter() {
	key := []byte("example key 1234")
	ciphertext, _ := hex.DecodeString("f363f3ccdcb12bb883abf484ba77d9cd7d32b5baecb3d4b1b3e0e4beffdb3ded")

	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err)
	}

	// The IV needs to be unique, but not secure. Therefore it's common to
	// include it at the beginning of the ciphertext.
	if len(ciphertext) < aes.BlockSize {
		panic("ciphertext too short")
	}
	iv := ciphertext[:aes.BlockSize]
	ciphertext = ciphertext[aes.BlockSize:]

	// CBC mode always works in whole blocks.
	if len(ciphertext)%aes.BlockSize != 0 {
		panic("ciphertext is not a multiple of the block size")
	}

	mode := cipher.NewCBCDecrypter(block, iv)

	// CryptBlocks can work in-place if the two arguments are the same.
	mode.CryptBlocks(ciphertext, ciphertext)

	// If the original plaintext lengths are not a multiple of the block
	// size, padding would have to be added when encrypting, which would be
	// removed at this point. For an example, see
	// https://tools.ietf.org/html/rfc5246#section-6.2.3.2. However, it's
	// critical to note that ciphertexts must be authenticated (i.e. by
	// using crypto/hmac) before being decrypted in order to avoid creating
	// a padding oracle.

	fmt.Printf("%s\n", ciphertext)
	// Output: exampleplaintext
}

func ExampleNewCBCEncrypter() {
	key := []byte("example key 1234")
	plaintext := []byte("exampleplaintext")

	// CBC mode works on blocks so plaintexts may need to be padded to the
	// next whole block. For an example of such padding, see
	// https://tools.ietf.org/html/rfc5246#section-6.2.3.2. Here we'll
	// assume that the plaintext is already of the correct length.
	if len(plaintext)%aes.BlockSize != 0 {
		panic("plaintext is not a multiple of the block size")
	}

	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err)
	}

	// The IV needs to be unique, but not secure. Therefore it's common to
	// include it at the beginning of the ciphertext.
	ciphertext := make([]byte, aes.BlockSize+len(plaintext))
	iv := ciphertext[:aes.BlockSize]
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		panic(err)
	}

	mode := cipher.NewCBCEncrypter(block, iv)
	mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)

	// It's important to remember that ciphertexts must be authenticated
	// (i.e. by using crypto/hmac) as well as being encrypted in order to
	// be secure.

	fmt.Printf("%x\n", ciphertext)
}

func ExampleNewCFBDecrypter() {
	key := []byte("example key 1234")
	ciphertext, _ := hex.DecodeString("22277966616d9bc47177bd02603d08c9a67d5380d0fe8cf3b44438dff7b9")

	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err)
	}

	// The IV needs to be unique, but not secure. Therefore it's common to
	// include it at the beginning of the ciphertext.
	if len(ciphertext) < aes.BlockSize {
		panic("ciphertext too short")
	}
	iv := ciphertext[:aes.BlockSize]
	ciphertext = ciphertext[aes.BlockSize:]

	stream := cipher.NewCFBDecrypter(block, iv)

	// XORKeyStream can work in-place if the two arguments are the same.
	stream.XORKeyStream(ciphertext, ciphertext)
	fmt.Printf("%s", ciphertext)
	// Output: some plaintext
}

func ExampleNewCFBEncrypter() {
	key := []byte("example key 1234")
	plaintext := []byte("some plaintext")

	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err)
	}

	// The IV needs to be unique, but not secure. Therefore it's common to
	// include it at the beginning of the ciphertext.
	ciphertext := make([]byte, aes.BlockSize+len(plaintext))
	iv := ciphertext[:aes.BlockSize]
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		panic(err)
	}

	stream := cipher.NewCFBEncrypter(block, iv)
	stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)

	// It's important to remember that ciphertexts must be authenticated
	// (i.e. by using crypto/hmac) as well as being encrypted in order to
	// be secure.
}

func ExampleNewCTR() {
	key := []byte("example key 1234")
	plaintext := []byte("some plaintext")

	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err)
	}

	// The IV needs to be unique, but not secure. Therefore it's common to
	// include it at the beginning of the ciphertext.
	ciphertext := make([]byte, aes.BlockSize+len(plaintext))
	iv := ciphertext[:aes.BlockSize]
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		panic(err)
	}

	stream := cipher.NewCTR(block, iv)
	stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)

	// It's important to remember that ciphertexts must be authenticated
	// (i.e. by using crypto/hmac) as well as being encrypted in order to
	// be secure.

	// CTR mode is the same for both encryption and decryption, so we can
	// also decrypt that ciphertext with NewCTR.

	plaintext2 := make([]byte, len(plaintext))
	stream = cipher.NewCTR(block, iv)
	stream.XORKeyStream(plaintext2, ciphertext[aes.BlockSize:])

	fmt.Printf("%s\n", plaintext2)
	// Output: some plaintext
}

func ExampleNewOFB() {
	key := []byte("example key 1234")
	plaintext := []byte("some plaintext")

	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err)
	}

	// The IV needs to be unique, but not secure. Therefore it's common to
	// include it at the beginning of the ciphertext.
	ciphertext := make([]byte, aes.BlockSize+len(plaintext))
	iv := ciphertext[:aes.BlockSize]
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		panic(err)
	}

	stream := cipher.NewOFB(block, iv)
	stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)

	// It's important to remember that ciphertexts must be authenticated
	// (i.e. by using crypto/hmac) as well as being encrypted in order to
	// be secure.

	// OFB mode is the same for both encryption and decryption, so we can
	// also decrypt that ciphertext with NewOFB.

	plaintext2 := make([]byte, len(plaintext))
	stream = cipher.NewOFB(block, iv)
	stream.XORKeyStream(plaintext2, ciphertext[aes.BlockSize:])

	fmt.Printf("%s\n", plaintext2)
	// Output: some plaintext
}

func ExampleStreamReader() {
	key := []byte("example key 1234")

	inFile, err := os.Open("encrypted-file")
	if err != nil {
		panic(err)
	}
	defer inFile.Close()

	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err)
	}

	// If the key is unique for each ciphertext, then it's ok to use a zero
	// IV.
	var iv [aes.BlockSize]byte
	stream := cipher.NewOFB(block, iv[:])

	outFile, err := os.OpenFile("decrypted-file", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		panic(err)
	}
	defer outFile.Close()

	reader := &cipher.StreamReader{S: stream, R: inFile}
	// Copy the input file to the output file, decrypting as we go.
	if _, err := io.Copy(outFile, reader); err != nil {
		panic(err)
	}

	// Note that this example is simplistic in that it omits any
	// authentication of the encrypted data. It you were actually to use
	// StreamReader in this manner, an attacker could flip arbitrary bits in
	// the output.
}

func ExampleStreamWriter() {
	key := []byte("example key 1234")

	inFile, err := os.Open("plaintext-file")
	if err != nil {
		panic(err)
	}
	defer inFile.Close()

	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err)
	}

	// If the key is unique for each ciphertext, then it's ok to use a zero
	// IV.
	var iv [aes.BlockSize]byte
	stream := cipher.NewOFB(block, iv[:])

	outFile, err := os.OpenFile("encrypted-file", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		panic(err)
	}
	defer outFile.Close()

	writer := &cipher.StreamWriter{S: stream, W: outFile}
	// Copy the input file to the output file, encrypting as we go.
	if _, err := io.Copy(writer, inFile); err != nil {
		panic(err)
	}

	// Note that this example is simplistic in that it omits any
	// authentication of the encrypted data. It you were actually to use
	// StreamReader in this manner, an attacker could flip arbitrary bits in
	// the decrypted result.
}
<commit_msg>crypto/cipher: Fix typo in example comment<commit_after>// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package cipher_test

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"io"
	"os"
)

func ExampleNewCBCDecrypter() {
	key := []byte("example key 1234")
	ciphertext, _ := hex.DecodeString("f363f3ccdcb12bb883abf484ba77d9cd7d32b5baecb3d4b1b3e0e4beffdb3ded")

	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err)
	}

	// The IV needs to be unique, but not secure. Therefore it's common to
	// include it at the beginning of the ciphertext.
	if len(ciphertext) < aes.BlockSize {
		panic("ciphertext too short")
	}
	iv := ciphertext[:aes.BlockSize]
	ciphertext = ciphertext[aes.BlockSize:]

	// CBC mode always works in whole blocks.
	if len(ciphertext)%aes.BlockSize != 0 {
		panic("ciphertext is not a multiple of the block size")
	}

	mode := cipher.NewCBCDecrypter(block, iv)

	// CryptBlocks can work in-place if the two arguments are the same.
	mode.CryptBlocks(ciphertext, ciphertext)

	// If the original plaintext lengths are not a multiple of the block
	// size, padding would have to be added when encrypting, which would be
	// removed at this point. For an example, see
	// https://tools.ietf.org/html/rfc5246#section-6.2.3.2. However, it's
	// critical to note that ciphertexts must be authenticated (i.e. by
	// using crypto/hmac) before being decrypted in order to avoid creating
	// a padding oracle.

	fmt.Printf("%s\n", ciphertext)
	// Output: exampleplaintext
}

func ExampleNewCBCEncrypter() {
	key := []byte("example key 1234")
	plaintext := []byte("exampleplaintext")

	// CBC mode works on blocks so plaintexts may need to be padded to the
	// next whole block. For an example of such padding, see
	// https://tools.ietf.org/html/rfc5246#section-6.2.3.2. Here we'll
	// assume that the plaintext is already of the correct length.
	if len(plaintext)%aes.BlockSize != 0 {
		panic("plaintext is not a multiple of the block size")
	}

	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err)
	}

	// The IV needs to be unique, but not secure. Therefore it's common to
	// include it at the beginning of the ciphertext.
	ciphertext := make([]byte, aes.BlockSize+len(plaintext))
	iv := ciphertext[:aes.BlockSize]
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		panic(err)
	}

	mode := cipher.NewCBCEncrypter(block, iv)
	mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)

	// It's important to remember that ciphertexts must be authenticated
	// (i.e. by using crypto/hmac) as well as being encrypted in order to
	// be secure.

	fmt.Printf("%x\n", ciphertext)
}

func ExampleNewCFBDecrypter() {
	key := []byte("example key 1234")
	ciphertext, _ := hex.DecodeString("22277966616d9bc47177bd02603d08c9a67d5380d0fe8cf3b44438dff7b9")

	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err)
	}

	// The IV needs to be unique, but not secure. Therefore it's common to
	// include it at the beginning of the ciphertext.
	if len(ciphertext) < aes.BlockSize {
		panic("ciphertext too short")
	}
	iv := ciphertext[:aes.BlockSize]
	ciphertext = ciphertext[aes.BlockSize:]

	stream := cipher.NewCFBDecrypter(block, iv)

	// XORKeyStream can work in-place if the two arguments are the same.
	stream.XORKeyStream(ciphertext, ciphertext)
	fmt.Printf("%s", ciphertext)
	// Output: some plaintext
}

func ExampleNewCFBEncrypter() {
	key := []byte("example key 1234")
	plaintext := []byte("some plaintext")

	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err)
	}

	// The IV needs to be unique, but not secure. Therefore it's common to
	// include it at the beginning of the ciphertext.
	ciphertext := make([]byte, aes.BlockSize+len(plaintext))
	iv := ciphertext[:aes.BlockSize]
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		panic(err)
	}

	stream := cipher.NewCFBEncrypter(block, iv)
	stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)

	// It's important to remember that ciphertexts must be authenticated
	// (i.e. by using crypto/hmac) as well as being encrypted in order to
	// be secure.
}

func ExampleNewCTR() {
	key := []byte("example key 1234")
	plaintext := []byte("some plaintext")

	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err)
	}

	// The IV needs to be unique, but not secure. Therefore it's common to
	// include it at the beginning of the ciphertext.
	ciphertext := make([]byte, aes.BlockSize+len(plaintext))
	iv := ciphertext[:aes.BlockSize]
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		panic(err)
	}

	stream := cipher.NewCTR(block, iv)
	stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)

	// It's important to remember that ciphertexts must be authenticated
	// (i.e. by using crypto/hmac) as well as being encrypted in order to
	// be secure.

	// CTR mode is the same for both encryption and decryption, so we can
	// also decrypt that ciphertext with NewCTR.

	plaintext2 := make([]byte, len(plaintext))
	stream = cipher.NewCTR(block, iv)
	stream.XORKeyStream(plaintext2, ciphertext[aes.BlockSize:])

	fmt.Printf("%s\n", plaintext2)
	// Output: some plaintext
}

func ExampleNewOFB() {
	key := []byte("example key 1234")
	plaintext := []byte("some plaintext")

	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err)
	}

	// The IV needs to be unique, but not secure. Therefore it's common to
	// include it at the beginning of the ciphertext.
	ciphertext := make([]byte, aes.BlockSize+len(plaintext))
	iv := ciphertext[:aes.BlockSize]
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		panic(err)
	}

	stream := cipher.NewOFB(block, iv)
	stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)

	// It's important to remember that ciphertexts must be authenticated
	// (i.e. by using crypto/hmac) as well as being encrypted in order to
	// be secure.

	// OFB mode is the same for both encryption and decryption, so we can
	// also decrypt that ciphertext with NewOFB.

	plaintext2 := make([]byte, len(plaintext))
	stream = cipher.NewOFB(block, iv)
	stream.XORKeyStream(plaintext2, ciphertext[aes.BlockSize:])

	fmt.Printf("%s\n", plaintext2)
	// Output: some plaintext
}

func ExampleStreamReader() {
	key := []byte("example key 1234")

	inFile, err := os.Open("encrypted-file")
	if err != nil {
		panic(err)
	}
	defer inFile.Close()

	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err)
	}

	// If the key is unique for each ciphertext, then it's ok to use a zero
	// IV.
	var iv [aes.BlockSize]byte
	stream := cipher.NewOFB(block, iv[:])

	outFile, err := os.OpenFile("decrypted-file", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		panic(err)
	}
	defer outFile.Close()

	reader := &cipher.StreamReader{S: stream, R: inFile}
	// Copy the input file to the output file, decrypting as we go.
	if _, err := io.Copy(outFile, reader); err != nil {
		panic(err)
	}

	// Note that this example is simplistic in that it omits any
	// authentication of the encrypted data. If you were actually to use
	// StreamReader in this manner, an attacker could flip arbitrary bits in
	// the output.
}

func ExampleStreamWriter() {
	key := []byte("example key 1234")

	inFile, err := os.Open("plaintext-file")
	if err != nil {
		panic(err)
	}
	defer inFile.Close()

	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err)
	}

	// If the key is unique for each ciphertext, then it's ok to use a zero
	// IV.
	var iv [aes.BlockSize]byte
	stream := cipher.NewOFB(block, iv[:])

	outFile, err := os.OpenFile("encrypted-file", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		panic(err)
	}
	defer outFile.Close()

	writer := &cipher.StreamWriter{S: stream, W: outFile}
	// Copy the input file to the output file, encrypting as we go.
	if _, err := io.Copy(writer, inFile); err != nil {
		panic(err)
	}

	// Note that this example is simplistic in that it omits any
	// authentication of the encrypted data. It you were actually to use
	// StreamReader in this manner, an attacker could flip arbitrary bits in
	// the decrypted result.
}
<|endoftext|>


<commit_before>package main

import "fmt"

func main() {
	fmt.Println("HEYHAHA")
}
<commit_msg>231241<commit_after>package main

import "fmt"

func main() {
	fmt.Println("HEYHAHA")
	fmt.Println("heiheiheiendring")
}
<|endoftext|>


<commit_before>// Copyright (c) 2015-2021 MinIO, Inc.
//
// This file is part of MinIO Object Storage stack
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package cmd

import (
	"context"
	"errors"
	"io"
	"sync"
	"sync/atomic"

	"github.com/minio/minio/internal/logger"
)

// Reads in parallel from readers.
type parallelReader struct {
	readers       []io.ReaderAt
	orgReaders    []io.ReaderAt
	dataBlocks    int
	offset        int64
	shardSize     int64
	shardFileSize int64
	buf           [][]byte
	readerToBuf   []int
}

// newParallelReader returns parallelReader.
func newParallelReader(readers []io.ReaderAt, e Erasure, offset, totalLength int64) *parallelReader {
	r2b := make([]int, len(readers))
	for i := range r2b {
		r2b[i] = i
	}
	return &parallelReader{
		readers:       readers,
		orgReaders:    readers,
		dataBlocks:    e.dataBlocks,
		offset:        (offset / e.blockSize) * e.ShardSize(),
		shardSize:     e.ShardSize(),
		shardFileSize: e.ShardFileSize(totalLength),
		buf:           make([][]byte, len(readers)),
		readerToBuf:   r2b,
	}
}

// preferReaders can mark readers as preferred.
// These will be chosen before others.
func (p *parallelReader) preferReaders(prefer []bool) {
	if len(prefer) != len(p.orgReaders) {
		return
	}
	// Copy so we don't change our input.
	tmp := make([]io.ReaderAt, len(p.orgReaders))
	copy(tmp, p.orgReaders)
	p.readers = tmp
	// next is the next non-preferred index.
	next := 0
	for i, ok := range prefer {
		if !ok || p.readers[i] == nil {
			continue
		}
		if i == next {
			next++
			continue
		}
		// Move reader with index i to index next.
		// Do this by swapping next and i
		p.readers[next], p.readers[i] = p.readers[i], p.readers[next]
		p.readerToBuf[next] = i
		p.readerToBuf[i] = next
		next++
	}
}

// Returns if buf can be erasure decoded.
func (p *parallelReader) canDecode(buf [][]byte) bool {
	bufCount := 0
	for _, b := range buf {
		if len(b) > 0 {
			bufCount++
		}
	}
	return bufCount >= p.dataBlocks
}

// Read reads from readers in parallel. Returns p.dataBlocks number of bufs.
func (p *parallelReader) Read(dst [][]byte) ([][]byte, error) {
	newBuf := dst
	if len(dst) != len(p.readers) {
		newBuf = make([][]byte, len(p.readers))
	} else {
		for i := range newBuf {
			newBuf[i] = newBuf[i][:0]
		}
	}
	var newBufLK sync.RWMutex

	if p.offset+p.shardSize > p.shardFileSize {
		p.shardSize = p.shardFileSize - p.offset
	}
	if p.shardSize == 0 {
		return newBuf, nil
	}

	readTriggerCh := make(chan bool, len(p.readers))
	for i := 0; i < p.dataBlocks; i++ {
		// Setup read triggers for p.dataBlocks number of reads so that it reads in parallel.
		readTriggerCh <- true
	}

	bitrotHeal := int32(0)       // Atomic bool flag.
	missingPartsHeal := int32(0) // Atomic bool flag.
	readerIndex := 0
	var wg sync.WaitGroup
	// if readTrigger is true, it implies next disk.ReadAt() should be tried
	// if readTrigger is false, it implies previous disk.ReadAt() was successful and there is no need
	// to try reading the next disk.
	for readTrigger := range readTriggerCh {
		newBufLK.RLock()
		canDecode := p.canDecode(newBuf)
		newBufLK.RUnlock()
		if canDecode {
			break
		}
		if readerIndex == len(p.readers) {
			break
		}
		if !readTrigger {
			continue
		}
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			rr := p.readers[i]
			if rr == nil {
				// Since reader is nil, trigger another read.
				readTriggerCh <- true
				return
			}
			bufIdx := p.readerToBuf[i]
			if p.buf[bufIdx] == nil {
				// Reading first time on this disk, hence the buffer needs to be allocated.
				// Subsequent reads will re-use this buffer.
				p.buf[bufIdx] = make([]byte, p.shardSize)
			}
			// For the last shard, the shardsize might be less than previous shard sizes.
			// Hence the following statement ensures that the buffer size is reset to the right size.
			p.buf[bufIdx] = p.buf[bufIdx][:p.shardSize]
			n, err := rr.ReadAt(p.buf[bufIdx], p.offset)
			if err != nil {
				if errors.Is(err, errFileNotFound) {
					atomic.StoreInt32(&missingPartsHeal, 1)
				} else if errors.Is(err, errFileCorrupt) {
					atomic.StoreInt32(&bitrotHeal, 1)
				}

				// This will be communicated upstream.
				p.orgReaders[bufIdx] = nil
				p.readers[i] = nil

				// Since ReadAt returned error, trigger another read.
				readTriggerCh <- true
				return
			}
			newBufLK.Lock()
			newBuf[bufIdx] = p.buf[bufIdx][:n]
			newBufLK.Unlock()
			// Since ReadAt returned success, there is no need to trigger another read.
			readTriggerCh <- false
		}(readerIndex)
		readerIndex++
	}
	wg.Wait()
	if p.canDecode(newBuf) {
		p.offset += p.shardSize
		if atomic.LoadInt32(&missingPartsHeal) == 1 {
			return newBuf, errFileNotFound
		} else if atomic.LoadInt32(&bitrotHeal) == 1 {
			return newBuf, errFileCorrupt
		}
		return newBuf, nil
	}

	// If we cannot decode, just return read quorum error.
	return nil, errErasureReadQuorum
}

// Decode reads from readers, reconstructs data if needed and writes the data to the writer.
// A set of preferred drives can be supplied. In that case they will be used and the data reconstructed.
func (e Erasure) Decode(ctx context.Context, writer io.Writer, readers []io.ReaderAt, offset, length, totalLength int64, prefer []bool) (written int64, derr error) {
	if offset < 0 || length < 0 {
		logger.LogIf(ctx, errInvalidArgument)
		return -1, errInvalidArgument
	}
	if offset+length > totalLength {
		logger.LogIf(ctx, errInvalidArgument)
		return -1, errInvalidArgument
	}

	if length == 0 {
		return 0, nil
	}

	reader := newParallelReader(readers, e, offset, totalLength)
	if len(prefer) == len(readers) {
		reader.preferReaders(prefer)
	}

	startBlock := offset / e.blockSize
	endBlock := (offset + length) / e.blockSize

	var bytesWritten int64
	var bufs [][]byte
	for block := startBlock; block <= endBlock; block++ {
		var blockOffset, blockLength int64
		switch {
		case startBlock == endBlock:
			blockOffset = offset % e.blockSize
			blockLength = length
		case block == startBlock:
			blockOffset = offset % e.blockSize
			blockLength = e.blockSize - blockOffset
		case block == endBlock:
			blockOffset = 0
			blockLength = (offset + length) % e.blockSize
		default:
			blockOffset = 0
			blockLength = e.blockSize
		}
		if blockLength == 0 {
			break
		}

		var err error
		bufs, err = reader.Read(bufs)
		if len(bufs) > 0 {
			// Set only if there are be enough data for reconstruction.
			// and only for expected errors, also set once.
			if errors.Is(err, errFileNotFound) || errors.Is(err, errFileCorrupt) {
				if derr == nil {
					derr = err
				}
			}
		} else if err != nil {
			// For all errors that cannot be reconstructed fail the read operation.
			return -1, err
		}

		if err = e.DecodeDataBlocks(bufs); err != nil {
			logger.LogIf(ctx, err)
			return -1, err
		}

		n, err := writeDataBlocks(ctx, writer, bufs, e.dataBlocks, blockOffset, blockLength)
		if err != nil {
			return -1, err
		}

		bytesWritten += n
	}

	if bytesWritten != length {
		logger.LogIf(ctx, errLessData)
		return bytesWritten, errLessData
	}

	return bytesWritten, derr
}
<commit_msg>fix: add missing readTriggerCh close (#12593)<commit_after>// Copyright (c) 2015-2021 MinIO, Inc.
//
// This file is part of MinIO Object Storage stack
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package cmd

import (
	"context"
	"errors"
	"io"
	"sync"
	"sync/atomic"

	"github.com/minio/minio/internal/logger"
)

// Reads in parallel from readers.
type parallelReader struct {
	readers       []io.ReaderAt
	orgReaders    []io.ReaderAt
	dataBlocks    int
	offset        int64
	shardSize     int64
	shardFileSize int64
	buf           [][]byte
	readerToBuf   []int
}

// newParallelReader returns parallelReader.
func newParallelReader(readers []io.ReaderAt, e Erasure, offset, totalLength int64) *parallelReader {
	r2b := make([]int, len(readers))
	for i := range r2b {
		r2b[i] = i
	}
	return &parallelReader{
		readers:       readers,
		orgReaders:    readers,
		dataBlocks:    e.dataBlocks,
		offset:        (offset / e.blockSize) * e.ShardSize(),
		shardSize:     e.ShardSize(),
		shardFileSize: e.ShardFileSize(totalLength),
		buf:           make([][]byte, len(readers)),
		readerToBuf:   r2b,
	}
}

// preferReaders can mark readers as preferred.
// These will be chosen before others.
func (p *parallelReader) preferReaders(prefer []bool) {
	if len(prefer) != len(p.orgReaders) {
		return
	}
	// Copy so we don't change our input.
	tmp := make([]io.ReaderAt, len(p.orgReaders))
	copy(tmp, p.orgReaders)
	p.readers = tmp
	// next is the next non-preferred index.
	next := 0
	for i, ok := range prefer {
		if !ok || p.readers[i] == nil {
			continue
		}
		if i == next {
			next++
			continue
		}
		// Move reader with index i to index next.
		// Do this by swapping next and i
		p.readers[next], p.readers[i] = p.readers[i], p.readers[next]
		p.readerToBuf[next] = i
		p.readerToBuf[i] = next
		next++
	}
}

// Returns if buf can be erasure decoded.
func (p *parallelReader) canDecode(buf [][]byte) bool {
	bufCount := 0
	for _, b := range buf {
		if len(b) > 0 {
			bufCount++
		}
	}
	return bufCount >= p.dataBlocks
}

// Read reads from readers in parallel. Returns p.dataBlocks number of bufs.
func (p *parallelReader) Read(dst [][]byte) ([][]byte, error) {
	newBuf := dst
	if len(dst) != len(p.readers) {
		newBuf = make([][]byte, len(p.readers))
	} else {
		for i := range newBuf {
			newBuf[i] = newBuf[i][:0]
		}
	}
	var newBufLK sync.RWMutex

	if p.offset+p.shardSize > p.shardFileSize {
		p.shardSize = p.shardFileSize - p.offset
	}
	if p.shardSize == 0 {
		return newBuf, nil
	}

	readTriggerCh := make(chan bool, len(p.readers))
	defer close(readTriggerCh) // close the channel upon return

	for i := 0; i < p.dataBlocks; i++ {
		// Setup read triggers for p.dataBlocks number of reads so that it reads in parallel.
		readTriggerCh <- true
	}

	bitrotHeal := int32(0)       // Atomic bool flag.
	missingPartsHeal := int32(0) // Atomic bool flag.
	readerIndex := 0
	var wg sync.WaitGroup
	// if readTrigger is true, it implies next disk.ReadAt() should be tried
	// if readTrigger is false, it implies previous disk.ReadAt() was successful and there is no need
	// to try reading the next disk.
	for readTrigger := range readTriggerCh {
		newBufLK.RLock()
		canDecode := p.canDecode(newBuf)
		newBufLK.RUnlock()
		if canDecode {
			break
		}
		if readerIndex == len(p.readers) {
			break
		}
		if !readTrigger {
			continue
		}
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			rr := p.readers[i]
			if rr == nil {
				// Since reader is nil, trigger another read.
				readTriggerCh <- true
				return
			}
			bufIdx := p.readerToBuf[i]
			if p.buf[bufIdx] == nil {
				// Reading first time on this disk, hence the buffer needs to be allocated.
				// Subsequent reads will re-use this buffer.
				p.buf[bufIdx] = make([]byte, p.shardSize)
			}
			// For the last shard, the shardsize might be less than previous shard sizes.
			// Hence the following statement ensures that the buffer size is reset to the right size.
			p.buf[bufIdx] = p.buf[bufIdx][:p.shardSize]
			n, err := rr.ReadAt(p.buf[bufIdx], p.offset)
			if err != nil {
				if errors.Is(err, errFileNotFound) {
					atomic.StoreInt32(&missingPartsHeal, 1)
				} else if errors.Is(err, errFileCorrupt) {
					atomic.StoreInt32(&bitrotHeal, 1)
				}

				// This will be communicated upstream.
				p.orgReaders[bufIdx] = nil
				p.readers[i] = nil

				// Since ReadAt returned error, trigger another read.
				readTriggerCh <- true
				return
			}
			newBufLK.Lock()
			newBuf[bufIdx] = p.buf[bufIdx][:n]
			newBufLK.Unlock()
			// Since ReadAt returned success, there is no need to trigger another read.
			readTriggerCh <- false
		}(readerIndex)
		readerIndex++
	}
	wg.Wait()
	if p.canDecode(newBuf) {
		p.offset += p.shardSize
		if atomic.LoadInt32(&missingPartsHeal) == 1 {
			return newBuf, errFileNotFound
		} else if atomic.LoadInt32(&bitrotHeal) == 1 {
			return newBuf, errFileCorrupt
		}
		return newBuf, nil
	}

	// If we cannot decode, just return read quorum error.
	return nil, errErasureReadQuorum
}

// Decode reads from readers, reconstructs data if needed and writes the data to the writer.
// A set of preferred drives can be supplied. In that case they will be used and the data reconstructed.
func (e Erasure) Decode(ctx context.Context, writer io.Writer, readers []io.ReaderAt, offset, length, totalLength int64, prefer []bool) (written int64, derr error) {
	if offset < 0 || length < 0 {
		logger.LogIf(ctx, errInvalidArgument)
		return -1, errInvalidArgument
	}
	if offset+length > totalLength {
		logger.LogIf(ctx, errInvalidArgument)
		return -1, errInvalidArgument
	}

	if length == 0 {
		return 0, nil
	}

	reader := newParallelReader(readers, e, offset, totalLength)
	if len(prefer) == len(readers) {
		reader.preferReaders(prefer)
	}

	startBlock := offset / e.blockSize
	endBlock := (offset + length) / e.blockSize

	var bytesWritten int64
	var bufs [][]byte
	for block := startBlock; block <= endBlock; block++ {
		var blockOffset, blockLength int64
		switch {
		case startBlock == endBlock:
			blockOffset = offset % e.blockSize
			blockLength = length
		case block == startBlock:
			blockOffset = offset % e.blockSize
			blockLength = e.blockSize - blockOffset
		case block == endBlock:
			blockOffset = 0
			blockLength = (offset + length) % e.blockSize
		default:
			blockOffset = 0
			blockLength = e.blockSize
		}
		if blockLength == 0 {
			break
		}

		var err error
		bufs, err = reader.Read(bufs)
		if len(bufs) > 0 {
			// Set only if there are be enough data for reconstruction.
			// and only for expected errors, also set once.
			if errors.Is(err, errFileNotFound) || errors.Is(err, errFileCorrupt) {
				if derr == nil {
					derr = err
				}
			}
		} else if err != nil {
			// For all errors that cannot be reconstructed fail the read operation.
			return -1, err
		}

		if err = e.DecodeDataBlocks(bufs); err != nil {
			logger.LogIf(ctx, err)
			return -1, err
		}

		n, err := writeDataBlocks(ctx, writer, bufs, e.dataBlocks, blockOffset, blockLength)
		if err != nil {
			return -1, err
		}

		bytesWritten += n
	}

	if bytesWritten != length {
		logger.LogIf(ctx, errLessData)
		return bytesWritten, errLessData
	}

	return bytesWritten, derr
}
<|endoftext|>


<commit_before>/*
   Copyright 2020 Docker Compose CLI authors

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

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

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

package formatter

import (
	"context"
	"fmt"
	"io"
	"strconv"
	"strings"

	"github.com/docker/compose/v2/pkg/api"
)

// NewLogConsumer creates a new LogConsumer
func NewLogConsumer(ctx context.Context, w io.Writer, color bool, prefix bool) api.LogConsumer {
	return &logConsumer{
		ctx:        ctx,
		presenters: map[string]*presenter{},
		width:      0,
		writer:     w,
		color:      color,
		prefix:     prefix,
	}
}

func (l *logConsumer) Register(name string) {
	l.register(name)
}

func (l *logConsumer) register(name string) *presenter {
	cf := monochrome
	if l.color {
		cf = nextColor()
	}
	p := &presenter{
		colors: cf,
		name:   name,
	}
	l.presenters[name] = p
	if l.prefix {
		l.computeWidth()
		for _, p := range l.presenters {
			p.setPrefix(l.width)
		}
	}
	return p
}

// Log formats a log message as received from name/container
func (l *logConsumer) Log(container, service, message string) {
	if l.ctx.Err() != nil {
		return
	}
	p, ok := l.presenters[container]
	if !ok { // should have been registered, but ¯\_(ツ)_/¯
		p = l.register(container)
	}
	for _, line := range strings.Split(message, "\n") {
		fmt.Fprintf(l.writer, "%s %s\n", p.prefix, line) // nolint:errcheck
	}
}

func (l *logConsumer) Status(container, msg string) {
	p, ok := l.presenters[container]
	if !ok {
		p = l.register(container)
	}
	s := p.colors(fmt.Sprintf("%s %s\n", container, msg))
	l.writer.Write([]byte(s)) // nolint:errcheck
}

func (l *logConsumer) computeWidth() {
	width := 0
	for _, p := range l.presenters {
		if len(p.name) > width {
			width = len(p.name)
		}
	}
	l.width = width + 1
}

// LogConsumer consume logs from services and format them
type logConsumer struct {
	ctx        context.Context
	presenters map[string]*presenter
	width      int
	writer     io.Writer
	color      bool
	prefix     bool
}

type presenter struct {
	colors colorFunc
	name   string
	prefix string
}

func (p *presenter) setPrefix(width int) {
	p.prefix = p.colors(fmt.Sprintf("%-"+strconv.Itoa(width)+"s |", p.name))
}
<commit_msg>use a sync.Map to avoid concurrency issue accessing log presenters<commit_after>/*
   Copyright 2020 Docker Compose CLI authors

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

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

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

package formatter

import (
	"context"
	"fmt"
	"io"
	"strconv"
	"strings"
	"sync"

	"github.com/docker/compose/v2/pkg/api"
)

// NewLogConsumer creates a new LogConsumer
func NewLogConsumer(ctx context.Context, w io.Writer, color bool, prefix bool) api.LogConsumer {
	return &logConsumer{
		ctx:        ctx,
		presenters: sync.Map{},
		width:      0,
		writer:     w,
		color:      color,
		prefix:     prefix,
	}
}

func (l *logConsumer) Register(name string) {
	l.register(name)
}

func (l *logConsumer) register(name string) *presenter {
	cf := monochrome
	if l.color {
		cf = nextColor()
	}
	p := &presenter{
		colors: cf,
		name:   name,
	}
	l.presenters.Store(name, p)
	if l.prefix {
		l.computeWidth()
		l.presenters.Range(func(key, value interface{}) bool {
			p := value.(*presenter)
			p.setPrefix(l.width)
			return true
		})
	}
	return p
}

func (l *logConsumer) getPresenter(container string) *presenter {
	p, ok := l.presenters.Load(container)
	if !ok { // should have been registered, but ¯\_(ツ)_/¯
		return l.register(container)
	}
	return p.(*presenter)
}

// Log formats a log message as received from name/container
func (l *logConsumer) Log(container, service, message string) {
	if l.ctx.Err() != nil {
		return
	}
	p := l.getPresenter(container)
	for _, line := range strings.Split(message, "\n") {
		fmt.Fprintf(l.writer, "%s %s\n", p.prefix, line) // nolint:errcheck
	}
}

func (l *logConsumer) Status(container, msg string) {
	p := l.getPresenter(container)
	s := p.colors(fmt.Sprintf("%s %s\n", container, msg))
	l.writer.Write([]byte(s)) // nolint:errcheck
}

func (l *logConsumer) computeWidth() {
	width := 0
	l.presenters.Range(func(key, value interface{}) bool {
		p := value.(*presenter)
		if len(p.name) > width {
			width = len(p.name)
		}
		return true
	})
	l.width = width + 1
}

// LogConsumer consume logs from services and format them
type logConsumer struct {
	ctx        context.Context
	presenters sync.Map // map[string]*presenter
	width      int
	writer     io.Writer
	color      bool
	prefix     bool
}

type presenter struct {
	colors colorFunc
	name   string
	prefix string
}

func (p *presenter) setPrefix(width int) {
	p.prefix = p.colors(fmt.Sprintf("%-"+strconv.Itoa(width)+"s |", p.name))
}
<|endoftext|>


<commit_before>package command

import (
	"flag"
	"fmt"
	"sort"
	"strings"
	"sync"
	"time"

	"github.com/Shopify/sarama"
	"github.com/funkygao/gafka/ctx"
	"github.com/funkygao/gafka/zk"
	"github.com/funkygao/gocli"
	"github.com/funkygao/golib/bjtime"
	"github.com/funkygao/golib/color"
	"github.com/funkygao/golib/gofmt"
	"github.com/funkygao/golib/progress"
	"github.com/funkygao/termui"
	"github.com/nsf/termbox-go"
)

type Top struct {
	Ui  cli.Ui
	Cmd string

	zone           string
	clusterPattern string

	mu sync.Mutex

	round int

	who            string
	limit          int
	topInterval    int
	batchMode      bool
	dashboardGraph bool
	topicPattern   string

	counters     map[string]float64 // key is cluster:topic
	lastCounters map[string]float64

	totalMps []float64 // for the dashboard graph
}

func (this *Top) Run(args []string) (exitCode int) {
	cmdFlags := flag.NewFlagSet("top", flag.ContinueOnError)
	cmdFlags.Usage = func() { this.Ui.Output(this.Help()) }
	cmdFlags.StringVar(&this.zone, "z", "", "")
	cmdFlags.StringVar(&this.topicPattern, "t", "", "")
	cmdFlags.IntVar(&this.topInterval, "interval", 3, "refresh interval")
	cmdFlags.StringVar(&this.clusterPattern, "c", "", "")
	cmdFlags.IntVar(&this.limit, "n", 34, "")
	cmdFlags.StringVar(&this.who, "who", "producer", "")
	cmdFlags.BoolVar(&this.dashboardGraph, "d", false, "")
	cmdFlags.BoolVar(&this.batchMode, "b", false, "")
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	if validateArgs(this, this.Ui).
		require("-z").
		invalid(args) {
		return 2
	}

	this.counters = make(map[string]float64)
	this.lastCounters = make(map[string]float64)
	this.totalMps = make([]float64, 0, 1000)

	zkzone := zk.NewZkZone(zk.DefaultConfig(this.zone, ctx.ZoneZkAddrs(this.zone)))
	zkzone.ForSortedClusters(func(zkcluster *zk.ZkCluster) {
		if !patternMatched(zkcluster.Name(), this.clusterPattern) {
			return
		}

		switch this.who {
		case "p", "producer":
			go this.clusterTopProducers(zkcluster)

		case "c", "consumer":
			go this.clusterTopConsumers(zkcluster)

		default:
			this.Ui.Error(fmt.Sprintf("unknown type: %s", this.who))
		}
	})

	if this.dashboardGraph {
		this.drawDashboard()
		return
	}

	bar := progress.New(this.topInterval)
	for {
		if this.batchMode {
			this.Ui.Output(bjtime.TimeToString(bjtime.NowBj()))
		} else {
			refreshScreen()
		}

		// header
		this.Ui.Output(fmt.Sprintf("%30s %50s %20s %15s",
			"cluster", "topic", "num", "mps")) // mps=msg per second
		this.Ui.Output(fmt.Sprintf(strings.Repeat("-", 118)))

		this.showAndResetCounters()

		if !this.batchMode {
			this.showRefreshBar(bar)
		} else {
			time.Sleep(time.Duration(this.topInterval) * time.Second)
		}

	}

	return
}

func (this *Top) drawDashboard() {
	err := termui.Init()
	if err != nil {
		panic(err)
	}
	defer termui.Close()

	termui.UseTheme("helloworld")

	refreshData := func() []float64 {
		this.showAndResetCounters()
		return this.totalMps
	}

	lc0 := termui.NewLineChart()
	lc0.Mode = "dot"
	lc0.Border.Label = fmt.Sprintf("%s mps totals: %s %s %s", this.who,
		this.zone, this.clusterPattern, this.topicPattern)
	lc0.Data = refreshData()
	lc0.Width = termui.TermWidth()
	lc0.Height = termui.TermHeight()
	lc0.X = 0
	lc0.Y = 0
	lc0.AxesColor = termui.ColorWhite
	lc0.LineColor = termui.ColorGreen | termui.AttrBold

	evt := make(chan termbox.Event)
	go func() {
		for {
			evt <- termbox.PollEvent()
		}
	}()

	termui.Render(lc0)
	tick := time.NewTicker(time.Duration(this.topInterval) * time.Second)
	defer tick.Stop()
	rounds := 0
	for {
		select {
		case e := <-evt:
			if e.Type == termbox.EventKey && e.Ch == 'q' {
				return
			}

		case <-tick.C:
			// refresh data, and skip the first 2 rounds
			rounds++
			if rounds > 1 {
				lc0.Data = refreshData()
				termui.Render(lc0)
			}

		}
	}
}

func (this *Top) showRefreshBar(bar *progress.Progress) {
	this.Ui.Output("")
	for i := 1; i <= this.topInterval; i++ {
		bar.ShowProgress(i)
		time.Sleep(time.Second)
	}
}

func (this *Top) showAndResetCounters() {
	this.mu.Lock()
	defer this.mu.Unlock()

	// FIXME counterFlip should be map[int][]string
	counterFlip := make(map[float64]string)
	sortedNum := make([]float64, 0, len(this.counters))
	for ct, num := range this.counters {
		if this.topicPattern != "" && !strings.HasSuffix(ct, ":"+this.topicPattern) {
			continue
		}

		counterFlip[num] = ct
		if num > 100 { // TODO kill the magic number
			sortedNum = append(sortedNum, num)
		}
	}
	sort.Float64s(sortedNum)

	othersNum := 0.
	othersMps := 0.
	totalNum := 0.
	totalMps := 0.
	limitReached := false
	for i := len(sortedNum) - 1; i >= 0; i-- {
		if !limitReached && this.limit > 0 && len(sortedNum)-i > this.limit {
			limitReached = true
		}

		num := sortedNum[i]
		mps := float64(num-this.lastCounters[counterFlip[num]]) / float64(this.topInterval) // msg per sec
		if this.round > 1 {
			totalNum += num
			totalMps += mps
		}

		if limitReached {
			othersNum += num
			othersMps += mps
		} else if !this.dashboardGraph {
			clusterAndTopic := strings.SplitN(counterFlip[num], ":", 2)
			this.Ui.Output(fmt.Sprintf("%30s %50s %20s %15.2f",
				clusterAndTopic[0], clusterAndTopic[1],
				gofmt.Comma(int64(num)),
				mps))
		}
	}

	// display the summary footer
	this.round++
	if this.dashboardGraph {
		if len(this.totalMps) > 5000 {
			// too long, so reset
			this.totalMps = make([]float64, 0, 1000)
		}

		this.totalMps = append(this.totalMps, totalMps)
	} else {
		// the catchall row
		this.Ui.Output(fmt.Sprintf("%30s %50s %20s %15.2f",
			"-OTHERS-", "-OTHERS-",
			gofmt.Comma(int64(othersNum)),
			othersMps))

		// total row
		this.Ui.Output(fmt.Sprintf("%30s %50s %20s %15.2f",
			"--TOTAL--", "--TOTAL--",
			gofmt.Comma(int64(totalNum)),
			totalMps))
	}

	// record last counters and reset current counters
	for k, v := range this.counters {
		this.lastCounters[k] = v
	}
	this.counters = make(map[string]float64)
}

func (this *Top) clusterTopConsumers(zkcluster *zk.ZkCluster) {

}

func (this *Top) clusterTopProducers(zkcluster *zk.ZkCluster) {
	cluster := zkcluster.Name()
	brokerList := zkcluster.BrokerList()
	if len(brokerList) == 0 {
		return
	}

	kfk, err := sarama.NewClient(brokerList, sarama.NewConfig())
	if err != nil {
		return
	}
	defer kfk.Close()

	for {
		topics, err := kfk.Topics()
		if err != nil || len(topics) == 0 {
			return
		}

		for _, topic := range topics {
			if !patternMatched(topic, this.topicPattern) {
				continue
			}

			msgs := int64(0)
			alivePartitions, err := kfk.WritablePartitions(topic)
			if err != nil {
				panic(err)
			}

			for _, partitionID := range alivePartitions {
				latestOffset, err := kfk.GetOffset(topic, partitionID,
					sarama.OffsetNewest)
				if err != nil {
					panic(err)
				}

				msgs += latestOffset
			}

			this.mu.Lock()
			this.counters[cluster+":"+topic] = float64(msgs)
			this.mu.Unlock()
		}

		time.Sleep(time.Second)
		kfk.RefreshMetadata(topics...)
	}

}

func (*Top) Synopsis() string {
	return "Unix “top” like utility for kafka"
}

func (this *Top) Help() string {
	help := fmt.Sprintf(`
Usage: %s top [options]

    Unix “top” like utility for kafka

Options:

    -z zone

    -c cluster pattern

    -t topic pattern

    -interval interval
      Refresh interval in seconds.

    -n limit

    -d
      Draw dashboard in graph.

    -b
      Batch mode operation.
      Could be useful for sending output from top to other programs or to a file.

    -who <%s%s|%s%s>
`, this.Cmd, color.Colorize([]string{color.Underscore}, "p"), "roducer",
		color.Colorize([]string{color.Underscore}, "c"), "onsumer")
	return strings.TrimSpace(help)
}
<commit_msg>adjust default value<commit_after>package command

import (
	"flag"
	"fmt"
	"sort"
	"strings"
	"sync"
	"time"

	"github.com/Shopify/sarama"
	"github.com/funkygao/gafka/ctx"
	"github.com/funkygao/gafka/zk"
	"github.com/funkygao/gocli"
	"github.com/funkygao/golib/bjtime"
	"github.com/funkygao/golib/color"
	"github.com/funkygao/golib/gofmt"
	"github.com/funkygao/golib/progress"
	"github.com/funkygao/termui"
	"github.com/nsf/termbox-go"
)

type Top struct {
	Ui  cli.Ui
	Cmd string

	zone           string
	clusterPattern string

	mu sync.Mutex

	round int

	who            string
	limit          int
	topInterval    int
	batchMode      bool
	dashboardGraph bool
	topicPattern   string

	counters     map[string]float64 // key is cluster:topic
	lastCounters map[string]float64

	totalMps []float64 // for the dashboard graph
}

func (this *Top) Run(args []string) (exitCode int) {
	cmdFlags := flag.NewFlagSet("top", flag.ContinueOnError)
	cmdFlags.Usage = func() { this.Ui.Output(this.Help()) }
	cmdFlags.StringVar(&this.zone, "z", "", "")
	cmdFlags.StringVar(&this.topicPattern, "t", "", "")
	cmdFlags.IntVar(&this.topInterval, "interval", 5, "refresh interval")
	cmdFlags.StringVar(&this.clusterPattern, "c", "", "")
	cmdFlags.IntVar(&this.limit, "n", 34, "")
	cmdFlags.StringVar(&this.who, "who", "producer", "")
	cmdFlags.BoolVar(&this.dashboardGraph, "d", false, "")
	cmdFlags.BoolVar(&this.batchMode, "b", false, "")
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	if validateArgs(this, this.Ui).
		require("-z").
		invalid(args) {
		return 2
	}

	this.counters = make(map[string]float64)
	this.lastCounters = make(map[string]float64)
	this.totalMps = make([]float64, 0, 1000)

	zkzone := zk.NewZkZone(zk.DefaultConfig(this.zone, ctx.ZoneZkAddrs(this.zone)))
	zkzone.ForSortedClusters(func(zkcluster *zk.ZkCluster) {
		if !patternMatched(zkcluster.Name(), this.clusterPattern) {
			return
		}

		switch this.who {
		case "p", "producer":
			go this.clusterTopProducers(zkcluster)

		case "c", "consumer":
			go this.clusterTopConsumers(zkcluster)

		default:
			this.Ui.Error(fmt.Sprintf("unknown type: %s", this.who))
		}
	})

	if this.dashboardGraph {
		this.drawDashboard()
		return
	}

	bar := progress.New(this.topInterval)
	for {
		if this.batchMode {
			this.Ui.Output(bjtime.TimeToString(bjtime.NowBj()))
		} else {
			refreshScreen()
		}

		// header
		this.Ui.Output(fmt.Sprintf("%30s %50s %20s %15s",
			"cluster", "topic", "num", "mps")) // mps=msg per second
		this.Ui.Output(fmt.Sprintf(strings.Repeat("-", 118)))

		this.showAndResetCounters()

		if !this.batchMode {
			this.showRefreshBar(bar)
		} else {
			time.Sleep(time.Duration(this.topInterval) * time.Second)
		}

	}

	return
}

func (this *Top) drawDashboard() {
	err := termui.Init()
	if err != nil {
		panic(err)
	}
	defer termui.Close()

	termui.UseTheme("helloworld")

	refreshData := func() []float64 {
		this.showAndResetCounters()
		return this.totalMps
	}

	lc0 := termui.NewLineChart()
	lc0.Mode = "dot"
	lc0.Border.Label = fmt.Sprintf("%s mps totals: %s %s %s", this.who,
		this.zone, this.clusterPattern, this.topicPattern)
	lc0.Data = refreshData()
	lc0.Width = termui.TermWidth()
	lc0.Height = termui.TermHeight()
	lc0.X = 0
	lc0.Y = 0
	lc0.AxesColor = termui.ColorWhite
	lc0.LineColor = termui.ColorGreen | termui.AttrBold

	evt := make(chan termbox.Event)
	go func() {
		for {
			evt <- termbox.PollEvent()
		}
	}()

	termui.Render(lc0)
	tick := time.NewTicker(time.Duration(this.topInterval) * time.Second)
	defer tick.Stop()
	rounds := 0
	for {
		select {
		case e := <-evt:
			if e.Type == termbox.EventKey && e.Ch == 'q' {
				return
			}

		case <-tick.C:
			// refresh data, and skip the first 2 rounds
			rounds++
			if rounds > 1 {
				lc0.Data = refreshData()
				termui.Render(lc0)
			}

		}
	}
}

func (this *Top) showRefreshBar(bar *progress.Progress) {
	this.Ui.Output("")
	for i := 1; i <= this.topInterval; i++ {
		bar.ShowProgress(i)
		time.Sleep(time.Second)
	}
}

func (this *Top) showAndResetCounters() {
	this.mu.Lock()
	defer this.mu.Unlock()

	// FIXME counterFlip should be map[int][]string
	counterFlip := make(map[float64]string)
	sortedNum := make([]float64, 0, len(this.counters))
	for ct, num := range this.counters {
		if this.topicPattern != "" && !strings.HasSuffix(ct, ":"+this.topicPattern) {
			continue
		}

		counterFlip[num] = ct
		if num > 100 { // TODO kill the magic number
			sortedNum = append(sortedNum, num)
		}
	}
	sort.Float64s(sortedNum)

	othersNum := 0.
	othersMps := 0.
	totalNum := 0.
	totalMps := 0.
	limitReached := false
	for i := len(sortedNum) - 1; i >= 0; i-- {
		if !limitReached && this.limit > 0 && len(sortedNum)-i > this.limit {
			limitReached = true
		}

		num := sortedNum[i]
		mps := float64(num-this.lastCounters[counterFlip[num]]) / float64(this.topInterval) // msg per sec
		if this.round > 1 {
			totalNum += num
			totalMps += mps
		}

		if limitReached {
			othersNum += num
			othersMps += mps
		} else if !this.dashboardGraph {
			clusterAndTopic := strings.SplitN(counterFlip[num], ":", 2)
			this.Ui.Output(fmt.Sprintf("%30s %50s %20s %15.2f",
				clusterAndTopic[0], clusterAndTopic[1],
				gofmt.Comma(int64(num)),
				mps))
		}
	}

	// display the summary footer
	this.round++
	if this.dashboardGraph {
		if len(this.totalMps) > 5000 {
			// too long, so reset
			this.totalMps = make([]float64, 0, 1000)
		}

		this.totalMps = append(this.totalMps, totalMps)
	} else {
		// the catchall row
		this.Ui.Output(fmt.Sprintf("%30s %50s %20s %15.2f",
			"-OTHERS-", "-OTHERS-",
			gofmt.Comma(int64(othersNum)),
			othersMps))

		// total row
		this.Ui.Output(fmt.Sprintf("%30s %50s %20s %15.2f",
			"--TOTAL--", "--TOTAL--",
			gofmt.Comma(int64(totalNum)),
			totalMps))
	}

	// record last counters and reset current counters
	for k, v := range this.counters {
		this.lastCounters[k] = v
	}
	this.counters = make(map[string]float64)
}

func (this *Top) clusterTopConsumers(zkcluster *zk.ZkCluster) {

}

func (this *Top) clusterTopProducers(zkcluster *zk.ZkCluster) {
	cluster := zkcluster.Name()
	brokerList := zkcluster.BrokerList()
	if len(brokerList) == 0 {
		return
	}

	kfk, err := sarama.NewClient(brokerList, sarama.NewConfig())
	if err != nil {
		return
	}
	defer kfk.Close()

	for {
		topics, err := kfk.Topics()
		if err != nil || len(topics) == 0 {
			return
		}

		for _, topic := range topics {
			if !patternMatched(topic, this.topicPattern) {
				continue
			}

			msgs := int64(0)
			alivePartitions, err := kfk.WritablePartitions(topic)
			if err != nil {
				panic(err)
			}

			for _, partitionID := range alivePartitions {
				latestOffset, err := kfk.GetOffset(topic, partitionID,
					sarama.OffsetNewest)
				if err != nil {
					panic(err)
				}

				msgs += latestOffset
			}

			this.mu.Lock()
			this.counters[cluster+":"+topic] = float64(msgs)
			this.mu.Unlock()
		}

		time.Sleep(time.Second)
		kfk.RefreshMetadata(topics...)
	}

}

func (*Top) Synopsis() string {
	return "Unix “top” like utility for kafka"
}

func (this *Top) Help() string {
	help := fmt.Sprintf(`
Usage: %s top [options]

    Unix “top” like utility for kafka

Options:

    -z zone

    -c cluster pattern

    -t topic pattern

    -interval interval
      Refresh interval in seconds.

    -n limit

    -d
      Draw dashboard in graph.

    -b
      Batch mode operation.
      Could be useful for sending output from top to other programs or to a file.

    -who <%s%s|%s%s>
`, this.Cmd, color.Colorize([]string{color.Underscore}, "p"), "roducer",
		color.Colorize([]string{color.Underscore}, "c"), "onsumer")
	return strings.TrimSpace(help)
}
<|endoftext|>


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

// The /doc/codewalk/ tree is synthesized from codewalk descriptions,
// files named $GOROOT/doc/codewalk/*.xml.
// For an example and a description of the format, see
// http://golang.org/doc/codewalk/codewalk or run godoc -http=:6060
// and see http://localhost:6060/doc/codewalk/codewalk .
// That page is itself a codewalk; the source code for it is
// $GOROOT/doc/codewalk/codewalk.xml.

package main

import (
	"log"
	"net/http"
	"text/template"

	"code.google.com/p/go.tools/godoc"
	"code.google.com/p/go.tools/godoc/vfs"
)

var (
	pres *godoc.Presentation
	fs   = vfs.NameSpace{}
)

func registerHandlers(pres *godoc.Presentation) {
	if pres == nil {
		panic("nil Presentation")
	}
	http.HandleFunc("/doc/codewalk/", codewalk)
	http.Handle("/doc/play/", pres.FileServer())
	http.Handle("/robots.txt", pres.FileServer())
	http.Handle("/", pres)
	handlePathRedirects(pkgRedirects, "/pkg/")
	handlePathRedirects(cmdRedirects, "/cmd/")
}

func readTemplate(name string) *template.Template {
	if pres == nil {
		panic("no global Presentation set yet")
	}
	path := "lib/godoc/" + name

	// use underlying file system fs to read the template file
	// (cannot use template ParseFile functions directly)
	data, err := vfs.ReadFile(fs, path)
	if err != nil {
		log.Fatal("readTemplate: ", err)
	}
	// be explicit with errors (for app engine use)
	t, err := template.New(name).Funcs(pres.FuncMap()).Parse(string(data))
	if err != nil {
		log.Fatal("readTemplate: ", err)
	}
	return t
}

func readTemplates(p *godoc.Presentation, html bool) {
	p.PackageText = readTemplate("package.txt")
	p.SearchText = readTemplate("search.txt")

	if html {
		codewalkHTML = readTemplate("codewalk.html")
		codewalkdirHTML = readTemplate("codewalkdir.html")
		p.DirlistHTML = readTemplate("dirlist.html")
		p.ErrorHTML = readTemplate("error.html")
		p.ExampleHTML = readTemplate("example.html")
		p.GodocHTML = readTemplate("godoc.html")
		p.PackageHTML = readTemplate("package.html")
		p.SearchHTML = readTemplate("search.html")
		p.SearchDescXML = readTemplate("opensearch.xml")
	}
}

// Packages that were renamed between r60 and go1.
var pkgRedirects = map[string]string{
	"asn1":              "encoding/asn1",
	"big":               "math/big",
	"cmath":             "math/cmplx",
	"csv":               "encoding/csv",
	"exec":              "os/exec",
	"exp/template/html": "html/template",
	"gob":               "encoding/gob",
	"http":              "net/http",
	"http/cgi":          "net/http/cgi",
	"http/fcgi":         "net/http/fcgi",
	"http/httptest":     "net/http/httptest",
	"http/pprof":        "net/http/pprof",
	"json":              "encoding/json",
	"mail":              "net/mail",
	"rand":              "math/rand",
	"rpc":               "net/rpc",
	"rpc/jsonrpc":       "net/rpc/jsonrpc",
	"scanner":           "text/scanner",
	"smtp":              "net/smtp",
	"tabwriter":         "text/tabwriter",
	"template":          "text/template",
	"template/parse":    "text/template/parse",
	"url":               "net/url",
	"utf16":             "unicode/utf16",
	"utf8":              "unicode/utf8",
	"xml":               "encoding/xml",
}

// Commands that were renamed between r60 and go1.
var cmdRedirects = map[string]string{
	"gofix":     "fix",
	"goinstall": "go",
	"gopack":    "pack",
	"govet":     "vet",
	"goyacc":    "yacc",
}

func handlePathRedirects(redirects map[string]string, prefix string) {
	for source, target := range pkgRedirects {
		h := makeRedirectHandler(prefix + target + "/")
		p := prefix + source
		http.HandleFunc(p, h)
		http.HandleFunc(p+"/", h)
	}
}

func makeRedirectHandler(target string) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, target, http.StatusMovedPermanently)
	}
}
<commit_msg>go.tools/cmd/godoc: add redirect helpers<commit_after>// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// The /doc/codewalk/ tree is synthesized from codewalk descriptions,
// files named $GOROOT/doc/codewalk/*.xml.
// For an example and a description of the format, see
// http://golang.org/doc/codewalk/codewalk or run godoc -http=:6060
// and see http://localhost:6060/doc/codewalk/codewalk .
// That page is itself a codewalk; the source code for it is
// $GOROOT/doc/codewalk/codewalk.xml.

package main

import (
	"log"
	"net/http"
	"regexp"
	"text/template"

	"code.google.com/p/go.tools/godoc"
	"code.google.com/p/go.tools/godoc/vfs"
)

var (
	pres *godoc.Presentation
	fs   = vfs.NameSpace{}
)

func registerHandlers(pres *godoc.Presentation) {
	if pres == nil {
		panic("nil Presentation")
	}
	http.HandleFunc("/doc/codewalk/", codewalk)
	http.Handle("/doc/play/", pres.FileServer())
	http.Handle("/robots.txt", pres.FileServer())
	http.Handle("/", pres)
	handlePathRedirects(pkgRedirects, "/pkg/")
	handlePathRedirects(cmdRedirects, "/cmd/")
	for prefix, redirect := range prefixHelpers {
		p := "/" + prefix + "/"
		h := makePrefixRedirectHandler(p, redirect)
		http.HandleFunc(p, h)
	}
	for path, redirect := range redirects {
		h := makeRedirectHandler(redirect)
		http.HandleFunc(path, h)
	}
}

func readTemplate(name string) *template.Template {
	if pres == nil {
		panic("no global Presentation set yet")
	}
	path := "lib/godoc/" + name

	// use underlying file system fs to read the template file
	// (cannot use template ParseFile functions directly)
	data, err := vfs.ReadFile(fs, path)
	if err != nil {
		log.Fatal("readTemplate: ", err)
	}
	// be explicit with errors (for app engine use)
	t, err := template.New(name).Funcs(pres.FuncMap()).Parse(string(data))
	if err != nil {
		log.Fatal("readTemplate: ", err)
	}
	return t
}

func readTemplates(p *godoc.Presentation, html bool) {
	p.PackageText = readTemplate("package.txt")
	p.SearchText = readTemplate("search.txt")

	if html {
		codewalkHTML = readTemplate("codewalk.html")
		codewalkdirHTML = readTemplate("codewalkdir.html")
		p.DirlistHTML = readTemplate("dirlist.html")
		p.ErrorHTML = readTemplate("error.html")
		p.ExampleHTML = readTemplate("example.html")
		p.GodocHTML = readTemplate("godoc.html")
		p.PackageHTML = readTemplate("package.html")
		p.SearchHTML = readTemplate("search.html")
		p.SearchDescXML = readTemplate("opensearch.xml")
	}
}

// Packages that were renamed between r60 and go1.
var pkgRedirects = map[string]string{
	"asn1":              "encoding/asn1",
	"big":               "math/big",
	"cmath":             "math/cmplx",
	"csv":               "encoding/csv",
	"exec":              "os/exec",
	"exp/template/html": "html/template",
	"gob":               "encoding/gob",
	"http":              "net/http",
	"http/cgi":          "net/http/cgi",
	"http/fcgi":         "net/http/fcgi",
	"http/httptest":     "net/http/httptest",
	"http/pprof":        "net/http/pprof",
	"json":              "encoding/json",
	"mail":              "net/mail",
	"rand":              "math/rand",
	"rpc":               "net/rpc",
	"rpc/jsonrpc":       "net/rpc/jsonrpc",
	"scanner":           "text/scanner",
	"smtp":              "net/smtp",
	"tabwriter":         "text/tabwriter",
	"template":          "text/template",
	"template/parse":    "text/template/parse",
	"url":               "net/url",
	"utf16":             "unicode/utf16",
	"utf8":              "unicode/utf8",
	"xml":               "encoding/xml",
}

// Commands that were renamed between r60 and go1.
var cmdRedirects = map[string]string{
	"gofix":     "fix",
	"goinstall": "go",
	"gopack":    "pack",
	"govet":     "vet",
	"goyacc":    "yacc",
}

var redirects = map[string]string{
	"/blog":   "http://blog.golang.org",
	"/build":  "http://build.golang.org",
	"/change": "https://code.google.com/p/go/source/list",
	"/cl":     "https://gocodereview.appspot.com/",
	"/doc/go_for_cpp_programmers.html": "https://code.google.com/p/go-wiki/wiki/GoForCPPProgrammers",
	"/doc/go_tutorial.html":            "http://tour.golang.org/",
	"/issue":                           "https://code.google.com/p/go/issues",
	"/issue/new":                       "https://code.google.com/p/go/issues/entry",
	"/issues":                          "https://code.google.com/p/go/issues",
	"/play":                            "http://play.golang.org",
	"/talks":                           "http://talks.golang.org",
	"/tour":                            "http://tour.golang.org",
	"/wiki":                            "https://code.google.com/p/go-wiki/w/list",
}

var prefixHelpers = map[string]string{
	"blog":   "http://blog.golang.org/",
	"change": "https://code.google.com/p/go/source/detail?r=",
	"cl":     "https://codereview.appspot.com/",
	"issue":  "https://code.google.com/p/go/issues/detail?id=",
	"play":   "http://play.golang.org/",
	"talks":  "http://talks.golang.org/",
	"wiki":   "https://code.google.com/p/go-wiki/wiki/",
}

func handlePathRedirects(redirects map[string]string, prefix string) {
	for source, target := range pkgRedirects {
		h := makeRedirectHandler(prefix + target + "/")
		p := prefix + source
		http.HandleFunc(p, h)
		http.HandleFunc(p+"/", h)
	}
}

func makeRedirectHandler(target string) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, target, http.StatusMovedPermanently)
	}
}

var validId = regexp.MustCompile(`^[a-z0-9]*$`)

func makePrefixRedirectHandler(prefix, baseURL string) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if p := r.URL.Path; p == prefix {
			// redirect /prefix/ to /prefix
			http.Redirect(w, r, p[:len(p)-1], http.StatusFound)
			return
		}
		id := r.URL.Path[len(prefix):]
		if !validId.MatchString(id) {
			http.Error(w, "Not found", http.StatusNotFound)
			return
		}
		target := baseURL + id
		http.Redirect(w, r, target, http.StatusFound)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"os"
)

func AddRightScriptMetadata(path string) error {
	script, err := os.Open(path)
	if err != nil {
		return err
	}
	defer script.Close()

	// check if metadata already exists

	// Load script

	// Add metadata to buffer version

	// write to file

	return nil
}
<commit_msg>Add check if metadata already exists in AddRightScriptMetadata<commit_after>package main

import (
	"bufio"
	"fmt"
	"os"
)

func AddRightScriptMetadata(path string) error {
	script, err := os.Open(path)
	if err != nil {
		return err
	}
	defer script.Close()

	// check if metadata section set by delimiters already exists
	scanner := bufio.NewScanner(script)
	inMetadata := false
	metadataExists := false
	for scanner.Scan() {
		line := scanner.Text()
		switch {
		case inMetadata:
			submatches := metadataEnd.FindStringSubmatch(line)
			if submatches != nil {
				metadataExists = true
				break
			}
		case metadataStart.MatchString(line):
			inMetadata = true
		}
	}

	if metadataExists == true {
		fmt.Println("metadata already exists")
	}

	// Load script

	// Add metadata to buffer version

	// write to file

	return nil
}
<|endoftext|>


<commit_before>package main

import (
    "bytes"
    "encoding/gob"

    "github.com/stretchr/testify/assert"
    "testing"
)

// This is just a dummy test to enable cross package coverage
func TestDecodeToMacIface(test *testing.T) {
    var TestCases = []MacIface {
        MacIface{ "00:00:00:00:00:00", "eth0" },
        MacIface{ "00:00:00:00:00:AA", "eth1" },
    }

    for _, entry := range TestCases {
        // First encode the MacIface to a bunch of bytes
        buf := bytes.NewBuffer(nil)
        err := gob.NewEncoder(buf).Encode(entry);
        assert.Equal(test, err, nil)

        // Invoke the function and validate that it is equal
        // to our starting MacIface
        result, err := DecodeToMacIface(buf)
        assert.Equal(test, err, nil)
        assert.Equal(test, entry, result)
    }
}

// TODO: Add BoltDB related alias tests here...
<commit_msg>Tests - AddAlias, AddDuplicateAlias<commit_after>package main

import (
	"bytes"
	"encoding/gob"

	"github.com/stretchr/testify/assert"
	"os"
	"testing"
)

// This is just a dummy test to enable cross package coverage
func TestDecodeToMacIface(test *testing.T) {
	var TestCases = []MacIface{
		{"00:00:00:00:00:00", "eth0"},
		{"00:00:00:00:00:AA", "eth1"},
	}

	for _, entry := range TestCases {
		// First encode the MacIface to a bunch of bytes
		buf := bytes.NewBuffer(nil)
		err := gob.NewEncoder(buf).Encode(entry)
		assert.Equal(test, nil, err)

		// Invoke the function and validate that it is equal
		// to our starting MacIface
		result, err := DecodeToMacIface(buf)
		assert.Equal(test, nil, err)
		assert.Equal(test, entry.Mac, result.Mac)
		assert.Equal(test, entry.Iface, result.Iface)
	}
}

// The Setup function is responsible for creating a temporary
// BoltDB to test against. Then, it returns the path to the
// db it creates so we can clean this up at teardown time (along
// with a pointer to the db instance).
func AliasSetup(test *testing.T, dbPath string) (*Aliases, string) {
	aliases, err := LoadAliases(dbPath)
	assert.Equal(test, nil, err)
	return aliases, dbPath
}

// The Teardown function closes the connection to the DB, and
// removes the temporary file created for the same
func AliasTeardown(test *testing.T, aliases *Aliases, dbPath string) {
	// Close the connection to the bolt db
	err := aliases.Close()
	assert.Equal(test, nil, err)
	// Remove the temporary test db
	err = os.Remove(dbPath)
	assert.Equal(test, nil, err)
}

func TestAddAlias(test *testing.T) {
	var TestCases = []struct {
		alias, mac, iface string
	}{
		{"one", "00:00:00:00:00:00", "eth0"},
		{"two", "00:00:00:00:00:AA", "eth1"},
		{"three", "00:00:00:00:11:00", ""},
		{"four", "00:00:00:00:11:AA", ""},
	}

	// Open a test db before we start any of the tests
	aliases, dbPath := AliasSetup(test, "./_test_TestAddAlias")
	// Cleanup after the tests run
	defer AliasTeardown(test, aliases, dbPath)

	entryCount := 0
	for _, entry := range TestCases {
		// Add the alias to the db
		err := aliases.Add(entry.alias, entry.mac, entry.iface)
		assert.Equal(test, nil, err)
		entryCount += 1

		// Validate that we have "entryCount" number of aliases added
		list, err := aliases.List()
		assert.Equal(test, nil, err)
		assert.Equal(test, entryCount, len(list))

		// Check to ensure that the current map contains the key we
		// just added to the db
		assert.Equal(test, entry.mac, list[entry.alias].Mac)
		assert.Equal(test, entry.iface, list[entry.alias].Iface)
	}
}

func TestAddDuplicateAlias(test *testing.T) {
	// Open a test db before we start any of the tests
	aliases, dbPath := AliasSetup(test, "./_test_TestAddDuplicateAlias")
	// Cleanup after the tests run
	defer AliasTeardown(test, aliases, dbPath)

	err := aliases.Add("test01", "00:11:22:33:44:55", "eth0")
	assert.Equal(test, nil, err)

	// Validate the first entry exists
	list, err := aliases.List()
	assert.Equal(test, nil, err)
	assert.Equal(test, "00:11:22:33:44:55", list["test01"].Mac)
	assert.Equal(test, "eth0", list["test01"].Iface)

	err = aliases.Add("test01", "00:11:22:33:44:66", "")
	assert.Equal(test, nil, err)

	list, err = aliases.List()
	assert.Equal(test, nil, err)
	assert.Equal(test, "00:11:22:33:44:66", list["test01"].Mac)
	assert.Equal(test, "", list["test01"].Iface)
}
<|endoftext|>


<commit_before>package command

import (
	"flag"
	"fmt"
	"sort"
	"strings"

	"github.com/funkygao/gafka/ctx"
	gzk "github.com/funkygao/gafka/zk"
	"github.com/funkygao/gocli"
	"github.com/funkygao/golib/color"
)

type Get struct {
	Ui  cli.Ui
	Cmd string

	zone      string
	path      string
	recursive bool
}

func (this *Get) Run(args []string) (exitCode int) {
	cmdFlags := flag.NewFlagSet("get", flag.ContinueOnError)
	cmdFlags.Usage = func() { this.Ui.Output(this.Help()) }
	cmdFlags.StringVar(&this.zone, "z", ctx.ZkDefaultZone(), "")
	cmdFlags.BoolVar(&this.recursive, "R", false, "")
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	if this.zone == "" {
		this.Ui.Error("unknown zone")
		return 2
	}

	this.path = args[len(args)-1]

	zkzone := gzk.NewZkZone(gzk.DefaultConfig(this.zone, ctx.ZoneZkAddrs(this.zone)))
	defer zkzone.Close()
	if this.recursive {
		datas := zkzone.ChildrenWithData(this.path)
		sortedPath := make([]string, 0, len(datas))
		for path, _ := range datas {
			sortedPath = append(sortedPath, path)
		}
		sort.Strings(sortedPath)

		if this.path == "/" {
			this.path = ""
		}
		for _, path := range sortedPath {
			this.Ui.Output(color.Green("%s/%s", this.path, path))
			data := datas[path]
			if len(data.Data()) > 0 {
				this.Ui.Output(fmt.Sprintf("%s %s", strings.Repeat(" ", 3),
					string(data.Data())))
			}
		}

		return 0
	}

	conn := zkzone.Conn()
	data, _, err := conn.Get(this.path)
	must(err)

	if len(data) == 0 {
		this.Ui.Output("empty znode")
		return
	}

	this.Ui.Output(color.Green("raw data:"))
	fmt.Println(data)
	this.Ui.Output(color.Green("string data:"))
	this.Ui.Output(string(data)) // TODO what if binary?

	return
}

func (*Get) Synopsis() string {
	return "Show znode data"
}

func (this *Get) Help() string {
	help := fmt.Sprintf(`
Usage: %s get [options] path

    Show znode data

Options:

    -z zone

    -R
      Recursively show subdirectories znode encountered.

`, this.Cmd)
	return strings.TrimSpace(help)
}
<commit_msg>show znode status when display znode content<commit_after>package command

import (
	"flag"
	"fmt"
	"sort"
	"strings"

	"github.com/funkygao/gafka/ctx"
	gzk "github.com/funkygao/gafka/zk"
	"github.com/funkygao/gocli"
	"github.com/funkygao/golib/color"
)

type Get struct {
	Ui  cli.Ui
	Cmd string

	zone      string
	path      string
	recursive bool
}

func (this *Get) Run(args []string) (exitCode int) {
	cmdFlags := flag.NewFlagSet("get", flag.ContinueOnError)
	cmdFlags.Usage = func() { this.Ui.Output(this.Help()) }
	cmdFlags.StringVar(&this.zone, "z", ctx.ZkDefaultZone(), "")
	cmdFlags.BoolVar(&this.recursive, "R", false, "")
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	if this.zone == "" {
		this.Ui.Error("unknown zone")
		return 2
	}

	this.path = args[len(args)-1]

	zkzone := gzk.NewZkZone(gzk.DefaultConfig(this.zone, ctx.ZoneZkAddrs(this.zone)))
	defer zkzone.Close()
	if this.recursive {
		datas := zkzone.ChildrenWithData(this.path)
		sortedPath := make([]string, 0, len(datas))
		for path, _ := range datas {
			sortedPath = append(sortedPath, path)
		}
		sort.Strings(sortedPath)

		if this.path == "/" {
			this.path = ""
		}
		for _, path := range sortedPath {
			this.Ui.Output(color.Green("%s/%s", this.path, path))
			data := datas[path]
			if len(data.Data()) > 0 {
				this.Ui.Output(fmt.Sprintf("%s %s", strings.Repeat(" ", 3),
					string(data.Data())))
			}
		}

		return 0
	}

	conn := zkzone.Conn()
	data, stat, err := conn.Get(this.path)
	must(err)

	if len(data) == 0 {
		this.Ui.Output("empty znode")
		return
	}

	this.Ui.Output(fmt.Sprintf("%#v", *stat))
	this.Ui.Output(color.Green("Data Bytes"))
	fmt.Println(data)
	this.Ui.Output(color.Green("Data as String"))
	this.Ui.Output(string(data))

	return
}

func (*Get) Synopsis() string {
	return "Show znode data"
}

func (this *Get) Help() string {
	help := fmt.Sprintf(`
Usage: %s get [options] path

    Show znode data

Options:

    -z zone

    -R
      Recursively show subdirectories znode encountered.

`, this.Cmd)
	return strings.TrimSpace(help)
}
<|endoftext|>