<commit_before>/*
Copyright 2017 The Kubernetes 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 upgrades

import (
	"encoding/json"
	"fmt"
	"net/http"
	"path/filepath"
	"reflect"

	"github.com/davecgh/go-spew/spew"
	. "github.com/onsi/ginkgo"

	compute "google.golang.org/api/compute/v1"
	extensions "k8s.io/api/extensions/v1beta1"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/kubernetes/test/e2e/framework"
)

// Dependent on "static-ip-2" manifests
const path = "foo"
const host = "ingress.test.com"

// IngressUpgradeTest adapts the Ingress e2e for upgrade testing
type IngressUpgradeTest struct {
	gceController *framework.GCEIngressController
	// holds GCP resources pre-upgrade
	resourceStore *GCPResourceStore
	jig           *framework.IngressTestJig
	httpClient    *http.Client
	ip            string
	ipName        string
}

// GCPResourceStore keeps track of the GCP resources spun up by an ingress.
// Note: Fields are exported so that we can utilize reflection.
type GCPResourceStore struct {
	Fw      *compute.Firewall
	FwdList []*compute.ForwardingRule
	UmList  []*compute.UrlMap
	TpList  []*compute.TargetHttpProxy
	TpsList []*compute.TargetHttpsProxy
	SslList []*compute.SslCertificate
	BeList  []*compute.BackendService
	Ip      *compute.Address
	IgList  []*compute.InstanceGroup
}

func (IngressUpgradeTest) Name() string { return "ingress-upgrade" }

// Setup creates a GLBC, allocates an ip, and an ingress resource,
// then waits for a successful connectivity check to the ip.
// Also keeps track of all load balancer resources for cross-checking
// during an IngressUpgrade.
func (t *IngressUpgradeTest) Setup(f *framework.Framework) {
	framework.SkipUnlessProviderIs("gce", "gke")

	// jig handles all Kubernetes testing logic
	jig := framework.NewIngressTestJig(f.ClientSet)

	ns := f.Namespace

	// gceController handles all cloud testing logic
	gceController := &framework.GCEIngressController{
		Ns:     ns.Name,
		Client: jig.Client,
		Cloud:  framework.TestContext.CloudConfig,
	}
	framework.ExpectNoError(gceController.Init())

	t.gceController = gceController
	t.jig = jig
	t.httpClient = framework.BuildInsecureClient(framework.IngressReqTimeout)

	// Allocate a static-ip for the Ingress, this IP is cleaned up via CleanupGCEIngressController
	t.ipName = fmt.Sprintf("%s-static-ip", ns.Name)
	t.ip = t.gceController.CreateStaticIP(t.ipName)

	// Create a working basic Ingress
	By(fmt.Sprintf("allocated static ip %v: %v through the GCE cloud provider", t.ipName, t.ip))
	jig.CreateIngress(filepath.Join(framework.IngressManifestPath, "static-ip-2"), ns.Name, map[string]string{
		framework.IngressStaticIPKey:  t.ipName,
		framework.IngressAllowHTTPKey: "false",
	}, map[string]string{})
	t.jig.SetHTTPS("tls-secret", "ingress.test.com")

	By("waiting for Ingress to come up with ip: " + t.ip)
	framework.ExpectNoError(framework.PollURL(fmt.Sprintf("https://%v/%v", t.ip, path), host, framework.LoadBalancerPollTimeout, t.jig.PollInterval, t.httpClient, false))

	By("keeping track of GCP resources created by Ingress")
	t.resourceStore = &GCPResourceStore{}
	t.populateGCPResourceStore(t.resourceStore)
}

// Test waits for the upgrade to complete, and then verifies
// with a connectvity check to the loadbalancer ip.
func (t *IngressUpgradeTest) Test(f *framework.Framework, done <-chan struct{}, upgrade UpgradeType) {
	switch upgrade {
	case MasterUpgrade:
		// Restarting the ingress controller shouldn't disrupt a steady state
		// Ingress. Restarting the ingress controller and deleting ingresses
		// while it's down will leak cloud resources, because the ingress
		// controller doesn't checkpoint to disk.
		t.verify(f, done, true)
	case IngressUpgrade:
		t.verify(f, done, true)
	default:
		// Currently ingress gets disrupted across node upgrade, because endpoints
		// get killed and we don't have any guarantees that 2 nodes don't overlap
		// their upgrades (even on cloud platforms like GCE, because VM level
		// rolling upgrades are not Kubernetes aware).
		t.verify(f, done, false)
	}
}

// Teardown cleans up any remaining resources.
func (t *IngressUpgradeTest) Teardown(f *framework.Framework) {
	if CurrentGinkgoTestDescription().Failed {
		framework.DescribeIng(t.gceController.Ns)
	}

	if t.jig.Ingress != nil {
		By("Deleting ingress")
		t.jig.TryDeleteIngress()
	} else {
		By("No ingress created, no cleanup necessary")
	}

	By("Cleaning up cloud resources")
	framework.ExpectNoError(t.gceController.CleanupGCEIngressController())
}

func (t *IngressUpgradeTest) verify(f *framework.Framework, done <-chan struct{}, testDuringDisruption bool) {
	if testDuringDisruption {
		By("continuously hitting the Ingress IP")
		wait.Until(func() {
			framework.ExpectNoError(framework.PollURL(fmt.Sprintf("https://%v/%v", t.ip, path), host, framework.LoadBalancerPollTimeout, t.jig.PollInterval, t.httpClient, false))
		}, t.jig.PollInterval, done)
	} else {
		By("waiting for upgrade to finish without checking if Ingress remains up")
		<-done
	}
	By("hitting the Ingress IP " + t.ip)
	framework.ExpectNoError(framework.PollURL(fmt.Sprintf("https://%v/%v", t.ip, path), host, framework.LoadBalancerPollTimeout, t.jig.PollInterval, t.httpClient, false))

	// We want to manually trigger a sync because then we can easily verify
	// a correct sync completed after update.
	By("updating ingress spec to manually trigger a sync")
	t.jig.Update(func(ing *extensions.Ingress) {
		ing.Spec.Rules[0].IngressRuleValue.HTTP.Paths = append(
			ing.Spec.Rules[0].IngressRuleValue.HTTP.Paths,
			extensions.HTTPIngressPath{
				Path: "/test",
				// Note: Dependant on using "static-ip-2" manifest.
				Backend: ing.Spec.Rules[0].IngressRuleValue.HTTP.Paths[0].Backend,
			})
	})
	// WaitForIngress() tests that all paths are pinged, which is how we know
	// everything is synced with the cloud.
	t.jig.WaitForIngress(false)
	By("comparing GCP resources post-upgrade")
	postUpgradeResourceStore := &GCPResourceStore{}
	t.populateGCPResourceStore(postUpgradeResourceStore)

	framework.ExpectNoError(compareGCPResourceStores(t.resourceStore, postUpgradeResourceStore, func(v1 reflect.Value, v2 reflect.Value) error {
		i1 := v1.Interface()
		i2 := v2.Interface()
		// Skip verifying the UrlMap since we did that via WaitForIngress()
		if !reflect.DeepEqual(i1, i2) && (v1.Type() != reflect.TypeOf([]*compute.UrlMap{})) {
			return spew.Errorf("resources after ingress upgrade were different:\n Pre-Upgrade: %#v\n Post-Upgrade: %#v", i1, i2)
		}
		return nil
	}))
}

func (t *IngressUpgradeTest) populateGCPResourceStore(resourceStore *GCPResourceStore) {
	cont := t.gceController
	resourceStore.Fw = cont.GetFirewallRule()
	resourceStore.FwdList = cont.ListGlobalForwardingRules()
	resourceStore.UmList = cont.ListUrlMaps()
	resourceStore.TpList = cont.ListTargetHttpProxies()
	resourceStore.TpsList = cont.ListTargetHttpsProxies()
	resourceStore.SslList = cont.ListSslCertificates()
	resourceStore.BeList = cont.ListGlobalBackendServices()
	resourceStore.Ip = cont.GetGlobalAddress(t.ipName)
	resourceStore.IgList = cont.ListInstanceGroups()
}

func compareGCPResourceStores(rs1 *GCPResourceStore, rs2 *GCPResourceStore, compare func(v1 reflect.Value, v2 reflect.Value) error) error {
	// Before we do a comparison, remove the ServerResponse field from the
	// Compute API structs. This is needed because two objects could be the same
	// but their ServerResponse will be different if they were populated through
	// separate API calls.
	rs1Json, _ := json.Marshal(rs1)
	rs2Json, _ := json.Marshal(rs2)
	rs1New := &GCPResourceStore{}
	rs2New := &GCPResourceStore{}
	json.Unmarshal(rs1Json, rs1New)
	json.Unmarshal(rs2Json, rs2New)

	// Iterate through struct fields and perform equality checks on the fields.
	// We do this rather than performing a deep equal on the struct itself because
	// it is easier to log which field, if any, is not the same.
	rs1V := reflect.ValueOf(*rs1New)
	rs2V := reflect.ValueOf(*rs2New)
	for i := 0; i < rs1V.NumField(); i++ {
		if err := compare(rs1V.Field(i), rs2V.Field(i)); err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>[e2e ingress-gce] Change ingress-upgrade test to not check for number of instances<commit_after>/*
Copyright 2017 The Kubernetes 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 upgrades

import (
	"encoding/json"
	"fmt"
	"net/http"
	"path/filepath"
	"reflect"

	"github.com/davecgh/go-spew/spew"
	. "github.com/onsi/ginkgo"

	compute "google.golang.org/api/compute/v1"
	extensions "k8s.io/api/extensions/v1beta1"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/kubernetes/test/e2e/framework"
)

// Dependent on "static-ip-2" manifests
const path = "foo"
const host = "ingress.test.com"

// IngressUpgradeTest adapts the Ingress e2e for upgrade testing
type IngressUpgradeTest struct {
	gceController *framework.GCEIngressController
	// holds GCP resources pre-upgrade
	resourceStore *GCPResourceStore
	jig           *framework.IngressTestJig
	httpClient    *http.Client
	ip            string
	ipName        string
}

// GCPResourceStore keeps track of the GCP resources spun up by an ingress.
// Note: Fields are exported so that we can utilize reflection.
type GCPResourceStore struct {
	Fw      *compute.Firewall
	FwdList []*compute.ForwardingRule
	UmList  []*compute.UrlMap
	TpList  []*compute.TargetHttpProxy
	TpsList []*compute.TargetHttpsProxy
	SslList []*compute.SslCertificate
	BeList  []*compute.BackendService
	Ip      *compute.Address
	IgList  []*compute.InstanceGroup
}

func (IngressUpgradeTest) Name() string { return "ingress-upgrade" }

// Setup creates a GLBC, allocates an ip, and an ingress resource,
// then waits for a successful connectivity check to the ip.
// Also keeps track of all load balancer resources for cross-checking
// during an IngressUpgrade.
func (t *IngressUpgradeTest) Setup(f *framework.Framework) {
	framework.SkipUnlessProviderIs("gce", "gke")

	// jig handles all Kubernetes testing logic
	jig := framework.NewIngressTestJig(f.ClientSet)

	ns := f.Namespace

	// gceController handles all cloud testing logic
	gceController := &framework.GCEIngressController{
		Ns:     ns.Name,
		Client: jig.Client,
		Cloud:  framework.TestContext.CloudConfig,
	}
	framework.ExpectNoError(gceController.Init())

	t.gceController = gceController
	t.jig = jig
	t.httpClient = framework.BuildInsecureClient(framework.IngressReqTimeout)

	// Allocate a static-ip for the Ingress, this IP is cleaned up via CleanupGCEIngressController
	t.ipName = fmt.Sprintf("%s-static-ip", ns.Name)
	t.ip = t.gceController.CreateStaticIP(t.ipName)

	// Create a working basic Ingress
	By(fmt.Sprintf("allocated static ip %v: %v through the GCE cloud provider", t.ipName, t.ip))
	jig.CreateIngress(filepath.Join(framework.IngressManifestPath, "static-ip-2"), ns.Name, map[string]string{
		framework.IngressStaticIPKey:  t.ipName,
		framework.IngressAllowHTTPKey: "false",
	}, map[string]string{})
	t.jig.SetHTTPS("tls-secret", "ingress.test.com")

	By("waiting for Ingress to come up with ip: " + t.ip)
	framework.ExpectNoError(framework.PollURL(fmt.Sprintf("https://%v/%v", t.ip, path), host, framework.LoadBalancerPollTimeout, t.jig.PollInterval, t.httpClient, false))

	By("keeping track of GCP resources created by Ingress")
	t.resourceStore = &GCPResourceStore{}
	t.populateGCPResourceStore(t.resourceStore)
}

// Test waits for the upgrade to complete, and then verifies
// with a connectvity check to the loadbalancer ip.
func (t *IngressUpgradeTest) Test(f *framework.Framework, done <-chan struct{}, upgrade UpgradeType) {
	switch upgrade {
	case MasterUpgrade:
		// Restarting the ingress controller shouldn't disrupt a steady state
		// Ingress. Restarting the ingress controller and deleting ingresses
		// while it's down will leak cloud resources, because the ingress
		// controller doesn't checkpoint to disk.
		t.verify(f, done, true)
	case IngressUpgrade:
		t.verify(f, done, true)
	default:
		// Currently ingress gets disrupted across node upgrade, because endpoints
		// get killed and we don't have any guarantees that 2 nodes don't overlap
		// their upgrades (even on cloud platforms like GCE, because VM level
		// rolling upgrades are not Kubernetes aware).
		t.verify(f, done, false)
	}
}

// Teardown cleans up any remaining resources.
func (t *IngressUpgradeTest) Teardown(f *framework.Framework) {
	if CurrentGinkgoTestDescription().Failed {
		framework.DescribeIng(t.gceController.Ns)
	}

	if t.jig.Ingress != nil {
		By("Deleting ingress")
		t.jig.TryDeleteIngress()
	} else {
		By("No ingress created, no cleanup necessary")
	}

	By("Cleaning up cloud resources")
	framework.ExpectNoError(t.gceController.CleanupGCEIngressController())
}

func (t *IngressUpgradeTest) verify(f *framework.Framework, done <-chan struct{}, testDuringDisruption bool) {
	if testDuringDisruption {
		By("continuously hitting the Ingress IP")
		wait.Until(func() {
			framework.ExpectNoError(framework.PollURL(fmt.Sprintf("https://%v/%v", t.ip, path), host, framework.LoadBalancerPollTimeout, t.jig.PollInterval, t.httpClient, false))
		}, t.jig.PollInterval, done)
	} else {
		By("waiting for upgrade to finish without checking if Ingress remains up")
		<-done
	}
	By("hitting the Ingress IP " + t.ip)
	framework.ExpectNoError(framework.PollURL(fmt.Sprintf("https://%v/%v", t.ip, path), host, framework.LoadBalancerPollTimeout, t.jig.PollInterval, t.httpClient, false))

	// We want to manually trigger a sync because then we can easily verify
	// a correct sync completed after update.
	By("updating ingress spec to manually trigger a sync")
	t.jig.Update(func(ing *extensions.Ingress) {
		ing.Spec.Rules[0].IngressRuleValue.HTTP.Paths = append(
			ing.Spec.Rules[0].IngressRuleValue.HTTP.Paths,
			extensions.HTTPIngressPath{
				Path: "/test",
				// Note: Dependant on using "static-ip-2" manifest.
				Backend: ing.Spec.Rules[0].IngressRuleValue.HTTP.Paths[0].Backend,
			})
	})
	// WaitForIngress() tests that all paths are pinged, which is how we know
	// everything is synced with the cloud.
	t.jig.WaitForIngress(false)
	By("comparing GCP resources post-upgrade")
	postUpgradeResourceStore := &GCPResourceStore{}
	t.populateGCPResourceStore(postUpgradeResourceStore)

	// Stub out the number of instances as that is out of Ingress controller's control.
	for _, ig := range t.resourceStore.IgList {
		ig.Size = 0
	}
	for _, ig := range postUpgradeResourceStore.IgList {
		ig.Size = 0
	}

	framework.ExpectNoError(compareGCPResourceStores(t.resourceStore, postUpgradeResourceStore, func(v1 reflect.Value, v2 reflect.Value) error {
		i1 := v1.Interface()
		i2 := v2.Interface()
		// Skip verifying the UrlMap since we did that via WaitForIngress()
		if !reflect.DeepEqual(i1, i2) && (v1.Type() != reflect.TypeOf([]*compute.UrlMap{})) {
			return spew.Errorf("resources after ingress upgrade were different:\n Pre-Upgrade: %#v\n Post-Upgrade: %#v", i1, i2)
		}
		return nil
	}))
}

func (t *IngressUpgradeTest) populateGCPResourceStore(resourceStore *GCPResourceStore) {
	cont := t.gceController
	resourceStore.Fw = cont.GetFirewallRule()
	resourceStore.FwdList = cont.ListGlobalForwardingRules()
	resourceStore.UmList = cont.ListUrlMaps()
	resourceStore.TpList = cont.ListTargetHttpProxies()
	resourceStore.TpsList = cont.ListTargetHttpsProxies()
	resourceStore.SslList = cont.ListSslCertificates()
	resourceStore.BeList = cont.ListGlobalBackendServices()
	resourceStore.Ip = cont.GetGlobalAddress(t.ipName)
	resourceStore.IgList = cont.ListInstanceGroups()
}

func compareGCPResourceStores(rs1 *GCPResourceStore, rs2 *GCPResourceStore, compare func(v1 reflect.Value, v2 reflect.Value) error) error {
	// Before we do a comparison, remove the ServerResponse field from the
	// Compute API structs. This is needed because two objects could be the same
	// but their ServerResponse will be different if they were populated through
	// separate API calls.
	rs1Json, _ := json.Marshal(rs1)
	rs2Json, _ := json.Marshal(rs2)
	rs1New := &GCPResourceStore{}
	rs2New := &GCPResourceStore{}
	json.Unmarshal(rs1Json, rs1New)
	json.Unmarshal(rs2Json, rs2New)

	// Iterate through struct fields and perform equality checks on the fields.
	// We do this rather than performing a deep equal on the struct itself because
	// it is easier to log which field, if any, is not the same.
	rs1V := reflect.ValueOf(*rs1New)
	rs2V := reflect.ValueOf(*rs2New)
	for i := 0; i < rs1V.NumField(); i++ {
		if err := compare(rs1V.Field(i), rs2V.Field(i)); err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright The OpenTelemetry 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 retry

import (
	"context"
	"errors"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func TestWait(t *testing.T) {
	tests := []struct {
		ctx      context.Context
		delay    time.Duration
		expected error
	}{
		{
			ctx:      context.Background(),
			delay:    time.Duration(0),
			expected: nil,
		},
		{
			ctx:      context.Background(),
			delay:    time.Duration(1),
			expected: nil,
		},
		{
			ctx:      context.Background(),
			delay:    time.Duration(-1),
			expected: nil,
		},
		{
			ctx: func() context.Context {
				ctx, cancel := context.WithCancel(context.Background())
				cancel()
				return ctx
			}(),
			// Ensure the timer and context do not end simultaneously.
			delay:    1 * time.Hour,
			expected: context.Canceled,
		},
	}

	for _, test := range tests {
		assert.Equal(t, test.expected, wait(test.ctx, test.delay))
	}
}

func TestNonRetryableError(t *testing.T) {
	ev := func(error) (bool, time.Duration) { return false, 0 }

	reqFunc := Config{
		Enabled:         true,
		InitialInterval: 1 * time.Nanosecond,
		MaxInterval:     1 * time.Nanosecond,
		// Never stop retrying.
		MaxElapsedTime: 0,
	}.RequestFunc(ev)
	ctx := context.Background()
	assert.NoError(t, reqFunc(ctx, func(context.Context) error {
		return nil
	}))
	assert.ErrorIs(t, reqFunc(ctx, func(context.Context) error {
		return assert.AnError
	}), assert.AnError)
}

func TestThrottledRetry(t *testing.T) {
	// Ensure the throttle delay is used by making longer than backoff delay.
	throttleDelay, backoffDelay := time.Second, time.Nanosecond

	ev := func(error) (bool, time.Duration) {
		// Retry everything with a throttle delay.
		return true, throttleDelay
	}

	reqFunc := Config{
		Enabled:         true,
		InitialInterval: backoffDelay,
		MaxInterval:     backoffDelay,
		// Never stop retrying.
		MaxElapsedTime: 0,
	}.RequestFunc(ev)

	origWait := waitFunc
	var done bool
	waitFunc = func(_ context.Context, delay time.Duration) error {
		assert.Equal(t, throttleDelay, delay, "retry not throttled")
		// Try twice to ensure call is attempted again after delay.
		if done {
			return assert.AnError
		}
		done = true
		return nil
	}
	defer func() { waitFunc = origWait }()

	ctx := context.Background()
	assert.ErrorIs(t, reqFunc(ctx, func(context.Context) error {
		return errors.New("not this error")
	}), assert.AnError)
}

func TestBackoffRetry(t *testing.T) {
	ev := func(error) (bool, time.Duration) { return true, 0 }

	delay := time.Nanosecond
	reqFunc := Config{
		Enabled:         true,
		InitialInterval: delay,
		MaxInterval:     delay,
		// Never stop retrying.
		MaxElapsedTime: 0,
	}.RequestFunc(ev)

	origWait := waitFunc
	var done bool
	waitFunc = func(_ context.Context, d time.Duration) error {
		assert.Equal(t, delay, d, "retry not backoffed")
		// Try twice to ensure call is attempted again after delay.
		if done {
			return assert.AnError
		}
		done = true
		return nil
	}
	defer func() { waitFunc = origWait }()

	ctx := context.Background()
	assert.ErrorIs(t, reqFunc(ctx, func(context.Context) error {
		return errors.New("not this error")
	}), assert.AnError)
}

func TestThrottledRetryGreaterThanMaxElapsedTime(t *testing.T) {
	// Ensure the throttle delay is used by making longer than backoff delay.
	tDelay, bDelay := time.Hour, time.Nanosecond
	ev := func(error) (bool, time.Duration) { return true, tDelay }
	reqFunc := Config{
		Enabled:         true,
		InitialInterval: bDelay,
		MaxInterval:     bDelay,
		MaxElapsedTime:  tDelay - (time.Nanosecond),
	}.RequestFunc(ev)

	ctx := context.Background()
	assert.Contains(t, reqFunc(ctx, func(context.Context) error {
		return assert.AnError
	}).Error(), "max retry time would elapse: ")
}

func TestMaxElapsedTime(t *testing.T) {
	ev := func(error) (bool, time.Duration) { return true, 0 }
	delay := time.Nanosecond
	reqFunc := Config{
		Enabled: true,
		// InitialInterval > MaxElapsedTime means immediate return.
		InitialInterval: 2 * delay,
		MaxElapsedTime:  delay,
	}.RequestFunc(ev)

	ctx := context.Background()
	assert.Contains(t, reqFunc(ctx, func(context.Context) error {
		return assert.AnError
	}).Error(), "max retry time elapsed: ")
}

func TestRetryNotEnabled(t *testing.T) {
	ev := func(error) (bool, time.Duration) {
		t.Error("evaluated retry when not enabled")
		return false, 0
	}

	reqFunc := Config{}.RequestFunc(ev)
	ctx := context.Background()
	assert.NoError(t, reqFunc(ctx, func(context.Context) error {
		return nil
	}))
	assert.ErrorIs(t, reqFunc(ctx, func(context.Context) error {
		return assert.AnError
	}), assert.AnError)
}
<commit_msg>Fix TestBackoffRetry in otlp/internal/retry package (#2562)<commit_after>// Copyright The OpenTelemetry 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 retry

import (
	"context"
	"errors"
	"math"
	"testing"
	"time"

	"github.com/cenkalti/backoff/v4"
	"github.com/stretchr/testify/assert"
)

func TestWait(t *testing.T) {
	tests := []struct {
		ctx      context.Context
		delay    time.Duration
		expected error
	}{
		{
			ctx:      context.Background(),
			delay:    time.Duration(0),
			expected: nil,
		},
		{
			ctx:      context.Background(),
			delay:    time.Duration(1),
			expected: nil,
		},
		{
			ctx:      context.Background(),
			delay:    time.Duration(-1),
			expected: nil,
		},
		{
			ctx: func() context.Context {
				ctx, cancel := context.WithCancel(context.Background())
				cancel()
				return ctx
			}(),
			// Ensure the timer and context do not end simultaneously.
			delay:    1 * time.Hour,
			expected: context.Canceled,
		},
	}

	for _, test := range tests {
		assert.Equal(t, test.expected, wait(test.ctx, test.delay))
	}
}

func TestNonRetryableError(t *testing.T) {
	ev := func(error) (bool, time.Duration) { return false, 0 }

	reqFunc := Config{
		Enabled:         true,
		InitialInterval: 1 * time.Nanosecond,
		MaxInterval:     1 * time.Nanosecond,
		// Never stop retrying.
		MaxElapsedTime: 0,
	}.RequestFunc(ev)
	ctx := context.Background()
	assert.NoError(t, reqFunc(ctx, func(context.Context) error {
		return nil
	}))
	assert.ErrorIs(t, reqFunc(ctx, func(context.Context) error {
		return assert.AnError
	}), assert.AnError)
}

func TestThrottledRetry(t *testing.T) {
	// Ensure the throttle delay is used by making longer than backoff delay.
	throttleDelay, backoffDelay := time.Second, time.Nanosecond

	ev := func(error) (bool, time.Duration) {
		// Retry everything with a throttle delay.
		return true, throttleDelay
	}

	reqFunc := Config{
		Enabled:         true,
		InitialInterval: backoffDelay,
		MaxInterval:     backoffDelay,
		// Never stop retrying.
		MaxElapsedTime: 0,
	}.RequestFunc(ev)

	origWait := waitFunc
	var done bool
	waitFunc = func(_ context.Context, delay time.Duration) error {
		assert.Equal(t, throttleDelay, delay, "retry not throttled")
		// Try twice to ensure call is attempted again after delay.
		if done {
			return assert.AnError
		}
		done = true
		return nil
	}
	defer func() { waitFunc = origWait }()

	ctx := context.Background()
	assert.ErrorIs(t, reqFunc(ctx, func(context.Context) error {
		return errors.New("not this error")
	}), assert.AnError)
}

func TestBackoffRetry(t *testing.T) {
	ev := func(error) (bool, time.Duration) { return true, 0 }

	delay := time.Nanosecond
	reqFunc := Config{
		Enabled:         true,
		InitialInterval: delay,
		MaxInterval:     delay,
		// Never stop retrying.
		MaxElapsedTime: 0,
	}.RequestFunc(ev)

	origWait := waitFunc
	var done bool
	waitFunc = func(_ context.Context, d time.Duration) error {
		delta := math.Ceil(float64(delay)*backoff.DefaultRandomizationFactor) - float64(delay)
		assert.InDelta(t, delay, d, delta, "retry not backoffed")
		// Try twice to ensure call is attempted again after delay.
		if done {
			return assert.AnError
		}
		done = true
		return nil
	}
	t.Cleanup(func() { waitFunc = origWait })

	ctx := context.Background()
	assert.ErrorIs(t, reqFunc(ctx, func(context.Context) error {
		return errors.New("not this error")
	}), assert.AnError)
}

func TestThrottledRetryGreaterThanMaxElapsedTime(t *testing.T) {
	// Ensure the throttle delay is used by making longer than backoff delay.
	tDelay, bDelay := time.Hour, time.Nanosecond
	ev := func(error) (bool, time.Duration) { return true, tDelay }
	reqFunc := Config{
		Enabled:         true,
		InitialInterval: bDelay,
		MaxInterval:     bDelay,
		MaxElapsedTime:  tDelay - (time.Nanosecond),
	}.RequestFunc(ev)

	ctx := context.Background()
	assert.Contains(t, reqFunc(ctx, func(context.Context) error {
		return assert.AnError
	}).Error(), "max retry time would elapse: ")
}

func TestMaxElapsedTime(t *testing.T) {
	ev := func(error) (bool, time.Duration) { return true, 0 }
	delay := time.Nanosecond
	reqFunc := Config{
		Enabled: true,
		// InitialInterval > MaxElapsedTime means immediate return.
		InitialInterval: 2 * delay,
		MaxElapsedTime:  delay,
	}.RequestFunc(ev)

	ctx := context.Background()
	assert.Contains(t, reqFunc(ctx, func(context.Context) error {
		return assert.AnError
	}).Error(), "max retry time elapsed: ")
}

func TestRetryNotEnabled(t *testing.T) {
	ev := func(error) (bool, time.Duration) {
		t.Error("evaluated retry when not enabled")
		return false, 0
	}

	reqFunc := Config{}.RequestFunc(ev)
	ctx := context.Background()
	assert.NoError(t, reqFunc(ctx, func(context.Context) error {
		return nil
	}))
	assert.ErrorIs(t, reqFunc(ctx, func(context.Context) error {
		return assert.AnError
	}), assert.AnError)
}
<|endoftext|>


<commit_before>package clear

import (
	"errors"
	"github.com/watermint/toolbox/domain/dropbox/api/dbx_conn"
	"github.com/watermint/toolbox/domain/dropbox/model/mo_member"
	"github.com/watermint/toolbox/domain/dropbox/service/sv_member"
	"github.com/watermint/toolbox/essentials/log/esl"
	"github.com/watermint/toolbox/infra/control/app_control"
	"github.com/watermint/toolbox/infra/feed/fd_file"
	"github.com/watermint/toolbox/infra/recipe/rc_exec"
	"github.com/watermint/toolbox/infra/recipe/rc_recipe"
	"github.com/watermint/toolbox/infra/report/rp_model"
	"github.com/watermint/toolbox/quality/infra/qt_errors"
	"github.com/watermint/toolbox/quality/infra/qt_file"
	"strings"
)

var (
	ErrorUnableToClearExternalId = errors.New("unable to clear external id")
)

type EmailRow struct {
	Email string `json:"email"`
}

type Externalid struct {
	Peer         dbx_conn.ConnBusinessMgmt
	File         fd_file.RowFeed
	OperationLog rp_model.TransactionReport
}

func (z *Externalid) Preset() {
	z.File.SetModel(&EmailRow{})
	z.OperationLog.SetModel(&EmailRow{}, &mo_member.Member{})
}

func (z *Externalid) Exec(c app_control.Control) error {
	l := c.Log()

	if err := z.OperationLog.Open(); err != nil {
		return err
	}
	svm := sv_member.NewCached(z.Peer.Context())

	return z.File.EachRow(func(m interface{}, rowIndex int) error {
		row := m.(*EmailRow)

		l.Debug("Resolving member", esl.String("email", row.Email))
		member, err := svm.ResolveByEmail(row.Email)
		if err != nil {
			z.OperationLog.Failure(err, row)
			return err
		}

		updated, err := svm.Update(member, sv_member.ClearExternalId())
		if err != nil {
			z.OperationLog.Failure(err, row)
			return err
		}
		if updated.ExternalId != "" {
			z.OperationLog.Failure(ErrorUnableToClearExternalId, row)
			return ErrorUnableToClearExternalId
		}

		z.OperationLog.Success(row, updated)
		return nil
	})
}

func (z *Externalid) Test(c app_control.Control) error {
	// replay test
	{
		dummyEmails := make([]string, 0)
		for i := 0; i < 8; i++ {
			dummyEmails = append(dummyEmails, "xxxxxxxx@xxxxxxxxx.xxx")
		}
		content := strings.Join(dummyEmails, "\n")
		path, err := qt_file.MakeTestFile("member.csv", content)
		if err != nil {
			return err
		}
		err = rc_exec.ExecReplay(c, &Externalid{}, "recipe-member-clear-externalid.json.gz", func(r rc_recipe.Recipe) {
			m := r.(*Externalid)
			m.File.SetFilePath(path)
		})
		if err != nil {
			return err
		}
	}

	err := rc_exec.ExecMock(c, &Externalid{}, func(r rc_recipe.Recipe) {
		f, err := qt_file.MakeTestFile("member-clear-externalid", "john@example.com\nalex@example.com\n")
		if err != nil {
			return
		}
		m := r.(*Externalid)
		m.File.SetFilePath(f)
	})
	if e, _ := qt_errors.ErrorsForTest(c.Log(), err); e != nil {
		return e
	}
	return nil
}
<commit_msg>#430 : fix test<commit_after>package clear

import (
	"errors"
	"github.com/watermint/toolbox/domain/dropbox/api/dbx_conn"
	"github.com/watermint/toolbox/domain/dropbox/model/mo_member"
	"github.com/watermint/toolbox/domain/dropbox/service/sv_member"
	"github.com/watermint/toolbox/essentials/log/esl"
	"github.com/watermint/toolbox/infra/control/app_control"
	"github.com/watermint/toolbox/infra/feed/fd_file"
	"github.com/watermint/toolbox/infra/recipe/rc_exec"
	"github.com/watermint/toolbox/infra/recipe/rc_recipe"
	"github.com/watermint/toolbox/infra/report/rp_model"
	"github.com/watermint/toolbox/quality/infra/qt_errors"
	"github.com/watermint/toolbox/quality/infra/qt_file"
	"strings"
)

var (
	ErrorUnableToClearExternalId = errors.New("unable to clear external id")
)

type EmailRow struct {
	Email string `json:"email"`
}

type Externalid struct {
	Peer         dbx_conn.ConnBusinessMgmt
	File         fd_file.RowFeed
	OperationLog rp_model.TransactionReport
}

func (z *Externalid) Preset() {
	z.File.SetModel(&EmailRow{})
	z.OperationLog.SetModel(&EmailRow{}, &mo_member.Member{})
}

func (z *Externalid) Exec(c app_control.Control) error {
	l := c.Log()

	if err := z.OperationLog.Open(); err != nil {
		return err
	}
	svm := sv_member.NewCached(z.Peer.Context())

	return z.File.EachRow(func(m interface{}, rowIndex int) error {
		row := m.(*EmailRow)

		l.Debug("Resolving member", esl.String("email", row.Email))
		member, err := svm.ResolveByEmail(row.Email)
		if err != nil {
			z.OperationLog.Failure(err, row)
			return err
		}

		updated, err := svm.Update(member, sv_member.ClearExternalId())
		if err != nil {
			z.OperationLog.Failure(err, row)
			return err
		}
		if updated.ExternalId != "" {
			z.OperationLog.Failure(ErrorUnableToClearExternalId, row)
			return ErrorUnableToClearExternalId
		}

		z.OperationLog.Success(row, updated)
		return nil
	})
}

func (z *Externalid) Test(c app_control.Control) error {
	// replay test
	{
		dummyEmails := make([]string, 0)
		for i := 0; i < 8; i++ {
			dummyEmails = append(dummyEmails, "xxxxxxx@xxxxxxxxx.xxx")
		}
		content := strings.Join(dummyEmails, "\n")
		path, err := qt_file.MakeTestFile("member.csv", content)
		if err != nil {
			return err
		}
		err = rc_exec.ExecReplay(c, &Externalid{}, "recipe-member-clear-externalid.json.gz", func(r rc_recipe.Recipe) {
			m := r.(*Externalid)
			m.File.SetFilePath(path)
		})
		if err != nil {
			return err
		}
	}

	err := rc_exec.ExecMock(c, &Externalid{}, func(r rc_recipe.Recipe) {
		f, err := qt_file.MakeTestFile("member-clear-externalid", "john@example.com\nalex@example.com\n")
		if err != nil {
			return
		}
		m := r.(*Externalid)
		m.File.SetFilePath(f)
	})
	if e, _ := qt_errors.ErrorsForTest(c.Log(), err); e != nil {
		return e
	}
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Camlistore 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 downloadbutton provides a Button element that is used in the sidebar of
// the web UI, to download as a zip file all selected files.
package downloadbutton

import (
	"fmt"
	"strings"

	"camlistore.org/pkg/blob"

	"github.com/myitcv/gopherjs/react"
	"honnef.co/go/js/dom"
)

// New returns the button element. It should be used as the entry point, to
// create the needed React element.
//
// key is the id for when the button is in a list, see
// https://facebook.github.io/react/docs/lists-and-keys.html
//
// config is the web UI config that was fetched from the server.
//
// getSelection returns the list of files (blobRefs) selected for downloading.
func New(key string, config map[string]string, getSelection func() []string) react.Element {
	if config == nil {
		fmt.Println("Nil config for DownloadItemsBtn")
		return nil
	}
	downloadHelper, ok := config["downloadHelper"]
	if !ok {
		fmt.Println("No downloadHelper in config for DownloadItemsBtn")
		return nil
	}
	if getSelection == nil {
		fmt.Println("Nil getSelection for DownloadItemsBtn")
		return nil
	}
	if key == "" {
		// A key is only needed in the context of a list, which is why
		// it is up to the caller to choose it. Just creating it here for
		// the sake of consistency.
		key = "downloadItemsButton"
	}
	props := DownloadItemsBtnProps{
		Key:            key,
		GetSelection:   getSelection,
		Config:         config,
		downloadHelper: downloadHelper,
	}
	return DownloadItemsBtn(props).Render()
}

// DownloadItemsBtnDef is the definition for the button, that Renders as a React
// Button.
type DownloadItemsBtnDef struct {
	react.ComponentDef
}

type DownloadItemsBtnProps struct {
	// Key is the id for when the button is in a list, see
	// https://facebook.github.io/react/docs/lists-and-keys.html
	Key string
	// GetSelection returns the list of files (blobRefs) selected
	// for downloading.
	GetSelection func() []string
	// Config is the web UI config that was fetched from the server.
	Config         map[string]string
	downloadHelper string
}

func (p *DownloadItemsBtnDef) Props() DownloadItemsBtnProps {
	uprops := p.ComponentDef.Props()
	return uprops.(DownloadItemsBtnProps)
}

func DownloadItemsBtn(p DownloadItemsBtnProps) *DownloadItemsBtnDef {
	res := &DownloadItemsBtnDef{}

	react.BlessElement(res, p)

	return res
}

func (d *DownloadItemsBtnDef) Render() react.Element {
	return react.Button(
		react.ButtonProps(func(bp *react.ButtonPropsDef) {
			bp.OnClick = d.handleDownloadSelection
			bp.Key = d.Props().Key
		}),
		react.S("Download"),
	)
}

func (d *DownloadItemsBtnDef) handleDownloadSelection(*react.SyntheticMouseEvent) {
	// Note: there's a "memleak", as in: until the selection is cleared and
	// another one is started, this button stays allocated. It is of no
	// consequence in this case as we don't allocate a lot for this element (in
	// previous experiments where the zip archive was in memory, the leak was
	// definitely noticeable then), but it is something to keep in mind for
	// future elements.
	go func() {
		if err := d.downloadSelection(); err != nil {
			dom.GetWindow().Alert(fmt.Sprintf("%v", err))
		}
	}()
}

func (d *DownloadItemsBtnDef) downloadSelection() error {
	selection := d.Props().GetSelection()
	downloadPrefix := d.Props().downloadHelper
	fileRefs := []string{}
	for _, file := range selection {
		ref, ok := blob.Parse(file)
		if !ok {
			return fmt.Errorf("Cannot download %q, not a valid blobRef\n", file)
		}
		fileRefs = append(fileRefs, ref.String())
	}

	el := dom.GetWindow().Document().CreateElement("input")
	input := el.(*dom.HTMLInputElement)
	input.Type = "text"
	input.Name = "files"
	input.Value = strings.Join(fileRefs, ",")

	el = dom.GetWindow().Document().CreateElement("form")
	form := el.(*dom.HTMLFormElement)
	form.Action = downloadPrefix
	form.Method = "POST"
	form.AppendChild(input)
	form.Submit()
	return nil
}
<commit_msg>camlistored/ui: connect temp form to DOM before submitting<commit_after>/*
Copyright 2017 The Camlistore 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 downloadbutton provides a Button element that is used in the sidebar of
// the web UI, to download as a zip file all selected files.
package downloadbutton

import (
	"fmt"
	"strings"

	"camlistore.org/pkg/blob"

	"github.com/myitcv/gopherjs/react"
	"honnef.co/go/js/dom"
)

// New returns the button element. It should be used as the entry point, to
// create the needed React element.
//
// key is the id for when the button is in a list, see
// https://facebook.github.io/react/docs/lists-and-keys.html
//
// config is the web UI config that was fetched from the server.
//
// getSelection returns the list of files (blobRefs) selected for downloading.
func New(key string, config map[string]string, getSelection func() []string) react.Element {
	if config == nil {
		fmt.Println("Nil config for DownloadItemsBtn")
		return nil
	}
	downloadHelper, ok := config["downloadHelper"]
	if !ok {
		fmt.Println("No downloadHelper in config for DownloadItemsBtn")
		return nil
	}
	if getSelection == nil {
		fmt.Println("Nil getSelection for DownloadItemsBtn")
		return nil
	}
	if key == "" {
		// A key is only needed in the context of a list, which is why
		// it is up to the caller to choose it. Just creating it here for
		// the sake of consistency.
		key = "downloadItemsButton"
	}
	props := DownloadItemsBtnProps{
		Key:            key,
		GetSelection:   getSelection,
		Config:         config,
		downloadHelper: downloadHelper,
	}
	return DownloadItemsBtn(props).Render()
}

// DownloadItemsBtnDef is the definition for the button, that Renders as a React
// Button.
type DownloadItemsBtnDef struct {
	react.ComponentDef
}

type DownloadItemsBtnProps struct {
	// Key is the id for when the button is in a list, see
	// https://facebook.github.io/react/docs/lists-and-keys.html
	Key string
	// GetSelection returns the list of files (blobRefs) selected
	// for downloading.
	GetSelection func() []string
	// Config is the web UI config that was fetched from the server.
	Config         map[string]string
	downloadHelper string
}

func (p *DownloadItemsBtnDef) Props() DownloadItemsBtnProps {
	uprops := p.ComponentDef.Props()
	return uprops.(DownloadItemsBtnProps)
}

func DownloadItemsBtn(p DownloadItemsBtnProps) *DownloadItemsBtnDef {
	res := &DownloadItemsBtnDef{}

	react.BlessElement(res, p)

	return res
}

func (d *DownloadItemsBtnDef) Render() react.Element {
	return react.Button(
		react.ButtonProps(func(bp *react.ButtonPropsDef) {
			bp.OnClick = d.handleDownloadSelection
			bp.Key = d.Props().Key
		}),
		react.S("Download"),
	)
}

func (d *DownloadItemsBtnDef) handleDownloadSelection(*react.SyntheticMouseEvent) {
	// Note: there's a "memleak", as in: until the selection is cleared and
	// another one is started, this button stays allocated. It is of no
	// consequence in this case as we don't allocate a lot for this element (in
	// previous experiments where the zip archive was in memory, the leak was
	// definitely noticeable then), but it is something to keep in mind for
	// future elements.
	go func() {
		if err := d.downloadSelection(); err != nil {
			dom.GetWindow().Alert(fmt.Sprintf("%v", err))
		}
	}()
}

func (d *DownloadItemsBtnDef) downloadSelection() error {
	selection := d.Props().GetSelection()
	downloadPrefix := d.Props().downloadHelper
	fileRefs := []string{}
	for _, file := range selection {
		ref, ok := blob.Parse(file)
		if !ok {
			return fmt.Errorf("Cannot download %q, not a valid blobRef\n", file)
		}
		fileRefs = append(fileRefs, ref.String())
	}

	el := dom.GetWindow().Document().CreateElement("input")
	input := el.(*dom.HTMLInputElement)
	input.Type = "text"
	input.Name = "files"
	input.Value = strings.Join(fileRefs, ",")

	el = dom.GetWindow().Document().CreateElement("form")
	form := el.(*dom.HTMLFormElement)
	form.Action = downloadPrefix
	form.Method = "POST"
	form.AppendChild(input)
	// As per
	// https://html.spec.whatwg.org/multipage/forms.html#form-submission-algorithm
	// step 2., a form must be connected to the DOM for submission.
	body := dom.GetWindow().Document().QuerySelector("body")
	body.AppendChild(form)
	defer body.RemoveChild(form)
	form.Submit()
	return nil
}
<|endoftext|>


<commit_before>package scheduler

import (
	"sync"

	log "github.com/Sirupsen/logrus"

	"github.com/intelsdi-x/pulse/core"
)

var (
	watcherLog = log.WithField("_module", "scheduler-watcher")
)

type TaskWatcher struct {
	id      uint64
	taskIds []uint64
	parent  *taskWatcherCollection
	stopped bool
	handler core.TaskWatcherHandler
}

// Stops watching a task. Cannot be restarted.
func (t *TaskWatcher) Close() error {
	for _, x := range t.taskIds {
		t.parent.rm(x, t)
	}
	return nil
}

type taskWatcherCollection struct {
	// Collection of task watchers by
	coll       map[uint64][]*TaskWatcher
	tIdCounter uint64
	mutex      *sync.Mutex
}

func newTaskWatcherCollection() *taskWatcherCollection {
	return &taskWatcherCollection{
		coll:       make(map[uint64][]*TaskWatcher),
		tIdCounter: 1,
		mutex:      &sync.Mutex{},
	}
}

func (t *taskWatcherCollection) rm(taskId uint64, tw *TaskWatcher) {
	t.mutex.Lock()
	defer t.mutex.Unlock()
	if t.coll[taskId] != nil {
		for i, w := range t.coll[taskId] {
			if w == tw {
				watcherLog.WithFields(log.Fields{
					"task-id":         taskId,
					"task-watcher-id": tw.id,
				}).Debug("removing watch from task")
				t.coll[taskId] = append(t.coll[taskId][:i], t.coll[taskId][i+1:]...)
				if len(t.coll[taskId]) == 0 {
					delete(t.coll, taskId)
				}
			}
		}
	}
}

func (t *taskWatcherCollection) add(taskId uint64, twh core.TaskWatcherHandler) (*TaskWatcher, error) {
	t.mutex.Lock()
	defer t.mutex.Unlock()
	// init map for task ID if it does not eist
	if t.coll[taskId] == nil {
		t.coll[taskId] = make([]*TaskWatcher, 0)
	}
	tw := &TaskWatcher{
		// Assign unique ID to task watcher
		id: t.tIdCounter,
		// Add ref to coll for cleanup later
		parent:  t,
		stopped: false,
		handler: twh,
	}
	// Increment number for next time
	t.tIdCounter++
	// Add task id to task watcher list
	tw.taskIds = append(tw.taskIds, taskId)
	// Add this task watcher in
	t.coll[taskId] = append(t.coll[taskId], tw)
	watcherLog.WithFields(log.Fields{
		"task-id":         taskId,
		"task-watcher-id": tw.id,
	}).Debug("Added to task watcher collection")
	return tw, nil
}

func (t *taskWatcherCollection) handleMetricCollected(taskId uint64, m []core.Metric) {
	t.mutex.Lock()
	defer t.mutex.Unlock()
	// no taskID means no watches, early exit
	if t.coll[taskId] == nil || len(t.coll[taskId]) == 0 {
		// Uncomment this debug line if needed. Otherwise this is too verbose for even debug level.
		// watcherLog.WithFields(log.Fields{
		// 	"task-id": taskId,
		// }).Debug("no watchers")
		return
	}
	// Walk all watchers for a task ID
	for i, v := range t.coll[taskId] {
		// Check if they have a catcher assigned
		watcherLog.WithFields(log.Fields{
			"task-id":         taskId,
			"task-watcher-id": i,
		}).Debug("calling taskwatcher collection func")
		// Call the catcher
		v.handler.CatchCollection(m)
	}
}

func (t *taskWatcherCollection) handleTaskStarted(taskId uint64) {
	t.mutex.Lock()
	defer t.mutex.Unlock()
	// no taskID means no watches, early exit
	if t.coll[taskId] == nil || len(t.coll[taskId]) == 0 {
		// Uncomment this debug line if needed. Otherwise this is too verbose for even debug level.
		watcherLog.WithFields(log.Fields{
			"task-id": taskId,
		}).Debug("no watchers")
		return
	}
	// Walk all watchers for a task ID
	for i, v := range t.coll[taskId] {
		// Check if they have a catcher assigned
		watcherLog.WithFields(log.Fields{
			"task-id":         taskId,
			"task-watcher-id": i,
		}).Debug("calling taskwatcher task started func")
		// Call the catcher
		v.handler.CatchTaskStarted()
	}
}

func (t *taskWatcherCollection) handleTaskStopped(taskId uint64) {
	t.mutex.Lock()
	defer t.mutex.Unlock()
	// no taskID means no watches, early exit
	if t.coll[taskId] == nil || len(t.coll[taskId]) == 0 {
		// Uncomment this debug line if needed. Otherwise this is too verbose for even debug level.
		watcherLog.WithFields(log.Fields{
			"task-id": taskId,
		}).Debug("no watchers")
		return
	}
	// Walk all watchers for a task ID
	for i, v := range t.coll[taskId] {
		// Check if they have a catcher assigned
		watcherLog.WithFields(log.Fields{
			"task-id":         taskId,
			"task-watcher-id": i,
		}).Debug("calling taskwatcher task stopped func")
		// Call the catcher
		v.handler.CatchTaskStopped()
	}
}

func (t *taskWatcherCollection) handleTaskDisabled(taskId uint64, why string) {
	t.mutex.Lock()
	defer t.mutex.Unlock()
	// no taskID means no watches, early exit
	if t.coll[taskId] == nil || len(t.coll[taskId]) == 0 {
		// Uncomment this debug line if needed. Otherwise this is too verbose for even debug level.
		// watcherLog.WithFields(log.Fields{
		// 	"task-id": taskId,
		// }).Debug("no watchers")
		return
	}
	// Walk all watchers for a task ID
	for i, v := range t.coll[taskId] {
		// Check if they have a catcher assigned
		watcherLog.WithFields(log.Fields{
			"task-id":         taskId,
			"task-watcher-id": i,
		}).Debug("calling taskwatcher task disabled func")
		// Call the catcher
		v.handler.CatchTaskDisabled(why)
	}
}
<commit_msg>Commented out some super debug log line<commit_after>package scheduler

import (
	"sync"

	log "github.com/Sirupsen/logrus"

	"github.com/intelsdi-x/pulse/core"
)

var (
	watcherLog = log.WithField("_module", "scheduler-watcher")
)

type TaskWatcher struct {
	id      uint64
	taskIds []uint64
	parent  *taskWatcherCollection
	stopped bool
	handler core.TaskWatcherHandler
}

// Stops watching a task. Cannot be restarted.
func (t *TaskWatcher) Close() error {
	for _, x := range t.taskIds {
		t.parent.rm(x, t)
	}
	return nil
}

type taskWatcherCollection struct {
	// Collection of task watchers by
	coll       map[uint64][]*TaskWatcher
	tIdCounter uint64
	mutex      *sync.Mutex
}

func newTaskWatcherCollection() *taskWatcherCollection {
	return &taskWatcherCollection{
		coll:       make(map[uint64][]*TaskWatcher),
		tIdCounter: 1,
		mutex:      &sync.Mutex{},
	}
}

func (t *taskWatcherCollection) rm(taskId uint64, tw *TaskWatcher) {
	t.mutex.Lock()
	defer t.mutex.Unlock()
	if t.coll[taskId] != nil {
		for i, w := range t.coll[taskId] {
			if w == tw {
				watcherLog.WithFields(log.Fields{
					"task-id":         taskId,
					"task-watcher-id": tw.id,
				}).Debug("removing watch from task")
				t.coll[taskId] = append(t.coll[taskId][:i], t.coll[taskId][i+1:]...)
				if len(t.coll[taskId]) == 0 {
					delete(t.coll, taskId)
				}
			}
		}
	}
}

func (t *taskWatcherCollection) add(taskId uint64, twh core.TaskWatcherHandler) (*TaskWatcher, error) {
	t.mutex.Lock()
	defer t.mutex.Unlock()
	// init map for task ID if it does not eist
	if t.coll[taskId] == nil {
		t.coll[taskId] = make([]*TaskWatcher, 0)
	}
	tw := &TaskWatcher{
		// Assign unique ID to task watcher
		id: t.tIdCounter,
		// Add ref to coll for cleanup later
		parent:  t,
		stopped: false,
		handler: twh,
	}
	// Increment number for next time
	t.tIdCounter++
	// Add task id to task watcher list
	tw.taskIds = append(tw.taskIds, taskId)
	// Add this task watcher in
	t.coll[taskId] = append(t.coll[taskId], tw)
	watcherLog.WithFields(log.Fields{
		"task-id":         taskId,
		"task-watcher-id": tw.id,
	}).Debug("Added to task watcher collection")
	return tw, nil
}

func (t *taskWatcherCollection) handleMetricCollected(taskId uint64, m []core.Metric) {
	t.mutex.Lock()
	defer t.mutex.Unlock()
	// no taskID means no watches, early exit
	if t.coll[taskId] == nil || len(t.coll[taskId]) == 0 {
		// Uncomment this debug line if needed. Otherwise this is too verbose for even debug level.
		// watcherLog.WithFields(log.Fields{
		// 	"task-id": taskId,
		// }).Debug("no watchers")
		return
	}
	// Walk all watchers for a task ID
	for i, v := range t.coll[taskId] {
		// Check if they have a catcher assigned
		watcherLog.WithFields(log.Fields{
			"task-id":         taskId,
			"task-watcher-id": i,
		}).Debug("calling taskwatcher collection func")
		// Call the catcher
		v.handler.CatchCollection(m)
	}
}

func (t *taskWatcherCollection) handleTaskStarted(taskId uint64) {
	t.mutex.Lock()
	defer t.mutex.Unlock()
	// no taskID means no watches, early exit
	if t.coll[taskId] == nil || len(t.coll[taskId]) == 0 {
		// Uncomment this debug line if needed. Otherwise this is too verbose for even debug level.
		// watcherLog.WithFields(log.Fields{
		// 	"task-id": taskId,
		// }).Debug("no watchers")
		return
	}
	// Walk all watchers for a task ID
	for i, v := range t.coll[taskId] {
		// Check if they have a catcher assigned
		watcherLog.WithFields(log.Fields{
			"task-id":         taskId,
			"task-watcher-id": i,
		}).Debug("calling taskwatcher task started func")
		// Call the catcher
		v.handler.CatchTaskStarted()
	}
}

func (t *taskWatcherCollection) handleTaskStopped(taskId uint64) {
	t.mutex.Lock()
	defer t.mutex.Unlock()
	// no taskID means no watches, early exit
	if t.coll[taskId] == nil || len(t.coll[taskId]) == 0 {
		// Uncomment this debug line if needed. Otherwise this is too verbose for even debug level.
		// watcherLog.WithFields(log.Fields{
		// 	"task-id": taskId,
		// }).Debug("no watchers")
		return
	}
	// Walk all watchers for a task ID
	for i, v := range t.coll[taskId] {
		// Check if they have a catcher assigned
		watcherLog.WithFields(log.Fields{
			"task-id":         taskId,
			"task-watcher-id": i,
		}).Debug("calling taskwatcher task stopped func")
		// Call the catcher
		v.handler.CatchTaskStopped()
	}
}

func (t *taskWatcherCollection) handleTaskDisabled(taskId uint64, why string) {
	t.mutex.Lock()
	defer t.mutex.Unlock()
	// no taskID means no watches, early exit
	if t.coll[taskId] == nil || len(t.coll[taskId]) == 0 {
		// Uncomment this debug line if needed. Otherwise this is too verbose for even debug level.
		// watcherLog.WithFields(log.Fields{
		// 	"task-id": taskId,
		// }).Debug("no watchers")
		return
	}
	// Walk all watchers for a task ID
	for i, v := range t.coll[taskId] {
		// Check if they have a catcher assigned
		watcherLog.WithFields(log.Fields{
			"task-id":         taskId,
			"task-watcher-id": i,
		}).Debug("calling taskwatcher task disabled func")
		// Call the catcher
		v.handler.CatchTaskDisabled(why)
	}
}
<|endoftext|>


<commit_before>package schema

import (
	"reflect"
	"testing"
)

func TestCastValue_Integer(t *testing.T) {
	f := Field{Type: "integer"}
	c, err := f.CastValue("42")
	if err != nil {
		t.Errorf("[Field.CastValue(integer)] err want:nil, got:%q", err)
	}
	intValue, ok := c.(int64)
	if !ok {
		t.Errorf("[Field.CastValue(integer)] cast want:int64, got:%s", reflect.TypeOf(c))
	}
	if intValue != 42 {
		t.Errorf("[Field.CastValue(integer)] val want:42, got:%d", intValue)
	}
}

func TestTestValue(t *testing.T) {
	f := Field{Type: "integer"}
	if !f.TestValue("42") {
		t.Errorf("[Field.TestValue(42)] want:true, got:false")
	}
	if f.TestValue("boo") {
		t.Errorf("[Field.TestValue(\"boo\")] want:false, got:true")
	}
}
<commit_msg>Add test to invalid field type case.<commit_after>package schema

import (
	"reflect"
	"testing"
)

func TestCastValue_Integer(t *testing.T) {
	f := Field{Type: "integer"}
	c, err := f.CastValue("42")
	if err != nil {
		t.Errorf("[Field.CastValue(integer)] err want:nil, got:%q", err)
	}
	intValue, ok := c.(int64)
	if !ok {
		t.Errorf("[Field.CastValue(integer)] cast want:int64, got:%s", reflect.TypeOf(c))
	}
	if intValue != 42 {
		t.Errorf("[Field.CastValue(integer)] val want:42, got:%d", intValue)
	}
}

func TestCastValue_InvalidFieldType(t *testing.T) {
	f := Field{Type: "invalidType"}
	if _, err := f.CastValue("42"); err == nil {
		t.Errorf("[Field.CastValue(invalidType)] err want:err, got:nil")
	}
}

func TestTestValue(t *testing.T) {
	f := Field{Type: "integer"}
	if !f.TestValue("42") {
		t.Errorf("[Field.TestValue(42)] want:true, got:false")
	}
	if f.TestValue("boo") {
		t.Errorf("[Field.TestValue(\"boo\")] want:false, got:true")
	}
}
<|endoftext|>


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

package calc

import (
	"github.com/Sirupsen/logrus"
	"github.com/projectcalico/libcalico-go/lib/backend/api"
	"github.com/projectcalico/libcalico-go/lib/validator"
	"reflect"
)

func NewValidationFilter(sink api.SyncerCallbacks) *ValidationFilter {
	return &ValidationFilter{
		sink: sink,
	}
}

type ValidationFilter struct {
	sink api.SyncerCallbacks
}

func (v *ValidationFilter) OnStatusUpdated(status api.SyncStatus) {
	// Pass through.
	v.sink.OnStatusUpdated(status)
}

func (v *ValidationFilter) OnUpdates(updates []api.Update) {
	filteredUpdates := make([]api.Update, len(updates))
	for i, update := range updates {
		logCxt := logrus.WithFields(logrus.Fields{
			"key":   update.Key,
			"value": update.Value,
		})
		logCxt.Debug("Validating KV pair.")
		if update.Value != nil {
			val := reflect.ValueOf(update.Value)
			if val.Kind() == reflect.Ptr {
				elem := val.Elem()
				if elem.Kind() == reflect.Struct {
					if err := validator.Validate(elem.Interface()); err != nil {
						logCxt.WithError(err).Warn("Validation failed; treating as missing")
						update.Value = nil
					}
				}
			}
		}
		filteredUpdates[i] = update
	}
	v.sink.OnUpdates(filteredUpdates)
}
<commit_msg>Validate that workload endpoints have names; required by felix.<commit_after>// Copyright (c) 2016 Tigera, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package calc

import (
	"errors"
	"github.com/Sirupsen/logrus"
	"github.com/projectcalico/libcalico-go/lib/backend/api"
	"github.com/projectcalico/libcalico-go/lib/backend/model"
	"github.com/projectcalico/libcalico-go/lib/validator"
	"reflect"
)

func NewValidationFilter(sink api.SyncerCallbacks) *ValidationFilter {
	return &ValidationFilter{
		sink: sink,
	}
}

type ValidationFilter struct {
	sink api.SyncerCallbacks
}

func (v *ValidationFilter) OnStatusUpdated(status api.SyncStatus) {
	// Pass through.
	v.sink.OnStatusUpdated(status)
}

func (v *ValidationFilter) OnUpdates(updates []api.Update) {
	filteredUpdates := make([]api.Update, len(updates))
	for i, update := range updates {
		logCxt := logrus.WithFields(logrus.Fields{
			"key":   update.Key,
			"value": update.Value,
		})
		logCxt.Debug("Validating KV pair.")
		if update.Value != nil {
			val := reflect.ValueOf(update.Value)
			if val.Kind() == reflect.Ptr {
				elem := val.Elem()
				if elem.Kind() == reflect.Struct {
					if err := validator.Validate(elem.Interface()); err != nil {
						logCxt.WithError(err).Warn("Validation failed; treating as missing")
						update.Value = nil
					}
				}
			}

			switch v := update.Value.(type) {
			case *model.WorkloadEndpoint:
				if v.Name == "" {
					logCxt.WithError(errors.New("Missing name")).Warn("Validation failed; treating as missing")
					update.Value = nil
				}
			}
		}
		filteredUpdates[i] = update
	}
	v.sink.OnUpdates(filteredUpdates)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"koding/db/mongodb/modelhelper"
	"net/http"

	"gopkg.in/mgo.v2/bson"
)

type LimitResponse struct {
	CanStart     bool    `json:"canStart"`
	Reason       string  `json:"reason,omitempty"`
	CurrentUsage float64 `json:"currentUsage,omitempty"`
	AllowedUsage float64 `json:"allowedUsage,omitempty"`
}

type ErrorResponse struct {
	Error string `json:"error"`
}

func checkerHTTP(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")

	accountId := r.URL.Query().Get("account_id")
	if accountId == "" {
		writeError(w, "", "account_id is required")
		return
	}

	yes := bson.IsObjectIdHex(accountId)
	if !yes {
		writeError(w, accountId, "account_id is not valid")
		return
	}

	account, err := modelhelper.GetAccountById(accountId)
	if err != nil {
		writeError(w, accountId, "account_id is not valid")
		return
	}

	var username = account.Profile.Nickname
	var response = checker(username)

	Log.Info(
		"Returning response#canStart: %v, for username: %v", response.CanStart, username,
	)

	err = json.NewEncoder(w).Encode(response)
	if err != nil {
		writeError(w, accountId, err.Error())
		return
	}
}

// iterate through each metric, check if user is over limit for that
// metric, return true if yes, go onto next metric if not
func checker(username string) *LimitResponse {
	var AllowedUsage float64
	var CurrentUsage float64

	for _, metric := range metricsToSave {
		response, err := metric.IsUserOverLimit(username, StopLimitKey)
		if err != nil {
			Log.Error(err.Error())
			continue
		}

		if !response.CanStart {
			return response
		}

		if metric.GetName() == NetworkOut {
			AllowedUsage = NetworkOutLimit
			CurrentUsage, err = storage.GetScore(metric.GetName(), username)
			if err != nil {
				return response
			}
		}
	}

	return &LimitResponse{CanStart: true, AllowedUsage: AllowedUsage, CurrentUsage: CurrentUsage}
}

func writeError(w http.ResponseWriter, accountId, err string) {
	Log.Error("accountId: %s; error: %s", accountId, err)

	js, _ := json.Marshal(ErrorResponse{err})

	w.WriteHeader(500)
	w.Write(js)
}
<commit_msg>fixed case when no special limit is defined<commit_after>package main

import (
	"encoding/json"
	"koding/db/mongodb/modelhelper"
	"net/http"

	"gopkg.in/mgo.v2/bson"
)

type LimitResponse struct {
	CanStart     bool    `json:"canStart"`
	Reason       string  `json:"reason,omitempty"`
	CurrentUsage float64 `json:"currentUsage,omitempty"`
	AllowedUsage float64 `json:"allowedUsage,omitempty"`
}

type ErrorResponse struct {
	Error string `json:"error"`
}

func checkerHTTP(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")

	accountId := r.URL.Query().Get("account_id")
	if accountId == "" {
		writeError(w, "", "account_id is required")
		return
	}

	yes := bson.IsObjectIdHex(accountId)
	if !yes {
		writeError(w, accountId, "account_id is not valid")
		return
	}

	account, err := modelhelper.GetAccountById(accountId)
	if err != nil {
		writeError(w, accountId, "account_id is not valid")
		return
	}

	var username = account.Profile.Nickname
	var response = checker(username)

	Log.Info(
		"Returning response#canStart: %v, for username: %v", response.CanStart, username,
	)

	err = json.NewEncoder(w).Encode(response)
	if err != nil {
		writeError(w, accountId, err.Error())
		return
	}
}

// iterate through each metric, check if user is over limit for that
// metric, return true if yes, go onto next metric if not
func checker(username string) *LimitResponse {
	var AllowedUsage float64
	var CurrentUsage float64

	for _, metric := range metricsToSave {
		response, err := metric.IsUserOverLimit(username, StopLimitKey)
		if err != nil {
			Log.Error(err.Error())
			continue
		}

		if !response.CanStart {
			return response
		}

		if metric.GetName() == NetworkOut {
			AllowedUsage, err = storage.GetUserLimit(username)
			if err != nil {
			    AllowedUsage = NetworkOutLimit
			}
			CurrentUsage, _ = storage.GetScore(metric.GetName(), username)
		}
	}

	return &LimitResponse{CanStart: true, AllowedUsage: AllowedUsage, CurrentUsage: CurrentUsage}
}

func writeError(w http.ResponseWriter, accountId, err string) {
	Log.Error("accountId: %s; error: %s", accountId, err)

	js, _ := json.Marshal(ErrorResponse{err})

	w.WriteHeader(500)
	w.Write(js)
}
<|endoftext|>


<commit_before>package models

import (
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/koding/bongo"
)

type Channel struct {
	// unique identifier of the channel
	Id int64 `json:"id,string"`

	// Name of the channel
	Name string `json:"name"                         sql:"NOT NULL;TYPE:VARCHAR(200);"`

	// Creator of the channel
	CreatorId int64 `json:"creatorId,string"         sql:"NOT NULL"`

	// Name of the group which channel is belong to
	GroupName string `json:"groupName"               sql:"NOT NULL;TYPE:VARCHAR(200);"`

	// Purpose of the channel
	Purpose string `json:"purpose"`

	// Secret key of the channel for event propagation purposes
	// we can put this key into another table?
	SecretKey string `json:"-"`

	// Type of the channel
	TypeConstant string `json:"typeConstant"         sql:"NOT NULL;TYPE:VARCHAR(100);"`

	// Privacy constant of the channel
	PrivacyConstant string `json:"privacyConstant"   sql:"NOT NULL;TYPE:VARCHAR(100);"`

	// Creation date of the channel
	CreatedAt time.Time `json:"createdAt"            sql:"NOT NULL"`

	// Modification date of the channel
	UpdatedAt time.Time `json:"updatedAt"            sql:"NOT NULL"`

	// Deletion date of the channel
	DeletedAt time.Time `json:"deletedAt"`
}

// to-do check for allowed channels
const (
	// TYPES
	Channel_TYPE_GROUP           = "group"
	Channel_TYPE_TOPIC           = "topic"
	Channel_TYPE_FOLLOWINGFEED   = "followingfeed"
	Channel_TYPE_FOLLOWERS       = "followers"
	Channel_TYPE_CHAT            = "chat"
	Channel_TYPE_PINNED_ACTIVITY = "pinnedactivity"
	Channel_TYPE_PRIVATE_MESSAGE = "privatemessage"
	Channel_TYPE_DEFAULT         = "default"
	// Privacy
	Channel_PRIVACY_PUBLIC  = "public"
	Channel_PRIVACY_PRIVATE = "private"
	// Koding Group Name
	Channel_KODING_NAME = "koding"
)

func NewChannel() *Channel {
	return &Channel{
		Name:            "Channel" + RandomName(),
		CreatorId:       0,
		GroupName:       Channel_KODING_NAME,
		Purpose:         "",
		SecretKey:       "",
		TypeConstant:    Channel_TYPE_DEFAULT,
		PrivacyConstant: Channel_PRIVACY_PRIVATE,
	}
}

func NewPrivateMessageChannel(creatorId int64, groupName string) *Channel {
	c := NewChannel()
	c.GroupName = groupName
	c.CreatorId = creatorId
	c.Name = RandomName()
	c.TypeConstant = Channel_TYPE_PRIVATE_MESSAGE
	c.PrivacyConstant = Channel_PRIVACY_PRIVATE
	c.Purpose = ""
	return c
}

func (c *Channel) BeforeCreate() {
	c.CreatedAt = time.Now().UTC()
	c.UpdatedAt = time.Now().UTC()
	c.DeletedAt = ZeroDate()
}

func (c *Channel) BeforeUpdate() {
	c.UpdatedAt = time.Now()
}

func (c Channel) GetId() int64 {
	return c.Id
}

func (c Channel) TableName() string {
	return "api.channel"
}

func (c *Channel) AfterCreate() {
	bongo.B.AfterCreate(c)
}

func (c *Channel) AfterUpdate() {
	bongo.B.AfterUpdate(c)
}

func (c Channel) AfterDelete() {
	bongo.B.AfterDelete(c)
}

func (c *Channel) Update() error {
	if c.Name == "" || c.GroupName == "" {
		return fmt.Errorf("Validation failed %s - %s", c.Name, c.GroupName)
	}

	return bongo.B.Update(c)
}

func (c *Channel) Create() error {
	if c.Name == "" || c.GroupName == "" || c.TypeConstant == "" {
		return fmt.Errorf("Validation failed %s - %s -%s", c.Name, c.GroupName, c.TypeConstant)
	}

	// golang returns -1 if item not in the string
	if strings.Index(c.Name, " ") > -1 {
		return fmt.Errorf("Channel name %q has empty space in it", c.Name)
	}

	if c.TypeConstant == Channel_TYPE_GROUP ||
		c.TypeConstant == Channel_TYPE_FOLLOWERS /* we can add more types here */ {

		var selector map[string]interface{}
		switch c.TypeConstant {
		case Channel_TYPE_GROUP:
			selector = map[string]interface{}{
				"group_name":    c.GroupName,
				"type_constant": c.TypeConstant,
			}
		case Channel_TYPE_FOLLOWERS:
			selector = map[string]interface{}{
				"creator_id":    c.CreatorId,
				"type_constant": c.TypeConstant,
			}
		}

		// if err is nil
		// it means we already have that channel
		err := c.One(bongo.NewQS(selector))
		if err == nil {
			return nil
			// return fmt.Errorf("%s typed channel is already created before for %s group", c.TypeConstant, c.GroupName)
		}

		if err != gorm.RecordNotFound {
			return err
		}

	}

	return bongo.B.Create(c)
}

