<commit_before>// Copyright 2018, 2019 VMware, Inc.
// SPDX-License-Identifier: Apache-2.0
//
// 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 client

import (
	"context"
	"fmt"
	"io"
	"time"

	"github.com/pkg/errors"

	"github.com/networkservicemesh/networkservicemesh/controlplane/api/connection/mechanisms/cls"

	"github.com/networkservicemesh/networkservicemesh/pkg/tools/spanhelper"

	"github.com/networkservicemesh/networkservicemesh/pkg/tools/jaeger"

	"github.com/sirupsen/logrus"

	"github.com/networkservicemesh/networkservicemesh/controlplane/api/connection"
	"github.com/networkservicemesh/networkservicemesh/controlplane/api/connectioncontext"
	"github.com/networkservicemesh/networkservicemesh/controlplane/api/networkservice"
	"github.com/networkservicemesh/networkservicemesh/pkg/tools"
	"github.com/networkservicemesh/networkservicemesh/sdk/common"
)

const (
	// ConnectTimeout - a default connection timeout
	ConnectTimeout = 15 * time.Second
	// ConnectionRetry - A number of retries for establish a network service, default == 10
	ConnectionRetry = 10
	// RequestDelay - A delay between attempts, default = 5sec
	RequestDelay = time.Second * 5
)

// NsmClient is the NSM client struct
type NsmClient struct {
	*common.NsmConnection
	ClientNetworkService string
	ClientLabels         map[string]string
	OutgoingConnections  []*connection.Connection
	NscInterfaceName     string
	tracerCloser         io.Closer
}

// Connect with no retry and delay
func (nsmc *NsmClient) Connect(ctx context.Context, name, mechanism, description string) (*connection.Connection, error) {
	return nsmc.ConnectRetry(ctx, name, mechanism, description, 1, 0)
}

// Connect implements the business logic
func (nsmc *NsmClient) ConnectRetry(ctx context.Context, name, mechanism, description string, retryCount int, retryDelay time.Duration) (*connection.Connection, error) {
	span := spanhelper.FromContext(ctx, "nsmClient.Connect")
	defer span.Finish()

	span.Logger().Infof("Initiating an outgoing connection.")
	nsmc.Lock()
	defer nsmc.Unlock()

	if nsmc.NscInterfaceName != "" {
		// The environment variable will override local call parameters
		name = nsmc.NscInterfaceName
	}

	outgoingMechanism, err := common.NewMechanism(cls.LOCAL, mechanism, name, description)

	span.LogObject("Selected mechanism", outgoingMechanism)

	if err != nil {
		err = errors.Wrap(err, "failure to prepare the outgoing mechanism preference with error")
		span.LogError(err)
		return nil, err
	}

	routes := []*connectioncontext.Route{}
	for _, r := range nsmc.Configuration.Routes {
		routes = append(routes, &connectioncontext.Route{
			Prefix: r,
		})
	}

	outgoingRequest := &networkservice.NetworkServiceRequest{
		Connection: &connection.Connection{
			NetworkService: nsmc.Configuration.ClientNetworkService,
			Context: &connectioncontext.ConnectionContext{
				IpContext: &connectioncontext.IPContext{
					SrcIpRequired: true,
					DstIpRequired: true,
					SrcRoutes:     routes,
				},
			},
			Labels: nsmc.ClientLabels,
		},
		MechanismPreferences: []*connection.Mechanism{
			outgoingMechanism,
		},
	}
	var outgoingConnection *connection.Connection
	maxRetry := retryCount
	for retryCount >= 0 {
		var attemptSpan = spanhelper.FromContext(span.Context(), fmt.Sprintf("nsmClient.Connect.attempt:%v", maxRetry-retryCount))
		defer attemptSpan.Finish()

		attempCtx, cancelProc := context.WithTimeout(attemptSpan.Context(), ConnectTimeout)
		defer cancelProc()

		attemptLogger := attemptSpan.Logger()
		attemptLogger.Infof("Requesting %v", outgoingRequest)
		outgoingConnection, err = nsmc.NsClient.Request(attempCtx, outgoingRequest)

		if err != nil {
			attemptSpan.LogError(err)

			cancelProc()
			if retryCount == 0 {
				return nil, errors.Wrap(err, "nsm client: Failed to connect")
			} else {
				attemptLogger.Errorf("nsm client: Failed to connect %v. Retry attempts: %v Delaying: %v", err, retryCount, retryDelay)
			}
			retryCount--
			attemptSpan.Finish()
			<-time.After(retryDelay)
			continue
		}
		break
	}
	span.Logger().Infof("Success connection")
	span.LogObject("connection", outgoingConnection)
	nsmc.OutgoingConnections = append(nsmc.OutgoingConnections, outgoingConnection)
	return outgoingConnection, nil
}

// Close will terminate a particular connection
func (nsmc *NsmClient) Close(ctx context.Context, outgoingConnection *connection.Connection) error {
	nsmc.Lock()
	defer nsmc.Unlock()

	span := spanhelper.FromContext(ctx, "Client.Close")
	defer span.Finish()
	span.LogObject("connection", outgoingConnection)

	_, err := nsmc.NsClient.Close(span.Context(), outgoingConnection)

	span.LogError(err)

	arr := nsmc.OutgoingConnections
	for i, c := range arr {
		if c == outgoingConnection {
			copy(arr[i:], arr[i+1:])
			arr[len(arr)-1] = nil
			arr = arr[:len(arr)-1]
		}
	}
	return err
}

// Destroy - Destroy stops the whole module
func (nsmc *NsmClient) Destroy(ctx context.Context) error {
	nsmc.Lock()
	defer nsmc.Unlock()

	span := spanhelper.FromContext(ctx, "Client.Destroy")
	defer span.Finish()

	err := nsmc.NsmConnection.Close()
	span.LogError(errors.Wrap(err, "failed to close opentracing context"))
	if nsmc.tracerCloser != nil {
		trErr := nsmc.tracerCloser.Close()
		if trErr != nil {
			logrus.Error(trErr)
		}
	}
	return err
}

