<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/rkoesters/xkcd"
	"log"
	"strconv"
)

func main() {
	var info xkcd.ComicInfo
	var err error

	flag.Parse()

	if flag.NArg() == 0 {
		info, err = xkcd.GetCurrentComicInfo()
		if err != nil {
			log.Fatal(err)
		}
	} else if flag.NArg() == 1 {
		num, err := strconv.Atoi(flag.Arg(0))
		if err != nil {
			log.Fatal(err)
		}

		info, err = xkcd.GetComicInfo(num)
		if err != nil {
			log.Fatal(err)
		}
	} else {
		log.Fatal("invalid args")
	}

	printInfo(info)
}

func printInfo(info xkcd.ComicInfo) {
	fmt.Printf("Num: %v\n", info.Num)
	fmt.Printf("Title: %v\n", info.Title)
	fmt.Printf("Safe-Title: %v\n", info.SafeTitle)

	fmt.Printf("Img: %v\n", info.Img)
	fmt.Printf("Alt: %v\n", info.Alt)

	fmt.Printf("Year: %v\n", info.Year)
	fmt.Printf("Month: %v\n", info.Month)
	fmt.Printf("Day: %v\n", info.Day)

	fmt.Printf("News: %v\n", info.News)
	fmt.Printf("Link: %v\n", info.Link)
	fmt.Printf("Transcript: %v\n", info.Transcript)
}
<commit_msg>Updated xkcd-info.<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/rkoesters/xkcd"
	"log"
	"strconv"
)

func main() {
	var comic xkcd.Comic
	var err error

	flag.Parse()

	if flag.NArg() == 0 {
		comic, err = xkcd.GetCurrentComic()
		if err != nil {
			log.Fatal(err)
		}
	} else if flag.NArg() == 1 {
		num, err := strconv.Atoi(flag.Arg(0))
		if err != nil {
			log.Fatal(err)
		}

		comic, err = xkcd.GetComic(num)
		if err != nil {
			log.Fatal(err)
		}
	} else {
		log.Fatal("invalid args")
	}

	printInfo(comic)
}

func printInfo(comic xkcd.Comic) {
	fmt.Printf("Num: %v\n", comic.Num)
	fmt.Printf("Title: %v\n", comic.Title)
	fmt.Printf("Safe-Title: %v\n", comic.SafeTitle)

	fmt.Printf("Img: %v\n", comic.Img)
	fmt.Printf("Alt: %v\n", comic.Alt)

	fmt.Printf("Year: %v\n", comic.Year)
	fmt.Printf("Month: %v\n", comic.Month)
	fmt.Printf("Day: %v\n", comic.Day)

	fmt.Printf("News: %v\n", comic.News)
	fmt.Printf("Link: %v\n", comic.Link)
	fmt.Printf("Transcript: %v\n", comic.Transcript)
}
<|endoftext|>


<commit_before>package server

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"testing"
	"time"

	"encoding/json"

	"github.com/smancke/guble/client"
	"github.com/smancke/guble/server/fcm"
	"github.com/smancke/guble/server/service"
	"github.com/smancke/guble/testutil"
	"github.com/stretchr/testify/assert"
)

var (
	testTopic            = "/path"
	testHttpPort         = 11000
	timeoutForOneMessage = 50 * time.Millisecond
)

type fcmMetricsMap struct {
	CurrentErrorsCount            int `json:"current_errors_count"`
	CurrentMessagesCount          int `json:"current_messages_count"`
	CurrentMessagesTotalLatencies int `json:"current_messages_total_latencies_nanos"`
	CurrentErrorsTotalLatencies   int `json:"current_errors_total_latencies_nanos"`
}

type fcmMetrics struct {
	TotalSentMessages      int           `json:"guble.fcm.total_sent_messages"`
	TotalSentMessageErrors int           `json:"guble.fcm.total_sent_message_errors"`
	Minute                 fcmMetricsMap `json:"guble.fcm.minute"`
	Hour                   fcmMetricsMap `json:"guble.fcm.hour"`
	Day                    fcmMetricsMap `json:"guble.fcm.day"`
}

type routerMetrics struct {
	CurrentRoutes        int `json:"guble.router.current_routes"`
	CurrentSubscriptions int `json:"guble.router.current_subscriptions"`
}

type expectedValues struct {
	ZeroLatencies        bool
	MessageCount         int
	CurrentRoutes        int
	CurrentSubscriptions int
}

// Test that restarting the service continues to fetch messages from store
// for a subscription from lastID
func TestFCMRestart(t *testing.T) {
	defer testutil.ResetDefaultRegistryHealthCheck()

	a := assert.New(t)

	receiveC := make(chan bool)
	s, cleanup := serviceSetUp(t)
	defer cleanup()

	assertMetrics(a, s, expectedValues{true, 0, 0, 0})

	var fcmConn *fcm.Connector
	var ok bool
	for _, iface := range s.ModulesSortedByStartOrder() {
		fcmConn, ok = iface.(*fcm.Connector)
		if ok {
			break
		}
	}
	a.True(ok, "There should be a module of type FCMConnector")

	// add a high timeout so the messages are processed slow
	fcmConn.Sender = testutil.CreateFcmSender(
		testutil.CreateRoundTripperWithCountAndTimeout(
			http.StatusOK, testutil.SuccessFCMResponse, receiveC, 10*time.Millisecond))

	// create subscription on topic
	subscriptionSetUp(t, s)

	assertMetrics(a, s, expectedValues{true, 0, 1, 1})

	c := clientSetUp(t, s)

	// send 3 messages in the router but read only one and close the service
	for i := 0; i < 3; i++ {
		c.Send(testTopic, "dummy body", "{dummy: value}")
	}

	// receive one message only from FCM
	select {
	case <-receiveC:
	case <-time.After(timeoutForOneMessage):
		a.Fail("Initial FCM message not received")
	}

	assertMetrics(a, s, expectedValues{false, 1, 1, 1})

	// restart the service
	a.NoError(s.Stop())

	time.Sleep(50 * time.Millisecond)
	testutil.ResetDefaultRegistryHealthCheck()
	a.NoError(s.Start())

	//TODO Cosmin Bogdan add 2 calls to assertMetrics before and after the next block

	// read the other 2 messages
	for i := 0; i < 2; i++ {
		select {
		case <-receiveC:
		case <-time.After(2 * timeoutForOneMessage):
			a.Fail("FCM message not received")
		}
	}
}

func serviceSetUp(t *testing.T) (*service.Service, func()) {
	dir, errTempDir := ioutil.TempDir("", "guble_fcm_test")
	assert.NoError(t, errTempDir)

	*Config.KVS = "memory"
	*Config.MS = "file"
	*Config.Cluster.NodeID = 0
	*Config.StoragePath = dir
	*Config.MetricsEndpoint = "/admin/metrics"
	*Config.FCM.Enabled = true
	*Config.FCM.APIKey = "WILL BE OVERWRITTEN"
	*Config.FCM.Workers = 1 // use only one worker so we can control the number of messages that go to FCM

	var s *service.Service
	for s == nil {
		testHttpPort++
		logger.WithField("port", testHttpPort).Debug("trying to use HTTP Port")
		*Config.HttpListen = fmt.Sprintf("127.0.0.1:%d", testHttpPort)
		s = StartService()
	}
	return s, func() {
		errRemove := os.RemoveAll(dir)
		if errRemove != nil {
			logger.WithError(errRemove).WithField("module", "testing").Error("Could not remove directory")
		}
	}
}

func clientSetUp(t *testing.T, service *service.Service) client.Client {
	wsURL := "ws://" + service.WebServer().GetAddr() + "/stream/user/user01"
	c, err := client.Open(wsURL, "http://localhost/", 1000, false)
	assert.NoError(t, err)
	return c
}

func subscriptionSetUp(t *testing.T, service *service.Service) {
	a := assert.New(t)

	urlFormat := fmt.Sprintf("http://%s/fcm/%%d/gcmId%%d/subscribe/%%s", service.WebServer().GetAddr())
	// create GCM subscription
	response, errPost := http.Post(
		fmt.Sprintf(urlFormat, 1, 1, strings.TrimPrefix(testTopic, "/")),
		"text/plain",
		bytes.NewBufferString(""),
	)
	a.NoError(errPost)
	a.Equal(response.StatusCode, 200)

	body, errReadAll := ioutil.ReadAll(response.Body)
	a.NoError(errReadAll)
	a.Equal(fmt.Sprintf(`{"subscribed":"%s"}`, testTopic), string(body))
}

func assertMetrics(a *assert.Assertions, s *service.Service, expected expectedValues) {
	httpClient := &http.Client{}
	u := fmt.Sprintf("http://%s%s", s.WebServer().GetAddr(), defaultMetricsEndpoint)
	request, err := http.NewRequest(http.MethodGet, u, nil)
	a.NoError(err)

	response, err := httpClient.Do(request)
	a.NoError(err)
	defer response.Body.Close()

	a.Equal(http.StatusOK, response.StatusCode)
	bodyBytes, err := ioutil.ReadAll(response.Body)
	a.NoError(err)
	logger.WithField("body", string(bodyBytes)).Debug("metrics response")

	mFCM := &fcmMetrics{}
	err = json.Unmarshal(bodyBytes, mFCM)
	a.NoError(err)

	a.Equal(0, mFCM.TotalSentMessageErrors)
	a.Equal(expected.MessageCount, mFCM.TotalSentMessages)

	a.Equal(0, mFCM.Minute.CurrentErrorsCount)
	a.Equal(expected.MessageCount, mFCM.Minute.CurrentMessagesCount)
	a.Equal(0, mFCM.Minute.CurrentErrorsTotalLatencies)
	a.Equal(expected.ZeroLatencies, mFCM.Minute.CurrentMessagesTotalLatencies == 0)

	a.Equal(0, mFCM.Hour.CurrentErrorsCount)
	a.Equal(expected.MessageCount, mFCM.Hour.CurrentMessagesCount)
	a.Equal(0, mFCM.Hour.CurrentErrorsTotalLatencies)
	a.Equal(expected.ZeroLatencies, mFCM.Hour.CurrentMessagesTotalLatencies == 0)

	a.Equal(0, mFCM.Day.CurrentErrorsCount)
	a.Equal(expected.MessageCount, mFCM.Day.CurrentMessagesCount)
	a.Equal(0, mFCM.Day.CurrentErrorsTotalLatencies)
	a.Equal(expected.ZeroLatencies, mFCM.Day.CurrentMessagesTotalLatencies == 0)

	mRouter := &routerMetrics{}
	err = json.Unmarshal(bodyBytes, mRouter)
	a.NoError(err)

	a.Equal(expected.CurrentRoutes, mRouter.CurrentRoutes)
	a.Equal(expected.CurrentSubscriptions, mRouter.CurrentSubscriptions)
}
<commit_msg>disabling APNS in test<commit_after>package server

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"testing"
	"time"

	"encoding/json"

	"github.com/smancke/guble/client"
	"github.com/smancke/guble/server/fcm"
	"github.com/smancke/guble/server/service"
	"github.com/smancke/guble/testutil"
	"github.com/stretchr/testify/assert"
)

var (
	testTopic            = "/path"
	testHttpPort         = 11000
	timeoutForOneMessage = 50 * time.Millisecond
)

type fcmMetricsMap struct {
	CurrentErrorsCount            int `json:"current_errors_count"`
	CurrentMessagesCount          int `json:"current_messages_count"`
	CurrentMessagesTotalLatencies int `json:"current_messages_total_latencies_nanos"`
	CurrentErrorsTotalLatencies   int `json:"current_errors_total_latencies_nanos"`
}

type fcmMetrics struct {
	TotalSentMessages      int           `json:"guble.fcm.total_sent_messages"`
	TotalSentMessageErrors int           `json:"guble.fcm.total_sent_message_errors"`
	Minute                 fcmMetricsMap `json:"guble.fcm.minute"`
	Hour                   fcmMetricsMap `json:"guble.fcm.hour"`
	Day                    fcmMetricsMap `json:"guble.fcm.day"`
}

type routerMetrics struct {
	CurrentRoutes        int `json:"guble.router.current_routes"`
	CurrentSubscriptions int `json:"guble.router.current_subscriptions"`
}

type expectedValues struct {
	ZeroLatencies        bool
	MessageCount         int
	CurrentRoutes        int
	CurrentSubscriptions int
}

