<commit_before>/*
Copyright 2018 The Knative 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.
*/

// request contains logic to make polling HTTP requests against an endpoint with optional host spoofing.

package test

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

	"github.com/knative/pkg/test/logging"
	"github.com/knative/pkg/test/spoof"
)

// MatchesAny is a NOP matcher. This is useful for polling until a 200 is returned.
func MatchesAny(_ *spoof.Response) (bool, error) {
	return true, nil
}

// Retrying modifies a ResponseChecker to retry certain response codes.
func Retrying(rc spoof.ResponseChecker, codes ...int) spoof.ResponseChecker {
	return func(resp *spoof.Response) (bool, error) {
		for _, code := range codes {
			if resp.StatusCode == code {
				// Returning (false, nil) causes SpoofingClient.Poll to retry.
				// sc.logger.Infof("Retrying for code %v", resp.StatusCode)
				return false, nil
			}
		}

		// If we didn't match any retryable codes, invoke the ResponseChecker that we wrapped.
		return rc(resp)
	}
}

// IsOneOfStatusCodes checks that the response code is equal to the given one.
func IsOneOfStatusCodes(codes ...int) spoof.ResponseChecker {
	return func(resp *spoof.Response) (bool, error) {
		for _, code := range codes {
			if resp.StatusCode == code {
				return true, nil
			}
		}

		return true, fmt.Errorf("status = %d, want one of: %v", resp.StatusCode, codes)
	}
}

// IsStatusOK checks that the response code is a 200.
func IsStatusOK(resp *spoof.Response) (bool, error) {
	return IsOneOfStatusCodes(http.StatusOK)(resp)
}

// MatchesBody checks that the *first* response body matches the "expected" body, otherwise failing.
func MatchesBody(expected string) spoof.ResponseChecker {
	return func(resp *spoof.Response) (bool, error) {
		if !strings.Contains(string(resp.Body), expected) {
			// Returning (true, err) causes SpoofingClient.Poll to fail.
			return true, fmt.Errorf("body = %s, want: %s", string(resp.Body), expected)
		}

		return true, nil
	}
}

// EventuallyMatchesBody checks that the response body *eventually* matches the expected body.
// TODO(#1178): Delete me. We don't want to need this; we should be waiting for an appropriate Status instead.
func EventuallyMatchesBody(expected string) spoof.ResponseChecker {
	return func(resp *spoof.Response) (bool, error) {
		if !strings.Contains(string(resp.Body), expected) {
			// Returning (false, nil) causes SpoofingClient.Poll to retry.
			return false, nil
		}

		return true, nil
	}
}

// MatchesAllOf combines multiple ResponseCheckers to one ResponseChecker with a logical AND. The
// checkers are executed in order. The first function to trigger an error or a retry will short-circuit
// the other functions (they will not be executed).
//
// This is useful for combining a body with a status check like:
// MatchesAllOf(IsStatusOK, MatchesBody("test"))
//
// The MatchesBody check will only be executed after the IsStatusOK has passed.
func MatchesAllOf(checkers ...spoof.ResponseChecker) spoof.ResponseChecker {
	return func(resp *spoof.Response) (bool, error) {
		for _, checker := range checkers {
			done, err := checker(resp)
			if err != nil || !done {
				return done, err
			}
		}
		return true, nil
	}
}

// WaitForEndpointState will poll an endpoint until inState indicates the state is achieved,
// or default timeout is reached.
// If resolvableDomain is false, it will use kubeClientset to look up the ingress and spoof
// the domain in the request headers, otherwise it will make the request directly to domain.
// desc will be used to name the metric that is emitted to track how long it took for the
// domain to get into the state checked by inState.  Commas in `desc` must be escaped.
func WaitForEndpointState(kubeClient *KubeClient, logf logging.FormatLogger, theURL string, inState spoof.ResponseChecker, desc string, resolvable bool) (*spoof.Response, error) {
	return WaitForEndpointStateWithTimeout(kubeClient, logf, theURL, inState, desc, resolvable, spoof.RequestTimeout)
}

// WaitForEndpointStateWithTimeout will poll an endpoint until inState indicates the state is achieved
// or the provided timeout is achieved.
// If resolvableDomain is false, it will use kubeClientset to look up the ingress and spoof
// the domain in the request headers, otherwise it will make the request directly to domain.
// desc will be used to name the metric that is emitted to track how long it took for the
// domain to get into the state checked by inState.  Commas in `desc` must be escaped.
func WaitForEndpointStateWithTimeout(
	kubeClient *KubeClient, logf logging.FormatLogger, theURL string, inState spoof.ResponseChecker,
	desc string, resolvable bool, timeout time.Duration) (*spoof.Response, error) {
	defer logging.GetEmitableSpan(context.Background(), fmt.Sprintf("WaitForEndpointState/%s", desc)).End()

	// Try parsing the "theURL" with and without a scheme.
	asURL, err := url.Parse(fmt.Sprintf("http://%s", theURL))
	if err != nil {
		asURL, err = url.Parse(theURL)
		if err != nil {
			return nil, err
		}
	}

	req, err := http.NewRequest(http.MethodGet, asURL.String(), nil)
	if err != nil {
		return nil, err
	}

	client, err := NewSpoofingClient(kubeClient, logf, asURL.Hostname(), resolvable)
	if err != nil {
		return nil, err
	}
	client.RequestTimeout = timeout

	return client.Poll(req, inState)
}
<commit_msg>Remove 'MatchesAny'. (#312)<commit_after>/*
Copyright 2018 The Knative 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.
*/

// request contains logic to make polling HTTP requests against an endpoint with optional host spoofing.

package test

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

	"github.com/knative/pkg/test/logging"
	"github.com/knative/pkg/test/spoof"
)

// Retrying modifies a ResponseChecker to retry certain response codes.
func Retrying(rc spoof.ResponseChecker, codes ...int) spoof.ResponseChecker {
	return func(resp *spoof.Response) (bool, error) {
		for _, code := range codes {
			if resp.StatusCode == code {
				// Returning (false, nil) causes SpoofingClient.Poll to retry.
				// sc.logger.Infof("Retrying for code %v", resp.StatusCode)
				return false, nil
			}
		}

		// If we didn't match any retryable codes, invoke the ResponseChecker that we wrapped.
		return rc(resp)
	}
}

// IsOneOfStatusCodes checks that the response code is equal to the given one.
func IsOneOfStatusCodes(codes ...int) spoof.ResponseChecker {
	return func(resp *spoof.Response) (bool, error) {
		for _, code := range codes {
			if resp.StatusCode == code {
				return true, nil
			}
		}

		return true, fmt.Errorf("status = %d, want one of: %v", resp.StatusCode, codes)
	}
}

// IsStatusOK checks that the response code is a 200.
func IsStatusOK(resp *spoof.Response) (bool, error) {
	return IsOneOfStatusCodes(http.StatusOK)(resp)
}

// MatchesBody checks that the *first* response body matches the "expected" body, otherwise failing.
func MatchesBody(expected string) spoof.ResponseChecker {
	return func(resp *spoof.Response) (bool, error) {
		if !strings.Contains(string(resp.Body), expected) {
			// Returning (true, err) causes SpoofingClient.Poll to fail.
			return true, fmt.Errorf("body = %s, want: %s", string(resp.Body), expected)
		}

		return true, nil
	}
}

// EventuallyMatchesBody checks that the response body *eventually* matches the expected body.
// TODO(#1178): Delete me. We don't want to need this; we should be waiting for an appropriate Status instead.
func EventuallyMatchesBody(expected string) spoof.ResponseChecker {
	return func(resp *spoof.Response) (bool, error) {
		if !strings.Contains(string(resp.Body), expected) {
			// Returning (false, nil) causes SpoofingClient.Poll to retry.
			return false, nil
		}

		return true, nil
	}
}

// MatchesAllOf combines multiple ResponseCheckers to one ResponseChecker with a logical AND. The
// checkers are executed in order. The first function to trigger an error or a retry will short-circuit
// the other functions (they will not be executed).
//
// This is useful for combining a body with a status check like:
// MatchesAllOf(IsStatusOK, MatchesBody("test"))
//
// The MatchesBody check will only be executed after the IsStatusOK has passed.
func MatchesAllOf(checkers ...spoof.ResponseChecker) spoof.ResponseChecker {
	return func(resp *spoof.Response) (bool, error) {
		for _, checker := range checkers {
			done, err := checker(resp)
			if err != nil || !done {
				return done, err
			}
		}
		return true, nil
	}
}

// WaitForEndpointState will poll an endpoint until inState indicates the state is achieved,
// or default timeout is reached.
// If resolvableDomain is false, it will use kubeClientset to look up the ingress and spoof
// the domain in the request headers, otherwise it will make the request directly to domain.
// desc will be used to name the metric that is emitted to track how long it took for the
// domain to get into the state checked by inState.  Commas in `desc` must be escaped.
func WaitForEndpointState(kubeClient *KubeClient, logf logging.FormatLogger, theURL string, inState spoof.ResponseChecker, desc string, resolvable bool) (*spoof.Response, error) {
	return WaitForEndpointStateWithTimeout(kubeClient, logf, theURL, inState, desc, resolvable, spoof.RequestTimeout)
}

// WaitForEndpointStateWithTimeout will poll an endpoint until inState indicates the state is achieved
// or the provided timeout is achieved.
// If resolvableDomain is false, it will use kubeClientset to look up the ingress and spoof
// the domain in the request headers, otherwise it will make the request directly to domain.
// desc will be used to name the metric that is emitted to track how long it took for the
// domain to get into the state checked by inState.  Commas in `desc` must be escaped.
func WaitForEndpointStateWithTimeout(
	kubeClient *KubeClient, logf logging.FormatLogger, theURL string, inState spoof.ResponseChecker,
	desc string, resolvable bool, timeout time.Duration) (*spoof.Response, error) {
	defer logging.GetEmitableSpan(context.Background(), fmt.Sprintf("WaitForEndpointState/%s", desc)).End()

	// Try parsing the "theURL" with and without a scheme.
	asURL, err := url.Parse(fmt.Sprintf("http://%s", theURL))
	if err != nil {
		asURL, err = url.Parse(theURL)
		if err != nil {
			return nil, err
		}
	}

	req, err := http.NewRequest(http.MethodGet, asURL.String(), nil)
	if err != nil {
		return nil, err
	}

	client, err := NewSpoofingClient(kubeClient, logf, asURL.Hostname(), resolvable)
	if err != nil {
		return nil, err
	}
	client.RequestTimeout = timeout

	return client.Poll(req, inState)
}
<|endoftext|>


<commit_before>// Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved.
// Copyright (c) 2006-2014 Sippy Software, Inc. All rights reserved.
// Copyright (c) 2016 Andriy Pylypenko. All rights reserved.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation and/or
// other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package sippy

import (
    "crypto/rand"
    "errors"
    "fmt"
    "math/big"
    "runtime"
    "sync"

    "sippy/conf"
    "sippy/sdp"
    "sippy/types"
)

type Rtp_proxy_session struct {
    caller_session_exists   bool
    call_id                 string
    from_tag                string
    to_tag                  string
    _rtp_proxy_client       sippy_types.RtpProxyClient
    max_index               int
    l4r                     *local4remote
    notify_socket           string
    notify_tag              string
    insert_nortpp           bool
    caller                  _rtpps_side
    callee                  _rtpps_side
    session_lock            sync.Locker
    config                  sippy_conf.Config
}

type rtpproxy_update_result struct {
    rtpproxy_address    string
    rtpproxy_port       string
    family              string
    sendonly            bool
}

