<commit_before>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package portforward

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"reflect"
	"sync"
	"testing"
	"time"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/client"
	"k8s.io/kubernetes/pkg/util/httpstream"
)

func TestParsePortsAndNew(t *testing.T) {
	tests := []struct {
		input            []string
		expected         []ForwardedPort
		expectParseError bool
		expectNewError   bool
	}{
		{input: []string{}, expectNewError: true},
		{input: []string{"a"}, expectParseError: true, expectNewError: true},
		{input: []string{":a"}, expectParseError: true, expectNewError: true},
		{input: []string{"-1"}, expectParseError: true, expectNewError: true},
		{input: []string{"65536"}, expectParseError: true, expectNewError: true},
		{input: []string{"0"}, expectParseError: true, expectNewError: true},
		{input: []string{"0:0"}, expectParseError: true, expectNewError: true},
		{input: []string{"a:5000"}, expectParseError: true, expectNewError: true},
		{input: []string{"5000:a"}, expectParseError: true, expectNewError: true},
		{
			input: []string{"5000", "5000:5000", "8888:5000", "5000:8888", ":5000", "0:5000"},
			expected: []ForwardedPort{
				{5000, 5000},
				{5000, 5000},
				{8888, 5000},
				{5000, 8888},
				{0, 5000},
				{0, 5000},
			},
		},
	}

	for i, test := range tests {
		parsed, err := parsePorts(test.input)
		haveError := err != nil
		if e, a := test.expectParseError, haveError; e != a {
			t.Fatalf("%d: parsePorts: error expected=%t, got %t: %s", i, e, a, err)
		}

		expectedRequest := &client.Request{}
		expectedConfig := &client.Config{}
		expectedStopChan := make(chan struct{})
		pf, err := New(expectedRequest, expectedConfig, test.input, expectedStopChan)
		haveError = err != nil
		if e, a := test.expectNewError, haveError; e != a {
			t.Fatalf("%d: New: error expected=%t, got %t: %s", i, e, a, err)
		}

		if test.expectParseError || test.expectNewError {
			continue
		}

		for pi, expectedPort := range test.expected {
			if e, a := expectedPort.Local, parsed[pi].Local; e != a {
				t.Fatalf("%d: local expected: %d, got: %d", i, e, a)
			}
			if e, a := expectedPort.Remote, parsed[pi].Remote; e != a {
				t.Fatalf("%d: remote expected: %d, got: %d", i, e, a)
			}
		}

		if e, a := expectedRequest, pf.req; e != a {
			t.Fatalf("%d: req: expected %#v, got %#v", i, e, a)
		}
		if e, a := expectedConfig, pf.config; e != a {
			t.Fatalf("%d: config: expected %#v, got %#v", i, e, a)
		}
		if e, a := test.expected, pf.ports; !reflect.DeepEqual(e, a) {
			t.Fatalf("%d: ports: expected %#v, got %#v", i, e, a)
		}
		if e, a := expectedStopChan, pf.stopChan; e != a {
			t.Fatalf("%d: stopChan: expected %#v, got %#v", i, e, a)
		}
		if pf.Ready == nil {
			t.Fatalf("%d: Ready should be non-nil", i)
		}
	}
}

type fakeUpgrader struct {
	conn *fakeUpgradeConnection
	err  error
}

func (u *fakeUpgrader) upgrade(req *client.Request, config *client.Config) (httpstream.Connection, error) {
	return u.conn, u.err
}

type fakeUpgradeConnection struct {
	closeCalled bool
	lock        sync.Mutex
	streams     map[string]*fakeUpgradeStream
	portData    map[string]string
}

func newFakeUpgradeConnection() *fakeUpgradeConnection {
	return &fakeUpgradeConnection{
		streams:  make(map[string]*fakeUpgradeStream),
		portData: make(map[string]string),
	}
}

func (c *fakeUpgradeConnection) CreateStream(headers http.Header) (httpstream.Stream, error) {
	c.lock.Lock()
	defer c.lock.Unlock()

	stream := &fakeUpgradeStream{}
	c.streams[headers.Get(api.PortHeader)] = stream
	// only simulate data on the data stream for now, not the error stream
	if headers.Get(api.StreamType) == api.StreamTypeData {
		stream.data = c.portData[headers.Get(api.PortHeader)]
	}

	return stream, nil
}

func (c *fakeUpgradeConnection) Close() error {
	c.lock.Lock()
	defer c.lock.Unlock()

	c.closeCalled = true
	return nil
}

func (c *fakeUpgradeConnection) CloseChan() <-chan bool {
	return make(chan bool)
}

func (c *fakeUpgradeConnection) SetIdleTimeout(timeout time.Duration) {
}

type fakeUpgradeStream struct {
	readCalled  bool
	writeCalled bool
	dataWritten []byte
	closeCalled bool
	resetCalled bool
	data        string
	lock        sync.Mutex
}

func (s *fakeUpgradeStream) Read(p []byte) (int, error) {
	s.lock.Lock()
	defer s.lock.Unlock()
	s.readCalled = true
	b := []byte(s.data)
	n := copy(p, b)
	return n, io.EOF
}

func (s *fakeUpgradeStream) Write(p []byte) (int, error) {
	s.lock.Lock()
	defer s.lock.Unlock()
	s.writeCalled = true
	s.dataWritten = make([]byte, len(p))
	copy(s.dataWritten, p)
	return len(p), io.EOF
}

func (s *fakeUpgradeStream) Close() error {
	s.lock.Lock()
	defer s.lock.Unlock()
	s.closeCalled = true
	return nil
}

func (s *fakeUpgradeStream) Reset() error {
	s.lock.Lock()
	defer s.lock.Unlock()
	s.resetCalled = true
	return nil
}

func (s *fakeUpgradeStream) Headers() http.Header {
	s.lock.Lock()
	defer s.lock.Unlock()
	return http.Header{}
}

type GetListenerTestCase struct {
	Hostname                string
	Protocol                string
	ShouldRaiseError        bool
	ExpectedListenerAddress string
}

func TestGetListener(t *testing.T) {
	var pf PortForwarder
	testCases := []GetListenerTestCase{
		{
			Hostname:                "localhost",
			Protocol:                "tcp4",
			ShouldRaiseError:        false,
			ExpectedListenerAddress: "127.0.0.1",
		},
		{
			Hostname:                "127.0.0.1",
			Protocol:                "tcp4",
			ShouldRaiseError:        false,
			ExpectedListenerAddress: "127.0.0.1",
		},
		{
			Hostname:                "[::1]",
			Protocol:                "tcp6",
			ShouldRaiseError:        false,
			ExpectedListenerAddress: "::1",
		},
		{
			Hostname:         "[::1]",
			Protocol:         "tcp4",
			ShouldRaiseError: true,
		},
		{
			Hostname:         "127.0.0.1",
			Protocol:         "tcp6",
			ShouldRaiseError: true,
		},
		{
			// IPv6 address must be put into brackets. This test reveals this.
			Hostname:         "::1",
			Protocol:         "tcp6",
			ShouldRaiseError: true,
		},
	}

	for i, testCase := range testCases {
		expectedListenerPort := "12345"
		listener, err := pf.getListener(testCase.Protocol, testCase.Hostname, &ForwardedPort{12345, 12345})
		errorRaised := err != nil

		if testCase.ShouldRaiseError != errorRaised {
			t.Errorf("Test case #%d failed: Data %v an error has been raised(%t) where it should not (or reciprocally): %v", i, testCase, testCase.ShouldRaiseError, err)
			continue
		}
		if errorRaised {
			continue
		}

		if listener == nil {
			t.Errorf("Test case #%d did not raised an error (%t) but failed in initializing listener", i, err)
			continue
		}

		host, port, _ := net.SplitHostPort(listener.Addr().String())
		t.Logf("Asked a %s forward for: %s:%v, got listener %s:%s, expected: %s", testCase.Protocol, testCase.Hostname, 12345, host, port, expectedListenerPort)
		if host != testCase.ExpectedListenerAddress {
			t.Errorf("Test case #%d failed: Listener does not listen on exepected address: asked %v got %v", i, testCase.ExpectedListenerAddress, host)
		}
		if port != expectedListenerPort {
			t.Errorf("Test case #%d failed: Listener does not listen on exepected port: asked %v got %v", i, expectedListenerPort, port)

		}
		listener.Close()

	}
}

func TestForwardPorts(t *testing.T) {
	testCases := []struct {
		Upgrader *fakeUpgrader
		Ports    []string
		Send     map[uint16]string
		Receive  map[uint16]string
		Err      bool
	}{
		{
			Upgrader: &fakeUpgrader{err: errors.New("bail")},
			Err:      true,
		},
		{
			Upgrader: &fakeUpgrader{conn: newFakeUpgradeConnection()},
			Ports:    []string{"5000"},
		},
		{
			Upgrader: &fakeUpgrader{conn: newFakeUpgradeConnection()},
			Ports:    []string{"5001", "6000"},
			Send: map[uint16]string{
				5001: "abcd",
				6000: "ghij",
			},
			Receive: map[uint16]string{
				5001: "1234",
				6000: "5678",
			},
		},
	}

	for i, testCase := range testCases {
		stopChan := make(chan struct{}, 1)

		pf, err := New(&client.Request{}, &client.Config{}, testCase.Ports, stopChan)
		hasErr := err != nil
		if hasErr != testCase.Err {
			t.Fatalf("%d: New: expected %t, got %t: %v", i, testCase.Err, hasErr, err)
		}
		if pf == nil {
			continue
		}
		pf.upgrader = testCase.Upgrader
		if testCase.Upgrader.err != nil {
			err := pf.ForwardPorts()
			hasErr := err != nil
			if hasErr != testCase.Err {
				t.Fatalf("%d: ForwardPorts: expected %t, got %t: %v", i, testCase.Err, hasErr, err)
			}
			continue
		}

		doneChan := make(chan error)
		go func() {
			doneChan <- pf.ForwardPorts()
		}()
		<-pf.Ready

		conn := testCase.Upgrader.conn

		for port, data := range testCase.Send {
			conn.lock.Lock()
			conn.portData[fmt.Sprintf("%d", port)] = testCase.Receive[port]
			conn.lock.Unlock()

			clientConn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port))
			if err != nil {
				t.Fatalf("%d: error dialing %d: %s", i, port, err)
			}
			defer clientConn.Close()

			n, err := clientConn.Write([]byte(data))
			if err != nil && err != io.EOF {
				t.Fatalf("%d: Error sending data '%s': %s", i, data, err)
			}
			if n == 0 {
				t.Fatalf("%d: unexpected write of 0 bytes", i)
			}
			b := make([]byte, 4)
			n, err = clientConn.Read(b)
			if err != nil && err != io.EOF {
				t.Fatalf("%d: Error reading data: %s", i, err)
			}
			if !bytes.Equal([]byte(testCase.Receive[port]), b) {
				t.Fatalf("%d: expected to read '%s', got '%s'", i, testCase.Receive[port], b)
			}
		}

		// tell r.ForwardPorts to stop
		close(stopChan)

		// wait for r.ForwardPorts to actually return
		err = <-doneChan
		if err != nil {
			t.Fatalf("%d: unexpected error: %s", i, err)
		}

		if e, a := len(testCase.Send), len(conn.streams); e != a {
			t.Fatalf("%d: expected %d streams to be created, got %d", i, e, a)
		}

		if !conn.closeCalled {
			t.Fatalf("%d: expected conn closure", i)
		}
	}

}

func TestForwardPortsReturnsErrorWhenAllBindsFailed(t *testing.T) {
	stopChan1 := make(chan struct{}, 1)
	defer close(stopChan1)

	pf1, err := New(&client.Request{}, &client.Config{}, []string{"5555"}, stopChan1)
	if err != nil {
		t.Fatalf("error creating pf1: %v", err)
	}
	pf1.upgrader = &fakeUpgrader{conn: newFakeUpgradeConnection()}
	go pf1.ForwardPorts()
	<-pf1.Ready

	stopChan2 := make(chan struct{}, 1)
	pf2, err := New(&client.Request{}, &client.Config{}, []string{"5555"}, stopChan2)
	if err != nil {
		t.Fatalf("error creating pf2: %v", err)
	}
	pf2.upgrader = &fakeUpgrader{conn: newFakeUpgradeConnection()}
	if err := pf2.ForwardPorts(); err == nil {
		t.Fatal("expected non-nil error for pf2.ForwardPorts")
	}
}
<commit_msg>UPSTREAM: 13107: Fix portforward test flake with GOMAXPROCS > 1<commit_after>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package portforward

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"reflect"
	"sync"
	"testing"
	"time"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/client"
	"k8s.io/kubernetes/pkg/util/httpstream"
)

