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

// The following enables go generate to generate the doc.go file.
//go:generate go run $JIRI_ROOT/release/go/src/v.io/x/lib/cmdline/testdata/gendoc.go -env=CMDLINE_PREFIX=jiri .

package main

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"v.io/jiri/collect"
	"v.io/jiri/gitutil"
	"v.io/jiri/jiri"
	"v.io/jiri/profiles"
	"v.io/jiri/profiles/profilescmdline"
	"v.io/jiri/profiles/profilesreader"
	"v.io/jiri/project"
	"v.io/jiri/runutil"
	"v.io/jiri/tool"
	"v.io/jiri/util"
	"v.io/x/devtools/jiri-v23-profile/v23_profile"
	"v.io/x/lib/cmdline"
	"v.io/x/lib/envvar"
)

var (
	detailedOutputFlag bool
	gotoolsBinPathFlag string
	readerFlags        profilescmdline.ReaderFlagValues

	commentRE = regexp.MustCompile("^($|[:space:]*#)")
)

func init() {
	cmdAPICheck.Flags.BoolVar(&detailedOutputFlag, "detailed", true, "If true, shows each API change in an expanded form. Otherwise, only a summary is shown.")
	cmdAPI.Flags.StringVar(&gotoolsBinPathFlag, "gotools-bin", "", "The path to the gotools binary to use. If empty, gotools will be built if necessary.")
	profilescmdline.RegisterReaderFlags(&cmdAPI.Flags, &readerFlags, v23_profile.DefaultDBFilename)
	tool.InitializeProjectFlags(&cmdAPI.Flags)
	tool.InitializeRunFlags(&cmdAPI.Flags)
}

// cmdAPI represents the "jiri api" command.
var cmdAPI = &cmdline.Command{
	Name:     "api",
	Short:    "Manage vanadium public API",
	Long:     "Use this command to ensure that no unintended changes are made to the vanadium public API.",
	Children: []*cmdline.Command{cmdAPICheck, cmdAPIUpdate},
}

// cmdAPICheck represents the "jiri api check" command.
var cmdAPICheck = &cmdline.Command{
	Runner:   jiri.RunnerFunc(runAPICheck),
	Name:     "check",
	Short:    "Check if any changes have been made to the public API",
	Long:     "Check if any changes have been made to the public API.",
	ArgsName: "<projects>",
	ArgsLong: "<projects> is a list of vanadium projects to check. If none are specified, all projects that require a public API check upon presubmit are checked.",
}

func readAPIFileContents(jirix *jiri.X, path string) (_ []byte, e error) {
	s := jirix.NewSeq()
	var buf bytes.Buffer
	file, err := s.Open(path)
	defer collect.Error(file.Close, &e)
	if err != nil {
		return nil, err
	}
	reader := bufio.NewReader(file)
	for {
		line, err := reader.ReadBytes('\n')
		if !commentRE.Match(line) {
			buf.Write(line)
		}
		if err == io.EOF {
			break
		} else if err != nil {
			return nil, err
		}
	}
	return buf.Bytes(), err
}

type packageChange struct {
	name          string
	projectName   string
	apiFilePath   string
	oldAPI        map[string]bool // set
	newAPI        map[string]bool // set
	newAPIContent []byte

	// If true, indicates that there was a problem reading the old API file.
	apiFileError error
}

// buildGotools builds the gotools binary and returns the path to the built
// binary and the function to call to clean up the built binary (always
// non-nil). If the binary could not be built, the empty string and a non-nil
// error are returned.
//
// If the gotools_bin flag is specified, that path, a no-op cleanup and a
// nil error are returned.
func buildGotools(jirix *jiri.X) (string, func() error, error) {
	nopCleanup := func() error { return nil }
	if gotoolsBinPathFlag != "" {
		return gotoolsBinPathFlag, nopCleanup, nil
	}

	// Determine the location of the gotools source.
	projects, _, err := project.ReadJiriManifest(jirix)
	if err != nil {
		return "", nopCleanup, err
	}

	project, err := projects.FindUnique("third_party")
	if err != nil {
		return "", nopCleanup, fmt.Errorf("error finding project %q: %v", "third_party", err)
	}
	newGoPath := filepath.Join(project.Path, "go")

	s := jirix.NewSeq()

	// Build the gotools binary.
	tempDir, err := s.TempDir("", "")
	if err != nil {
		return "", nopCleanup, err
	}
	cleanup := func() error { return jirix.NewSeq().RemoveAll(tempDir).Done() }

	gotoolsBin := filepath.Join(tempDir, "gotools")
	env := envvar.CopyMap(jirix.Env())
	env["GOPATH"] = newGoPath
	if err := s.Env(env).Last("go", "build", "-o", gotoolsBin, "github.com/visualfc/gotools"); err != nil {
		return "", cleanup, err
	}

	return gotoolsBin, cleanup, nil
}

// getCurrentAPI runs the gotools api command against the given directory and
// returns the bytes that should go into the .api file for that directory.
func getCurrentAPI(jirix *jiri.X, gotoolsBin, dir string) ([]byte, error) {
	rd, err := profilesreader.NewReader(jirix, readerFlags.ProfilesMode, readerFlags.DBFilename)
	if err != nil {
		return nil, err
	}
	rd.MergeEnvFromProfiles(readerFlags.MergePolicies, profiles.NativeTarget(), "jiri")
	s := jirix.NewSeq()
	var output bytes.Buffer
	if err := s.Capture(&output, nil).Env(rd.ToMap()).Last(gotoolsBin, "goapi", dir); err != nil {
		return nil, err
	}
	return output.Bytes(), nil
}

func isFailedAPICheckFatal(projectName string, apiCheckProjects map[string]struct{}, apiFileError error) bool {
	if runutil.IsNotExist(apiFileError) {
		if _, ok := apiCheckProjects[projectName]; !ok {
			return false
		}
	}
	return true
}

func shouldIgnoreFile(file string) bool {
	if !strings.HasSuffix(file, ".go") {
		return true
	}
	pathComponents := strings.Split(file, string(os.PathSeparator))
	for _, component := range pathComponents {
		if component == "testdata" || component == "internal" {
			return true
		}
	}
	return false
}

func splitLinesToSet(in []byte) map[string]bool {
	result := make(map[string]bool)
	scanner := bufio.NewScanner(bytes.NewReader(in))
	for scanner.Scan() {
		result[scanner.Text()] = true
	}
	return result
}

func packageName(path string) string {
	components := strings.Split(path, string(os.PathSeparator))
	for i, component := range components {
		if component == "src" {
			return strings.Join(components[i+1:], "/")
		}
	}
	return ""
}

func getPackageChanges(jirix *jiri.X, apiCheckProjects map[string]struct{}, args []string) (changes []packageChange, e error) {
	gotoolsBin, cleanup, err := buildGotools(jirix)
	if err != nil {
		return nil, err
	}
	defer collect.Error(cleanup, &e)
	projects, err := project.ParseNames(jirix, args, apiCheckProjects)
	if err != nil {
		return nil, err
	}
	for _, project := range projects {
		path := project.Path
		branch, err := gitutil.New(jirix.NewSeq(), gitutil.RootDirOpt(path)).CurrentBranchName()
		if err != nil {
			return nil, err
		}
		files, err := gitutil.New(jirix.NewSeq(), gitutil.RootDirOpt(path)).ModifiedFiles("master", branch)
		if err != nil {
			return nil, err
		}
		// Extract the directories for these files.
		dirs := make(map[string]bool) // set
		for _, file := range files {
			if !shouldIgnoreFile(file) {
				dirs[filepath.Join(path, filepath.Dir(file))] = true
			}
		}
		if len(dirs) == 0 {
			continue
		}
		for dir := range dirs {
			// Read the API state in the working directory.
			currentAPI, err := getCurrentAPI(jirix, gotoolsBin, dir)
			if err != nil {
				return nil, err
			}
			// Read the existing public API file.
			apiFilePath := filepath.Join(dir, ".api")
			apiFileContents, apiFileError := readAPIFileContents(jirix, apiFilePath)
			if apiFileError != nil {
				if runutil.IsNotExist(apiFileError) && len(currentAPI) == 0 {
					// The API file doesn't exist but the
					// public API in the working directory
					// is empty anyway.
					continue
				}
				if !isFailedAPICheckFatal(project.Name, apiCheckProjects, apiFileError) {
					// We couldn't read the API file, but this project doesn't
					// require one.  Just warn the user.
					fmt.Fprintf(jirix.Stderr(), "WARNING: could not read public API from %s: %v\n", apiFilePath, err)
					fmt.Fprintf(jirix.Stderr(), "WARNING: skipping public API check for %s\n", dir)
					continue
				}
			}
			if apiFileError != nil || !bytes.Equal(currentAPI, apiFileContents) {
				pkgName := packageName(dir)
				if pkgName == "" {
					pkgName = dir
				}
				// The user has changed the public API or we
				// couldn't read the public API in the first
				// place.
				changes = append(changes, packageChange{
					name:          pkgName,
					projectName:   project.Name,
					apiFilePath:   apiFilePath,
					oldAPI:        splitLinesToSet(apiFileContents),
					newAPI:        splitLinesToSet(currentAPI),
					newAPIContent: currentAPI,
					apiFileError:  apiFileError,
				})
			}
		}
	}
	return
}

func runAPICheck(jirix *jiri.X, args []string) error {
	return doAPICheck(jirix, args, detailedOutputFlag)
}

func printChangeSummary(out io.Writer, change packageChange, detailedOutput bool) {
	var removedEntries []string
	var addedEntries []string
	for entry, _ := range change.oldAPI {
		if !change.newAPI[entry] {
			removedEntries = append(removedEntries, entry)
		}
	}
	for entry, _ := range change.newAPI {
		if !change.oldAPI[entry] {
			addedEntries = append(addedEntries, entry)
		}
	}
	if detailedOutput {
		fmt.Fprintf(out, "Changes for package %s\n", change.name)
		if len(removedEntries) > 0 {
			fmt.Fprintf(out, "The following %d entries were removed:\n", len(removedEntries))
			for _, entry := range removedEntries {
				fmt.Fprintf(out, "\t%s\n", entry)
			}
		}
		if len(addedEntries) > 0 {
			fmt.Fprintf(out, "The following %d entries were added:\n", len(addedEntries))
			for _, entry := range addedEntries {
				fmt.Fprintf(out, "\t%s\n", entry)
			}
		}
	} else {
		fmt.Fprintf(out, "package %s: %d entries removed, %d entries added\n", change.name, len(removedEntries), len(addedEntries))
	}
}

func doAPICheck(jirix *jiri.X, args []string, detailedOutput bool) error {
	config, err := util.LoadConfig(jirix)
	if err != nil {
		return err
	}
	changes, err := getPackageChanges(jirix, config.APICheckProjects(), args)
	if err != nil {
		return err
	} else if len(changes) > 0 {
		for _, change := range changes {
			if change.apiFileError != nil {
				fmt.Fprintf(jirix.Stdout(), "ERROR: package %s: could not read the package's .api file: %v\n", change.name, change.apiFileError)
				fmt.Fprintf(jirix.Stdout(), "ERROR: a readable .api file is required for all packages in project %s\n", change.projectName)
			} else {
				printChangeSummary(jirix.Stdout(), change, detailedOutput)
			}
		}
	}
	return nil
}

// cmdAPIUpdate represents the "jiri api fix" command.
var cmdAPIUpdate = &cmdline.Command{
	Runner:   jiri.RunnerFunc(runAPIFix),
	Name:     "fix",
	Short:    "Update api files to reflect changes to the public API",
	Long:     "Update .api files to reflect changes to the public API.",
	ArgsName: "<projects>",
	ArgsLong: "<projects> is a list of vanadium projects to update. If none are specified, all project APIs are updated.",
}

func runAPIFix(jirix *jiri.X, args []string) error {
	config, err := util.LoadConfig(jirix)
	if err != nil {
		return err
	}
	changes, err := getPackageChanges(jirix, config.APICheckProjects(), args)
	if err != nil {
		return err
	}
	s := jirix.NewSeq()
	for _, change := range changes {
		if len(change.newAPIContent) == 0 {
			if _, err := s.Stat(change.apiFilePath); !runutil.IsNotExist(err) {
				if err != nil {
					return err
				}
				// No API contents? Remove the file.
				if err := s.RemoveAll(change.apiFilePath).Done(); err != nil {
					return err
				}
			}
		} else if err := s.WriteFile(change.apiFilePath, []byte(change.newAPIContent), 0644).Done(); err != nil {
			return fmt.Errorf("WriteFile(%s) failed: %v", change.apiFilePath, err)
		}
		fmt.Fprintf(jirix.Stdout(), "Updated %s.\n", change.apiFilePath)
	}
	return nil
}

func main() {
	cmdline.Main(cmdAPI)
}
<commit_msg>devtools: Replace ReadJiriManifest with LocalProjects<commit_after>// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// The following enables go generate to generate the doc.go file.
//go:generate go run $JIRI_ROOT/release/go/src/v.io/x/lib/cmdline/testdata/gendoc.go -env=CMDLINE_PREFIX=jiri .