func (self *rtpproxy_update_result) Address() string {
    return self.rtpproxy_address
}

func NewRtp_proxy_session(config sippy_conf.Config, rtp_proxy_clients []sippy_types.RtpProxyClient, call_id, from_tag, to_tag, notify_socket, notify_tag string, session_lock sync.Locker, callee_origin *sippy_sdp.SdpOrigin) (*Rtp_proxy_session, error) {
    self := &Rtp_proxy_session{
        notify_socket   : notify_socket,
        notify_tag      : notify_tag,
        call_id         : call_id,
        from_tag        : from_tag,
        to_tag          : to_tag,
        insert_nortpp   : false,
        max_index       : -1,
        session_lock    : session_lock,
        config          : config,
    }
    self.caller.otherside = &self.callee
    self.callee.otherside = &self.caller
    self.caller.owner = self
    self.callee.owner = self
    self.caller.session_exists = false
    self.callee.session_exists = false
    // RFC4566
    // *******
    // For privacy reasons, it is sometimes desirable to obfuscate the
    // username and IP address of the session originator.  If this is a
    // concern, an arbitrary <username> and private <unicast-address> MAY be
    // chosen to populate the "o=" field, provided that these are selected
    // in a manner that does not affect the global uniqueness of the field.
    // *******
    addr := "192.0.2.1" // 192.0.2.0/24 (TEST-NET-1)
    self.caller.origin, _ = sippy_sdp.NewSdpOrigin(addr)
    if callee_origin != nil {
        self.callee.origin = callee_origin.GetCopy()
        // New session means new RTP port so the SDP is now different and the SDP
        // version must be increased.
        self.callee.origin.IncVersion()
    } else {
        self.callee.origin, _ = sippy_sdp.NewSdpOrigin(addr)
    }
    online_clients := []sippy_types.RtpProxyClient{}
    for _, cl := range rtp_proxy_clients {
        if cl.IsOnline() {
            online_clients = append(online_clients, cl)
        }
    }
    n := len(online_clients)
    if n == 0 {
        return nil, fmt.Errorf("No online RTP proxy client has been found")
    }
    idx, err := rand.Int(rand.Reader, big.NewInt(int64(n)))
    if err != nil {
        self._rtp_proxy_client = online_clients[0]
    } else {
        self._rtp_proxy_client = online_clients[idx.Int64()]
    }
    if self.call_id == "" {
        buf := make([]byte, 16)
        rand.Read(buf)
        self.call_id = fmt.Sprintf("%x", buf)
    }
    if from_tag == "" {
        buf := make([]byte, 16)
        rand.Read(buf)
        self.from_tag = fmt.Sprintf("%x", buf)
    }
    if to_tag == "" {
        buf := make([]byte, 16)
        rand.Read(buf)
        self.to_tag = fmt.Sprintf("%x", buf)
    }
    runtime.SetFinalizer(self, rtp_proxy_session_destructor)
    return self, nil
}
/*
    def version(self, result_callback):
        self.send_command("V", self.version_result, result_callback)

    def version_result(self, result, result_callback):
        result_callback(result)
*/
func (self *Rtp_proxy_session) PlayCaller(prompt_name string, times int/*= 1*/, result_callback func(string)/*= nil*/, index int /*= 0*/) {
    self.caller._play(prompt_name, times, result_callback, index)
}

func (self *Rtp_proxy_session) send_command(cmd string, cb func(string)) {
    rtp_proxy_client := self._rtp_proxy_client
    if rtp_proxy_client != nil {
        rtp_proxy_client.SendCommand(cmd, cb, self.session_lock)
    }
}

func (self *Rtp_proxy_session) StopPlayCaller(result_callback func(string)/*= nil*/, index int/*= 0*/) {
    if ! self.caller_session_exists {
        return
    }
    command := fmt.Sprintf("S %s-%d %s %s", self.call_id, index, self.from_tag, self.to_tag)
    self.send_command(command, func(r string) { self.command_result(r, result_callback) })
}

func (self *Rtp_proxy_session) StartRecording(rname/*= nil*/ string, result_callback func(string)/*= nil*/, index int/*= 0*/) {
    if ! self.caller.session_exists {
        self.caller.update("0.0.0.0", "0", func(*rtpproxy_update_result) { self._start_recording(rname, result_callback, index) }, "", index, "IP4")
        return
    }
    self._start_recording(rname, result_callback, index)
}

func (self *Rtp_proxy_session) _start_recording(rname string, result_callback func(string), index int) {
    if rname == "" {
        command := fmt.Sprintf("R %s-%d %s %s", self.call_id, index, self.from_tag, self.to_tag)
        self.send_command(command, func (r string) { self.command_result(r, result_callback) })
        return
    }
    command := fmt.Sprintf("C %s-%d %s.a %s %s", self.call_id, index, rname, self.from_tag, self.to_tag)
    self.send_command(command, func(string) { self._start_recording1(rname, result_callback, index) })
}

func (self *Rtp_proxy_session) _start_recording1(rname string, result_callback func(string), index int) {
    command := fmt.Sprintf("C %s-%d %s.o %s %s", self.call_id, index, rname, self.to_tag, self.from_tag)
    self.send_command(command, func (r string) { self.command_result(r, result_callback) })
}

func (self *Rtp_proxy_session) command_result(result string, result_callback func(string)) {
    //print "%s.command_result(%s)" % (id(self), result)
    if result_callback != nil {
        result_callback(result)
    }
}

func (self *Rtp_proxy_session) Delete() {
    if self._rtp_proxy_client == nil {
        return
    }
    for self.max_index >= 0 {
        command := fmt.Sprintf("D %s-%d %s %s", self.call_id, self.max_index, self.from_tag, self.to_tag)
        self.send_command(command, nil)
        self.max_index--
    }
    self._rtp_proxy_client = nil
}

func (self *Rtp_proxy_session) OnCallerSdpChange(sdp_body sippy_types.MsgBody, cc_event sippy_types.CCEvent, result_callback func(sippy_types.MsgBody)) error {
    return self.caller._on_sdp_change(sdp_body, result_callback)
}

func (self *Rtp_proxy_session) OnCalleeSdpChange(sdp_body sippy_types.MsgBody, msg sippy_types.SipMsg, result_callback func(sippy_types.MsgBody)) error {
    return self.callee._on_sdp_change(sdp_body, result_callback)
}

func rtp_proxy_session_destructor(self *Rtp_proxy_session) {
    self.Delete()
}

func (self *Rtp_proxy_session) CallerSessionExists() bool { return self.caller_session_exists }

func (self *Rtp_proxy_session) SetCallerLaddress(addr string) {
    self.caller.laddress = addr
}

func (self *Rtp_proxy_session) SetCallerRaddress(addr *sippy_conf.HostPort) {
    self.caller.raddress = addr
}

func (self *Rtp_proxy_session) SetCalleeLaddress(addr string) {
    self.callee.laddress = addr
}

func (self *Rtp_proxy_session) SetCalleeRaddress(addr *sippy_conf.HostPort) {
    self.callee.raddress = addr
}

func (self *Rtp_proxy_session) SetInsertNortpp(v bool) {
    self.insert_nortpp = v
}

func (self *Rtp_proxy_session) SetAfterCallerSdpChange(cb func(sippy_types.RtpProxyUpdateResult)) {
    self.caller.after_sdp_change = cb
}

func (self *Rtp_proxy_session) CalleeOrigin() *sippy_sdp.SdpOrigin {
    if self == nil {
        return nil
    }
    return self.callee.origin
}

func (self Rtp_proxy_session) SBindSupported() (bool, error) {
    rtp_proxy_client := self._rtp_proxy_client
    if rtp_proxy_client == nil {
        return true, errors.New("the session already deleted")
    }
    return rtp_proxy_client.SBindSupported(), nil
}

func (self Rtp_proxy_session) IsLocal() (bool, error) {
    rtp_proxy_client := self._rtp_proxy_client
    if rtp_proxy_client == nil {
        return true, errors.New("the session already deleted")
    }
    return rtp_proxy_client.IsLocal(), nil
}

func (self Rtp_proxy_session) TNotSupported() (bool, error) {
    rtp_proxy_client := self._rtp_proxy_client
    if rtp_proxy_client == nil {
        return true, errors.New("the session already deleted")
    }
    return rtp_proxy_client.TNotSupported(), nil
}

func (self Rtp_proxy_session) GetProxyAddress() (string, error) {
    rtp_proxy_client := self._rtp_proxy_client
    if rtp_proxy_client == nil {
        return "", errors.New("the session already deleted")
    }
    return rtp_proxy_client.GetProxyAddress(), nil
}
<commit_msg>Fix for the issue #3: Send the commands to the rtpproxy in exactly the same order as they were requested.<commit_after>// Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved.
// Copyright (c) 2006-2014 Sippy Software, Inc. All rights reserved.
// Copyright (c) 2016 Andriy Pylypenko. All rights reserved.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation and/or
// other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package sippy

import (
    "crypto/rand"
    "errors"
    "fmt"
    "math/big"
    "runtime"
    "sync"

    "sippy/conf"
    "sippy/sdp"
    "sippy/types"
)

type Rtp_proxy_session struct {
    caller_session_exists   bool
    call_id                 string
    from_tag                string
    to_tag                  string
    _rtp_proxy_client       sippy_types.RtpProxyClient
    max_index               int
    l4r                     *local4remote
    notify_socket           string
    notify_tag              string
    insert_nortpp           bool
    caller                  _rtpps_side
    callee                  _rtpps_side
    session_lock            sync.Locker
    config                  sippy_conf.Config
    inflight_lock           sync.Mutex
    inflight_cmd            *rtpp_cmd
    rtpp_wi                 chan *rtpp_cmd
}

type rtpproxy_update_result struct {
    rtpproxy_address    string
    rtpproxy_port       string
    family              string
    sendonly            bool
}

type rtpp_cmd struct {
    cmd         string
    cb          func(string)
}

func (self *rtpproxy_update_result) Address() string {
    return self.rtpproxy_address
}

