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

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

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

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

package simulator

import (
	"flag"
	"fmt"
	"math"
	"math/rand"
	"time"

	kube_api "k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/resource"
	kube_client "k8s.io/kubernetes/pkg/client/unversioned"
	cmd "k8s.io/kubernetes/pkg/kubectl/cmd"
	"k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache"

	"github.com/golang/glog"
)

var (
	skipNodesWithSystemPods = flag.Bool("skip-nodes-with-system-pods", true,
		"If true cluster autoscaler will never delete nodes with pods from kube-system (except for DeamonSet "+
			"or mirror pods)")
	skipNodesWithLocalStorage = flag.Bool("skip-nodes-with-local-storage", true,
		"If true cluster autoscaler will never delete nodes with pods with local storage, e.g. EmptyDir or HostPath")
)

// FindNodesToRemove finds nodes that can be removed. Returns also an information about good
// rescheduling location for each of the pods.
func FindNodesToRemove(candidates []*kube_api.Node, allNodes []*kube_api.Node, pods []*kube_api.Pod,
	client *kube_client.Client, predicateChecker *PredicateChecker, maxCount int,
	fastCheck bool, oldHints map[string]string, usageTracker *UsageTracker,
	timestamp time.Time) (nodesToRemove []*kube_api.Node, podReschedulingHints map[string]string, finalError error) {

	nodeNameToNodeInfo := schedulercache.CreateNodeNameToInfoMap(pods)
	for _, node := range allNodes {
		if nodeInfo, found := nodeNameToNodeInfo[node.Name]; found {
			nodeInfo.SetNode(node)
		}
	}
	result := make([]*kube_api.Node, 0)

	evaluationType := "Detailed evaluation"
	if fastCheck {
		evaluationType = "Fast evaluation"
	}
	newHints := make(map[string]string, len(oldHints))

candidateloop:
	for _, node := range candidates {
		glog.V(2).Infof("%s: %s for removal", evaluationType, node.Name)

		var podsToRemove []*kube_api.Pod
		var err error

		if fastCheck {
			if nodeInfo, found := nodeNameToNodeInfo[node.Name]; found {
				podsToRemove, err = FastGetPodsToMove(nodeInfo, false, *skipNodesWithSystemPods, *skipNodesWithLocalStorage)
				if err != nil {
					glog.V(2).Infof("%s: node %s cannot be removed: %v", evaluationType, node.Name, err)
					continue candidateloop
				}
			} else {
				glog.V(2).Infof("%s: nodeInfo for %s not found", evaluationType, node.Name)
				continue candidateloop
			}
		} else {
			drainResult, _, _, err := cmd.GetPodsForDeletionOnNodeDrain(client, node.Name,
				kube_api.Codecs.UniversalDecoder(), false, true)

			if err != nil {
				glog.V(2).Infof("%s: node %s cannot be removed: %v", evaluationType, node.Name, err)
				continue candidateloop
			}
			podsToRemove = make([]*kube_api.Pod, 0, len(drainResult))
			for i := range drainResult {
				podsToRemove = append(podsToRemove, &drainResult[i])
			}
		}
		findProblems := findPlaceFor(node.Name, podsToRemove, allNodes, nodeNameToNodeInfo, predicateChecker, oldHints, newHints,
			usageTracker, timestamp)

		if findProblems == nil {
			result = append(result, node)
			glog.V(2).Infof("%s: node %s may be removed", evaluationType, node.Name)
			if len(result) >= maxCount {
				break candidateloop
			}
		} else {
			glog.V(2).Infof("%s: node %s is not suitable for removal %v", evaluationType, node.Name, err)
		}
	}
	return result, newHints, nil
}

// CalculateUtilization calculates utilization of a node, defined as total amount of requested resources divided by capacity.
func CalculateUtilization(node *kube_api.Node, nodeInfo *schedulercache.NodeInfo) (float64, error) {
	cpu, err := calculateUtilizationOfResource(node, nodeInfo, kube_api.ResourceCPU)
	if err != nil {
		return 0, err
	}
	mem, err := calculateUtilizationOfResource(node, nodeInfo, kube_api.ResourceMemory)
	if err != nil {
		return 0, err
	}
	return math.Max(cpu, mem), nil
}

func calculateUtilizationOfResource(node *kube_api.Node, nodeInfo *schedulercache.NodeInfo, resourceName kube_api.ResourceName) (float64, error) {
	nodeCapacity, found := node.Status.Capacity[resourceName]
	if !found {
		return 0, fmt.Errorf("Failed to get %v from %s", resourceName, node.Name)
	}
	if nodeCapacity.MilliValue() == 0 {
		return 0, fmt.Errorf("%v is 0 at %s", resourceName, node.Name)
	}
	podsRequest := resource.MustParse("0")
	for _, pod := range nodeInfo.Pods() {
		for _, container := range pod.Spec.Containers {
			if resourceValue, found := container.Resources.Requests[resourceName]; found {
				podsRequest.Add(resourceValue)
			}
		}
	}
	return float64(podsRequest.MilliValue()) / float64(nodeCapacity.MilliValue()), nil
}

// TODO: We don't need to pass list of nodes here as they are already available in nodeInfos.
func findPlaceFor(removedNode string, pods []*kube_api.Pod, nodes []*kube_api.Node, nodeInfos map[string]*schedulercache.NodeInfo,
	predicateChecker *PredicateChecker, oldHints map[string]string, newHints map[string]string, usageTracker *UsageTracker,
	timestamp time.Time) error {

	newNodeInfos := make(map[string]*schedulercache.NodeInfo)

	podKey := func(pod *kube_api.Pod) string {
		return fmt.Sprintf("%s/%s", pod.Namespace, pod.Name)
	}

	tryNodeForPod := func(nodename string, pod *kube_api.Pod) bool {
		nodeInfo, found := newNodeInfos[nodename]
		if !found {
			nodeInfo, found = nodeInfos[nodename]
		}
		if found {
			nodeInfo.Node().Status.Allocatable = nodeInfo.Node().Status.Capacity
			err := predicateChecker.CheckPredicates(pod, nodeInfo)
			glog.V(4).Infof("Evaluation %s for %s/%s -> %v", nodename, pod.Namespace, pod.Name, err)
			if err == nil {
				// TODO(mwielgus): Optimize it.
				podsOnNode := nodeInfo.Pods()
				podsOnNode = append(podsOnNode, pod)
				newNodeInfo := schedulercache.NewNodeInfo(podsOnNode...)
				newNodeInfo.SetNode(nodeInfo.Node())
				newNodeInfos[nodename] = newNodeInfo
				newHints[podKey(pod)] = nodename
				return true
			}
		}
		return false
	}

	// TODO: come up with a better semi-random semi-utilization sorted
	// layout.
	shuffledNodes := shuffleNodes(nodes)

	for _, podptr := range pods {
		newpod := *podptr
		newpod.Spec.NodeName = ""
		pod := &newpod

		foundPlace := false
		targetNode := ""

		glog.V(4).Infof("Looking for place for %s/%s", pod.Namespace, pod.Name)

		hintedNode, hasHint := oldHints[podKey(pod)]
		if hasHint {
			if hintedNode != removedNode && tryNodeForPod(hintedNode, pod) {
				foundPlace = true
				targetNode = hintedNode
			}
		}
		if !foundPlace {
			for _, node := range shuffledNodes {
				if node.Name == removedNode {
					continue
				}
				if tryNodeForPod(node.Name, pod) {
					foundPlace = true
					targetNode = node.Name
					break
				}
			}
			if !foundPlace {
				return fmt.Errorf("failed to find place for %s", podKey)
			}
		}

		usageTracker.RegisterUsage(removedNode, targetNode, timestamp)
	}
	return nil
}

func shuffleNodes(nodes []*kube_api.Node) []*kube_api.Node {
	result := make([]*kube_api.Node, len(nodes))
	for i := range nodes {
		result[i] = nodes[i]
	}
	for i := range result {
		j := rand.Intn(len(result))
		result[i], result[j] = result[j], result[i]
	}
	return result
}
<commit_msg>Cluster-autoscaler: skip nodeinfos with nil nodes<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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

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

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

package simulator

import (
	"flag"
	"fmt"
	"math"
	"math/rand"
	"time"

	kube_api "k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/resource"
	kube_client "k8s.io/kubernetes/pkg/client/unversioned"
	cmd "k8s.io/kubernetes/pkg/kubectl/cmd"
	"k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache"

	"github.com/golang/glog"
)

var (
	skipNodesWithSystemPods = flag.Bool("skip-nodes-with-system-pods", true,
		"If true cluster autoscaler will never delete nodes with pods from kube-system (except for DeamonSet "+
			"or mirror pods)")
	skipNodesWithLocalStorage = flag.Bool("skip-nodes-with-local-storage", true,
		"If true cluster autoscaler will never delete nodes with pods with local storage, e.g. EmptyDir or HostPath")
)

// FindNodesToRemove finds nodes that can be removed. Returns also an information about good
// rescheduling location for each of the pods.
func FindNodesToRemove(candidates []*kube_api.Node, allNodes []*kube_api.Node, pods []*kube_api.Pod,
	client *kube_client.Client, predicateChecker *PredicateChecker, maxCount int,
	fastCheck bool, oldHints map[string]string, usageTracker *UsageTracker,
	timestamp time.Time) (nodesToRemove []*kube_api.Node, podReschedulingHints map[string]string, finalError error) {

	nodeNameToNodeInfo := schedulercache.CreateNodeNameToInfoMap(pods)
	for _, node := range allNodes {
		if nodeInfo, found := nodeNameToNodeInfo[node.Name]; found {
			nodeInfo.SetNode(node)
		}
	}
	result := make([]*kube_api.Node, 0)

	evaluationType := "Detailed evaluation"
	if fastCheck {
		evaluationType = "Fast evaluation"
	}
	newHints := make(map[string]string, len(oldHints))

candidateloop:
	for _, node := range candidates {
		glog.V(2).Infof("%s: %s for removal", evaluationType, node.Name)

		var podsToRemove []*kube_api.Pod
		var err error

		if fastCheck {
			if nodeInfo, found := nodeNameToNodeInfo[node.Name]; found {
				podsToRemove, err = FastGetPodsToMove(nodeInfo, false, *skipNodesWithSystemPods, *skipNodesWithLocalStorage)
				if err != nil {
					glog.V(2).Infof("%s: node %s cannot be removed: %v", evaluationType, node.Name, err)
					continue candidateloop
				}
			} else {
				glog.V(2).Infof("%s: nodeInfo for %s not found", evaluationType, node.Name)
				continue candidateloop
			}
		} else {
			drainResult, _, _, err := cmd.GetPodsForDeletionOnNodeDrain(client, node.Name,
				kube_api.Codecs.UniversalDecoder(), false, true)

			if err != nil {
				glog.V(2).Infof("%s: node %s cannot be removed: %v", evaluationType, node.Name, err)
				continue candidateloop
			}
			podsToRemove = make([]*kube_api.Pod, 0, len(drainResult))
			for i := range drainResult {
				podsToRemove = append(podsToRemove, &drainResult[i])
			}
		}
		findProblems := findPlaceFor(node.Name, podsToRemove, allNodes, nodeNameToNodeInfo, predicateChecker, oldHints, newHints,
			usageTracker, timestamp)

		if findProblems == nil {
			result = append(result, node)
			glog.V(2).Infof("%s: node %s may be removed", evaluationType, node.Name)
			if len(result) >= maxCount {
				break candidateloop
			}
		} else {
			glog.V(2).Infof("%s: node %s is not suitable for removal %v", evaluationType, node.Name, err)
		}
	}
	return result, newHints, nil
}

// CalculateUtilization calculates utilization of a node, defined as total amount of requested resources divided by capacity.
func CalculateUtilization(node *kube_api.Node, nodeInfo *schedulercache.NodeInfo) (float64, error) {
	cpu, err := calculateUtilizationOfResource(node, nodeInfo, kube_api.ResourceCPU)
	if err != nil {
		return 0, err
	}
	mem, err := calculateUtilizationOfResource(node, nodeInfo, kube_api.ResourceMemory)
	if err != nil {
		return 0, err
	}
	return math.Max(cpu, mem), nil
}

func calculateUtilizationOfResource(node *kube_api.Node, nodeInfo *schedulercache.NodeInfo, resourceName kube_api.ResourceName) (float64, error) {
	nodeCapacity, found := node.Status.Capacity[resourceName]
	if !found {
		return 0, fmt.Errorf("Failed to get %v from %s", resourceName, node.Name)
	}
	if nodeCapacity.MilliValue() == 0 {
		return 0, fmt.Errorf("%v is 0 at %s", resourceName, node.Name)
	}
	podsRequest := resource.MustParse("0")
	for _, pod := range nodeInfo.Pods() {
		for _, container := range pod.Spec.Containers {
			if resourceValue, found := container.Resources.Requests[resourceName]; found {
				podsRequest.Add(resourceValue)
			}
		}
	}
	return float64(podsRequest.MilliValue()) / float64(nodeCapacity.MilliValue()), nil
}