package main

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"v.io/jiri/collect"
	"v.io/jiri/gitutil"
	"v.io/jiri/jiri"
	"v.io/jiri/profiles"
	"v.io/jiri/profiles/profilescmdline"
	"v.io/jiri/profiles/profilesreader"
	"v.io/jiri/project"
	"v.io/jiri/runutil"
	"v.io/jiri/tool"
	"v.io/jiri/util"
	"v.io/x/devtools/jiri-v23-profile/v23_profile"
	"v.io/x/lib/cmdline"
	"v.io/x/lib/envvar"
)

var (
	detailedOutputFlag bool
	gotoolsBinPathFlag string
	readerFlags        profilescmdline.ReaderFlagValues

	commentRE = regexp.MustCompile("^($|[:space:]*#)")
)

func init() {
	cmdAPICheck.Flags.BoolVar(&detailedOutputFlag, "detailed", true, "If true, shows each API change in an expanded form. Otherwise, only a summary is shown.")
	cmdAPI.Flags.StringVar(&gotoolsBinPathFlag, "gotools-bin", "", "The path to the gotools binary to use. If empty, gotools will be built if necessary.")
	profilescmdline.RegisterReaderFlags(&cmdAPI.Flags, &readerFlags, v23_profile.DefaultDBFilename)
	tool.InitializeProjectFlags(&cmdAPI.Flags)
	tool.InitializeRunFlags(&cmdAPI.Flags)
}

// cmdAPI represents the "jiri api" command.
var cmdAPI = &cmdline.Command{
	Name:     "api",
	Short:    "Manage vanadium public API",
	Long:     "Use this command to ensure that no unintended changes are made to the vanadium public API.",
	Children: []*cmdline.Command{cmdAPICheck, cmdAPIUpdate},
}

// cmdAPICheck represents the "jiri api check" command.
var cmdAPICheck = &cmdline.Command{
	Runner:   jiri.RunnerFunc(runAPICheck),
	Name:     "check",
	Short:    "Check if any changes have been made to the public API",
	Long:     "Check if any changes have been made to the public API.",
	ArgsName: "<projects>",
	ArgsLong: "<projects> is a list of vanadium projects to check. If none are specified, all projects that require a public API check upon presubmit are checked.",
}

func readAPIFileContents(jirix *jiri.X, path string) (_ []byte, e error) {
	s := jirix.NewSeq()
	var buf bytes.Buffer
	file, err := s.Open(path)
	defer collect.Error(file.Close, &e)
	if err != nil {
		return nil, err
	}
	reader := bufio.NewReader(file)
	for {
		line, err := reader.ReadBytes('\n')
		if !commentRE.Match(line) {
			buf.Write(line)
		}
		if err == io.EOF {
			break
		} else if err != nil {
			return nil, err
		}
	}
	return buf.Bytes(), err
}

type packageChange struct {
	name          string
	projectName   string
	apiFilePath   string
	oldAPI        map[string]bool // set
	newAPI        map[string]bool // set
	newAPIContent []byte

	// If true, indicates that there was a problem reading the old API file.
	apiFileError error
}

// buildGotools builds the gotools binary and returns the path to the built
// binary and the function to call to clean up the built binary (always
// non-nil). If the binary could not be built, the empty string and a non-nil
// error are returned.
//
// If the gotools_bin flag is specified, that path, a no-op cleanup and a
// nil error are returned.
func buildGotools(jirix *jiri.X) (string, func() error, error) {
	nopCleanup := func() error { return nil }
	if gotoolsBinPathFlag != "" {
		return gotoolsBinPathFlag, nopCleanup, nil
	}

	// Determine the location of the gotools source.
	projects, err := project.LocalProjects(jirix, project.FastScan)
	if err != nil {
		return "", nopCleanup, err
	}
	project, err := projects.FindUnique("third_party")
	if err != nil {
		return "", nopCleanup, fmt.Errorf("error finding project %q: %v", "third_party", err)
	}
	newGoPath := filepath.Join(project.Path, "go")

	s := jirix.NewSeq()

	// Build the gotools binary.
	tempDir, err := s.TempDir("", "")
	if err != nil {
		return "", nopCleanup, err
	}
	cleanup := func() error { return jirix.NewSeq().RemoveAll(tempDir).Done() }

	gotoolsBin := filepath.Join(tempDir, "gotools")
	env := envvar.CopyMap(jirix.Env())
	env["GOPATH"] = newGoPath
	if err := s.Env(env).Last("go", "build", "-o", gotoolsBin, "github.com/visualfc/gotools"); err != nil {
		return "", cleanup, err
	}

	return gotoolsBin, cleanup, nil
}

// getCurrentAPI runs the gotools api command against the given directory and
// returns the bytes that should go into the .api file for that directory.
func getCurrentAPI(jirix *jiri.X, gotoolsBin, dir string) ([]byte, error) {
	rd, err := profilesreader.NewReader(jirix, readerFlags.ProfilesMode, readerFlags.DBFilename)
	if err != nil {
		return nil, err
	}
	rd.MergeEnvFromProfiles(readerFlags.MergePolicies, profiles.NativeTarget(), "jiri")
	s := jirix.NewSeq()
	var output bytes.Buffer
	if err := s.Capture(&output, nil).Env(rd.ToMap()).Last(gotoolsBin, "goapi", dir); err != nil {
		return nil, err
	}
	return output.Bytes(), nil
}

func isFailedAPICheckFatal(projectName string, apiCheckProjects map[string]struct{}, apiFileError error) bool {
	if runutil.IsNotExist(apiFileError) {
		if _, ok := apiCheckProjects[projectName]; !ok {
			return false
		}
	}
	return true
}

func shouldIgnoreFile(file string) bool {
	if !strings.HasSuffix(file, ".go") {
		return true
	}
	pathComponents := strings.Split(file, string(os.PathSeparator))
	for _, component := range pathComponents {
		if component == "testdata" || component == "internal" {
			return true
		}
	}
	return false
}

func splitLinesToSet(in []byte) map[string]bool {
	result := make(map[string]bool)
	scanner := bufio.NewScanner(bytes.NewReader(in))
	for scanner.Scan() {
		result[scanner.Text()] = true
	}
	return result
}

func packageName(path string) string {
	components := strings.Split(path, string(os.PathSeparator))
	for i, component := range components {
		if component == "src" {
			return strings.Join(components[i+1:], "/")
		}
	}
	return ""
}

func getPackageChanges(jirix *jiri.X, apiCheckProjects map[string]struct{}, args []string) (changes []packageChange, e error) {
	gotoolsBin, cleanup, err := buildGotools(jirix)
	if err != nil {
		return nil, err
	}
	defer collect.Error(cleanup, &e)
	projects, err := project.ParseNames(jirix, args, apiCheckProjects)
	if err != nil {
		return nil, err
	}
	for _, project := range projects {
		path := project.Path
		branch, err := gitutil.New(jirix.NewSeq(), gitutil.RootDirOpt(path)).CurrentBranchName()
		if err != nil {
			return nil, err
		}
		files, err := gitutil.New(jirix.NewSeq(), gitutil.RootDirOpt(path)).ModifiedFiles("master", branch)
		if err != nil {
			return nil, err
		}
		// Extract the directories for these files.
		dirs := make(map[string]bool) // set
		for _, file := range files {
			if !shouldIgnoreFile(file) {
				dirs[filepath.Join(path, filepath.Dir(file))] = true
			}
		}
		if len(dirs) == 0 {
			continue
		}
		for dir := range dirs {
			// Read the API state in the working directory.
			currentAPI, err := getCurrentAPI(jirix, gotoolsBin, dir)
			if err != nil {
				return nil, err
			}
			// Read the existing public API file.
			apiFilePath := filepath.Join(dir, ".api")
			apiFileContents, apiFileError := readAPIFileContents(jirix, apiFilePath)
			if apiFileError != nil {
				if runutil.IsNotExist(apiFileError) && len(currentAPI) == 0 {
					// The API file doesn't exist but the
					// public API in the working directory
					// is empty anyway.
					continue
				}
				if !isFailedAPICheckFatal(project.Name, apiCheckProjects, apiFileError) {
					// We couldn't read the API file, but this project doesn't
					// require one.  Just warn the user.
					fmt.Fprintf(jirix.Stderr(), "WARNING: could not read public API from %s: %v\n", apiFilePath, err)
					fmt.Fprintf(jirix.Stderr(), "WARNING: skipping public API check for %s\n", dir)
					continue
				}
			}
			if apiFileError != nil || !bytes.Equal(currentAPI, apiFileContents) {
				pkgName := packageName(dir)
				if pkgName == "" {
					pkgName = dir
				}
				// The user has changed the public API or we
				// couldn't read the public API in the first
				// place.
				changes = append(changes, packageChange{
					name:          pkgName,
					projectName:   project.Name,
					apiFilePath:   apiFilePath,
					oldAPI:        splitLinesToSet(apiFileContents),
					newAPI:        splitLinesToSet(currentAPI),
					newAPIContent: currentAPI,
					apiFileError:  apiFileError,
				})
			}
		}
	}
	return
}

func runAPICheck(jirix *jiri.X, args []string) error {
	return doAPICheck(jirix, args, detailedOutputFlag)
}

func printChangeSummary(out io.Writer, change packageChange, detailedOutput bool) {
	var removedEntries []string
	var addedEntries []string
	for entry, _ := range change.oldAPI {
		if !change.newAPI[entry] {
			removedEntries = append(removedEntries, entry)
		}
	}
	for entry, _ := range change.newAPI {
		if !change.oldAPI[entry] {
			addedEntries = append(addedEntries, entry)
		}
	}
	if detailedOutput {
		fmt.Fprintf(out, "Changes for package %s\n", change.name)
		if len(removedEntries) > 0 {
			fmt.Fprintf(out, "The following %d entries were removed:\n", len(removedEntries))
			for _, entry := range removedEntries {
				fmt.Fprintf(out, "\t%s\n", entry)
			}
		}
		if len(addedEntries) > 0 {
			fmt.Fprintf(out, "The following %d entries were added:\n", len(addedEntries))
			for _, entry := range addedEntries {
				fmt.Fprintf(out, "\t%s\n", entry)
			}
		}
	} else {
		fmt.Fprintf(out, "package %s: %d entries removed, %d entries added\n", change.name, len(removedEntries), len(addedEntries))
	}
}

func doAPICheck(jirix *jiri.X, args []string, detailedOutput bool) error {
	config, err := util.LoadConfig(jirix)
	if err != nil {
		return err
	}
	changes, err := getPackageChanges(jirix, config.APICheckProjects(), args)
	if err != nil {
		return err
	} else if len(changes) > 0 {
		for _, change := range changes {
			if change.apiFileError != nil {
				fmt.Fprintf(jirix.Stdout(), "ERROR: package %s: could not read the package's .api file: %v\n", change.name, change.apiFileError)
				fmt.Fprintf(jirix.Stdout(), "ERROR: a readable .api file is required for all packages in project %s\n", change.projectName)
			} else {
				printChangeSummary(jirix.Stdout(), change, detailedOutput)
			}
		}
	}
	return nil
}

// cmdAPIUpdate represents the "jiri api fix" command.
var cmdAPIUpdate = &cmdline.Command{
	Runner:   jiri.RunnerFunc(runAPIFix),
	Name:     "fix",
	Short:    "Update api files to reflect changes to the public API",
	Long:     "Update .api files to reflect changes to the public API.",
	ArgsName: "<projects>",
	ArgsLong: "<projects> is a list of vanadium projects to update. If none are specified, all project APIs are updated.",
}

func runAPIFix(jirix *jiri.X, args []string) error {
	config, err := util.LoadConfig(jirix)
	if err != nil {
		return err
	}
	changes, err := getPackageChanges(jirix, config.APICheckProjects(), args)
	if err != nil {
		return err
	}
	s := jirix.NewSeq()
	for _, change := range changes {
		if len(change.newAPIContent) == 0 {
			if _, err := s.Stat(change.apiFilePath); !runutil.IsNotExist(err) {
				if err != nil {
					return err
				}
				// No API contents? Remove the file.
				if err := s.RemoveAll(change.apiFilePath).Done(); err != nil {
					return err
				}
			}
		} else if err := s.WriteFile(change.apiFilePath, []byte(change.newAPIContent), 0644).Done(); err != nil {
			return fmt.Errorf("WriteFile(%s) failed: %v", change.apiFilePath, err)
		}
		fmt.Fprintf(jirix.Stdout(), "Updated %s.\n", change.apiFilePath)
	}
	return nil
}

func main() {
	cmdline.Main(cmdAPI)
}
<|endoftext|>


<commit_before>package instance

import (
	"bytes"
	"fmt"
	"github.com/mitchellh/goamz/ec2"
	"github.com/mitchellh/multistep"
	"github.com/mitchellh/packer/packer"
	"text/template"
)