func NewRtp_proxy_session(config sippy_conf.Config, rtp_proxy_clients []sippy_types.RtpProxyClient, call_id, from_tag, to_tag, notify_socket, notify_tag string, session_lock sync.Locker, callee_origin *sippy_sdp.SdpOrigin) (*Rtp_proxy_session, error) {
    self := &Rtp_proxy_session{
        notify_socket   : notify_socket,
        notify_tag      : notify_tag,
        call_id         : call_id,
        from_tag        : from_tag,
        to_tag          : to_tag,
        insert_nortpp   : false,
        max_index       : -1,
        session_lock    : session_lock,
        config          : config,
        rtpp_wi         : make(chan *rtpp_cmd, 50),
    }
    self.caller.otherside = &self.callee
    self.callee.otherside = &self.caller
    self.caller.owner = self
    self.callee.owner = self
    self.caller.session_exists = false
    self.callee.session_exists = false
    // RFC4566
    // *******
    // For privacy reasons, it is sometimes desirable to obfuscate the
    // username and IP address of the session originator.  If this is a
    // concern, an arbitrary <username> and private <unicast-address> MAY be
    // chosen to populate the "o=" field, provided that these are selected
    // in a manner that does not affect the global uniqueness of the field.
    // *******
    addr := "192.0.2.1" // 192.0.2.0/24 (TEST-NET-1)
    self.caller.origin, _ = sippy_sdp.NewSdpOrigin(addr)
    if callee_origin != nil {
        self.callee.origin = callee_origin.GetCopy()
        // New session means new RTP port so the SDP is now different and the SDP
        // version must be increased.
        self.callee.origin.IncVersion()
    } else {
        self.callee.origin, _ = sippy_sdp.NewSdpOrigin(addr)
    }
    online_clients := []sippy_types.RtpProxyClient{}
    for _, cl := range rtp_proxy_clients {
        if cl.IsOnline() {
            online_clients = append(online_clients, cl)
        }
    }
    n := len(online_clients)
    if n == 0 {
        return nil, fmt.Errorf("No online RTP proxy client has been found")
    }
    idx, err := rand.Int(rand.Reader, big.NewInt(int64(n)))
    if err != nil {
        self._rtp_proxy_client = online_clients[0]
    } else {
        self._rtp_proxy_client = online_clients[idx.Int64()]
    }
    if self.call_id == "" {
        buf := make([]byte, 16)
        rand.Read(buf)
        self.call_id = fmt.Sprintf("%x", buf)
    }
    if from_tag == "" {
        buf := make([]byte, 16)
        rand.Read(buf)
        self.from_tag = fmt.Sprintf("%x", buf)
    }
    if to_tag == "" {
        buf := make([]byte, 16)
        rand.Read(buf)
        self.to_tag = fmt.Sprintf("%x", buf)
    }
    runtime.SetFinalizer(self, rtp_proxy_session_destructor)
    return self, nil
}
/*
    def version(self, result_callback):
        self.send_command("V", self.version_result, result_callback)

    def version_result(self, result, result_callback):
        result_callback(result)
*/
func (self *Rtp_proxy_session) PlayCaller(prompt_name string, times int/*= 1*/, result_callback func(string)/*= nil*/, index int /*= 0*/) {
    self.caller._play(prompt_name, times, result_callback, index)
}

func (self *Rtp_proxy_session) send_command(cmd string, cb func(string)) {
    if rtp_proxy_client := self._rtp_proxy_client; rtp_proxy_client != nil {
        self.inflight_lock.Lock()
        defer self.inflight_lock.Unlock()
        new_cmd := &rtpp_cmd{ cmd, cb }
        if self.inflight_cmd == nil {
            self.inflight_cmd = new_cmd
            rtp_proxy_client.SendCommand(cmd, self.cmd_done, nil)
        } else {
            self.rtpp_wi <- new_cmd
        }
    }
}

func (self *Rtp_proxy_session) cmd_done(res string) {
    self.inflight_lock.Lock()
    done_cmd := self.inflight_cmd
    select {
        case self.inflight_cmd = <-self.rtpp_wi:
            if rtp_proxy_client := self._rtp_proxy_client; rtp_proxy_client != nil {
                rtp_proxy_client.SendCommand(self.inflight_cmd.cmd, self.cmd_done, nil)
            } else {
                self.inflight_cmd = nil // the client is not ready. just drop the command
            }
        default:
            self.inflight_cmd = nil
    }
    self.inflight_lock.Unlock()
    if done_cmd != nil && done_cmd.cb != nil {
        self.session_lock.Lock()
        done_cmd.cb(res)
        self.session_lock.Unlock()
    }
}

func (self *Rtp_proxy_session) StopPlayCaller(result_callback func(string)/*= nil*/, index int/*= 0*/) {
    if ! self.caller_session_exists {
        return
    }
    command := fmt.Sprintf("S %s-%d %s %s", self.call_id, index, self.from_tag, self.to_tag)
    self.send_command(command, func(r string) { self.command_result(r, result_callback) })
}

func (self *Rtp_proxy_session) StartRecording(rname/*= nil*/ string, result_callback func(string)/*= nil*/, index int/*= 0*/) {
    if ! self.caller.session_exists {
        self.caller.update("0.0.0.0", "0", func(*rtpproxy_update_result) { self._start_recording(rname, result_callback, index) }, "", index, "IP4")
        return
    }
    self._start_recording(rname, result_callback, index)
}

func (self *Rtp_proxy_session) _start_recording(rname string, result_callback func(string), index int) {
    if rname == "" {
        command := fmt.Sprintf("R %s-%d %s %s", self.call_id, index, self.from_tag, self.to_tag)
        self.send_command(command, func (r string) { self.command_result(r, result_callback) })
        return
    }
    command := fmt.Sprintf("C %s-%d %s.a %s %s", self.call_id, index, rname, self.from_tag, self.to_tag)
    self.send_command(command, func(string) { self._start_recording1(rname, result_callback, index) })
}

func (self *Rtp_proxy_session) _start_recording1(rname string, result_callback func(string), index int) {
    command := fmt.Sprintf("C %s-%d %s.o %s %s", self.call_id, index, rname, self.to_tag, self.from_tag)
    self.send_command(command, func (r string) { self.command_result(r, result_callback) })
}

func (self *Rtp_proxy_session) command_result(result string, result_callback func(string)) {
    //print "%s.command_result(%s)" % (id(self), result)
    if result_callback != nil {
        result_callback(result)
    }
}

func (self *Rtp_proxy_session) Delete() {
    if self._rtp_proxy_client == nil {
        return
    }
    for self.max_index >= 0 {
        command := fmt.Sprintf("D %s-%d %s %s", self.call_id, self.max_index, self.from_tag, self.to_tag)
        self.send_command(command, nil)
        self.max_index--
    }
    self._rtp_proxy_client = nil
}

func (self *Rtp_proxy_session) OnCallerSdpChange(sdp_body sippy_types.MsgBody, cc_event sippy_types.CCEvent, result_callback func(sippy_types.MsgBody)) error {
    return self.caller._on_sdp_change(sdp_body, result_callback)
}

func (self *Rtp_proxy_session) OnCalleeSdpChange(sdp_body sippy_types.MsgBody, msg sippy_types.SipMsg, result_callback func(sippy_types.MsgBody)) error {
    return self.callee._on_sdp_change(sdp_body, result_callback)
}

func rtp_proxy_session_destructor(self *Rtp_proxy_session) {
    self.Delete()
}

func (self *Rtp_proxy_session) CallerSessionExists() bool { return self.caller_session_exists }

func (self *Rtp_proxy_session) SetCallerLaddress(addr string) {
    self.caller.laddress = addr
}

func (self *Rtp_proxy_session) SetCallerRaddress(addr *sippy_conf.HostPort) {
    self.caller.raddress = addr
}

func (self *Rtp_proxy_session) SetCalleeLaddress(addr string) {
    self.callee.laddress = addr
}

func (self *Rtp_proxy_session) SetCalleeRaddress(addr *sippy_conf.HostPort) {
    self.callee.raddress = addr
}

func (self *Rtp_proxy_session) SetInsertNortpp(v bool) {
    self.insert_nortpp = v
}

func (self *Rtp_proxy_session) SetAfterCallerSdpChange(cb func(sippy_types.RtpProxyUpdateResult)) {
    self.caller.after_sdp_change = cb
}

func (self *Rtp_proxy_session) CalleeOrigin() *sippy_sdp.SdpOrigin {
    if self == nil {
        return nil
    }
    return self.callee.origin
}

func (self Rtp_proxy_session) SBindSupported() (bool, error) {
    rtp_proxy_client := self._rtp_proxy_client
    if rtp_proxy_client == nil {
        return true, errors.New("the session already deleted")
    }
    return rtp_proxy_client.SBindSupported(), nil
}

func (self Rtp_proxy_session) IsLocal() (bool, error) {
    rtp_proxy_client := self._rtp_proxy_client
    if rtp_proxy_client == nil {
        return true, errors.New("the session already deleted")
    }
    return rtp_proxy_client.IsLocal(), nil
}

func (self Rtp_proxy_session) TNotSupported() (bool, error) {
    rtp_proxy_client := self._rtp_proxy_client
    if rtp_proxy_client == nil {
        return true, errors.New("the session already deleted")
    }
    return rtp_proxy_client.TNotSupported(), nil
}

func (self Rtp_proxy_session) GetProxyAddress() (string, error) {
    rtp_proxy_client := self._rtp_proxy_client
    if rtp_proxy_client == nil {
        return "", errors.New("the session already deleted")
    }
    return rtp_proxy_client.GetProxyAddress(), nil
}
<|endoftext|>


<commit_before>package vpp

import (
	"github.com/ligato/cn-infra/core"
	"github.com/ligato/cn-infra/datasync"
	"github.com/ligato/cn-infra/datasync/resync"
	"github.com/ligato/cn-infra/flavors/etcdkafka"
	"github.com/ligato/vpp-agent/plugins/defaultplugins"
	"github.com/ligato/vpp-agent/plugins/govppmux"
	"github.com/ligato/vpp-agent/plugins/linuxplugin"
	"github.com/ligato/cn-infra/flavors/redis"
	"github.com/ligato/cn-infra/flavors/rpc"
)

// Flavor glues together multiple plugins to translate ETCD configuration into VPP.
type Flavor struct {
	*rpc.FlavorLocal
	*rpc.FlavorRPC
	*etcdkafka.FlavorEtcdKafka
	*redis.FlavorRedis
	GoVPP     govppmux.GOVPPPlugin
	Linux     linuxplugin.Plugin
	VPP       defaultplugins.Plugin

	injected bool
}

// Inject sets object references
func (f *Flavor) Inject() error {
	if f.injected {
		return nil
	}
	f.injected = true

	if f.FlavorLocal == nil {
		f.FlavorLocal = &rpc.FlavorLocal{}
	}
	f.FlavorLocal.Inject()

	if f.FlavorEtcdKafka == nil {
		f.FlavorEtcdKafka = &etcdkafka.FlavorEtcdKafka{FlavorLocal: f.FlavorLocal}
	}
	f.FlavorEtcdKafka.Inject()

	if f.FlavorRPC == nil {
		f.FlavorRPC = &rpc.FlavorRPC{FlavorLocal: f.FlavorLocal}
	}
	f.FlavorRPC.Inject()

	if f.FlavorRedis == nil {
		f.FlavorRedis = &redis.FlavorRedis{FlavorLocal: f.FlavorLocal}
	}
	f.FlavorRedis.Inject()

	f.GoVPP.Deps.PluginInfraDeps = *f.FlavorEtcdKafka.FlavorLocal.InfraDeps("govpp")
	f.VPP.Deps.PluginInfraDeps = *f.FlavorEtcdKafka.FlavorLocal.InfraDeps("default-plugins")
	f.VPP.Deps.Publish = &f.FlavorEtcdKafka.ETCDDataSync
	f.VPP.Deps.PublishStatistics = datasync.CompositeKVProtoWriter{
		Adapters: []datasync.KeyProtoValWriter{&f.FlavorEtcdKafka.ETCDDataSync, &f.FlavorRedis.RedisDataSync},
	}
	f.VPP.Deps.Watch = &f.FlavorEtcdKafka.ETCDDataSync
	f.VPP.Deps.Kafka = &f.FlavorEtcdKafka.Kafka
	f.VPP.Deps.GoVppmux = &f.GoVPP
	f.VPP.Deps.Linux = &f.Linux
	f.VPP.Linux.Deps.Watcher = &f.FlavorEtcdKafka.ETCDDataSync

	return nil
}

// Plugins combines Generic Plugins and Standard VPP Plugins + (their ETCD Connector/Adapter with RESYNC)
func (f *Flavor) Plugins() []*core.NamedPlugin {
	f.Inject()
	return core.ListPluginsInFlavor(f)
}
<commit_msg>Cleanup FlavorLocal in vpp flavor<commit_after>package vpp