// TODO: We don't need to pass list of nodes here as they are already available in nodeInfos.
func findPlaceFor(removedNode string, pods []*kube_api.Pod, nodes []*kube_api.Node, nodeInfos map[string]*schedulercache.NodeInfo,
	predicateChecker *PredicateChecker, oldHints map[string]string, newHints map[string]string, usageTracker *UsageTracker,
	timestamp time.Time) error {

	newNodeInfos := make(map[string]*schedulercache.NodeInfo)

	podKey := func(pod *kube_api.Pod) string {
		return fmt.Sprintf("%s/%s", pod.Namespace, pod.Name)
	}

	tryNodeForPod := func(nodename string, pod *kube_api.Pod) bool {
		nodeInfo, found := newNodeInfos[nodename]
		if !found {
			nodeInfo, found = nodeInfos[nodename]
		}
		if found {
			if nodeInfo.Node() == nil {
				// NodeInfo is generated based on pods. It is possible that node is removed from
				// an api server faster than the pod that were running on them. In such a case
				// we have to skip this nodeInfo. It should go away pretty soon.
				glog.Warningf("No node in nodeInfo %s -> %v", nodename, nodeInfo)
				return false
			}
			nodeInfo.Node().Status.Allocatable = nodeInfo.Node().Status.Capacity
			err := predicateChecker.CheckPredicates(pod, nodeInfo)
			glog.V(4).Infof("Evaluation %s for %s/%s -> %v", nodename, pod.Namespace, pod.Name, err)
			if err == nil {
				// TODO(mwielgus): Optimize it.
				podsOnNode := nodeInfo.Pods()
				podsOnNode = append(podsOnNode, pod)
				newNodeInfo := schedulercache.NewNodeInfo(podsOnNode...)
				newNodeInfo.SetNode(nodeInfo.Node())
				newNodeInfos[nodename] = newNodeInfo
				newHints[podKey(pod)] = nodename
				return true
			}
		}
		return false
	}

	// TODO: come up with a better semi-random semi-utilization sorted
	// layout.
	shuffledNodes := shuffleNodes(nodes)

	for _, podptr := range pods {
		newpod := *podptr
		newpod.Spec.NodeName = ""
		pod := &newpod

		foundPlace := false
		targetNode := ""

		glog.V(4).Infof("Looking for place for %s/%s", pod.Namespace, pod.Name)

		hintedNode, hasHint := oldHints[podKey(pod)]
		if hasHint {
			if hintedNode != removedNode && tryNodeForPod(hintedNode, pod) {
				foundPlace = true
				targetNode = hintedNode
			}
		}
		if !foundPlace {
			for _, node := range shuffledNodes {
				if node.Name == removedNode {
					continue
				}
				if tryNodeForPod(node.Name, pod) {
					foundPlace = true
					targetNode = node.Name
					break
				}
			}
			if !foundPlace {
				return fmt.Errorf("failed to find place for %s", podKey)
			}
		}

		usageTracker.RegisterUsage(removedNode, targetNode, timestamp)
	}
	return nil
}

func shuffleNodes(nodes []*kube_api.Node) []*kube_api.Node {
	result := make([]*kube_api.Node, len(nodes))
	for i := range nodes {
		result[i] = nodes[i]
	}
	for i := range result {
		j := rand.Intn(len(result))
		result[i], result[j] = result[j], result[i]
	}
	return result
}
<|endoftext|>


<commit_before>package t411client

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"sort"
	"strconv"
)

var (
	errEOF             = errors.New("no more torrents to find")
	errTorrentNotFound = &errAPI{
		Code: 301,
		Text: "Torrent not found",
	}
)

// Torrent represents a torrent as return by the t411 API
type Torrent struct {
	ID             string `json:"id"`
	Name           string `json:"name"`
	Category       string `json:"category"`
	Rewritename    string `json:"rewritename"`
	Seeders        string `json:"seeders"`
	Leechers       string `json:"leechers"`
	Comments       string `json:"comments"`
	IsVerified     string `json:"isVerified"`
	Added          string `json:"added"`
	Size           string `json:"size"`
	TimesCompleted string `json:"times_completed"`
	Owner          string `json:"owner"`
	Categoryname   string `json:"categoryname"`
	Categoryimage  string `json:"categoryimage"`
	Username       string `json:"username"`
	Privacy        string `json:"privacy"`
}

// Torrents represents the torrents data.
type Torrents struct {
	Query    string    `json:"query"`
	Total    string    `json:"total"`
	Offset   string    `json:"offset"`
	Limit    string    `json:"limit"`
	Torrents []Torrent `json:"torrents"`
}

func (t *Torrent) String() string {
	return fmt.Sprintf("%s - %s (s:%s, l:%s)", t.ID, t.Name, t.Seeders, t.Leechers)
}

// used for sorting
type torrentsList []Torrent

// bySeeder implements sort.Interface by providing Less and using the Len and
// Swap methods of the embedded Torrents value.
type bySeeder struct {
	torrentsList
}

// Less implements the sort.Interface
func (s bySeeder) Less(i, j int) bool {
	seederI, _ := strconv.Atoi(s.torrentsList[i].Seeders)
	seederJ, _ := strconv.Atoi(s.torrentsList[j].Seeders)
	return seederI < seederJ
}

// Len implements the sort.Interface
func (t torrentsList) Len() int {
	return len(t)
}

// Swap implements the sort.Interface
func (t torrentsList) Swap(i, j int) { t[i], t[j] = t[j], t[i] }

// T411 search API is quite strange to use. see https://api.t411.li/
// they use 'terms' to allow search by category.
// In this case we are only interested in category Season and Episode number.
// Season and Episode number also have specific ID. init method creates the mapping
var (
	catSeasonID       = 45
	catEpisodeID      = 46
	catLanguageID     = 51
	catQualityID      = 7
	episodeNbrIDStart = 936
	seasonNbrIDStart  = 968
	seasonNbrID       = map[int]int{}
	episodeNbrID      = map[int]int{}
	// LanguageMap is a map giving you the keys corresponding
	// to every available language filter
	LanguageMap = map[string]int{
		"english":    1209,
		"french":     1210,
		"mute":       1211,
		"multi-fr":   1212,
		"multi-qb":   1213,
		"quebecker ": 1214,
		"vfstfr":     1215,
		"vostfr":     1216,
		"voasta":     1217,
	}
	// QualityMap is a map giving you the keys corresponding
	// to every available quality filter
	QualityMap = map[string]int{
		"BDrip/BRrip [Rip SD (non HD) depuis Bluray ou HDrip": 8,
		"Bluray 4K [Full ou Remux]":                           1171,
		"Bluray [Full]":                                       17,
		"Bluray [Remux]":                                      1220,
		"DVD-R 5 [DVD < 4.37GB]":                              13,
		"DVD-R 9 [DVD > 4.37GB]":                              14,
		"DVDrip [Rip depuis DVD-R]":                           10,
		"HDrip 1080 [Rip HD depuis Bluray]":                   16,
		"HDrip 4k [Rip HD 4k depuis source 4k]":               1219,
		"HDrip 720 [Rip HD depuis Bluray]":                    15,
		"TVrip [Rip SD (non HD) depuis Source Tv HD/SD]":      11,
		"TVripHD 1080 [Rip HD depuis Source Tv HD]":           1162,
		"TvripHD 4k [Rip HD 4k depuis Source Tv 4k]":          1235,
		"TVripHD 720 [Rip HD depuis Source Tv HD]":            12,
		"VCD/SVCD/VHSrip":                                     18,
		"Web-Dl":                                              1233,
		"Web-Dl 1080":                                         1174,
		"Web-Dl 4K":                                           1182,
		"Web-Dl 720":                                          1175,
		"WEBrip":                                              19,
	}
)

func init() {
	for i := 0; i < 30; i++ {
		seasonNbrID[i+1] = seasonNbrIDStart + i
	}
	seasonNbrID[0] = 998
	for i := 0; i < 61; i++ {
		episodeNbrID[i] = episodeNbrIDStart + i
	}
}

func addEpisode(v url.Values, episode int) {
	if episode >= 0 {
		v.Add(fmt.Sprintf("term[%d][]", catEpisodeID), fmt.Sprintf("%d", episodeNbrID[episode]))
	}
}

func addSeason(v url.Values, season int) {
	if season >= 0 {
		v.Add(fmt.Sprintf("term[%d][]", catSeasonID), fmt.Sprintf("%d", seasonNbrID[season]))
	}
}

func addLanguage(v url.Values, language string) {
	if ID, ok := LanguageMap[language]; ok {
		v.Add(fmt.Sprintf("term[%d][]", catLanguageID), fmt.Sprintf("%d", ID))
	}
}

func addQuality(v url.Values, quality string) {
	if ID, ok := QualityMap[quality]; ok {
		v.Add(fmt.Sprintf("term[%d][]", catQualityID), fmt.Sprintf("%d", ID))
	}
}

func addOffset(v url.Values, offset int) {
	if offset >= 0 {
		v.Add("offset", fmt.Sprintf("%d", offset))
	}
}

func addLimit(v url.Values, limit int) {
	if limit > 0 {
		v.Add("limit", fmt.Sprintf("%d", limit))
	}
}

// URL returns the url of the search request
func makeURL(title string, season, episode int, language, quality string, offset, limit int) (string, *url.URL, error) {
	usedAPI := "/torrents/search/"
	u, err := url.Parse(fmt.Sprintf("%s%s%s", t411BaseURL, usedAPI, title))
	if err != nil {
		return usedAPI, nil, err
	}
	v := u.Query()
	addSeason(v, season)
	addEpisode(v, episode)
	addLanguage(v, language)
	addQuality(v, quality)
	addOffset(v, offset)
	addLimit(v, limit)
	u.RawQuery = v.Encode()
	return usedAPI, u, nil
}

// SearchTorrentsByTerms searches a torrent using terms and return a list of torrents
// with a maximum of 10 torrents by default.
// The 'title' parameter is mandatory of course. All the others are optionals.
// For 'season' and 'episode', a value of 0 means respectively "complete/integral tv show" and "complete season",
// Note that for the complete tv show, both 'season' and 'episode' must be set to 0.
// 'season' available range is from 0 to 30 and 'episode' range is from 0 to 60.
// The 'language' parameter must be one the values of the LanguageMap variable.
// The 'quality' parameter must be one the values of the QualityMap variable.
func (t *T411) SearchTorrentsByTerms(title string, season, episode int, language, quality string, offset, limit int) (*Torrents, error) {
	usedAPI, u, err := makeURL(title, season, episode, language, quality, offset, limit)
	if err != nil {
		return nil, err
	}
	resp, err := t.doGet(u)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	torrents := &Torrents{}
	err = t.decode(torrents, resp, usedAPI, u.RawQuery)
	if err != nil {
		return nil, err
	}
	return torrents, nil
}

// SearchAllTorrentByTerms does the same as SearchTorrentByTerms but get all the possible torrents
// for the specific search in a single torrent slice.
func (t *T411) SearchAllTorrentByTerms(title string, season, episode int, language, quality string) (*Torrents, error) {
	torrents, err := t.SearchTorrentsByTerms(title, season, episode, language, quality, 0, 100)
	if err != nil {
		return nil, err
	}
	total, err := strconv.Atoi(torrents.Total)
	if err != nil {
		return nil, err
	}
	if len(torrents.Torrents) == total {
		return torrents, nil
	}

	torrents, err = t.SearchTorrentsByTerms(title, season, episode, language, quality, 0, total)
	if err != nil {
		return nil, err
	}
	return torrents, nil
}

// TorrentDetails represents the torrent detail data.
type TorrentDetails struct {
	ID            string            `json:"id"`
	Name          string            `json:"name"`
	Category      string            `json:"category"`
	Categoryname  string            `json:"categoryname"`
	Categoryimage string            `json:"categoryimage"`
	Rewritename   string            `json:"rewritename"`
	Owner         string            `json:"owner"`
	Username      string            `json:"username"`
	Privacy       string            `json:"privacy"`
	Description   string            `json:"description"`
	Terms         map[string]string `json:"terms"`
}

// TorrentsDetails returns the details of a torrent from a torrent 'id'.
func (t *T411) TorrentsDetails(id string) (*TorrentDetails, error) {
	usedAPI := "/torrents/details/"
	u, err := url.Parse(fmt.Sprintf("%s%s%s", t411BaseURL, usedAPI, id))
	if err != nil {
		return nil, err
	}
	resp, err := t.doGet(u)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	details := &TorrentDetails{}
	err = t.decode(details, resp, usedAPI, u.RawQuery)
	if err != nil {
		return nil, err
	}
	return details, nil
}

// SortBySeeders sorts the given torrents by seeders.
func (*T411) SortBySeeders(torrents []Torrent) {
	sort.Sort(bySeeder{torrents})
}

func (t *T411) download(ID string) (io.ReadCloser, error) {
	u, err := url.Parse(fmt.Sprintf("%s/torrents/download/%s", t.baseURL, ID))
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return nil, err
	}

	resp, err := t.do(req)
	if err != nil {
		return nil, err
	}
	return resp.Body, err
}

// DownloadTorrentByID downloads the torrent of id 'id' into a temporary
// filename begining with 'prefix' and returns the complete temporary filename
// on success.
func (t *T411) DownloadTorrentByID(id, prefix string) (string, error) {
	r, err := t.download(id)
	if err != nil {
		return "", err
	}
	defer r.Close()

	tmpfile, err := ioutil.TempFile("", prefix)
	if err != nil {
		return "", err
	}
	defer tmpfile.Close()

	if _, err = io.Copy(tmpfile, r); err != nil {
		return "", err
	}

	return tmpfile.Name(), nil
}

// DownloadTorrentByTerms searches the torrent corresponding to the title,
// season, episode and language, downloads the one with the most seeders
// and return the location of the file located in a temporary folder.
// Note: the search is done with an offset of 0 and a limit of 10 results per search by default.
// Note: the 'language' parameter must be one of the values of LanguageMap variable.
// Note: the 'quality' parameter must be one of the values of QualityMap variable.
func (t *T411) DownloadTorrentByTerms(title string, season, episode int, language, quality string) (string, error) {
	torrents, err := t.SearchTorrentsByTerms(title, season, episode, language, quality, 0, 0)
	if err != nil {
		return "", err
	}

	t.SortBySeeders(torrents.Torrents)
	return t.DownloadTorrentByID(torrents.Torrents[len(torrents.Torrents)-1].ID, fmt.Sprintf("%sS%02dE%02d", title, season, episode))
}
<commit_msg>fix episode terms keys from 0 to 60<commit_after>package t411client

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"sort"
	"strconv"
)

var (
	errEOF             = errors.New("no more torrents to find")
	errTorrentNotFound = &errAPI{
		Code: 301,
		Text: "Torrent not found",
	}
)