func (c *Channel) Delete() error {
	return bongo.B.Delete(c)
}

func (c *Channel) ById(id int64) error {
	return bongo.B.ById(c, id)
}

func (c *Channel) One(q *bongo.Query) error {
	return bongo.B.One(c, c, q)
}

func (c *Channel) Some(data interface{}, q *bongo.Query) error {
	return bongo.B.Some(c, data, q)
}

func (c *Channel) FetchByIds(ids []int64) ([]Channel, error) {
	var channels []Channel

	if len(ids) == 0 {
		return channels, nil
	}

	if err := bongo.B.FetchByIds(c, &channels, ids); err != nil {
		return nil, err
	}
	return channels, nil
}

func (c *Channel) AddParticipant(participantId int64) (*ChannelParticipant, error) {
	if c.Id == 0 {
		return nil, errors.New("Channel Id is not set")
	}

	cp := NewChannelParticipant()
	cp.ChannelId = c.Id
	cp.AccountId = participantId

	err := cp.FetchParticipant()
	if err != nil && err != gorm.RecordNotFound {
		return nil, err
	}

	// if we have this record in DB
	if cp.Id != 0 {
		// if status is not active
		if cp.StatusConstant == ChannelParticipant_STATUS_ACTIVE {
			return nil, errors.New(fmt.Sprintf("Account %d is already a participant of channel %d", cp.AccountId, cp.ChannelId))
		}
		cp.StatusConstant = ChannelParticipant_STATUS_ACTIVE
		if err := cp.Update(); err != nil {
			return nil, err
		}
		return cp, nil
	}

	cp.StatusConstant = ChannelParticipant_STATUS_ACTIVE

	if err := cp.Create(); err != nil {
		return nil, err
	}

	return cp, nil
}