func TestParsePortsAndNew(t *testing.T) {
	tests := []struct {
		input            []string
		expected         []ForwardedPort
		expectParseError bool
		expectNewError   bool
	}{
		{input: []string{}, expectNewError: true},
		{input: []string{"a"}, expectParseError: true, expectNewError: true},
		{input: []string{":a"}, expectParseError: true, expectNewError: true},
		{input: []string{"-1"}, expectParseError: true, expectNewError: true},
		{input: []string{"65536"}, expectParseError: true, expectNewError: true},
		{input: []string{"0"}, expectParseError: true, expectNewError: true},
		{input: []string{"0:0"}, expectParseError: true, expectNewError: true},
		{input: []string{"a:5000"}, expectParseError: true, expectNewError: true},
		{input: []string{"5000:a"}, expectParseError: true, expectNewError: true},
		{
			input: []string{"5000", "5000:5000", "8888:5000", "5000:8888", ":5000", "0:5000"},
			expected: []ForwardedPort{
				{5000, 5000},
				{5000, 5000},
				{8888, 5000},
				{5000, 8888},
				{0, 5000},
				{0, 5000},
			},
		},
	}

	for i, test := range tests {
		parsed, err := parsePorts(test.input)
		haveError := err != nil
		if e, a := test.expectParseError, haveError; e != a {
			t.Fatalf("%d: parsePorts: error expected=%t, got %t: %s", i, e, a, err)
		}

		expectedRequest := &client.Request{}
		expectedConfig := &client.Config{}
		expectedStopChan := make(chan struct{})
		pf, err := New(expectedRequest, expectedConfig, test.input, expectedStopChan)
		haveError = err != nil
		if e, a := test.expectNewError, haveError; e != a {
			t.Fatalf("%d: New: error expected=%t, got %t: %s", i, e, a, err)
		}

		if test.expectParseError || test.expectNewError {
			continue
		}

		for pi, expectedPort := range test.expected {
			if e, a := expectedPort.Local, parsed[pi].Local; e != a {
				t.Fatalf("%d: local expected: %d, got: %d", i, e, a)
			}
			if e, a := expectedPort.Remote, parsed[pi].Remote; e != a {
				t.Fatalf("%d: remote expected: %d, got: %d", i, e, a)
			}
		}

		if e, a := expectedRequest, pf.req; e != a {
			t.Fatalf("%d: req: expected %#v, got %#v", i, e, a)
		}
		if e, a := expectedConfig, pf.config; e != a {
			t.Fatalf("%d: config: expected %#v, got %#v", i, e, a)
		}
		if e, a := test.expected, pf.ports; !reflect.DeepEqual(e, a) {
			t.Fatalf("%d: ports: expected %#v, got %#v", i, e, a)
		}
		if e, a := expectedStopChan, pf.stopChan; e != a {
			t.Fatalf("%d: stopChan: expected %#v, got %#v", i, e, a)
		}
		if pf.Ready == nil {
			t.Fatalf("%d: Ready should be non-nil", i)
		}
	}
}

type fakeUpgrader struct {
	conn *fakeUpgradeConnection
	err  error
}

func (u *fakeUpgrader) upgrade(req *client.Request, config *client.Config) (httpstream.Connection, error) {
	return u.conn, u.err
}

type fakeUpgradeConnection struct {
	closeCalled bool
	lock        sync.Mutex
	streams     map[string]*fakeUpgradeStream
	portData    map[string]string
}

func newFakeUpgradeConnection() *fakeUpgradeConnection {
	return &fakeUpgradeConnection{
		streams:  make(map[string]*fakeUpgradeStream),
		portData: make(map[string]string),
	}
}

func (c *fakeUpgradeConnection) CreateStream(headers http.Header) (httpstream.Stream, error) {
	c.lock.Lock()
	defer c.lock.Unlock()

	stream := &fakeUpgradeStream{}
	c.streams[headers.Get(api.PortHeader)] = stream
	// only simulate data on the data stream for now, not the error stream
	if headers.Get(api.StreamType) == api.StreamTypeData {
		stream.data = c.portData[headers.Get(api.PortHeader)]
	}

	return stream, nil
}

func (c *fakeUpgradeConnection) Close() error {
	c.lock.Lock()
	defer c.lock.Unlock()

	c.closeCalled = true
	return nil
}

func (c *fakeUpgradeConnection) CloseChan() <-chan bool {
	return make(chan bool)
}

func (c *fakeUpgradeConnection) SetIdleTimeout(timeout time.Duration) {
}

type fakeUpgradeStream struct {
	readCalled  bool
	writeCalled bool
	dataWritten []byte
	closeCalled bool
	resetCalled bool
	data        string
	lock        sync.Mutex
}

func (s *fakeUpgradeStream) Read(p []byte) (int, error) {
	s.lock.Lock()
	defer s.lock.Unlock()
	s.readCalled = true
	b := []byte(s.data)
	n := copy(p, b)
	// Indicate we returned all the data, and have no more data (EOF)
	// Returning an EOF here will cause the port forwarder to immediately terminate, which is correct when we have no more data to send
	return n, io.EOF
}

func (s *fakeUpgradeStream) Write(p []byte) (int, error) {
	s.lock.Lock()
	defer s.lock.Unlock()
	s.writeCalled = true
	s.dataWritten = append(s.dataWritten, p...)
	// Indicate the stream accepted all the data, and can accept more (no err)
	// Returning an EOF here will cause the port forwarder to immediately terminate, which is incorrect, in case someone writes more data
	return len(p), nil
}

func (s *fakeUpgradeStream) Close() error {
	s.lock.Lock()
	defer s.lock.Unlock()
	s.closeCalled = true
	return nil
}

func (s *fakeUpgradeStream) Reset() error {
	s.lock.Lock()
	defer s.lock.Unlock()
	s.resetCalled = true
	return nil
}

func (s *fakeUpgradeStream) Headers() http.Header {
	s.lock.Lock()
	defer s.lock.Unlock()
	return http.Header{}
}

type GetListenerTestCase struct {
	Hostname                string
	Protocol                string
	ShouldRaiseError        bool
	ExpectedListenerAddress string
}

func TestGetListener(t *testing.T) {
	var pf PortForwarder
	testCases := []GetListenerTestCase{
		{
			Hostname:                "localhost",
			Protocol:                "tcp4",
			ShouldRaiseError:        false,
			ExpectedListenerAddress: "127.0.0.1",
		},
		{
			Hostname:                "127.0.0.1",
			Protocol:                "tcp4",
			ShouldRaiseError:        false,
			ExpectedListenerAddress: "127.0.0.1",
		},
		{
			Hostname:                "[::1]",
			Protocol:                "tcp6",
			ShouldRaiseError:        false,
			ExpectedListenerAddress: "::1",
		},
		{
			Hostname:         "[::1]",
			Protocol:         "tcp4",
			ShouldRaiseError: true,
		},
		{
			Hostname:         "127.0.0.1",
			Protocol:         "tcp6",
			ShouldRaiseError: true,
		},
		{
			// IPv6 address must be put into brackets. This test reveals this.
			Hostname:         "::1",
			Protocol:         "tcp6",
			ShouldRaiseError: true,
		},
	}

	for i, testCase := range testCases {
		expectedListenerPort := "12345"
		listener, err := pf.getListener(testCase.Protocol, testCase.Hostname, &ForwardedPort{12345, 12345})
		errorRaised := err != nil

		if testCase.ShouldRaiseError != errorRaised {
			t.Errorf("Test case #%d failed: Data %v an error has been raised(%t) where it should not (or reciprocally): %v", i, testCase, testCase.ShouldRaiseError, err)
			continue
		}
		if errorRaised {
			continue
		}

		if listener == nil {
			t.Errorf("Test case #%d did not raised an error (%t) but failed in initializing listener", i, err)
			continue
		}

		host, port, _ := net.SplitHostPort(listener.Addr().String())
		t.Logf("Asked a %s forward for: %s:%v, got listener %s:%s, expected: %s", testCase.Protocol, testCase.Hostname, 12345, host, port, expectedListenerPort)
		if host != testCase.ExpectedListenerAddress {
			t.Errorf("Test case #%d failed: Listener does not listen on exepected address: asked %v got %v", i, testCase.ExpectedListenerAddress, host)
		}
		if port != expectedListenerPort {
			t.Errorf("Test case #%d failed: Listener does not listen on exepected port: asked %v got %v", i, expectedListenerPort, port)

		}
		listener.Close()

	}
}

func TestForwardPorts(t *testing.T) {
	testCases := []struct {
		Upgrader *fakeUpgrader
		Ports    []string
		Send     map[uint16]string
		Receive  map[uint16]string
		Err      bool
	}{
		{
			Upgrader: &fakeUpgrader{err: errors.New("bail")},
			Err:      true,
		},
		{
			Upgrader: &fakeUpgrader{conn: newFakeUpgradeConnection()},
			Ports:    []string{"5000"},
		},
		{
			Upgrader: &fakeUpgrader{conn: newFakeUpgradeConnection()},
			Ports:    []string{"5001", "6000"},
			Send: map[uint16]string{
				5001: "abcd",
				6000: "ghij",
			},
			Receive: map[uint16]string{
				5001: "1234",
				6000: "5678",
			},
		},
	}

	for i, testCase := range testCases {
		stopChan := make(chan struct{}, 1)

		pf, err := New(&client.Request{}, &client.Config{}, testCase.Ports, stopChan)
		hasErr := err != nil
		if hasErr != testCase.Err {
			t.Fatalf("%d: New: expected %t, got %t: %v", i, testCase.Err, hasErr, err)
		}
		if pf == nil {
			continue
		}
		pf.upgrader = testCase.Upgrader
		if testCase.Upgrader.err != nil {
			err := pf.ForwardPorts()
			hasErr := err != nil
			if hasErr != testCase.Err {
				t.Fatalf("%d: ForwardPorts: expected %t, got %t: %v", i, testCase.Err, hasErr, err)
			}
			continue
		}

		doneChan := make(chan error)
		go func() {
			doneChan <- pf.ForwardPorts()
		}()
		<-pf.Ready

		conn := testCase.Upgrader.conn

		for port, data := range testCase.Send {
			conn.lock.Lock()
			conn.portData[fmt.Sprintf("%d", port)] = testCase.Receive[port]
			conn.lock.Unlock()

			clientConn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port))
			if err != nil {
				t.Fatalf("%d: error dialing %d: %s", i, port, err)
			}
			defer clientConn.Close()

			n, err := clientConn.Write([]byte(data))
			if err != nil && err != io.EOF {
				t.Fatalf("%d: Error sending data '%s': %s", i, data, err)
			}
			if n == 0 {
				t.Fatalf("%d: unexpected write of 0 bytes", i)
			}
			b := make([]byte, 4)
			n, err = clientConn.Read(b)
			if err != nil && err != io.EOF {
				t.Fatalf("%d: Error reading data: %s", i, err)
			}
			if !bytes.Equal([]byte(testCase.Receive[port]), b) {
				t.Fatalf("%d: expected to read '%s', got '%s'", i, testCase.Receive[port], b)
			}
		}

		// tell r.ForwardPorts to stop
		close(stopChan)

		// wait for r.ForwardPorts to actually return
		err = <-doneChan
		if err != nil {
			t.Fatalf("%d: unexpected error: %s", i, err)
		}

		if e, a := len(testCase.Send), len(conn.streams); e != a {
			t.Fatalf("%d: expected %d streams to be created, got %d", i, e, a)
		}

		if !conn.closeCalled {
			t.Fatalf("%d: expected conn closure", i)
		}
	}

}

func TestForwardPortsReturnsErrorWhenAllBindsFailed(t *testing.T) {
	stopChan1 := make(chan struct{}, 1)
	defer close(stopChan1)

	pf1, err := New(&client.Request{}, &client.Config{}, []string{"5555"}, stopChan1)
	if err != nil {
		t.Fatalf("error creating pf1: %v", err)
	}
	pf1.upgrader = &fakeUpgrader{conn: newFakeUpgradeConnection()}
	go pf1.ForwardPorts()
	<-pf1.Ready

	stopChan2 := make(chan struct{}, 1)
	pf2, err := New(&client.Request{}, &client.Config{}, []string{"5555"}, stopChan2)
	if err != nil {
		t.Fatalf("error creating pf2: %v", err)
	}
	pf2.upgrader = &fakeUpgrader{conn: newFakeUpgradeConnection()}
	if err := pf2.ForwardPorts(); err == nil {
		t.Fatal("expected non-nil error for pf2.ForwardPorts")
	}
}
<|endoftext|>


<commit_before>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package azure

import (
	"fmt"
	"io/ioutil"
	. "launchpad.net/gocheck"
	"launchpad.net/gwacl"
	"launchpad.net/juju-core/environs/config"
	"launchpad.net/juju-core/errors"
	"net/http"
	"strings"
)

type StorageSuite struct {
	ProviderSuite
}

var _ = Suite(new(StorageSuite))

func (StorageSuite) TestNewStorage(c *C) {
	attrs := makeAzureConfigMap(c)
	container := "test container name"
	accountName := "test account name"
	accountKey := "test account key"
	attrs["storage-container-name"] = container
	attrs["storage-account-name"] = accountName
	attrs["storage-account-key"] = accountKey
	provider := azureEnvironProvider{}
	config, err := config.New(attrs)
	c.Assert(err, IsNil)
	azureConfig, err := provider.newConfig(config)
	c.Assert(err, IsNil)
	environ := &azureEnviron{name: "azure", ecfg: azureConfig}
	storage := NewStorage(environ).(*azureStorage)

	c.Check(storage.storageContext.getContainer(), Equals, container)
	context, err := storage.getStorageContext()
	c.Assert(err, IsNil)
	c.Check(context.Key, Equals, accountKey)
	c.Check(context.Account, Equals, accountName)
}

// TestTransport is used as an http.Client.Transport for testing.  It records
// the latest request, and returns a predetermined Response and error.
type TestTransport struct {
	Request  *http.Request
	Response *http.Response
	Error    error
}

func (t *TestTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
	t.Request = req
	return t.Response, t.Error
}

