<commit_before>package fastly

import (
	"fmt"
	"sort"
)

// Backend represents a backend response from the Fastly API.
type Backend struct {
	ServiceID string `mapstructure:"service_id"`
	Version   string `mapstructure:"version"`

	Name                string   `mapstructure:"name"`
	Address             string   `mapstructure:"address"`
	Port                uint     `mapstructure:"port"`
	ConnectTimeout      uint     `mapstructure:"connect_timeout"`
	MaxConn             uint     `mapstructure:"max_conn"`
	ErrorThreshold      uint     `mapstructure:"error_threshold"`
	FirstByteTimeout    uint     `mapstructure:"first_byte_timeout"`
	BetweenBytesTimeout uint     `mapstructure:"between_bytes_timeout"`
	AutoLoadbalance     bool     `mapstructure:"auto_loadbalance"`
	Weight              uint     `mapstructure:"weight"`
	RequestCondition    string   `mapstructure:"request_condition"`
	HealthCheck         string   `mapstructure:"healthcheck"`
	Hostname            string   `mapstructure:"hostname"`
	Shield              string   `mapstructure:"shield"`
	UseSSL              bool     `mapstructure:"use_ssl"`
	SSLCheckCert        bool     `mapstructure:"ssl_check_cert"`
	SSLHostname         string   `mapstructure:"ssl_hostname"`
	SSLCertHostname     string   `mapstructure:"ssl_cert_hostname"`
	SSLSNIHostname      string   `mapstructure:"ssl_sni_hostname"`
	MinTLSVersion       string   `mapstructure:"min_tls_version"`
	MaxTLSVersion       string   `mapstructure:"max_tls_version"`
	SSLCiphers          []string `mapstructure:"ssl_ciphers"`
}

// backendsByName is a sortable list of backends.
type backendsByName []*Backend

// Len, Swap, and Less implement the sortable interface.
func (s backendsByName) Len() int      { return len(s) }
func (s backendsByName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s backendsByName) Less(i, j int) bool {
	return s[i].Name < s[j].Name
}

// ListBackendsInput is used as input to the ListBackends function.
type ListBackendsInput struct {
	// Service is the ID of the service (required).
	Service string

	// Version is the specific configuration version (required).
	Version string
}

// ListBackends returns the list of backends for the configuration version.
func (c *Client) ListBackends(i *ListBackendsInput) ([]*Backend, error) {
	if i.Service == "" {
		return nil, ErrMissingService
	}

	if i.Version == "" {
		return nil, ErrMissingVersion
	}

	path := fmt.Sprintf("/service/%s/version/%s/backend", i.Service, i.Version)
	resp, err := c.Get(path, nil)
	if err != nil {
		return nil, err
	}

	var bs []*Backend
	if err := decodeJSON(&bs, resp.Body); err != nil {
		return nil, err
	}
	sort.Stable(backendsByName(bs))
	return bs, nil
}

// CreateBackendInput is used as input to the CreateBackend function.
type CreateBackendInput struct {
	// Service is the ID of the service. Version is the specific configuration
	// version. Both fields are required.
	Service string
	Version string

	Name                string   `form:"name,omitempty"`
	Address             string   `form:"address,omitempty"`
	Port                uint     `form:"port,omitempty"`
	ConnectTimeout      uint     `form:"connect_timeout,omitempty"`
	MaxConn             uint     `form:"max_conn,omitempty"`
	ErrorThreshold      uint     `form:"error_threshold,omitempty"`
	FirstByteTimeout    uint     `form:"first_byte_timeout,omitempty"`
	BetweenBytesTimeout uint     `form:"between_bytes_timeout,omitempty"`
	AutoLoadbalance     *bool    `form:"auto_loadbalance,omitempty"`
	Weight              uint     `form:"weight,omitempty"`
	RequestCondition    string   `form:"request_condition,omitempty"`
	HealthCheck         string   `form:"healthcheck,omitempty"`
	Shield              string   `form:"shield,omitempty"`
	UseSSL              *bool    `form:"use_ssl,omitempty"`
	SSLCheckCert        *bool    `form:"ssl_check_cert,omitempty"`
	SSLHostname         string   `form:"ssl_hostname,omitempty"`
	SSLCertHostname     string   `form:"ssl_cert_hostname,omitempty"`
	SSLSNIHostname      string   `form:"ssl_sni_hostname,omitempty"`
	MinTLSVersion       string   `form:"min_tls_version,omitempty"`
	MaxTLSVersion       string   `form:"max_tls_version,omitempty"`
	SSLCiphers          []string `form:"ssl_ciphers,omitempty"`
}

// CreateBackend creates a new Fastly backend.
func (c *Client) CreateBackend(i *CreateBackendInput) (*Backend, error) {
	if i.Service == "" {
		return nil, ErrMissingService
	}

	if i.Version == "" {
		return nil, ErrMissingVersion
	}

	path := fmt.Sprintf("/service/%s/version/%s/backend", i.Service, i.Version)
	resp, err := c.PostForm(path, i, nil)
	if err != nil {
		return nil, err
	}

	var b *Backend
	if err := decodeJSON(&b, resp.Body); err != nil {
		return nil, err
	}
	return b, nil
}

// GetBackendInput is used as input to the GetBackend function.
type GetBackendInput struct {
	// Service is the ID of the service. Version is the specific configuration
	// version. Both fields are required.
	Service string
	Version string

	// Name is the name of the backend to fetch.
	Name string
}

// GetBackend gets the backend configuration with the given parameters.
func (c *Client) GetBackend(i *GetBackendInput) (*Backend, error) {
	if i.Service == "" {
		return nil, ErrMissingService
	}

	if i.Version == "" {
		return nil, ErrMissingVersion
	}

	if i.Name == "" {
		return nil, ErrMissingName
	}

	path := fmt.Sprintf("/service/%s/version/%s/backend/%s", i.Service, i.Version, i.Name)
	resp, err := c.Get(path, nil)
	if err != nil {
		return nil, err
	}

	var b *Backend
	if err := decodeJSON(&b, resp.Body); err != nil {
		return nil, err
	}
	return b, nil
}

// UpdateBackendInput is used as input to the UpdateBackend function.
type UpdateBackendInput struct {
	// Service is the ID of the service. Version is the specific configuration
	// version. Both fields are required.
	Service string
	Version string

	// Name is the name of the backend to update.
	Name string

	NewName             string   `form:"name,omitempty"`
	Address             string   `form:"address,omitempty"`
	Port                uint     `form:"port,omitempty"`
	ConnectTimeout      uint     `form:"connect_timeout,omitempty"`
	MaxConn             uint     `form:"max_conn,omitempty"`
	ErrorThreshold      uint     `form:"error_threshold,omitempty"`
	FirstByteTimeout    uint     `form:"first_byte_timeout,omitempty"`
	BetweenBytesTimeout uint     `form:"between_bytes_timeout,omitempty"`
	AutoLoadbalance     *bool    `form:"auto_loadbalance,omitempty"`
	Weight              uint     `form:"weight,omitempty"`
	RequestCondition    string   `form:"request_condition,omitempty"`
	HealthCheck         string   `form:"healthcheck,omitempty"`
	Shield              string   `form:"shield,omitempty"`
	UseSSL              *bool    `form:"use_ssl,omitempty"`
	SSLCheckCert        *bool    `form:"ssl_check_cert,omitempty"`
	SSLHostname         string   `form:"ssl_hostname,omitempty"`
	SSLCertHostname     string   `form:"ssl_cert_hostname,omitempty"`
	SSLSNIHostname      string   `form:"ssl_sni_hostname,omitempty"`
	MinTLSVersion       string   `form:"min_tls_version,omitempty"`
	MaxTLSVersion       string   `form:"max_tls_version,omitempty"`
	SSLCiphers          []string `form:"ssl_ciphers,omitempty"`
}

// UpdateBackend updates a specific backend.
func (c *Client) UpdateBackend(i *UpdateBackendInput) (*Backend, error) {
	if i.Service == "" {
		return nil, ErrMissingService
	}

	if i.Version == "" {
		return nil, ErrMissingVersion
	}

	if i.Name == "" {
		return nil, ErrMissingName
	}

	path := fmt.Sprintf("/service/%s/version/%s/backend/%s", i.Service, i.Version, i.Name)
	resp, err := c.PutForm(path, i, nil)
	if err != nil {
		return nil, err
	}

	var b *Backend
	if err := decodeJSON(&b, resp.Body); err != nil {
		return nil, err
	}
	return b, nil
}

// DeleteBackendInput is the input parameter to DeleteBackend.
type DeleteBackendInput struct {
	// Service is the ID of the service. Version is the specific configuration
	// version. Both fields are required.
	Service string
	Version string

	// Name is the name of the backend to delete (required).
	Name string
}

// DeleteBackend deletes the given backend version.
func (c *Client) DeleteBackend(i *DeleteBackendInput) error {
	if i.Service == "" {
		return ErrMissingService
	}

	if i.Version == "" {
		return ErrMissingVersion
	}

	if i.Name == "" {
		return ErrMissingName
	}

	path := fmt.Sprintf("/service/%s/version/%s/backend/%s", i.Service, i.Version, i.Name)
	resp, err := c.Delete(path, nil)
	if err != nil {
		return err
	}

	var r *statusResp
	if err := decodeJSON(&r, resp.Body); err != nil {
		return err
	}
	if !r.Ok() {
		return fmt.Errorf("Not Ok")
	}
	return nil
}
<commit_msg>Use Compatibool for the Backend requests<commit_after>package fastly

import (
	"fmt"
	"sort"
)

// Backend represents a backend response from the Fastly API.
type Backend struct {
	ServiceID string `mapstructure:"service_id"`
	Version   string `mapstructure:"version"`

	Name                string   `mapstructure:"name"`
	Address             string   `mapstructure:"address"`
	Port                uint     `mapstructure:"port"`
	ConnectTimeout      uint     `mapstructure:"connect_timeout"`
	MaxConn             uint     `mapstructure:"max_conn"`
	ErrorThreshold      uint     `mapstructure:"error_threshold"`
	FirstByteTimeout    uint     `mapstructure:"first_byte_timeout"`
	BetweenBytesTimeout uint     `mapstructure:"between_bytes_timeout"`
	AutoLoadbalance     bool     `mapstructure:"auto_loadbalance"`
	Weight              uint     `mapstructure:"weight"`
	RequestCondition    string   `mapstructure:"request_condition"`
	HealthCheck         string   `mapstructure:"healthcheck"`
	Hostname            string   `mapstructure:"hostname"`
	Shield              string   `mapstructure:"shield"`
	UseSSL              bool     `mapstructure:"use_ssl"`
	SSLCheckCert        bool     `mapstructure:"ssl_check_cert"`
	SSLHostname         string   `mapstructure:"ssl_hostname"`
	SSLCertHostname     string   `mapstructure:"ssl_cert_hostname"`
	SSLSNIHostname      string   `mapstructure:"ssl_sni_hostname"`
	MinTLSVersion       string   `mapstructure:"min_tls_version"`
	MaxTLSVersion       string   `mapstructure:"max_tls_version"`
	SSLCiphers          []string `mapstructure:"ssl_ciphers"`
}

// backendsByName is a sortable list of backends.
type backendsByName []*Backend

// Len, Swap, and Less implement the sortable interface.
func (s backendsByName) Len() int      { return len(s) }
func (s backendsByName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s backendsByName) Less(i, j int) bool {
	return s[i].Name < s[j].Name
}

// ListBackendsInput is used as input to the ListBackends function.
type ListBackendsInput struct {
	// Service is the ID of the service (required).
	Service string

	// Version is the specific configuration version (required).
	Version string
}

// ListBackends returns the list of backends for the configuration version.
func (c *Client) ListBackends(i *ListBackendsInput) ([]*Backend, error) {
	if i.Service == "" {
		return nil, ErrMissingService
	}

	if i.Version == "" {
		return nil, ErrMissingVersion
	}

	path := fmt.Sprintf("/service/%s/version/%s/backend", i.Service, i.Version)
	resp, err := c.Get(path, nil)
	if err != nil {
		return nil, err
	}

	var bs []*Backend
	if err := decodeJSON(&bs, resp.Body); err != nil {
		return nil, err
	}
	sort.Stable(backendsByName(bs))
	return bs, nil
}

