<commit_before>package clusterstats

import (
	"context"
	"reflect"
	"time"

	"github.com/pkg/errors"
	v32 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3"
	"github.com/rancher/rancher/pkg/clustermanager"
	v3 "github.com/rancher/rancher/pkg/generated/norman/management.cattle.io/v3"
	"github.com/rancher/rancher/pkg/types/config"
	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/runtime"
)

const (
	nodeRoleControlPlane       = "node-role.kubernetes.io/controlplane"
	nodeRoleControlPlaneHyphen = "node-role.kubernetes.io/control-plane"
	nodeRoleETCD               = "node-role.kubernetes.io/etcd"
	nodeRoleMaster             = "node-role.kubernetes.io/master"
)

type StatsAggregator struct {
	NodesLister    v3.NodeLister
	Clusters       v3.ClusterInterface
	ClusterManager *clustermanager.Manager
}

type ClusterNodeData struct {
	Capacity                        v1.ResourceList
	Allocatable                     v1.ResourceList
	Requested                       v1.ResourceList
	Limits                          v1.ResourceList
	ConditionNoDiskPressureStatus   v1.ConditionStatus
	ConditionNoMemoryPressureStatus v1.ConditionStatus
}

func Register(ctx context.Context, management *config.ManagementContext, clusterManager *clustermanager.Manager) {
	clustersClient := management.Management.Clusters("")
	machinesClient := management.Management.Nodes("")

	s := &StatsAggregator{
		NodesLister:    machinesClient.Controller().Lister(),
		Clusters:       clustersClient,
		ClusterManager: clusterManager,
	}

	clustersClient.AddHandler(ctx, "cluster-stats", s.sync)
	machinesClient.AddHandler(ctx, "cluster-stats", s.machineChanged)
}

func (s *StatsAggregator) sync(key string, cluster *v3.Cluster) (runtime.Object, error) {
	if cluster == nil {
		return nil, nil
	}

	return nil, s.aggregate(cluster, cluster.Name)
}

func (s *StatsAggregator) aggregate(cluster *v3.Cluster, clusterName string) error {
	allMachines, err := s.NodesLister.List(cluster.Name, labels.Everything())
	if err != nil {
		return err
	}

	workerCounts := make(map[string]int)
	var machines []*v3.Node
	// only include worker nodes
	for _, m := range allMachines {
		// if none are set, then nodes syncer has not completed
		if !m.Spec.Worker && !m.Spec.ControlPlane && !m.Spec.Etcd {
			return errors.Errorf("node role cannot be determined because node %s has not finished syncing. retrying...", m.Status.NodeName)
		}
		if isTaintedNoExecuteNoSchedule(m) && !m.Spec.Worker {
			continue
		}
		if m.Spec.InternalNodeSpec.Unschedulable {
			continue
		}

		if os, ok := m.Status.NodeLabels["kubernetes.io/os"]; ok && m.Spec.Worker {
			workerCounts[os]++
		}

		machines = append(machines, m)
	}

	origStatus := cluster.Status.DeepCopy()
	cluster = cluster.DeepCopy()

	cluster.Status.NodeCount = len(allMachines)
	if c, ok := workerCounts["windows"]; ok {
		cluster.Status.WindowsWorkerCount = c
	}
	if c, ok := workerCounts["linux"]; ok {
		cluster.Status.LinuxWorkerCount = c
	}

	// capacity keys
	pods, mem, cpu := resource.Quantity{}, resource.Quantity{}, resource.Quantity{}
	// allocatable keys
	apods, amem, acpu := resource.Quantity{}, resource.Quantity{}, resource.Quantity{}
	// requested keys
	rpods, rmem, rcpu := resource.Quantity{}, resource.Quantity{}, resource.Quantity{}
	// limited keys
	lpods, lmem, lcpu := resource.Quantity{}, resource.Quantity{}, resource.Quantity{}

	condDisk := v1.ConditionTrue
	condMem := v1.ConditionTrue

	for _, machine := range machines {
		capacity := machine.Status.InternalNodeStatus.Capacity
		if capacity != nil {
			pods.Add(*capacity.Pods())
			mem.Add(*capacity.Memory())
			cpu.Add(*capacity.Cpu())
		}
		allocatable := machine.Status.InternalNodeStatus.Allocatable
		if allocatable != nil {
			apods.Add(*allocatable.Pods())
			amem.Add(*allocatable.Memory())
			acpu.Add(*allocatable.Cpu())
		}
		requested := machine.Status.Requested
		if requested != nil {
			rpods.Add(*requested.Pods())
			rmem.Add(*requested.Memory())
			rcpu.Add(*requested.Cpu())
		}
		limits := machine.Status.Limits
		if limits != nil {
			lpods.Add(*limits.Pods())
			lmem.Add(*limits.Memory())
			lcpu.Add(*limits.Cpu())
		}

		if condDisk == v1.ConditionTrue && v32.ClusterConditionNoDiskPressure.IsTrue(machine) {
			condDisk = v1.ConditionFalse
		}
		if condMem == v1.ConditionTrue && v32.ClusterConditionNoMemoryPressure.IsTrue(machine) {
			condMem = v1.ConditionFalse
		}
	}

	cluster.Status.Capacity = v1.ResourceList{v1.ResourcePods: pods, v1.ResourceMemory: mem, v1.ResourceCPU: cpu}
	cluster.Status.Allocatable = v1.ResourceList{v1.ResourcePods: apods, v1.ResourceMemory: amem, v1.ResourceCPU: acpu}
	cluster.Status.Requested = v1.ResourceList{v1.ResourcePods: rpods, v1.ResourceMemory: rmem, v1.ResourceCPU: rcpu}
	cluster.Status.Limits = v1.ResourceList{v1.ResourcePods: lpods, v1.ResourceMemory: lmem, v1.ResourceCPU: lcpu}
	if condDisk == v1.ConditionTrue {
		v32.ClusterConditionNoDiskPressure.True(cluster)
	} else {
		v32.ClusterConditionNoDiskPressure.False(cluster)
	}
	if condMem == v1.ConditionTrue {
		v32.ClusterConditionNoMemoryPressure.True(cluster)
	} else {
		v32.ClusterConditionNoMemoryPressure.False(cluster)
	}

	versionChanged := s.updateVersion(cluster)

	if statusChanged(origStatus, &cluster.Status) || versionChanged {
		_, err = s.Clusters.Update(cluster)
		return err
	}

	return nil
}

func (s *StatsAggregator) updateVersion(cluster *v3.Cluster) bool {
	updated := false
	userContext, err := s.ClusterManager.UserContextNoControllers(cluster.Name)
	if err == nil {
		callWithTimeout(func() {
			// This has the tendency to timeout
			version, err := userContext.K8sClient.Discovery().ServerVersion()
			if err == nil {
				isClusterVersionOk := cluster.Status.Version != nil
				isNewVersionOk := version != nil
				if isClusterVersionOk != isNewVersionOk ||
					(isClusterVersionOk && *cluster.Status.Version != *version) {
					cluster.Status.Version = version
					updated = true
				}
			}
		})
	}
	return updated
}

func statusChanged(existingCluster, newCluster *v32.ClusterStatus) bool {
	if !reflect.DeepEqual(existingCluster.Conditions, newCluster.Conditions) {
		return true
	}

	if existingCluster.LinuxWorkerCount != newCluster.LinuxWorkerCount ||
		existingCluster.WindowsWorkerCount != newCluster.WindowsWorkerCount ||
		existingCluster.NodeCount != newCluster.NodeCount {
		return true
	}

	if resourceListChanged(existingCluster.Capacity, newCluster.Capacity) {
		return true
	}

	if resourceListChanged(existingCluster.Allocatable, newCluster.Allocatable) {
		return true
	}

	if resourceListChanged(existingCluster.Requested, newCluster.Requested) {
		return true
	}

	if resourceListChanged(existingCluster.Limits, newCluster.Limits) {
		return true
	}

	return false
}

func resourceListChanged(oldList, newList v1.ResourceList) bool {
	if len(oldList) != len(newList) {
		return true
	}

	for k, v := range oldList {
		if v.Cmp(newList[k]) != 0 {
			return true
		}
	}

	return false
}

func callWithTimeout(do func()) {
	done := make(chan struct{})
	go func() {
		do()
		done <- struct{}{}
	}()

	select {
	case <-done:
	case <-time.After(15 * time.Second):
	}
}

func (s *StatsAggregator) machineChanged(key string, machine *v3.Node) (runtime.Object, error) {
	if machine != nil {
		s.Clusters.Controller().Enqueue("", machine.Namespace)
	}
	return nil, nil
}

func isTaintedNoExecuteNoSchedule(m *v3.Node) bool {
	for _, taint := range m.Spec.InternalNodeSpec.Taints {
		isETCDOrControlPlane := taint.Key == nodeRoleControlPlane || taint.Key == nodeRoleETCD ||
			taint.Key == nodeRoleControlPlaneHyphen || taint.Key == nodeRoleMaster
		if isETCDOrControlPlane && (taint.Effect == "NoSchedule" || taint.Effect == "NoExecute") {
			return true
		}
	}
	return false
}
<commit_msg>Restart agent pods after upgrade to 1.22<commit_after>package clusterstats

import (
	"context"
	"reflect"
	"strconv"
	"time"

	"github.com/pkg/errors"
	v32 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3"
	"github.com/rancher/rancher/pkg/clustermanager"
	v3 "github.com/rancher/rancher/pkg/generated/norman/management.cattle.io/v3"
	"github.com/rancher/rancher/pkg/types/config"
	"github.com/sirupsen/logrus"
	v1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/runtime"
)

const (
	nodeRoleControlPlane       = "node-role.kubernetes.io/controlplane"
	nodeRoleControlPlaneHyphen = "node-role.kubernetes.io/control-plane"
	nodeRoleETCD               = "node-role.kubernetes.io/etcd"
	nodeRoleMaster             = "node-role.kubernetes.io/master"
	agentVersionUpgraded       = "agent.cluster.cattle.io/upgraded-v1.22"
)

type StatsAggregator struct {
	NodesLister    v3.NodeLister
	Clusters       v3.ClusterInterface
	ClusterManager *clustermanager.Manager
}

type ClusterNodeData struct {
	Capacity                        v1.ResourceList
	Allocatable                     v1.ResourceList
	Requested                       v1.ResourceList
	Limits                          v1.ResourceList
	ConditionNoDiskPressureStatus   v1.ConditionStatus
	ConditionNoMemoryPressureStatus v1.ConditionStatus
}

func Register(ctx context.Context, management *config.ManagementContext, clusterManager *clustermanager.Manager) {
	clustersClient := management.Management.Clusters("")
	machinesClient := management.Management.Nodes("")

	s := &StatsAggregator{
		NodesLister:    machinesClient.Controller().Lister(),
		Clusters:       clustersClient,
		ClusterManager: clusterManager,
	}

	clustersClient.AddHandler(ctx, "cluster-stats", s.sync)
	machinesClient.AddHandler(ctx, "cluster-stats", s.machineChanged)
}

func (s *StatsAggregator) sync(key string, cluster *v3.Cluster) (runtime.Object, error) {
	if cluster == nil {
		return nil, nil
	}

	return nil, s.aggregate(cluster, cluster.Name)
}