// Torrent represents a torrent as return by the t411 API
type Torrent struct {
	ID             string `json:"id"`
	Name           string `json:"name"`
	Category       string `json:"category"`
	Rewritename    string `json:"rewritename"`
	Seeders        string `json:"seeders"`
	Leechers       string `json:"leechers"`
	Comments       string `json:"comments"`
	IsVerified     string `json:"isVerified"`
	Added          string `json:"added"`
	Size           string `json:"size"`
	TimesCompleted string `json:"times_completed"`
	Owner          string `json:"owner"`
	Categoryname   string `json:"categoryname"`
	Categoryimage  string `json:"categoryimage"`
	Username       string `json:"username"`
	Privacy        string `json:"privacy"`
}

// Torrents represents the torrents data.
type Torrents struct {
	Query    string    `json:"query"`
	Total    string    `json:"total"`
	Offset   string    `json:"offset"`
	Limit    string    `json:"limit"`
	Torrents []Torrent `json:"torrents"`
}

func (t *Torrent) String() string {
	return fmt.Sprintf("%s - %s (s:%s, l:%s)", t.ID, t.Name, t.Seeders, t.Leechers)
}

// used for sorting
type torrentsList []Torrent

// bySeeder implements sort.Interface by providing Less and using the Len and
// Swap methods of the embedded Torrents value.
type bySeeder struct {
	torrentsList
}

// Less implements the sort.Interface
func (s bySeeder) Less(i, j int) bool {
	seederI, _ := strconv.Atoi(s.torrentsList[i].Seeders)
	seederJ, _ := strconv.Atoi(s.torrentsList[j].Seeders)
	return seederI < seederJ
}

// Len implements the sort.Interface
func (t torrentsList) Len() int {
	return len(t)
}

// Swap implements the sort.Interface
func (t torrentsList) Swap(i, j int) { t[i], t[j] = t[j], t[i] }

// T411 search API is quite strange to use. see https://api.t411.li/
// they use 'terms' to allow search by category.
// In this case we are only interested in category Season and Episode number.
// Season and Episode number also have specific ID. init method creates the mapping
var (
	catSeasonID        = 45
	catEpisodeID       = 46
	catLanguageID      = 51
	catQualityID       = 7
	episodeNbrIDStart  = 936
	episodeNbrIDMiddle = 1088
	seasonNbrIDStart   = 968
	seasonNbrID        = map[int]int{}
	episodeNbrID       = map[int]int{}
	// LanguageMap is a map giving you the keys corresponding
	// to every available language filter
	LanguageMap = map[string]int{
		"english":    1209,
		"french":     1210,
		"mute":       1211,
		"multi-fr":   1212,
		"multi-qb":   1213,
		"quebecker ": 1214,
		"vfstfr":     1215,
		"vostfr":     1216,
		"voasta":     1217,
	}
	// QualityMap is a map giving you the keys corresponding
	// to every available quality filter
	QualityMap = map[string]int{
		"BDrip/BRrip [Rip SD (non HD) depuis Bluray ou HDrip": 8,
		"Bluray 4K [Full ou Remux]":                           1171,
		"Bluray [Full]":                                       17,
		"Bluray [Remux]":                                      1220,
		"DVD-R 5 [DVD < 4.37GB]":                              13,
		"DVD-R 9 [DVD > 4.37GB]":                              14,
		"DVDrip [Rip depuis DVD-R]":                           10,
		"HDrip 1080 [Rip HD depuis Bluray]":                   16,
		"HDrip 4k [Rip HD 4k depuis source 4k]":               1219,
		"HDrip 720 [Rip HD depuis Bluray]":                    15,
		"TVrip [Rip SD (non HD) depuis Source Tv HD/SD]":      11,
		"TVripHD 1080 [Rip HD depuis Source Tv HD]":           1162,
		"TvripHD 4k [Rip HD 4k depuis Source Tv 4k]":          1235,
		"TVripHD 720 [Rip HD depuis Source Tv HD]":            12,
		"VCD/SVCD/VHSrip":                                     18,
		"Web-Dl":                                              1233,
		"Web-Dl 1080":                                         1174,
		"Web-Dl 4K":                                           1182,
		"Web-Dl 720":                                          1175,
		"WEBrip":                                              19,
	}
)

func init() {
	for i := 0; i < 30; i++ {
		seasonNbrID[i+1] = seasonNbrIDStart + i
	}
	seasonNbrID[0] = 998
	// see https://github.com/dns-gh/t411-client/issues/3 for more explanation
	for i := 0; i < 31; i++ {
		if i == 9 {
			episodeNbrIDStart++
		}
		episodeNbrID[i] = episodeNbrIDStart + i
		if i != 30 {
			episodeNbrID[31+i] = episodeNbrIDMiddle + i
		}
	}
}

func addEpisode(v url.Values, episode int) {
	if episode >= 0 {
		v.Add(fmt.Sprintf("term[%d][]", catEpisodeID), fmt.Sprintf("%d", episodeNbrID[episode]))
	}
}

func addSeason(v url.Values, season int) {
	if season >= 0 {
		v.Add(fmt.Sprintf("term[%d][]", catSeasonID), fmt.Sprintf("%d", seasonNbrID[season]))
	}
}

func addLanguage(v url.Values, language string) {
	if ID, ok := LanguageMap[language]; ok {
		v.Add(fmt.Sprintf("term[%d][]", catLanguageID), fmt.Sprintf("%d", ID))
	}
}

func addQuality(v url.Values, quality string) {
	if ID, ok := QualityMap[quality]; ok {
		v.Add(fmt.Sprintf("term[%d][]", catQualityID), fmt.Sprintf("%d", ID))
	}
}

func addOffset(v url.Values, offset int) {
	if offset >= 0 {
		v.Add("offset", fmt.Sprintf("%d", offset))
	}
}

func addLimit(v url.Values, limit int) {
	if limit > 0 {
		v.Add("limit", fmt.Sprintf("%d", limit))
	}
}

// URL returns the url of the search request
func makeURL(title string, season, episode int, language, quality string, offset, limit int) (string, *url.URL, error) {
	usedAPI := "/torrents/search/"
	u, err := url.Parse(fmt.Sprintf("%s%s%s", t411BaseURL, usedAPI, title))
	if err != nil {
		return usedAPI, nil, err
	}
	v := u.Query()
	addSeason(v, season)
	addEpisode(v, episode)
	addLanguage(v, language)
	addQuality(v, quality)
	addOffset(v, offset)
	addLimit(v, limit)
	u.RawQuery = v.Encode()
	return usedAPI, u, nil
}

// SearchTorrentsByTerms searches a torrent using terms and return a list of torrents
// with a maximum of 10 torrents by default.
// The 'title' parameter is mandatory of course. All the others are optionals.
// For 'season' and 'episode', a value of 0 means respectively "complete/integral tv show" and "complete season",
// Note that for the complete tv show, both 'season' and 'episode' must be set to 0.
// 'season' available range is from 0 to 30 and 'episode' range is from 0 to 60.
// The 'language' parameter must be one the values of the LanguageMap variable.
// The 'quality' parameter must be one the values of the QualityMap variable.
func (t *T411) SearchTorrentsByTerms(title string, season, episode int, language, quality string, offset, limit int) (*Torrents, error) {
	usedAPI, u, err := makeURL(title, season, episode, language, quality, offset, limit)
	if err != nil {
		return nil, err
	}
	resp, err := t.doGet(u)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	torrents := &Torrents{}
	err = t.decode(torrents, resp, usedAPI, u.RawQuery)
	if err != nil {
		return nil, err
	}
	return torrents, nil
}

// SearchAllTorrentByTerms does the same as SearchTorrentByTerms but get all the possible torrents
// for the specific search in a single torrent slice.
func (t *T411) SearchAllTorrentByTerms(title string, season, episode int, language, quality string) (*Torrents, error) {
	torrents, err := t.SearchTorrentsByTerms(title, season, episode, language, quality, 0, 100)
	if err != nil {
		return nil, err
	}
	total, err := strconv.Atoi(torrents.Total)
	if err != nil {
		return nil, err
	}
	if len(torrents.Torrents) == total {
		return torrents, nil
	}

	torrents, err = t.SearchTorrentsByTerms(title, season, episode, language, quality, 0, total)
	if err != nil {
		return nil, err
	}
	return torrents, nil
}

// TorrentDetails represents the torrent detail data.
type TorrentDetails struct {
	ID            string            `json:"id"`
	Name          string            `json:"name"`
	Category      string            `json:"category"`
	Categoryname  string            `json:"categoryname"`
	Categoryimage string            `json:"categoryimage"`
	Rewritename   string            `json:"rewritename"`
	Owner         string            `json:"owner"`
	Username      string            `json:"username"`
	Privacy       string            `json:"privacy"`
	Description   string            `json:"description"`
	Terms         map[string]string `json:"terms"`
}

// TorrentsDetails returns the details of a torrent from a torrent 'id'.
func (t *T411) TorrentsDetails(id string) (*TorrentDetails, error) {
	usedAPI := "/torrents/details/"
	u, err := url.Parse(fmt.Sprintf("%s%s%s", t411BaseURL, usedAPI, id))
	if err != nil {
		return nil, err
	}
	resp, err := t.doGet(u)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	details := &TorrentDetails{}
	err = t.decode(details, resp, usedAPI, u.RawQuery)
	if err != nil {
		return nil, err
	}
	return details, nil
}

// SortBySeeders sorts the given torrents by seeders.
func (*T411) SortBySeeders(torrents []Torrent) {
	sort.Sort(bySeeder{torrents})
}

func (t *T411) download(ID string) (io.ReadCloser, error) {
	u, err := url.Parse(fmt.Sprintf("%s/torrents/download/%s", t.baseURL, ID))
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return nil, err
	}

	resp, err := t.do(req)
	if err != nil {
		return nil, err
	}
	return resp.Body, err
}

// DownloadTorrentByID downloads the torrent of id 'id' into a temporary
// filename begining with 'prefix' and returns the complete temporary filename
// on success.
func (t *T411) DownloadTorrentByID(id, prefix string) (string, error) {
	r, err := t.download(id)
	if err != nil {
		return "", err
	}
	defer r.Close()

	tmpfile, err := ioutil.TempFile("", prefix)
	if err != nil {
		return "", err
	}
	defer tmpfile.Close()

	if _, err = io.Copy(tmpfile, r); err != nil {
		return "", err
	}

	return tmpfile.Name(), nil
}

// DownloadTorrentByTerms searches the torrent corresponding to the title,
// season, episode and language, downloads the one with the most seeders
// and return the location of the file located in a temporary folder.
// Note: the search is done with an offset of 0 and a limit of 10 results per search by default.
// Note: the 'language' parameter must be one of the values of LanguageMap variable.
// Note: the 'quality' parameter must be one of the values of QualityMap variable.
func (t *T411) DownloadTorrentByTerms(title string, season, episode int, language, quality string) (string, error) {
	torrents, err := t.SearchTorrentsByTerms(title, season, episode, language, quality, 0, 0)
	if err != nil {
		return "", err
	}

	t.SortBySeeders(torrents.Torrents)
	return t.DownloadTorrentByID(torrents.Torrents[len(torrents.Torrents)-1].ID, fmt.Sprintf("%sS%02dE%02d", title, season, episode))
}
<|endoftext|>


<commit_before>// +build js

package runtime

import (
	"runtime/internal/sys"

	"github.com/gopherjs/gopherjs/js"
)

const GOOS = sys.GOOS
const GOARCH = "js"
const Compiler = "gopherjs"

// fake for error.go
type eface struct {
	_type *_type
}
type _type struct {
}

func (t *_type) string() string {
	return ""
}

func init() {
	jsPkg := js.Global.Get("$packages").Get("github.com/gopherjs/gopherjs/js")
	js.Global.Set("$jsObjectPtr", jsPkg.Get("Object").Get("ptr"))
	js.Global.Set("$jsErrorPtr", jsPkg.Get("Error").Get("ptr"))
	js.Global.Set("$throwRuntimeError", js.InternalObject(throw))
	// avoid dead code elimination
	var e error
	e = &TypeAssertionError{}
	_ = e
}

func GOROOT() string {
	process := js.Global.Get("process")
	if process == js.Undefined {
		return "/"
	}
	goroot := process.Get("env").Get("GOROOT")
	if goroot != js.Undefined {
		return goroot.String()
	}
	return sys.DefaultGoroot
}

func Breakpoint() {
	js.Debugger()
}

func Caller(skip int) (pc uintptr, file string, line int, ok bool) {
	info := js.Global.Get("Error").New().Get("stack").Call("split", "\n").Index(skip + 2)
	if info == js.Undefined {
		return 0, "", 0, false
	}
	parts := info.Call("substring", info.Call("indexOf", "(").Int()+1, info.Call("indexOf", ")").Int()).Call("split", ":")
	return 0, parts.Index(0).String(), parts.Index(1).Int(), true
}

func Callers(skip int, pc []uintptr) int {
	return 0
}

func GC() {
}

func Goexit() {
	js.Global.Get("$curGoroutine").Set("exit", true)
	js.Global.Call("$throw", nil)
}

func GOMAXPROCS(n int) int {
	return 1
}

func Gosched() {
	c := make(chan struct{})
	js.Global.Call("$setTimeout", js.InternalObject(func() { close(c) }), 0)
	<-c
}

func NumCPU() int {
	return 1
}

func NumGoroutine() int {
	return js.Global.Get("$totalGoroutines").Int()
}