type bundleCmdData struct {
	AccountId    string
	Architecture string
	CertPath     string
	KeyPath      string
	PrivatePath  string
}

type StepBundleVolume struct{}

func (s *StepBundleVolume) Run(state map[string]interface{}) multistep.StepAction {
	comm := state["communicator"].(packer.Communicator)
	config := state["config"].(*Config)
	instance := state["instance"].(*ec2.Instance)
	ui := state["ui"].(packer.Ui)
	x509RemoteCertPath := state["x509RemoteCertPath"].(string)
	x509RemoteKeyPath := state["x509RemoteKeyPath"].(string)

	// Verify the AMI tools are available
	ui.Say("Checking for EC2 AMI tools...")
	cmd := &packer.RemoteCmd{Command: "ec2-ami-tools-version"}
	if err := comm.Start(cmd); err != nil {
		state["error"] = fmt.Errorf("Error checking for AMI tools: %s", err)
		ui.Error(state["error"].(error).Error())
		return multistep.ActionHalt
	}
	cmd.Wait()

	if cmd.ExitStatus != 0 {
		state["error"] = fmt.Errorf(
			"The EC2 AMI tools could not be detected. These must be manually\n" +
				"via a provisioner or some other means and are required for Packer\n" +
				"to create an instance-store AMI.")
		ui.Error(state["error"].(error).Error())
		return multistep.ActionHalt
	}

	// Bundle the volume
	var bundleCmd bytes.Buffer
	tData := bundleCmdData{
		AccountId:    config.AccountId,
		Architecture: instance.Architecture,
		CertPath:     x509RemoteCertPath,
		KeyPath:      x509RemoteKeyPath,
		PrivatePath:  config.X509UploadPath,
	}
	t := template.Must(template.New("bundleCmd").Parse(config.BundleVolCommand))
	t.Execute(&bundleCmd, tData)

	ui.Say("Bundling the volume...")
	cmd = new(packer.RemoteCmd)
	cmd.Command = bundleCmd.String()
	if err := cmd.StartWithUi(comm, ui); err != nil {
		state["error"] = fmt.Errorf("Error bundling volume: %s", err)
		ui.Error(state["error"].(error).Error())
		return multistep.ActionHalt
	}

	return multistep.ActionContinue
}

func (s *StepBundleVolume) Cleanup(map[string]interface{}) {}
<commit_msg>builder/amazon/instance: detect error bundling<commit_after>package instance

import (
	"bytes"
	"fmt"
	"github.com/mitchellh/goamz/ec2"
	"github.com/mitchellh/multistep"
	"github.com/mitchellh/packer/packer"
	"text/template"
)

type bundleCmdData struct {
	AccountId    string
	Architecture string
	CertPath     string
	KeyPath      string
	PrivatePath  string
}

type StepBundleVolume struct{}

func (s *StepBundleVolume) Run(state map[string]interface{}) multistep.StepAction {
	comm := state["communicator"].(packer.Communicator)
	config := state["config"].(*Config)
	instance := state["instance"].(*ec2.Instance)
	ui := state["ui"].(packer.Ui)
	x509RemoteCertPath := state["x509RemoteCertPath"].(string)
	x509RemoteKeyPath := state["x509RemoteKeyPath"].(string)

	// Verify the AMI tools are available
	ui.Say("Checking for EC2 AMI tools...")
	cmd := &packer.RemoteCmd{Command: "ec2-ami-tools-version"}
	if err := comm.Start(cmd); err != nil {
		state["error"] = fmt.Errorf("Error checking for AMI tools: %s", err)
		ui.Error(state["error"].(error).Error())
		return multistep.ActionHalt
	}
	cmd.Wait()

	if cmd.ExitStatus != 0 {
		state["error"] = fmt.Errorf(
			"The EC2 AMI tools could not be detected. These must be manually\n" +
				"via a provisioner or some other means and are required for Packer\n" +
				"to create an instance-store AMI.")
		ui.Error(state["error"].(error).Error())
		return multistep.ActionHalt
	}

	// Bundle the volume
	var bundleCmd bytes.Buffer
	tData := bundleCmdData{
		AccountId:    config.AccountId,
		Architecture: instance.Architecture,
		CertPath:     x509RemoteCertPath,
		KeyPath:      x509RemoteKeyPath,
		PrivatePath:  config.X509UploadPath,
	}
	t := template.Must(template.New("bundleCmd").Parse(config.BundleVolCommand))
	t.Execute(&bundleCmd, tData)

	ui.Say("Bundling the volume...")
	cmd = new(packer.RemoteCmd)
	cmd.Command = bundleCmd.String()
	if err := cmd.StartWithUi(comm, ui); err != nil {
		state["error"] = fmt.Errorf("Error bundling volume: %s", err)
		ui.Error(state["error"].(error).Error())
		return multistep.ActionHalt
	}

	if cmd.ExitStatus != 0 {
		state["error"] = fmt.Errorf(
			"Volume bundling failed. Please see the output above for more\n" +
				"details on what went wrong.")
		ui.Error(state["error"].(error).Error())
		return multistep.ActionHalt
	}

	return multistep.ActionContinue
}

func (s *StepBundleVolume) Cleanup(map[string]interface{}) {}
<|endoftext|>


<commit_before>package gossl

/*
#cgo pkg-config: openssl
#include "openssl/ssl.h"
#include "openssl/err.h"
extern void go_conn_put_error(const char*);
extern void set_errno(int);
*/
import "C"
import "io"
import "unsafe"
import "reflect"
import "syscall"
import "fmt"
import "net"

var _ = fmt.Println

//export go_conn_bio_write
func go_conn_bio_write(bio *C.BIO, buf *C.char, num C.int) C.int {
	var conn *Conn = (*Conn)(bio.ptr)
	var size int = int(num)
	data := GoSliceFromCString(buf, size)
	n, err := conn.conn.Write(data)
	if err != nil && err != io.EOF {
		//We expect either a syscall error
		//or a netOp error wrapping a syscall error
	TESTERR:
		switch err.(type) {
		case syscall.Errno:
			C.set_errno(C.int(err.(syscall.Errno)))
		case *net.OpError:
			err = err.(*net.OpError).Err
			break TESTERR
		}
		return C.int(-1)
	}
	return C.int(n)
}

//export go_conn_bio_read
func go_conn_bio_read(bio *C.BIO, buf *C.char, num C.int) C.int {
	var conn *Conn = (*Conn)(bio.ptr)
	var size int = int(num)
	data := GoSliceFromCString(buf, size)
	if n, err := conn.conn.Read(data); err == nil {
		return C.int(n)
	}
	if err == io.EOF || err == io.ErrUnexpectedEOF {
		return 0
	}
	//We expect either a syscall error
	//or a netOp error wrapping a syscall error
TESTERR:
	switch err.(type) {
	case syscall.Errno:
		C.set_errno(C.int(err.(syscall.Errno)))
	case *net.OpError:
		err = err.(*net.OpError).Err
		break TESTERR
	}
	return C.int(-1)
}

//export go_conn_bio_new
func go_conn_bio_new(bio *C.BIO) C.int {
	//we are initializing here
	bio.init = C.int(1)
	//see mem_new()
	bio.num = C.int(-1)
	bio.ptr = nil
	bio.flags = C.BIO_FLAGS_READ | C.BIO_FLAGS_WRITE
	return C.int(1)
}

//export go_conn_bio_free
func go_conn_bio_free(bio *C.BIO) C.int {
	var conn *Conn = (*Conn)(bio.ptr)
	conn.conn.Close()
	if C.int(bio.shutdown) != 0 {
		bio.ptr = nil
		bio.flags = 0
		bio.init = 0
	}
	return C.int(1)
}

//export go_conn_bio_ctrl
func go_conn_bio_ctrl(bio *C.BIO, cmd C.int, num C.long, ptr unsafe.Pointer) C.long {
	//always return operation not supported
	//http://www.openssl.org/docs/crypto/BIO_ctrl.html
	return C.long(1)
}

// Provides a zero copy interface for returning a go slice backed by a c array.
func GoSliceFromCString(cArray *C.char, size int) (cslice []byte) {
	//See http://code.google.com/p/go-wiki/wiki/cgo
	//It turns out it's really easy to
	//make a string from a *C.char and vise versa.
	//not so easy to write to a c array.
	sliceHeader := (*reflect.SliceHeader)((unsafe.Pointer(&cslice)))
	sliceHeader.Cap = size
	sliceHeader.Len = size
	sliceHeader.Data = uintptr(unsafe.Pointer(cArray))
	return
}
<commit_msg>bio_s_conn: fix undefined err<commit_after>package gossl

/*
#cgo pkg-config: openssl
#include "openssl/ssl.h"
#include "openssl/err.h"
extern void go_conn_put_error(const char*);
extern void set_errno(int);
*/
import "C"
import "io"
import "unsafe"
import "reflect"
import "syscall"
import "fmt"
import "net"

var _ = fmt.Println

//export go_conn_bio_write
func go_conn_bio_write(bio *C.BIO, buf *C.char, num C.int) C.int {
	var conn *Conn = (*Conn)(bio.ptr)
	var size int = int(num)
	data := GoSliceFromCString(buf, size)
	n, err := conn.conn.Write(data)
	if err != nil && err != io.EOF {
		//We expect either a syscall error
		//or a netOp error wrapping a syscall error
	TESTERR:
		switch err.(type) {
		case syscall.Errno:
			C.set_errno(C.int(err.(syscall.Errno)))
		case *net.OpError:
			err = err.(*net.OpError).Err
			break TESTERR
		}
		return C.int(-1)
	}
	return C.int(n)
}

//export go_conn_bio_read
func go_conn_bio_read(bio *C.BIO, buf *C.char, num C.int) C.int {
	var conn *Conn = (*Conn)(bio.ptr)
	var size int = int(num)
	data := GoSliceFromCString(buf, size)
	n, err := conn.conn.Read(data)
	if err == nil {
		return C.int(n)
	}
	if err == io.EOF || err == io.ErrUnexpectedEOF {
		return 0
	}
	//We expect either a syscall error
	//or a netOp error wrapping a syscall error
TESTERR:
	switch err.(type) {
	case syscall.Errno:
		C.set_errno(C.int(err.(syscall.Errno)))
	case *net.OpError:
		err = err.(*net.OpError).Err
		break TESTERR
	}
	return C.int(-1)
}

//export go_conn_bio_new
func go_conn_bio_new(bio *C.BIO) C.int {
	//we are initializing here
	bio.init = C.int(1)
	//see mem_new()
	bio.num = C.int(-1)
	bio.ptr = nil
	bio.flags = C.BIO_FLAGS_READ | C.BIO_FLAGS_WRITE
	return C.int(1)
}

//export go_conn_bio_free
func go_conn_bio_free(bio *C.BIO) C.int {
	var conn *Conn = (*Conn)(bio.ptr)
	conn.conn.Close()
	if C.int(bio.shutdown) != 0 {
		bio.ptr = nil
		bio.flags = 0
		bio.init = 0
	}
	return C.int(1)
}

//export go_conn_bio_ctrl
func go_conn_bio_ctrl(bio *C.BIO, cmd C.int, num C.long, ptr unsafe.Pointer) C.long {
	//always return operation not supported
	//http://www.openssl.org/docs/crypto/BIO_ctrl.html
	return C.long(1)
}

// Provides a zero copy interface for returning a go slice backed by a c array.
func GoSliceFromCString(cArray *C.char, size int) (cslice []byte) {
	//See http://code.google.com/p/go-wiki/wiki/cgo
	//It turns out it's really easy to
	//make a string from a *C.char and vise versa.
	//not so easy to write to a c array.
	sliceHeader := (*reflect.SliceHeader)((unsafe.Pointer(&cslice)))
	sliceHeader.Cap = size
	sliceHeader.Len = size
	sliceHeader.Data = uintptr(unsafe.Pointer(cArray))
	return
}
<|endoftext|>


<commit_before>package main

import (
	"testing"
)

func TestNewBlock(t *testing.T) {
	startApplication()

	// Register on volume
	b := datastore.NewBlock()

	// Persist
	b.Persist()

	// Encode
	b.ErasureEncoding()

	// Persist with encoding
	b.Persist()
}
<commit_msg>Validate data shard integrity on erasure coding<commit_after>package main

import (
	"crypto/md5"
	"fmt"
	"testing"
)

func TestNewBlock(t *testing.T) {
	startApplication()

	// Register on volume
	b := datastore.NewBlock()

	// Persist
	b.Persist()

	// Calculate hashes
	preHash := make([]string, 10)
	for i, ds := range b.DataShards {
		h := md5.New()
		h.Write(ds.Contents().Bytes())
		preHash[i] = fmt.Sprintf("%x", h.Sum(nil))
	}

	// Encode
	b.ErasureEncoding()

	// Validate hashes after are equal, meaning the data has not changed
	for i, ds := range b.DataShards {
		h := md5.New()
		h.Write(ds.Contents().Bytes())
		if preHash[i] != fmt.Sprintf("%x", h.Sum(nil)) {
			panic("Erasure encoding has changed file contents of data partition")
		}
	}

	// Persist with encoding
	b.Persist()
}
<|endoftext|>