func makeResponse(content string, status int) *http.Response {
	return &http.Response{
		Status:     fmt.Sprintf("%d", status),
		StatusCode: status,
		Body:       ioutil.NopCloser(strings.NewReader(content)),
	}
}

// testStorageContext is a struct implementing the storageContext interface
// used in test.  It will return, via getContainer() and getStorageContext()
// the objects used at creation time.
type testStorageContext struct {
	container      string
	storageContext *gwacl.StorageContext
}

func (context *testStorageContext) getContainer() string {
	return context.container
}

func (context *testStorageContext) getStorageContext() (*gwacl.StorageContext, error) {
	return context.storageContext, nil
}

// makeAzureStorage creates a test azureStorage object that will talk to a
// fake http server set up to always return the given http.Response object.
// makeAzureStorage returns an azureStorage object and a TestTransport object.
// The TestTransport object can be used to check that the expected query has
// been issued to the test server.
func makeAzureStorage(response *http.Response, container string) (azureStorage, *TestTransport) {
	transport := &TestTransport{Response: response}
	client := &http.Client{Transport: transport}
	context := &testStorageContext{container: container, storageContext: gwacl.NewTestStorageContext(client)}
	azStorage := azureStorage{context}
	return azStorage, transport
}

var blobListResponse = `
  <?xml version="1.0" encoding="utf-8"?>
  <EnumerationResults ContainerName="http://myaccount.blob.core.windows.net/mycontainer">
    <Prefix>prefix</Prefix>
    <Marker>marker</Marker>
    <MaxResults>maxresults</MaxResults>
    <Delimiter>delimiter</Delimiter>
    <Blobs>
      <Blob>
        <Name>prefix-1</Name>
        <Url>blob-url1</Url>
      </Blob>
      <Blob>
        <Name>prefix-2</Name>
        <Url>blob-url2</Url>
      </Blob>
    </Blobs>
    <NextMarker />
  </EnumerationResults>`

func (StorageSuite) TestList(c *C) {
	container := "container"
	response := makeResponse(blobListResponse, http.StatusOK)
	azStorage, transport := makeAzureStorage(response, container)
	prefix := "prefix"
	names, err := azStorage.List(prefix)
	c.Assert(err, IsNil)
	// The prefix has been passed down as a query parameter.
	c.Check(transport.Request.URL.Query()["prefix"], DeepEquals, []string{prefix})
	// The container name is used in the requested URL.
	c.Check(transport.Request.URL.String(), Matches, ".*"+container+".*")
	c.Check(names, DeepEquals, []string{"prefix-1", "prefix-2"})
}

func (StorageSuite) TestGet(c *C) {
	blobContent := "test blob"
	container := "container"
	filename := "blobname"
	response := makeResponse(blobContent, http.StatusOK)
	azStorage, transport := makeAzureStorage(response, container)
	reader, err := azStorage.Get(filename)
	c.Assert(err, IsNil)
	c.Assert(reader, NotNil)
	defer reader.Close()

	context, err := azStorage.getStorageContext()
	c.Assert(err, IsNil)
	c.Check(transport.Request.URL.String(), Matches, context.GetFileURL(container, filename)+"?.*")
	data, err := ioutil.ReadAll(reader)
	c.Assert(err, IsNil)
	c.Check(string(data), Equals, blobContent)
}

func (StorageSuite) TestGetReturnsNotFoundIf404(c *C) {
	container := "container"
	filename := "blobname"
	response := makeResponse("not found", http.StatusNotFound)
	azStorage, _ := makeAzureStorage(response, container)
	_, err := azStorage.Get(filename)
	c.Assert(err, NotNil)
	c.Check(errors.IsNotFoundError(err), Equals, true)
}

func (StorageSuite) TestPut(c *C) {
	blobContent := "test blob"
	container := "container"
	filename := "blobname"
	response := makeResponse("", http.StatusCreated)
	azStorage, transport := makeAzureStorage(response, container)
	err := azStorage.Put(filename, strings.NewReader(blobContent), 10)
	c.Assert(err, IsNil)

	context, err := azStorage.getStorageContext()
	c.Assert(err, IsNil)
	c.Check(transport.Request.URL.String(), Matches, context.GetFileURL(container, filename)+"?.*")
}

func (StorageSuite) TestRemove(c *C) {
	container := "container"
	filename := "blobname"
	response := makeResponse("", http.StatusAccepted)
	azStorage, transport := makeAzureStorage(response, container)
	err := azStorage.Remove(filename)
	c.Assert(err, IsNil)

	context, err := azStorage.getStorageContext()
	c.Assert(err, IsNil)
	c.Check(transport.Request.URL.String(), Matches, context.GetFileURL(container, filename)+"?.*")
	c.Check(transport.Request.Method, Equals, "DELETE")
}

func (StorageSuite) TestRemoveNonExistantBlobSucceeds(c *C) {
	container := "container"
	filename := "blobname"
	response := makeResponse("", http.StatusNotFound)
	azStorage, _ := makeAzureStorage(response, container)
	err := azStorage.Remove(filename)
	c.Assert(err, IsNil)
}
<commit_msg>Review fixes.<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package azure

import (
	"fmt"
	"io/ioutil"
	. "launchpad.net/gocheck"
	"launchpad.net/gwacl"
	"launchpad.net/juju-core/environs/config"
	"launchpad.net/juju-core/errors"
	"net/http"
	"strings"
)

type StorageSuite struct {
	ProviderSuite
}

var _ = Suite(new(StorageSuite))

func (StorageSuite) TestNewStorage(c *C) {
	attrs := makeAzureConfigMap(c)
	container := "test container name"
	accountName := "test account name"
	accountKey := "test account key"
	attrs["storage-container-name"] = container
	attrs["storage-account-name"] = accountName
	attrs["storage-account-key"] = accountKey
	provider := azureEnvironProvider{}
	config, err := config.New(attrs)
	c.Assert(err, IsNil)
	azureConfig, err := provider.newConfig(config)
	c.Assert(err, IsNil)
	environ := &azureEnviron{name: "azure", ecfg: azureConfig}
	storage := NewStorage(environ).(*azureStorage)

	c.Check(storage.storageContext.getContainer(), Equals, container)
	context, err := storage.getStorageContext()
	c.Assert(err, IsNil)
	c.Check(context.Key, Equals, accountKey)
	c.Check(context.Account, Equals, accountName)
}

// TestTransport is used as an http.Client.Transport for testing.  It records
// the latest request, and returns a predetermined Response and error.
type TestTransport struct {
	Request  *http.Request
	Response *http.Response
	Error    error
}

func (t *TestTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
	t.Request = req
	return t.Response, t.Error
}

func makeResponse(content string, status int) *http.Response {
	return &http.Response{
		Status:     fmt.Sprintf("%d", status),
		StatusCode: status,
		Body:       ioutil.NopCloser(strings.NewReader(content)),
	}
}

// testStorageContext is a struct implementing the storageContext interface
// used in test.  It will return, via getContainer() and getStorageContext()
// the objects used at creation time.
type testStorageContext struct {
	container      string
	storageContext *gwacl.StorageContext
}

func (context *testStorageContext) getContainer() string {
	return context.container
}

func (context *testStorageContext) getStorageContext() (*gwacl.StorageContext, error) {
	return context.storageContext, nil
}

// makeAzureStorage creates a test azureStorage object that will talk to a
// fake http server set up to always return the given http.Response object.
// makeAzureStorage returns an azureStorage object and a TestTransport object.
// The TestTransport object can be used to check that the expected query has
// been issued to the test server.
func makeAzureStorage(response *http.Response, container string) (azureStorage, *TestTransport) {
	transport := &TestTransport{Response: response}
	client := &http.Client{Transport: transport}
	context := &testStorageContext{container: container, storageContext: gwacl.NewTestStorageContext(client)}
	azStorage := azureStorage{context}
	return azStorage, transport
}

var blobListResponse = `
  <?xml version="1.0" encoding="utf-8"?>
  <EnumerationResults ContainerName="http://myaccount.blob.core.windows.net/mycontainer">
    <Prefix>prefix</Prefix>
    <Marker>marker</Marker>
    <MaxResults>maxresults</MaxResults>
    <Delimiter>delimiter</Delimiter>
    <Blobs>
      <Blob>
        <Name>prefix-1</Name>
        <Url>blob-url1</Url>
      </Blob>
      <Blob>
        <Name>prefix-2</Name>
        <Url>blob-url2</Url>
      </Blob>
    </Blobs>
    <NextMarker />
  </EnumerationResults>`

func (StorageSuite) TestList(c *C) {
	container := "container"
	response := makeResponse(blobListResponse, http.StatusOK)
	azStorage, transport := makeAzureStorage(response, container)
	prefix := "prefix"
	names, err := azStorage.List(prefix)
	c.Assert(err, IsNil)
	// The prefix has been passed down as a query parameter.
	c.Check(transport.Request.URL.Query()["prefix"], DeepEquals, []string{prefix})
	// The container name is used in the requested URL.
	c.Check(transport.Request.URL.String(), Matches, ".*"+container+".*")
	c.Check(names, DeepEquals, []string{"prefix-1", "prefix-2"})
}

func (StorageSuite) TestGet(c *C) {
	blobContent := "test blob"
	container := "container"
	filename := "blobname"
	response := makeResponse(blobContent, http.StatusOK)
	azStorage, transport := makeAzureStorage(response, container)
	reader, err := azStorage.Get(filename)
	c.Assert(err, IsNil)
	c.Assert(reader, NotNil)
	defer reader.Close()

	context, err := azStorage.getStorageContext()
	c.Assert(err, IsNil)
	c.Check(transport.Request.URL.String(), Matches, context.GetFileURL(container, filename)+"?.*")
	data, err := ioutil.ReadAll(reader)
	c.Assert(err, IsNil)
	c.Check(string(data), Equals, blobContent)
}

func (StorageSuite) TestGetReturnsNotFoundIf404(c *C) {
	container := "container"
	filename := "blobname"
	response := makeResponse("not found", http.StatusNotFound)
	azStorage, _ := makeAzureStorage(response, container)
	_, err := azStorage.Get(filename)
	c.Assert(err, NotNil)
	c.Check(errors.IsNotFoundError(err), Equals, true)
}

func (StorageSuite) TestPut(c *C) {
	blobContent := "test blob"
	container := "container"
	filename := "blobname"
	response := makeResponse("", http.StatusCreated)
	azStorage, transport := makeAzureStorage(response, container)
	err := azStorage.Put(filename, strings.NewReader(blobContent), int64(len(blobContent)))
	c.Assert(err, IsNil)

	context, err := azStorage.getStorageContext()
	c.Assert(err, IsNil)
	c.Check(transport.Request.URL.String(), Matches, context.GetFileURL(container, filename)+"?.*")
}

func (StorageSuite) TestRemove(c *C) {
	container := "container"
	filename := "blobname"
	response := makeResponse("", http.StatusAccepted)
	azStorage, transport := makeAzureStorage(response, container)
	err := azStorage.Remove(filename)
	c.Assert(err, IsNil)

	context, err := azStorage.getStorageContext()
	c.Assert(err, IsNil)
	c.Check(transport.Request.URL.String(), Matches, context.GetFileURL(container, filename)+"?.*")
	c.Check(transport.Request.Method, Equals, "DELETE")
}

func (StorageSuite) TestRemoveErrors(c *C) {
	container := "container"
	filename := "blobname"
	response := makeResponse("", http.StatusForbidden)
	azStorage, _ := makeAzureStorage(response, container)
	err := azStorage.Remove(filename)
	c.Assert(err, NotNil)
}

func (StorageSuite) TestRemoveNonExistantBlobSucceeds(c *C) {
	container := "container"
	filename := "blobname"
	response := makeResponse("", http.StatusNotFound)
	azStorage, _ := makeAzureStorage(response, container)
	err := azStorage.Remove(filename)
	c.Assert(err, IsNil)
}
<|endoftext|>


<commit_before>// Copyright Istio 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 k8s

import (
	"context"
	"fmt"
	"testing"
	"time"

	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/informers"
	informersv1 "k8s.io/client-go/informers/core/v1"
	"k8s.io/client-go/kubernetes/fake"
	ktesting "k8s.io/client-go/testing"

	"istio.io/istio/pkg/config/constants"
	"istio.io/istio/pkg/kube"
)

const (
	configMapName = "test-configmap-name"
	namespaceName = "test-ns"
	dataName      = "test-data-name"
)

