<commit_before>package service

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
	"sync"

	hg "github.com/mosaicnetworks/babble/src/hashgraph"

	"github.com/mosaicnetworks/babble/src/node"
	"github.com/mosaicnetworks/babble/src/peers"
	"github.com/sirupsen/logrus"
)

// Service ...
type Service struct {
	sync.Mutex

	bindAddress string
	node        *node.Node
	graph       *node.Graph
	logger      *logrus.Entry
}

// NewService ...
func NewService(bindAddress string, n *node.Node, logger *logrus.Entry) *Service {
	service := Service{
		bindAddress: bindAddress,
		node:        n,
		graph:       node.NewGraph(n),
		logger:      logger,
	}

	service.registerHandlers()

	return &service
}

// registerHandlers registers the API handlers with the DefaultServerMux of the
// http package. It is possible that another server in the same process is
// simultaneously using the DefaultServerMux. In which case, the handlers will
// be accessible from both servers. This is usefull when Babble is used
// in-memory and expecpted to use the same endpoint (address:port) as the
// application's API.
func (s *Service) registerHandlers() {
	s.logger.Debug("Registering Babble API handlers")
	http.HandleFunc("/stats", s.makeHandler(s.GetStats))
	http.HandleFunc("/block/", s.makeHandler(s.GetBlock))
	http.HandleFunc("/blocks/", s.makeHandler(s.GetBlocks))
	http.HandleFunc("/graph", s.makeHandler(s.GetGraph))
	http.HandleFunc("/peers", s.makeHandler(s.GetPeers))
	http.HandleFunc("/genesispeers", s.makeHandler(s.GetGenesisPeers))
}

func (s *Service) makeHandler(fn func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		s.Lock()
		defer s.Unlock()

		// enable CORS
		w.Header().Set("Access-Control-Allow-Origin", "*")

		fn(w, r)
	}
}

// Serve calls ListenAndServe. This is a blocking call. It is not necessary to
// call Serve when Babble is used in-memory and another server has already been
// started with the DefaultServerMux and the same address:port combination.
// Indeed, Babble API handlers have already been registered when the service was
// instantiated.
func (s *Service) Serve() {
	s.logger.WithField("bind_address", s.bindAddress).Debug("Serving Babble API")

	// Use the DefaultServerMux
	err := http.ListenAndServe(s.bindAddress, nil)
	if err != nil {
		s.logger.Error(err)
	}
}

// GetStats ...
func (s *Service) GetStats(w http.ResponseWriter, r *http.Request) {
	stats := s.node.GetStats()

	w.Header().Set("Content-Type", "application/json")

	json.NewEncoder(w).Encode(stats)
}

// GetBlock ...
func (s *Service) GetBlock(w http.ResponseWriter, r *http.Request) {
	param := r.URL.Path[len("/block/"):]

	blockIndex, err := strconv.Atoi(param)

	if err != nil {
		s.logger.WithError(err).Errorf("Parsing block_index parameter %s", param)

		http.Error(w, err.Error(), http.StatusInternalServerError)

		return
	}

	block, err := s.node.GetBlock(blockIndex)

	if err != nil {
		s.logger.WithError(err).Errorf("Retrieving block %d", blockIndex)

		http.Error(w, err.Error(), http.StatusInternalServerError)

		return
	}

	w.Header().Set("Content-Type", "application/json")

	json.NewEncoder(w).Encode(block)
}