// CreateBackendInput is used as input to the CreateBackend function.
type CreateBackendInput struct {
	// Service is the ID of the service. Version is the specific configuration
	// version. Both fields are required.
	Service string
	Version string

	Name                string      `form:"name,omitempty"`
	Address             string      `form:"address,omitempty"`
	Port                uint        `form:"port,omitempty"`
	ConnectTimeout      uint        `form:"connect_timeout,omitempty"`
	MaxConn             uint        `form:"max_conn,omitempty"`
	ErrorThreshold      uint        `form:"error_threshold,omitempty"`
	FirstByteTimeout    uint        `form:"first_byte_timeout,omitempty"`
	BetweenBytesTimeout uint        `form:"between_bytes_timeout,omitempty"`
	AutoLoadbalance     Compatibool `form:"auto_loadbalance,omitempty"`
	Weight              uint        `form:"weight,omitempty"`
	RequestCondition    string      `form:"request_condition,omitempty"`
	HealthCheck         string      `form:"healthcheck,omitempty"`
	Shield              string      `form:"shield,omitempty"`
	UseSSL              Compatibool `form:"use_ssl,omitempty"`
	SSLCheckCert        Compatibool `form:"ssl_check_cert,omitempty"`
	SSLHostname         string      `form:"ssl_hostname,omitempty"`
	SSLCertHostname     string      `form:"ssl_cert_hostname,omitempty"`
	SSLSNIHostname      string      `form:"ssl_sni_hostname,omitempty"`
	MinTLSVersion       string      `form:"min_tls_version,omitempty"`
	MaxTLSVersion       string      `form:"max_tls_version,omitempty"`
	SSLCiphers          []string    `form:"ssl_ciphers,omitempty"`
}

// CreateBackend creates a new Fastly backend.
func (c *Client) CreateBackend(i *CreateBackendInput) (*Backend, error) {
	if i.Service == "" {
		return nil, ErrMissingService
	}

	if i.Version == "" {
		return nil, ErrMissingVersion
	}

	path := fmt.Sprintf("/service/%s/version/%s/backend", i.Service, i.Version)
	resp, err := c.PostForm(path, i, nil)
	if err != nil {
		return nil, err
	}

	var b *Backend
	if err := decodeJSON(&b, resp.Body); err != nil {
		return nil, err
	}
	return b, nil
}

// GetBackendInput is used as input to the GetBackend function.
type GetBackendInput struct {
	// Service is the ID of the service. Version is the specific configuration
	// version. Both fields are required.
	Service string
	Version string

	// Name is the name of the backend to fetch.
	Name string
}

// GetBackend gets the backend configuration with the given parameters.
func (c *Client) GetBackend(i *GetBackendInput) (*Backend, error) {
	if i.Service == "" {
		return nil, ErrMissingService
	}

	if i.Version == "" {
		return nil, ErrMissingVersion
	}

	if i.Name == "" {
		return nil, ErrMissingName
	}

	path := fmt.Sprintf("/service/%s/version/%s/backend/%s", i.Service, i.Version, i.Name)
	resp, err := c.Get(path, nil)
	if err != nil {
		return nil, err
	}

	var b *Backend
	if err := decodeJSON(&b, resp.Body); err != nil {
		return nil, err
	}
	return b, nil
}

// UpdateBackendInput is used as input to the UpdateBackend function.
type UpdateBackendInput struct {
	// Service is the ID of the service. Version is the specific configuration
	// version. Both fields are required.
	Service string
	Version string

	// Name is the name of the backend to update.
	Name string

	NewName             string      `form:"name,omitempty"`
	Address             string      `form:"address,omitempty"`
	Port                uint        `form:"port,omitempty"`
	ConnectTimeout      uint        `form:"connect_timeout,omitempty"`
	MaxConn             uint        `form:"max_conn,omitempty"`
	ErrorThreshold      uint        `form:"error_threshold,omitempty"`
	FirstByteTimeout    uint        `form:"first_byte_timeout,omitempty"`
	BetweenBytesTimeout uint        `form:"between_bytes_timeout,omitempty"`
	AutoLoadbalance     Compatibool `form:"auto_loadbalance,omitempty"`
	Weight              uint        `form:"weight,omitempty"`
	RequestCondition    string      `form:"request_condition,omitempty"`
	HealthCheck         string      `form:"healthcheck,omitempty"`
	Shield              string      `form:"shield,omitempty"`
	UseSSL              Compatibool `form:"use_ssl,omitempty"`
	SSLCheckCert        Compatibool `form:"ssl_check_cert,omitempty"`
	SSLHostname         string      `form:"ssl_hostname,omitempty"`
	SSLCertHostname     string      `form:"ssl_cert_hostname,omitempty"`
	SSLSNIHostname      string      `form:"ssl_sni_hostname,omitempty"`
	MinTLSVersion       string      `form:"min_tls_version,omitempty"`
	MaxTLSVersion       string      `form:"max_tls_version,omitempty"`
	SSLCiphers          []string    `form:"ssl_ciphers,omitempty"`
}

// UpdateBackend updates a specific backend.
func (c *Client) UpdateBackend(i *UpdateBackendInput) (*Backend, error) {
	if i.Service == "" {
		return nil, ErrMissingService
	}

	if i.Version == "" {
		return nil, ErrMissingVersion
	}

	if i.Name == "" {
		return nil, ErrMissingName
	}

	path := fmt.Sprintf("/service/%s/version/%s/backend/%s", i.Service, i.Version, i.Name)
	resp, err := c.PutForm(path, i, nil)
	if err != nil {
		return nil, err
	}

	var b *Backend
	if err := decodeJSON(&b, resp.Body); err != nil {
		return nil, err
	}
	return b, nil
}

// DeleteBackendInput is the input parameter to DeleteBackend.
type DeleteBackendInput struct {
	// Service is the ID of the service. Version is the specific configuration
	// version. Both fields are required.
	Service string
	Version string

	// Name is the name of the backend to delete (required).
	Name string
}

// DeleteBackend deletes the given backend version.
func (c *Client) DeleteBackend(i *DeleteBackendInput) error {
	if i.Service == "" {
		return ErrMissingService
	}

	if i.Version == "" {
		return ErrMissingVersion
	}

	if i.Name == "" {
		return ErrMissingName
	}

	path := fmt.Sprintf("/service/%s/version/%s/backend/%s", i.Service, i.Version, i.Name)
	resp, err := c.Delete(path, nil)
	if err != nil {
		return err
	}

	var r *statusResp
	if err := decodeJSON(&r, resp.Body); err != nil {
		return err
	}
	if !r.Ok() {
		return fmt.Errorf("Not Ok")
	}
	return nil
}
<|endoftext|>


<commit_before>package parser

import "testing"

func TestBasicLit(t *testing.T) {
	consumes(t, BasicLit, map[string]bool{
		``:    false,
		`1`:   true,
		`1.1`: true,
		`1i`:  true,
		`'a'`: true,
		`"a"`: true,
		`a`:   false,
	})
}

func TestIdentifierList(t *testing.T) {
	consumes(t, IdentifierList, map[string]bool{
		`a`:   true,
		`a,a`: true,
		`1`:   false,
	})
}

func TestTypeName(t *testing.T) {
	consumes(t, TypeName, map[string]bool{
		`a`:   true,
		`a.a`: true,
		`1`:   false,
	})
}

func TestQualifiedIdent(t *testing.T) {
	consumes(t, QualifiedIdent, map[string]bool{
		`1`:   false,
		`a`:   false,
		`a.a`: true,
		`_.a`: false,
		`a._`: true,
	})
}