import (
	"github.com/ligato/cn-infra/core"
	"github.com/ligato/cn-infra/datasync"
	"github.com/ligato/cn-infra/datasync/resync"
	"github.com/ligato/cn-infra/flavors/etcdkafka"
	"github.com/ligato/cn-infra/flavors/local"
	"github.com/ligato/cn-infra/flavors/redis"
	"github.com/ligato/cn-infra/flavors/rpc"
	"github.com/ligato/vpp-agent/plugins/defaultplugins"
	"github.com/ligato/vpp-agent/plugins/govppmux"
	"github.com/ligato/vpp-agent/plugins/linuxplugin"
)

// Flavor glues together multiple plugins to translate ETCD configuration into VPP.
type Flavor struct {
	*local.FlavorLocal
	*rpc.FlavorRPC
	*etcdkafka.FlavorEtcdKafka
	*redis.FlavorRedis
	GoVPP     govppmux.GOVPPPlugin
	Linux     linuxplugin.Plugin
	VPP       defaultplugins.Plugin

	injected bool
}

// Inject sets object references
func (f *Flavor) Inject() error {
	if f.injected {
		return nil
	}
	f.injected = true

	if f.FlavorLocal == nil {
		f.FlavorLocal = &rpc.FlavorLocal{}
	}
	f.FlavorLocal.Inject()

	if f.FlavorEtcdKafka == nil {
		f.FlavorEtcdKafka = &etcdkafka.FlavorEtcdKafka{FlavorLocal: f.FlavorLocal}
	}
	f.FlavorEtcdKafka.Inject()

	if f.FlavorRPC == nil {
		f.FlavorRPC = &rpc.FlavorRPC{FlavorLocal: f.FlavorLocal}
	}
	f.FlavorRPC.Inject()

	if f.FlavorRedis == nil {
		f.FlavorRedis = &redis.FlavorRedis{FlavorLocal: f.FlavorLocal}
	}
	f.FlavorRedis.Inject()

	f.GoVPP.Deps.PluginInfraDeps = *f.FlavorEtcdKafka.FlavorLocal.InfraDeps("govpp")
	f.VPP.Deps.PluginInfraDeps = *f.FlavorEtcdKafka.FlavorLocal.InfraDeps("default-plugins")
	f.VPP.Deps.Publish = &f.FlavorEtcdKafka.ETCDDataSync
	f.VPP.Deps.PublishStatistics = datasync.CompositeKVProtoWriter{
		Adapters: []datasync.KeyProtoValWriter{&f.FlavorEtcdKafka.ETCDDataSync, &f.FlavorRedis.RedisDataSync},
	}
	f.VPP.Deps.Watch = &f.FlavorEtcdKafka.ETCDDataSync
	f.VPP.Deps.Kafka = &f.FlavorEtcdKafka.Kafka
	f.VPP.Deps.GoVppmux = &f.GoVPP
	f.VPP.Deps.Linux = &f.Linux
	f.VPP.Linux.Deps.Watcher = &f.FlavorEtcdKafka.ETCDDataSync

	return nil
}

// Plugins combines Generic Plugins and Standard VPP Plugins + (their ETCD Connector/Adapter with RESYNC)
func (f *Flavor) Plugins() []*core.NamedPlugin {
	f.Inject()
	return core.ListPluginsInFlavor(f)
}
<|endoftext|>


<commit_before>package gitter

import (
	"bufio"
	"encoding/json"
	"fmt"
	"net/http"
	"time"

	"github.com/mreiferson/go-httpclient"
)

var defaultConnectionWaitTime time.Duration = 3000 // millis
var defaultConnectionMaxRetries = 5

// Stream initialize stream
func (gitter *Gitter) Stream(roomID string) *Stream {
	return &Stream{
		url:    streamBaseURL + "rooms/" + roomID + "/chatMessages",
		Event:  make(chan Event),
		gitter: gitter,
		streamConnection: gitter.newStreamConnection(
			defaultConnectionWaitTime,
			defaultConnectionMaxRetries),
	}
}

// Implemented to conform with https://developer.gitter.im/docs/streaming-api
func (gitter *Gitter) Listen(stream *Stream) {

	defer stream.destroy()

	var reader *bufio.Reader
	var gitterMessage Message
	lastKeepalive := time.Now().Unix()

	// connect
	stream.connect()

Loop:
	for {

		// if closed then stop trying
		if stream.isClosed() {
			stream.Event <- Event{
				Data: &GitterConnectionClosed{},
			}
			break Loop
		}

		resp := stream.getResponse()
		if resp.StatusCode != 200 {
			gitter.log(fmt.Sprintf("Unexpected response code %v", resp.StatusCode))
			continue
		}

		//"The JSON stream returns messages as JSON objects that are delimited by carriage return (\r)" <- Not true crap it's (\n) only
		reader = bufio.NewReader(resp.Body)
		line, err := reader.ReadBytes('\n')

		//Check if the line only consists of whitespace
		onlyWhitespace := true
		for _, b := range line {
			if b != ' ' && b != '\t' && b != '\r' && b != '\n' {
				onlyWhitespace = false
			}
		}

		if onlyWhitespace {
			//"Parsers must be tolerant of occasional extra newline characters placed between messages."
			currentKeepalive := time.Now().Unix() //interesting behavior of 100+ keepalives per seconds was observed
			if currentKeepalive-lastKeepalive > 10 {
				lastKeepalive = currentKeepalive
				gitter.log("Keepalive was received")
			}
			continue
		} else if stream.isClosed() {
			gitter.log("Stream closed")
			continue
		} else if err != nil {
			gitter.log("ReadBytes error: " + err.Error())
			stream.connect()
			continue
		}

		// unmarshal the streamed data
		err = json.Unmarshal(line, &gitterMessage)
		if err != nil {
			gitter.log("JSON Unmarshal error: " + err.Error())
			continue
		}

		// we are here, then we got the good message. pipe it forward.
		stream.Event <- Event{
			Data: &MessageReceived{
				Message: gitterMessage,
			},
		}
	}

	gitter.log("Listening was completed")
}

// Stream holds stream data.
type Stream struct {
	url              string
	Event            chan Event
	streamConnection *streamConnection
	gitter           *Gitter
}

func (stream *Stream) destroy() {
	close(stream.Event)
}

type Event struct {
	Data interface{}
}

type GitterConnectionClosed struct {
}

type MessageReceived struct {
	Message Message
}

// connect and try to reconnect with
func (stream *Stream) connect() {

	if stream.streamConnection.retries == stream.streamConnection.currentRetries {
		stream.Close()
		stream.gitter.log("Number of retries exceeded the max retries number, we are done here")
		return
	}

	res, err := stream.gitter.getResponse(stream.url, stream)
	if stream.streamConnection.canceled {
		// do nothing
	} else if err != nil || res.StatusCode != 200 {
		stream.gitter.log("Failed to get response, trying reconnect ")
		stream.gitter.log(err)

		// sleep and wait
		stream.streamConnection.currentRetries++
		time.Sleep(time.Millisecond * stream.streamConnection.wait * time.Duration(stream.streamConnection.currentRetries))

		// connect again
		stream.Close()
		stream.connect()
	} else {
		stream.gitter.log("Response was received")
		stream.streamConnection.currentRetries = 0
		stream.streamConnection.closed = false
		stream.streamConnection.response = res
	}
}

type streamConnection struct {

	// connection was closed
	closed bool

	// canceled
	canceled bool

	// wait time till next try
	wait time.Duration

	// max tries to recover
	retries int

	// current streamed response
	response *http.Response

	// current request
	request *http.Request

	// current status
	currentRetries int
}

// Close the stream connection and stop receiving streamed data
func (stream *Stream) Close() {
	conn := stream.streamConnection
	conn.closed = true
	if conn.response != nil {
		stream.gitter.log("Stream connection close response")
		defer conn.response.Body.Close()
	}
	if conn.request != nil {
		stream.gitter.log("Stream connection close request")
		switch transport := stream.gitter.config.client.Transport.(type) {
		case *httpclient.Transport:
			stream.streamConnection.canceled = true
			transport.CancelRequest(conn.request)
		default:
		}

	}
	conn.currentRetries = 0
}

func (stream *Stream) isClosed() bool {
	return stream.streamConnection.closed
}

func (stream *Stream) getResponse() *http.Response {
	return stream.streamConnection.response
}

// Optional, set stream connection properties
// wait - time in milliseconds of waiting between reconnections. Will grow exponentially.
// retries - number of reconnections retries before dropping the stream.
func (gitter *Gitter) newStreamConnection(wait time.Duration, retries int) *streamConnection {
	return &streamConnection{
		closed:  true,
		wait:    wait,
		retries: retries,
	}
}
<commit_msg>Fix cpu spinning loop<commit_after>package gitter

import (
	"bufio"
	"encoding/json"
	"fmt"
	"net/http"
	"time"

	"github.com/mreiferson/go-httpclient"
)

var defaultConnectionWaitTime time.Duration = 3000 // millis
var defaultConnectionMaxRetries = 5

// Stream initialize stream
func (gitter *Gitter) Stream(roomID string) *Stream {
	return &Stream{
		url:    streamBaseURL + "rooms/" + roomID + "/chatMessages",
		Event:  make(chan Event),
		gitter: gitter,
		streamConnection: gitter.newStreamConnection(
			defaultConnectionWaitTime,
			defaultConnectionMaxRetries),
	}
}

// Implemented to conform with https://developer.gitter.im/docs/streaming-api
func (gitter *Gitter) Listen(stream *Stream) {

	defer stream.destroy()

	var reader *bufio.Reader
	var gitterMessage Message
	lastKeepalive := time.Now().Unix()

	// connect
	stream.connect()

Loop:
	for {

		// if closed then stop trying
		if stream.isClosed() {
			stream.Event <- Event{
				Data: &GitterConnectionClosed{},
			}
			break Loop
		}

		resp := stream.getResponse()
		if resp.StatusCode != 200 {
			gitter.log(fmt.Sprintf("Unexpected response code %v", resp.StatusCode))
			continue
		}

		//"The JSON stream returns messages as JSON objects that are delimited by carriage return (\r)" <- Not true crap it's (\n) only
		reader = bufio.NewReader(resp.Body)
		line, err := reader.ReadBytes('\n')
		if err != nil {
			gitter.log("ReadBytes error: " + err.Error())
			stream.connect()
			continue
		}

		//Check if the line only consists of whitespace
		onlyWhitespace := true
		for _, b := range line {
			if b != ' ' && b != '\t' && b != '\r' && b != '\n' {
				onlyWhitespace = false
			}
		}

		if onlyWhitespace {
			//"Parsers must be tolerant of occasional extra newline characters placed between messages."
			currentKeepalive := time.Now().Unix() //interesting behavior of 100+ keepalives per seconds was observed
			if currentKeepalive-lastKeepalive > 10 {
				lastKeepalive = currentKeepalive
				gitter.log("Keepalive was received")
			}
			continue
		} else if stream.isClosed() {
			gitter.log("Stream closed")
			continue
		}

		// unmarshal the streamed data
		err = json.Unmarshal(line, &gitterMessage)
		if err != nil {
			gitter.log("JSON Unmarshal error: " + err.Error())
			continue
		}

		// we are here, then we got the good message. pipe it forward.
		stream.Event <- Event{
			Data: &MessageReceived{
				Message: gitterMessage,
			},
		}
	}

	gitter.log("Listening was completed")
}

// Stream holds stream data.
type Stream struct {
	url              string
	Event            chan Event
	streamConnection *streamConnection
	gitter           *Gitter
}

func (stream *Stream) destroy() {
	close(stream.Event)
}

type Event struct {
	Data interface{}
}

type GitterConnectionClosed struct {
}

type MessageReceived struct {
	Message Message
}