// NewNSMClient creates the NsmClient
func NewNSMClient(ctx context.Context, configuration *common.NSConfiguration) (*NsmClient, error) {
	if configuration == nil {
		configuration = &common.NSConfiguration{}
	}

	client := &NsmClient{
		ClientNetworkService: configuration.ClientNetworkService,
		ClientLabels:         tools.ParseKVStringToMap(configuration.ClientLabels, ",", "="),
		NscInterfaceName:     configuration.NscInterfaceName,
	}

	client.tracerCloser = jaeger.InitJaeger("nsm-client")

	nsmConnection, err := common.NewNSMConnection(ctx, configuration)
	if err != nil {
		logrus.Errorf("Error: %v", err)
		return nil, err
	}

	client.NsmConnection = nsmConnection

	return client, nil
}
<commit_msg>increase connect time for TestInterdomainKernelForwarderWireguard (#2199)<commit_after>// Copyright 2018, 2019 VMware, Inc.
// SPDX-License-Identifier: Apache-2.0
//
// 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 client

import (
	"context"
	"fmt"
	"io"
	"time"

	"github.com/pkg/errors"

	"github.com/networkservicemesh/networkservicemesh/controlplane/api/connection/mechanisms/cls"

	"github.com/networkservicemesh/networkservicemesh/pkg/tools/spanhelper"

	"github.com/networkservicemesh/networkservicemesh/pkg/tools/jaeger"

	"github.com/sirupsen/logrus"

	"github.com/networkservicemesh/networkservicemesh/controlplane/api/connection"
	"github.com/networkservicemesh/networkservicemesh/controlplane/api/connectioncontext"
	"github.com/networkservicemesh/networkservicemesh/controlplane/api/networkservice"
	"github.com/networkservicemesh/networkservicemesh/pkg/tools"
	"github.com/networkservicemesh/networkservicemesh/sdk/common"
)

const (
	// ConnectTimeout - a default connection timeout
	ConnectTimeout = time.Minute
	// ConnectionRetry - A number of retries for establish a network service, default == 10
	ConnectionRetry = 10
	// RequestDelay - A delay between attempts, default = 5sec
	RequestDelay = time.Second * 5
)

// NsmClient is the NSM client struct
type NsmClient struct {
	*common.NsmConnection
	ClientNetworkService string
	ClientLabels         map[string]string
	OutgoingConnections  []*connection.Connection
	NscInterfaceName     string
	tracerCloser         io.Closer
}

// Connect with no retry and delay
func (nsmc *NsmClient) Connect(ctx context.Context, name, mechanism, description string) (*connection.Connection, error) {
	return nsmc.ConnectRetry(ctx, name, mechanism, description, 1, 0)
}

// Connect implements the business logic
func (nsmc *NsmClient) ConnectRetry(ctx context.Context, name, mechanism, description string, retryCount int, retryDelay time.Duration) (*connection.Connection, error) {
	span := spanhelper.FromContext(ctx, "nsmClient.Connect")
	defer span.Finish()

	span.Logger().Infof("Initiating an outgoing connection.")
	nsmc.Lock()
	defer nsmc.Unlock()

	if nsmc.NscInterfaceName != "" {
		// The environment variable will override local call parameters
		name = nsmc.NscInterfaceName
	}

	outgoingMechanism, err := common.NewMechanism(cls.LOCAL, mechanism, name, description)

	span.LogObject("Selected mechanism", outgoingMechanism)

	if err != nil {
		err = errors.Wrap(err, "failure to prepare the outgoing mechanism preference with error")
		span.LogError(err)
		return nil, err
	}

	routes := []*connectioncontext.Route{}
	for _, r := range nsmc.Configuration.Routes {
		routes = append(routes, &connectioncontext.Route{
			Prefix: r,
		})
	}

	outgoingRequest := &networkservice.NetworkServiceRequest{
		Connection: &connection.Connection{
			NetworkService: nsmc.Configuration.ClientNetworkService,
			Context: &connectioncontext.ConnectionContext{
				IpContext: &connectioncontext.IPContext{
					SrcIpRequired: true,
					DstIpRequired: true,
					SrcRoutes:     routes,
				},
			},
			Labels: nsmc.ClientLabels,
		},
		MechanismPreferences: []*connection.Mechanism{
			outgoingMechanism,
		},
	}
	var outgoingConnection *connection.Connection
	maxRetry := retryCount
	for retryCount >= 0 {
		var attemptSpan = spanhelper.FromContext(span.Context(), fmt.Sprintf("nsmClient.Connect.attempt:%v", maxRetry-retryCount))
		defer attemptSpan.Finish()

		attempCtx, cancelProc := context.WithTimeout(attemptSpan.Context(), ConnectTimeout)
		defer cancelProc()

		attemptLogger := attemptSpan.Logger()
		attemptLogger.Infof("Requesting %v", outgoingRequest)
		outgoingConnection, err = nsmc.NsClient.Request(attempCtx, outgoingRequest)

		if err != nil {
			attemptSpan.LogError(err)

			cancelProc()
			if retryCount == 0 {
				return nil, errors.Wrap(err, "nsm client: Failed to connect")
			} else {
				attemptLogger.Errorf("nsm client: Failed to connect %v. Retry attempts: %v Delaying: %v", err, retryCount, retryDelay)
			}
			retryCount--
			attemptSpan.Finish()
			<-time.After(retryDelay)
			continue
		}
		break
	}
	span.Logger().Infof("Success connection")
	span.LogObject("connection", outgoingConnection)
	nsmc.OutgoingConnections = append(nsmc.OutgoingConnections, outgoingConnection)
	return outgoingConnection, nil
}

// Close will terminate a particular connection
func (nsmc *NsmClient) Close(ctx context.Context, outgoingConnection *connection.Connection) error {
	nsmc.Lock()
	defer nsmc.Unlock()

	span := spanhelper.FromContext(ctx, "Client.Close")
	defer span.Finish()
	span.LogObject("connection", outgoingConnection)

	_, err := nsmc.NsClient.Close(span.Context(), outgoingConnection)

	span.LogError(err)

	arr := nsmc.OutgoingConnections
	for i, c := range arr {
		if c == outgoingConnection {
			copy(arr[i:], arr[i+1:])
			arr[len(arr)-1] = nil
			arr = arr[:len(arr)-1]
		}
	}
	return err
}

// Destroy - Destroy stops the whole module
func (nsmc *NsmClient) Destroy(ctx context.Context) error {
	nsmc.Lock()
	defer nsmc.Unlock()

	span := spanhelper.FromContext(ctx, "Client.Destroy")
	defer span.Finish()

	err := nsmc.NsmConnection.Close()
	span.LogError(errors.Wrap(err, "failed to close opentracing context"))
	if nsmc.tracerCloser != nil {
		trErr := nsmc.tracerCloser.Close()
		if trErr != nil {
			logrus.Error(trErr)
		}
	}
	return err
}

// NewNSMClient creates the NsmClient
func NewNSMClient(ctx context.Context, configuration *common.NSConfiguration) (*NsmClient, error) {
	if configuration == nil {
		configuration = &common.NSConfiguration{}
	}

	client := &NsmClient{
		ClientNetworkService: configuration.ClientNetworkService,
		ClientLabels:         tools.ParseKVStringToMap(configuration.ClientLabels, ",", "="),
		NscInterfaceName:     configuration.NscInterfaceName,
	}

	client.tracerCloser = jaeger.InitJaeger("nsm-client")

	nsmConnection, err := common.NewNSMConnection(ctx, configuration)
	if err != nil {
		logrus.Errorf("Error: %v", err)
		return nil, err
	}

	client.NsmConnection = nsmConnection

	return client, nil
}
<|endoftext|>


<commit_before>package docker

import (
	"context"
	"github.com/ViBiOh/dashboard/auth"
	"github.com/ViBiOh/dashboard/jsonHttp"
	"log"
	"net/http"
	"regexp"
)

var available = true

const authorizationHeader = `Authorization`

type results struct {
	Results interface{} `json:"results"`
}

var gracefulCloseRequest = regexp.MustCompile(`^gracefulClose$`)
var healthRequest = regexp.MustCompile(`^health$`)
var containersRequest = regexp.MustCompile(`containers/?$`)
var containerRequest = regexp.MustCompile(`containers/([^/]+)/?$`)
var containerStartRequest = regexp.MustCompile(`containers/([^/]+)/start`)
var containerStopRequest = regexp.MustCompile(`containers/([^/]+)/stop`)
var containerRestartRequest = regexp.MustCompile(`containers/([^/]+)/restart`)
var servicesRequest = regexp.MustCompile(`services/?$`)
var infoRequest = regexp.MustCompile(`info/?$`)

func errorHandler(w http.ResponseWriter, err error) {
	log.Print(err)
	http.Error(w, err.Error(), http.StatusInternalServerError)
}

func gracefulCloseHandler(w http.ResponseWriter, r *http.Request, user *auth.User) {
	if isAdmin(user) {
		available = false
		w.WriteHeader(http.StatusAccepted)
	} else {
		w.WriteHeader(http.StatusForbidden)
	}
}

func healthHandler(w http.ResponseWriter, r *http.Request) {
	if available && docker != nil {
		w.WriteHeader(http.StatusOK)
	} else if !available {
		w.WriteHeader(http.StatusGone)
	} else if docker == nil {
		w.WriteHeader(http.StatusServiceUnavailable)
	}
}

func unauthorized(w http.ResponseWriter, err error) {
	http.Error(w, err.Error(), http.StatusUnauthorized)
}

func forbidden(w http.ResponseWriter) {
	http.Error(w, `Forbidden`, http.StatusForbidden)
}

func infoHandler(w http.ResponseWriter) {
	if info, err := docker.Info(context.Background()); err != nil {
		errorHandler(w, err)
	} else {
		jsonHttp.ResponseJSON(w, info)
	}
}

// Handler for Docker request. Should be use with net/http
type Handler struct {
}

func (handler Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Header().Add(`Access-Control-Allow-Origin`, `*`)
	w.Header().Add(`Access-Control-Allow-Headers`, `Content-Type, Authorization`)
	w.Header().Add(`Access-Control-Allow-Methods`, `GET, POST, DELETE`)
	w.Header().Add(`X-Content-Type-Options`, `nosniff`)

	if r.Method == http.MethodOptions {
		w.Write(nil)
		return
	}

	urlPath := []byte(r.URL.Path)

	if healthRequest.Match(urlPath) && r.Method == http.MethodGet {
		healthHandler(w, r)
		return
	}

	user, err := auth.IsAuthenticatedByAuth(r.Header.Get(authorizationHeader))
	if err != nil {
		unauthorized(w, err)
		return
	}

	if !available {
		w.WriteHeader(http.StatusGone)
		return
	}

	if gracefulCloseRequest.Match(urlPath) && r.Method == http.MethodPost {
		gracefulCloseHandler(w, r, user)
	} else if infoRequest.Match(urlPath) && r.Method == http.MethodGet {
		infoHandler(w)
	} else if containersRequest.Match(urlPath) && r.Method == http.MethodGet {
		listContainersHandler(w, user)
	} else if containerRequest.Match(urlPath) && r.Method == http.MethodGet {
		inspectContainerHandler(w, containerRequest.FindSubmatch(urlPath)[1])
	} else if containerStartRequest.Match(urlPath) && r.Method == http.MethodPost {
		basicActionHandler(w, user, containerStartRequest.FindSubmatch(urlPath)[1], startContainer)
	} else if containerStopRequest.Match(urlPath) && r.Method == http.MethodPost {
		basicActionHandler(w, user, containerStopRequest.FindSubmatch(urlPath)[1], stopContainer)
	} else if containerRestartRequest.Match(urlPath) && r.Method == http.MethodPost {
		basicActionHandler(w, user, containerRestartRequest.FindSubmatch(urlPath)[1], restartContainer)
	} else if containerRequest.Match(urlPath) && r.Method == http.MethodDelete {
		basicActionHandler(w, user, containerRequest.FindSubmatch(urlPath)[1], rmContainer)
	} else if containerRequest.Match(urlPath) && r.Method == http.MethodPost {
		if composeBody, err := readBody(r.Body); err != nil {
			errorHandler(w, err)
		} else {
			createAppHandler(w, user, containerRequest.FindSubmatch(urlPath)[1], composeBody)
		}
	} else if servicesRequest.Match(urlPath) && r.Method == http.MethodGet {
		listServicesHandler(w, user)
	}
}
<commit_msg>Changing graceful method calculation<commit_after>package docker

import (
	"context"
	"github.com/ViBiOh/dashboard/auth"
	"github.com/ViBiOh/dashboard/jsonHttp"
	"log"
	"net/http"
	"regexp"
	"time"
)

const authorizationHeader = `Authorization`
const gracefulCloseDelay = 30

var gracefulCloseTimestamp time.Time

type results struct {
	Results interface{} `json:"results"`
}

var gracefulCloseRequest = regexp.MustCompile(`^gracefulClose$`)
var healthRequest = regexp.MustCompile(`^health$`)
var containersRequest = regexp.MustCompile(`containers/?$`)
var containerRequest = regexp.MustCompile(`containers/([^/]+)/?$`)
var containerStartRequest = regexp.MustCompile(`containers/([^/]+)/start`)
var containerStopRequest = regexp.MustCompile(`containers/([^/]+)/stop`)
var containerRestartRequest = regexp.MustCompile(`containers/([^/]+)/restart`)
var servicesRequest = regexp.MustCompile(`services/?$`)
var infoRequest = regexp.MustCompile(`info/?$`)

func errorHandler(w http.ResponseWriter, err error) {
	log.Print(err)
	http.Error(w, err.Error(), http.StatusInternalServerError)
}

func gracefulCloseHandler(w http.ResponseWriter, r *http.Request, user *auth.User) {
	if isAdmin(user) {
		gracefulCloseTimestamp = time.Now().Add(gracefulCloseDelay * time.Second)
		w.WriteHeader(http.StatusAccepted)
	} else {
		w.WriteHeader(http.StatusForbidden)
	}
}

func healthHandler(w http.ResponseWriter, r *http.Request) {
	if time.Time.IsZero(gracefulCloseTimestamp) && docker != nil {
		w.WriteHeader(http.StatusOK)
	} else if !time.Time.IsZero(gracefulCloseTimestamp) {
		w.WriteHeader(http.StatusGone)
	} else if docker == nil {
		w.WriteHeader(http.StatusServiceUnavailable)
	}
}

func unauthorized(w http.ResponseWriter, err error) {
	http.Error(w, err.Error(), http.StatusUnauthorized)
}

func forbidden(w http.ResponseWriter) {
	http.Error(w, `Forbidden`, http.StatusForbidden)
}

func infoHandler(w http.ResponseWriter) {
	if info, err := docker.Info(context.Background()); err != nil {
		errorHandler(w, err)
	} else {
		jsonHttp.ResponseJSON(w, info)
	}
}

// Handler for Docker request. Should be use with net/http
type Handler struct {
}

func (handler Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Header().Add(`Access-Control-Allow-Origin`, `*`)
	w.Header().Add(`Access-Control-Allow-Headers`, `Content-Type, Authorization`)
	w.Header().Add(`Access-Control-Allow-Methods`, `GET, POST, DELETE`)
	w.Header().Add(`X-Content-Type-Options`, `nosniff`)

	if r.Method == http.MethodOptions {
		w.Write(nil)
		return
	}

	urlPath := []byte(r.URL.Path)

	if healthRequest.Match(urlPath) && r.Method == http.MethodGet {
		healthHandler(w, r)
		return
	}

	user, err := auth.IsAuthenticatedByAuth(r.Header.Get(authorizationHeader))
	if err != nil {
		unauthorized(w, err)
		return
	}

	if !time.Time.IsZero(gracefulCloseTimestamp) && time.Now().After(gracefulCloseTimestamp) {
		w.WriteHeader(http.StatusGone)
		return
	}

	if gracefulCloseRequest.Match(urlPath) && r.Method == http.MethodPost {
		gracefulCloseHandler(w, r, user)
	} else if infoRequest.Match(urlPath) && r.Method == http.MethodGet {
		infoHandler(w)
	} else if containersRequest.Match(urlPath) && r.Method == http.MethodGet {
		listContainersHandler(w, user)
	} else if containerRequest.Match(urlPath) && r.Method == http.MethodGet {
		inspectContainerHandler(w, containerRequest.FindSubmatch(urlPath)[1])
	} else if containerStartRequest.Match(urlPath) && r.Method == http.MethodPost {
		basicActionHandler(w, user, containerStartRequest.FindSubmatch(urlPath)[1], startContainer)
	} else if containerStopRequest.Match(urlPath) && r.Method == http.MethodPost {
		basicActionHandler(w, user, containerStopRequest.FindSubmatch(urlPath)[1], stopContainer)
	} else if containerRestartRequest.Match(urlPath) && r.Method == http.MethodPost {
		basicActionHandler(w, user, containerRestartRequest.FindSubmatch(urlPath)[1], restartContainer)
	} else if containerRequest.Match(urlPath) && r.Method == http.MethodDelete {
		basicActionHandler(w, user, containerRequest.FindSubmatch(urlPath)[1], rmContainer)
	} else if containerRequest.Match(urlPath) && r.Method == http.MethodPost {
		if composeBody, err := readBody(r.Body); err != nil {
			errorHandler(w, err)
		} else {
			createAppHandler(w, user, containerRequest.FindSubmatch(urlPath)[1], composeBody)
		}
	} else if servicesRequest.Match(urlPath) && r.Method == http.MethodGet {
		listServicesHandler(w, user)
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2014-2015 by Michael Dvorkin. All Rights Reserved.
// Use of this source code is governed by a MIT-style license that can
// be found in the LICENSE file.

package donna

// Quiescence search.
func (p *Position) searchQuiescence(alpha, beta, iteration int, inCheck bool) (score int) {
	ply := ply()

	// Reset principal variation.
	game.pv[ply] = game.pv[ply][:0]

	// Return if it's time to stop search.
	if ply >= MaxPly || engine.clock.halt {
		return p.Evaluate()
	}

	// Insufficient material and repetition/perpetual check pruning.
	if p.insufficient() || p.repetition() || p.fifty() {
		return 0
	}

	// If you pick up a starving dog and make him prosperous, he will not
	// bite you. This is the principal difference between a dog and a man.
        // ―- Mark Twain
	isPrincipal := (beta - alpha > 1)

	// Use fixed depth for caching.
	depth := 0
	if !inCheck && iteration > 0 {
		depth--
	}

	// Probe cache.
	staticScore := alpha
	if cached := p.probeCache(); cached != nil {
		if int(cached.depth) >= depth {
			staticScore = uncache(int(cached.score), ply)
			if (cached.flags == cacheExact && isPrincipal) ||
			   (cached.flags == cacheBeta  && staticScore >= beta) ||
			   (cached.flags == cacheAlpha && staticScore <= alpha) {
				return staticScore
			}
		}
	}

	if !inCheck {
		staticScore = p.Evaluate()
		if staticScore >= beta {
			p.cache(Move(0), staticScore, depth, ply, cacheBeta)
			return staticScore
		}
		if isPrincipal {
			alpha = max(alpha, staticScore)
		}
	}

	// Generate check evasions or captures.
	gen := NewGen(p, ply)
	if inCheck {
		gen.generateEvasions()
	} else {
		gen.generateCaptures()
	}
	gen.quickRank()

	cacheFlags := cacheAlpha
	moveCount, bestMove := 0, Move(0)
	for move := gen.NextMove(); move != 0; move = gen.NextMove() {
		if (!inCheck && p.exchange(move) < 0) || !gen.isValid(move) {
			continue
		}

		position := p.makeMove(move)
		moveCount++
		giveCheck := position.isInCheck(position.color)

		// Prune useless captures -- but make sure it's not a capture move that checks.
		if !inCheck && !giveCheck && !isPrincipal && !move.isPromo() && staticScore + pieceValue[move.capture()] + 72 < alpha {
			position.undoLastMove()
			continue
		}
		score = -position.searchQuiescence(-beta, -alpha, iteration + 1, giveCheck)
		position.undoLastMove()

		if score > alpha {
			alpha = score
			bestMove = move
			if alpha >= beta {
				p.cache(bestMove, score, depth, ply, cacheBeta)
				game.qnodes += moveCount
				return
			}
			cacheFlags = cacheExact
		}
		if engine.clock.halt {
			game.qnodes += moveCount
			return alpha
		}
	}

	if !inCheck && iteration < 1 {
		gen = NewGen(p, ply).generateChecks().quickRank()
		for move := gen.NextMove(); move != 0; move = gen.NextMove() {
			if p.exchange(move) < 0 || !gen.isValid(move) {
				continue
			}

			position := p.makeMove(move)
			moveCount++
			score = -position.searchQuiescence(-beta, -alpha, iteration + 1, position.isInCheck(position.color))
			position.undoLastMove()

			if score > alpha {
				alpha = score
				bestMove = move
				if alpha >= beta {
					p.cache(bestMove, score, depth, ply, cacheBeta)
					game.qnodes += moveCount
					return
				}
				cacheFlags = cacheExact
			}
			if engine.clock.halt {
				game.qnodes += moveCount
				return alpha
			}
		}
	}

	game.qnodes += moveCount

	score = alpha
	if inCheck && moveCount == 0 {
		score = -Checkmate + ply
	}
	p.cache(bestMove, score, depth, ply, cacheFlags)

	return
}
<commit_msg>More compact quiescence search<commit_after>// Copyright (c) 2014-2015 by Michael Dvorkin. All Rights Reserved.
// Use of this source code is governed by a MIT-style license that can
// be found in the LICENSE file.

package donna

// Quiescence search.
func (p *Position) searchQuiescence(alpha, beta, iteration int, inCheck bool) (score int) {
	ply := ply()

	// Reset principal variation.
	game.pv[ply] = game.pv[ply][:0]

	// Return if it's time to stop search.
	if ply >= MaxPly || engine.clock.halt {
		return p.Evaluate()
	}

	// Insufficient material and repetition/perpetual check pruning.
	if p.insufficient() || p.repetition() || p.fifty() {
		return 0
	}

	// If you pick up a starving dog and make him prosperous, he will not
	// bite you. This is the principal difference between a dog and a man.
        // ―- Mark Twain
	isPrincipal := (beta - alpha > 1)

	// Use fixed depth for caching.
	depth := 0
	if !inCheck && iteration > 0 {
		depth--
	}

	// Probe cache.
	staticScore := alpha
	if cached := p.probeCache(); cached != nil {
		if int(cached.depth) >= depth {
			staticScore = uncache(int(cached.score), ply)
			if (cached.flags == cacheExact && isPrincipal) ||
			   (cached.flags == cacheBeta  && staticScore >= beta) ||
			   (cached.flags == cacheAlpha && staticScore <= alpha) {
				return staticScore
			}
		}
	}

	if !inCheck {
		staticScore = p.Evaluate()
		if staticScore >= beta {
			p.cache(Move(0), staticScore, depth, ply, cacheBeta)
			return staticScore
		}
		if isPrincipal {
			alpha = max(alpha, staticScore)
		}
	}

	// Generate check evasions or captures.
	gen := NewGen(p, ply)
	if inCheck {
		gen.generateEvasions()
	} else {
		gen.generateCaptures()
		if iteration < 1 {
			gen.generateChecks()
		}
	}
	gen.quickRank()

	cacheFlags := cacheAlpha
	moveCount, bestMove := 0, Move(0)
	for move := gen.NextMove(); move != 0; move = gen.NextMove() {
		capture := move.capture()
		if (!inCheck && capture != 0 && p.exchange(move) < 0) || !gen.isValid(move) {
			continue
		}

		position := p.makeMove(move)
		moveCount++
		giveCheck := position.isInCheck(position.color)

		// Prune useless captures -- but make sure it's not a capture move that checks.
		if !inCheck && !giveCheck && !isPrincipal && !move.isPromo() && capture != 0 && staticScore + pieceValue[capture] + 72 < alpha {
			position.undoLastMove()
			continue
		}
		score = -position.searchQuiescence(-beta, -alpha, iteration + 1, giveCheck)
		position.undoLastMove()

		if score > alpha {
			alpha = score
			bestMove = move
			if alpha >= beta {
				p.cache(bestMove, score, depth, ply, cacheBeta)
				game.qnodes += moveCount
				return
			}
			cacheFlags = cacheExact
		}
		if engine.clock.halt {
			game.qnodes += moveCount
			return alpha
		}
	}

	game.qnodes += moveCount

	score = alpha
	if inCheck && moveCount == 0 {
		score = -Checkmate + ply
	}
	p.cache(bestMove, score, depth, ply, cacheFlags)

	return
}
<|endoftext|>


<commit_before>package couchbase

import (
	"errors"
	"log"
	"runtime/debug"
	"strings"
	"sync"

	"github.com/couchbase/indexing/secondary/dcp/transport"
	"github.com/couchbase/indexing/secondary/dcp/transport/client"
)

// ErrorInvalidVbucket
var ErrorInvalidVbucket = errors.New("dcp.invalidVbucket")

// ErrorConnectionOverflow
var ErrorConnectionOverflow = errors.New("dcp.connectionOverflow")

// ErrorFailoverLog
var ErrorFailoverLog = errors.New("dcp.failoverLog")

// ErrorInvalidBucket
var ErrorInvalidBucket = errors.New("dcp.invalidBucket")

// ErrorInvalidFeed
var ErrorInvalidFeed = errors.New("dcp.invalidFeed")

// ErrorClosed
var ErrorClosed = errors.New("dcp.closed")

// GetFailoverLogs, get the failover logs for a set of vbucket ids
func (b *Bucket) GetFailoverLogs(vBuckets []uint16) (FailoverLog, error) {
	// map vbids to their corresponding hosts
	vbHostList := make(map[string][]uint16)
	vbm := b.VBServerMap()
	for _, vb := range vBuckets {
		if l := len(vbm.VBucketMap); int(vb) >= l {
			log.Printf("error invalid vbucket id %d >= %d\n", vb, l)
			return nil, ErrorInvalidVbucket
		}

		masterID := vbm.VBucketMap[vb][0]
		master := b.getMasterNode(masterID)
		if master == "" {
			log.Printf("error master node not found for vbucket %d\n", vb)
			return nil, ErrorInvalidVbucket
		}

		vbList := vbHostList[master]
		if vbList == nil {
			vbList = make([]uint16, 0)
		}
		vbList = append(vbList, vb)
		vbHostList[master] = vbList
	}

	failoverLogMap := make(FailoverLog)
	for _, serverConn := range b.getConnPools() {
		vbList := vbHostList[serverConn.host]
		if vbList == nil {
			continue
		}

		mc, err := serverConn.Get()
		if err != nil {
			log.Printf("error connections overflow for vblist %v\n", vbList)
			return nil, ErrorConnectionOverflow
		}
		mc.Hijack()

		failoverlogs, err := mc.UprGetFailoverLog(vbList)
		if err != nil {
			format := "error getting failover log for host %s: %v\n"
			log.Printf(format, serverConn.host, err)
			return nil, ErrorFailoverLog
		}
		for vb, log := range failoverlogs {
			failoverLogMap[vb] = *log
		}
		serverConn.Return(mc)
	}

	return failoverLogMap, nil
}

// UprFeed from a single connection
type FeedInfo struct {
	uprFeed *memcached.UprFeed // UPR feed handle
	host    string             // hostname
	healthy bool
	mu      sync.Mutex
}

type FailoverLog map[uint16]memcached.FailoverLog

// A UprFeed streams mutation events from a bucket.
//
// Events from the bucket can be read from the channel 'C'.
// Remember to call Close() on it when you're done, unless
// its channel has closed itself already.
type UprFeed struct {
	C <-chan *memcached.UprEvent

	bucket    *Bucket
	nodeFeeds map[string]*FeedInfo     // The UPR feeds of the individual nodes
	output    chan *memcached.UprEvent // Same as C but writeably-typed
	name      string                   // name of this UPR feed
	sequence  uint32                   // sequence number for this feed
	// gen-server
	reqch  chan []interface{}
	finch  chan bool
	wgroup sync.WaitGroup
}

// StartUprFeed creates and starts a new Upr feed.
// No data will be sent on the channel unless vbuckets streams
// are requested.
func (b *Bucket) StartUprFeed(name string, sequence uint32) (*UprFeed, error) {
	return b.StartUprFeedOver(name, sequence, nil)
}

// StartUprFeed creates and starts a new Upr feed.
// No data will be sent on the channel unless vbuckets streams
// are requested. Connections will be made only to specified
// kvnodes `kvaddrs`, to connect will all kvnodes hosting the bucket,
// pass `kvaddrs` as nil
func (b *Bucket) StartUprFeedOver(
	name string, sequence uint32, kvaddrs []string) (*UprFeed, error) {

	feed := &UprFeed{
		bucket:    b,
		output:    make(chan *memcached.UprEvent, 10), // TODO: no magic num.
		nodeFeeds: make(map[string]*FeedInfo),
		name:      name,
		sequence:  sequence,
		reqch:     make(chan []interface{}, 16), // TODO: no magic num.
		finch:     make(chan bool),
	}
	feed.C = feed.output
	err := feed.connectToNodes(kvaddrs)
	if err != nil {
		log.Printf("error cannot connect to bucket %v\n", err)
		return nil, ErrorInvalidBucket
	}
	go feed.genServer(feed.reqch)
	return feed, nil
}

const (
	ufCmdRequestStream byte = iota + 1
	ufCmdCloseStream
	ufCmdClose
)

// UprRequestStream starts a stream for a vb on a feed
// and immediately returns, it is upto the channel listener
// to detect StreamBegin.
// Synchronous call.
func (feed *UprFeed) UprRequestStream(vb uint16, opaque uint16, flags uint32,
	vbuuid, startSequence, endSequence, snapStart, snapEnd uint64) error {

	respch := make(chan []interface{}, 1)
	cmd := []interface{}{
		ufCmdRequestStream, vb, opaque, flags, vbuuid, startSequence,
		endSequence, snapStart, snapEnd, respch}
	resp, err := failsafeOp(feed.reqch, respch, cmd, feed.finch)
	return opError(err, resp, 0)
}

// UprCloseStream closes a stream for a vb on a feed
// and immediately returns, it is upto the channel listener
// to detect StreamEnd.
func (feed *UprFeed) UprCloseStream(vb, opaqueMSB uint16) error {
	respch := make(chan []interface{}, 1)
	cmd := []interface{}{ufCmdCloseStream, vb, opaqueMSB, respch}
	resp, err := failsafeOp(feed.reqch, respch, cmd, feed.finch)
	return opError(err, resp, 0)
}

// Close UprFeed. Synchronous call.
func (feed *UprFeed) Close() error {
	respch := make(chan []interface{}, 1)
	cmd := []interface{}{ufCmdClose, respch}
	resp, err := failsafeOp(feed.reqch, respch, cmd, feed.finch)
	return opError(err, resp, 0)
}

func (feed *UprFeed) genServer(reqch chan []interface{}) {
	defer func() { // panic safe
		if r := recover(); r != nil {
			log.Printf("error UprFeed for %v crashed: %v\n", feed.bucket, r)
			stackTrace(string(debug.Stack()))
		}
	}()

loop:
	for {
		select {
		case msg := <-reqch:
			cmd := msg[0].(byte)
			switch cmd {
			case ufCmdRequestStream:
				vb, opaque := msg[1].(uint16), msg[2].(uint16)
				flags, vbuuid := msg[3].(uint32), msg[4].(uint64)
				startSeq, endSeq := msg[5].(uint64), msg[6].(uint64)
				snapStart, snapEnd := msg[7].(uint64), msg[8].(uint64)
				err := feed.uprRequestStream(
					vb, opaque, flags, vbuuid, startSeq, endSeq,
					snapStart, snapEnd)
				respch := msg[9].(chan []interface{})
				respch <- []interface{}{err}

			case ufCmdCloseStream:
				vb, opaqueMSB := msg[1].(uint16), msg[2].(uint16)
				err := feed.uprCloseStream(vb, opaqueMSB)
				respch := msg[3].(chan []interface{})
				respch <- []interface{}{err}

			case ufCmdClose:
				respch := msg[1].(chan []interface{})
				respch <- []interface{}{nil}
				break loop
			}
		}
	}

	close(feed.finch)
	feed.wgroup.Wait()
	feed.nodeFeeds = nil
	close(feed.output)
}

func (feed *UprFeed) connectToNodes(kvaddrs []string) error {
	kvcache := make(map[string]bool)
	m, err := feed.bucket.GetVBmap(kvaddrs)
	if err != nil {
		return err
	}
	for kvaddr := range m {
		kvcache[kvaddr] = true
	}

	for _, serverConn := range feed.bucket.getConnPools() {
		if _, ok := kvcache[serverConn.host]; !ok {
			continue
		}
		feedInfo := feed.nodeFeeds[serverConn.host]
		if feedInfo != nil && feedInfo.isHealthy() == true {
			continue
		}

		var name string
		if feed.name == "" {
			name = "DefaultUprClient"
		} else {
			name = feed.name
		}
		singleFeed, err := serverConn.StartUprFeed(name, feed.sequence)
		if err != nil {
			format := "dcp-client: Error connecting to upr feed of %s: %v"
			log.Printf(format, serverConn.host, err)
			for _, f := range feed.nodeFeeds {
				f.uprFeed.Close()
			}
			return ErrorInvalidFeed
		}
		// add the node to the connection map
		feedInfo = &FeedInfo{
			uprFeed: singleFeed,
			healthy: true,
			host:    serverConn.host,
		}
		feed.nodeFeeds[serverConn.host] = feedInfo
		feed.wgroup.Add(1)
		go feed.forwardUprEvents(feedInfo, feed.finch)
	}
	return nil
}

func (feed *UprFeed) uprRequestStream(vb uint16, opaque uint16, flags uint32,
	vbuuid, startSequence, endSequence, snapStart, snapEnd uint64) error {

	vbm := feed.bucket.VBServerMap()
	if l := len(vbm.VBucketMap); int(vb) >= l {
		log.Printf("error invalid vbucket id %d >= %d\n", vb, l)
		return ErrorInvalidVbucket
	}

	masterID := vbm.VBucketMap[vb][0]
	master := feed.bucket.getMasterNode(masterID)
	if master == "" {
		log.Printf("error master node not found for vbucket %d\n", vb)
		return ErrorInvalidVbucket
	}
	log.Printf("Posting UPR_REQUEST to %v\n", master)
	singleFeed, ok := feed.nodeFeeds[master]
	if !ok {
		log.Printf("error UprFeed for host %q (vb:%d) not found", master, vb)
		return ErrorInvalidFeed
	}
	if err := singleFeed.uprFeed.UprRequestStream(vb, opaque, flags,
		vbuuid, startSequence, endSequence, snapStart, snapEnd); err != nil {
		return err
	}
	return nil
}

func (feed *UprFeed) uprCloseStream(vb, opaqueMSB uint16) error {
	vbm := feed.bucket.VBServerMap()
	if l := len(vbm.VBucketMap); int(vb) >= l {
		log.Printf("error invalid vbucket id %d >= %d\n", vb, l)
		return ErrorInvalidVbucket
	}

	masterID := vbm.VBucketMap[vb][0]
	master := feed.bucket.getMasterNode(masterID)
	if master == "" {
		log.Printf("error master node not found for vbucket %d\n", vb)
		return ErrorInvalidVbucket
	}
	singleFeed, ok := feed.nodeFeeds[master]
	if !ok {
		log.Printf("error UprFeed for host %q (vb:%d) not found", master, vb)
		return ErrorInvalidFeed
	}
	if err := singleFeed.uprFeed.CloseStream(vb, opaqueMSB); err != nil {
		return err
	}
	return nil
}

// go routine
func (feed *UprFeed) forwardUprEvents(nodeFeed *FeedInfo, finch chan bool) {
	singleFeed := nodeFeed.uprFeed
loop:
	for {
		select {
		case event, ok := <-singleFeed.C:
			if !ok {
				if singleFeed.Error != nil {
					format := "dcp-client: Upr feed from %s failed: %v"
					log.Printf(format, nodeFeed.host, singleFeed.Error)
				}
				break loop
			}
			feed.output <- event
			if event.Status == transport.NOT_MY_VBUCKET {
				log.Printf("Got a not my vbucket error !! ")
				if err := feed.bucket.Refresh(); err != nil {
					log.Printf("error unable to refresh bucket : %v", err)
					break loop
				}
			}

		case <-finch:
			break loop
		}
	}

	feed.wgroup.Done()
	go feed.Close()
	nodeFeed.uprFeed.Close()

	nodeFeed.mu.Lock()
	defer nodeFeed.mu.Unlock()
	nodeFeed.healthy = false
}

func (nodeFeed *FeedInfo) isHealthy() bool {
	nodeFeed.mu.Lock()
	defer nodeFeed.mu.Unlock()
	return nodeFeed.healthy
}

// failsafeOp can be used by gen-server implementors to avoid infinitely
// blocked API calls.
func failsafeOp(
	reqch, respch chan []interface{},
	cmd []interface{},
	finch chan bool) ([]interface{}, error) {

	select {
	case reqch <- cmd:
		if respch != nil {
			select {
			case resp := <-respch:
				return resp, nil
			case <-finch:
				return nil, ErrorClosed
			}
		}
	case <-finch:
		return nil, ErrorClosed
	}
	return nil, nil
}

// stackTrace formats the output of debug.Stack()
func stackTrace(s string) {
	for _, line := range strings.Split(s, "\n") {
		log.Printf("%s\n", line)
	}
}

// opError suppliments FailsafeOp used by gen-servers.
func opError(err error, vals []interface{}, idx int) error {
	if err != nil {
		return err
	} else if vals[idx] == nil {
		return nil
	}
	return vals[idx].(error)
}
<commit_msg>Bugfix: Connection leak when GetFailoverLogs() call breaks.<commit_after>package couchbase

import (
	"errors"
	"log"
	"runtime/debug"
	"strings"
	"sync"

	"github.com/couchbase/indexing/secondary/dcp/transport"
	"github.com/couchbase/indexing/secondary/dcp/transport/client"
)

// ErrorInvalidVbucket
var ErrorInvalidVbucket = errors.New("dcp.invalidVbucket")

// ErrorConnectionOverflow
var ErrorConnectionOverflow = errors.New("dcp.connectionOverflow")

// ErrorFailoverLog
var ErrorFailoverLog = errors.New("dcp.failoverLog")

// ErrorInvalidBucket
var ErrorInvalidBucket = errors.New("dcp.invalidBucket")

// ErrorInvalidFeed
var ErrorInvalidFeed = errors.New("dcp.invalidFeed")

// ErrorClosed
var ErrorClosed = errors.New("dcp.closed")

// GetFailoverLogs, get the failover logs for a set of vbucket ids
func (b *Bucket) GetFailoverLogs(vBuckets []uint16) (FailoverLog, error) {
	// map vbids to their corresponding hosts
	vbHostList := make(map[string][]uint16)
	vbm := b.VBServerMap()
	for _, vb := range vBuckets {
		if l := len(vbm.VBucketMap); int(vb) >= l {
			log.Printf("error invalid vbucket id %d >= %d\n", vb, l)
			return nil, ErrorInvalidVbucket
		}

		masterID := vbm.VBucketMap[vb][0]
		master := b.getMasterNode(masterID)
		if master == "" {
			log.Printf("error master node not found for vbucket %d\n", vb)
			return nil, ErrorInvalidVbucket
		}

		vbList := vbHostList[master]
		if vbList == nil {
			vbList = make([]uint16, 0)
		}
		vbList = append(vbList, vb)
		vbHostList[master] = vbList
	}

	failoverLogMap := make(FailoverLog)
	for _, serverConn := range b.getConnPools() {
		vbList := vbHostList[serverConn.host]
		if vbList == nil {
			continue
		}

		mc, err := serverConn.Get()
		if err != nil {
			log.Printf("error connections overflow for vblist %v\n", vbList)
			return nil, ErrorConnectionOverflow
		}
		mc.Hijack()
		defer serverConn.Return(mc)

		failoverlogs, err := mc.UprGetFailoverLog(vbList)
		if err != nil {
			format := "error getting failover log for host %s: %v\n"
			log.Printf(format, serverConn.host, err)
			return nil, ErrorFailoverLog
		}
		for vb, log := range failoverlogs {
			failoverLogMap[vb] = *log
		}
	}

	return failoverLogMap, nil
}

// UprFeed from a single connection
type FeedInfo struct {
	uprFeed *memcached.UprFeed // UPR feed handle
	host    string             // hostname
	healthy bool
	mu      sync.Mutex
}

type FailoverLog map[uint16]memcached.FailoverLog

// A UprFeed streams mutation events from a bucket.
//
// Events from the bucket can be read from the channel 'C'.
// Remember to call Close() on it when you're done, unless
// its channel has closed itself already.
type UprFeed struct {
	C <-chan *memcached.UprEvent

	bucket    *Bucket
	nodeFeeds map[string]*FeedInfo     // The UPR feeds of the individual nodes
	output    chan *memcached.UprEvent // Same as C but writeably-typed
	name      string                   // name of this UPR feed
	sequence  uint32                   // sequence number for this feed
	// gen-server
	reqch  chan []interface{}
	finch  chan bool
	wgroup sync.WaitGroup
}

// StartUprFeed creates and starts a new Upr feed.
// No data will be sent on the channel unless vbuckets streams
// are requested.
func (b *Bucket) StartUprFeed(name string, sequence uint32) (*UprFeed, error) {
	return b.StartUprFeedOver(name, sequence, nil)
}

// StartUprFeed creates and starts a new Upr feed.
// No data will be sent on the channel unless vbuckets streams
// are requested. Connections will be made only to specified
// kvnodes `kvaddrs`, to connect will all kvnodes hosting the bucket,
// pass `kvaddrs` as nil
func (b *Bucket) StartUprFeedOver(
	name string, sequence uint32, kvaddrs []string) (*UprFeed, error) {

	feed := &UprFeed{
		bucket:    b,
		output:    make(chan *memcached.UprEvent, 10), // TODO: no magic num.
		nodeFeeds: make(map[string]*FeedInfo),
		name:      name,
		sequence:  sequence,
		reqch:     make(chan []interface{}, 16), // TODO: no magic num.
		finch:     make(chan bool),
	}
	feed.C = feed.output
	err := feed.connectToNodes(kvaddrs)
	if err != nil {
		log.Printf("error cannot connect to bucket %v\n", err)
		return nil, ErrorInvalidBucket
	}
	go feed.genServer(feed.reqch)
	return feed, nil
}

const (
	ufCmdRequestStream byte = iota + 1
	ufCmdCloseStream
	ufCmdClose
)

// UprRequestStream starts a stream for a vb on a feed
// and immediately returns, it is upto the channel listener
// to detect StreamBegin.
// Synchronous call.
func (feed *UprFeed) UprRequestStream(vb uint16, opaque uint16, flags uint32,
	vbuuid, startSequence, endSequence, snapStart, snapEnd uint64) error {

	respch := make(chan []interface{}, 1)
	cmd := []interface{}{
		ufCmdRequestStream, vb, opaque, flags, vbuuid, startSequence,
		endSequence, snapStart, snapEnd, respch}
	resp, err := failsafeOp(feed.reqch, respch, cmd, feed.finch)
	return opError(err, resp, 0)
}

// UprCloseStream closes a stream for a vb on a feed
// and immediately returns, it is upto the channel listener
// to detect StreamEnd.
func (feed *UprFeed) UprCloseStream(vb, opaqueMSB uint16) error {
	respch := make(chan []interface{}, 1)
	cmd := []interface{}{ufCmdCloseStream, vb, opaqueMSB, respch}
	resp, err := failsafeOp(feed.reqch, respch, cmd, feed.finch)
	return opError(err, resp, 0)
}

// Close UprFeed. Synchronous call.
func (feed *UprFeed) Close() error {
	respch := make(chan []interface{}, 1)
	cmd := []interface{}{ufCmdClose, respch}
	resp, err := failsafeOp(feed.reqch, respch, cmd, feed.finch)
	return opError(err, resp, 0)
}

func (feed *UprFeed) genServer(reqch chan []interface{}) {
	defer func() { // panic safe
		if r := recover(); r != nil {
			log.Printf("error UprFeed for %v crashed: %v\n", feed.bucket, r)
			stackTrace(string(debug.Stack()))
		}
	}()

loop:
	for {
		select {
		case msg := <-reqch:
			cmd := msg[0].(byte)
			switch cmd {
			case ufCmdRequestStream:
				vb, opaque := msg[1].(uint16), msg[2].(uint16)
				flags, vbuuid := msg[3].(uint32), msg[4].(uint64)
				startSeq, endSeq := msg[5].(uint64), msg[6].(uint64)
				snapStart, snapEnd := msg[7].(uint64), msg[8].(uint64)
				err := feed.uprRequestStream(
					vb, opaque, flags, vbuuid, startSeq, endSeq,
					snapStart, snapEnd)
				respch := msg[9].(chan []interface{})
				respch <- []interface{}{err}

			case ufCmdCloseStream:
				vb, opaqueMSB := msg[1].(uint16), msg[2].(uint16)
				err := feed.uprCloseStream(vb, opaqueMSB)
				respch := msg[3].(chan []interface{})
				respch <- []interface{}{err}

			case ufCmdClose:
				respch := msg[1].(chan []interface{})
				respch <- []interface{}{nil}
				break loop
			}
		}
	}

	close(feed.finch)
	feed.wgroup.Wait()
	feed.nodeFeeds = nil
	close(feed.output)
}

func (feed *UprFeed) connectToNodes(kvaddrs []string) error {
	kvcache := make(map[string]bool)
	m, err := feed.bucket.GetVBmap(kvaddrs)
	if err != nil {
		return err
	}
	for kvaddr := range m {
		kvcache[kvaddr] = true
	}

	for _, serverConn := range feed.bucket.getConnPools() {
		if _, ok := kvcache[serverConn.host]; !ok {
			continue
		}
		feedInfo := feed.nodeFeeds[serverConn.host]
		if feedInfo != nil && feedInfo.isHealthy() == true {
			continue
		}

		var name string
		if feed.name == "" {
			name = "DefaultUprClient"
		} else {
			name = feed.name
		}
		singleFeed, err := serverConn.StartUprFeed(name, feed.sequence)
		if err != nil {
			format := "dcp-client: Error connecting to upr feed of %s: %v"
			log.Printf(format, serverConn.host, err)
			for _, f := range feed.nodeFeeds {
				f.uprFeed.Close()
			}
			return ErrorInvalidFeed
		}
		// add the node to the connection map
		feedInfo = &FeedInfo{
			uprFeed: singleFeed,
			healthy: true,
			host:    serverConn.host,
		}
		feed.nodeFeeds[serverConn.host] = feedInfo
		feed.wgroup.Add(1)
		go feed.forwardUprEvents(feedInfo, feed.finch)
	}
	return nil
}

func (feed *UprFeed) uprRequestStream(vb uint16, opaque uint16, flags uint32,
	vbuuid, startSequence, endSequence, snapStart, snapEnd uint64) error {

	vbm := feed.bucket.VBServerMap()
	if l := len(vbm.VBucketMap); int(vb) >= l {
		log.Printf("error invalid vbucket id %d >= %d\n", vb, l)
		return ErrorInvalidVbucket
	}

	masterID := vbm.VBucketMap[vb][0]
	master := feed.bucket.getMasterNode(masterID)
	if master == "" {
		log.Printf("error master node not found for vbucket %d\n", vb)
		return ErrorInvalidVbucket
	}
	log.Printf("Posting UPR_REQUEST to %v\n", master)
	singleFeed, ok := feed.nodeFeeds[master]
	if !ok {
		log.Printf("error UprFeed for host %q (vb:%d) not found", master, vb)
		return ErrorInvalidFeed
	}
	if err := singleFeed.uprFeed.UprRequestStream(vb, opaque, flags,
		vbuuid, startSequence, endSequence, snapStart, snapEnd); err != nil {
		return err
	}
	return nil
}

func (feed *UprFeed) uprCloseStream(vb, opaqueMSB uint16) error {
	vbm := feed.bucket.VBServerMap()
	if l := len(vbm.VBucketMap); int(vb) >= l {
		log.Printf("error invalid vbucket id %d >= %d\n", vb, l)
		return ErrorInvalidVbucket
	}

	masterID := vbm.VBucketMap[vb][0]
	master := feed.bucket.getMasterNode(masterID)
	if master == "" {
		log.Printf("error master node not found for vbucket %d\n", vb)
		return ErrorInvalidVbucket
	}
	singleFeed, ok := feed.nodeFeeds[master]
	if !ok {
		log.Printf("error UprFeed for host %q (vb:%d) not found", master, vb)
		return ErrorInvalidFeed
	}
	if err := singleFeed.uprFeed.CloseStream(vb, opaqueMSB); err != nil {
		return err
	}
	return nil
}

// go routine
func (feed *UprFeed) forwardUprEvents(nodeFeed *FeedInfo, finch chan bool) {
	singleFeed := nodeFeed.uprFeed
loop:
	for {
		select {
		case event, ok := <-singleFeed.C:
			if !ok {
				if singleFeed.Error != nil {
					format := "dcp-client: Upr feed from %s failed: %v"
					log.Printf(format, nodeFeed.host, singleFeed.Error)
				}
				break loop
			}
			feed.output <- event
			if event.Status == transport.NOT_MY_VBUCKET {
				log.Printf("Got a not my vbucket error !! ")
				if err := feed.bucket.Refresh(); err != nil {
					log.Printf("error unable to refresh bucket : %v", err)
					break loop
				}
			}

		case <-finch:
			break loop
		}
	}

	feed.wgroup.Done()
	go feed.Close()
	nodeFeed.uprFeed.Close()

	nodeFeed.mu.Lock()
	defer nodeFeed.mu.Unlock()
	nodeFeed.healthy = false
}

func (nodeFeed *FeedInfo) isHealthy() bool {
	nodeFeed.mu.Lock()
	defer nodeFeed.mu.Unlock()
	return nodeFeed.healthy
}

// failsafeOp can be used by gen-server implementors to avoid infinitely
// blocked API calls.
func failsafeOp(
	reqch, respch chan []interface{},
	cmd []interface{},
	finch chan bool) ([]interface{}, error) {

	select {
	case reqch <- cmd:
		if respch != nil {
			select {
			case resp := <-respch:
				return resp, nil
			case <-finch:
				return nil, ErrorClosed
			}
		}
	case <-finch:
		return nil, ErrorClosed
	}
	return nil, nil
}

// stackTrace formats the output of debug.Stack()
func stackTrace(s string) {
	for _, line := range strings.Split(s, "\n") {
		log.Printf("%s\n", line)
	}
}

// opError suppliments FailsafeOp used by gen-servers.
func opError(err error, vals []interface{}, idx int) error {
	if err != nil {
		return err
	} else if vals[idx] == nil {
		return nil
	}
	return vals[idx].(error)
}
<|endoftext|>


<commit_before>// Copyright 2016-2017 The psh Authors. All rights reserved.
package psh
<commit_msg>Test compile segment root<commit_after>// Copyright 2016-2017 The psh Authors. All rights reserved.
package psh

import "testing"

func TestSegmentRootCompile(t *testing.T) {
	expected := "\\$"
	segment := NewSegmentRoot()
	segment.Compile()
	if string(segment.Data) != expected {
		t.Fatalf("Compiled data expected to be %s but got %s", expected, segment.Data)
	}
}
<|endoftext|>


<commit_before>// fileinfo.go contains FileInfo struct with what is known
// of individual file/folder in the list and methods to fetch this info
package main

import (
	"os"
	"strconv"
)

// FileInfo is to store everything known about the file object
type FileInfo struct {
	f           os.FileInfo
	special     string // description for symlinks, device files and named pipes or unix domain sockets, empty otherwise
	description string
}

func (fi FileInfo) representSize() string {
	return strconv.Itoa(int(fi.f.Size()))
}
<commit_msg>render file description based on running mode<commit_after>// fileinfo.go contains FileInfo struct with what is known
// of individual file/folder in the list and methods to fetch this info
package main

import (
	"os"

	humanize "github.com/dustin/go-humanize"
)

// FileInfo is to store everything known about the file object
type FileInfo struct {
	f           os.FileInfo
	special     string // description for symlinks, device files and named pipes or unix domain sockets, empty otherwise
	description string
}

// Description yeilds description line appropriate to the running mode
func (fi FileInfo) Description() (description string) {
	switch {
	case mode.size:
		description = fi.representSize()
	case mode.time && mode.long:
		description = fi.representTimeDetailed()
	case mode.time:
		description = fi.representTime()

	default:
		description = fi.description

	}
	return
}

func (fi FileInfo) representSize() string {
	return humanize.Bytes(uint64(fi.f.Size()))
}

func (fi FileInfo) representTimeDetailed() string {
	return humanize.Time(fi.f.ModTime()) + " (" + fi.f.ModTime().String() + ")"
}

func (fi FileInfo) representTime() string {
	return humanize.Time(fi.f.ModTime())
}
<|endoftext|>


<commit_before>package fileutil

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

func CopyDirectory(src, dst string) error {
	fi, err := os.Stat(src)
	if err != nil {
		return err
	}

	_, err = os.Open(dst)
	if !os.IsNotExist(err) {
		return errors.New("Destination directory already exists.")
	}

	err = os.MkdirAll(dst, fi.Mode())
	if err != nil {
		return err
	}

	subfiles, err := ioutil.ReadDir(src)
	if err != nil {
		return err
	}

	for _, file := range subfiles {
		source_file := filepath.Join(src, file.Name())
		destination_file := filepath.Join(dst, file.Name())
		if file.IsDir() {
			err = CopyDirectory(source_file, destination_file)
			if err != nil {
				return err
			}
		} else {
			err = CopyFile(source_file, destination_file)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

func CopyFile(src, dst string) error {
	source_file, err := os.Open(src)
	if err != nil {
		return err
	}
	source_stat, err := os.Stat(src)
	if err != nil {
		return err
	}
	destination_file, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY, source_stat.Mode())
	if err != nil {
		return err
	}
	_, err = io.Copy(source_file, destination_file)
	return err
}
<commit_msg>Swapped the src/dst arguments around.<commit_after>package fileutil

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

func CopyDirectory(dst, src string) error {
	fi, err := os.Stat(src)
	if err != nil {
		return err
	}

	_, err = os.Open(dst)
	if !os.IsNotExist(err) {
		return errors.New("Destination directory already exists.")
	}

	err = os.MkdirAll(dst, fi.Mode())
	if err != nil {
		return err
	}

	subfiles, err := ioutil.ReadDir(src)
	if err != nil {
		return err
	}

	for _, file := range subfiles {
		source_file := filepath.Join(src, file.Name())
		destination_file := filepath.Join(dst, file.Name())
		if file.IsDir() {
			err = CopyDirectory(destination_file, source_file)
			if err != nil {
				return err
			}
		} else {
			err = CopyFile(destination_file, source_file)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

func CopyFile(dst, src string) error {
	source_file, err := os.Open(src)
	if err != nil {
		return err
	}
	source_stat, err := os.Stat(src)
	if err != nil {
		return err
	}
	destination_file, err := os.OpenFile(dst, os.O_CREATE|os.O_RDWR, source_stat.Mode())
	if err != nil {
		return err
	}
	defer destination_file.Close()
	_, err = io.Copy(destination_file, source_file)
	return err
}
<|endoftext|>


<commit_before>package model

import (
	"fmt"
	"strings"
	"time"

	"github.com/go-sql-driver/mysql"
	"github.com/jinzhu/gorm"
	"github.com/oinume/lekcije/server/errors"
)

type User struct {
	ID                uint32 `gorm:"primary_key;AUTO_INCREMENT"`
	Name              string
	Email             string
	EmailVerified     bool
	PlanID            uint8
	FollowedTeacherAt mysql.NullTime
	CreatedAt         time.Time
	UpdatedAt         time.Time
}

func (*User) TableName() string {
	return "user"
}

type UserService struct {
	db *gorm.DB
}

func NewUserService(db *gorm.DB) *UserService {
	return &UserService{db: db}
}

func (s *UserService) TableName() string {
	return (&User{}).TableName()
}

func (s *UserService) FindByPK(id uint32) (*User, error) {
	user := &User{}
	if result := s.db.First(user, &User{ID: id}); result.Error != nil {
		if err := wrapNotFound(result, user.TableName(), "id", fmt.Sprint(id)); err != nil {
			return nil, err
		}
		return nil, errors.NewInternalError(
			errors.WithError(result.Error),
			errors.WithResource(errors.NewResource(user.TableName(), "id", id)),
		)
	}
	if err := s.db.First(user, &User{ID: id}).Error; err != nil {
		return nil, err
	}
	return user, nil
}

func (s *UserService) FindByEmail(email string) (*User, error) {
	user := &User{}
	if result := s.db.First(user, &User{Email: email}); result.Error != nil {
		if err := wrapNotFound(result, user.TableName(), "email", email); err != nil {
			return nil, err
		}
		return nil, errors.NewInternalError(
			errors.WithError(result.Error),
			errors.WithResource(errors.NewResource(user.TableName(), "email", email)),
		)
	}
	return user, nil
}

func (s *UserService) FindByGoogleID(googleID string) (*User, error) {
	user := &User{}
	sql := `
	SELECT u.* FROM user AS u
	INNER JOIN user_google AS ug ON u.id = ug.user_id
	WHERE ug.google_id = ?
	LIMIT 1
	`
	if result := s.db.Raw(sql, googleID).Scan(user); result.Error != nil {
		if err := wrapNotFound(result, "user_google", "google_id", googleID); err != nil {
			return nil, err
		}
		return nil, errors.NewInternalError(
			errors.WithError(result.Error),
			errors.WithResource(errors.NewResource("user_google", "google_id", googleID)),
		)
	}
	return user, nil
}

func (s *UserService) FindByUserAPIToken(userAPIToken string) (*User, error) {
	user := &User{}
	sql := `
	SELECT u.* FROM user AS u
	INNER JOIN user_api_token AS uat ON u.id = uat.user_id
	WHERE uat.token = ?
	`
	if result := s.db.Raw(sql, userAPIToken).Scan(user); result.Error != nil {
		if err := wrapNotFound(result, user.TableName(), "userAPIToken", userAPIToken); err != nil {
			return nil, err
		}
		return nil, errors.NewInternalError(
			errors.WithError(result.Error),
			errors.WithResource(errors.NewResource(user.TableName(), "userAPIToken", userAPIToken)),
		)
	}
	return user, nil
}

// Returns an empty slice if no users found
func (s *UserService) FindAllEmailVerifiedIsTrue(notificationInterval int) ([]*User, error) {
	var users []*User
	sql := `
	SELECT u.* FROM user AS u
	INNER JOIN m_plan AS mp ON u.plan_id = mp.id
	WHERE
	  u.email_verified = 1
	  AND mp.notification_interval = ?
	`
	result := s.db.Raw(sql, notificationInterval).Scan(&users)
	if result.Error != nil && !result.RecordNotFound() {
		return nil, errors.NewInternalError(
			errors.WithError(result.Error),
			errors.WithMessage("Failed to find Users"),
		)
	}
	return users, nil
}

// Returns an empty slice if no users found
func (s *UserService) FindAllFollowedTeacherAtIsNull(createdAt time.Time) ([]*User, error) {
	var users []*User
	sql := `SELECT * FROM user WHERE followed_teacher_at IS NULL AND CAST(created_at AS DATE) = ? ORDER BY id`
	result := s.db.Raw(sql, createdAt.Format(dbDateFormat)).Scan(&users)
	if result.Error != nil && !result.RecordNotFound() {
		return nil, errors.NewInternalError(
			errors.WithError(result.Error),
			errors.WithMessage("Failed to find users"),
		)
	}
	return users, nil
}

func (s *UserService) Create(name, email string) (*User, error) {
	user := &User{
		Name:          name,
		Email:         email,
		EmailVerified: true,
		PlanID:        DefaultMPlanID,
	}
	if result := s.db.Create(user); result.Error != nil {
		return nil, errors.NewInternalError(
			errors.WithError(result.Error),
			errors.WithMessage("Failed to Create user"),
			errors.WithResource(errors.NewResource("user", "email", email)),
		)
	}
	return user, nil
}

func (s *UserService) CreateWithGoogle(name, email, googleID string) (*User, *UserGoogle, error) {
	user, err := s.FindByEmail(email)
	if e, ok := err.(*errors.AnnotatedError); ok && e.IsNotFound() {
		user = &User{
			Name:          name,
			Email:         email,
			EmailVerified: true,
			PlanID:        DefaultMPlanID,
		}
		if result := s.db.Create(user); result.Error != nil {
			return nil, nil, errors.NewInternalError(
				errors.WithError(result.Error),
				errors.WithMessage("Failed to create User"),
				errors.WithResource(errors.NewResourceWithEntries(
					"user", []errors.ResourceEntry{
						{"email", email}, {"googleID", googleID},
					},
				)),
			)
		}
	}
	// Do nothing if the user exists.

	userGoogleService := NewUserGoogleService(s.db)
	userGoogle, err := userGoogleService.FindByUserID(user.ID)
	if e, ok := err.(*errors.AnnotatedError); ok && e.IsNotFound() {
		userGoogle = &UserGoogle{
			GoogleID: googleID,
			UserID:   user.ID,
		}
		if result := s.db.Create(userGoogle); result.Error != nil {
			return nil, nil, errors.NewInternalError(
				errors.WithError(result.Error),
				errors.WithMessage("Failed to create UserGoogle"),
				errors.WithResource(errors.NewResource("user_google", "googleID", googleID)),
			)
		}
	}
	// Do nothing if the user google exists.

	return user, userGoogle, nil
}

func (s *UserService) UpdateEmail(user *User, newEmail string) error {
	result := s.db.Exec("UPDATE user SET email = ? WHERE id = ?", newEmail, user.ID)
	if result.Error != nil {
		return errors.NewInternalError(
			errors.WithError(result.Error),
			errors.WithMessage("Failed to update user email"),
			errors.WithResource(errors.NewResourceWithEntries(
				user.TableName(), []errors.ResourceEntry{
					{"id", user.ID}, {"email", newEmail},
				},
			)),
		)
	}
	return nil
}

func (s *UserService) UpdateFollowedTeacherAt(user *User) error {
	sql := "UPDATE user SET followed_teacher_at = NOW() WHERE id = ?"
	if err := s.db.Exec(sql, user.ID).Error; err != nil {
		return errors.NewInternalError(
			errors.WithError(err),
			errors.WithMessage("Failed to update followed_teacher_at"),
			errors.WithResource(errors.NewResource(user.TableName(), "id", user.ID)),
		)
	}
	return nil
}

func (s *UserService) FindLoggedInUser(token string) (*User, error) {
	user := &User{}
	sql := `
		SELECT * FROM user AS u
		INNER JOIN user_api_token AS uat ON u.id = uat.user_id
		WHERE uat.token = ?
		`
	result := s.db.Model(&User{}).Raw(strings.TrimSpace(sql), token).Scan(user)
	if result.Error != nil {
		if err := wrapNotFound(result, user.TableName(), "token", token); err != nil {
			return nil, err
		}
		return nil, errors.NewInternalError(
			errors.WithError(result.Error),
			errors.WithResource(errors.NewResource(user.TableName(), "token", token)),
		)
	}
	return user, nil
}
<commit_msg>Add followed_teacher_at to WHERE<commit_after>package model

import (
	"fmt"
	"strings"
	"time"

	"github.com/go-sql-driver/mysql"
	"github.com/jinzhu/gorm"
	"github.com/oinume/lekcije/server/errors"
)

type User struct {
	ID                uint32 `gorm:"primary_key;AUTO_INCREMENT"`
	Name              string
	Email             string
	EmailVerified     bool
	PlanID            uint8
	FollowedTeacherAt mysql.NullTime
	CreatedAt         time.Time
	UpdatedAt         time.Time
}

func (*User) TableName() string {
	return "user"
}

type UserService struct {
	db *gorm.DB
}

func NewUserService(db *gorm.DB) *UserService {
	return &UserService{db: db}
}

func (s *UserService) TableName() string {
	return (&User{}).TableName()
}

func (s *UserService) FindByPK(id uint32) (*User, error) {
	user := &User{}
	if result := s.db.First(user, &User{ID: id}); result.Error != nil {
		if err := wrapNotFound(result, user.TableName(), "id", fmt.Sprint(id)); err != nil {
			return nil, err
		}
		return nil, errors.NewInternalError(
			errors.WithError(result.Error),
			errors.WithResource(errors.NewResource(user.TableName(), "id", id)),
		)
	}
	if err := s.db.First(user, &User{ID: id}).Error; err != nil {
		return nil, err
	}
	return user, nil
}

func (s *UserService) FindByEmail(email string) (*User, error) {
	user := &User{}
	if result := s.db.First(user, &User{Email: email}); result.Error != nil {
		if err := wrapNotFound(result, user.TableName(), "email", email); err != nil {
			return nil, err
		}
		return nil, errors.NewInternalError(
			errors.WithError(result.Error),
			errors.WithResource(errors.NewResource(user.TableName(), "email", email)),
		)
	}
	return user, nil
}

func (s *UserService) FindByGoogleID(googleID string) (*User, error) {
	user := &User{}
	sql := `
	SELECT u.* FROM user AS u
	INNER JOIN user_google AS ug ON u.id = ug.user_id
	WHERE ug.google_id = ?
	LIMIT 1
	`
	if result := s.db.Raw(sql, googleID).Scan(user); result.Error != nil {
		if err := wrapNotFound(result, "user_google", "google_id", googleID); err != nil {
			return nil, err
		}
		return nil, errors.NewInternalError(
			errors.WithError(result.Error),
			errors.WithResource(errors.NewResource("user_google", "google_id", googleID)),
		)
	}
	return user, nil
}

func (s *UserService) FindByUserAPIToken(userAPIToken string) (*User, error) {
	user := &User{}
	sql := `
	SELECT u.* FROM user AS u
	INNER JOIN user_api_token AS uat ON u.id = uat.user_id
	WHERE uat.token = ?
	`
	if result := s.db.Raw(sql, userAPIToken).Scan(user); result.Error != nil {
		if err := wrapNotFound(result, user.TableName(), "userAPIToken", userAPIToken); err != nil {
			return nil, err
		}
		return nil, errors.NewInternalError(
			errors.WithError(result.Error),
			errors.WithResource(errors.NewResource(user.TableName(), "userAPIToken", userAPIToken)),
		)
	}
	return user, nil
}

// Returns an empty slice if no users found
func (s *UserService) FindAllEmailVerifiedIsTrue(notificationInterval int) ([]*User, error) {
	var users []*User
	sql := `
	SELECT u.* FROM user AS u
	INNER JOIN m_plan AS mp ON u.plan_id = mp.id
	WHERE
	  u.email_verified = 1
	  AND u.followed_teacher_at IS NOT NULL
	  AND mp.notification_interval = ?
	`
	result := s.db.Raw(sql, notificationInterval).Scan(&users)
	if result.Error != nil && !result.RecordNotFound() {
		return nil, errors.NewInternalError(
			errors.WithError(result.Error),
			errors.WithMessage("Failed to find Users"),
		)
	}
	return users, nil
}

// Returns an empty slice if no users found
func (s *UserService) FindAllFollowedTeacherAtIsNull(createdAt time.Time) ([]*User, error) {
	var users []*User
	sql := `SELECT * FROM user WHERE followed_teacher_at IS NULL AND CAST(created_at AS DATE) = ? ORDER BY id`
	result := s.db.Raw(sql, createdAt.Format(dbDateFormat)).Scan(&users)
	if result.Error != nil && !result.RecordNotFound() {
		return nil, errors.NewInternalError(
			errors.WithError(result.Error),
			errors.WithMessage("Failed to find users"),
		)
	}
	return users, nil
}

func (s *UserService) Create(name, email string) (*User, error) {
	user := &User{
		Name:          name,
		Email:         email,
		EmailVerified: true,
		PlanID:        DefaultMPlanID,
	}
	if result := s.db.Create(user); result.Error != nil {
		return nil, errors.NewInternalError(
			errors.WithError(result.Error),
			errors.WithMessage("Failed to Create user"),
			errors.WithResource(errors.NewResource("user", "email", email)),
		)
	}
	return user, nil
}

func (s *UserService) CreateWithGoogle(name, email, googleID string) (*User, *UserGoogle, error) {
	user, err := s.FindByEmail(email)
	if e, ok := err.(*errors.AnnotatedError); ok && e.IsNotFound() {
		user = &User{
			Name:          name,
			Email:         email,
			EmailVerified: true,
			PlanID:        DefaultMPlanID,
		}
		if result := s.db.Create(user); result.Error != nil {
			return nil, nil, errors.NewInternalError(
				errors.WithError(result.Error),
				errors.WithMessage("Failed to create User"),
				errors.WithResource(errors.NewResourceWithEntries(
					"user", []errors.ResourceEntry{
						{"email", email}, {"googleID", googleID},
					},
				)),
			)
		}
	}
	// Do nothing if the user exists.

	userGoogleService := NewUserGoogleService(s.db)
	userGoogle, err := userGoogleService.FindByUserID(user.ID)
	if e, ok := err.(*errors.AnnotatedError); ok && e.IsNotFound() {
		userGoogle = &UserGoogle{
			GoogleID: googleID,
			UserID:   user.ID,
		}
		if result := s.db.Create(userGoogle); result.Error != nil {
			return nil, nil, errors.NewInternalError(
				errors.WithError(result.Error),
				errors.WithMessage("Failed to create UserGoogle"),
				errors.WithResource(errors.NewResource("user_google", "googleID", googleID)),
			)
		}
	}
	// Do nothing if the user google exists.

	return user, userGoogle, nil
}

func (s *UserService) UpdateEmail(user *User, newEmail string) error {
	result := s.db.Exec("UPDATE user SET email = ? WHERE id = ?", newEmail, user.ID)
	if result.Error != nil {
		return errors.NewInternalError(
			errors.WithError(result.Error),
			errors.WithMessage("Failed to update user email"),
			errors.WithResource(errors.NewResourceWithEntries(
				user.TableName(), []errors.ResourceEntry{
					{"id", user.ID}, {"email", newEmail},
				},
			)),
		)
	}
	return nil
}

func (s *UserService) UpdateFollowedTeacherAt(user *User) error {
	sql := "UPDATE user SET followed_teacher_at = NOW() WHERE id = ?"
	if err := s.db.Exec(sql, user.ID).Error; err != nil {
		return errors.NewInternalError(
			errors.WithError(err),
			errors.WithMessage("Failed to update followed_teacher_at"),
			errors.WithResource(errors.NewResource(user.TableName(), "id", user.ID)),
		)
	}
	return nil
}

func (s *UserService) FindLoggedInUser(token string) (*User, error) {
	user := &User{}
	sql := `
		SELECT * FROM user AS u
		INNER JOIN user_api_token AS uat ON u.id = uat.user_id
		WHERE uat.token = ?
		`
	result := s.db.Model(&User{}).Raw(strings.TrimSpace(sql), token).Scan(user)
	if result.Error != nil {
		if err := wrapNotFound(result, user.TableName(), "token", token); err != nil {
			return nil, err
		}
		return nil, errors.NewInternalError(
			errors.WithError(result.Error),
			errors.WithResource(errors.NewResource(user.TableName(), "token", token)),
		)
	}
	return user, nil
}
<|endoftext|>


<commit_before>//   Copyright 2017 MSolution.IO
//
//   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"
	"database/sql"
	"errors"
	"flag"
	"math/rand"
	"strconv"
	"time"

	"github.com/trackit/jsonlog"

	"github.com/trackit/trackit2/aws"
	"github.com/trackit/trackit2/aws/s3"
	"github.com/trackit/trackit2/db"
)

func taskIngest(ctx context.Context) error {
	args := flag.Args()
	if len(args) != 2 {
		return errors.New("taskIngest requires two integer arguments")
	} else if aa, err := strconv.Atoi(args[0]); err != nil {
		return err
	} else if br, err := strconv.Atoi(args[1]); err != nil {
		return err
	} else {
		return ingestBillingDataForBillRepository(ctx, aa, br)
	}
}

func taskIngestDue(ctx context.Context) (err error) {
	var tx *sql.Tx
	logger := jsonlog.LoggerFromContextOrDefault(ctx)
	defer func() {
		if tx != nil {
			if err != nil {
				tx.Rollback()
				logger.Debug("Rolled back transaction.", nil)
			} else {
				tx.Commit()
				logger.Debug("Commited transaction.", nil)
			}
		}
	}()
	if tx, err = db.Db.BeginTx(ctx, nil); err != nil {
	} else {
		logger.Debug("Started transaction.", nil)
		conclusion, err := s3.UpdateDueReports(ctx, tx)
		if err == nil {
			err = updateBillRepositoriesFromConclusion(ctx, tx, conclusion)
		}
	}
	return
}

func updateBillRepositoriesFromConclusion(ctx context.Context, tx *sql.Tx, ruccs []s3.ReportUpdateConclusion) error {
	for _, r := range ruccs {
		if r.Error != nil {
			return r.Error
		}
		if err := updateBillRepositoryForNextUpdate(ctx, tx, r.BillRepository, r.LastImportedManifest); err != nil {
			return err
		}
	}
	return nil
}

func ingestBillingDataForBillRepository(ctx context.Context, aaId, brId int) (err error) {
	var tx *sql.Tx
	var aa aws.AwsAccount
	var br s3.BillRepository
	logger := jsonlog.LoggerFromContextOrDefault(ctx)
	defer func() {
		if tx != nil {
			if err != nil {
				tx.Rollback()
			} else {
				tx.Commit()
			}
		}
	}()
	if tx, err = db.Db.BeginTx(ctx, nil); err != nil {
	} else if aa, err = aws.GetAwsAccountWithId(aaId, tx); err != nil {
	} else if br, err = s3.GetBillRepositoryForAwsAccountById(aa, brId, tx); err != nil {
	} else if latestManifest, err := s3.UpdateReport(ctx, aa, br); err != nil {
	} else {
		err = updateBillRepositoryForNextUpdate(ctx, tx, br, latestManifest)
	}
	if err != nil {
		logger.Error("Failed to ingest billing data.", map[string]interface{}{
			"error":            err.Error(),
			"awsAccountId":     aaId,
			"billRepositoryId": brId,
		})
	}
	return
}

const (
	UpdateIntervalMinutes = 6 * 60
	UpdateIntervalWindow  = 2 * 60
)

func updateBillRepositoryForNextUpdate(ctx context.Context, tx *sql.Tx, br s3.BillRepository, latestManifest time.Time) error {
	if latestManifest.After(br.LastImportedManifest) {
		br.LastImportedManifest = latestManifest
	}
	updateDeltaMinutes := time.Duration(UpdateIntervalMinutes-UpdateIntervalWindow/2+rand.Int63n(UpdateIntervalWindow)) * time.Minute
	br.NextUpdate = time.Now().Add(updateDeltaMinutes)
	return s3.UpdateBillRepository(br, tx)
}
<commit_msg>server: log task arguments<commit_after>//   Copyright 2017 MSolution.IO
//
//   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"
	"database/sql"
	"errors"
	"flag"
	"math/rand"
	"strconv"
	"time"

	"github.com/trackit/jsonlog"

	"github.com/trackit/trackit2/aws"
	"github.com/trackit/trackit2/aws/s3"
	"github.com/trackit/trackit2/db"
)

func taskIngest(ctx context.Context) error {
	args := flag.Args()
	logger := jsonlog.LoggerFromContextOrDefault(ctx)
	logger.Debug("Running task 'ingest'.", map[string]interface{}{
		"args": args,
	})
	if len(args) != 2 {
		return errors.New("taskIngest requires two integer arguments")
	} else if aa, err := strconv.Atoi(args[0]); err != nil {
		return err
	} else if br, err := strconv.Atoi(args[1]); err != nil {
		return err
	} else {
		return ingestBillingDataForBillRepository(ctx, aa, br)
	}
}

func taskIngestDue(ctx context.Context) (err error) {
	var tx *sql.Tx
	logger := jsonlog.LoggerFromContextOrDefault(ctx)
	defer func() {
		if tx != nil {
			if err != nil {
				tx.Rollback()
				logger.Debug("Rolled back transaction.", nil)
			} else {
				tx.Commit()
				logger.Debug("Commited transaction.", nil)
			}
		}
	}()
	if tx, err = db.Db.BeginTx(ctx, nil); err != nil {
	} else {
		logger.Debug("Started transaction.", nil)
		conclusion, err := s3.UpdateDueReports(ctx, tx)
		if err == nil {
			err = updateBillRepositoriesFromConclusion(ctx, tx, conclusion)
		}
	}
	return
}

func updateBillRepositoriesFromConclusion(ctx context.Context, tx *sql.Tx, ruccs []s3.ReportUpdateConclusion) error {
	for _, r := range ruccs {
		if r.Error != nil {
			return r.Error
		}
		if err := updateBillRepositoryForNextUpdate(ctx, tx, r.BillRepository, r.LastImportedManifest); err != nil {
			return err
		}
	}
	return nil
}

func ingestBillingDataForBillRepository(ctx context.Context, aaId, brId int) (err error) {
	var tx *sql.Tx
	var aa aws.AwsAccount
	var br s3.BillRepository
	logger := jsonlog.LoggerFromContextOrDefault(ctx)
	defer func() {
		if tx != nil {
			if err != nil {
				tx.Rollback()
			} else {
				tx.Commit()
			}
		}
	}()
	if tx, err = db.Db.BeginTx(ctx, nil); err != nil {
	} else if aa, err = aws.GetAwsAccountWithId(aaId, tx); err != nil {
	} else if br, err = s3.GetBillRepositoryForAwsAccountById(aa, brId, tx); err != nil {
	} else if latestManifest, err := s3.UpdateReport(ctx, aa, br); err != nil {
	} else {
		err = updateBillRepositoryForNextUpdate(ctx, tx, br, latestManifest)
	}
	if err != nil {
		logger.Error("Failed to ingest billing data.", map[string]interface{}{
			"error":            err.Error(),
			"awsAccountId":     aaId,
			"billRepositoryId": brId,
		})
	}
	return
}

const (
	UpdateIntervalMinutes = 6 * 60
	UpdateIntervalWindow  = 2 * 60
)

func updateBillRepositoryForNextUpdate(ctx context.Context, tx *sql.Tx, br s3.BillRepository, latestManifest time.Time) error {
	if latestManifest.After(br.LastImportedManifest) {
		br.LastImportedManifest = latestManifest
	}
	updateDeltaMinutes := time.Duration(UpdateIntervalMinutes-UpdateIntervalWindow/2+rand.Int63n(UpdateIntervalWindow)) * time.Minute
	br.NextUpdate = time.Now().Add(updateDeltaMinutes)
	return s3.UpdateBillRepository(br, tx)
}
<|endoftext|>


<commit_before>// Copyright 2020 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 tq

import (
	"context"
	"fmt"
	"net/http"
	"strings"

	taskspb "google.golang.org/genproto/googleapis/cloud/tasks/v2"

	"go.chromium.org/luci/common/logging"
	"go.chromium.org/luci/server/tq/tqtesting"
)

// TestingContext creates a scheduler that executes tasks through the given
// dispatcher (or Default one if nil) and puts it into the context as Submitter,
// so AddTask calls eventually submit tasks into this scheduler.
//
// The end result is that tasks submitted using such context end up in the
// returned Scheduler (allowing them to be examined), and when the Scheduler
// delivers them, they result in calls to corresponding handlers registered in
// the Dispatcher.
func TestingContext(ctx context.Context, d *Dispatcher) (context.Context, *tqtesting.Scheduler) {
	if d == nil {
		d = &Default
	}
	sched := &tqtesting.Scheduler{Executor: &directExecutor{d}}
	return UseSubmitter(ctx, sched), sched
}

// directExecutor implements tqtesting.Executor via handlePush.
type directExecutor struct {
	d *Dispatcher
}

func (e *directExecutor) Execute(ctx context.Context, t *tqtesting.Task, done func(retry bool)) {
	retry := false
	defer func() { done(retry) }()

	if t.Message != nil {
		panic("Executing PubSub tasks is not supported yet") // break tests loudly
	}

	var body []byte
	var headers map[string]string
	switch mt := t.Task.MessageType.(type) {
	case *taskspb.Task_HttpRequest:
		body = mt.HttpRequest.Body
		headers = mt.HttpRequest.Headers
	case *taskspb.Task_AppEngineHttpRequest:
		body = mt.AppEngineHttpRequest.Body
		headers = mt.AppEngineHttpRequest.Headers
	default:
		panic(fmt.Sprintf("Bad task, no payload: %q", t.Task))
	}

	hdr := make(http.Header, len(headers))
	for k, v := range headers {
		hdr.Set(k, v)
	}
	info := parseHeaders(hdr)

	// The direct executor doesn't emulate X-CloudTasks-* headers.
	info.ExecutionCount = t.Attempts - 1
	if index := strings.LastIndex(t.Name, "/tasks/"); index > 0 {
		info.TaskID = t.Name[index+len("/tasks/"):]
	}

	ctx = logging.SetField(ctx, fmt.Sprintf("TQ-%.8s", info.TaskID), info.ExecutionCount)
	err := e.d.handlePush(ctx, body, info)
	if err != nil {
		if !quietOnError.In(err) {
			logging.Errorf(ctx, "server/tq task error: %s", err)
		}
		retry = !Fatal.In(err)
	}
}
<commit_msg>tqtesting: don't retry on tq.Ignore<commit_after>// Copyright 2020 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 tq

import (
	"context"
	"fmt"
	"net/http"
	"strings"

	taskspb "google.golang.org/genproto/googleapis/cloud/tasks/v2"

	"go.chromium.org/luci/common/logging"
	"go.chromium.org/luci/server/tq/tqtesting"
)

// TestingContext creates a scheduler that executes tasks through the given
// dispatcher (or Default one if nil) and puts it into the context as Submitter,
// so AddTask calls eventually submit tasks into this scheduler.
//
// The end result is that tasks submitted using such context end up in the
// returned Scheduler (allowing them to be examined), and when the Scheduler
// delivers them, they result in calls to corresponding handlers registered in
// the Dispatcher.
func TestingContext(ctx context.Context, d *Dispatcher) (context.Context, *tqtesting.Scheduler) {
	if d == nil {
		d = &Default
	}
	sched := &tqtesting.Scheduler{Executor: &directExecutor{d}}
	return UseSubmitter(ctx, sched), sched
}

// directExecutor implements tqtesting.Executor via handlePush.
type directExecutor struct {
	d *Dispatcher
}

func (e *directExecutor) Execute(ctx context.Context, t *tqtesting.Task, done func(retry bool)) {
	retry := false
	defer func() { done(retry) }()

	if t.Message != nil {
		panic("Executing PubSub tasks is not supported yet") // break tests loudly
	}

	var body []byte
	var headers map[string]string
	switch mt := t.Task.MessageType.(type) {
	case *taskspb.Task_HttpRequest:
		body = mt.HttpRequest.Body
		headers = mt.HttpRequest.Headers
	case *taskspb.Task_AppEngineHttpRequest:
		body = mt.AppEngineHttpRequest.Body
		headers = mt.AppEngineHttpRequest.Headers
	default:
		panic(fmt.Sprintf("Bad task, no payload: %q", t.Task))
	}

	hdr := make(http.Header, len(headers))
	for k, v := range headers {
		hdr.Set(k, v)
	}
	info := parseHeaders(hdr)

	// The direct executor doesn't emulate X-CloudTasks-* headers.
	info.ExecutionCount = t.Attempts - 1
	if index := strings.LastIndex(t.Name, "/tasks/"); index > 0 {
		info.TaskID = t.Name[index+len("/tasks/"):]
	}

	ctx = logging.SetField(ctx, fmt.Sprintf("TQ-%.8s", info.TaskID), info.ExecutionCount)
	err := e.d.handlePush(ctx, body, info)
	if err != nil {
		if !quietOnError.In(err) {
			logging.Errorf(ctx, "server/tq task error: %s", err)
		}
		retry = !Fatal.In(err) && !Ignore.In(err)
	}
}
<|endoftext|>