type MemStats struct {
	// General statistics.
	Alloc      uint64 // bytes allocated and still in use
	TotalAlloc uint64 // bytes allocated (even if freed)
	Sys        uint64 // bytes obtained from system (sum of XxxSys below)
	Lookups    uint64 // number of pointer lookups
	Mallocs    uint64 // number of mallocs
	Frees      uint64 // number of frees

	// Main allocation heap statistics.
	HeapAlloc    uint64 // bytes allocated and still in use
	HeapSys      uint64 // bytes obtained from system
	HeapIdle     uint64 // bytes in idle spans
	HeapInuse    uint64 // bytes in non-idle span
	HeapReleased uint64 // bytes released to the OS
	HeapObjects  uint64 // total number of allocated objects

	// Low-level fixed-size structure allocator statistics.
	//	Inuse is bytes used now.
	//	Sys is bytes obtained from system.
	StackInuse  uint64 // bytes used by stack allocator
	StackSys    uint64
	MSpanInuse  uint64 // mspan structures
	MSpanSys    uint64
	MCacheInuse uint64 // mcache structures
	MCacheSys   uint64
	BuckHashSys uint64 // profiling bucket hash table
	GCSys       uint64 // GC metadata
	OtherSys    uint64 // other system allocations

	// Garbage collector statistics.
	NextGC        uint64 // next collection will happen when HeapAlloc ≥ this amount
	LastGC        uint64 // end time of last collection (nanoseconds since 1970)
	PauseTotalNs  uint64
	PauseNs       [256]uint64 // circular buffer of recent GC pause durations, most recent at [(NumGC+255)%256]
	PauseEnd      [256]uint64 // circular buffer of recent GC pause end times
	NumGC         uint32
	GCCPUFraction float64 // fraction of CPU time used by GC
	EnableGC      bool
	DebugGC       bool

	// Per-size allocation statistics.
	// 61 is NumSizeClasses in the C code.
	BySize [61]struct {
		Size    uint32
		Mallocs uint64
		Frees   uint64
	}
}

func ReadMemStats(m *MemStats) {
}

func SetFinalizer(x, f interface{}) {
}

type Func struct {
	opaque struct{} // unexported field to disallow conversions
}

func (_ *Func) Entry() uintptr                              { return 0 }
func (_ *Func) FileLine(pc uintptr) (file string, line int) { return "", 0 }
func (_ *Func) Name() string                                { return "" }

func FuncForPC(pc uintptr) *Func {
	return nil
}

var MemProfileRate int = 512 * 1024

func SetBlockProfileRate(rate int) {
}

func SetMutexProfileFraction(rate int) int {
	// TODO: Investigate this. If it's possible to implement, consider doing so, otherwise remove this comment.
	return 0
}

func Stack(buf []byte, all bool) int {
	s := js.Global.Get("Error").New().Get("stack")
	if s == js.Undefined {
		return 0
	}
	return copy(buf, s.Call("substr", s.Call("indexOf", "\n").Int()+1).String())
}

func LockOSThread() {}

func UnlockOSThread() {}

func Version() string {
	return sys.TheVersion
}

func StartTrace() error { return nil }
func StopTrace()        {}
func ReadTrace() []byte

// We fake a cgo environment to catch errors. Therefor we have to implement this and always return 0
func NumCgoCall() int64 {
	return 0
}

func efaceOf(ep *interface{}) *eface {
	panic("efaceOf: not supported")
}

func KeepAlive(interface{}) {}

func throw(s string) {
	panic(errorString(s))
}
<commit_msg>compiler/natives/src/runtime: Add noop implementation of CallersFrames.<commit_after>// +build js

package runtime

import (
	"runtime/internal/sys"

	"github.com/gopherjs/gopherjs/js"
)

const GOOS = sys.GOOS
const GOARCH = "js"
const Compiler = "gopherjs"

// fake for error.go
type eface struct {
	_type *_type
}
type _type struct {
}

func (t *_type) string() string {
	return ""
}

func init() {
	jsPkg := js.Global.Get("$packages").Get("github.com/gopherjs/gopherjs/js")
	js.Global.Set("$jsObjectPtr", jsPkg.Get("Object").Get("ptr"))
	js.Global.Set("$jsErrorPtr", jsPkg.Get("Error").Get("ptr"))
	js.Global.Set("$throwRuntimeError", js.InternalObject(throw))
	// avoid dead code elimination
	var e error
	e = &TypeAssertionError{}
	_ = e
}

func GOROOT() string {
	process := js.Global.Get("process")
	if process == js.Undefined {
		return "/"
	}
	goroot := process.Get("env").Get("GOROOT")
	if goroot != js.Undefined {
		return goroot.String()
	}
	return sys.DefaultGoroot
}

func Breakpoint() {
	js.Debugger()
}

func Caller(skip int) (pc uintptr, file string, line int, ok bool) {
	info := js.Global.Get("Error").New().Get("stack").Call("split", "\n").Index(skip + 2)
	if info == js.Undefined {
		return 0, "", 0, false
	}
	parts := info.Call("substring", info.Call("indexOf", "(").Int()+1, info.Call("indexOf", ")").Int()).Call("split", ":")
	return 0, parts.Index(0).String(), parts.Index(1).Int(), true
}

func Callers(skip int, pc []uintptr) int {
	return 0
}

// CallersFrames is not implemented for GOARCH=js.
// TODO: Implement if possible.
func CallersFrames(callers []uintptr) *Frames { return &Frames{} }

type Frames struct{}

func (ci *Frames) Next() (frame Frame, more bool) { return }

type Frame struct {
	PC       uintptr
	Func     *Func
	Function string
	File     string
	Line     int
	Entry    uintptr
}

func GC() {
}

func Goexit() {
	js.Global.Get("$curGoroutine").Set("exit", true)
	js.Global.Call("$throw", nil)
}

func GOMAXPROCS(n int) int {
	return 1
}

func Gosched() {
	c := make(chan struct{})
	js.Global.Call("$setTimeout", js.InternalObject(func() { close(c) }), 0)
	<-c
}

func NumCPU() int {
	return 1
}

func NumGoroutine() int {
	return js.Global.Get("$totalGoroutines").Int()
}

type MemStats struct {
	// General statistics.
	Alloc      uint64 // bytes allocated and still in use
	TotalAlloc uint64 // bytes allocated (even if freed)
	Sys        uint64 // bytes obtained from system (sum of XxxSys below)
	Lookups    uint64 // number of pointer lookups
	Mallocs    uint64 // number of mallocs
	Frees      uint64 // number of frees

	// Main allocation heap statistics.
	HeapAlloc    uint64 // bytes allocated and still in use
	HeapSys      uint64 // bytes obtained from system
	HeapIdle     uint64 // bytes in idle spans
	HeapInuse    uint64 // bytes in non-idle span
	HeapReleased uint64 // bytes released to the OS
	HeapObjects  uint64 // total number of allocated objects

	// Low-level fixed-size structure allocator statistics.
	//	Inuse is bytes used now.
	//	Sys is bytes obtained from system.
	StackInuse  uint64 // bytes used by stack allocator
	StackSys    uint64
	MSpanInuse  uint64 // mspan structures
	MSpanSys    uint64
	MCacheInuse uint64 // mcache structures
	MCacheSys   uint64
	BuckHashSys uint64 // profiling bucket hash table
	GCSys       uint64 // GC metadata
	OtherSys    uint64 // other system allocations

	// Garbage collector statistics.
	NextGC        uint64 // next collection will happen when HeapAlloc ≥ this amount
	LastGC        uint64 // end time of last collection (nanoseconds since 1970)
	PauseTotalNs  uint64
	PauseNs       [256]uint64 // circular buffer of recent GC pause durations, most recent at [(NumGC+255)%256]
	PauseEnd      [256]uint64 // circular buffer of recent GC pause end times
	NumGC         uint32
	GCCPUFraction float64 // fraction of CPU time used by GC
	EnableGC      bool
	DebugGC       bool

	// Per-size allocation statistics.
	// 61 is NumSizeClasses in the C code.
	BySize [61]struct {
		Size    uint32
		Mallocs uint64
		Frees   uint64
	}
}

func ReadMemStats(m *MemStats) {
}

func SetFinalizer(x, f interface{}) {
}

type Func struct {
	opaque struct{} // unexported field to disallow conversions
}

func (_ *Func) Entry() uintptr                              { return 0 }
func (_ *Func) FileLine(pc uintptr) (file string, line int) { return "", 0 }
func (_ *Func) Name() string                                { return "" }

func FuncForPC(pc uintptr) *Func {
	return nil
}

var MemProfileRate int = 512 * 1024

func SetBlockProfileRate(rate int) {
}

func SetMutexProfileFraction(rate int) int {
	// TODO: Investigate this. If it's possible to implement, consider doing so, otherwise remove this comment.
	return 0
}

func Stack(buf []byte, all bool) int {
	s := js.Global.Get("Error").New().Get("stack")
	if s == js.Undefined {
		return 0
	}
	return copy(buf, s.Call("substr", s.Call("indexOf", "\n").Int()+1).String())
}

func LockOSThread() {}

func UnlockOSThread() {}

func Version() string {
	return sys.TheVersion
}

func StartTrace() error { return nil }
func StopTrace()        {}
func ReadTrace() []byte

// We fake a cgo environment to catch errors. Therefor we have to implement this and always return 0
func NumCgoCall() int64 {
	return 0
}

func efaceOf(ep *interface{}) *eface {
	panic("efaceOf: not supported")
}

func KeepAlive(interface{}) {}

func throw(s string) {
	panic(errorString(s))
}
<|endoftext|>


<commit_before>package indexer

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

	"github.com/attwad/cdf/data"
)

type Indexer interface {
	Index(data.Course, []string) error
}

type elasticIndexer struct {
	client *http.Client
	host   string
}

func NewElasticIndexer(host string) Indexer {
	return &elasticIndexer{
		client: &http.Client{
			Timeout: time.Second * 10,
		},
		host: host,
	}
}

type entry struct {
	Index indexEntry `json:"index"`
}

type indexEntry struct {
	Index string `json:"_index"`
	Type  string `json:"_type"`
}

type transcript struct {
	data.Course
	Transcript string `json:"transcript"`
}

func (i *elasticIndexer) Index(c data.Course, sentences []string) error {
	js := make([]string, 0)
	e := entry{Index: indexEntry{Index: "course", Type: "transcript"}}
	eb, err := json.Marshal(e)
	if err != nil {
		return err
	}
	seb := string(eb)
	for _, sentence := range sentences {
		jt := transcript{Course: c, Transcript: sentence}
		b, err := json.Marshal(jt)
		if err != nil {
			return err
		}
		js = append(js, seb, string(b))
	}
	// log.Println(strings.Join(js, "\n"))
	r := strings.NewReader(strings.Join(js, "\n"))
	_, err = i.client.Post(i.host+"/_bulk", "application/json", r)
	return err
}
<commit_msg>fix lints in indexer.go<commit_after>package indexer

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

	"github.com/attwad/cdf/data"
)

// Indexer handles indexing of a course's transcript.
type Indexer interface {
	Index(data.Course, []string) error
}

type elasticIndexer struct {
	client *http.Client
	host   string
}

// NewElasticIndexer creates a new Indexer connected to elastic search.
func NewElasticIndexer(host string) Indexer {
	return &elasticIndexer{
		client: &http.Client{
			Timeout: time.Second * 10,
		},
		host: host,
	}
}

type entry struct {
	Index indexEntry `json:"index"`
}

type indexEntry struct {
	Index string `json:"_index"`
	Type  string `json:"_type"`
}

type transcript struct {
	data.Course
	Transcript string `json:"transcript"`
}

func (i *elasticIndexer) Index(c data.Course, sentences []string) error {
	js := make([]string, 0)
	e := entry{Index: indexEntry{Index: "course", Type: "transcript"}}
	eb, err := json.Marshal(e)
	if err != nil {
		return err
	}
	seb := string(eb)
	for _, sentence := range sentences {
		jt := transcript{Course: c, Transcript: sentence}
		b, err2 := json.Marshal(jt)
		if err2 != nil {
			return err
		}
		js = append(js, seb, string(b))
	}
	// log.Println(strings.Join(js, "\n"))
	r := strings.NewReader(strings.Join(js, "\n"))
	_, err = i.client.Post(i.host+"/_bulk", "application/json", r)
	return err
}
<|endoftext|>


<commit_before>// Copyright 2019 Ka-Hing Cheung
//
// 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 internal

import (
	"errors"
	"io/ioutil"
	"path/filepath"
	"strconv"
	"strings"
)

const CGROUP_PATH = "/proc/self/cgroup"
const CGROUP_FOLDER_PREFIX = "/sys/fs/cgroup/memory"
const MEM_LIMIT_FILE_SUFFIX = "/memory.limit_in_bytes"
const MEM_USAGE_FILE_SUFFIX = "/memory.usage_in_bytes"

func getCgroupAvailableMem() (retVal uint64, err error) {
	//get the memory cgroup for self and send limit - usage for the cgroup

	data, err := ioutil.ReadFile(CGROUP_PATH)
	if err != nil {
		log.Debugf("Unable to read file %s error: %s", CGROUP_PATH, err)
		return 0, err
	}

	path, err := getMemoryCgroupPath(string(data))
	if err != nil {
		log.Debugf("Unable to get memory cgroup path")
		return 0, err
	}
	log.Debugf("the memory cgroup path for the current process is %v", path)

	mem_limit, err := readFileAndGetValue(filepath.Join(CGROUP_FOLDER_PREFIX, path, MEM_LIMIT_FILE_SUFFIX))
	if err != nil {
		log.Debugf("Unable to get memory limit from cgroup error: %v", err)
		return 0, err
	}

	mem_usage, err := readFileAndGetValue(filepath.Join(CGROUP_FOLDER_PREFIX, path, MEM_USAGE_FILE_SUFFIX))
	if err != nil {
		log.Debugf("Unable to get memory usage from cgroup error: %v", err)
		return 0, err
	}

	return (mem_limit - mem_usage), nil
}

func getMemoryCgroupPath(data string) (string, error) {

	/*
	   Content of /proc/self/cgroup

	   11:hugetlb:/
	   10:memory:/user.slice
	   9:cpuset:/
	   8:blkio:/user.slice
	   7:perf_event:/
	   6:net_prio,net_cls:/
	   5:cpuacct,cpu:/user.slice
	   4:devices:/user.slice
	   3:freezer:/
	   2:pids:/
	   1:name=systemd:/user.slice/user-1000.slice/session-1759.scope
	*/

	dataArray := strings.Split(data, "\n")
	for index := range dataArray {
		kvArray := strings.Split(dataArray[index], ":")
		if len(kvArray) == 3 {
			if kvArray[1] == "memory" {
				return kvArray[2], nil
			}
		}
	}

	return "", errors.New("Unable to get memory cgroup path")
}