<commit_before>package vault

import (
	"encoding/json"
	"errors"
	"fmt"
	"strings"
	"sync"

	"github.com/hashicorp/vault/logical"
)

const (
	// coreMountConfigPath is used to store the mount configuration.
	// Mounts are protected within the Vault itself, which means they
	// can only be viewed or modified after an unseal.
	coreMountConfigPath = "core/mounts"

	// backendBarrierPrefix is the prefix to the UUID used in the
	// barrier view for the backends.
	backendBarrierPrefix = "logical/"

	// systemBarrierPrefix is sthe prefix used for the
	// system logical backend.
	systemBarrierPrefix = "sys/"
)

var (
	// loadMountsFailed if loadMounts encounters an error
	loadMountsFailed = errors.New("failed to setup mount table")

	// protectedMounts cannot be remounted
	protectedMounts = []string{
		"audit/",
		"auth/",
		"sys/",
	}
)

// MountTable is used to represent the internal mount table
type MountTable struct {
	// This lock should be held whenever modifying the Entries field.
	sync.RWMutex

	Entries []*MountEntry `json:"entries"`
}

// Returns a deep copy of the mount table
func (t *MountTable) Clone() *MountTable {
	mt := &MountTable{
		Entries: make([]*MountEntry, len(t.Entries)),
	}
	for i, e := range t.Entries {
		mt.Entries[i] = e.Clone()
	}
	return mt
}

// MountEntry is used to represent a mount table entry
type MountEntry struct {
	Path        string            `json:"path"`              // Mount Path
	Type        string            `json:"type"`              // Logical backend Type
	Description string            `json:"description"`       // User-provided description
	UUID        string            `json:"uuid"`              // Barrier view UUID
	Options     map[string]string `json:"options"`           // Backend configuration
	Tainted     bool              `json:"tainted,omitempty"` // Set as a Write-Ahead flag for unmount/remount
}

// Returns a deep copy of the mount entry
func (e *MountEntry) Clone() *MountEntry {
	optClone := make(map[string]string)
	for k, v := range e.Options {
		optClone[k] = v
	}
	return &MountEntry{
		Path:        e.Path,
		Type:        e.Type,
		Description: e.Description,
		UUID:        e.UUID,
		Options:     optClone,
	}
}

// Mount is used to mount a new backend to the mount table.
func (c *Core) mount(me *MountEntry) error {
	c.mounts.Lock()
	defer c.mounts.Unlock()

	// Ensure we end the path in a slash
	if !strings.HasSuffix(me.Path, "/") {
		me.Path += "/"
	}

	// Prevent protected paths from being unmounted
	for _, p := range protectedMounts {
		if strings.HasPrefix(me.Path, p) {
			return fmt.Errorf("cannot mount '%s'", me.Path)
		}
	}

	// Verify there is no conflicting mount
	if match := c.router.MatchingMount(me.Path); match != "" {
		return fmt.Errorf("existing mount at '%s'", match)
	}

	// Lookup the new backend
	backend, err := c.newLogicalBackend(me.Type, nil)
	if err != nil {
		return err
	}

	// Generate a new UUID and view
	me.UUID = generateUUID()
	view := NewBarrierView(c.barrier, backendBarrierPrefix+me.UUID+"/")

	// Update the mount table
	newTable := c.mounts.Clone()
	newTable.Entries = append(newTable.Entries, me)
	if err := c.persistMounts(newTable); err != nil {
		return errors.New("failed to update mount table")
	}
	c.mounts = newTable

	// Mount the backend
	if err := c.router.Mount(backend, me.Path, me.UUID, view); err != nil {
		return err
	}
	c.logger.Printf("[INFO] core: mounted '%s' type: %s", me.Path, me.Type)
	return nil
}

// Unmount is used to unmount a path.
func (c *Core) unmount(path string) error {
	c.mounts.Lock()
	defer c.mounts.Unlock()

	// Ensure we end the path in a slash
	if !strings.HasSuffix(path, "/") {
		path += "/"
	}

	// Prevent protected paths from being unmounted
	for _, p := range protectedMounts {
		if strings.HasPrefix(path, p) {
			return fmt.Errorf("cannot unmount '%s'", path)
		}
	}

	// Verify exact match of the route
	match := c.router.MatchingMount(path)
	if match == "" || path != match {
		return fmt.Errorf("no matching mount")
	}

	// Store the view for this backend
	view := c.router.MatchingView(path)

	// Mark the entry as tainted
	if err := c.taintMountEntry(path); err != nil {
		return err
	}

	// Taint the router path to prevent routing
	if err := c.router.Taint(path); err != nil {
		return err
	}

	// Invoke the rollback manager a final time
	if err := c.rollback.Rollback(path); err != nil {
		return err
	}

	// Revoke all the dynamic keys
	if err := c.expiration.RevokePrefix(path); err != nil {
		return err
	}

	// Unmount the backend entirely
	if err := c.router.Unmount(path); err != nil {
		return err
	}

	// Clear the data in the view
	if err := ClearView(view); err != nil {
		return err
	}

	// Remove the mount table entry
	if err := c.removeMountEntry(path); err != nil {
		return err
	}
	c.logger.Printf("[INFO] core: unmounted '%s'", path)
	return nil
}

// removeMountEntry is used to remove an entry from the mount table
func (c *Core) removeMountEntry(path string) error {
	// Remove the entry from the mount table
	newTable := c.mounts.Clone()
	n := len(newTable.Entries)
	for i := 0; i < n; i++ {
		if newTable.Entries[i].Path == path {
			newTable.Entries[i], newTable.Entries[n-1] = newTable.Entries[n-1], nil
			newTable.Entries = newTable.Entries[:n-1]
			break
		}
	}

	// Update the mount table
	if err := c.persistMounts(newTable); err != nil {
		return errors.New("failed to update mount table")
	}
	c.mounts = newTable
	return nil
}

// taintMountEntry is used to mark an entry in the mount table as tainted
func (c *Core) taintMountEntry(path string) error {
	// Remove the entry from the mount table
	newTable := c.mounts.Clone()
	n := len(newTable.Entries)
	for i := 0; i < n; i++ {
		if newTable.Entries[i].Path == path {
			newTable.Entries[i].Tainted = true
			break
		}
	}

	// Update the mount table
	if err := c.persistMounts(newTable); err != nil {
		return errors.New("failed to update mount table")
	}
	c.mounts = newTable
	return nil
}

// Remount is used to remount a path at a new mount point.
func (c *Core) remount(src, dst string) error {
	c.mounts.Lock()
	defer c.mounts.Unlock()

	// Ensure we end the path in a slash
	if !strings.HasSuffix(src, "/") {
		src += "/"
	}
	if !strings.HasSuffix(dst, "/") {
		dst += "/"
	}

	// Prevent protected paths from being remounted
	for _, p := range protectedMounts {
		if strings.HasPrefix(src, p) {
			return fmt.Errorf("cannot remount '%s'", src)
		}
	}

	// Verify exact match of the route
	match := c.router.MatchingMount(src)
	if match == "" || src != match {
		return fmt.Errorf("no matching mount at '%s'", src)
	}

	// Verify there is no conflicting mount
	if match := c.router.MatchingMount(dst); match != "" {
		return fmt.Errorf("existing mount at '%s'", match)
	}

	// Mark the entry as tainted
	if err := c.taintMountEntry(src); err != nil {
		return err
	}

	// Taint the router path to prevent routing
	if err := c.router.Taint(src); err != nil {
		return err
	}

	// Invoke the rollback manager a final time
	if err := c.rollback.Rollback(src); err != nil {
		return err
	}

	// Revoke all the dynamic keys
	if err := c.expiration.RevokePrefix(src); err != nil {
		return err
	}

	// Update the entry in the mount table
	newTable := c.mounts.Clone()
	for _, ent := range newTable.Entries {
		if ent.Path == src {
			ent.Path = dst
			ent.Tainted = false
			break
		}
	}

	// Update the mount table
	if err := c.persistMounts(newTable); err != nil {
		return errors.New("failed to update mount table")
	}
	c.mounts = newTable

	// Remount the backend
	if err := c.router.Remount(src, dst); err != nil {
		return err
	}

	// Un-taint the path
	if err := c.router.Untaint(dst); err != nil {
		return err
	}

	c.logger.Printf("[INFO] core: remounted '%s' to '%s'", src, dst)
	return nil
}

// loadMounts is invoked as part of postUnseal to load the mount table
func (c *Core) loadMounts() error {
	// Load the existing mount table
	raw, err := c.barrier.Get(coreMountConfigPath)
	if err != nil {
		c.logger.Printf("[ERR] core: failed to read mount table: %v", err)
		return loadMountsFailed
	}
	if raw != nil {
		c.mounts = &MountTable{}
		if err := json.Unmarshal(raw.Value, c.mounts); err != nil {
			c.logger.Printf("[ERR] core: failed to decode mount table: %v", err)
			return loadMountsFailed
		}
	}

	// Done if we have restored the mount table
	if c.mounts != nil {
		return nil
	}

	// Create and persist the default mount table
	c.mounts = defaultMountTable()
	if err := c.persistMounts(c.mounts); err != nil {
		return loadMountsFailed
	}
	return nil
}

// persistMounts is used to persist the mount table after modification
func (c *Core) persistMounts(table *MountTable) error {
	// Marshal the table
	raw, err := json.Marshal(table)
	if err != nil {
		c.logger.Printf("[ERR] core: failed to encode mount table: %v", err)
		return err
	}

	// Create an entry
	entry := &Entry{
		Key:   coreMountConfigPath,
		Value: raw,
	}

	// Write to the physical backend
	if err := c.barrier.Put(entry); err != nil {
		c.logger.Printf("[ERR] core: failed to persist mount table: %v", err)
		return err
	}
	return nil
}

// setupMounts is invoked after we've loaded the mount table to
// initialize the logical backends and setup the router
func (c *Core) setupMounts() error {
	var backend logical.Backend
	var view *BarrierView
	var err error
	for _, entry := range c.mounts.Entries {
		// Initialize the backend, special casing for system
		barrierPath := backendBarrierPrefix + entry.UUID + "/"
		if entry.Type == "system" {
			barrierPath = systemBarrierPrefix
		}

		backend, err = c.newLogicalBackend(entry.Type, nil)
		if err != nil {
			c.logger.Printf(
				"[ERR] core: failed to create mount entry %#v: %v",
				entry, err)
			return loadMountsFailed
		}

		// Create a barrier view using the UUID
		view = NewBarrierView(c.barrier, barrierPath)

		if entry.Type == "system" {
			c.systemView = view
		}

		// Mount the backend
		err = c.router.Mount(backend, entry.Path, entry.UUID, view)
		if err != nil {
			c.logger.Printf("[ERR] core: failed to mount entry %#v: %v", entry, err)
			return loadMountsFailed
		}

		// Ensure the path is tainted if set in the mount table
		if entry.Tainted {
			c.router.Taint(entry.Path)
		}
	}
	return nil
}

// unloadMounts is used before we seal the vault to reset the mounts to
// their unloaded state. This is reversed by load and setup mounts.
func (c *Core) unloadMounts() error {
	c.mounts = nil
	c.router = NewRouter()
	c.systemView = nil
	return nil
}

// newLogicalBackend is used to create and configure a new logical backend by name
func (c *Core) newLogicalBackend(t string, conf map[string]string) (logical.Backend, error) {
	f, ok := c.logicalBackends[t]
	if !ok {
		return nil, fmt.Errorf("unknown backend type: %s", t)
	}

	return f(conf)
}

// defaultMountTable creates a default mount table
func defaultMountTable() *MountTable {
	table := &MountTable{}
	genericMount := &MountEntry{
		Path:        "secret/",
		Type:        "generic",
		Description: "generic secret storage",
		UUID:        generateUUID(),
	}
	sysMount := &MountEntry{
		Path:        "sys/",
		Type:        "system",
		Description: "system endpoints used for control, policy and debugging",
		UUID:        generateUUID(),
	}
	table.Entries = append(table.Entries, genericMount)
	table.Entries = append(table.Entries, sysMount)
	return table
}
<commit_msg>vault: Refactor common methods<commit_after>package vault

import (
	"encoding/json"
	"errors"
	"fmt"
	"strings"
	"sync"

	"github.com/hashicorp/vault/logical"
)

const (
	// coreMountConfigPath is used to store the mount configuration.
	// Mounts are protected within the Vault itself, which means they
	// can only be viewed or modified after an unseal.
	coreMountConfigPath = "core/mounts"

	// backendBarrierPrefix is the prefix to the UUID used in the
	// barrier view for the backends.
	backendBarrierPrefix = "logical/"

	// systemBarrierPrefix is sthe prefix used for the
	// system logical backend.
	systemBarrierPrefix = "sys/"
)