// Test that restarting the service continues to fetch messages from store
// for a subscription from lastID
func TestFCMRestart(t *testing.T) {
	defer testutil.ResetDefaultRegistryHealthCheck()

	a := assert.New(t)

	receiveC := make(chan bool)
	s, cleanup := serviceSetUp(t)
	defer cleanup()

	assertMetrics(a, s, expectedValues{true, 0, 0, 0})

	var fcmConn *fcm.Connector
	var ok bool
	for _, iface := range s.ModulesSortedByStartOrder() {
		fcmConn, ok = iface.(*fcm.Connector)
		if ok {
			break
		}
	}
	a.True(ok, "There should be a module of type FCMConnector")

	// add a high timeout so the messages are processed slow
	fcmConn.Sender = testutil.CreateFcmSender(
		testutil.CreateRoundTripperWithCountAndTimeout(
			http.StatusOK, testutil.SuccessFCMResponse, receiveC, 10*time.Millisecond))

	// create subscription on topic
	subscriptionSetUp(t, s)

	assertMetrics(a, s, expectedValues{true, 0, 1, 1})

	c := clientSetUp(t, s)

	// send 3 messages in the router but read only one and close the service
	for i := 0; i < 3; i++ {
		c.Send(testTopic, "dummy body", "{dummy: value}")
	}

	// receive one message only from FCM
	select {
	case <-receiveC:
	case <-time.After(timeoutForOneMessage):
		a.Fail("Initial FCM message not received")
	}

	assertMetrics(a, s, expectedValues{false, 1, 1, 1})

	// restart the service
	a.NoError(s.Stop())

	time.Sleep(50 * time.Millisecond)
	testutil.ResetDefaultRegistryHealthCheck()
	a.NoError(s.Start())

	//TODO Cosmin Bogdan add 2 calls to assertMetrics before and after the next block

	// read the other 2 messages
	for i := 0; i < 2; i++ {
		select {
		case <-receiveC:
		case <-time.After(2 * timeoutForOneMessage):
			a.Fail("FCM message not received")
		}
	}
}

func serviceSetUp(t *testing.T) (*service.Service, func()) {
	dir, errTempDir := ioutil.TempDir("", "guble_fcm_test")
	assert.NoError(t, errTempDir)

	*Config.KVS = "memory"
	*Config.MS = "file"
	*Config.Cluster.NodeID = 0
	*Config.StoragePath = dir
	*Config.MetricsEndpoint = "/admin/metrics"
	*Config.FCM.Enabled = true
	*Config.FCM.APIKey = "WILL BE OVERWRITTEN"
	*Config.FCM.Workers = 1 // use only one worker so we can control the number of messages that go to FCM
	*Config.APNS.Enabled = false

	var s *service.Service
	for s == nil {
		testHttpPort++
		logger.WithField("port", testHttpPort).Debug("trying to use HTTP Port")
		*Config.HttpListen = fmt.Sprintf("127.0.0.1:%d", testHttpPort)
		s = StartService()
	}
	return s, func() {
		errRemove := os.RemoveAll(dir)
		if errRemove != nil {
			logger.WithError(errRemove).WithField("module", "testing").Error("Could not remove directory")
		}
	}
}

func clientSetUp(t *testing.T, service *service.Service) client.Client {
	wsURL := "ws://" + service.WebServer().GetAddr() + "/stream/user/user01"
	c, err := client.Open(wsURL, "http://localhost/", 1000, false)
	assert.NoError(t, err)
	return c
}

func subscriptionSetUp(t *testing.T, service *service.Service) {
	a := assert.New(t)

	urlFormat := fmt.Sprintf("http://%s/fcm/%%d/gcmId%%d/subscribe/%%s", service.WebServer().GetAddr())
	// create GCM subscription
	response, errPost := http.Post(
		fmt.Sprintf(urlFormat, 1, 1, strings.TrimPrefix(testTopic, "/")),
		"text/plain",
		bytes.NewBufferString(""),
	)
	a.NoError(errPost)
	a.Equal(response.StatusCode, 200)

	body, errReadAll := ioutil.ReadAll(response.Body)
	a.NoError(errReadAll)
	a.Equal(fmt.Sprintf(`{"subscribed":"%s"}`, testTopic), string(body))
}

func assertMetrics(a *assert.Assertions, s *service.Service, expected expectedValues) {
	httpClient := &http.Client{}
	u := fmt.Sprintf("http://%s%s", s.WebServer().GetAddr(), defaultMetricsEndpoint)
	request, err := http.NewRequest(http.MethodGet, u, nil)
	a.NoError(err)

	response, err := httpClient.Do(request)
	a.NoError(err)
	defer response.Body.Close()

	a.Equal(http.StatusOK, response.StatusCode)
	bodyBytes, err := ioutil.ReadAll(response.Body)
	a.NoError(err)
	logger.WithField("body", string(bodyBytes)).Debug("metrics response")

	mFCM := &fcmMetrics{}
	err = json.Unmarshal(bodyBytes, mFCM)
	a.NoError(err)

	a.Equal(0, mFCM.TotalSentMessageErrors)
	a.Equal(expected.MessageCount, mFCM.TotalSentMessages)

	a.Equal(0, mFCM.Minute.CurrentErrorsCount)
	a.Equal(expected.MessageCount, mFCM.Minute.CurrentMessagesCount)
	a.Equal(0, mFCM.Minute.CurrentErrorsTotalLatencies)
	a.Equal(expected.ZeroLatencies, mFCM.Minute.CurrentMessagesTotalLatencies == 0)

	a.Equal(0, mFCM.Hour.CurrentErrorsCount)
	a.Equal(expected.MessageCount, mFCM.Hour.CurrentMessagesCount)
	a.Equal(0, mFCM.Hour.CurrentErrorsTotalLatencies)
	a.Equal(expected.ZeroLatencies, mFCM.Hour.CurrentMessagesTotalLatencies == 0)

	a.Equal(0, mFCM.Day.CurrentErrorsCount)
	a.Equal(expected.MessageCount, mFCM.Day.CurrentMessagesCount)
	a.Equal(0, mFCM.Day.CurrentErrorsTotalLatencies)
	a.Equal(expected.ZeroLatencies, mFCM.Day.CurrentMessagesTotalLatencies == 0)

	mRouter := &routerMetrics{}
	err = json.Unmarshal(bodyBytes, mRouter)
	a.NoError(err)

	a.Equal(expected.CurrentRoutes, mRouter.CurrentRoutes)
	a.Equal(expected.CurrentSubscriptions, mRouter.CurrentSubscriptions)
}
<|endoftext|>


<commit_before>/*
 * This file is part of the KubeVirt project
 *
 * 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.
 *
 * Copyright 2020 Red Hat, Inc.
 *
 */

package network

import (
	"time"

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

	"kubevirt.io/kubevirt/tests/util"

	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	v1 "kubevirt.io/api/core/v1"
	"kubevirt.io/client-go/kubecli"

	"kubevirt.io/kubevirt/tests"
	"kubevirt.io/kubevirt/tests/console"
	cd "kubevirt.io/kubevirt/tests/containerdisk"
	"kubevirt.io/kubevirt/tests/libnet"
	"kubevirt.io/kubevirt/tests/libvmi"
)

var _ = SIGDescribe("Primary Pod Network", func() {
	var virtClient kubecli.KubevirtClient
	BeforeEach(func() {
		var err error
		virtClient, err = kubecli.GetKubevirtClient()
		Expect(err).NotTo(HaveOccurred(), "Should successfully initialize an API client")
	})

	Describe("Status", func() {
		AssertReportedIP := func(vmi *v1.VirtualMachineInstance) {
			By("Getting pod of the VMI")
			vmiPod := tests.GetRunningPodByVirtualMachineInstance(vmi, util.NamespaceTestDefault)

			By("Making sure IP/s reported on the VMI matches the ones on the pod")
			Expect(libnet.ValidateVMIandPodIPMatch(vmi, vmiPod)).To(Succeed(), "Should have matching IP/s between pod and vmi")
		}

		Context("VMI connected to the pod network using the default (implicit) binding", func() {
			var vmi *v1.VirtualMachineInstance

			BeforeEach(func() {
				vmi = setupVMI(virtClient, vmiWithDefaultBinding())
			})

			AfterEach(func() {
				cleanupVMI(virtClient, vmi)
			})

			It("should report PodIP as its own on interface status", func() { AssertReportedIP(vmi) })
		})

		Context("VMI connected to the pod network using bridge binding", func() {
			When("Guest Agent exists", func() {
				var (
					vmi   *v1.VirtualMachineInstance
					vmiIP = func() string {
						var err error
						vmi, err = virtClient.VirtualMachineInstance(vmi.Namespace).Get(vmi.Name, &metav1.GetOptions{})
						ExpectWithOffset(1, err).ToNot(HaveOccurred(), "should success retrieving VMI to get IP")
						return vmi.Status.Interfaces[0].IP
					}

					vmiIPs = func() []string {
						var err error
						vmi, err = virtClient.VirtualMachineInstance(vmi.Namespace).Get(vmi.Name, &metav1.GetOptions{})
						ExpectWithOffset(1, err).ToNot(HaveOccurred(), "should success retrieving VMI to get IPs")
						return vmi.Status.Interfaces[0].IPs
					}
				)
				BeforeEach(func() {
					libnet.SkipWhenClusterNotSupportIpv4(virtClient)
					var err error

					vmi, err = newFedoraWithGuestAgentAndDefaultInterface(libvmi.InterfaceDeviceWithBridgeBinding(libvmi.DefaultInterfaceName))
					Expect(err).NotTo(HaveOccurred())

					vmi, err = virtClient.VirtualMachineInstance(util.NamespaceTestDefault).Create(vmi)
					Expect(err).NotTo(HaveOccurred())
					tests.WaitForSuccessfulVMIStart(vmi)
					tests.WaitAgentConnected(virtClient, vmi)
				})

				AfterEach(func() {
					cleanupVMI(virtClient, vmi)
				})

				It("should report PodIP/s IPv4 as its own on interface status", func() {
					vmiPod := tests.GetRunningPodByVirtualMachineInstance(vmi, vmi.Namespace)
					Eventually(vmiIP).Should(Equal(vmiPod.Status.PodIP), "should contain VMI Status IP as Pod status ip")
					Eventually(vmiIPs).Should(ContainElement(vmiPod.Status.PodIP), "should contain IPv4 reported by guest agent")
				})

				It("should report VMIs static IPv6 at interface status", func() {
					Eventually(vmiIPs).Should(ContainElement(libnet.DefaultIPv6Address), "should contain IPv6 address set by cloud-init and reported by guest agent")
				})
			})
			When("no Guest Agent exists", func() {
				var vmi *v1.VirtualMachineInstance

				BeforeEach(func() {
					vmi = setupVMI(virtClient, vmiWithBridgeBinding())
				})

				AfterEach(func() {
					cleanupVMI(virtClient, vmi)
				})

				It("should report PodIP as its own on interface status", func() { AssertReportedIP(vmi) })
			})
		})

		Context("VMI connected to the pod network using masquerade binding", func() {
			When("Guest Agent exists", func() {
				var vmi *v1.VirtualMachineInstance

				BeforeEach(func() {
					tmpVmi, err := newFedoraWithGuestAgentAndDefaultInterface(libvmi.InterfaceDeviceWithMasqueradeBinding())
					Expect(err).NotTo(HaveOccurred())

					tmpVmi, err = virtClient.VirtualMachineInstance(util.NamespaceTestDefault).Create(tmpVmi)
					Expect(err).NotTo(HaveOccurred())
					vmi = tests.WaitUntilVMIReady(tmpVmi, console.LoginToFedora)

					tests.WaitAgentConnected(virtClient, vmi)
				})

				AfterEach(func() {
					cleanupVMI(virtClient, vmi)
				})

				It("[test_id:4153]should report PodIP/s as its own on interface status", func() {
					vmiPod := tests.GetRunningPodByVirtualMachineInstance(vmi, vmi.Namespace)
					Consistently(func() error {
						vmi, err := virtClient.VirtualMachineInstance(vmi.Namespace).Get(vmi.Name, &metav1.GetOptions{})
						if err != nil {
							return err
						}
						return libnet.ValidateVMIandPodIPMatch(vmi, vmiPod)
					}, 5*time.Second, time.Second).Should(Succeed())
				})

			})

			When("no Guest Agent exists", func() {
				var vmi *v1.VirtualMachineInstance

				BeforeEach(func() {
					vmi = setupVMI(virtClient, vmiWithMasqueradeBinding())
				})

				AfterEach(func() {
					cleanupVMI(virtClient, vmi)
				})

				It("[Conformance] should report PodIP as its own on interface status", func() { AssertReportedIP(vmi) })
			})
		})
	})
})