func (c *Channel) RemoveParticipant(participantId int64) error {
	if c.Id == 0 {
		return errors.New("Channel Id is not set")
	}

	cp := NewChannelParticipant()
	cp.ChannelId = c.Id
	cp.AccountId = participantId

	err := cp.FetchParticipant()
	// if user is not in this channel, do nothing
	if err == gorm.RecordNotFound {
		return nil
	}

	if err != nil {
		return err
	}

	if cp.StatusConstant == ChannelParticipant_STATUS_LEFT {
		return nil
	}

	cp.StatusConstant = ChannelParticipant_STATUS_LEFT
	if err := cp.Update(); err != nil {
		return err
	}

	return nil
}

func (c *Channel) FetchParticipantIds() ([]int64, error) {
	var participantIds []int64

	if c.Id == 0 {
		return participantIds, errors.New("Channel Id is not set")
	}

	query := &bongo.Query{
		Selector: map[string]interface{}{
			"channel_id":      c.Id,
			"status_constant": ChannelParticipant_STATUS_ACTIVE,
		},
		Pluck: "account_id",
	}

	cp := NewChannelParticipant()
	err := cp.Some(&participantIds, query)
	if err != nil {
		return nil, err
	}

	return participantIds, nil
}

func (c *Channel) AddMessage(messageId int64) (*ChannelMessageList, error) {
	if c.Id == 0 {
		return nil, errors.New("Channel Id is not set")
	}

	cml := NewChannelMessageList()

	selector := map[string]interface{}{
		"channel_id": c.Id,
		"message_id": messageId,
	}
	err := cml.One(bongo.NewQS(selector))
	if err == nil {
		return nil, errors.New("Message is already in the channel")
	}

	// silence record not found err
	if err != gorm.RecordNotFound {
		return nil, err
	}

	cml.ChannelId = c.Id
	cml.MessageId = messageId

	if err := cml.Create(); err != nil {
		return nil, err
	}

	return cml, nil
}