func TestUpdateDataInConfigMap(t *testing.T) {
	gvr := schema.GroupVersionResource{
		Resource: "configmaps",
		Version:  "v1",
	}
	testMeta := metav1.ObjectMeta{Namespace: namespaceName, Name: configMapName}
	caBundle := "test-data"
	testData := map[string]string{
		constants.CACertNamespaceConfigMapDataName: "test-data",
	}
	testCases := []struct {
		name              string
		existingConfigMap *v1.ConfigMap
		expectedActions   []ktesting.Action
		expectedErr       string
	}{
		{
			name:        "non-existing ConfigMap",
			expectedErr: "cannot update nil configmap",
		},
		{
			name:              "existing empty ConfigMap",
			existingConfigMap: createConfigMap(namespaceName, configMapName, map[string]string{}),
			expectedActions: []ktesting.Action{
				ktesting.NewUpdateAction(gvr, namespaceName, createConfigMap(namespaceName, configMapName, testData)),
			},
			expectedErr: "",
		},
		{
			name:              "existing nop ConfigMap",
			existingConfigMap: createConfigMap(namespaceName, configMapName, testData),
			expectedActions:   []ktesting.Action{},
			expectedErr:       "",
		},
		{
			name:              "existing with other keys",
			existingConfigMap: createConfigMap(namespaceName, configMapName, map[string]string{"foo": "bar"}),
			expectedActions: []ktesting.Action{
				ktesting.NewUpdateAction(gvr, namespaceName, createConfigMap(namespaceName, configMapName,
					map[string]string{"test-key": "test-data", "foo": "bar"})),
			},
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			client := fake.NewSimpleClientset()
			if tc.existingConfigMap != nil {
				if _, err := client.CoreV1().ConfigMaps(testMeta.Namespace).Create(context.TODO(), tc.existingConfigMap, metav1.CreateOptions{}); err != nil {
					t.Errorf("failed to create configmap %v", err)
				}
			}
			client.ClearActions()
			err := updateDataInConfigMap(client.CoreV1(), tc.existingConfigMap, []byte(caBundle))
			if err != nil && err.Error() != tc.expectedErr {
				t.Errorf("actual error (%s) different from expected error (%s).", err.Error(), tc.expectedErr)
			}
			if err == nil {
				if tc.expectedErr != "" {
					t.Errorf("expecting error %s but got no error", tc.expectedErr)
				} else if err := checkActions(client.Actions(), tc.expectedActions); err != nil {
					t.Error(err)
				}
			}
		})
	}
}

func TestInsertDataToConfigMap(t *testing.T) {
	gvr := schema.GroupVersionResource{
		Resource: "configmaps",
		Version:  "v1",
	}
	caBundle := []byte("test-data")
	testData := map[string]string{
		constants.CACertNamespaceConfigMapDataName: "test-data",
	}
	testCases := []struct {
		name              string
		meta              metav1.ObjectMeta
		existingConfigMap *v1.ConfigMap
		caBundle          []byte
		expectedActions   []ktesting.Action
		expectedErr       string
		client            *fake.Clientset
	}{
		{
			name:              "non-existing ConfigMap",
			existingConfigMap: nil,
			caBundle:          caBundle,
			meta:              metav1.ObjectMeta{Namespace: namespaceName, Name: configMapName},
			expectedActions: []ktesting.Action{
				ktesting.NewCreateAction(gvr, namespaceName, createConfigMap(namespaceName,
					configMapName, testData)),
			},
			expectedErr: "",
		},
		{
			name:              "existing ConfigMap",
			meta:              metav1.ObjectMeta{Namespace: namespaceName, Name: configMapName},
			existingConfigMap: createConfigMap(namespaceName, configMapName, map[string]string{}),
			caBundle:          caBundle,
			expectedActions: []ktesting.Action{
				ktesting.NewUpdateAction(gvr, namespaceName, createConfigMap(namespaceName, configMapName, testData)),
			},
			expectedErr: "",
		},
		{
			name:              "creation failure for ConfigMap",
			existingConfigMap: nil,
			caBundle:          caBundle,
			meta:              metav1.ObjectMeta{Namespace: namespaceName, Name: configMapName},
			expectedActions: []ktesting.Action{
				ktesting.NewGetAction(gvr, namespaceName, configMapName),
				ktesting.NewGetAction(gvr, namespaceName, configMapName),
				ktesting.NewCreateAction(gvr, namespaceName, createConfigMap(namespaceName, configMapName,
					map[string]string{dataName: "test-data"})),
			},
			expectedErr: fmt.Sprintf("error when creating configmap %v: no permission to create configmap",
				configMapName),
			client: createConfigMapDisabledClient(),
		},
		{
			name:              "creation: concurrently created by other client",
			existingConfigMap: nil,
			caBundle:          caBundle,
			meta:              metav1.ObjectMeta{Namespace: namespaceName, Name: configMapName},
			expectedActions: []ktesting.Action{
				ktesting.NewCreateAction(gvr, namespaceName, createConfigMap(namespaceName, configMapName,
					map[string]string{dataName: "test-data"})),
			},
			expectedErr: "",
			client:      createConfigMapAlreadyExistClient(),
		},
		{
			name:              "creation: namespace is deleting",
			existingConfigMap: nil,
			caBundle:          caBundle,
			meta:              metav1.ObjectMeta{Namespace: namespaceName, Name: configMapName},
			expectedActions: []ktesting.Action{
				ktesting.NewCreateAction(gvr, namespaceName, createConfigMap(namespaceName, configMapName,
					map[string]string{dataName: "test-data"})),
			},
			expectedErr: "",
			client:      createConfigMapNamespaceDeletingClient(),
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			var client *fake.Clientset
			if tc.client == nil {
				client = fake.NewSimpleClientset()
			} else {
				client = tc.client
			}
			lister := createFakeLister(client)
			if tc.existingConfigMap != nil {
				if _, err := client.CoreV1().ConfigMaps(tc.meta.Namespace).Create(context.TODO(), tc.existingConfigMap, metav1.CreateOptions{}); err != nil {
					t.Errorf("failed to create configmap %v", err)
				}
				if err := lister.Informer().GetIndexer().Add(tc.existingConfigMap); err != nil {
					t.Errorf("failed to add configmap to informer %v", err)
				}
			}
			client.ClearActions()
			err := InsertDataToConfigMap(client.CoreV1(), lister.Lister(), tc.meta, tc.caBundle)
			if err != nil && err.Error() != tc.expectedErr {
				t.Errorf("actual error (%s) different from expected error (%s).", err.Error(), tc.expectedErr)
			}
			if err == nil {
				if tc.expectedErr != "" {
					t.Errorf("expecting error %s but got no error", tc.expectedErr)
				} else if err := checkActions(client.Actions(), tc.expectedActions); err != nil {
					t.Error(err)
				}
			}
		})
	}
}

func createConfigMapDisabledClient() *fake.Clientset {
	client := &fake.Clientset{}
	fakeWatch := watch.NewFake()
	client.AddWatchReactor("configmaps", ktesting.DefaultWatchReactor(fakeWatch, nil))
	client.AddReactor("get", "configmaps", func(action ktesting.Action) (bool, runtime.Object, error) {
		return true, &v1.ConfigMap{}, errors.NewNotFound(v1.Resource("configmaps"), configMapName)
	})
	client.AddReactor("create", "configmaps", func(action ktesting.Action) (bool, runtime.Object, error) {
		return true, &v1.ConfigMap{}, errors.NewUnauthorized("no permission to create configmap")
	})
	return client
}

func createConfigMapAlreadyExistClient() *fake.Clientset {
	client := &fake.Clientset{}
	fakeWatch := watch.NewFake()
	client.AddWatchReactor("configmaps", ktesting.DefaultWatchReactor(fakeWatch, nil))
	client.AddReactor("get", "configmaps", func(action ktesting.Action) (bool, runtime.Object, error) {
		return true, &v1.ConfigMap{}, errors.NewNotFound(v1.Resource("configmaps"), configMapName)
	})
	client.AddReactor("create", "configmaps", func(action ktesting.Action) (bool, runtime.Object, error) {
		return true, &v1.ConfigMap{}, errors.NewAlreadyExists(v1.Resource("configmaps"), configMapName)
	})
	return client
}

func createConfigMapNamespaceDeletingClient() *fake.Clientset {
	client := &fake.Clientset{}
	fakeWatch := watch.NewFake()
	client.AddWatchReactor("configmaps", ktesting.DefaultWatchReactor(fakeWatch, nil))
	client.AddReactor("get", "configmaps", func(action ktesting.Action) (bool, runtime.Object, error) {
		return true, &v1.ConfigMap{}, errors.NewNotFound(v1.Resource("configmaps"), configMapName)
	})

	err := errors.NewForbidden(v1.Resource("configmaps"), configMapName,
		fmt.Errorf("unable to create new content in namespace %s because it is being terminated", namespaceName))
	err.ErrStatus.Details.Causes = append(err.ErrStatus.Details.Causes, metav1.StatusCause{
		Type:    v1.NamespaceTerminatingCause,
		Message: fmt.Sprintf("namespace %s is being terminated", namespaceName),
		Field:   "metadata.namespace",
	})
	client.AddReactor("create", "configmaps", func(action ktesting.Action) (bool, runtime.Object, error) {
		return true, &v1.ConfigMap{}, err
	})
	return client
}

// nolint: unparam
func createConfigMap(namespace, configName string, data map[string]string) *v1.ConfigMap {
	return &v1.ConfigMap{
		ObjectMeta: metav1.ObjectMeta{
			Name:      configName,
			Namespace: namespace,
		},
		Data: data,
	}
}

func checkActions(actual, expected []ktesting.Action) error {
	if len(actual) != len(expected) {
		return fmt.Errorf("unexpected number of actions, want %d but got %d, %v", len(expected), len(actual), actual)
	}

	for i, action := range actual {
		expectedAction := expected[i]
		verb := expectedAction.GetVerb()
		resource := expectedAction.GetResource().Resource
		if !action.Matches(verb, resource) {
			return fmt.Errorf("unexpected %dth action, want \n%+v but got \n%+v", i, expectedAction, action)
		}
	}

	return nil
}

func createFakeLister(kubeClient *fake.Clientset) informersv1.ConfigMapInformer {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	informerFactory := informers.NewSharedInformerFactory(kubeClient, time.Second)
	configmapInformer := informerFactory.Core().V1().ConfigMaps().Informer()
	go configmapInformer.Run(ctx.Done())
	kube.WaitForCacheSync(ctx.Done(), configmapInformer.HasSynced)
	return informerFactory.Core().V1().ConfigMaps()
}
<commit_msg>improve test coverage (#39662)<commit_after>// Copyright Istio 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 k8s

import (
	"context"
	"fmt"
	"reflect"
	"testing"
	"time"

	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/informers"
	informersv1 "k8s.io/client-go/informers/core/v1"
	"k8s.io/client-go/kubernetes/fake"
	ktesting "k8s.io/client-go/testing"

	"istio.io/istio/pkg/config/constants"
	"istio.io/istio/pkg/kube"
)

const (
	configMapName = "test-configmap-name"
	namespaceName = "test-ns"
	dataName      = "test-data-name"
)

func TestUpdateDataInConfigMap(t *testing.T) {
	gvr := schema.GroupVersionResource{
		Resource: "configmaps",
		Version:  "v1",
	}
	testMeta := metav1.ObjectMeta{Namespace: namespaceName, Name: configMapName}
	caBundle := "test-data"
	testData := map[string]string{
		constants.CACertNamespaceConfigMapDataName: "test-data",
	}
	testCases := []struct {
		name              string
		existingConfigMap *v1.ConfigMap
		expectedActions   []ktesting.Action
		expectedErr       string
	}{
		{
			name:        "non-existing ConfigMap",
			expectedErr: "cannot update nil configmap",
		},
		{
			name:              "existing empty ConfigMap",
			existingConfigMap: createConfigMap(namespaceName, configMapName, map[string]string{}),
			expectedActions: []ktesting.Action{
				ktesting.NewUpdateAction(gvr, namespaceName, createConfigMap(namespaceName, configMapName, testData)),
			},
			expectedErr: "",
		},
		{
			name:              "existing nop ConfigMap",
			existingConfigMap: createConfigMap(namespaceName, configMapName, testData),
			expectedActions:   []ktesting.Action{},
			expectedErr:       "",
		},
		{
			name:              "existing with other keys",
			existingConfigMap: createConfigMap(namespaceName, configMapName, map[string]string{"foo": "bar"}),
			expectedActions: []ktesting.Action{
				ktesting.NewUpdateAction(gvr, namespaceName, createConfigMap(namespaceName, configMapName,
					map[string]string{"test-key": "test-data", "foo": "bar"})),
			},
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			client := fake.NewSimpleClientset()
			if tc.existingConfigMap != nil {
				if _, err := client.CoreV1().ConfigMaps(testMeta.Namespace).Create(context.TODO(), tc.existingConfigMap, metav1.CreateOptions{}); err != nil {
					t.Errorf("failed to create configmap %v", err)
				}
			}
			client.ClearActions()
			err := updateDataInConfigMap(client.CoreV1(), tc.existingConfigMap, []byte(caBundle))
			if err != nil && err.Error() != tc.expectedErr {
				t.Errorf("actual error (%s) different from expected error (%s).", err.Error(), tc.expectedErr)
			}
			if err == nil {
				if tc.expectedErr != "" {
					t.Errorf("expecting error %s but got no error", tc.expectedErr)
				} else if err := checkActions(client.Actions(), tc.expectedActions); err != nil {
					t.Error(err)
				}
			}
		})
	}
}