func readFileAndGetValue(path string) (uint64, error) {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		log.Debugf("Unable to read file %v error: %v", path, err)
		return 0, err
	}

	return strconv.ParseUint(strings.TrimSpace(string(data)), 10, 64)
}
<commit_msg>Fix docker memory limit<commit_after>// Copyright 2019 Ka-Hing Cheung
//
// 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 internal

import (
	"errors"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"strings"
)

const CGROUP_PATH = "/proc/self/cgroup"
const CGROUP_FOLDER_PREFIX = "/sys/fs/cgroup/memory"
const MEM_LIMIT_FILE_SUFFIX = "/memory.limit_in_bytes"
const MEM_USAGE_FILE_SUFFIX = "/memory.usage_in_bytes"

func getCgroupAvailableMem() (retVal uint64, err error) {
	//get the memory cgroup for self and send limit - usage for the cgroup

	data, err := ioutil.ReadFile(CGROUP_PATH)
	if err != nil {
		log.Debugf("Unable to read file %s error: %s", CGROUP_PATH, err)
		return 0, err
	}

	path, err := getMemoryCgroupPath(string(data))
	if err != nil {
		log.Debugf("Unable to get memory cgroup path")
		return 0, err
	}

	// newer version of docker mounts the cgroup memory limit/usage files directly under
	// /sys/fs/cgroup/memory/ rather than /sys/fs/cgroup/memory/docker/$container_id/
	if _, err := os.Stat(filepath.Join(CGROUP_FOLDER_PREFIX, path)); os.IsExist(err) {
		path = filepath.Join(CGROUP_FOLDER_PREFIX, path)
	} else {
		path = filepath.Join(CGROUP_FOLDER_PREFIX)
	}

	log.Debugf("the memory cgroup path for the current process is %v", path)

	mem_limit, err := readFileAndGetValue(filepath.Join(path, MEM_LIMIT_FILE_SUFFIX))
	if err != nil {
		log.Debugf("Unable to get memory limit from cgroup error: %v", err)
		return 0, err
	}

	mem_usage, err := readFileAndGetValue(filepath.Join(path, MEM_USAGE_FILE_SUFFIX))
	if err != nil {
		log.Debugf("Unable to get memory usage from cgroup error: %v", err)
		return 0, err
	}

	return (mem_limit - mem_usage), nil
}

func getMemoryCgroupPath(data string) (string, error) {

	/*
	   Content of /proc/self/cgroup

	   11:hugetlb:/
	   10:memory:/user.slice
	   9:cpuset:/
	   8:blkio:/user.slice
	   7:perf_event:/
	   6:net_prio,net_cls:/
	   5:cpuacct,cpu:/user.slice
	   4:devices:/user.slice
	   3:freezer:/
	   2:pids:/
	   1:name=systemd:/user.slice/user-1000.slice/session-1759.scope
	*/

	dataArray := strings.Split(data, "\n")
	for index := range dataArray {
		kvArray := strings.Split(dataArray[index], ":")
		if len(kvArray) == 3 {
			if kvArray[1] == "memory" {
				return kvArray[2], nil
			}
		}
	}

	return "", errors.New("Unable to get memory cgroup path")
}

func readFileAndGetValue(path string) (uint64, error) {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		log.Debugf("Unable to read file %v error: %v", path, err)
		return 0, err
	}

	return strconv.ParseUint(strings.TrimSpace(string(data)), 10, 64)
}
<|endoftext|>


<commit_before>package ipfs

import (
	"bytes"
	"context"
	"encoding/base64"
	"errors"
	"golang.org/x/net/proxy"
	"io/ioutil"
	"net/http"
	"time"

	"gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid"
	peer "gx/ipfs/QmYVXrKrKHDC9FobgmcmshCDyWwdrfwfanNQN4oxJ9Fk3h/go-libp2p-peer"
	routing "gx/ipfs/QmYxUdYY9S6yg5tSPVin5GFTvtfsLauVcr7reHDD3dM8xf/go-libp2p-routing"
	ropts "gx/ipfs/QmYxUdYY9S6yg5tSPVin5GFTvtfsLauVcr7reHDD3dM8xf/go-libp2p-routing/options"
	pstore "gx/ipfs/QmaCTz9RkrU13bm9kMB54f7atgqM4qkjDZpRwRoJiWXEqs/go-libp2p-peerstore"
)

var apiRouterHTTPClient = &http.Client{
	Timeout: time.Second * 30,
}

// ensure APIRouter satisfies the interface
var _ routing.IpfsRouting = &APIRouter{}

// ErrNotStarted is returned if a method is called before the router
// is started using the Start() method.
var ErrNotStarted = errors.New("API router not started")

// APIRouter is a routing.IpfsRouting compliant struct backed by an API. It only
// provides the features offerened by routing.ValueStore and marks the others as
// unsupported.
type APIRouter struct {
	uri     string
	started bool
}

// NewAPIRouter creates a new APIRouter backed by the given URI.
func NewAPIRouter(uri string) APIRouter {
	return APIRouter{uri: uri}
}

func (r APIRouter) Start(proxyDialer proxy.Dialer) {
	if proxyDialer != nil {
		tbTransport := &http.Transport{Dial: proxyDialer.Dial}
		apiRouterHTTPClient.Transport = tbTransport
	}
	r.started = true
}

// Bootstrap is a no-op. We don't need any setup to query the API.
func (r APIRouter) Bootstrap(_ context.Context) error {
	return nil
}

// PutValue writes the given value to the API for the given key
func (r APIRouter) PutValue(ctx context.Context, key string, value []byte, opts ...ropts.Option) error {
	if !r.started {
		return ErrNotStarted
	}
	req, err := http.NewRequest("PUT", r.pathForKey(key), bytes.NewBuffer(value))
	if err != nil {
		return err
	}

	_, err = apiRouterHTTPClient.Do(req)
	return err
}

// GetValue reads the value for the given key
func (r APIRouter) GetValue(ctx context.Context, key string, opts ...ropts.Option) ([]byte, error) {
	if !r.started {
		return nil, ErrNotStarted
	}
	resp, err := apiRouterHTTPClient.Get(r.pathForKey(key))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	return ioutil.ReadAll(resp.Body)
}

// GetValues reads the value for the given key. The API does not return multiple
// values.
func (r APIRouter) GetValues(ctx context.Context, key string, opts ...ropts.Option) ([]byte, error) {
	if !r.started {
		return nil, ErrNotStarted
	}
	return r.GetValue(ctx, key, opts...)
}

// SearchValue returns the value for the given key. It return either an error or
// a closed channel containing one value.
func (r APIRouter) SearchValue(ctx context.Context, key string, opts ...ropts.Option) (<-chan []byte, error) {
	if !r.started {
		return nil, ErrNotStarted
	}
	value, err := r.GetValue(ctx, key, opts...)
	if err != nil {
		return nil, err
	}

	valueCh := make(chan []byte, 1)
	valueCh <- value
	close(valueCh)
	return valueCh, nil
}

// FindPeer is unsupported
func (r APIRouter) FindPeer(_ context.Context, id peer.ID) (pstore.PeerInfo, error) {
	return pstore.PeerInfo{}, routing.ErrNotSupported
}

// FindProvidersAsync is unsupported
func (r APIRouter) FindProvidersAsync(_ context.Context, _ cid.Cid, _ int) <-chan pstore.PeerInfo {
	return nil
}

// Provide is unsupported
func (r APIRouter) Provide(_ context.Context, _ cid.Cid, _ bool) error {
	return routing.ErrNotSupported
}

func (r APIRouter) pathForKey(key string) string {
	return r.uri + "/" + base64.URLEncoding.EncodeToString([]byte(key))
}
<commit_msg>BUGFIX: Fix APIRouter KV store path.<commit_after>package ipfs

import (
	"bytes"
	"context"
	"encoding/base64"
	"errors"
	"golang.org/x/net/proxy"
	"io/ioutil"
	"net/http"
	"time"

	"gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid"
	peer "gx/ipfs/QmYVXrKrKHDC9FobgmcmshCDyWwdrfwfanNQN4oxJ9Fk3h/go-libp2p-peer"
	routing "gx/ipfs/QmYxUdYY9S6yg5tSPVin5GFTvtfsLauVcr7reHDD3dM8xf/go-libp2p-routing"
	ropts "gx/ipfs/QmYxUdYY9S6yg5tSPVin5GFTvtfsLauVcr7reHDD3dM8xf/go-libp2p-routing/options"
	pstore "gx/ipfs/QmaCTz9RkrU13bm9kMB54f7atgqM4qkjDZpRwRoJiWXEqs/go-libp2p-peerstore"
)

var apiRouterHTTPClient = &http.Client{
	Timeout: time.Second * 30,
}

// ensure APIRouter satisfies the interface
var _ routing.IpfsRouting = &APIRouter{}

// ErrNotStarted is returned if a method is called before the router
// is started using the Start() method.
var ErrNotStarted = errors.New("API router not started")

// APIRouter is a routing.IpfsRouting compliant struct backed by an API. It only
// provides the features offerened by routing.ValueStore and marks the others as
// unsupported.
type APIRouter struct {
	uri     string
	started bool
}

// NewAPIRouter creates a new APIRouter backed by the given URI.
func NewAPIRouter(uri string) APIRouter {
	return APIRouter{uri: uri}
}

func (r APIRouter) Start(proxyDialer proxy.Dialer) {
	if proxyDialer != nil {
		tbTransport := &http.Transport{Dial: proxyDialer.Dial}
		apiRouterHTTPClient.Transport = tbTransport
	}
	r.started = true
}

// Bootstrap is a no-op. We don't need any setup to query the API.
func (r APIRouter) Bootstrap(_ context.Context) error {
	return nil
}

// PutValue writes the given value to the API for the given key
func (r APIRouter) PutValue(ctx context.Context, key string, value []byte, opts ...ropts.Option) error {
	if !r.started {
		return ErrNotStarted
	}
	req, err := http.NewRequest("PUT", r.pathForKey(key), bytes.NewBuffer(value))
	if err != nil {
		return err
	}

	_, err = apiRouterHTTPClient.Do(req)
	return err
}

// GetValue reads the value for the given key
func (r APIRouter) GetValue(ctx context.Context, key string, opts ...ropts.Option) ([]byte, error) {
	if !r.started {
		return nil, ErrNotStarted
	}
	resp, err := apiRouterHTTPClient.Get(r.pathForKey(key))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	return ioutil.ReadAll(resp.Body)
}

// GetValues reads the value for the given key. The API does not return multiple
// values.
func (r APIRouter) GetValues(ctx context.Context, key string, opts ...ropts.Option) ([]byte, error) {
	if !r.started {
		return nil, ErrNotStarted
	}
	return r.GetValue(ctx, key, opts...)
}

// SearchValue returns the value for the given key. It return either an error or
// a closed channel containing one value.
func (r APIRouter) SearchValue(ctx context.Context, key string, opts ...ropts.Option) (<-chan []byte, error) {
	if !r.started {
		return nil, ErrNotStarted
	}
	value, err := r.GetValue(ctx, key, opts...)
	if err != nil {
		return nil, err
	}

	valueCh := make(chan []byte, 1)
	valueCh <- value
	close(valueCh)
	return valueCh, nil
}

// FindPeer is unsupported
func (r APIRouter) FindPeer(_ context.Context, id peer.ID) (pstore.PeerInfo, error) {
	return pstore.PeerInfo{}, routing.ErrNotSupported
}

// FindProvidersAsync is unsupported
func (r APIRouter) FindProvidersAsync(_ context.Context, _ cid.Cid, _ int) <-chan pstore.PeerInfo {
	return nil
}

// Provide is unsupported
func (r APIRouter) Provide(_ context.Context, _ cid.Cid, _ bool) error {
	return routing.ErrNotSupported
}

func (r APIRouter) pathForKey(key string) string {
	return r.uri + "/value/" + base64.URLEncoding.EncodeToString([]byte(key))
}
<|endoftext|>


<commit_before>package main

import (
	"net"
	"log"
	"bytes"
	"encoding/binary"
	"github.com/rmxymh/infra-ecosphere/utils"
	"github.com/rmxymh/infra-ecosphere/ipmi"
	"fmt"
	"github.com/rmxymh/infra-ecosphere/web"
	"net/http"
	"github.com/jmcvetta/restclient"
)

var EcosphereIP string = "10.0.2.2"