func (c *Channel) RemoveMessage(messageId int64) (*ChannelMessageList, error) {
	if c.Id == 0 {
		return nil, errors.New("Channel Id is not set")
	}

	cml := NewChannelMessageList()
	selector := map[string]interface{}{
		"channel_id": c.Id,
		"message_id": messageId,
	}
	err := cml.One(bongo.NewQS(selector))
	// one returns error when record not found case
	// but we dont care if it is not there tho
	if err != nil {
		return nil, err
	}

	if err := cml.Delete(); err != nil {
		return nil, err
	}

	return cml, nil
}

func (c *Channel) FetchChannelIdByNameAndGroupName(name, groupName string) (int64, error) {
	query := &bongo.Query{
		Selector: map[string]interface{}{
			"name":       name,
			"group_name": groupName,
		},
		Pagination: *bongo.NewPagination(1, 0),
		Pluck:      "id",
	}
	var ids []int64
	if err := c.Some(&ids, query); err != nil {
		return 0, err
	}

	if ids == nil {
		return 0, gorm.RecordNotFound
	}

	if len(ids) == 0 {
		return 0, gorm.RecordNotFound
	}

	return ids[0], nil
}

func (c *Channel) Search(q *Query) ([]Channel, error) {

	if q.GroupName == "" {
		return nil, fmt.Errorf("Query doesnt have any Group info %+v", q)
	}

	var channels []Channel

	query := bongo.B.DB.Table(c.TableName()).Limit(q.Limit)

	query = query.Where("type_constant = ?", q.Type)
	query = query.Where("privacy_constant = ?", Channel_PRIVACY_PUBLIC)
	query = query.Where("group_name = ?", q.GroupName)
	query = query.Where("name like ?", q.Name+"%")

	if err := query.Find(&channels).Error; err != nil {
		return nil, err
	}

	if channels == nil {
		return make([]Channel, 0), nil
	}

	return channels, nil
}