// connect and try to reconnect with
func (stream *Stream) connect() {

	if stream.streamConnection.retries == stream.streamConnection.currentRetries {
		stream.Close()
		stream.gitter.log("Number of retries exceeded the max retries number, we are done here")
		return
	}

	res, err := stream.gitter.getResponse(stream.url, stream)
	if stream.streamConnection.canceled {
		// do nothing
	} else if err != nil || res.StatusCode != 200 {
		stream.gitter.log("Failed to get response, trying reconnect ")
		stream.gitter.log(err)

		// sleep and wait
		stream.streamConnection.currentRetries++
		time.Sleep(time.Millisecond * stream.streamConnection.wait * time.Duration(stream.streamConnection.currentRetries))

		// connect again
		stream.Close()
		stream.connect()
	} else {
		stream.gitter.log("Response was received")
		stream.streamConnection.currentRetries = 0
		stream.streamConnection.closed = false
		stream.streamConnection.response = res
	}
}

type streamConnection struct {

	// connection was closed
	closed bool

	// canceled
	canceled bool

	// wait time till next try
	wait time.Duration

	// max tries to recover
	retries int

	// current streamed response
	response *http.Response

	// current request
	request *http.Request

	// current status
	currentRetries int
}

// Close the stream connection and stop receiving streamed data
func (stream *Stream) Close() {
	conn := stream.streamConnection
	conn.closed = true
	if conn.response != nil {
		stream.gitter.log("Stream connection close response")
		defer conn.response.Body.Close()
	}
	if conn.request != nil {
		stream.gitter.log("Stream connection close request")
		switch transport := stream.gitter.config.client.Transport.(type) {
		case *httpclient.Transport:
			stream.streamConnection.canceled = true
			transport.CancelRequest(conn.request)
		default:
		}

	}
	conn.currentRetries = 0
}

func (stream *Stream) isClosed() bool {
	return stream.streamConnection.closed
}

func (stream *Stream) getResponse() *http.Response {
	return stream.streamConnection.response
}

// Optional, set stream connection properties
// wait - time in milliseconds of waiting between reconnections. Will grow exponentially.
// retries - number of reconnections retries before dropping the stream.
func (gitter *Gitter) newStreamConnection(wait time.Duration, retries int) *streamConnection {
	return &streamConnection{
		closed:  true,
		wait:    wait,
		retries: retries,
	}
}
<|endoftext|>


<commit_before>/*
Package stream provides filters that can be chained together in a manner
similar to Unix pipelines.  A simple example that prints all go files
under the current directory:

	stream.Run(
		stream.Find("."),
		stream.Grep(`\.go$`),
		stream.WriteLines(os.Stdout),
	)

stream.Run is passed a list of filters that are chained together
(stream.Find, stream.Grep, stream.WriteLines are filters).  Each
filter takes as input a sequence of strings and produces a sequence of
strings. The empty sequence is passed as input to the first
filter. The output of one filter is fed as input to the next filter.

stream.Run is just one way to execute filters.  Others are stream.Contents
(returns the output of the last filter as a []string), and
stream.ForEach (executes a supplied function for every output item).

Error handling

Filter execution can result in errors.  These are returned from stream
functions normally.  For example, the following call will return a
non-nil error.

	err := stream.Run(
		stream.Items("hello", "world"),
		stream.Grep("["), // Invalid regular expression
		stream.WriteLines(os.Stdout),
	)
	// err will be non-nil

User defined filters

Each filter takes as input a sequence of strings (read from a channel)
and produces as output a sequence of strings (written to a channel).
The stream package provides a bunch of useful filters.  Applications can
define their own filters easily. For example, here is a filter that
repeats every input n times:

	func Repeat(n int) stream.FilterFunc {
		return func(arg stream.Arg) error {
			for s := range arg.In {
				for i := 0; i < n; i++ {
					arg.Out <- s
				}
			}
			return nil
		}
	}

	stream.Run(
		stream.Items("hello", "world"),
		Repeat(2),
		stream.WriteLines(os.Stdout),
	)

The output will be:

	hello
	hello
	world
	world

Note that Repeat returns a FilterFunc, a function type that implements the
Filter interface. This is a common implementation pattern: many simple filters
can be expressed as a single function of type FilterFunc.

Tunable Filters

FilterFunc is an appropriate type to use for most filters like Repeat
above.  However for some filters, dynamic customization is
appropriate.  Such filters provide their own implementation of the
Filter interface with extra methods. For example, stream.Sort provides
extra methods that can be used to control how items are sorted:

	stream.Run(
		stream.Command("ls", "-l"),
		stream.Sort().Num(5),  // Sort numerically by size (column 5)
		stream.WriteLines(os.Stdout),
	)

Acknowledgments

The interface of this package is inspired by the http://labix.org/pipe
package. Users may wish to consider that package in case it fits their
needs better.
*/
package stream

import "sync"

// filterErrors records errors accumulated during the execution of a filter.
type filterErrors struct {
	mu  sync.Mutex
	err error
}

func (e *filterErrors) record(err error) {
	if err != nil {
		e.mu.Lock()
		if e.err == nil {
			e.err = err
		}
		e.mu.Unlock()
	}
}

func (e *filterErrors) getError() error {
	e.mu.Lock()
	defer e.mu.Unlock()
	return e.err
}

// Arg contains the data passed to Filter.Run. Arg.In is a channel that
// produces the input to the filter, and Arg.Out is a channel that
// receives the output from the filter.
type Arg struct {
	In    <-chan string
	Out   chan<- string
	dummy bool // To allow later expansion
}

// The Filter interface represents a process that takes as input a
// sequence of strings from a channel and produces a sequence on
// another channel.
type Filter interface {
	// RunFilter reads a sequence of items from Arg.In and produces a
	// sequence of items on Arg.Out.  RunFilter returns nil on success,
	// an error otherwise.  RunFilter must *not* close the Arg.Out
	// channel.
	RunFilter(Arg) error
}

// FilterFunc is an adapter type that allows the use of ordinary
// functions as Filters.  If f is a function with the appropriate
// signature, FilterFunc(f) is a Filter that calls f.
type FilterFunc func(Arg) error

// RunFilter calls this function. It implements the Filter interface.
func (f FilterFunc) RunFilter(arg Arg) error { return f(arg) }

const channelBuffer = 1000

// Sequence returns a filter that is the concatenation of all filter arguments.
// The output of a filter is fed as input to the next filter.
func Sequence(filters ...Filter) Filter {
	if len(filters) == 1 {
		return filters[0]
	}
	return FilterFunc(func(arg Arg) error {
		e := &filterErrors{}
		in := arg.In
		for _, f := range filters {
			c := make(chan string, channelBuffer)
			go runFilter(f, Arg{In: in, Out: c}, e)
			in = c
		}
		for s := range in {
			arg.Out <- s
		}
		return e.getError()
	})
}

// Run executes the sequence of filters and discards all output.
// It returns either nil, an error if any filter reported an error.
func Run(filters ...Filter) error {
	return ForEach(Sequence(filters...), func(s string) {})
}

// ForEach calls fn(s) for every item s in the output of filter and
// returns either nil, or any error reported by the execution of the filter.
func ForEach(filter Filter, fn func(s string)) error {
	in := make(chan string)
	close(in)
	out := make(chan string, channelBuffer)
	e := &filterErrors{}
	go runFilter(filter, Arg{In: in, Out: out}, e)
	for s := range out {
		fn(s)
	}
	return e.getError()
}

// Contents returns a slice that contains all items that are
// the output of filters.
func Contents(filters ...Filter) ([]string, error) {
	var result []string
	err := ForEach(Sequence(filters...), func(s string) {
		result = append(result, s)
	})
	if err != nil {
		result = nil // Discard results on error
	}
	return result, err
}

func runFilter(f Filter, arg Arg, e *filterErrors) {
	e.record(f.RunFilter(arg))
	close(arg.Out)
	for _ = range arg.In { // Discard all unhandled input
	}
}
<commit_msg>simplify a range stmt<commit_after>/*
Package stream provides filters that can be chained together in a manner
similar to Unix pipelines.  A simple example that prints all go files
under the current directory:

	stream.Run(
		stream.Find("."),
		stream.Grep(`\.go$`),
		stream.WriteLines(os.Stdout),
	)

stream.Run is passed a list of filters that are chained together
(stream.Find, stream.Grep, stream.WriteLines are filters).  Each
filter takes as input a sequence of strings and produces a sequence of
strings. The empty sequence is passed as input to the first
filter. The output of one filter is fed as input to the next filter.

stream.Run is just one way to execute filters.  Others are stream.Contents
(returns the output of the last filter as a []string), and
stream.ForEach (executes a supplied function for every output item).

Error handling

Filter execution can result in errors.  These are returned from stream
functions normally.  For example, the following call will return a
non-nil error.

	err := stream.Run(
		stream.Items("hello", "world"),
		stream.Grep("["), // Invalid regular expression
		stream.WriteLines(os.Stdout),
	)
	// err will be non-nil

User defined filters

Each filter takes as input a sequence of strings (read from a channel)
and produces as output a sequence of strings (written to a channel).
The stream package provides a bunch of useful filters.  Applications can
define their own filters easily. For example, here is a filter that
repeats every input n times:

	func Repeat(n int) stream.FilterFunc {
		return func(arg stream.Arg) error {
			for s := range arg.In {
				for i := 0; i < n; i++ {
					arg.Out <- s
				}
			}
			return nil
		}
	}

	stream.Run(
		stream.Items("hello", "world"),
		Repeat(2),
		stream.WriteLines(os.Stdout),
	)

The output will be:

	hello
	hello
	world
	world

Note that Repeat returns a FilterFunc, a function type that implements the
Filter interface. This is a common implementation pattern: many simple filters
can be expressed as a single function of type FilterFunc.

Tunable Filters

FilterFunc is an appropriate type to use for most filters like Repeat
above.  However for some filters, dynamic customization is
appropriate.  Such filters provide their own implementation of the
Filter interface with extra methods. For example, stream.Sort provides
extra methods that can be used to control how items are sorted:

	stream.Run(
		stream.Command("ls", "-l"),
		stream.Sort().Num(5),  // Sort numerically by size (column 5)
		stream.WriteLines(os.Stdout),
	)

Acknowledgments

The interface of this package is inspired by the http://labix.org/pipe
package. Users may wish to consider that package in case it fits their
needs better.
*/
package stream

import "sync"

// filterErrors records errors accumulated during the execution of a filter.
type filterErrors struct {
	mu  sync.Mutex
	err error
}

func (e *filterErrors) record(err error) {
	if err != nil {
		e.mu.Lock()
		if e.err == nil {
			e.err = err
		}
		e.mu.Unlock()
	}
}

func (e *filterErrors) getError() error {
	e.mu.Lock()
	defer e.mu.Unlock()
	return e.err
}

// Arg contains the data passed to Filter.Run. Arg.In is a channel that
// produces the input to the filter, and Arg.Out is a channel that
// receives the output from the filter.
type Arg struct {
	In    <-chan string
	Out   chan<- string
	dummy bool // To allow later expansion
}

// The Filter interface represents a process that takes as input a
// sequence of strings from a channel and produces a sequence on
// another channel.
type Filter interface {
	// RunFilter reads a sequence of items from Arg.In and produces a
	// sequence of items on Arg.Out.  RunFilter returns nil on success,
	// an error otherwise.  RunFilter must *not* close the Arg.Out
	// channel.
	RunFilter(Arg) error
}

// FilterFunc is an adapter type that allows the use of ordinary
// functions as Filters.  If f is a function with the appropriate
// signature, FilterFunc(f) is a Filter that calls f.
type FilterFunc func(Arg) error