func SetBootDevice(addr *net.UDPAddr, server *net.UDPConn, wrapper ipmi.IPMISessionWrapper, message ipmi.IPMIMessage, selector ipmi.IPMIChassisBootOptionParameterSelector) {
	localIP := utils.GetLocalIP(server)

	buf := bytes.NewBuffer(selector.Parameters)
	request := ipmi.IPMIChassisBootOptionBootFlags{}
	binary.Read(buf, binary.BigEndian, &request)

	// Simulate: We just dump log but do nothing here.
	if request.BootParam & ipmi.BOOT_PARAM_BITMASK_VALID != 0 {
		log.Println("        IPMI CHASSIS BOOT FLAG: Valid")
	}
	if request.BootParam & ipmi.BOOT_PARAM_BITMASK_PERSISTENT != 0 {
		log.Println("        IPMI CHASSIS BOOT FLAG: Persistent")
	} else {
		log.Println("        IPMI CHASSIS BOOT FLAG: Only on the next boot")
	}
	if request.BootParam & ipmi.BOOT_PARAM_BITMASK_BOOT_TYPE_EFI != 0 {
		log.Println("        IPMI CHASSIS BOOT FLAG: Boot Type = EFI")
	} else {
		log.Println("        IPMI CHASSIS BOOT FLAG: Boot Type = PC Compatible (Legacy)")
	}

	// Simulate: We just dump log but do nothing here
	if request.BootDevice & ipmi.BOOT_DEVICE_BITMASK_CMOS_CLEAR != 0 {
		log.Println("        IPMI CHASSIS BOOT DEVICE: CMOS Clear")
	}
	if request.BootDevice & ipmi.BOOT_DEVICE_BITMASK_LOCK_KEYBOARD != 0 {
		log.Println("        IPMI CHASSIS BOOT DEVICE: Lock Keyboard")
	}
	if request.BootDevice & ipmi.BOOT_DEVICE_BITMASK_SCREEN_BLANK != 0 {
		log.Println("        IPMI CHASSIS BOOT DEVICE: Screen Blank")
	}
	if request.BootDevice & ipmi.BOOT_DEVICE_BITMASK_LOCK_RESET != 0 {
		log.Println("        IPMI CHASSIS BOOT DEVICE: Lock RESET Buttons")
	}

	// This part contains some options that we only support: PXE, CD, HDD
	//   Maybe there is another way to simulate remote device.
	device := (request.BootDevice & ipmi.BOOT_DEVICE_BITMASK_DEVICE) >> 2

	bootdevReq := web.WebReqBootDev{}
	bootdevResp := web.WebRespBootDev{}
	baseAPI := fmt.Sprintf("http://%s/api/BMCs/%s/bootdev", EcosphereIP, localIP)

	switch device {
	case ipmi.BOOT_DEVICE_FORCE_PXE:
		log.Println("        IPMI CHASSIS BOOT DEVICE: BOOT_DEVICE_FORCE_PXE")
		bootdevReq.Device = "PXE"
		req := restclient.RequestResponse{
			Url: baseAPI,
			Method: http.MethodPut,
			Data: &bootdevReq,
			Result: &bootdevResp,
		}
		status, err := restclient.Do(&req)
		if status != 200 {
			log.Println("Failed to call ecosphere Web API for setting bootdev: ", err.Error())
		}

	case ipmi.BOOT_DEVICE_FORCE_HDD:
		log.Println("        IPMI CHASSIS BOOT DEVICE: BOOT_DEVICE_FORCE_HDD")
		bootdevReq.Device = "DISK"
		req := restclient.RequestResponse{
			Url: baseAPI,
			Method: http.MethodPut,
			Data: &bootdevReq,
			Result: &bootdevResp,
		}
		status, err := restclient.Do(&req)
		if status != 200 {
			log.Println("Failed to call ecosphere Web API for setting bootdev: ", err.Error())
		}

	case ipmi.BOOT_DEVICE_FORCE_HDD_SAFE:
		log.Println("        IPMI CHASSIS BOOT DEVICE: BOOT_DEVICE_FORCE_HDD_SAFE")
	case ipmi.BOOT_DEVICE_FORCE_DIAG_PARTITION:
		log.Println("        IPMI CHASSIS BOOT DEVICE: BOOT_DEVICE_FORCE_DIAG_PARTITION")
	case ipmi.BOOT_DEVICE_FORCE_CD:
		log.Println("        IPMI CHASSIS BOOT DEVICE: BOOT_DEVICE_FORCE_CD")
	case ipmi.BOOT_DEVICE_FORCE_BIOS:
		log.Println("        IPMI CHASSIS BOOT DEVICE: BOOT_DEVICE_FORCE_BIOS")
	case ipmi.BOOT_DEVICE_FORCE_REMOTE_FLOPPY:
		log.Println("        IPMI CHASSIS BOOT DEVICE: BOOT_DEVICE_FORCE_REMOTE_FLOPPY")
	case ipmi.BOOT_DEVICE_FORCE_REMOTE_MEDIA:
		log.Println("        IPMI CHASSIS BOOT DEVICE: BOOT_DEVICE_FORCE_REMOTE_MEDIA")
	case ipmi.BOOT_DEVICE_FORCE_REMOTE_CD:
		log.Println("        IPMI CHASSIS BOOT DEVICE: BOOT_DEVICE_FORCE_REMOTE_CD")
	case ipmi.BOOT_DEVICE_FORCE_REMOTE_HDD:
		log.Println("        IPMI CHASSIS BOOT DEVICE: BOOT_DEVICE_FORCE_REMOTE_HDD")
	}

	// Simulate: We just dump log but do nothing here.
	if request.BIOSVerbosity & ipmi.BOOT_BIOS_BITMASK_LOCK_VIA_POWER != 0 {
		log.Println("        IPMI CHASSIS BOOT DEVICE: Lock out (power off / sleep request) via Power Button")
	}
	if request.BIOSVerbosity & ipmi.BOOT_BIOS_BITMASK_EVENT_TRAP != 0 {
		log.Println("        IPMI CHASSIS BOOT DEVICE: Force Progress Event Trap (Only for IPMI 2.0)")
	}
	if request.BIOSVerbosity & ipmi.BOOT_BIOS_BITMASK_PASSWORD_BYPASS != 0 {
		log.Println("        IPMI CHASSIS BOOT DEVICE: User password bypass")
	}
	if request.BIOSVerbosity & ipmi.BOOT_BIOS_BITMASK_LOCK_SLEEP != 0 {
		log.Println("        IPMI CHASSIS BOOT DEVICE: Lock out Sleep Button")
	}
	verbosity := (request.BIOSVerbosity & ipmi.BOOT_BIOS_BITMASK_FIRMWARE) >> 5
	switch verbosity {
	case ipmi.BOOT_BIOS_FIRMWARE_SYSTEM_DEFAULT:
		log.Println("        IPMI CHASSIS BOOT BIOS: BOOT_BIOS_FIRMWARE_SYSTEM_DEFAULT")
	case ipmi.BOOT_BIOS_FIRMWARE_REQUEST_QUIET:
		log.Println("        IPMI CHASSIS BOOT BIOS: BOOT_BIOS_FIRMWARE_REQUEST_QUIET")
	case ipmi.BOOT_BIOS_FIRMWARE_REQUEST_VERBOSE:
		log.Println("        IPMI CHASSIS BOOT BIOS: BOOT_BIOS_FIRMWARE_REQUEST_VERBOSE")
	}
	console_redirect := (request.BIOSVerbosity & ipmi.BOOT_BIOS_BITMASK_CONSOLE_REDIRECT)
	switch console_redirect {
	case ipmi.BOOT_BIOS_CONSOLE_REDIRECT_OCCURS_PER_BIOS_SETTING:
		log.Println("        IPMI CHASSIS BOOT BIOS: BOOT_BIOS_CONSOLE_REDIRECT_OCCURS_PER_BIOS_SETTING")
	case ipmi.BOOT_BIOS_CONSOLE_REDIRECT_SUPRESS_CONSOLE_IF_ENABLED:
		log.Println("        IPMI CHASSIS BOOT BIOS: BOOT_BIOS_CONSOLE_REDIRECT_SUPRESS_CONSOLE_IF_ENABLED")
	case ipmi.BOOT_BIOS_CONSOLE_REDIRECT_REQUEST_ENABLED:
		log.Println("        IPMI CHASSIS BOOT BIOS: BOOT_BIOS_CONSOLE_REDIRECT_REQUEST_ENABLED")
	}

	// Simulate: We just dump log but do nothing here.
	if request.BIOSSharedMode & ipmi.BOOT_BIOS_SHARED_BITMASK_OVERRIDE != 0 {
		log.Println("        IPMI CHASSIS BOOT BIOS: BOOT_BIOS_SHARED_BITMASK_OVERRIDE")
	}
	mux_control := request.BIOSSharedMode & ipmi.BOOT_BIOS_SHARED_BITMASK_MUX_CONTROL_OVERRIDE
	switch mux_control {
	case ipmi.BOOT_BIOS_SHARED_MUX_RECOMMENDED:
		log.Println("        IPMI CHASSIS BOOT BIOS: BOOT_BIOS_SHARED_MUX_RECOMMENDED")
	case ipmi.BOOT_BIOS_SHARED_MUX_TO_SYSTEM:
		log.Println("        IPMI CHASSIS BOOT BIOS: BOOT_BIOS_SHARED_MUX_TO_SYSTEM")
	case ipmi.BOOT_BIOS_SHARED_MUX_TO_BMC:
		log.Println("        IPMI CHASSIS BOOT BIOS: BOOT_BIOS_SHARED_MUX_TO_BMC")
	}

	ipmi.SendIPMIChassisSetBootOptionResponseBack(addr, server, wrapper, message);
}
<commit_msg>Use napping to replace restclient<commit_after>package main

import (
	"net"
	"log"
	"bytes"
	"encoding/binary"
	"github.com/rmxymh/infra-ecosphere/utils"
	"github.com/rmxymh/infra-ecosphere/ipmi"
	"fmt"
	"github.com/rmxymh/infra-ecosphere/web"
	"github.com/jmcvetta/napping"
)

var EcosphereIP string = "10.0.2.2"

func SetBootDevice(addr *net.UDPAddr, server *net.UDPConn, wrapper ipmi.IPMISessionWrapper, message ipmi.IPMIMessage, selector ipmi.IPMIChassisBootOptionParameterSelector) {
	localIP := utils.GetLocalIP(server)

	buf := bytes.NewBuffer(selector.Parameters)
	request := ipmi.IPMIChassisBootOptionBootFlags{}
	binary.Read(buf, binary.BigEndian, &request)

	// Simulate: We just dump log but do nothing here.
	if request.BootParam & ipmi.BOOT_PARAM_BITMASK_VALID != 0 {
		log.Println("        IPMI CHASSIS BOOT FLAG: Valid")
	}
	if request.BootParam & ipmi.BOOT_PARAM_BITMASK_PERSISTENT != 0 {
		log.Println("        IPMI CHASSIS BOOT FLAG: Persistent")
	} else {
		log.Println("        IPMI CHASSIS BOOT FLAG: Only on the next boot")
	}
	if request.BootParam & ipmi.BOOT_PARAM_BITMASK_BOOT_TYPE_EFI != 0 {
		log.Println("        IPMI CHASSIS BOOT FLAG: Boot Type = EFI")
	} else {
		log.Println("        IPMI CHASSIS BOOT FLAG: Boot Type = PC Compatible (Legacy)")
	}

	// Simulate: We just dump log but do nothing here
	if request.BootDevice & ipmi.BOOT_DEVICE_BITMASK_CMOS_CLEAR != 0 {
		log.Println("        IPMI CHASSIS BOOT DEVICE: CMOS Clear")
	}
	if request.BootDevice & ipmi.BOOT_DEVICE_BITMASK_LOCK_KEYBOARD != 0 {
		log.Println("        IPMI CHASSIS BOOT DEVICE: Lock Keyboard")
	}
	if request.BootDevice & ipmi.BOOT_DEVICE_BITMASK_SCREEN_BLANK != 0 {
		log.Println("        IPMI CHASSIS BOOT DEVICE: Screen Blank")
	}
	if request.BootDevice & ipmi.BOOT_DEVICE_BITMASK_LOCK_RESET != 0 {
		log.Println("        IPMI CHASSIS BOOT DEVICE: Lock RESET Buttons")
	}

	// This part contains some options that we only support: PXE, CD, HDD
	//   Maybe there is another way to simulate remote device.
	device := (request.BootDevice & ipmi.BOOT_DEVICE_BITMASK_DEVICE) >> 2

	bootdevReq := web.WebReqBootDev{}
	bootdevResp := web.WebRespBootDev{}
	baseAPI := fmt.Sprintf("http://%s/api/BMCs/%s/bootdev", EcosphereIP, localIP)

	switch device {
	case ipmi.BOOT_DEVICE_FORCE_PXE:
		log.Println("        IPMI CHASSIS BOOT DEVICE: BOOT_DEVICE_FORCE_PXE")
		bootdevReq.Device = "PXE"
		status, err := napping.Put(baseAPI, &bootdevReq, &bootdevResp, nil)
		if status != 200 {
			log.Println("Failed to call ecosphere Web API for setting bootdev: ", err.Error())
		}

	case ipmi.BOOT_DEVICE_FORCE_HDD:
		log.Println("        IPMI CHASSIS BOOT DEVICE: BOOT_DEVICE_FORCE_HDD")
		bootdevReq.Device = "DISK"
		status, err := napping.Put(baseAPI, &bootdevReq, &bootdevResp, nil)
		if status != 200 {
			log.Println("Failed to call ecosphere Web API for setting bootdev: ", err.Error())
		}

	case ipmi.BOOT_DEVICE_FORCE_HDD_SAFE:
		log.Println("        IPMI CHASSIS BOOT DEVICE: BOOT_DEVICE_FORCE_HDD_SAFE")
	case ipmi.BOOT_DEVICE_FORCE_DIAG_PARTITION:
		log.Println("        IPMI CHASSIS BOOT DEVICE: BOOT_DEVICE_FORCE_DIAG_PARTITION")
	case ipmi.BOOT_DEVICE_FORCE_CD:
		log.Println("        IPMI CHASSIS BOOT DEVICE: BOOT_DEVICE_FORCE_CD")
	case ipmi.BOOT_DEVICE_FORCE_BIOS:
		log.Println("        IPMI CHASSIS BOOT DEVICE: BOOT_DEVICE_FORCE_BIOS")
	case ipmi.BOOT_DEVICE_FORCE_REMOTE_FLOPPY:
		log.Println("        IPMI CHASSIS BOOT DEVICE: BOOT_DEVICE_FORCE_REMOTE_FLOPPY")
	case ipmi.BOOT_DEVICE_FORCE_REMOTE_MEDIA:
		log.Println("        IPMI CHASSIS BOOT DEVICE: BOOT_DEVICE_FORCE_REMOTE_MEDIA")
	case ipmi.BOOT_DEVICE_FORCE_REMOTE_CD:
		log.Println("        IPMI CHASSIS BOOT DEVICE: BOOT_DEVICE_FORCE_REMOTE_CD")
	case ipmi.BOOT_DEVICE_FORCE_REMOTE_HDD:
		log.Println("        IPMI CHASSIS BOOT DEVICE: BOOT_DEVICE_FORCE_REMOTE_HDD")
	}

	// Simulate: We just dump log but do nothing here.
	if request.BIOSVerbosity & ipmi.BOOT_BIOS_BITMASK_LOCK_VIA_POWER != 0 {
		log.Println("        IPMI CHASSIS BOOT DEVICE: Lock out (power off / sleep request) via Power Button")
	}
	if request.BIOSVerbosity & ipmi.BOOT_BIOS_BITMASK_EVENT_TRAP != 0 {
		log.Println("        IPMI CHASSIS BOOT DEVICE: Force Progress Event Trap (Only for IPMI 2.0)")
	}
	if request.BIOSVerbosity & ipmi.BOOT_BIOS_BITMASK_PASSWORD_BYPASS != 0 {
		log.Println("        IPMI CHASSIS BOOT DEVICE: User password bypass")
	}
	if request.BIOSVerbosity & ipmi.BOOT_BIOS_BITMASK_LOCK_SLEEP != 0 {
		log.Println("        IPMI CHASSIS BOOT DEVICE: Lock out Sleep Button")
	}
	verbosity := (request.BIOSVerbosity & ipmi.BOOT_BIOS_BITMASK_FIRMWARE) >> 5
	switch verbosity {
	case ipmi.BOOT_BIOS_FIRMWARE_SYSTEM_DEFAULT:
		log.Println("        IPMI CHASSIS BOOT BIOS: BOOT_BIOS_FIRMWARE_SYSTEM_DEFAULT")
	case ipmi.BOOT_BIOS_FIRMWARE_REQUEST_QUIET:
		log.Println("        IPMI CHASSIS BOOT BIOS: BOOT_BIOS_FIRMWARE_REQUEST_QUIET")
	case ipmi.BOOT_BIOS_FIRMWARE_REQUEST_VERBOSE:
		log.Println("        IPMI CHASSIS BOOT BIOS: BOOT_BIOS_FIRMWARE_REQUEST_VERBOSE")
	}
	console_redirect := (request.BIOSVerbosity & ipmi.BOOT_BIOS_BITMASK_CONSOLE_REDIRECT)
	switch console_redirect {
	case ipmi.BOOT_BIOS_CONSOLE_REDIRECT_OCCURS_PER_BIOS_SETTING:
		log.Println("        IPMI CHASSIS BOOT BIOS: BOOT_BIOS_CONSOLE_REDIRECT_OCCURS_PER_BIOS_SETTING")
	case ipmi.BOOT_BIOS_CONSOLE_REDIRECT_SUPRESS_CONSOLE_IF_ENABLED:
		log.Println("        IPMI CHASSIS BOOT BIOS: BOOT_BIOS_CONSOLE_REDIRECT_SUPRESS_CONSOLE_IF_ENABLED")
	case ipmi.BOOT_BIOS_CONSOLE_REDIRECT_REQUEST_ENABLED:
		log.Println("        IPMI CHASSIS BOOT BIOS: BOOT_BIOS_CONSOLE_REDIRECT_REQUEST_ENABLED")
	}

	// Simulate: We just dump log but do nothing here.
	if request.BIOSSharedMode & ipmi.BOOT_BIOS_SHARED_BITMASK_OVERRIDE != 0 {
		log.Println("        IPMI CHASSIS BOOT BIOS: BOOT_BIOS_SHARED_BITMASK_OVERRIDE")
	}
	mux_control := request.BIOSSharedMode & ipmi.BOOT_BIOS_SHARED_BITMASK_MUX_CONTROL_OVERRIDE
	switch mux_control {
	case ipmi.BOOT_BIOS_SHARED_MUX_RECOMMENDED:
		log.Println("        IPMI CHASSIS BOOT BIOS: BOOT_BIOS_SHARED_MUX_RECOMMENDED")
	case ipmi.BOOT_BIOS_SHARED_MUX_TO_SYSTEM:
		log.Println("        IPMI CHASSIS BOOT BIOS: BOOT_BIOS_SHARED_MUX_TO_SYSTEM")
	case ipmi.BOOT_BIOS_SHARED_MUX_TO_BMC:
		log.Println("        IPMI CHASSIS BOOT BIOS: BOOT_BIOS_SHARED_MUX_TO_BMC")
	}

	ipmi.SendIPMIChassisSetBootOptionResponseBack(addr, server, wrapper, message);
}
<|endoftext|>


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