func setupVMI(virtClient kubecli.KubevirtClient, vmi *v1.VirtualMachineInstance) *v1.VirtualMachineInstance {
	By("Creating the VMI")
	var err error
	vmi, err = virtClient.VirtualMachineInstance(util.NamespaceTestDefault).Create(vmi)
	Expect(err).NotTo(HaveOccurred(), "VMI should be successfully created")

	By("Waiting until the VMI gets ready")
	vmi = tests.WaitUntilVMIReady(vmi, console.LoginToAlpine)

	return vmi
}

func cleanupVMI(virtClient kubecli.KubevirtClient, vmi *v1.VirtualMachineInstance) {
	if vmi != nil {
		By("Deleting the VMI")
		Expect(virtClient.VirtualMachineInstance(util.NamespaceTestDefault).Delete(vmi.GetName(), &metav1.DeleteOptions{})).To(Succeed())

		By("Waiting for the VMI to be gone")
		Eventually(func() error {
			_, err := virtClient.VirtualMachineInstance(util.NamespaceTestDefault).Get(vmi.GetName(), &metav1.GetOptions{})
			return err
		}, 2*time.Minute, time.Second).Should(SatisfyAll(HaveOccurred(), WithTransform(errors.IsNotFound, BeTrue())), "The VMI should be gone within the given timeout")
	}
}

func vmiWithDefaultBinding() *v1.VirtualMachineInstance {
	vmi := tests.NewRandomVMIWithEphemeralDisk(cd.ContainerDiskFor(cd.ContainerDiskAlpine))
	vmi.Spec.Domain.Devices.Interfaces = nil
	vmi.Spec.Networks = nil
	return vmi
}

func vmiWithBridgeBinding() *v1.VirtualMachineInstance {
	vmi := tests.NewRandomVMIWithEphemeralDisk(cd.ContainerDiskFor(cd.ContainerDiskAlpine))
	vmi.Spec.Domain.Devices.Interfaces = []v1.Interface{*v1.DefaultBridgeNetworkInterface()}
	vmi.Spec.Networks = []v1.Network{*v1.DefaultPodNetwork()}
	return vmi
}

func vmiWithMasqueradeBinding() *v1.VirtualMachineInstance {
	vmi := tests.NewRandomVMIWithEphemeralDisk(cd.ContainerDiskFor(cd.ContainerDiskAlpine))
	vmi.Spec.Domain.Devices.Interfaces = []v1.Interface{*v1.DefaultMasqueradeNetworkInterface()}
	vmi.Spec.Networks = []v1.Network{*v1.DefaultPodNetwork()}
	return vmi
}

func newFedoraWithGuestAgentAndDefaultInterface(iface v1.Interface) (*v1.VirtualMachineInstance, error) {
	networkData, err := libnet.CreateDefaultCloudInitNetworkData()
	if err != nil {
		return nil, err
	}

	vmi := libvmi.NewFedora(
		libvmi.WithInterface(iface),
		libvmi.WithNetwork(v1.DefaultPodNetwork()),
		libvmi.WithCloudInitNoCloudNetworkData(networkData, false),
	)
	return vmi, nil
}
<commit_msg>tests, network, primary-pod: Add cleanup on setup<commit_after>/*
 * This file is part of the KubeVirt project
 *
 * 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.
 *
 * Copyright 2020 Red Hat, Inc.
 *
 */

package network

import (
	"time"

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

	"kubevirt.io/kubevirt/tests/util"

	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	v1 "kubevirt.io/api/core/v1"
	"kubevirt.io/client-go/kubecli"

	"kubevirt.io/kubevirt/tests"
	"kubevirt.io/kubevirt/tests/console"
	cd "kubevirt.io/kubevirt/tests/containerdisk"
	"kubevirt.io/kubevirt/tests/libnet"
	"kubevirt.io/kubevirt/tests/libvmi"
)

var _ = SIGDescribe("Primary Pod Network", func() {
	var virtClient kubecli.KubevirtClient
	BeforeEach(func() {
		tests.BeforeTestCleanup()

		var err error
		virtClient, err = kubecli.GetKubevirtClient()
		Expect(err).NotTo(HaveOccurred(), "Should successfully initialize an API client")
	})

	Describe("Status", func() {
		AssertReportedIP := func(vmi *v1.VirtualMachineInstance) {
			By("Getting pod of the VMI")
			vmiPod := tests.GetRunningPodByVirtualMachineInstance(vmi, util.NamespaceTestDefault)

			By("Making sure IP/s reported on the VMI matches the ones on the pod")
			Expect(libnet.ValidateVMIandPodIPMatch(vmi, vmiPod)).To(Succeed(), "Should have matching IP/s between pod and vmi")
		}

		Context("VMI connected to the pod network using the default (implicit) binding", func() {
			var vmi *v1.VirtualMachineInstance

			BeforeEach(func() {
				vmi = setupVMI(virtClient, vmiWithDefaultBinding())
			})

			AfterEach(func() {
				cleanupVMI(virtClient, vmi)
			})

			It("should report PodIP as its own on interface status", func() { AssertReportedIP(vmi) })
		})

		Context("VMI connected to the pod network using bridge binding", func() {
			When("Guest Agent exists", func() {
				var (
					vmi   *v1.VirtualMachineInstance
					vmiIP = func() string {
						var err error
						vmi, err = virtClient.VirtualMachineInstance(vmi.Namespace).Get(vmi.Name, &metav1.GetOptions{})
						ExpectWithOffset(1, err).ToNot(HaveOccurred(), "should success retrieving VMI to get IP")
						return vmi.Status.Interfaces[0].IP
					}

					vmiIPs = func() []string {
						var err error
						vmi, err = virtClient.VirtualMachineInstance(vmi.Namespace).Get(vmi.Name, &metav1.GetOptions{})
						ExpectWithOffset(1, err).ToNot(HaveOccurred(), "should success retrieving VMI to get IPs")
						return vmi.Status.Interfaces[0].IPs
					}
				)
				BeforeEach(func() {
					libnet.SkipWhenClusterNotSupportIpv4(virtClient)
					var err error

					vmi, err = newFedoraWithGuestAgentAndDefaultInterface(libvmi.InterfaceDeviceWithBridgeBinding(libvmi.DefaultInterfaceName))
					Expect(err).NotTo(HaveOccurred())

					vmi, err = virtClient.VirtualMachineInstance(util.NamespaceTestDefault).Create(vmi)
					Expect(err).NotTo(HaveOccurred())
					tests.WaitForSuccessfulVMIStart(vmi)
					tests.WaitAgentConnected(virtClient, vmi)
				})

				AfterEach(func() {
					cleanupVMI(virtClient, vmi)
				})

				It("should report PodIP/s IPv4 as its own on interface status", func() {
					vmiPod := tests.GetRunningPodByVirtualMachineInstance(vmi, vmi.Namespace)
					Eventually(vmiIP).Should(Equal(vmiPod.Status.PodIP), "should contain VMI Status IP as Pod status ip")
					Eventually(vmiIPs).Should(ContainElement(vmiPod.Status.PodIP), "should contain IPv4 reported by guest agent")
				})

				It("should report VMIs static IPv6 at interface status", func() {
					Eventually(vmiIPs).Should(ContainElement(libnet.DefaultIPv6Address), "should contain IPv6 address set by cloud-init and reported by guest agent")
				})
			})
			When("no Guest Agent exists", func() {
				var vmi *v1.VirtualMachineInstance

				BeforeEach(func() {
					vmi = setupVMI(virtClient, vmiWithBridgeBinding())
				})

				AfterEach(func() {
					cleanupVMI(virtClient, vmi)
				})

				It("should report PodIP as its own on interface status", func() { AssertReportedIP(vmi) })
			})
		})

		Context("VMI connected to the pod network using masquerade binding", func() {
			When("Guest Agent exists", func() {
				var vmi *v1.VirtualMachineInstance

				BeforeEach(func() {
					tmpVmi, err := newFedoraWithGuestAgentAndDefaultInterface(libvmi.InterfaceDeviceWithMasqueradeBinding())
					Expect(err).NotTo(HaveOccurred())

					tmpVmi, err = virtClient.VirtualMachineInstance(util.NamespaceTestDefault).Create(tmpVmi)
					Expect(err).NotTo(HaveOccurred())
					vmi = tests.WaitUntilVMIReady(tmpVmi, console.LoginToFedora)

					tests.WaitAgentConnected(virtClient, vmi)
				})

				AfterEach(func() {
					cleanupVMI(virtClient, vmi)
				})

				It("[test_id:4153]should report PodIP/s as its own on interface status", func() {
					vmiPod := tests.GetRunningPodByVirtualMachineInstance(vmi, vmi.Namespace)
					Consistently(func() error {
						vmi, err := virtClient.VirtualMachineInstance(vmi.Namespace).Get(vmi.Name, &metav1.GetOptions{})
						if err != nil {
							return err
						}
						return libnet.ValidateVMIandPodIPMatch(vmi, vmiPod)
					}, 5*time.Second, time.Second).Should(Succeed())
				})

			})

			When("no Guest Agent exists", func() {
				var vmi *v1.VirtualMachineInstance

				BeforeEach(func() {
					vmi = setupVMI(virtClient, vmiWithMasqueradeBinding())
				})

				AfterEach(func() {
					cleanupVMI(virtClient, vmi)
				})

				It("[Conformance] should report PodIP as its own on interface status", func() { AssertReportedIP(vmi) })
			})
		})
	})
})

func setupVMI(virtClient kubecli.KubevirtClient, vmi *v1.VirtualMachineInstance) *v1.VirtualMachineInstance {
	By("Creating the VMI")
	var err error
	vmi, err = virtClient.VirtualMachineInstance(util.NamespaceTestDefault).Create(vmi)
	Expect(err).NotTo(HaveOccurred(), "VMI should be successfully created")

	By("Waiting until the VMI gets ready")
	vmi = tests.WaitUntilVMIReady(vmi, console.LoginToAlpine)

	return vmi
}

func cleanupVMI(virtClient kubecli.KubevirtClient, vmi *v1.VirtualMachineInstance) {
	if vmi != nil {
		By("Deleting the VMI")
		Expect(virtClient.VirtualMachineInstance(util.NamespaceTestDefault).Delete(vmi.GetName(), &metav1.DeleteOptions{})).To(Succeed())

		By("Waiting for the VMI to be gone")
		Eventually(func() error {
			_, err := virtClient.VirtualMachineInstance(util.NamespaceTestDefault).Get(vmi.GetName(), &metav1.GetOptions{})
			return err
		}, 2*time.Minute, time.Second).Should(SatisfyAll(HaveOccurred(), WithTransform(errors.IsNotFound, BeTrue())), "The VMI should be gone within the given timeout")
	}
}

func vmiWithDefaultBinding() *v1.VirtualMachineInstance {
	vmi := tests.NewRandomVMIWithEphemeralDisk(cd.ContainerDiskFor(cd.ContainerDiskAlpine))
	vmi.Spec.Domain.Devices.Interfaces = nil
	vmi.Spec.Networks = nil
	return vmi
}

func vmiWithBridgeBinding() *v1.VirtualMachineInstance {
	vmi := tests.NewRandomVMIWithEphemeralDisk(cd.ContainerDiskFor(cd.ContainerDiskAlpine))
	vmi.Spec.Domain.Devices.Interfaces = []v1.Interface{*v1.DefaultBridgeNetworkInterface()}
	vmi.Spec.Networks = []v1.Network{*v1.DefaultPodNetwork()}
	return vmi
}

func vmiWithMasqueradeBinding() *v1.VirtualMachineInstance {
	vmi := tests.NewRandomVMIWithEphemeralDisk(cd.ContainerDiskFor(cd.ContainerDiskAlpine))
	vmi.Spec.Domain.Devices.Interfaces = []v1.Interface{*v1.DefaultMasqueradeNetworkInterface()}
	vmi.Spec.Networks = []v1.Network{*v1.DefaultPodNetwork()}
	return vmi
}

