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

package translation

import (
	"code.gitea.io/gitea/modules/log"
	"code.gitea.io/gitea/modules/options"
	"code.gitea.io/gitea/modules/setting"

	"github.com/unknwon/i18n"
	"golang.org/x/text/language"
)

// Locale represents an interface to translation
type Locale interface {
	Language() string
	Tr(string, ...interface{}) string
}

// LangType represents a lang type
type LangType struct {
	Lang, Name string
}

var (
	matcher  language.Matcher
	allLangs []LangType
)

// AllLangs returns all supported langauages
func AllLangs() []LangType {
	return allLangs
}

// InitLocales loads the locales
func InitLocales() {
	localeNames, err := options.Dir("locale")
	if err != nil {
		log.Fatal("Failed to list locale files: %v", err)
	}

	localFiles := make(map[string][]byte)
	for _, name := range localeNames {
		localFiles[name], err = options.Locale(name)
		if err != nil {
			log.Fatal("Failed to load %s locale file. %v", name, err)
		}
	}

	tags := make([]language.Tag, len(setting.Langs))
	for i, lang := range setting.Langs {
		tags[i] = language.Raw.Make(lang)
	}

	matcher = language.NewMatcher(tags)
	for i := range setting.Names {
		key := "locale_" + setting.Langs[i] + ".ini"
		if err := i18n.SetMessageWithDesc(setting.Langs[i], setting.Names[i], localFiles[key]); err != nil {
			log.Fatal("Failed to set messages to %s: %v", setting.Langs[i], err)
		}
	}
	i18n.SetDefaultLang("en-US")

	allLangs = make([]LangType, 0, i18n.Count()-1)
	langs := i18n.ListLangs()
	names := i18n.ListLangDescs()
	for i, v := range langs {
		allLangs = append(allLangs, LangType{v, names[i]})
	}
}

// Match matches accept languages
func Match(tags ...language.Tag) (tag language.Tag, index int, c language.Confidence) {
	return matcher.Match(tags...)
}

// locale represents the information of localization.
type locale struct {
	Lang string
}

// NewLocale return a locale
func NewLocale(lang string) Locale {
	return &locale{
		Lang: lang,
	}
}

func (l *locale) Language() string {
	return l.Lang
}