// RunFilter calls this function. It implements the Filter interface.
func (f FilterFunc) RunFilter(arg Arg) error { return f(arg) }

const channelBuffer = 1000

// Sequence returns a filter that is the concatenation of all filter arguments.
// The output of a filter is fed as input to the next filter.
func Sequence(filters ...Filter) Filter {
	if len(filters) == 1 {
		return filters[0]
	}
	return FilterFunc(func(arg Arg) error {
		e := &filterErrors{}
		in := arg.In
		for _, f := range filters {
			c := make(chan string, channelBuffer)
			go runFilter(f, Arg{In: in, Out: c}, e)
			in = c
		}
		for s := range in {
			arg.Out <- s
		}
		return e.getError()
	})
}

// Run executes the sequence of filters and discards all output.
// It returns either nil, an error if any filter reported an error.
func Run(filters ...Filter) error {
	return ForEach(Sequence(filters...), func(s string) {})
}

// ForEach calls fn(s) for every item s in the output of filter and
// returns either nil, or any error reported by the execution of the filter.
func ForEach(filter Filter, fn func(s string)) error {
	in := make(chan string)
	close(in)
	out := make(chan string, channelBuffer)
	e := &filterErrors{}
	go runFilter(filter, Arg{In: in, Out: out}, e)
	for s := range out {
		fn(s)
	}
	return e.getError()
}

// Contents returns a slice that contains all items that are
// the output of filters.
func Contents(filters ...Filter) ([]string, error) {
	var result []string
	err := ForEach(Sequence(filters...), func(s string) {
		result = append(result, s)
	})
	if err != nil {
		result = nil // Discard results on error
	}
	return result, err
}

func runFilter(f Filter, arg Arg, e *filterErrors) {
	e.record(f.RunFilter(arg))
	close(arg.Out)
	for range arg.In { // Discard all unhandled input
	}
}
<|endoftext|>