func TestInsertDataToConfigMap(t *testing.T) {
	gvr := schema.GroupVersionResource{
		Resource: "configmaps",
		Version:  "v1",
	}
	caBundle := []byte("test-data")
	testData := map[string]string{
		constants.CACertNamespaceConfigMapDataName: "test-data",
	}
	testCases := []struct {
		name              string
		meta              metav1.ObjectMeta
		existingConfigMap *v1.ConfigMap
		caBundle          []byte
		expectedActions   []ktesting.Action
		expectedErr       string
		client            *fake.Clientset
	}{
		{
			name:              "non-existing ConfigMap",
			existingConfigMap: nil,
			caBundle:          caBundle,
			meta:              metav1.ObjectMeta{Namespace: namespaceName, Name: configMapName},
			expectedActions: []ktesting.Action{
				ktesting.NewCreateAction(gvr, namespaceName, createConfigMap(namespaceName,
					configMapName, testData)),
			},
			expectedErr: "",
		},
		{
			name:              "existing ConfigMap",
			meta:              metav1.ObjectMeta{Namespace: namespaceName, Name: configMapName},
			existingConfigMap: createConfigMap(namespaceName, configMapName, map[string]string{}),
			caBundle:          caBundle,
			expectedActions: []ktesting.Action{
				ktesting.NewUpdateAction(gvr, namespaceName, createConfigMap(namespaceName, configMapName, testData)),
			},
			expectedErr: "",
		},
		{
			name:              "creation failure for ConfigMap",
			existingConfigMap: nil,
			caBundle:          caBundle,
			meta:              metav1.ObjectMeta{Namespace: namespaceName, Name: configMapName},
			expectedActions: []ktesting.Action{
				ktesting.NewGetAction(gvr, namespaceName, configMapName),
				ktesting.NewGetAction(gvr, namespaceName, configMapName),
				ktesting.NewCreateAction(gvr, namespaceName, createConfigMap(namespaceName, configMapName,
					map[string]string{dataName: "test-data"})),
			},
			expectedErr: fmt.Sprintf("error when creating configmap %v: no permission to create configmap",
				configMapName),
			client: createConfigMapDisabledClient(),
		},
		{
			name:              "creation: concurrently created by other client",
			existingConfigMap: nil,
			caBundle:          caBundle,
			meta:              metav1.ObjectMeta{Namespace: namespaceName, Name: configMapName},
			expectedActions: []ktesting.Action{
				ktesting.NewCreateAction(gvr, namespaceName, createConfigMap(namespaceName, configMapName,
					map[string]string{dataName: "test-data"})),
			},
			expectedErr: "",
			client:      createConfigMapAlreadyExistClient(),
		},
		{
			name:              "creation: namespace is deleting",
			existingConfigMap: nil,
			caBundle:          caBundle,
			meta:              metav1.ObjectMeta{Namespace: namespaceName, Name: configMapName},
			expectedActions: []ktesting.Action{
				ktesting.NewCreateAction(gvr, namespaceName, createConfigMap(namespaceName, configMapName,
					map[string]string{dataName: "test-data"})),
			},
			expectedErr: "",
			client:      createConfigMapNamespaceDeletingClient(),
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			var client *fake.Clientset
			if tc.client == nil {
				client = fake.NewSimpleClientset()
			} else {
				client = tc.client
			}
			lister := createFakeLister(client)
			if tc.existingConfigMap != nil {
				if _, err := client.CoreV1().ConfigMaps(tc.meta.Namespace).Create(context.TODO(), tc.existingConfigMap, metav1.CreateOptions{}); err != nil {
					t.Errorf("failed to create configmap %v", err)
				}
				if err := lister.Informer().GetIndexer().Add(tc.existingConfigMap); err != nil {
					t.Errorf("failed to add configmap to informer %v", err)
				}
			}
			client.ClearActions()
			err := InsertDataToConfigMap(client.CoreV1(), lister.Lister(), tc.meta, tc.caBundle)
			if err != nil && err.Error() != tc.expectedErr {
				t.Errorf("actual error (%s) different from expected error (%s).", err.Error(), tc.expectedErr)
			}
			if err == nil {
				if tc.expectedErr != "" {
					t.Errorf("expecting error %s but got no error", tc.expectedErr)
				} else if err := checkActions(client.Actions(), tc.expectedActions); err != nil {
					t.Error(err)
				}
			}
		})
	}
}

func createConfigMapDisabledClient() *fake.Clientset {
	client := &fake.Clientset{}
	fakeWatch := watch.NewFake()
	client.AddWatchReactor("configmaps", ktesting.DefaultWatchReactor(fakeWatch, nil))
	client.AddReactor("get", "configmaps", func(action ktesting.Action) (bool, runtime.Object, error) {
		return true, &v1.ConfigMap{}, errors.NewNotFound(v1.Resource("configmaps"), configMapName)
	})
	client.AddReactor("create", "configmaps", func(action ktesting.Action) (bool, runtime.Object, error) {
		return true, &v1.ConfigMap{}, errors.NewUnauthorized("no permission to create configmap")
	})
	return client
}

func createConfigMapAlreadyExistClient() *fake.Clientset {
	client := &fake.Clientset{}
	fakeWatch := watch.NewFake()
	client.AddWatchReactor("configmaps", ktesting.DefaultWatchReactor(fakeWatch, nil))
	client.AddReactor("get", "configmaps", func(action ktesting.Action) (bool, runtime.Object, error) {
		return true, &v1.ConfigMap{}, errors.NewNotFound(v1.Resource("configmaps"), configMapName)
	})
	client.AddReactor("create", "configmaps", func(action ktesting.Action) (bool, runtime.Object, error) {
		return true, &v1.ConfigMap{}, errors.NewAlreadyExists(v1.Resource("configmaps"), configMapName)
	})
	return client
}

func createConfigMapNamespaceDeletingClient() *fake.Clientset {
	client := &fake.Clientset{}
	fakeWatch := watch.NewFake()
	client.AddWatchReactor("configmaps", ktesting.DefaultWatchReactor(fakeWatch, nil))
	client.AddReactor("get", "configmaps", func(action ktesting.Action) (bool, runtime.Object, error) {
		return true, &v1.ConfigMap{}, errors.NewNotFound(v1.Resource("configmaps"), configMapName)
	})

	err := errors.NewForbidden(v1.Resource("configmaps"), configMapName,
		fmt.Errorf("unable to create new content in namespace %s because it is being terminated", namespaceName))
	err.ErrStatus.Details.Causes = append(err.ErrStatus.Details.Causes, metav1.StatusCause{
		Type:    v1.NamespaceTerminatingCause,
		Message: fmt.Sprintf("namespace %s is being terminated", namespaceName),
		Field:   "metadata.namespace",
	})
	client.AddReactor("create", "configmaps", func(action ktesting.Action) (bool, runtime.Object, error) {
		return true, &v1.ConfigMap{}, err
	})
	return client
}

// nolint: unparam
func createConfigMap(namespace, configName string, data map[string]string) *v1.ConfigMap {
	return &v1.ConfigMap{
		ObjectMeta: metav1.ObjectMeta{
			Name:      configName,
			Namespace: namespace,
		},
		Data: data,
	}
}

func checkActions(actual, expected []ktesting.Action) error {
	if len(actual) != len(expected) {
		return fmt.Errorf("unexpected number of actions, want %d but got %d, %v", len(expected), len(actual), actual)
	}

	for i, action := range actual {
		expectedAction := expected[i]
		verb := expectedAction.GetVerb()
		resource := expectedAction.GetResource().Resource
		if !action.Matches(verb, resource) {
			return fmt.Errorf("unexpected %dth action, want \n%+v but got \n%+v", i, expectedAction, action)
		}
	}

	return nil
}

func createFakeLister(kubeClient *fake.Clientset) informersv1.ConfigMapInformer {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	informerFactory := informers.NewSharedInformerFactory(kubeClient, time.Second)
	configmapInformer := informerFactory.Core().V1().ConfigMaps().Informer()
	go configmapInformer.Run(ctx.Done())
	kube.WaitForCacheSync(ctx.Done(), configmapInformer.HasSynced)
	return informerFactory.Core().V1().ConfigMaps()
}

func Test_insertData(t *testing.T) {
	type args struct {
		cm   *v1.ConfigMap
		data map[string]string
	}
	tests := []struct {
		name       string
		args       args
		want       bool
		expectedCM *v1.ConfigMap
	}{
		{
			name: "unchanged",
			args: args{
				cm:   createConfigMap(namespaceName, configMapName, map[string]string{"foo": "bar"}),
				data: nil,
			},
			want:       false,
			expectedCM: createConfigMap(namespaceName, configMapName, map[string]string{"foo": "bar"}),
		},
		{
			name: "unchanged",
			args: args{
				cm:   createConfigMap(namespaceName, configMapName, map[string]string{"foo": "bar"}),
				data: map[string]string{"foo": "bar"},
			},
			want:       false,
			expectedCM: createConfigMap(namespaceName, configMapName, map[string]string{"foo": "bar"}),
		},
		{
			name: "changed",
			args: args{
				cm:   createConfigMap(namespaceName, configMapName, map[string]string{"foo": "bar"}),
				data: map[string]string{"bar": "foo"},
			},
			want:       true,
			expectedCM: createConfigMap(namespaceName, configMapName, map[string]string{"foo": "bar", "bar": "foo"}),
		},
		{
			name: "changed",
			args: args{
				cm:   createConfigMap(namespaceName, configMapName, map[string]string{"foo": "bar"}),
				data: map[string]string{"foo": "foo"},
			},
			want:       true,
			expectedCM: createConfigMap(namespaceName, configMapName, map[string]string{"foo": "foo"}),
		},
		{
			name: "changed",
			args: args{
				cm:   createConfigMap(namespaceName, configMapName, nil),
				data: map[string]string{"bar": "foo"},
			},
			want:       true,
			expectedCM: createConfigMap(namespaceName, configMapName, map[string]string{"bar": "foo"}),
		},
		{
			name: "changed",
			args: args{
				cm:   createConfigMap(namespaceName, configMapName, nil),
				data: nil,
			},
			want:       true,
			expectedCM: createConfigMap(namespaceName, configMapName, nil),
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := insertData(tt.args.cm, tt.args.data); got != tt.want {
				t.Errorf("insertData() = %v, want %v", got, tt.want)
			}
			if !reflect.DeepEqual(tt.args.cm.Data, tt.expectedCM.Data) {
				t.Errorf("configmap data: %v, want %v", tt.args.cm.Data, tt.expectedCM)
			}
		})
	}
}
<|endoftext|>


<commit_before>package handler

import (
	"fullerite/metric"

	"fmt"
	"testing"
	"time"

	l "github.com/Sirupsen/logrus"
	"github.com/stretchr/testify/assert"
)

func assertEmpty(t *testing.T, channel chan metric.Metric) {
	close(channel)
	for range channel {
		t.Fatal("The channel was not empty")
	}
}

func TestNewHandler(t *testing.T) {
	names := []string{"Graphite", "Kairos", "SignalFx", "Datadog", "Log"}
	for _, name := range names {
		h := New(name)
		assert.NotNil(t, h, "should create a Handler for "+name)
		assert.NotNil(t, h.Channel(), "should create a channel")
		assert.Equal(t, name, h.Name())
		assert.Equal(t, "", h.Prefix(), "")
		assert.Equal(t, 0, len(h.DefaultDimensions()))
		assert.Equal(t, DefaultBufferSize, h.MaxBufferSize())
		assert.Equal(t, DefaultInterval, h.Interval())
		assert.Equal(t, name+"Handler", fmt.Sprintf("%s", h), "String() should append Handler to the name for "+name)

		// Test Set* functions
		h.SetInterval(999)
		assert.Equal(t, 999, h.Interval())

		h.SetMaxBufferSize(999)
		assert.Equal(t, 999, h.MaxBufferSize())

		dims := map[string]string{"test": "test value"}
		h.SetDefaultDimensions(dims)
		assert.Equal(t, 1, len(h.DefaultDimensions()))
	}
}

// If configured, per handler dimensions should over write default dimensions
func TestPerHandlerDimensions(t *testing.T) {
	b := new(BaseHandler)
	dims := map[string]string{"test": "test value", "host": "test host"}
	b.SetDefaultDimensions(dims)
	assert.Equal(t, 2, len(b.DefaultDimensions()))

	handlerLevelDimensions := "{ \"test\" : \"updated value\", \"runtimeenv\": \"dev\", \"region\":\"uswest1-devc\"}"
	configMap := map[string]interface{}{
		"defaultDimensions": handlerLevelDimensions,
	}

	b.configureCommonParams(configMap)
	assert.Equal(t, 3, len(b.DefaultDimensions()))
	assert.Equal(t, "updated value", b.DefaultDimensions()["test"])
	assert.Equal(t, "", b.DefaultDimensions()["host"])
}

func TestCollectorBlackList(t *testing.T) {
	b := new(BaseHandler)
	collectorBlackList := "[\"TestCollector1\", \"TestCollector2\"]"
	configMap := map[string]interface{}{
		"collectorBlackList": collectorBlackList,
	}

	b.configureCommonParams(configMap)
	assert.Equal(t, 2, len(b.CollectorBlackList()))

	val, _ := b.IsCollectorBlackListed("TestCollector1")
	assert.Equal(t, true, val)

	val, _ = b.IsCollectorBlackListed("WhiteListed")
	assert.Equal(t, false, val)
}

func TestCommonKeepAliveConfig(t *testing.T) {
	b := new(BaseHandler)

	configMap := map[string]interface{}{
		"keepAliveInterval":         100,
		"maxIdleConnectionsPerHost": 5,
	}
	b.configureCommonParams(configMap)
	assert.Equal(t, 5, b.MaxIdleConnectionsPerHost())
	assert.Equal(t, 100, b.KeepAliveInterval())
}