// Tr translates content to target language.
func (l *locale) Tr(format string, args ...interface{}) string {
	return i18n.Tr(l.Lang, format, args...)
}
<commit_msg>Fix locale init (#14582)<commit_after>// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package translation

import (
	"errors"

	"code.gitea.io/gitea/modules/log"
	"code.gitea.io/gitea/modules/options"
	"code.gitea.io/gitea/modules/setting"

	"github.com/unknwon/i18n"
	"golang.org/x/text/language"
)

// Locale represents an interface to translation
type Locale interface {
	Language() string
	Tr(string, ...interface{}) string
}

// LangType represents a lang type
type LangType struct {
	Lang, Name string
}

var (
	matcher  language.Matcher
	allLangs []LangType
)

// AllLangs returns all supported langauages
func AllLangs() []LangType {
	return allLangs
}

// InitLocales loads the locales
func InitLocales() {
	localeNames, err := options.Dir("locale")
	if err != nil {
		log.Fatal("Failed to list locale files: %v", err)
	}

	localFiles := make(map[string][]byte)
	for _, name := range localeNames {
		localFiles[name], err = options.Locale(name)
		if err != nil {
			log.Fatal("Failed to load %s locale file. %v", name, err)
		}
	}

	tags := make([]language.Tag, len(setting.Langs))
	for i, lang := range setting.Langs {
		tags[i] = language.Raw.Make(lang)
	}

	matcher = language.NewMatcher(tags)
	for i := range setting.Names {
		key := "locale_" + setting.Langs[i] + ".ini"
		if err = i18n.SetMessageWithDesc(setting.Langs[i], setting.Names[i], localFiles[key]); err != nil {
			if errors.Is(err, i18n.ErrLangAlreadyExist) {
				// just log if lang is already loaded since we can not reload it
				log.Warn("Can not load language '%s' since already loaded", setting.Langs[i])
			} else {
				log.Error("Failed to set messages to %s: %v", setting.Langs[i], err)
			}
		}
	}
	i18n.SetDefaultLang("en-US")

	allLangs = make([]LangType, 0, i18n.Count()-1)
	langs := i18n.ListLangs()
	names := i18n.ListLangDescs()
	for i, v := range langs {
		allLangs = append(allLangs, LangType{v, names[i]})
	}
}

// Match matches accept languages
func Match(tags ...language.Tag) (tag language.Tag, index int, c language.Confidence) {
	return matcher.Match(tags...)
}

// locale represents the information of localization.
type locale struct {
	Lang string
}

// NewLocale return a locale
func NewLocale(lang string) Locale {
	return &locale{
		Lang: lang,
	}
}

func (l *locale) Language() string {
	return l.Lang
}

// Tr translates content to target language.
func (l *locale) Tr(format string, args ...interface{}) string {
	return i18n.Tr(l.Lang, format, args...)
}
<|endoftext|>


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

package storage

import (
	"fmt"
	"strings"

	"github.com/GoogleCloudPlatform/compute-image-tools/cli_tools/common/domain"
	"github.com/GoogleCloudPlatform/compute-image-tools/daisy"
	daisycompute "github.com/GoogleCloudPlatform/compute-image-tools/daisy/compute"
	"google.golang.org/api/compute/v1"
)

type multiRegion struct {
	regions                      []string
	isRegionToMultiRegionAllowed bool
}

var (
	// for each GCS multi region, regions are sorted by GCE cost in ascending order as of the time of writing
	// this code.
	// GCE cost per region: https://cloud.google.com/compute/vm-instance-pricing
	// GCS multi-regions: https://cloud.google.com/storage/docs/locations#location-mr
	multiRegions = map[string]multiRegion{
		"US":   {[]string{"us-central1", "us-east1", "us-west1", "us-east4", "us-west4", "us-west2", "us-west3"}, true},
		"EU":   {[]string{"europe-west1", "europe-west4", "europe-north1", "europe-west2", "europe-west3", "europe-west6"}, true},
		"ASIA": {[]string{"asia-east1", "asia-south1", "asia-southeast1", "asia-northeast1", "asia-northeast2", "asia-northeast3", "asia-southeast2", "asia-east2"}, true},

		"EUR4": {[]string{"europe-north1", "europe-west4"}, false},
		"NAM4": {[]string{"us-central1", "us-east1"}, false},
	}
)

// ResourceLocationRetriever is responsible for retrieving GCE zone to run import in
type ResourceLocationRetriever struct {
	Mgce              domain.MetadataGCEInterface
	ComputeGCEService daisycompute.Client
}

// NewResourceLocationRetriever creates a ResourceLocationRetriever
func NewResourceLocationRetriever(aMgce domain.MetadataGCEInterface, cs daisycompute.Client) *ResourceLocationRetriever {
	return &ResourceLocationRetriever{Mgce: aMgce, ComputeGCEService: cs}
}

// GetZone retrieves GCE zone to run import in based on imported source file location and available
// zones in the project.
// If storageRegion is provided and valid, first zone within that region will be used.
// If no storageRegion is provided, GCE Zone from the running process
// will be used.
func (rlr *ResourceLocationRetriever) GetZone(storageLocation string, project string) (string, error) {
	zone := ""
	var err error
	if storageLocation != "" {
		// pick a zone from the region where data is stored
		zone, err = rlr.getZoneFromStorageLocation(storageLocation, project)
		if err == nil {
			return zone, err
		}
	}

	// determine zone based on the zone Cloud Build is running in
	if rlr.Mgce.OnGCE() {
		zone, err = rlr.Mgce.Zone()
	}
	if err != nil {
		return "", daisy.Errf("can't infer zone: %v", err)
	}
	if zone == "" {
		return "", daisy.Errf("zone is empty")
	}
	fmt.Printf("[image-import] Zone not provided, using %v\n", zone)

	return zone, nil
}

func (rlr *ResourceLocationRetriever) getZoneFromStorageLocation(location string, project string) (string, daisy.DError) {
	if project == "" {
		return "", daisy.Errf("project cannot be empty in order to find a zone from a location")
	}
	zones, err := rlr.ComputeGCEService.ListZones(project)
	if err != nil {
		return "", daisy.Errf("Failed to list zones: %v", err)
	}
	if rlr.isMultiRegion(location) {
		return rlr.getBestZoneForMultiRegion(location, zones)
	}
	return rlr.getZoneForRegion(location, zones)
}

func (rlr *ResourceLocationRetriever) getZoneForRegion(region string, zones []*compute.Zone) (string, daisy.DError) {
	for _, zone := range zones {
		if isZoneUp(zone) && strings.HasSuffix(strings.ToLower(zone.Region), strings.ToLower(region)) {
			return zone.Name, nil
		}
	}
	return "", daisy.Errf("no zone found for %v region", region)
}

func (rlr *ResourceLocationRetriever) getMultiRegionForRegion(region string) string {
	for multiRegionID, multiRegion := range multiRegions {
		if !multiRegion.isRegionToMultiRegionAllowed {
			continue
		}

		for _, aRegion := range multiRegion.regions {
			if strings.EqualFold(aRegion, region) {
				return multiRegionID
			}
		}
	}

	return ""
}

func (rlr *ResourceLocationRetriever) getBestZoneForMultiRegion(multiRegion string, zones []*compute.Zone) (string, daisy.DError) {
	for _, region := range multiRegions[multiRegion].regions {
		if zone, err := rlr.getZoneForRegion(region, zones); err == nil {
			return zone, nil
		}
	}
	return "", daisy.Errf("no zones found for %v multi region", multiRegion)
}

func (rlr *ResourceLocationRetriever) isMultiRegion(location string) bool {
	_, containsKey := multiRegions[strings.ToUpper(location)]
	return containsKey
}

func isZoneUp(zone *compute.Zone) bool {
	return zone != nil && zone.Status == "UP"
}

// GetLargestStorageLocation returns the largest storage location that includes provided argument.
// If argument is a multi-region, the argument is returned. If argument is a region within a multi-region,
// the multi-region is returned. If argument is a region not within a multi-region, argument is returned.
func (rlr *ResourceLocationRetriever) GetLargestStorageLocation(storageLocation string) string {
	if rlr.isMultiRegion(storageLocation) {
		return storageLocation
	}

	if multiRegion := rlr.getMultiRegionForRegion(storageLocation); multiRegion != "" {
		return multiRegion
	}

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

package storage

import (
	"fmt"
	"strings"

	"github.com/GoogleCloudPlatform/compute-image-tools/cli_tools/common/domain"
	"github.com/GoogleCloudPlatform/compute-image-tools/daisy"
	daisycompute "github.com/GoogleCloudPlatform/compute-image-tools/daisy/compute"
	"google.golang.org/api/compute/v1"
)

type multiRegion struct {
	regions                      []string
	isRegionToMultiRegionAllowed bool
}

var (
	// for each GCS multi region, regions are sorted by GCE cost in ascending order as of the time of writing
	// this code.
	// GCE cost per region: https://cloud.google.com/compute/vm-instance-pricing
	// GCS multi-regions: https://cloud.google.com/storage/docs/locations#location-mr
	multiRegions = map[string]multiRegion{
		"US":   {[]string{"us-central1", "us-east1", "us-west1", "us-east4", "us-west4", "us-west2", "us-west3"}, true},
		"EU":   {[]string{"europe-west1", "europe-west4", "europe-north1", "europe-west2", "europe-west3", "europe-west6"}, true},
		"ASIA": {[]string{"asia-east1", "asia-south1", "asia-southeast1", "asia-northeast1", "asia-northeast2", "asia-northeast3", "asia-southeast2", "asia-east2"}, true},

		"EUR4":  {[]string{"europe-west4", "europe-north1"}, false},
		"NAM4":  {[]string{"us-central1", "us-east1"}, false},
		"ASIA1": {[]string{"asia-northeast1", "asia-northeast2"}, false},
	}
)

// ResourceLocationRetriever is responsible for retrieving GCE zone to run import in
type ResourceLocationRetriever struct {
	Mgce              domain.MetadataGCEInterface
	ComputeGCEService daisycompute.Client
}

// NewResourceLocationRetriever creates a ResourceLocationRetriever
func NewResourceLocationRetriever(aMgce domain.MetadataGCEInterface, cs daisycompute.Client) *ResourceLocationRetriever {
	return &ResourceLocationRetriever{Mgce: aMgce, ComputeGCEService: cs}
}

// GetZone retrieves GCE zone to run import in based on imported source file location and available
// zones in the project.
// If storageRegion is provided and valid, first zone within that region will be used.
// If no storageRegion is provided, GCE Zone from the running process
// will be used.
func (rlr *ResourceLocationRetriever) GetZone(storageLocation string, project string) (string, error) {
	zone := ""
	var err error
	if storageLocation != "" {
		// pick a zone from the region where data is stored
		zone, err = rlr.getZoneFromStorageLocation(storageLocation, project)
		if err == nil {
			return zone, err
		}
	}

	// determine zone based on the zone Cloud Build is running in
	if rlr.Mgce.OnGCE() {
		zone, err = rlr.Mgce.Zone()
	}
	if err != nil {
		return "", daisy.Errf("can't infer zone: %v", err)
	}
	if zone == "" {
		return "", daisy.Errf("zone is empty")
	}
	fmt.Printf("[image-import] Zone not provided, using %v\n", zone)

	return zone, nil
}

func (rlr *ResourceLocationRetriever) getZoneFromStorageLocation(location string, project string) (string, daisy.DError) {
	if project == "" {
		return "", daisy.Errf("project cannot be empty in order to find a zone from a location")
	}
	zones, err := rlr.ComputeGCEService.ListZones(project)
	if err != nil {
		return "", daisy.Errf("Failed to list zones: %v", err)
	}
	if rlr.isMultiRegion(location) {
		return rlr.getBestZoneForMultiRegion(location, zones)
	}
	return rlr.getZoneForRegion(location, zones)
}

func (rlr *ResourceLocationRetriever) getZoneForRegion(region string, zones []*compute.Zone) (string, daisy.DError) {
	for _, zone := range zones {
		if isZoneUp(zone) && strings.HasSuffix(strings.ToLower(zone.Region), strings.ToLower(region)) {
			return zone.Name, nil
		}
	}
	return "", daisy.Errf("no zone found for %v region", region)
}

func (rlr *ResourceLocationRetriever) getMultiRegionForRegion(region string) string {
	for multiRegionID, multiRegion := range multiRegions {
		if !multiRegion.isRegionToMultiRegionAllowed {
			continue
		}

		for _, aRegion := range multiRegion.regions {
			if strings.EqualFold(aRegion, region) {
				return multiRegionID
			}
		}
	}

	return ""
}

func (rlr *ResourceLocationRetriever) getBestZoneForMultiRegion(multiRegion string, zones []*compute.Zone) (string, daisy.DError) {
	for _, region := range multiRegions[multiRegion].regions {
		if zone, err := rlr.getZoneForRegion(region, zones); err == nil {
			return zone, nil
		}
	}
	return "", daisy.Errf("no zones found for %v multi region", multiRegion)
}

func (rlr *ResourceLocationRetriever) isMultiRegion(location string) bool {
	_, containsKey := multiRegions[strings.ToUpper(location)]
	return containsKey
}

func isZoneUp(zone *compute.Zone) bool {
	return zone != nil && zone.Status == "UP"
}

// GetLargestStorageLocation returns the largest storage location that includes provided argument.
// If argument is a multi-region, the argument is returned. If argument is a region within a multi-region,
// the multi-region is returned. If argument is a region not within a multi-region, argument is returned.
func (rlr *ResourceLocationRetriever) GetLargestStorageLocation(storageLocation string) string {
	if rlr.isMultiRegion(storageLocation) {
		return storageLocation
	}

	if multiRegion := rlr.getMultiRegionForRegion(storageLocation); multiRegion != "" {
		return multiRegion
	}

	return storageLocation
}
<|endoftext|>


<commit_before>package commands

import (
	"bytes"
	"fmt"
	"io"
	"os"

	"github.com/github/git-lfs/config"
	"github.com/github/git-lfs/errors"
	"github.com/github/git-lfs/git"
	"github.com/github/git-lfs/lfs"
	"github.com/github/git-lfs/progress"
	"github.com/spf13/cobra"
)

const (
	// cleanFilterBufferCapacity is the desired capacity of the
	// `*git.PacketWriter`'s internal buffer when the filter protocol
	// dictates the "clean" command. 512 bytes is (in most cases) enough to
	// hold an entire LFS pointer in memory.
	cleanFilterBufferCapacity = 512

	// smudgeFilterBufferCapacity is the desired capacity of the
	// `*git.PacketWriter`'s internal buffer when the filter protocol
	// dictates the "smudge" command.
	smudgeFilterBufferCapacity = git.MaxPacketLength
)

var (
	filterSmudgeSkip = false
)

func clean(to io.Writer, reader io.Reader, fileName string) error {
	var cb progress.CopyCallback
	var file *os.File
	var fileSize int64
	if len(fileName) > 0 {
		stat, err := os.Stat(fileName)
		if err == nil && stat != nil {
			fileSize = stat.Size()

			localCb, localFile, err := lfs.CopyCallbackFile("clean", fileName, 1, 1)
			if err != nil {
				Error(err.Error())
			} else {
				cb = localCb
				file = localFile
			}
		}
	}

	cleaned, err := lfs.PointerClean(reader, fileName, fileSize, cb)
	if file != nil {
		file.Close()
	}

	if cleaned != nil {
		defer cleaned.Teardown()
	}

	if errors.IsCleanPointerError(err) {
		// If the contents read from the working directory was _already_
		// a pointer, we'll get a `CleanPointerError`, with the context
		// containing the bytes that we should write back out to Git.
		_, err = to.Write(errors.GetContext(err, "bytes").([]byte))
		return err
	}

	if err != nil {
		Panic(err, "Error cleaning asset.")
	}

	tmpfile := cleaned.Filename
	mediafile, err := lfs.LocalMediaPath(cleaned.Oid)
	if err != nil {
		Panic(err, "Unable to get local media path.")
	}

	if stat, _ := os.Stat(mediafile); stat != nil {
		if stat.Size() != cleaned.Size && len(cleaned.Pointer.Extensions) == 0 {
			Exit("Files don't match:\n%s\n%s", mediafile, tmpfile)
		}
		Debug("%s exists", mediafile)
	} else {
		if err := os.Rename(tmpfile, mediafile); err != nil {
			Panic(err, "Unable to move %s to %s\n", tmpfile, mediafile)
		}

		Debug("Writing %s", mediafile)
	}

	_, err = cleaned.Pointer.Encode(to)
	return err
}

func smudge(to io.Writer, reader io.Reader, filename string) error {
	var pbuf bytes.Buffer
	reader = io.TeeReader(reader, &pbuf)

	ptr, err := lfs.DecodePointer(reader)
	if err != nil {
		// If we tried to decode a pointer out of the data given to us,
		// and the file was _empty_, write out an empty file in
		// response. This occurs because when the clean filter
		// encounters an empty file, and writes out an empty file,
		// instead of a pointer.
		//
		// TODO(taylor): figure out if there is more data on the reader,
		// and buffer that as well.
		if len(pbuf.Bytes()) == 0 {
			if _, cerr := io.Copy(to, &pbuf); cerr != nil {
				Panic(cerr, "Error writing data to stdout:")
			}
			return nil
		}

		return err
	}

	lfs.LinkOrCopyFromReference(ptr.Oid, ptr.Size)

	cb, file, err := lfs.CopyCallbackFile("smudge", filename, 1, 1)
	if err != nil {
		Error(err.Error())
	}

	cfg := config.Config
	download := lfs.FilenamePassesIncludeExcludeFilter(filename, cfg.FetchIncludePaths(), cfg.FetchExcludePaths())

	if filterSmudgeSkip || cfg.Os.Bool("GIT_LFS_SKIP_SMUDGE", false) {
		download = false
	}

	err = ptr.Smudge(to, filename, download, TransferManifest(), cb)
	if file != nil {
		file.Close()
	}

	if err != nil {
		// Download declined error is ok to skip if we weren't requesting download
		if !(errors.IsDownloadDeclinedError(err) && !download) {
			LoggedError(err, "Error downloading object: %s (%s)", filename, ptr.Oid)
			if !cfg.SkipDownloadErrors() {
				// TODO: What to do best here?
				os.Exit(2)
			}
		}

		_, err = ptr.Encode(to)
		return err
	}

	return nil
}

func filterCommand(cmd *cobra.Command, args []string) {
	requireStdin("This command should be run by the Git filter process")
	lfs.InstallHooks(false)

	s := git.NewObjectScanner(os.Stdin, os.Stdout)

	if err := s.Init(); err != nil {
		ExitWithError(err)
	}
	if err := s.NegotiateCapabilities(); err != nil {
		ExitWithError(err)
	}

Scan:
	for s.Scan() {
		var err error
		var w io.Writer

		req := s.Request()
		if req == nil {
			break
		}
		s.WriteStatus("success")

		switch req.Header["command"] {
		case "clean":
			w = git.NewPacketWriter(os.Stdout, cleanFilterBufferCapacity)
			err = clean(w, req.Payload, req.Header["pathname"])
		case "smudge":
			w = git.NewPacketWriter(os.Stdout, smudgeFilterBufferCapacity)
			err = smudge(w, req.Payload, req.Header["pathname"])
		default:
			fmt.Errorf("Unknown command %s", cmd)
			break Scan
		}

		var status string
		if _, ferr := w.Write(nil); ferr != nil {
			status = "error"
		} else {
			if err != nil && err != io.EOF {
				status = "error"
			} else {
				status = "success"
			}
		}
		s.WriteStatus(status)
	}

	if err := s.Err(); err != nil && err != io.EOF {
		ExitWithError(err)
	}
}

func init() {
	RegisterCommand("filter", filterCommand, func(cmd *cobra.Command) {
		cmd.Flags().BoolVarP(&filterSmudgeSkip, "skip", "s", false, "")
	})
}
<commit_msg>commands/filter: don't break on empty packets<commit_after>package commands

import (
	"bytes"
	"fmt"
	"io"
	"os"

	"github.com/github/git-lfs/config"
	"github.com/github/git-lfs/errors"
	"github.com/github/git-lfs/git"
	"github.com/github/git-lfs/lfs"
	"github.com/github/git-lfs/progress"
	"github.com/spf13/cobra"
)

const (
	// cleanFilterBufferCapacity is the desired capacity of the
	// `*git.PacketWriter`'s internal buffer when the filter protocol
	// dictates the "clean" command. 512 bytes is (in most cases) enough to
	// hold an entire LFS pointer in memory.
	cleanFilterBufferCapacity = 512

	// smudgeFilterBufferCapacity is the desired capacity of the
	// `*git.PacketWriter`'s internal buffer when the filter protocol
	// dictates the "smudge" command.
	smudgeFilterBufferCapacity = git.MaxPacketLength
)

var (
	filterSmudgeSkip = false
)

func clean(to io.Writer, reader io.Reader, fileName string) error {
	var cb progress.CopyCallback
	var file *os.File
	var fileSize int64
	if len(fileName) > 0 {
		stat, err := os.Stat(fileName)
		if err == nil && stat != nil {
			fileSize = stat.Size()

			localCb, localFile, err := lfs.CopyCallbackFile("clean", fileName, 1, 1)
			if err != nil {
				Error(err.Error())
			} else {
				cb = localCb
				file = localFile
			}
		}
	}

	cleaned, err := lfs.PointerClean(reader, fileName, fileSize, cb)
	if file != nil {
		file.Close()
	}

	if cleaned != nil {
		defer cleaned.Teardown()
	}

	if errors.IsCleanPointerError(err) {
		// If the contents read from the working directory was _already_
		// a pointer, we'll get a `CleanPointerError`, with the context
		// containing the bytes that we should write back out to Git.
		_, err = to.Write(errors.GetContext(err, "bytes").([]byte))
		return err
	}

	if err != nil {
		Panic(err, "Error cleaning asset.")
	}

	tmpfile := cleaned.Filename
	mediafile, err := lfs.LocalMediaPath(cleaned.Oid)
	if err != nil {
		Panic(err, "Unable to get local media path.")
	}

	if stat, _ := os.Stat(mediafile); stat != nil {
		if stat.Size() != cleaned.Size && len(cleaned.Pointer.Extensions) == 0 {
			Exit("Files don't match:\n%s\n%s", mediafile, tmpfile)
		}
		Debug("%s exists", mediafile)
	} else {
		if err := os.Rename(tmpfile, mediafile); err != nil {
			Panic(err, "Unable to move %s to %s\n", tmpfile, mediafile)
		}

		Debug("Writing %s", mediafile)
	}

	_, err = cleaned.Pointer.Encode(to)
	return err
}

func smudge(to io.Writer, reader io.Reader, filename string) error {
	var pbuf bytes.Buffer
	reader = io.TeeReader(reader, &pbuf)

	ptr, err := lfs.DecodePointer(reader)
	if err != nil {
		// If we tried to decode a pointer out of the data given to us,
		// and the file was _empty_, write out an empty file in
		// response. This occurs because when the clean filter
		// encounters an empty file, and writes out an empty file,
		// instead of a pointer.
		//
		// TODO(taylor): figure out if there is more data on the reader,
		// and buffer that as well.
		if len(pbuf.Bytes()) == 0 {
			if _, cerr := io.Copy(to, &pbuf); cerr != nil {
				Panic(cerr, "Error writing data to stdout:")
			}
			return nil
		}

		return err
	}

	lfs.LinkOrCopyFromReference(ptr.Oid, ptr.Size)

	cb, file, err := lfs.CopyCallbackFile("smudge", filename, 1, 1)
	if err != nil {
		Error(err.Error())
	}

	cfg := config.Config
	download := lfs.FilenamePassesIncludeExcludeFilter(filename, cfg.FetchIncludePaths(), cfg.FetchExcludePaths())

	if filterSmudgeSkip || cfg.Os.Bool("GIT_LFS_SKIP_SMUDGE", false) {
		download = false
	}

	err = ptr.Smudge(to, filename, download, TransferManifest(), cb)
	if file != nil {
		file.Close()
	}

	if err != nil {
		// Download declined error is ok to skip if we weren't requesting download
		if !(errors.IsDownloadDeclinedError(err) && !download) {
			LoggedError(err, "Error downloading object: %s (%s)", filename, ptr.Oid)
			if !cfg.SkipDownloadErrors() {
				// TODO: What to do best here?
				os.Exit(2)
			}
		}

		_, err = ptr.Encode(to)
		return err
	}

	return nil
}

func filterCommand(cmd *cobra.Command, args []string) {
	requireStdin("This command should be run by the Git filter process")
	lfs.InstallHooks(false)

	s := git.NewObjectScanner(os.Stdin, os.Stdout)

	if err := s.Init(); err != nil {
		ExitWithError(err)
	}
	if err := s.NegotiateCapabilities(); err != nil {
		ExitWithError(err)
	}

Scan:
	for s.Scan() {
		var err error
		var w io.Writer

		req := s.Request()
		s.WriteStatus("success")

		switch req.Header["command"] {
		case "clean":
			w = git.NewPacketWriter(os.Stdout, cleanFilterBufferCapacity)
			err = clean(w, req.Payload, req.Header["pathname"])
		case "smudge":
			w = git.NewPacketWriter(os.Stdout, smudgeFilterBufferCapacity)
			err = smudge(w, req.Payload, req.Header["pathname"])
		default:
			fmt.Errorf("Unknown command %s", cmd)
			break Scan
		}

		var status string
		if _, ferr := w.Write(nil); ferr != nil {
			status = "error"
		} else {
			if err != nil && err != io.EOF {
				status = "error"
			} else {
				status = "success"
			}
		}
		s.WriteStatus(status)
	}

	if err := s.Err(); err != nil && err != io.EOF {
		ExitWithError(err)
	}
}

func init() {
	RegisterCommand("filter", filterCommand, func(cmd *cobra.Command) {
		cmd.Flags().BoolVarP(&filterSmudgeSkip, "skip", "s", false, "")
	})
}
<|endoftext|>


<commit_before>package commands

import (
	"bytes"
	"github.com/github/git-media/gitmedia"
	"github.com/github/git-media/pointer"
	"github.com/spf13/cobra"
	"io"
	"os"
	"path/filepath"
)

var (
	smudgeInfo = false
	smudgeCmd  = &cobra.Command{
		Use:   "smudge",
		Short: "Implements the Git smudge filter",
		Run:   smudgeCommand,
	}
)

func smudgeCommand(cmd *cobra.Command, args []string) {
	gitmedia.InstallHooks()

	b := &bytes.Buffer{}
	r := io.TeeReader(os.Stdin, b)

	ptr, err := pointer.Decode(r)
	if err != nil {
		mr := io.MultiReader(b, os.Stdin)
		io.Copy(os.Stdout, mr)
		return
	}

	if smudgeInfo {
		localPath, err := gitmedia.LocalMediaPath(ptr.Oid)
		if err != nil {
			Exit(err.Error())
		}

		stat, err := os.Stat(localPath)
		if err != nil {
			Print("%d --", ptr.Size)
		} else {
			Print("%d %s", stat.Size(), localPath)
		}
		return
	}

	filename := smudgeFilename(args, err)
	cb, file, err := gitmedia.CopyCallbackFile("smudge", filename, 1, 1)
	if err != nil {
		Error(err.Error())
	}

	err = ptr.Smudge(os.Stdout, cb)
	if file != nil {
		file.Close()
	}

	if err != nil {
		ptr.Encode(os.Stdout)
		LoggedError(err, "Error accessing media: %s (%s)", filename, ptr.Oid)
	}
}

func smudgeFilename(args []string, err error) string {
	if len(args) > 0 {
		return args[0]
	}

	if smudgeErr, ok := err.(*pointer.SmudgeError); ok {
		return filepath.Base(smudgeErr.Filename)
	}

	return "<unknown file>"
}

func init() {
	smudgeCmd.Flags().BoolVarP(&smudgeInfo, "info", "i", false, "whatever")
	RootCmd.AddCommand(smudgeCmd)
}
<commit_msg>Check for an error on Copy<commit_after>package commands

import (
	"bytes"
	"github.com/github/git-media/gitmedia"
	"github.com/github/git-media/pointer"
	"github.com/spf13/cobra"
	"io"
	"os"
	"path/filepath"
)

var (
	smudgeInfo = false
	smudgeCmd  = &cobra.Command{
		Use:   "smudge",
		Short: "Implements the Git smudge filter",
		Run:   smudgeCommand,
	}
)

func smudgeCommand(cmd *cobra.Command, args []string) {
	gitmedia.InstallHooks()

	b := &bytes.Buffer{}
	r := io.TeeReader(os.Stdin, b)

	ptr, err := pointer.Decode(r)
	if err != nil {
		mr := io.MultiReader(b, os.Stdin)
		_, err := io.Copy(os.Stdout, mr)
		if err != nil {
			Panic(err, "Error writing data to stdout:")
		}
		return
	}

	if smudgeInfo {
		localPath, err := gitmedia.LocalMediaPath(ptr.Oid)
		if err != nil {
			Exit(err.Error())
		}

		stat, err := os.Stat(localPath)
		if err != nil {
			Print("%d --", ptr.Size)
		} else {
			Print("%d %s", stat.Size(), localPath)
		}
		return
	}

	filename := smudgeFilename(args, err)
	cb, file, err := gitmedia.CopyCallbackFile("smudge", filename, 1, 1)
	if err != nil {
		Error(err.Error())
	}

	err = ptr.Smudge(os.Stdout, cb)
	if file != nil {
		file.Close()
	}

	if err != nil {
		ptr.Encode(os.Stdout)
		LoggedError(err, "Error accessing media: %s (%s)", filename, ptr.Oid)
	}
}

func smudgeFilename(args []string, err error) string {
	if len(args) > 0 {
		return args[0]
	}

	if smudgeErr, ok := err.(*pointer.SmudgeError); ok {
		return filepath.Base(smudgeErr.Filename)
	}

	return "<unknown file>"
}

func init() {
	smudgeCmd.Flags().BoolVarP(&smudgeInfo, "info", "i", false, "whatever")
	RootCmd.AddCommand(smudgeCmd)
}
<|endoftext|>


<commit_before>package terminal

import (
	"bufio"
	"bytes"
	"io"
	"os"
	"strconv"
	"strings"

	"github.com/elpinal/coco3/config"

	"github.com/mattn/go-runewidth"
)

type Terminal struct {
	w   *bufio.Writer
	msg string
}

func New(w io.Writer) *Terminal {
	return &Terminal{
		w: bufio.NewWriterSize(w, 32),
	}
}

func getwd() string {
	wd, _ := os.Getwd()
	if home := os.Getenv("HOME"); strings.HasPrefix(wd, home) {
		wd = strings.Replace(wd, home, "~", 1)
	}
	return wd
}

func (t *Terminal) Start(conf *config.Config, s []rune, pos int) {
	var promptWidth int
	if conf.PromptTmpl != nil {
		var buf bytes.Buffer
		conf.PromptTmpl.Execute(&buf, config.Info{WD: getwd()})
		prompt := buf.String()
		t.w.WriteString("\r\033[J")
		t.w.WriteString(prompt)
		i := strings.LastIndex(prompt, "\n") + 1
		lastLine := prompt[i:]
		promptWidth = runewidth.StringWidth(lastLine)
	} else {
		t.w.WriteString("\r\033[J")
		t.w.WriteString(conf.Prompt)
		promptWidth = runewidth.StringWidth(conf.Prompt)
	}
	t.w.WriteString(string(s))
	if t.msg != "" {
		t.w.WriteString("\n")
		t.w.WriteString(t.msg)
		t.w.WriteString("\033[A")
	}
	t.w.WriteString("\033[")
	t.w.WriteString(strconv.Itoa(promptWidth + runesWidth(s[:pos]) + 1))
	t.w.WriteString("G")
	t.w.Flush()
}

func (t *Terminal) Refresh(conf *config.Config, s []rune, pos int) {
	var promptWidth int
	if conf.PromptTmpl != nil {
		var buf bytes.Buffer
		conf.PromptTmpl.Execute(&buf, config.Info{WD: getwd()})
		prompt := buf.String()
		count := strings.Count(prompt, "\n")
		if count > 0 {
			t.w.WriteString("\033[")
			t.w.WriteString(strconv.Itoa(count))
			t.w.WriteString("A")
		}
		t.w.WriteString("\r\033[J")
		t.w.WriteString(prompt)
		i := strings.LastIndex(prompt, "\n") + 1
		lastLine := prompt[i:]
		promptWidth = runewidth.StringWidth(lastLine)
	} else {
		t.w.WriteString("\r\033[J")
		t.w.WriteString(conf.Prompt)
		promptWidth = runewidth.StringWidth(conf.Prompt)
	}
	t.w.WriteString(string(s))
	if t.msg != "" {
		t.w.WriteString("\n")
		t.w.WriteString(t.msg)
		t.w.WriteString("\033[A")
	}
	t.w.WriteString("\033[")
	t.w.WriteString(strconv.Itoa(promptWidth + runesWidth(s[:pos]) + 1))
	t.w.WriteString("G")
	t.w.Flush()
}

func runesWidth(s []rune) (width int) {
	for _, r := range s {
		width += runewidth.RuneWidth(r)
	}
	return width
}

func (t *Terminal) SetLastLine(msg string) {
	t.msg = msg
}
<commit_msg>Support multi-line for fixed prompt<commit_after>package terminal

import (
	"bufio"
	"bytes"
	"io"
	"os"
	"strconv"
	"strings"

	"github.com/elpinal/coco3/config"

	"github.com/mattn/go-runewidth"
)

type Terminal struct {
	w   *bufio.Writer
	msg string
}

func New(w io.Writer) *Terminal {
	return &Terminal{
		w: bufio.NewWriterSize(w, 32),
	}
}

func getwd() string {
	wd, _ := os.Getwd()
	if home := os.Getenv("HOME"); strings.HasPrefix(wd, home) {
		wd = strings.Replace(wd, home, "~", 1)
	}
	return wd
}

func (t *Terminal) Start(conf *config.Config, s []rune, pos int) {
	var promptWidth int
	if conf.PromptTmpl != nil {
		var buf bytes.Buffer
		conf.PromptTmpl.Execute(&buf, config.Info{WD: getwd()})
		prompt := buf.String()
		t.w.WriteString("\r\033[J")
		t.w.WriteString(prompt)
		i := strings.LastIndex(prompt, "\n") + 1
		lastLine := prompt[i:]
		promptWidth = runewidth.StringWidth(lastLine)
	} else {
		t.w.WriteString("\r\033[J")
		t.w.WriteString(conf.Prompt)
		i := strings.LastIndex(conf.Prompt, "\n") + 1
		promptWidth = runewidth.StringWidth(conf.Prompt[i:])
	}
	t.w.WriteString(string(s))
	if t.msg != "" {
		t.w.WriteString("\n")
		t.w.WriteString(t.msg)
		t.w.WriteString("\033[A")
	}
	t.w.WriteString("\033[")
	t.w.WriteString(strconv.Itoa(promptWidth + runesWidth(s[:pos]) + 1))
	t.w.WriteString("G")
	t.w.Flush()
}

func (t *Terminal) Refresh(conf *config.Config, s []rune, pos int) {
	var promptWidth int
	if conf.PromptTmpl != nil {
		var buf bytes.Buffer
		conf.PromptTmpl.Execute(&buf, config.Info{WD: getwd()})
		prompt := buf.String()
		count := strings.Count(prompt, "\n")
		if count > 0 {
			t.w.WriteString("\033[")
			t.w.WriteString(strconv.Itoa(count))
			t.w.WriteString("A")
		}
		t.w.WriteString("\r\033[J")
		t.w.WriteString(prompt)
		i := strings.LastIndex(prompt, "\n") + 1
		lastLine := prompt[i:]
		promptWidth = runewidth.StringWidth(lastLine)
	} else {
		count := strings.Count(conf.Prompt, "\n")
		if count > 0 {
			t.w.WriteString("\033[")
			t.w.WriteString(strconv.Itoa(count))
			t.w.WriteString("A")
		}
		t.w.WriteString("\r\033[J")
		t.w.WriteString(conf.Prompt)
		i := strings.LastIndex(conf.Prompt, "\n") + 1
		promptWidth = runewidth.StringWidth(conf.Prompt[i:])
	}
	t.w.WriteString(string(s))
	if t.msg != "" {
		t.w.WriteString("\n")
		t.w.WriteString(t.msg)
		t.w.WriteString("\033[A")
	}
	t.w.WriteString("\033[")
	t.w.WriteString(strconv.Itoa(promptWidth + runesWidth(s[:pos]) + 1))
	t.w.WriteString("G")
	t.w.Flush()
}

func runesWidth(s []rune) (width int) {
	for _, r := range s {
		width += runewidth.RuneWidth(r)
	}
	return width
}

func (t *Terminal) SetLastLine(msg string) {
	t.msg = msg
}
<|endoftext|>


<commit_before>// Copyright 2015 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

// execprog executes a single program or a set of programs
// and optinally prints information about execution.
package main

import (
	"bytes"
	"encoding/binary"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"runtime"
	"sync"
	"time"

	"github.com/google/syzkaller/pkg/cover"
	"github.com/google/syzkaller/pkg/ipc"
	. "github.com/google/syzkaller/pkg/log"
	"github.com/google/syzkaller/pkg/osutil"
	"github.com/google/syzkaller/prog"
	_ "github.com/google/syzkaller/sys"
)

var (
	flagArch      = flag.String("arch", runtime.GOARCH, "target arch")
	flagExecutor  = flag.String("executor", "./syz-executor", "path to executor binary")
	flagCoverFile = flag.String("coverfile", "", "write coverage to the file")
	flagRepeat    = flag.Int("repeat", 1, "repeat execution that many times (0 for infinite loop)")
	flagProcs     = flag.Int("procs", 1, "number of parallel processes to execute programs")
	flagOutput    = flag.String("output", "none", "write programs to none/stdout")
	flagFaultCall = flag.Int("fault_call", -1, "inject fault into this call (0-based)")
	flagFaultNth  = flag.Int("fault_nth", 0, "inject fault on n-th operation (0-based)")
	flagHints     = flag.Bool("hints", false, "do a hints-generation run")
)

func main() {
	flag.Parse()
	if len(flag.Args()) == 0 {
		fmt.Fprintf(os.Stderr, "usage: execprog [flags] file-with-programs+\n")
		flag.PrintDefaults()
		os.Exit(1)
	}

	target, err := prog.GetTarget(runtime.GOOS, *flagArch)
	if err != nil {
		Fatalf("%v", err)
	}

	var progs []*prog.Prog
	for _, fn := range flag.Args() {
		data, err := ioutil.ReadFile(fn)
		if err != nil {
			Fatalf("failed to read log file: %v", err)
		}
		entries := target.ParseLog(data)
		for _, ent := range entries {
			progs = append(progs, ent.P)
		}
	}
	Logf(0, "parsed %v programs", len(progs))
	if len(progs) == 0 {
		return
	}

	execOpts := &ipc.ExecOpts{}
	config, err := ipc.DefaultConfig()
	if err != nil {
		Fatalf("%v", err)
	}
	if config.Flags&ipc.FlagSignal != 0 {
		execOpts.Flags |= ipc.FlagCollectCover
	}
	execOpts.Flags |= ipc.FlagDedupCover
	if *flagCoverFile != "" {
		config.Flags |= ipc.FlagSignal
		execOpts.Flags |= ipc.FlagCollectCover
		execOpts.Flags &^= ipc.FlagDedupCover
	}
	if *flagHints {
		if execOpts.Flags&ipc.FlagCollectCover != 0 {
			execOpts.Flags ^= ipc.FlagCollectCover
		}
		execOpts.Flags |= ipc.FlagCollectComps
	}

	if *flagFaultCall >= 0 {
		config.Flags |= ipc.FlagEnableFault
		execOpts.Flags |= ipc.FlagInjectFault
		execOpts.FaultCall = *flagFaultCall
		execOpts.FaultNth = *flagFaultNth
	}

	handled := make(map[string]bool)
	for _, prog := range progs {
		for _, call := range prog.Calls {
			handled[call.Meta.CallName] = true
		}
	}
	if handled["syz_emit_ethernet"] || handled["syz_extract_tcp_res"] {
		config.Flags |= ipc.FlagEnableTun
	}

	var wg sync.WaitGroup
	wg.Add(*flagProcs)
	var posMu, logMu sync.Mutex
	gate := ipc.NewGate(2**flagProcs, nil)
	var pos int
	var lastPrint time.Time
	shutdown := make(chan struct{})
	for p := 0; p < *flagProcs; p++ {
		pid := p
		go func() {
			defer wg.Done()
			env, err := ipc.MakeEnv(*flagExecutor, pid, config)
			if err != nil {
				Fatalf("failed to create ipc env: %v", err)
			}
			defer env.Close()
			for {
				if !func() bool {
					// Limit concurrency window.
					ticket := gate.Enter()
					defer gate.Leave(ticket)

					posMu.Lock()
					idx := pos
					pos++
					if idx%len(progs) == 0 && time.Since(lastPrint) > 5*time.Second {
						Logf(0, "executed programs: %v", idx)
						lastPrint = time.Now()
					}
					posMu.Unlock()
					if *flagRepeat > 0 && idx >= len(progs)**flagRepeat {
						return false
					}
					p := progs[idx%len(progs)]
					switch *flagOutput {
					case "stdout":
						data := p.Serialize()
						logMu.Lock()
						Logf(0, "executing program %v:\n%s", pid, data)
						logMu.Unlock()
					}
					output, info, failed, hanged, err := env.Exec(execOpts, p)
					select {
					case <-shutdown:
						return false
					default:
					}
					if failed {
						fmt.Printf("BUG: executor-detected bug:\n%s", output)
					}
					if config.Flags&ipc.FlagDebug != 0 || err != nil {
						fmt.Printf("result: failed=%v hanged=%v err=%v\n\n%s", failed, hanged, err, output)
					}
					if *flagCoverFile != "" {
						// Coverage is dumped in sanitizer format.
						// github.com/google/sanitizers/tools/sancov command can be used to dump PCs,
						// then they can be piped via addr2line to symbolize.
						for i, inf := range info {
							fmt.Printf("call #%v: signal %v, coverage %v\n", i, len(inf.Signal), len(inf.Cover))
							if len(inf.Cover) == 0 {
								continue
							}
							buf := new(bytes.Buffer)
							binary.Write(buf, binary.LittleEndian, uint64(0xC0BFFFFFFFFFFF64))
							for _, pc := range inf.Cover {
								binary.Write(buf, binary.LittleEndian, cover.RestorePC(pc, 0xffffffff))
							}
							err := osutil.WriteFile(fmt.Sprintf("%v.%v", *flagCoverFile, i), buf.Bytes())
							if err != nil {
								Fatalf("failed to write coverage file: %v", err)
							}
						}
					}
					if *flagHints {
						compMaps := ipc.GetCompMaps(info)
						p.MutateWithHints(compMaps, func(p *prog.Prog) {
							fmt.Printf("%v\n", string(p.Serialize()))
						})
					}

					return true
				}() {
					return
				}
			}
		}()
	}

	osutil.HandleInterrupts(shutdown)
	wg.Wait()
}
<commit_msg>tools/syz-execprog: print total number of comps/hints<commit_after>// Copyright 2015 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

// execprog executes a single program or a set of programs
// and optinally prints information about execution.
package main

import (
	"bytes"
	"encoding/binary"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"runtime"
	"sync"
	"time"

	"github.com/google/syzkaller/pkg/cover"
	"github.com/google/syzkaller/pkg/ipc"
	. "github.com/google/syzkaller/pkg/log"
	"github.com/google/syzkaller/pkg/osutil"
	"github.com/google/syzkaller/prog"
	_ "github.com/google/syzkaller/sys"
)

var (
	flagArch      = flag.String("arch", runtime.GOARCH, "target arch")
	flagExecutor  = flag.String("executor", "./syz-executor", "path to executor binary")
	flagCoverFile = flag.String("coverfile", "", "write coverage to the file")
	flagRepeat    = flag.Int("repeat", 1, "repeat execution that many times (0 for infinite loop)")
	flagProcs     = flag.Int("procs", 1, "number of parallel processes to execute programs")
	flagOutput    = flag.String("output", "none", "write programs to none/stdout")
	flagFaultCall = flag.Int("fault_call", -1, "inject fault into this call (0-based)")
	flagFaultNth  = flag.Int("fault_nth", 0, "inject fault on n-th operation (0-based)")
	flagHints     = flag.Bool("hints", false, "do a hints-generation run")
)

func main() {
	flag.Parse()
	if len(flag.Args()) == 0 {
		fmt.Fprintf(os.Stderr, "usage: execprog [flags] file-with-programs+\n")
		flag.PrintDefaults()
		os.Exit(1)
	}

	target, err := prog.GetTarget(runtime.GOOS, *flagArch)
	if err != nil {
		Fatalf("%v", err)
	}

	var progs []*prog.Prog
	for _, fn := range flag.Args() {
		data, err := ioutil.ReadFile(fn)
		if err != nil {
			Fatalf("failed to read log file: %v", err)
		}
		entries := target.ParseLog(data)
		for _, ent := range entries {
			progs = append(progs, ent.P)
		}
	}
	Logf(0, "parsed %v programs", len(progs))
	if len(progs) == 0 {
		return
	}

	execOpts := &ipc.ExecOpts{}
	config, err := ipc.DefaultConfig()
	if err != nil {
		Fatalf("%v", err)
	}
	if config.Flags&ipc.FlagSignal != 0 {
		execOpts.Flags |= ipc.FlagCollectCover
	}
	execOpts.Flags |= ipc.FlagDedupCover
	if *flagCoverFile != "" {
		config.Flags |= ipc.FlagSignal
		execOpts.Flags |= ipc.FlagCollectCover
		execOpts.Flags &^= ipc.FlagDedupCover
	}
	if *flagHints {
		if execOpts.Flags&ipc.FlagCollectCover != 0 {
			execOpts.Flags ^= ipc.FlagCollectCover
		}
		execOpts.Flags |= ipc.FlagCollectComps
	}

	if *flagFaultCall >= 0 {
		config.Flags |= ipc.FlagEnableFault
		execOpts.Flags |= ipc.FlagInjectFault
		execOpts.FaultCall = *flagFaultCall
		execOpts.FaultNth = *flagFaultNth
	}

	handled := make(map[string]bool)
	for _, prog := range progs {
		for _, call := range prog.Calls {
			handled[call.Meta.CallName] = true
		}
	}
	if handled["syz_emit_ethernet"] || handled["syz_extract_tcp_res"] {
		config.Flags |= ipc.FlagEnableTun
	}

	var wg sync.WaitGroup
	wg.Add(*flagProcs)
	var posMu, logMu sync.Mutex
	gate := ipc.NewGate(2**flagProcs, nil)
	var pos int
	var lastPrint time.Time
	shutdown := make(chan struct{})
	for p := 0; p < *flagProcs; p++ {
		pid := p
		go func() {
			defer wg.Done()
			env, err := ipc.MakeEnv(*flagExecutor, pid, config)
			if err != nil {
				Fatalf("failed to create ipc env: %v", err)
			}
			defer env.Close()
			for {
				if !func() bool {
					// Limit concurrency window.
					ticket := gate.Enter()
					defer gate.Leave(ticket)

					posMu.Lock()
					idx := pos
					pos++
					if idx%len(progs) == 0 && time.Since(lastPrint) > 5*time.Second {
						Logf(0, "executed programs: %v", idx)
						lastPrint = time.Now()
					}
					posMu.Unlock()
					if *flagRepeat > 0 && idx >= len(progs)**flagRepeat {
						return false
					}
					p := progs[idx%len(progs)]
					switch *flagOutput {
					case "stdout":
						data := p.Serialize()
						logMu.Lock()
						Logf(0, "executing program %v:\n%s", pid, data)
						logMu.Unlock()
					}
					output, info, failed, hanged, err := env.Exec(execOpts, p)
					select {
					case <-shutdown:
						return false
					default:
					}
					if failed {
						fmt.Printf("BUG: executor-detected bug:\n%s", output)
					}
					if config.Flags&ipc.FlagDebug != 0 || err != nil {
						fmt.Printf("result: failed=%v hanged=%v err=%v\n\n%s", failed, hanged, err, output)
					}
					if *flagCoverFile != "" {
						// Coverage is dumped in sanitizer format.
						// github.com/google/sanitizers/tools/sancov command can be used to dump PCs,
						// then they can be piped via addr2line to symbolize.
						for i, inf := range info {
							fmt.Printf("call #%v: signal %v, coverage %v\n", i, len(inf.Signal), len(inf.Cover))
							if len(inf.Cover) == 0 {
								continue
							}
							buf := new(bytes.Buffer)
							binary.Write(buf, binary.LittleEndian, uint64(0xC0BFFFFFFFFFFF64))
							for _, pc := range inf.Cover {
								binary.Write(buf, binary.LittleEndian, cover.RestorePC(pc, 0xffffffff))
							}
							err := osutil.WriteFile(fmt.Sprintf("%v.%v", *flagCoverFile, i), buf.Bytes())
							if err != nil {
								Fatalf("failed to write coverage file: %v", err)
							}
						}
					}
					if *flagHints {
						compMaps := ipc.GetCompMaps(info)
						ncomps, ncandidates := 0, 0
						for _, comps := range compMaps {
							for v, args := range comps {
								ncomps += len(args)
								if *flagOutput == "stdout" {
									fmt.Printf("comp 0x%x:", v)
									for arg := range args {
										fmt.Printf(" 0x%x", arg)
									}
									fmt.Printf("\n")
								}
							}
						}
						p.MutateWithHints(compMaps, func(p *prog.Prog) {
							ncandidates++
							if *flagOutput == "stdout" {
								fmt.Printf("PROGRAM:\n%s\n", p.Serialize())
							}
						})
						fmt.Printf("ncomps=%v ncandidates=%v\n", ncomps, ncandidates)
					}
					return true
				}() {
					return
				}
			}
		}()
	}

	osutil.HandleInterrupts(shutdown)
	wg.Wait()
}
<|endoftext|>


<commit_before>package commandclass

import (
	"encoding/binary"
	"errors"
	"math"
)

/* Thermostat Setpoint command class commands */
const (
	CommandThermostatSetpointSet             byte = 0x01
	CommandThermostatSetpointGet                  = 0x02
	CommandThermostatSetpointReport               = 0x03
	CommandThermostatSetpointSupportedGet         = 0x04
	CommandThermostatSetpointSupportedReport      = 0x05
)

const ThermostatSetpointTypeMask byte = 0x0F

type ThermostatSetpointType byte

const (
	ThermostatSetpointTypeNotSupported   ThermostatSetpointType = 0x00
	ThermostatSetpointTypeHeating                               = 0x01
	ThermostatSetpointTypeCooling                               = 0x02
	ThermostatSetpointTypeNotSupported1                         = 0x03
	ThermostatSetpointTypeNotSupported2                         = 0x04
	ThermostatSetpointTypeNotSupported3                         = 0x05
	ThermostatSetpointTypeNotSupported4                         = 0x06
	ThermostatSetpointTypeFurnace                               = 0x07
	ThermostatSetpointTypeDryAir                                = 0x08
	ThermostatSetpointTypeMoistAir                              = 0x09
	ThermostatSetpointTypeAutoChangeover                        = 0x0A
)

const (
	SetpointScaleCelcius   byte = 0x0
	SetpointScaleFarenheit      = 0x1
)

const (
	setpointTypeMask       byte = 0x0F
	setpointPrecisionMask       = 0xE0
	setpointPrecisionShift      = 0x05
	setpointScaleMask           = 0x18
	setpointScaleShift          = 0x03
	setpointSizeMask            = 0x07
)

type Temperature struct {
	Value float64
	Scale byte
}

type ThermostatSetpoint struct {
	Type      ThermostatSetpointType
	Precision byte
	Scale     byte
	Size      byte
	Value     []byte
}

// Even though we can handle receiving any theoretically-valid value from a
// thermostat, we're only going to support 0-100 degrees (C or F) for setting,
// and for now, we're not going to support decimal values.
func NewThermostatSetpoint(setpointType ThermostatSetpointType, temp Temperature) (*ThermostatSetpoint, error) {
	val := math.Floor(temp.Value)
	if val > 100 {
		return nil, errors.New("Setpoint temperature too high")
	}

	if val < 0 {
		return nil, errors.New("Setpoint temperature too low")
	}

	setpoint := &ThermostatSetpoint{
		Type:      setpointType,
		Precision: 0,
		Scale:     temp.Scale,
		Size:      1,
		Value:     []byte{byte(val)},
	}

	return setpoint, nil
}

func (t *ThermostatSetpoint) GetTemperature() (*Temperature, error) {
	switch t.Size {
	case 1:
		return &Temperature{
			Value: float64(int8(t.Value[0])) / math.Pow(10, float64(t.Precision)),
			Scale: t.Scale,
		}, nil
	case 2:
		value := int16(binary.BigEndian.Uint16(t.Value))
		return &Temperature{
			Value: float64(int16(value)) / math.Pow(10, float64(t.Precision)),
			Scale: t.Scale,
		}, nil
	case 4:
		value := int32(binary.BigEndian.Uint32(t.Value))
		return &Temperature{
			Value: float64(int32(value)) / math.Pow(10, float64(t.Precision)),
			Scale: t.Scale,
		}, nil
	default:
		return nil, errors.New("Invalid size field in setpoint report")
	}
}

func NewThermostatSetpointSet(setpointType ThermostatSetpointType, temperature Temperature) ([]byte, error) {
	setpoint, err := NewThermostatSetpoint(setpointType, temperature)
	if err != nil {
		return nil, err
	}

	precision := (setpoint.Precision << setpointPrecisionShift) & setpointPrecisionMask
	scale := (setpoint.Scale << setpointScaleShift) & setpointScaleMask
	size := setpoint.Size & setpointSizeMask

	payload := []byte{
		CommandClassThermostatSetpoint,
		CommandThermostatSetpointSet,
		byte(setpointType) & setpointTypeMask, // setpoint type
		precision | scale | size,
	}

	payload = append(payload, setpoint.Value...)

	return payload, nil
}

func ParseThermostatSetpointReport(payload []byte) ThermostatSetpoint {
	return ThermostatSetpoint{
		Type:      ThermostatSetpointType(payload[2] & 0x0F),
		Precision: (payload[3] & setpointPrecisionMask) >> setpointPrecisionShift,
		Scale:     (payload[3] & setpointScaleMask) >> setpointScaleShift,
		Size:      (payload[3] & setpointSizeMask),
		Value:     payload[4:],
	}
}
<commit_msg>create type ThermostatSetpointScale<commit_after>package commandclass

import (
	"encoding/binary"
	"errors"
	"math"
)

/* Thermostat Setpoint command class commands */
const (
	CommandThermostatSetpointSet             byte = 0x01
	CommandThermostatSetpointGet                  = 0x02
	CommandThermostatSetpointReport               = 0x03
	CommandThermostatSetpointSupportedGet         = 0x04
	CommandThermostatSetpointSupportedReport      = 0x05
)

const ThermostatSetpointTypeMask byte = 0x0F

type ThermostatSetpointType byte

const (
	ThermostatSetpointTypeNotSupported   ThermostatSetpointType = 0x00
	ThermostatSetpointTypeHeating                               = 0x01
	ThermostatSetpointTypeCooling                               = 0x02
	ThermostatSetpointTypeNotSupported1                         = 0x03
	ThermostatSetpointTypeNotSupported2                         = 0x04
	ThermostatSetpointTypeNotSupported3                         = 0x05
	ThermostatSetpointTypeNotSupported4                         = 0x06
	ThermostatSetpointTypeFurnace                               = 0x07
	ThermostatSetpointTypeDryAir                                = 0x08
	ThermostatSetpointTypeMoistAir                              = 0x09
	ThermostatSetpointTypeAutoChangeover                        = 0x0A
)

type ThermostatSetpointScale byte

const (
	SetpointScaleCelcius   byte = 0x0
	SetpointScaleFarenheit      = 0x1
)

const (
	setpointTypeMask       byte = 0x0F
	setpointPrecisionMask       = 0xE0
	setpointPrecisionShift      = 0x05
	setpointScaleMask           = 0x18
	setpointScaleShift          = 0x03
	setpointSizeMask            = 0x07
)

type Temperature struct {
	Value float64
	Scale ThermostatSetpointScale
}

type ThermostatSetpoint struct {
	Type      ThermostatSetpointType
	Precision byte
	Scale     byte
	Size      byte
	Value     []byte
}

// Even though we can handle receiving any theoretically-valid value from a
// thermostat, we're only going to support 0-100 degrees (C or F) for setting,
// and for now, we're not going to support decimal values.
func NewThermostatSetpoint(setpointType ThermostatSetpointType, temp Temperature) (*ThermostatSetpoint, error) {
	val := math.Floor(temp.Value)
	if val > 100 {
		return nil, errors.New("Setpoint temperature too high")
	}

	if val < 0 {
		return nil, errors.New("Setpoint temperature too low")
	}

	setpoint := &ThermostatSetpoint{
		Type:      setpointType,
		Precision: 0,
		Scale:     byte(temp.Scale),
		Size:      1,
		Value:     []byte{byte(val)},
	}

	return setpoint, nil
}

func (t *ThermostatSetpoint) GetTemperature() (*Temperature, error) {
	switch t.Size {
	case 1:
		return &Temperature{
			Value: float64(int8(t.Value[0])) / math.Pow(10, float64(t.Precision)),
			Scale: ThermostatSetpointScale(t.Scale),
		}, nil
	case 2:
		value := int16(binary.BigEndian.Uint16(t.Value))
		return &Temperature{
			Value: float64(int16(value)) / math.Pow(10, float64(t.Precision)),
			Scale: ThermostatSetpointScale(t.Scale),
		}, nil
	case 4:
		value := int32(binary.BigEndian.Uint32(t.Value))
		return &Temperature{
			Value: float64(int32(value)) / math.Pow(10, float64(t.Precision)),
			Scale: ThermostatSetpointScale(t.Scale),
		}, nil
	default:
		return nil, errors.New("Invalid size field in setpoint report")
	}
}

func NewThermostatSetpointSet(setpointType ThermostatSetpointType, temperature Temperature) ([]byte, error) {
	setpoint, err := NewThermostatSetpoint(setpointType, temperature)
	if err != nil {
		return nil, err
	}

	precision := (setpoint.Precision << setpointPrecisionShift) & setpointPrecisionMask
	scale := (setpoint.Scale << setpointScaleShift) & setpointScaleMask
	size := setpoint.Size & setpointSizeMask

	payload := []byte{
		CommandClassThermostatSetpoint,
		CommandThermostatSetpointSet,
		byte(setpointType) & setpointTypeMask, // setpoint type
		precision | scale | size,
	}

	payload = append(payload, setpoint.Value...)

	return payload, nil
}

func ParseThermostatSetpointReport(payload []byte) ThermostatSetpoint {
	return ThermostatSetpoint{
		Type:      ThermostatSetpointType(payload[2] & 0x0F),
		Precision: (payload[3] & setpointPrecisionMask) >> setpointPrecisionShift,
		Scale:     (payload[3] & setpointScaleMask) >> setpointScaleShift,
		Size:      (payload[3] & setpointSizeMask),
		Value:     payload[4:],
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Davis Webb
// Copyright 2015 Zhandos Suleimenov
// Copyright 2015 Luke Shumaker

package domain_handlers

import (
	"bytes"
	"encoding/json"
	"io"
	"io/ioutil"
	"locale"
	"net/http"
	"net/mail"
	"net/url"
	"periwinkle"
	"periwinkle/backend"
	"periwinkle/twilio"
	"postfixpipe"
	"strings"
)

func HandleSMS(r io.Reader, name string, db *periwinkle.Tx, cfg *periwinkle.Cfg) postfixpipe.ExitStatus {
	message, uerr := mail.ReadMessage(r)
	if uerr != nil {
		periwinkle.LogErr(locale.UntranslatedError(uerr))
		return postfixpipe.EX_NOINPUT
	}

	group := message.Header.Get("From")
	user := backend.GetUserByAddress(db, "sms", name)

	smsFrom := backend.GetTwilioNumberByUserAndGroup(db, user.ID, strings.Split(group, "@")[0])

	if smsFrom == "" {
		twilio_num := twilio.GetUnusedTwilioNumbersByUser(cfg, db, user.ID)
		if twilio_num == nil {
			new_num, err := twilio.NewPhoneNum(cfg)
			if err != nil {
				periwinkle.LogErr(err)
				return postfixpipe.EX_UNAVAILABLE
			}
			backend.AssignTwilioNumber(db, user.ID, strings.Split(group, "@")[0], new_num)
			smsFrom = new_num
		} else {
			backend.AssignTwilioNumber(db, user.ID, strings.Split(group, "@")[0], twilio_num[0])
			smsFrom = twilio_num[0]
		}
	}

	smsBody := message.Header.Get("Subject")
	//smsBody, err := ioutil.ReadAll(message.Body)
	//if err != nil {
	//	return "", err
	//}

	messagesURL := "https://api.twilio.com/2010-04-01/Accounts/" + cfg.TwilioAccountID + "/Messages.json"

	v := url.Values{}
	v.Set("From", smsFrom)
	v.Set("To", name)
	v.Set("Body", string(smsBody))
	v.Set("StatusCallback", cfg.WebRoot+"/callbacks/twilio-sms")
	//host,_ := os.Hostname()
	//v.Set("StatusCallback", "http://" + host + ":8080/callbacks/twilio-sms")
	client := &http.Client{}

	req, uerr := http.NewRequest("POST", messagesURL, bytes.NewBuffer([]byte(v.Encode())))
	if uerr != nil {
		periwinkle.LogErr(locale.UntranslatedError(uerr))
		return postfixpipe.EX_UNAVAILABLE
	}
	req.SetBasicAuth(cfg.TwilioAccountID, cfg.TwilioAuthToken)
	req.Header.Add("Accept", "application/json")
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

	resp, uerr := client.Do(req)
	defer resp.Body.Close()
	if uerr != nil {
		periwinkle.LogErr(locale.UntranslatedError(uerr))
		return postfixpipe.EX_UNAVAILABLE
	}

	if resp.StatusCode != 200 && resp.StatusCode != 201 {
		return postfixpipe.EX_UNAVAILABLE
	}

	body, uerr := ioutil.ReadAll(resp.Body)
	if uerr != nil {
		periwinkle.LogErr(locale.UntranslatedError(uerr))
		return postfixpipe.EX_UNAVAILABLE
	}

	tmessage := twilio.Message{}
	json.Unmarshal([]byte(body), &tmessage)
	_, err := TwilioSMSWaitForCallback(cfg, tmessage.Sid)
	if err != nil {
		periwinkle.LogErr(err)
		return postfixpipe.EX_UNAVAILABLE
	}
	return postfixpipe.EX_OK
}
<commit_msg>changes sitting here<commit_after>// Copyright 2015 Davis Webb
// Copyright 2015 Zhandos Suleimenov
// Copyright 2015 Luke Shumaker

package domain_handlers

import (
	"bytes"
	"encoding/json"
	"io"
	"io/ioutil"
	"locale"
	"net/http"
	"net/mail"
	"net/url"
	"periwinkle"
	"periwinkle/backend"
	"periwinkle/twilio"
	"postfixpipe"
	"strings"
)

func HandleSMS(r io.Reader, name string, db *periwinkle.Tx, cfg *periwinkle.Cfg) postfixpipe.ExitStatus {
	message, uerr := mail.ReadMessage(r)
	if uerr != nil {
		periwinkle.LogErr(locale.UntranslatedError(uerr))
		return postfixpipe.EX_NOINPUT
	}

	group := message.Header.Get("From")
	user := backend.GetUserByAddress(db, "sms", name)

	smsFrom := backend.GetTwilioNumberByUserAndGroup(db, user.ID, strings.Split(group, "@")[0])

	if smsFrom == "" {
		twilio_num := twilio.GetUnusedTwilioNumbersByUser(cfg, db, user.ID)
		if twilio_num == nil {
			new_num, err := twilio.NewPhoneNum(cfg)
			if err != nil {
				periwinkle.LogErr(err)
				return postfixpipe.EX_UNAVAILABLE
			}
			backend.AssignTwilioNumber(db, user.ID, strings.Split(group, "@")[0], new_num)
			smsFrom = new_num
		} else {
			backend.AssignTwilioNumber(db, user.ID, strings.Split(group, "@")[0], twilio_num[0])
			smsFrom = twilio_num[0]
		}
	}

	smsBody := message.Header.Get("Subject")
	//smsBody, err := ioutil.ReadAll(message.Body)
	//if err != nil {
	//	return "", err
	//}

	messagesURL := "https://api.twilio.com/2010-04-01/Accounts/" + cfg.TwilioAccountID + "/Messages.json"

	v := url.Values{}
	v.Set("From", smsFrom)
	v.Set("To", name)
	v.Set("Body", string(smsBody))
	v.Set("StatusCallback", cfg.WebRoot+"/callbacks/twilio-sms")
	//host,_ := os.Hostname()
	//v.Set("StatusCallback", "http://" + host + ":8080/callbacks/twilio-sms")
	client := &http.Client{}

	req, uerr := http.NewRequest("POST", messagesURL, bytes.NewBuffer([]byte(v.Encode())))
	if uerr != nil {
		periwinkle.LogErr(locale.UntranslatedError(uerr))
		return postfixpipe.EX_UNAVAILABLE
	}
	req.SetBasicAuth(cfg.TwilioAccountID, cfg.TwilioAuthToken)
	req.Header.Add("Accept", "application/json")
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

	resp, uerr := client.Do(req)
	defer resp.Body.Close()
	if uerr != nil {
		periwinkle.LogErr(locale.UntranslatedError(uerr))
		return postfixpipe.EX_UNAVAILABLE
	}

	if resp.StatusCode != 200 && resp.StatusCode != 201 {
		return postfixpipe.EX_UNAVAILABLE
	}

	body, uerr := ioutil.ReadAll(resp.Body)
	if uerr != nil {
		periwinkle.LogErr(locale.UntranslatedError(uerr))
		return postfixpipe.EX_UNAVAILABLE
	}
	return postfixpipe.EX_OK
	tmessage := twilio.Message{}
	json.Unmarshal([]byte(body), &tmessage)
	_, err := TwilioSMSWaitForCallback(cfg, tmessage.Sid)
	if err != nil {
		periwinkle.LogErr(err)
		return postfixpipe.EX_UNAVAILABLE
	}
	return postfixpipe.EX_OK
}
<|endoftext|>


<commit_before>package shell

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"strings"

	prompt "github.com/c-bata/go-prompt"
	"github.com/fatih/color"
	"github.com/pachyderm/pachyderm/src/client/pkg/config"
	"github.com/pachyderm/pachyderm/src/server/pkg/uuid"
	"github.com/spf13/cobra"
)

const (
	completionAnnotation  string = "completion"
	ldThreshold           int    = 2
	defaultMaxCompletions int64  = 64
)

type CompletionFunc func(flag, arg string, maxCompletions int64) []prompt.Suggest

var completions map[string]CompletionFunc = make(map[string]CompletionFunc)

func RegisterCompletionFunc(cmd *cobra.Command, completionFunc CompletionFunc) {
	id := uuid.NewWithoutDashes()

	if cmd.Annotations == nil {
		cmd.Annotations = make(map[string]string)
	} else if _, ok := cmd.Annotations[completionAnnotation]; ok {
		panic("duplicate completion func registration")
	}
	cmd.Annotations[completionAnnotation] = id
	completions[id] = completionFunc
}

type shell struct {
	rootCmd        *cobra.Command
	maxCompletions int64
}

func newShell(rootCmd *cobra.Command, maxCompletions int64) *shell {
	if maxCompletions == 0 {
		maxCompletions = defaultMaxCompletions
	}
	return &shell{
		rootCmd:        rootCmd,
		maxCompletions: maxCompletions,
	}
}

func (s *shell) executor(in string) {
	cmd := exec.Command("bash")
	cmd.Stdin = strings.NewReader("pachctl " + in)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Run()
	return
}

func (s *shell) suggestor(in prompt.Document) []prompt.Suggest {
	args := strings.Fields(in.Text)
	if len(strings.TrimSuffix(in.Text, " ")) < len(in.Text) {
		args = append(args, "")
	}
	cmd := s.rootCmd
	text := ""
	if len(args) > 0 {
		var err error
		cmd, _, err = s.rootCmd.Traverse(args[:len(args)-1])
		if err != nil {
			log.Fatal(err)
		}
		text = args[len(args)-1]
	}
	flag := ""
	if len(args) > 1 {
		if args[len(args)-2][0] == '-' {
			flag = args[len(args)-2]
		}
	}
	suggestions := cmd.SuggestionsFor(text)
	if len(suggestions) > 0 {
		var result []prompt.Suggest
		for _, suggestion := range suggestions {
			cmd, _, err := cmd.Traverse([]string{suggestion})
			if err != nil {
				log.Fatal(err)
			}
			result = append(result, prompt.Suggest{
				Text:        suggestion,
				Description: cmd.Short,
			})
		}
		return result
	}
	if id, ok := cmd.Annotations[completionAnnotation]; ok {
		completionFunc := completions[id]
		suggests := completionFunc(flag, text, s.maxCompletions)
		if int64(len(suggests)) > s.maxCompletions {
			suggests = suggests[:s.maxCompletions]
		}
		var result []prompt.Suggest
		for _, s := range suggests {
			sText := s.Text
			if len(text) < len(sText) {
				sText = sText[:len(text)]
			}
			if ld(sText, text, true) < ldThreshold {
				result = append(result, s)
			}
		}
		return result
	}
	return nil
}

func (s *shell) run() {
	color.NoColor = true // color doesn't work in terminal
	prompt.New(
		s.executor,
		s.suggestor,
		prompt.OptionPrefix(">>> "),
		prompt.OptionTitle("Pachyderm Shell"),
		prompt.OptionLivePrefix(func() (string, bool) {
			cfg, err := config.Read(true)
			if err != nil {
				return "", false
			}
			activeContext, _, err := cfg.ActiveContext()
			if err != nil {
				return "", false
			}
			return fmt.Sprintf("context:(%s) >>> ", activeContext), true
		}),
	).Run()
}

// Run runs a prompt, it does not return.
func Run(rootCmd *cobra.Command, maxCompletions int64) {
	newShell(rootCmd, maxCompletions).run()
}

// ld computes the Levenshtein Distance for two strings.
func ld(s, t string, ignoreCase bool) int {
	if ignoreCase {
		s = strings.ToLower(s)
		t = strings.ToLower(t)
	}
	d := make([][]int, len(s)+1)
	for i := range d {
		d[i] = make([]int, len(t)+1)
	}
	for i := range d {
		d[i][0] = i
	}
	for j := range d[0] {
		d[0][j] = j
	}
	for j := 1; j <= len(t); j++ {
		for i := 1; i <= len(s); i++ {
			if s[i-1] == t[j-1] {
				d[i][j] = d[i-1][j-1]
			} else {
				min := d[i-1][j]
				if d[i][j-1] < min {
					min = d[i][j-1]
				}
				if d[i-1][j-1] < min {
					min = d[i-1][j-1]
				}
				d[i][j] = min + 1
			}
		}

	}
	return d[len(s)][len(t)]
}
<commit_msg>Enforce maxCompletions after spelling matching.<commit_after>package shell

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"strings"

	prompt "github.com/c-bata/go-prompt"
	"github.com/fatih/color"
	"github.com/pachyderm/pachyderm/src/client/pkg/config"
	"github.com/pachyderm/pachyderm/src/server/pkg/uuid"
	"github.com/spf13/cobra"
)

const (
	completionAnnotation  string = "completion"
	ldThreshold           int    = 2
	defaultMaxCompletions int64  = 64
)

type CompletionFunc func(flag, arg string, maxCompletions int64) []prompt.Suggest

var completions map[string]CompletionFunc = make(map[string]CompletionFunc)

func RegisterCompletionFunc(cmd *cobra.Command, completionFunc CompletionFunc) {
	id := uuid.NewWithoutDashes()

	if cmd.Annotations == nil {
		cmd.Annotations = make(map[string]string)
	} else if _, ok := cmd.Annotations[completionAnnotation]; ok {
		panic("duplicate completion func registration")
	}
	cmd.Annotations[completionAnnotation] = id
	completions[id] = completionFunc
}

type shell struct {
	rootCmd        *cobra.Command
	maxCompletions int64
}

func newShell(rootCmd *cobra.Command, maxCompletions int64) *shell {
	if maxCompletions == 0 {
		maxCompletions = defaultMaxCompletions
	}
	return &shell{
		rootCmd:        rootCmd,
		maxCompletions: maxCompletions,
	}
}

func (s *shell) executor(in string) {
	cmd := exec.Command("bash")
	cmd.Stdin = strings.NewReader("pachctl " + in)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Run()
	return
}

func (s *shell) suggestor(in prompt.Document) []prompt.Suggest {
	args := strings.Fields(in.Text)
	if len(strings.TrimSuffix(in.Text, " ")) < len(in.Text) {
		args = append(args, "")
	}
	cmd := s.rootCmd
	text := ""
	if len(args) > 0 {
		var err error
		cmd, _, err = s.rootCmd.Traverse(args[:len(args)-1])
		if err != nil {
			log.Fatal(err)
		}
		text = args[len(args)-1]
	}
	flag := ""
	if len(args) > 1 {
		if args[len(args)-2][0] == '-' {
			flag = args[len(args)-2]
		}
	}
	suggestions := cmd.SuggestionsFor(text)
	if len(suggestions) > 0 {
		var result []prompt.Suggest
		for _, suggestion := range suggestions {
			cmd, _, err := cmd.Traverse([]string{suggestion})
			if err != nil {
				log.Fatal(err)
			}
			result = append(result, prompt.Suggest{
				Text:        suggestion,
				Description: cmd.Short,
			})
		}
		return result
	}
	if id, ok := cmd.Annotations[completionAnnotation]; ok {
		completionFunc := completions[id]
		suggests := completionFunc(flag, text, s.maxCompletions)
		var result []prompt.Suggest
		for _, sug := range suggests {
			sText := sug.Text
			if len(text) < len(sText) {
				sText = sText[:len(text)]
			}
			if ld(sText, text, true) < ldThreshold {
				result = append(result, sug)
			}
			if int64(len(result)) > s.maxCompletions {
				break
			}
		}
		return result
	}
	return nil
}

func (s *shell) run() {
	color.NoColor = true // color doesn't work in terminal
	prompt.New(
		s.executor,
		s.suggestor,
		prompt.OptionPrefix(">>> "),
		prompt.OptionTitle("Pachyderm Shell"),
		prompt.OptionLivePrefix(func() (string, bool) {
			cfg, err := config.Read(true)
			if err != nil {
				return "", false
			}
			activeContext, _, err := cfg.ActiveContext()
			if err != nil {
				return "", false
			}
			return fmt.Sprintf("context:(%s) >>> ", activeContext), true
		}),
	).Run()
}

// Run runs a prompt, it does not return.
func Run(rootCmd *cobra.Command, maxCompletions int64) {
	newShell(rootCmd, maxCompletions).run()
}

// ld computes the Levenshtein Distance for two strings.
func ld(s, t string, ignoreCase bool) int {
	if ignoreCase {
		s = strings.ToLower(s)
		t = strings.ToLower(t)
	}
	d := make([][]int, len(s)+1)
	for i := range d {
		d[i] = make([]int, len(t)+1)
	}
	for i := range d {
		d[i][0] = i
	}
	for j := range d[0] {
		d[0][j] = j
	}
	for j := 1; j <= len(t); j++ {
		for i := 1; i <= len(s); i++ {
			if s[i-1] == t[j-1] {
				d[i][j] = d[i-1][j-1]
			} else {
				min := d[i-1][j]
				if d[i][j-1] < min {
					min = d[i][j-1]
				}
				if d[i-1][j-1] < min {
					min = d[i-1][j-1]
				}
				d[i][j] = min + 1
			}
		}

	}
	return d[len(s)][len(t)]
}
<|endoftext|>


<commit_before>// Copyright © 2017 uxbh
// This file is part of github.com/uxbh/ztdns.

package ztapi

import "fmt"

// GetNetworkInfo returns a Nework containing information about a ZeroTier network
func GetNetworkInfo(API, host, networkID string) (*Network, error) {
	fmt.Printf("%v\n", ^uint(0))
	resp := new(Network)
	url := fmt.Sprintf("%s/network/%s", host, networkID)
	err := getJSON(url, API, resp)
	if err != nil {
		return nil, fmt.Errorf("Unable to get network info: %s", err.Error())
	}
	return resp, nil
}

// Network contains the JSON response for a request for a network
type Network struct {
	ID    string
	Type  string
	Clock apiTime
	UI    struct {
		FlowRulesCollapsed    bool
		MembersCollapsed      bool
		MembersHelpCollapsed  bool
		RulesHelpCollapsed    bool
		SettingsCollapsed     bool
		SettingsHelpCollapsed bool
		V4EasyMode            bool
	}
	Config struct {
		ActiveMemberCount     int
		AuthorizedMemberCount int
		Capabilities          []string
		Clock                 apiTime
		CreationTime          apiTime
		EnableBroadcast       bool
		ID                    string
		IPAssignmentPools     []struct {
			IPRangeEnd   string
			IPRangeStart string
		}
		MulticastLimit int
		Name           string
		Nwid           string
		Objtype        string
		Private        bool
		Revision       int
		Routes         []struct {
			Target string
			Via    string
		}
		Rules []struct {
			EtherType int
			Not       bool
			Or        bool
			Type      string
		}
		Tags             []string
		TotalMemberCount int
		V4AssignMode     struct {
			Zt bool
		}
		V6AssignMode struct {
			Sixplane bool `json:"6plane"`
			Rfc4193  bool
			Zt       bool
		}
	}
	RuleSource  string
	Description string
	Permissions map[string]struct {
		A bool
		D bool
		M bool
		R bool
		T string
	}
	OnlineMemberCount int
	CapabilitesByName map[string]string
	TagsByName        map[string]string
	CircuitTestEvery  int
}
<commit_msg>Remove debugging message<commit_after>// Copyright © 2017 uxbh
// This file is part of github.com/uxbh/ztdns.

package ztapi

import "fmt"

// GetNetworkInfo returns a Nework containing information about a ZeroTier network
func GetNetworkInfo(API, host, networkID string) (*Network, error) {
	resp := new(Network)
	url := fmt.Sprintf("%s/network/%s", host, networkID)
	err := getJSON(url, API, resp)
	if err != nil {
		return nil, fmt.Errorf("Unable to get network info: %s", err.Error())
	}
	return resp, nil
}

// Network contains the JSON response for a request for a network
type Network struct {
	ID    string
	Type  string
	Clock apiTime
	UI    struct {
		FlowRulesCollapsed    bool
		MembersCollapsed      bool
		MembersHelpCollapsed  bool
		RulesHelpCollapsed    bool
		SettingsCollapsed     bool
		SettingsHelpCollapsed bool
		V4EasyMode            bool
	}
	Config struct {
		ActiveMemberCount     int
		AuthorizedMemberCount int
		Capabilities          []string
		Clock                 apiTime
		CreationTime          apiTime
		EnableBroadcast       bool
		ID                    string
		IPAssignmentPools     []struct {
			IPRangeEnd   string
			IPRangeStart string
		}
		MulticastLimit int
		Name           string
		Nwid           string
		Objtype        string
		Private        bool
		Revision       int
		Routes         []struct {
			Target string
			Via    string
		}
		Rules []struct {
			EtherType int
			Not       bool
			Or        bool
			Type      string
		}
		Tags             []string
		TotalMemberCount int
		V4AssignMode     struct {
			Zt bool
		}
		V6AssignMode struct {
			Sixplane bool `json:"6plane"`
			Rfc4193  bool
			Zt       bool
		}
	}
	RuleSource  string
	Description string
	Permissions map[string]struct {
		A bool
		D bool
		M bool
		R bool
		T string
	}
	OnlineMemberCount int
	CapabilitesByName map[string]string
	TagsByName        map[string]string
	CircuitTestEvery  int
}
<|endoftext|>


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

package orchestrator

import (
	"allmark.io/modules/common/route"
	"allmark.io/modules/web/view/viewmodel"
)

type NavigationOrchestrator struct {
	*Orchestrator

	toplevelNavigation *viewmodel.ToplevelNavigation
}

func (orchestrator *NavigationOrchestrator) GetToplevelNavigation() *viewmodel.ToplevelNavigation {

	if orchestrator.toplevelNavigation != nil {
		return orchestrator.toplevelNavigation
	}

	// updateToplevelNavigation creates a new toplevel navigation and stores it in the cache
	updateToplevelNavigation := func(r route.Route) {
		root := route.New()
		toplevelEntries := make([]*viewmodel.ToplevelEntry, 0)

		topLevelChilds := orchestrator.getChilds(root)

		// abort if there are more than 5 childs
		if len(topLevelChilds) > 4 {
			orchestrator.toplevelNavigation = &viewmodel.ToplevelNavigation{
				Entries: toplevelEntries,
			}

			return
		}

		for _, child := range topLevelChilds {

			toplevelEntries = append(toplevelEntries, &viewmodel.ToplevelEntry{
				Title: child.Title,
				Path:  orchestrator.itemPather().Path(child.Route().Value()),
			})

		}

		orchestrator.toplevelNavigation = &viewmodel.ToplevelNavigation{
			Entries: toplevelEntries,
		}
	}

	// write the cache
	updateToplevelNavigation(route.New())

	// register update callbacks
	orchestrator.registerUpdateCallback("update toplevel navigation", UpdateTypeNew, updateToplevelNavigation)
	orchestrator.registerUpdateCallback("update toplevel navigation", UpdateTypeModified, updateToplevelNavigation)
	orchestrator.registerUpdateCallback("update toplevel navigation", UpdateTypeDeleted, updateToplevelNavigation)

	return orchestrator.GetToplevelNavigation()
}

func (orchestrator *NavigationOrchestrator) GetBreadcrumbNavigation(route route.Route) *viewmodel.BreadcrumbNavigation {

	// create a new bread crumb navigation
	navigation := &viewmodel.BreadcrumbNavigation{
		Entries: make([]*viewmodel.Breadcrumb, 0),
	}

	// get the item for the supplied route
	item := orchestrator.getItem(route)
	if item == nil {
		orchestrator.logger.Error("Returning an empty navigation model because there is no item for route %q.", route)
		return navigation
	}

	// recurse if there is a parent
	if parent := orchestrator.getParent(item.Route()); parent != nil {
		navigation.Entries = append(navigation.Entries, orchestrator.GetBreadcrumbNavigation(parent.Route()).Entries...)
	}

	// append a new navigation entry and return it
	navigation.Entries = append(navigation.Entries, &viewmodel.Breadcrumb{
		Title: item.Title,
		Level: item.Route().Level(),
		Path:  orchestrator.itemPather().Path(item.Route().Value()),
	})

	// mark the entries
	for index, entry := range navigation.Entries {
		if index < (len(navigation.Entries) - 1) {
			entry.IsLast = false
		} else {
			entry.IsLast = true
		}
	}

	return navigation
}

func (orchestrator *NavigationOrchestrator) GetItemNavigation(route route.Route) *viewmodel.ItemNavigation {

	// create a new item navigation
	navigation := &viewmodel.ItemNavigation{}

	// get the item for the supplied route
	item := orchestrator.getItem(route)
	if item == nil {
		return navigation
	}

	// get the parent
	if parent := orchestrator.getParent(item.Route()); parent != nil {
		navigation.Parent = &viewmodel.NavEntry{
			Title:       parent.Title,
			Description: parent.Description,
			Path:        orchestrator.itemPather().Path(parent.Route().Value()),
		}
	}

	// previous
	if previous := orchestrator.getPrevious(item.Route()); previous != nil {
		navigation.Previous = &viewmodel.NavEntry{
			Title:       previous.Title,
			Description: previous.Description,
			Path:        orchestrator.itemPather().Path(previous.Route().Value()),
		}
	}

	// next
	if next := orchestrator.getNext(item.Route()); next != nil {
		navigation.Next = &viewmodel.NavEntry{
			Title:       next.Title,
			Description: next.Description,
			Path:        orchestrator.itemPather().Path(next.Route().Value()),
		}
	}

	return navigation
}
<commit_msg>Revert "Skip the top-level navigation if there are more than 5 entries"<commit_after>// Copyright 2014 Andreas Koch. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package orchestrator

import (
	"allmark.io/modules/common/route"
	"allmark.io/modules/web/view/viewmodel"
)

type NavigationOrchestrator struct {
	*Orchestrator

	toplevelNavigation *viewmodel.ToplevelNavigation
}

func (orchestrator *NavigationOrchestrator) GetToplevelNavigation() *viewmodel.ToplevelNavigation {

	if orchestrator.toplevelNavigation != nil {
		return orchestrator.toplevelNavigation
	}

	// updateToplevelNavigation creates a new toplevel navigation and stores it in the cache
	updateToplevelNavigation := func(r route.Route) {
		root := route.New()
		toplevelEntries := make([]*viewmodel.ToplevelEntry, 0)

		for _, child := range orchestrator.getChilds(root) {

			toplevelEntries = append(toplevelEntries, &viewmodel.ToplevelEntry{
				Title: child.Title,
				Path:  orchestrator.itemPather().Path(child.Route().Value()),
			})

		}

		orchestrator.toplevelNavigation = &viewmodel.ToplevelNavigation{
			Entries: toplevelEntries,
		}
	}

	// write the cache
	updateToplevelNavigation(route.New())

	// register update callbacks
	orchestrator.registerUpdateCallback("update toplevel navigation", UpdateTypeNew, updateToplevelNavigation)
	orchestrator.registerUpdateCallback("update toplevel navigation", UpdateTypeModified, updateToplevelNavigation)
	orchestrator.registerUpdateCallback("update toplevel navigation", UpdateTypeDeleted, updateToplevelNavigation)

	return orchestrator.GetToplevelNavigation()
}

func (orchestrator *NavigationOrchestrator) GetBreadcrumbNavigation(route route.Route) *viewmodel.BreadcrumbNavigation {

	// create a new bread crumb navigation
	navigation := &viewmodel.BreadcrumbNavigation{
		Entries: make([]*viewmodel.Breadcrumb, 0),
	}

	// get the item for the supplied route
	item := orchestrator.getItem(route)
	if item == nil {
		orchestrator.logger.Error("Returning an empty navigation model because there is no item for route %q.", route)
		return navigation
	}

	// recurse if there is a parent
	if parent := orchestrator.getParent(item.Route()); parent != nil {
		navigation.Entries = append(navigation.Entries, orchestrator.GetBreadcrumbNavigation(parent.Route()).Entries...)
	}

	// append a new navigation entry and return it
	navigation.Entries = append(navigation.Entries, &viewmodel.Breadcrumb{
		Title: item.Title,
		Level: item.Route().Level(),
		Path:  orchestrator.itemPather().Path(item.Route().Value()),
	})

	// mark the entries
	for index, entry := range navigation.Entries {
		if index < (len(navigation.Entries) - 1) {
			entry.IsLast = false
		} else {
			entry.IsLast = true
		}
	}

	return navigation
}

func (orchestrator *NavigationOrchestrator) GetItemNavigation(route route.Route) *viewmodel.ItemNavigation {

	// create a new item navigation
	navigation := &viewmodel.ItemNavigation{}

	// get the item for the supplied route
	item := orchestrator.getItem(route)
	if item == nil {
		return navigation
	}

	// get the parent
	if parent := orchestrator.getParent(item.Route()); parent != nil {
		navigation.Parent = &viewmodel.NavEntry{
			Title:       parent.Title,
			Description: parent.Description,
			Path:        orchestrator.itemPather().Path(parent.Route().Value()),
		}
	}

	// previous
	if previous := orchestrator.getPrevious(item.Route()); previous != nil {
		navigation.Previous = &viewmodel.NavEntry{
			Title:       previous.Title,
			Description: previous.Description,
			Path:        orchestrator.itemPather().Path(previous.Route().Value()),
		}
	}

	// next
	if next := orchestrator.getNext(item.Route()); next != nil {
		navigation.Next = &viewmodel.NavEntry{
			Title:       next.Title,
			Description: next.Description,
			Path:        orchestrator.itemPather().Path(next.Route().Value()),
		}
	}

	return navigation
}
<|endoftext|>


<commit_before>package compute

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
)

// HealthMonitor represents a load-balancer persistence (stickiness) profile.
type HealthMonitor struct {
	ID               string `json:"id"`
	Name             string `json:"name"`
	IsNodeCompatible bool   `json:"nodeCompatible"`
	IsPoolCompatible bool   `json:"poolCompatible"`
}

// HealthMonitors represents a page of HealthMonitor results.
type HealthMonitors struct {
	Items []HealthMonitor `json:"defaultHealthMonitor"`

	PagedResult
}

// ListDefaultHealthMonitors retrieves a list of all default load-balancing health monitors in the specified network domain.
func (client *Client) ListDefaultHealthMonitors(networkDomainID string, paging *Paging) (healthMonitors *HealthMonitors, err error) {
	organizationID, err := client.getOrganizationID()
	if err != nil {
		return nil, err
	}

	requestURI := fmt.Sprintf("%s/networkDomainVip/defaultHealthMonitor?networkDomainId=%s&%s",
		organizationID,
		url.QueryEscape(networkDomainID),
		paging.EnsurePaging().toQueryParameters(),
	)
	request, err := client.newRequestV22(requestURI, http.MethodGet, nil)
	if err != nil {
		return nil, err
	}

	responseBody, statusCode, err := client.executeRequest(request)
	if err != nil {
		return nil, err
	}

	if statusCode != http.StatusOK {
		var apiResponse *APIResponseV2

		apiResponse, err = readAPIResponseAsJSON(responseBody, statusCode)
		if err != nil {
			return nil, err
		}

		return nil, apiResponse.ToError("Request to list default health monitors in network domain '%s' failed with status code %d (%s): %s", networkDomainID, statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	healthMonitors = &HealthMonitors{}
	err = json.Unmarshal(responseBody, healthMonitors)
	if err != nil {
		return nil, err
	}

	return healthMonitors, nil
}
<commit_msg>Escape query parameters for server health monitor request URIs (#7).<commit_after>package compute

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
)

// HealthMonitor represents a load-balancer persistence (stickiness) profile.
type HealthMonitor struct {
	ID               string `json:"id"`
	Name             string `json:"name"`
	IsNodeCompatible bool   `json:"nodeCompatible"`
	IsPoolCompatible bool   `json:"poolCompatible"`
}

// HealthMonitors represents a page of HealthMonitor results.
type HealthMonitors struct {
	Items []HealthMonitor `json:"defaultHealthMonitor"`

	PagedResult
}

// ListDefaultHealthMonitors retrieves a list of all default load-balancing health monitors in the specified network domain.
func (client *Client) ListDefaultHealthMonitors(networkDomainID string, paging *Paging) (healthMonitors *HealthMonitors, err error) {
	organizationID, err := client.getOrganizationID()
	if err != nil {
		return nil, err
	}

	requestURI := fmt.Sprintf("%s/networkDomainVip/defaultHealthMonitor?networkDomainId=%s&%s",
		url.QueryEscape(organizationID),
		url.QueryEscape(networkDomainID),
		paging.EnsurePaging().toQueryParameters(),
	)
	request, err := client.newRequestV22(requestURI, http.MethodGet, nil)
	if err != nil {
		return nil, err
	}

	responseBody, statusCode, err := client.executeRequest(request)
	if err != nil {
		return nil, err
	}

	if statusCode != http.StatusOK {
		var apiResponse *APIResponseV2

		apiResponse, err = readAPIResponseAsJSON(responseBody, statusCode)
		if err != nil {
			return nil, err
		}

		return nil, apiResponse.ToError("Request to list default health monitors in network domain '%s' failed with status code %d (%s): %s", networkDomainID, statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	healthMonitors = &HealthMonitors{}
	err = json.Unmarshal(responseBody, healthMonitors)
	if err != nil {
		return nil, err
	}

	return healthMonitors, nil
}
<|endoftext|>


<commit_before>package compute

import (
	"fmt"
	"log"
	"time"
)

// WaitForDeploy waits for a resource's pending deployment operation to complete.
func (client *Client) WaitForDeploy(resourceType ResourceType, id string, timeout time.Duration) (resource Resource, err error) {
	return client.waitForPendingOperation(resourceType, id, "Deploy", ResourceStatusPendingAdd, timeout)
}

// WaitForEdit waits for a resource's pending edit operation to complete.
func (client *Client) WaitForEdit(resourceType ResourceType, id string, timeout time.Duration) (resource Resource, err error) {
	return client.WaitForChange(resourceType, id, "Edit", timeout)
}

// WaitForAdd waits for a resource's pending add operation to complete.
func (client *Client) WaitForAdd(resourceType ResourceType, id string, actionDescription string, timeout time.Duration) (resource Resource, err error) {
	return client.waitForPendingOperation(resourceType, id, actionDescription, ResourceStatusPendingAdd, timeout)
}

// WaitForChange waits for a resource's pending change operation to complete.
func (client *Client) WaitForChange(resourceType ResourceType, id string, actionDescription string, timeout time.Duration) (resource Resource, err error) {
	return client.waitForPendingOperation(resourceType, id, actionDescription, ResourceStatusPendingChange, timeout)
}

// WaitForDelete waits for a resource's pending deletion to complete.
func (client *Client) WaitForDelete(resourceType ResourceType, id string, timeout time.Duration) error {
	_, err := client.waitForPendingOperation(resourceType, id, "Delete", ResourceStatusPendingDelete, timeout)

	return err
}

// waitForPendingOperation waits for a resource's pending operation to complete (i.e. for its status to become ResourceStatusNormal or the resource to disappear if expectedStatus is ResourceStatusPendingDelete).
func (client *Client) waitForPendingOperation(resourceType ResourceType, id string, actionDescription string, expectedStatus string, timeout time.Duration) (resource Resource, err error) {
	return client.waitForResourceStatus(resourceType, id, actionDescription, expectedStatus, ResourceStatusNormal, timeout)
}

// waitForResourceStatus polls a resource for its status (which is expected to initially be expectedStatus) until it becomes expectedStatus.
// getResource is a function that, given the resource Id, will retrieve the resource.
// timeout is the length of time before the wait times out.
func (client *Client) waitForResourceStatus(resourceType ResourceType, id string, actionDescription string, expectedStatus string, targetStatus string, timeout time.Duration) (resource Resource, err error) {
	waitTimeout := time.NewTimer(timeout)
	defer waitTimeout.Stop()

	pollTicker := time.NewTicker(5 * time.Second)
	defer pollTicker.Stop()

	resourceDescription, err := GetResourceDescription(resourceType)
	if err != nil {
		return nil, err
	}

	for {
		select {
		case <-waitTimeout.C:
			return nil, fmt.Errorf("Timed out after waiting %d seconds for %s of %s '%s' to complete", timeout/time.Second, actionDescription, resourceDescription, id)

		case <-pollTicker.C:
			log.Printf("Polling status for %s '%s'...", resourceDescription, id)
			resource, err := client.GetResource(id, resourceType)
			if err != nil {
				return nil, err
			}
			if err != nil {
				return nil, err
			}

			if resource == nil || resource.IsDeleted() {
				if expectedStatus == ResourceStatusPendingDelete {
					log.Printf("%s '%s' has been successfully deleted.", resourceDescription, id)

					return nil, nil
				}

				return nil, fmt.Errorf("No %s was found with Id '%s'", resourceDescription, id)
			}

			switch resource.GetState() {
			case ResourceStatusNormal:
				log.Printf("%s of %s '%s' has successfully completed.", actionDescription, resourceDescription, id)

				return resource, nil

			case ResourceStatusPendingAdd:
				log.Printf("%s of %s '%s' is still in progress...", actionDescription, resourceDescription, id)

				continue
			case ResourceStatusPendingChange:
				log.Printf("%s of %s '%s' is still in progress...", actionDescription, resourceDescription, id)

				continue
			case ResourceStatusPendingDelete:
				log.Printf("%s of %s '%s' is still in progress...", actionDescription, resourceDescription, id)

				continue
			default:
				log.Printf("Unexpected status for %s '%s' ('%s').", resourceDescription, id, resource.GetState())

				return nil, fmt.Errorf("%s failed for %s '%s' ('%s'): encountered unexpected state '%s'", actionDescription, resourceDescription, id, resource.GetName(), resource.GetState())
			}
		}
	}
}
<commit_msg>Add special Wait function for Change that represents Delete of nested resource.<commit_after>package compute

import (
	"fmt"
	"log"
	"time"
)

// WaitForDeploy waits for a resource's pending deployment operation to complete.
func (client *Client) WaitForDeploy(resourceType ResourceType, id string, timeout time.Duration) (resource Resource, err error) {
	return client.waitForPendingOperation(resourceType, id, "Deploy", ResourceStatusPendingAdd, false, timeout)
}

// WaitForEdit waits for a resource's pending edit operation to complete.
func (client *Client) WaitForEdit(resourceType ResourceType, id string, timeout time.Duration) (resource Resource, err error) {
	return client.WaitForChange(resourceType, id, "Edit", timeout)
}

// WaitForAdd waits for a resource's pending add operation to complete.
func (client *Client) WaitForAdd(resourceType ResourceType, id string, actionDescription string, timeout time.Duration) (resource Resource, err error) {
	return client.waitForPendingOperation(resourceType, id, actionDescription, ResourceStatusPendingAdd, false, timeout)
}

// WaitForChange waits for a resource's pending change operation to complete.
func (client *Client) WaitForChange(resourceType ResourceType, id string, actionDescription string, timeout time.Duration) (resource Resource, err error) {
	return client.waitForPendingOperation(resourceType, id, actionDescription, ResourceStatusPendingChange, false, timeout)
}

// WaitForNestedDeleteChange waits for a resource's pending change operation (actually the delete of a nested resource) to complete.
func (client *Client) WaitForNestedDeleteChange(resourceType ResourceType, id string, actionDescription string, timeout time.Duration) (resource Resource, err error) {
	return client.waitForPendingOperation(resourceType, id, actionDescription, ResourceStatusPendingChange, true, timeout)
}

// WaitForDelete waits for a resource's pending deletion to complete.
func (client *Client) WaitForDelete(resourceType ResourceType, id string, timeout time.Duration) error {
	_, err := client.waitForPendingOperation(resourceType, id, "Delete", ResourceStatusPendingDelete, true, timeout)

	return err
}

// waitForPendingOperation waits for a resource's pending operation to complete (i.e. for its status to become ResourceStatusNormal or the resource to disappear if expectedStatus is ResourceStatusPendingDelete).
func (client *Client) waitForPendingOperation(resourceType ResourceType, id string, actionDescription string, expectedStatus string, isDelete bool, timeout time.Duration) (resource Resource, err error) {
	return client.waitForResourceStatus(resourceType, id, actionDescription, expectedStatus, ResourceStatusNormal, isDelete, timeout)
}

// waitForResourceStatus polls a resource for its status (which is expected to initially be expectedStatus) until it becomes expectedStatus.
// getResource is a function that, given the resource Id, will retrieve the resource.
// timeout is the length of time before the wait times out.
func (client *Client) waitForResourceStatus(resourceType ResourceType, id string, actionDescription string, expectedStatus string, targetStatus string, isDelete bool, timeout time.Duration) (resource Resource, err error) {
	waitTimeout := time.NewTimer(timeout)
	defer waitTimeout.Stop()

	pollTicker := time.NewTicker(5 * time.Second)
	defer pollTicker.Stop()

	resourceDescription, err := GetResourceDescription(resourceType)
	if err != nil {
		return nil, err
	}

	for {
		select {
		case <-waitTimeout.C:
			return nil, fmt.Errorf("Timed out after waiting %d seconds for %s of %s '%s' to complete", timeout/time.Second, actionDescription, resourceDescription, id)

		case <-pollTicker.C:
			log.Printf("Polling status for %s '%s'...", resourceDescription, id)
			resource, err := client.GetResource(id, resourceType)
			if err != nil {
				return nil, err
			}
			if err != nil {
				return nil, err
			}

			if resource == nil || resource.IsDeleted() {
				if isDelete {
					log.Printf("%s '%s' has been successfully deleted.", resourceDescription, id)

					return nil, nil
				}

				return nil, fmt.Errorf("No %s was found with Id '%s'", resourceDescription, id)
			}

			switch resource.GetState() {
			case ResourceStatusNormal:
				log.Printf("%s of %s '%s' has successfully completed.", actionDescription, resourceDescription, id)

				return resource, nil

			case ResourceStatusPendingAdd:
				log.Printf("%s of %s '%s' is still in progress...", actionDescription, resourceDescription, id)

				continue
			case ResourceStatusPendingChange:
				log.Printf("%s of %s '%s' is still in progress...", actionDescription, resourceDescription, id)

				continue
			case ResourceStatusPendingDelete:
				log.Printf("%s of %s '%s' is still in progress...", actionDescription, resourceDescription, id)

				continue
			default:
				log.Printf("Unexpected status for %s '%s' ('%s').", resourceDescription, id, resource.GetState())

				return nil, fmt.Errorf("%s failed for %s '%s' ('%s'): encountered unexpected state '%s'", actionDescription, resourceDescription, id, resource.GetName(), resource.GetState())
			}
		}
	}
}
<|endoftext|>