func TestPackageName(t *testing.T) {
	consumes(t, PackageName, map[string]bool{
		`a`: true,
		`1`: false,
		`_`: false,
	})
}
<commit_msg>now with more underscore (#14)<commit_after>package parser

import "testing"

func TestBasicLit(t *testing.T) {
	consumes(t, BasicLit, map[string]bool{
		``:    false,
		`1`:   true,
		`1.1`: true,
		`1i`:  true,
		`'a'`: true,
		`"a"`: true,
		`a`:   false,
		`_`:   false,
	})
}

func TestIdentifierList(t *testing.T) {
	consumes(t, IdentifierList, map[string]bool{
		`a`:   true,
		`a,a`: true,
		`1`:   false,
		`_`:   true,
	})
}

func TestTypeName(t *testing.T) {
	consumes(t, TypeName, map[string]bool{
		`a`:   true,
		`a.a`: true,
		`1`:   false,
		`_`:   true,
	})
}

func TestQualifiedIdent(t *testing.T) {
	consumes(t, QualifiedIdent, map[string]bool{
		`1`:   false,
		`a`:   false,
		`a.a`: true,
		`_.a`: false,
		`a._`: true,
		`_._`: false,
	})
}

func TestPackageName(t *testing.T) {
	consumes(t, PackageName, map[string]bool{
		`a`: true,
		`1`: false,
		`_`: false,
	})
}
<|endoftext|>


<commit_before>// Copyright 2016 Tim Shannon. All rights reserved.
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.

package bolthold_test

import (
	"fmt"
	"testing"

	"github.com/timshannon/bolthold"
)

func TestFindAggregateGroup(t *testing.T) {
	testWrap(t, func(store *bolthold.Store, t *testing.T) {
		insertTestData(t, store)

		result, err := store.FindAggregate(&ItemTest{}, nil, "Category")

		if err != nil {
			t.Fatalf("Error finding aggregate data from bolthold: %s", err)
		}

		if len(result) != 3 {
			t.Fatalf("Wrong number of groupings.  Wanted %d got %d", 3, len(result))
		}

		for i := range result {
			var items []ItemTest
			var group string

			result[i].Reduction(&items)
			result[i].Group(&group)

			for j := range items {
				if items[j].Category != group {
					t.Fatalf("Reduction item is not in the proper grouping.  Wanted %s, Got %s",
						group, items[j].Category)
				}
				fmt.Println(items[i])
			}
		}

		//test min
		for i := range result {
			min := &ItemTest{}
			max := &ItemTest{}
			var group string

			result[i].Group(&group)

			result[i].Min("ID", min)
			result[i].Max("ID", max)

			fmt.Printf("Max %s: %d\n", group, max.Key)
			fmt.Printf("Min %s: %d\n", group, min.Key)

			switch group {
			case "animal":
				if !min.equal(&testData[5]) {
					t.Fatalf("Expected min value of %v Got %v", testData[5], min)
				}
			case "food":
				if !min.equal(&testData[7]) {
					t.Fatalf("Expected min value of %v Got %v", testData[7], min)
				}

			case "vehicle":
				if !min.equal(&testData[1]) {
					t.Fatalf("Expected min value of %v Got %v", testData[1], min)
				}
			}
		}
		// test max
		// test avg
		// test count

	})
}
<commit_msg>Not much<commit_after>// Copyright 2016 Tim Shannon. All rights reserved.
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.

package bolthold_test

import (
	"fmt"
	"testing"

	"github.com/timshannon/bolthold"
)

func TestFindAggregateGroup(t *testing.T) {
	testWrap(t, func(store *bolthold.Store, t *testing.T) {
		insertTestData(t, store)

		result, err := store.FindAggregate(&ItemTest{}, nil, "Category")

		if err != nil {
			t.Fatalf("Error finding aggregate data from bolthold: %s", err)
		}

		if len(result) != 3 {
			t.Fatalf("Wrong number of groupings.  Wanted %d got %d", 3, len(result))
		}

		for i := range result {
			var items []ItemTest
			var group string

			result[i].Reduction(&items)
			result[i].Group(&group)

			for j := range items {
				if items[j].Category != group {
					t.Fatalf("Reduction item is not in the proper grouping.  Wanted %s, Got %s",
						group, items[j].Category)
				}
			}
		}

		//test min
		for i := range result {
			min := &ItemTest{}
			max := &ItemTest{}
			var group string

			result[i].Group(&group)

			result[i].Min("ID", min)
			result[i].Max("ID", max)

			fmt.Printf("Max %s: %d\n", group, max.Key)
			fmt.Printf("Min %s: %d\n", group, min.Key)

			switch group {
			case "animal":
				if !min.equal(&testData[5]) {
					t.Fatalf("Expected min value of %v Got %v", testData[5], min)
				}
			case "food":
				if !min.equal(&testData[7]) {
					t.Fatalf("Expected min value of %v Got %v", testData[7], min)
				}

			case "vehicle":
				if !min.equal(&testData[1]) {
					t.Fatalf("Expected min value of %v Got %v", testData[1], min)
				}
			}
		}
		// test max
		// test avg
		// test count

	})
}
<|endoftext|>


<commit_before>package errorx

import (
	"bytes"
	"encoding/json"
	"fmt"
	"path/filepath"
	"runtime"
	"strings"
)

const (
	Info = iota
	Verbose
	Debug
	Trace
)

// verbosity variable stores global verbosity setting for errorx package
// based on it's value different level of error details will be provided
// by Error, Errorf, Json and Jsonf
var verbosity int

// Errorx is a more feature rich implementation of error interface inspired
// by PostgreSQL error style guide
type Errorx struct {
	Code    int      `json:"error_code,omitempty"`
	Message string   `json:"error_message"`
	Details []string `json:"error_details,omitempty"`
	Cause   error    `json:"cause,omitempty"`
	Stack   Stack    `json:"stack,omitempty"`
}

type StackFrame struct {
	File     string `json:"file,omitempty"`
	Line     int    `json:"line,omitempty"`
	Function string `json:"function,omitempty"`
}

type Stack []StackFrame

func (s Stack) String() string {
	var buffer bytes.Buffer
	for i := 0; i < len(s); i++ {
		if s[i].File == "<autogenerated>" {
			break
		}
		buffer.WriteString(fmt.Sprintf("\n%s:%d %s", s[i].File, s[i].Line, s[i].Function))
	}
	return buffer.String()
}

// New returns an error with error code and error messages provided in
// function params
func New(code int, ErrorMsg ...string) *Errorx {
	e := Errorx{Code: code}

	msgCount := len(ErrorMsg)
	if msgCount > 0 {
		e.Message = ErrorMsg[0]
	}
	if msgCount > 1 {
		e.Details = ErrorMsg[1:]
	}

	return &e
}

// Wrap
func (e *Errorx) Wrap(err error) {
	e.Cause = err
}

// SetVerbosity changes global verbosity setting
func SetVerbosity(v int) {
	verbosity = v
}

// ErrorCode returns Errorx error code value. It's intended primarily to allow
// easy error comparison / matching
func (e Errorx) ErrorCode() int {
	return e.Code
}

// Error returns a string representation of errorx. It includes at least
// error code and message. Error details and hint are provided depending
// on verbosity level set
func (e Errorx) Error() string {
	maxMsg := len(e.Details)
	if maxMsg > verbosity {
		maxMsg = verbosity
	}

	switch verbosity {
	case 0:
		return fmt.Sprintf("error %d: %s", e.Code, e.Message)
	case 1:
		if e.Cause == nil {
			return fmt.Sprintf("error %d: %s | %s", e.Code, e.Message, strings.Join(e.Details[0:maxMsg], "; "))
		}
		return fmt.Sprintf("error %d: %s | %s\ncause: %s", e.Code, e.Message, strings.Join(e.Details[0:maxMsg], "; "), e.Cause.Error())
	case 2:
		e.getTrace()

		if e.Cause == nil {
			if e.Stack[0].File != "<autogenerated>" {
				return fmt.Sprintf("%s:%d: error %d: %s | %s", e.Stack[0].File, e.Stack[0].Line, e.Code, e.Message, strings.Join(e.Details[0:maxMsg], "; "))
			}
			return fmt.Sprintf("error %d: %s | %s", e.Code, e.Message, strings.Join(e.Details[0:maxMsg], "; "))
		}
		if e.Stack[0].File != "<autogenerated>" {
			return fmt.Sprintf("%s:%d: error %d: %s | %s\ncause: %s", e.Stack[0].File, e.Stack[0].Line, e.Code, e.Message, strings.Join(e.Details[0:maxMsg], "; "), e.Cause.Error())
		}
		return fmt.Sprintf("error %d: %s | %s\ncause: %s", e.Code, e.Message, strings.Join(e.Details[0:maxMsg], "; "), e.Cause.Error())
	default:
		e.getTrace()

		if e.Cause == nil {
			if e.Stack[0].File != "<autogenerated>" {
				return fmt.Sprintf("%s:%d: error %d: %s | %s%s", e.Stack[0].File, e.Stack[0].Line, e.Code, e.Message, strings.Join(e.Details[0:maxMsg], "; "), e.Stack.String())
			}
			return fmt.Sprintf("error %d: %s | %s%s", e.Code, e.Message, strings.Join(e.Details[0:maxMsg], "; "), e.Stack.String())
		}
		if e.Stack[0].File != "<autogenerated>" {
			return fmt.Sprintf("%s:%d: error %d: %s | %s\ncause: %s%s", e.Stack[0].File, e.Stack[0].Line, e.Code, e.Message, strings.Join(e.Details[0:maxMsg], "; "), e.Cause.Error(), e.Stack.String())
		}
		return fmt.Sprintf("error %d: %s | %s\ncause: %s%s", e.Code, e.Message, strings.Join(e.Details[0:maxMsg], "; "), e.Cause.Error(), e.Stack.String())
	}
}

// Json returns a json representation (as []byte) of errorx and error
// if marshaling fails
func (e Errorx) Json() ([]byte, error) {
	e.getTrace()
	err := e.verbositySubset()

	return json.Marshal(err)
}

func (e Errorx) verbositySubset() Errorx {
	err := Errorx{Code: e.Code, Message: e.Message}
	maxMsg := len(e.Details)
	if maxMsg > verbosity {
		maxMsg = verbosity
	}

	if verbosity > 0 {
		err.Details = e.Details[0:maxMsg]
	}
	if verbosity > 1 {
		if e.Cause != nil {
			if cause, ok := e.Cause.(*Errorx); ok {
				err.Cause = cause.verbositySubset()
			} else {
				err.Cause = Errorx{Message: e.Cause.Error()}
			}
		}
		err.Stack = e.Stack
	}
	return err
}

func (e *Errorx) getTrace() {
	if verbosity < 2 {
		return
	}

	if verbosity == 2 {
		pc, fn, line, ok := runtime.Caller(2)
		if !ok {
			return
		}

		s := StackFrame{Line: line}
		s.Function = funcName(pc)
		_, s.File = filepath.Split(fn)

		e.Stack = []StackFrame{s}
		return
	}

	e.Stack = make([]StackFrame, 0)

	for i := 2; ; i++ {
		pc, fn, line, ok := runtime.Caller(i)
		if !ok {
			// no more frames - we're done
			break
		}
		_, fn = filepath.Split(fn)

		f := StackFrame{File: fn, Line: line, Function: funcName(pc)}
		e.Stack = append(e.Stack, f)
	}
}

// funcName gets the name of the function at pointer or "??" if one can't be found
func funcName(pc uintptr) string {
	if f := runtime.FuncForPC(pc); f != nil {
		return f.Name()
	}
	return "??"
}
<commit_msg>godoc<commit_after>package errorx

import (
	"bytes"
	"encoding/json"
	"fmt"
	"path/filepath"
	"runtime"
	"strings"
)

const (
	Info = iota
	Verbose
	Debug
	Trace
)

// verbosity variable stores global verbosity setting for errorx package
// based on it's value different level of error details will be provided
// by Error, Errorf, Json and Jsonf
var verbosity int

// Errorx is a more feature rich implementation of error interface inspired
// by PostgreSQL error style guide
type Errorx struct {
	Code    int      `json:"error_code,omitempty"`
	Message string   `json:"error_message"`
	Details []string `json:"error_details,omitempty"`
	Cause   error    `json:"cause,omitempty"`
	Stack   Stack    `json:"stack,omitempty"`
}

// StackFrame represents a single frame of a stack trace
type StackFrame struct {
	File     string `json:"file,omitempty"`
	Line     int    `json:"line,omitempty"`
	Function string `json:"function,omitempty"`
}

// Stack is a slice of StackFrames representing a stack trace
type Stack []StackFrame

// String generates a string representation of a stack trace generated by
// getTrace
func (s Stack) String() string {
	var buffer bytes.Buffer
	for i := 0; i < len(s); i++ {
		if s[i].File == "<autogenerated>" {
			break
		}
		buffer.WriteString(fmt.Sprintf("\n%s:%d %s", s[i].File, s[i].Line, s[i].Function))
	}
	return buffer.String()
}

// New returns an error with error code and error messages provided in
// function params
func New(code int, ErrorMsg ...string) *Errorx {
	e := Errorx{Code: code}

	msgCount := len(ErrorMsg)
	if msgCount > 0 {
		e.Message = ErrorMsg[0]
	}
	if msgCount > 1 {
		e.Details = ErrorMsg[1:]
	}

	return &e
}

// Wrap wraps error in Errorx for nested errors
func (e *Errorx) Wrap(err error) {
	e.Cause = err
}

// SetVerbosity changes global verbosity setting
func SetVerbosity(v int) {
	verbosity = v
}

// ErrorCode returns Errorx error code value. It's intended primarily to allow
// easy error comparison / matching
func (e Errorx) ErrorCode() int {
	return e.Code
}

// Error returns a string representation of errorx. It includes at least
// error code and message. Error details and hint are provided depending
// on verbosity level set
func (e Errorx) Error() string {
	maxMsg := len(e.Details)
	if maxMsg > verbosity {
		maxMsg = verbosity
	}

	switch verbosity {
	case 0:
		return fmt.Sprintf("error %d: %s", e.Code, e.Message)
	case 1:
		if e.Cause == nil {
			return fmt.Sprintf("error %d: %s | %s", e.Code, e.Message, strings.Join(e.Details[0:maxMsg], "; "))
		}
		return fmt.Sprintf("error %d: %s | %s\ncause: %s", e.Code, e.Message, strings.Join(e.Details[0:maxMsg], "; "), e.Cause.Error())
	case 2:
		e.getTrace()

		if e.Cause == nil {
			if e.Stack[0].File != "<autogenerated>" {
				return fmt.Sprintf("%s:%d: error %d: %s | %s", e.Stack[0].File, e.Stack[0].Line, e.Code, e.Message, strings.Join(e.Details[0:maxMsg], "; "))
			}
			return fmt.Sprintf("error %d: %s | %s", e.Code, e.Message, strings.Join(e.Details[0:maxMsg], "; "))
		}
		if e.Stack[0].File != "<autogenerated>" {
			return fmt.Sprintf("%s:%d: error %d: %s | %s\ncause: %s", e.Stack[0].File, e.Stack[0].Line, e.Code, e.Message, strings.Join(e.Details[0:maxMsg], "; "), e.Cause.Error())
		}
		return fmt.Sprintf("error %d: %s | %s\ncause: %s", e.Code, e.Message, strings.Join(e.Details[0:maxMsg], "; "), e.Cause.Error())
	default:
		e.getTrace()

		if e.Cause == nil {
			if e.Stack[0].File != "<autogenerated>" {
				return fmt.Sprintf("%s:%d: error %d: %s | %s%s", e.Stack[0].File, e.Stack[0].Line, e.Code, e.Message, strings.Join(e.Details[0:maxMsg], "; "), e.Stack.String())
			}
			return fmt.Sprintf("error %d: %s | %s%s", e.Code, e.Message, strings.Join(e.Details[0:maxMsg], "; "), e.Stack.String())
		}
		if e.Stack[0].File != "<autogenerated>" {
			return fmt.Sprintf("%s:%d: error %d: %s | %s\ncause: %s%s", e.Stack[0].File, e.Stack[0].Line, e.Code, e.Message, strings.Join(e.Details[0:maxMsg], "; "), e.Cause.Error(), e.Stack.String())
		}
		return fmt.Sprintf("error %d: %s | %s\ncause: %s%s", e.Code, e.Message, strings.Join(e.Details[0:maxMsg], "; "), e.Cause.Error(), e.Stack.String())
	}
}

// Json returns a json representation (as []byte) of errorx and error
// if marshaling fails
func (e Errorx) Json() ([]byte, error) {
	e.getTrace()
	err := e.verbositySubset()

	return json.Marshal(err)
}

// verbositySubset returns a subset of Errorx fields, depending on verbosity,
// allowing only a part of the error to be marshaled to JSON
func (e Errorx) verbositySubset() Errorx {
	err := Errorx{Code: e.Code, Message: e.Message}
	maxMsg := len(e.Details)
	if maxMsg > verbosity {
		maxMsg = verbosity
	}

	if verbosity > 0 {
		err.Details = e.Details[0:maxMsg]
	}
	if verbosity > 1 {
		if e.Cause != nil {
			if cause, ok := e.Cause.(*Errorx); ok {
				err.Cause = cause.verbositySubset()
			} else {
				err.Cause = Errorx{Message: e.Cause.Error()}
			}
		}
		err.Stack = e.Stack
	}
	return err
}

// getTrace generates trace for Errorx
func (e *Errorx) getTrace() {
	if verbosity < 2 {
		return
	}

	if verbosity == 2 {
		pc, fn, line, ok := runtime.Caller(2)
		if !ok {
			return
		}

		s := StackFrame{Line: line}
		s.Function = funcName(pc)
		_, s.File = filepath.Split(fn)

		e.Stack = []StackFrame{s}
		return
	}

	e.Stack = make([]StackFrame, 0)

	for i := 2; ; i++ {
		pc, fn, line, ok := runtime.Caller(i)
		if !ok {
			// no more frames - we're done
			break
		}
		_, fn = filepath.Split(fn)

		f := StackFrame{File: fn, Line: line, Function: funcName(pc)}
		e.Stack = append(e.Stack, f)
	}
}

// funcName gets the name of the function at pointer or "??" if one can't be found
func funcName(pc uintptr) string {
	if f := runtime.FuncForPC(pc); f != nil {
		return f.Name()
	}
	return "??"
}
<|endoftext|>


<commit_before>package fsstatus

import (
	"math"
	"os/exec"
	"testing"
)

var fileParameters = []string{
	"/proc/net/tcp",
	"/bin/bash",
	"/proc/filesystems",
	"/proc/uptime",
	"/proc/cpuinfo",
}

var dirParameters = []string{
	"/dev", "/var", "/tmp", "/opt", "/usr", "/usr/bin",
}

var symlinkParameters = []string{"/bin/sh"}

var inodeFilesystem = "tmpfs" // TODO: not available on all distros

type fileTest func(path string) (bool, error)

// abstraction of TestIsFile, TestIsDir, TestIsSymlink
func testIsType(f fileTest, goodEggs, badEggs []string, t *testing.T) {
	for _, egg := range goodEggs {
		if is, _ := f(egg); !is {
			t.Errorf("Test reported incorrectly for %s", egg)
		}
	}
	for _, egg := range badEggs {
		if is, _ := f(egg); is {
			t.Errorf("Test reported incorrectly for %s", egg)
		}
	}
}

func TestIsFile(t *testing.T) {
	t.Parallel()
	badEggs := append(dirParameters, symlinkParameters...)
	testIsType(IsFile, fileParameters, badEggs, t)
}

func TestIsDirectory(t *testing.T) {
	t.Parallel()
	badEggs := append(fileParameters, symlinkParameters...)
	testIsType(IsDirectory, dirParameters, badEggs, t)
}

func TestIsSymlink(t *testing.T) {
	t.Parallel()
	badEggs := append(fileParameters, dirParameters...)
	testIsType(IsSymlink, symlinkParameters, badEggs, t)
}

func TestChecksum(t *testing.T) {
	t.Parallel()
	lst := [][]string{
		{"", "md5", "d41d8cd98f00b204e9800998ecf8427e"},
		{"", "sha1", "da39a3ee5e6b4b0d3255bfef95601890afd80709"},
		{"", "sha224", "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f"},
		{"", "sha256", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"},
		{"", "sha384", "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b"},
		{"", "sha512", "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"},
	}
	for _, trio := range lst {
		actual, _ := Checksum(trio[1], []byte(trio[0]))
		if actual != trio[2] {
			t.Error("Data did not have expected checksum: %s", trio[0])
		}
	}
}

// Type of function that counts inodes
type inodeFun func(string) (uint64, error)

func testInodeCountingFunction(t *testing.T, f inodeFun, fName string) {
	// test bad names, make sure they throw errors
	for _, name := range []string{"askdjlfba", "", "12034", "testfail"} {
		_, err := f(name)
		if err == nil {
			msg := "Got nil error with filesystem name %v and function %v"
			t.Errorf(msg, name, fName)
		}
	}

}

func TestInodeCountingFunctions(t *testing.T) {
	t.Parallel()
	cmd := exec.Command("df", "-i")
	out, _ := cmd.CombinedOutput()
	testInodeCountingFunction(t, FreeInodes, "FreeInodes")
	testInodeCountingFunction(t, UsedInodes, "UsedInodes")
	testInodeCountingFunction(t, TotalInodes, "TotalInodes")
	// TODO: this filesystem is not available on all systems
	free, freeErr := FreeInodes(inodeFilesystem)
	used, usedErr := UsedInodes(inodeFilesystem)
	total, totalErr := TotalInodes(inodeFilesystem)
	for _, err := range []error{freeErr, usedErr, totalErr} {
		if err != nil {
			t.Logf("Output of `df -i`: %v", string(out))
			t.Error(err)
		}
	}
	if free+used != total {
		t.Logf("Output of `df -i`: %v", string(out))
		msg := "(free inodes) + (used inodes) != (total inodes), %v + %v != %v"
		t.Errorf(msg, free, used, total)
	}
}

func TestInodePercentFunction(t *testing.T) {
	t.Parallel()
	// assume no errors because we just tested these
	used, _ := UsedInodes(inodeFilesystem)
	total, _ := TotalInodes(inodeFilesystem)
	givenPercent, err := PercentInodesUsed(inodeFilesystem)
	if err != nil {
		t.Error(err)
	}
	// GNU Coreutils rounds the percent up. see lines 1092-1095 here:
	// http://git.savannah.gnu.org/gitweb/?p=coreutils.git;a=blob;f=src/df.c;h=c1c1e683178f843febeb167224fe8ad2a1122a4f;hb=5148302771f1e36f3ea3e7ed33e55bd7a7a1cc3b
	calculatedPercent := uint8(math.Ceil((float64(used) / float64(total)) * 100))
	if math.Abs(float64(calculatedPercent-givenPercent)) >= 1 {
		t.Logf("Used: %v, total: %v", used, total)
		t.Logf("used/total = %v", float32(used)/float32(total))
		msg := "Calculated percent (%v) ≉ Given percent: (%v)"
		t.Errorf(msg, calculatedPercent, givenPercent)
	}
}
<commit_msg>don't assume presence of tmpfs<commit_after>package fsstatus

import (
	"math"
	"os/exec"
	"testing"
)

var fileParameters = []string{
	"/proc/net/tcp",
	"/bin/bash",
	"/proc/filesystems",
	"/proc/uptime",
	"/proc/cpuinfo",
}

var dirParameters = []string{
	"/dev", "/var", "/tmp", "/opt", "/usr", "/usr/bin",
}

var symlinkParameters = []string{"/bin/sh"}

var inodeFilesystem = "tmpfs" // TODO: not available on all distros

type fileTest func(path string) (bool, error)

// abstraction of TestIsFile, TestIsDir, TestIsSymlink
func testIsType(f fileTest, goodEggs, badEggs []string, t *testing.T) {
	for _, egg := range goodEggs {
		if is, _ := f(egg); !is {
			t.Errorf("Test reported incorrectly for %s", egg)
		}
	}
	for _, egg := range badEggs {
		if is, _ := f(egg); is {
			t.Errorf("Test reported incorrectly for %s", egg)
		}
	}
}

func TestIsFile(t *testing.T) {
	t.Parallel()
	badEggs := append(dirParameters, symlinkParameters...)
	testIsType(IsFile, fileParameters, badEggs, t)
}

func TestIsDirectory(t *testing.T) {
	t.Parallel()
	badEggs := append(fileParameters, symlinkParameters...)
	testIsType(IsDirectory, dirParameters, badEggs, t)
}

func TestIsSymlink(t *testing.T) {
	t.Parallel()
	badEggs := append(fileParameters, dirParameters...)
	testIsType(IsSymlink, symlinkParameters, badEggs, t)
}

func TestChecksum(t *testing.T) {
	t.Parallel()
	lst := [][]string{
		{"", "md5", "d41d8cd98f00b204e9800998ecf8427e"},
		{"", "sha1", "da39a3ee5e6b4b0d3255bfef95601890afd80709"},
		{"", "sha224", "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f"},
		{"", "sha256", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"},
		{"", "sha384", "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b"},
		{"", "sha512", "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"},
	}
	for _, trio := range lst {
		actual, _ := Checksum(trio[1], []byte(trio[0]))
		if actual != trio[2] {
			t.Error("Data did not have expected checksum: %s", trio[0])
		}
	}
}

// Type of function that counts inodes
type inodeFun func(string) (uint64, error)

func testInodeCountingFunction(t *testing.T, f inodeFun, fName string) {
	// test bad names, make sure they throw errors
	for _, name := range []string{"askdjlfba", "", "12034", "testfail"} {
		_, err := f(name)
		if err == nil {
			msg := "Got nil error with filesystem name %v and function %v"
			t.Errorf(msg, name, fName)
		}
	}

}

func TestInodeCountingFunctions(t *testing.T) {
	t.Parallel()
	cmd := exec.Command("df", "-i")
	out, _ := cmd.CombinedOutput()
	testInodeCountingFunction(t, FreeInodes, "FreeInodes")
	testInodeCountingFunction(t, UsedInodes, "UsedInodes")
	testInodeCountingFunction(t, TotalInodes, "TotalInodes")
	// TODO: this filesystem is not available on all systems
	free, freeErr := FreeInodes(inodeFilesystem)
	used, usedErr := UsedInodes(inodeFilesystem)
	total, totalErr := TotalInodes(inodeFilesystem)
	for _, err := range []error{freeErr, usedErr, totalErr} {
		if err != nil {
			//t.Logf("Output of `df -i`: %v", string(out))
			t.Log(err)
		}
	}
	if free+used != total {
		t.Logf("Output of `df -i`: %v", string(out))
		msg := "(free inodes) + (used inodes) != (total inodes), %v + %v != %v"
		t.Errorf(msg, free, used, total)
	}
}

func TestInodePercentFunction(t *testing.T) {
	t.Parallel()
	// assume no errors because we just tested these
	used, _ := UsedInodes(inodeFilesystem)
	total, _ := TotalInodes(inodeFilesystem)
	givenPercent, err := PercentInodesUsed(inodeFilesystem)
	if err != nil {
		t.Log(err)
	}
	// GNU Coreutils rounds the percent up. see lines 1092-1095 here:
	// http://git.savannah.gnu.org/gitweb/?p=coreutils.git;a=blob;f=src/df.c;h=c1c1e683178f843febeb167224fe8ad2a1122a4f;hb=5148302771f1e36f3ea3e7ed33e55bd7a7a1cc3b
	calculatedPercent := uint8(math.Ceil((float64(used) / float64(total)) * 100))
	if math.Abs(float64(calculatedPercent-givenPercent)) >= 1 {
		t.Logf("Used: %v, total: %v", used, total)
		t.Logf("used/total = %v", float32(used)/float32(total))
		msg := "Calculated percent (%v) ≉ Given percent: (%v)"
		t.Errorf(msg, calculatedPercent, givenPercent)
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2018 Cisco and/or its affiliates.
//
// 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.

// //go:generate protoc -I ./model/pod --go_out=plugins=grpc:./model/pod ./model/pod/pod.proto

package netmeshplugincrd

import (
	"fmt"
	"reflect"
	"sync"
	"time"

	apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
	apiextcs "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	meta "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/tools/clientcmd"

	"github.com/ligato/cn-infra/config"
	"github.com/ligato/cn-infra/flavors/local"
	"github.com/ligato/cn-infra/health/statuscheck"
	"github.com/ligato/cn-infra/logging"
	"github.com/ligato/networkservicemesh/pkg/apis/networkservicemesh.io/v1"
	client "github.com/ligato/networkservicemesh/pkg/client/clientset/versioned"
	factory "github.com/ligato/networkservicemesh/pkg/client/informers/externalversions"
)

// Plugin watches K8s resources and causes all changes to be reflected in the ETCD
// data store.
type Plugin struct {
	Deps

	pluginStopCh    chan struct{}
	wg              sync.WaitGroup
	k8sClientConfig *rest.Config
	k8sClientset    *kubernetes.Clientset
	apiclientset    *apiextcs.Clientset
	crdClient       client.Interface
	StatusMonitor   statuscheck.StatusReader

	// These can be used to stop all the informers, as well as control loops
	// within the application.
	stopChNS  chan struct{}
	stopChNSE chan struct{}
	stopChNSC chan struct{}
	// sharedFactory's are shared informer factorys used as a cache for
	// items in the API server. They saves each informer listing and watch the
	// same resources independently of each other, thus providing more up to
	// date results with less 'effort'
	sharedFactoryNS  factory.SharedInformerFactory
	sharedFactoryNSE factory.SharedInformerFactory
	sharedFactoryNSC factory.SharedInformerFactory

	// Informer factories per CRD object
	informerNS  cache.SharedIndexInformer
	informerNSE cache.SharedIndexInformer
	informerNSC cache.SharedIndexInformer
}

// Deps defines dependencies of netmesh plugin.
type Deps struct {
	local.PluginInfraDeps
	// Kubeconfig with k8s cluster address and access credentials to use.
	KubeConfig config.PluginConfig
}

// Init builds K8s client-set based on the supplied kubeconfig and initializes
// all reflectors.
func (plugin *Plugin) Init() error {
	var err error
	plugin.Log.SetLevel(logging.DebugLevel)
	plugin.pluginStopCh = make(chan struct{})

	kubeconfig := plugin.KubeConfig.GetConfigName()
	plugin.Log.WithField("kubeconfig", kubeconfig).Info("Loading kubernetes client config")
	plugin.k8sClientConfig, err = clientcmd.BuildConfigFromFlags("", kubeconfig)
	if err != nil {
		return fmt.Errorf("Failed to build kubernetes client config: %s", err)
	}

	plugin.k8sClientset, err = kubernetes.NewForConfig(plugin.k8sClientConfig)
	if err != nil {
		return fmt.Errorf("Failed to build kubernetes client: %s", err)
	}

	plugin.stopChNS = make(chan struct{})
	plugin.stopChNSC = make(chan struct{})
	plugin.stopChNSE = make(chan struct{})

	return nil
}

// networkServiceValidation generates OpenAPIV3 validator for NetworkService CRD
func networkServiceValidation() *apiextv1beta1.CustomResourceValidation {
	maxLength := int64(64)
	validation := &apiextv1beta1.CustomResourceValidation{
		OpenAPIV3Schema: &apiextv1beta1.JSONSchemaProps{
			Properties: map[string]apiextv1beta1.JSONSchemaProps{
				"spec": apiextv1beta1.JSONSchemaProps{
					Required: []string{"name"},
					Properties: map[string]apiextv1beta1.JSONSchemaProps{
						"name": apiextv1beta1.JSONSchemaProps{
							Type:        "string",
							MaxLength:   &maxLength,
							Description: "NetworkService Name",
							Pattern:     `^[a-zA-Z0-9]+[\-a-zA-Z0-9]*$`,
						},
						"uuid": apiextv1beta1.JSONSchemaProps{
							Type:        "string",
							MaxLength:   &maxLength,
							Description: "NetworkService UUID",
							Pattern:     `[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}`,
						},
					},
				},
			},
		},
	}
	return validation
}

// networkServiceEndpointsValidation generates OpenAPIV3 validator for NetworkServiceEndpoints CRD
func networkServiceEndpointsValidation() *apiextv1beta1.CustomResourceValidation {
	maxLength := int64(64)
	validation := &apiextv1beta1.CustomResourceValidation{
		OpenAPIV3Schema: &apiextv1beta1.JSONSchemaProps{
			Properties: map[string]apiextv1beta1.JSONSchemaProps{
				"spec": apiextv1beta1.JSONSchemaProps{
					Required: []string{"name"},
					Properties: map[string]apiextv1beta1.JSONSchemaProps{
						"name": apiextv1beta1.JSONSchemaProps{
							Type:        "string",
							MaxLength:   &maxLength,
							Description: "NetworkServiceEndpoints Name",
							Pattern:     `^[a-zA-Z0-9]+[\-a-zA-Z0-9]*$`,
						},
						"uuid": apiextv1beta1.JSONSchemaProps{
							Type:        "string",
							MaxLength:   &maxLength,
							Description: "NetworkServiceEndpoints UUID",
							Pattern:     `[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}`,
						},
					},
				},
			},
		},
	}
	return validation
}

// networkServiceChannels generates OpenAPIV3 validator for NetworkServiceChannels CRD
func networkServiceChannelsValidation() *apiextv1beta1.CustomResourceValidation {
	maxLength := int64(64)
	validation := &apiextv1beta1.CustomResourceValidation{
		OpenAPIV3Schema: &apiextv1beta1.JSONSchemaProps{
			Properties: map[string]apiextv1beta1.JSONSchemaProps{
				"spec": apiextv1beta1.JSONSchemaProps{
					Required: []string{"name"},
					Properties: map[string]apiextv1beta1.JSONSchemaProps{
						"name": apiextv1beta1.JSONSchemaProps{
							Type:        "string",
							MaxLength:   &maxLength,
							Description: "NetworkServiceChannels Name",
							Pattern:     `^[a-zA-Z0-9]+[\-a-zA-Z0-9]*$`,
						},
						"payload": apiextv1beta1.JSONSchemaProps{
							Type:        "string",
							MaxLength:   &maxLength,
							Description: "NetworkServiceChannels Payload",
							Pattern:     `^[a-zA-Z0-9]+[\-a-zA-Z0-9]*$`,
						},
					},
				},
			},
		},
	}
	return validation
}

// Create the CRD resource, ignore error if it already exists
func createCRD(plugin *Plugin, FullName, Group, Version, Plural, Name string) error {

	var validation *apiextv1beta1.CustomResourceValidation
	switch Name {
	case "NetworkService":
		validation = networkServiceValidation()
	case "NetworkServiceEndpoints":
		validation = networkServiceEndpointsValidation()
	case "NetworkServiceChannels":
		validation = networkServiceChannelsValidation()
	default:
		validation = &apiextv1beta1.CustomResourceValidation{}
	}
	crd := &apiextv1beta1.CustomResourceDefinition{
		ObjectMeta: meta.ObjectMeta{Name: FullName},
		Spec: apiextv1beta1.CustomResourceDefinitionSpec{
			Group:   Group,
			Version: Version,
			Scope:   apiextv1beta1.NamespaceScoped,
			Names: apiextv1beta1.CustomResourceDefinitionNames{
				Plural: Plural,
				Kind:   Name,
			},
			Validation: validation,
		},
	}
	_, cserr := plugin.apiclientset.ApiextensionsV1beta1().CustomResourceDefinitions().Create(crd)
	if apierrors.IsAlreadyExists(cserr) {
		return nil
	}

	return cserr
}

func informerNetworkServices(plugin *Plugin) {
	// We use a shared informer from the informer factory, to save calls to the
	// API as we grow our application and so state is consistent between our
	// control loops. We set a resync period of 30 seconds, in case any
	// create/replace/update/delete operations are missed when watching
	plugin.sharedFactoryNS = factory.NewSharedInformerFactory(plugin.crdClient, time.Second*30)

	plugin.informerNS = plugin.sharedFactoryNS.Networkservice().V1().NetworkServices().Informer()
	// We add a new event handler, watching for changes to API resources.
	plugin.informerNS.AddEventHandler(
		cache.ResourceEventHandlerFuncs{
			AddFunc: networkserviceEnqueue,
			UpdateFunc: func(old, cur interface{}) {
				if !reflect.DeepEqual(old, cur) {
					networkserviceEnqueue(cur)
				}
			},
			DeleteFunc: networkserviceEnqueue,
		},
	)

	// Start the informer. This will cause it to begin receiving updates from
	// the configured API server and firing event handlers in response.
	plugin.sharedFactoryNS.Start(plugin.stopChNS)
	plugin.Log.Info("Started NetworkService informer factory.")

	// Wait for the informer cache to finish performing it's initial sync of
	// resources
	if !cache.WaitForCacheSync(plugin.stopChNS, plugin.informerNS.HasSynced) {
		plugin.Log.Error("Error waiting for informer cache to sync")
	}

	plugin.Log.Info("NetworkService Informer is ready")

	// Read forever from the work queue
	workforever(plugin, queueNS, plugin.informerNS, plugin.stopChNS)
}

func informerNetworkServiceChannels(plugin *Plugin) {
	// We use a shared informer from the informer factory, to save calls to the
	// API as we grow our application and so state is consistent between our
	// control loops. We set a resync period of 30 seconds, in case any
	// create/replace/update/delete operations are missed when watching
	plugin.sharedFactoryNSC = factory.NewSharedInformerFactory(plugin.crdClient, time.Second*30)

	plugin.informerNSC = plugin.sharedFactoryNSC.Networkservice().V1().NetworkServiceChannels().Informer()
	// we add a new event handler, watching for changes to API resources.
	plugin.informerNSC.AddEventHandler(
		cache.ResourceEventHandlerFuncs{
			AddFunc: networkservicechannelEnqueue,
			UpdateFunc: func(old, cur interface{}) {
				if !reflect.DeepEqual(old, cur) {
					networkservicechannelEnqueue(cur)
				}
			},
			DeleteFunc: networkservicechannelEnqueue,
		},
	)

	// Start the informer. This will cause it to begin receiving updates from
	// the configured API server and firing event handlers in response.
	plugin.sharedFactoryNSC.Start(plugin.stopChNSC)
	plugin.Log.Info("Started NetworkServiceChannel informer factory.")

	// Wait for the informer cache to finish performing it's initial sync of
	// resources
	if !cache.WaitForCacheSync(plugin.stopChNSC, plugin.informerNSC.HasSynced) {
		plugin.Log.Errorf("Error waiting for informer cache to sync")
	}

	plugin.Log.Info("NetworkServiceChannel Informer is ready")

	// Read forever from the work queue
	workforever(plugin, queueNSC, plugin.informerNSC, plugin.stopChNSC)
}

func informerNetworkServiceEndpoints(plugin *Plugin) {
	// We use a shared informer from the informer factory, to save calls to the
	// API as we grow our application and so state is consistent between our
	// control loops. We set a resync period of 30 seconds, in case any
	// create/replace/update/delete operations are missed when watching
	plugin.sharedFactoryNSE = factory.NewSharedInformerFactory(plugin.crdClient, time.Second*30)

	plugin.informerNSE = plugin.sharedFactoryNSE.Networkservice().V1().NetworkServiceEndpoints().Informer()
	// we add a new event handler, watching for changes to API resources.
	plugin.informerNSE.AddEventHandler(
		cache.ResourceEventHandlerFuncs{
			AddFunc: networkserviceendpointEnqueue,
			UpdateFunc: func(old, cur interface{}) {
				if !reflect.DeepEqual(old, cur) {
					networkserviceendpointEnqueue(cur)
				}
			},
			DeleteFunc: networkserviceendpointEnqueue,
		},
	)

	// Start the informer. This will cause it to begin receiving updates from
	// the configured API server and firing event handlers in response.
	plugin.sharedFactoryNSE.Start(plugin.stopChNSE)
	plugin.Log.Info("Started NetworkServiceEndpoints informer factory.")

	// Wait for the informer cache to finish performing it's initial sync of
	// resources
	if !cache.WaitForCacheSync(plugin.stopChNSE, plugin.informerNSE.HasSynced) {
		plugin.Log.Errorf("Error waiting for informer cache to sync")
	}

	plugin.Log.Info("NetworkServiceEndpoint Informer is ready")

	// Read forever from the work queue
	workforever(plugin, queueNSE, plugin.informerNSE, plugin.stopChNSE)
}

// AfterInit This will create all of the CRDs for NetworkServiceMesh.
func (plugin *Plugin) AfterInit() error {
	var err error
	var crdname string

	// Create clientset and create our CRD, this only needs to run once
	plugin.apiclientset, err = apiextcs.NewForConfig(plugin.k8sClientConfig)
	if err != nil {
		panic(err.Error())
	}

	// Create an instance of our own API client
	plugin.crdClient, err = client.NewForConfig(plugin.k8sClientConfig)
	if err != nil {
		plugin.Log.Errorf("Error creating CRD client: %s", err.Error())
		panic(err.Error())
	}

	crdname = reflect.TypeOf(v1.NetworkServiceEndpoint{}).Name()
	err = createCRD(plugin, v1.FullNSMEPName,
		v1.NSMGroup,
		v1.NSMGroupVersion,
		v1.NSMEPPlural,
		crdname)

	if err != nil {
		plugin.Log.Error("Error initializing NetworkServiceEndpoint CRD")
		return err
	}

	crdname = reflect.TypeOf(v1.NetworkServiceChannel{}).Name()
	err = createCRD(plugin, v1.FullNSMChannelName,
		v1.NSMGroup,
		v1.NSMGroupVersion,
		v1.NSMChannelPlural,
		crdname)

	if err != nil {
		plugin.Log.Error("Error initializing NetworkServiceChannel CRD")
		return err
	}

	crdname = reflect.TypeOf(v1.NetworkService{}).Name()
	err = createCRD(plugin, v1.FullNSMName,
		v1.NSMGroup,
		v1.NSMGroupVersion,
		v1.NSMPlural,
		crdname)

	if err != nil {
		plugin.Log.Error("Error initializing NetworkService CRD")
		return err
	}

	go informerNetworkServices(plugin)
	go informerNetworkServiceChannels(plugin)
	go informerNetworkServiceEndpoints(plugin)

	return nil
}

// Close stops all reflectors.
func (plugin *Plugin) Close() error {
	close(plugin.pluginStopCh)
	plugin.wg.Wait()
	return nil
}
<commit_msg>Use a single shared informer for all CRDs (#103)<commit_after>// Copyright (c) 2018 Cisco and/or its affiliates.
//
// 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.

// //go:generate protoc -I ./model/pod --go_out=plugins=grpc:./model/pod ./model/pod/pod.proto

package netmeshplugincrd

import (
	"fmt"
	"reflect"
	"sync"
	"time"

	apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
	apiextcs "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	meta "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/tools/clientcmd"

	"github.com/ligato/cn-infra/config"
	"github.com/ligato/cn-infra/flavors/local"
	"github.com/ligato/cn-infra/health/statuscheck"
	"github.com/ligato/cn-infra/logging"
	"github.com/ligato/networkservicemesh/pkg/apis/networkservicemesh.io/v1"
	client "github.com/ligato/networkservicemesh/pkg/client/clientset/versioned"
	factory "github.com/ligato/networkservicemesh/pkg/client/informers/externalversions"
)

// Plugin watches K8s resources and causes all changes to be reflected in the ETCD
// data store.
type Plugin struct {
	Deps

	pluginStopCh    chan struct{}
	wg              sync.WaitGroup
	k8sClientConfig *rest.Config
	k8sClientset    *kubernetes.Clientset
	apiclientset    *apiextcs.Clientset
	crdClient       client.Interface
	StatusMonitor   statuscheck.StatusReader

	// These can be used to stop all the informers, as well as control loops
	// within the application.
	stopChNS  chan struct{}
	stopChNSE chan struct{}
	stopChNSC chan struct{}
	// sharedFactory is a shared informer factory used as a cache for
	// items in the API server. It saves each informer listing and watches the
	// same resources independently of each other, thus providing more up to
	// date results with less 'effort'
	sharedFactory factory.SharedInformerFactory

	// Informer factories per CRD object
	informerNS  cache.SharedIndexInformer
	informerNSE cache.SharedIndexInformer
	informerNSC cache.SharedIndexInformer
}

// Deps defines dependencies of netmesh plugin.
type Deps struct {
	local.PluginInfraDeps
	// Kubeconfig with k8s cluster address and access credentials to use.
	KubeConfig config.PluginConfig
}

// Init builds K8s client-set based on the supplied kubeconfig and initializes
// all reflectors.
func (plugin *Plugin) Init() error {
	var err error
	plugin.Log.SetLevel(logging.DebugLevel)
	plugin.pluginStopCh = make(chan struct{})

	kubeconfig := plugin.KubeConfig.GetConfigName()
	plugin.Log.WithField("kubeconfig", kubeconfig).Info("Loading kubernetes client config")
	plugin.k8sClientConfig, err = clientcmd.BuildConfigFromFlags("", kubeconfig)
	if err != nil {
		return fmt.Errorf("Failed to build kubernetes client config: %s", err)
	}

	plugin.k8sClientset, err = kubernetes.NewForConfig(plugin.k8sClientConfig)
	if err != nil {
		return fmt.Errorf("Failed to build kubernetes client: %s", err)
	}

	plugin.stopChNS = make(chan struct{})
	plugin.stopChNSC = make(chan struct{})
	plugin.stopChNSE = make(chan struct{})

	return nil
}

// networkServiceValidation generates OpenAPIV3 validator for NetworkService CRD
func networkServiceValidation() *apiextv1beta1.CustomResourceValidation {
	maxLength := int64(64)
	validation := &apiextv1beta1.CustomResourceValidation{
		OpenAPIV3Schema: &apiextv1beta1.JSONSchemaProps{
			Properties: map[string]apiextv1beta1.JSONSchemaProps{
				"spec": apiextv1beta1.JSONSchemaProps{
					Required: []string{"name"},
					Properties: map[string]apiextv1beta1.JSONSchemaProps{
						"name": apiextv1beta1.JSONSchemaProps{
							Type:        "string",
							MaxLength:   &maxLength,
							Description: "NetworkService Name",
							Pattern:     `^[a-zA-Z0-9]+[\-a-zA-Z0-9]*$`,
						},
						"uuid": apiextv1beta1.JSONSchemaProps{
							Type:        "string",
							MaxLength:   &maxLength,
							Description: "NetworkService UUID",
							Pattern:     `[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}`,
						},
					},
				},
			},
		},
	}
	return validation
}

// networkServiceEndpointsValidation generates OpenAPIV3 validator for NetworkServiceEndpoints CRD
func networkServiceEndpointsValidation() *apiextv1beta1.CustomResourceValidation {
	maxLength := int64(64)
	validation := &apiextv1beta1.CustomResourceValidation{
		OpenAPIV3Schema: &apiextv1beta1.JSONSchemaProps{
			Properties: map[string]apiextv1beta1.JSONSchemaProps{
				"spec": apiextv1beta1.JSONSchemaProps{
					Required: []string{"name"},
					Properties: map[string]apiextv1beta1.JSONSchemaProps{
						"name": apiextv1beta1.JSONSchemaProps{
							Type:        "string",
							MaxLength:   &maxLength,
							Description: "NetworkServiceEndpoints Name",
							Pattern:     `^[a-zA-Z0-9]+[\-a-zA-Z0-9]*$`,
						},
						"uuid": apiextv1beta1.JSONSchemaProps{
							Type:        "string",
							MaxLength:   &maxLength,
							Description: "NetworkServiceEndpoints UUID",
							Pattern:     `[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}`,
						},
					},
				},
			},
		},
	}
	return validation
}

// networkServiceChannels generates OpenAPIV3 validator for NetworkServiceChannels CRD
func networkServiceChannelsValidation() *apiextv1beta1.CustomResourceValidation {
	maxLength := int64(64)
	validation := &apiextv1beta1.CustomResourceValidation{
		OpenAPIV3Schema: &apiextv1beta1.JSONSchemaProps{
			Properties: map[string]apiextv1beta1.JSONSchemaProps{
				"spec": apiextv1beta1.JSONSchemaProps{
					Required: []string{"name"},
					Properties: map[string]apiextv1beta1.JSONSchemaProps{
						"name": apiextv1beta1.JSONSchemaProps{
							Type:        "string",
							MaxLength:   &maxLength,
							Description: "NetworkServiceChannels Name",
							Pattern:     `^[a-zA-Z0-9]+[\-a-zA-Z0-9]*$`,
						},
						"payload": apiextv1beta1.JSONSchemaProps{
							Type:        "string",
							MaxLength:   &maxLength,
							Description: "NetworkServiceChannels Payload",
							Pattern:     `^[a-zA-Z0-9]+[\-a-zA-Z0-9]*$`,
						},
					},
				},
			},
		},
	}
	return validation
}

// Create the CRD resource, ignore error if it already exists
func createCRD(plugin *Plugin, FullName, Group, Version, Plural, Name string) error {

	var validation *apiextv1beta1.CustomResourceValidation
	switch Name {
	case "NetworkService":
		validation = networkServiceValidation()
	case "NetworkServiceEndpoints":
		validation = networkServiceEndpointsValidation()
	case "NetworkServiceChannels":
		validation = networkServiceChannelsValidation()
	default:
		validation = &apiextv1beta1.CustomResourceValidation{}
	}
	crd := &apiextv1beta1.CustomResourceDefinition{
		ObjectMeta: meta.ObjectMeta{Name: FullName},
		Spec: apiextv1beta1.CustomResourceDefinitionSpec{
			Group:   Group,
			Version: Version,
			Scope:   apiextv1beta1.NamespaceScoped,
			Names: apiextv1beta1.CustomResourceDefinitionNames{
				Plural: Plural,
				Kind:   Name,
			},
			Validation: validation,
		},
	}
	_, cserr := plugin.apiclientset.ApiextensionsV1beta1().CustomResourceDefinitions().Create(crd)
	if apierrors.IsAlreadyExists(cserr) {
		return nil
	}

	return cserr
}

func informerNetworkServices(plugin *Plugin) {
	plugin.informerNS = plugin.sharedFactory.Networkservice().V1().NetworkServices().Informer()
	// We add a new event handler, watching for changes to API resources.
	plugin.informerNS.AddEventHandler(
		cache.ResourceEventHandlerFuncs{
			AddFunc: networkserviceEnqueue,
			UpdateFunc: func(old, cur interface{}) {
				if !reflect.DeepEqual(old, cur) {
					networkserviceEnqueue(cur)
				}
			},
			DeleteFunc: networkserviceEnqueue,
		},
	)
}

func informerNetworkServiceChannels(plugin *Plugin) {
	plugin.informerNSC = plugin.sharedFactory.Networkservice().V1().NetworkServiceChannels().Informer()
	// we add a new event handler, watching for changes to API resources.
	plugin.informerNSC.AddEventHandler(
		cache.ResourceEventHandlerFuncs{
			AddFunc: networkservicechannelEnqueue,
			UpdateFunc: func(old, cur interface{}) {
				if !reflect.DeepEqual(old, cur) {
					networkservicechannelEnqueue(cur)
				}
			},
			DeleteFunc: networkservicechannelEnqueue,
		},
	)
}

func informerNetworkServiceEndpoints(plugin *Plugin) {
	plugin.informerNSE = plugin.sharedFactory.Networkservice().V1().NetworkServiceEndpoints().Informer()
	// we add a new event handler, watching for changes to API resources.
	plugin.informerNSE.AddEventHandler(
		cache.ResourceEventHandlerFuncs{
			AddFunc: networkserviceendpointEnqueue,
			UpdateFunc: func(old, cur interface{}) {
				if !reflect.DeepEqual(old, cur) {
					networkserviceendpointEnqueue(cur)
				}
			},
			DeleteFunc: networkserviceendpointEnqueue,
		},
	)
}

// AfterInit This will create all of the CRDs for NetworkServiceMesh.
func (plugin *Plugin) AfterInit() error {
	var err error
	var crdname string

	// Create clientset and create our CRD, this only needs to run once
	plugin.apiclientset, err = apiextcs.NewForConfig(plugin.k8sClientConfig)
	if err != nil {
		panic(err.Error())
	}

	// Create an instance of our own API client
	plugin.crdClient, err = client.NewForConfig(plugin.k8sClientConfig)
	if err != nil {
		plugin.Log.Errorf("Error creating CRD client: %s", err.Error())
		panic(err.Error())
	}

	crdname = reflect.TypeOf(v1.NetworkServiceEndpoint{}).Name()
	err = createCRD(plugin, v1.FullNSMEPName,
		v1.NSMGroup,
		v1.NSMGroupVersion,
		v1.NSMEPPlural,
		crdname)

	if err != nil {
		plugin.Log.Error("Error initializing NetworkServiceEndpoint CRD")
		return err
	}

	crdname = reflect.TypeOf(v1.NetworkServiceChannel{}).Name()
	err = createCRD(plugin, v1.FullNSMChannelName,
		v1.NSMGroup,
		v1.NSMGroupVersion,
		v1.NSMChannelPlural,
		crdname)

	if err != nil {
		plugin.Log.Error("Error initializing NetworkServiceChannel CRD")
		return err
	}

	crdname = reflect.TypeOf(v1.NetworkService{}).Name()
	err = createCRD(plugin, v1.FullNSMName,
		v1.NSMGroup,
		v1.NSMGroupVersion,
		v1.NSMPlural,
		crdname)

	if err != nil {
		plugin.Log.Error("Error initializing NetworkService CRD")
		return err
	}

	// We use a shared informer from the informer factory, to save calls to the
	// API as we grow our application and so state is consistent between our
	// control loops. We set a resync period of 30 seconds, in case any
	// create/replace/update/delete operations are missed when watching
	plugin.sharedFactory = factory.NewSharedInformerFactory(plugin.crdClient, time.Second*30)

	informerNetworkServices(plugin)
	informerNetworkServiceChannels(plugin)
	informerNetworkServiceEndpoints(plugin)

	// Start the informer. This will cause it to begin receiving updates from
	// the configured API server and firing event handlers in response.
	plugin.sharedFactory.Start(plugin.stopChNS)
	plugin.Log.Info("Started NetworkService informer factory.")

	// Wait for the informer cache to finish performing it's initial sync of
	// resources
	if !cache.WaitForCacheSync(plugin.stopChNS, plugin.informerNS.HasSynced) {
		plugin.Log.Error("Error waiting for informer cache to sync")
	}

	plugin.Log.Info("NetworkService Informer is ready")

	// Read forever from the work queue
	go workforever(plugin, queueNS, plugin.informerNS, plugin.stopChNS)
	go workforever(plugin, queueNSC, plugin.informerNSC, plugin.stopChNSC)
	go workforever(plugin, queueNSE, plugin.informerNSE, plugin.stopChNSE)

	return nil
}

// Close stops all reflectors.
func (plugin *Plugin) Close() error {
	close(plugin.pluginStopCh)
	plugin.wg.Wait()
	return nil
}
<|endoftext|>


<commit_before>package matchers

import "encoding/binary"

const (
	ZstdMagicSkippableStart = 0x184D2A50
	ZstdMagicSkippableMask  = 0xFFFFFFF0
)

var (
	TypeEpub   = newType("epub", "application/epub+zip")
	TypeZip    = newType("zip", "application/zip")
	TypeTar    = newType("tar", "application/x-tar")
	TypeRar    = newType("rar", "application/vnd.rar")
	TypeGz     = newType("gz", "application/gzip")
	TypeBz2    = newType("bz2", "application/x-bzip2")
	Type7z     = newType("7z", "application/x-7z-compressed")
	TypeXz     = newType("xz", "application/x-xz")
	TypeZstd   = newType("zst", "application/zstd")
	TypePdf    = newType("pdf", "application/pdf")
	TypeExe    = newType("exe", "application/vnd.microsoft.portable-executable")
	TypeSwf    = newType("swf", "application/x-shockwave-flash")
	TypeRtf    = newType("rtf", "application/rtf")
	TypeEot    = newType("eot", "application/octet-stream")
	TypePs     = newType("ps", "application/postscript")
	TypeSqlite = newType("sqlite", "application/vnd.sqlite3")
	TypeNes    = newType("nes", "application/x-nintendo-nes-rom")
	TypeCrx    = newType("crx", "application/x-google-chrome-extension")
	TypeCab    = newType("cab", "application/vnd.ms-cab-compressed")
	TypeDeb    = newType("deb", "application/vnd.debian.binary-package")
	TypeAr     = newType("ar", "application/x-unix-archive")
	TypeZ      = newType("Z", "application/x-compress")
	TypeLz     = newType("lz", "application/x-lzip")
	TypeRpm    = newType("rpm", "application/x-rpm")
	TypeElf    = newType("elf", "application/x-executable")
	TypeDcm    = newType("dcm", "application/dicom")
	TypeIso    = newType("iso", "application/x-iso9660-image")
	TypeMachO  = newType("macho", "application/x-mach-binary") // Mach-O binaries have no common extension.
)

var Archive = Map{
	TypeEpub:   bytePrefixMatcher(epubMagic),
	TypeZip:    Zip,
	TypeTar:    Tar,
	TypeRar:    Rar,
	TypeGz:     bytePrefixMatcher(gzMagic),
	TypeBz2:    bytePrefixMatcher(bz2Magic),
	Type7z:     bytePrefixMatcher(sevenzMagic),
	TypeXz:     bytePrefixMatcher(xzMagic),
	TypeZstd:   Zst,
	TypePdf:    bytePrefixMatcher(pdfMagic),
	TypeExe:    bytePrefixMatcher(exeMagic),
	TypeSwf:    Swf,
	TypeRtf:    bytePrefixMatcher(rtfMagic),
	TypeEot:    Eot,
	TypePs:     bytePrefixMatcher(psMagic),
	TypeSqlite: bytePrefixMatcher(sqliteMagic),
	TypeNes:    bytePrefixMatcher(nesMagic),
	TypeCrx:    bytePrefixMatcher(crxMagic),
	TypeCab:    Cab,
	TypeDeb:    bytePrefixMatcher(debMagic),
	TypeAr:     bytePrefixMatcher(arMagic),
	TypeZ:      Z,
	TypeLz:     bytePrefixMatcher(lzMagic),
	TypeRpm:    Rpm,
	TypeElf:    Elf,
	TypeDcm:    Dcm,
	TypeIso:    Iso,
	TypeMachO:  MachO,
}

var (
	epubMagic = []byte{
		0x50, 0x4B, 0x03, 0x04, 0x6D, 0x69, 0x6D, 0x65,
		0x74, 0x79, 0x70, 0x65, 0x61, 0x70, 0x70, 0x6C,
		0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x2F,
		0x65, 0x70, 0x75, 0x62, 0x2B, 0x7A, 0x69, 0x70,
	}
	gzMagic     = []byte{0x1F, 0x8B, 0x08}
	bz2Magic    = []byte{0x42, 0x5A, 0x68}
	sevenzMagic = []byte{0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C}
	pdfMagic    = []byte{0x25, 0x50, 0x44, 0x46}
	exeMagic    = []byte{0x4D, 0x5A}
	rtfMagic    = []byte{0x7B, 0x5C, 0x72, 0x74, 0x66}
	nesMagic    = []byte{0x4E, 0x45, 0x53, 0x1A}
	crxMagic    = []byte{0x43, 0x72, 0x32, 0x34}
	psMagic     = []byte{0x25, 0x21}
	xzMagic     = []byte{0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00}
	sqliteMagic = []byte{0x53, 0x51, 0x4C, 0x69}
	debMagic    = []byte{
		0x21, 0x3C, 0x61, 0x72, 0x63, 0x68, 0x3E, 0x0A,
		0x64, 0x65, 0x62, 0x69, 0x61, 0x6E, 0x2D, 0x62,
		0x69, 0x6E, 0x61, 0x72, 0x79,
	}
	arMagic   = []byte{0x21, 0x3C, 0x61, 0x72, 0x63, 0x68, 0x3E}
	zstdMagic = []byte{0x28, 0xB5, 0x2F, 0xFD}
	lzMagic   = []byte{0x4C, 0x5A, 0x49, 0x50}
)

func bytePrefixMatcher(magicPattern []byte) Matcher {
	return func(data []byte) bool {
		return compareBytes(data, magicPattern, 0)
	}
}

func Zip(buf []byte) bool {
	return len(buf) > 3 &&
		buf[0] == 0x50 && buf[1] == 0x4B &&
		(buf[2] == 0x3 || buf[2] == 0x5 || buf[2] == 0x7) &&
		(buf[3] == 0x4 || buf[3] == 0x6 || buf[3] == 0x8)
}

func Tar(buf []byte) bool {
	return len(buf) > 261 &&
		buf[257] == 0x75 && buf[258] == 0x73 &&
		buf[259] == 0x74 && buf[260] == 0x61 &&
		buf[261] == 0x72
}

func Rar(buf []byte) bool {
	return len(buf) > 6 &&
		buf[0] == 0x52 && buf[1] == 0x61 && buf[2] == 0x72 &&
		buf[3] == 0x21 && buf[4] == 0x1A && buf[5] == 0x7 &&
		(buf[6] == 0x0 || buf[6] == 0x1)
}

func Swf(buf []byte) bool {
	return len(buf) > 2 &&
		(buf[0] == 0x43 || buf[0] == 0x46) &&
		buf[1] == 0x57 && buf[2] == 0x53
}

func Cab(buf []byte) bool {
	return len(buf) > 3 &&
		((buf[0] == 0x4D && buf[1] == 0x53 && buf[2] == 0x43 && buf[3] == 0x46) ||
			(buf[0] == 0x49 && buf[1] == 0x53 && buf[2] == 0x63 && buf[3] == 0x28))
}

func Eot(buf []byte) bool {
	return len(buf) > 35 &&
		buf[34] == 0x4C && buf[35] == 0x50 &&
		((buf[8] == 0x02 && buf[9] == 0x00 &&
			buf[10] == 0x01) || (buf[8] == 0x01 &&
			buf[9] == 0x00 && buf[10] == 0x00) ||
			(buf[8] == 0x02 && buf[9] == 0x00 &&
				buf[10] == 0x02))
}

func Z(buf []byte) bool {
	return len(buf) > 1 &&
		((buf[0] == 0x1F && buf[1] == 0xA0) ||
			(buf[0] == 0x1F && buf[1] == 0x9D))
}

func Rpm(buf []byte) bool {
	return len(buf) > 96 &&
		buf[0] == 0xED && buf[1] == 0xAB &&
		buf[2] == 0xEE && buf[3] == 0xDB
}

func Elf(buf []byte) bool {
	return len(buf) > 52 &&
		buf[0] == 0x7F && buf[1] == 0x45 &&
		buf[2] == 0x4C && buf[3] == 0x46
}

func Dcm(buf []byte) bool {
	return len(buf) > 131 &&
		buf[128] == 0x44 && buf[129] == 0x49 &&
		buf[130] == 0x43 && buf[131] == 0x4D
}

func Iso(buf []byte) bool {
	return len(buf) > 32773 &&
		buf[32769] == 0x43 && buf[32770] == 0x44 &&
		buf[32771] == 0x30 && buf[32772] == 0x30 &&
		buf[32773] == 0x31
}

func MachO(buf []byte) bool {
	return len(buf) > 3 && ((buf[0] == 0xFE && buf[1] == 0xED && buf[2] == 0xFA && buf[3] == 0xCF) ||
		(buf[0] == 0xFE && buf[1] == 0xED && buf[2] == 0xFA && buf[3] == 0xCE) ||
		(buf[0] == 0xBE && buf[1] == 0xBA && buf[2] == 0xFE && buf[3] == 0xCA) ||
		// Big endian versions below here...
		(buf[0] == 0xCF && buf[1] == 0xFA && buf[2] == 0xED && buf[3] == 0xFE) ||
		(buf[0] == 0xCE && buf[1] == 0xFA && buf[2] == 0xED && buf[3] == 0xFE) ||
		(buf[0] == 0xCA && buf[1] == 0xFE && buf[2] == 0xBA && buf[3] == 0xBE))
}

// Zstandard compressed data is made of one or more frames.
// There are two frame formats defined by Zstandard: Zstandard frames and Skippable frames.
// See more details from https://tools.ietf.org/id/draft-kucherawy-dispatch-zstd-00.html#rfc.section.2
func Zst(buf []byte) bool {
	if compareBytes(buf, zstdMagic, 0) {
		return true
	} else {
		// skippable frames
    if len(buf) < 8 {
      return false
    }
		if binary.LittleEndian.Uint32(buf[:4]) & ZstdMagicSkippableMask == ZstdMagicSkippableStart {
			userDataLength := binary.LittleEndian.Uint32(buf[4:8])
			if len(buf) < 8 + int(userDataLength) {
			  return false
      }
			nextFrame := buf[8+userDataLength:]
			return Zst(nextFrame)
		}
		return false
	}
}
<commit_msg>fix(archive): format issue indentation<commit_after>package matchers

import "encoding/binary"

const (
	ZstdMagicSkippableStart = 0x184D2A50
	ZstdMagicSkippableMask  = 0xFFFFFFF0
)

var (
	TypeEpub   = newType("epub", "application/epub+zip")
	TypeZip    = newType("zip", "application/zip")
	TypeTar    = newType("tar", "application/x-tar")
	TypeRar    = newType("rar", "application/vnd.rar")
	TypeGz     = newType("gz", "application/gzip")
	TypeBz2    = newType("bz2", "application/x-bzip2")
	Type7z     = newType("7z", "application/x-7z-compressed")
	TypeXz     = newType("xz", "application/x-xz")
	TypeZstd   = newType("zst", "application/zstd")
	TypePdf    = newType("pdf", "application/pdf")
	TypeExe    = newType("exe", "application/vnd.microsoft.portable-executable")
	TypeSwf    = newType("swf", "application/x-shockwave-flash")
	TypeRtf    = newType("rtf", "application/rtf")
	TypeEot    = newType("eot", "application/octet-stream")
	TypePs     = newType("ps", "application/postscript")
	TypeSqlite = newType("sqlite", "application/vnd.sqlite3")
	TypeNes    = newType("nes", "application/x-nintendo-nes-rom")
	TypeCrx    = newType("crx", "application/x-google-chrome-extension")
	TypeCab    = newType("cab", "application/vnd.ms-cab-compressed")
	TypeDeb    = newType("deb", "application/vnd.debian.binary-package")
	TypeAr     = newType("ar", "application/x-unix-archive")
	TypeZ      = newType("Z", "application/x-compress")
	TypeLz     = newType("lz", "application/x-lzip")
	TypeRpm    = newType("rpm", "application/x-rpm")
	TypeElf    = newType("elf", "application/x-executable")
	TypeDcm    = newType("dcm", "application/dicom")
	TypeIso    = newType("iso", "application/x-iso9660-image")
	TypeMachO  = newType("macho", "application/x-mach-binary") // Mach-O binaries have no common extension.
)

var Archive = Map{
	TypeEpub:   bytePrefixMatcher(epubMagic),
	TypeZip:    Zip,
	TypeTar:    Tar,
	TypeRar:    Rar,
	TypeGz:     bytePrefixMatcher(gzMagic),
	TypeBz2:    bytePrefixMatcher(bz2Magic),
	Type7z:     bytePrefixMatcher(sevenzMagic),
	TypeXz:     bytePrefixMatcher(xzMagic),
	TypeZstd:   Zst,
	TypePdf:    bytePrefixMatcher(pdfMagic),
	TypeExe:    bytePrefixMatcher(exeMagic),
	TypeSwf:    Swf,
	TypeRtf:    bytePrefixMatcher(rtfMagic),
	TypeEot:    Eot,
	TypePs:     bytePrefixMatcher(psMagic),
	TypeSqlite: bytePrefixMatcher(sqliteMagic),
	TypeNes:    bytePrefixMatcher(nesMagic),
	TypeCrx:    bytePrefixMatcher(crxMagic),
	TypeCab:    Cab,
	TypeDeb:    bytePrefixMatcher(debMagic),
	TypeAr:     bytePrefixMatcher(arMagic),
	TypeZ:      Z,
	TypeLz:     bytePrefixMatcher(lzMagic),
	TypeRpm:    Rpm,
	TypeElf:    Elf,
	TypeDcm:    Dcm,
	TypeIso:    Iso,
	TypeMachO:  MachO,
}

var (
	epubMagic = []byte{
		0x50, 0x4B, 0x03, 0x04, 0x6D, 0x69, 0x6D, 0x65,
		0x74, 0x79, 0x70, 0x65, 0x61, 0x70, 0x70, 0x6C,
		0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x2F,
		0x65, 0x70, 0x75, 0x62, 0x2B, 0x7A, 0x69, 0x70,
	}
	gzMagic     = []byte{0x1F, 0x8B, 0x08}
	bz2Magic    = []byte{0x42, 0x5A, 0x68}
	sevenzMagic = []byte{0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C}
	pdfMagic    = []byte{0x25, 0x50, 0x44, 0x46}
	exeMagic    = []byte{0x4D, 0x5A}
	rtfMagic    = []byte{0x7B, 0x5C, 0x72, 0x74, 0x66}
	nesMagic    = []byte{0x4E, 0x45, 0x53, 0x1A}
	crxMagic    = []byte{0x43, 0x72, 0x32, 0x34}
	psMagic     = []byte{0x25, 0x21}
	xzMagic     = []byte{0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00}
	sqliteMagic = []byte{0x53, 0x51, 0x4C, 0x69}
	debMagic    = []byte{
		0x21, 0x3C, 0x61, 0x72, 0x63, 0x68, 0x3E, 0x0A,
		0x64, 0x65, 0x62, 0x69, 0x61, 0x6E, 0x2D, 0x62,
		0x69, 0x6E, 0x61, 0x72, 0x79,
	}
	arMagic   = []byte{0x21, 0x3C, 0x61, 0x72, 0x63, 0x68, 0x3E}
	zstdMagic = []byte{0x28, 0xB5, 0x2F, 0xFD}
	lzMagic   = []byte{0x4C, 0x5A, 0x49, 0x50}
)

func bytePrefixMatcher(magicPattern []byte) Matcher {
	return func(data []byte) bool {
		return compareBytes(data, magicPattern, 0)
	}
}

func Zip(buf []byte) bool {
	return len(buf) > 3 &&
		buf[0] == 0x50 && buf[1] == 0x4B &&
		(buf[2] == 0x3 || buf[2] == 0x5 || buf[2] == 0x7) &&
		(buf[3] == 0x4 || buf[3] == 0x6 || buf[3] == 0x8)
}

func Tar(buf []byte) bool {
	return len(buf) > 261 &&
		buf[257] == 0x75 && buf[258] == 0x73 &&
		buf[259] == 0x74 && buf[260] == 0x61 &&
		buf[261] == 0x72
}

func Rar(buf []byte) bool {
	return len(buf) > 6 &&
		buf[0] == 0x52 && buf[1] == 0x61 && buf[2] == 0x72 &&
		buf[3] == 0x21 && buf[4] == 0x1A && buf[5] == 0x7 &&
		(buf[6] == 0x0 || buf[6] == 0x1)
}

func Swf(buf []byte) bool {
	return len(buf) > 2 &&
		(buf[0] == 0x43 || buf[0] == 0x46) &&
		buf[1] == 0x57 && buf[2] == 0x53
}

func Cab(buf []byte) bool {
	return len(buf) > 3 &&
		((buf[0] == 0x4D && buf[1] == 0x53 && buf[2] == 0x43 && buf[3] == 0x46) ||
			(buf[0] == 0x49 && buf[1] == 0x53 && buf[2] == 0x63 && buf[3] == 0x28))
}

func Eot(buf []byte) bool {
	return len(buf) > 35 &&
		buf[34] == 0x4C && buf[35] == 0x50 &&
		((buf[8] == 0x02 && buf[9] == 0x00 &&
			buf[10] == 0x01) || (buf[8] == 0x01 &&
			buf[9] == 0x00 && buf[10] == 0x00) ||
			(buf[8] == 0x02 && buf[9] == 0x00 &&
				buf[10] == 0x02))
}

func Z(buf []byte) bool {
	return len(buf) > 1 &&
		((buf[0] == 0x1F && buf[1] == 0xA0) ||
			(buf[0] == 0x1F && buf[1] == 0x9D))
}

func Rpm(buf []byte) bool {
	return len(buf) > 96 &&
		buf[0] == 0xED && buf[1] == 0xAB &&
		buf[2] == 0xEE && buf[3] == 0xDB
}

func Elf(buf []byte) bool {
	return len(buf) > 52 &&
		buf[0] == 0x7F && buf[1] == 0x45 &&
		buf[2] == 0x4C && buf[3] == 0x46
}

func Dcm(buf []byte) bool {
	return len(buf) > 131 &&
		buf[128] == 0x44 && buf[129] == 0x49 &&
		buf[130] == 0x43 && buf[131] == 0x4D
}

func Iso(buf []byte) bool {
	return len(buf) > 32773 &&
		buf[32769] == 0x43 && buf[32770] == 0x44 &&
		buf[32771] == 0x30 && buf[32772] == 0x30 &&
		buf[32773] == 0x31
}

func MachO(buf []byte) bool {
	return len(buf) > 3 && ((buf[0] == 0xFE && buf[1] == 0xED && buf[2] == 0xFA && buf[3] == 0xCF) ||
		(buf[0] == 0xFE && buf[1] == 0xED && buf[2] == 0xFA && buf[3] == 0xCE) ||
		(buf[0] == 0xBE && buf[1] == 0xBA && buf[2] == 0xFE && buf[3] == 0xCA) ||
		// Big endian versions below here...
		(buf[0] == 0xCF && buf[1] == 0xFA && buf[2] == 0xED && buf[3] == 0xFE) ||
		(buf[0] == 0xCE && buf[1] == 0xFA && buf[2] == 0xED && buf[3] == 0xFE) ||
		(buf[0] == 0xCA && buf[1] == 0xFE && buf[2] == 0xBA && buf[3] == 0xBE))
}

// Zstandard compressed data is made of one or more frames.
// There are two frame formats defined by Zstandard: Zstandard frames and Skippable frames.
// See more details from https://tools.ietf.org/id/draft-kucherawy-dispatch-zstd-00.html#rfc.section.2
func Zst(buf []byte) bool {
  if compareBytes(buf, zstdMagic, 0) {
    return true
  } else {
		// skippable frames
    if len(buf) < 8 {
      return false
    }
    if binary.LittleEndian.Uint32(buf[:4]) & ZstdMagicSkippableMask == ZstdMagicSkippableStart {
      userDataLength := binary.LittleEndian.Uint32(buf[4:8])
      if len(buf) < 8 + int(userDataLength) {
        return false
      }
      nextFrame := buf[8+userDataLength:]
      return Zst(nextFrame)
    }
    return false
  }
}
<|endoftext|>


<commit_before>// rand2 is a drop-in replacement for the "math/rand" package.  It initializes
// the global random generator with a random seed (instead of 1), and provides
// additional functionality over the standard "math/rand" package.
package rand2

import (
	"math/rand"
	"os"
	"sort"
	"sync"
	"time"

	"github.com/dropbox/godropbox/container/set"
	"github.com/dropbox/godropbox/errors"
)

type lockedSource struct {
	mutex sync.Mutex
	src   rand.Source
}

func (r *lockedSource) Int63() int64 {
	r.mutex.Lock()
	val := r.src.Int63()
	r.mutex.Unlock()
	return val
}

func (r *lockedSource) Seed(seed int64) {
	r.mutex.Lock()
	r.src.Seed(seed)
	r.mutex.Unlock()
}

// This returns a thread-safe random source.
func NewSource(seed int64) rand.Source {
	return &lockedSource{
		src: rand.NewSource(seed),
	}
}

// This returns a new Rand.  See math/rand for documentation.
func New(src rand.Source) *rand.Rand {
	return rand.New(src)
}

var globalRand *rand.Rand

func init() {
	now := time.Now()
	seed := now.Unix() + int64(now.Nanosecond()) + 12345*int64(os.Getpid())
	globalRand = New(NewSource(seed))
}

// See math/rand for documentation.
func Seed(seed int64) { globalRand.Seed(seed) }

// See math/rand for documentation.
func Int63() int64 { return globalRand.Int63() }

// See math/rand for documentation.
func Uint32() uint32 { return globalRand.Uint32() }

// See math/rand for documentation.
func Int31() int32 { return globalRand.Int31() }

// See math/rand for documentation.
func Int() int { return globalRand.Int() }

// See math/rand for documentation.
func Int63n(n int64) int64 { return globalRand.Int63n(n) }

// See math/rand for documentation.
func Int31n(n int32) int32 { return globalRand.Int31n(n) }

// See math/rand for documentation.
func Intn(n int) int { return globalRand.Intn(n) }

// See math/rand for documentation.
func Float64() float64 { return globalRand.Float64() }

// See math/rand for documentation.
func Float32() float32 { return globalRand.Float32() }

// See math/rand for documentation.
func Perm(n int) []int { return globalRand.Perm(n) }

// See math/rand for documentation.
func NormFloat64() float64 { return globalRand.NormFloat64() }

// See math/rand for documentation.
func ExpFloat64() float64 { return globalRand.ExpFloat64() }

// Samples 'k' unique ints from the range [0, n)
func SampleInts(n int, k int) (res []int, err error) {
	if k < 0 {
		err = errors.Newf("invalid sample size k")
		return
	}

	if n < k {
		err = errors.Newf("sample size k larger than n")
		return
	}

	picked := set.NewSet()
	for picked.Len() < k {
		i := Intn(n)
		picked.Add(i)
	}

	res = make([]int, k)
	e := 0
	for i := range picked.Iter() {
		res[e] = i.(int)
		e++
	}

	return
}

// Samples 'k' elements from the given slice
func Sample(population []interface{}, k int) (res []interface{}, err error) {
	n := len(population)
	idxs, err := SampleInts(n, k)
	if err != nil {
		return
	}

	res = []interface{}{}
	for _, idx := range idxs {
		res = append(res, population[idx])
	}

	return
}

// Same as 'Sample' except it returns both the 'picked' sample set and the 'remaining' elements.
func PickN(population []interface{}, n int) (
	picked []interface{}, remaining []interface{}, err error) {

	total := len(population)
	idxs, err := SampleInts(total, n)
	if err != nil {
		return
	}
	sort.Ints(idxs)

	picked, remaining = []interface{}{}, []interface{}{}
	for x, elem := range population {
		if len(idxs) > 0 && x == idxs[0] {
			picked = append(picked, elem)
			idxs = idxs[1:]
		} else {
			remaining = append(remaining, elem)
		}
	}

	return
}
<commit_msg>add Zipf<commit_after>// rand2 is a drop-in replacement for the "math/rand" package.  It initializes
// the global random generator with a random seed (instead of 1), and provides
// additional functionality over the standard "math/rand" package.
package rand2

import (
	"math/rand"
	"os"
	"sort"
	"sync"
	"time"

	"github.com/dropbox/godropbox/container/set"
	"github.com/dropbox/godropbox/errors"
)

type lockedSource struct {
	mutex sync.Mutex
	src   rand.Source
}

func (r *lockedSource) Int63() int64 {
	r.mutex.Lock()
	val := r.src.Int63()
	r.mutex.Unlock()
	return val
}

func (r *lockedSource) Seed(seed int64) {
	r.mutex.Lock()
	r.src.Seed(seed)
	r.mutex.Unlock()
}

// This returns a thread-safe random source.
func NewSource(seed int64) rand.Source {
	return &lockedSource{
		src: rand.NewSource(seed),
	}
}

// This returns a new Rand.  See math/rand for documentation.
func New(src rand.Source) *rand.Rand {
	return rand.New(src)
}

var globalRand *rand.Rand

func init() {
	now := time.Now()
	seed := now.Unix() + int64(now.Nanosecond()) + 12345*int64(os.Getpid())
	globalRand = New(NewSource(seed))
}

// See math/rand for documentation.
func Seed(seed int64) { globalRand.Seed(seed) }

// See math/rand for documentation.
func Int63() int64 { return globalRand.Int63() }

// See math/rand for documentation.
func Uint32() uint32 { return globalRand.Uint32() }

// See math/rand for documentation.
func Int31() int32 { return globalRand.Int31() }

// See math/rand for documentation.
func Int() int { return globalRand.Int() }

// See math/rand for documentation.
func Int63n(n int64) int64 { return globalRand.Int63n(n) }

// See math/rand for documentation.
func Int31n(n int32) int32 { return globalRand.Int31n(n) }

// See math/rand for documentation.
func Intn(n int) int { return globalRand.Intn(n) }

// See math/rand for documentation.
func Float64() float64 { return globalRand.Float64() }

// See math/rand for documentation.
func Float32() float32 { return globalRand.Float32() }

// See math/rand for documentation.
func Perm(n int) []int { return globalRand.Perm(n) }

// See math/rand for documentation.
func NormFloat64() float64 { return globalRand.NormFloat64() }

// See math/rand for documentation.
func ExpFloat64() float64 { return globalRand.ExpFloat64() }

// See math/rand for documentation.
func NewZipf(r *rand.Rand, s float64, v float64, imax uint64) *rand.Zipf {
	return rand.NewZipf(r, s, v, imax)
}

// Samples 'k' unique ints from the range [0, n)
func SampleInts(n int, k int) (res []int, err error) {
	if k < 0 {
		err = errors.Newf("invalid sample size k")
		return
	}

	if n < k {
		err = errors.Newf("sample size k larger than n")
		return
	}

	picked := set.NewSet()
	for picked.Len() < k {
		i := Intn(n)
		picked.Add(i)
	}

	res = make([]int, k)
	e := 0
	for i := range picked.Iter() {
		res[e] = i.(int)
		e++
	}

	return
}

// Samples 'k' elements from the given slice
func Sample(population []interface{}, k int) (res []interface{}, err error) {
	n := len(population)
	idxs, err := SampleInts(n, k)
	if err != nil {
		return
	}

	res = []interface{}{}
	for _, idx := range idxs {
		res = append(res, population[idx])
	}

	return
}

// Same as 'Sample' except it returns both the 'picked' sample set and the 'remaining' elements.
func PickN(population []interface{}, n int) (
	picked []interface{}, remaining []interface{}, err error) {

	total := len(population)
	idxs, err := SampleInts(total, n)
	if err != nil {
		return
	}
	sort.Ints(idxs)

	picked, remaining = []interface{}{}, []interface{}{}
	for x, elem := range population {
		if len(idxs) > 0 && x == idxs[0] {
			picked = append(picked, elem)
			idxs = idxs[1:]
		} else {
			remaining = append(remaining, elem)
		}
	}

	return
}
<|endoftext|>