func TestEmissionAndRecord(t *testing.T) {
	emitCalled := false

	callbackChannel := make(chan emissionTiming)
	emitFunc := func([]metric.Metric) bool {
		emitCalled = true
		return true
	}
	metrics := []metric.Metric{metric.New("example")}

	base := BaseHandler{}
	base.log = l.WithField("testing", "basehandler_emit")
	go base.emitAndTime(metrics, emitFunc, callbackChannel)

	select {
	case timing := <-callbackChannel:
		assert.NotNil(t, timing)
		assert.Equal(t, 1, timing.metricsSent)
		assert.NotNil(t, timing.timestamp)
		assert.NotNil(t, timing.duration)
	case <-time.After(2 * time.Second):
		t.Fatal("Failed to read from the callback channel after 2 seconds")
	}

	assert.True(t, emitCalled)
	callbackChannel = nil
}

func TestRecordTimings(t *testing.T) {
	base := BaseHandler{}
	base.log = l.WithField("testing", "basehandler_record")
	base.interval = 2

	minusFiveSec := -1 * 5 * time.Second
	minusSixSec := -1 * 6 * time.Second
	someDur := time.Duration(5)
	now := time.Now()

	// create a list of emissions in order with some older than 1 second
	timingsChannel := make(chan emissionTiming)
	base.emissionTimes.PushBack(emissionTiming{now.Add(minusSixSec), someDur, 0})
	base.emissionTimes.PushBack(emissionTiming{now.Add(minusFiveSec), someDur, 0})

	go base.recordEmissions(timingsChannel)
	timingsChannel <- emissionTiming{now, someDur, 0}

	assert.Equal(t, 1, base.emissionTimes.Len())
	timingsChannel = nil
}

func TestHandlerRunFlushInterval(t *testing.T) {
	base := BaseHandler{}
	base.log = l.WithField("testing", "basehandler_flush")
	base.interval = 1
	base.maxBufferSize = 2
	base.channel = make(chan metric.Metric)

	emitCalledOnce := false
	emitCalledTwice := false
	emitCalledThrice := false
	emitFunc := func(metrics []metric.Metric) bool {
		if emitCalledOnce && emitCalledTwice {
			emitCalledThrice = true
			close(base.channel)
		}
		if emitCalledOnce && !emitCalledTwice {
			assert.Equal(t, 1, len(metrics))
			emitCalledTwice = true
		} else {
			assert.Equal(t, 2, len(metrics))
			emitCalledOnce = true
		}
		return true
	}

	// now we are waiting for some metrics
	go base.run(emitFunc)

	base.channel <- metric.New("testMetric")
	base.channel <- metric.New("testMetric1")
	base.channel <- metric.New("testMetric2")
	time.Sleep(2 * time.Second)
	assert.True(t, emitCalledOnce)
	assert.True(t, emitCalledTwice)
	assert.False(t, emitCalledThrice)
	assert.Equal(t, 1, base.emissionTimes.Len())
	assert.Equal(t, uint64(3), base.metricsSent)
	assert.Equal(t, uint64(0), base.metricsDropped)
	assert.Equal(t, uint64(2), base.totalEmissions)
}

func TestHandlerRun(t *testing.T) {
	base := BaseHandler{}
	base.log = l.WithField("testing", "basehandler_run")
	base.interval = 1
	base.maxBufferSize = 1
	base.channel = make(chan metric.Metric)

	emitCalled := false
	emitFunc := func(metrics []metric.Metric) bool {
		assert.Equal(t, 1, len(metrics))
		emitCalled = true
		return true
	}

	// now we are waiting for some metrics
	go base.run(emitFunc)

	base.channel <- metric.New("testMetric")
	time.Sleep(1 * time.Second)
	assert.True(t, emitCalled)
	assert.Equal(t, 1, base.emissionTimes.Len())
	assert.Equal(t, uint64(1), base.metricsSent)
	assert.Equal(t, uint64(0), base.metricsDropped)
	assert.Equal(t, uint64(1), base.totalEmissions)
	assertEmpty(t, base.channel)
	base.channel = nil
}

func TestInternalMetrics(t *testing.T) {
	base := BaseHandler{}
	base.totalEmissions = 10
	base.metricsDropped = 100
	base.metricsSent = 2
	base.interval = 4

	timing := emissionTiming{time.Now(), 5 * time.Second, 0}
	base.emissionTimes.PushBack(timing)
	timing = emissionTiming{time.Now(), 10 * time.Second, 0}
	base.emissionTimes.PushBack(timing)
	timing = emissionTiming{time.Now(), 6 * time.Second, 0}
	base.emissionTimes.PushBack(timing)

	results := base.InternalMetrics()
	expected := InternalMetrics{
		Counters: map[string]float64{
			"metricsDropped": 100,
			"metricsSent":    2,
			"totalEmissions": 10,
		},
		Gauges: map[string]float64{
			"averageEmissionTiming": 7,
			"emissionsInWindow":     3,
			"intervalLength":        4,
			"maxEmissionTiming":     10,
		},
	}
	assert.Equal(t, expected, results)
}

func TestInternalMetricsWithNan(t *testing.T) {
	base := BaseHandler{}

	expected := InternalMetrics{
		Counters: map[string]float64{
			"metricsDropped": 0,
			"metricsSent":    0,
			"totalEmissions": 0,
		},
		// specifically missing the averageEmissionTiming
		// because we have no emissions yet
		Gauges: map[string]float64{
			"emissionsInWindow": 0,
			"intervalLength":    0,
		},
	}
	im := base.InternalMetrics()
	assert.Equal(t, expected, im)
}

func TestKeepAliveConfig(t *testing.T) {
	base := BaseHandler{}

	assert.Equal(t, 0, base.KeepAliveInterval())
	assert.Equal(t, 0, base.MaxIdleConnectionsPerHost())
}
<commit_msg>Empty channel assertion causes infinite loop<commit_after>package handler

import (
	"fullerite/metric"

	"fmt"
	"testing"
	"time"

	l "github.com/Sirupsen/logrus"
	"github.com/stretchr/testify/assert"
)

func assertEmpty(t *testing.T, channel chan metric.Metric) {
	close(channel)
	for range channel {
		t.Fatal("The channel was not empty")
	}
}

func TestNewHandler(t *testing.T) {
	names := []string{"Graphite", "Kairos", "SignalFx", "Datadog", "Log"}
	for _, name := range names {
		h := New(name)
		assert.NotNil(t, h, "should create a Handler for "+name)
		assert.NotNil(t, h.Channel(), "should create a channel")
		assert.Equal(t, name, h.Name())
		assert.Equal(t, "", h.Prefix(), "")
		assert.Equal(t, 0, len(h.DefaultDimensions()))
		assert.Equal(t, DefaultBufferSize, h.MaxBufferSize())
		assert.Equal(t, DefaultInterval, h.Interval())
		assert.Equal(t, name+"Handler", fmt.Sprintf("%s", h), "String() should append Handler to the name for "+name)

		// Test Set* functions
		h.SetInterval(999)
		assert.Equal(t, 999, h.Interval())

		h.SetMaxBufferSize(999)
		assert.Equal(t, 999, h.MaxBufferSize())

		dims := map[string]string{"test": "test value"}
		h.SetDefaultDimensions(dims)
		assert.Equal(t, 1, len(h.DefaultDimensions()))
	}
}

// If configured, per handler dimensions should over write default dimensions
func TestPerHandlerDimensions(t *testing.T) {
	b := new(BaseHandler)
	dims := map[string]string{"test": "test value", "host": "test host"}
	b.SetDefaultDimensions(dims)
	assert.Equal(t, 2, len(b.DefaultDimensions()))

	handlerLevelDimensions := "{ \"test\" : \"updated value\", \"runtimeenv\": \"dev\", \"region\":\"uswest1-devc\"}"
	configMap := map[string]interface{}{
		"defaultDimensions": handlerLevelDimensions,
	}

	b.configureCommonParams(configMap)
	assert.Equal(t, 3, len(b.DefaultDimensions()))
	assert.Equal(t, "updated value", b.DefaultDimensions()["test"])
	assert.Equal(t, "", b.DefaultDimensions()["host"])
}

func TestCollectorBlackList(t *testing.T) {
	b := new(BaseHandler)
	collectorBlackList := "[\"TestCollector1\", \"TestCollector2\"]"
	configMap := map[string]interface{}{
		"collectorBlackList": collectorBlackList,
	}

	b.configureCommonParams(configMap)
	assert.Equal(t, 2, len(b.CollectorBlackList()))

	val, _ := b.IsCollectorBlackListed("TestCollector1")
	assert.Equal(t, true, val)

	val, _ = b.IsCollectorBlackListed("WhiteListed")
	assert.Equal(t, false, val)
}

func TestCommonKeepAliveConfig(t *testing.T) {
	b := new(BaseHandler)

	configMap := map[string]interface{}{
		"keepAliveInterval":         100,
		"maxIdleConnectionsPerHost": 5,
	}
	b.configureCommonParams(configMap)
	assert.Equal(t, 5, b.MaxIdleConnectionsPerHost())
	assert.Equal(t, 100, b.KeepAliveInterval())
}

func TestEmissionAndRecord(t *testing.T) {
	emitCalled := false

	callbackChannel := make(chan emissionTiming)
	emitFunc := func([]metric.Metric) bool {
		emitCalled = true
		return true
	}
	metrics := []metric.Metric{metric.New("example")}

	base := BaseHandler{}
	base.log = l.WithField("testing", "basehandler_emit")
	go base.emitAndTime(metrics, emitFunc, callbackChannel)

	select {
	case timing := <-callbackChannel:
		assert.NotNil(t, timing)
		assert.Equal(t, 1, timing.metricsSent)
		assert.NotNil(t, timing.timestamp)
		assert.NotNil(t, timing.duration)
	case <-time.After(2 * time.Second):
		t.Fatal("Failed to read from the callback channel after 2 seconds")
	}

	assert.True(t, emitCalled)
	callbackChannel = nil
}

func TestRecordTimings(t *testing.T) {
	base := BaseHandler{}
	base.log = l.WithField("testing", "basehandler_record")
	base.interval = 2

	minusFiveSec := -1 * 5 * time.Second
	minusSixSec := -1 * 6 * time.Second
	someDur := time.Duration(5)
	now := time.Now()

	// create a list of emissions in order with some older than 1 second
	timingsChannel := make(chan emissionTiming)
	base.emissionTimes.PushBack(emissionTiming{now.Add(minusSixSec), someDur, 0})
	base.emissionTimes.PushBack(emissionTiming{now.Add(minusFiveSec), someDur, 0})

	go base.recordEmissions(timingsChannel)
	timingsChannel <- emissionTiming{now, someDur, 0}

	assert.Equal(t, 1, base.emissionTimes.Len())
	timingsChannel = nil
}

func TestHandlerRunFlushInterval(t *testing.T) {
	base := BaseHandler{}
	base.log = l.WithField("testing", "basehandler_flush")
	base.interval = 1
	base.maxBufferSize = 2
	base.channel = make(chan metric.Metric)

	emitCalledOnce := false
	emitCalledTwice := false
	emitCalledThrice := false
	emitFunc := func(metrics []metric.Metric) bool {
		if emitCalledOnce && emitCalledTwice {
			emitCalledThrice = true
			close(base.channel)
		}
		if emitCalledOnce && !emitCalledTwice {
			assert.Equal(t, 1, len(metrics))
			emitCalledTwice = true
		} else {
			assert.Equal(t, 2, len(metrics))
			emitCalledOnce = true
		}
		return true
	}

	// now we are waiting for some metrics
	go base.run(emitFunc)

	base.channel <- metric.New("testMetric")
	base.channel <- metric.New("testMetric1")
	base.channel <- metric.New("testMetric2")
	time.Sleep(2 * time.Second)
	assert.True(t, emitCalledOnce)
	assert.True(t, emitCalledTwice)
	assert.False(t, emitCalledThrice)
	assert.Equal(t, 1, base.emissionTimes.Len())
	assert.Equal(t, uint64(3), base.metricsSent)
	assert.Equal(t, uint64(0), base.metricsDropped)
	assert.Equal(t, uint64(2), base.totalEmissions)
}

func TestHandlerRun(t *testing.T) {
	base := BaseHandler{}
	base.log = l.WithField("testing", "basehandler_run")
	base.interval = 1
	base.maxBufferSize = 1
	base.channel = make(chan metric.Metric)

	emitCalled := false
	emitFunc := func(metrics []metric.Metric) bool {
		assert.Equal(t, 1, len(metrics))
		emitCalled = true
		close(base.channel)
		return true
	}

	// now we are waiting for some metrics
	go base.run(emitFunc)

	base.channel <- metric.New("testMetric")
	time.Sleep(1 * time.Second)
	assert.True(t, emitCalled)
	assert.Equal(t, 1, base.emissionTimes.Len())
	assert.Equal(t, uint64(1), base.metricsSent)
	assert.Equal(t, uint64(0), base.metricsDropped)
	assert.Equal(t, uint64(1), base.totalEmissions)
}

func TestInternalMetrics(t *testing.T) {
	base := BaseHandler{}
	base.totalEmissions = 10
	base.metricsDropped = 100
	base.metricsSent = 2
	base.interval = 4

	timing := emissionTiming{time.Now(), 5 * time.Second, 0}
	base.emissionTimes.PushBack(timing)
	timing = emissionTiming{time.Now(), 10 * time.Second, 0}
	base.emissionTimes.PushBack(timing)
	timing = emissionTiming{time.Now(), 6 * time.Second, 0}
	base.emissionTimes.PushBack(timing)

	results := base.InternalMetrics()
	expected := InternalMetrics{
		Counters: map[string]float64{
			"metricsDropped": 100,
			"metricsSent":    2,
			"totalEmissions": 10,
		},
		Gauges: map[string]float64{
			"averageEmissionTiming": 7,
			"emissionsInWindow":     3,
			"intervalLength":        4,
			"maxEmissionTiming":     10,
		},
	}
	assert.Equal(t, expected, results)
}