func (c *Channel) List(q *Query) ([]Channel, error) {

	if q.GroupName == "" {
		return nil, fmt.Errorf("Query doesnt have any Group info %+v", q)
	}

	var channels []Channel

	query := &bongo.Query{
		Selector: map[string]interface{}{
			"group_name": q.GroupName,
		},
		Pagination: *bongo.NewPagination(q.Limit, q.Skip),
	}

	if q.Type != "" {
		query.Selector["type_constant"] = q.Type
	}

	err := c.Some(&channels, query)
	if err != nil {
		return nil, err
	}

	if channels == nil {
		return make([]Channel, 0), nil
	}

	return channels, nil
}

func (c *Channel) FetchLastMessage() (*ChannelMessage, error) {
	if c.Id == 0 {
		return nil, errors.New("Channel Id is not set")
	}

	cml := NewChannelMessageList()
	query := &bongo.Query{
		Selector: map[string]interface{}{
			"channel_id": c.Id,
		},
		Sort: map[string]string{
			"added_at": "DESC",
		},
		Pagination: *bongo.NewPagination(1, 0),
		Pluck:      "message_id",
	}

	var messageIds []int64
	err := cml.Some(&messageIds, query)
	if err != nil {
		return nil, err
	}

	if messageIds == nil || len(messageIds) == 0 {
		return nil, nil
	}

	cm := NewChannelMessage()
	if err := cm.ById(messageIds[0]); err != nil {
		return nil, err
	}

	return cm, nil
}