<commit_before>/*
 * Copyright 2015 Fabrício Godoy
 *
 * 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 slice

import "strings"

// A String represents a slice of strings.
type String []string

// A PredicateStringFunc represents a function that defines a criteria and
// determines whether specified string meets that criteria.
type PredicateStringFunc func(string) bool

// IndexOf looks for specified string into current slice, and optionally ignores
// letter casing.
func (s String) IndexOf(str string, ignoreCase bool) int {
	if ignoreCase {
		str = strings.ToLower(str)
		for i, v := range s {
			if str == strings.ToLower(v) {
				return i
			}
		}

		return -1
	}

	for i, v := range s {
		if str == v {
			return i
		}
	}

	return -1
}

// Exists determines whether specified string exists into current slice.
func (s String) Exists(str string, ignoreCase bool) bool {
	return s.IndexOf(str, ignoreCase) != -1
}

// ExistsAll determine whether all specified strings exists into
// current slice.
func (s String) ExistsAll(str []string, ignoreCase bool) bool {
	for _, v := range str {
		if s.IndexOf(v, ignoreCase) == -1 {
			return false
		}
	}

	return true
}

// ExistsAny determine whether any of specified strings exists into current
// slice
func (s String) ExistsAny(str []string, ignoreCase bool) bool {
	for _, v := range str {
		if s.IndexOf(v, ignoreCase) != -1 {
			return true
		}
	}

	return false
}

// TrueForAll tests whether every element of current slice matches the
// conditions specified by predicate.
func (s String) TrueForAll(pred PredicateStringFunc) bool {
	for _, v := range s {
		if !pred(v) {
			return false
		}
	}

	return true
}

// TrueForAny tests whether any element of current slice matches the conditions
// specified by predicate.
func (s String) TrueForAny(pred PredicateStringFunc) bool {
	for _, v := range s {
		if pred(v) {
			return true
		}
	}

	return false
}
<commit_msg>Small documentation fix<commit_after>/*
 * Copyright 2015 Fabrício Godoy
 *
 * 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 slice

import "strings"

// A String represents a slice of string.
type String []string

// A PredicateStringFunc represents a function that defines a criteria and
// determines whether specified string meets that criteria.
type PredicateStringFunc func(string) bool

// IndexOf looks for specified string into current slice, and optionally ignores
// letter casing.
func (s String) IndexOf(str string, ignoreCase bool) int {
	if ignoreCase {
		str = strings.ToLower(str)
		for i, v := range s {
			if str == strings.ToLower(v) {
				return i
			}
		}

		return -1
	}

	for i, v := range s {
		if str == v {
			return i
		}
	}

	return -1
}

// Exists determines whether specified string exists into current slice.
func (s String) Exists(str string, ignoreCase bool) bool {
	return s.IndexOf(str, ignoreCase) != -1
}

// ExistsAll determine whether all specified strings exists into
// current slice.
func (s String) ExistsAll(str []string, ignoreCase bool) bool {
	for _, v := range str {
		if s.IndexOf(v, ignoreCase) == -1 {
			return false
		}
	}

	return true
}

// ExistsAny determine whether any of specified strings exists into current
// slice.
func (s String) ExistsAny(str []string, ignoreCase bool) bool {
	for _, v := range str {
		if s.IndexOf(v, ignoreCase) != -1 {
			return true
		}
	}

	return false
}

// TrueForAll tests whether every element of current slice matches the
// conditions specified by predicate.
func (s String) TrueForAll(pred PredicateStringFunc) bool {
	for _, v := range s {
		if !pred(v) {
			return false
		}
	}

	return true
}

// TrueForAny tests whether any element of current slice matches the conditions
// specified by predicate.
func (s String) TrueForAny(pred PredicateStringFunc) bool {
	for _, v := range s {
		if pred(v) {
			return true
		}
	}

	return false
}
<|endoftext|>


<commit_before>// Copyright 2015 Eleme Inc. All rights reserved.

package cursor

import (
	"math/rand"
	"testing"

	"github.com/eleme/banshee/models"
	"github.com/eleme/banshee/util/assert"
)

// Help to generate random numbers between min and max.
func genMetrics(min, max float64, count int) []*models.Metric {
	var arr []*models.Metric
	delta := max - min
	for i := 0; i < count; i++ {
		value := rand.Float64()*delta + min
		m := &models.Metric{Value: value}
		arr = append(arr, m)
	}
	return arr
}

// Help to generate random numbers around a slowly trending up line.
func genMetricsAroundTrendUpline(min, max, randomRange float64, count int) []*models.Metric {
	var arr []*models.Metric
	for i := 0; i < count; i++ {
		base := min + (max-min)/(float64(count)-1.0)*float64(i)
		value := base + rand.Float64()*randomRange - randomRange/2.0
		m := &models.Metric{Value: value}
		arr = append(arr, m)
	}
	return arr
}

// Case as first
func TestAsFirst(t *testing.T) {
	wf := 0.05
	leastC := 18
	c := New(wf, leastC)
	m := &models.Metric{Value: 1.32}
	s := c.Next(nil, m)
	assert.Ok(t, m.Average == m.Value)
	assert.Ok(t, m.Score == 0)
	assert.Ok(t, s.Count == 1)
	assert.Ok(t, s.StdDev == 0)
}

// Case count not enough
func TestNotEnough(t *testing.T) {
	wf := 0.05
	leastC := 18
	c := New(wf, leastC)
	s := &models.State{Count: leastC - 1, Average: 0.1, StdDev: 0.1}
	m := &models.Metric{Value: 0.1}
	n := c.Next(s, m)
	assert.Ok(t, m.Score == 0)
	assert.Ok(t, n.Count == leastC)
}

// Simple case.
func TestSimple(t *testing.T) {
	wf := 0.05
	leastC := 18
	c := New(wf, leastC)
	l := genMetrics(120.0, 140.0, leastC)
	var s *models.State
	for _, m := range l {
		s = c.Next(s, m)
		assert.Ok(t, !m.IsAnomalous())
	}
	// Should be normal
	m := &models.Metric{Value: 130.0}
	s = c.Next(s, m)
	assert.Ok(t, !m.IsAnomalous())
	// Should be anomalous up
	m = &models.Metric{Value: 160.0}
	s = c.Next(s, m)
	assert.Ok(t, m.IsAnomalous())
	assert.Ok(t, m.IsAnomalousTrendUp())
	// Should be anomalous down
	m = &models.Metric{Value: 100.0}
	s = c.Next(s, m)
	assert.Ok(t, m.IsAnomalous())
	assert.Ok(t, m.IsAnomalousTrendDown())
}

// Case anomaly after an anomaly.
func TestAnomalyAfterBigAnomaly(t *testing.T) {
	wf := 0.05
	leastC := 18
	c := New(wf, leastC)
	l := genMetrics(120.0, 140.0, 100)
	var s *models.State
	for _, m := range l {
		s = c.Next(s, m)
		assert.Ok(t, !m.IsAnomalous())
	}
	// Give a big anomaly
	m := &models.Metric{Value: 2000}
	s = c.Next(s, m)
	assert.Ok(t, m.IsAnomalousTrendUp())
	// Test up and down anomaly
	m = &models.Metric{Value: 190}
	s = c.Next(s, m)
	assert.Ok(t, m.IsAnomalousTrendUp())
	m = &models.Metric{Value: 80}
	s = c.Next(s, m)
	assert.Ok(t, m.IsAnomalousTrendDown())
	// Test normal
	m = &models.Metric{Value: 130}
	s = c.Next(s, m)
	assert.Ok(t, !m.IsAnomalous())
}

// Case test random value range cover negative zone
func TestNegativeValue(t *testing.T) {
	wf := 0.05
	leastC := 18
	c := New(wf, leastC)
	l := genMetrics(-210.0, -190.0, 60)
	var s *models.State
	for _, m := range l {
		s = c.Next(s, m)
		assert.Ok(t, !m.IsAnomalous())
	}
	l = genMetricsLine(-200.0, -100.0, 10.0, 60)
	for _, m := range l {
		s = c.Next(s, m)
		assert.Ok(t, !m.IsAnomalous())
	}
}

// Case slowly trending up.
func TestSlowlyTrendingUp(t *testing.T) {
	wf := 0.05
	leastC := 18
	c := New(wf, leastC)
	l := genMetricsAroundTrendUpline(100.0, 200.0, 10.0, 60)
	var s *models.State
	for _, m := range l {
		s = c.Next(s, m)
		assert.Ok(t, !m.IsAnomalous())
	}
}

// Case change avg from low level to high level with only one accidental data,
// banshee should not alert forever or for a long time
func TestAnomalyLowToHigh(t *testing.T) {
	wf := 0.05
	leastC := 18
	c := New(wf, leastC)
	l := genMetrics(120.0, 140.0, 100)
	var s *models.State
	for _, m := range l {
		s = c.Next(s, m)
		assert.Ok(t, !m.IsAnomalous())
	}
	l = genMetrics(220.0, 240.0, 30)
	for _, m := range l {
		s = c.Next(s, m)
	}
	l = genMetrics(220.0, 240.0, 30)
	for _, m := range l {
		s = c.Next(s, m)
		assert.Ok(t, !m.IsAnomalous())
	}
}

// Case change avg from high level to low level with only one accidental data,
// banshee should not alert forever or for a long time
func TestAnomalyHighToLow(t *testing.T) {
	wf := 0.05
	leastC := 18
	c := New(wf, leastC)
	l := genMetrics(220.0, 240.0, 100)
	var s *models.State
	for _, m := range l {
		s = c.Next(s, m)
		assert.Ok(t, !m.IsAnomalous())
	}
	l = genMetrics(120.0, 140.0, 30)
	for _, m := range l {
		s = c.Next(s, m)
	}
	l = genMetrics(120.0, 140.0, 30)
	for _, m := range l {
		s = c.Next(s, m)
		assert.Ok(t, !m.IsAnomalous())
	}
}
<commit_msg>Fix cherry-pick<commit_after>// Copyright 2015 Eleme Inc. All rights reserved.

package cursor

import (
	"math/rand"
	"testing"

	"github.com/eleme/banshee/models"
	"github.com/eleme/banshee/util/assert"
)

// Help to generate random numbers between min and max.
func genMetrics(min, max float64, count int) []*models.Metric {
	var arr []*models.Metric
	delta := max - min
	for i := 0; i < count; i++ {
		value := rand.Float64()*delta + min
		m := &models.Metric{Value: value}
		arr = append(arr, m)
	}
	return arr
}

// Help to generate random numbers around a slowly trending up line.
func genMetricsAroundTrendUpline(min, max, randomRange float64, count int) []*models.Metric {
	var arr []*models.Metric
	for i := 0; i < count; i++ {
		base := min + (max-min)/(float64(count)-1.0)*float64(i)
		value := base + rand.Float64()*randomRange - randomRange/2.0
		m := &models.Metric{Value: value}
		arr = append(arr, m)
	}
	return arr
}

// Case as first
func TestAsFirst(t *testing.T) {
	wf := 0.05
	leastC := 18
	c := New(wf, leastC)
	m := &models.Metric{Value: 1.32}
	s := c.Next(nil, m)
	assert.Ok(t, m.Average == m.Value)
	assert.Ok(t, m.Score == 0)
	assert.Ok(t, s.Count == 1)
	assert.Ok(t, s.StdDev == 0)
}

// Case count not enough
func TestNotEnough(t *testing.T) {
	wf := 0.05
	leastC := 18
	c := New(wf, leastC)
	s := &models.State{Count: leastC - 1, Average: 0.1, StdDev: 0.1}
	m := &models.Metric{Value: 0.1}
	n := c.Next(s, m)
	assert.Ok(t, m.Score == 0)
	assert.Ok(t, n.Count == leastC)
}

// Simple case.
func TestSimple(t *testing.T) {
	wf := 0.05
	leastC := 18
	c := New(wf, leastC)
	l := genMetrics(120.0, 140.0, leastC)
	var s *models.State
	for _, m := range l {
		s = c.Next(s, m)
		assert.Ok(t, !m.IsAnomalous())
	}
	// Should be normal
	m := &models.Metric{Value: 130.0}
	s = c.Next(s, m)
	assert.Ok(t, !m.IsAnomalous())
	// Should be anomalous up
	m = &models.Metric{Value: 160.0}
	s = c.Next(s, m)
	assert.Ok(t, m.IsAnomalous())
	assert.Ok(t, m.IsAnomalousTrendUp())
	// Should be anomalous down
	m = &models.Metric{Value: 100.0}
	s = c.Next(s, m)
	assert.Ok(t, m.IsAnomalous())
	assert.Ok(t, m.IsAnomalousTrendDown())
}

// Case anomaly after an anomaly.
func TestAnomalyAfterBigAnomaly(t *testing.T) {
	wf := 0.05
	leastC := 18
	c := New(wf, leastC)
	l := genMetrics(120.0, 140.0, 100)
	var s *models.State
	for _, m := range l {
		s = c.Next(s, m)
		assert.Ok(t, !m.IsAnomalous())
	}
	// Give a big anomaly
	m := &models.Metric{Value: 2000}
	s = c.Next(s, m)
	assert.Ok(t, m.IsAnomalousTrendUp())
	// Test up and down anomaly
	m = &models.Metric{Value: 190}
	s = c.Next(s, m)
	assert.Ok(t, m.IsAnomalousTrendUp())
	m = &models.Metric{Value: 80}
	s = c.Next(s, m)
	assert.Ok(t, m.IsAnomalousTrendDown())
	// Test normal
	m = &models.Metric{Value: 130}
	s = c.Next(s, m)
	assert.Ok(t, !m.IsAnomalous())
}

// Case test random value range cover negative zone
func TestNegativeValue(t *testing.T) {
	wf := 0.05
	leastC := 18
	c := New(wf, leastC)
	l := genMetrics(-210.0, -190.0, 60)
	var s *models.State
	for _, m := range l {
		s = c.Next(s, m)
		assert.Ok(t, !m.IsAnomalous())
	}
	l = genMetricsAroundTrendUpline(-200.0, -100.0, 10.0, 60)
	for _, m := range l {
		s = c.Next(s, m)
		assert.Ok(t, !m.IsAnomalous())
	}
}

// Case slowly trending up.
func TestSlowlyTrendingUp(t *testing.T) {
	wf := 0.05
	leastC := 18
	c := New(wf, leastC)
	l := genMetricsAroundTrendUpline(100.0, 200.0, 10.0, 60)
	var s *models.State
	for _, m := range l {
		s = c.Next(s, m)
		assert.Ok(t, !m.IsAnomalous())
	}
}

// Case change avg from low level to high level with only one accidental data,
// banshee should not alert forever or for a long time
func TestAnomalyLowToHigh(t *testing.T) {
	wf := 0.05
	leastC := 18
	c := New(wf, leastC)
	l := genMetrics(120.0, 140.0, 100)
	var s *models.State
	for _, m := range l {
		s = c.Next(s, m)
		assert.Ok(t, !m.IsAnomalous())
	}
	l = genMetrics(220.0, 240.0, 30)
	for _, m := range l {
		s = c.Next(s, m)
	}
	l = genMetrics(220.0, 240.0, 30)
	for _, m := range l {
		s = c.Next(s, m)
		assert.Ok(t, !m.IsAnomalous())
	}
}

// Case change avg from high level to low level with only one accidental data,
// banshee should not alert forever or for a long time
func TestAnomalyHighToLow(t *testing.T) {
	wf := 0.05
	leastC := 18
	c := New(wf, leastC)
	l := genMetrics(220.0, 240.0, 100)
	var s *models.State
	for _, m := range l {
		s = c.Next(s, m)
		assert.Ok(t, !m.IsAnomalous())
	}
	l = genMetrics(120.0, 140.0, 30)
	for _, m := range l {
		s = c.Next(s, m)
	}
	l = genMetrics(120.0, 140.0, 30)
	for _, m := range l {
		s = c.Next(s, m)
		assert.Ok(t, !m.IsAnomalous())
	}
}
<|endoftext|>


<commit_before>package container // import "github.com/docker/docker/integration/container"

import (
	"archive/tar"
	"bytes"
	"context"
	"encoding/json"
	"io"
	"os"
	"path/filepath"
	"testing"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/client"
	"github.com/docker/docker/integration/internal/container"
	"github.com/docker/docker/pkg/archive"
	"github.com/docker/docker/pkg/jsonmessage"
	"github.com/docker/docker/testutil/fakecontext"
	"gotest.tools/v3/assert"
	is "gotest.tools/v3/assert/cmp"
	"gotest.tools/v3/skip"
)

func TestCopyFromContainerPathDoesNotExist(t *testing.T) {
	defer setupTest(t)()

	ctx := context.Background()
	apiclient := testEnv.APIClient()
	cid := container.Create(ctx, t, apiclient)

	_, _, err := apiclient.CopyFromContainer(ctx, cid, "/dne")
	assert.Check(t, client.IsErrNotFound(err))
	assert.ErrorContains(t, err, "Could not find the file /dne in container "+cid)
}

func TestCopyFromContainerPathIsNotDir(t *testing.T) {
	defer setupTest(t)()

	ctx := context.Background()
	apiclient := testEnv.APIClient()
	cid := container.Create(ctx, t, apiclient)

	path := "/etc/passwd/"
	expected := "not a directory"
	if testEnv.OSType == "windows" {
		path = "c:/windows/system32/drivers/etc/hosts/"
		expected = "The filename, directory name, or volume label syntax is incorrect."
	}
	_, _, err := apiclient.CopyFromContainer(ctx, cid, path)
	assert.Assert(t, is.ErrorContains(err, expected))
}

func TestCopyToContainerPathDoesNotExist(t *testing.T) {
	defer setupTest(t)()

	ctx := context.Background()
	apiclient := testEnv.APIClient()
	cid := container.Create(ctx, t, apiclient)

	err := apiclient.CopyToContainer(ctx, cid, "/dne", nil, types.CopyToContainerOptions{})
	assert.Check(t, client.IsErrNotFound(err))
	assert.ErrorContains(t, err, "Could not find the file /dne in container "+cid)
}

func TestCopyEmptyFile(t *testing.T) {
	defer setupTest(t)()

	tmpDir := t.TempDir()
	srcPath := filepath.Join(tmpDir, "empty-file.txt")
	err := os.WriteFile(srcPath, []byte(""), 0400)
	assert.NilError(t, err)

	// TODO(thaJeztah) Add utilities to the client to make steps below less complicated.
	// Code below is taken from copyToContainer() in docker/cli.
	srcInfo, err := archive.CopyInfoSourcePath(srcPath, false)
	assert.NilError(t, err)

	srcArchive, err := archive.TarResource(srcInfo)
	assert.NilError(t, err)
	defer srcArchive.Close()

	ctrPath := "/empty-file.txt"
	dstInfo := archive.CopyInfo{Path: ctrPath}
	dstDir, preparedArchive, err := archive.PrepareArchiveCopy(srcArchive, srcInfo, dstInfo)
	assert.NilError(t, err)
	defer preparedArchive.Close()

	ctx := context.Background()
	apiclient := testEnv.APIClient()
	cid := container.Create(ctx, t, apiclient)

	// empty content
	err = apiclient.CopyToContainer(ctx, cid, dstDir, bytes.NewReader([]byte("")), types.CopyToContainerOptions{})
	assert.NilError(t, err)

	// tar with empty file
	err = apiclient.CopyToContainer(ctx, cid, dstDir, preparedArchive, types.CopyToContainerOptions{})
	assert.NilError(t, err)

	// copy from empty file
	rdr, _, err := apiclient.CopyFromContainer(ctx, cid, dstDir)
	assert.NilError(t, err)
	defer rdr.Close()
}

func TestCopyToContainerPathIsNotDir(t *testing.T) {
	defer setupTest(t)()

	ctx := context.Background()
	apiclient := testEnv.APIClient()
	cid := container.Create(ctx, t, apiclient)

	path := "/etc/passwd/"
	if testEnv.OSType == "windows" {
		path = "c:/windows/system32/drivers/etc/hosts/"
	}
	err := apiclient.CopyToContainer(ctx, cid, path, nil, types.CopyToContainerOptions{})
	assert.Assert(t, is.ErrorContains(err, "not a directory"))
}

func TestCopyFromContainer(t *testing.T) {
	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
	defer setupTest(t)()

	ctx := context.Background()
	apiClient := testEnv.APIClient()

	dir, err := os.MkdirTemp("", t.Name())
	assert.NilError(t, err)
	defer os.RemoveAll(dir)

	buildCtx := fakecontext.New(t, dir, fakecontext.WithFile("foo", "hello"), fakecontext.WithFile("baz", "world"), fakecontext.WithDockerfile(`
		FROM busybox
		COPY foo /foo
		COPY baz /bar/quux/baz
		RUN ln -s notexist /bar/notarget && ln -s quux/baz /bar/filesymlink && ln -s quux /bar/dirsymlink && ln -s / /bar/root
		CMD /fake
	`))
	defer buildCtx.Close()

	resp, err := apiClient.ImageBuild(ctx, buildCtx.AsTarReader(t), types.ImageBuildOptions{})
	assert.NilError(t, err)
	defer resp.Body.Close()

	var imageID string
	err = jsonmessage.DisplayJSONMessagesStream(resp.Body, io.Discard, 0, false, func(msg jsonmessage.JSONMessage) {
		var r types.BuildResult
		assert.NilError(t, json.Unmarshal(*msg.Aux, &r))
		imageID = r.ID
	})
	assert.NilError(t, err)
	assert.Assert(t, imageID != "")

	cid := container.Create(ctx, t, apiClient, container.WithImage(imageID))

	for _, x := range []struct {
		src    string
		expect map[string]string
	}{
		{"/", map[string]string{"/": "", "/foo": "hello", "/bar/quux/baz": "world", "/bar/filesymlink": "", "/bar/dirsymlink": "", "/bar/notarget": ""}},
		{"/bar/root", map[string]string{"root": ""}},
		{"/bar/root/", map[string]string{"root/": "", "root/foo": "hello", "root/bar/quux/baz": "world", "root/bar/filesymlink": "", "root/bar/dirsymlink": "", "root/bar/notarget": ""}},

		{"bar/quux", map[string]string{"quux/": "", "quux/baz": "world"}},
		{"bar/quux/", map[string]string{"quux/": "", "quux/baz": "world"}},
		{"bar/quux/baz", map[string]string{"baz": "world"}},

		{"bar/filesymlink", map[string]string{"filesymlink": ""}},
		{"bar/dirsymlink", map[string]string{"dirsymlink": ""}},
		{"bar/dirsymlink/", map[string]string{"dirsymlink/": "", "dirsymlink/baz": "world"}},
		{"bar/notarget", map[string]string{"notarget": ""}},
	} {
		t.Run(x.src, func(t *testing.T) {
			rdr, _, err := apiClient.CopyFromContainer(ctx, cid, x.src)
			assert.NilError(t, err)
			defer rdr.Close()

			found := make(map[string]bool, len(x.expect))
			var numFound int
			tr := tar.NewReader(rdr)
			for numFound < len(x.expect) {
				h, err := tr.Next()
				if err == io.EOF {
					break
				}
				assert.NilError(t, err)

				expected, exists := x.expect[h.Name]
				if !exists {
					// this archive will have extra stuff in it since we are copying from root
					// and docker adds a bunch of stuff
					continue
				}

				numFound++
				found[h.Name] = true

				buf, err := io.ReadAll(tr)
				if err == nil {
					assert.Check(t, is.Equal(string(buf), expected))
				}
			}

			for f := range x.expect {
				assert.Check(t, found[f], f+" not found in archive")
			}
		})
	}
}
<commit_msg>integration: test more copy edge-cases<commit_after>package container // import "github.com/docker/docker/integration/container"

import (
	"archive/tar"
	"bytes"
	"context"
	"encoding/json"
	"io"
	"os"
	"path/filepath"
	"testing"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/client"
	"github.com/docker/docker/integration/internal/container"
	"github.com/docker/docker/pkg/archive"
	"github.com/docker/docker/pkg/jsonmessage"
	"github.com/docker/docker/testutil/fakecontext"
	"gotest.tools/v3/assert"
	is "gotest.tools/v3/assert/cmp"
	"gotest.tools/v3/skip"
)

func TestCopyFromContainerPathDoesNotExist(t *testing.T) {
	defer setupTest(t)()

	ctx := context.Background()
	apiclient := testEnv.APIClient()
	cid := container.Create(ctx, t, apiclient)

	_, _, err := apiclient.CopyFromContainer(ctx, cid, "/dne")
	assert.Check(t, client.IsErrNotFound(err))
	assert.ErrorContains(t, err, "Could not find the file /dne in container "+cid)
}

func TestCopyFromContainerPathIsNotDir(t *testing.T) {
	defer setupTest(t)()

	ctx := context.Background()
	apiclient := testEnv.APIClient()
	cid := container.Create(ctx, t, apiclient)

	path := "/etc/passwd/"
	expected := "not a directory"
	if testEnv.OSType == "windows" {
		path = "c:/windows/system32/drivers/etc/hosts/"
		expected = "The filename, directory name, or volume label syntax is incorrect."
	}
	_, _, err := apiclient.CopyFromContainer(ctx, cid, path)
	assert.Assert(t, is.ErrorContains(err, expected))
}

func TestCopyToContainerPathDoesNotExist(t *testing.T) {
	defer setupTest(t)()

	ctx := context.Background()
	apiclient := testEnv.APIClient()
	cid := container.Create(ctx, t, apiclient)

	err := apiclient.CopyToContainer(ctx, cid, "/dne", nil, types.CopyToContainerOptions{})
	assert.Check(t, client.IsErrNotFound(err))
	assert.ErrorContains(t, err, "Could not find the file /dne in container "+cid)
}

func TestCopyEmptyFile(t *testing.T) {
	defer setupTest(t)()

	tmpDir := t.TempDir()
	srcPath := filepath.Join(tmpDir, "empty-file.txt")
	err := os.WriteFile(srcPath, []byte(""), 0400)
	assert.NilError(t, err)

	// TODO(thaJeztah) Add utilities to the client to make steps below less complicated.
	// Code below is taken from copyToContainer() in docker/cli.
	srcInfo, err := archive.CopyInfoSourcePath(srcPath, false)
	assert.NilError(t, err)

	srcArchive, err := archive.TarResource(srcInfo)
	assert.NilError(t, err)
	defer srcArchive.Close()

	ctrPath := "/empty-file.txt"
	dstInfo := archive.CopyInfo{Path: ctrPath}
	dstDir, preparedArchive, err := archive.PrepareArchiveCopy(srcArchive, srcInfo, dstInfo)
	assert.NilError(t, err)
	defer preparedArchive.Close()

	ctx := context.Background()
	apiclient := testEnv.APIClient()
	cid := container.Create(ctx, t, apiclient)

	// empty content
	err = apiclient.CopyToContainer(ctx, cid, dstDir, bytes.NewReader([]byte("")), types.CopyToContainerOptions{})
	assert.NilError(t, err)

	// tar with empty file
	err = apiclient.CopyToContainer(ctx, cid, dstDir, preparedArchive, types.CopyToContainerOptions{})
	assert.NilError(t, err)

	// copy from empty file
	rdr, _, err := apiclient.CopyFromContainer(ctx, cid, dstDir)
	assert.NilError(t, err)
	defer rdr.Close()
}

func TestCopyToContainerPathIsNotDir(t *testing.T) {
	defer setupTest(t)()

	ctx := context.Background()
	apiclient := testEnv.APIClient()
	cid := container.Create(ctx, t, apiclient)

	path := "/etc/passwd/"
	if testEnv.OSType == "windows" {
		path = "c:/windows/system32/drivers/etc/hosts/"
	}
	err := apiclient.CopyToContainer(ctx, cid, path, nil, types.CopyToContainerOptions{})
	assert.Assert(t, is.ErrorContains(err, "not a directory"))
}

func TestCopyFromContainer(t *testing.T) {
	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
	defer setupTest(t)()

	ctx := context.Background()
	apiClient := testEnv.APIClient()

	dir, err := os.MkdirTemp("", t.Name())
	assert.NilError(t, err)
	defer os.RemoveAll(dir)

	buildCtx := fakecontext.New(t, dir, fakecontext.WithFile("foo", "hello"), fakecontext.WithFile("baz", "world"), fakecontext.WithDockerfile(`
		FROM busybox
		COPY foo /foo
		COPY baz /bar/quux/baz
		RUN ln -s notexist /bar/notarget && ln -s quux/baz /bar/filesymlink && ln -s quux /bar/dirsymlink && ln -s / /bar/root
		CMD /fake
	`))
	defer buildCtx.Close()

	resp, err := apiClient.ImageBuild(ctx, buildCtx.AsTarReader(t), types.ImageBuildOptions{})
	assert.NilError(t, err)
	defer resp.Body.Close()

	var imageID string
	err = jsonmessage.DisplayJSONMessagesStream(resp.Body, io.Discard, 0, false, func(msg jsonmessage.JSONMessage) {
		var r types.BuildResult
		assert.NilError(t, json.Unmarshal(*msg.Aux, &r))
		imageID = r.ID
	})
	assert.NilError(t, err)
	assert.Assert(t, imageID != "")

	cid := container.Create(ctx, t, apiClient, container.WithImage(imageID))

	for _, x := range []struct {
		src    string
		expect map[string]string
	}{
		{"/", map[string]string{"/": "", "/foo": "hello", "/bar/quux/baz": "world", "/bar/filesymlink": "", "/bar/dirsymlink": "", "/bar/notarget": ""}},
		{".", map[string]string{"./": "", "./foo": "hello", "./bar/quux/baz": "world", "./bar/filesymlink": "", "./bar/dirsymlink": "", "./bar/notarget": ""}},
		{"/.", map[string]string{"./": "", "./foo": "hello", "./bar/quux/baz": "world", "./bar/filesymlink": "", "./bar/dirsymlink": "", "./bar/notarget": ""}},
		{"./", map[string]string{"./": "", "./foo": "hello", "./bar/quux/baz": "world", "./bar/filesymlink": "", "./bar/dirsymlink": "", "./bar/notarget": ""}},
		{"/./", map[string]string{"./": "", "./foo": "hello", "./bar/quux/baz": "world", "./bar/filesymlink": "", "./bar/dirsymlink": "", "./bar/notarget": ""}},
		{"/bar/root", map[string]string{"root": ""}},
		{"/bar/root/", map[string]string{"root/": "", "root/foo": "hello", "root/bar/quux/baz": "world", "root/bar/filesymlink": "", "root/bar/dirsymlink": "", "root/bar/notarget": ""}},
		{"/bar/root/.", map[string]string{"./": "", "./foo": "hello", "./bar/quux/baz": "world", "./bar/filesymlink": "", "./bar/dirsymlink": "", "./bar/notarget": ""}},

		{"bar/quux", map[string]string{"quux/": "", "quux/baz": "world"}},
		{"bar/quux/", map[string]string{"quux/": "", "quux/baz": "world"}},
		{"bar/quux/.", map[string]string{"./": "", "./baz": "world"}},
		{"bar/quux/baz", map[string]string{"baz": "world"}},

		{"bar/filesymlink", map[string]string{"filesymlink": ""}},
		{"bar/dirsymlink", map[string]string{"dirsymlink": ""}},
		{"bar/dirsymlink/", map[string]string{"dirsymlink/": "", "dirsymlink/baz": "world"}},
		{"bar/dirsymlink/.", map[string]string{"./": "", "./baz": "world"}},
		{"bar/notarget", map[string]string{"notarget": ""}},
	} {
		t.Run(x.src, func(t *testing.T) {
			rdr, _, err := apiClient.CopyFromContainer(ctx, cid, x.src)
			assert.NilError(t, err)
			defer rdr.Close()

			found := make(map[string]bool, len(x.expect))
			var numFound int
			tr := tar.NewReader(rdr)
			for numFound < len(x.expect) {
				h, err := tr.Next()
				if err == io.EOF {
					break
				}
				assert.NilError(t, err)

				expected, exists := x.expect[h.Name]
				if !exists {
					// this archive will have extra stuff in it since we are copying from root
					// and docker adds a bunch of stuff
					continue
				}

				numFound++
				found[h.Name] = true

				buf, err := io.ReadAll(tr)
				if err == nil {
					assert.Check(t, is.Equal(string(buf), expected))
				}
			}

			for f := range x.expect {
				assert.Check(t, found[f], f+" not found in archive")
			}
		})
	}
}
<|endoftext|>