func (s *StatsAggregator) aggregate(cluster *v3.Cluster, clusterName string) error {
	allMachines, err := s.NodesLister.List(cluster.Name, labels.Everything())
	if err != nil {
		return err
	}

	workerCounts := make(map[string]int)
	var machines []*v3.Node
	// only include worker nodes
	for _, m := range allMachines {
		// if none are set, then nodes syncer has not completed
		if !m.Spec.Worker && !m.Spec.ControlPlane && !m.Spec.Etcd {
			return errors.Errorf("node role cannot be determined because node %s has not finished syncing. retrying...", m.Status.NodeName)
		}
		if isTaintedNoExecuteNoSchedule(m) && !m.Spec.Worker {
			continue
		}
		if m.Spec.InternalNodeSpec.Unschedulable {
			continue
		}

		if os, ok := m.Status.NodeLabels["kubernetes.io/os"]; ok && m.Spec.Worker {
			workerCounts[os]++
		}

		machines = append(machines, m)
	}

	origStatus := cluster.Status.DeepCopy()
	cluster = cluster.DeepCopy()

	cluster.Status.NodeCount = len(allMachines)
	if c, ok := workerCounts["windows"]; ok {
		cluster.Status.WindowsWorkerCount = c
	}
	if c, ok := workerCounts["linux"]; ok {
		cluster.Status.LinuxWorkerCount = c
	}

	// capacity keys
	pods, mem, cpu := resource.Quantity{}, resource.Quantity{}, resource.Quantity{}
	// allocatable keys
	apods, amem, acpu := resource.Quantity{}, resource.Quantity{}, resource.Quantity{}
	// requested keys
	rpods, rmem, rcpu := resource.Quantity{}, resource.Quantity{}, resource.Quantity{}
	// limited keys
	lpods, lmem, lcpu := resource.Quantity{}, resource.Quantity{}, resource.Quantity{}

	condDisk := v1.ConditionTrue
	condMem := v1.ConditionTrue

	for _, machine := range machines {
		capacity := machine.Status.InternalNodeStatus.Capacity
		if capacity != nil {
			pods.Add(*capacity.Pods())
			mem.Add(*capacity.Memory())
			cpu.Add(*capacity.Cpu())
		}
		allocatable := machine.Status.InternalNodeStatus.Allocatable
		if allocatable != nil {
			apods.Add(*allocatable.Pods())
			amem.Add(*allocatable.Memory())
			acpu.Add(*allocatable.Cpu())
		}
		requested := machine.Status.Requested
		if requested != nil {
			rpods.Add(*requested.Pods())
			rmem.Add(*requested.Memory())
			rcpu.Add(*requested.Cpu())
		}
		limits := machine.Status.Limits
		if limits != nil {
			lpods.Add(*limits.Pods())
			lmem.Add(*limits.Memory())
			lcpu.Add(*limits.Cpu())
		}

		if condDisk == v1.ConditionTrue && v32.ClusterConditionNoDiskPressure.IsTrue(machine) {
			condDisk = v1.ConditionFalse
		}
		if condMem == v1.ConditionTrue && v32.ClusterConditionNoMemoryPressure.IsTrue(machine) {
			condMem = v1.ConditionFalse
		}
	}

	cluster.Status.Capacity = v1.ResourceList{v1.ResourcePods: pods, v1.ResourceMemory: mem, v1.ResourceCPU: cpu}
	cluster.Status.Allocatable = v1.ResourceList{v1.ResourcePods: apods, v1.ResourceMemory: amem, v1.ResourceCPU: acpu}
	cluster.Status.Requested = v1.ResourceList{v1.ResourcePods: rpods, v1.ResourceMemory: rmem, v1.ResourceCPU: rcpu}
	cluster.Status.Limits = v1.ResourceList{v1.ResourcePods: lpods, v1.ResourceMemory: lmem, v1.ResourceCPU: lcpu}
	if condDisk == v1.ConditionTrue {
		v32.ClusterConditionNoDiskPressure.True(cluster)
	} else {
		v32.ClusterConditionNoDiskPressure.False(cluster)
	}
	if condMem == v1.ConditionTrue {
		v32.ClusterConditionNoMemoryPressure.True(cluster)
	} else {
		v32.ClusterConditionNoMemoryPressure.False(cluster)
	}

	var oldVersion int
	if cluster.Status.Version != nil {
		oldVersion, err = strconv.Atoi(cluster.Status.Version.Minor)
		if err != nil {
			return err
		}
	}
	versionChanged := s.updateVersion(cluster)

	// If the cluster went through an upgrade from <=1.21 to >=1.22, restart
	// the cluster agent in order to restart controllers that will no longer work
	// with the new API.
	if versionChanged {
		newVersion, err := strconv.Atoi(cluster.Status.Version.Minor)
		if err != nil {
			return err
		}
		if newVersion >= 22 && oldVersion <= 21 {
			err := s.restartAgentDeployment(cluster)
			if err != nil {
				return err
			}
		}
	}

	if statusChanged(origStatus, &cluster.Status) || versionChanged {
		_, err = s.Clusters.Update(cluster)
		return err
	}

	return nil
}

func (s *StatsAggregator) updateVersion(cluster *v3.Cluster) bool {
	updated := false
	userContext, err := s.ClusterManager.UserContextNoControllers(cluster.Name)
	if err == nil {
		callWithTimeout(func() {
			// This has the tendency to timeout
			version, err := userContext.K8sClient.Discovery().ServerVersion()
			if err == nil {
				isClusterVersionOk := cluster.Status.Version != nil
				isNewVersionOk := version != nil
				if isClusterVersionOk != isNewVersionOk ||
					(isClusterVersionOk && *cluster.Status.Version != *version) {
					cluster.Status.Version = version
					updated = true
				}
			}
		})
	}
	return updated
}