package main

import (
	"context"

	"cloud.google.com/go/spanner"

	"go.chromium.org/luci/common/clock"
	"go.chromium.org/luci/common/errors"
	"go.chromium.org/luci/grpc/grpcutil"

	"go.chromium.org/luci/resultdb/internal/span"
	"go.chromium.org/luci/resultdb/pbutil"
	pb "go.chromium.org/luci/resultdb/proto/rpc/v1"
)

// validateFinalizeInvocationRequest returns a non-nil error if req is determined
// to be invalid.
func validateFinalizeInvocationRequest(req *pb.FinalizeInvocationRequest) error {
	if _, err := pbutil.ParseInvocationName(req.Name); err != nil {
		return errors.Annotate(err, "name").Err()
	}

	return nil
}

func getUnmatchedInterruptedFlagError(invID span.InvocationID) error {
	return errors.Reason("%q has already been finalized with different interrupted flag", invID.Name()).Tag(grpcutil.FailedPreconditionTag).Err()
}

// FinalizeInvocation implements pb.RecorderServer.
func (s *recorderServer) FinalizeInvocation(ctx context.Context, in *pb.FinalizeInvocationRequest) (*pb.Invocation, error) {
	if err := validateFinalizeInvocationRequest(in); err != nil {
		return nil, errors.Annotate(err, "bad request").Tag(grpcutil.InvalidArgumentTag).Err()
	}

	userToken, err := extractUserUpdateToken(ctx)
	if err != nil {
		return nil, err
	}

	invID := span.MustParseInvocationName(in.Name)
	requestState := pb.Invocation_COMPLETED

	ret := &pb.Invocation{Name: in.Name}
	var retErr error

	_, err = span.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error {
		now := clock.Now(ctx)

		var updateToken spanner.NullString

		err = span.ReadInvocation(ctx, txn, invID, map[string]interface{}{
			"UpdateToken":  &updateToken,
			"State":        &ret.State,
			"Interrupted":  &ret.Interrupted,
			"CreateTime":   &ret.CreateTime,
			"FinalizeTime": &ret.FinalizeTime,
			"Deadline":     &ret.Deadline,
			"Tags":         &ret.Tags,
		})

		finalizeTime := now
		deadline := pbutil.MustTimestamp(ret.Deadline)
		switch {
		case err != nil:
			return err

		case ret.State == requestState && ret.Interrupted == in.Interrupted:
			// Idempotent.
			return nil
		case ret.State == requestState && ret.Interrupted != in.Interrupted:
			return getUnmatchedInterruptedFlagError(invID)
		case deadline.Before(now):
			ret.State = requestState
			ret.FinalizeTime = ret.Deadline
			ret.Interrupted = true
			finalizeTime = deadline

			if !in.Interrupted {
				retErr = getUnmatchedInterruptedFlagError(invID)
			}
		default:
			// Finalize as requested.
			ret.State = requestState
			ret.FinalizeTime = pbutil.MustTimestampProto(now)
			ret.Interrupted = in.Interrupted
		}

		if err = validateUserUpdateToken(updateToken, userToken); err != nil {
			return err
		}

		return finalizeInvocation(ctx, txn, invID, in.Interrupted, finalizeTime)
	})

	if err != nil {
		return nil, err
	}

	if retErr != nil {
		return nil, retErr
	}

	return ret, nil
}
<commit_msg>[resultdb] speculative fix of the flaky test in finalize_invocation_test.go<commit_after>// Copyright 2019 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"context"

	"cloud.google.com/go/spanner"

	"go.chromium.org/luci/common/clock"
	"go.chromium.org/luci/common/errors"
	"go.chromium.org/luci/grpc/grpcutil"

	"go.chromium.org/luci/resultdb/internal/span"
	"go.chromium.org/luci/resultdb/pbutil"
	pb "go.chromium.org/luci/resultdb/proto/rpc/v1"
)

// validateFinalizeInvocationRequest returns a non-nil error if req is determined
// to be invalid.
func validateFinalizeInvocationRequest(req *pb.FinalizeInvocationRequest) error {
	if _, err := pbutil.ParseInvocationName(req.Name); err != nil {
		return errors.Annotate(err, "name").Err()
	}

	return nil
}

func getUnmatchedInterruptedFlagError(invID span.InvocationID) error {
	return errors.Reason("%q has already been finalized with different interrupted flag", invID.Name()).Tag(grpcutil.FailedPreconditionTag).Err()
}

// FinalizeInvocation implements pb.RecorderServer.
func (s *recorderServer) FinalizeInvocation(ctx context.Context, in *pb.FinalizeInvocationRequest) (*pb.Invocation, error) {
	if err := validateFinalizeInvocationRequest(in); err != nil {
		return nil, errors.Annotate(err, "bad request").Tag(grpcutil.InvalidArgumentTag).Err()
	}

	userToken, err := extractUserUpdateToken(ctx)
	if err != nil {
		return nil, err
	}

	invID := span.MustParseInvocationName(in.Name)
	requestState := pb.Invocation_COMPLETED

	ret := &pb.Invocation{Name: in.Name}
	var retErr error

	_, err = span.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error {
		now := clock.Now(ctx)

		var updateToken spanner.NullString

		err = span.ReadInvocation(ctx, txn, invID, map[string]interface{}{
			"UpdateToken":  &updateToken,
			"State":        &ret.State,
			"Interrupted":  &ret.Interrupted,
			"CreateTime":   &ret.CreateTime,
			"FinalizeTime": &ret.FinalizeTime,
			"Deadline":     &ret.Deadline,
			"Tags":         &ret.Tags,
		})
		if err != nil {
			return err
		}

		finalizeTime := now
		deadline := pbutil.MustTimestamp(ret.Deadline)
		switch {
		case ret.State == requestState && ret.Interrupted == in.Interrupted:
			// Idempotent.
			return nil
		case ret.State == requestState && ret.Interrupted != in.Interrupted:
			return getUnmatchedInterruptedFlagError(invID)
		case deadline.Before(now):
			ret.State = requestState
			ret.FinalizeTime = ret.Deadline
			ret.Interrupted = true
			finalizeTime = deadline

			if !in.Interrupted {
				retErr = getUnmatchedInterruptedFlagError(invID)
			}
		default:
			// Finalize as requested.
			ret.State = requestState
			ret.FinalizeTime = pbutil.MustTimestampProto(now)
			ret.Interrupted = in.Interrupted
		}

		if err = validateUserUpdateToken(updateToken, userToken); err != nil {
			return err
		}

		return finalizeInvocation(ctx, txn, invID, in.Interrupted, finalizeTime)
	})

	if err != nil {
		return nil, err
	}

	if retErr != nil {
		return nil, retErr
	}

	return ret, nil
}
<|endoftext|>


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

package kbfsgit

import (
	"context"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"testing"

	"github.com/keybase/kbfs/libfs"
	"github.com/keybase/kbfs/libkbfs"
	"github.com/keybase/kbfs/tlf"
	"github.com/stretchr/testify/require"
	gogit "gopkg.in/src-d/go-git.v4"
	gogitcfg "gopkg.in/src-d/go-git.v4/config"
	"gopkg.in/src-d/go-git.v4/storage/filesystem"
)

func makeFS(t *testing.T, subdir string) (
	context.Context, *libkbfs.TlfHandle, *libfs.FS) {
	ctx := libkbfs.BackgroundContextWithCancellationDelayer()
	config := libkbfs.MakeTestConfigOrBust(t, "user1", "user2")
	h, err := libkbfs.ParseTlfHandle(ctx, config.KBPKI(), "user1", tlf.Private)
	require.NoError(t, err)
	fs, err := libfs.NewFS(ctx, config, h, subdir, "")
	require.NoError(t, err)
	return ctx, h, fs
}

// This tests pushing code to a bare repo stored in KBFS, and pulling
// code from that bare repo into a new working tree.  This is a simple
// version of how the full KBFS Git system will work.  Specifically,
// this test does the following:
//
// 1) Initializes a new repo on the local file system with one file.
// 2) Initializes a new bare repo in KBFS.
// 3) Simulates a user push by having the bare repo fetch from the
//    local file system repo.  (This seems like the easiest way for
//    the git remote helper to get data from the local repo into the
//    server repo.)
// 4) Initializes a second new repo on the local file system.
// 5) Simulates a user pull by having the bare repo push into this
// second repo onto a branch, and then checking out that branch.
func TestBareRepoInKBFS(t *testing.T) {
	ctx, _, fs := makeFS(t, "")
	defer libkbfs.CheckConfigAndShutdown(ctx, t, fs.Config())

	storer, err := filesystem.NewStorage(fs)
	require.NoError(t, err)

	repo, err := gogit.Init(storer, nil)
	require.NoError(t, err)
	defer func() {
		err = fs.SyncAll()
		require.NoError(t, err)
	}()

	git1, err := ioutil.TempDir(os.TempDir(), "kbfsgittest")
	require.NoError(t, err)
	defer os.RemoveAll(git1)

	t.Logf("Make a new repo in %s with one file", git1)
	err = ioutil.WriteFile(filepath.Join(git1, "foo"), []byte("hello"), 0600)
	require.NoError(t, err)
	dotgit1 := filepath.Join(git1, ".git")
	cmd := exec.Command(
		"git", "--git-dir", dotgit1, "--work-tree", git1, "init")
	cmd.Start()
	err = cmd.Wait()
	require.NoError(t, err)

	cmd = exec.Command(
		"git", "--git-dir", dotgit1, "--work-tree", git1, "add", "foo")
	cmd.Start()
	err = cmd.Wait()
	require.NoError(t, err)

	cmd = exec.Command(
		"git", "--git-dir", dotgit1, "--work-tree", git1, "commit", "-a",
		"-m", "foo")
	cmd.Start()
	err = cmd.Wait()
	require.NoError(t, err)

	_, err = repo.CreateRemote(&gogitcfg.RemoteConfig{
		Name: "git1",
		URL:  git1,
	})
	require.NoError(t, err)

	err = repo.Fetch(&gogit.FetchOptions{
		RemoteName: "git1",
		RefSpecs:   []gogitcfg.RefSpec{"refs/heads/master:refs/heads/master"},
	})
	require.NoError(t, err)

	git2, err := ioutil.TempDir(os.TempDir(), "kbfsgittest")
	require.NoError(t, err)
	defer os.RemoveAll(git2)

	t.Logf("Make a new repo in %s to clone from the KBFS repo", git2)
	dotgit2 := filepath.Join(git2, ".git")
	cmd = exec.Command(
		"git", "--git-dir", dotgit2, "--work-tree", git2, "init")
	cmd.Start()
	err = cmd.Wait()
	require.NoError(t, err)

	_, err = repo.CreateRemote(&gogitcfg.RemoteConfig{
		Name: "git2",
		URL:  git2,
	})
	require.NoError(t, err)

	err = repo.Push(&gogit.PushOptions{
		RemoteName: "git2",
		RefSpecs: []gogitcfg.RefSpec{
			"refs/heads/master:refs/heads/kb/master",
		},
	})
	require.NoError(t, err)

	cmd = exec.Command(
		"git", "--git-dir", dotgit2, "--work-tree", git2, "checkout",
		"kb/master")
	cmd.Start()
	err = cmd.Wait()
	require.NoError(t, err)

	data, err := ioutil.ReadFile(filepath.Join(git2, "foo"))
	require.NoError(t, err)
	require.Equal(t, "hello", string(data))
}
<commit_msg>kbfsgit: make fs test work on windows<commit_after>// Copyright 2017 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package kbfsgit