var (
	// loadMountsFailed if loadMounts encounters an error
	loadMountsFailed = errors.New("failed to setup mount table")

	// protectedMounts cannot be remounted
	protectedMounts = []string{
		"audit/",
		"auth/",
		"sys/",
	}
)

// MountTable is used to represent the internal mount table
type MountTable struct {
	// This lock should be held whenever modifying the Entries field.
	sync.RWMutex

	Entries []*MountEntry `json:"entries"`
}

// Returns a deep copy of the mount table
func (t *MountTable) Clone() *MountTable {
	mt := &MountTable{
		Entries: make([]*MountEntry, len(t.Entries)),
	}
	for i, e := range t.Entries {
		mt.Entries[i] = e.Clone()
	}
	return mt
}

// Find is used to lookup an entry
func (t *MountTable) Find(path string) *MountEntry {
	n := len(t.Entries)
	for i := 0; i < n; i++ {
		if t.Entries[i].Path == path {
			return t.Entries[i]
		}
	}
	return nil
}

// SetTaint is used to set the taint on given entry
func (t *MountTable) SetTaint(path string, value bool) {
	n := len(t.Entries)
	for i := 0; i < n; i++ {
		if t.Entries[i].Path == path {
			t.Entries[i].Tainted = value
			return
		}
	}
}

// Remove is used to remove a given path entry
func (t *MountTable) Remove(path string) {
	n := len(t.Entries)
	for i := 0; i < n; i++ {
		if t.Entries[i].Path == path {
			t.Entries[i], t.Entries[n-1] = t.Entries[n-1], nil
			t.Entries = t.Entries[:n-1]
			return
		}
	}
}

// MountEntry is used to represent a mount table entry
type MountEntry struct {
	Path        string            `json:"path"`              // Mount Path
	Type        string            `json:"type"`              // Logical backend Type
	Description string            `json:"description"`       // User-provided description
	UUID        string            `json:"uuid"`              // Barrier view UUID
	Options     map[string]string `json:"options"`           // Backend configuration
	Tainted     bool              `json:"tainted,omitempty"` // Set as a Write-Ahead flag for unmount/remount
}

// Returns a deep copy of the mount entry
func (e *MountEntry) Clone() *MountEntry {
	optClone := make(map[string]string)
	for k, v := range e.Options {
		optClone[k] = v
	}
	return &MountEntry{
		Path:        e.Path,
		Type:        e.Type,
		Description: e.Description,
		UUID:        e.UUID,
		Options:     optClone,
	}
}

// Mount is used to mount a new backend to the mount table.
func (c *Core) mount(me *MountEntry) error {
	c.mounts.Lock()
	defer c.mounts.Unlock()

	// Ensure we end the path in a slash
	if !strings.HasSuffix(me.Path, "/") {
		me.Path += "/"
	}

	// Prevent protected paths from being unmounted
	for _, p := range protectedMounts {
		if strings.HasPrefix(me.Path, p) {
			return fmt.Errorf("cannot mount '%s'", me.Path)
		}
	}

	// Verify there is no conflicting mount
	if match := c.router.MatchingMount(me.Path); match != "" {
		return fmt.Errorf("existing mount at '%s'", match)
	}

	// Lookup the new backend
	backend, err := c.newLogicalBackend(me.Type, nil)
	if err != nil {
		return err
	}

	// Generate a new UUID and view
	me.UUID = generateUUID()
	view := NewBarrierView(c.barrier, backendBarrierPrefix+me.UUID+"/")

	// Update the mount table
	newTable := c.mounts.Clone()
	newTable.Entries = append(newTable.Entries, me)
	if err := c.persistMounts(newTable); err != nil {
		return errors.New("failed to update mount table")
	}
	c.mounts = newTable

	// Mount the backend
	if err := c.router.Mount(backend, me.Path, me.UUID, view); err != nil {
		return err
	}
	c.logger.Printf("[INFO] core: mounted '%s' type: %s", me.Path, me.Type)
	return nil
}

// Unmount is used to unmount a path.
func (c *Core) unmount(path string) error {
	c.mounts.Lock()
	defer c.mounts.Unlock()

	// Ensure we end the path in a slash
	if !strings.HasSuffix(path, "/") {
		path += "/"
	}

	// Prevent protected paths from being unmounted
	for _, p := range protectedMounts {
		if strings.HasPrefix(path, p) {
			return fmt.Errorf("cannot unmount '%s'", path)
		}
	}

	// Verify exact match of the route
	match := c.router.MatchingMount(path)
	if match == "" || path != match {
		return fmt.Errorf("no matching mount")
	}

	// Store the view for this backend
	view := c.router.MatchingView(path)

	// Mark the entry as tainted
	if err := c.taintMountEntry(path); err != nil {
		return err
	}

	// Taint the router path to prevent routing
	if err := c.router.Taint(path); err != nil {
		return err
	}

	// Invoke the rollback manager a final time
	if err := c.rollback.Rollback(path); err != nil {
		return err
	}

	// Revoke all the dynamic keys
	if err := c.expiration.RevokePrefix(path); err != nil {
		return err
	}

	// Unmount the backend entirely
	if err := c.router.Unmount(path); err != nil {
		return err
	}

	// Clear the data in the view
	if err := ClearView(view); err != nil {
		return err
	}

	// Remove the mount table entry
	if err := c.removeMountEntry(path); err != nil {
		return err
	}
	c.logger.Printf("[INFO] core: unmounted '%s'", path)
	return nil
}

// removeMountEntry is used to remove an entry from the mount table
func (c *Core) removeMountEntry(path string) error {
	// Remove the entry from the mount table
	newTable := c.mounts.Clone()
	newTable.Remove(path)

	// Update the mount table
	if err := c.persistMounts(newTable); err != nil {
		return errors.New("failed to update mount table")
	}
	c.mounts = newTable
	return nil
}

// taintMountEntry is used to mark an entry in the mount table as tainted
func (c *Core) taintMountEntry(path string) error {
	// Remove the entry from the mount table
	newTable := c.mounts.Clone()
	newTable.SetTaint(path, true)

	// Update the mount table
	if err := c.persistMounts(newTable); err != nil {
		return errors.New("failed to update mount table")
	}
	c.mounts = newTable
	return nil
}

// Remount is used to remount a path at a new mount point.
func (c *Core) remount(src, dst string) error {
	c.mounts.Lock()
	defer c.mounts.Unlock()

	// Ensure we end the path in a slash
	if !strings.HasSuffix(src, "/") {
		src += "/"
	}
	if !strings.HasSuffix(dst, "/") {
		dst += "/"
	}

	// Prevent protected paths from being remounted
	for _, p := range protectedMounts {
		if strings.HasPrefix(src, p) {
			return fmt.Errorf("cannot remount '%s'", src)
		}
	}

	// Verify exact match of the route
	match := c.router.MatchingMount(src)
	if match == "" || src != match {
		return fmt.Errorf("no matching mount at '%s'", src)
	}

	// Verify there is no conflicting mount
	if match := c.router.MatchingMount(dst); match != "" {
		return fmt.Errorf("existing mount at '%s'", match)
	}

	// Mark the entry as tainted
	if err := c.taintMountEntry(src); err != nil {
		return err
	}

	// Taint the router path to prevent routing
	if err := c.router.Taint(src); err != nil {
		return err
	}

	// Invoke the rollback manager a final time
	if err := c.rollback.Rollback(src); err != nil {
		return err
	}

	// Revoke all the dynamic keys
	if err := c.expiration.RevokePrefix(src); err != nil {
		return err
	}

	// Update the entry in the mount table
	newTable := c.mounts.Clone()
	for _, ent := range newTable.Entries {
		if ent.Path == src {
			ent.Path = dst
			ent.Tainted = false
			break
		}
	}

	// Update the mount table
	if err := c.persistMounts(newTable); err != nil {
		return errors.New("failed to update mount table")
	}
	c.mounts = newTable

	// Remount the backend
	if err := c.router.Remount(src, dst); err != nil {
		return err
	}

	// Un-taint the path
	if err := c.router.Untaint(dst); err != nil {
		return err
	}

	c.logger.Printf("[INFO] core: remounted '%s' to '%s'", src, dst)
	return nil
}

// loadMounts is invoked as part of postUnseal to load the mount table
func (c *Core) loadMounts() error {
	// Load the existing mount table
	raw, err := c.barrier.Get(coreMountConfigPath)
	if err != nil {
		c.logger.Printf("[ERR] core: failed to read mount table: %v", err)
		return loadMountsFailed
	}
	if raw != nil {
		c.mounts = &MountTable{}
		if err := json.Unmarshal(raw.Value, c.mounts); err != nil {
			c.logger.Printf("[ERR] core: failed to decode mount table: %v", err)
			return loadMountsFailed
		}
	}

	// Done if we have restored the mount table
	if c.mounts != nil {
		return nil
	}

	// Create and persist the default mount table
	c.mounts = defaultMountTable()
	if err := c.persistMounts(c.mounts); err != nil {
		return loadMountsFailed
	}
	return nil
}

// persistMounts is used to persist the mount table after modification
func (c *Core) persistMounts(table *MountTable) error {
	// Marshal the table
	raw, err := json.Marshal(table)
	if err != nil {
		c.logger.Printf("[ERR] core: failed to encode mount table: %v", err)
		return err
	}

	// Create an entry
	entry := &Entry{
		Key:   coreMountConfigPath,
		Value: raw,
	}

	// Write to the physical backend
	if err := c.barrier.Put(entry); err != nil {
		c.logger.Printf("[ERR] core: failed to persist mount table: %v", err)
		return err
	}
	return nil
}

// setupMounts is invoked after we've loaded the mount table to
// initialize the logical backends and setup the router
func (c *Core) setupMounts() error {
	var backend logical.Backend
	var view *BarrierView
	var err error
	for _, entry := range c.mounts.Entries {
		// Initialize the backend, special casing for system
		barrierPath := backendBarrierPrefix + entry.UUID + "/"
		if entry.Type == "system" {
			barrierPath = systemBarrierPrefix
		}

		backend, err = c.newLogicalBackend(entry.Type, nil)
		if err != nil {
			c.logger.Printf(
				"[ERR] core: failed to create mount entry %#v: %v",
				entry, err)
			return loadMountsFailed
		}

		// Create a barrier view using the UUID
		view = NewBarrierView(c.barrier, barrierPath)

		if entry.Type == "system" {
			c.systemView = view
		}

		// Mount the backend
		err = c.router.Mount(backend, entry.Path, entry.UUID, view)
		if err != nil {
			c.logger.Printf("[ERR] core: failed to mount entry %#v: %v", entry, err)
			return loadMountsFailed
		}

		// Ensure the path is tainted if set in the mount table
		if entry.Tainted {
			c.router.Taint(entry.Path)
		}
	}
	return nil
}

// unloadMounts is used before we seal the vault to reset the mounts to
// their unloaded state. This is reversed by load and setup mounts.
func (c *Core) unloadMounts() error {
	c.mounts = nil
	c.router = NewRouter()
	c.systemView = nil
	return nil
}

// newLogicalBackend is used to create and configure a new logical backend by name
func (c *Core) newLogicalBackend(t string, conf map[string]string) (logical.Backend, error) {
	f, ok := c.logicalBackends[t]
	if !ok {
		return nil, fmt.Errorf("unknown backend type: %s", t)
	}

	return f(conf)
}

// defaultMountTable creates a default mount table
func defaultMountTable() *MountTable {
	table := &MountTable{}
	genericMount := &MountEntry{
		Path:        "secret/",
		Type:        "generic",
		Description: "generic secret storage",
		UUID:        generateUUID(),
	}
	sysMount := &MountEntry{
		Path:        "sys/",
		Type:        "system",
		Description: "system endpoints used for control, policy and debugging",
		UUID:        generateUUID(),
	}
	table.Entries = append(table.Entries, genericMount)
	table.Entries = append(table.Entries, sysMount)
	return table
}
<|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 main

import (
	"fmt"
	"os"

	"github.com/docker/compose-cli/aci/etchosts"
)

const hosts = "/etc/hosts"

func main() {
	if len(os.Args) < 2 {
		fmt.Fprint(os.Stderr, "usage: hosts HOSTNAME [HOSTNAME]")
		os.Exit(1)
	}

	err := etchosts.SetHostNames(hosts, os.Args[1:]...)
	if err != nil {
		fmt.Fprint(os.Stderr, err.Error())
		os.Exit(1)
	}

	// ACI restart policy is currently at container group level, cannot let the sidecar terminate quietly once /etc/hosts has been edited
	// Pricing is done at the container group level so letting the sidecar container "sleep" should not impact the price for the whole group
	fmt.Scanln() // pause
}
<commit_msg>fmt.Scanln() does not wait forever… waiting for process termination signal <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 main