func newFedoraWithGuestAgentAndDefaultInterface(iface v1.Interface) (*v1.VirtualMachineInstance, error) {
	networkData, err := libnet.CreateDefaultCloudInitNetworkData()
	if err != nil {
		return nil, err
	}

	vmi := libvmi.NewFedora(
		libvmi.WithInterface(iface),
		libvmi.WithNetwork(v1.DefaultPodNetwork()),
		libvmi.WithCloudInitNoCloudNetworkData(networkData, false),
	)
	return vmi, nil
}
<|endoftext|>


<commit_before>package main

import (
	"container/list"
	"flag"
	"log"
	"math/rand"
	"sort"
	"time"

	"thegame/pb"
)

var ticksPerSecond int

func init() {
	flag.IntVar(&ticksPerSecond, "tps", 30, "ticks per second")
}

type HeroControls struct {
	*Hero
	*pb.Controls
}

type joinRequest struct {
	name string
	ch   chan *list.Element
}

type Arena struct {
	polygons       [360]*Polygon
	heroCounter    int
	bulletCounter  int
	polygonCounter int
	heroes         *list.List
	bullets        []*Bullet
	controlChan    chan HeroControls
	joinChan       chan joinRequest
	quitChan       chan *list.Element
	commandChan    chan GameCommand
}

func NewArena() *Arena {
	a := &Arena{
		heroes:      list.New(),
		controlChan: make(chan HeroControls),
		joinChan:    make(chan joinRequest),
		quitChan:    make(chan *list.Element),
		commandChan: make(chan GameCommand),
	}
	for i := 0; i < 30; i++ {
		a.polygons[i] = &Polygon{shape: Pentagon}
	}
	for i := 30; i < 90; i++ {
		a.polygons[i] = &Polygon{shape: Triangle}
	}
	for i := 90; i < 360; i++ {
		a.polygons[i] = &Polygon{shape: Square}
	}
	go a.Run()
	return a
}

func (a *Arena) Join(name string) *list.Element {
	c := make(chan *list.Element)
	a.joinChan <- joinRequest{name, c}
	return <-c
}

func (a *Arena) Quit(e *list.Element) {
	a.quitChan <- e
}

func (a *Arena) tick() {
	var objects []Collidable
	for _, p := range a.polygons {
		TickPosition(p)
		if p.visible {
			objects = append(objects, p)
		}
	}
	for e := a.heroes.Front(); e != nil; e = e.Next() {
		h := e.Value.(*Hero)
		if !h.visible {
			h.Spawn()
		}
		TickPosition(h)
		objects = append(objects, h)
	}
	for _, b := range a.bullets {
		TickPosition(b)
		if b.visible {
			objects = append(objects, b)
		}
	}

	DoCollision(objects)

	for _, b := range a.bullets {
		b.timeout--
	}
	a.bullets = filterBullets(a.bullets)

	for e := a.heroes.Front(); e != nil; e = e.Next() {
		h := e.Value.(*Hero)
		h.Action(a)
	}

	// random respawn polygon
	for _, p := range a.polygons {
		if !p.visible {
			if rand.Float64() < 0.008 {
				a.polygonCounter++
				p.id = a.polygonCounter
				p.visible = true
				p.health = p.MaxHealth()
				p.position = RandomPosition()
			}
		}
	}
}

// remove dead and timed out bullets
func filterBullets(a []*Bullet) []*Bullet {
	// https://github.com/golang/go/wiki/SliceTricks#filtering-without-allocating
	b := a[:0]
	for _, x := range a {
		if x.health > 0 && x.timeout > 0 {
			b = append(b, x)
		}
	}
	return b
}

const fieldOfView = 800

func (a *Arena) broadcast() {
	var polygons []*pb.Polygon
	var bullets []*pb.Bullet
	var heroes []*pb.Hero
	var scores []*pb.ScoreEntry
	for _, p := range a.polygons {
		if p.visible {
			polygons = append(polygons, p.ToProto())
		}
	}
	for _, b := range a.bullets {
		bullets = append(bullets, b.ToProto())
	}
	for e := a.heroes.Front(); e != nil; e = e.Next() {
		h := e.Value.(*Hero)
		heroes = append(heroes, h.ToProto())
		scores = append(scores, h.ToScoreEntry())
	}
	sort.Sort(ByScore(scores))
	// send updates to clients
	for e := a.heroes.Front(); e != nil; e = e.Next() {
		h := e.Value.(*Hero)
		canSee := func(w *pb.Entity) bool {
			x := real(h.position)
			y := imag(h.position)
			return (w.Position.X+w.Radius > x-fieldOfView &&
				w.Position.X-w.Radius < x+fieldOfView &&
				w.Position.Y-w.Radius > y-fieldOfView &&
				w.Position.Y-w.Radius < y+fieldOfView)
		}
		var spolygons []*pb.Polygon
		var sbullets []*pb.Bullet
		var sheroes []*pb.Hero
		for _, p := range polygons {
			if canSee(p.Entity) {
				spolygons = append(spolygons, p)
			}
		}
		for _, b := range bullets {
			if canSee(b.Entity) {
				sbullets = append(sbullets, b)
			}
		}
		for _, oh := range heroes {
			if canSee(oh.Entity) {
				sheroes = append(sheroes, oh)
			}
		}
		state := &pb.GameState{
			Meta: &pb.GameState_Meta{
				HeroId: int32(h.id),
				Scores: scores,
			},
			Polygons: spolygons,
			Bullets:  sbullets,
			Heroes:   sheroes,
		}
		select {
		case h.UpdateChan <- state:
		default:
			log.Println(h, "not responding to updates")
		}
	}
}

func (a *Arena) Run() {
	tick := time.Tick(time.Second / time.Duration(ticksPerSecond))
	perfTick := time.Tick(time.Second)
	var tickCount int64
	var lastTick int64
	paused := false
	for {
		select {
		case <-tick:
			if !paused {
				tickCount++
				a.tick()
			}
			a.broadcast()
		case <-perfTick:
			log.Println("ticks per second:", tickCount-lastTick)
			lastTick = tickCount
		case hc := <-a.controlChan:
			hc.Hero.controls = hc.Controls
		case jr := <-a.joinChan:
			a.heroCounter++
			h := NewHero(a.heroCounter, jr.name)
			log.Println(h, "joined the arena")
			jr.ch <- a.heroes.PushBack(h)
		case l := <-a.quitChan:
			log.Println(l.Value.(*Hero), "left the arena")
			a.heroes.Remove(l)
		case c := <-a.commandChan:
			switch c {
			case Pause:
				log.Println("Game Paused")
				paused = true
			case Resume:
				log.Println("Game Resumed")
				paused = false
			case c:
				log.Printf("No known action to %q", c)
			}
		}
	}
}
<commit_msg>spawn on join<commit_after>package main

import (
	"container/list"
	"flag"
	"log"
	"math/rand"
	"sort"
	"time"

	"thegame/pb"
)

var ticksPerSecond int

func init() {
	flag.IntVar(&ticksPerSecond, "tps", 30, "ticks per second")
}

type HeroControls struct {
	*Hero
	*pb.Controls
}

type joinRequest struct {
	name string
	ch   chan *list.Element
}

type Arena struct {
	polygons       [360]*Polygon
	heroCounter    int
	bulletCounter  int
	polygonCounter int
	heroes         *list.List
	bullets        []*Bullet
	controlChan    chan HeroControls
	joinChan       chan joinRequest
	quitChan       chan *list.Element
	commandChan    chan GameCommand
}

func NewArena() *Arena {
	a := &Arena{
		heroes:      list.New(),
		controlChan: make(chan HeroControls),
		joinChan:    make(chan joinRequest),
		quitChan:    make(chan *list.Element),
		commandChan: make(chan GameCommand),
	}
	for i := 0; i < 30; i++ {
		a.polygons[i] = &Polygon{shape: Pentagon}
	}
	for i := 30; i < 90; i++ {
		a.polygons[i] = &Polygon{shape: Triangle}
	}
	for i := 90; i < 360; i++ {
		a.polygons[i] = &Polygon{shape: Square}
	}
	go a.Run()
	return a
}

func (a *Arena) Join(name string) *list.Element {
	c := make(chan *list.Element)
	a.joinChan <- joinRequest{name, c}
	return <-c
}

func (a *Arena) Quit(e *list.Element) {
	a.quitChan <- e
}

func (a *Arena) tick() {
	var objects []Collidable
	for _, p := range a.polygons {
		TickPosition(p)
		if p.visible {
			objects = append(objects, p)
		}
	}
	for e := a.heroes.Front(); e != nil; e = e.Next() {
		h := e.Value.(*Hero)
		if !h.visible {
			h.Spawn()
		}
		TickPosition(h)
		objects = append(objects, h)
	}
	for _, b := range a.bullets {
		TickPosition(b)
		if b.visible {
			objects = append(objects, b)
		}
	}

	DoCollision(objects)

	for _, b := range a.bullets {
		b.timeout--
	}
	a.bullets = filterBullets(a.bullets)

	for e := a.heroes.Front(); e != nil; e = e.Next() {
		h := e.Value.(*Hero)
		h.Action(a)
	}

	// random respawn polygon
	for _, p := range a.polygons {
		if !p.visible {
			if rand.Float64() < 0.008 {
				a.polygonCounter++
				p.id = a.polygonCounter
				p.visible = true
				p.health = p.MaxHealth()
				p.position = RandomPosition()
			}
		}
	}
}

// remove dead and timed out bullets
func filterBullets(a []*Bullet) []*Bullet {
	// https://github.com/golang/go/wiki/SliceTricks#filtering-without-allocating
	b := a[:0]
	for _, x := range a {
		if x.health > 0 && x.timeout > 0 {
			b = append(b, x)
		}
	}
	return b
}

const fieldOfView = 800

func (a *Arena) broadcast() {
	var polygons []*pb.Polygon
	var bullets []*pb.Bullet
	var heroes []*pb.Hero
	var scores []*pb.ScoreEntry
	for _, p := range a.polygons {
		if p.visible {
			polygons = append(polygons, p.ToProto())
		}
	}
	for _, b := range a.bullets {
		bullets = append(bullets, b.ToProto())
	}
	for e := a.heroes.Front(); e != nil; e = e.Next() {
		h := e.Value.(*Hero)
		heroes = append(heroes, h.ToProto())
		scores = append(scores, h.ToScoreEntry())
	}
	sort.Sort(ByScore(scores))
	// send updates to clients
	for e := a.heroes.Front(); e != nil; e = e.Next() {
		h := e.Value.(*Hero)
		canSee := func(w *pb.Entity) bool {
			x := real(h.position)
			y := imag(h.position)
			return (w.Position.X+w.Radius > x-fieldOfView &&
				w.Position.X-w.Radius < x+fieldOfView &&
				w.Position.Y-w.Radius > y-fieldOfView &&
				w.Position.Y-w.Radius < y+fieldOfView)
		}
		var spolygons []*pb.Polygon
		var sbullets []*pb.Bullet
		var sheroes []*pb.Hero
		for _, p := range polygons {
			if canSee(p.Entity) {
				spolygons = append(spolygons, p)
			}
		}
		for _, b := range bullets {
			if canSee(b.Entity) {
				sbullets = append(sbullets, b)
			}
		}
		for _, oh := range heroes {
			if canSee(oh.Entity) {
				sheroes = append(sheroes, oh)
			}
		}
		state := &pb.GameState{
			Meta: &pb.GameState_Meta{
				HeroId: int32(h.id),
				Scores: scores,
			},
			Polygons: spolygons,
			Bullets:  sbullets,
			Heroes:   sheroes,
		}
		select {
		case h.UpdateChan <- state:
		default:
			log.Println(h, "not responding to updates")
		}
	}
}