// GetBlocks returns an array of blocks starting with blocks/[index]?limit=x and finishing
// at x blocks later
func (s *Service) GetBlocks(w http.ResponseWriter, r *http.Request) {
	// blocks slice
	var blocks []*hg.Block

	// max limit on blocks set back
	maxLimit := 5

	// check last block index and make sure a block exists
	sLastBlockIndex := s.node.GetStats()["last_block_index"]
	if sLastBlockIndex == "-1" {
		s.logger.Errorf("No blocks found")
		http.Error(w, "No blocks found", http.StatusInternalServerError)
		return
	}

	// convert to int
	lastBlockIndex, err := strconv.Atoi(sLastBlockIndex)
	if err != nil {
		s.logger.WithError(err).Errorf("Converting last block index to int")
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// parse starting block index
	qi := r.URL.Path[len("/blocks/"):]
	i, err := strconv.Atoi(qi)
	if err != nil {
		s.logger.WithError(err).Errorf("Converting block index to int")
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	if i > lastBlockIndex {
		s.logger.Errorf("Requested index larger than last block index")
		http.Error(w, "Requested starting index larger than last block index", http.StatusInternalServerError)
		return
	}

	// get max limit, if empty set to maxlimit
	ql := r.URL.Query().Get("limit")
	if ql == "" {
		ql = strconv.Itoa(maxLimit)
	}

	// parse to int
	l, err := strconv.Atoi(ql)
	if err != nil {
		s.logger.WithError(err).Errorf("Converting blocks limit to int")
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// make sure requested limit does not exceed max
	if l > maxLimit {
		l = maxLimit
	}

	// make limit does not exceed last block index
	if i+l > lastBlockIndex {
		l = lastBlockIndex - i
	}

	// get blocks
	for c := 0; c <= l; {
		fmt.Println("Fetching block: ", i+c)
		fmt.Println("Limit: ", l)

		block, err := s.node.GetBlock(i + c)
		if err != nil {
			s.logger.WithError(err).Errorf("Retrieving block %d", i+c)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		blocks = append(blocks, block)
		c++
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(blocks)
}

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

	encoder := json.NewEncoder(w)

	res, _ := s.graph.GetInfos()

	encoder.Encode(res)
}

// GetPeers ...
func (s *Service) GetPeers(w http.ResponseWriter, r *http.Request) {
	returnPeerSet(w, r, s.node.GetPeers())
}

// GetGenesisPeers ...
func (s *Service) GetGenesisPeers(w http.ResponseWriter, r *http.Request) {
	returnPeerSet(w, r, s.node.GetGenesisPeers())
}

func returnPeerSet(w http.ResponseWriter, r *http.Request, peers []*peers.Peer) {
	w.Header().Set("Content-Type", "application/json")

	encoder := json.NewEncoder(w)

	encoder.Encode(peers)
}
<commit_msg>Removed debug logging and added method docstring<commit_after>package service

import (
	"encoding/json"
	"net/http"
	"strconv"
	"sync"

	hg "github.com/mosaicnetworks/babble/src/hashgraph"

	"github.com/mosaicnetworks/babble/src/node"
	"github.com/mosaicnetworks/babble/src/peers"
	"github.com/sirupsen/logrus"
)

// Service ...
type Service struct {
	sync.Mutex

	bindAddress string
	node        *node.Node
	graph       *node.Graph
	logger      *logrus.Entry
}

// NewService ...
func NewService(bindAddress string, n *node.Node, logger *logrus.Entry) *Service {
	service := Service{
		bindAddress: bindAddress,
		node:        n,
		graph:       node.NewGraph(n),
		logger:      logger,
	}

	service.registerHandlers()

	return &service
}

// registerHandlers registers the API handlers with the DefaultServerMux of the
// http package. It is possible that another server in the same process is
// simultaneously using the DefaultServerMux. In which case, the handlers will
// be accessible from both servers. This is usefull when Babble is used
// in-memory and expecpted to use the same endpoint (address:port) as the
// application's API.
func (s *Service) registerHandlers() {
	s.logger.Debug("Registering Babble API handlers")
	http.HandleFunc("/stats", s.makeHandler(s.GetStats))
	http.HandleFunc("/block/", s.makeHandler(s.GetBlock))
	http.HandleFunc("/blocks/", s.makeHandler(s.GetBlocks))
	http.HandleFunc("/graph", s.makeHandler(s.GetGraph))
	http.HandleFunc("/peers", s.makeHandler(s.GetPeers))
	http.HandleFunc("/genesispeers", s.makeHandler(s.GetGenesisPeers))
}

func (s *Service) makeHandler(fn func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		s.Lock()
		defer s.Unlock()

		// enable CORS
		w.Header().Set("Access-Control-Allow-Origin", "*")

		fn(w, r)
	}
}

// Serve calls ListenAndServe. This is a blocking call. It is not necessary to
// call Serve when Babble is used in-memory and another server has already been
// started with the DefaultServerMux and the same address:port combination.
// Indeed, Babble API handlers have already been registered when the service was
// instantiated.
func (s *Service) Serve() {
	s.logger.WithField("bind_address", s.bindAddress).Debug("Serving Babble API")

	// Use the DefaultServerMux
	err := http.ListenAndServe(s.bindAddress, nil)
	if err != nil {
		s.logger.Error(err)
	}
}

// GetStats ...
func (s *Service) GetStats(w http.ResponseWriter, r *http.Request) {
	stats := s.node.GetStats()

	w.Header().Set("Content-Type", "application/json")

	json.NewEncoder(w).Encode(stats)
}

// GetBlock ...
func (s *Service) GetBlock(w http.ResponseWriter, r *http.Request) {
	param := r.URL.Path[len("/block/"):]

	blockIndex, err := strconv.Atoi(param)

	if err != nil {
		s.logger.WithError(err).Errorf("Parsing block_index parameter %s", param)

		http.Error(w, err.Error(), http.StatusInternalServerError)

		return
	}

	block, err := s.node.GetBlock(blockIndex)

	if err != nil {
		s.logger.WithError(err).Errorf("Retrieving block %d", blockIndex)

		http.Error(w, err.Error(), http.StatusInternalServerError)

		return
	}

	w.Header().Set("Content-Type", "application/json")

	json.NewEncoder(w).Encode(block)
}

/*
GetBlocks will fetch an array of blocks starting at {startIndex} and finishing
{x} blocks later.

GET /blocks/{startIndex}?limit={x}
example: /blocks/{0}?limit={50}
returns: JSON []hashgraph.Block
*/
func (s *Service) GetBlocks(w http.ResponseWriter, r *http.Request) {
	// blocks slice
	var blocks []*hg.Block

	// max limit on blocks set back
	maxLimit := 50

	// check last block index and make sure a block exists
	sLastBlockIndex := s.node.GetStats()["last_block_index"]
	if sLastBlockIndex == "-1" {
		s.logger.Errorf("No blocks found")
		http.Error(w, "No blocks found", http.StatusInternalServerError)
		return
	}

	// convert to int
	lastBlockIndex, err := strconv.Atoi(sLastBlockIndex)
	if err != nil {
		s.logger.WithError(err).Errorf("Converting last block index to int")
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// parse starting block index
	qi := r.URL.Path[len("/blocks/"):]
	i, err := strconv.Atoi(qi)
	if err != nil {
		s.logger.WithError(err).Errorf("Converting block index to int")
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	if i > lastBlockIndex {
		s.logger.Errorf("Requested index larger than last block index")
		http.Error(w, "Requested starting index larger than last block index", http.StatusInternalServerError)
		return
	}

	// get max limit, if empty set to maxlimit
	ql := r.URL.Query().Get("limit")
	if ql == "" {
		ql = strconv.Itoa(maxLimit)
	}

	// parse to int
	l, err := strconv.Atoi(ql)
	if err != nil {
		s.logger.WithError(err).Errorf("Converting blocks limit to int")
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// make sure requested limit does not exceed max
	if l > maxLimit {
		l = maxLimit
	}

	// make limit does not exceed last block index
	if i+l > lastBlockIndex {
		l = lastBlockIndex - i
	}

	// get blocks
	for c := 0; c <= l; {
		block, err := s.node.GetBlock(i + c)
		if err != nil {
			s.logger.WithError(err).Errorf("Retrieving block %d", i+c)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		blocks = append(blocks, block)
		c++
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(blocks)
}

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

	encoder := json.NewEncoder(w)

	res, _ := s.graph.GetInfos()

	encoder.Encode(res)
}

// GetPeers ...
func (s *Service) GetPeers(w http.ResponseWriter, r *http.Request) {
	returnPeerSet(w, r, s.node.GetPeers())
}

// GetGenesisPeers ...
func (s *Service) GetGenesisPeers(w http.ResponseWriter, r *http.Request) {
	returnPeerSet(w, r, s.node.GetGenesisPeers())
}

func returnPeerSet(w http.ResponseWriter, r *http.Request, peers []*peers.Peer) {
	w.Header().Set("Content-Type", "application/json")

	encoder := json.NewEncoder(w)

	encoder.Encode(peers)
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceContainerReplicaController() *schema.Resource {
	return &schema.Resource{
		Create: resourceContainerReplicaControllerCreate,
		Read:   resourceContainerReplicaControllerRead,
		Delete: resourceContainerReplicaControllerDelete,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"docker_image": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"container_name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"zone": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"external_port": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},

			"optional_args": &schema.Schema{
				Type:     schema.TypeMap,
				Optional: true,
				ForceNew: true,
				Elem:	  schema.TypeString,
			},

			"env_args": &schema.Schema{
				Type:     schema.TypeMap,
				Optional: true,
				ForceNew: true,
				Elem:	  schema.TypeString,
			},

			"external_ip": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

		},
	}
}

func resourceContainerReplicaControllerCreate(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	err := config.initKubectl(d.Get("container_name").(string), d.Get("zone").(string))
	if err != nil {
		return err
	}

	optional_args := cleanAdditionalArgs(d.Get("optional_args").(map[string]interface{}))
	env_args := cleanAdditionalArgs(d.Get("env_args").(map[string]interface{}))
	uid, err := CreateKubeRC(d.Get("name").(string), d.Get("docker_image").(string), d.Get("external_port").(string), optional_args, env_args)
	if err != nil {
		return err
	}

	err = resourceContainerReplicaControllerRead(d, meta)
	if err != nil {
		return err
	}

	d.SetId(uid)

	return nil
}

func resourceContainerReplicaControllerRead(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	err := config.initKubectl(d.Get("container_name").(string), d.Get("zone").(string))
	if err != nil {
		return err
	}

	pod_count, external_ip, err := ReadKubeRC(d.Get("name").(string), d.Get("external_port").(string))
	if err != nil {
		return err
	}

	if pod_count == 0 {
		//  something has gone awry, there should always be at least one pod
		log.Printf("There are no pods associated with this Replica Controller.  This is unexpected and probably wrong.  Please investigate")
	}

	if external_ip != "" {
		d.Set("external_ip", external_ip)
	}

	return nil
}

func resourceContainerReplicaControllerDelete(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	err := config.initKubectl(d.Get("container_name").(string), d.Get("zone").(string))
	if err != nil {
		return err
	}

	err = DeleteKubeRC(d.Get("name").(string),d.Get("external_port").(string))
	if err != nil {
		return err
	}

	d.SetId("")
	return nil
}
<commit_msg>aand add appengine to the resource hash life<commit_after>package main

import (
	"log"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceContainerReplicaController() *schema.Resource {
	return &schema.Resource{
		Create: resourceContainerReplicaControllerCreate,
		Read:   resourceContainerReplicaControllerRead,
		Delete: resourceContainerReplicaControllerDelete,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"docker_image": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"container_name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"zone": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"external_port": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},

			"resource_hash": &schema.Schema{
				Type:     schema.TypeList,
				ForceNew: true,
				Optional: true,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
			},

			"optional_args": &schema.Schema{
				Type:     schema.TypeMap,
				Optional: true,
				ForceNew: true,
				Elem:	  schema.TypeString,
			},

			"env_args": &schema.Schema{
				Type:     schema.TypeMap,
				Optional: true,
				ForceNew: true,
				Elem:	  schema.TypeString,
			},

			"external_ip": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

		},
	}
}

func resourceContainerReplicaControllerCreate(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	err := config.initKubectl(d.Get("container_name").(string), d.Get("zone").(string))
	if err != nil {
		return err
	}

	optional_args := cleanAdditionalArgs(d.Get("optional_args").(map[string]interface{}))
	env_args := cleanAdditionalArgs(d.Get("env_args").(map[string]interface{}))
	uid, err := CreateKubeRC(d.Get("name").(string), d.Get("docker_image").(string), d.Get("external_port").(string), optional_args, env_args)
	if err != nil {
		return err
	}

	err = resourceContainerReplicaControllerRead(d, meta)
	if err != nil {
		return err
	}

	d.SetId(uid)

	return nil
}

func resourceContainerReplicaControllerRead(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	err := config.initKubectl(d.Get("container_name").(string), d.Get("zone").(string))
	if err != nil {
		return err
	}

	pod_count, external_ip, err := ReadKubeRC(d.Get("name").(string), d.Get("external_port").(string))
	if err != nil {
		return err
	}

	if pod_count == 0 {
		//  something has gone awry, there should always be at least one pod
		log.Printf("There are no pods associated with this Replica Controller.  This is unexpected and probably wrong.  Please investigate")
	}

	if external_ip != "" {
		d.Set("external_ip", external_ip)
	}

	return nil
}

func resourceContainerReplicaControllerDelete(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	err := config.initKubectl(d.Get("container_name").(string), d.Get("zone").(string))
	if err != nil {
		return err
	}

	err = DeleteKubeRC(d.Get("name").(string),d.Get("external_port").(string))
	if err != nil {
		return err
	}

	d.SetId("")
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Kubernetes Authors.

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

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

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

package kubeadm

import (
	"fmt"

	authv1 "k8s.io/api/authorization/v1"
	rbacv1 "k8s.io/api/rbac/v1"
	"k8s.io/apimachinery/pkg/util/version"
	"k8s.io/kubernetes/test/e2e/framework"

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

const (
	kubeletConfigConfigMapKey = "kubelet"
)

var (
	kubeletConfigConfigMapName   string
	kubeletConfigRoleName        string
	kubeletConfigRoleBindingName string

	kubeletConfigConfigMapResource = &authv1.ResourceAttributes{
		Namespace: kubeSystemNamespace,
		Name:      "",
		Resource:  "configmaps",
		Verb:      "get",
	}
)

// Define container for all the test specification aimed at verifying
// that kubeadm creates the kubelet-config ConfigMap, that it is properly configured
// and that all the related RBAC rules are in place
var _ = Describe("kubelet-config ConfigMap", func() {

	// Get an instance of the k8s test framework
	f := framework.NewDefaultFramework("kubelet-config")

	// Tests in this container are not expected to create new objects in the cluster
	// so we are disabling the creation of a namespace in order to get a faster execution
	f.SkipNamespaceCreation = true

	// kubelet-config map is named using the kubernetesVersion as a suffix, and so
	// it is necessary to get it from the kubeadm-config ConfigMap before testing
	ginkgo.BeforeEach(func() {
		// if the kubelet-config map name is already known exit
		if kubeletConfigConfigMapName != "" {
			return
		}

		// gets the ClusterConfiguration from the kubeadm kubeadm-config ConfigMap as a untyped map
		m := getClusterConfiguration(f.ClientSet)

		// Extract the kubernetesVersion
		gomega.Expect(m).To(gomega.HaveKey("kubernetesVersion"))
		k8sVersionString := m["kubernetesVersion"].(string)
		k8sVersion, err := version.ParseSemantic(k8sVersionString)
		if err != nil {
			framework.Failf("error reading kubernetesVersion from %s ConfigMap: %v", kubeadmConfigName, err)
		}

		// Computes all the names derived from the kubernetesVersion
		kubeletConfigConfigMapName = fmt.Sprintf("kubelet-config-%d.%d", k8sVersion.Major(), k8sVersion.Minor())
		kubeletConfigRoleName = fmt.Sprintf("kubeadm:kubelet-config-%d.%d", k8sVersion.Major(), k8sVersion.Minor())
		kubeletConfigRoleBindingName = kubeletConfigRoleName
		kubeletConfigConfigMapResource.Name = kubeletConfigConfigMapName
	})

	ginkgo.It("should exist and be properly configured", func() {
		cm := GetConfigMap(f.ClientSet, kubeSystemNamespace, kubeletConfigConfigMapName)

		gomega.Expect(cm.Data).To(gomega.HaveKey(kubeletConfigConfigMapKey))
	})

	ginkgo.It("should have related Role and RoleBinding", func() {
		ExpectRole(f.ClientSet, kubeSystemNamespace, kubeletConfigRoleName)
		ExpectRoleBinding(f.ClientSet, kubeSystemNamespace, kubeletConfigRoleBindingName)
	})

	ginkgo.It("should be accessible for bootstrap tokens", func() {
		ExpectSubjectHasAccessToResource(f.ClientSet,
			rbacv1.GroupKind, bootstrapTokensGroup,
			kubeadmConfigConfigMapResource,
		)
	})

	ginkgo.It("should be accessible for nodes", func() {
		ExpectSubjectHasAccessToResource(f.ClientSet,
			rbacv1.GroupKind, nodesGroup,
			kubeadmConfigConfigMapResource,
		)
	})
})
<commit_msg>kubeadm: update e2e tests for the kubelet-config<commit_after>/*
Copyright 2019 The Kubernetes Authors.

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

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

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

package kubeadm

import (
	"fmt"

	authv1 "k8s.io/api/authorization/v1"
	rbacv1 "k8s.io/api/rbac/v1"
	"k8s.io/apimachinery/pkg/util/version"
	"k8s.io/kubernetes/test/e2e/framework"

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

const (
	kubeletConfigConfigMapKey = "kubelet"
)

var (
	kubeletConfigConfigMapName   string
	kubeletConfigRoleName        string
	kubeletConfigRoleBindingName string

	kubeletConfigConfigMapResource = &authv1.ResourceAttributes{
		Namespace: kubeSystemNamespace,
		Name:      "",
		Resource:  "configmaps",
		Verb:      "get",
	}
)

// Define container for all the test specification aimed at verifying
// that kubeadm creates the kubelet-config ConfigMap, that it is properly configured
// and that all the related RBAC rules are in place
var _ = Describe("kubelet-config ConfigMap", func() {

	// Get an instance of the k8s test framework
	f := framework.NewDefaultFramework("kubelet-config")

	// Tests in this container are not expected to create new objects in the cluster
	// so we are disabling the creation of a namespace in order to get a faster execution
	f.SkipNamespaceCreation = true

	// kubelet-config map is named using the kubernetesVersion as a suffix, and so
	// it is necessary to get it from the kubeadm-config ConfigMap before testing
	ginkgo.BeforeEach(func() {
		// if the kubelet-config map name is already known exit
		if kubeletConfigConfigMapName != "" {
			return
		}

		// gets the ClusterConfiguration from the kubeadm kubeadm-config ConfigMap as a untyped map
		m := getClusterConfiguration(f.ClientSet)

		// Extract the kubernetesVersion
		// TODO: remove this after the UnversionedKubeletConfigMap feature gate goes GA:
		// https://github.com/kubernetes/kubeadm/issues/1582
		// At that point parsing the k8s version will no longer be needed in this test.
		gomega.Expect(m).To(gomega.HaveKey("kubernetesVersion"))
		k8sVersionString := m["kubernetesVersion"].(string)
		k8sVersion, err := version.ParseSemantic(k8sVersionString)
		if err != nil {
			framework.Failf("error reading kubernetesVersion from %s ConfigMap: %v", kubeadmConfigName, err)
		}

		// Extract the value of the UnversionedKubeletConfigMap feature gate if its present.
		// TODO: remove this after the UnversionedKubeletConfigMap feature gate goes GA:
		// https://github.com/kubernetes/kubeadm/issues/1582
		var UnversionedKubeletConfigMap bool
		if _, ok := m["featureGates"]; ok {
			if featureGates, ok := m["featureGates"].(map[string]bool); ok {
				// TODO: update the default to true once this graduates to Beta.
				UnversionedKubeletConfigMap = false
				if val, ok := featureGates["UnversionedKubeletConfigMap"]; ok {
					UnversionedKubeletConfigMap = val
				}
			} else {
				framework.Failf("unable to cast the featureGates field in the %s ConfigMap", kubeadmConfigName)
			}
		}

		// Computes all the names derived from the kubernetesVersion
		kubeletConfigConfigMapName = "kubelet-config"
		kubeletConfigRoleName = "kubeadm:kubelet-config"
		// TODO: remove this after the UnversionedKubeletConfigMap feature gate goes GA:
		// https://github.com/kubernetes/kubeadm/issues/1582
		if !UnversionedKubeletConfigMap {
			kubeletConfigConfigMapName = fmt.Sprintf("kubelet-config-%d.%d", k8sVersion.Major(), k8sVersion.Minor())
			kubeletConfigRoleName = fmt.Sprintf("kubeadm:kubelet-config-%d.%d", k8sVersion.Major(), k8sVersion.Minor())
		}
		kubeletConfigRoleBindingName = kubeletConfigRoleName
		kubeletConfigConfigMapResource.Name = kubeletConfigConfigMapName
	})

	ginkgo.It("should exist and be properly configured", func() {
		cm := GetConfigMap(f.ClientSet, kubeSystemNamespace, kubeletConfigConfigMapName)

		gomega.Expect(cm.Data).To(gomega.HaveKey(kubeletConfigConfigMapKey))
	})

	ginkgo.It("should have related Role and RoleBinding", func() {
		ExpectRole(f.ClientSet, kubeSystemNamespace, kubeletConfigRoleName)
		ExpectRoleBinding(f.ClientSet, kubeSystemNamespace, kubeletConfigRoleBindingName)
	})

	ginkgo.It("should be accessible for bootstrap tokens", func() {
		ExpectSubjectHasAccessToResource(f.ClientSet,
			rbacv1.GroupKind, bootstrapTokensGroup,
			kubeadmConfigConfigMapResource,
		)
	})

	ginkgo.It("should be accessible for nodes", func() {
		ExpectSubjectHasAccessToResource(f.ClientSet,
			rbacv1.GroupKind, nodesGroup,
			kubeadmConfigConfigMapResource,
		)
	})
})
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package e2enode

import (
	"context"
	"os/exec"
	"strconv"
	"time"

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/uuid"
	"k8s.io/component-base/metrics/testutil"
	kubeletmetrics "k8s.io/kubernetes/pkg/kubelet/metrics"
	"k8s.io/kubernetes/test/e2e/framework"
	e2egpu "k8s.io/kubernetes/test/e2e/framework/gpu"
	e2emanifest "k8s.io/kubernetes/test/e2e/framework/manifest"
	e2emetrics "k8s.io/kubernetes/test/e2e/framework/metrics"

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

// numberOfNVIDIAGPUs returns the number of GPUs advertised by a node
// This is based on the Device Plugin system and expected to run on a COS based node
// After the NVIDIA drivers were installed
// TODO make this generic and not linked to COS only
func numberOfNVIDIAGPUs(node *v1.Node) int64 {
	val, ok := node.Status.Capacity[e2egpu.NVIDIAGPUResourceName]
	if !ok {
		return 0
	}
	return val.Value()
}

// NVIDIADevicePlugin returns the official Google Device Plugin pod for NVIDIA GPU in GKE
func NVIDIADevicePlugin() *v1.Pod {
	ds, err := e2emanifest.DaemonSetFromURL(e2egpu.GPUDevicePluginDSYAML)
	framework.ExpectNoError(err)
	p := &v1.Pod{
		ObjectMeta: metav1.ObjectMeta{
			Name: "device-plugin-nvidia-gpu-" + string(uuid.NewUUID()),
		},
		Spec: ds.Spec.Template.Spec,
	}
	// Remove node affinity
	p.Spec.Affinity = nil
	return p
}

// Serial because the test restarts Kubelet
var _ = SIGDescribe("NVIDIA GPU Device Plugin [Feature:GPUDevicePlugin][NodeFeature:GPUDevicePlugin][Serial] [Disruptive]", func() {
	f := framework.NewDefaultFramework("device-plugin-gpus-errors")

	ginkgo.Context("DevicePlugin", func() {
		var devicePluginPod *v1.Pod
		ginkgo.BeforeEach(func() {
			ginkgo.By("Ensuring that Nvidia GPUs exists on the node")
			if !checkIfNvidiaGPUsExistOnNode() {
				ginkgo.Skip("Nvidia GPUs do not exist on the node. Skipping test.")
			}

			if framework.TestContext.ContainerRuntime != "docker" {
				ginkgo.Skip("Test works only with in-tree dockershim. Skipping test.")
			}

			ginkgo.By("Creating the Google Device Plugin pod for NVIDIA GPU")
			devicePluginPod = f.PodClient().Create(NVIDIADevicePlugin())

			ginkgo.By("Waiting for GPUs to become available on the local node")
			gomega.Eventually(func() bool {
				return numberOfNVIDIAGPUs(getLocalNode(f)) > 0
			}, 5*time.Minute, framework.Poll).Should(gomega.BeTrue(), "GPUs never became available on the local node")

			if numberOfNVIDIAGPUs(getLocalNode(f)) < 2 {
				ginkgo.Skip("Not enough GPUs to execute this test (at least two needed)")
			}
		})

		ginkgo.AfterEach(func() {
			l, err := f.PodClient().List(context.TODO(), metav1.ListOptions{})
			framework.ExpectNoError(err)

			for _, p := range l.Items {
				if p.Namespace != f.Namespace.Name {
					continue
				}

				f.PodClient().Delete(context.TODO(), p.Name, metav1.DeleteOptions{})
			}
		})

		ginkgo.It("checks that when Kubelet restarts exclusive GPU assignation to pods is kept.", func() {
			ginkgo.By("Creating one GPU pod on a node with at least two GPUs")
			podRECMD := "devs=$(ls /dev/ | egrep '^nvidia[0-9]+$') && echo gpu devices: $devs"
			p1 := f.PodClient().CreateSync(makeBusyboxPod(e2egpu.NVIDIAGPUResourceName, podRECMD))

			deviceIDRE := "gpu devices: (nvidia[0-9]+)"
			devID1 := parseLog(f, p1.Name, p1.Name, deviceIDRE)
			p1, err := f.PodClient().Get(context.TODO(), p1.Name, metav1.GetOptions{})
			framework.ExpectNoError(err)

			ginkgo.By("Restarting Kubelet and waiting for the current running pod to restart")
			restartKubelet()

			ginkgo.By("Confirming that after a kubelet and pod restart, GPU assignment is kept")
			ensurePodContainerRestart(f, p1.Name, p1.Name)
			devIDRestart1 := parseLog(f, p1.Name, p1.Name, deviceIDRE)
			framework.ExpectEqual(devIDRestart1, devID1)

			ginkgo.By("Restarting Kubelet and creating another pod")
			restartKubelet()
			framework.WaitForAllNodesSchedulable(f.ClientSet, framework.TestContext.NodeSchedulableTimeout)
			gomega.Eventually(func() bool {
				return numberOfNVIDIAGPUs(getLocalNode(f)) > 0
			}, 5*time.Minute, framework.Poll).Should(gomega.BeTrue())
			p2 := f.PodClient().CreateSync(makeBusyboxPod(e2egpu.NVIDIAGPUResourceName, podRECMD))

			ginkgo.By("Checking that pods got a different GPU")
			devID2 := parseLog(f, p2.Name, p2.Name, deviceIDRE)

			framework.ExpectEqual(devID1, devID2)

			ginkgo.By("Deleting device plugin.")
			f.ClientSet.CoreV1().Pods(metav1.NamespaceSystem).Delete(context.TODO(), devicePluginPod.Name, metav1.DeleteOptions{})
			ginkgo.By("Waiting for GPUs to become unavailable on the local node")
			gomega.Eventually(func() bool {
				node, err := f.ClientSet.CoreV1().Nodes().Get(context.TODO(), framework.TestContext.NodeName, metav1.GetOptions{})
				framework.ExpectNoError(err)
				return numberOfNVIDIAGPUs(node) <= 0
			}, 10*time.Minute, framework.Poll).Should(gomega.BeTrue())
			ginkgo.By("Checking that scheduled pods can continue to run even after we delete device plugin.")
			ensurePodContainerRestart(f, p1.Name, p1.Name)
			devIDRestart1 = parseLog(f, p1.Name, p1.Name, deviceIDRE)
			framework.ExpectEqual(devIDRestart1, devID1)

			ensurePodContainerRestart(f, p2.Name, p2.Name)
			devIDRestart2 := parseLog(f, p2.Name, p2.Name, deviceIDRE)
			framework.ExpectEqual(devIDRestart2, devID2)
			ginkgo.By("Restarting Kubelet.")
			restartKubelet()
			ginkgo.By("Checking that scheduled pods can continue to run even after we delete device plugin and restart Kubelet.")
			ensurePodContainerRestart(f, p1.Name, p1.Name)
			devIDRestart1 = parseLog(f, p1.Name, p1.Name, deviceIDRE)
			framework.ExpectEqual(devIDRestart1, devID1)
			ensurePodContainerRestart(f, p2.Name, p2.Name)
			devIDRestart2 = parseLog(f, p2.Name, p2.Name, deviceIDRE)
			framework.ExpectEqual(devIDRestart2, devID2)
			logDevicePluginMetrics()

			// Cleanup
			f.PodClient().DeleteSync(p1.Name, metav1.DeleteOptions{}, framework.DefaultPodDeletionTimeout)
			f.PodClient().DeleteSync(p2.Name, metav1.DeleteOptions{}, framework.DefaultPodDeletionTimeout)
		})
	})
})

func checkIfNvidiaGPUsExistOnNode() bool {
	// Cannot use `lspci` because it is not installed on all distros by default.
	err := exec.Command("/bin/sh", "-c", "find /sys/devices/pci* -type f | grep vendor | xargs cat | grep 0x10de").Run()
	if err != nil {
		framework.Logf("check for nvidia GPUs failed. Got Error: %v", err)
		return false
	}
	return true
}

func logDevicePluginMetrics() {
	ms, err := e2emetrics.GrabKubeletMetricsWithoutProxy(framework.TestContext.NodeName+":10255", "/metrics")
	framework.ExpectNoError(err)
	for msKey, samples := range ms {
		switch msKey {
		case kubeletmetrics.KubeletSubsystem + "_" + kubeletmetrics.DevicePluginAllocationDurationKey:
			for _, sample := range samples {
				latency := sample.Value
				resource := string(sample.Metric["resource_name"])
				var quantile float64
				if val, ok := sample.Metric[testutil.QuantileLabel]; ok {
					var err error
					if quantile, err = strconv.ParseFloat(string(val), 64); err != nil {
						continue
					}
					framework.Logf("Metric: %v ResourceName: %v Quantile: %v Latency: %v", msKey, resource, quantile, latency)
				}
			}
		case kubeletmetrics.KubeletSubsystem + "_" + kubeletmetrics.DevicePluginRegistrationCountKey:
			for _, sample := range samples {
				resource := string(sample.Metric["resource_name"])
				count := sample.Value
				framework.Logf("Metric: %v ResourceName: %v Count: %v", msKey, resource, count)
			}
		}
	}
}
<commit_msg>e2e_node: run gpu pod long enough to become ready<commit_after>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package e2enode

import (
	"context"
	"os/exec"
	"strconv"
	"time"

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/uuid"
	"k8s.io/component-base/metrics/testutil"
	kubeletmetrics "k8s.io/kubernetes/pkg/kubelet/metrics"
	"k8s.io/kubernetes/test/e2e/framework"
	e2egpu "k8s.io/kubernetes/test/e2e/framework/gpu"
	e2emanifest "k8s.io/kubernetes/test/e2e/framework/manifest"
	e2emetrics "k8s.io/kubernetes/test/e2e/framework/metrics"

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

// numberOfNVIDIAGPUs returns the number of GPUs advertised by a node
// This is based on the Device Plugin system and expected to run on a COS based node
// After the NVIDIA drivers were installed
// TODO make this generic and not linked to COS only
func numberOfNVIDIAGPUs(node *v1.Node) int64 {
	val, ok := node.Status.Capacity[e2egpu.NVIDIAGPUResourceName]
	if !ok {
		return 0
	}
	return val.Value()
}

// NVIDIADevicePlugin returns the official Google Device Plugin pod for NVIDIA GPU in GKE
func NVIDIADevicePlugin() *v1.Pod {
	ds, err := e2emanifest.DaemonSetFromURL(e2egpu.GPUDevicePluginDSYAML)
	framework.ExpectNoError(err)
	p := &v1.Pod{
		ObjectMeta: metav1.ObjectMeta{
			Name: "device-plugin-nvidia-gpu-" + string(uuid.NewUUID()),
		},
		Spec: ds.Spec.Template.Spec,
	}
	// Remove node affinity
	p.Spec.Affinity = nil
	return p
}

// Serial because the test restarts Kubelet
var _ = SIGDescribe("NVIDIA GPU Device Plugin [Feature:GPUDevicePlugin][NodeFeature:GPUDevicePlugin][Serial] [Disruptive]", func() {
	f := framework.NewDefaultFramework("device-plugin-gpus-errors")

	ginkgo.Context("DevicePlugin", func() {
		var devicePluginPod *v1.Pod
		ginkgo.BeforeEach(func() {
			ginkgo.By("Ensuring that Nvidia GPUs exists on the node")
			if !checkIfNvidiaGPUsExistOnNode() {
				ginkgo.Skip("Nvidia GPUs do not exist on the node. Skipping test.")
			}

			if framework.TestContext.ContainerRuntime != "docker" {
				ginkgo.Skip("Test works only with in-tree dockershim. Skipping test.")
			}

			ginkgo.By("Creating the Google Device Plugin pod for NVIDIA GPU")
			devicePluginPod = f.PodClient().Create(NVIDIADevicePlugin())

			ginkgo.By("Waiting for GPUs to become available on the local node")
			gomega.Eventually(func() bool {
				return numberOfNVIDIAGPUs(getLocalNode(f)) > 0
			}, 5*time.Minute, framework.Poll).Should(gomega.BeTrue(), "GPUs never became available on the local node")

			if numberOfNVIDIAGPUs(getLocalNode(f)) < 2 {
				ginkgo.Skip("Not enough GPUs to execute this test (at least two needed)")
			}
		})

		ginkgo.AfterEach(func() {
			l, err := f.PodClient().List(context.TODO(), metav1.ListOptions{})
			framework.ExpectNoError(err)

			for _, p := range l.Items {
				if p.Namespace != f.Namespace.Name {
					continue
				}

				f.PodClient().Delete(context.TODO(), p.Name, metav1.DeleteOptions{})
			}
		})

		ginkgo.It("checks that when Kubelet restarts exclusive GPU assignation to pods is kept.", func() {
			ginkgo.By("Creating one GPU pod on a node with at least two GPUs")
			podRECMD := "devs=$(ls /dev/ | egrep '^nvidia[0-9]+$') && echo gpu devices: $devs && sleep 120"
			p1 := f.PodClient().CreateSync(makeBusyboxPod(e2egpu.NVIDIAGPUResourceName, podRECMD))

			deviceIDRE := "gpu devices: (nvidia[0-9]+)"
			devID1 := parseLog(f, p1.Name, p1.Name, deviceIDRE)
			p1, err := f.PodClient().Get(context.TODO(), p1.Name, metav1.GetOptions{})
			framework.ExpectNoError(err)

			ginkgo.By("Restarting Kubelet and waiting for the current running pod to restart")
			restartKubelet()

			ginkgo.By("Confirming that after a kubelet and pod restart, GPU assignment is kept")
			ensurePodContainerRestart(f, p1.Name, p1.Name)
			devIDRestart1 := parseLog(f, p1.Name, p1.Name, deviceIDRE)
			framework.ExpectEqual(devIDRestart1, devID1)

			ginkgo.By("Restarting Kubelet and creating another pod")
			restartKubelet()
			framework.WaitForAllNodesSchedulable(f.ClientSet, framework.TestContext.NodeSchedulableTimeout)
			gomega.Eventually(func() bool {
				return numberOfNVIDIAGPUs(getLocalNode(f)) > 0
			}, 5*time.Minute, framework.Poll).Should(gomega.BeTrue())
			p2 := f.PodClient().CreateSync(makeBusyboxPod(e2egpu.NVIDIAGPUResourceName, podRECMD))

			ginkgo.By("Checking that pods got a different GPU")
			devID2 := parseLog(f, p2.Name, p2.Name, deviceIDRE)

			framework.ExpectEqual(devID1, devID2)

			ginkgo.By("Deleting device plugin.")
			f.ClientSet.CoreV1().Pods(metav1.NamespaceSystem).Delete(context.TODO(), devicePluginPod.Name, metav1.DeleteOptions{})
			ginkgo.By("Waiting for GPUs to become unavailable on the local node")
			gomega.Eventually(func() bool {
				node, err := f.ClientSet.CoreV1().Nodes().Get(context.TODO(), framework.TestContext.NodeName, metav1.GetOptions{})
				framework.ExpectNoError(err)
				return numberOfNVIDIAGPUs(node) <= 0
			}, 10*time.Minute, framework.Poll).Should(gomega.BeTrue())
			ginkgo.By("Checking that scheduled pods can continue to run even after we delete device plugin.")
			ensurePodContainerRestart(f, p1.Name, p1.Name)
			devIDRestart1 = parseLog(f, p1.Name, p1.Name, deviceIDRE)
			framework.ExpectEqual(devIDRestart1, devID1)

			ensurePodContainerRestart(f, p2.Name, p2.Name)
			devIDRestart2 := parseLog(f, p2.Name, p2.Name, deviceIDRE)
			framework.ExpectEqual(devIDRestart2, devID2)
			ginkgo.By("Restarting Kubelet.")
			restartKubelet()
			ginkgo.By("Checking that scheduled pods can continue to run even after we delete device plugin and restart Kubelet.")
			ensurePodContainerRestart(f, p1.Name, p1.Name)
			devIDRestart1 = parseLog(f, p1.Name, p1.Name, deviceIDRE)
			framework.ExpectEqual(devIDRestart1, devID1)
			ensurePodContainerRestart(f, p2.Name, p2.Name)
			devIDRestart2 = parseLog(f, p2.Name, p2.Name, deviceIDRE)
			framework.ExpectEqual(devIDRestart2, devID2)
			logDevicePluginMetrics()

			// Cleanup
			f.PodClient().DeleteSync(p1.Name, metav1.DeleteOptions{}, framework.DefaultPodDeletionTimeout)
			f.PodClient().DeleteSync(p2.Name, metav1.DeleteOptions{}, framework.DefaultPodDeletionTimeout)
		})
	})
})

func checkIfNvidiaGPUsExistOnNode() bool {
	// Cannot use `lspci` because it is not installed on all distros by default.
	err := exec.Command("/bin/sh", "-c", "find /sys/devices/pci* -type f | grep vendor | xargs cat | grep 0x10de").Run()
	if err != nil {
		framework.Logf("check for nvidia GPUs failed. Got Error: %v", err)
		return false
	}
	return true
}

func logDevicePluginMetrics() {
	ms, err := e2emetrics.GrabKubeletMetricsWithoutProxy(framework.TestContext.NodeName+":10255", "/metrics")
	framework.ExpectNoError(err)
	for msKey, samples := range ms {
		switch msKey {
		case kubeletmetrics.KubeletSubsystem + "_" + kubeletmetrics.DevicePluginAllocationDurationKey:
			for _, sample := range samples {
				latency := sample.Value
				resource := string(sample.Metric["resource_name"])
				var quantile float64
				if val, ok := sample.Metric[testutil.QuantileLabel]; ok {
					var err error
					if quantile, err = strconv.ParseFloat(string(val), 64); err != nil {
						continue
					}
					framework.Logf("Metric: %v ResourceName: %v Quantile: %v Latency: %v", msKey, resource, quantile, latency)
				}
			}
		case kubeletmetrics.KubeletSubsystem + "_" + kubeletmetrics.DevicePluginRegistrationCountKey:
			for _, sample := range samples {
				resource := string(sample.Metric["resource_name"])
				count := sample.Value
				framework.Logf("Metric: %v ResourceName: %v Count: %v", msKey, resource, count)
			}
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package time_test

import (
	"errors"
	"fmt"
	"runtime"
	"strings"
	"sync"
	"sync/atomic"
	"testing"
	. "time"
)

// Go runtime uses different Windows timers for time.Now and sleeping.
// These can tick at different frequencies and can arrive out of sync.
// The effect can be seen, for example, as time.Sleep(100ms) is actually
// shorter then 100ms when measured as difference between time.Now before and
// after time.Sleep call. This was observed on Windows XP SP3 (windows/386).
// windowsInaccuracy is to ignore such errors.
const windowsInaccuracy = 17 * Millisecond

func TestSleep(t *testing.T) {
	const delay = 100 * Millisecond
	go func() {
		Sleep(delay / 2)
		Interrupt()
	}()
	start := Now()
	Sleep(delay)
	delayadj := delay
	if runtime.GOOS == "windows" {
		delayadj -= windowsInaccuracy
	}
	duration := Now().Sub(start)
	if duration < delayadj {
		t.Fatalf("Sleep(%s) slept for only %s", delay, duration)
	}
}

// Test the basic function calling behavior. Correct queueing
// behavior is tested elsewhere, since After and AfterFunc share
// the same code.
func TestAfterFunc(t *testing.T) {
	i := 10
	c := make(chan bool)
	var f func()
	f = func() {
		i--
		if i >= 0 {
			AfterFunc(0, f)
			Sleep(1 * Second)
		} else {
			c <- true
		}
	}

	AfterFunc(0, f)
	<-c
}

func TestAfterStress(t *testing.T) {
	stop := uint32(0)
	go func() {
		for atomic.LoadUint32(&stop) == 0 {
			runtime.GC()
			// Yield so that the OS can wake up the timer thread,
			// so that it can generate channel sends for the main goroutine,
			// which will eventually set stop = 1 for us.
			Sleep(Nanosecond)
		}
	}()
	ticker := NewTicker(1)
	for i := 0; i < 100; i++ {
		<-ticker.C
	}
	ticker.Stop()
	atomic.StoreUint32(&stop, 1)
}

func benchmark(b *testing.B, bench func(n int)) {

	// Create equal number of garbage timers on each P before starting
	// the benchmark.
	var wg sync.WaitGroup
	garbageAll := make([][]*Timer, runtime.GOMAXPROCS(0))
	for i := range garbageAll {
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			garbage := make([]*Timer, 1<<15)
			for j := range garbage {
				garbage[j] = AfterFunc(Hour, nil)
			}
			garbageAll[i] = garbage
		}(i)
	}
	wg.Wait()

	b.ResetTimer()
	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			bench(1000)
		}
	})
	b.StopTimer()

	for _, garbage := range garbageAll {
		for _, t := range garbage {
			t.Stop()
		}
	}
}

func BenchmarkAfterFunc(b *testing.B) {
	benchmark(b, func(n int) {
		c := make(chan bool)
		var f func()
		f = func() {
			n--
			if n >= 0 {
				AfterFunc(0, f)
			} else {
				c <- true
			}
		}

		AfterFunc(0, f)
		<-c
	})
}

func BenchmarkAfter(b *testing.B) {
	benchmark(b, func(n int) {
		for i := 0; i < n; i++ {
			<-After(1)
		}
	})
}

func BenchmarkStop(b *testing.B) {
	benchmark(b, func(n int) {
		for i := 0; i < n; i++ {
			NewTimer(1 * Second).Stop()
		}
	})
}

func BenchmarkSimultaneousAfterFunc(b *testing.B) {
	benchmark(b, func(n int) {
		var wg sync.WaitGroup
		wg.Add(n)
		for i := 0; i < n; i++ {
			AfterFunc(0, wg.Done)
		}
		wg.Wait()
	})
}

func BenchmarkStartStop(b *testing.B) {
	benchmark(b, func(n int) {
		timers := make([]*Timer, n)
		for i := 0; i < n; i++ {
			timers[i] = AfterFunc(Hour, nil)
		}

		for i := 0; i < n; i++ {
			timers[i].Stop()
		}
	})
}

func BenchmarkReset(b *testing.B) {
	benchmark(b, func(n int) {
		t := NewTimer(Hour)
		for i := 0; i < n; i++ {
			t.Reset(Hour)
		}
		t.Stop()
	})
}

func BenchmarkSleep(b *testing.B) {
	benchmark(b, func(n int) {
		var wg sync.WaitGroup
		wg.Add(n)
		for i := 0; i < n; i++ {
			go func() {
				Sleep(Nanosecond)
				wg.Done()
			}()
		}
		wg.Wait()
	})
}

func TestAfter(t *testing.T) {
	const delay = 100 * Millisecond
	start := Now()
	end := <-After(delay)
	delayadj := delay
	if runtime.GOOS == "windows" {
		delayadj -= windowsInaccuracy
	}
	if duration := Now().Sub(start); duration < delayadj {
		t.Fatalf("After(%s) slept for only %d ns", delay, duration)
	}
	if min := start.Add(delayadj); end.Before(min) {
		t.Fatalf("After(%s) expect >= %s, got %s", delay, min, end)
	}
}

func TestAfterTick(t *testing.T) {
	const Count = 10
	Delta := 100 * Millisecond
	if testing.Short() {
		Delta = 10 * Millisecond
	}
	t0 := Now()
	for i := 0; i < Count; i++ {
		<-After(Delta)
	}
	t1 := Now()
	d := t1.Sub(t0)
	target := Delta * Count
	if d < target*9/10 {
		t.Fatalf("%d ticks of %s too fast: took %s, expected %s", Count, Delta, d, target)
	}
	if !testing.Short() && d > target*30/10 {
		t.Fatalf("%d ticks of %s too slow: took %s, expected %s", Count, Delta, d, target)
	}
}

func TestAfterStop(t *testing.T) {
	AfterFunc(100*Millisecond, func() {})
	t0 := NewTimer(50 * Millisecond)
	c1 := make(chan bool, 1)
	t1 := AfterFunc(150*Millisecond, func() { c1 <- true })
	c2 := After(200 * Millisecond)
	if !t0.Stop() {
		t.Fatalf("failed to stop event 0")
	}
	if !t1.Stop() {
		t.Fatalf("failed to stop event 1")
	}
	<-c2
	select {
	case <-t0.C:
		t.Fatalf("event 0 was not stopped")
	case <-c1:
		t.Fatalf("event 1 was not stopped")
	default:
	}
	if t1.Stop() {
		t.Fatalf("Stop returned true twice")
	}
}

func TestAfterQueuing(t *testing.T) {
	// This test flakes out on some systems,
	// so we'll try it a few times before declaring it a failure.
	const attempts = 5
	err := errors.New("!=nil")
	for i := 0; i < attempts && err != nil; i++ {
		delta := Duration(20+i*50) * Millisecond
		if err = testAfterQueuing(delta); err != nil {
			t.Logf("attempt %v failed: %v", i, err)
		}
	}
	if err != nil {
		t.Fatal(err)
	}
}

var slots = []int{5, 3, 6, 6, 6, 1, 1, 2, 7, 9, 4, 8, 0}

type afterResult struct {
	slot int
	t    Time
}

func await(slot int, result chan<- afterResult, ac <-chan Time) {
	result <- afterResult{slot, <-ac}
}

func testAfterQueuing(delta Duration) error {
	// make the result channel buffered because we don't want
	// to depend on channel queueing semantics that might
	// possibly change in the future.
	result := make(chan afterResult, len(slots))

	t0 := Now()
	for _, slot := range slots {
		go await(slot, result, After(Duration(slot)*delta))
	}
	var order []int
	var times []Time
	for range slots {
		r := <-result
		order = append(order, r.slot)
		times = append(times, r.t)
	}
	for i := range order {
		if i > 0 && order[i] < order[i-1] {
			return fmt.Errorf("After calls returned out of order: %v", order)
		}
	}
	for i, t := range times {
		dt := t.Sub(t0)
		target := Duration(order[i]) * delta
		if dt < target-delta/2 || dt > target+delta*10 {
			return fmt.Errorf("After(%s) arrived at %s, expected [%s,%s]", target, dt, target-delta/2, target+delta*10)
		}
	}
	return nil
}

func TestTimerStopStress(t *testing.T) {
	if testing.Short() {
		return
	}
	for i := 0; i < 100; i++ {
		go func(i int) {
			timer := AfterFunc(2*Second, func() {
				t.Fatalf("timer %d was not stopped", i)
			})
			Sleep(1 * Second)
			timer.Stop()
		}(i)
	}
	Sleep(3 * Second)
}

func TestSleepZeroDeadlock(t *testing.T) {
	// Sleep(0) used to hang, the sequence of events was as follows.
	// Sleep(0) sets G's status to Gwaiting, but then immediately returns leaving the status.
	// Then the goroutine calls e.g. new and falls down into the scheduler due to pending GC.
	// After the GC nobody wakes up the goroutine from Gwaiting status.
	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
	c := make(chan bool)
	go func() {
		for i := 0; i < 100; i++ {
			runtime.GC()
		}
		c <- true
	}()
	for i := 0; i < 100; i++ {
		Sleep(0)
		tmp := make(chan bool, 1)
		tmp <- true
		<-tmp
	}
	<-c
}

func testReset(d Duration) error {
	t0 := NewTimer(2 * d)
	Sleep(d)
	if t0.Reset(3*d) != true {
		return errors.New("resetting unfired timer returned false")
	}
	Sleep(2 * d)
	select {
	case <-t0.C:
		return errors.New("timer fired early")
	default:
	}
	Sleep(2 * d)
	select {
	case <-t0.C:
	default:
		return errors.New("reset timer did not fire")
	}

	if t0.Reset(50*Millisecond) != false {
		return errors.New("resetting expired timer returned true")
	}
	return nil
}

func TestReset(t *testing.T) {
	// We try to run this test with increasingly larger multiples
	// until one works so slow, loaded hardware isn't as flaky,
	// but without slowing down fast machines unnecessarily.
	const unit = 25 * Millisecond
	tries := []Duration{
		1 * unit,
		3 * unit,
		7 * unit,
		15 * unit,
	}
	var err error
	for _, d := range tries {
		err = testReset(d)
		if err == nil {
			t.Logf("passed using duration %v", d)
			return
		}
	}
	t.Error(err)
}

// Test that sleeping for an interval so large it overflows does not
// result in a short sleep duration.
func TestOverflowSleep(t *testing.T) {
	const big = Duration(int64(1<<63 - 1))
	select {
	case <-After(big):
		t.Fatalf("big timeout fired")
	case <-After(25 * Millisecond):
		// OK
	}
	const neg = Duration(-1 << 63)
	select {
	case <-After(neg):
		// OK
	case <-After(1 * Second):
		t.Fatalf("negative timeout didn't fire")
	}
}

// Test that a panic while deleting a timer does not leave
// the timers mutex held, deadlocking a ticker.Stop in a defer.
func TestIssue5745(t *testing.T) {
	if runtime.GOOS == "darwin" && runtime.GOARCH == "arm" {
		t.Skipf("skipping on %s/%s, see issue 10043", runtime.GOOS, runtime.GOARCH)
	}

	ticker := NewTicker(Hour)
	defer func() {
		// would deadlock here before the fix due to
		// lock taken before the segfault.
		ticker.Stop()

		if r := recover(); r == nil {
			t.Error("Expected panic, but none happened.")
		}
	}()

	// cause a panic due to a segfault
	var timer *Timer
	timer.Stop()
	t.Error("Should be unreachable.")
}

func TestOverflowRuntimeTimer(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping in short mode, see issue 6874")
	}
	// This may hang forever if timers are broken. See comment near
	// the end of CheckRuntimeTimerOverflow in internal_test.go.
	CheckRuntimeTimerOverflow()
}

func checkZeroPanicString(t *testing.T) {
	e := recover()
	s, _ := e.(string)
	if want := "called on uninitialized Timer"; !strings.Contains(s, want) {
		t.Errorf("panic = %v; want substring %q", e, want)
	}
}

func TestZeroTimerResetPanics(t *testing.T) {
	defer checkZeroPanicString(t)
	var tr Timer
	tr.Reset(1)
}

func TestZeroTimerStopPanics(t *testing.T) {
	defer checkZeroPanicString(t)
	var tr Timer
	tr.Stop()
}
<commit_msg>time: simplify comparison in test<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package time_test

import (
	"errors"
	"fmt"
	"runtime"
	"strings"
	"sync"
	"sync/atomic"
	"testing"
	. "time"
)

// Go runtime uses different Windows timers for time.Now and sleeping.
// These can tick at different frequencies and can arrive out of sync.
// The effect can be seen, for example, as time.Sleep(100ms) is actually
// shorter then 100ms when measured as difference between time.Now before and
// after time.Sleep call. This was observed on Windows XP SP3 (windows/386).
// windowsInaccuracy is to ignore such errors.
const windowsInaccuracy = 17 * Millisecond

func TestSleep(t *testing.T) {
	const delay = 100 * Millisecond
	go func() {
		Sleep(delay / 2)
		Interrupt()
	}()
	start := Now()
	Sleep(delay)
	delayadj := delay
	if runtime.GOOS == "windows" {
		delayadj -= windowsInaccuracy
	}
	duration := Now().Sub(start)
	if duration < delayadj {
		t.Fatalf("Sleep(%s) slept for only %s", delay, duration)
	}
}

// Test the basic function calling behavior. Correct queueing
// behavior is tested elsewhere, since After and AfterFunc share
// the same code.
func TestAfterFunc(t *testing.T) {
	i := 10
	c := make(chan bool)
	var f func()
	f = func() {
		i--
		if i >= 0 {
			AfterFunc(0, f)
			Sleep(1 * Second)
		} else {
			c <- true
		}
	}

	AfterFunc(0, f)
	<-c
}

func TestAfterStress(t *testing.T) {
	stop := uint32(0)
	go func() {
		for atomic.LoadUint32(&stop) == 0 {
			runtime.GC()
			// Yield so that the OS can wake up the timer thread,
			// so that it can generate channel sends for the main goroutine,
			// which will eventually set stop = 1 for us.
			Sleep(Nanosecond)
		}
	}()
	ticker := NewTicker(1)
	for i := 0; i < 100; i++ {
		<-ticker.C
	}
	ticker.Stop()
	atomic.StoreUint32(&stop, 1)
}

func benchmark(b *testing.B, bench func(n int)) {

	// Create equal number of garbage timers on each P before starting
	// the benchmark.
	var wg sync.WaitGroup
	garbageAll := make([][]*Timer, runtime.GOMAXPROCS(0))
	for i := range garbageAll {
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			garbage := make([]*Timer, 1<<15)
			for j := range garbage {
				garbage[j] = AfterFunc(Hour, nil)
			}
			garbageAll[i] = garbage
		}(i)
	}
	wg.Wait()

	b.ResetTimer()
	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			bench(1000)
		}
	})
	b.StopTimer()

	for _, garbage := range garbageAll {
		for _, t := range garbage {
			t.Stop()
		}
	}
}

func BenchmarkAfterFunc(b *testing.B) {
	benchmark(b, func(n int) {
		c := make(chan bool)
		var f func()
		f = func() {
			n--
			if n >= 0 {
				AfterFunc(0, f)
			} else {
				c <- true
			}
		}

		AfterFunc(0, f)
		<-c
	})
}

func BenchmarkAfter(b *testing.B) {
	benchmark(b, func(n int) {
		for i := 0; i < n; i++ {
			<-After(1)
		}
	})
}

func BenchmarkStop(b *testing.B) {
	benchmark(b, func(n int) {
		for i := 0; i < n; i++ {
			NewTimer(1 * Second).Stop()
		}
	})
}

func BenchmarkSimultaneousAfterFunc(b *testing.B) {
	benchmark(b, func(n int) {
		var wg sync.WaitGroup
		wg.Add(n)
		for i := 0; i < n; i++ {
			AfterFunc(0, wg.Done)
		}
		wg.Wait()
	})
}

func BenchmarkStartStop(b *testing.B) {
	benchmark(b, func(n int) {
		timers := make([]*Timer, n)
		for i := 0; i < n; i++ {
			timers[i] = AfterFunc(Hour, nil)
		}

		for i := 0; i < n; i++ {
			timers[i].Stop()
		}
	})
}

func BenchmarkReset(b *testing.B) {
	benchmark(b, func(n int) {
		t := NewTimer(Hour)
		for i := 0; i < n; i++ {
			t.Reset(Hour)
		}
		t.Stop()
	})
}

func BenchmarkSleep(b *testing.B) {
	benchmark(b, func(n int) {
		var wg sync.WaitGroup
		wg.Add(n)
		for i := 0; i < n; i++ {
			go func() {
				Sleep(Nanosecond)
				wg.Done()
			}()
		}
		wg.Wait()
	})
}

func TestAfter(t *testing.T) {
	const delay = 100 * Millisecond
	start := Now()
	end := <-After(delay)
	delayadj := delay
	if runtime.GOOS == "windows" {
		delayadj -= windowsInaccuracy
	}
	if duration := Now().Sub(start); duration < delayadj {
		t.Fatalf("After(%s) slept for only %d ns", delay, duration)
	}
	if min := start.Add(delayadj); end.Before(min) {
		t.Fatalf("After(%s) expect >= %s, got %s", delay, min, end)
	}
}

func TestAfterTick(t *testing.T) {
	const Count = 10
	Delta := 100 * Millisecond
	if testing.Short() {
		Delta = 10 * Millisecond
	}
	t0 := Now()
	for i := 0; i < Count; i++ {
		<-After(Delta)
	}
	t1 := Now()
	d := t1.Sub(t0)
	target := Delta * Count
	if d < target*9/10 {
		t.Fatalf("%d ticks of %s too fast: took %s, expected %s", Count, Delta, d, target)
	}
	if !testing.Short() && d > target*30/10 {
		t.Fatalf("%d ticks of %s too slow: took %s, expected %s", Count, Delta, d, target)
	}
}

func TestAfterStop(t *testing.T) {
	AfterFunc(100*Millisecond, func() {})
	t0 := NewTimer(50 * Millisecond)
	c1 := make(chan bool, 1)
	t1 := AfterFunc(150*Millisecond, func() { c1 <- true })
	c2 := After(200 * Millisecond)
	if !t0.Stop() {
		t.Fatalf("failed to stop event 0")
	}
	if !t1.Stop() {
		t.Fatalf("failed to stop event 1")
	}
	<-c2
	select {
	case <-t0.C:
		t.Fatalf("event 0 was not stopped")
	case <-c1:
		t.Fatalf("event 1 was not stopped")
	default:
	}
	if t1.Stop() {
		t.Fatalf("Stop returned true twice")
	}
}

func TestAfterQueuing(t *testing.T) {
	// This test flakes out on some systems,
	// so we'll try it a few times before declaring it a failure.
	const attempts = 5
	err := errors.New("!=nil")
	for i := 0; i < attempts && err != nil; i++ {
		delta := Duration(20+i*50) * Millisecond
		if err = testAfterQueuing(delta); err != nil {
			t.Logf("attempt %v failed: %v", i, err)
		}
	}
	if err != nil {
		t.Fatal(err)
	}
}

var slots = []int{5, 3, 6, 6, 6, 1, 1, 2, 7, 9, 4, 8, 0}

type afterResult struct {
	slot int
	t    Time
}

func await(slot int, result chan<- afterResult, ac <-chan Time) {
	result <- afterResult{slot, <-ac}
}

func testAfterQueuing(delta Duration) error {
	// make the result channel buffered because we don't want
	// to depend on channel queueing semantics that might
	// possibly change in the future.
	result := make(chan afterResult, len(slots))

	t0 := Now()
	for _, slot := range slots {
		go await(slot, result, After(Duration(slot)*delta))
	}
	var order []int
	var times []Time
	for range slots {
		r := <-result
		order = append(order, r.slot)
		times = append(times, r.t)
	}
	for i := range order {
		if i > 0 && order[i] < order[i-1] {
			return fmt.Errorf("After calls returned out of order: %v", order)
		}
	}
	for i, t := range times {
		dt := t.Sub(t0)
		target := Duration(order[i]) * delta
		if dt < target-delta/2 || dt > target+delta*10 {
			return fmt.Errorf("After(%s) arrived at %s, expected [%s,%s]", target, dt, target-delta/2, target+delta*10)
		}
	}
	return nil
}

func TestTimerStopStress(t *testing.T) {
	if testing.Short() {
		return
	}
	for i := 0; i < 100; i++ {
		go func(i int) {
			timer := AfterFunc(2*Second, func() {
				t.Fatalf("timer %d was not stopped", i)
			})
			Sleep(1 * Second)
			timer.Stop()
		}(i)
	}
	Sleep(3 * Second)
}

func TestSleepZeroDeadlock(t *testing.T) {
	// Sleep(0) used to hang, the sequence of events was as follows.
	// Sleep(0) sets G's status to Gwaiting, but then immediately returns leaving the status.
	// Then the goroutine calls e.g. new and falls down into the scheduler due to pending GC.
	// After the GC nobody wakes up the goroutine from Gwaiting status.
	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
	c := make(chan bool)
	go func() {
		for i := 0; i < 100; i++ {
			runtime.GC()
		}
		c <- true
	}()
	for i := 0; i < 100; i++ {
		Sleep(0)
		tmp := make(chan bool, 1)
		tmp <- true
		<-tmp
	}
	<-c
}

func testReset(d Duration) error {
	t0 := NewTimer(2 * d)
	Sleep(d)
	if !t0.Reset(3 * d) {
		return errors.New("resetting unfired timer returned false")
	}
	Sleep(2 * d)
	select {
	case <-t0.C:
		return errors.New("timer fired early")
	default:
	}
	Sleep(2 * d)
	select {
	case <-t0.C:
	default:
		return errors.New("reset timer did not fire")
	}

	if t0.Reset(50 * Millisecond) {
		return errors.New("resetting expired timer returned true")
	}
	return nil
}

func TestReset(t *testing.T) {
	// We try to run this test with increasingly larger multiples
	// until one works so slow, loaded hardware isn't as flaky,
	// but without slowing down fast machines unnecessarily.
	const unit = 25 * Millisecond
	tries := []Duration{
		1 * unit,
		3 * unit,
		7 * unit,
		15 * unit,
	}
	var err error
	for _, d := range tries {
		err = testReset(d)
		if err == nil {
			t.Logf("passed using duration %v", d)
			return
		}
	}
	t.Error(err)
}

// Test that sleeping for an interval so large it overflows does not
// result in a short sleep duration.
func TestOverflowSleep(t *testing.T) {
	const big = Duration(int64(1<<63 - 1))
	select {
	case <-After(big):
		t.Fatalf("big timeout fired")
	case <-After(25 * Millisecond):
		// OK
	}
	const neg = Duration(-1 << 63)
	select {
	case <-After(neg):
		// OK
	case <-After(1 * Second):
		t.Fatalf("negative timeout didn't fire")
	}
}

// Test that a panic while deleting a timer does not leave
// the timers mutex held, deadlocking a ticker.Stop in a defer.
func TestIssue5745(t *testing.T) {
	if runtime.GOOS == "darwin" && runtime.GOARCH == "arm" {
		t.Skipf("skipping on %s/%s, see issue 10043", runtime.GOOS, runtime.GOARCH)
	}

	ticker := NewTicker(Hour)
	defer func() {
		// would deadlock here before the fix due to
		// lock taken before the segfault.
		ticker.Stop()

		if r := recover(); r == nil {
			t.Error("Expected panic, but none happened.")
		}
	}()

	// cause a panic due to a segfault
	var timer *Timer
	timer.Stop()
	t.Error("Should be unreachable.")
}

func TestOverflowRuntimeTimer(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping in short mode, see issue 6874")
	}
	// This may hang forever if timers are broken. See comment near
	// the end of CheckRuntimeTimerOverflow in internal_test.go.
	CheckRuntimeTimerOverflow()
}

func checkZeroPanicString(t *testing.T) {
	e := recover()
	s, _ := e.(string)
	if want := "called on uninitialized Timer"; !strings.Contains(s, want) {
		t.Errorf("panic = %v; want substring %q", e, want)
	}
}

func TestZeroTimerResetPanics(t *testing.T) {
	defer checkZeroPanicString(t)
	var tr Timer
	tr.Reset(1)
}

func TestZeroTimerStopPanics(t *testing.T) {
	defer checkZeroPanicString(t)
	var tr Timer
	tr.Stop()
}
<|endoftext|>


<commit_before>// Copyright 2016 Volker Dobler.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// showcase implements a server to run the showcase.suite against
package main

import (
	"flag"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"os"
	"time"
)

var (
	port = flag.String("port", ":8080", "Port on localhost to run the showcase server.")
)

func main() {
	flag.Parse()
	http.HandleFunc("/", homeHandler)
	http.HandleFunc("/admin/load", loadHandler)
	http.HandleFunc("/login", loginHandler)
	http.HandleFunc("/not/there", missingHandler)
	http.HandleFunc("/api/v1/books", booksHandler)
	http.HandleFunc("/api/v1/", jsonHandler)
	http.HandleFunc("/static/image/", logoHandler)
	http.HandleFunc("/search", searchHandler)
	http.HandleFunc("/tac.pdf", tacHandler)
	http.HandleFunc("/server/ready", readyHandler)
	http.HandleFunc("/shut/down/now", shutdownHandler)
	log.Fatal(http.ListenAndServe(*port, nil))
}

func homeHandler(w http.ResponseWriter, r *http.Request) {
	time.Sleep(time.Duration(rand.Intn(9)+1) * time.Millisecond)
	if sc, err := r.Cookie("sessionid"); err != nil || sc.Value == "" {
		sc = &http.Cookie{Name: "sessionid", Value: "abc123E", Path: "/"}
		http.SetCookie(w, sc)
	}
	w.Header().Set("Warning", "Demo only!")
	w.Header().Set("X-Frame-Options", "none")
	fmt.Fprintf(w, `<!doctype html>

<title>Showcase</title>
<link href="/not/there" />

Welcome to the demo server

Less Bugs

 A broken link

Offer 1

Offer 2

Happiness

 Other endpoints: some JSON and
 some XML</>

Login

Look for gluons

  Host: Atari 1040 ST

`)
}

func loadHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	w.WriteHeader(http.StatusAccepted)
	fmt.Fprintf(w, `<!doctype html>

<title>Load</title>

Loading...

`)
}

func loginHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	http.SetCookie(w, &http.Cookie{Name: "history", Value: "", Path: "/"})
	http.SetCookie(w, &http.Cookie{Name: "username", Value: "Joe Average", Path: "/"})
	http.SetCookie(w, &http.Cookie{Name: "session", Value: "123random",
		Path: "/", MaxAge: 300})
	fmt.Fprintf(w, "Welcome Joe Average!")
}

var readyPolled int

func readyHandler(w http.ResponseWriter, r *http.Request) {
	readyPolled++
	if readyPolled < 3 {
		w.WriteHeader(http.StatusServiceUnavailable)
		fmt.Fprintln(w, "System not ready jet")
		return
	}
	w.WriteHeader(http.StatusOK)
	fmt.Fprintln(w, "System up and running")
}

func missingHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
	w.WriteHeader(http.StatusNotFound)
	fmt.Fprintf(w, "Ooops")
}

func booksHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/xml; charset=utf-8")
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, `<?xml version="1.0" encoding="UTF-8"?>
<library>

  <book id="b0836217462" available="true">
    <isbn>0836217462</isbn>
    <title lang="en">Being a Dog Is a Full-Time Job</title>
    <quote>I'd dog paddle the deepest ocean.</quote>
    <author id="CMS">
      <?echo "go rocks"?>
      <name>Charles M Schulz</name>
      <born>1922-11-26</born>
      <dead>2000-02-12</dead>
    </author>
    <character id="PP">
      <name>Peppermint Patty</name>
      <born>1966-08-22</born>
      <qualification>bold, brash and tomboyish</qualification>
    </character>
    <character id="Snoopy">
      <name>Snoopy</name>
      <born>1950-10-04</born>
      <qualification>extroverted beagle</qualification>
    </character>
  </book>
  <book id="299,792,459" available="true">
    <title lang="en">Faster than light</title>
    <character>
      <name>Flash Gordon</name>
    </character>
  </book>
  <book unpublished="true">
    <title lang="en">The year 3826 in pictures</title>
  </book>

</library>`)
}

func logoHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "image/png")
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52"+
		"\x00\x00\x00\x08\x00\x00\x00\x06\x08\x06\x00\x00\x00\xfe\x05\xdf"+
		"\xfb\x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00"+
		"\x00\x06\x62\x4b\x47\x44\x00\x00\x00\x00\x00\x00\xf9\x43\xbb\x7f"+
		"\x00\x00\x00\x34\x49\x44\x41\x54\x08\xd7\x85\x8e\x41\x0e\x00\x20"+
		"\x0c\xc2\x28\xff\xff\x33\x9e\x30\x6a\xa2\x72\x21\xa3\x5b\x06\x49"+
		"\xa2\x87\x2c\x49\xc0\x16\xae\xb3\xcf\x8b\xc2\xba\x57\x00\xa8\x1f"+
		"\xeb\x73\xe1\x56\xc5\xfa\x68\x00\x8c\x59\x0d\x11\x87\x39\xe4\xc3"+
		"\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82")
}

func tacHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/pdf")
	w.Header().Set("Content-Disposition", `attachment; filename="tac.pdf"`)
	w.WriteHeader(http.StatusOK)
	fmt.Fprintln(w, `%PDF-1.4
1 0 obj
<< /Type /Catalog
/Outlines 2 0 R
/Pages 3 0 R
>>
endobj
2 0 obj
<< /Type /Outlines
/Count 0
>>
endobj
3 0 obj
<< /Type /Pages
/Kids [4 0 R]
/Count 1
>>
endobj
4 0 obj
<< /Type /Page
/Parent 3 0 R
/MediaBox [0 0 612 792]
/Contents 5 0 R
/Resources << /ProcSet 6 0 R
/Font << /F1 7 0 R >>
>>
>>
endobj
5 0 obj
<< /Length 73 >>
stream
BT
/F1 24 Tf
100 100 Td
(ht rules) Tj
ET
endstream
endobj
6 0 obj
[/PDF /Text]
endobj
7 0 obj
<< /Type /Font
/Subtype /Type1
/Name /F1
/BaseFont /Helvetica
/Encoding /MacRomanEncoding
>>
endobj
xref
0 8
0000000000 65535 f
0000000009 00000 n
0000000074 00000 n
0000000120 00000 n
0000000179 00000 n
0000000364 00000 n
0000000466 00000 n
0000000496 00000 n
trailer
<< /Size 8
/Root 1 0 R
>>
startxref
625
%%EOF`)
}

func jsonHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, `
{
  "query": "jo nesbo",
  "result: [ 1, 2 ]
}`)
}

func searchHandler(w http.ResponseWriter, r *http.Request) {
	q := r.FormValue("q")
	switch q {
	case "gluon":
		fmt.Fprintf(w, "Search for 'gluon' found 12 results.\n")
	case "quark":
		fmt.Fprintf(w, "Search for 'quark' found 8^16 results.\n")
	case "tachyon":
		fmt.Fprintf(w, "Search for 'tachyon' found no results.\n")
		fmt.Fprintf(w, "Please try again.")
	case "axion":
		fmt.Fprintf(w, "Search for 'axion' found really no results.\n")
		fmt.Fprintf(w, "But please keep looking.\n")
	default:
		fmt.Fprintf(w, "Nothing to see here. try 'gluon' or 'axion'.\n")
	}
}

func shutdownHandler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusServiceUnavailable)
	fmt.Fprintln(w, "System shuting down")
	time.Sleep(100 * time.Millisecond)
	os.Exit(0)
}
<commit_msg>showcase: use valid json to demonstrate json checks<commit_after>// Copyright 2016 Volker Dobler.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// showcase implements a server to run the showcase.suite against
package main

import (
	"flag"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"os"
	"time"
)

var (
	port = flag.String("port", ":8080", "Port on localhost to run the showcase server.")
)

func main() {
	flag.Parse()
	http.HandleFunc("/", homeHandler)
	http.HandleFunc("/admin/load", loadHandler)
	http.HandleFunc("/login", loginHandler)
	http.HandleFunc("/not/there", missingHandler)
	http.HandleFunc("/api/v1/books", booksHandler)
	http.HandleFunc("/api/v1/", jsonHandler)
	http.HandleFunc("/static/image/", logoHandler)
	http.HandleFunc("/search", searchHandler)
	http.HandleFunc("/tac.pdf", tacHandler)
	http.HandleFunc("/server/ready", readyHandler)
	http.HandleFunc("/shut/down/now", shutdownHandler)
	log.Fatal(http.ListenAndServe(*port, nil))
}

func homeHandler(w http.ResponseWriter, r *http.Request) {
	time.Sleep(time.Duration(rand.Intn(9)+1) * time.Millisecond)
	if sc, err := r.Cookie("sessionid"); err != nil || sc.Value == "" {
		sc = &http.Cookie{Name: "sessionid", Value: "abc123E", Path: "/"}
		http.SetCookie(w, sc)
	}
	w.Header().Set("Warning", "Demo only!")
	w.Header().Set("X-Frame-Options", "none")
	fmt.Fprintf(w, `<!doctype html>

<title>Showcase</title>
<link href="/not/there" />

Welcome to the demo server

Less Bugs

 A broken link

Offer 1

Offer 2

Happiness

 Other endpoints: some JSON and
 some XML</>

Login

Look for gluons

  Host: Atari 1040 ST

`)
}

func loadHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	w.WriteHeader(http.StatusAccepted)
	fmt.Fprintf(w, `<!doctype html>

<title>Load</title>

Loading...

`)
}

func loginHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	http.SetCookie(w, &http.Cookie{Name: "history", Value: "", Path: "/"})
	http.SetCookie(w, &http.Cookie{Name: "username", Value: "Joe Average", Path: "/"})
	http.SetCookie(w, &http.Cookie{Name: "session", Value: "123random",
		Path: "/", MaxAge: 300})
	fmt.Fprintf(w, "Welcome Joe Average!")
}

var readyPolled int

func readyHandler(w http.ResponseWriter, r *http.Request) {
	readyPolled++
	if readyPolled < 3 {
		w.WriteHeader(http.StatusServiceUnavailable)
		fmt.Fprintln(w, "System not ready jet")
		return
	}
	w.WriteHeader(http.StatusOK)
	fmt.Fprintln(w, "System up and running")
}

func missingHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
	w.WriteHeader(http.StatusNotFound)
	fmt.Fprintf(w, "Ooops")
}

func booksHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/xml; charset=utf-8")
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, `<?xml version="1.0" encoding="UTF-8"?>
<library>

  <book id="b0836217462" available="true">
    <isbn>0836217462</isbn>
    <title lang="en">Being a Dog Is a Full-Time Job</title>
    <quote>I'd dog paddle the deepest ocean.</quote>
    <author id="CMS">
      <?echo "go rocks"?>
      <name>Charles M Schulz</name>
      <born>1922-11-26</born>
      <dead>2000-02-12</dead>
    </author>
    <character id="PP">
      <name>Peppermint Patty</name>
      <born>1966-08-22</born>
      <qualification>bold, brash and tomboyish</qualification>
    </character>
    <character id="Snoopy">
      <name>Snoopy</name>
      <born>1950-10-04</born>
      <qualification>extroverted beagle</qualification>
    </character>
  </book>
  <book id="299,792,459" available="true">
    <title lang="en">Faster than light</title>
    <character>
      <name>Flash Gordon</name>
    </character>
  </book>
  <book unpublished="true">
    <title lang="en">The year 3826 in pictures</title>
  </book>

</library>`)
}

func logoHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "image/png")
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52"+
		"\x00\x00\x00\x08\x00\x00\x00\x06\x08\x06\x00\x00\x00\xfe\x05\xdf"+
		"\xfb\x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00"+
		"\x00\x06\x62\x4b\x47\x44\x00\x00\x00\x00\x00\x00\xf9\x43\xbb\x7f"+
		"\x00\x00\x00\x34\x49\x44\x41\x54\x08\xd7\x85\x8e\x41\x0e\x00\x20"+
		"\x0c\xc2\x28\xff\xff\x33\x9e\x30\x6a\xa2\x72\x21\xa3\x5b\x06\x49"+
		"\xa2\x87\x2c\x49\xc0\x16\xae\xb3\xcf\x8b\xc2\xba\x57\x00\xa8\x1f"+
		"\xeb\x73\xe1\x56\xc5\xfa\x68\x00\x8c\x59\x0d\x11\x87\x39\xe4\xc3"+
		"\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82")
}

func tacHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/pdf")
	w.Header().Set("Content-Disposition", `attachment; filename="tac.pdf"`)
	w.WriteHeader(http.StatusOK)
	fmt.Fprintln(w, `%PDF-1.4
1 0 obj
<< /Type /Catalog
/Outlines 2 0 R
/Pages 3 0 R
>>
endobj
2 0 obj
<< /Type /Outlines
/Count 0
>>
endobj
3 0 obj
<< /Type /Pages
/Kids [4 0 R]
/Count 1
>>
endobj
4 0 obj
<< /Type /Page
/Parent 3 0 R
/MediaBox [0 0 612 792]
/Contents 5 0 R
/Resources << /ProcSet 6 0 R
/Font << /F1 7 0 R >>
>>
>>
endobj
5 0 obj
<< /Length 73 >>
stream
BT
/F1 24 Tf
100 100 Td
(ht rules) Tj
ET
endstream
endobj
6 0 obj
[/PDF /Text]
endobj
7 0 obj
<< /Type /Font
/Subtype /Type1
/Name /F1
/BaseFont /Helvetica
/Encoding /MacRomanEncoding
>>
endobj
xref
0 8
0000000000 65535 f
0000000009 00000 n
0000000074 00000 n
0000000120 00000 n
0000000179 00000 n
0000000364 00000 n
0000000466 00000 n
0000000496 00000 n
trailer
<< /Size 8
/Root 1 0 R
>>
startxref
625
%%EOF`)
}

func jsonHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, `
{
  "query": "jo nesbo",
  "result": [ 1, 2 ]
}`)
}

func searchHandler(w http.ResponseWriter, r *http.Request) {
	q := r.FormValue("q")
	switch q {
	case "gluon":
		fmt.Fprintf(w, "Search for 'gluon' found 12 results.\n")
	case "quark":
		fmt.Fprintf(w, "Search for 'quark' found 8^16 results.\n")
	case "tachyon":
		fmt.Fprintf(w, "Search for 'tachyon' found no results.\n")
		fmt.Fprintf(w, "Please try again.")
	case "axion":
		fmt.Fprintf(w, "Search for 'axion' found really no results.\n")
		fmt.Fprintf(w, "But please keep looking.\n")
	default:
		fmt.Fprintf(w, "Nothing to see here. try 'gluon' or 'axion'.\n")
	}
}

func shutdownHandler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusServiceUnavailable)
	fmt.Fprintln(w, "System shuting down")
	time.Sleep(100 * time.Millisecond)
	os.Exit(0)
}
<|endoftext|>