// restartAgentDeployment sets an annotation on the cluster agent deployment template
// which will force a restart of the deployment. This is used when the cluster is
// upgraded to >=1.22 to ensure that controllers that are not compatible with v1.22 APIs
// are stopped, and controllers that are only compatible with v1.22 are started.
func (s *StatsAggregator) restartAgentDeployment(cluster *v3.Cluster) error {
	userContext, err := s.ClusterManager.UserContextNoControllers(cluster.Name)
	if err != nil {
		return err
	}
	deployment, err := userContext.Apps.Deployments("cattle-system").Get("cattle-cluster-agent", metav1.GetOptions{})
	if err != nil && !apierrors.IsNotFound(err) {
		return err
	}
	// Add annotation to the agent deployment template in order to force a restart
	if deployment != nil {
		if deployment.Spec.Template.Annotations == nil {
			deployment.Spec.Template.Annotations = make(map[string]string)
		}
		if deployment.Spec.Template.Annotations[agentVersionUpgraded] != "true" {
			logrus.Tracef("statsAggregator: updated cluster %s to v1.22, annotating agent deployment", cluster.Name)
			toUpdate := deployment.DeepCopy()
			toUpdate.Spec.Template.Annotations[agentVersionUpgraded] = "true"
			_, err = userContext.Apps.Deployments("cattle-system").Update(toUpdate)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

func statusChanged(existingCluster, newCluster *v32.ClusterStatus) bool {
	if !reflect.DeepEqual(existingCluster.Conditions, newCluster.Conditions) {
		return true
	}

	if existingCluster.LinuxWorkerCount != newCluster.LinuxWorkerCount ||
		existingCluster.WindowsWorkerCount != newCluster.WindowsWorkerCount ||
		existingCluster.NodeCount != newCluster.NodeCount {
		return true
	}

	if resourceListChanged(existingCluster.Capacity, newCluster.Capacity) {
		return true
	}

	if resourceListChanged(existingCluster.Allocatable, newCluster.Allocatable) {
		return true
	}

	if resourceListChanged(existingCluster.Requested, newCluster.Requested) {
		return true
	}

	if resourceListChanged(existingCluster.Limits, newCluster.Limits) {
		return true
	}

	return false
}

func resourceListChanged(oldList, newList v1.ResourceList) bool {
	if len(oldList) != len(newList) {
		return true
	}

	for k, v := range oldList {
		if v.Cmp(newList[k]) != 0 {
			return true
		}
	}

	return false
}

func callWithTimeout(do func()) {
	done := make(chan struct{})
	go func() {
		do()
		done <- struct{}{}
	}()

	select {
	case <-done:
	case <-time.After(15 * time.Second):
	}
}

func (s *StatsAggregator) machineChanged(key string, machine *v3.Node) (runtime.Object, error) {
	if machine != nil {
		s.Clusters.Controller().Enqueue("", machine.Namespace)
	}
	return nil, nil
}

func isTaintedNoExecuteNoSchedule(m *v3.Node) bool {
	for _, taint := range m.Spec.InternalNodeSpec.Taints {
		isETCDOrControlPlane := taint.Key == nodeRoleControlPlane || taint.Key == nodeRoleETCD ||
			taint.Key == nodeRoleControlPlaneHyphen || taint.Key == nodeRoleMaster
		if isETCDOrControlPlane && (taint.Effect == "NoSchedule" || taint.Effect == "NoExecute") {
			return true
		}
	}
	return false
}
<|endoftext|>


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

package iptables

import (
	"bytes"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/projectcalico/felix/go/felix/set"
	"os/exec"
	"reflect"
	"regexp"
	"strings"
)

const (
	MaxChainNameLength = 28
)

var (
	chainCreateRegexp = regexp.MustCompile(`^:(\S+)`)
	appendRegexp      = regexp.MustCompile(`^-A (\S+)`)
)

// Table represents a single one of the iptables tables i.e. "raw", "nat", "filter", etc.
type Table struct {
	Name string

	// TODO(smc) Implement rule insertions to hook root chains.
	// chainToInsertedFragments maps from chain name to a list of iptables fragments that should
	// be inserted into that chain.  Used for hooking top-level chains.
	chainToInsertedFragments map[string][]string
	dirtyInserts             set.Set
	// chainToRemovedFragments maps from chain name to a list of iptables fragments that should
	// be removed from that chain.  Used for unhooking top-level chains.  We need to track
	// removals forever in case another process reinstates an old rule by mistake.  Hence,
	// only suitable for a static set of top-level rules.
	chainToRemovedFragments map[string][]string
	dirtyRemoves            set.Set
	// chainToRuleFragments contains the desired state of our iptables chains, indexed by
	// chain name.  The values are slices of iptables fragments, such as
	// "--match foo --jump DROP" (i.e. omitting the action and chain name, which are calculated
	// as needed).
	chainNameToChain map[string]*Chain
	dirtyChains      set.Set

	inSyncWithDataPlane bool
	// chainToDataplaneHashes contains the rule hashes that we think are in the dataplane.
	// it is updated when we write to the dataplane but it can also be read back and compared
	// to what we calculate from chainToContents.
	chainToDataplaneHashes map[string][]string

	logCxt *log.Entry

	chainPrefixes   []string
	hashPrefix      string
	commentRegexp   *regexp.Regexp
	ourChainsRegexp *regexp.Regexp

	restoreCmd  string
	saveCmd     string
	iptablesCmd string
}

func NewTable(name string, ipVersion uint8, chainPrefixes []string, hashPrefix string) *Table {
	hashCommentRegexp := regexp.MustCompile(`--comment "?` + hashPrefix + `([a-zA-Z0-9_-]+)"?`)
	ourChainsPattern := "^(" + strings.Join(chainPrefixes, "|") + ")"
	ourChainsRegexp := regexp.MustCompile(ourChainsPattern)
	table := &Table{
		Name: name,
		chainToInsertedFragments: map[string][]string{},
		dirtyInserts:             set.New(),
		chainToRemovedFragments:  map[string][]string{},
		dirtyRemoves:             set.New(),
		chainNameToChain:         map[string]*Chain{},
		dirtyChains:              set.New(),
		chainToDataplaneHashes:   map[string][]string{},
		logCxt: log.WithFields(log.Fields{
			"ipVersion": ipVersion,
			"table":     name,
		}),
		chainPrefixes:   chainPrefixes,
		hashPrefix:      hashPrefix,
		commentRegexp:   hashCommentRegexp,
		ourChainsRegexp: ourChainsRegexp,
	}

	if ipVersion == 4 {
		table.restoreCmd = "iptables-restore"
		table.saveCmd = "iptables-save"
		table.iptablesCmd = "iptables"
	} else {
		table.restoreCmd = "ip6tables-restore"
		table.saveCmd = "ip6tables-save"
		table.iptablesCmd = "ip6tables"
	}
	return table
}

func (t *Table) UpdateChain(chain *Chain) {
	t.chainNameToChain[chain.Name] = chain
	t.dirtyChains.Add(chain.Name)
}

func (t *Table) RemoveChain(name string) {
	delete(t.chainNameToChain, name)
	t.dirtyChains.Add(name)
}

func (t *Table) loadDataplaneState() {
	// Load the hashes from the dataplane.
	dataplaneHashes := t.getHashesFromDataplane()

	// Check that the rules we think we've programmed are still there and mark any inconsistent
	// chains for refresh.
	t.logCxt.Info("Scanning for out-of-sync iptables chains")
	for chainName, expectedHashes := range t.chainToDataplaneHashes {
		if !reflect.DeepEqual(dataplaneHashes[chainName], expectedHashes) {
			t.logCxt.WithField("chainName", chainName).Warn("Detected out-of-sync iptables chain, marking for resync")
			t.dirtyChains.Add("chainName")
		}
	}

	// Now scan for chains that shouldn't be there and mark for deletion.
	t.logCxt.Info("Scanning for unexpected iptables chains")
	for chainName := range dataplaneHashes {
		if !t.ourChainsRegexp.MatchString(chainName) {
			// Skip non-felix chain
			t.logCxt.WithField("chainName", chainName).Debug("Skipping non-calico chain")
			continue
		}
		if _, ok := t.chainToDataplaneHashes[chainName]; ok {
			// Chain expected, we'll have checked its contents above.
			t.logCxt.WithField("chainName", chainName).Debug("Skipping expected chain")
			continue
		}
		// Chain exists in dataplane but not in memory, mark as dirty so we'll clean it up.
		t.logCxt.WithField("chainName", chainName).Info("Found unexpected chain, marking for cleanup")
		t.dirtyChains.Add(chainName)
	}

	t.logCxt.Info("Done scanning, in sync with dataplane")
	t.chainToDataplaneHashes = dataplaneHashes
}

// getHashesFromDataplane loads the current state of our table and parses out the hashes that we
// add to rules.  It returns a map with an entry for each chain in the table.  Each entry is a slice
// containing the hashes for the rules in that table.  Rules with no hashes are represented by
// an empty string.
func (t *Table) getHashesFromDataplane() map[string][]string {
	cmd := exec.Command(t.saveCmd, "-t", t.Name)
	output, err := cmd.Output()
	if err != nil {
		log.WithError(err).Panic("iptables save failed")
	}
	buf := bytes.NewBuffer(output)
	newHashes := map[string][]string{}
	for {
		// Read the next line of the output.
		line, err := buf.ReadString('\n')
		if err != nil { // EOF
			break
		}

		// Look for lines of the form ":chain-name - [0:0]", which are forward declarations
		// for (possibly empty) chains.
		logCxt := log.WithField("line", line)
		logCxt.Debug("Parsing line")
		captures := chainCreateRegexp.FindStringSubmatch(line)
		if captures != nil {
			// Chain forward-reference, make sure the chain exists.
			chainName := captures[1]
			logCxt.WithField("chainName", chainName).Debug("Found forward-reference")
			newHashes[chainName] = []string{}
			continue
		}

		// Look for append lines, such as "-A chain-name -m foo --foo bar"; these are the
		// actual rules.
		captures = appendRegexp.FindStringSubmatch(line)
		if captures == nil {
			// Skip any non-append lines.
			logCxt.Debug("Not an append, skipping")
			continue
		}
		chainName := captures[1]

		// Look for one of our hashes on the rule.  We record a zero hash for unknown rules
		// so that they get cleaned up.
		hash := ""
		captures = t.commentRegexp.FindStringSubmatch(line)
		if captures != nil {
			hash = captures[1]
			logCxt.WithField("hash", hash).Debug("Found felix hash")
		}
		newHashes[chainName] = append(newHashes[chainName], hash)
	}
	log.WithField("newHashes", newHashes).Debug("Read hashes from dataplane")
	return newHashes
}

func (t *Table) Apply() {
	// Retry until we succeed.  There are several reasons that updating iptables may fail:
	// - a concurrent write may invalidate iptables-restore's compare-and-swap
	// - another process may have clobbered some of our state, resulting in inconsistencies
	//   in what we try to program.
	for {
		if !t.inSyncWithDataPlane {
			// We have reason to believe that our picture of the dataplane is out of
			// sync.  Refresh it.  This may mark more chains as dirty.
			t.logCxt.Info("Out-of-sync with iptables, loading current state")
			t.loadDataplaneState()
		}

		if err := t.flushUpdates(); err != nil {
			log.WithError(err).Warn("Failed to program iptables, will retry")
			continue
		}
		break
	}
}

func (t *Table) flushUpdates() error {
	var inputBuf bytes.Buffer
	// iptables-restore input starts with a line indicating the table name.
	inputBuf.WriteString(fmt.Sprintf("*%s\n", t.Name))

	// Make a pass over the dirty chains and generate a forward reference for any that need to
	// be created.
	t.dirtyChains.Iter(func(item interface{}) error {
		chainName := item.(string)
		chainNeedsToBeFlushed := false
		if _, ok := t.chainNameToChain[chainName]; !ok {
			// About to delete this chain, flush it first to sever dependencies.
			chainNeedsToBeFlushed = true
		} else if _, ok := t.chainToDataplaneHashes[chainName]; !ok {
			// Chain doesn't exist in dataplane, mark it for creation.
			chainNeedsToBeFlushed = true
		}
		if chainNeedsToBeFlushed {
			inputBuf.WriteString(fmt.Sprintf(":%s - -\n", chainName))
		}
		return nil
	})

	// Make a second pass over the dirty chains.  This time, we write out the rule changes.
	newHashes := map[string][]string{}
	t.dirtyChains.Iter(func(item interface{}) error {
		chainName := item.(string)
		if chain, ok := t.chainNameToChain[chainName]; !ok {
			// Chain deletion
			// TODO(smc) Might need to do deletions in a second pass because can fail if someone else is referencing our chain.
			inputBuf.WriteString(fmt.Sprintf("--delete-chain %s\n", chainName))
			newHashes[chainName] = nil
		} else {
			// Chain update or creation.  Scan the chain against its previous hashes
			// and replace/append/delete as appropriate.
			previousHashes := t.chainToDataplaneHashes[chainName]
			currentHashes := chain.RuleHashes()
			newHashes[chainName] = currentHashes
			for i := 0; i < len(previousHashes) || i < len(currentHashes); i++ {
				var line string
				if i < len(previousHashes) && i < len(currentHashes) {
					if previousHashes[i] != currentHashes[i] {
						// Hash doesn't match, replace the rule.
						ruleNum := i + 1 // 1-indexed.
						comment := t.commentFrag(currentHashes[i])
						line = fmt.Sprintf("-R %s %d %s %s %s\n", chainName, ruleNum, comment,
							chain.Rules[i].MatchCriteria, chain.Rules[i].Action)
					}
				} else if i < len(previousHashes) {
					// previousHashes was longer, remove the old rules from the end.
					ruleNum := len(currentHashes) + 1 // 1-indexed
					line = fmt.Sprintf("-D %s %d\n", chainName, ruleNum)
				} else {
					// currentHashes was longer.  Append.
					comment := t.commentFrag(currentHashes[i])
					line = fmt.Sprintf("-A %s %s %s %s\n", chainName, comment,
						chain.Rules[i].MatchCriteria, chain.Rules[i].Action)
				}
				inputBuf.WriteString(line)
			}
		}
		return nil
	})

	// iptables-restore input ends with a COMMIT.
	inputBuf.WriteString("COMMIT\n")

	// Actually execute iptables-restore.
	if log.GetLevel() >= log.DebugLevel {
		log.WithField("iptablesInput", inputBuf.String()).Debug("Writing to iptables")
	}
	var outputBuf, errBuf bytes.Buffer
	cmd := exec.Command(t.restoreCmd, "--noflush", "--verbose")
	cmd.Stdin = &inputBuf
	cmd.Stdout = &outputBuf
	cmd.Stderr = &errBuf
	err := cmd.Run()
	if err != nil {
		log.WithFields(log.Fields{
			"output":      outputBuf.String(),
			"errorOutput": errBuf.String(),
			"error":       err,
		}).Warn("Failed to execute iptable restore command")
		t.inSyncWithDataPlane = false
		return err
	}
	// Clear the dirty set.
	t.dirtyChains = set.New()

	// Store off the updates.
	for chainName, hashes := range newHashes {
		if hashes == nil {
			delete(t.chainToDataplaneHashes, chainName)
		} else {
			t.chainToDataplaneHashes[chainName] = hashes
		}
	}
	return nil
}

func (t *Table) commentFrag(hash string) string {
	return fmt.Sprintf(`-m comment --comment "%s%s"`, t.hashPrefix, hash)
}
<commit_msg>Minor clean-ups.<commit_after>// Copyright (c) 2016 Tigera, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package iptables

import (
	"bytes"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/projectcalico/felix/go/felix/set"
	"os/exec"
	"reflect"
	"regexp"
	"strings"
)

const (
	MaxChainNameLength = 28
)

var (
	chainCreateRegexp = regexp.MustCompile(`^:(\S+)`)
	appendRegexp      = regexp.MustCompile(`^-A (\S+)`)
)

// Table represents a single one of the iptables tables i.e. "raw", "nat", "filter", etc.
type Table struct {
	Name string

	// TODO(smc) Implement rule insertions to hook root chains.
	// chainToInsertedFragments maps from chain name to a list of iptables fragments that should
	// be inserted into that chain.  Used for hooking top-level chains.
	chainToInsertedFragments map[string][]string
	dirtyInserts             set.Set
	// chainToRemovedFragments maps from chain name to a list of iptables fragments that should
	// be removed from that chain.  Used for unhooking top-level chains.  We need to track
	// removals forever in case another process reinstates an old rule by mistake.  Hence,
	// only suitable for a static set of top-level rules.
	chainToRemovedFragments map[string][]string
	dirtyRemoves            set.Set
	// chainToRuleFragments contains the desired state of our iptables chains, indexed by
	// chain name.  The values are slices of iptables fragments, such as
	// "--match foo --jump DROP" (i.e. omitting the action and chain name, which are calculated
	// as needed).
	chainNameToChain map[string]*Chain
	dirtyChains      set.Set

	inSyncWithDataPlane bool

	// chainToDataplaneHashes contains the rule hashes that we think are in the dataplane.
	// it is updated when we write to the dataplane but it can also be read back and compared
	// to what we calculate from chainToContents.
	chainToDataplaneHashes map[string][]string

	// hashCommentPrefix holds the prefix that we prepend to our rule-tracking hashes.
	hashCommentPrefix string
	// hashCommentRegexp matches the rule-tracking comment, capturing the rule hash.
	hashCommentRegexp *regexp.Regexp
	// ourChainsRegexp matches the names of chains that are "ours", i.e. start with one of our
	// prefixes.
	ourChainsRegexp *regexp.Regexp

	restoreCmd  string
	saveCmd     string
	iptablesCmd string

	logCxt *log.Entry
}

func NewTable(name string, ipVersion uint8, chainPrefixes []string, hashPrefix string) *Table {
	hashCommentRegexp := regexp.MustCompile(`--comment "?` + hashPrefix + `([a-zA-Z0-9_-]+)"?`)
	ourChainsPattern := "^(" + strings.Join(chainPrefixes, "|") + ")"
	ourChainsRegexp := regexp.MustCompile(ourChainsPattern)
	table := &Table{
		Name: name,
		chainToInsertedFragments: map[string][]string{},
		dirtyInserts:             set.New(),
		chainToRemovedFragments:  map[string][]string{},
		dirtyRemoves:             set.New(),
		chainNameToChain:         map[string]*Chain{},
		dirtyChains:              set.New(),
		chainToDataplaneHashes:   map[string][]string{},
		logCxt: log.WithFields(log.Fields{
			"ipVersion": ipVersion,
			"table":     name,
		}),
		hashCommentPrefix: hashPrefix,
		hashCommentRegexp: hashCommentRegexp,
		ourChainsRegexp:   ourChainsRegexp,
	}

	if ipVersion == 4 {
		table.restoreCmd = "iptables-restore"
		table.saveCmd = "iptables-save"
		table.iptablesCmd = "iptables"
	} else {
		table.restoreCmd = "ip6tables-restore"
		table.saveCmd = "ip6tables-save"
		table.iptablesCmd = "ip6tables"
	}
	return table
}

func (t *Table) UpdateChain(chain *Chain) {
	t.chainNameToChain[chain.Name] = chain
	t.dirtyChains.Add(chain.Name)
}

func (t *Table) RemoveChain(name string) {
	delete(t.chainNameToChain, name)
	t.dirtyChains.Add(name)
}

func (t *Table) loadDataplaneState() {
	// Load the hashes from the dataplane.
	dataplaneHashes := t.getHashesFromDataplane()

	// Check that the rules we think we've programmed are still there and mark any inconsistent
	// chains for refresh.
	t.logCxt.Info("Scanning for out-of-sync iptables chains")
	for chainName, expectedHashes := range t.chainToDataplaneHashes {
		if !reflect.DeepEqual(dataplaneHashes[chainName], expectedHashes) {
			t.logCxt.WithField("chainName", chainName).Warn("Detected out-of-sync iptables chain, marking for resync")
			t.dirtyChains.Add("chainName")
		}
	}

	// Now scan for chains that shouldn't be there and mark for deletion.
	t.logCxt.Info("Scanning for unexpected iptables chains")
	for chainName := range dataplaneHashes {
		if !t.ourChainsRegexp.MatchString(chainName) {
			// Skip non-felix chain
			t.logCxt.WithField("chainName", chainName).Debug("Skipping non-calico chain")
			continue
		}
		if _, ok := t.chainToDataplaneHashes[chainName]; ok {
			// Chain expected, we'll have checked its contents above.
			t.logCxt.WithField("chainName", chainName).Debug("Skipping expected chain")
			continue
		}
		// Chain exists in dataplane but not in memory, mark as dirty so we'll clean it up.
		t.logCxt.WithField("chainName", chainName).Info("Found unexpected chain, marking for cleanup")
		t.dirtyChains.Add(chainName)
	}

	t.logCxt.Info("Done scanning, in sync with dataplane")
	t.chainToDataplaneHashes = dataplaneHashes
}

// getHashesFromDataplane loads the current state of our table and parses out the hashes that we
// add to rules.  It returns a map with an entry for each chain in the table.  Each entry is a slice
// containing the hashes for the rules in that table.  Rules with no hashes are represented by
// an empty string.
func (t *Table) getHashesFromDataplane() map[string][]string {
	cmd := exec.Command(t.saveCmd, "-t", t.Name)
	output, err := cmd.Output()
	if err != nil {
		log.WithError(err).Panic("iptables save failed")
	}
	buf := bytes.NewBuffer(output)
	newHashes := map[string][]string{}
	for {
		// Read the next line of the output.
		line, err := buf.ReadString('\n')
		if err != nil { // EOF
			break
		}

		// Look for lines of the form ":chain-name - [0:0]", which are forward declarations
		// for (possibly empty) chains.
		logCxt := log.WithField("line", line)
		logCxt.Debug("Parsing line")
		captures := chainCreateRegexp.FindStringSubmatch(line)
		if captures != nil {
			// Chain forward-reference, make sure the chain exists.
			chainName := captures[1]
			logCxt.WithField("chainName", chainName).Debug("Found forward-reference")
			newHashes[chainName] = []string{}
			continue
		}

		// Look for append lines, such as "-A chain-name -m foo --foo bar"; these are the
		// actual rules.
		captures = appendRegexp.FindStringSubmatch(line)
		if captures == nil {
			// Skip any non-append lines.
			logCxt.Debug("Not an append, skipping")
			continue
		}
		chainName := captures[1]

		// Look for one of our hashes on the rule.  We record a zero hash for unknown rules
		// so that they get cleaned up.
		hash := ""
		captures = t.hashCommentRegexp.FindStringSubmatch(line)
		if captures != nil {
			hash = captures[1]
			logCxt.WithField("hash", hash).Debug("Found felix hash")
		}
		newHashes[chainName] = append(newHashes[chainName], hash)
	}
	log.WithField("newHashes", newHashes).Debug("Read hashes from dataplane")
	return newHashes
}

func (t *Table) Apply() {
	// Retry until we succeed.  There are several reasons that updating iptables may fail:
	// - a concurrent write may invalidate iptables-restore's compare-and-swap
	// - another process may have clobbered some of our state, resulting in inconsistencies
	//   in what we try to program.
	for {
		if !t.inSyncWithDataPlane {
			// We have reason to believe that our picture of the dataplane is out of
			// sync.  Refresh it.  This may mark more chains as dirty.
			t.logCxt.Info("Out-of-sync with iptables, loading current state")
			t.loadDataplaneState()
		}

		if err := t.flushUpdates(); err != nil {
			log.WithError(err).Warn("Failed to program iptables, will retry")
			continue
		}
		break
	}
}

func (t *Table) flushUpdates() error {
	var inputBuf bytes.Buffer
	// iptables-restore input starts with a line indicating the table name.
	inputBuf.WriteString(fmt.Sprintf("*%s\n", t.Name))

	// Make a pass over the dirty chains and generate a forward reference for any that need to
	// be created.
	t.dirtyChains.Iter(func(item interface{}) error {
		chainName := item.(string)
		chainNeedsToBeFlushed := false
		if _, ok := t.chainNameToChain[chainName]; !ok {
			// About to delete this chain, flush it first to sever dependencies.
			chainNeedsToBeFlushed = true
		} else if _, ok := t.chainToDataplaneHashes[chainName]; !ok {
			// Chain doesn't exist in dataplane, mark it for creation.
			chainNeedsToBeFlushed = true
		}
		if chainNeedsToBeFlushed {
			inputBuf.WriteString(fmt.Sprintf(":%s - -\n", chainName))
		}
		return nil
	})

	// Make a second pass over the dirty chains.  This time, we write out the rule changes.
	newHashes := map[string][]string{}
	t.dirtyChains.Iter(func(item interface{}) error {
		chainName := item.(string)
		if chain, ok := t.chainNameToChain[chainName]; !ok {
			// Chain deletion
			// TODO(smc) Might need to do deletions in a second pass because can fail if someone else is referencing our chain.
			inputBuf.WriteString(fmt.Sprintf("--delete-chain %s\n", chainName))
			newHashes[chainName] = nil
		} else {
			// Chain update or creation.  Scan the chain against its previous hashes
			// and replace/append/delete as appropriate.
			previousHashes := t.chainToDataplaneHashes[chainName]
			currentHashes := chain.RuleHashes()
			newHashes[chainName] = currentHashes
			for i := 0; i < len(previousHashes) || i < len(currentHashes); i++ {
				var line string
				if i < len(previousHashes) && i < len(currentHashes) {
					if previousHashes[i] != currentHashes[i] {
						// Hash doesn't match, replace the rule.
						ruleNum := i + 1 // 1-indexed.
						comment := t.commentFrag(currentHashes[i])
						line = fmt.Sprintf("-R %s %d %s %s %s\n", chainName, ruleNum, comment,
							chain.Rules[i].MatchCriteria, chain.Rules[i].Action)
					}
				} else if i < len(previousHashes) {
					// previousHashes was longer, remove the old rules from the end.
					ruleNum := len(currentHashes) + 1 // 1-indexed
					line = fmt.Sprintf("-D %s %d\n", chainName, ruleNum)
				} else {
					// currentHashes was longer.  Append.
					comment := t.commentFrag(currentHashes[i])
					line = fmt.Sprintf("-A %s %s %s %s\n", chainName, comment,
						chain.Rules[i].MatchCriteria, chain.Rules[i].Action)
				}
				inputBuf.WriteString(line)
			}
		}
		return nil
	})

	// iptables-restore input ends with a COMMIT.
	inputBuf.WriteString("COMMIT\n")

	// Actually execute iptables-restore.
	if log.GetLevel() >= log.DebugLevel {
		log.WithField("iptablesInput", inputBuf.String()).Debug("Writing to iptables")
	}
	var outputBuf, errBuf bytes.Buffer
	cmd := exec.Command(t.restoreCmd, "--noflush", "--verbose")
	cmd.Stdin = &inputBuf
	cmd.Stdout = &outputBuf
	cmd.Stderr = &errBuf
	err := cmd.Run()
	if err != nil {
		log.WithFields(log.Fields{
			"output":      outputBuf.String(),
			"errorOutput": errBuf.String(),
			"error":       err,
		}).Warn("Failed to execute iptable restore command")
		t.inSyncWithDataPlane = false
		return err
	}
	// Clear the dirty set.
	t.dirtyChains = set.New()

	// Store off the updates.
	for chainName, hashes := range newHashes {
		if hashes == nil {
			delete(t.chainToDataplaneHashes, chainName)
		} else {
			t.chainToDataplaneHashes[chainName] = hashes
		}
	}
	return nil
}

func (t *Table) commentFrag(hash string) string {
	return fmt.Sprintf(`-m comment --comment "%s%s"`, t.hashCommentPrefix, hash)
}
<|endoftext|>


<commit_before>package internal

import (
	"github.com/influxdata/influxdb/v2/models"
	"github.com/influxdata/influxdb/v2/pkg/bloom"
	"github.com/influxdata/influxdb/v2/pkg/estimator"
	"github.com/influxdata/influxdb/v2/tsdb"
	"github.com/influxdata/influxdb/v2/tsdb/index/tsi1"
)

// File is a mock implementation of a tsi1.File.
type File struct {
	Closef                       func() error
	Pathf                        func() string
	IDf                          func() int
	Levelf                       func() int
	Measurementf                 func(name []byte) tsi1.MeasurementElem
	MeasurementIteratorf         func() tsi1.MeasurementIterator
	HasSeriesf                   func(name []byte, tags models.Tags, buf []byte) (exists, tombstoned bool)
	TagKeyf                      func(name, key []byte) tsi1.TagKeyElem
	TagKeyIteratorf              func(name []byte) tsi1.TagKeyIterator
	TagValuef                    func(name, key, value []byte) tsi1.TagValueElem
	TagValueIteratorf            func(name, key []byte) tsi1.TagValueIterator
	SeriesIDIteratorf            func() tsdb.SeriesIDIterator
	MeasurementSeriesIDIteratorf func(name []byte) tsdb.SeriesIDIterator
	TagKeySeriesIDIteratorf      func(name, key []byte) tsdb.SeriesIDIterator
	TagValueSeriesIDIteratorf    func(name, key, value []byte) tsdb.SeriesIDIterator
	MergeSeriesSketchesf         func(s, t estimator.Sketch) error
	MergeMeasurementsSketchesf   func(s, t estimator.Sketch) error
	Retainf                      func()
	Releasef                     func()
	Filterf                      func() *bloom.Filter
}

func (f *File) Close() error                                  { return f.Closef() }
func (f *File) Path() string                                  { return f.Pathf() }
func (f *File) ID() int                                       { return f.IDf() }
func (f *File) Level() int                                    { return f.Levelf() }
func (f *File) Measurement(name []byte) tsi1.MeasurementElem  { return f.Measurementf(name) }
func (f *File) MeasurementIterator() tsi1.MeasurementIterator { return f.MeasurementIteratorf() }
func (f *File) HasSeries(name []byte, tags models.Tags, buf []byte) (exists, tombstoned bool) {
	return f.HasSeriesf(name, tags, buf)
}
func (f *File) TagKey(name, key []byte) tsi1.TagKeyElem        { return f.TagKeyf(name, key) }
func (f *File) TagKeyIterator(name []byte) tsi1.TagKeyIterator { return f.TagKeyIteratorf(name) }

func (f *File) TagValue(name, key, value []byte) tsi1.TagValueElem {
	return f.TagValuef(name, key, value)
}
func (f *File) TagValueIterator(name, key []byte) tsi1.TagValueIterator {
	return f.TagValueIteratorf(name, key)
}
func (f *File) SeriesIDIterator() tsdb.SeriesIDIterator { return f.SeriesIDIteratorf() }
func (f *File) MeasurementSeriesIDIterator(name []byte) tsdb.SeriesIDIterator {
	return f.MeasurementSeriesIDIteratorf(name)
}
func (f *File) TagKeySeriesIDIterator(name, key []byte) tsdb.SeriesIDIterator {
	return f.TagKeySeriesIDIteratorf(name, key)
}
func (f *File) TagValueSeriesIDIterator(name, key, value []byte) tsdb.SeriesIDIterator {
	return f.TagValueSeriesIDIteratorf(name, key, value)
}
func (f *File) MergeSeriesSketches(s, t estimator.Sketch) error { return f.MergeSeriesSketchesf(s, t) }
func (f *File) MergeMeasurementsSketches(s, t estimator.Sketch) error {
	return f.MergeMeasurementsSketchesf(s, t)
}
func (f *File) Retain()               { f.Retainf() }
func (f *File) Release()              { f.Releasef() }
func (f *File) Filter() *bloom.Filter { return f.Filterf() }
<commit_msg>chore: remove unreferenced tsdb/index/internal package<commit_after><|endoftext|>


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

package installer

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"

	"github.com/tsuru/tsuru/cmd"

	"gopkg.in/check.v1"
)

func (s *S) TestParseConfigDefaultConfig(c *check.C) {
	dmConfig, err := parseConfigFile("")
	c.Assert(err, check.IsNil)
	c.Assert(dmConfig, check.DeepEquals, defaultTsuruInstallConfig)
}

func (s *S) TestParseConfigFileNotExists(c *check.C) {
	_, err := parseConfigFile("not-exist-conf.yml")
	c.Assert(err, check.NotNil)
}

func (s *S) TestParseConfigFile(c *check.C) {
	conf := `
name: tsuru-test
hosts:
    components:
        quantity: 2
    pool:
        quantity: 1
        dedicated: true
ca-path: /tmp/certs
driver:
    name: amazonec2
    options:
        opt1: option1-value
`
	err := ioutil.WriteFile("/tmp/config.yml", []byte(conf), 0644)
	if err != nil {
		c.Fatal("Failed to write config file for test")
	}
	defer os.Remove("/tmp/config.yml")
	expected := &TsuruInstallConfig{
		DockerMachineConfig: &DockerMachineConfig{
			DriverName: "amazonec2",
			DriverOpts: map[string]interface{}{
				"opt1": "option1-value",
			},
			CAPath: "/tmp/certs",
			Name:   "tsuru-test",
		},
		ComponentsHosts: 2,
		PoolHosts:       1,
		DedicatedPool:   true,
	}
	dmConfig, err := parseConfigFile("/tmp/config.yml")
	c.Assert(err, check.IsNil)
	c.Assert(dmConfig, check.DeepEquals, expected)
}

func (s *S) TestInstallInfo(c *check.C) {
	c.Assert((&Install{}).Info(), check.NotNil)
}

func (s *S) TestInstallCommandFlags(c *check.C) {
	command := Install{}
	flags := command.Flags()
	c.Assert(flags, check.NotNil)
	flags.Parse(true, []string{"-c", "my-conf.yml"})
	config := flags.Lookup("c")
	usage := "Configuration file"
	c.Check(config, check.NotNil)
	c.Check(config.Name, check.Equals, "c")
	c.Check(config.Usage, check.Equals, usage)
	c.Check(config.Value.String(), check.Equals, "my-conf.yml")
	c.Check(config.DefValue, check.Equals, "")
	config = flags.Lookup("config")
	c.Check(config, check.NotNil)
	c.Check(config.Name, check.Equals, "config")
	c.Check(config.Usage, check.Equals, usage)
	c.Check(config.Value.String(), check.Equals, "my-conf.yml")
	c.Check(config.DefValue, check.Equals, "")
}

func (s *S) TestInstallTargetAlreadyExists(c *check.C) {
	var stdout, stderr bytes.Buffer
	manager := cmd.BuildBaseManager("uninstall-client", "0.0.0", "", nil)
	client := cmd.NewClient(&http.Client{}, nil, manager)
	context := cmd.Context{
		Args:   []string{"test", fmt.Sprintf("%s:8080", "1.2.3.4")},
		Stdout: &stdout,
		Stderr: &stderr,
	}
	targetadd := manager.Commands["target-add"]
	t, ok := targetadd.(cmd.FlaggedCommand)
	c.Assert(ok, check.Equals, true)
	err := t.Flags().Parse(true, []string{"-s"})
	c.Assert(err, check.IsNil)
	err = t.Run(&context, client)
	c.Assert(err, check.IsNil)
	defer func(manager *cmd.Manager) {
		c := cmd.NewClient(&http.Client{}, nil, manager)
		cont := cmd.Context{
			Args:   []string{"test"},
			Stdout: os.Stdout,
			Stderr: os.Stderr,
		}
		targetrm := manager.Commands["target-remove"]
		targetrm.Run(&cont, c)
	}(manager)
	command := Install{}
	command.Flags().Parse(true, []string{"-c", "./testdata/wrong-conf.yml"})
	context = cmd.Context{
		Stdout: &stdout,
		Stderr: &stderr,
	}
	expectedErr := "pre-install checks failed: tsuru target \"test\" already exists"
	err = command.Run(&context, client)
	c.Assert(err, check.NotNil)
	c.Assert(expectedErr, check.Equals, err.Error())
}

func (s *S) TestUninstallInfo(c *check.C) {
	c.Assert((&Uninstall{}).Info(), check.NotNil)
}

func (s *S) TestUninstallCommandFlags(c *check.C) {
	command := Uninstall{}
	flags := command.Flags()
	c.Assert(flags, check.NotNil)
	flags.Parse(true, []string{"-c", "my-conf.yml"})
	config := flags.Lookup("c")
	usage := "Configuration file"
	c.Check(config, check.NotNil)
	c.Check(config.Name, check.Equals, "c")
	c.Check(config.Usage, check.Equals, usage)
	c.Check(config.Value.String(), check.Equals, "my-conf.yml")
	c.Check(config.DefValue, check.Equals, "")
	config = flags.Lookup("config")
	c.Check(config, check.NotNil)
	c.Check(config.Name, check.Equals, "config")
	c.Check(config.Usage, check.Equals, usage)
	c.Check(config.Value.String(), check.Equals, "my-conf.yml")
	c.Check(config.DefValue, check.Equals, "")
}

func (s *S) TestBuildClusterTable(c *check.C) {
	cluster := &FakeServiceCluster{}
	table := buildClusterTable(cluster)
	expected := `+-----------+---------+---------+
| IP        | State   | Manager |
+-----------+---------+---------+
| 127.0.0.1 | running | true    |
+-----------+---------+---------+
`
	c.Assert(table.String(), check.Equals, expected)
}

func (s *S) TestBuildComponentsTable(c *check.C) {
	cluster := &FakeServiceCluster{}
	table := buildComponentsTable([]TsuruComponent{&MongoDB{}}, cluster)
	expected := `+-----------+-------+----------+
| Component | Ports | Replicas |
+-----------+-------+----------+
| MongoDB   | 8080  | 1        |
+-----------+-------+----------+
`
	c.Assert(table.String(), check.Equals, expected)
}

type FakeMachineProvisioner struct {
	hostsProvisioned int
}

func (p *FakeMachineProvisioner) ProvisionMachines(hosts int, ports []string) ([]*Machine, error) {
	var machines []*Machine
	for i := 0; i < hosts; i++ {
		p.hostsProvisioned = p.hostsProvisioned + 1
		machines = append(machines, &Machine{})
	}
	return machines, nil
}

func (s *S) TestProvisionPool(c *check.C) {
	tt := []struct {
		poolHosts           int
		dedicatedPool       bool
		machines            []*Machine
		expectedProvisioned int
	}{
		{1, false, []*Machine{&Machine{}}, 0},
		{2, false, []*Machine{&Machine{}}, 1},
		{1, true, []*Machine{&Machine{}}, 1},
		{2, true, []*Machine{&Machine{}, &Machine{}}, 2},
		{3, true, []*Machine{&Machine{}}, 3},
	}
	for _, t := range tt {
		p := &FakeMachineProvisioner{}
		config := &TsuruInstallConfig{PoolHosts: t.poolHosts, DedicatedPool: t.dedicatedPool}
		_, err := ProvisionPool(p, config, t.machines)
		c.Assert(err, check.IsNil)
		c.Assert(p.hostsProvisioned, check.Equals, t.expectedProvisioned)
	}
}
<commit_msg>installer: fix gofmt<commit_after>// Copyright 2016 tsuru-client authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package installer

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"

	"github.com/tsuru/tsuru/cmd"

	"gopkg.in/check.v1"
)

func (s *S) TestParseConfigDefaultConfig(c *check.C) {
	dmConfig, err := parseConfigFile("")
	c.Assert(err, check.IsNil)
	c.Assert(dmConfig, check.DeepEquals, defaultTsuruInstallConfig)
}

func (s *S) TestParseConfigFileNotExists(c *check.C) {
	_, err := parseConfigFile("not-exist-conf.yml")
	c.Assert(err, check.NotNil)
}

func (s *S) TestParseConfigFile(c *check.C) {
	conf := `
name: tsuru-test
hosts:
    components:
        quantity: 2
    pool:
        quantity: 1
        dedicated: true
ca-path: /tmp/certs
driver:
    name: amazonec2
    options:
        opt1: option1-value
`
	err := ioutil.WriteFile("/tmp/config.yml", []byte(conf), 0644)
	if err != nil {
		c.Fatal("Failed to write config file for test")
	}
	defer os.Remove("/tmp/config.yml")
	expected := &TsuruInstallConfig{
		DockerMachineConfig: &DockerMachineConfig{
			DriverName: "amazonec2",
			DriverOpts: map[string]interface{}{
				"opt1": "option1-value",
			},
			CAPath: "/tmp/certs",
			Name:   "tsuru-test",
		},
		ComponentsHosts: 2,
		PoolHosts:       1,
		DedicatedPool:   true,
	}
	dmConfig, err := parseConfigFile("/tmp/config.yml")
	c.Assert(err, check.IsNil)
	c.Assert(dmConfig, check.DeepEquals, expected)
}

func (s *S) TestInstallInfo(c *check.C) {
	c.Assert((&Install{}).Info(), check.NotNil)
}

func (s *S) TestInstallCommandFlags(c *check.C) {
	command := Install{}
	flags := command.Flags()
	c.Assert(flags, check.NotNil)
	flags.Parse(true, []string{"-c", "my-conf.yml"})
	config := flags.Lookup("c")
	usage := "Configuration file"
	c.Check(config, check.NotNil)
	c.Check(config.Name, check.Equals, "c")
	c.Check(config.Usage, check.Equals, usage)
	c.Check(config.Value.String(), check.Equals, "my-conf.yml")
	c.Check(config.DefValue, check.Equals, "")
	config = flags.Lookup("config")
	c.Check(config, check.NotNil)
	c.Check(config.Name, check.Equals, "config")
	c.Check(config.Usage, check.Equals, usage)
	c.Check(config.Value.String(), check.Equals, "my-conf.yml")
	c.Check(config.DefValue, check.Equals, "")
}

func (s *S) TestInstallTargetAlreadyExists(c *check.C) {
	var stdout, stderr bytes.Buffer
	manager := cmd.BuildBaseManager("uninstall-client", "0.0.0", "", nil)
	client := cmd.NewClient(&http.Client{}, nil, manager)
	context := cmd.Context{
		Args:   []string{"test", fmt.Sprintf("%s:8080", "1.2.3.4")},
		Stdout: &stdout,
		Stderr: &stderr,
	}
	targetadd := manager.Commands["target-add"]
	t, ok := targetadd.(cmd.FlaggedCommand)
	c.Assert(ok, check.Equals, true)
	err := t.Flags().Parse(true, []string{"-s"})
	c.Assert(err, check.IsNil)
	err = t.Run(&context, client)
	c.Assert(err, check.IsNil)
	defer func(manager *cmd.Manager) {
		c := cmd.NewClient(&http.Client{}, nil, manager)
		cont := cmd.Context{
			Args:   []string{"test"},
			Stdout: os.Stdout,
			Stderr: os.Stderr,
		}
		targetrm := manager.Commands["target-remove"]
		targetrm.Run(&cont, c)
	}(manager)
	command := Install{}
	command.Flags().Parse(true, []string{"-c", "./testdata/wrong-conf.yml"})
	context = cmd.Context{
		Stdout: &stdout,
		Stderr: &stderr,
	}
	expectedErr := "pre-install checks failed: tsuru target \"test\" already exists"
	err = command.Run(&context, client)
	c.Assert(err, check.NotNil)
	c.Assert(expectedErr, check.Equals, err.Error())
}

func (s *S) TestUninstallInfo(c *check.C) {
	c.Assert((&Uninstall{}).Info(), check.NotNil)
}

func (s *S) TestUninstallCommandFlags(c *check.C) {
	command := Uninstall{}
	flags := command.Flags()
	c.Assert(flags, check.NotNil)
	flags.Parse(true, []string{"-c", "my-conf.yml"})
	config := flags.Lookup("c")
	usage := "Configuration file"
	c.Check(config, check.NotNil)
	c.Check(config.Name, check.Equals, "c")
	c.Check(config.Usage, check.Equals, usage)
	c.Check(config.Value.String(), check.Equals, "my-conf.yml")
	c.Check(config.DefValue, check.Equals, "")
	config = flags.Lookup("config")
	c.Check(config, check.NotNil)
	c.Check(config.Name, check.Equals, "config")
	c.Check(config.Usage, check.Equals, usage)
	c.Check(config.Value.String(), check.Equals, "my-conf.yml")
	c.Check(config.DefValue, check.Equals, "")
}

func (s *S) TestBuildClusterTable(c *check.C) {
	cluster := &FakeServiceCluster{}
	table := buildClusterTable(cluster)
	expected := `+-----------+---------+---------+
| IP        | State   | Manager |
+-----------+---------+---------+
| 127.0.0.1 | running | true    |
+-----------+---------+---------+
`
	c.Assert(table.String(), check.Equals, expected)
}

func (s *S) TestBuildComponentsTable(c *check.C) {
	cluster := &FakeServiceCluster{}
	table := buildComponentsTable([]TsuruComponent{&MongoDB{}}, cluster)
	expected := `+-----------+-------+----------+
| Component | Ports | Replicas |
+-----------+-------+----------+
| MongoDB   | 8080  | 1        |
+-----------+-------+----------+
`
	c.Assert(table.String(), check.Equals, expected)
}

type FakeMachineProvisioner struct {
	hostsProvisioned int
}

func (p *FakeMachineProvisioner) ProvisionMachines(hosts int, ports []string) ([]*Machine, error) {
	var machines []*Machine
	for i := 0; i < hosts; i++ {
		p.hostsProvisioned = p.hostsProvisioned + 1
		machines = append(machines, &Machine{})
	}
	return machines, nil
}

func (s *S) TestProvisionPool(c *check.C) {
	tt := []struct {
		poolHosts           int
		dedicatedPool       bool
		machines            []*Machine
		expectedProvisioned int
	}{
		{1, false, []*Machine{{}}, 0},
		{2, false, []*Machine{{}}, 1},
		{1, true, []*Machine{{}}, 1},
		{2, true, []*Machine{{}, {}}, 2},
		{3, true, []*Machine{{}}, 3},
	}
	for _, t := range tt {
		p := &FakeMachineProvisioner{}
		config := &TsuruInstallConfig{PoolHosts: t.poolHosts, DedicatedPool: t.dedicatedPool}
		_, err := ProvisionPool(p, config, t.machines)
		c.Assert(err, check.IsNil)
		c.Assert(p.hostsProvisioned, check.Equals, t.expectedProvisioned)
	}
}
<|endoftext|>


<commit_before>// Name: action.go
// Desc:
//   This portion of the package manager is meant to go through each
//   PkgOperation and perform them.
package pkginstall

import (
	"errors"
	"github.com/crockeo/dotfile-manager/pkgfile"
)

var (
	MalformedPkgOperationError error = errors.New("Malformed package operation.")
)

//MoveFile
//RunScript
//InstallPackage

// Performing a MoveFile operation.
func performMoveFile(moveFile pkgfile.MoveFile) error {
	return nil
}

// Performing a RunScript operation.
func performRunScript(runScript pkgfile.RunScript) error {
	return nil
}

// Performing an InstallPackage operation.
func performInstallPackage(installPackage pkgfile.InstallPackage) error {
	return InstallPackage(installPackage.Url)
}

// Performing a single package operation.
func PerformPackageOperation(pkgOperation pkgfile.PkgOperation) error {
	t := pkgOperation.GetType()

	switch t {
	case pkgfile.MoveFileType:
		v, succ := pkgOperation.(pkgfile.MoveFile)
		if !succ {
			return MalformedPkgOperationError
		}

		return performMoveFile(v)
	case pkgfile.RunScriptType:
		v, succ := pkgOperation.(pkgfile.RunScript)
		if !succ {
			return MalformedPkgOperationError
		}

		return performRunScript(v)
	case pkgfile.InstallPackageType:
		v, succ := pkgOperation.(pkgfile.InstallPackage)
		if !succ {
			return MalformedPkgOperationError
		}

		return performInstallPackage(v)
	}

	return MalformedPkgOperationError
}

// Performing a whole set of package operations (as described in a PkgFile).
func PerformPackageOperations(pkgFile pkgfile.PkgFile) error {
	var err error
	for _, v := range pkgFile.Ops {
		err = PerformPackageOperation(v)
		if err != nil {
			return err
		}
	}

	return nil
}
<commit_msg>Removed some comments.<commit_after>// Name: action.go
// Desc:
//   This portion of the package manager is meant to go through each
//   PkgOperation and perform them.
package pkginstall

import (
	"errors"
	"github.com/crockeo/dotfile-manager/pkgfile"
)

var (
	MalformedPkgOperationError error = errors.New("Malformed package operation.")
)

// Performing a MoveFile operation.
func performMoveFile(moveFile pkgfile.MoveFile) error {
	return nil
}

// Performing a RunScript operation.
func performRunScript(runScript pkgfile.RunScript) error {
	return nil
}

// Performing an InstallPackage operation.
func performInstallPackage(installPackage pkgfile.InstallPackage) error {
	return InstallPackage(installPackage.Url)
}

// Performing a single package operation.
func PerformPackageOperation(pkgOperation pkgfile.PkgOperation) error {
	t := pkgOperation.GetType()

	switch t {
	case pkgfile.MoveFileType:
		v, succ := pkgOperation.(pkgfile.MoveFile)
		if !succ {
			return MalformedPkgOperationError
		}

		return performMoveFile(v)
	case pkgfile.RunScriptType:
		v, succ := pkgOperation.(pkgfile.RunScript)
		if !succ {
			return MalformedPkgOperationError
		}

		return performRunScript(v)
	case pkgfile.InstallPackageType:
		v, succ := pkgOperation.(pkgfile.InstallPackage)
		if !succ {
			return MalformedPkgOperationError
		}

		return performInstallPackage(v)
	}

	return MalformedPkgOperationError
}

// Performing a whole set of package operations (as described in a PkgFile).
func PerformPackageOperations(pkgFile pkgfile.PkgFile) error {
	var err error
	for _, v := range pkgFile.Ops {
		err = PerformPackageOperation(v)
		if err != nil {
			return err
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package leafnodes_test

import (
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/ginkgo/internal/leafnodes"
	. "github.com/onsi/gomega"

	"reflect"
	"runtime"
	"time"

	"github.com/onsi/ginkgo/internal/codelocation"
	Failer "github.com/onsi/ginkgo/internal/failer"
	"github.com/onsi/ginkgo/types"
)

type runnable interface {
	Run() (outcome types.SpecState, failure types.SpecFailure)
	CodeLocation() types.CodeLocation
}

func SynchronousSharedRunnerBehaviors(build func(body interface{}, timeout time.Duration, failer *Failer.Failer, componentCodeLocation types.CodeLocation) runnable, componentType types.SpecComponentType, componentIndex int) {
	var (
		outcome types.SpecState
		failure types.SpecFailure

		failer *Failer.Failer

		componentCodeLocation types.CodeLocation
		innerCodeLocation     types.CodeLocation

		didRun bool
	)

	BeforeEach(func() {
		failer = Failer.New()
		componentCodeLocation = codelocation.New(0)
		innerCodeLocation = codelocation.New(0)

		didRun = false
	})

	Describe("synchronous functions", func() {
		Context("when the function passes", func() {
			BeforeEach(func() {
				outcome, failure = build(func() {
					didRun = true
				}, 0, failer, componentCodeLocation).Run()
			})

			It("should have a succesful outcome", func() {
				Ω(didRun).Should(BeTrue())

				Ω(outcome).Should(Equal(types.SpecStatePassed))
				Ω(failure).Should(BeZero())
			})
		})

		Context("when a failure occurs", func() {
			BeforeEach(func() {
				outcome, failure = build(func() {
					didRun = true
					failer.Fail("bam", innerCodeLocation)
					panic("should not matter")
				}, 0, failer, componentCodeLocation).Run()
			})

			It("should return the failure", func() {
				Ω(didRun).Should(BeTrue())

				Ω(outcome).Should(Equal(types.SpecStateFailed))
				Ω(failure).Should(Equal(types.SpecFailure{
					Message:               "bam",
					Location:              innerCodeLocation,
					ForwardedPanic:        "",
					ComponentIndex:        componentIndex,
					ComponentType:         componentType,
					ComponentCodeLocation: componentCodeLocation,
				}))
			})
		})

		Context("when a panic occurs", func() {
			BeforeEach(func() {
				outcome, failure = build(func() {
					didRun = true
					innerCodeLocation = codelocation.New(0)
					panic("ack!")
				}, 0, failer, componentCodeLocation).Run()
			})

			It("should return the panic", func() {
				Ω(didRun).Should(BeTrue())

				Ω(outcome).Should(Equal(types.SpecStatePanicked))
				Ω(failure.ForwardedPanic).Should(Equal("ack!"))
			})
		})

		Context("when a panic occurs with a nil value", func() {
			BeforeEach(func() {
				outcome, failure = build(func() {
					didRun = true
					innerCodeLocation = codelocation.New(0)
					panic(nil)
				}, 0, failer, componentCodeLocation).Run()
			})

			It("should return the nil-valued panic", func() {
				Ω(didRun).Should(BeTrue())

				Ω(outcome).Should(Equal(types.SpecStatePanicked))
				Ω(failure.ForwardedPanic).Should(Equal("<nil>"))
			})
		})

	})
}

func AsynchronousSharedRunnerBehaviors(build func(body interface{}, timeout time.Duration, failer *Failer.Failer, componentCodeLocation types.CodeLocation) runnable, componentType types.SpecComponentType, componentIndex int) {
	var (
		outcome types.SpecState
		failure types.SpecFailure

		failer *Failer.Failer

		componentCodeLocation types.CodeLocation
		innerCodeLocation     types.CodeLocation

		didRun bool
	)

	BeforeEach(func() {
		failer = Failer.New()
		componentCodeLocation = codelocation.New(0)
		innerCodeLocation = codelocation.New(0)

		didRun = false
	})

	Describe("asynchronous functions", func() {
		var timeoutDuration time.Duration

		BeforeEach(func() {
			timeoutDuration = time.Duration(1 * float64(time.Second))
		})

		Context("when running", func() {
			It("should run the function as a goroutine, and block until it's done", func() {
				initialNumberOfGoRoutines := runtime.NumGoroutine()
				numberOfGoRoutines := 0

				build(func(done Done) {
					didRun = true
					numberOfGoRoutines = runtime.NumGoroutine()
					close(done)
				}, timeoutDuration, failer, componentCodeLocation).Run()

				Ω(didRun).Should(BeTrue())
				Ω(numberOfGoRoutines).Should(BeNumerically(">=", initialNumberOfGoRoutines+1))
			})
		})

		Context("when the function passes", func() {
			BeforeEach(func() {
				outcome, failure = build(func(done Done) {
					didRun = true
					close(done)
				}, timeoutDuration, failer, componentCodeLocation).Run()
			})

			It("should have a succesful outcome", func() {
				Ω(didRun).Should(BeTrue())
				Ω(outcome).Should(Equal(types.SpecStatePassed))
				Ω(failure).Should(BeZero())
			})
		})

		Context("when the function fails", func() {
			BeforeEach(func() {
				outcome, failure = build(func(done Done) {
					didRun = true
					failer.Fail("bam", innerCodeLocation)
					time.Sleep(20 * time.Millisecond)
					panic("doesn't matter")
					close(done)
				}, 10*time.Millisecond, failer, componentCodeLocation).Run()
			})

			It("should return the failure", func() {
				Ω(didRun).Should(BeTrue())

				Ω(outcome).Should(Equal(types.SpecStateFailed))
				Ω(failure).Should(Equal(types.SpecFailure{
					Message:               "bam",
					Location:              innerCodeLocation,
					ForwardedPanic:        "",
					ComponentIndex:        componentIndex,
					ComponentType:         componentType,
					ComponentCodeLocation: componentCodeLocation,
				}))
			})
		})

		Context("when the function times out", func() {
			var guard chan struct{}

			BeforeEach(func() {
				guard = make(chan struct{})
				outcome, failure = build(func(done Done) {
					didRun = true
					time.Sleep(20 * time.Millisecond)
					close(guard)
					panic("doesn't matter")
					close(done)
				}, 10*time.Millisecond, failer, componentCodeLocation).Run()
			})

			It("should return the timeout", func() {
				<-guard
				Ω(didRun).Should(BeTrue())

				Ω(outcome).Should(Equal(types.SpecStateTimedOut))
				Ω(failure).Should(Equal(types.SpecFailure{
					Message:               "Timed out",
					Location:              componentCodeLocation,
					ForwardedPanic:        "",
					ComponentIndex:        componentIndex,
					ComponentType:         componentType,
					ComponentCodeLocation: componentCodeLocation,
				}))
			})
		})

		Context("when the function panics", func() {
			BeforeEach(func() {
				outcome, failure = build(func(done Done) {
					didRun = true
					innerCodeLocation = codelocation.New(0)
					panic("ack!")
				}, 100*time.Millisecond, failer, componentCodeLocation).Run()
			})

			It("should return the panic", func() {
				Ω(didRun).Should(BeTrue())

				Ω(outcome).Should(Equal(types.SpecStatePanicked))
				Ω(failure.ForwardedPanic).Should(Equal("ack!"))
			})
		})

		Context("when the function panics with a nil value", func() {
			BeforeEach(func() {
				outcome, failure = build(func(done Done) {
					didRun = true
					innerCodeLocation = codelocation.New(0)
					panic(nil)
				}, 100*time.Millisecond, failer, componentCodeLocation).Run()
			})

			It("should return the nil-valued panic", func() {
				Ω(didRun).Should(BeTrue())

				Ω(outcome).Should(Equal(types.SpecStatePanicked))
				Ω(failure.ForwardedPanic).Should(Equal("<nil>"))
			})
		})
	})
}

func InvalidSharedRunnerBehaviors(build func(body interface{}, timeout time.Duration, failer *Failer.Failer, componentCodeLocation types.CodeLocation) runnable, componentType types.SpecComponentType) {
	var (
		failer                *Failer.Failer
		componentCodeLocation types.CodeLocation
		innerCodeLocation     types.CodeLocation
	)

	BeforeEach(func() {
		failer = Failer.New()
		componentCodeLocation = codelocation.New(0)
		innerCodeLocation = codelocation.New(0)
	})

	Describe("invalid functions", func() {
		Context("when passed something that's not a function", func() {
			It("should panic", func() {
				Ω(func() {
					build("not a function", 0, failer, componentCodeLocation)
				}).Should(Panic())
			})
		})

		Context("when the function takes the wrong kind of argument", func() {
			It("should panic", func() {
				Ω(func() {
					build(func(oops string) {}, 0, failer, componentCodeLocation)
				}).Should(Panic())
			})
		})

		Context("when the function takes more than one argument", func() {
			It("should panic", func() {
				Ω(func() {
					build(func(done Done, oops string) {}, 0, failer, componentCodeLocation)
				}).Should(Panic())
			})
		})
	})
}

var _ = Describe("Shared RunnableNode behavior", func() {
	Describe("It Nodes", func() {
		build := func(body interface{}, timeout time.Duration, failer *Failer.Failer, componentCodeLocation types.CodeLocation) runnable {
			return NewItNode("", body, types.FlagTypeFocused, componentCodeLocation, timeout, failer, 3)
		}

		SynchronousSharedRunnerBehaviors(build, types.SpecComponentTypeIt, 3)
		AsynchronousSharedRunnerBehaviors(build, types.SpecComponentTypeIt, 3)
		InvalidSharedRunnerBehaviors(build, types.SpecComponentTypeIt)
	})

	Describe("Measure Nodes", func() {
		build := func(body interface{}, _ time.Duration, failer *Failer.Failer, componentCodeLocation types.CodeLocation) runnable {
			return NewMeasureNode("", func(Benchmarker) {
				reflect.ValueOf(body).Call([]reflect.Value{})
			}, types.FlagTypeFocused, componentCodeLocation, 10, failer, 3)
		}

		SynchronousSharedRunnerBehaviors(build, types.SpecComponentTypeMeasure, 3)
	})

	Describe("BeforeEach Nodes", func() {
		build := func(body interface{}, timeout time.Duration, failer *Failer.Failer, componentCodeLocation types.CodeLocation) runnable {
			return NewBeforeEachNode(body, componentCodeLocation, timeout, failer, 3)
		}

		SynchronousSharedRunnerBehaviors(build, types.SpecComponentTypeBeforeEach, 3)
		AsynchronousSharedRunnerBehaviors(build, types.SpecComponentTypeBeforeEach, 3)
		InvalidSharedRunnerBehaviors(build, types.SpecComponentTypeBeforeEach)
	})

	Describe("AfterEach Nodes", func() {
		build := func(body interface{}, timeout time.Duration, failer *Failer.Failer, componentCodeLocation types.CodeLocation) runnable {
			return NewAfterEachNode(body, componentCodeLocation, timeout, failer, 3)
		}

		SynchronousSharedRunnerBehaviors(build, types.SpecComponentTypeAfterEach, 3)
		AsynchronousSharedRunnerBehaviors(build, types.SpecComponentTypeAfterEach, 3)
		InvalidSharedRunnerBehaviors(build, types.SpecComponentTypeAfterEach)
	})

	Describe("JustBeforeEach Nodes", func() {
		build := func(body interface{}, timeout time.Duration, failer *Failer.Failer, componentCodeLocation types.CodeLocation) runnable {
			return NewJustBeforeEachNode(body, componentCodeLocation, timeout, failer, 3)
		}

		SynchronousSharedRunnerBehaviors(build, types.SpecComponentTypeJustBeforeEach, 3)
		AsynchronousSharedRunnerBehaviors(build, types.SpecComponentTypeJustBeforeEach, 3)
		InvalidSharedRunnerBehaviors(build, types.SpecComponentTypeJustBeforeEach)
	})
})
<commit_msg>remove unnecessary variable<commit_after>package leafnodes_test

import (
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/ginkgo/internal/leafnodes"
	. "github.com/onsi/gomega"

	"reflect"
	"runtime"
	"time"

	"github.com/onsi/ginkgo/internal/codelocation"
	Failer "github.com/onsi/ginkgo/internal/failer"
	"github.com/onsi/ginkgo/types"
)

type runnable interface {
	Run() (outcome types.SpecState, failure types.SpecFailure)
	CodeLocation() types.CodeLocation
}

func SynchronousSharedRunnerBehaviors(build func(body interface{}, timeout time.Duration, failer *Failer.Failer, componentCodeLocation types.CodeLocation) runnable, componentType types.SpecComponentType, componentIndex int) {
	var (
		outcome types.SpecState
		failure types.SpecFailure

		failer *Failer.Failer

		componentCodeLocation types.CodeLocation
		innerCodeLocation     types.CodeLocation

		didRun bool
	)

	BeforeEach(func() {
		failer = Failer.New()
		componentCodeLocation = codelocation.New(0)
		innerCodeLocation = codelocation.New(0)

		didRun = false
	})

	Describe("synchronous functions", func() {
		Context("when the function passes", func() {
			BeforeEach(func() {
				outcome, failure = build(func() {
					didRun = true
				}, 0, failer, componentCodeLocation).Run()
			})

			It("should have a succesful outcome", func() {
				Ω(didRun).Should(BeTrue())

				Ω(outcome).Should(Equal(types.SpecStatePassed))
				Ω(failure).Should(BeZero())
			})
		})

		Context("when a failure occurs", func() {
			BeforeEach(func() {
				outcome, failure = build(func() {
					didRun = true
					failer.Fail("bam", innerCodeLocation)
					panic("should not matter")
				}, 0, failer, componentCodeLocation).Run()
			})

			It("should return the failure", func() {
				Ω(didRun).Should(BeTrue())

				Ω(outcome).Should(Equal(types.SpecStateFailed))
				Ω(failure).Should(Equal(types.SpecFailure{
					Message:               "bam",
					Location:              innerCodeLocation,
					ForwardedPanic:        "",
					ComponentIndex:        componentIndex,
					ComponentType:         componentType,
					ComponentCodeLocation: componentCodeLocation,
				}))
			})
		})

		Context("when a panic occurs", func() {
			BeforeEach(func() {
				outcome, failure = build(func() {
					didRun = true
					innerCodeLocation = codelocation.New(0)
					panic("ack!")
				}, 0, failer, componentCodeLocation).Run()
			})

			It("should return the panic", func() {
				Ω(didRun).Should(BeTrue())

				Ω(outcome).Should(Equal(types.SpecStatePanicked))
				Ω(failure.ForwardedPanic).Should(Equal("ack!"))
			})
		})

		Context("when a panic occurs with a nil value", func() {
			BeforeEach(func() {
				outcome, failure = build(func() {
					didRun = true
					innerCodeLocation = codelocation.New(0)
					panic(nil)
				}, 0, failer, componentCodeLocation).Run()
			})

			It("should return the nil-valued panic", func() {
				Ω(didRun).Should(BeTrue())

				Ω(outcome).Should(Equal(types.SpecStatePanicked))
				Ω(failure.ForwardedPanic).Should(Equal("<nil>"))
			})
		})

	})
}

func AsynchronousSharedRunnerBehaviors(build func(body interface{}, timeout time.Duration, failer *Failer.Failer, componentCodeLocation types.CodeLocation) runnable, componentType types.SpecComponentType, componentIndex int) {
	var (
		outcome types.SpecState
		failure types.SpecFailure

		failer *Failer.Failer

		componentCodeLocation types.CodeLocation
		innerCodeLocation     types.CodeLocation

		didRun bool
	)

	BeforeEach(func() {
		failer = Failer.New()
		componentCodeLocation = codelocation.New(0)
		innerCodeLocation = codelocation.New(0)

		didRun = false
	})

	Describe("asynchronous functions", func() {
		var timeoutDuration time.Duration

		BeforeEach(func() {
			timeoutDuration = time.Duration(1 * float64(time.Second))
		})

		Context("when running", func() {
			It("should run the function as a goroutine, and block until it's done", func() {
				initialNumberOfGoRoutines := runtime.NumGoroutine()
				numberOfGoRoutines := 0

				build(func(done Done) {
					didRun = true
					numberOfGoRoutines = runtime.NumGoroutine()
					close(done)
				}, timeoutDuration, failer, componentCodeLocation).Run()

				Ω(didRun).Should(BeTrue())
				Ω(numberOfGoRoutines).Should(BeNumerically(">=", initialNumberOfGoRoutines+1))
			})
		})

		Context("when the function passes", func() {
			BeforeEach(func() {
				outcome, failure = build(func(done Done) {
					didRun = true
					close(done)
				}, timeoutDuration, failer, componentCodeLocation).Run()
			})

			It("should have a succesful outcome", func() {
				Ω(didRun).Should(BeTrue())
				Ω(outcome).Should(Equal(types.SpecStatePassed))
				Ω(failure).Should(BeZero())
			})
		})

		Context("when the function fails", func() {
			BeforeEach(func() {
				outcome, failure = build(func(done Done) {
					didRun = true
					failer.Fail("bam", innerCodeLocation)
					time.Sleep(20 * time.Millisecond)
					panic("doesn't matter")
					close(done)
				}, 10*time.Millisecond, failer, componentCodeLocation).Run()
			})

			It("should return the failure", func() {
				Ω(didRun).Should(BeTrue())

				Ω(outcome).Should(Equal(types.SpecStateFailed))
				Ω(failure).Should(Equal(types.SpecFailure{
					Message:               "bam",
					Location:              innerCodeLocation,
					ForwardedPanic:        "",
					ComponentIndex:        componentIndex,
					ComponentType:         componentType,
					ComponentCodeLocation: componentCodeLocation,
				}))
			})
		})

		Context("when the function times out", func() {
			var guard chan struct{}

			BeforeEach(func() {
				guard = make(chan struct{})
				outcome, failure = build(func(done Done) {
					didRun = true
					time.Sleep(20 * time.Millisecond)
					close(guard)
					panic("doesn't matter")
					close(done)
				}, 10*time.Millisecond, failer, componentCodeLocation).Run()
			})

			It("should return the timeout", func() {
				<-guard
				Ω(didRun).Should(BeTrue())

				Ω(outcome).Should(Equal(types.SpecStateTimedOut))
				Ω(failure).Should(Equal(types.SpecFailure{
					Message:               "Timed out",
					Location:              componentCodeLocation,
					ForwardedPanic:        "",
					ComponentIndex:        componentIndex,
					ComponentType:         componentType,
					ComponentCodeLocation: componentCodeLocation,
				}))
			})
		})

		Context("when the function panics", func() {
			BeforeEach(func() {
				outcome, failure = build(func(done Done) {
					didRun = true
					innerCodeLocation = codelocation.New(0)
					panic("ack!")
				}, 100*time.Millisecond, failer, componentCodeLocation).Run()
			})

			It("should return the panic", func() {
				Ω(didRun).Should(BeTrue())

				Ω(outcome).Should(Equal(types.SpecStatePanicked))
				Ω(failure.ForwardedPanic).Should(Equal("ack!"))
			})
		})

		Context("when the function panics with a nil value", func() {
			BeforeEach(func() {
				outcome, failure = build(func(done Done) {
					didRun = true
					innerCodeLocation = codelocation.New(0)
					panic(nil)
				}, 100*time.Millisecond, failer, componentCodeLocation).Run()
			})

			It("should return the nil-valued panic", func() {
				Ω(didRun).Should(BeTrue())

				Ω(outcome).Should(Equal(types.SpecStatePanicked))
				Ω(failure.ForwardedPanic).Should(Equal("<nil>"))
			})
		})
	})
}

func InvalidSharedRunnerBehaviors(build func(body interface{}, timeout time.Duration, failer *Failer.Failer, componentCodeLocation types.CodeLocation) runnable, componentType types.SpecComponentType) {
	var (
		failer                *Failer.Failer
		componentCodeLocation types.CodeLocation
	)

	BeforeEach(func() {
		failer = Failer.New()
		componentCodeLocation = codelocation.New(0)
	})

	Describe("invalid functions", func() {
		Context("when passed something that's not a function", func() {
			It("should panic", func() {
				Ω(func() {
					build("not a function", 0, failer, componentCodeLocation)
				}).Should(Panic())
			})
		})

		Context("when the function takes the wrong kind of argument", func() {
			It("should panic", func() {
				Ω(func() {
					build(func(oops string) {}, 0, failer, componentCodeLocation)
				}).Should(Panic())
			})
		})

		Context("when the function takes more than one argument", func() {
			It("should panic", func() {
				Ω(func() {
					build(func(done Done, oops string) {}, 0, failer, componentCodeLocation)
				}).Should(Panic())
			})
		})
	})
}

var _ = Describe("Shared RunnableNode behavior", func() {
	Describe("It Nodes", func() {
		build := func(body interface{}, timeout time.Duration, failer *Failer.Failer, componentCodeLocation types.CodeLocation) runnable {
			return NewItNode("", body, types.FlagTypeFocused, componentCodeLocation, timeout, failer, 3)
		}

		SynchronousSharedRunnerBehaviors(build, types.SpecComponentTypeIt, 3)
		AsynchronousSharedRunnerBehaviors(build, types.SpecComponentTypeIt, 3)
		InvalidSharedRunnerBehaviors(build, types.SpecComponentTypeIt)
	})

	Describe("Measure Nodes", func() {
		build := func(body interface{}, _ time.Duration, failer *Failer.Failer, componentCodeLocation types.CodeLocation) runnable {
			return NewMeasureNode("", func(Benchmarker) {
				reflect.ValueOf(body).Call([]reflect.Value{})
			}, types.FlagTypeFocused, componentCodeLocation, 10, failer, 3)
		}

		SynchronousSharedRunnerBehaviors(build, types.SpecComponentTypeMeasure, 3)
	})

	Describe("BeforeEach Nodes", func() {
		build := func(body interface{}, timeout time.Duration, failer *Failer.Failer, componentCodeLocation types.CodeLocation) runnable {
			return NewBeforeEachNode(body, componentCodeLocation, timeout, failer, 3)
		}

		SynchronousSharedRunnerBehaviors(build, types.SpecComponentTypeBeforeEach, 3)
		AsynchronousSharedRunnerBehaviors(build, types.SpecComponentTypeBeforeEach, 3)
		InvalidSharedRunnerBehaviors(build, types.SpecComponentTypeBeforeEach)
	})

	Describe("AfterEach Nodes", func() {
		build := func(body interface{}, timeout time.Duration, failer *Failer.Failer, componentCodeLocation types.CodeLocation) runnable {
			return NewAfterEachNode(body, componentCodeLocation, timeout, failer, 3)
		}

		SynchronousSharedRunnerBehaviors(build, types.SpecComponentTypeAfterEach, 3)
		AsynchronousSharedRunnerBehaviors(build, types.SpecComponentTypeAfterEach, 3)
		InvalidSharedRunnerBehaviors(build, types.SpecComponentTypeAfterEach)
	})

	Describe("JustBeforeEach Nodes", func() {
		build := func(body interface{}, timeout time.Duration, failer *Failer.Failer, componentCodeLocation types.CodeLocation) runnable {
			return NewJustBeforeEachNode(body, componentCodeLocation, timeout, failer, 3)
		}

		SynchronousSharedRunnerBehaviors(build, types.SpecComponentTypeJustBeforeEach, 3)
		AsynchronousSharedRunnerBehaviors(build, types.SpecComponentTypeJustBeforeEach, 3)
		InvalidSharedRunnerBehaviors(build, types.SpecComponentTypeJustBeforeEach)
	})
})
<|endoftext|>


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

package piazza

//----------------------------------------------------------

// Elasticsearch, at least, does it this way:
//  - perform the query, giving a huge result set
//  - sort the result set
//  - select out the page you want

type PaginationOrder string

const PaginationOrderAscending PaginationOrder = "asc" // (the default)
const PaginationOrderDescending PaginationOrder = "desc"

type JsonPagination struct {
	Count   int             `json:"count"` // only used when writing output
	Page    int             `json:"page"`
	PerPage int             `json:"perPage"`
	SortBy  string          `json:"sortBy"`
	Order   PaginationOrder `json:"order"`
}

func (p *JsonPagination) StartIndex() int {
	return p.Page * p.PerPage
}

func (p *JsonPagination) EndIndex() int {
	return p.StartIndex() + p.PerPage
}

func NewJsonPagination(params *HttpQueryParams, defalt *JsonPagination) (*JsonPagination, error) {

	perPage, err := params.GetInt("perPage", defalt.PerPage)
	if err != nil {
		return nil, err
	}

	page, err := params.GetInt("page", defalt.Page)
	if err != nil {
		return nil, err
	}

	sortBy, err := params.GetString("sortBy", defalt.SortBy)
	if err != nil {
		return nil, err
	}

	order, err := params.GetOrder("order", string(defalt.Order))
	if err != nil {
		return nil, err
	}

	p := &JsonPagination{
		PerPage: perPage,
		Page:    page,
		SortBy:  sortBy,
		Order:   PaginationOrder(order),
	}

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

package piazza

import "log"

//----------------------------------------------------------

// Elasticsearch, at least, does it this way:
//  - perform the query, giving a huge result set
//  - sort the result set
//  - select out the page you want

type PaginationOrder string

const PaginationOrderAscending PaginationOrder = "asc" // (the default)
const PaginationOrderDescending PaginationOrder = "desc"

type JsonPagination struct {
	Count   int             `json:"count"` // only used when writing output
	Page    int             `json:"page"`
	PerPage int             `json:"perPage"`
	SortBy  string          `json:"sortBy"`
	Order   PaginationOrder `json:"order"`
}

func (p *JsonPagination) StartIndex() int {
	return p.Page * p.PerPage
}

func (p *JsonPagination) EndIndex() int {
	return p.StartIndex() + p.PerPage
}

func NewJsonPagination(params *HttpQueryParams, defalt *JsonPagination) (*JsonPagination, error) {
	log.Printf("NewJsonPagination/1: %#v", params)

	perPage, err := params.GetInt("perPage", defalt.PerPage)
	if err != nil {
		return nil, err
	}

	page, err := params.GetInt("page", defalt.Page)
	if err != nil {
		return nil, err
	}

	sortBy, err := params.GetString("sortBy", defalt.SortBy)
	if err != nil {
		return nil, err
	}

	order, err := params.GetOrder("order", string(defalt.Order))
	if err != nil {
		return nil, err
	}

	p := &JsonPagination{
		PerPage: perPage,
		Page:    page,
		SortBy:  sortBy,
		Order:   PaginationOrder(order),
	}
	log.Printf("NewJsonPagination/2: %#v", p)

	return p, nil
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/sha256"
	"crypto/sha512"
	"flag"
	"fmt"
	"hash"
	"io"
	"os"
	"strings"
)

func calcSHA(h hash.Hash, r io.Reader) ([]byte, error) {
	if _, err := io.Copy(h, r); err != nil {
		return nil, err
	}
	return h.Sum(nil), nil
}

func showSHA(h hash.Hash) {
	sum, err := calcSHA(h, os.Stdin)
	if err != nil {
		fmt.Fprintf(os.Stderr, "error calculation sha: %s\n", err)
		os.Exit(1)
	}
	fmt.Printf("%x\n", sum)
}

func main() {

	var shaTypes = []string{"sha256", "sha384", "sha512"}

	shaType := flag.String("type", "sha256",
		fmt.Sprintf("type of sha: %s", strings.Join(shaTypes, ",")))
	flag.Parse()
	flag.Visit(func(f *flag.Flag) {
		val := f.Value.String()
		for _, ss := range shaTypes {
			if val == ss {
				return
			}
		}
		fmt.Fprintf(os.Stderr, "error: invalid sha '%s'\n", *shaType)
		flag.Usage()
		os.Exit(1)
	})

	switch *shaType {
	case "sha256":
		showSHA(sha256.New())
	case "sha384":
		showSHA(sha512.New384())
	case "sha512":
		showSHA(sha512.New())
	}
}
<commit_msg>Updated error msg for task 4.2<commit_after>package main

import (
	"crypto/sha256"
	"crypto/sha512"
	"flag"
	"fmt"
	"hash"
	"io"
	"os"
	"strings"
)

func calcSHA(h hash.Hash, r io.Reader) ([]byte, error) {
	if _, err := io.Copy(h, r); err != nil {
		return nil, err
	}
	return h.Sum(nil), nil
}

func showSHA(h hash.Hash) {
	sum, err := calcSHA(h, os.Stdin)
	if err != nil {
		fmt.Fprintf(os.Stderr, "error calculation sha: %s\n", err)
		os.Exit(1)
	}
	fmt.Printf("%x\n", sum)
}

func main() {

	var shaTypes = []string{"sha256", "sha384", "sha512"}

	shaType := flag.String("type", "sha256",
		fmt.Sprintf("type of sha: %s", strings.Join(shaTypes, ",")))
	flag.Parse()
	flag.Visit(func(f *flag.Flag) {
		val := f.Value.String()
		for _, ss := range shaTypes {
			if val == ss {
				return
			}
		}
		fmt.Fprintf(os.Stderr, "error: invalid sha type '%s'\n", *shaType)
		flag.Usage()
		os.Exit(1)
	})

	switch *shaType {
	case "sha256":
		showSHA(sha256.New())
	case "sha384":
		showSHA(sha512.New384())
	case "sha512":
		showSHA(sha512.New())
	}
}
<|endoftext|>