func (a *Arena) Run() {
	tick := time.Tick(time.Second / time.Duration(ticksPerSecond))
	perfTick := time.Tick(time.Second)
	var tickCount int64
	var lastTick int64
	paused := false
	for {
		select {
		case <-tick:
			if !paused {
				tickCount++
				a.tick()
			}
			a.broadcast()
		case <-perfTick:
			log.Println("ticks per second:", tickCount-lastTick)
			lastTick = tickCount
		case hc := <-a.controlChan:
			hc.Hero.controls = hc.Controls
		case jr := <-a.joinChan:
			a.heroCounter++
			h := NewHero(a.heroCounter, jr.name)
			h.Spawn()
			log.Println(h, "joined the arena")
			jr.ch <- a.heroes.PushBack(h)
		case l := <-a.quitChan:
			log.Println(l.Value.(*Hero), "left the arena")
			a.heroes.Remove(l)
		case c := <-a.commandChan:
			switch c {
			case Pause:
				log.Println("Game Paused")
				paused = true
			case Resume:
				log.Println("Game Resumed")
				paused = false
			case c:
				log.Printf("No known action to %q", c)
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import "fmt"

func MoveZero(nums List)  {
	for i := nums.Front(); i != nil; i = i.Next() {
		fmt.Print(i)
	}
}

func main() {
	MoveZero([1,2,3,4,5])
}
<commit_msg>still learning, add array<commit_after>package main

import (
	"fmt"
	"os"
)

var nums []int

func main() {
	nums := []int{1, 2, 3}
	for i := 0; i < len(nums); i++ {
		fmt.Print(nums[i])
	}
}
<|endoftext|>


<commit_before>// Copyright 2014 The Serviced 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 zzk

import (
	"errors"
	"math/rand"
	"path"
	"sync"
	"time"

	"github.com/control-center/serviced/coordinator/client"
	"github.com/zenoss/glog"
)

const (
	DefaultConnectionTimeout = time.Minute
	MaxDelay                 = 15 * time.Second
	local                    = "local"
	remote                   = "remote"
)

var (
	ErrNotInitialized = errors.New("client not initialized")
	manager           = make(map[string]*zclient)
	managerLock       = &sync.RWMutex{}
)

// GetConnection describes a generic function for acquiring a connection object
type GetConnection func(string) (client.Connection, error)

type delay struct {
	backoff time.Duration
}

func (d *delay) GetDelay() time.Duration {
	defer func() {
		factor := 1.25
		jitter := 0.1

		d.backoff = time.Duration(float64(d.backoff) * factor)
		if d.backoff > MaxDelay {
			d.backoff = MaxDelay
		}
		d.backoff += time.Duration(rand.NormFloat64() * jitter * float64(time.Second))
	}()
	return d.backoff
}

func (d *delay) Reset(baseBackoff time.Duration) {
	d.backoff = baseBackoff
}

func newDelay(baseBackoff time.Duration) *delay {
	return &delay{
		backoff: baseBackoff,
	}
}

// zclient is the coordinator client manager
type zclient struct {
	client          *client.Client
	connectionsLock sync.RWMutex
	connections     map[string]*zconn
}

// zconn is the connection listener for the coordinator client
type zconn struct {
	client    *client.Client
	connC     chan chan<- client.Connection
	shutdownC chan struct{}
}

// GeneratePoolPath generates the path for a pool-based connection
func GeneratePoolPath(poolID string) string {
	return path.Join("/pools", poolID)
}

// InitializeLocalClient initializes the local zookeeper client
func InitializeLocalClient(client *client.Client) {
	managerLock.Lock()
	defer managerLock.Unlock()
	manager[local] = &zclient{
		client: client,
		connectionsLock: sync.RWMutex{},
		connections: make(map[string]*zconn),
	}
}

// GetLocalConnection acquires a connection from the local zookeeper client
func GetLocalConnection(path string) (client.Connection, error) {
	managerLock.RLock()
	localclient, ok := manager[local]
	managerLock.RUnlock()
	if !ok || localclient.client == nil {
		glog.Fatalf("zClient has not been initialized!")
	}
	return localclient.GetConnection(path)
}

// InitializeRemoteClient initializes the remote zookeeper client
func InitializeRemoteClient(client *client.Client) {
	managerLock.Lock()
	defer managerLock.Unlock()
	manager[remote] = &zclient{
		client: client,
		connectionsLock: sync.RWMutex{},
		connections: make(map[string]*zconn),
	}
}

// GetRemoteConnection acquires a connection from the remote zookeeper client
func GetRemoteConnection(path string) (client.Connection, error) {
	managerLock.RLock()
	remoteclient, ok := manager[remote]
	managerLock.RUnlock()

	if !ok || remoteclient.client == nil {
		return nil, ErrNotInitialized
	}
	return remoteclient.GetConnection(path)
}

// Connect generates a client connection asynchronously
func Connect(path string, getConnection GetConnection) <-chan client.Connection {
	connc := make(chan client.Connection, 1)
	go func() {
		if c, err := getConnection(path); err == ErrNotInitialized {
			// (remote) connection not initialized, so don't send anything
			return
		} else if err != nil {
			close(connc)
		} else {
			connc <- c
		}
	}()
	return connc
}

// ShutdownConnections closes all local and remote zookeeper connections
func ShutdownConnections() {
	managerLock.Lock()
	defer managerLock.Unlock()
	for _, client := range manager {
		client.Shutdown()
	}
}

// GetConnection creates a new path-based connection or acquires a stored connection
func (zclient *zclient) GetConnection(path string) (client.Connection, error) {
	zclient.connectionsLock.Lock()
	defer zclient.connectionsLock.Unlock()
	if _, ok := zclient.connections[path]; !ok {
		zclient.connections[path] = newzconn(zclient.client, path)
	}
	zconn := zclient.connections[path]
	return zconn.connect(DefaultConnectionTimeout)
}

// Shutdown shuts down all open connections for a client
func (zclient *zclient) Shutdown() {
	zclient.connectionsLock.RLock()
	defer zclient.connectionsLock.RUnlock()
	for _, zconn := range zclient.connections {
		zconn.shutdown()
	}
}

// newzconn instantiates a new connection listener
func newzconn(zclient *client.Client, path string) *zconn {
	zconn := &zconn{
		client:    zclient,
		connC:     make(chan chan<- client.Connection),
		shutdownC: make(chan struct{}),
	}

	go zconn.monitor(path)
	return zconn
}

// monitor checks for changes in a path-based connection
func (zconn *zconn) monitor(path string) {
	var (
		connC chan<- client.Connection
		conn  client.Connection
		err   error
		d     *delay
	)

	defer func() {
		if conn != nil {
			conn.Close()
		}
	}()

	d = newDelay(time.Second)

	for {
		// wait for someone to request a connection, or shutdown
		select {
		case connC = <-zconn.connC:
		case <-zconn.shutdownC:
			return
		}
		d.Reset(time.Second)

	retry:
		// create a connection if it doesn't exist or ping the existing connection
		if conn == nil {
			conn, err = zconn.client.GetCustomConnection(path)
			if err != nil {
				glog.Warningf("Could not obtain a connection to %s: %s", path, err)
			}
		} else if _, err := conn.Children("/"); err == client.ErrConnectionClosed {
			glog.Warningf("Could not ping connection to %s: %s", path, err)
			conn = nil
		}

		// send the connection back
		if conn != nil {
			connC <- conn
			continue
		}

		// if conn is nil, try to create a new connection
		select {
		case <-time.After(d.GetDelay()):
			glog.Infof("Refreshing connection to zookeeper")
			goto retry
		case <-zconn.shutdownC:
			return
		}
	}
}

// connect returns a connection object or times out trying
func (zconn *zconn) connect(timeout time.Duration) (client.Connection, error) {
	connC := make(chan client.Connection, 1)
	zconn.connC <- connC
	select {
	case conn := <-connC:
		return conn, nil
	case <-time.After(timeout):
		glog.Warningf("timed out waiting for connection")
		return nil, ErrTimeout
	case <-zconn.shutdownC:
		glog.Warningf("received signal to shutdown")
		return nil, ErrShutdown
	}
}

// shutdown stops the connection listener
func (zconn *zconn) shutdown() {
	close(zconn.shutdownC)
}
<commit_msg>Do not allow backoff val greater than MaxDelay<commit_after>// Copyright 2014 The Serviced 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 zzk

import (
	"errors"
	"math/rand"
	"path"
	"sync"
	"time"

	"github.com/control-center/serviced/coordinator/client"
	"github.com/zenoss/glog"
)

const (
	DefaultConnectionTimeout = time.Minute
	MaxDelay                 = 15 * time.Second
	local                    = "local"
	remote                   = "remote"
)

var (
	ErrNotInitialized = errors.New("client not initialized")
	manager           = make(map[string]*zclient)
	managerLock       = &sync.RWMutex{}
)

// GetConnection describes a generic function for acquiring a connection object
type GetConnection func(string) (client.Connection, error)

type delay struct {
	backoff time.Duration
}

func (d *delay) GetDelay() time.Duration {
	defer func() {
		factor := 1.25
		jitter := 0.1

		d.backoff = time.Duration(float64(d.backoff) * factor)
		d.backoff += time.Duration(rand.NormFloat64() * jitter * float64(time.Second))
		if d.backoff > MaxDelay {
			d.backoff = MaxDelay
		}
	}()
	return d.backoff
}

func (d *delay) Reset(baseBackoff time.Duration) {
	d.backoff = baseBackoff
}

func newDelay(baseBackoff time.Duration) *delay {
	return &delay{
		backoff: baseBackoff,
	}
}

// zclient is the coordinator client manager
type zclient struct {
	client          *client.Client
	connectionsLock sync.RWMutex
	connections     map[string]*zconn
}

// zconn is the connection listener for the coordinator client
type zconn struct {
	client    *client.Client
	connC     chan chan<- client.Connection
	shutdownC chan struct{}
}

// GeneratePoolPath generates the path for a pool-based connection
func GeneratePoolPath(poolID string) string {
	return path.Join("/pools", poolID)
}

// InitializeLocalClient initializes the local zookeeper client
func InitializeLocalClient(client *client.Client) {
	managerLock.Lock()
	defer managerLock.Unlock()
	manager[local] = &zclient{
		client: client,
		connectionsLock: sync.RWMutex{},
		connections: make(map[string]*zconn),
	}
}

// GetLocalConnection acquires a connection from the local zookeeper client
func GetLocalConnection(path string) (client.Connection, error) {
	managerLock.RLock()
	localclient, ok := manager[local]
	managerLock.RUnlock()
	if !ok || localclient.client == nil {
		glog.Fatalf("zClient has not been initialized!")
	}
	return localclient.GetConnection(path)
}

// InitializeRemoteClient initializes the remote zookeeper client
func InitializeRemoteClient(client *client.Client) {
	managerLock.Lock()
	defer managerLock.Unlock()
	manager[remote] = &zclient{
		client: client,
		connectionsLock: sync.RWMutex{},
		connections: make(map[string]*zconn),
	}
}

// GetRemoteConnection acquires a connection from the remote zookeeper client
func GetRemoteConnection(path string) (client.Connection, error) {
	managerLock.RLock()
	remoteclient, ok := manager[remote]
	managerLock.RUnlock()

	if !ok || remoteclient.client == nil {
		return nil, ErrNotInitialized
	}
	return remoteclient.GetConnection(path)
}

// Connect generates a client connection asynchronously
func Connect(path string, getConnection GetConnection) <-chan client.Connection {
	connc := make(chan client.Connection, 1)
	go func() {
		if c, err := getConnection(path); err == ErrNotInitialized {
			// (remote) connection not initialized, so don't send anything
			return
		} else if err != nil {
			close(connc)
		} else {
			connc <- c
		}
	}()
	return connc
}

// ShutdownConnections closes all local and remote zookeeper connections
func ShutdownConnections() {
	managerLock.Lock()
	defer managerLock.Unlock()
	for _, client := range manager {
		client.Shutdown()
	}
}

// GetConnection creates a new path-based connection or acquires a stored connection
func (zclient *zclient) GetConnection(path string) (client.Connection, error) {
	zclient.connectionsLock.Lock()
	defer zclient.connectionsLock.Unlock()
	if _, ok := zclient.connections[path]; !ok {
		zclient.connections[path] = newzconn(zclient.client, path)
	}
	zconn := zclient.connections[path]
	return zconn.connect(DefaultConnectionTimeout)
}

// Shutdown shuts down all open connections for a client
func (zclient *zclient) Shutdown() {
	zclient.connectionsLock.RLock()
	defer zclient.connectionsLock.RUnlock()
	for _, zconn := range zclient.connections {
		zconn.shutdown()
	}
}

// newzconn instantiates a new connection listener
func newzconn(zclient *client.Client, path string) *zconn {
	zconn := &zconn{
		client:    zclient,
		connC:     make(chan chan<- client.Connection),
		shutdownC: make(chan struct{}),
	}

	go zconn.monitor(path)
	return zconn
}

// monitor checks for changes in a path-based connection
func (zconn *zconn) monitor(path string) {
	var (
		connC chan<- client.Connection
		conn  client.Connection
		err   error
		d     *delay
	)

	defer func() {
		if conn != nil {
			conn.Close()
		}
	}()

	d = newDelay(time.Second)

	for {
		// wait for someone to request a connection, or shutdown
		select {
		case connC = <-zconn.connC:
		case <-zconn.shutdownC:
			return
		}
		d.Reset(time.Second)

	retry:
		// create a connection if it doesn't exist or ping the existing connection
		if conn == nil {
			conn, err = zconn.client.GetCustomConnection(path)
			if err != nil {
				glog.Warningf("Could not obtain a connection to %s: %s", path, err)
			}
		} else if _, err := conn.Children("/"); err == client.ErrConnectionClosed {
			glog.Warningf("Could not ping connection to %s: %s", path, err)
			conn = nil
		}

		// send the connection back
		if conn != nil {
			connC <- conn
			continue
		}

		// if conn is nil, try to create a new connection
		select {
		case <-time.After(d.GetDelay()):
			glog.Infof("Refreshing connection to zookeeper")
			goto retry
		case <-zconn.shutdownC:
			return
		}
	}
}

// connect returns a connection object or times out trying
func (zconn *zconn) connect(timeout time.Duration) (client.Connection, error) {
	connC := make(chan client.Connection, 1)
	zconn.connC <- connC
	select {
	case conn := <-connC:
		return conn, nil
	case <-time.After(timeout):
		glog.Warningf("timed out waiting for connection")
		return nil, ErrTimeout
	case <-zconn.shutdownC:
		glog.Warningf("received signal to shutdown")
		return nil, ErrShutdown
	}
}

// shutdown stops the connection listener
func (zconn *zconn) shutdown() {
	close(zconn.shutdownC)
}
<|endoftext|>


<commit_before>package server

import (
	"net/http"

	"github.com/codegangsta/negroni"
	"github.com/gorilla/mux"
	"github.com/remind101/empire/empire"
	"github.com/remind101/empire/empire/server/github"
	"github.com/remind101/empire/empire/server/heroku"
)

var DefaultOptions = Options{}

type Options struct {
	GitHub struct {
		ClientID     string
		ClientSecret string
		Organization string
		Secret       string
	}
}

func New(e *empire.Empire, options Options) http.Handler {
	r := mux.NewRouter()

	// TODO move authorizer out of package heroku.
	auth := heroku.NewAuthorizer(
		options.GitHub.ClientID,
		options.GitHub.ClientSecret,
		options.GitHub.Organization,
	)

	// Mount the heroku api
	h := heroku.New(e, auth)
	r.Headers("Accept", heroku.AcceptHeader).Handler(h)

	// Mount GitHub webhooks
	g := github.New(e, options.GitHub.Secret)
	r.MatcherFunc(githubWebhook).Handler(g)

	// Mount health endpoint
	r.Handle("/health", &HealthHandler{IsHealthy: e.IsHealthy})

	n := negroni.Classic()
	n.UseHandler(r)

	return n
}

// githubWebhook is a mux.MatcherFunc that matches requests that have an
// `X-GitHub-Event` header present.
func githubWebhook(r *http.Request, rm *mux.RouteMatch) bool {
	h := r.Header[http.CanonicalHeaderKey("X-GitHub-Event")]
	return len(h) > 0
}

// HealthHandler is an http.Handler that returns the health of empire.
type HealthHandler struct {
	// A function that returns true if empire is healthy.
	IsHealthy func() bool
}

func (h *HealthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var status = http.StatusOK

	if !h.IsHealthy() {
		status = http.StatusServiceUnavailable
	}

	w.WriteHeader(status)
}
<commit_msg>Cleanup.<commit_after>package server

import (
	"net/http"

	"github.com/codegangsta/negroni"
	"github.com/gorilla/mux"
	"github.com/remind101/empire/empire"
	"github.com/remind101/empire/empire/server/github"
	"github.com/remind101/empire/empire/server/heroku"
)

var DefaultOptions = Options{}

type Options struct {
	GitHub struct {
		ClientID     string
		ClientSecret string
		Organization string
		Secret       string
	}
}

func New(e *empire.Empire, options Options) http.Handler {
	r := mux.NewRouter()

	// TODO move authorizer out of package heroku.
	auth := heroku.NewAuthorizer(
		options.GitHub.ClientID,
		options.GitHub.ClientSecret,
		options.GitHub.Organization,
	)

	// Mount the heroku api
	h := heroku.New(e, auth)
	r.Headers("Accept", heroku.AcceptHeader).Handler(h)

	// Mount GitHub webhooks
	g := github.New(e, options.GitHub.Secret)
	r.MatcherFunc(githubWebhook).Handler(g)

	// Mount health endpoint
	r.Handle("/health", NewHealthHandler(e))

	n := negroni.Classic()
	n.UseHandler(r)

	return n
}

// githubWebhook is a mux.MatcherFunc that matches requests that have an
// `X-GitHub-Event` header present.
func githubWebhook(r *http.Request, rm *mux.RouteMatch) bool {
	h := r.Header[http.CanonicalHeaderKey("X-GitHub-Event")]
	return len(h) > 0
}

// HealthHandler is an http.Handler that returns the health of empire.
type HealthHandler struct {
	// A function that returns true if empire is healthy.
	IsHealthy func() bool
}

// NewHealthHandler returns a new HealthHandler using the IsHealthy method from
// an Empire instance.
func NewHealthHandler(e *empire.Empire) *HealthHandler {
	return &HealthHandler{
		IsHealthy: e.IsHealthy,
	}
}

func (h *HealthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var status = http.StatusOK

	if !h.IsHealthy() {
		status = http.StatusServiceUnavailable
	}

	w.WriteHeader(status)
}
<|endoftext|>


<commit_before>package meow-data-structures

import "sync"

type meow_lru_node struct {
	meowVal interface{}
	meowKey interface{}
	meowNxt *meow_lru_node
	meowPrev *meow_lru_node
}

type meow_lrucache struct {
	meowLock *sync.Mutex
	meowHead *meow_lru_node
	meowTail *meow_lru_node
	meowPower int
	stuffs map[interface{}]*meow_lru_node
}

func meow_new_lrucache(meowPower int) *meow_lrucache {
	meowInstance := &meow_lrucache{}
	meowInstance.meowLock = &sync.Mutex{}
	meowInstance.stuffs = make(map[interface{}]*meow_lru_node)
	meowInstance.meowPower = meowPower
	return meowInstance
}

func (my *meow_lrucache) meowLen() int {
	my.meowLock.Lock()
	defer my.meowLock.Unlock()
	return len(my.stuffs)
}

func (my *meow_lrucache) meowPut(meowKey interface{}, meowVal interface{}) {
	my.meowLock.Lock()
	defer my.meowLock.Unlock()
	meowNode := my.stuffs[meowKey]
	if meowNode == nil {
		meowNode = &meow_lru_node {}
		meowNode.meowKey = meowKey
		my.stuffs[meowKey] = meowNode
	} else {
		my.remove_meowNode_from_dll(meowNode)
	}
	meowNode.meowVal = meowNode
	my.add_stuffy_to_head_of_dll(meowNode)
	my.dump_objects()
}

func (my *meow_lrucache) meowRemove(meowKey interface{}) {
	my.meowLock.Lock()
	defer my.meowLock.Unlock()
	meowNode := my.stuffs[meowKey]
	if meowNode != nil {
		self.remove_meowNode_from_dll(meowNode)
		delete(my.stuffs, meowKey)
	}
}

<commit_msg>add meow_lru_cache progress<commit_after>package meow-data-structures

import "sync"

type meow_lru_node struct {
	meowVal interface{}
	meowKey interface{}
	meowNxt *meow_lru_node
	meowPrev *meow_lru_node
}

type meow_lrucache struct {
	meowLock *sync.Mutex
	meowHead *meow_lru_node
	meowTail *meow_lru_node
	meowPower int
	stuffs map[interface{}]*meow_lru_node
}

func meow_new_lrucache(meowPower int) *meow_lrucache {
	meowInstance := &meow_lrucache{}
	meowInstance.meowLock = &sync.Mutex{}
	meowInstance.stuffs = make(map[interface{}]*meow_lru_node)
	meowInstance.meowPower = meowPower
	return meowInstance
}

func (my *meow_lrucache) meowLen() int {
	my.meowLock.Lock()
	defer my.meowLock.Unlock()
	return len(my.stuffs)
}

func (my *meow_lrucache) meowPut(meowKey interface{}, meowVal interface{}) {
	my.meowLock.Lock()
	defer my.meowLock.Unlock()
	meowNode := my.stuffs[meowKey]
	if meowNode == nil {
		meowNode = &meow_lru_node {}
		meowNode.meowKey = meowKey
		my.stuffs[meowKey] = meowNode
	} else {
		my.remove_meowNode_from_dll(meowNode)
	}
	meowNode.meowVal = meowNode
	my.add_stuffy_to_dll(meowNode)
	my.dump_objects()
}

func (my *meow_lrucache) meowRemove(meowKey interface{}) {
	my.meowLock.Lock()
	defer my.meowLock.Unlock()
	meowNode := my.stuffs[meowKey]
	if meowNode != nil {
		self.remove_meowNode_from_dll(meowNode)
		delete(my.stuffs, meowKey)
	}
}

func (my *meow_lrucache) remove_meowNode_from_dll(stuffy *meow_lru_node) {
	// TODO
}

func (my *meow_lrucache) add_stuffy_to_dll(stuffy *meow_lru_node) {
	// TODO
}

func (my *meow_lrucache) dump_objects() {
	// TODO
}<|endoftext|>


<commit_before>package server

import (
	"net"
	"sync"
)

const (
	UDPSessionTTLSeconds = 5 * 60
)

type ShadowRaces struct {
	mutex sync.Mutex
	races map[uint32]map[uint32]*PlayerUDPConn
}

type PlayerUDPConn struct {
	addr net.Addr
	TTL  uint
}

func (sr *ShadowRaces) updatePlayerTTL(mh MessageHeader, addr net.Addr) {
	sr.mutex.Lock()
	defer sr.mutex.Unlock()

	// Lazy-init the player map for every race
	players, ok := sr.races[mh.RaceID]
	if !ok {
		players = make(map[uint32]*PlayerUDPConn)
		sr.races[mh.RaceID] = players
	}

	// Lazy-init the player connection
	conn, ok := players[mh.UserID]
	if !ok {
		conn = &PlayerUDPConn{addr, UDPSessionTTLSeconds}
		players[mh.UserID] = conn
	}

	conn.TTL = UDPSessionTTLSeconds
}

func (sr *ShadowRaces) getConnection(mh MessageHeader) *PlayerUDPConn {
	// We do not need to acquire the mutex if we are just reading values

	players, ok := sr.races[mh.RaceID]
	if !ok {
		return nil
	}

	conn, ok := players[mh.UserID]
	if !ok {
		return nil
	}

	return conn
}

func (sr *ShadowRaces) getOtherPlayerConnections(mh MessageHeader) []*PlayerUDPConn {
	// We do not need to acquire the mutex if we are just reading values

	players, ok := sr.races[mh.RaceID]
	if !ok {
		return make([]*PlayerUDPConn, 0)
	}

	otherPlayerConnections := make([]*PlayerUDPConn, 0)
	for userID, conn := range players {
		if userID != mh.UserID {
			otherPlayerConnections = append(otherPlayerConnections, conn)
		}
	}

	return otherPlayerConnections
}

func (sr *ShadowRaces) purgeOldSessions() {
	sr.mutex.Lock()
	defer sr.mutex.Unlock()

	for raceID, players := range sr.races {
		for playerID, conn := range players {
			if conn == nil {
				continue
			}

			conn.TTL--

			if conn.TTL > 0 {
				continue
			}

			delete(players, playerID)
			if len(players) == 0 {
				delete(sr.races, raceID)
			}
		}
	}
}
<commit_msg>more debugging output<commit_after>package server

import (
	"net"
	"sync"
)

const (
	UDPSessionTTLSeconds = 5 * 60
)

type ShadowRaces struct {
	mutex sync.Mutex
	races map[uint32]map[uint32]*PlayerUDPConn
}

type PlayerUDPConn struct {
	addr net.Addr
	TTL  uint
}

func (sr *ShadowRaces) updatePlayerTTL(mh MessageHeader, addr net.Addr) {
	sr.mutex.Lock()
	defer sr.mutex.Unlock()

	// Lazy-init the player map for every race
	players, ok := sr.races[mh.RaceID]
	if !ok {
		players = make(map[uint32]*PlayerUDPConn)
		sr.races[mh.RaceID] = players
	}

	// Lazy-init the player connection
	conn, ok := players[mh.UserID]
	if !ok {
		conn = &PlayerUDPConn{addr, UDPSessionTTLSeconds}
		players[mh.UserID] = conn
	}

	conn.TTL = UDPSessionTTLSeconds
}

func (sr *ShadowRaces) getConnection(mh MessageHeader) *PlayerUDPConn {
	// We do not need to acquire the mutex if we are just reading values

	players, ok := sr.races[mh.RaceID]
	if !ok {
		return nil
	}

	conn, ok := players[mh.UserID]
	if !ok {
		return nil
	}

	return conn
}

func (sr *ShadowRaces) getOtherPlayerConnections(mh MessageHeader) []*PlayerUDPConn {
	// We do not need to acquire the mutex if we are just reading values

	players, ok := sr.races[mh.RaceID]
	if !ok {
		return make([]*PlayerUDPConn, 0)
	}

	otherPlayerConnections := make([]*PlayerUDPConn, 0)
	for userID, conn := range players {
		if userID != mh.UserID {
			otherPlayerConnections = append(otherPlayerConnections, conn)
		}
	}

	return otherPlayerConnections
}

func (sr *ShadowRaces) purgeOldSessions() {
	sr.mutex.Lock()
	defer sr.mutex.Unlock()

	for raceID, players := range sr.races {
		for userID, conn := range players {
			if conn == nil {
				continue
			}

			conn.TTL--

			if conn.TTL > 0 {
				continue
			}

			delete(players, userID)
			logger.Debug("Deleted user ID:", userID)
			if len(players) == 0 {
				delete(sr.races, raceID)
				logger.Debug("Deleted race ID:", raceID)
			}
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2018 The gVisor 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 integration provides end-to-end integration tests for runsc.
//
// Each test calls docker commands to start up a container, and tests that it is
// behaving properly, with various runsc commands. The container is killed and
// deleted at the end.
//
// Setup instruction in test/README.md.
package integration

import (
	"flag"
	"fmt"
	"net"
	"net/http"
	"os"
	"strconv"
	"strings"
	"syscall"
	"testing"
	"time"

	"gvisor.dev/gvisor/runsc/dockerutil"
	"gvisor.dev/gvisor/runsc/testutil"
)

// httpRequestSucceeds sends a request to a given url and checks that the status is OK.
func httpRequestSucceeds(client http.Client, server string, port int) error {
	url := fmt.Sprintf("http://%s:%d", server, port)
	// Ensure that content is being served.
	resp, err := client.Get(url)
	if err != nil {
		return fmt.Errorf("error reaching http server: %v", err)
	}
	if want := http.StatusOK; resp.StatusCode != want {
		return fmt.Errorf("wrong response code, got: %d, want: %d", resp.StatusCode, want)
	}
	return nil
}

// TestLifeCycle tests a basic Create/Start/Stop docker container life cycle.
func TestLifeCycle(t *testing.T) {
	if err := dockerutil.Pull("nginx"); err != nil {
		t.Fatal("docker pull failed:", err)
	}
	d := dockerutil.MakeDocker("lifecycle-test")
	if err := d.Create("-p", "80", "nginx"); err != nil {
		t.Fatal("docker create failed:", err)
	}
	if err := d.Start(); err != nil {
		d.CleanUp()
		t.Fatal("docker start failed:", err)
	}

	// Test that container is working
	port, err := d.FindPort(80)
	if err != nil {
		t.Fatal("docker.FindPort(80) failed: ", err)
	}
	if err := testutil.WaitForHTTP(port, 30*time.Second); err != nil {
		t.Fatal("WaitForHTTP() timeout:", err)
	}
	client := http.Client{Timeout: time.Duration(2 * time.Second)}
	if err := httpRequestSucceeds(client, "localhost", port); err != nil {
		t.Error("http request failed:", err)
	}

	if err := d.Stop(); err != nil {
		d.CleanUp()
		t.Fatal("docker stop failed:", err)
	}
	if err := d.Remove(); err != nil {
		t.Fatal("docker rm failed:", err)
	}
}

func TestPauseResume(t *testing.T) {
	const img = "gcr.io/gvisor-presubmit/python-hello"
	if !testutil.IsCheckpointSupported() {
		t.Log("Checkpoint is not supported, skipping test.")
		return
	}

	if err := dockerutil.Pull(img); err != nil {
		t.Fatal("docker pull failed:", err)
	}
	d := dockerutil.MakeDocker("pause-resume-test")
	if err := d.Run("-p", "8080", img); err != nil {
		t.Fatalf("docker run failed: %v", err)
	}
	defer d.CleanUp()

	// Find where port 8080 is mapped to.
	port, err := d.FindPort(8080)
	if err != nil {
		t.Fatal("docker.FindPort(8080) failed:", err)
	}

	// Wait until it's up and running.
	if err := testutil.WaitForHTTP(port, 30*time.Second); err != nil {
		t.Fatal("WaitForHTTP() timeout:", err)
	}

	// Check that container is working.
	client := http.Client{Timeout: time.Duration(2 * time.Second)}
	if err := httpRequestSucceeds(client, "localhost", port); err != nil {
		t.Error("http request failed:", err)
	}

	if err := d.Pause(); err != nil {
		t.Fatal("docker pause failed:", err)
	}

	// Check if container is paused.
	switch _, err := client.Get(fmt.Sprintf("http://localhost:%d", port)); v := err.(type) {
	case nil:
		t.Errorf("http req expected to fail but it succeeded")
	case net.Error:
		if !v.Timeout() {
			t.Errorf("http req got error %v, wanted timeout", v)
		}
	default:
		t.Errorf("http req got unexpected error %v", v)
	}

	if err := d.Unpause(); err != nil {
		t.Fatal("docker unpause failed:", err)
	}

	// Wait until it's up and running.
	if err := testutil.WaitForHTTP(port, 30*time.Second); err != nil {
		t.Fatal("WaitForHTTP() timeout:", err)
	}

	// Check if container is working again.
	if err := httpRequestSucceeds(client, "localhost", port); err != nil {
		t.Error("http request failed:", err)
	}
}

func TestCheckpointRestore(t *testing.T) {
	const img = "gcr.io/gvisor-presubmit/python-hello"
	if !testutil.IsCheckpointSupported() {
		t.Log("Pause/resume is not supported, skipping test.")
		return
	}

	if err := dockerutil.Pull(img); err != nil {
		t.Fatal("docker pull failed:", err)
	}
	d := dockerutil.MakeDocker("save-restore-test")
	if err := d.Run("-p", "8080", img); err != nil {
		t.Fatalf("docker run failed: %v", err)
	}
	defer d.CleanUp()

	if err := d.Checkpoint("test"); err != nil {
		t.Fatal("docker checkpoint failed:", err)
	}

	if _, err := d.Wait(30 * time.Second); err != nil {
		t.Fatal(err)
	}

	if err := d.Restore("test"); err != nil {
		t.Fatal("docker restore failed:", err)
	}

	// Find where port 8080 is mapped to.
	port, err := d.FindPort(8080)
	if err != nil {
		t.Fatal("docker.FindPort(8080) failed:", err)
	}

	// Wait until it's up and running.
	if err := testutil.WaitForHTTP(port, 30*time.Second); err != nil {
		t.Fatal("WaitForHTTP() timeout:", err)
	}

	// Check if container is working again.
	client := http.Client{Timeout: time.Duration(2 * time.Second)}
	if err := httpRequestSucceeds(client, "localhost", port); err != nil {
		t.Error("http request failed:", err)
	}
}

// Create client and server that talk to each other using the local IP.
func TestConnectToSelf(t *testing.T) {
	d := dockerutil.MakeDocker("connect-to-self-test")

	// Creates server that replies "server" and exists. Sleeps at the end because
	// 'docker exec' gets killed if the init process exists before it can finish.
	if err := d.Run("ubuntu:trusty", "/bin/sh", "-c", "echo server | nc -l -p 8080 && sleep 1"); err != nil {
		t.Fatal("docker run failed:", err)
	}
	defer d.CleanUp()

	// Finds IP address for host.
	ip, err := d.Exec("/bin/sh", "-c", "cat /etc/hosts | grep ${HOSTNAME} | awk '{print $1}'")
	if err != nil {
		t.Fatal("docker exec failed:", err)
	}
	ip = strings.TrimRight(ip, "\n")

	// Runs client that sends "client" to the server and exits.
	reply, err := d.Exec("/bin/sh", "-c", fmt.Sprintf("echo client | nc %s 8080", ip))
	if err != nil {
		t.Fatal("docker exec failed:", err)
	}

	// Ensure both client and server got the message from each other.
	if want := "server\n"; reply != want {
		t.Errorf("Error on server, want: %q, got: %q", want, reply)
	}
	if _, err := d.WaitForOutput("^client\n$", 1*time.Second); err != nil {
		t.Fatal("docker.WaitForOutput(client) timeout:", err)
	}
}

func TestMemLimit(t *testing.T) {
	if err := dockerutil.Pull("alpine"); err != nil {
		t.Fatal("docker pull failed:", err)
	}
	d := dockerutil.MakeDocker("cgroup-test")
	cmd := "cat /proc/meminfo | grep MemTotal: | awk '{print $2}'"
	out, err := d.RunFg("--memory=500MB", "alpine", "sh", "-c", cmd)
	if err != nil {
		t.Fatal("docker run failed:", err)
	}
	defer d.CleanUp()

	// Remove warning message that swap isn't present.
	if strings.HasPrefix(out, "WARNING") {
		lines := strings.Split(out, "\n")
		if len(lines) != 3 {
			t.Fatalf("invalid output: %s", out)
		}
		out = lines[1]
	}

	got, err := strconv.ParseUint(strings.TrimSpace(out), 10, 64)
	if err != nil {
		t.Fatalf("failed to parse %q: %v", out, err)
	}
	if want := uint64(500 * 1024); got != want {
		t.Errorf("MemTotal got: %d, want: %d", got, want)
	}
}

func TestNumCPU(t *testing.T) {
	if err := dockerutil.Pull("alpine"); err != nil {
		t.Fatal("docker pull failed:", err)
	}
	d := dockerutil.MakeDocker("cgroup-test")
	cmd := "cat /proc/cpuinfo | grep 'processor.*:' | wc -l"
	out, err := d.RunFg("--cpuset-cpus=0", "alpine", "sh", "-c", cmd)
	if err != nil {
		t.Fatal("docker run failed:", err)
	}
	defer d.CleanUp()

	got, err := strconv.Atoi(strings.TrimSpace(out))
	if err != nil {
		t.Fatalf("failed to parse %q: %v", out, err)
	}
	if want := 1; got != want {
		t.Errorf("MemTotal got: %d, want: %d", got, want)
	}
}

// TestJobControl tests that job control characters are handled properly.
func TestJobControl(t *testing.T) {
	if err := dockerutil.Pull("alpine"); err != nil {
		t.Fatalf("docker pull failed: %v", err)
	}
	d := dockerutil.MakeDocker("job-control-test")

	// Start the container with an attached PTY.
	_, ptmx, err := d.RunWithPty("alpine", "sh")
	if err != nil {
		t.Fatalf("docker run failed: %v", err)
	}
	defer ptmx.Close()
	defer d.CleanUp()

	// Call "sleep 100" in the shell.
	if _, err := ptmx.Write([]byte("sleep 100\n")); err != nil {
		t.Fatalf("error writing to pty: %v", err)
	}

	// Give shell a few seconds to start executing the sleep.
	time.Sleep(2 * time.Second)

	// Send a ^C to the pty, which should kill sleep, but not the shell.
	// \x03 is ASCII "end of text", which is the same as ^C.
	if _, err := ptmx.Write([]byte{'\x03'}); err != nil {
		t.Fatalf("error writing to pty: %v", err)
	}

	// The shell should still be alive at this point. Sleep should have
	// exited with code 2+128=130. We'll exit with 10 plus that number, so
	// that we can be sure that the shell did not get signalled.
	if _, err := ptmx.Write([]byte("exit $(expr $? + 10)\n")); err != nil {
		t.Fatalf("error writing to pty: %v", err)
	}

	// Wait for the container to exit.
	got, err := d.Wait(5 * time.Second)
	if err != nil {
		t.Fatalf("error getting exit code: %v", err)
	}
	// Container should exit with code 10+130=140.
	if want := syscall.WaitStatus(140); got != want {
		t.Errorf("container exited with code %d want %d", got, want)
	}
}

// TestTmpFile checks that files inside '/tmp' are not overridden. In addition,
// it checks that working dir is created if it doesn't exit.
func TestTmpFile(t *testing.T) {
	if err := dockerutil.Pull("alpine"); err != nil {
		t.Fatal("docker pull failed:", err)
	}
	d := dockerutil.MakeDocker("tmp-file-test")
	if err := d.Run("-w=/tmp/foo/bar", "--read-only", "alpine", "touch", "/tmp/foo/bar/file"); err != nil {
		t.Fatal("docker run failed:", err)
	}
	defer d.CleanUp()
}

func TestMain(m *testing.M) {
	dockerutil.EnsureSupportedDockerVersion()
	flag.Parse()
	os.Exit(m.Run())
}
<commit_msg>Deflake TestCheckpointRestore<commit_after>// Copyright 2018 The gVisor 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 integration provides end-to-end integration tests for runsc.
//
// Each test calls docker commands to start up a container, and tests that it is
// behaving properly, with various runsc commands. The container is killed and
// deleted at the end.
//
// Setup instruction in test/README.md.
package integration

import (
	"flag"
	"fmt"
	"net"
	"net/http"
	"os"
	"strconv"
	"strings"
	"syscall"
	"testing"
	"time"

	"gvisor.dev/gvisor/runsc/dockerutil"
	"gvisor.dev/gvisor/runsc/testutil"
)

// httpRequestSucceeds sends a request to a given url and checks that the status is OK.
func httpRequestSucceeds(client http.Client, server string, port int) error {
	url := fmt.Sprintf("http://%s:%d", server, port)
	// Ensure that content is being served.
	resp, err := client.Get(url)
	if err != nil {
		return fmt.Errorf("error reaching http server: %v", err)
	}
	if want := http.StatusOK; resp.StatusCode != want {
		return fmt.Errorf("wrong response code, got: %d, want: %d", resp.StatusCode, want)
	}
	return nil
}

// TestLifeCycle tests a basic Create/Start/Stop docker container life cycle.
func TestLifeCycle(t *testing.T) {
	if err := dockerutil.Pull("nginx"); err != nil {
		t.Fatal("docker pull failed:", err)
	}
	d := dockerutil.MakeDocker("lifecycle-test")
	if err := d.Create("-p", "80", "nginx"); err != nil {
		t.Fatal("docker create failed:", err)
	}
	if err := d.Start(); err != nil {
		d.CleanUp()
		t.Fatal("docker start failed:", err)
	}

	// Test that container is working
	port, err := d.FindPort(80)
	if err != nil {
		t.Fatal("docker.FindPort(80) failed: ", err)
	}
	if err := testutil.WaitForHTTP(port, 30*time.Second); err != nil {
		t.Fatal("WaitForHTTP() timeout:", err)
	}
	client := http.Client{Timeout: time.Duration(2 * time.Second)}
	if err := httpRequestSucceeds(client, "localhost", port); err != nil {
		t.Error("http request failed:", err)
	}

	if err := d.Stop(); err != nil {
		d.CleanUp()
		t.Fatal("docker stop failed:", err)
	}
	if err := d.Remove(); err != nil {
		t.Fatal("docker rm failed:", err)
	}
}

func TestPauseResume(t *testing.T) {
	const img = "gcr.io/gvisor-presubmit/python-hello"
	if !testutil.IsCheckpointSupported() {
		t.Log("Checkpoint is not supported, skipping test.")
		return
	}

	if err := dockerutil.Pull(img); err != nil {
		t.Fatal("docker pull failed:", err)
	}
	d := dockerutil.MakeDocker("pause-resume-test")
	if err := d.Run("-p", "8080", img); err != nil {
		t.Fatalf("docker run failed: %v", err)
	}
	defer d.CleanUp()

	// Find where port 8080 is mapped to.
	port, err := d.FindPort(8080)
	if err != nil {
		t.Fatal("docker.FindPort(8080) failed:", err)
	}

	// Wait until it's up and running.
	if err := testutil.WaitForHTTP(port, 30*time.Second); err != nil {
		t.Fatal("WaitForHTTP() timeout:", err)
	}

	// Check that container is working.
	client := http.Client{Timeout: time.Duration(2 * time.Second)}
	if err := httpRequestSucceeds(client, "localhost", port); err != nil {
		t.Error("http request failed:", err)
	}

	if err := d.Pause(); err != nil {
		t.Fatal("docker pause failed:", err)
	}

	// Check if container is paused.
	switch _, err := client.Get(fmt.Sprintf("http://localhost:%d", port)); v := err.(type) {
	case nil:
		t.Errorf("http req expected to fail but it succeeded")
	case net.Error:
		if !v.Timeout() {
			t.Errorf("http req got error %v, wanted timeout", v)
		}
	default:
		t.Errorf("http req got unexpected error %v", v)
	}

	if err := d.Unpause(); err != nil {
		t.Fatal("docker unpause failed:", err)
	}

	// Wait until it's up and running.
	if err := testutil.WaitForHTTP(port, 30*time.Second); err != nil {
		t.Fatal("WaitForHTTP() timeout:", err)
	}

	// Check if container is working again.
	if err := httpRequestSucceeds(client, "localhost", port); err != nil {
		t.Error("http request failed:", err)
	}
}

func TestCheckpointRestore(t *testing.T) {
	const img = "gcr.io/gvisor-presubmit/python-hello"
	if !testutil.IsCheckpointSupported() {
		t.Log("Pause/resume is not supported, skipping test.")
		return
	}

	if err := dockerutil.Pull(img); err != nil {
		t.Fatal("docker pull failed:", err)
	}
	d := dockerutil.MakeDocker("save-restore-test")
	if err := d.Run("-p", "8080", img); err != nil {
		t.Fatalf("docker run failed: %v", err)
	}
	defer d.CleanUp()

	if err := d.Checkpoint("test"); err != nil {
		t.Fatal("docker checkpoint failed:", err)
	}

	if _, err := d.Wait(30 * time.Second); err != nil {
		t.Fatal(err)
	}

	// TODO(b/143498576): Remove after github.com/moby/moby/issues/38963 is fixed.
	time.Sleep(1 * time.Second)

	if err := d.Restore("test"); err != nil {
		t.Fatal("docker restore failed:", err)
	}

	// Find where port 8080 is mapped to.
	port, err := d.FindPort(8080)
	if err != nil {
		t.Fatal("docker.FindPort(8080) failed:", err)
	}

	// Wait until it's up and running.
	if err := testutil.WaitForHTTP(port, 30*time.Second); err != nil {
		t.Fatal("WaitForHTTP() timeout:", err)
	}

	// Check if container is working again.
	client := http.Client{Timeout: time.Duration(2 * time.Second)}
	if err := httpRequestSucceeds(client, "localhost", port); err != nil {
		t.Error("http request failed:", err)
	}
}

// Create client and server that talk to each other using the local IP.
func TestConnectToSelf(t *testing.T) {
	d := dockerutil.MakeDocker("connect-to-self-test")

	// Creates server that replies "server" and exists. Sleeps at the end because
	// 'docker exec' gets killed if the init process exists before it can finish.
	if err := d.Run("ubuntu:trusty", "/bin/sh", "-c", "echo server | nc -l -p 8080 && sleep 1"); err != nil {
		t.Fatal("docker run failed:", err)
	}
	defer d.CleanUp()

	// Finds IP address for host.
	ip, err := d.Exec("/bin/sh", "-c", "cat /etc/hosts | grep ${HOSTNAME} | awk '{print $1}'")
	if err != nil {
		t.Fatal("docker exec failed:", err)
	}
	ip = strings.TrimRight(ip, "\n")

	// Runs client that sends "client" to the server and exits.
	reply, err := d.Exec("/bin/sh", "-c", fmt.Sprintf("echo client | nc %s 8080", ip))
	if err != nil {
		t.Fatal("docker exec failed:", err)
	}

	// Ensure both client and server got the message from each other.
	if want := "server\n"; reply != want {
		t.Errorf("Error on server, want: %q, got: %q", want, reply)
	}
	if _, err := d.WaitForOutput("^client\n$", 1*time.Second); err != nil {
		t.Fatal("docker.WaitForOutput(client) timeout:", err)
	}
}

func TestMemLimit(t *testing.T) {
	if err := dockerutil.Pull("alpine"); err != nil {
		t.Fatal("docker pull failed:", err)
	}
	d := dockerutil.MakeDocker("cgroup-test")
	cmd := "cat /proc/meminfo | grep MemTotal: | awk '{print $2}'"
	out, err := d.RunFg("--memory=500MB", "alpine", "sh", "-c", cmd)
	if err != nil {
		t.Fatal("docker run failed:", err)
	}
	defer d.CleanUp()

	// Remove warning message that swap isn't present.
	if strings.HasPrefix(out, "WARNING") {
		lines := strings.Split(out, "\n")
		if len(lines) != 3 {
			t.Fatalf("invalid output: %s", out)
		}
		out = lines[1]
	}

	got, err := strconv.ParseUint(strings.TrimSpace(out), 10, 64)
	if err != nil {
		t.Fatalf("failed to parse %q: %v", out, err)
	}
	if want := uint64(500 * 1024); got != want {
		t.Errorf("MemTotal got: %d, want: %d", got, want)
	}
}

func TestNumCPU(t *testing.T) {
	if err := dockerutil.Pull("alpine"); err != nil {
		t.Fatal("docker pull failed:", err)
	}
	d := dockerutil.MakeDocker("cgroup-test")
	cmd := "cat /proc/cpuinfo | grep 'processor.*:' | wc -l"
	out, err := d.RunFg("--cpuset-cpus=0", "alpine", "sh", "-c", cmd)
	if err != nil {
		t.Fatal("docker run failed:", err)
	}
	defer d.CleanUp()

	got, err := strconv.Atoi(strings.TrimSpace(out))
	if err != nil {
		t.Fatalf("failed to parse %q: %v", out, err)
	}
	if want := 1; got != want {
		t.Errorf("MemTotal got: %d, want: %d", got, want)
	}
}

// TestJobControl tests that job control characters are handled properly.
func TestJobControl(t *testing.T) {
	if err := dockerutil.Pull("alpine"); err != nil {
		t.Fatalf("docker pull failed: %v", err)
	}
	d := dockerutil.MakeDocker("job-control-test")

	// Start the container with an attached PTY.
	_, ptmx, err := d.RunWithPty("alpine", "sh")
	if err != nil {
		t.Fatalf("docker run failed: %v", err)
	}
	defer ptmx.Close()
	defer d.CleanUp()

	// Call "sleep 100" in the shell.
	if _, err := ptmx.Write([]byte("sleep 100\n")); err != nil {
		t.Fatalf("error writing to pty: %v", err)
	}

	// Give shell a few seconds to start executing the sleep.
	time.Sleep(2 * time.Second)

	// Send a ^C to the pty, which should kill sleep, but not the shell.
	// \x03 is ASCII "end of text", which is the same as ^C.
	if _, err := ptmx.Write([]byte{'\x03'}); err != nil {
		t.Fatalf("error writing to pty: %v", err)
	}

	// The shell should still be alive at this point. Sleep should have
	// exited with code 2+128=130. We'll exit with 10 plus that number, so
	// that we can be sure that the shell did not get signalled.
	if _, err := ptmx.Write([]byte("exit $(expr $? + 10)\n")); err != nil {
		t.Fatalf("error writing to pty: %v", err)
	}

	// Wait for the container to exit.
	got, err := d.Wait(5 * time.Second)
	if err != nil {
		t.Fatalf("error getting exit code: %v", err)
	}
	// Container should exit with code 10+130=140.
	if want := syscall.WaitStatus(140); got != want {
		t.Errorf("container exited with code %d want %d", got, want)
	}
}

// TestTmpFile checks that files inside '/tmp' are not overridden. In addition,
// it checks that working dir is created if it doesn't exit.
func TestTmpFile(t *testing.T) {
	if err := dockerutil.Pull("alpine"); err != nil {
		t.Fatal("docker pull failed:", err)
	}
	d := dockerutil.MakeDocker("tmp-file-test")
	if err := d.Run("-w=/tmp/foo/bar", "--read-only", "alpine", "touch", "/tmp/foo/bar/file"); err != nil {
		t.Fatal("docker run failed:", err)
	}
	defer d.CleanUp()
}

func TestMain(m *testing.M) {
	dockerutil.EnsureSupportedDockerVersion()
	flag.Parse()
	os.Exit(m.Run())
}
<|endoftext|>