<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 gce

import (
	"fmt"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	computealpha "google.golang.org/api/compute/v0.alpha"
	compute "google.golang.org/api/compute/v1"

	"k8s.io/api/core/v1"
	"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud"
	kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestEnsureStaticIP(t *testing.T) {
	fcas := NewFakeCloudAddressService()
	ipName := "some-static-ip"
	serviceName := ""
	region := "us-central1"

	// First ensure call
	ip, existed, err := ensureStaticIP(fcas, ipName, serviceName, region, "", NetworkTierDefault)
	if err != nil || existed || ip == "" {
		t.Fatalf(`ensureStaticIP(%v, %v, %v, %v, "") = %v, %v, %v; want valid ip, false, nil`, fcas, ipName, serviceName, region, ip, existed, err)
	}

	// Second ensure call
	var ipPrime string
	ipPrime, existed, err = ensureStaticIP(fcas, ipName, serviceName, region, ip, NetworkTierDefault)
	if err != nil || !existed || ip != ipPrime {
		t.Fatalf(`ensureStaticIP(%v, %v, %v, %v, %v) = %v, %v, %v; want %v, true, nil`, fcas, ipName, serviceName, region, ip, ipPrime, existed, err, ip)
	}
}

func TestEnsureStaticIPWithTier(t *testing.T) {
	s := NewFakeCloudAddressService()
	serviceName := ""
	region := "us-east1"

	for desc, tc := range map[string]struct {
		name     string
		netTier  NetworkTier
		expected string
	}{
		"Premium (default)": {
			name:     "foo-1",
			netTier:  NetworkTierPremium,
			expected: "PREMIUM",
		},
		"Standard": {
			name:     "foo-2",
			netTier:  NetworkTierStandard,
			expected: "STANDARD",
		},
	} {
		t.Run(desc, func(t *testing.T) {
			ip, existed, err := ensureStaticIP(s, tc.name, serviceName, region, "", tc.netTier)
			assert.NoError(t, err)
			assert.False(t, existed)
			assert.NotEqual(t, "", ip)
			// Get the Address from the fake address service and verify that the tier
			// is set correctly.
			alphaAddr, err := s.GetAlphaRegionAddress(tc.name, region)
			require.NoError(t, err)
			assert.Equal(t, tc.expected, alphaAddr.NetworkTier)
		})
	}
}

func TestVerifyRequestedIP(t *testing.T) {
	region := "test-region"
	lbRef := "test-lb"
	s := NewFakeCloudAddressService()

	for desc, tc := range map[string]struct {
		requestedIP     string
		fwdRuleIP       string
		netTier         NetworkTier
		addrList        []*computealpha.Address
		expectErr       bool
		expectUserOwned bool
	}{
		"requested IP exists": {
			requestedIP:     "1.1.1.1",
			netTier:         NetworkTierPremium,
			addrList:        []*computealpha.Address{{Name: "foo", Address: "1.1.1.1", NetworkTier: "PREMIUM"}},
			expectErr:       false,
			expectUserOwned: true,
		},
		"requested IP is not static, but is in use by the fwd rule": {
			requestedIP: "1.1.1.1",
			fwdRuleIP:   "1.1.1.1",
			netTier:     NetworkTierPremium,
			expectErr:   false,
		},
		"requested IP is not static and is not used by the fwd rule": {
			requestedIP: "1.1.1.1",
			fwdRuleIP:   "2.2.2.2",
			netTier:     NetworkTierPremium,
			expectErr:   true,
		},
		"no requested IP": {
			netTier:   NetworkTierPremium,
			expectErr: false,
		},
		"requested IP exists, but network tier does not match": {
			requestedIP: "1.1.1.1",
			netTier:     NetworkTierStandard,
			addrList:    []*computealpha.Address{{Name: "foo", Address: "1.1.1.1", NetworkTier: "PREMIUM"}},
			expectErr:   true,
		},
	} {
		t.Run(desc, func(t *testing.T) {
			s.SetRegionalAddresses(region, tc.addrList)
			isUserOwnedIP, err := verifyUserRequestedIP(s, region, tc.requestedIP, tc.fwdRuleIP, lbRef, tc.netTier)
			assert.Equal(t, tc.expectErr, err != nil, fmt.Sprintf("err: %v", err))
			assert.Equal(t, tc.expectUserOwned, isUserOwnedIP)
		})
	}
}

func TestCreateForwardingRuleWithTier(t *testing.T) {
	s := NewFakeCloudForwardingRuleService()
	// Common variables among the tests.
	ports := []v1.ServicePort{{Name: "foo", Protocol: v1.ProtocolTCP, Port: int32(123)}}
	region := "test-region"
	target := "test-target-pool"
	svcName := "foo-svc"

	for desc, tc := range map[string]struct {
		netTier      NetworkTier
		expectedRule *computealpha.ForwardingRule
	}{
		"Premium tier": {
			netTier: NetworkTierPremium,
			expectedRule: &computealpha.ForwardingRule{
				Name:        "lb-1",
				Description: `{"kubernetes.io/service-name":"foo-svc"}`,
				IPAddress:   "1.1.1.1",
				IPProtocol:  "TCP",
				PortRange:   "123-123",
				Target:      target,
				NetworkTier: "PREMIUM",
			},
		},
		"Standard tier": {
			netTier: NetworkTierStandard,
			expectedRule: &computealpha.ForwardingRule{
				Name:        "lb-2",
				Description: `{"kubernetes.io/service-name":"foo-svc"}`,
				IPAddress:   "2.2.2.2",
				IPProtocol:  "TCP",
				PortRange:   "123-123",
				Target:      target,
				NetworkTier: "STANDARD",
			},
		},
	} {
		t.Run(desc, func(t *testing.T) {
			lbName := tc.expectedRule.Name
			ipAddr := tc.expectedRule.IPAddress

			err := createForwardingRule(s, lbName, svcName, region, ipAddr, target, ports, tc.netTier)
			assert.NoError(t, err)

			alphaRule, err := s.GetAlphaRegionForwardingRule(lbName, region)
			assert.NoError(t, err)
			assert.Equal(t, tc.expectedRule, alphaRule)
		})
	}
}

func TestDeleteAddressWithWrongTier(t *testing.T) {
	region := "test-region"
	lbRef := "test-lb"
	s := NewFakeCloudAddressService()

	for desc, tc := range map[string]struct {
		addrName     string
		netTier      NetworkTier
		addrList     []*computealpha.Address
		expectDelete bool
	}{
		"Network tiers (premium) match; do nothing": {
			addrName: "foo1",
			netTier:  NetworkTierPremium,
			addrList: []*computealpha.Address{{Name: "foo1", Address: "1.1.1.1", NetworkTier: "PREMIUM"}},
		},
		"Network tiers (standard) match; do nothing": {
			addrName: "foo2",
			netTier:  NetworkTierStandard,
			addrList: []*computealpha.Address{{Name: "foo2", Address: "1.1.1.2", NetworkTier: "STANDARD"}},
		},
		"Wrong network tier (standard); delete address": {
			addrName:     "foo3",
			netTier:      NetworkTierPremium,
			addrList:     []*computealpha.Address{{Name: "foo3", Address: "1.1.1.3", NetworkTier: "STANDARD"}},
			expectDelete: true,
		},
		"Wrong network tier (preimium); delete address": {
			addrName:     "foo4",
			netTier:      NetworkTierStandard,
			addrList:     []*computealpha.Address{{Name: "foo4", Address: "1.1.1.4", NetworkTier: "PREMIUM"}},
			expectDelete: true,
		},
	} {
		t.Run(desc, func(t *testing.T) {
			s.SetRegionalAddresses(region, tc.addrList)
			// Sanity check to ensure we inject the right address.
			_, err := s.GetRegionAddress(tc.addrName, region)
			require.NoError(t, err)

			err = deleteAddressWithWrongTier(s, region, tc.addrName, lbRef, tc.netTier)
			assert.NoError(t, err)
			// Check whether the address still exists.
			_, err = s.GetRegionAddress(tc.addrName, region)
			if tc.expectDelete {
				assert.True(t, isNotFound(err))
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

func TestEnsureExternalLoadBalancer(t *testing.T) {
	gceProjectId := "test-project"
	gceRegion := "test-region"
	zone := "zone1"
	nodeName := "test-node-1"

	client, clientErr := newOauthClient(nil)
	require.NoError(t, clientErr)

	service, serviceErr := compute.New(client)
	require.NoError(t, serviceErr)

	fakeManager := newFakeManager(gceProjectId, gceRegion)

	alphaFeatureGate, featureGateErr := NewAlphaFeatureGate([]string{})
	require.NoError(t, featureGateErr)

	cloud := cloud.NewMockGCE()
	zonesWithNodes := createNodeZones([]string{zone})

	gce := GCECloud{
		service:						service,
		manager:            fakeManager,
		managedZones:       []string{zone},
		projectID:          gceProjectId,
		AlphaFeatureGate:   alphaFeatureGate,
		nodeZones:          zonesWithNodes,
		nodeInformerSynced: func() bool { return true },
		c:									cloud,
	}

	gce.InsertInstance(
		gceProjectId,
		zone,
		&compute.Instance{
			Name: nodeName,
			Tags: &compute.Tags{
				Items: []string{nodeName},
			},
		},
	)

	clusterName := "Test Cluster Name"
	clusterID := "test-cluster-id"

	apiService := &v1.Service{
		Spec: v1.ServiceSpec{
			SessionAffinity: v1.ServiceAffinityClientIP,
			Type: v1.ServiceTypeClusterIP,
			Ports: []v1.ServicePort{{Protocol: v1.ProtocolTCP, Port: int32(123)}},
		},
	}

	existingFwdRule := &compute.ForwardingRule{}

	nodes := []*v1.Node{
		&v1.Node{
			ObjectMeta: metav1.ObjectMeta{
				Name: nodeName,
				Labels: map[string]string{
					kubeletapis.LabelHostname: nodeName,
				},
			},
			Status: v1.NodeStatus{
				NodeInfo: v1.NodeSystemInfo{
					KubeProxyVersion: "v1.7.2",
				},
			},
		},
	}

	status, err := gce.ensureExternalLoadBalancer(
		clusterName,
		clusterID,
		apiService,
		existingFwdRule,
		nodes,
	)

	assert.NotNil(t, status)
	assert.NoError(t, err)
}
<commit_msg>Move shared load balancer variables out of test.lb update/delete tests<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 gce

import (
	"fmt"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	computealpha "google.golang.org/api/compute/v0.alpha"
	compute "google.golang.org/api/compute/v1"

	"k8s.io/api/core/v1"
	"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud"
	kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestEnsureStaticIP(t *testing.T) {
	fcas := NewFakeCloudAddressService()
	ipName := "some-static-ip"
	serviceName := ""
	region := "us-central1"

	// First ensure call
	ip, existed, err := ensureStaticIP(fcas, ipName, serviceName, region, "", NetworkTierDefault)
	if err != nil || existed || ip == "" {
		t.Fatalf(`ensureStaticIP(%v, %v, %v, %v, "") = %v, %v, %v; want valid ip, false, nil`, fcas, ipName, serviceName, region, ip, existed, err)
	}

	// Second ensure call
	var ipPrime string
	ipPrime, existed, err = ensureStaticIP(fcas, ipName, serviceName, region, ip, NetworkTierDefault)
	if err != nil || !existed || ip != ipPrime {
		t.Fatalf(`ensureStaticIP(%v, %v, %v, %v, %v) = %v, %v, %v; want %v, true, nil`, fcas, ipName, serviceName, region, ip, ipPrime, existed, err, ip)
	}
}

func TestEnsureStaticIPWithTier(t *testing.T) {
	s := NewFakeCloudAddressService()
	serviceName := ""
	region := "us-east1"

	for desc, tc := range map[string]struct {
		name     string
		netTier  NetworkTier
		expected string
	}{
		"Premium (default)": {
			name:     "foo-1",
			netTier:  NetworkTierPremium,
			expected: "PREMIUM",
		},
		"Standard": {
			name:     "foo-2",
			netTier:  NetworkTierStandard,
			expected: "STANDARD",
		},
	} {
		t.Run(desc, func(t *testing.T) {
			ip, existed, err := ensureStaticIP(s, tc.name, serviceName, region, "", tc.netTier)
			assert.NoError(t, err)
			assert.False(t, existed)
			assert.NotEqual(t, "", ip)
			// Get the Address from the fake address service and verify that the tier
			// is set correctly.
			alphaAddr, err := s.GetAlphaRegionAddress(tc.name, region)
			require.NoError(t, err)
			assert.Equal(t, tc.expected, alphaAddr.NetworkTier)
		})
	}
}

func TestVerifyRequestedIP(t *testing.T) {
	region := "test-region"
	lbRef := "test-lb"
	s := NewFakeCloudAddressService()

	for desc, tc := range map[string]struct {
		requestedIP     string
		fwdRuleIP       string
		netTier         NetworkTier
		addrList        []*computealpha.Address
		expectErr       bool
		expectUserOwned bool
	}{
		"requested IP exists": {
			requestedIP:     "1.1.1.1",
			netTier:         NetworkTierPremium,
			addrList:        []*computealpha.Address{{Name: "foo", Address: "1.1.1.1", NetworkTier: "PREMIUM"}},
			expectErr:       false,
			expectUserOwned: true,
		},
		"requested IP is not static, but is in use by the fwd rule": {
			requestedIP: "1.1.1.1",
			fwdRuleIP:   "1.1.1.1",
			netTier:     NetworkTierPremium,
			expectErr:   false,
		},
		"requested IP is not static and is not used by the fwd rule": {
			requestedIP: "1.1.1.1",
			fwdRuleIP:   "2.2.2.2",
			netTier:     NetworkTierPremium,
			expectErr:   true,
		},
		"no requested IP": {
			netTier:   NetworkTierPremium,
			expectErr: false,
		},
		"requested IP exists, but network tier does not match": {
			requestedIP: "1.1.1.1",
			netTier:     NetworkTierStandard,
			addrList:    []*computealpha.Address{{Name: "foo", Address: "1.1.1.1", NetworkTier: "PREMIUM"}},
			expectErr:   true,
		},
	} {
		t.Run(desc, func(t *testing.T) {
			s.SetRegionalAddresses(region, tc.addrList)
			isUserOwnedIP, err := verifyUserRequestedIP(s, region, tc.requestedIP, tc.fwdRuleIP, lbRef, tc.netTier)
			assert.Equal(t, tc.expectErr, err != nil, fmt.Sprintf("err: %v", err))
			assert.Equal(t, tc.expectUserOwned, isUserOwnedIP)
		})
	}
}

func TestCreateForwardingRuleWithTier(t *testing.T) {
	s := NewFakeCloudForwardingRuleService()
	// Common variables among the tests.
	ports := []v1.ServicePort{{Name: "foo", Protocol: v1.ProtocolTCP, Port: int32(123)}}
	region := "test-region"
	target := "test-target-pool"
	svcName := "foo-svc"

	for desc, tc := range map[string]struct {
		netTier      NetworkTier
		expectedRule *computealpha.ForwardingRule
	}{
		"Premium tier": {
			netTier: NetworkTierPremium,
			expectedRule: &computealpha.ForwardingRule{
				Name:        "lb-1",
				Description: `{"kubernetes.io/service-name":"foo-svc"}`,
				IPAddress:   "1.1.1.1",
				IPProtocol:  "TCP",
				PortRange:   "123-123",
				Target:      target,
				NetworkTier: "PREMIUM",
			},
		},
		"Standard tier": {
			netTier: NetworkTierStandard,
			expectedRule: &computealpha.ForwardingRule{
				Name:        "lb-2",
				Description: `{"kubernetes.io/service-name":"foo-svc"}`,
				IPAddress:   "2.2.2.2",
				IPProtocol:  "TCP",
				PortRange:   "123-123",
				Target:      target,
				NetworkTier: "STANDARD",
			},
		},
	} {
		t.Run(desc, func(t *testing.T) {
			lbName := tc.expectedRule.Name
			ipAddr := tc.expectedRule.IPAddress

			err := createForwardingRule(s, lbName, svcName, region, ipAddr, target, ports, tc.netTier)
			assert.NoError(t, err)

			alphaRule, err := s.GetAlphaRegionForwardingRule(lbName, region)
			assert.NoError(t, err)
			assert.Equal(t, tc.expectedRule, alphaRule)
		})
	}
}

func TestDeleteAddressWithWrongTier(t *testing.T) {
	region := "test-region"
	lbRef := "test-lb"
	s := NewFakeCloudAddressService()

	for desc, tc := range map[string]struct {
		addrName     string
		netTier      NetworkTier
		addrList     []*computealpha.Address
		expectDelete bool
	}{
		"Network tiers (premium) match; do nothing": {
			addrName: "foo1",
			netTier:  NetworkTierPremium,
			addrList: []*computealpha.Address{{Name: "foo1", Address: "1.1.1.1", NetworkTier: "PREMIUM"}},
		},
		"Network tiers (standard) match; do nothing": {
			addrName: "foo2",
			netTier:  NetworkTierStandard,
			addrList: []*computealpha.Address{{Name: "foo2", Address: "1.1.1.2", NetworkTier: "STANDARD"}},
		},
		"Wrong network tier (standard); delete address": {
			addrName:     "foo3",
			netTier:      NetworkTierPremium,
			addrList:     []*computealpha.Address{{Name: "foo3", Address: "1.1.1.3", NetworkTier: "STANDARD"}},
			expectDelete: true,
		},
		"Wrong network tier (preimium); delete address": {
			addrName:     "foo4",
			netTier:      NetworkTierStandard,
			addrList:     []*computealpha.Address{{Name: "foo4", Address: "1.1.1.4", NetworkTier: "PREMIUM"}},
			expectDelete: true,
		},
	} {
		t.Run(desc, func(t *testing.T) {
			s.SetRegionalAddresses(region, tc.addrList)
			// Sanity check to ensure we inject the right address.
			_, err := s.GetRegionAddress(tc.addrName, region)
			require.NoError(t, err)

			err = deleteAddressWithWrongTier(s, region, tc.addrName, lbRef, tc.netTier)
			assert.NoError(t, err)
			// Check whether the address still exists.
			_, err = s.GetRegionAddress(tc.addrName, region)
			if tc.expectDelete {
				assert.True(t, isNotFound(err))
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

const nodeName = "test-node-1"
const clusterName = "Test Cluster Name"
const clusterID = "test-cluster-id"

var apiService = &v1.Service{
	Spec: v1.ServiceSpec{
		SessionAffinity: v1.ServiceAffinityClientIP,
		Type: v1.ServiceTypeClusterIP,
		Ports: []v1.ServicePort{{Protocol: v1.ProtocolTCP, Port: int32(123)}},
	},
}

var nodes = []*v1.Node{
	&v1.Node{
		ObjectMeta: metav1.ObjectMeta{
			Name: nodeName,
			Labels: map[string]string{
				kubeletapis.LabelHostname: nodeName,
			},
		},
		Status: v1.NodeStatus{
			NodeInfo: v1.NodeSystemInfo{
				KubeProxyVersion: "v1.7.2",
			},
		},
	},
}

func fakeGCECloud() (*GCECloud, error) {
	gceProjectId := "test-project"
	gceRegion := "test-region"
	zone := "zone1"

	client, err := newOauthClient(nil)
	if err != nil {
		return nil, err
	}

	service, err := compute.New(client)
	if err != nil {
		return nil, err
	}

	fakeManager := newFakeManager(gceProjectId, gceRegion)

	alphaFeatureGate, err := NewAlphaFeatureGate([]string{})
	if err != nil {
		return nil, err
	}

	cloud := cloud.NewMockGCE()
	zonesWithNodes := createNodeZones([]string{zone})

	gce := GCECloud{
		region:							gceRegion,
		service:						service,
		manager:            fakeManager,
		managedZones:       []string{zone},
		projectID:          gceProjectId,
		AlphaFeatureGate:   alphaFeatureGate,
		nodeZones:          zonesWithNodes,
		nodeInformerSynced: func() bool { return true },
		c:									cloud,
	}

	gce.InsertInstance(
		gceProjectId,
		zone,
		&compute.Instance{
			Name: nodeName,
			Tags: &compute.Tags{
				Items: []string{nodeName},
			},
		},
	)

	return &gce, nil
}

func createExternalLoadBalancer(gce *GCECloud) (*v1.LoadBalancerStatus, error) {
	existingFwdRule := &compute.ForwardingRule{}

	status, err := gce.ensureExternalLoadBalancer(
		clusterName,
		clusterID,
		apiService,
		existingFwdRule,
		nodes,
	)

	return status, err
}

func TestEnsureExternalLoadBalancer(t *testing.T) {
	gce, err := fakeGCECloud()
	assert.NoError(t, err)

	status, err := createExternalLoadBalancer(gce)
	assert.NotNil(t, status)
	assert.NoError(t, err)
}

func TestUpdateExternalLoadBalancer(t *testing.T) {
	gce, err := fakeGCECloud()
	require.NoError(t, err)
	createExternalLoadBalancer(gce)

	err = gce.updateExternalLoadBalancer(clusterName, apiService, nodes)
	assert.NoError(t, err)

	// Assert pool is modified
}

func TestEnsureExternalLoadBalancerDeleted(t *testing.T) {
	gce, err := fakeGCECloud()
	require.NoError(t, err)
	createExternalLoadBalancer(gce)

	err = gce.ensureExternalLoadBalancerDeleted(clusterName, clusterID, apiService)
	assert.NoError(t, err)
}
<|endoftext|>


<commit_before>package group

import (
	"net/http"
	"testing"
	"time"

	"github.com/messagebird/go-rest-api/internal/mbtest"
)

func TestMain(m *testing.M) {
	mbtest.EnableServer(m)
}

func TestCreate(t *testing.T) {
	mbtest.WillReturnTestdata(t, "groupObject.json", http.StatusCreated)
	client := mbtest.Client(t)

	group, err := Create(client, &Request{"Friends"})
	if err != nil {
		t.Fatalf("unexpected error creating Group: %s", err)
	}

	mbtest.AssertEndpointCalled(t, http.MethodPost, "/groups")
	mbtest.AssertTestdata(t, "groupRequestCreateObject.json", mbtest.Request.Body)

	if group.Name != "Friends" {
		t.Fatalf("expected Friends, got %s", group.Name)
	}
}

func TestCreateWithEmptyName(t *testing.T) {
	client := mbtest.Client(t)

	if _, err := Create(client, &Request{""}); err == nil {
		t.Fatalf("expected error, got nil")
	}
}

func TestDelete(t *testing.T) {
	mbtest.WillReturn([]byte(""), http.StatusNoContent)
	client := mbtest.Client(t)

	if err := Delete(client, "group-id"); err != nil {
		t.Fatalf("unexpected error deleting Group: %s", err)
	}

	mbtest.AssertEndpointCalled(t, http.MethodDelete, "/groups/group-id")
}

func TestList(t *testing.T) {
	mbtest.WillReturnTestdata(t, "groupListObject.json", http.StatusOK)
	client := mbtest.Client(t)

	list, err := List(client, DefaultListOptions)
	if err != nil {
		t.Fatalf("unexpected error retrieving Contact list: %s", err)
	}

	if list.Offset != 0 {
		t.Fatalf("expected 0, got %d", list.Offset)
	}

	if list.Limit != 10 {
		t.Fatalf("expected 10, got %d", list.Limit)
	}

	if list.Count != 2 {
		t.Fatalf("expected 2, got %d", list.Count)
	}

	if list.TotalCount != 2 {
		t.Fatalf("expected 2, got %d", list.TotalCount)
	}

	if actualCount := len(list.Items); actualCount != 2 {
		t.Fatalf("expected 2, got %d", actualCount)
	}

	if list.Items[0].ID != "first-id" {
		t.Fatalf("expected first-id, got %s", list.Items[0].ID)
	}

	if list.Items[1].ID != "second-id" {
		t.Fatalf("expected second-id, got %s", list.Items[1].ID)
	}

	mbtest.AssertEndpointCalled(t, http.MethodGet, "/groups")
}

func TestListPagination(t *testing.T) {
	client := mbtest.Client(t)

	tt := []struct {
		expected string
		options  *ListOptions
	}{
		{"limit=10&offset=0", DefaultListOptions},
		{"limit=10&offset=25", &ListOptions{10, 25}},
		{"limit=50&offset=10", &ListOptions{50, 10}},
	}

	for _, tc := range tt {
		List(client, tc.options)

		if query := mbtest.Request.URL.RawQuery; query != tc.expected {
			t.Fatalf("expected %s, got %s", tc.expected, query)
		}
	}
}

func TestRead(t *testing.T) {
	mbtest.WillReturnTestdata(t, "groupObject.json", http.StatusOK)
	client := mbtest.Client(t)

	group, err := Read(client, "group-id")
	if err != nil {
		t.Fatalf("unexpected error reading Group: %s", err)
	}

	mbtest.AssertEndpointCalled(t, http.MethodGet, "/groups/group-id")

	if group.ID != "group-id" {
		t.Fatalf("expected group-id, got %s", group.ID)
	}

	if group.HRef != "https://rest.messagebird.com/groups/group-id" {
		t.Fatalf("expected https://rest.messagebird.com/groups/group-id, got %s", group.HRef)
	}

	if group.Name != "Friends" {
		t.Fatalf("expected Friends, got %s", group.Name)
	}

	if group.Contacts.TotalCount != 3 {
		t.Fatalf("expected 3, got %d", group.Contacts.TotalCount)
	}

	if group.Contacts.HRef != "https://rest.messagebird.com/groups/group-id" {
		t.Fatalf("expected https://rest.messagebird.com/groups/group-id, got %s", group.Contacts.HRef)
	}

	if created, _ := time.Parse(time.RFC3339, "2018-07-25T12:16:10+00:00"); !created.Equal(group.CreatedDatetime) {
		t.Fatalf("expected 2018-07-25T12:16:10+00:00, got %s", group.CreatedDatetime)
	}

	if updated, _ := time.Parse(time.RFC3339, "2018-07-25T12:16:23+00:00"); !updated.Equal(group.UpdatedDatetime) {
		t.Fatalf("expected 2018-07-25T12:16:23+00:00, got %s", group.UpdatedDatetime)
	}
}

func TestUpdate(t *testing.T) {
	mbtest.WillReturn([]byte(""), http.StatusNoContent)
	client := mbtest.Client(t)

	if err := Update(client, "group-id", &Request{"Family"}); err != nil {
		t.Fatalf("unexpected error updating Group: %s", err)
	}

	mbtest.AssertEndpointCalled(t, http.MethodPatch, "/groups/group-id")
	mbtest.AssertTestdata(t, "groupRequestUpdateObject.json", mbtest.Request.Body)
}

func TestAddContacts(t *testing.T) {
	mbtest.WillReturn([]byte(""), http.StatusNoContent)
	client := mbtest.Client(t)

	if err := AddContacts(client, "group-id", []string{"first-contact-id", "second-contact-id"}); err != nil {
		t.Fatalf("unexpected error removing Contacts from Group: %s", err)
	}

	mbtest.AssertEndpointCalled(t, http.MethodGet, "/groups/group-id/contacts")

	if mbtest.Request.URL.RawQuery != "_method=PUT&ids[]=first-contact-id&ids[]=second-contact-id" {
		t.Fatalf("expected _method=PUT&ids[]=first-contact-id&ids[]=second-contact-id, got %s", mbtest.Request.URL.RawQuery)
	}
}

func TestAddContactsWithEmptyContacts(t *testing.T) {
	client := mbtest.Client(t)

	tt := []struct {
		contactIDs []string
	}{
		{[]string{}},
		{nil},
	}

	for _, tc := range tt {
		if err := AddContacts(client, "group-id", tc.contactIDs); err == nil {
			t.Fatalf("expected error, got nil")
		}
	}
}

func TestAddContactsWithTooManyContacts(t *testing.T) {
	client := mbtest.Client(t)

	// Only 50 contacts are allowed at a time.
	contactIDs := make([]string, 51)

	if err := AddContacts(client, "group-id", contactIDs); err == nil {
		t.Fatalf("expected error, got nil")
	}
}

func TestListContacts(t *testing.T) {
	mbtest.WillReturnTestdata(t, "groupContactListObject.json", http.StatusOK)
	client := mbtest.Client(t)

	list, err := ListContacts(client, "group-id", DefaultListOptions)
	if err != nil {
		t.Fatalf("unexpected error listing Contacts: %s", err)
	}

	if list.Offset != 0 {
		t.Fatalf("expected 0, got %d", list.Offset)
	}

	if list.Limit != 20 {
		t.Fatalf("expected 20, got %d", list.Limit)
	}

	if list.Count != 3 {
		t.Fatalf("expected 3, got %d", list.Count)
	}

	if list.TotalCount != 3 {
		t.Fatalf("expected 3, got %d", list.TotalCount)
	}

	if list.Items[0].ID != "first-contact-id" {
		t.Fatalf("expected first-contact-id, got %s", list.Items[0].ID)
	}

	if list.Items[1].ID != "second-contact-id" {
		t.Fatalf("expected second-contact-id, got %s", list.Items[1].ID)
	}

	if list.Items[2].ID != "third-contact-id" {
		t.Fatalf("expected third-contact-id, got %s", list.Items[2].ID)
	}

	mbtest.AssertEndpointCalled(t, http.MethodGet, "/groups/group-id/contacts")
}

func TestRemoveContact(t *testing.T) {
	mbtest.WillReturn([]byte(""), http.StatusNoContent)
	client := mbtest.Client(t)

	if err := RemoveContact(client, "group-id", "contact-id"); err != nil {
		t.Fatalf("unexpected error deleting Group: %s", err)
	}

	mbtest.AssertEndpointCalled(t, http.MethodDelete, "/groups/group-id/contacts/contact-id")
}
<commit_msg>Change order of assertions (expected, actual -> actual, expected): https://github.com/golang/go/wiki/CodeReviewComments#useful-test-failures<commit_after>package group

import (
	"net/http"
	"testing"
	"time"

	"github.com/messagebird/go-rest-api/internal/mbtest"
)

func TestMain(m *testing.M) {
	mbtest.EnableServer(m)
}

func TestCreate(t *testing.T) {
	mbtest.WillReturnTestdata(t, "groupObject.json", http.StatusCreated)
	client := mbtest.Client(t)

	group, err := Create(client, &Request{"Friends"})
	if err != nil {
		t.Fatalf("unexpected error creating Group: %s", err)
	}

	mbtest.AssertEndpointCalled(t, http.MethodPost, "/groups")
	mbtest.AssertTestdata(t, "groupRequestCreateObject.json", mbtest.Request.Body)

	if group.Name != "Friends" {
		t.Fatalf("got %s, expected Friends", group.Name)
	}
}

func TestCreateWithEmptyName(t *testing.T) {
	client := mbtest.Client(t)

	if _, err := Create(client, &Request{""}); err == nil {
		t.Fatalf("got nil, expected error")
	}
}

func TestDelete(t *testing.T) {
	mbtest.WillReturn([]byte(""), http.StatusNoContent)
	client := mbtest.Client(t)

	if err := Delete(client, "group-id"); err != nil {
		t.Fatalf("unexpected error deleting Group: %s", err)
	}

	mbtest.AssertEndpointCalled(t, http.MethodDelete, "/groups/group-id")
}

func TestList(t *testing.T) {
	mbtest.WillReturnTestdata(t, "groupListObject.json", http.StatusOK)
	client := mbtest.Client(t)

	list, err := List(client, DefaultListOptions)
	if err != nil {
		t.Fatalf("unexpected error retrieving Contact list: %s", err)
	}

	if list.Offset != 0 {
		t.Fatalf("got %d, expected 0", list.Offset)
	}

	if list.Limit != 10 {
		t.Fatalf("got %d, expected 10", list.Limit)
	}

	if list.Count != 2 {
		t.Fatalf("got %d, expected 2", list.Count)
	}

	if list.TotalCount != 2 {
		t.Fatalf("got %d, expected 2", list.TotalCount)
	}

	if actualCount := len(list.Items); actualCount != 2 {
		t.Fatalf("got %d, expected 2", actualCount)
	}

	if list.Items[0].ID != "first-id" {
		t.Fatalf("got %s, expected first-id", list.Items[0].ID)
	}

	if list.Items[1].ID != "second-id" {
		t.Fatalf("got %s, expected second-id", list.Items[1].ID)
	}

	mbtest.AssertEndpointCalled(t, http.MethodGet, "/groups")
}

func TestListPagination(t *testing.T) {
	client := mbtest.Client(t)

	tt := []struct {
		expected string
		options  *ListOptions
	}{
		{"limit=10&offset=0", DefaultListOptions},
		{"limit=10&offset=25", &ListOptions{10, 25}},
		{"limit=50&offset=10", &ListOptions{50, 10}},
	}

	for _, tc := range tt {
		List(client, tc.options)

		if query := mbtest.Request.URL.RawQuery; query != tc.expected {
			t.Fatalf("got %s, expected %s", tc.expected, query)
		}
	}
}

func TestRead(t *testing.T) {
	mbtest.WillReturnTestdata(t, "groupObject.json", http.StatusOK)
	client := mbtest.Client(t)

	group, err := Read(client, "group-id")
	if err != nil {
		t.Fatalf("unexpected error reading Group: %s", err)
	}

	mbtest.AssertEndpointCalled(t, http.MethodGet, "/groups/group-id")

	if group.ID != "group-id" {
		t.Fatalf("got %s, expected group-id", group.ID)
	}

	if group.HRef != "https://rest.messagebird.com/groups/group-id" {
		t.Fatalf("got %s, expected https://rest.messagebird.com/groups/group-id", group.HRef)
	}

	if group.Name != "Friends" {
		t.Fatalf("got %s, expected Friends", group.Name)
	}

	if group.Contacts.TotalCount != 3 {
		t.Fatalf("got %d, expected 3", group.Contacts.TotalCount)
	}

	if group.Contacts.HRef != "https://rest.messagebird.com/groups/group-id" {
		t.Fatalf("got %s, expected https://rest.messagebird.com/groups/group-id", group.Contacts.HRef)
	}

	if created, _ := time.Parse(time.RFC3339, "2018-07-25T12:16:10+00:00"); !created.Equal(group.CreatedDatetime) {
		t.Fatalf("got %s, expected 2018-07-25T12:16:10+00:00", group.CreatedDatetime)
	}

	if updated, _ := time.Parse(time.RFC3339, "2018-07-25T12:16:23+00:00"); !updated.Equal(group.UpdatedDatetime) {
		t.Fatalf("got %s, expected 2018-07-25T12:16:23+00:00", group.UpdatedDatetime)
	}
}

func TestUpdate(t *testing.T) {
	mbtest.WillReturn([]byte(""), http.StatusNoContent)
	client := mbtest.Client(t)

	if err := Update(client, "group-id", &Request{"Family"}); err != nil {
		t.Fatalf("unexpected error updating Group: %s", err)
	}

	mbtest.AssertEndpointCalled(t, http.MethodPatch, "/groups/group-id")
	mbtest.AssertTestdata(t, "groupRequestUpdateObject.json", mbtest.Request.Body)
}

func TestAddContacts(t *testing.T) {
	mbtest.WillReturn([]byte(""), http.StatusNoContent)
	client := mbtest.Client(t)

	if err := AddContacts(client, "group-id", []string{"first-contact-id", "second-contact-id"}); err != nil {
		t.Fatalf("unexpected error removing Contacts from Group: %s", err)
	}

	mbtest.AssertEndpointCalled(t, http.MethodGet, "/groups/group-id/contacts")

	if mbtest.Request.URL.RawQuery != "_method=PUT&ids[]=first-contact-id&ids[]=second-contact-id" {
		t.Fatalf("got %s, expected _method=PUT&ids[]=first-contact-id&ids[]=second-contact-id", mbtest.Request.URL.RawQuery)
	}
}

func TestAddContactsWithEmptyContacts(t *testing.T) {
	client := mbtest.Client(t)

	tt := []struct {
		contactIDs []string
	}{
		{[]string{}},
		{nil},
	}

	for _, tc := range tt {
		if err := AddContacts(client, "group-id", tc.contactIDs); err == nil {
			t.Fatalf("got nil, expected error")
		}
	}
}

func TestAddContactsWithTooManyContacts(t *testing.T) {
	client := mbtest.Client(t)

	// Only 50 contacts are allowed at a time.
	contactIDs := make([]string, 51)

	if err := AddContacts(client, "group-id", contactIDs); err == nil {
		t.Fatalf("got nil, expected error")
	}
}

func TestListContacts(t *testing.T) {
	mbtest.WillReturnTestdata(t, "groupContactListObject.json", http.StatusOK)
	client := mbtest.Client(t)

	list, err := ListContacts(client, "group-id", DefaultListOptions)
	if err != nil {
		t.Fatalf("unexpected error listing Contacts: %s", err)
	}

	if list.Offset != 0 {
		t.Fatalf("got %d, expected 0", list.Offset)
	}

	if list.Limit != 20 {
		t.Fatalf("got %d, expected 20", list.Limit)
	}

	if list.Count != 3 {
		t.Fatalf("got %d, expected 3", list.Count)
	}

	if list.TotalCount != 3 {
		t.Fatalf("got %d, expected 3", list.TotalCount)
	}

	if list.Items[0].ID != "first-contact-id" {
		t.Fatalf("got %s, expected first-contact-id", list.Items[0].ID)
	}

	if list.Items[1].ID != "second-contact-id" {
		t.Fatalf("got %s, expected second-contact-id", list.Items[1].ID)
	}

	if list.Items[2].ID != "third-contact-id" {
		t.Fatalf("got %s, expected third-contact-id", list.Items[2].ID)
	}

	mbtest.AssertEndpointCalled(t, http.MethodGet, "/groups/group-id/contacts")
}

func TestRemoveContact(t *testing.T) {
	mbtest.WillReturn([]byte(""), http.StatusNoContent)
	client := mbtest.Client(t)

	if err := RemoveContact(client, "group-id", "contact-id"); err != nil {
		t.Fatalf("unexpected error deleting Group: %s", err)
	}

	mbtest.AssertEndpointCalled(t, http.MethodDelete, "/groups/group-id/contacts/contact-id")
}
<|endoftext|>


<commit_before>package cfutil

import (
	"errors"
	"fmt"
	consul "github.com/hashicorp/consul/api"
	"net/url"
)

func ServiceRegister(name string, tags ...string) error {
	appEnv, _ := Current()
	client, err := NewConsulClient()
	if err != nil {
		return err
	}
	err = client.Agent().ServiceRegister(&consul.AgentServiceRegistration{
		Name:    name,
		Address: appEnv.ApplicationURIs[0],
		Check: &consul.AgentServiceCheck{
			HTTP:     fmt.Sprintf(schemaForServices() + "://" + appEnv.ApplicationURIs[0] + "/health"),
			Interval: "30s",
		},
	})
	if err != nil {
		return err
	}
	return nil
}

func NewConsulClient() (*consul.Client, error) {
	dialString, err := consulDialstring("consul")
	if err != nil {
		return nil, err
	}
	client, consulErr := consul.NewClient(&consul.Config{
		Address: dialString,
		Scheme:  schemaForConsul(),
	})
	if consulErr != nil {
		return nil, consulErr
	}
	return client, nil
}

func consulDialstring(serviceName string) (string, error) {
	appEnv, _ := Current()
	consulService, err := appEnv.Services.WithName(serviceName)
	if err != nil {
		return "", err
	}
	port := "8500"

	uri, ok := consulService.Credentials["uri"].(string)
	if ok {
		u, err := url.Parse(uri)
		if err == nil {
			return fmt.Sprintf("%s", u.Host), nil
		}
		// Fallback to hostname/port lookup
	}

	hostname, ok := consulService.Credentials["hostname"].(string)
	if !ok {
		return "", errors.New("consul service not available")
	}
	port, ok = consulService.Credentials["port"].(string)
	if ok {
		return fmt.Sprintf("%s:%s", hostname, port), nil
	}
	return fmt.Sprintf("%s:8500", hostname), nil
}

func schemaForServices() string {
	if ForceHTTP() {
		return "http"
	}
	return "https"
}

func schemaForConsul() string {
	return "http"
}
<commit_msg>Add tags to register<commit_after>package cfutil

import (
	"errors"
	"fmt"
	consul "github.com/hashicorp/consul/api"
	"net/url"
)

func ServiceRegister(name string, tags ...string) error {
	appEnv, _ := Current()
	client, err := NewConsulClient()
	if err != nil {
		return err
	}
	err = client.Agent().ServiceRegister(&consul.AgentServiceRegistration{
		Name:    name,
		Address: appEnv.ApplicationURIs[0],
		Tags:    tags,
		Check: &consul.AgentServiceCheck{
			HTTP:     fmt.Sprintf(schemaForServices() + "://" + appEnv.ApplicationURIs[0] + "/health"),
			Interval: "30s",
		},
	})
	if err != nil {
		return err
	}
	return nil
}

func NewConsulClient() (*consul.Client, error) {
	dialString, err := consulDialstring("consul")
	if err != nil {
		return nil, err
	}
	client, consulErr := consul.NewClient(&consul.Config{
		Address: dialString,
		Scheme:  schemaForConsul(),
	})
	if consulErr != nil {
		return nil, consulErr
	}
	return client, nil
}

func consulDialstring(serviceName string) (string, error) {
	appEnv, _ := Current()
	consulService, err := appEnv.Services.WithName(serviceName)
	if err != nil {
		return "", err
	}
	port := "8500"

	uri, ok := consulService.Credentials["uri"].(string)
	if ok {
		u, err := url.Parse(uri)
		if err == nil {
			return fmt.Sprintf("%s", u.Host), nil
		}
		// Fallback to hostname/port lookup
	}

	hostname, ok := consulService.Credentials["hostname"].(string)
	if !ok {
		return "", errors.New("consul service not available")
	}
	port, ok = consulService.Credentials["port"].(string)
	if ok {
		return fmt.Sprintf("%s:%s", hostname, port), nil
	}
	return fmt.Sprintf("%s:8500", hostname), nil
}

func schemaForServices() string {
	if ForceHTTP() {
		return "http"
	}
	return "https"
}

func schemaForConsul() string {
	return "http"
}
<|endoftext|>


<commit_before>package goblet

import (
	"errors"
	"fmt"
	"net/http"
)

// CookieIsMissing is returned when a cookie is missing.
var CookieIsMissing error = errors.New("Cookie is missing")

// SignedCookieIsMissing is returned when the signed cookie is missing.
var SignedCookieIsMissing error = errors.New("Signed cookie is missing")

// CookieNotValid is returned when the cookie and its signed counterpart do not match.
//
// I.e. the cookie value has been tampered with.
var CookieNotValid error = errors.New("Cookie and signed cookie do not match")

var (
	// SignedCookieFormat is the format string used to decide the name of the
	// signed cookie.
	SignedCookieFormat string = "%s_signed"
)

// toSignedCookieName gets the signed cookie name from the specified cookie name,
// by running it through the SignedCookieFormat string.
func toSignedCookieName(name string) string {
	return fmt.Sprintf(SignedCookieFormat, name)
}

// AddSignedCookie adds the specified cookie to the response and also adds an
// additional 'signed' cookie that is used to validate the cookies value when
// SignedCookie is called.
func (c *Context) AddSignedCookie(cookie *http.Cookie) (*http.Cookie, error) {

	// make the signed cookie
	signedCookie := new(http.Cookie)

	// copy the cookie settings
	signedCookie.Path = cookie.Path
	signedCookie.Domain = cookie.Domain
	signedCookie.RawExpires = cookie.RawExpires
	signedCookie.Expires = cookie.Expires
	signedCookie.MaxAge = cookie.MaxAge
	signedCookie.Secure = cookie.Secure
	signedCookie.HttpOnly = cookie.HttpOnly
	signedCookie.Raw = cookie.Raw

	// set the signed cookie specifics
	signedCookie.Name = toSignedCookieName(cookie.Name)
	signedCookie.Value = c.Server.Hash(cookie.Value)

	// add the cookies
	http.SetCookie(c.writer, cookie)
	http.SetCookie(c.writer, signedCookie)

	// return the new signed cookie (and no error)
	return signedCookie, nil

}

// Gets the cookie specified by name and validates that its value has not been
// tampered with by checking the signed cookie too.  Will return CookieNotValid error
// if it has been tampered with, otherwise, it will return the actual cookie.
func (c *Context) SignedCookie(name string) (*http.Cookie, error) {

	valid, validErr := c.cookieIsValid(name)
	if valid {
		return c.Request.Cookie(name)
	} else if validErr != nil {
		return nil, validErr
	}

	return nil, CookieNotValid
}

// cookieIsValid checks to see if the cookie and its signed counterpart
// match.
func (c *Context) cookieIsValid(name string) (bool, error) {

	// get the cookies
	cookie, cookieErr := c.Request.Cookie(name)
	signedCookie, signedCookieErr := c.Request.Cookie(toSignedCookieName(name))

	// handle errors reading cookies
	if cookieErr == http.ErrNoCookie {
		return false, CookieIsMissing
	}
	if cookieErr != nil {
		return false, cookieErr
	}
	if signedCookieErr == http.ErrNoCookie {
		return false, SignedCookieIsMissing
	}
	if signedCookieErr != nil {
		return false, signedCookieErr
	}

	// check the cookies
	if c.Server.Hash(cookie.Value) != signedCookie.Value {
		return false, nil
	}

	// success - therefore valid
	return true, nil

}
<commit_msg>add AddCookie<commit_after>package goblet

import (
	"errors"
	"fmt"
	"net/http"
)

// CookieIsMissing is returned when a cookie is missing.
var CookieIsMissing error = errors.New("Cookie is missing")

// SignedCookieIsMissing is returned when the signed cookie is missing.
var SignedCookieIsMissing error = errors.New("Signed cookie is missing")

// CookieNotValid is returned when the cookie and its signed counterpart do not match.
//
// I.e. the cookie value has been tampered with.
var CookieNotValid error = errors.New("Cookie and signed cookie do not match")

var (
	// SignedCookieFormat is the format string used to decide the name of the
	// signed cookie.
	SignedCookieFormat string = "%s_signed"
)

// toSignedCookieName gets the signed cookie name from the specified cookie name,
// by running it through the SignedCookieFormat string.
func toSignedCookieName(name string) string {
	return fmt.Sprintf(SignedCookieFormat, name)
}

// AddSignedCookie adds the specified cookie to the response and also adds an
// additional 'signed' cookie that is used to validate the cookies value when
// SignedCookie is called.
func (c *Context) AddSignedCookie(cookie *http.Cookie) (*http.Cookie, error) {

	// make the signed cookie
	signedCookie := new(http.Cookie)

	// copy the cookie settings
	signedCookie.Path = cookie.Path
	signedCookie.Domain = cookie.Domain
	signedCookie.RawExpires = cookie.RawExpires
	signedCookie.Expires = cookie.Expires
	signedCookie.MaxAge = cookie.MaxAge
	signedCookie.Secure = cookie.Secure
	signedCookie.HttpOnly = cookie.HttpOnly
	signedCookie.Raw = cookie.Raw

	// set the signed cookie specifics
	signedCookie.Name = toSignedCookieName(cookie.Name)
	signedCookie.Value = c.Server.Hash(cookie.Value)

	// add the cookies
	http.SetCookie(c.writer, cookie)
	http.SetCookie(c.writer, signedCookie)

	// return the new signed cookie (and no error)
	return signedCookie, nil

}

func (c *Context) AddCookie(cookie *http.Cookie) error {

	// add the cookies
	http.SetCookie(c.writer, cookie)

	// return the new signed cookie (and no error)
	return nil

}

// Gets the cookie specified by name and validates that its value has not been
// tampered with by checking the signed cookie too.  Will return CookieNotValid error
// if it has been tampered with, otherwise, it will return the actual cookie.
func (c *Context) SignedCookie(name string) (*http.Cookie, error) {

	valid, validErr := c.cookieIsValid(name)
	if valid {
		return c.Request.Cookie(name)
	} else if validErr != nil {
		return nil, validErr
	}

	return nil, CookieNotValid
}

// cookieIsValid checks to see if the cookie and its signed counterpart
// match.
func (c *Context) cookieIsValid(name string) (bool, error) {

	// get the cookies
	cookie, cookieErr := c.Request.Cookie(name)
	signedCookie, signedCookieErr := c.Request.Cookie(toSignedCookieName(name))

	// handle errors reading cookies
	if cookieErr == http.ErrNoCookie {
		return false, CookieIsMissing
	}
	if cookieErr != nil {
		return false, cookieErr
	}
	if signedCookieErr == http.ErrNoCookie {
		return false, SignedCookieIsMissing
	}
	if signedCookieErr != nil {
		return false, signedCookieErr
	}

	// check the cookies
	if c.Server.Hash(cookie.Value) != signedCookie.Value {
		return false, nil
	}

	// success - therefore valid
	return true, nil

}
<|endoftext|>


<commit_before>// The MIT License (MIT)
//
// Copyright (c) 2015 Arnaud Vazard
//
// See LICENSE file.
package memo

import (
	"database/sql"
	"fmt"
	"github.com/thoj/go-ircevent"
	"log"
	"strings"
)

var _database *sql.DB

func Init(db *sql.DB) {
	_database = db
	sqlStmt := `CREATE TABLE IF NOT EXISTS Memo (
    id integer NOT NULL PRIMARY KEY,
    user_to TEXT,
    user_from TEXT,
    message TEXT,
    date DATETIME DEFAULT CURRENT_TIMESTAMP);`

	_, err := db.Exec(sqlStmt)
	if err != nil {
		log.Fatalf("%q: %s\n", err, sqlStmt)
	}
}

func HandleMemoCmd(event *irc.Event, callback func(string)) bool {
	fields := strings.Fields(event.Message())
	// fields[0]  => Command
	// fields[1]  => recipient's nick
	// fields[2:] => message
	if len(fields) == 0 || fields[0] != "!memo" {
		return false
	}

	if len(fields) < 3 {
		if callback != nil {
			callback(fmt.Sprintf("Memo usage: %s \"recipient's nick\" \"message\"", fields[0]))
		}
		return false
	}
	user_from := event.Nick
	message := strings.Join(fields[2:], " ")

	sqlStmt := "INSERT INTO Memo (user_to, user_from, message) VALUES ($1, $2, $3)"
	_, err := _database.Exec(sqlStmt, fields[1], user_from, message)
	if err != nil {
		log.Fatalf("%q: %s\n", err, sqlStmt)
	}

	if callback != nil {
		callback(fmt.Sprintf("Memo for %s from %s: \"%s\"", fields[1], user_from, message))
	}
	return true

}

func SendMemo(event *irc.Event, callback func(string)) bool {
	if callback == nil {
		log.Println("Callback nil for the SendMemo function, unable to send the memo")
	}

	user := event.Nick
	sqlQuery := "SELECT id, user_to, user_from, message, strftime('%d/%m/%Y @ %H:%M', datetime(date, 'localtime')) FROM Memo WHERE user_to = $1;"
	rows, err := _database.Query(sqlQuery, user)
	if err != nil {
		log.Fatalf("%q: %s\n", err, sqlQuery)
	}
	defer rows.Close()

	var (
		id        int
		date      string
		message   string
		user_from string
		user_to   string
		idList    []int
	)

	for rows.Next() {
		rows.Scan(&id, &user_to, &user_from, &message, &date)
		idList = append(idList, id)
		callback(fmt.Sprintf("%s: %s left you a memo => \"%s\" (%s)", user_to, user_from, message, date))
	}
	rows.Close()

	for _, id = range idList {
		sqlQuery = "DELETE FROM Memo WHERE id = $1"
		_, err = _database.Exec(sqlQuery, id)
		if err != nil {
			log.Fatalf("%q: %s\n", err, sqlQuery)
		}
	}
	return false
}
<commit_msg>Memo: updated to use callback with *core.ReplyCallbackData parameter + Added memoData struct<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2015 Arnaud Vazard
//
// See LICENSE file.
package memo

import (
	"database/sql"
	"fmt"
	"github.com/romainletendart/goxxx/core"
	"github.com/thoj/go-ircevent"
	"log"
	"strings"
)

var _database *sql.DB

type memoData struct {
	id        int
	date      string
	message   string
	user_from string
	user_to   string
}

func Init(db *sql.DB) {
	_database = db
	sqlStmt := `CREATE TABLE IF NOT EXISTS Memo (
    id integer NOT NULL PRIMARY KEY,
    user_to TEXT,
    user_from TEXT,
    message TEXT,
    date DATETIME DEFAULT CURRENT_TIMESTAMP);`

	_, err := db.Exec(sqlStmt)
	if err != nil {
		log.Fatalf("%q: %s\n", err, sqlStmt)
	}
}

func HandleMemoCmd(event *irc.Event, callback func(*core.ReplyCallbackData)) bool {
	fields := strings.Fields(event.Message())
	// fields[0]  => Command
	// fields[1]  => recipient's nick
	// fields[2:] => message
	if len(fields) == 0 || fields[0] != "!memo" {
		return false
	}

	if len(fields) < 3 {
		if callback != nil {
			callback(&core.ReplyCallbackData{
				Message: fmt.Sprintf("Memo usage: %s \"recipient's nick\" \"message\"", fields[0])})
		}
		return false
	}

	memo := memoData{
		user_to:   fields[1],
		user_from: event.Nick,
		message:   strings.Join(fields[2:], " ")}

	sqlStmt := "INSERT INTO Memo (user_to, user_from, message) VALUES ($1, $2, $3)"
	_, err := _database.Exec(sqlStmt, memo.user_to, memo.user_from, memo.message)
	if err != nil {
		log.Fatalf("%q: %s\n", err, sqlStmt)
	}

	if callback != nil {
		callback(&core.ReplyCallbackData{
			// Message: fmt.Sprintf("Memo for %s from %s: \"%s\"", fields[1], user_from, message)})
			Message: fmt.Sprintf("%s: memo for %s saved", memo.user_from, memo.user_from)})
	}
	return true
}

func SendMemo(event *irc.Event, callback func(*core.ReplyCallbackData)) bool {
	if callback == nil {
		log.Println("Callback nil for the SendMemo function, unable to send the memo")
	}

	user := event.Nick
	sqlQuery := "SELECT id, user_to, user_from, message, strftime('%d/%m/%Y @ %H:%M', datetime(date, 'localtime')) FROM Memo WHERE user_to = $1;"
	rows, err := _database.Query(sqlQuery, user)
	if err != nil {
		log.Fatalf("%q: %s\n", err, sqlQuery)
	}
	defer rows.Close()

	var memoList []memoData
	for rows.Next() {
		var memo memoData
		rows.Scan(&memo.id, &memo.user_to, &memo.user_from, &memo.message, &memo.date)
		memoList = append(memoList, memo)
		callback(&core.ReplyCallbackData{Message: fmt.Sprintf("%s: memo from %s => \"%s\" (%s)", memo.user_to, memo.user_from, memo.message, memo.date)})
	}
	rows.Close()

	for _, memo := range memoList {
		sqlQuery = "DELETE FROM Memo WHERE id = $1"
		_, err = _database.Exec(sqlQuery, memo.id)
		if err != nil {
			log.Fatalf("%q: %s\n", err, sqlQuery)
		}
	}
	return false
}
<|endoftext|>


<commit_before>/*******************************************************************************
The MIT License (MIT)

Copyright (c) 2015 Hajime Nakagami

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*******************************************************************************/

package toybroker

import (
	"sort"
	"sync"
)

type MemoryTopics struct {
	mapClientQoS map[string]map[string]int
	sync.RWMutex
}

func NewMemoryTopics() MemoryTopics {
	return MemoryTopics{
		mapClientQoS: make(map[string]map[string]int),
	}
}

func (t MemoryTopics) Add(topicName string, clientID string, qos int) {
	t.Lock()
	defer t.Unlock()
	topic, ok := t.mapClientQoS[topicName]
	if !ok {
		topic = make(map[string]int)
		t.mapClientQoS[topicName] = topic
	}
	topic[clientID] = qos
}

func (t MemoryTopics) Remove(topicName string, clientID string) {
	t.Lock()
	defer t.Unlock()
	topic, ok := t.mapClientQoS[topicName]
	if ok {
		delete(topic, clientID)
	}
}

func (t MemoryTopics) TopicList() []string {
	t.RLock()
	defer t.RUnlock()
	list := make([]string, 0)
	for k, _ := range t.mapClientQoS {
		list = append(list, k)
	}
	sort.Strings(list)
	return list
}

func (t MemoryTopics) List(topicName string) ([]string, []int) {
	t.RLock()
	defer t.RUnlock()
	clientList := make([]string, 0)
	qosList := make([]int, 0)
	topic, ok := t.mapClientQoS[topicName]
	if ok {
		for c := range topic {
			clientList = append(clientList, c)
			qosList = append(qosList, topic[c])
		}
	}
	return clientList, qosList
}

func (t MemoryTopics) AddRetainMessage(topicName string, payload []byte) {
}

func (t MemoryTopics) GetRetainMessage(topicName string) [] byte {
    return nil
}
<commit_msg>AddRetainMessage() and GetRetainMessage()<commit_after>/*******************************************************************************
The MIT License (MIT)

Copyright (c) 2015 Hajime Nakagami

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*******************************************************************************/

package toybroker

import (
	"sort"
	"sync"
)

type MemoryTopics struct {
	mapClientQoS map[string]map[string]int
	mapRetain    map[string][]byte
	sync.RWMutex
}

func NewMemoryTopics() MemoryTopics {
	return MemoryTopics{
		mapClientQoS: make(map[string]map[string]int),
	}
}

func (t MemoryTopics) Add(topicName string, clientID string, qos int) {
	t.Lock()
	defer t.Unlock()
	topic, ok := t.mapClientQoS[topicName]
	if !ok {
		topic = make(map[string]int)
		t.mapClientQoS[topicName] = topic
	}
	topic[clientID] = qos
}

func (t MemoryTopics) Remove(topicName string, clientID string) {
	t.Lock()
	defer t.Unlock()
	topic, ok := t.mapClientQoS[topicName]
	if ok {
		delete(topic, clientID)
	}
}

func (t MemoryTopics) TopicList() []string {
	t.RLock()
	defer t.RUnlock()
	list := make([]string, 0)
	for k, _ := range t.mapClientQoS {
		list = append(list, k)
	}
	sort.Strings(list)
	return list
}

func (t MemoryTopics) List(topicName string) ([]string, []int) {
	t.RLock()
	defer t.RUnlock()
	clientList := make([]string, 0)
	qosList := make([]int, 0)
	topic, ok := t.mapClientQoS[topicName]
	if ok {
		for c := range topic {
			clientList = append(clientList, c)
			qosList = append(qosList, topic[c])
		}
	}
	return clientList, qosList
}

func (t MemoryTopics) AddRetainMessage(topicName string, payload []byte) {
	t.Lock()
	defer t.Unlock()
	if len(payload) == 0 {
		t.mapRetain[topicName] = nil
	} else {
		t.mapRetain[topicName] = payload
	}
}

func (t MemoryTopics) GetRetainMessage(topicName string) []byte {
	t.RLock()
	defer t.RUnlock()
	return t.mapRetain[topicName]
}
<|endoftext|>


<commit_before>package logger

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"sync"
	"testing"
	"time"
)

type TestLoggerJSON struct {
	*json.Encoder
	mu    sync.Mutex
	delay time.Duration
}

func (l *TestLoggerJSON) Log(m *Message) error {
	if l.delay > 0 {
		time.Sleep(l.delay)
	}
	l.mu.Lock()
	defer l.mu.Unlock()
	return l.Encode(m)
}

func (l *TestLoggerJSON) Close() error { return nil }

func (l *TestLoggerJSON) Name() string { return "json" }

func TestCopier(t *testing.T) {
	stdoutLine := "Line that thinks that it is log line from docker stdout"
	stderrLine := "Line that thinks that it is log line from docker stderr"
	var stdout bytes.Buffer
	var stderr bytes.Buffer
	for i := 0; i < 30; i++ {
		if _, err := stdout.WriteString(stdoutLine + "\n"); err != nil {
			t.Fatal(err)
		}
		if _, err := stderr.WriteString(stderrLine + "\n"); err != nil {
			t.Fatal(err)
		}
	}

	var jsonBuf bytes.Buffer

	jsonLog := &TestLoggerJSON{Encoder: json.NewEncoder(&jsonBuf)}

	c := NewCopier(
		map[string]io.Reader{
			"stdout": &stdout,
			"stderr": &stderr,
		},
		jsonLog)
	c.Run()
	wait := make(chan struct{})
	go func() {
		c.Wait()
		close(wait)
	}()
	select {
	case <-time.After(1 * time.Second):
		t.Fatal("Copier failed to do its work in 1 second")
	case <-wait:
	}
	dec := json.NewDecoder(&jsonBuf)
	for {
		var msg Message
		if err := dec.Decode(&msg); err != nil {
			if err == io.EOF {
				break
			}
			t.Fatal(err)
		}
		if msg.Source != "stdout" && msg.Source != "stderr" {
			t.Fatalf("Wrong Source: %q, should be %q or %q", msg.Source, "stdout", "stderr")
		}
		if msg.Source == "stdout" {
			if string(msg.Line) != stdoutLine {
				t.Fatalf("Wrong Line: %q, expected %q", msg.Line, stdoutLine)
			}
		}
		if msg.Source == "stderr" {
			if string(msg.Line) != stderrLine {
				t.Fatalf("Wrong Line: %q, expected %q", msg.Line, stderrLine)
			}
		}
	}
}

func TestCopierSlow(t *testing.T) {
	stdoutLine := "Line that thinks that it is log line from docker stdout"
	var stdout bytes.Buffer
	for i := 0; i < 30; i++ {
		if _, err := stdout.WriteString(stdoutLine + "\n"); err != nil {
			t.Fatal(err)
		}
	}

	var jsonBuf bytes.Buffer
	//encoder := &encodeCloser{Encoder: json.NewEncoder(&jsonBuf)}
	jsonLog := &TestLoggerJSON{Encoder: json.NewEncoder(&jsonBuf), delay: 100 * time.Millisecond}

	c := NewCopier(map[string]io.Reader{"stdout": &stdout}, jsonLog)
	c.Run()
	wait := make(chan struct{})
	go func() {
		c.Wait()
		close(wait)
	}()
	<-time.After(150 * time.Millisecond)
	c.Close()
	select {
	case <-time.After(200 * time.Millisecond):
		t.Fatalf("failed to exit in time after the copier is closed")
	case <-wait:
	}
}

type BenchmarkLoggerDummy struct {
}

func (l *BenchmarkLoggerDummy) Log(m *Message) error { return nil }

func (l *BenchmarkLoggerDummy) Close() error { return nil }

func (l *BenchmarkLoggerDummy) Name() string { return "dummy" }

func BenchmarkCopier64(b *testing.B) {
	benchmarkCopier(b, 1<<6)
}
func BenchmarkCopier128(b *testing.B) {
	benchmarkCopier(b, 1<<7)
}
func BenchmarkCopier256(b *testing.B) {
	benchmarkCopier(b, 1<<8)
}
func BenchmarkCopier512(b *testing.B) {
	benchmarkCopier(b, 1<<9)
}
func BenchmarkCopier1K(b *testing.B) {
	benchmarkCopier(b, 1<<10)
}
func BenchmarkCopier2K(b *testing.B) {
	benchmarkCopier(b, 1<<11)
}
func BenchmarkCopier4K(b *testing.B) {
	benchmarkCopier(b, 1<<12)
}
func BenchmarkCopier8K(b *testing.B) {
	benchmarkCopier(b, 1<<13)
}
func BenchmarkCopier16K(b *testing.B) {
	benchmarkCopier(b, 1<<14)
}
func BenchmarkCopier32K(b *testing.B) {
	benchmarkCopier(b, 1<<15)
}
func BenchmarkCopier64K(b *testing.B) {
	benchmarkCopier(b, 1<<16)
}
func BenchmarkCopier128K(b *testing.B) {
	benchmarkCopier(b, 1<<17)
}
func BenchmarkCopier256K(b *testing.B) {
	benchmarkCopier(b, 1<<18)
}

func piped(b *testing.B, iterations int, delay time.Duration, buf []byte) io.Reader {
	r, w, err := os.Pipe()
	if err != nil {
		b.Fatal(err)
		return nil
	}
	go func() {
		for i := 0; i < iterations; i++ {
			time.Sleep(delay)
			if n, err := w.Write(buf); err != nil || n != len(buf) {
				if err != nil {
					b.Fatal(err)
				}
				b.Fatal(fmt.Errorf("short write"))
			}
		}
		w.Close()
	}()
	return r
}

func benchmarkCopier(b *testing.B, length int) {
	b.StopTimer()
	buf := []byte{'A'}
	for len(buf) < length {
		buf = append(buf, buf...)
	}
	buf = append(buf[:length-1], []byte{'\n'}...)
	b.StartTimer()
	for i := 0; i < b.N; i++ {
		c := NewCopier(
			map[string]io.Reader{
				"buffer": piped(b, 10, time.Nanosecond, buf),
			},
			&BenchmarkLoggerDummy{})
		c.Run()
		c.Wait()
		c.Close()
	}
}
<commit_msg>add tests for long log-lines and trailing lines<commit_after>package logger

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"strings"
	"sync"
	"testing"
	"time"
)

type TestLoggerJSON struct {
	*json.Encoder
	mu    sync.Mutex
	delay time.Duration
}

func (l *TestLoggerJSON) Log(m *Message) error {
	if l.delay > 0 {
		time.Sleep(l.delay)
	}
	l.mu.Lock()
	defer l.mu.Unlock()
	return l.Encode(m)
}

func (l *TestLoggerJSON) Close() error { return nil }

func (l *TestLoggerJSON) Name() string { return "json" }

func TestCopier(t *testing.T) {
	stdoutLine := "Line that thinks that it is log line from docker stdout"
	stderrLine := "Line that thinks that it is log line from docker stderr"
	stdoutTrailingLine := "stdout trailing line"
	stderrTrailingLine := "stderr trailing line"

	var stdout bytes.Buffer
	var stderr bytes.Buffer
	for i := 0; i < 30; i++ {
		if _, err := stdout.WriteString(stdoutLine + "\n"); err != nil {
			t.Fatal(err)
		}
		if _, err := stderr.WriteString(stderrLine + "\n"); err != nil {
			t.Fatal(err)
		}
	}

	// Test remaining lines without line-endings
	if _, err := stdout.WriteString(stdoutTrailingLine); err != nil {
		t.Fatal(err)
	}
	if _, err := stderr.WriteString(stderrTrailingLine); err != nil {
		t.Fatal(err)
	}

	var jsonBuf bytes.Buffer

	jsonLog := &TestLoggerJSON{Encoder: json.NewEncoder(&jsonBuf)}

	c := NewCopier(
		map[string]io.Reader{
			"stdout": &stdout,
			"stderr": &stderr,
		},
		jsonLog)
	c.Run()
	wait := make(chan struct{})
	go func() {
		c.Wait()
		close(wait)
	}()
	select {
	case <-time.After(1 * time.Second):
		t.Fatal("Copier failed to do its work in 1 second")
	case <-wait:
	}
	dec := json.NewDecoder(&jsonBuf)
	for {
		var msg Message
		if err := dec.Decode(&msg); err != nil {
			if err == io.EOF {
				break
			}
			t.Fatal(err)
		}
		if msg.Source != "stdout" && msg.Source != "stderr" {
			t.Fatalf("Wrong Source: %q, should be %q or %q", msg.Source, "stdout", "stderr")
		}
		if msg.Source == "stdout" {
			if string(msg.Line) != stdoutLine && string(msg.Line) != stdoutTrailingLine {
				t.Fatalf("Wrong Line: %q, expected %q or %q", msg.Line, stdoutLine, stdoutTrailingLine)
			}
		}
		if msg.Source == "stderr" {
			if string(msg.Line) != stderrLine && string(msg.Line) != stderrTrailingLine {
				t.Fatalf("Wrong Line: %q, expected %q or %q", msg.Line, stderrLine, stderrTrailingLine)
			}
		}
	}
}

// TestCopierLongLines tests long lines without line breaks
func TestCopierLongLines(t *testing.T) {
	// Long lines (should be split at "bufSize")
	const bufSize = 16 * 1024
	stdoutLongLine := strings.Repeat("a", bufSize)
	stderrLongLine := strings.Repeat("b", bufSize)
	stdoutTrailingLine := "stdout trailing line"
	stderrTrailingLine := "stderr trailing line"

	var stdout bytes.Buffer
	var stderr bytes.Buffer

	for i := 0; i < 3; i++ {
		if _, err := stdout.WriteString(stdoutLongLine); err != nil {
			t.Fatal(err)
		}
		if _, err := stderr.WriteString(stderrLongLine); err != nil {
			t.Fatal(err)
		}
	}

	if _, err := stdout.WriteString(stdoutTrailingLine); err != nil {
		t.Fatal(err)
	}
	if _, err := stderr.WriteString(stderrTrailingLine); err != nil {
		t.Fatal(err)
	}

	var jsonBuf bytes.Buffer

	jsonLog := &TestLoggerJSON{Encoder: json.NewEncoder(&jsonBuf)}

	c := NewCopier(
		map[string]io.Reader{
			"stdout": &stdout,
			"stderr": &stderr,
		},
		jsonLog)
	c.Run()
	wait := make(chan struct{})
	go func() {
		c.Wait()
		close(wait)
	}()
	select {
	case <-time.After(1 * time.Second):
		t.Fatal("Copier failed to do its work in 1 second")
	case <-wait:
	}
	dec := json.NewDecoder(&jsonBuf)
	for {
		var msg Message
		if err := dec.Decode(&msg); err != nil {
			if err == io.EOF {
				break
			}
			t.Fatal(err)
		}
		if msg.Source != "stdout" && msg.Source != "stderr" {
			t.Fatalf("Wrong Source: %q, should be %q or %q", msg.Source, "stdout", "stderr")
		}
		if msg.Source == "stdout" {
			if string(msg.Line) != stdoutLongLine && string(msg.Line) != stdoutTrailingLine {
				t.Fatalf("Wrong Line: %q, expected 'stdoutLongLine' or 'stdoutTrailingLine'", msg.Line)
			}
		}
		if msg.Source == "stderr" {
			if string(msg.Line) != stderrLongLine && string(msg.Line) != stderrTrailingLine {
				t.Fatalf("Wrong Line: %q, expected 'stderrLongLine' or 'stderrTrailingLine'", msg.Line)
			}
		}
	}
}

func TestCopierSlow(t *testing.T) {
	stdoutLine := "Line that thinks that it is log line from docker stdout"
	var stdout bytes.Buffer
	for i := 0; i < 30; i++ {
		if _, err := stdout.WriteString(stdoutLine + "\n"); err != nil {
			t.Fatal(err)
		}
	}

	var jsonBuf bytes.Buffer
	//encoder := &encodeCloser{Encoder: json.NewEncoder(&jsonBuf)}
	jsonLog := &TestLoggerJSON{Encoder: json.NewEncoder(&jsonBuf), delay: 100 * time.Millisecond}

	c := NewCopier(map[string]io.Reader{"stdout": &stdout}, jsonLog)
	c.Run()
	wait := make(chan struct{})
	go func() {
		c.Wait()
		close(wait)
	}()
	<-time.After(150 * time.Millisecond)
	c.Close()
	select {
	case <-time.After(200 * time.Millisecond):
		t.Fatalf("failed to exit in time after the copier is closed")
	case <-wait:
	}
}

type BenchmarkLoggerDummy struct {
}

func (l *BenchmarkLoggerDummy) Log(m *Message) error { return nil }

func (l *BenchmarkLoggerDummy) Close() error { return nil }

func (l *BenchmarkLoggerDummy) Name() string { return "dummy" }

func BenchmarkCopier64(b *testing.B) {
	benchmarkCopier(b, 1<<6)
}
func BenchmarkCopier128(b *testing.B) {
	benchmarkCopier(b, 1<<7)
}
func BenchmarkCopier256(b *testing.B) {
	benchmarkCopier(b, 1<<8)
}
func BenchmarkCopier512(b *testing.B) {
	benchmarkCopier(b, 1<<9)
}
func BenchmarkCopier1K(b *testing.B) {
	benchmarkCopier(b, 1<<10)
}
func BenchmarkCopier2K(b *testing.B) {
	benchmarkCopier(b, 1<<11)
}
func BenchmarkCopier4K(b *testing.B) {
	benchmarkCopier(b, 1<<12)
}
func BenchmarkCopier8K(b *testing.B) {
	benchmarkCopier(b, 1<<13)
}
func BenchmarkCopier16K(b *testing.B) {
	benchmarkCopier(b, 1<<14)
}
func BenchmarkCopier32K(b *testing.B) {
	benchmarkCopier(b, 1<<15)
}
func BenchmarkCopier64K(b *testing.B) {
	benchmarkCopier(b, 1<<16)
}
func BenchmarkCopier128K(b *testing.B) {
	benchmarkCopier(b, 1<<17)
}
func BenchmarkCopier256K(b *testing.B) {
	benchmarkCopier(b, 1<<18)
}

func piped(b *testing.B, iterations int, delay time.Duration, buf []byte) io.Reader {
	r, w, err := os.Pipe()
	if err != nil {
		b.Fatal(err)
		return nil
	}
	go func() {
		for i := 0; i < iterations; i++ {
			time.Sleep(delay)
			if n, err := w.Write(buf); err != nil || n != len(buf) {
				if err != nil {
					b.Fatal(err)
				}
				b.Fatal(fmt.Errorf("short write"))
			}
		}
		w.Close()
	}()
	return r
}

func benchmarkCopier(b *testing.B, length int) {
	b.StopTimer()
	buf := []byte{'A'}
	for len(buf) < length {
		buf = append(buf, buf...)
	}
	buf = append(buf[:length-1], []byte{'\n'}...)
	b.StartTimer()
	for i := 0; i < b.N; i++ {
		c := NewCopier(
			map[string]io.Reader{
				"buffer": piped(b, 10, time.Nanosecond, buf),
			},
			&BenchmarkLoggerDummy{})
		c.Run()
		c.Wait()
		c.Close()
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 Jacob Taylor jacob@ablox.io
// License: Apache2 - http://www.apache.org/licenses/LICENSE-2.0
package main

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"github.com/rjeczalik/notify"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"sort"
	"sync"
	"syscall"
)

type ChangeHandler interface {
	FolderCreated(name string) (err error)
	FolderDeleted(name string) (err error)
}

type StorageTracker interface {
	CreateFolder(name string) (err error)
	DeleteFolder(name string) (err error)
	ListFolders() (folders []string, err error)
}

// sample change handler
type LogOnlyChangeHandler struct {
}

func (self *LogOnlyChangeHandler) FolderCreated(name string) (err error) {
	fmt.Printf("LogOnlyChangeHandler:FolderCreated: %s\n", name)
	return nil
}

func (self *LogOnlyChangeHandler) FolderDeleted(name string) (err error) {
	fmt.Printf("LogOnlyChangeHandler:FolderDeleted: %s\n", name)
	return nil
}

type Tracker struct {
}

var primaryTracker = new(Tracker)

type FilesystemTracker struct {
	directory       string
	contents        DirTreeMap
	setup           bool
	watcher         *ChangeHandler
	fsEventsChannel chan notify.EventInfo
	fsLock          sync.RWMutex
}

func (self *FilesystemTracker) init(directory string) {
	self.fsLock.Lock()
	defer self.fsLock.Unlock()

	if self.setup {
		return
	}

	fmt.Printf("FilesystemTracker:init called with %s\n", directory)
	self.directory = directory

	// Make the channel buffered to ensure no event is dropped. Notify will drop
	// an event if the receiver is not able to keep up the sending pace.
	self.fsEventsChannel = make(chan notify.EventInfo, 1)

	// Update the path that traffic is served from to be the filesystem canonical path. This will allow the event folders that come in to match what we have.
	fullPath := validatePath(directory)
	self.directory = fullPath

	if fullPath != globalSettings.Directory {
		fmt.Printf("Updating serving directory to: %s\n", fullPath)
		self.directory = fullPath
	}

	fmt.Println("Setting up filesystemTracker!")
	err := self.scanFolders()
	if err != nil {
		panic(err)
	}
	self.setup = true
}

func (self *FilesystemTracker) cleanup() {
	self.fsLock.Lock()
	defer self.fsLock.Unlock()

	if !self.setup {
		panic("cleanup called when not yet setup")
	}

	notify.Stop(self.fsEventsChannel)
}

func (self *FilesystemTracker) watchDirectory(watcher *ChangeHandler) {
	self.fsLock.Lock()
	defer self.fsLock.Unlock()

	if !self.setup {
		panic("FilesystemTracker:watchDirectory called when not yet setup")
	}

	if self.watcher != nil {
		panic("watchDirectory called a second time. Not allowed")
	}

	self.watcher = watcher

	go self.monitorLoop(self.fsEventsChannel)

	// Set up a watch point listening for events within a directory tree rooted at the specified folder
	err := notify.Watch(self.directory+"/...", self.fsEventsChannel, notify.All)
	if err != nil {
		log.Panic(err)
	}
}

func validatePath(directory string) (fullPath string) {
	fullPath, err := filepath.EvalSymlinks(directory)
	if err != nil {
		var err2, err3 error
		// We have an error. If the directory does not exist, then try to create it. Fail if we cannot create it and return the original error
		if os.IsNotExist(err) {
			err2 = os.Mkdir(directory, os.ModeDir+os.ModePerm)
			fullPath, err3 = filepath.EvalSymlinks(directory)
			if err2 != nil || err3 != nil {
				panic(fmt.Sprintf("err: %v\nerr2: %v\nerr3: %v\n", err, err2, err3))
			}
		} else {
			panic(err)
		}
	}

	return
}

func (self *FilesystemTracker) CreateFolder(name string) (err error) {
	if !self.setup {
		panic("FilesystemTracker:CreateFolder called when not yet setup")
	}

	self.fsLock.Lock()
	defer self.fsLock.Unlock()

	_, exists := self.contents[name]
	fmt.Printf("CreateFolder: '%s' (%v)\n", name, exists)

	if !exists {
		self.contents[name] = make([]string, 0, 10)
	}

	return nil
}

func (self *FilesystemTracker) DeleteFolder(name string) (err error) {
	if !self.setup {
		panic("FilesystemTracker:DeleteFolder called when not yet setup")
	}

	self.fsLock.Lock()
	defer self.fsLock.Unlock()

	fmt.Printf("%d before delete of: %s\n", len(self.contents), name)
	fmt.Printf("DeleteFolder: '%s'\n", name)
	delete(self.contents, name)
	fmt.Printf("%d after delete of: %s\n", len(self.contents), name)

	return nil
}

func (self *FilesystemTracker) ListFolders() (folderList []string) {
	if !self.setup {
		panic("FilesystemTracker:ListFolders called when not yet setup")
	}

	self.fsLock.Lock()
	defer self.fsLock.Unlock()

	folderList = make([]string, len(self.contents))
	index := 0

	for k, _ := range self.contents {
		folderList[index] = k
		index++
	}

	sort.Strings(folderList)
	return
}

// Monitor the filesystem looking for changes to files we are keeping track of.
func (self *FilesystemTracker) monitorLoop(c chan notify.EventInfo) {
	directoryLength := len(self.directory)
	for {
		ei := <-c

		fmt.Println("We have an event")
		fullPath := string(ei.Path())

		path := fullPath
		if len(fullPath) >= directoryLength && self.directory == fullPath[:directoryLength] {
			if len(fullPath) == directoryLength {
				path = "."
			} else {
				// update the path to not have this prefix
				path = fullPath[directoryLength+1:]
			}
		}

		event := Event{Name: ei.Event().String(), Message: path}
		log.Printf("Event captured name: %s location: %s", event.Name, event.Message)

		// It looks like there is some sort of reversal of paths now. where adds are being removed from the other side.
		isDirectory := self.checkIfDirectory(event, path, fullPath)

		if isDirectory {
			self.processEvent(event, path)
		}

	}
}

func (self *FilesystemTracker) checkIfDirectory(event Event, path, fullPath string) bool {
	// Check to see if this was a path we knew about
	_, isDirectory := self.contents[path]
	var iNode uint64
	// if we have not found it yet, check to see if it can be stated
	info, err := os.Stat(fullPath)
	if err == nil {
		isDirectory = info.IsDir()
		sysInterface := info.Sys()
		fmt.Printf("sysInterface: %v\n", sysInterface)
		if sysInterface != nil {
			foo := sysInterface.(*syscall.Stat_t)
			iNode = foo.Ino
		}
	}

	fmt.Printf("checkIfDirectory: event raw data: %s with path: %s fullPath: %s isDirectory: %v iNode: %v\n", event.Name, path, fullPath, isDirectory, iNode)

	return isDirectory

}

func (self *FilesystemTracker) processEvent(event Event, pathName string) {
	log.Printf("handleFilsystemEvent name: %s pathName: %s serverMap: %v\n", event.Name, pathName, serverMap)

	currentValue, exists := self.contents[pathName]
	//fmt.Printf("Before: %s: Existing value for %s: %v (%v)\n", event.Name, pathName, currentValue, exists)

	switch event.Name {
	case "notify.Create":
		fmt.Printf("processEvent: About to assign from one path to the next. Original: %s Map: %s\n", self.contents[pathName], self.contents)
		// make sure there is an entry in the DirTreeMap for this folder. Since and empty list will always be returned, we can use that
		_, exists := self.contents[pathName]
		if !exists {
			self.contents[pathName] = make([]string, 0)
		}

		updated_value, exists := self.contents[pathName]
		if self.watcher != nil {
			(*self.watcher).FolderCreated(pathName)
		}
		fmt.Printf("notify.Create: Updated  value for %s: %v (%s)\n", pathName, updated_value, exists)

	case "notify.Remove":
		// clean out the entry in the DirTreeMap for this folder
		delete(self.contents, pathName)

		updated_value, exists := self.contents[pathName]

		if self.watcher != nil {
			(*self.watcher).FolderDeleted(pathName)
		} else {
			fmt.Println("In the notify.Remove section but did not see a watcher")
		}

		fmt.Printf("notify.Remove: Updated  value for %s: %v (%s)\n", pathName, updated_value, exists)

	// todo fix this to handle the two rename events to be one event
	//case "notify.Rename":
	//	err = os.Remove(pathName)
	//	if err != nil && !os.IsNotExist(err) {
	//		panic(fmt.Sprintf("Error deleting folder that was renamed %s: %v\n", pathName, err))
	//	}
	//	fmt.Printf("notify.Rename: %s\n", pathName)
	default:
		fmt.Printf("%s: %s not known, skipping\n", event.Name, pathName)
	}

	currentValue, exists = self.contents[pathName]
	fmt.Printf("After: %s: Existing value for %s: %v (%v)\n", event.Name, pathName, currentValue, exists)

	// sendEvent to manager
	sendEvent(&event, globalSettings.ManagerAddress, globalSettings.ManagerCredentials)

	// todo - make this actually send to the peers
	log.Println("TODO Send to peers here")
}

// Scan the files and folders inside of the directory we are watching and add them to the contents. This function
// can only be called inside of a writelock on self.fsLock
func (self *FilesystemTracker) scanFolders() error {
	pendingPaths := make([]string, 0, 100)
	pendingPaths = append(pendingPaths, self.directory)
	self.contents = make(DirTreeMap)

	for len(pendingPaths) > 0 {
		currentPath := pendingPaths[0]
		fileList := make([]string, 0, 100)
		pendingPaths = pendingPaths[1:]

		// Read the directories in the path
		f, err := os.Open(currentPath)
		if err != nil {
			return err
		}

		dirEntries, err := f.Readdir(-1)
		for _, entry := range dirEntries {
			if entry.IsDir() {
				newDirectory := filepath.Join(currentPath, entry.Name())
				pendingPaths = append(pendingPaths, newDirectory)
			} else {
				fileList = append(fileList, entry.Name())
			}
		}

		f.Close()
		if err != nil {
			return err
		}

		sort.Strings(fileList)

		// Strip the base path off of the current path
		// make sure all of the paths are still '/' prefixed
		relativePath := currentPath[len(self.directory):]
		if relativePath == "" {
			relativePath = "."
		}

		self.contents[relativePath] = fileList
	}

	return nil
}
<commit_msg>imports<commit_after>// Copyright 2016 Jacob Taylor jacob@ablox.io
// License: Apache2 - http://www.apache.org/licenses/LICENSE-2.0
package main

import (
	"fmt"
	"github.com/rjeczalik/notify"
	"log"
	"os"
	"path/filepath"
	"sort"
	"sync"
	"syscall"
)

type ChangeHandler interface {
	FolderCreated(name string) (err error)
	FolderDeleted(name string) (err error)
}

type StorageTracker interface {
	CreateFolder(name string) (err error)
	DeleteFolder(name string) (err error)
	ListFolders() (folders []string, err error)
}

// sample change handler
type LogOnlyChangeHandler struct {
}

func (self *LogOnlyChangeHandler) FolderCreated(name string) (err error) {
	fmt.Printf("LogOnlyChangeHandler:FolderCreated: %s\n", name)
	return nil
}

func (self *LogOnlyChangeHandler) FolderDeleted(name string) (err error) {
	fmt.Printf("LogOnlyChangeHandler:FolderDeleted: %s\n", name)
	return nil
}

type Tracker struct {
}

var primaryTracker = new(Tracker)

type FilesystemTracker struct {
	directory       string
	contents        DirTreeMap
	setup           bool
	watcher         *ChangeHandler
	fsEventsChannel chan notify.EventInfo
	fsLock          sync.RWMutex
}

func (self *FilesystemTracker) init(directory string) {
	self.fsLock.Lock()
	defer self.fsLock.Unlock()

	if self.setup {
		return
	}

	fmt.Printf("FilesystemTracker:init called with %s\n", directory)
	self.directory = directory

	// Make the channel buffered to ensure no event is dropped. Notify will drop
	// an event if the receiver is not able to keep up the sending pace.
	self.fsEventsChannel = make(chan notify.EventInfo, 1)

	// Update the path that traffic is served from to be the filesystem canonical path. This will allow the event folders that come in to match what we have.
	fullPath := validatePath(directory)
	self.directory = fullPath

	if fullPath != globalSettings.Directory {
		fmt.Printf("Updating serving directory to: %s\n", fullPath)
		self.directory = fullPath
	}

	fmt.Println("Setting up filesystemTracker!")
	err := self.scanFolders()
	if err != nil {
		panic(err)
	}
	self.setup = true
}

func (self *FilesystemTracker) cleanup() {
	self.fsLock.Lock()
	defer self.fsLock.Unlock()

	if !self.setup {
		panic("cleanup called when not yet setup")
	}

	notify.Stop(self.fsEventsChannel)
}

func (self *FilesystemTracker) watchDirectory(watcher *ChangeHandler) {
	self.fsLock.Lock()
	defer self.fsLock.Unlock()

	if !self.setup {
		panic("FilesystemTracker:watchDirectory called when not yet setup")
	}

	if self.watcher != nil {
		panic("watchDirectory called a second time. Not allowed")
	}

	self.watcher = watcher

	go self.monitorLoop(self.fsEventsChannel)

	// Set up a watch point listening for events within a directory tree rooted at the specified folder
	err := notify.Watch(self.directory+"/...", self.fsEventsChannel, notify.All)
	if err != nil {
		log.Panic(err)
	}
}

func validatePath(directory string) (fullPath string) {
	fullPath, err := filepath.EvalSymlinks(directory)
	if err != nil {
		var err2, err3 error
		// We have an error. If the directory does not exist, then try to create it. Fail if we cannot create it and return the original error
		if os.IsNotExist(err) {
			err2 = os.Mkdir(directory, os.ModeDir+os.ModePerm)
			fullPath, err3 = filepath.EvalSymlinks(directory)
			if err2 != nil || err3 != nil {
				panic(fmt.Sprintf("err: %v\nerr2: %v\nerr3: %v\n", err, err2, err3))
			}
		} else {
			panic(err)
		}
	}

	return
}

func (self *FilesystemTracker) CreateFolder(name string) (err error) {
	if !self.setup {
		panic("FilesystemTracker:CreateFolder called when not yet setup")
	}

	self.fsLock.Lock()
	defer self.fsLock.Unlock()

	_, exists := self.contents[name]
	fmt.Printf("CreateFolder: '%s' (%v)\n", name, exists)

	if !exists {
		self.contents[name] = make([]string, 0, 10)
	}

	return nil
}

func (self *FilesystemTracker) DeleteFolder(name string) (err error) {
	if !self.setup {
		panic("FilesystemTracker:DeleteFolder called when not yet setup")
	}

	self.fsLock.Lock()
	defer self.fsLock.Unlock()

	fmt.Printf("%d before delete of: %s\n", len(self.contents), name)
	fmt.Printf("DeleteFolder: '%s'\n", name)
	delete(self.contents, name)
	fmt.Printf("%d after delete of: %s\n", len(self.contents), name)

	return nil
}

func (self *FilesystemTracker) ListFolders() (folderList []string) {
	if !self.setup {
		panic("FilesystemTracker:ListFolders called when not yet setup")
	}

	self.fsLock.Lock()
	defer self.fsLock.Unlock()

	folderList = make([]string, len(self.contents))
	index := 0

	for k, _ := range self.contents {
		folderList[index] = k
		index++
	}

	sort.Strings(folderList)
	return
}

// Monitor the filesystem looking for changes to files we are keeping track of.
func (self *FilesystemTracker) monitorLoop(c chan notify.EventInfo) {
	directoryLength := len(self.directory)
	for {
		ei := <-c

		fmt.Println("We have an event")
		fullPath := string(ei.Path())

		path := fullPath
		if len(fullPath) >= directoryLength && self.directory == fullPath[:directoryLength] {
			if len(fullPath) == directoryLength {
				path = "."
			} else {
				// update the path to not have this prefix
				path = fullPath[directoryLength+1:]
			}
		}

		event := Event{Name: ei.Event().String(), Message: path}
		log.Printf("Event captured name: %s location: %s", event.Name, event.Message)

		// It looks like there is some sort of reversal of paths now. where adds are being removed from the other side.
		isDirectory := self.checkIfDirectory(event, path, fullPath)

		if isDirectory {
			self.processEvent(event, path)
		}

	}
}

func (self *FilesystemTracker) checkIfDirectory(event Event, path, fullPath string) bool {
	// Check to see if this was a path we knew about
	_, isDirectory := self.contents[path]
	var iNode uint64
	// if we have not found it yet, check to see if it can be stated
	info, err := os.Stat(fullPath)
	if err == nil {
		isDirectory = info.IsDir()
		sysInterface := info.Sys()
		fmt.Printf("sysInterface: %v\n", sysInterface)
		if sysInterface != nil {
			foo := sysInterface.(*syscall.Stat_t)
			iNode = foo.Ino
		}
	}

	fmt.Printf("checkIfDirectory: event raw data: %s with path: %s fullPath: %s isDirectory: %v iNode: %v\n", event.Name, path, fullPath, isDirectory, iNode)

	return isDirectory

}

func (self *FilesystemTracker) processEvent(event Event, pathName string) {
	log.Printf("handleFilsystemEvent name: %s pathName: %s serverMap: %v\n", event.Name, pathName, serverMap)

	currentValue, exists := self.contents[pathName]
	//fmt.Printf("Before: %s: Existing value for %s: %v (%v)\n", event.Name, pathName, currentValue, exists)

	switch event.Name {
	case "notify.Create":
		fmt.Printf("processEvent: About to assign from one path to the next. Original: %s Map: %s\n", self.contents[pathName], self.contents)
		// make sure there is an entry in the DirTreeMap for this folder. Since and empty list will always be returned, we can use that
		_, exists := self.contents[pathName]
		if !exists {
			self.contents[pathName] = make([]string, 0)
		}

		updated_value, exists := self.contents[pathName]
		if self.watcher != nil {
			(*self.watcher).FolderCreated(pathName)
		}
		fmt.Printf("notify.Create: Updated  value for %s: %v (%s)\n", pathName, updated_value, exists)

	case "notify.Remove":
		// clean out the entry in the DirTreeMap for this folder
		delete(self.contents, pathName)

		updated_value, exists := self.contents[pathName]

		if self.watcher != nil {
			(*self.watcher).FolderDeleted(pathName)
		} else {
			fmt.Println("In the notify.Remove section but did not see a watcher")
		}

		fmt.Printf("notify.Remove: Updated  value for %s: %v (%s)\n", pathName, updated_value, exists)

	// todo fix this to handle the two rename events to be one event
	//case "notify.Rename":
	//	err = os.Remove(pathName)
	//	if err != nil && !os.IsNotExist(err) {
	//		panic(fmt.Sprintf("Error deleting folder that was renamed %s: %v\n", pathName, err))
	//	}
	//	fmt.Printf("notify.Rename: %s\n", pathName)
	default:
		fmt.Printf("%s: %s not known, skipping\n", event.Name, pathName)
	}

	currentValue, exists = self.contents[pathName]
	fmt.Printf("After: %s: Existing value for %s: %v (%v)\n", event.Name, pathName, currentValue, exists)

	// sendEvent to manager
	sendEvent(&event, globalSettings.ManagerAddress, globalSettings.ManagerCredentials)

	// todo - make this actually send to the peers
	log.Println("TODO Send to peers here")
}

// Scan the files and folders inside of the directory we are watching and add them to the contents. This function
// can only be called inside of a writelock on self.fsLock
func (self *FilesystemTracker) scanFolders() error {
	pendingPaths := make([]string, 0, 100)
	pendingPaths = append(pendingPaths, self.directory)
	self.contents = make(DirTreeMap)

	for len(pendingPaths) > 0 {
		currentPath := pendingPaths[0]
		fileList := make([]string, 0, 100)
		pendingPaths = pendingPaths[1:]

		// Read the directories in the path
		f, err := os.Open(currentPath)
		if err != nil {
			return err
		}

		dirEntries, err := f.Readdir(-1)
		for _, entry := range dirEntries {
			if entry.IsDir() {
				newDirectory := filepath.Join(currentPath, entry.Name())
				pendingPaths = append(pendingPaths, newDirectory)
			} else {
				fileList = append(fileList, entry.Name())
			}
		}

		f.Close()
		if err != nil {
			return err
		}

		sort.Strings(fileList)

		// Strip the base path off of the current path
		// make sure all of the paths are still '/' prefixed
		relativePath := currentPath[len(self.directory):]
		if relativePath == "" {
			relativePath = "."
		}

		self.contents[relativePath] = fileList
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/spf13/cobra"

	"arvika.pulcy.com/pulcy/droplets/providers"
)

const (
	defaultDomain         = "pulcy.com"
	defaultClusterImage   = "coreos-stable"
	defaultClusterRegion  = "ams3"
	defaultClusterSize    = "512mb"
	defaultInstanceCount  = 3
	defaultYardImage      = "pulcy/yard:0.4.5"
	sshKey                = "ewout@prangsma.net"
	defaultRebootStrategy = "etcd-lock"
)

var (
	cmdCreate = &cobra.Command{
		Use: "create",
		Run: showUsage,
	}
	cmdCreateCluster = &cobra.Command{
		Use: "cluster",
		Run: createCluster,
	}

	createClusterFlags providers.CreateClusterOptions
)

func init() {
	cmdCreateCluster.Flags().StringVar(&createClusterFlags.Domain, "domain", def("DROPLETS_DOMAIN", defaultDomain), "Cluster domain")
	cmdCreateCluster.Flags().StringVar(&createClusterFlags.Name, "name", "", "Cluster name")
	cmdCreateCluster.Flags().StringVar(&createClusterFlags.Image, "image", defaultClusterImage, "OS image to run on new droplets")
	cmdCreateCluster.Flags().StringVar(&createClusterFlags.Region, "region", defaultClusterRegion, "Region to create the droplets in")
	cmdCreateCluster.Flags().StringVar(&createClusterFlags.Size, "size", defaultClusterSize, "Size of the new droplet")
	cmdCreateCluster.Flags().IntVar(&createClusterFlags.InstanceCount, "instance-count", defaultInstanceCount, "Number of instances in cluster")
	cmdCreateCluster.Flags().StringVar(&createClusterFlags.YardImage, "yard-image", defaultYardImage, "Image containing encrypted yard")
	cmdCreateCluster.Flags().StringVar(&createClusterFlags.YardPassphrase, "yard-passphrase", def("YARD_PASSPHRASE", ""), "Passphrase used to decrypt yard.gpg")
	cmdCreateCluster.Flags().StringVar(&createClusterFlags.RebootStrategy, "reboot-strategy", defaultRebootStrategy, "CoreOS reboot strategy")
	cmdCreate.AddCommand(cmdCreateCluster)
	cmdMain.AddCommand(cmdCreate)
}

func createCluster(cmd *cobra.Command, args []string) {
	createClusterFlags.SSHKeyNames = []string{sshKey}
	provider := newProvider()

	// Validate
	if err := createClusterFlags.Validate(); err != nil {
		Exitf("Create failed: %s\n", err.Error())
	}

	// See if there are already instances for the given cluster
	instances, err := provider.GetInstances(&createClusterFlags.ClusterInfo)
	if err != nil {
		Exitf("Failed to query existing instances: %v\n", err)
	}
	if len(instances) > 0 {
		Exitf("Cluster %s.%s already exists.\n", createClusterFlags.Name, createClusterFlags.Domain)
	}

	// Create
	err = provider.CreateCluster(&createClusterFlags, newDnsProvider())
	if err != nil {
		Exitf("Failed to create new cluster: %v\n", err)
	}

	Infof("Cluster created\n")
}
<commit_msg>Using yard 0.4.6<commit_after>package main

import (
	"github.com/spf13/cobra"

	"arvika.pulcy.com/pulcy/droplets/providers"
)

const (
	defaultDomain         = "pulcy.com"
	defaultClusterImage   = "coreos-stable"
	defaultClusterRegion  = "ams3"
	defaultClusterSize    = "512mb"
	defaultInstanceCount  = 3
	defaultYardImage      = "pulcy/yard:0.4.6"
	sshKey                = "ewout@prangsma.net"
	defaultRebootStrategy = "etcd-lock"
)

var (
	cmdCreate = &cobra.Command{
		Use: "create",
		Run: showUsage,
	}
	cmdCreateCluster = &cobra.Command{
		Use: "cluster",
		Run: createCluster,
	}

	createClusterFlags providers.CreateClusterOptions
)

func init() {
	cmdCreateCluster.Flags().StringVar(&createClusterFlags.Domain, "domain", def("DROPLETS_DOMAIN", defaultDomain), "Cluster domain")
	cmdCreateCluster.Flags().StringVar(&createClusterFlags.Name, "name", "", "Cluster name")
	cmdCreateCluster.Flags().StringVar(&createClusterFlags.Image, "image", defaultClusterImage, "OS image to run on new droplets")
	cmdCreateCluster.Flags().StringVar(&createClusterFlags.Region, "region", defaultClusterRegion, "Region to create the droplets in")
	cmdCreateCluster.Flags().StringVar(&createClusterFlags.Size, "size", defaultClusterSize, "Size of the new droplet")
	cmdCreateCluster.Flags().IntVar(&createClusterFlags.InstanceCount, "instance-count", defaultInstanceCount, "Number of instances in cluster")
	cmdCreateCluster.Flags().StringVar(&createClusterFlags.YardImage, "yard-image", defaultYardImage, "Image containing encrypted yard")
	cmdCreateCluster.Flags().StringVar(&createClusterFlags.YardPassphrase, "yard-passphrase", def("YARD_PASSPHRASE", ""), "Passphrase used to decrypt yard.gpg")
	cmdCreateCluster.Flags().StringVar(&createClusterFlags.RebootStrategy, "reboot-strategy", defaultRebootStrategy, "CoreOS reboot strategy")
	cmdCreate.AddCommand(cmdCreateCluster)
	cmdMain.AddCommand(cmdCreate)
}

func createCluster(cmd *cobra.Command, args []string) {
	createClusterFlags.SSHKeyNames = []string{sshKey}
	provider := newProvider()

	// Validate
	if err := createClusterFlags.Validate(); err != nil {
		Exitf("Create failed: %s\n", err.Error())
	}

	// See if there are already instances for the given cluster
	instances, err := provider.GetInstances(&createClusterFlags.ClusterInfo)
	if err != nil {
		Exitf("Failed to query existing instances: %v\n", err)
	}
	if len(instances) > 0 {
		Exitf("Cluster %s.%s already exists.\n", createClusterFlags.Name, createClusterFlags.Domain)
	}

	// Create
	err = provider.CreateCluster(&createClusterFlags, newDnsProvider())
	if err != nil {
		Exitf("Failed to create new cluster: %v\n", err)
	}

	Infof("Cluster created\n")
}
<|endoftext|>


<commit_before>package sdk

const VERSION = "v1.14.5"
<commit_msg>Incremented version.<commit_after>package sdk

const VERSION = "v1.14.9"
<|endoftext|>


<commit_before>package baloo

// Version defines the current semantic version.
const Version = "0.1.0"
<commit_msg>feat(version): bump<commit_after>package baloo

// Version defines the current semantic version.
const Version = "0.1.1"
<|endoftext|>


<commit_before>// Copyright (c) 2013-2017 The btcsuite developers
// Copyright (c) 2015-2016 The Decred developers
// Heavily inspired by https://github.com/btcsuite/btcd/blob/master/version.go
// Copyright (C) 2015-2017 The Lightning Network Developers

package main

import (
	"bytes"
	"fmt"
	"strings"
)

// semanticAlphabet
const semanticAlphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-"

// These constants define the application version and follow the semantic
// versioning 2.0.0 spec (http://semver.org/).
const (
	appMajor uint = 0
	appMinor uint = 4
	appPatch uint = 0

	// appPreRelease MUST only contain characters from semanticAlphabet
	// per the semantic versioning spec.
	appPreRelease = "beta"
)

// appBuild is defined as a variable so it can be overridden during the build
// process with '-ldflags "-X main.appBuild foo' if needed.  It MUST only
// contain characters from semanticAlphabet per the semantic versioning spec.
var appBuild string

// version returns the application version as a properly formed string per the
// semantic versioning 2.0.0 spec (http://semver.org/).
func version() string {
	// Start with the major, minor, and patch versions.
	version := fmt.Sprintf("%d.%d.%d", appMajor, appMinor, appPatch)

	// Append pre-release version if there is one.  The hyphen called for
	// by the semantic versioning spec is automatically appended and should
	// not be contained in the pre-release string.  The pre-release version
	// is not appended if it contains invalid characters.
	preRelease := normalizeVerString(appPreRelease)
	if preRelease != "" {
		version = fmt.Sprintf("%s-%s", version, preRelease)
	}

	// Append build metadata if there is any.  The plus called for
	// by the semantic versioning spec is automatically appended and should
	// not be contained in the build metadata string.  The build metadata
	// string is not appended if it contains invalid characters.
	build := normalizeVerString(appBuild)
	if build != "" {
		version = fmt.Sprintf("%s+%s", version, build)
	}

	return version
}

// normalizeVerString returns the passed string stripped of all characters which
// are not valid according to the semantic versioning guidelines for pre-release
// version and build metadata strings.  In particular they MUST only contain
// characters in semanticAlphabet.
func normalizeVerString(str string) string {
	var result bytes.Buffer
	for _, r := range str {
		if strings.ContainsRune(semanticAlphabet, r) {
			result.WriteRune(r)
		}
	}
	return result.String()
}
<commit_msg>version: append commit hash to version string<commit_after>// Copyright (c) 2013-2017 The btcsuite developers
// Copyright (c) 2015-2016 The Decred developers
// Heavily inspired by https://github.com/btcsuite/btcd/blob/master/version.go
// Copyright (C) 2015-2017 The Lightning Network Developers

package main

import (
	"bytes"
	"fmt"
	"strings"
)

// semanticAlphabet
const semanticAlphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-"

// These constants define the application version and follow the semantic
// versioning 2.0.0 spec (http://semver.org/).
const (
	appMajor uint = 0
	appMinor uint = 4
	appPatch uint = 0

	// appPreRelease MUST only contain characters from semanticAlphabet
	// per the semantic versioning spec.
	appPreRelease = "beta"
)

// appBuild is defined as a variable so it can be overridden during the build
// process with '-ldflags "-X main.appBuild foo' if needed.  It MUST only
// contain characters from semanticAlphabet per the semantic versioning spec.
var appBuild string

// version returns the application version as a properly formed string per the
// semantic versioning 2.0.0 spec (http://semver.org/).
func version() string {
	// Start with the major, minor, and patch versions.
	version := fmt.Sprintf("%d.%d.%d", appMajor, appMinor, appPatch)

	// Append pre-release version if there is one.  The hyphen called for
	// by the semantic versioning spec is automatically appended and should
	// not be contained in the pre-release string.  The pre-release version
	// is not appended if it contains invalid characters.
	preRelease := normalizeVerString(appPreRelease)
	if preRelease != "" {
		version = fmt.Sprintf("%s-%s", version, preRelease)
	}

	// Append build metadata if there is any.  The plus called for
	// by the semantic versioning spec is automatically appended and should
	// not be contained in the build metadata string.  The build metadata
	// string is not appended if it contains invalid characters.
	build := normalizeVerString(appBuild)
	if build != "" {
		version = fmt.Sprintf("%s+%s", version, build)
	}

	// Append commit hash of current build to version.
	version = fmt.Sprintf("%s commit=%s", version, Commit)

	return version
}

// normalizeVerString returns the passed string stripped of all characters which
// are not valid according to the semantic versioning guidelines for pre-release
// version and build metadata strings.  In particular they MUST only contain
// characters in semanticAlphabet.
func normalizeVerString(str string) string {
	var result bytes.Buffer
	for _, r := range str {
		if strings.ContainsRune(semanticAlphabet, r) {
			result.WriteRune(r)
		}
	}
	return result.String()
}
<|endoftext|>


<commit_before>package main

import "fmt"

type version struct {
	Major, Minor, Patch int
	Label               string
	Nick                string
}

var ver = version{
	Major: 0,
	Minor: 3,
	Patch: 1,
	Label: "beta"}

// CommitHash may be set on the build command line:
// go build -ldflags "-X main.CommitHash=`git rev-parse --short HEAD`"
var CommitHash string

const appName string = "dcrdata"

func (v *version) String() string {
	var hashStr string
	if CommitHash != "" {
		hashStr = "+" + CommitHash
	}
	if v.Label != "" {
		return fmt.Sprintf("%d.%d.%d-%s%s",
			v.Major, v.Minor, v.Patch, v.Label, hashStr)
	}
	return fmt.Sprintf("%d.%d.%d%s",
		v.Major, v.Minor, v.Patch, hashStr)
}
<commit_msg>bump version<commit_after>package main

import "fmt"

type version struct {
	Major, Minor, Patch int
	Label               string
	Nick                string
}

var ver = version{
	Major: 0,
	Minor: 3,
	Patch: 2,
	Label: "beta"}

// CommitHash may be set on the build command line:
// go build -ldflags "-X main.CommitHash=`git rev-parse --short HEAD`"
var CommitHash string

const appName string = "dcrdata"

func (v *version) String() string {
	var hashStr string
	if CommitHash != "" {
		hashStr = "+" + CommitHash
	}
	if v.Label != "" {
		return fmt.Sprintf("%d.%d.%d-%s%s",
			v.Major, v.Minor, v.Patch, v.Label, hashStr)
	}
	return fmt.Sprintf("%d.%d.%d%s",
		v.Major, v.Minor, v.Patch, hashStr)
}
<|endoftext|>