func TestInternalMetricsWithNan(t *testing.T) {
	base := BaseHandler{}

	expected := InternalMetrics{
		Counters: map[string]float64{
			"metricsDropped": 0,
			"metricsSent":    0,
			"totalEmissions": 0,
		},
		// specifically missing the averageEmissionTiming
		// because we have no emissions yet
		Gauges: map[string]float64{
			"emissionsInWindow": 0,
			"intervalLength":    0,
		},
	}
	im := base.InternalMetrics()
	assert.Equal(t, expected, im)
}

func TestKeepAliveConfig(t *testing.T) {
	base := BaseHandler{}

	assert.Equal(t, 0, base.KeepAliveInterval())
	assert.Equal(t, 0, base.MaxIdleConnectionsPerHost())
}
<|endoftext|>


<commit_before>package virtual_guest_lifecycle_test

import (
	"fmt"
	"os"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	datatypes "github.com/maximilien/softlayer-go/data_types"
	softlayer "github.com/maximilien/softlayer-go/softlayer"
	testhelpers "github.com/maximilien/softlayer-go/test_helpers"
)

var _ = Describe("SoftLayer Virtual Guest Lifecycle", func() {
	var (
		err error

		accountService      softlayer.SoftLayer_Account_Service
		virtualGuestService softlayer.SoftLayer_Virtual_Guest_Service
	)

	BeforeEach(func() {
		accountService, err = testhelpers.CreateAccountService()
		Expect(err).ToNot(HaveOccurred())

		virtualGuestService, err = testhelpers.CreateVirtualGuestService()
		Expect(err).ToNot(HaveOccurred())

		testhelpers.TIMEOUT = 35 * time.Minute
		testhelpers.POLLING_INTERVAL = 10 * time.Second
	})

	Context("SoftLayer_Account#<getSshKeys, getVirtualGuests>", func() {
		It("returns an array of SoftLayer_Virtual_Guest objects", func() {
			virtualGuests, err := accountService.GetVirtualGuests()
			Expect(err).ToNot(HaveOccurred())
			Expect(len(virtualGuests)).To(BeNumerically(">=", 0))
		})

		It("returns an array of SoftLayer_Security_Ssh_Keys objects", func() {
			sshKeys, err := accountService.GetSshKeys()
			Expect(err).ToNot(HaveOccurred())
			Expect(len(sshKeys)).To(BeNumerically(">=", 0))
		})
	})

	Context("SoftLayer_SecuritySshKey#CreateObject and SoftLayer_SecuritySshKey#DeleteObject", func() {
		It("creates the ssh key and verify it is present and then deletes it", func() {
			sshKeyPath := os.Getenv("SOFTLAYER_GO_TEST_SSH_KEY_PATH1")
			Expect(sshKeyPath).ToNot(Equal(""), "SOFTLAYER_GO_TEST_SSH_KEY_PATH1 env variable is not set")

			createdSshKey := testhelpers.CreateTestSshKey(sshKeyPath)
			testhelpers.WaitForCreatedSshKeyToBePresent(createdSshKey.Id)

			sshKeyService, err := testhelpers.CreateSecuritySshKeyService()
			Expect(err).ToNot(HaveOccurred())

			deleted, err := sshKeyService.DeleteObject(createdSshKey.Id)
			Expect(err).ToNot(HaveOccurred())
			Expect(deleted).To(BeTrue())

			testhelpers.WaitForDeletedSshKeyToNoLongerBePresent(createdSshKey.Id)
		})
	})

	Context("SoftLayer_VirtualGuest#CreateObject, SoftLayer_VirtualGuest#GetVirtualGuestPrimaryIpAddress, and SoftLayer_VirtualGuest#DeleteObject", func() {
		It("creates the virtual guest instance and waits for it to be active, get it's IP address, and then delete it", func() {
			virtualGuest := testhelpers.CreateVirtualGuestAndMarkItTest([]datatypes.SoftLayer_Security_Ssh_Key{})

			testhelpers.WaitForVirtualGuestToBeRunning(virtualGuest.Id)
			testhelpers.WaitForVirtualGuestToHaveNoActiveTransactions(virtualGuest.Id)

			ipAddress := testhelpers.GetVirtualGuestPrimaryIpAddress(virtualGuest.Id)
			Expect(ipAddress).ToNot(Equal(""))

			testhelpers.DeleteVirtualGuest(virtualGuest.Id)
		})
	})

	Context("SoftLayer_VirtualGuest#CreateObject, SoftLayer_VirtualGuest#rebootSoft, wait for reboot to complete, and SoftLayer_VirtualGuest#DeleteObject", func() {
		It("creates the virtual guest instance, wait for active, SOFT reboots it, wait for RUNNING, then delete it", func() {
			virtualGuest := testhelpers.CreateVirtualGuestAndMarkItTest([]datatypes.SoftLayer_Security_Ssh_Key{})

			testhelpers.WaitForVirtualGuestToBeRunning(virtualGuest.Id)
			testhelpers.WaitForVirtualGuestToHaveNoActiveTransactions(virtualGuest.Id)

			virtualGuestService, err := testhelpers.CreateVirtualGuestService()
			Expect(err).ToNot(HaveOccurred())

			fmt.Printf("----> will attempt to SOFT reboot virtual guest `%d`\n", virtualGuest.Id)
			rebooted, err := virtualGuestService.RebootSoft(virtualGuest.Id)
			Expect(err).ToNot(HaveOccurred())
			Expect(rebooted).To(BeTrue())
			fmt.Printf("----> successfully SOFT rebooted virtual guest `%d`\n", virtualGuest.Id)

			testhelpers.WaitForVirtualGuestToBeRunning(virtualGuest.Id)
			testhelpers.WaitForVirtualGuestToHaveNoActiveTransactions(virtualGuest.Id)

			testhelpers.DeleteVirtualGuest(virtualGuest.Id)
		})
	})

	Context("SoftLayer_VirtualGuest#CreateObject, SoftLayer_VirtualGuest#rebootHard, wait for reboot to complete, and SoftLayer_VirtualGuest#DeleteObject", func() {
		It("creates the virtual guest instance, wait for active, HARD reboots it, wait for RUNNING, then delete it", func() {
			virtualGuest := testhelpers.CreateVirtualGuestAndMarkItTest([]datatypes.SoftLayer_Security_Ssh_Key{})

			testhelpers.WaitForVirtualGuestToBeRunning(virtualGuest.Id)
			testhelpers.WaitForVirtualGuestToHaveNoActiveTransactions(virtualGuest.Id)

			virtualGuestService, err := testhelpers.CreateVirtualGuestService()
			Expect(err).ToNot(HaveOccurred())

			fmt.Printf("----> will attempt to HARD reboot virtual guest `%d`\n", virtualGuest.Id)
			rebooted, err := virtualGuestService.RebootHard(virtualGuest.Id)
			Expect(err).ToNot(HaveOccurred())
			Expect(rebooted).To(BeTrue())
			fmt.Printf("----> successfully HARD rebooted virtual guest `%d`\n", virtualGuest.Id)

			testhelpers.WaitForVirtualGuestToBeRunning(virtualGuest.Id)
			testhelpers.WaitForVirtualGuestToHaveNoActiveTransactions(virtualGuest.Id)

			testhelpers.DeleteVirtualGuest(virtualGuest.Id)
		})
	})

	Context("SoftLayer_SecuritySshKey#CreateObject and SoftLayer_VirtualGuest#CreateObject", func() {
		It("creates key, creates virtual guest and adds key to list of VG", func() {
			sshKeyPath := os.Getenv("SOFTLAYER_GO_TEST_SSH_KEY_PATH2")
			Expect(sshKeyPath).ToNot(Equal(""), "SOFTLAYER_GO_TEST_SSH_KEY_PATH2 env variable is not set")

			err = testhelpers.FindAndDeleteTestSshKeys()
			Expect(err).ToNot(HaveOccurred())

			createdSshKey := testhelpers.CreateTestSshKey(sshKeyPath)
			testhelpers.WaitForCreatedSshKeyToBePresent(createdSshKey.Id)

			virtualGuest := testhelpers.CreateVirtualGuestAndMarkItTest([]datatypes.SoftLayer_Security_Ssh_Key{createdSshKey})

			testhelpers.WaitForVirtualGuestToBeRunning(virtualGuest.Id)
			testhelpers.WaitForVirtualGuestToHaveNoActiveTransactions(virtualGuest.Id)

			testhelpers.DeleteVirtualGuest(virtualGuest.Id)
			testhelpers.DeleteSshKey(createdSshKey.Id)
		})
	})

	FContext("SoftLayer_VirtualGuest#CreateObject, SoftLayer_VirtualGuest#setTags, and SoftLayer_VirtualGuest#DeleteObject", func() {
		It("creates the virtual guest instance, wait for active, wait for RUNNING, set some tags, verify that tags are added, then delete it", func() {
			virtualGuest := testhelpers.CreateVirtualGuestAndMarkItTest([]datatypes.SoftLayer_Security_Ssh_Key{})

			testhelpers.WaitForVirtualGuestToBeRunning(virtualGuest.Id)
			testhelpers.WaitForVirtualGuestToHaveNoActiveTransactions(virtualGuest.Id)

			virtualGuestService, err := testhelpers.CreateVirtualGuestService()
			Expect(err).ToNot(HaveOccurred())

			fmt.Printf("----> will attempt to set tags to the virtual guest `%d`\n", virtualGuest.Id)
			tags := []string{"tag0", "tag1", "tag2"}
			tagsWasSet, err := virtualGuestService.SetTags(virtualGuest.Id, tags)
			Expect(err).ToNot(HaveOccurred())
			Expect(tagsWasSet).To(BeTrue())

			fmt.Printf("----> verifying that tags were set the tags virtual guest `%d`\n", virtualGuest.Id)
			tagReferences, err := virtualGuestService.GetTagReferences(virtualGuest.Id)
			Expect(err).ToNot(HaveOccurred())
			Expect(len(tagReferences)).To(Equal(3))

			fmt.Printf("----> verify that each tag was set to virtual guest: `%d`\n", virtualGuest.Id)
			found := false
			for _, tag := range tags {
				for _, tagReference := range tagReferences {
					if tag == tagReference.Tag.Name {
						found = true
						break
					}
				}
				Expect(found).To(BeTrue())
				found = false
			}

			fmt.Printf("----> successfully set the tags and verified tags were set in virtual guest `%d`\n", virtualGuest.Id)

			testhelpers.DeleteVirtualGuest(virtualGuest.Id)
		})
	})
})
<commit_msg>added integration test for SLVG#getNetworkVlans method<commit_after>package virtual_guest_lifecycle_test

import (
	"fmt"
	"os"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	datatypes "github.com/maximilien/softlayer-go/data_types"
	softlayer "github.com/maximilien/softlayer-go/softlayer"
	testhelpers "github.com/maximilien/softlayer-go/test_helpers"
)