func (c *Channel) CheckChannelPinned(ids []int64) (bool, error) {
	err := bongo.B.DB.Table(c.TableName()).
		Where(ids).
		Where("type_constant = ?", Channel_TYPE_PINNED_ACTIVITY).
		Where("creator_id = ?", c.CreatorId).
		Limit(1).
		Find(c).Error

	if err != nil {
		if err == gorm.RecordNotFound {

			return false, nil
		}

		return false, err
	}

	return true, nil
}
<commit_msg>Social: added ensurePinnedActivityChannel to FetchPinnedActivityChannel<commit_after>package models

import (
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/koding/bongo"
)

type Channel struct {
	// unique identifier of the channel
	Id int64 `json:"id,string"`

	// Name of the channel
	Name string `json:"name"                         sql:"NOT NULL;TYPE:VARCHAR(200);"`

	// Creator of the channel
	CreatorId int64 `json:"creatorId,string"         sql:"NOT NULL"`

	// Name of the group which channel is belong to
	GroupName string `json:"groupName"               sql:"NOT NULL;TYPE:VARCHAR(200);"`

	// Purpose of the channel
	Purpose string `json:"purpose"`

	// Secret key of the channel for event propagation purposes
	// we can put this key into another table?
	SecretKey string `json:"-"`

	// Type of the channel
	TypeConstant string `json:"typeConstant"         sql:"NOT NULL;TYPE:VARCHAR(100);"`

	// Privacy constant of the channel
	PrivacyConstant string `json:"privacyConstant"   sql:"NOT NULL;TYPE:VARCHAR(100);"`

	// Creation date of the channel
	CreatedAt time.Time `json:"createdAt"            sql:"NOT NULL"`

	// Modification date of the channel
	UpdatedAt time.Time `json:"updatedAt"            sql:"NOT NULL"`

	// Deletion date of the channel
	DeletedAt time.Time `json:"deletedAt"`
}