import (
	"context"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"testing"

	"github.com/keybase/kbfs/libfs"
	"github.com/keybase/kbfs/libkbfs"
	"github.com/keybase/kbfs/tlf"
	"github.com/stretchr/testify/require"
	gogit "gopkg.in/src-d/go-git.v4"
	gogitcfg "gopkg.in/src-d/go-git.v4/config"
	"gopkg.in/src-d/go-git.v4/plumbing/storer"
	"gopkg.in/src-d/go-git.v4/storage"
	"gopkg.in/src-d/go-git.v4/storage/filesystem"
)

func makeFS(t *testing.T, subdir string) (
	context.Context, *libkbfs.TlfHandle, *libfs.FS) {
	ctx := libkbfs.BackgroundContextWithCancellationDelayer()
	config := libkbfs.MakeTestConfigOrBust(t, "user1", "user2")
	h, err := libkbfs.ParseTlfHandle(ctx, config.KBPKI(), "user1", tlf.Private)
	require.NoError(t, err)
	fs, err := libfs.NewFS(ctx, config, h, subdir, "")
	require.NoError(t, err)
	return ctx, h, fs
}

// configInMemoryStorer keeps the git config in memory, to work around
// a gcfg bug (used by go-git when reading configs from disk) that
// causes a freakout when it sees backslashes in git file URLs.
type configInMemoryStorer struct {
	*filesystem.Storage
	cfg *gogitcfg.Config
}

func (cims *configInMemoryStorer) Init() error {
	return cims.Storage.Init()
}

func (cims *configInMemoryStorer) Config() (*gogitcfg.Config, error) {
	return cims.cfg, nil
}

func (cims *configInMemoryStorer) SetConfig(c *gogitcfg.Config) error {
	cims.cfg = c
	return nil
}

var _ storage.Storer = (*configInMemoryStorer)(nil)
var _ storer.Initializer = (*configInMemoryStorer)(nil)

// This tests pushing code to a bare repo stored in KBFS, and pulling
// code from that bare repo into a new working tree.  This is a simple
// version of how the full KBFS Git system will work.  Specifically,
// this test does the following:
//
// 1) Initializes a new repo on the local file system with one file.
// 2) Initializes a new bare repo in KBFS.
// 3) Simulates a user push by having the bare repo fetch from the
//    local file system repo.  (This seems like the easiest way for
//    the git remote helper to get data from the local repo into the
//    server repo.)
// 4) Initializes a second new repo on the local file system.
// 5) Simulates a user pull by having the bare repo push into this
// second repo onto a branch, and then checking out that branch.
func TestBareRepoInKBFS(t *testing.T) {
	ctx, _, fs := makeFS(t, "")
	defer libkbfs.CheckConfigAndShutdown(ctx, t, fs.Config())

	fsStorer, err := filesystem.NewStorage(fs)
	require.NoError(t, err)
	cfg, err := fsStorer.Config()
	require.NoError(t, err)
	storer := &configInMemoryStorer{fsStorer, cfg}

	repo, err := gogit.Init(storer, nil)
	require.NoError(t, err)
	defer func() {
		err = fs.SyncAll()
		require.NoError(t, err)
	}()

	git1, err := ioutil.TempDir(os.TempDir(), "kbfsgittest")
	require.NoError(t, err)
	defer os.RemoveAll(git1)

	t.Logf("Make a new repo in %s with one file", git1)
	err = ioutil.WriteFile(filepath.Join(git1, "foo"), []byte("hello"), 0600)
	require.NoError(t, err)
	dotgit1 := filepath.Join(git1, ".git")
	cmd := exec.Command(
		"git", "--git-dir", dotgit1, "--work-tree", git1, "init")
	err = cmd.Run()
	require.NoError(t, err)

	cmd = exec.Command(
		"git", "--git-dir", dotgit1, "--work-tree", git1, "add", "foo")
	err = cmd.Run()
	require.NoError(t, err)

	cmd = exec.Command(
		"git", "--git-dir", dotgit1, "--work-tree", git1, "-c", "user.name=Foo",
		"-c", "user.email=foo@foo.com", "commit", "-a", "-m", "foo")
	err = cmd.Run()
	require.NoError(t, err)

	remote, err := repo.CreateRemote(&gogitcfg.RemoteConfig{
		Name: "git1",
		URL:  git1,
	})
	require.NoError(t, err)

	err = remote.Fetch(&gogit.FetchOptions{
		RemoteName: "git1",
		RefSpecs:   []gogitcfg.RefSpec{"refs/heads/master:refs/heads/master"},
	})
	require.NoError(t, err)

	git2, err := ioutil.TempDir(os.TempDir(), "kbfsgittest")
	require.NoError(t, err)
	defer os.RemoveAll(git2)

	t.Logf("Make a new repo in %s to clone from the KBFS repo", git2)
	dotgit2 := filepath.Join(git2, ".git")
	cmd = exec.Command(
		"git", "--git-dir", dotgit2, "--work-tree", git2, "init")
	err = cmd.Run()
	require.NoError(t, err)

	remote, err = repo.CreateRemote(&gogitcfg.RemoteConfig{
		Name: "git2",
		URL:  git2,
	})
	require.NoError(t, err)

	err = remote.Push(&gogit.PushOptions{
		RemoteName: "git2",
		RefSpecs: []gogitcfg.RefSpec{
			"refs/heads/master:refs/heads/kb/master",
		},
	})
	require.NoError(t, err)

	cmd = exec.Command(
		"git", "--git-dir", dotgit2, "--work-tree", git2, "checkout",
		"kb/master")
	err = cmd.Run()
	require.NoError(t, err)

	data, err := ioutil.ReadFile(filepath.Join(git2, "foo"))
	require.NoError(t, err)
	require.Equal(t, "hello", string(data))
}
<|endoftext|>


<commit_before>package terraform

import (
	"sync/atomic"

	"github.com/zclconf/go-cty/cty"

	"github.com/hashicorp/terraform/addrs"
	"github.com/hashicorp/terraform/plans"
	"github.com/hashicorp/terraform/providers"
	"github.com/hashicorp/terraform/states"
)

// stopHook is a private Hook implementation that Terraform uses to
// signal when to stop or cancel actions.
type stopHook struct {
	stop uint32
}

var _ Hook = (*stopHook)(nil)

func (h *stopHook) PreApply(addr addrs.AbsResourceInstance, gen states.Generation, action plans.Action, priorState, plannedNewState cty.Value) (HookAction, error) {
	return h.hook()
}

func (h *stopHook) PostApply(addr addrs.AbsResourceInstance, gen states.Generation, newState cty.Value, err error) (HookAction, error) {
	return h.hook()
}

func (h *stopHook) PreDiff(addr addrs.AbsResourceInstance, gen states.Generation, priorState, proposedNewState cty.Value) (HookAction, error) {
	return h.hook()
}

func (h *stopHook) PostDiff(addr addrs.AbsResourceInstance, gen states.Generation, action plans.Action, priorState, plannedNewState cty.Value) (HookAction, error) {
	return h.hook()
}

func (h *stopHook) PreProvisionInstance(addr addrs.AbsResourceInstance, state cty.Value) (HookAction, error) {
	return h.hook()
}

func (h *stopHook) PostProvisionInstance(addr addrs.AbsResourceInstance, state cty.Value) (HookAction, error) {
	return h.hook()
}

func (h *stopHook) PreProvisionInstanceStep(addr addrs.AbsResourceInstance, typeName string) (HookAction, error) {
	return h.hook()
}

func (h *stopHook) PostProvisionInstanceStep(addr addrs.AbsResourceInstance, typeName string, err error) (HookAction, error) {
	return h.hook()
}

func (h *stopHook) ProvisionOutput(addr addrs.AbsResourceInstance, typeName string, line string) {
}

func (h *stopHook) PreRefresh(addr addrs.AbsResourceInstance, gen states.Generation, priorState cty.Value) (HookAction, error) {
	return h.hook()
}

func (h *stopHook) PostRefresh(addr addrs.AbsResourceInstance, gen states.Generation, priorState cty.Value, newState cty.Value) (HookAction, error) {
	return h.hook()
}

func (h *stopHook) PreImportState(addr addrs.AbsResourceInstance, importID string) (HookAction, error) {
	return h.hook()
}

func (h *stopHook) PostImportState(addr addrs.AbsResourceInstance, imported []providers.ImportedResource) (HookAction, error) {
	return h.hook()
}

func (h *stopHook) PostStateUpdate(new *states.State) (HookAction, error) {
	return h.hook()
}

func (h *stopHook) hook() (HookAction, error) {
	if h.Stopped() {
		return HookActionHalt, nil
	}

	return HookActionContinue, nil
}

// reset should be called within the lock context
func (h *stopHook) Reset() {
	atomic.StoreUint32(&h.stop, 0)
}

func (h *stopHook) Stop() {
	atomic.StoreUint32(&h.stop, 1)
}

func (h *stopHook) Stopped() bool {
	return atomic.LoadUint32(&h.stop) == 1
}
<commit_msg>command: Fix TestPlan_shutdown<commit_after>package terraform

import (
	"sync/atomic"

	"github.com/zclconf/go-cty/cty"

	"github.com/hashicorp/terraform/addrs"
	"github.com/hashicorp/terraform/plans"
	"github.com/hashicorp/terraform/providers"
	"github.com/hashicorp/terraform/states"
)

// stopHook is a private Hook implementation that Terraform uses to
// signal when to stop or cancel actions.
type stopHook struct {
	stop uint32
}

var _ Hook = (*stopHook)(nil)

func (h *stopHook) PreApply(addr addrs.AbsResourceInstance, gen states.Generation, action plans.Action, priorState, plannedNewState cty.Value) (HookAction, error) {
	return h.hook()
}

func (h *stopHook) PostApply(addr addrs.AbsResourceInstance, gen states.Generation, newState cty.Value, err error) (HookAction, error) {
	return h.hook()
}

func (h *stopHook) PreDiff(addr addrs.AbsResourceInstance, gen states.Generation, priorState, proposedNewState cty.Value) (HookAction, error) {
	return h.hook()
}

func (h *stopHook) PostDiff(addr addrs.AbsResourceInstance, gen states.Generation, action plans.Action, priorState, plannedNewState cty.Value) (HookAction, error) {
	return h.hook()
}

func (h *stopHook) PreProvisionInstance(addr addrs.AbsResourceInstance, state cty.Value) (HookAction, error) {
	return h.hook()
}

func (h *stopHook) PostProvisionInstance(addr addrs.AbsResourceInstance, state cty.Value) (HookAction, error) {
	return h.hook()
}

func (h *stopHook) PreProvisionInstanceStep(addr addrs.AbsResourceInstance, typeName string) (HookAction, error) {
	return h.hook()
}

func (h *stopHook) PostProvisionInstanceStep(addr addrs.AbsResourceInstance, typeName string, err error) (HookAction, error) {
	return h.hook()
}

func (h *stopHook) ProvisionOutput(addr addrs.AbsResourceInstance, typeName string, line string) {
}

func (h *stopHook) PreRefresh(addr addrs.AbsResourceInstance, gen states.Generation, priorState cty.Value) (HookAction, error) {
	return h.hook()
}

func (h *stopHook) PostRefresh(addr addrs.AbsResourceInstance, gen states.Generation, priorState cty.Value, newState cty.Value) (HookAction, error) {
	return h.hook()
}

func (h *stopHook) PreImportState(addr addrs.AbsResourceInstance, importID string) (HookAction, error) {
	return h.hook()
}

func (h *stopHook) PostImportState(addr addrs.AbsResourceInstance, imported []providers.ImportedResource) (HookAction, error) {
	return h.hook()
}

func (h *stopHook) PostStateUpdate(new *states.State) (HookAction, error) {
	return h.hook()
}

func (h *stopHook) hook() (HookAction, error) {
	if h.Stopped() {
		// FIXME: This should really return an error since stopping partway
		// through is not a successful run-to-completion, but we'll need to
		// introduce that cautiously since existing automation solutions may
		// be depending on this behavior.
		return HookActionHalt, nil
	}

	return HookActionContinue, nil
}

// reset should be called within the lock context
func (h *stopHook) Reset() {
	atomic.StoreUint32(&h.stop, 0)
}

func (h *stopHook) Stop() {
	atomic.StoreUint32(&h.stop, 1)
}

func (h *stopHook) Stopped() bool {
	return atomic.LoadUint32(&h.stop) == 1
}
<|endoftext|>