var _ = Describe("SoftLayer Virtual Guest Lifecycle", func() {
	var (
		err error

		accountService      softlayer.SoftLayer_Account_Service
		virtualGuestService softlayer.SoftLayer_Virtual_Guest_Service
	)

	BeforeEach(func() {
		accountService, err = testhelpers.CreateAccountService()
		Expect(err).ToNot(HaveOccurred())

		virtualGuestService, err = testhelpers.CreateVirtualGuestService()
		Expect(err).ToNot(HaveOccurred())

		testhelpers.TIMEOUT = 35 * time.Minute
		testhelpers.POLLING_INTERVAL = 10 * time.Second
	})

	Context("SoftLayer_Account#<getSshKeys, getVirtualGuests>", func() {
		It("returns an array of SoftLayer_Virtual_Guest objects", func() {
			virtualGuests, err := accountService.GetVirtualGuests()
			Expect(err).ToNot(HaveOccurred())
			Expect(len(virtualGuests)).To(BeNumerically(">=", 0))
		})

		It("returns an array of SoftLayer_Security_Ssh_Keys objects", func() {
			sshKeys, err := accountService.GetSshKeys()
			Expect(err).ToNot(HaveOccurred())
			Expect(len(sshKeys)).To(BeNumerically(">=", 0))
		})
	})

	Context("SoftLayer_SecuritySshKey#CreateObject and SoftLayer_SecuritySshKey#DeleteObject", func() {
		It("creates the ssh key and verify it is present and then deletes it", func() {
			sshKeyPath := os.Getenv("SOFTLAYER_GO_TEST_SSH_KEY_PATH1")
			Expect(sshKeyPath).ToNot(Equal(""), "SOFTLAYER_GO_TEST_SSH_KEY_PATH1 env variable is not set")

			createdSshKey := testhelpers.CreateTestSshKey(sshKeyPath)
			testhelpers.WaitForCreatedSshKeyToBePresent(createdSshKey.Id)

			sshKeyService, err := testhelpers.CreateSecuritySshKeyService()
			Expect(err).ToNot(HaveOccurred())

			deleted, err := sshKeyService.DeleteObject(createdSshKey.Id)
			Expect(err).ToNot(HaveOccurred())
			Expect(deleted).To(BeTrue())

			testhelpers.WaitForDeletedSshKeyToNoLongerBePresent(createdSshKey.Id)
		})
	})

	Context("SoftLayer_VirtualGuest#CreateObject, SoftLayer_VirtualGuest#GetVirtualGuestPrimaryIpAddress, and SoftLayer_VirtualGuest#DeleteObject", func() {
		It("creates the virtual guest instance and waits for it to be active, get it's IP address, and then delete it", func() {
			virtualGuest := testhelpers.CreateVirtualGuestAndMarkItTest([]datatypes.SoftLayer_Security_Ssh_Key{})

			testhelpers.WaitForVirtualGuestToBeRunning(virtualGuest.Id)
			testhelpers.WaitForVirtualGuestToHaveNoActiveTransactions(virtualGuest.Id)

			ipAddress := testhelpers.GetVirtualGuestPrimaryIpAddress(virtualGuest.Id)
			Expect(ipAddress).ToNot(Equal(""))

			testhelpers.DeleteVirtualGuest(virtualGuest.Id)
		})

		It("creates the virtual guest instance and waits for it to be active, get it's network VLANS, and then delete it", func() {
			virtualGuest := testhelpers.CreateVirtualGuestAndMarkItTest([]datatypes.SoftLayer_Security_Ssh_Key{})

			testhelpers.WaitForVirtualGuestToBeRunning(virtualGuest.Id)

			networkVlans, err := virtualGuestService.GetNetworkVlans(virtualGuest.Id)
			Expect(err).ToNot(HaveOccurred())
			Expect(len(networkVlans)).To(BeNumerically(">", 0))

			testhelpers.DeleteVirtualGuest(virtualGuest.Id)
		})
	})

	Context("SoftLayer_VirtualGuest#CreateObject, SoftLayer_VirtualGuest#rebootSoft, wait for reboot to complete, and SoftLayer_VirtualGuest#DeleteObject", func() {
		It("creates the virtual guest instance, wait for active, SOFT reboots it, wait for RUNNING, then delete it", func() {
			virtualGuest := testhelpers.CreateVirtualGuestAndMarkItTest([]datatypes.SoftLayer_Security_Ssh_Key{})

			testhelpers.WaitForVirtualGuestToBeRunning(virtualGuest.Id)
			testhelpers.WaitForVirtualGuestToHaveNoActiveTransactions(virtualGuest.Id)

			virtualGuestService, err := testhelpers.CreateVirtualGuestService()
			Expect(err).ToNot(HaveOccurred())

			fmt.Printf("----> will attempt to SOFT reboot virtual guest `%d`\n", virtualGuest.Id)
			rebooted, err := virtualGuestService.RebootSoft(virtualGuest.Id)
			Expect(err).ToNot(HaveOccurred())
			Expect(rebooted).To(BeTrue())
			fmt.Printf("----> successfully SOFT rebooted virtual guest `%d`\n", virtualGuest.Id)

			testhelpers.WaitForVirtualGuestToBeRunning(virtualGuest.Id)
			testhelpers.WaitForVirtualGuestToHaveNoActiveTransactions(virtualGuest.Id)

			testhelpers.DeleteVirtualGuest(virtualGuest.Id)
		})
	})

	Context("SoftLayer_VirtualGuest#CreateObject, SoftLayer_VirtualGuest#rebootHard, wait for reboot to complete, and SoftLayer_VirtualGuest#DeleteObject", func() {
		It("creates the virtual guest instance, wait for active, HARD reboots it, wait for RUNNING, then delete it", func() {
			virtualGuest := testhelpers.CreateVirtualGuestAndMarkItTest([]datatypes.SoftLayer_Security_Ssh_Key{})

			testhelpers.WaitForVirtualGuestToBeRunning(virtualGuest.Id)
			testhelpers.WaitForVirtualGuestToHaveNoActiveTransactions(virtualGuest.Id)

			virtualGuestService, err := testhelpers.CreateVirtualGuestService()
			Expect(err).ToNot(HaveOccurred())

			fmt.Printf("----> will attempt to HARD reboot virtual guest `%d`\n", virtualGuest.Id)
			rebooted, err := virtualGuestService.RebootHard(virtualGuest.Id)
			Expect(err).ToNot(HaveOccurred())
			Expect(rebooted).To(BeTrue())
			fmt.Printf("----> successfully HARD rebooted virtual guest `%d`\n", virtualGuest.Id)

			testhelpers.WaitForVirtualGuestToBeRunning(virtualGuest.Id)
			testhelpers.WaitForVirtualGuestToHaveNoActiveTransactions(virtualGuest.Id)

			testhelpers.DeleteVirtualGuest(virtualGuest.Id)
		})
	})

	Context("SoftLayer_SecuritySshKey#CreateObject and SoftLayer_VirtualGuest#CreateObject", func() {
		It("creates key, creates virtual guest and adds key to list of VG", func() {
			sshKeyPath := os.Getenv("SOFTLAYER_GO_TEST_SSH_KEY_PATH2")
			Expect(sshKeyPath).ToNot(Equal(""), "SOFTLAYER_GO_TEST_SSH_KEY_PATH2 env variable is not set")

			err = testhelpers.FindAndDeleteTestSshKeys()
			Expect(err).ToNot(HaveOccurred())

			createdSshKey := testhelpers.CreateTestSshKey(sshKeyPath)
			testhelpers.WaitForCreatedSshKeyToBePresent(createdSshKey.Id)

			virtualGuest := testhelpers.CreateVirtualGuestAndMarkItTest([]datatypes.SoftLayer_Security_Ssh_Key{createdSshKey})

			testhelpers.WaitForVirtualGuestToBeRunning(virtualGuest.Id)
			testhelpers.WaitForVirtualGuestToHaveNoActiveTransactions(virtualGuest.Id)

			testhelpers.DeleteVirtualGuest(virtualGuest.Id)
			testhelpers.DeleteSshKey(createdSshKey.Id)
		})
	})

	FContext("SoftLayer_VirtualGuest#CreateObject, SoftLayer_VirtualGuest#setTags, and SoftLayer_VirtualGuest#DeleteObject", func() {
		It("creates the virtual guest instance, wait for active, wait for RUNNING, set some tags, verify that tags are added, then delete it", func() {
			virtualGuest := testhelpers.CreateVirtualGuestAndMarkItTest([]datatypes.SoftLayer_Security_Ssh_Key{})

			testhelpers.WaitForVirtualGuestToBeRunning(virtualGuest.Id)
			testhelpers.WaitForVirtualGuestToHaveNoActiveTransactions(virtualGuest.Id)

			virtualGuestService, err := testhelpers.CreateVirtualGuestService()
			Expect(err).ToNot(HaveOccurred())

			fmt.Printf("----> will attempt to set tags to the virtual guest `%d`\n", virtualGuest.Id)
			tags := []string{"tag0", "tag1", "tag2"}
			tagsWasSet, err := virtualGuestService.SetTags(virtualGuest.Id, tags)
			Expect(err).ToNot(HaveOccurred())
			Expect(tagsWasSet).To(BeTrue())

			fmt.Printf("----> verifying that tags were set the tags virtual guest `%d`\n", virtualGuest.Id)
			tagReferences, err := virtualGuestService.GetTagReferences(virtualGuest.Id)
			Expect(err).ToNot(HaveOccurred())
			Expect(len(tagReferences)).To(Equal(3))

			fmt.Printf("----> verify that each tag was set to virtual guest: `%d`\n", virtualGuest.Id)
			found := false
			for _, tag := range tags {
				for _, tagReference := range tagReferences {
					if tag == tagReference.Tag.Name {
						found = true
						break
					}
				}
				Expect(found).To(BeTrue())
				found = false
			}

			fmt.Printf("----> successfully set the tags and verified tags were set in virtual guest `%d`\n", virtualGuest.Id)

			testhelpers.DeleteVirtualGuest(virtualGuest.Id)
		})
	})
})
<|endoftext|>


<commit_before>// Copied and adapted from github.com/dgryski/go-ketama , which has no explicit
// license.

// Package ketama implements consistent hashing compatible with Algorithm::ConsistentHash::Ketama
/*
This implementation draws from the Daisuke Maki's Perl module, which itself is
based on the original libketama code.  That code was licensed under the GPLv2,
and thus so it this.

The major API change from libketama is that Algorithm::ConsistentHash::Ketama allows hashing
arbitrary strings, instead of just memcached server IP addresses.
*/

package shredis

import (
	"crypto/md5"
	"fmt"
	"sort"

	"github.com/realzeitmedia/fnv"
)

type bucket struct {
	Label  string
	ID     int
	Weight int
}

type continuumPoint struct {
	bucket bucket
	point  uint64
}

type continuum []continuumPoint

func (c continuum) Less(i, j int) bool { return c[i].point < c[j].point }
func (c continuum) Len() int           { return len(c) }
func (c continuum) Swap(i, j int)      { c[i], c[j] = c[j], c[i] }

func md5Digest(in string) []byte {
	h := md5.New()
	h.Write([]byte(in))
	return h.Sum(nil)
}

func hashKey(k []byte) uint64 {
	h := fnv.New()
	h = fnv.AddBytes(h, k)
	return uint64(uint32(h)) // something nutcracker does
}

func ketamaNew(buckets []bucket) continuum {

	numbuckets := len(buckets)

	if numbuckets == 0 {
		// let them error when they try to use it
		return continuum(nil)
	}

	ket := make([]continuumPoint, 0, numbuckets*160)

	totalweight := 0
	for _, b := range buckets {
		totalweight += b.Weight
	}

	for i, b := range buckets {
		pct := float32(b.Weight) / float32(totalweight)

		// this is the equivalent of C's promotion rules, but in Go, to maintain exact compatibility with the C library
		limit := int(float32(float64(pct) * 40.0 * float64(numbuckets)))

		for k := 0; k < limit; k++ {
			/* 40 hashes, 4 numbers per hash = 160 points per bucket */
			ss := fmt.Sprintf("%s-%d", b.Label, k)
			digest := md5Digest(ss)

			for h := 0; h < 4; h++ {
				point := continuumPoint{
					point:  uint64(digest[3+h*4])<<24 | uint64(digest[2+h*4])<<16 | uint64(digest[1+h*4])<<8 | uint64(digest[h*4]),
					bucket: buckets[i],
				}
				ket = append(ket, point)
			}
		}
	}

	cont := continuum(ket)

	sort.Sort(cont)

	return cont
}

func (c continuum) Hash(thing []byte) int {
	if len(c) == 0 {
		return 0
	}

	h := hashKey(thing)
	i := sort.Search(len(c), func(i int) bool { return c[i].point >= h })
	if i >= len(c) {
		i = 0
	}
	return c[i].bucket.ID
}
<commit_msg>Less lines for the same thing<commit_after>// Copied and adapted from github.com/dgryski/go-ketama , which has no explicit
// license.

// Package ketama implements consistent hashing compatible with Algorithm::ConsistentHash::Ketama
/*
This implementation draws from the Daisuke Maki's Perl module, which itself is
based on the original libketama code.  That code was licensed under the GPLv2,
and thus so it this.

The major API change from libketama is that Algorithm::ConsistentHash::Ketama allows hashing
arbitrary strings, instead of just memcached server IP addresses.
*/

package shredis

import (
	"crypto/md5"
	"fmt"
	"sort"

	"github.com/realzeitmedia/fnv"
)

type bucket struct {
	Label  string
	ID     int
	Weight int
}

type continuumPoint struct {
	bucket bucket
	point  uint64
}

type continuum []continuumPoint

func (c continuum) Less(i, j int) bool { return c[i].point < c[j].point }
func (c continuum) Len() int           { return len(c) }
func (c continuum) Swap(i, j int)      { c[i], c[j] = c[j], c[i] }

func md5Digest(in string) []byte {
	h := md5.New()
	h.Write([]byte(in))
	return h.Sum(nil)
}

func hashKey(k []byte) uint64 {
	h := fnv.AddBytes(fnv.New(), k)
	return uint64(uint32(h)) // something nutcracker does
}

func ketamaNew(buckets []bucket) continuum {

	numbuckets := len(buckets)

	if numbuckets == 0 {
		// let them error when they try to use it
		return continuum(nil)
	}

	ket := make([]continuumPoint, 0, numbuckets*160)

	totalweight := 0
	for _, b := range buckets {
		totalweight += b.Weight
	}

	for i, b := range buckets {
		pct := float32(b.Weight) / float32(totalweight)

		// this is the equivalent of C's promotion rules, but in Go, to maintain exact compatibility with the C library
		limit := int(float32(float64(pct) * 40.0 * float64(numbuckets)))

		for k := 0; k < limit; k++ {
			/* 40 hashes, 4 numbers per hash = 160 points per bucket */
			ss := fmt.Sprintf("%s-%d", b.Label, k)
			digest := md5Digest(ss)

			for h := 0; h < 4; h++ {
				point := continuumPoint{
					point:  uint64(digest[3+h*4])<<24 | uint64(digest[2+h*4])<<16 | uint64(digest[1+h*4])<<8 | uint64(digest[h*4]),
					bucket: buckets[i],
				}
				ket = append(ket, point)
			}
		}
	}

	cont := continuum(ket)

	sort.Sort(cont)

	return cont
}

func (c continuum) Hash(thing []byte) int {
	if len(c) == 0 {
		return 0
	}

	h := hashKey(thing)
	i := sort.Search(len(c), func(i int) bool { return c[i].point >= h })
	if i >= len(c) {
		i = 0
	}
	return c[i].bucket.ID
}
<|endoftext|>