// to-do check for allowed channels
const (
	// TYPES
	Channel_TYPE_GROUP           = "group"
	Channel_TYPE_TOPIC           = "topic"
	Channel_TYPE_FOLLOWINGFEED   = "followingfeed"
	Channel_TYPE_FOLLOWERS       = "followers"
	Channel_TYPE_CHAT            = "chat"
	Channel_TYPE_PINNED_ACTIVITY = "pinnedactivity"
	Channel_TYPE_PRIVATE_MESSAGE = "privatemessage"
	Channel_TYPE_DEFAULT         = "default"
	// Privacy
	Channel_PRIVACY_PUBLIC  = "public"
	Channel_PRIVACY_PRIVATE = "private"
	// Koding Group Name
	Channel_KODING_NAME = "koding"
)

func NewChannel() *Channel {
	return &Channel{
		Name:            "Channel" + RandomName(),
		CreatorId:       0,
		GroupName:       Channel_KODING_NAME,
		Purpose:         "",
		SecretKey:       "",
		TypeConstant:    Channel_TYPE_DEFAULT,
		PrivacyConstant: Channel_PRIVACY_PRIVATE,
	}
}

func NewPrivateMessageChannel(creatorId int64, groupName string) *Channel {
	c := NewChannel()
	c.GroupName = groupName
	c.CreatorId = creatorId
	c.Name = RandomName()
	c.TypeConstant = Channel_TYPE_PRIVATE_MESSAGE
	c.PrivacyConstant = Channel_PRIVACY_PRIVATE
	c.Purpose = ""
	return c
}

func (c *Channel) BeforeCreate() {
	c.CreatedAt = time.Now().UTC()
	c.UpdatedAt = time.Now().UTC()
	c.DeletedAt = ZeroDate()
}

func (c *Channel) BeforeUpdate() {
	c.UpdatedAt = time.Now()
}

func (c Channel) GetId() int64 {
	return c.Id
}

func (c Channel) TableName() string {
	return "api.channel"
}

func (c *Channel) AfterCreate() {
	bongo.B.AfterCreate(c)
}

func (c *Channel) AfterUpdate() {
	bongo.B.AfterUpdate(c)
}

func (c Channel) AfterDelete() {
	bongo.B.AfterDelete(c)
}

func (c *Channel) Update() error {
	if c.Name == "" || c.GroupName == "" {
		return fmt.Errorf("Validation failed %s - %s", c.Name, c.GroupName)
	}

	return bongo.B.Update(c)
}

func (c *Channel) Create() error {
	if c.Name == "" || c.GroupName == "" || c.TypeConstant == "" {
		return fmt.Errorf("Validation failed %s - %s -%s", c.Name, c.GroupName, c.TypeConstant)
	}

	// golang returns -1 if item not in the string
	if strings.Index(c.Name, " ") > -1 {
		return fmt.Errorf("Channel name %q has empty space in it", c.Name)
	}

	if c.TypeConstant == Channel_TYPE_GROUP ||
		c.TypeConstant == Channel_TYPE_FOLLOWERS /* we can add more types here */ {

		var selector map[string]interface{}
		switch c.TypeConstant {
		case Channel_TYPE_GROUP:
			selector = map[string]interface{}{
				"group_name":    c.GroupName,
				"type_constant": c.TypeConstant,
			}
		case Channel_TYPE_FOLLOWERS:
			selector = map[string]interface{}{
				"creator_id":    c.CreatorId,
				"type_constant": c.TypeConstant,
			}
		}

		// if err is nil
		// it means we already have that channel
		err := c.One(bongo.NewQS(selector))
		if err == nil {
			return nil
			// return fmt.Errorf("%s typed channel is already created before for %s group", c.TypeConstant, c.GroupName)
		}

		if err != gorm.RecordNotFound {
			return err
		}

	}

	return bongo.B.Create(c)
}

func (c *Channel) Delete() error {
	return bongo.B.Delete(c)
}

func (c *Channel) ById(id int64) error {
	return bongo.B.ById(c, id)
}

func (c *Channel) One(q *bongo.Query) error {
	return bongo.B.One(c, c, q)
}

func (c *Channel) Some(data interface{}, q *bongo.Query) error {
	return bongo.B.Some(c, data, q)
}

func (c *Channel) FetchByIds(ids []int64) ([]Channel, error) {
	var channels []Channel

	if len(ids) == 0 {
		return channels, nil
	}

	if err := bongo.B.FetchByIds(c, &channels, ids); err != nil {
		return nil, err
	}
	return channels, nil
}

func (c *Channel) AddParticipant(participantId int64) (*ChannelParticipant, error) {
	if c.Id == 0 {
		return nil, errors.New("Channel Id is not set")
	}

	cp := NewChannelParticipant()
	cp.ChannelId = c.Id
	cp.AccountId = participantId

	err := cp.FetchParticipant()
	if err != nil && err != gorm.RecordNotFound {
		return nil, err
	}

	// if we have this record in DB
	if cp.Id != 0 {
		// if status is not active
		if cp.StatusConstant == ChannelParticipant_STATUS_ACTIVE {
			return nil, errors.New(fmt.Sprintf("Account %d is already a participant of channel %d", cp.AccountId, cp.ChannelId))
		}
		cp.StatusConstant = ChannelParticipant_STATUS_ACTIVE
		if err := cp.Update(); err != nil {
			return nil, err
		}
		return cp, nil
	}

	cp.StatusConstant = ChannelParticipant_STATUS_ACTIVE

	if err := cp.Create(); err != nil {
		return nil, err
	}

	return cp, nil
}

func (c *Channel) RemoveParticipant(participantId int64) error {
	if c.Id == 0 {
		return errors.New("Channel Id is not set")
	}

	cp := NewChannelParticipant()
	cp.ChannelId = c.Id
	cp.AccountId = participantId

	err := cp.FetchParticipant()
	// if user is not in this channel, do nothing
	if err == gorm.RecordNotFound {
		return nil
	}

	if err != nil {
		return err
	}

	if cp.StatusConstant == ChannelParticipant_STATUS_LEFT {
		return nil
	}

	cp.StatusConstant = ChannelParticipant_STATUS_LEFT
	if err := cp.Update(); err != nil {
		return err
	}

	return nil
}

func (c *Channel) FetchParticipantIds() ([]int64, error) {
	var participantIds []int64

	if c.Id == 0 {
		return participantIds, errors.New("Channel Id is not set")
	}

	query := &bongo.Query{
		Selector: map[string]interface{}{
			"channel_id":      c.Id,
			"status_constant": ChannelParticipant_STATUS_ACTIVE,
		},
		Pluck: "account_id",
	}

	cp := NewChannelParticipant()
	err := cp.Some(&participantIds, query)
	if err != nil {
		return nil, err
	}

	return participantIds, nil
}

func (c *Channel) AddMessage(messageId int64) (*ChannelMessageList, error) {
	if c.Id == 0 {
		return nil, errors.New("Channel Id is not set")
	}

	cml := NewChannelMessageList()

	selector := map[string]interface{}{
		"channel_id": c.Id,
		"message_id": messageId,
	}
	err := cml.One(bongo.NewQS(selector))
	if err == nil {
		return nil, errors.New("Message is already in the channel")
	}

	// silence record not found err
	if err != gorm.RecordNotFound {
		return nil, err
	}

	cml.ChannelId = c.Id
	cml.MessageId = messageId

	if err := cml.Create(); err != nil {
		return nil, err
	}

	return cml, nil
}

func (c *Channel) RemoveMessage(messageId int64) (*ChannelMessageList, error) {
	if c.Id == 0 {
		return nil, errors.New("Channel Id is not set")
	}

	cml := NewChannelMessageList()
	selector := map[string]interface{}{
		"channel_id": c.Id,
		"message_id": messageId,
	}
	err := cml.One(bongo.NewQS(selector))
	// one returns error when record not found case
	// but we dont care if it is not there tho
	if err != nil {
		return nil, err
	}

	if err := cml.Delete(); err != nil {
		return nil, err
	}

	return cml, nil
}

func (c *Channel) FetchChannelIdByNameAndGroupName(name, groupName string) (int64, error) {
	query := &bongo.Query{
		Selector: map[string]interface{}{
			"name":       name,
			"group_name": groupName,
		},
		Pagination: *bongo.NewPagination(1, 0),
		Pluck:      "id",
	}
	var ids []int64
	if err := c.Some(&ids, query); err != nil {
		return 0, err
	}

	if ids == nil {
		return 0, gorm.RecordNotFound
	}

	if len(ids) == 0 {
		return 0, gorm.RecordNotFound
	}

	return ids[0], nil
}

func (c *Channel) Search(q *Query) ([]Channel, error) {

	if q.GroupName == "" {
		return nil, fmt.Errorf("Query doesnt have any Group info %+v", q)
	}

	var channels []Channel

	query := bongo.B.DB.Table(c.TableName()).Limit(q.Limit)

	query = query.Where("type_constant = ?", q.Type)
	query = query.Where("privacy_constant = ?", Channel_PRIVACY_PUBLIC)
	query = query.Where("group_name = ?", q.GroupName)
	query = query.Where("name like ?", q.Name+"%")

	if err := query.Find(&channels).Error; err != nil {
		return nil, err
	}

	if channels == nil {
		return make([]Channel, 0), nil
	}

	return channels, nil
}