import (
	"fmt"
	"github.com/docker/compose-cli/aci/etchosts"
	"os"
	"os/signal"
	"syscall"
)

const hosts = "/etc/hosts"

func main() {
	if len(os.Args) < 2 {
		fmt.Fprint(os.Stderr, "usage: hosts HOSTNAME [HOSTNAME]")
		os.Exit(1)
	}

	err := etchosts.SetHostNames(hosts, os.Args[1:]...)
	if err != nil {
		fmt.Fprint(os.Stderr, err.Error())
		os.Exit(1)
	}

	// ACI restart policy is currently at container group level, cannot let the sidecar terminate quietly once /etc/hosts has been edited
	// Pause forever (until someone explicitely terminates this process ; go is not happy to stop all goroutines otherwise)
	exitSignal := make(chan os.Signal)
	signal.Notify(exitSignal, syscall.SIGINT, syscall.SIGTERM)
	<-exitSignal
}
<|endoftext|>


<commit_before>// Copyright 2019 PingCAP, 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package expensivequery

import (
	"testing"
	"time"

	. "github.com/pingcap/check"
	"github.com/pingcap/tidb/sessionctx/stmtctx"
	"github.com/pingcap/tidb/util"
	"github.com/pingcap/tidb/util/memory"
	"github.com/pingcap/tidb/util/testleak"
)

func TestT(t *testing.T) {
	TestingT(t)
}

type testSuite struct{}

func (s *testSuite) SetUpSuite(c *C)    {}
func (s *testSuite) TearDownSuite(c *C) {}
func (s *testSuite) SetUpTest(c *C)     { testleak.BeforeTest() }
func (s *testSuite) TearDownTest(c *C)  { testleak.AfterTest(c)() }

var _ = Suite(&testSuite{})