func (c *Channel) List(q *Query) ([]Channel, error) {

	if q.GroupName == "" {
		return nil, fmt.Errorf("Query doesnt have any Group info %+v", q)
	}

	var channels []Channel

	query := &bongo.Query{
		Selector: map[string]interface{}{
			"group_name": q.GroupName,
		},
		Pagination: *bongo.NewPagination(q.Limit, q.Skip),
	}

	if q.Type != "" {
		query.Selector["type_constant"] = q.Type
	}

	err := c.Some(&channels, query)
	if err != nil {
		return nil, err
	}

	if channels == nil {
		return make([]Channel, 0), nil
	}

	return channels, nil
}

func (c *Channel) FetchLastMessage() (*ChannelMessage, error) {
	if c.Id == 0 {
		return nil, errors.New("Channel Id is not set")
	}

	cml := NewChannelMessageList()
	query := &bongo.Query{
		Selector: map[string]interface{}{
			"channel_id": c.Id,
		},
		Sort: map[string]string{
			"added_at": "DESC",
		},
		Pagination: *bongo.NewPagination(1, 0),
		Pluck:      "message_id",
	}

	var messageIds []int64
	err := cml.Some(&messageIds, query)
	if err != nil {
		return nil, err
	}

	if messageIds == nil || len(messageIds) == 0 {
		return nil, nil
	}

	cm := NewChannelMessage()
	if err := cm.ById(messageIds[0]); err != nil {
		return nil, err
	}

	return cm, nil
}

func (c *Channel) FetchPinnedActivityChannel(accountId int64, groupName string) error {
	query := &bongo.Query{
		Selector: map[string]interface{}{
			"creator_id":    accountId,
			"group_name":    groupName,
			"type_constant": Channel_TYPE_PINNED_ACTIVITY,
		},
	}

	return c.One(query)
}
<|endoftext|>


<commit_before>/*
Copyright IBM Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package service

import (
	"fmt"
	"sync"
	"testing"
	"time"

	"github.com/hyperledger/fabric/core/deliverservice"
	"github.com/hyperledger/fabric/core/deliverservice/blocksprovider"
	"github.com/hyperledger/fabric/gossip/api"
	"github.com/hyperledger/fabric/gossip/election"
	"github.com/hyperledger/fabric/gossip/identity"
	"github.com/hyperledger/fabric/gossip/state"
	"github.com/spf13/viper"
	"github.com/stretchr/testify/assert"
)

type embeddingDeliveryService struct {
	deliverclient.DeliverService
	startSignal sync.WaitGroup
	stopSignal  sync.WaitGroup
}

func newEmbeddingDeliveryService(ds deliverclient.DeliverService) *embeddingDeliveryService {
	eds := &embeddingDeliveryService{
		DeliverService: ds,
	}
	eds.startSignal.Add(1)
	eds.stopSignal.Add(1)
	return eds
}

func (eds *embeddingDeliveryService) waitForDeliveryServiceActivation() {
	eds.startSignal.Wait()
}

func (eds *embeddingDeliveryService) waitForDeliveryServiceTermination() {
	eds.stopSignal.Wait()
}

func (eds *embeddingDeliveryService) StartDeliverForChannel(chainID string, ledgerInfo blocksprovider.LedgerInfo, finalizer func()) error {
	eds.startSignal.Done()
	return eds.DeliverService.StartDeliverForChannel(chainID, ledgerInfo, finalizer)
}

func (eds *embeddingDeliveryService) StopDeliverForChannel(chainID string) error {
	eds.stopSignal.Done()
	return eds.DeliverService.StopDeliverForChannel(chainID)
}

func (eds *embeddingDeliveryService) Stop() {
	eds.DeliverService.Stop()
}

type embeddingDeliveryServiceFactory struct {
	DeliveryServiceFactory
}

func (edsf *embeddingDeliveryServiceFactory) Service(g GossipService, endpoints []string, mcs api.MessageCryptoService) (deliverclient.DeliverService, error) {
	ds, _ := edsf.DeliveryServiceFactory.Service(g, endpoints, mcs)
	return newEmbeddingDeliveryService(ds), nil
}

func TestLeaderYield(t *testing.T) {
	// Scenario: Spawn 2 peers and wait for the first one to be the leader
	// There isn't any orderer present so the leader peer won't be able to
	// connect to the orderer, and should relinquish its leadership after a while.
	// Make sure the other peer declares itself as the leader soon after.
	deliverclient.SetReconnectTotalTimeThreshold(time.Second * 5)
	viper.Set("peer.gossip.useLeaderElection", true)
	viper.Set("peer.gossip.orgLeader", false)
	n := 2
	portPrefix := 30000
	gossips := startPeers(t, n, portPrefix)
	defer stopPeers(gossips)
	channelName := "channelA"
	peerIndexes := []int{0, 1}
	// Add peers to the channel
	addPeersToChannel(t, n, portPrefix, channelName, gossips, peerIndexes)
	// Prime the membership view of the peers
	waitForFullMembership(t, gossips, n, time.Second*30, time.Second*2)
	mcs := &naiveCryptoService{}
	// Helper function that creates a gossipService instance
	newGossipService := func(i int) *gossipServiceImpl {
		peerIdentity := api.PeerIdentityType(fmt.Sprintf("localhost:%d", portPrefix+i))
		gs := &gossipServiceImpl{
			mcs:             mcs,
			gossipSvc:       gossips[i],
			chains:          make(map[string]state.GossipStateProvider),
			leaderElection:  make(map[string]election.LeaderElectionService),
			deliveryFactory: &embeddingDeliveryServiceFactory{&deliveryFactoryImpl{}},
			idMapper:        identity.NewIdentityMapper(mcs, peerIdentity),
			peerIdentity:    peerIdentity,
			secAdv:          &secAdvMock{},
		}
		gossipServiceInstance = gs
		gs.InitializeChannel(channelName, &mockLedgerInfo{1}, []string{"localhost:7050"})
		return gs
	}

	p0 := newGossipService(0)
	p1 := newGossipService(1)

	// Returns index of the leader or -1 if no leader elected
	getLeader := func() int {
		p0.lock.RLock()
		p1.lock.RLock()
		defer p0.lock.RUnlock()
		defer p1.lock.RUnlock()

		if p0.leaderElection[channelName].IsLeader() {
			// Ensure p1 isn't a leader at the same time
			assert.False(t, p1.leaderElection[channelName].IsLeader())
			return 0
		}
		if p1.leaderElection[channelName].IsLeader() {
			return 1
		}
		return -1
	}

	ds0 := p0.deliveryService.(*embeddingDeliveryService)
	ds1 := p1.deliveryService.(*embeddingDeliveryService)

	// Wait for p0 to connect to the ordering service
	ds0.waitForDeliveryServiceActivation()
	t.Log("p0 started its delivery service")
	// Ensure it's a leader
	assert.Equal(t, 0, getLeader())
	// Wait for p0 to lose its leadership
	ds0.waitForDeliveryServiceTermination()
	t.Log("p0 stopped its delivery service")
	// Ensure there is no leader
	assert.Equal(t, -1, getLeader())
	// Wait for p1 to take over
	ds1.waitForDeliveryServiceActivation()
	t.Log("p1 started its delivery service")
	// Ensure it's a leader now
	assert.Equal(t, 1, getLeader())
	p0.chains[channelName].Stop()
	p1.chains[channelName].Stop()
	p0.deliveryService.Stop()
	p1.deliveryService.Stop()
}
<commit_msg>[FAB-5503] Disable misbehaving test<commit_after>/*
Copyright IBM Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package service

import (
	"fmt"
	"sync"
	"testing"
	"time"

	"github.com/hyperledger/fabric/core/deliverservice"
	"github.com/hyperledger/fabric/core/deliverservice/blocksprovider"
	"github.com/hyperledger/fabric/gossip/api"
	"github.com/hyperledger/fabric/gossip/election"
	"github.com/hyperledger/fabric/gossip/identity"
	"github.com/hyperledger/fabric/gossip/state"
	"github.com/spf13/viper"
	"github.com/stretchr/testify/assert"
)

type embeddingDeliveryService struct {
	deliverclient.DeliverService
	startSignal sync.WaitGroup
	stopSignal  sync.WaitGroup
}

func newEmbeddingDeliveryService(ds deliverclient.DeliverService) *embeddingDeliveryService {
	eds := &embeddingDeliveryService{
		DeliverService: ds,
	}
	eds.startSignal.Add(1)
	eds.stopSignal.Add(1)
	return eds
}

func (eds *embeddingDeliveryService) waitForDeliveryServiceActivation() {
	eds.startSignal.Wait()
}

func (eds *embeddingDeliveryService) waitForDeliveryServiceTermination() {
	eds.stopSignal.Wait()
}

func (eds *embeddingDeliveryService) StartDeliverForChannel(chainID string, ledgerInfo blocksprovider.LedgerInfo, finalizer func()) error {
	eds.startSignal.Done()
	return eds.DeliverService.StartDeliverForChannel(chainID, ledgerInfo, finalizer)
}

func (eds *embeddingDeliveryService) StopDeliverForChannel(chainID string) error {
	eds.stopSignal.Done()
	return eds.DeliverService.StopDeliverForChannel(chainID)
}

func (eds *embeddingDeliveryService) Stop() {
	eds.DeliverService.Stop()
}

type embeddingDeliveryServiceFactory struct {
	DeliveryServiceFactory
}

func (edsf *embeddingDeliveryServiceFactory) Service(g GossipService, endpoints []string, mcs api.MessageCryptoService) (deliverclient.DeliverService, error) {
	ds, _ := edsf.DeliveryServiceFactory.Service(g, endpoints, mcs)
	return newEmbeddingDeliveryService(ds), nil
}

func TestLeaderYield(t *testing.T) {
	t.Skip()
	// Scenario: Spawn 2 peers and wait for the first one to be the leader
	// There isn't any orderer present so the leader peer won't be able to
	// connect to the orderer, and should relinquish its leadership after a while.
	// Make sure the other peer declares itself as the leader soon after.
	deliverclient.SetReconnectTotalTimeThreshold(time.Second * 5)
	viper.Set("peer.gossip.useLeaderElection", true)
	viper.Set("peer.gossip.orgLeader", false)
	n := 2
	portPrefix := 30000
	gossips := startPeers(t, n, portPrefix)
	defer stopPeers(gossips)
	channelName := "channelA"
	peerIndexes := []int{0, 1}
	// Add peers to the channel
	addPeersToChannel(t, n, portPrefix, channelName, gossips, peerIndexes)
	// Prime the membership view of the peers
	waitForFullMembership(t, gossips, n, time.Second*30, time.Second*2)
	mcs := &naiveCryptoService{}
	// Helper function that creates a gossipService instance
	newGossipService := func(i int) *gossipServiceImpl {
		peerIdentity := api.PeerIdentityType(fmt.Sprintf("localhost:%d", portPrefix+i))
		gs := &gossipServiceImpl{
			mcs:             mcs,
			gossipSvc:       gossips[i],
			chains:          make(map[string]state.GossipStateProvider),
			leaderElection:  make(map[string]election.LeaderElectionService),
			deliveryFactory: &embeddingDeliveryServiceFactory{&deliveryFactoryImpl{}},
			idMapper:        identity.NewIdentityMapper(mcs, peerIdentity),
			peerIdentity:    peerIdentity,
			secAdv:          &secAdvMock{},
		}
		gossipServiceInstance = gs
		gs.InitializeChannel(channelName, &mockLedgerInfo{1}, []string{"localhost:7050"})
		return gs
	}

	p0 := newGossipService(0)
	p1 := newGossipService(1)

	// Returns index of the leader or -1 if no leader elected
	getLeader := func() int {
		p0.lock.RLock()
		p1.lock.RLock()
		defer p0.lock.RUnlock()
		defer p1.lock.RUnlock()

		if p0.leaderElection[channelName].IsLeader() {
			// Ensure p1 isn't a leader at the same time
			assert.False(t, p1.leaderElection[channelName].IsLeader())
			return 0
		}
		if p1.leaderElection[channelName].IsLeader() {
			return 1
		}
		return -1
	}

	ds0 := p0.deliveryService.(*embeddingDeliveryService)
	ds1 := p1.deliveryService.(*embeddingDeliveryService)

	// Wait for p0 to connect to the ordering service
	ds0.waitForDeliveryServiceActivation()
	t.Log("p0 started its delivery service")
	// Ensure it's a leader
	assert.Equal(t, 0, getLeader())
	// Wait for p0 to lose its leadership
	ds0.waitForDeliveryServiceTermination()
	t.Log("p0 stopped its delivery service")
	// Ensure there is no leader
	assert.Equal(t, -1, getLeader())
	// Wait for p1 to take over
	ds1.waitForDeliveryServiceActivation()
	t.Log("p1 started its delivery service")
	// Ensure it's a leader now
	assert.Equal(t, 1, getLeader())
	p0.chains[channelName].Stop()
	p1.chains[channelName].Stop()
	p0.deliveryService.Stop()
	p1.deliveryService.Stop()
}
<|endoftext|>