func (s *testSuite) TestLogFormat(c *C) {
	mem := new(memory.Tracker)
	mem.Consume(1<<30 + 1<<29 + 1<<28 + 1<<27)
	info := &util.ProcessInfo{
		ID:            233,
		User:          "PingCAP",
		Host:          "127.0.0.1",
		DB:            "Database",
		Info:          "select * from table where a > 1",
		CurTxnStartTS: 23333,
		StatsInfo: func(interface{}) map[string]uint64 {
			return nil
		},
		StmtCtx: &stmtctx.StatementContext{
			MemTracker: mem,
		},
		RedactSQL: false,
	}
	costTime := time.Second * 233
	logFields := genLogFields(costTime, info)
	c.Assert(len(logFields), Equals, 7)
	c.Assert(logFields[0].Key, Equals, "cost_time")
	c.Assert(logFields[0].String, Equals, "233s")
	c.Assert(logFields[1].Key, Equals, "conn_id")
	c.Assert(logFields[1].Integer, Equals, int64(233))
	c.Assert(logFields[2].Key, Equals, "user")
	c.Assert(logFields[2].String, Equals, "PingCAP")
	c.Assert(logFields[3].Key, Equals, "database")
	c.Assert(logFields[3].String, Equals, "Database")
	c.Assert(logFields[4].Key, Equals, "txn_start_ts")
	c.Assert(logFields[4].Integer, Equals, int64(23333))
	c.Assert(logFields[5].Key, Equals, "mem_max")
	c.Assert(logFields[5].String, Equals, "2013265920 Bytes (1.88 GB)")
	c.Assert(logFields[6].Key, Equals, "sql")
	c.Assert(logFields[6].String, Equals, "select * from table where a > 1")

	info.RedactSQL = true
	logFields = genLogFields(costTime, info)
	c.Assert(logFields[6].String, Equals, "select * from table where `a` > ?")
}
<commit_msg>util/expensivequery: migrate test-infra to testify (#26287)<commit_after>// Copyright 2019 PingCAP, 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package expensivequery

import (
	"testing"
	"time"

	"github.com/pingcap/tidb/sessionctx/stmtctx"
	"github.com/pingcap/tidb/util"
	"github.com/pingcap/tidb/util/memory"
	"github.com/pingcap/tidb/util/testbridge"
	"github.com/stretchr/testify/assert"
	"go.uber.org/goleak"
)

func TestMain(m *testing.M) {
	testbridge.WorkaroundGoCheckFlags()
	goleak.VerifyTestMain(m)
}

func TestLogFormat(t *testing.T) {
	t.Parallel()

	mem := new(memory.Tracker)
	mem.Consume(1<<30 + 1<<29 + 1<<28 + 1<<27)
	info := &util.ProcessInfo{
		ID:            233,
		User:          "PingCAP",
		Host:          "127.0.0.1",
		DB:            "Database",
		Info:          "select * from table where a > 1",
		CurTxnStartTS: 23333,
		StatsInfo: func(interface{}) map[string]uint64 {
			return nil
		},
		StmtCtx: &stmtctx.StatementContext{
			MemTracker: mem,
		},
		RedactSQL: false,
	}
	costTime := time.Second * 233
	logFields := genLogFields(costTime, info)

	assert.Len(t, logFields, 7)
	assert.Equal(t, "cost_time", logFields[0].Key)
	assert.Equal(t, "233s", logFields[0].String)
	assert.Equal(t, "conn_id", logFields[1].Key)
	assert.Equal(t, int64(233), logFields[1].Integer)
	assert.Equal(t, "user", logFields[2].Key)
	assert.Equal(t, "PingCAP", logFields[2].String)
	assert.Equal(t, "database", logFields[3].Key)
	assert.Equal(t, "Database", logFields[3].String)
	assert.Equal(t, "txn_start_ts", logFields[4].Key)
	assert.Equal(t, int64(23333), logFields[4].Integer)
	assert.Equal(t, "mem_max", logFields[5].Key)
	assert.Equal(t, "2013265920 Bytes (1.88 GB)", logFields[5].String)
	assert.Equal(t, "sql", logFields[6].Key)
	assert.Equal(t, "select * from table where a > 1", logFields[6].String)

	info.RedactSQL = true
	logFields = genLogFields(costTime, info)
	assert.Equal(t, "select * from table where `a` > ?", logFields[6].String)
}
<|endoftext|>


<commit_before>package telemetry

import (
	"fmt"
	"io"
	"os"
)

func stringInSlice(a string, list []string) bool {
	for _, b := range list {
		if b == a {
			return true
		}
	}
	return false
}

func Read(f *os.File) (*TELEM, error) {
	labels := []string{
		"ACCL",
		"DEVC",
		"DVID",
		"DVNM",
		"EMPT",
		"GPS5",
		"GPSF",
		"GPSP",
		"GPSU",
		"GYRO",
		"SCAL",
		"SIUN",
		"STRM",
		"TMPC",
		"TSMP",
		"UNIT",
	}

	label := make([]byte, 4, 4) // 4 byte ascii label of data
	desc := make([]byte, 4, 4)  // 4 byte description of length of data

	// keep a copy of the scale to apply to subsequent sentences
	s := SCAL{}

	// keep a copy of the full telemetry
	t := &TELEM{}

	for {
		// pick out the label
		read, err := f.Read(label)
		if err == io.EOF || read == 0 {
			return nil, err
		}

		label_string := string(label)

		if !stringInSlice(label_string, labels) {
			err := fmt.Errorf("Could not find label in list: %s (%x)\n", label, label)
			return nil, err
		}

		// pick out the label description
		read, err = f.Read(desc)
		if err == io.EOF || read == 0 {
			break
		}

		// first byte is zero, there is no length
		if 0x0 == desc[0] {
			continue
		}

		// skip empty packets
		if "EMPT" == label_string {
			_, err = f.Seek(4, 1)
			if err != nil {
				fmt.Println(err)
				break
			}
			continue
		}

		// extract the size and length
		val_size := int64(desc[1])
		num_values := (int64(desc[2]) << 8) | int64(desc[3])
		length := val_size * num_values

		// uncomment to see label, type, size and length
		//fmt.Printf("%s (%c) of size %v and len %v\n", label, desc[0], val_size, length)

		if "SCAL" == label_string {
			value := make([]byte, val_size*num_values, val_size*num_values)
			read, err = f.Read(value)
			if err == io.EOF || read == 0 {
				return nil, err
			}

			// clear the scales
			s.Values = s.Values[:0]

			err := s.Parse(value, val_size)
			if err != nil {
				return nil, err
			}
		} else {
			value := make([]byte, val_size, val_size)

			for i := int64(0); i < num_values; i++ {
				read, err := f.Read(value)
				if err == io.EOF || read == 0 {
					return nil, err
				}

				// I think DVID is the payload boundary; this might be a bad assumption
				if "DVID" == label_string {

					// XXX: I think this might skip the first sentence
					return t, nil

				} else if "GPS5" == label_string {
					g := GPS5{}
					g.Parse(value, &s)
					t.Gps = append(t.Gps, g)
				} else if "GPSU" == label_string {
					g := GPSU{}
					err := g.Parse(value)
					if err != nil {
						return nil, err
					}
					t.Time = g
				} else if "ACCL" == label_string {
					a := ACCL{}
					err := a.Parse(value, &s)
					if err != nil {
						return nil, err
					}
					t.Accl = append(t.Accl, a)
				} else if "TMPC" == label_string {
					tmp := TMPC{}
					tmp.Parse(value)
					t.Temp = tmp
				} else if "TSMP" == label_string {
					tsmp := TSMP{}
					tsmp.Parse(value, &s)
				} else if "GYRO" == label_string {
					g := GYRO{}
					err := g.Parse(value, &s)
					if err != nil {
						return nil, err
					}
					t.Gyro = append(t.Gyro, g)
				} else if "GPSP" == label_string {
					g := GPSP{}
					err := g.Parse(value)
					if err != nil {
						return nil, err
					}
					t.GpsAccuracy = g
				} else if "GPSF" == label_string {
					g := GPSF{}
					err := g.Parse(value)
					if err != nil {
						return nil, err
					}
					t.GpsFix = g
				} else if "UNIT" == label_string {
					// this is a string of units like "rad/s", not sure if it changes
					//fmt.Printf("\tvals: %s\n", value)
				} else if "SIUN" == label_string {
					// this is the SI unit - also not sure if it changes
					//fmt.Printf("\tvals: %s\n", value)
				} else if "DVNM" == label_string {
					// device name, "Camera"
					//fmt.Printf("\tvals: %s\n", value)
				} else {
					//fmt.Printf("\tvalue is %v\n", value)
				}
			}
		}

		// pack into 4 bytes
		mod := length % 4
		if mod != 0 {
			seek := 4 - mod
			_, err = f.Seek(seek, 1)
			if err != nil {
				break
			}
		}
	}

	return nil, nil
}
<commit_msg>use io.Reader for telem.Read instead of passing file<commit_after>package telemetry

import (
	"fmt"
	"io"
	"io/ioutil"
)

func stringInSlice(a string, list []string) bool {
	for _, b := range list {
		if b == a {
			return true
		}
	}
	return false
}

func Read(f io.Reader) (*TELEM, error) {
	labels := []string{
		"ACCL",
		"DEVC",
		"DVID",
		"DVNM",
		"EMPT",
		"GPS5",
		"GPSF",
		"GPSP",
		"GPSU",
		"GYRO",
		"SCAL",
		"SIUN",
		"STRM",
		"TMPC",
		"TSMP",
		"UNIT",
	}

	label := make([]byte, 4, 4) // 4 byte ascii label of data
	desc := make([]byte, 4, 4)  // 4 byte description of length of data

	// keep a copy of the scale to apply to subsequent sentences
	s := SCAL{}

	// the full telemetry for this period
	t := &TELEM{}

	for {
		// pick out the label
		read, err := f.Read(label)
		if err == io.EOF || read == 0 {
			return nil, err
		}

		label_string := string(label)

		if !stringInSlice(label_string, labels) {
			err := fmt.Errorf("Could not find label in list: %s (%x)\n", label, label)
			return nil, err
		}

		// pick out the label description
		read, err = f.Read(desc)
		if err == io.EOF || read == 0 {
			break
		}

		// first byte is zero, there is no length
		if 0x0 == desc[0] {
			continue
		}

		// skip empty packets
		if "EMPT" == label_string {
			io.CopyN(ioutil.Discard, f, 4)
			continue
		}

		// extract the size and length
		val_size := int64(desc[1])
		num_values := (int64(desc[2]) << 8) | int64(desc[3])
		length := val_size * num_values

		// uncomment to see label, type, size and length
		//fmt.Printf("%s (%c) of size %v and len %v\n", label, desc[0], val_size, length)

		if "SCAL" == label_string {
			value := make([]byte, val_size*num_values, val_size*num_values)
			read, err = f.Read(value)
			if err == io.EOF || read == 0 {
				return nil, err
			}

			// clear the scales
			s.Values = s.Values[:0]

			err := s.Parse(value, val_size)
			if err != nil {
				return nil, err
			}
		} else {
			value := make([]byte, val_size, val_size)

			for i := int64(0); i < num_values; i++ {
				read, err := f.Read(value)
				if err == io.EOF || read == 0 {
					return nil, err
				}

				// I think DVID is the payload boundary; this might be a bad assumption
				if "DVID" == label_string {

					// XXX: I think this might skip the first sentence
					return t, nil

				} else if "GPS5" == label_string {
					g := GPS5{}
					g.Parse(value, &s)
					t.Gps = append(t.Gps, g)
				} else if "GPSU" == label_string {
					g := GPSU{}
					err := g.Parse(value)
					if err != nil {
						return nil, err
					}
					t.Time = g
				} else if "ACCL" == label_string {
					a := ACCL{}
					err := a.Parse(value, &s)
					if err != nil {
						return nil, err
					}
					t.Accl = append(t.Accl, a)
				} else if "TMPC" == label_string {
					tmp := TMPC{}
					tmp.Parse(value)
					t.Temp = tmp
				} else if "TSMP" == label_string {
					tsmp := TSMP{}
					tsmp.Parse(value, &s)
				} else if "GYRO" == label_string {
					g := GYRO{}
					err := g.Parse(value, &s)
					if err != nil {
						return nil, err
					}
					t.Gyro = append(t.Gyro, g)
				} else if "GPSP" == label_string {
					g := GPSP{}
					err := g.Parse(value)
					if err != nil {
						return nil, err
					}
					t.GpsAccuracy = g
				} else if "GPSF" == label_string {
					g := GPSF{}
					err := g.Parse(value)
					if err != nil {
						return nil, err
					}
					t.GpsFix = g
				} else if "UNIT" == label_string {
					// this is a string of units like "rad/s", not sure if it changes
					//fmt.Printf("\tvals: %s\n", value)
				} else if "SIUN" == label_string {
					// this is the SI unit - also not sure if it changes
					//fmt.Printf("\tvals: %s\n", value)
				} else if "DVNM" == label_string {
					// device name, "Camera"
					//fmt.Printf("\tvals: %s\n", value)
				} else {
					//fmt.Printf("\tvalue is %v\n", value)
				}
			}
		}

		// pack into 4 bytes
		mod := length % 4
		if mod != 0 {
			seek := 4 - mod
			io.CopyN(ioutil.Discard, f, seek)
		}
	}

	return nil, nil
}
<|endoftext|>


<commit_before>package utils

import (
	"bytes"
	"encoding/json"
	"io"
	"io/ioutil"
	"net/http"
	"time"
	"strings"
)

type Response struct {
	Response     http.Response
}

func (this *Response) GetBody() []byte{
	defer this.Response.Body.Close()
	r, _ := ioutil.ReadAll(this.Response.Body)
	return r
}

// Must be called if not using AsJson or GetBody
func (this *Response) Close() {
	this.Response.Body.Close()
}

func (this *Response) AsJson(result interface{}) error {
	return json.NewDecoder(bytes.NewReader(this.GetBody())).Decode(result)
}

type RestClient struct {
	Headers map[string]string
	client           *http.Client
}

type ClientOptsFunc func(*http.Client)

func NewRestClient(optFuncs ...ClientOptsFunc) *RestClient {
	timeout := time.Duration(5 * time.Second)
	c := &http.Client{Timeout: timeout}
	for _, f := range optFuncs {
		f(c)
	}
	return &RestClient{nil, c}
}

func (c *RestClient) Client() *http.Client {
	return c.Client()
}

func doRequest(c *RestClient, req *http.Request) (*Response, error) {
	for name, value := range c.Headers {
		req.Header.Add(name, value)
	}
	response, err := c.client.Do(req)
	if err != nil {
		return nil, err
	}
	res := &Response{
		Response: *response,
	}
	return res, err
}

func (c *RestClient) GetWithBytes(url string, byteData []byte) (*Response,  error) {
	req, err := http.NewRequest("GET", url, bytes.NewBuffer(byteData))
	if err != nil {
		return nil, err
	}

	return doRequest(c, req)
}

func (c *RestClient) Get(url string) (*Response, error) {
	return c.GetWithBytes(url, nil)
}

func (c *RestClient) Post(url string, data io.Reader) (*Response, error) {

	req, err := http.NewRequest("POST", url, data)
	if err != nil {
		return nil, err
	}

	return doRequest(c, req)
}

func (c *RestClient) PostString(url string, data string) (*Response, error) {
	return c.Post(url, bytes.NewBufferString(data))
}

func (c *RestClient) PostJson(url string, data interface{}) (*Response, error) {

	byteData, err := json.Marshal(data)
	if err != nil {
		return nil, err
	}
	return c.Post(url, bytes.NewBuffer(byteData))
}

func (c *RestClient) Put(url string, data io.Reader) (*Response, error) {

	req, err := http.NewRequest("PUT", url, data)
	if err != nil {
		return nil, err
	}

	return doRequest(c, req)
}

func (c *RestClient) PutString(url string, data string) (*Response, error) {
	return c.Put(url, bytes.NewBufferString(data))
}

func (c *RestClient) PutJson(url string, data interface{}) (*Response, error) {

	byteData, err := json.Marshal(data)
	if err != nil {
		return nil, err
	}
	return c.Put(url, bytes.NewBuffer(byteData))
}

func (c *RestClient) Patch(url string, data io.Reader) (*Response, error) {

	req, err := http.NewRequest("PATCH", url, data)
	if err != nil {
		return nil, err
	}

	return doRequest(c, req)
}

func (c *RestClient) PatchString(url string, data string) (*Response, error) {
	return c.Patch(url, bytes.NewBufferString(data))
}

func (c *RestClient) PatchJson(url string, data interface{}) (*Response, error) {

	byteData, err := json.Marshal(data)
	if err != nil {
		return nil, err

	}
	return c.Patch(url, bytes.NewBuffer(byteData))
}

func (c *RestClient) Delete(url string, data io.Reader) (*Response, error) {
	req, err := http.NewRequest("DELETE", url, data)
	if err != nil {
		return nil, err
	}

	return doRequest(c, req)
}

func (c *RestClient) DeleteString(url string, data string) (*Response, error) {
	return c.Put(url, bytes.NewBufferString(data))
}

func (c *RestClient) DeleteJson(url string, data interface{}) (*Response, error) {

	byteData, err := json.Marshal(data)
	if err != nil {
		return nil, err
	}
	return c.Put(url, bytes.NewBuffer(byteData))
}

func  (c *RestClient) Do(method string, url string, data io.Reader) (*Response, error) {
	switch strings.ToLower(method) {
	case "get":
		return c.Get(url)
	case "post":
		return c.Post(url, data)
	case "put":
		return c.Put(url, data)
	case "patch":
		return c.Patch(url, data)
	case "delete":
		return c.Delete(url, data)
	default:
		panic("invalid method " + method)
	}
}

func  (c *RestClient) DoString(method string, url string, data string) (*Response, error) {
	switch strings.ToLower(method) {
	case "get":
		return c.Get(url)
	case "post":
		return c.PostString(url, data)
	case "put":
		return c.PutString(url, data)
	case "patch":
		return c.PatchString(url, data)
	case "delete":
		return c.DeleteString(url, data)
	default:
		panic("invalid method " + method)
	}
}

func  (c *RestClient) DoJson(method string, url string, data interface{}) (*Response, error) {
	switch strings.ToLower(method) {
	case "get":
		return c.Get(url)
	case "post":
		return c.PostJson(url, data)
	case "put":
		return c.PutJson(url, data)
	case "patch":
		return c.PatchJson(url, data)
	case "delete":
		return c.DeleteJson(url, data)
	default:
		panic("invalid method " + method)
	}
}
<commit_msg>Protect rest close with if<commit_after>package utils

import (
	"bytes"
	"encoding/json"
	"io"
	"io/ioutil"
	"net/http"
	"strings"
	"time"
)

type Response struct {
	Response http.Response
}

func (this *Response) GetBody() []byte {
	defer this.Response.Body.Close()
	r, _ := ioutil.ReadAll(this.Response.Body)
	return r
}

// Must be called if not using AsJson or GetBody
func (this *Response) Close() {
	if this.Response.Body != nil {
		this.Response.Body.Close()
	}
}

func (this *Response) AsJson(result interface{}) error {
	return json.NewDecoder(bytes.NewReader(this.GetBody())).Decode(result)
}

type RestClient struct {
	Headers map[string]string
	client  *http.Client
}

type ClientOptsFunc func(*http.Client)

func NewRestClient(optFuncs ...ClientOptsFunc) *RestClient {
	timeout := time.Duration(5 * time.Second)
	c := &http.Client{Timeout: timeout}
	for _, f := range optFuncs {
		f(c)
	}
	return &RestClient{nil, c}
}

func (c *RestClient) Client() *http.Client {
	return c.Client()
}

func doRequest(c *RestClient, req *http.Request) (*Response, error) {
	for name, value := range c.Headers {
		req.Header.Add(name, value)
	}
	response, err := c.client.Do(req)
	if err != nil {
		return nil, err
	}
	res := &Response{
		Response: *response,
	}
	return res, err
}

func (c *RestClient) GetWithBytes(url string, byteData []byte) (*Response, error) {
	req, err := http.NewRequest("GET", url, bytes.NewBuffer(byteData))
	if err != nil {
		return nil, err
	}

	return doRequest(c, req)
}

func (c *RestClient) Get(url string) (*Response, error) {
	return c.GetWithBytes(url, nil)
}

func (c *RestClient) Post(url string, data io.Reader) (*Response, error) {

	req, err := http.NewRequest("POST", url, data)
	if err != nil {
		return nil, err
	}

	return doRequest(c, req)
}

func (c *RestClient) PostString(url string, data string) (*Response, error) {
	return c.Post(url, bytes.NewBufferString(data))
}

func (c *RestClient) PostJson(url string, data interface{}) (*Response, error) {

	byteData, err := json.Marshal(data)
	if err != nil {
		return nil, err
	}
	return c.Post(url, bytes.NewBuffer(byteData))
}

func (c *RestClient) Put(url string, data io.Reader) (*Response, error) {

	req, err := http.NewRequest("PUT", url, data)
	if err != nil {
		return nil, err
	}

	return doRequest(c, req)
}

func (c *RestClient) PutString(url string, data string) (*Response, error) {
	return c.Put(url, bytes.NewBufferString(data))
}

func (c *RestClient) PutJson(url string, data interface{}) (*Response, error) {

	byteData, err := json.Marshal(data)
	if err != nil {
		return nil, err
	}
	return c.Put(url, bytes.NewBuffer(byteData))
}

func (c *RestClient) Patch(url string, data io.Reader) (*Response, error) {

	req, err := http.NewRequest("PATCH", url, data)
	if err != nil {
		return nil, err
	}

	return doRequest(c, req)
}

func (c *RestClient) PatchString(url string, data string) (*Response, error) {
	return c.Patch(url, bytes.NewBufferString(data))
}

func (c *RestClient) PatchJson(url string, data interface{}) (*Response, error) {

	byteData, err := json.Marshal(data)
	if err != nil {
		return nil, err

	}
	return c.Patch(url, bytes.NewBuffer(byteData))
}

func (c *RestClient) Delete(url string, data io.Reader) (*Response, error) {
	req, err := http.NewRequest("DELETE", url, data)
	if err != nil {
		return nil, err
	}

	return doRequest(c, req)
}

func (c *RestClient) DeleteString(url string, data string) (*Response, error) {
	return c.Put(url, bytes.NewBufferString(data))
}

func (c *RestClient) DeleteJson(url string, data interface{}) (*Response, error) {

	byteData, err := json.Marshal(data)
	if err != nil {
		return nil, err
	}
	return c.Put(url, bytes.NewBuffer(byteData))
}

func (c *RestClient) Do(method string, url string, data io.Reader) (*Response, error) {
	switch strings.ToLower(method) {
	case "get":
		return c.Get(url)
	case "post":
		return c.Post(url, data)
	case "put":
		return c.Put(url, data)
	case "patch":
		return c.Patch(url, data)
	case "delete":
		return c.Delete(url, data)
	default:
		panic("invalid method " + method)
	}
}

func (c *RestClient) DoString(method string, url string, data string) (*Response, error) {
	switch strings.ToLower(method) {
	case "get":
		return c.Get(url)
	case "post":
		return c.PostString(url, data)
	case "put":
		return c.PutString(url, data)
	case "patch":
		return c.PatchString(url, data)
	case "delete":
		return c.DeleteString(url, data)
	default:
		panic("invalid method " + method)
	}
}

func (c *RestClient) DoJson(method string, url string, data interface{}) (*Response, error) {
	switch strings.ToLower(method) {
	case "get":
		return c.Get(url)
	case "post":
		return c.PostJson(url, data)
	case "put":
		return c.PutJson(url, data)
	case "patch":
		return c.PatchJson(url, data)
	case "delete":
		return c.DeleteJson(url, data)
	default:
		panic("invalid method " + method)
	}
}
<|endoftext|>


<commit_before>package utils

import (
    "crypto/md5"
    "io"
    "encoding/binary"
)

// 字符串签名为数字,使用md5结果对折.
func StringSignMd5(text string) int64 {
    // TODO 目前还没测试该算法的冲突率.
    h := md5.New()
    io.WriteString(h,text)
    md5res := h.Sum(nil)
    order := binary.BigEndian
    sign1 := order.Uint64(md5res[0:8])
    sign2 := order.Uint64(md5res[8:16])

    return int64(sign1) + int64(sign2)
 }

 // 字符串签名为数字,使用BKDRHash算法.
 func StringSignBKDR(text string) int64 {
    // 代码参考:
    // https://www.byvoid.com/blog/string-hash-compare
     var seed int64 = 131   // 31 131 1313 13131 131313 etc..
     var hash int64

     for _,c := range text {
         hash = hash * seed + int64(c)
     }

     return hash
 }

<commit_msg>sign.go func indent<commit_after>package utils

import (
    "crypto/md5"
    "io"
    "encoding/binary"
)

// 字符串签名为数字,使用md5结果对折.
func StringSignMd5(text string) int64 {
    // TODO 目前还没测试该算法的冲突率.
    h := md5.New()
    io.WriteString(h,text)
    md5res := h.Sum(nil)
    order := binary.BigEndian
    sign1 := order.Uint64(md5res[0:8])
    sign2 := order.Uint64(md5res[8:16])

    return int64(sign1) + int64(sign2)
 }

// 字符串签名为数字,使用BKDRHash算法.
func StringSignBKDR(text string) int64 {
    // 代码参考:
    // https://www.byvoid.com/blog/string-hash-compare
    var seed int64 = 131   // 31 131 1313 13131 131313 etc..
    var hash int64

    for _,c := range text {
        hash = hash * seed + int64(c)
    }

    return hash
}

<|endoftext|>


<commit_before>package injector

import (
	"reflect"
	"testing"

	"github.com/stretchr/testify/require"
)

func Test_hasInjectTag_no_tags(t *testing.T) {
	type structNoTag struct{}
	v := &structNoTag{}
	require.False(t, hasInjectTag(&dependency{
		value:        v,
		reflectValue: reflect.ValueOf(v),
		reflectType:  reflect.TypeOf(v),
	}))
}
<commit_msg>test: more coverage for hasInjectTag (#5)<commit_after>package injector

import (
	"reflect"
	"testing"

	"github.com/stretchr/testify/require"
)

func Test_hasInjectTag_no_tags(t *testing.T) {
	type structNoTag struct{}
	v := structNoTag{}
	require.False(t, hasInjectTag(&dependency{
		value:        v,
		reflectValue: reflect.ValueOf(v),
		reflectType:  reflect.TypeOf(v),
	}))
}
<|endoftext|>


<commit_before>package lily

import (
	"testing"
	"io/ioutil"
)

// Author João Nuno.
//
// joaonrb@gmail.com
//

import (
	"fmt"
	"io/ioutil"
	"os"
	"testing"
)

func TestLineIterator(t *testing.T) {
	tmp := "/tmp/line_iterator.txt"
	content := "line1\n1\n"
	//defer os.Remove(tmp)
	err := ioutil.WriteFile(tmp, []byte(content), 0644)
	if err != nil {
		fmt.Printf("Tmp file couldn't be written becauser error %s", err.Error())
		os.Exit(1)
	}
	iter, err := NewLineIterator(tmp)
	switch {
	case err != nil:
		t.Error(err.Error())
	case iter.Next() != "line1" && iter.HasNext():
		t.Error("Line 1 is not expected(line1).")
	case iter.Next() != "1" && iter.HasNext():
		t.Error("Line 2 is not expected(1).")
	case iter.Next() != "" && !iter.HasNext():
		t.Error("Line 3 is not expected().")
	case iter.Next() != "":
		t.Error("Line 4 is not expected at all.")
	}
}

func TestGenerateBase64StringSize(t *testing.T) {
	for i, n := range []int{0, 1, 2, 5, 10, 25, 30, 50, 75, 100} {
		hex := GenerateBase64String(n)
		if len(hex) != n {
			t.Errorf("%d - Size of GenerateBase64String(%d) should be %d, got %d instead.", i, n, n, len(hex))
			return
		}
	}
}

func BGenerateBase64StringN(n int, b *testing.B) {
	for i := 0; i < b.N; i++ {
		GenerateBase64String(n)
	}
}

func BenchmarkGenerateBase64String1(b *testing.B) {
	BGenerateBase64StringN(1, b)
}

func BenchmarkGenerateBase64String5(b *testing.B) {
	BGenerateBase64StringN(5, b)
}

func BenchmarkGenerateBase64String10(b *testing.B) {
	BGenerateBase64StringN(10, b)
}

func BenchmarkGenerateBase64String25(b *testing.B) {
	BGenerateBase64StringN(25, b)
}

func BenchmarkGenerateBase64String50(b *testing.B) {
	BGenerateBase64StringN(50, b)
}

func BenchmarkGenerateBase64String75(b *testing.B) {
	BGenerateBase64StringN(75, b)
}

func BenchmarkGenerateBase64String100(b *testing.B) {
	BGenerateBase64StringN(100, b)
}
<commit_msg>Update doc<commit_after>package lily
// Author João Nuno.
//
// joaonrb@gmail.com
//

import (
	"fmt"
	"io/ioutil"
	"os"
	"testing"
)

func TestLineIterator(t *testing.T) {
	tmp := "/tmp/line_iterator.txt"
	content := "line1\n1\n"
	//defer os.Remove(tmp)
	err := ioutil.WriteFile(tmp, []byte(content), 0644)
	if err != nil {
		fmt.Printf("Tmp file couldn't be written becauser error %s", err.Error())
		os.Exit(1)
	}
	iter, err := NewLineIterator(tmp)
	switch {
	case err != nil:
		t.Error(err.Error())
	case iter.Next() != "line1" && iter.HasNext():
		t.Error("Line 1 is not expected(line1).")
	case iter.Next() != "1" && iter.HasNext():
		t.Error("Line 2 is not expected(1).")
	case iter.Next() != "" && !iter.HasNext():
		t.Error("Line 3 is not expected().")
	case iter.Next() != "":
		t.Error("Line 4 is not expected at all.")
	}
}

func TestGenerateBase64StringSize(t *testing.T) {
	for i, n := range []int{0, 1, 2, 5, 10, 25, 30, 50, 75, 100} {
		hex := GenerateBase64String(n)
		if len(hex) != n {
			t.Errorf("%d - Size of GenerateBase64String(%d) should be %d, got %d instead.", i, n, n, len(hex))
			return
		}
	}
}

func BGenerateBase64StringN(n int, b *testing.B) {
	for i := 0; i < b.N; i++ {
		GenerateBase64String(n)
	}
}

func BenchmarkGenerateBase64String1(b *testing.B) {
	BGenerateBase64StringN(1, b)
}

func BenchmarkGenerateBase64String5(b *testing.B) {
	BGenerateBase64StringN(5, b)
}

func BenchmarkGenerateBase64String10(b *testing.B) {
	BGenerateBase64StringN(10, b)
}

func BenchmarkGenerateBase64String25(b *testing.B) {
	BGenerateBase64StringN(25, b)
}

func BenchmarkGenerateBase64String50(b *testing.B) {
	BGenerateBase64StringN(50, b)
}

func BenchmarkGenerateBase64String75(b *testing.B) {
	BGenerateBase64StringN(75, b)
}

func BenchmarkGenerateBase64String100(b *testing.B) {
	BGenerateBase64StringN(100, b)
}
<|endoftext|>


<commit_before>// Copyright 2016-2020 The Libsacloud 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 libsacloud

// Version バージョン
const Version = "2.8.4"
<commit_msg>Bump to v2.8.5<commit_after>// Copyright 2016-2020 The Libsacloud 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 libsacloud

// Version バージョン
const Version = "2.8.5"
<|endoftext|>


<commit_before>// Copyright ©2017 The go-hep 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 fwktest

import (
	"fmt"
	"reflect"
	"sync"

	"go-hep.org/x/hep/fwk"
)

type reducer struct {
	fwk.TaskBase

	input string
	exp   int64
	sum   int64
	nevts int
	mux   sync.RWMutex
}

func (tsk *reducer) Configure(ctx fwk.Context) error {
	var err error

	err = tsk.DeclInPort(tsk.input, reflect.TypeOf(int64(0)))
	if err != nil {
		return err
	}

	return err
}

func (tsk *reducer) StartTask(ctx fwk.Context) error {
	var err error
	return err
}

func (tsk *reducer) StopTask(ctx fwk.Context) error {
	var err error

	tsk.mux.RLock()
	sum := tsk.sum
	nevts := tsk.nevts
	tsk.mux.RUnlock()

	msg := ctx.Msg()
	if sum != tsk.exp {
		msg.Errorf("expected sum=%v. got=%v (nevts=%d)\n", tsk.exp, sum, nevts)
		return fmt.Errorf("%s: expected sum=%v. got=%v (nevts=%d)", tsk.Name(), tsk.exp, sum, nevts)
	}
	msg.Debugf("expected sum=%v. got=%v (all GOOD) (nevts=%d)\n", tsk.exp, sum, nevts)

	return err
}

func (tsk *reducer) Process(ctx fwk.Context) error {
	var err error

	tsk.mux.Lock()
	tsk.nevts += 1
	tsk.mux.Unlock()

	store := ctx.Store()
	v, err := store.Get(tsk.input)
	if err != nil {
		return err
	}

	val := v.(int64)
	tsk.mux.Lock()
	tsk.sum += val
	sum := tsk.sum
	tsk.mux.Unlock()

	msg := ctx.Msg()
	msg.Infof("sum=%d (id=%d|%d)\n", sum, ctx.ID(), ctx.Slot())
	return err
}

func newreducer(typ, name string, mgr fwk.App) (fwk.Component, error) {
	var err error

	tsk := &reducer{
		TaskBase: fwk.NewTask(typ, name, mgr),
		input:    "Input",
		sum:      0,
		exp:      0,
		nevts:    0,
	}

	err = tsk.DeclProp("Input", &tsk.input)
	if err != nil {
		return nil, err
	}

	err = tsk.DeclProp("Sum", &tsk.exp)
	if err != nil {
		return nil, err
	}

	return tsk, err
}

func init() {
	fwk.Register(reflect.TypeOf(reducer{}), newreducer)
}
<commit_msg>fwk/internal/fwktest: apply staticcheck cosmetic change<commit_after>// Copyright ©2017 The go-hep 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 fwktest

import (
	"fmt"
	"reflect"
	"sync"

	"go-hep.org/x/hep/fwk"
)

type reducer struct {
	fwk.TaskBase

	input string
	exp   int64
	sum   int64
	nevts int
	mux   sync.RWMutex
}

func (tsk *reducer) Configure(ctx fwk.Context) error {
	err := tsk.DeclInPort(tsk.input, reflect.TypeOf(int64(0)))
	if err != nil {
		return err
	}

	return err
}

func (tsk *reducer) StartTask(ctx fwk.Context) error {
	var err error
	return err
}

func (tsk *reducer) StopTask(ctx fwk.Context) error {
	var err error

	tsk.mux.RLock()
	sum := tsk.sum
	nevts := tsk.nevts
	tsk.mux.RUnlock()

	msg := ctx.Msg()
	if sum != tsk.exp {
		msg.Errorf("expected sum=%v. got=%v (nevts=%d)\n", tsk.exp, sum, nevts)
		return fmt.Errorf("%s: expected sum=%v. got=%v (nevts=%d)", tsk.Name(), tsk.exp, sum, nevts)
	}
	msg.Debugf("expected sum=%v. got=%v (all GOOD) (nevts=%d)\n", tsk.exp, sum, nevts)

	return err
}

func (tsk *reducer) Process(ctx fwk.Context) error {
	var err error

	tsk.mux.Lock()
	tsk.nevts += 1
	tsk.mux.Unlock()

	store := ctx.Store()
	v, err := store.Get(tsk.input)
	if err != nil {
		return err
	}

	val := v.(int64)
	tsk.mux.Lock()
	tsk.sum += val
	sum := tsk.sum
	tsk.mux.Unlock()

	msg := ctx.Msg()
	msg.Infof("sum=%d (id=%d|%d)\n", sum, ctx.ID(), ctx.Slot())
	return err
}

func newreducer(typ, name string, mgr fwk.App) (fwk.Component, error) {
	var err error

	tsk := &reducer{
		TaskBase: fwk.NewTask(typ, name, mgr),
		input:    "Input",
		sum:      0,
		exp:      0,
		nevts:    0,
	}

	err = tsk.DeclProp("Input", &tsk.input)
	if err != nil {
		return nil, err
	}

	err = tsk.DeclProp("Sum", &tsk.exp)
	if err != nil {
		return nil, err
	}

	return tsk, err
}

func init() {
	fwk.Register(reflect.TypeOf(reducer{}), newreducer)
}
<|endoftext|>