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

// Contact Group API support - Fetch, Create, Update, Delete, and Search
// See: https://login.circonus.com/resources/api/calls/contact_group

package api

import (
	"encoding/json"
	"fmt"
	"net/url"
	"regexp"

	"github.com/circonus-labs/circonus-gometrics/api/config"
)

// ContactGroupAlertFormats define alert formats
type ContactGroupAlertFormats struct {
	LongMessage  *string `json:"long_message"`
	LongSubject  *string `json:"long_subject"`
	LongSummary  *string `json:"long_summary"`
	ShortMessage *string `json:"short_message"`
	ShortSummary *string `json:"short_summary"`
}

// ContactGroupContactsExternal external contacts
type ContactGroupContactsExternal struct {
	Info   string `json:"contact_info"`
	Method string `json:"method"`
}

// ContactGroupContactsUser user contacts
type ContactGroupContactsUser struct {
	Info    string `json:"_contact_info,omitempty"`
	Method  string `json:"method"`
	UserCID string `json:"user"`
}

// ContactGroupContacts list of contacts
type ContactGroupContacts struct {
	External []ContactGroupContactsExternal `json:"external"`
	Users    []ContactGroupContactsUser     `json:"users"`
}

// ContactGroupEscalation defines escalations for severity levels
type ContactGroupEscalation struct {
	After           uint   `json:"after"`
	ContactGroupCID string `json:"contact_group"`
}

// ContactGroup defines a contact group. See https://login.circonus.com/resources/api/calls/contact_group for more information.
type ContactGroup struct {
	CID               string                    `json:"_cid,omitempty"`
	LastModified      uint                      `json:"_last_modified,omitempty"`
	LastModifiedBy    string                    `json:"_last_modified_by,omitempty"`
	AggregationWindow uint                      `json:"aggregation_window,omitempty"`
	AlertFormats      ContactGroupAlertFormats  `json:"alert_formats,omitempty"`
	Contacts          ContactGroupContacts      `json:"contacts,omitempty"`
	Escalations       []*ContactGroupEscalation `json:"escalations,omitempty"`
	Name              string                    `json:"name,omitempty"`
	Reminders         []uint                    `json:"reminders,omitempty"`
	Tags              []string                  `json:"tags,omitempty"`
}

// NewContactGroup returns a ContactGroup (with defaults, if applicable)
func NewContactGroup() *ContactGroup {
	return &ContactGroup{
		Escalations: make([]*ContactGroupEscalation, config.NumSeverityLevels),
		Reminders:   make([]uint, config.NumSeverityLevels),
		Contacts: ContactGroupContacts{
			External: []ContactGroupContactsExternal{},
			Users:    []ContactGroupContactsUser{},
		},
	}
}

// FetchContactGroup retrieves contact group with passed cid.
func (a *API) FetchContactGroup(cid CIDType) (*ContactGroup, error) {
	if cid == nil || *cid == "" {
		return nil, fmt.Errorf("Invalid contact group CID [none]")
	}

	groupCID := string(*cid)

	matched, err := regexp.MatchString(config.ContactGroupCIDRegex, groupCID)
	if err != nil {
		return nil, err
	}
	if !matched {
		return nil, fmt.Errorf("Invalid contact group CID [%s]", groupCID)
	}

	result, err := a.Get(groupCID)
	if err != nil {
		return nil, err
	}

	if a.Debug {
		a.Log.Printf("[DEBUG] fetch contact group, received JSON: %s", string(result))
	}

	group := new(ContactGroup)
	if err := json.Unmarshal(result, group); err != nil {
		return nil, err
	}

	return group, nil
}

// FetchContactGroups retrieves all contact groups available to the API Token.
func (a *API) FetchContactGroups() (*[]ContactGroup, error) {
	result, err := a.Get(config.ContactGroupPrefix)
	if err != nil {
		return nil, err
	}

	var groups []ContactGroup
	if err := json.Unmarshal(result, &groups); err != nil {
		return nil, err
	}

	return &groups, nil
}

// UpdateContactGroup updates passed contact group.
func (a *API) UpdateContactGroup(cfg *ContactGroup) (*ContactGroup, error) {
	if cfg == nil {
		return nil, fmt.Errorf("Invalid contact group config [nil]")
	}

	groupCID := string(cfg.CID)

	matched, err := regexp.MatchString(config.ContactGroupCIDRegex, groupCID)
	if err != nil {
		return nil, err
	}
	if !matched {
		return nil, fmt.Errorf("Invalid contact group CID [%s]", groupCID)
	}

	jsonCfg, err := json.Marshal(cfg)
	if err != nil {
		return nil, err
	}

	if a.Debug {
		a.Log.Printf("[DEBUG] update contact group, sending JSON: %s", string(jsonCfg))
	}

	result, err := a.Put(groupCID, jsonCfg)
	if err != nil {
		return nil, err
	}

	group := &ContactGroup{}
	if err := json.Unmarshal(result, group); err != nil {
		return nil, err
	}

	return group, nil
}

// CreateContactGroup creates a new contact group.
func (a *API) CreateContactGroup(cfg *ContactGroup) (*ContactGroup, error) {
	if cfg == nil {
		return nil, fmt.Errorf("Invalid contact group config [nil]")
	}

	jsonCfg, err := json.Marshal(cfg)
	if err != nil {
		return nil, err
	}

	if a.Debug {
		a.Log.Printf("[DEBUG] create contact group, sending JSON: %s", string(jsonCfg))
	}

	result, err := a.Post(config.ContactGroupPrefix, jsonCfg)
	if err != nil {
		return nil, err
	}

	group := &ContactGroup{}
	if err := json.Unmarshal(result, group); err != nil {
		return nil, err
	}

	return group, nil
}

// DeleteContactGroup deletes passed contact group.
func (a *API) DeleteContactGroup(cfg *ContactGroup) (bool, error) {
	if cfg == nil {
		return false, fmt.Errorf("Invalid contact group config [nil]")
	}
	return a.DeleteContactGroupByCID(CIDType(&cfg.CID))
}

// DeleteContactGroupByCID deletes contact group with passed cid.
func (a *API) DeleteContactGroupByCID(cid CIDType) (bool, error) {
	if cid == nil || *cid == "" {
		return false, fmt.Errorf("Invalid contact group CID [none]")
	}

	groupCID := string(*cid)

	matched, err := regexp.MatchString(config.ContactGroupCIDRegex, groupCID)
	if err != nil {
		return false, err
	}
	if !matched {
		return false, fmt.Errorf("Invalid contact group CID [%s]", groupCID)
	}

	_, err = a.Delete(groupCID)
	if err != nil {
		return false, err
	}

	return true, nil
}

// SearchContactGroups returns contact groups matching the specified
// search query and/or filter. If nil is passed for both parameters
// all contact groups will be returned.
func (a *API) SearchContactGroups(searchCriteria *SearchQueryType, filterCriteria *SearchFilterType) (*[]ContactGroup, error) {
	q := url.Values{}

	if searchCriteria != nil && *searchCriteria != "" {
		q.Set("search", string(*searchCriteria))
	}

	if filterCriteria != nil && len(*filterCriteria) > 0 {
		for filter, criteria := range *filterCriteria {
			for _, val := range criteria {
				q.Add(filter, val)
			}
		}
	}

	if q.Encode() == "" {
		return a.FetchContactGroups()
	}

	reqURL := url.URL{
		Path:     config.ContactGroupPrefix,
		RawQuery: q.Encode(),
	}

	result, err := a.Get(reqURL.String())
	if err != nil {
		return nil, fmt.Errorf("[ERROR] API call error %+v", err)
	}

	var groups []ContactGroup
	if err := json.Unmarshal(result, &groups); err != nil {
		return nil, err
	}

	return &groups, nil
}
<commit_msg>upd: document/update struct member types to reflect what is received from api<commit_after>// Copyright 2016 Circonus, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Contact Group API support - Fetch, Create, Update, Delete, and Search
// See: https://login.circonus.com/resources/api/calls/contact_group

package api

import (
	"encoding/json"
	"fmt"
	"net/url"
	"regexp"

	"github.com/circonus-labs/circonus-gometrics/api/config"
)

// ContactGroupAlertFormats define alert formats
type ContactGroupAlertFormats struct {
	LongMessage  *string `json:"long_message"`  // string or null
	LongSubject  *string `json:"long_subject"`  // string or null
	LongSummary  *string `json:"long_summary"`  // string or null
	ShortMessage *string `json:"short_message"` // string or null
	ShortSummary *string `json:"short_summary"` // string or null
}

// ContactGroupContactsExternal external contacts
type ContactGroupContactsExternal struct {
	Info   string `json:"contact_info"` // string
	Method string `json:"method"`       // string
}

// ContactGroupContactsUser user contacts
type ContactGroupContactsUser struct {
	Info    string `json:"_contact_info,omitempty"` // string
	Method  string `json:"method"`                  // string
	UserCID string `json:"user"`                    // string
}

// ContactGroupContacts list of contacts
type ContactGroupContacts struct {
	External []ContactGroupContactsExternal `json:"external"` // [] len >= 0
	Users    []ContactGroupContactsUser     `json:"users"`    // [] len >= 0
}

// ContactGroupEscalation defines escalations for severity levels
type ContactGroupEscalation struct {
	After           uint   `json:"after"`         // uint
	ContactGroupCID string `json:"contact_group"` // string
}

// ContactGroup defines a contact group. See https://login.circonus.com/resources/api/calls/contact_group for more information.
type ContactGroup struct {
	CID               string                    `json:"_cid,omitempty"`               // string
	LastModified      uint                      `json:"_last_modified,omitempty"`     // uint
	LastModifiedBy    string                    `json:"_last_modified_by,omitempty"`  // string
	AggregationWindow uint                      `json:"aggregation_window,omitempty"` // uint
	AlertFormats      ContactGroupAlertFormats  `json:"alert_formats,omitempty"`      // ContactGroupAlertFormats
	Contacts          ContactGroupContacts      `json:"contacts,omitempty"`           // ContactGroupContacts
	Escalations       []*ContactGroupEscalation `json:"escalations,omitempty"`        // [] len == 5, elements: ContactGroupEscalation or null
	Name              string                    `json:"name,omitempty"`               // string
	Reminders         []uint                    `json:"reminders,omitempty"`          // [] len == 5
	Tags              []string                  `json:"tags,omitempty"`               // [] len >= 0
}

// NewContactGroup returns a ContactGroup (with defaults, if applicable)
func NewContactGroup() *ContactGroup {
	return &ContactGroup{
		Escalations: make([]*ContactGroupEscalation, config.NumSeverityLevels),
		Reminders:   make([]uint, config.NumSeverityLevels),
		Contacts: ContactGroupContacts{
			External: []ContactGroupContactsExternal{},
			Users:    []ContactGroupContactsUser{},
		},
	}
}

// FetchContactGroup retrieves contact group with passed cid.
func (a *API) FetchContactGroup(cid CIDType) (*ContactGroup, error) {
	if cid == nil || *cid == "" {
		return nil, fmt.Errorf("Invalid contact group CID [none]")
	}

	groupCID := string(*cid)

	matched, err := regexp.MatchString(config.ContactGroupCIDRegex, groupCID)
	if err != nil {
		return nil, err
	}
	if !matched {
		return nil, fmt.Errorf("Invalid contact group CID [%s]", groupCID)
	}

	result, err := a.Get(groupCID)
	if err != nil {
		return nil, err
	}

	if a.Debug {
		a.Log.Printf("[DEBUG] fetch contact group, received JSON: %s", string(result))
	}

	group := new(ContactGroup)
	if err := json.Unmarshal(result, group); err != nil {
		return nil, err
	}

	return group, nil
}

// FetchContactGroups retrieves all contact groups available to the API Token.
func (a *API) FetchContactGroups() (*[]ContactGroup, error) {
	result, err := a.Get(config.ContactGroupPrefix)
	if err != nil {
		return nil, err
	}

	var groups []ContactGroup
	if err := json.Unmarshal(result, &groups); err != nil {
		return nil, err
	}

	return &groups, nil
}

// UpdateContactGroup updates passed contact group.
func (a *API) UpdateContactGroup(cfg *ContactGroup) (*ContactGroup, error) {
	if cfg == nil {
		return nil, fmt.Errorf("Invalid contact group config [nil]")
	}

	groupCID := string(cfg.CID)

	matched, err := regexp.MatchString(config.ContactGroupCIDRegex, groupCID)
	if err != nil {
		return nil, err
	}
	if !matched {
		return nil, fmt.Errorf("Invalid contact group CID [%s]", groupCID)
	}

	jsonCfg, err := json.Marshal(cfg)
	if err != nil {
		return nil, err
	}

	if a.Debug {
		a.Log.Printf("[DEBUG] update contact group, sending JSON: %s", string(jsonCfg))
	}

	result, err := a.Put(groupCID, jsonCfg)
	if err != nil {
		return nil, err
	}

	group := &ContactGroup{}
	if err := json.Unmarshal(result, group); err != nil {
		return nil, err
	}

	return group, nil
}

// CreateContactGroup creates a new contact group.
func (a *API) CreateContactGroup(cfg *ContactGroup) (*ContactGroup, error) {
	if cfg == nil {
		return nil, fmt.Errorf("Invalid contact group config [nil]")
	}

	jsonCfg, err := json.Marshal(cfg)
	if err != nil {
		return nil, err
	}

	if a.Debug {
		a.Log.Printf("[DEBUG] create contact group, sending JSON: %s", string(jsonCfg))
	}

	result, err := a.Post(config.ContactGroupPrefix, jsonCfg)
	if err != nil {
		return nil, err
	}

	group := &ContactGroup{}
	if err := json.Unmarshal(result, group); err != nil {
		return nil, err
	}

	return group, nil
}

// DeleteContactGroup deletes passed contact group.
func (a *API) DeleteContactGroup(cfg *ContactGroup) (bool, error) {
	if cfg == nil {
		return false, fmt.Errorf("Invalid contact group config [nil]")
	}
	return a.DeleteContactGroupByCID(CIDType(&cfg.CID))
}

// DeleteContactGroupByCID deletes contact group with passed cid.
func (a *API) DeleteContactGroupByCID(cid CIDType) (bool, error) {
	if cid == nil || *cid == "" {
		return false, fmt.Errorf("Invalid contact group CID [none]")
	}

	groupCID := string(*cid)

	matched, err := regexp.MatchString(config.ContactGroupCIDRegex, groupCID)
	if err != nil {
		return false, err
	}
	if !matched {
		return false, fmt.Errorf("Invalid contact group CID [%s]", groupCID)
	}

	_, err = a.Delete(groupCID)
	if err != nil {
		return false, err
	}

	return true, nil
}

// SearchContactGroups returns contact groups matching the specified
// search query and/or filter. If nil is passed for both parameters
// all contact groups will be returned.
func (a *API) SearchContactGroups(searchCriteria *SearchQueryType, filterCriteria *SearchFilterType) (*[]ContactGroup, error) {
	q := url.Values{}

	if searchCriteria != nil && *searchCriteria != "" {
		q.Set("search", string(*searchCriteria))
	}

	if filterCriteria != nil && len(*filterCriteria) > 0 {
		for filter, criteria := range *filterCriteria {
			for _, val := range criteria {
				q.Add(filter, val)
			}
		}
	}

	if q.Encode() == "" {
		return a.FetchContactGroups()
	}

	reqURL := url.URL{
		Path:     config.ContactGroupPrefix,
		RawQuery: q.Encode(),
	}

	result, err := a.Get(reqURL.String())
	if err != nil {
		return nil, fmt.Errorf("[ERROR] API call error %+v", err)
	}

	var groups []ContactGroup
	if err := json.Unmarshal(result, &groups); err != nil {
		return nil, err
	}

	return &groups, nil
}
<|endoftext|>


<commit_before>package config

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"

	"github.com/containernetworking/cni/pkg/types"
)

// OVNNetConf is the Go structure representing configuration for the
// ovn-kubernetes CNI plugin
type OVNNetConf struct {
	types.NetConf

	ConfigFilePath string `json:"configFilePath,omitempty"`
}

// WriteCNIConfig writes a CNI JSON config file to directory given by global config
func WriteCNIConfig(configFilePath string) error {
	bytes, err := json.Marshal(&OVNNetConf{
		NetConf: types.NetConf{
			CNIVersion: "0.3.1",
			Name:       "ovn-kubernetes",
			Type:       CNI.Plugin,
		},
		ConfigFilePath: configFilePath,
	})
	if err != nil {
		return fmt.Errorf("failed to marshal CNI config JSON: %v", err)
	}

	// Install the CNI config file after all initialization is done
	// MkdirAll() returns no error if the path already exists
	err = os.MkdirAll(CNI.ConfDir, os.ModeDir)
	if err != nil {
		return err
	}

	// Always create the CNI config for consistency.
	confFile := filepath.Join(CNI.ConfDir, "10-ovn-kubernetes.conf")

	var f *os.File
	f, err = os.OpenFile(confFile, os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		return err
	}
	defer f.Close()

	_, err = f.Write(bytes)
	return err
}

// ReadCNIConfig unmarshals a CNI JSON config into an OVNNetConf structure
func ReadCNIConfig(bytes []byte) (*OVNNetConf, error) {
	conf := &OVNNetConf{}
	if err := json.Unmarshal(bytes, conf); err != nil {
		return nil, err
	}
	return conf, nil
}
<commit_msg>config: Use temporary file and rename to write CNI config atomically<commit_after>package config

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/containernetworking/cni/pkg/types"
)

// OVNNetConf is the Go structure representing configuration for the
// ovn-kubernetes CNI plugin
type OVNNetConf struct {
	types.NetConf

	ConfigFilePath string `json:"configFilePath,omitempty"`
}

// WriteCNIConfig writes a CNI JSON config file to directory given by global config
func WriteCNIConfig(configFilePath string) error {
	bytes, err := json.Marshal(&OVNNetConf{
		NetConf: types.NetConf{
			CNIVersion: "0.3.1",
			Name:       "ovn-kubernetes",
			Type:       CNI.Plugin,
		},
		ConfigFilePath: configFilePath,
	})
	if err != nil {
		return fmt.Errorf("failed to marshal CNI config JSON: %v", err)
	}

	// Install the CNI config file after all initialization is done
	// MkdirAll() returns no error if the path already exists
	err = os.MkdirAll(CNI.ConfDir, os.ModeDir)
	if err != nil {
		return err
	}

	// Always create the CNI config for consistency.
	confFile := filepath.Join(CNI.ConfDir, "10-ovn-kubernetes.conf")

	var f *os.File
	f, err = ioutil.TempFile(CNI.ConfDir, "ovnkube-")
	if err != nil {
		return err
	}

	_, err = f.Write(bytes)
	if err != nil {
		return err
	}
	err = f.Close()
	if err != nil {
		return err
	}

	return os.Rename(f.Name(), confFile)
}

// ReadCNIConfig unmarshals a CNI JSON config into an OVNNetConf structure
func ReadCNIConfig(bytes []byte) (*OVNNetConf, error) {
	conf := &OVNNetConf{}
	if err := json.Unmarshal(bytes, conf); err != nil {
		return nil, err
	}
	return conf, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Vitess 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 mysql

import (
	"bytes"
	"encoding/binary"

	binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata"
	"vitess.io/vitess/go/vt/proto/vtrpc"
	"vitess.io/vitess/go/vt/vterrors"
)

// binlogEvent wraps a raw packet buffer and provides methods to examine it
// by partially implementing BinlogEvent. These methods can be composed
// into flavor-specific event types to pull in common parsing code.
//
// The default v4 header format is:
//                  offset : size
//   +============================+
//   | timestamp         0 : 4    |
//   +----------------------------+
//   | type_code         4 : 1    |
//   +----------------------------+
//   | server_id         5 : 4    |
//   +----------------------------+
//   | event_length      9 : 4    |
//   +----------------------------+
//   | next_position    13 : 4    |
//   +----------------------------+
//   | flags            17 : 2    |
//   +----------------------------+
//   | extra_headers    19 : x-19 |
//   +============================+
//   http://dev.mysql.com/doc/internals/en/event-header-fields.html
type binlogEvent []byte

const (
	semiSyncIndicator    byte = 0xef
	semiSyncAckRequested byte = 0x01
)

// isSemiSyncAckRequested examines the given buffer. If semi sync is enables, then the event is preceded by two bytes,
// the 1st is a semi-sync indicator, the 2nd specifies whether the source expects an ACK for this event
// see https://dev.mysql.com/doc/internals/en/semi-sync-binlog-event.html
func isSemiSyncAckRequested(buf []byte) ([]byte, bool) {
	ackRequested := false
	if buf[0] == semiSyncIndicator {
		ackRequested = (buf[1] == semiSyncAckRequested)
		return buf[2:], ackRequested
	}
	return buf, false
}

// dataBytes returns the event bytes without header prefix and without checksum suffix
func (ev binlogEvent) dataBytes(f BinlogFormat) []byte {
	data := ev.Bytes()[f.HeaderLength:]
	data = data[0 : len(data)-4]
	return data
}

// IsValid implements BinlogEvent.IsValid().
func (ev binlogEvent) IsValid() bool {
	bufLen := len(ev.Bytes())

	// The buffer must be at least 19 bytes to contain a valid header.
	if bufLen < 19 {
		return false
	}

	// It's now safe to use methods that examine header fields.
	// Let's see if the event is right about its own size.
	evLen := ev.Length()
	if evLen < 19 || evLen != uint32(bufLen) {
		return false
	}

	// Everything's there, so we shouldn't have any out-of-bounds issues while
	// reading header fields or constant-offset data fields. We should still check
	// bounds any time we compute an offset based on values in the buffer itself.
	return true
}

// Bytes returns the underlying byte buffer.
func (ev binlogEvent) Bytes() []byte {
	return []byte(ev)
}

// Type returns the type_code field from the header.
func (ev binlogEvent) Type() byte {
	return ev.Bytes()[4]
}

// Flags returns the flags field from the header.
func (ev binlogEvent) Flags() uint16 {
	return binary.LittleEndian.Uint16(ev.Bytes()[17 : 17+2])
}

// Timestamp returns the timestamp field from the header.
func (ev binlogEvent) Timestamp() uint32 {
	return binary.LittleEndian.Uint32(ev.Bytes()[:4])
}

// ServerID returns the server_id field from the header.
func (ev binlogEvent) ServerID() uint32 {
	return binary.LittleEndian.Uint32(ev.Bytes()[5 : 5+4])
}

// Length returns the event_length field from the header.
func (ev binlogEvent) Length() uint32 {
	return binary.LittleEndian.Uint32(ev.Bytes()[9 : 9+4])
}

// nextPosition the nextPosition field from the header
func (ev binlogEvent) NextPosition() uint32 {
	return binary.LittleEndian.Uint32(ev.Bytes()[13 : 13+4])
}

// IsFormatDescription implements BinlogEvent.IsFormatDescription().
func (ev binlogEvent) IsFormatDescription() bool {
	return ev.Type() == eFormatDescriptionEvent
}

// IsQuery implements BinlogEvent.IsQuery().
func (ev binlogEvent) IsQuery() bool {
	return ev.Type() == eQueryEvent
}

// IsRotate implements BinlogEvent.IsRotate().
func (ev binlogEvent) IsRotate() bool {
	return ev.Type() == eRotateEvent
}

// IsXID implements BinlogEvent.IsXID().
func (ev binlogEvent) IsXID() bool {
	return ev.Type() == eXIDEvent
}

// IsStop implements BinlogEvent.IsStop().
func (ev binlogEvent) IsStop() bool {
	return ev.Type() == eStopEvent
}

// IsIntVar implements BinlogEvent.IsIntVar().
func (ev binlogEvent) IsIntVar() bool {
	return ev.Type() == eIntVarEvent
}

// IsRand implements BinlogEvent.IsRand().
func (ev binlogEvent) IsRand() bool {
	return ev.Type() == eRandEvent
}

// IsPreviousGTIDs implements BinlogEvent.IsPreviousGTIDs().
func (ev binlogEvent) IsPreviousGTIDs() bool {
	return ev.Type() == ePreviousGTIDsEvent
}

// IsTableMap implements BinlogEvent.IsTableMap().
func (ev binlogEvent) IsTableMap() bool {
	return ev.Type() == eTableMapEvent
}

// IsWriteRows implements BinlogEvent.IsWriteRows().
// We do not support v0.
func (ev binlogEvent) IsWriteRows() bool {
	return ev.Type() == eWriteRowsEventV1 ||
		ev.Type() == eWriteRowsEventV2
}

// IsUpdateRows implements BinlogEvent.IsUpdateRows().
// We do not support v0.
func (ev binlogEvent) IsUpdateRows() bool {
	return ev.Type() == eUpdateRowsEventV1 ||
		ev.Type() == eUpdateRowsEventV2
}

// IsDeleteRows implements BinlogEvent.IsDeleteRows().
// We do not support v0.
func (ev binlogEvent) IsDeleteRows() bool {
	return ev.Type() == eDeleteRowsEventV1 ||
		ev.Type() == eDeleteRowsEventV2
}

// IsPseudo is always false for a native binlogEvent.
func (ev binlogEvent) IsPseudo() bool {
	return false
}

// IsCompressed returns true if a compressed event is found (binlog_transaction_compression=ON)
func (ev binlogEvent) IsCompressed() bool {
	return ev.Type() == eCompressedEvent
}

// Format implements BinlogEvent.Format().
//
// Expected format (L = total length of event data):
//   # bytes   field
//   2         format version
//   50        server version string, 0-padded but not necessarily 0-terminated
//   4         timestamp (same as timestamp header field)
//   1         header length
//   p         (one byte per packet type) event type header lengths
//             Rest was inferred from reading source code:
//   1         checksum algorithm
//   4         checksum
func (ev binlogEvent) Format() (f BinlogFormat, err error) {
	// FORMAT_DESCRIPTION_EVENT has a fixed header size of 19
	// because we have to read it before we know the header_length.
	data := ev.Bytes()[19:]

	f.FormatVersion = binary.LittleEndian.Uint16(data[:2])
	if f.FormatVersion != 4 {
		return f, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "format version = %d, we only support version 4", f.FormatVersion)
	}
	f.ServerVersion = string(bytes.TrimRight(data[2:2+50], "\x00"))
	f.HeaderLength = data[2+50+4]
	if f.HeaderLength < 19 {
		return f, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "header length = %d, should be >= 19", f.HeaderLength)
	}

	// MySQL/MariaDB 5.6.1+ always adds a 4-byte checksum to the end of a
	// FORMAT_DESCRIPTION_EVENT, regardless of the server setting. The byte
	// immediately before that checksum tells us which checksum algorithm
	// (if any) is used for the rest of the events.
	f.ChecksumAlgorithm = data[len(data)-5]

	f.HeaderSizes = data[2+50+4+1 : len(data)-5]
	return f, nil
}

// Query implements BinlogEvent.Query().
//
// Expected format (L = total length of event data):
//   # bytes   field
//   4         thread_id
//   4         execution time
//   1         length of db_name, not including NULL terminator (X)
//   2         error code
//   2         length of status vars block (Y)
//   Y         status vars block
//   X+1       db_name + NULL terminator
//   L-X-1-Y   SQL statement (no NULL terminator)
func (ev binlogEvent) Query(f BinlogFormat) (query Query, err error) {
	const varsPos = 4 + 4 + 1 + 2 + 2

	data := ev.Bytes()[f.HeaderLength:]

	// length of database name
	dbLen := int(data[4+4])
	// length of status variables block
	varsLen := int(binary.LittleEndian.Uint16(data[4+4+1+2 : 4+4+1+2+2]))

	// position of database name
	dbPos := varsPos + varsLen
	// position of SQL query
	sqlPos := dbPos + dbLen + 1 // +1 for NULL terminator
	if sqlPos > len(data) {
		return query, vterrors.Errorf(vtrpc.Code_INTERNAL, "SQL query position overflows buffer (%v > %v)", sqlPos, len(data))
	}

	// We've checked that the buffer is big enough for sql, so everything before
	// it (db and vars) is in-bounds too.
	query.Database = string(data[dbPos : dbPos+dbLen])
	query.SQL = string(data[sqlPos:])

	// Scan the status vars for ones we care about. This requires us to know the
	// size of every var that comes before the ones we're interested in.
	vars := data[varsPos : varsPos+varsLen]

varsLoop:
	for pos := 0; pos < len(vars); {
		code := vars[pos]
		pos++

		// All codes are optional, but if present they must occur in numerically
		// increasing order (except for 6 which occurs in the place of 2) to allow
		// for backward compatibility.
		switch code {
		case QFlags2Code, QAutoIncrement:
			pos += 4
		case QSQLModeCode:
			pos += 8
		case QCatalog: // Used in MySQL 5.0.0 - 5.0.3
			if pos+1 > len(vars) {
				return query, vterrors.Errorf(vtrpc.Code_INTERNAL, "Q_CATALOG status var overflows buffer (%v + 1 > %v)", pos, len(vars))
			}
			pos += 1 + int(vars[pos]) + 1
		case QCatalogNZCode: // Used in MySQL > 5.0.3 to replace QCatalog
			if pos+1 > len(vars) {
				return query, vterrors.Errorf(vtrpc.Code_INTERNAL, "Q_CATALOG_NZ_CODE status var overflows buffer (%v + 1 > %v)", pos, len(vars))
			}
			pos += 1 + int(vars[pos])
		case QCharsetCode:
			if pos+6 > len(vars) {
				return query, vterrors.Errorf(vtrpc.Code_INTERNAL, "Q_CHARSET_CODE status var overflows buffer (%v + 6 > %v)", pos, len(vars))
			}
			query.Charset = &binlogdatapb.Charset{
				Client: int32(binary.LittleEndian.Uint16(vars[pos : pos+2])),
				Conn:   int32(binary.LittleEndian.Uint16(vars[pos+2 : pos+4])),
				Server: int32(binary.LittleEndian.Uint16(vars[pos+4 : pos+6])),
			}
			pos += 6
		default:
			// If we see something higher than what we're interested in, we can stop.
			break varsLoop
		}
	}

	return query, nil
}

// IntVar implements BinlogEvent.IntVar().
//
// Expected format (L = total length of event data):
//   # bytes   field
//   1         variable ID
//   8         variable value
func (ev binlogEvent) IntVar(f BinlogFormat) (byte, uint64, error) {
	data := ev.Bytes()[f.HeaderLength:]

	typ := data[0]
	if typ != IntVarLastInsertID && typ != IntVarInsertID {
		return 0, 0, vterrors.Errorf(vtrpc.Code_INTERNAL, "invalid IntVar ID: %v", data[0])
	}

	value := binary.LittleEndian.Uint64(data[1 : 1+8])
	return typ, value, nil
}

// Rand implements BinlogEvent.Rand().
//
// Expected format (L = total length of event data):
//   # bytes   field
//   8         seed 1
//   8         seed 2
func (ev binlogEvent) Rand(f BinlogFormat) (seed1 uint64, seed2 uint64, err error) {
	data := ev.Bytes()[f.HeaderLength:]
	seed1 = binary.LittleEndian.Uint64(data[0:8])
	seed2 = binary.LittleEndian.Uint64(data[8 : 8+8])
	return seed1, seed2, nil
}

func (ev binlogEvent) TableID(f BinlogFormat) uint64 {
	typ := ev.Type()
	pos := f.HeaderLength
	if f.HeaderSize(typ) == 6 {
		// Encoded in 4 bytes.
		return uint64(binary.LittleEndian.Uint32(ev[pos : pos+4]))
	}

	// Encoded in 6 bytes.
	return uint64(ev[pos]) |
		uint64(ev[pos+1])<<8 |
		uint64(ev[pos+2])<<16 |
		uint64(ev[pos+3])<<24 |
		uint64(ev[pos+4])<<32 |
		uint64(ev[pos+5])<<40
}

func (ev binlogEvent) NextLogFile(f BinlogFormat) (string, uint64, error) {
	data := ev.dataBytes(f)
	pos := 0
	logPos, pos, _ := readUint64(data, pos)
	logFile := string(data[pos:])
	return logFile, logPos, nil
}
<commit_msg>simplify code<commit_after>/*
Copyright 2019 The Vitess 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 mysql

import (
	"bytes"
	"encoding/binary"

	binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata"
	"vitess.io/vitess/go/vt/proto/vtrpc"
	"vitess.io/vitess/go/vt/vterrors"
)

// binlogEvent wraps a raw packet buffer and provides methods to examine it
// by partially implementing BinlogEvent. These methods can be composed
// into flavor-specific event types to pull in common parsing code.
//
// The default v4 header format is:
//                  offset : size
//   +============================+
//   | timestamp         0 : 4    |
//   +----------------------------+
//   | type_code         4 : 1    |
//   +----------------------------+
//   | server_id         5 : 4    |
//   +----------------------------+
//   | event_length      9 : 4    |
//   +----------------------------+
//   | next_position    13 : 4    |
//   +----------------------------+
//   | flags            17 : 2    |
//   +----------------------------+
//   | extra_headers    19 : x-19 |
//   +============================+
//   http://dev.mysql.com/doc/internals/en/event-header-fields.html
type binlogEvent []byte

const (
	semiSyncIndicator    byte = 0xef
	semiSyncAckRequested byte = 0x01
)

// isSemiSyncAckRequested examines the given buffer. If semi sync is enables, then the event is preceded by two bytes,
// the 1st is a semi-sync indicator, the 2nd specifies whether the source expects an ACK for this event
// see https://dev.mysql.com/doc/internals/en/semi-sync-binlog-event.html
func isSemiSyncAckRequested(buf []byte) ([]byte, bool) {
	if len(buf) < 2 {
		return buf, false
	}
	if buf[0] == semiSyncIndicator {
		ackRequested := (buf[1] == semiSyncAckRequested)
		return buf[2:], ackRequested
	}
	return buf, false
}

// dataBytes returns the event bytes without header prefix and without checksum suffix
func (ev binlogEvent) dataBytes(f BinlogFormat) []byte {
	data := ev.Bytes()[f.HeaderLength:]
	data = data[0 : len(data)-4]
	return data
}

// IsValid implements BinlogEvent.IsValid().
func (ev binlogEvent) IsValid() bool {
	bufLen := len(ev.Bytes())

	// The buffer must be at least 19 bytes to contain a valid header.
	if bufLen < 19 {
		return false
	}

	// It's now safe to use methods that examine header fields.
	// Let's see if the event is right about its own size.
	evLen := ev.Length()
	if evLen < 19 || evLen != uint32(bufLen) {
		return false
	}

	// Everything's there, so we shouldn't have any out-of-bounds issues while
	// reading header fields or constant-offset data fields. We should still check
	// bounds any time we compute an offset based on values in the buffer itself.
	return true
}

// Bytes returns the underlying byte buffer.
func (ev binlogEvent) Bytes() []byte {
	return []byte(ev)
}

// Type returns the type_code field from the header.
func (ev binlogEvent) Type() byte {
	return ev.Bytes()[4]
}

// Flags returns the flags field from the header.
func (ev binlogEvent) Flags() uint16 {
	return binary.LittleEndian.Uint16(ev.Bytes()[17 : 17+2])
}

// Timestamp returns the timestamp field from the header.
func (ev binlogEvent) Timestamp() uint32 {
	return binary.LittleEndian.Uint32(ev.Bytes()[:4])
}

// ServerID returns the server_id field from the header.
func (ev binlogEvent) ServerID() uint32 {
	return binary.LittleEndian.Uint32(ev.Bytes()[5 : 5+4])
}

// Length returns the event_length field from the header.
func (ev binlogEvent) Length() uint32 {
	return binary.LittleEndian.Uint32(ev.Bytes()[9 : 9+4])
}

// nextPosition the nextPosition field from the header
func (ev binlogEvent) NextPosition() uint32 {
	return binary.LittleEndian.Uint32(ev.Bytes()[13 : 13+4])
}

// IsFormatDescription implements BinlogEvent.IsFormatDescription().
func (ev binlogEvent) IsFormatDescription() bool {
	return ev.Type() == eFormatDescriptionEvent
}

// IsQuery implements BinlogEvent.IsQuery().
func (ev binlogEvent) IsQuery() bool {
	return ev.Type() == eQueryEvent
}

// IsRotate implements BinlogEvent.IsRotate().
func (ev binlogEvent) IsRotate() bool {
	return ev.Type() == eRotateEvent
}

// IsXID implements BinlogEvent.IsXID().
func (ev binlogEvent) IsXID() bool {
	return ev.Type() == eXIDEvent
}

// IsStop implements BinlogEvent.IsStop().
func (ev binlogEvent) IsStop() bool {
	return ev.Type() == eStopEvent
}

// IsIntVar implements BinlogEvent.IsIntVar().
func (ev binlogEvent) IsIntVar() bool {
	return ev.Type() == eIntVarEvent
}

// IsRand implements BinlogEvent.IsRand().
func (ev binlogEvent) IsRand() bool {
	return ev.Type() == eRandEvent
}

// IsPreviousGTIDs implements BinlogEvent.IsPreviousGTIDs().
func (ev binlogEvent) IsPreviousGTIDs() bool {
	return ev.Type() == ePreviousGTIDsEvent
}

// IsTableMap implements BinlogEvent.IsTableMap().
func (ev binlogEvent) IsTableMap() bool {
	return ev.Type() == eTableMapEvent
}

// IsWriteRows implements BinlogEvent.IsWriteRows().
// We do not support v0.
func (ev binlogEvent) IsWriteRows() bool {
	return ev.Type() == eWriteRowsEventV1 ||
		ev.Type() == eWriteRowsEventV2
}

// IsUpdateRows implements BinlogEvent.IsUpdateRows().
// We do not support v0.
func (ev binlogEvent) IsUpdateRows() bool {
	return ev.Type() == eUpdateRowsEventV1 ||
		ev.Type() == eUpdateRowsEventV2
}

// IsDeleteRows implements BinlogEvent.IsDeleteRows().
// We do not support v0.
func (ev binlogEvent) IsDeleteRows() bool {
	return ev.Type() == eDeleteRowsEventV1 ||
		ev.Type() == eDeleteRowsEventV2
}

// IsPseudo is always false for a native binlogEvent.
func (ev binlogEvent) IsPseudo() bool {
	return false
}

// IsCompressed returns true if a compressed event is found (binlog_transaction_compression=ON)
func (ev binlogEvent) IsCompressed() bool {
	return ev.Type() == eCompressedEvent
}

// Format implements BinlogEvent.Format().
//
// Expected format (L = total length of event data):
//   # bytes   field
//   2         format version
//   50        server version string, 0-padded but not necessarily 0-terminated
//   4         timestamp (same as timestamp header field)
//   1         header length
//   p         (one byte per packet type) event type header lengths
//             Rest was inferred from reading source code:
//   1         checksum algorithm
//   4         checksum
func (ev binlogEvent) Format() (f BinlogFormat, err error) {
	// FORMAT_DESCRIPTION_EVENT has a fixed header size of 19
	// because we have to read it before we know the header_length.
	data := ev.Bytes()[19:]

	f.FormatVersion = binary.LittleEndian.Uint16(data[:2])
	if f.FormatVersion != 4 {
		return f, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "format version = %d, we only support version 4", f.FormatVersion)
	}
	f.ServerVersion = string(bytes.TrimRight(data[2:2+50], "\x00"))
	f.HeaderLength = data[2+50+4]
	if f.HeaderLength < 19 {
		return f, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "header length = %d, should be >= 19", f.HeaderLength)
	}

	// MySQL/MariaDB 5.6.1+ always adds a 4-byte checksum to the end of a
	// FORMAT_DESCRIPTION_EVENT, regardless of the server setting. The byte
	// immediately before that checksum tells us which checksum algorithm
	// (if any) is used for the rest of the events.
	f.ChecksumAlgorithm = data[len(data)-5]

	f.HeaderSizes = data[2+50+4+1 : len(data)-5]
	return f, nil
}

// Query implements BinlogEvent.Query().
//
// Expected format (L = total length of event data):
//   # bytes   field
//   4         thread_id
//   4         execution time
//   1         length of db_name, not including NULL terminator (X)
//   2         error code
//   2         length of status vars block (Y)
//   Y         status vars block
//   X+1       db_name + NULL terminator
//   L-X-1-Y   SQL statement (no NULL terminator)
func (ev binlogEvent) Query(f BinlogFormat) (query Query, err error) {
	const varsPos = 4 + 4 + 1 + 2 + 2

	data := ev.Bytes()[f.HeaderLength:]

	// length of database name
	dbLen := int(data[4+4])
	// length of status variables block
	varsLen := int(binary.LittleEndian.Uint16(data[4+4+1+2 : 4+4+1+2+2]))

	// position of database name
	dbPos := varsPos + varsLen
	// position of SQL query
	sqlPos := dbPos + dbLen + 1 // +1 for NULL terminator
	if sqlPos > len(data) {
		return query, vterrors.Errorf(vtrpc.Code_INTERNAL, "SQL query position overflows buffer (%v > %v)", sqlPos, len(data))
	}

	// We've checked that the buffer is big enough for sql, so everything before
	// it (db and vars) is in-bounds too.
	query.Database = string(data[dbPos : dbPos+dbLen])
	query.SQL = string(data[sqlPos:])

	// Scan the status vars for ones we care about. This requires us to know the
	// size of every var that comes before the ones we're interested in.
	vars := data[varsPos : varsPos+varsLen]

varsLoop:
	for pos := 0; pos < len(vars); {
		code := vars[pos]
		pos++

		// All codes are optional, but if present they must occur in numerically
		// increasing order (except for 6 which occurs in the place of 2) to allow
		// for backward compatibility.
		switch code {
		case QFlags2Code, QAutoIncrement:
			pos += 4
		case QSQLModeCode:
			pos += 8
		case QCatalog: // Used in MySQL 5.0.0 - 5.0.3
			if pos+1 > len(vars) {
				return query, vterrors.Errorf(vtrpc.Code_INTERNAL, "Q_CATALOG status var overflows buffer (%v + 1 > %v)", pos, len(vars))
			}
			pos += 1 + int(vars[pos]) + 1
		case QCatalogNZCode: // Used in MySQL > 5.0.3 to replace QCatalog
			if pos+1 > len(vars) {
				return query, vterrors.Errorf(vtrpc.Code_INTERNAL, "Q_CATALOG_NZ_CODE status var overflows buffer (%v + 1 > %v)", pos, len(vars))
			}
			pos += 1 + int(vars[pos])
		case QCharsetCode:
			if pos+6 > len(vars) {
				return query, vterrors.Errorf(vtrpc.Code_INTERNAL, "Q_CHARSET_CODE status var overflows buffer (%v + 6 > %v)", pos, len(vars))
			}
			query.Charset = &binlogdatapb.Charset{
				Client: int32(binary.LittleEndian.Uint16(vars[pos : pos+2])),
				Conn:   int32(binary.LittleEndian.Uint16(vars[pos+2 : pos+4])),
				Server: int32(binary.LittleEndian.Uint16(vars[pos+4 : pos+6])),
			}
			pos += 6
		default:
			// If we see something higher than what we're interested in, we can stop.
			break varsLoop
		}
	}

	return query, nil
}

// IntVar implements BinlogEvent.IntVar().
//
// Expected format (L = total length of event data):
//   # bytes   field
//   1         variable ID
//   8         variable value
func (ev binlogEvent) IntVar(f BinlogFormat) (byte, uint64, error) {
	data := ev.Bytes()[f.HeaderLength:]

	typ := data[0]
	if typ != IntVarLastInsertID && typ != IntVarInsertID {
		return 0, 0, vterrors.Errorf(vtrpc.Code_INTERNAL, "invalid IntVar ID: %v", data[0])
	}

	value := binary.LittleEndian.Uint64(data[1 : 1+8])
	return typ, value, nil
}

// Rand implements BinlogEvent.Rand().
//
// Expected format (L = total length of event data):
//   # bytes   field
//   8         seed 1
//   8         seed 2
func (ev binlogEvent) Rand(f BinlogFormat) (seed1 uint64, seed2 uint64, err error) {
	data := ev.Bytes()[f.HeaderLength:]
	seed1 = binary.LittleEndian.Uint64(data[0:8])
	seed2 = binary.LittleEndian.Uint64(data[8 : 8+8])
	return seed1, seed2, nil
}

func (ev binlogEvent) TableID(f BinlogFormat) uint64 {
	typ := ev.Type()
	pos := f.HeaderLength
	if f.HeaderSize(typ) == 6 {
		// Encoded in 4 bytes.
		return uint64(binary.LittleEndian.Uint32(ev[pos : pos+4]))
	}

	// Encoded in 6 bytes.
	return uint64(ev[pos]) |
		uint64(ev[pos+1])<<8 |
		uint64(ev[pos+2])<<16 |
		uint64(ev[pos+3])<<24 |
		uint64(ev[pos+4])<<32 |
		uint64(ev[pos+5])<<40
}

func (ev binlogEvent) NextLogFile(f BinlogFormat) (string, uint64, error) {
	data := ev.dataBytes(f)
	pos := 0
	logPos, pos, _ := readUint64(data, pos)
	logFile := string(data[pos:])
	return logFile, logPos, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2016 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 network

import (
	"context"
	"fmt"
	"strings"
	"time"

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/apimachinery/pkg/util/wait"
	clientset "k8s.io/client-go/kubernetes"
	cloudprovider "k8s.io/cloud-provider"
	"k8s.io/kubernetes/pkg/cluster/ports"
	kubeschedulerconfig "k8s.io/kubernetes/pkg/scheduler/apis/config"
	"k8s.io/kubernetes/test/e2e/framework"
	e2enetwork "k8s.io/kubernetes/test/e2e/framework/network"
	e2enode "k8s.io/kubernetes/test/e2e/framework/node"
	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
	"k8s.io/kubernetes/test/e2e/framework/providers/gce"
	e2eservice "k8s.io/kubernetes/test/e2e/framework/service"
	e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
	"k8s.io/kubernetes/test/e2e/network/common"
	gcecloud "k8s.io/legacy-cloud-providers/gce"

	"github.com/onsi/ginkgo"
)

const (
	firewallTestTCPTimeout = time.Duration(1 * time.Second)
	// Set ports outside of 30000-32767, 80 and 8080 to avoid being whitelisted by the e2e cluster
	firewallTestHTTPPort = int32(29999)
	firewallTestUDPPort  = int32(29998)
)

var _ = common.SIGDescribe("Firewall rule", func() {
	var firewallTestName = "firewall-test"
	f := framework.NewDefaultFramework(firewallTestName)

	var cs clientset.Interface
	var cloudConfig framework.CloudConfig
	var gceCloud *gcecloud.Cloud

	ginkgo.BeforeEach(func() {
		e2eskipper.SkipUnlessProviderIs("gce")

		var err error
		cs = f.ClientSet
		cloudConfig = framework.TestContext.CloudConfig
		gceCloud, err = gce.GetGCECloud()
		framework.ExpectNoError(err)
	})

	// This test takes around 6 minutes to run
	ginkgo.It("[Slow] [Serial] should create valid firewall rules for LoadBalancer type service", func() {
		ns := f.Namespace.Name
		// This source ranges is just used to examine we have exact same things on LB firewall rules
		firewallTestSourceRanges := []string{"0.0.0.0/1", "128.0.0.0/1"}
		serviceName := "firewall-test-loadbalancer"

		ginkgo.By("Getting cluster ID")
		clusterID, err := gce.GetClusterID(cs)
		framework.ExpectNoError(err)
		framework.Logf("Got cluster ID: %v", clusterID)

		jig := e2eservice.NewTestJig(cs, ns, serviceName)
		nodeList, err := e2enode.GetBoundedReadySchedulableNodes(cs, e2eservice.MaxNodesForEndpointsTests)
		framework.ExpectNoError(err)

		nodesNames := []string{}
		for _, node := range nodeList.Items {
			nodesNames = append(nodesNames, node.Name)
		}
		nodesSet := sets.NewString(nodesNames...)

		ginkgo.By("Creating a LoadBalancer type service with ExternalTrafficPolicy=Global")
		svc, err := jig.CreateLoadBalancerService(e2eservice.GetServiceLoadBalancerCreationTimeout(cs), func(svc *v1.Service) {
			svc.Spec.Ports = []v1.ServicePort{{Protocol: v1.ProtocolTCP, Port: firewallTestHTTPPort}}
			svc.Spec.LoadBalancerSourceRanges = firewallTestSourceRanges
		})
		framework.ExpectNoError(err)
		defer func() {
			_, err = jig.UpdateService(func(svc *v1.Service) {
				svc.Spec.Type = v1.ServiceTypeNodePort
				svc.Spec.LoadBalancerSourceRanges = nil
			})
			framework.ExpectNoError(err)
			err = cs.CoreV1().Services(svc.Namespace).Delete(context.TODO(), svc.Name, metav1.DeleteOptions{})
			framework.ExpectNoError(err)
			ginkgo.By("Waiting for the local traffic health check firewall rule to be deleted")
			localHCFwName := gce.MakeHealthCheckFirewallNameForLBService(clusterID, cloudprovider.DefaultLoadBalancerName(svc), false)
			_, err := gce.WaitForFirewallRule(gceCloud, localHCFwName, false, e2eservice.LoadBalancerCleanupTimeout)
			framework.ExpectNoError(err)
		}()
		svcExternalIP := svc.Status.LoadBalancer.Ingress[0].IP

		ginkgo.By("Checking if service's firewall rule is correct")
		lbFw := gce.ConstructFirewallForLBService(svc, cloudConfig.NodeTag)
		fw, err := gceCloud.GetFirewall(lbFw.Name)
		framework.ExpectNoError(err)
		err = gce.VerifyFirewallRule(fw, lbFw, cloudConfig.Network, false)
		framework.ExpectNoError(err)

		ginkgo.By("Checking if service's nodes health check firewall rule is correct")
		nodesHCFw := gce.ConstructHealthCheckFirewallForLBService(clusterID, svc, cloudConfig.NodeTag, true)
		fw, err = gceCloud.GetFirewall(nodesHCFw.Name)
		framework.ExpectNoError(err)
		err = gce.VerifyFirewallRule(fw, nodesHCFw, cloudConfig.Network, false)
		framework.ExpectNoError(err)

		// OnlyLocal service is needed to examine which exact nodes the requests are being forwarded to by the Load Balancer on GCE
		ginkgo.By("Updating LoadBalancer service to ExternalTrafficPolicy=Local")
		svc, err = jig.UpdateService(func(svc *v1.Service) {
			svc.Spec.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyTypeLocal
		})
		framework.ExpectNoError(err)

		ginkgo.By("Waiting for the nodes health check firewall rule to be deleted")
		_, err = gce.WaitForFirewallRule(gceCloud, nodesHCFw.Name, false, e2eservice.LoadBalancerCleanupTimeout)
		framework.ExpectNoError(err)

		ginkgo.By("Waiting for the correct local traffic health check firewall rule to be created")
		localHCFw := gce.ConstructHealthCheckFirewallForLBService(clusterID, svc, cloudConfig.NodeTag, false)
		fw, err = gce.WaitForFirewallRule(gceCloud, localHCFw.Name, true, e2eservice.GetServiceLoadBalancerCreationTimeout(cs))
		framework.ExpectNoError(err)
		err = gce.VerifyFirewallRule(fw, localHCFw, cloudConfig.Network, false)
		framework.ExpectNoError(err)

		ginkgo.By(fmt.Sprintf("Creating netexec pods on at most %v nodes", e2eservice.MaxNodesForEndpointsTests))
		for i, nodeName := range nodesNames {
			podName := fmt.Sprintf("netexec%v", i)

			framework.Logf("Creating netexec pod %q on node %v in namespace %q", podName, nodeName, ns)
			pod := e2epod.NewAgnhostPod(ns, podName, nil, nil, nil,
				"netexec",
				fmt.Sprintf("--http-port=%d", firewallTestHTTPPort),
				fmt.Sprintf("--udp-port=%d", firewallTestUDPPort))
			pod.ObjectMeta.Labels = jig.Labels
			pod.Spec.NodeName = nodeName
			pod.Spec.HostNetwork = true
			_, err := cs.CoreV1().Pods(ns).Create(context.TODO(), pod, metav1.CreateOptions{})
			framework.ExpectNoError(err)
			framework.ExpectNoError(e2epod.WaitTimeoutForPodReadyInNamespace(f.ClientSet, podName, f.Namespace.Name, framework.PodStartTimeout))
			framework.Logf("Netexec pod %q in namespace %q running", podName, ns)

			defer func() {
				framework.Logf("Cleaning up the netexec pod: %v", podName)
				err = cs.CoreV1().Pods(ns).Delete(context.TODO(), podName, metav1.DeleteOptions{})
				framework.ExpectNoError(err)
			}()
		}

		// Send requests from outside of the cluster because internal traffic is whitelisted
		ginkgo.By("Accessing the external service ip from outside, all non-master nodes should be reached")
		err = testHitNodesFromOutside(svcExternalIP, firewallTestHTTPPort, e2eservice.GetServiceLoadBalancerPropagationTimeout(cs), nodesSet)
		framework.ExpectNoError(err)

		// Check if there are overlapping tags on the firewall that extend beyond just the vms in our cluster
		// by removing the tag on one vm and make sure it doesn't get any traffic. This is an imperfect
		// simulation, we really want to check that traffic doesn't reach a vm outside the GKE cluster, but
		// that's much harder to do in the current e2e framework.
		ginkgo.By(fmt.Sprintf("Removing tags from one of the nodes: %v", nodesNames[0]))
		nodesSet.Delete(nodesNames[0])
		// Instance could run in a different zone in multi-zone test. Figure out which zone
		// it is in before proceeding.
		zone := cloudConfig.Zone
		if zoneInLabel, ok := nodeList.Items[0].Labels[v1.LabelFailureDomainBetaZone]; ok {
			zone = zoneInLabel
		} else if zoneInLabel, ok := nodeList.Items[0].Labels[v1.LabelTopologyZone]; ok {
			zone = zoneInLabel
		}
		removedTags := gce.SetInstanceTags(cloudConfig, nodesNames[0], zone, []string{})
		defer func() {
			ginkgo.By("Adding tags back to the node and wait till the traffic is recovered")
			nodesSet.Insert(nodesNames[0])
			gce.SetInstanceTags(cloudConfig, nodesNames[0], zone, removedTags)
			// Make sure traffic is recovered before exit
			err = testHitNodesFromOutside(svcExternalIP, firewallTestHTTPPort, e2eservice.GetServiceLoadBalancerPropagationTimeout(cs), nodesSet)
			framework.ExpectNoError(err)
		}()

		ginkgo.By("Accessing serivce through the external ip and examine got no response from the node without tags")
		err = testHitNodesFromOutsideWithCount(svcExternalIP, firewallTestHTTPPort, e2eservice.GetServiceLoadBalancerPropagationTimeout(cs), nodesSet, 15)
		framework.ExpectNoError(err)
	})

	ginkgo.It("should have correct firewall rules for e2e cluster", func() {
		ginkgo.By("Checking if e2e firewall rules are correct")
		for _, expFw := range gce.GetE2eFirewalls(cloudConfig.MasterName, cloudConfig.MasterTag, cloudConfig.NodeTag, cloudConfig.Network, cloudConfig.ClusterIPRange) {
			fw, err := gceCloud.GetFirewall(expFw.Name)
			framework.ExpectNoError(err)
			err = gce.VerifyFirewallRule(fw, expFw, cloudConfig.Network, false)
			framework.ExpectNoError(err)
		}
	})

	ginkgo.It("control plane should not expose well-known ports", func() {
		nodes, err := e2enode.GetReadySchedulableNodes(cs)
		framework.ExpectNoError(err)

		ginkgo.By("Checking well known ports on master and nodes are not exposed externally")
		nodeAddr := e2enode.FirstAddress(nodes, v1.NodeExternalIP)
		if nodeAddr == "" {
			framework.Failf("did not find any node addresses")
		}

		controlPlaneAddresses := framework.GetControlPlaneAddresses(cs)
		for _, instanceAddress := range controlPlaneAddresses {
			assertNotReachableHTTPTimeout(instanceAddress, "/healthz", ports.KubeControllerManagerPort, firewallTestTCPTimeout, true)
			assertNotReachableHTTPTimeout(instanceAddress, "/healthz", kubeschedulerconfig.DefaultKubeSchedulerPort, firewallTestTCPTimeout, true)
		}
		assertNotReachableHTTPTimeout(nodeAddr, "/", ports.KubeletPort, firewallTestTCPTimeout, false)
		assertNotReachableHTTPTimeout(nodeAddr, "/", ports.KubeletReadOnlyPort, firewallTestTCPTimeout, false)
		assertNotReachableHTTPTimeout(nodeAddr, "/", ports.ProxyStatusPort, firewallTestTCPTimeout, false)
	})
})

func assertNotReachableHTTPTimeout(ip, path string, port int, timeout time.Duration, enableHTTPS bool) {
	result := e2enetwork.PokeHTTP(ip, port, path, &e2enetwork.HTTPPokeParams{Timeout: timeout, EnableHTTPS: enableHTTPS})
	if result.Status == e2enetwork.HTTPError {
		framework.Failf("Unexpected error checking for reachability of %s:%d: %v", ip, port, result.Error)
	}
	if result.Code != 0 {
		framework.Failf("Was unexpectedly able to reach %s:%d", ip, port)
	}
}

// testHitNodesFromOutside checkes HTTP connectivity from outside.
func testHitNodesFromOutside(externalIP string, httpPort int32, timeout time.Duration, expectedHosts sets.String) error {
	return testHitNodesFromOutsideWithCount(externalIP, httpPort, timeout, expectedHosts, 1)
}

// testHitNodesFromOutsideWithCount checkes HTTP connectivity from outside with count.
func testHitNodesFromOutsideWithCount(externalIP string, httpPort int32, timeout time.Duration, expectedHosts sets.String,
	countToSucceed int) error {
	framework.Logf("Waiting up to %v for satisfying expectedHosts for %v times", timeout, countToSucceed)
	hittedHosts := sets.NewString()
	count := 0
	condition := func() (bool, error) {
		result := e2enetwork.PokeHTTP(externalIP, int(httpPort), "/hostname", &e2enetwork.HTTPPokeParams{Timeout: 1 * time.Second})
		if result.Status != e2enetwork.HTTPSuccess {
			return false, nil
		}

		hittedHost := strings.TrimSpace(string(result.Body))
		if !expectedHosts.Has(hittedHost) {
			framework.Logf("Error hitting unexpected host: %v, reset counter: %v", hittedHost, count)
			count = 0
			return false, nil
		}
		if !hittedHosts.Has(hittedHost) {
			hittedHosts.Insert(hittedHost)
			framework.Logf("Missing %+v, got %+v", expectedHosts.Difference(hittedHosts), hittedHosts)
		}
		if hittedHosts.Equal(expectedHosts) {
			count++
			if count >= countToSucceed {
				return true, nil
			}
		}
		return false, nil
	}

	if err := wait.Poll(time.Second, timeout, condition); err != nil {
		return fmt.Errorf("error waiting for expectedHosts: %v, hittedHosts: %v, count: %v, expected count: %v",
			expectedHosts, hittedHosts, count, countToSucceed)
	}
	return nil
}
<commit_msg>UPSTREAM: 101488: e2e/network/firewall: don't assume nodes are exposed externally<commit_after>/*
Copyright 2016 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 network

import (
	"context"
	"fmt"
	"strings"
	"time"

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/apimachinery/pkg/util/wait"
	clientset "k8s.io/client-go/kubernetes"
	cloudprovider "k8s.io/cloud-provider"
	"k8s.io/kubernetes/pkg/cluster/ports"
	kubeschedulerconfig "k8s.io/kubernetes/pkg/scheduler/apis/config"
	"k8s.io/kubernetes/test/e2e/framework"
	e2enetwork "k8s.io/kubernetes/test/e2e/framework/network"
	e2enode "k8s.io/kubernetes/test/e2e/framework/node"
	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
	"k8s.io/kubernetes/test/e2e/framework/providers/gce"
	e2eservice "k8s.io/kubernetes/test/e2e/framework/service"
	e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
	"k8s.io/kubernetes/test/e2e/network/common"
	gcecloud "k8s.io/legacy-cloud-providers/gce"

	"github.com/onsi/ginkgo"
)

const (
	firewallTestTCPTimeout = time.Duration(1 * time.Second)
	// Set ports outside of 30000-32767, 80 and 8080 to avoid being whitelisted by the e2e cluster
	firewallTestHTTPPort = int32(29999)
	firewallTestUDPPort  = int32(29998)
)

var _ = common.SIGDescribe("Firewall rule", func() {
	var firewallTestName = "firewall-test"
	f := framework.NewDefaultFramework(firewallTestName)

	var cs clientset.Interface
	var cloudConfig framework.CloudConfig
	var gceCloud *gcecloud.Cloud

	ginkgo.BeforeEach(func() {
		e2eskipper.SkipUnlessProviderIs("gce")

		var err error
		cs = f.ClientSet
		cloudConfig = framework.TestContext.CloudConfig
		gceCloud, err = gce.GetGCECloud()
		framework.ExpectNoError(err)
	})

	// This test takes around 6 minutes to run
	ginkgo.It("[Slow] [Serial] should create valid firewall rules for LoadBalancer type service", func() {
		ns := f.Namespace.Name
		// This source ranges is just used to examine we have exact same things on LB firewall rules
		firewallTestSourceRanges := []string{"0.0.0.0/1", "128.0.0.0/1"}
		serviceName := "firewall-test-loadbalancer"

		ginkgo.By("Getting cluster ID")
		clusterID, err := gce.GetClusterID(cs)
		framework.ExpectNoError(err)
		framework.Logf("Got cluster ID: %v", clusterID)

		jig := e2eservice.NewTestJig(cs, ns, serviceName)
		nodeList, err := e2enode.GetBoundedReadySchedulableNodes(cs, e2eservice.MaxNodesForEndpointsTests)
		framework.ExpectNoError(err)

		nodesNames := []string{}
		for _, node := range nodeList.Items {
			nodesNames = append(nodesNames, node.Name)
		}
		nodesSet := sets.NewString(nodesNames...)

		ginkgo.By("Creating a LoadBalancer type service with ExternalTrafficPolicy=Global")
		svc, err := jig.CreateLoadBalancerService(e2eservice.GetServiceLoadBalancerCreationTimeout(cs), func(svc *v1.Service) {
			svc.Spec.Ports = []v1.ServicePort{{Protocol: v1.ProtocolTCP, Port: firewallTestHTTPPort}}
			svc.Spec.LoadBalancerSourceRanges = firewallTestSourceRanges
		})
		framework.ExpectNoError(err)
		defer func() {
			_, err = jig.UpdateService(func(svc *v1.Service) {
				svc.Spec.Type = v1.ServiceTypeNodePort
				svc.Spec.LoadBalancerSourceRanges = nil
			})
			framework.ExpectNoError(err)
			err = cs.CoreV1().Services(svc.Namespace).Delete(context.TODO(), svc.Name, metav1.DeleteOptions{})
			framework.ExpectNoError(err)
			ginkgo.By("Waiting for the local traffic health check firewall rule to be deleted")
			localHCFwName := gce.MakeHealthCheckFirewallNameForLBService(clusterID, cloudprovider.DefaultLoadBalancerName(svc), false)
			_, err := gce.WaitForFirewallRule(gceCloud, localHCFwName, false, e2eservice.LoadBalancerCleanupTimeout)
			framework.ExpectNoError(err)
		}()
		svcExternalIP := svc.Status.LoadBalancer.Ingress[0].IP

		ginkgo.By("Checking if service's firewall rule is correct")
		lbFw := gce.ConstructFirewallForLBService(svc, cloudConfig.NodeTag)
		fw, err := gceCloud.GetFirewall(lbFw.Name)
		framework.ExpectNoError(err)
		err = gce.VerifyFirewallRule(fw, lbFw, cloudConfig.Network, false)
		framework.ExpectNoError(err)

		ginkgo.By("Checking if service's nodes health check firewall rule is correct")
		nodesHCFw := gce.ConstructHealthCheckFirewallForLBService(clusterID, svc, cloudConfig.NodeTag, true)
		fw, err = gceCloud.GetFirewall(nodesHCFw.Name)
		framework.ExpectNoError(err)
		err = gce.VerifyFirewallRule(fw, nodesHCFw, cloudConfig.Network, false)
		framework.ExpectNoError(err)

		// OnlyLocal service is needed to examine which exact nodes the requests are being forwarded to by the Load Balancer on GCE
		ginkgo.By("Updating LoadBalancer service to ExternalTrafficPolicy=Local")
		svc, err = jig.UpdateService(func(svc *v1.Service) {
			svc.Spec.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyTypeLocal
		})
		framework.ExpectNoError(err)

		ginkgo.By("Waiting for the nodes health check firewall rule to be deleted")
		_, err = gce.WaitForFirewallRule(gceCloud, nodesHCFw.Name, false, e2eservice.LoadBalancerCleanupTimeout)
		framework.ExpectNoError(err)

		ginkgo.By("Waiting for the correct local traffic health check firewall rule to be created")
		localHCFw := gce.ConstructHealthCheckFirewallForLBService(clusterID, svc, cloudConfig.NodeTag, false)
		fw, err = gce.WaitForFirewallRule(gceCloud, localHCFw.Name, true, e2eservice.GetServiceLoadBalancerCreationTimeout(cs))
		framework.ExpectNoError(err)
		err = gce.VerifyFirewallRule(fw, localHCFw, cloudConfig.Network, false)
		framework.ExpectNoError(err)

		ginkgo.By(fmt.Sprintf("Creating netexec pods on at most %v nodes", e2eservice.MaxNodesForEndpointsTests))
		for i, nodeName := range nodesNames {
			podName := fmt.Sprintf("netexec%v", i)

			framework.Logf("Creating netexec pod %q on node %v in namespace %q", podName, nodeName, ns)
			pod := e2epod.NewAgnhostPod(ns, podName, nil, nil, nil,
				"netexec",
				fmt.Sprintf("--http-port=%d", firewallTestHTTPPort),
				fmt.Sprintf("--udp-port=%d", firewallTestUDPPort))
			pod.ObjectMeta.Labels = jig.Labels
			pod.Spec.NodeName = nodeName
			pod.Spec.HostNetwork = true
			_, err := cs.CoreV1().Pods(ns).Create(context.TODO(), pod, metav1.CreateOptions{})
			framework.ExpectNoError(err)
			framework.ExpectNoError(e2epod.WaitTimeoutForPodReadyInNamespace(f.ClientSet, podName, f.Namespace.Name, framework.PodStartTimeout))
			framework.Logf("Netexec pod %q in namespace %q running", podName, ns)

			defer func() {
				framework.Logf("Cleaning up the netexec pod: %v", podName)
				err = cs.CoreV1().Pods(ns).Delete(context.TODO(), podName, metav1.DeleteOptions{})
				framework.ExpectNoError(err)
			}()
		}

		// Send requests from outside of the cluster because internal traffic is whitelisted
		ginkgo.By("Accessing the external service ip from outside, all non-master nodes should be reached")
		err = testHitNodesFromOutside(svcExternalIP, firewallTestHTTPPort, e2eservice.GetServiceLoadBalancerPropagationTimeout(cs), nodesSet)
		framework.ExpectNoError(err)

		// Check if there are overlapping tags on the firewall that extend beyond just the vms in our cluster
		// by removing the tag on one vm and make sure it doesn't get any traffic. This is an imperfect
		// simulation, we really want to check that traffic doesn't reach a vm outside the GKE cluster, but
		// that's much harder to do in the current e2e framework.
		ginkgo.By(fmt.Sprintf("Removing tags from one of the nodes: %v", nodesNames[0]))
		nodesSet.Delete(nodesNames[0])
		// Instance could run in a different zone in multi-zone test. Figure out which zone
		// it is in before proceeding.
		zone := cloudConfig.Zone
		if zoneInLabel, ok := nodeList.Items[0].Labels[v1.LabelFailureDomainBetaZone]; ok {
			zone = zoneInLabel
		} else if zoneInLabel, ok := nodeList.Items[0].Labels[v1.LabelTopologyZone]; ok {
			zone = zoneInLabel
		}
		removedTags := gce.SetInstanceTags(cloudConfig, nodesNames[0], zone, []string{})
		defer func() {
			ginkgo.By("Adding tags back to the node and wait till the traffic is recovered")
			nodesSet.Insert(nodesNames[0])
			gce.SetInstanceTags(cloudConfig, nodesNames[0], zone, removedTags)
			// Make sure traffic is recovered before exit
			err = testHitNodesFromOutside(svcExternalIP, firewallTestHTTPPort, e2eservice.GetServiceLoadBalancerPropagationTimeout(cs), nodesSet)
			framework.ExpectNoError(err)
		}()

		ginkgo.By("Accessing serivce through the external ip and examine got no response from the node without tags")
		err = testHitNodesFromOutsideWithCount(svcExternalIP, firewallTestHTTPPort, e2eservice.GetServiceLoadBalancerPropagationTimeout(cs), nodesSet, 15)
		framework.ExpectNoError(err)
	})

	ginkgo.It("should have correct firewall rules for e2e cluster", func() {
		ginkgo.By("Checking if e2e firewall rules are correct")
		for _, expFw := range gce.GetE2eFirewalls(cloudConfig.MasterName, cloudConfig.MasterTag, cloudConfig.NodeTag, cloudConfig.Network, cloudConfig.ClusterIPRange) {
			fw, err := gceCloud.GetFirewall(expFw.Name)
			framework.ExpectNoError(err)
			err = gce.VerifyFirewallRule(fw, expFw, cloudConfig.Network, false)
			framework.ExpectNoError(err)
		}
	})

	ginkgo.It("control plane should not expose well-known ports", func() {
		nodes, err := e2enode.GetReadySchedulableNodes(cs)
		framework.ExpectNoError(err)

		ginkgo.By("Checking well known ports on master and nodes are not exposed externally")
		nodeAddr := e2enode.FirstAddress(nodes, v1.NodeExternalIP)
		if nodeAddr != "" {
			assertNotReachableHTTPTimeout(nodeAddr, "/", ports.KubeletPort, firewallTestTCPTimeout, false)
			assertNotReachableHTTPTimeout(nodeAddr, "/", ports.KubeletReadOnlyPort, firewallTestTCPTimeout, false)
			assertNotReachableHTTPTimeout(nodeAddr, "/", ports.ProxyStatusPort, firewallTestTCPTimeout, false)
		}

		controlPlaneAddresses := framework.GetControlPlaneAddresses(cs)
		for _, instanceAddress := range controlPlaneAddresses {
			assertNotReachableHTTPTimeout(instanceAddress, "/healthz", ports.KubeControllerManagerPort, firewallTestTCPTimeout, true)
			assertNotReachableHTTPTimeout(instanceAddress, "/healthz", kubeschedulerconfig.DefaultKubeSchedulerPort, firewallTestTCPTimeout, true)
		}
	})
})

func assertNotReachableHTTPTimeout(ip, path string, port int, timeout time.Duration, enableHTTPS bool) {
	result := e2enetwork.PokeHTTP(ip, port, path, &e2enetwork.HTTPPokeParams{Timeout: timeout, EnableHTTPS: enableHTTPS})
	if result.Status == e2enetwork.HTTPError {
		framework.Failf("Unexpected error checking for reachability of %s:%d: %v", ip, port, result.Error)
	}
	if result.Code != 0 {
		framework.Failf("Was unexpectedly able to reach %s:%d", ip, port)
	}
}

// testHitNodesFromOutside checkes HTTP connectivity from outside.
func testHitNodesFromOutside(externalIP string, httpPort int32, timeout time.Duration, expectedHosts sets.String) error {
	return testHitNodesFromOutsideWithCount(externalIP, httpPort, timeout, expectedHosts, 1)
}

// testHitNodesFromOutsideWithCount checkes HTTP connectivity from outside with count.
func testHitNodesFromOutsideWithCount(externalIP string, httpPort int32, timeout time.Duration, expectedHosts sets.String,
	countToSucceed int) error {
	framework.Logf("Waiting up to %v for satisfying expectedHosts for %v times", timeout, countToSucceed)
	hittedHosts := sets.NewString()
	count := 0
	condition := func() (bool, error) {
		result := e2enetwork.PokeHTTP(externalIP, int(httpPort), "/hostname", &e2enetwork.HTTPPokeParams{Timeout: 1 * time.Second})
		if result.Status != e2enetwork.HTTPSuccess {
			return false, nil
		}

		hittedHost := strings.TrimSpace(string(result.Body))
		if !expectedHosts.Has(hittedHost) {
			framework.Logf("Error hitting unexpected host: %v, reset counter: %v", hittedHost, count)
			count = 0
			return false, nil
		}
		if !hittedHosts.Has(hittedHost) {
			hittedHosts.Insert(hittedHost)
			framework.Logf("Missing %+v, got %+v", expectedHosts.Difference(hittedHosts), hittedHosts)
		}
		if hittedHosts.Equal(expectedHosts) {
			count++
			if count >= countToSucceed {
				return true, nil
			}
		}
		return false, nil
	}

	if err := wait.Poll(time.Second, timeout, condition); err != nil {
		return fmt.Errorf("error waiting for expectedHosts: %v, hittedHosts: %v, count: %v, expected count: %v",
			expectedHosts, hittedHosts, count, countToSucceed)
	}
	return nil
}
<|endoftext|>


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

package vtgate

import (
	"flag"
	"sync"
	"time"

	log "github.com/golang/glog"

	"github.com/youtube/vitess/go/vt/topo"
)

var (
	srvTopoCacheTTL = flag.Duration("-srv_topo_cache_ttl", 1*time.Second, "how long to use cached entries for topology")
)

// SrvTopoServer is a subset of topo.Server that only contains the serving
// graph read-only calls used by clients to resolve serving addresses.
type SrvTopoServer interface {
	GetSrvKeyspaceNames(cell string) ([]string, error)

	GetSrvKeyspace(cell, keyspace string) (*topo.SrvKeyspace, error)

	GetEndPoints(cell, keyspace, shard string, tabletType topo.TabletType) (*topo.EndPoints, error)
}

// ResilientSrvTopoServer is an implementation of SrvTopoServer based
// on another SrvTopoServer that uses a cache for two purposes:
// - limit the QPS to the underlying SrvTopoServer
// - return the last known value of the data if there is an error
type ResilientSrvTopoServer struct {
	Toposerv SrvTopoServer

	// mutex protects the cache
	mu                    sync.Mutex
	svrKeyspaceNamesCache map[string]svrKeyspaceNamesEntry
	srvKeyspaceCache      map[string]svrKeyspaceEntry
	endPointsCache        map[string]endPointsEntry
}

type svrKeyspaceNamesEntry struct {
	insertionTime time.Time
	value         []string
}

type svrKeyspaceEntry struct {
	insertionTime time.Time
	value         *topo.SrvKeyspace
}

type endPointsEntry struct {
	insertionTime time.Time
	value         *topo.EndPoints
}

// NewResilientSrvTopoServer creates a new ResilientSrvTopoServer
// based on the provided SrvTopoServer.
func NewResilientSrvTopoServer(base SrvTopoServer) *ResilientSrvTopoServer {
	return &ResilientSrvTopoServer{
		Toposerv:              base,
		svrKeyspaceNamesCache: make(map[string]svrKeyspaceNamesEntry),
		srvKeyspaceCache:      make(map[string]svrKeyspaceEntry),
		endPointsCache:        make(map[string]endPointsEntry),
	}
}

func (server *ResilientSrvTopoServer) GetSrvKeyspaceNames(cell string) ([]string, error) {
	// try the cache first
	key := cell
	now := time.Now()
	server.mu.Lock()
	entry, ok := server.svrKeyspaceNamesCache[key]
	server.mu.Unlock()
	if ok && now.Sub(entry.insertionTime) < *srvTopoCacheTTL {
		return entry.value, nil
	}

	// not in cache or too old, get the real value
	result, err := server.Toposerv.GetSrvKeyspaceNames(cell)
	if err != nil {
		if ok {
			log.Warningf("GetSrvKeyspaceNames(%v) failed: %v (returning cached value)", cell, err)
			return entry.value, nil
		} else {
			log.Warningf("GetSrvKeyspaceNames(%v) failed: %v (no cached value, returning error)", cell, err)
			return nil, err
		}
	}

	// save the last value in the cache
	server.mu.Lock()
	server.svrKeyspaceNamesCache[key] = svrKeyspaceNamesEntry{
		insertionTime: now,
		value:         result,
	}
	server.mu.Unlock()
	return result, nil
}

func (server *ResilientSrvTopoServer) GetSrvKeyspace(cell, keyspace string) (*topo.SrvKeyspace, error) {
	// try the cache first
	key := cell + ":" + keyspace
	now := time.Now()
	server.mu.Lock()
	entry, ok := server.srvKeyspaceCache[key]
	server.mu.Unlock()
	if ok && now.Sub(entry.insertionTime) < *srvTopoCacheTTL {
		return entry.value, nil
	}

	// not in cache or too old, get the real value
	result, err := server.Toposerv.GetSrvKeyspace(cell, keyspace)
	if err != nil {
		if ok {
			log.Warningf("GetSrvKeyspace(%v, %v) failed: %v (returning cached value)", cell, keyspace, err)
			return entry.value, nil
		} else {
			log.Warningf("GetSrvKeyspace(%v, %v) failed: %v (no cached value, returning error)", cell, keyspace, err)
			return nil, err
		}
	}

	// save the last value in the cache
	server.mu.Lock()
	server.srvKeyspaceCache[key] = svrKeyspaceEntry{
		insertionTime: time.Now(),
		value:         result,
	}
	server.mu.Unlock()
	return result, nil
}

func (server *ResilientSrvTopoServer) GetEndPoints(cell, keyspace, shard string, tabletType topo.TabletType) (*topo.EndPoints, error) {
	// try the cache first
	key := cell + ":" + keyspace + ":" + shard + ":" + string(tabletType)
	now := time.Now()
	server.mu.Lock()
	entry, ok := server.endPointsCache[key]
	server.mu.Unlock()
	if ok && now.Sub(entry.insertionTime) < *srvTopoCacheTTL {
		return entry.value, nil
	}

	// not in cache or too old, get the real value
	result, err := server.Toposerv.GetEndPoints(cell, keyspace, shard, tabletType)
	if err != nil {
		if ok {
			log.Warningf("GetEndPoints(%v, %v%, v, %v) failed: %v (returning cached value)", cell, keyspace, shard, tabletType, err)
			return entry.value, nil
		} else {
			log.Warningf("GetEndPoints(%v, %v, %v, %v) failed: %v (no cached value, returning error)", cell, keyspace, shard, tabletType, err)
			return nil, err
		}
	}

	// save the last value in the cache
	server.mu.Lock()
	server.endPointsCache[key] = endPointsEntry{
		insertionTime: time.Now(),
		value:         result,
	}
	server.mu.Unlock()
	return result, nil
}
<commit_msg>Small fix to use time.Now() all the time.<commit_after>// Copyright 2013, Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package vtgate

import (
	"flag"
	"sync"
	"time"

	log "github.com/golang/glog"

	"github.com/youtube/vitess/go/vt/topo"
)

var (
	srvTopoCacheTTL = flag.Duration("-srv_topo_cache_ttl", 1*time.Second, "how long to use cached entries for topology")
)

// SrvTopoServer is a subset of topo.Server that only contains the serving
// graph read-only calls used by clients to resolve serving addresses.
type SrvTopoServer interface {
	GetSrvKeyspaceNames(cell string) ([]string, error)

	GetSrvKeyspace(cell, keyspace string) (*topo.SrvKeyspace, error)

	GetEndPoints(cell, keyspace, shard string, tabletType topo.TabletType) (*topo.EndPoints, error)
}

// ResilientSrvTopoServer is an implementation of SrvTopoServer based
// on another SrvTopoServer that uses a cache for two purposes:
// - limit the QPS to the underlying SrvTopoServer
// - return the last known value of the data if there is an error
type ResilientSrvTopoServer struct {
	Toposerv SrvTopoServer

	// mutex protects the cache
	mu                    sync.Mutex
	svrKeyspaceNamesCache map[string]svrKeyspaceNamesEntry
	srvKeyspaceCache      map[string]svrKeyspaceEntry
	endPointsCache        map[string]endPointsEntry
}

type svrKeyspaceNamesEntry struct {
	insertionTime time.Time
	value         []string
}

type svrKeyspaceEntry struct {
	insertionTime time.Time
	value         *topo.SrvKeyspace
}

type endPointsEntry struct {
	insertionTime time.Time
	value         *topo.EndPoints
}

// NewResilientSrvTopoServer creates a new ResilientSrvTopoServer
// based on the provided SrvTopoServer.
func NewResilientSrvTopoServer(base SrvTopoServer) *ResilientSrvTopoServer {
	return &ResilientSrvTopoServer{
		Toposerv:              base,
		svrKeyspaceNamesCache: make(map[string]svrKeyspaceNamesEntry),
		srvKeyspaceCache:      make(map[string]svrKeyspaceEntry),
		endPointsCache:        make(map[string]endPointsEntry),
	}
}

func (server *ResilientSrvTopoServer) GetSrvKeyspaceNames(cell string) ([]string, error) {
	// try the cache first
	key := cell
	server.mu.Lock()
	entry, ok := server.svrKeyspaceNamesCache[key]
	server.mu.Unlock()
	if ok && time.Now().Sub(entry.insertionTime) < *srvTopoCacheTTL {
		return entry.value, nil
	}

	// not in cache or too old, get the real value
	result, err := server.Toposerv.GetSrvKeyspaceNames(cell)
	if err != nil {
		if ok {
			log.Warningf("GetSrvKeyspaceNames(%v) failed: %v (returning cached value)", cell, err)
			return entry.value, nil
		} else {
			log.Warningf("GetSrvKeyspaceNames(%v) failed: %v (no cached value, returning error)", cell, err)
			return nil, err
		}
	}

	// save the last value in the cache
	server.mu.Lock()
	server.svrKeyspaceNamesCache[key] = svrKeyspaceNamesEntry{
		insertionTime: time.Now(),
		value:         result,
	}
	server.mu.Unlock()
	return result, nil
}

func (server *ResilientSrvTopoServer) GetSrvKeyspace(cell, keyspace string) (*topo.SrvKeyspace, error) {
	// try the cache first
	key := cell + ":" + keyspace
	server.mu.Lock()
	entry, ok := server.srvKeyspaceCache[key]
	server.mu.Unlock()
	if ok && time.Now().Sub(entry.insertionTime) < *srvTopoCacheTTL {
		return entry.value, nil
	}

	// not in cache or too old, get the real value
	result, err := server.Toposerv.GetSrvKeyspace(cell, keyspace)
	if err != nil {
		if ok {
			log.Warningf("GetSrvKeyspace(%v, %v) failed: %v (returning cached value)", cell, keyspace, err)
			return entry.value, nil
		} else {
			log.Warningf("GetSrvKeyspace(%v, %v) failed: %v (no cached value, returning error)", cell, keyspace, err)
			return nil, err
		}
	}

	// save the last value in the cache
	server.mu.Lock()
	server.srvKeyspaceCache[key] = svrKeyspaceEntry{
		insertionTime: time.Now(),
		value:         result,
	}
	server.mu.Unlock()
	return result, nil
}

func (server *ResilientSrvTopoServer) GetEndPoints(cell, keyspace, shard string, tabletType topo.TabletType) (*topo.EndPoints, error) {
	// try the cache first
	key := cell + ":" + keyspace + ":" + shard + ":" + string(tabletType)
	server.mu.Lock()
	entry, ok := server.endPointsCache[key]
	server.mu.Unlock()
	if ok && time.Now().Sub(entry.insertionTime) < *srvTopoCacheTTL {
		return entry.value, nil
	}

	// not in cache or too old, get the real value
	result, err := server.Toposerv.GetEndPoints(cell, keyspace, shard, tabletType)
	if err != nil {
		if ok {
			log.Warningf("GetEndPoints(%v, %v%, v, %v) failed: %v (returning cached value)", cell, keyspace, shard, tabletType, err)
			return entry.value, nil
		} else {
			log.Warningf("GetEndPoints(%v, %v, %v, %v) failed: %v (no cached value, returning error)", cell, keyspace, shard, tabletType, err)
			return nil, err
		}
	}

	// save the last value in the cache
	server.mu.Lock()
	server.endPointsCache[key] = endPointsEntry{
		insertionTime: time.Now(),
		value:         result,
	}
	server.mu.Unlock()
	return result, nil
}
<|endoftext|>


<commit_before>// errorcheck -0 -race

package foo

const benchmarkNumNodes = 10000

func BenchmarkUpdateNodeTransaction(b B) {
	s, nodeIDs := setupNodes(benchmarkNumNodes)
	b.ResetTimer()
	for i := 0; i < b.N(); i++ {
		_ = s.Update(func(tx1 Tx) error {
			_ = UpdateNode(tx1, &Node{
				ID: nodeIDs[i%benchmarkNumNodes],
			})
			return nil
		})
	}
}

type B interface {
	ResetTimer()
	N() int
}

type Tx interface {
}

type Node struct {
	ID string
}

type MemoryStore struct {
}

// go:noinline
func setupNodes(n int) (s *MemoryStore, nodeIDs []string) {
	return
}

//go:noinline
func (s *MemoryStore) Update(cb func(Tx) error) error {
	return nil
}

var sink interface{}

//go:noinline
func UpdateNode(tx Tx, n *Node) error {
	sink = tx
	sink = n
	return nil
}
<commit_msg>test: add missing copyright notice<commit_after>// errorcheck -0 -race

// Copyright 2016 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 foo

const benchmarkNumNodes = 10000

func BenchmarkUpdateNodeTransaction(b B) {
	s, nodeIDs := setupNodes(benchmarkNumNodes)
	b.ResetTimer()
	for i := 0; i < b.N(); i++ {
		_ = s.Update(func(tx1 Tx) error {
			_ = UpdateNode(tx1, &Node{
				ID: nodeIDs[i%benchmarkNumNodes],
			})
			return nil
		})
	}
}

type B interface {
	ResetTimer()
	N() int
}

type Tx interface {
}

type Node struct {
	ID string
}

type MemoryStore struct {
}

// go:noinline
func setupNodes(n int) (s *MemoryStore, nodeIDs []string) {
	return
}

//go:noinline
func (s *MemoryStore) Update(cb func(Tx) error) error {
	return nil
}

var sink interface{}

//go:noinline
func UpdateNode(tx Tx, n *Node) error {
	sink = tx
	sink = n
	return nil
}
<|endoftext|>


<commit_before>// buildrun -t 30

// +build !js

// Copyright 2021 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.

// Ensure that runtime traceback does not infinite loop for
// the testcase below.

package main

import (
	"bytes"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
)

const prog = `

package main

import "context"

var gpi *int

type nAO struct {
	eE bool
}

type NAO func(*nAO)

func WEA() NAO {
	return func(o *nAO) { o.eE = true }
}

type R struct {
	cM *CM
}

type CM int

type A string

func (m *CM) NewA(ctx context.Context, cN string, nn *nAO, opts ...NAO) (*A, error) {
	for _, o := range opts {
		o(nn)
	}
	s := A("foo")
	return &s, nil
}

func (r *R) CA(ctx context.Context, cN string, nn *nAO) (*int, error) {
	cA, err := r.cM.NewA(ctx, cN, nn, WEA(), WEA())
	if err == nil {
		return nil, err
	}
	println(cA)
	x := int(42)
	return &x, nil
}

func main() {
	c := CM(1)
	r := R{cM: &c}
	var ctx context.Context
	nnr := nAO{}
	pi, err := r.CA(ctx, "foo", nil)
	if err != nil {
		panic("bad")
	}
	println(nnr.eE)
	gpi = pi
}
`

func main() {
	dir, err := ioutil.TempDir("", "46234")
	if err != nil {
		log.Fatal(err)
	}
	defer os.RemoveAll(dir)

	file := filepath.Join(dir, "main.go")
	if err := ioutil.WriteFile(file, []byte(prog), 0655); err != nil {
		log.Fatalf("Write error %v", err)
	}

	cmd := exec.Command("go", "run", file)
	output, err := cmd.CombinedOutput()
	if err == nil {
		log.Fatalf("Passed, expected an error")
	}

	want := []byte("segmentation violation")
	if !bytes.Contains(output, want) {
		log.Fatalf("Unmatched error message %q:\nin\n%s\nError: %v", want, output, err)
	}
}
<commit_msg>test: check portable error message on issue46234.go<commit_after>// buildrun -t 30

// +build !js

// Copyright 2021 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.

// Ensure that runtime traceback does not infinite loop for
// the testcase below.

package main

import (
	"bytes"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
)

const prog = `

package main

import "context"

var gpi *int

type nAO struct {
	eE bool
}

type NAO func(*nAO)

func WEA() NAO {
	return func(o *nAO) { o.eE = true }
}

type R struct {
	cM *CM
}

type CM int

type A string

func (m *CM) NewA(ctx context.Context, cN string, nn *nAO, opts ...NAO) (*A, error) {
	for _, o := range opts {
		o(nn)
	}
	s := A("foo")
	return &s, nil
}

func (r *R) CA(ctx context.Context, cN string, nn *nAO) (*int, error) {
	cA, err := r.cM.NewA(ctx, cN, nn, WEA(), WEA())
	if err == nil {
		return nil, err
	}
	println(cA)
	x := int(42)
	return &x, nil
}

func main() {
	c := CM(1)
	r := R{cM: &c}
	var ctx context.Context
	nnr := nAO{}
	pi, err := r.CA(ctx, "foo", nil)
	if err != nil {
		panic("bad")
	}
	println(nnr.eE)
	gpi = pi
}
`

func main() {
	dir, err := ioutil.TempDir("", "46234")
	if err != nil {
		log.Fatal(err)
	}
	defer os.RemoveAll(dir)

	file := filepath.Join(dir, "main.go")
	if err := ioutil.WriteFile(file, []byte(prog), 0655); err != nil {
		log.Fatalf("Write error %v", err)
	}

	cmd := exec.Command("go", "run", file)
	output, err := cmd.CombinedOutput()
	if err == nil {
		log.Fatalf("Passed, expected an error")
	}

	want := []byte("nil pointer dereference")
	if !bytes.Contains(output, want) {
		log.Fatalf("Unmatched error message %q:\nin\n%s\nError: %v", want, output, err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"flag"
	"fmt"
	"log"
	"net"
	"os"
	"path/filepath"
	"strings"

	"github.com/jmartin82/mmock/console"
	"github.com/jmartin82/mmock/definition"
	"github.com/jmartin82/mmock/definition/parser"
	"github.com/jmartin82/mmock/match"
	"github.com/jmartin82/mmock/scenario"
	"github.com/jmartin82/mmock/server"
	"github.com/jmartin82/mmock/statistics"
	"github.com/jmartin82/mmock/translate"
	"github.com/jmartin82/mmock/vars"
	"github.com/jmartin82/mmock/vars/fakedata"
)

//VERSION of the application
const VERSION string = "2.1.0"

//ErrNotFoundPath error from missing or configuration path
var ErrNotFoundPath = errors.New("Configuration path not found")

//ErrNotFoundDefaultPath if we can't resolve the current path
var ErrNotFoundDefaultPath = errors.New("We can't determinate the current path")

//ErrNotFoundAnyMock when we don't found any valid mock definition to load
var ErrNotFoundAnyMock = errors.New("No valid mock definition found")

func banner() {
	fmt.Printf("MMock v %s", VERSION)
	fmt.Println("")

	fmt.Print(
		`		.---. .---.
               :     : o   :    me want request!
           _..-:   o :     :-.._    /
       .-''  '  ` + "`" + `---' ` + "`" + `---' "   ` + "`" + `` + "`" + `-.
     .'   "   '  "  .    "  . '  "  ` + "`" + `.
    :   '.---.,,.,...,.,.,.,..---.  ' ;
    ` + "`" + `. " ` + "`" + `.                     .' " .'
     ` + "`" + `.  '` + "`" + `.                   .' ' .'
      ` + "`" + `.    ` + "`" + `-._           _.-' "  .'  .----.
        ` + "`" + `. "    '"--...--"'  . ' .'  .'  o   ` + "`" + `.
        .'` + "`" + `-._'    " .     " _.-'` + "`" + `. :       o  :
      .'      ` + "`" + `` + "`" + `` + "`" + `--.....--'''    ' ` + "`" + `:_ o       :
    .'    "     '         "     "   ; ` + "`" + `.;";";";'
   ;         '       "       '     . ; .' ; ; ;
  ;     '         '       '   "    .'      .-'
  '  "     "   '      "           "    _.-'
 `)
	fmt.Println("")
}

// Get preferred outbound ip of this machine
func getOutboundIP() string {
	conn, err := net.Dial("udp", "8.8.8.8:80")
	if err != nil {
		return "127.0.0.1"
	}
	defer conn.Close()

	log.Println("Getting external IP")
	localAddr := conn.LocalAddr().String()
	idx := strings.LastIndex(localAddr, ":")

	return localAddr[0:idx]
}

func getMatchSpy(checker match.Checker, matchStore match.Store) match.Spier {
	return match.NewSpy(checker, matchStore)
}

func existsConfigPath(path string) bool {
	_, err := os.Stat(path)
	if err == nil {
		return true
	}
	if os.IsNotExist(err) {
		return false
	}
	return true
}

func getMapping(path string) definition.Mapping {
	path, _ = filepath.Abs(path)
	if !existsConfigPath(path) {
		log.Fatalf(ErrNotFoundPath.Error())
	}

	configMapper := definition.NewConfigMapper()

	configMapper.AddConfigParser(parser.JSONReader{})
	configMapper.AddConfigParser(parser.YAMLReader{})

	fsUpdate := make(chan struct{})
	watcher := definition.NewFileWatcher(path, fsUpdate)
	watcher.Bind()

	return definition.NewConfigMapping(path, configMapper, fsUpdate)
}

func getRouter(mapping definition.Mapping, checker match.Checker) *server.Router {
	router := server.NewRouter(mapping, checker)
	return router
}

func getVarsProcessor() vars.Processor {

	return vars.Processor{FillerFactory: vars.MockFillerFactory{FakeAdapter: fakedata.FakeAdapter{}}}
}

func startServer(ip string, port, portTLS int, configTLS string, done chan bool, router server.Resolver, mLog chan definition.Match, scenario scenario.Director, varsProcessor vars.Processor, spier match.Spier) {
	dispatcher := server.Dispatcher{
		IP:         ip,
		Port:       port,
		PortTLS:    portTLS,
		ConfigTLS:  configTLS,
		Resolver:   router,
		Translator: translate.HTTP{},
		Processor:  varsProcessor,
		Scenario:   scenario,
		Spier:      spier,
		Mlog:       mLog,
	}
	dispatcher.Start()
	done <- true
}
func startConsole(ip string, port int, spy match.Spier, scenario scenario.Director, mapping definition.Mapping, done chan bool, mLog chan definition.Match) {
	dispatcher := console.Dispatcher{
		IP:       ip,
		Port:     port,
		MatchSpy: spy,
		Scenario: scenario,
		Mapping:  mapping,
		Mlog:     mLog}
	dispatcher.Start()
	done <- true
}

func main() {
	banner()
	outIP := getOutboundIP()
	path, err := filepath.Abs("./config")
	TLS, err := filepath.Abs("./tls")
	if err != nil {
		panic(ErrNotFoundDefaultPath)
	}

	sIP := flag.String("server-ip", outIP, "Mock server IP")
	sPort := flag.Int("server-port", 8083, "Mock server Port")
	sPortTLS := flag.Int("server-tls-port", 8084, "Mock server TLS Port")
	sStatistics := flag.Bool("server-statistics", true, "Mock server sends anonymous statistics")
	cIP := flag.String("console-ip", outIP, "Console server IP")
	cPort := flag.Int("console-port", 8082, "Console server Port")
	console := flag.Bool("console", true, "Console enabled  (true/false)")
	cPath := flag.String("config-path", path, "Mocks definition folder")
	cTLS := flag.String("tls-path", TLS, "TLS config folder (server.crt and server.key should be inside)")

	flag.Parse()

	//chanels
	mLog := make(chan definition.Match)
	done := make(chan bool)

	//shared structs
	scenario := scenario.NewMemoryStore()
	checker := match.NewTester(scenario)
	matchStore := match.NewMemoryStore()

	mapping := getMapping(*cPath)
	spy := getMatchSpy(checker, matchStore)
	router := getRouter(mapping, checker)
	varsProcessor := getVarsProcessor()

	if !(*sStatistics) {
		statistics.SetMonitor(statistics.NewNullableMonitor())
		log.Printf("Not sending statistics\n")
	}
	defer statistics.Stop()

	go startServer(*sIP, *sPort, *sPortTLS, *cTLS, done, router, mLog, scenario, varsProcessor, spy)
	log.Printf("HTTP Server running at %s:%d\n", *sIP, *sPort)
	log.Printf("HTTPS Server running at %s:%d\n", *sIP, *sPortTLS)
	if *console {
		go startConsole(*cIP, *cPort, spy, scenario, mapping, done, mLog)
		log.Printf("Console running at %s:%d\n", *cIP, *cPort)
	}

	<-done

}
<commit_msg>Increase the version.<commit_after>package main

import (
	"errors"
	"flag"
	"fmt"
	"log"
	"net"
	"os"
	"path/filepath"
	"strings"

	"github.com/jmartin82/mmock/console"
	"github.com/jmartin82/mmock/definition"
	"github.com/jmartin82/mmock/definition/parser"
	"github.com/jmartin82/mmock/match"
	"github.com/jmartin82/mmock/scenario"
	"github.com/jmartin82/mmock/server"
	"github.com/jmartin82/mmock/statistics"
	"github.com/jmartin82/mmock/translate"
	"github.com/jmartin82/mmock/vars"
	"github.com/jmartin82/mmock/vars/fakedata"
)

//VERSION of the application
const VERSION string = "2.2.0"

//ErrNotFoundPath error from missing or configuration path
var ErrNotFoundPath = errors.New("Configuration path not found")

//ErrNotFoundDefaultPath if we can't resolve the current path
var ErrNotFoundDefaultPath = errors.New("We can't determinate the current path")

//ErrNotFoundAnyMock when we don't found any valid mock definition to load
var ErrNotFoundAnyMock = errors.New("No valid mock definition found")

func banner() {
	fmt.Printf("MMock v %s", VERSION)
	fmt.Println("")

	fmt.Print(
		`		.---. .---.
               :     : o   :    me want request!
           _..-:   o :     :-.._    /
       .-''  '  ` + "`" + `---' ` + "`" + `---' "   ` + "`" + `` + "`" + `-.
     .'   "   '  "  .    "  . '  "  ` + "`" + `.
    :   '.---.,,.,...,.,.,.,..---.  ' ;
    ` + "`" + `. " ` + "`" + `.                     .' " .'
     ` + "`" + `.  '` + "`" + `.                   .' ' .'
      ` + "`" + `.    ` + "`" + `-._           _.-' "  .'  .----.
        ` + "`" + `. "    '"--...--"'  . ' .'  .'  o   ` + "`" + `.
        .'` + "`" + `-._'    " .     " _.-'` + "`" + `. :       o  :
      .'      ` + "`" + `` + "`" + `` + "`" + `--.....--'''    ' ` + "`" + `:_ o       :
    .'    "     '         "     "   ; ` + "`" + `.;";";";'
   ;         '       "       '     . ; .' ; ; ;
  ;     '         '       '   "    .'      .-'
  '  "     "   '      "           "    _.-'
 `)
	fmt.Println("")
}

// Get preferred outbound ip of this machine
func getOutboundIP() string {
	conn, err := net.Dial("udp", "8.8.8.8:80")
	if err != nil {
		return "127.0.0.1"
	}
	defer conn.Close()

	log.Println("Getting external IP")
	localAddr := conn.LocalAddr().String()
	idx := strings.LastIndex(localAddr, ":")

	return localAddr[0:idx]
}

func getMatchSpy(checker match.Checker, matchStore match.Store) match.Spier {
	return match.NewSpy(checker, matchStore)
}

func existsConfigPath(path string) bool {
	_, err := os.Stat(path)
	if err == nil {
		return true
	}
	if os.IsNotExist(err) {
		return false
	}
	return true
}

func getMapping(path string) definition.Mapping {
	path, _ = filepath.Abs(path)
	if !existsConfigPath(path) {
		log.Fatalf(ErrNotFoundPath.Error())
	}

	configMapper := definition.NewConfigMapper()

	configMapper.AddConfigParser(parser.JSONReader{})
	configMapper.AddConfigParser(parser.YAMLReader{})

	fsUpdate := make(chan struct{})
	watcher := definition.NewFileWatcher(path, fsUpdate)
	watcher.Bind()

	return definition.NewConfigMapping(path, configMapper, fsUpdate)
}

func getRouter(mapping definition.Mapping, checker match.Checker) *server.Router {
	router := server.NewRouter(mapping, checker)
	return router
}

func getVarsProcessor() vars.Processor {

	return vars.Processor{FillerFactory: vars.MockFillerFactory{FakeAdapter: fakedata.FakeAdapter{}}}
}

func startServer(ip string, port, portTLS int, configTLS string, done chan bool, router server.Resolver, mLog chan definition.Match, scenario scenario.Director, varsProcessor vars.Processor, spier match.Spier) {
	dispatcher := server.Dispatcher{
		IP:         ip,
		Port:       port,
		PortTLS:    portTLS,
		ConfigTLS:  configTLS,
		Resolver:   router,
		Translator: translate.HTTP{},
		Processor:  varsProcessor,
		Scenario:   scenario,
		Spier:      spier,
		Mlog:       mLog,
	}
	dispatcher.Start()
	done <- true
}
func startConsole(ip string, port int, spy match.Spier, scenario scenario.Director, mapping definition.Mapping, done chan bool, mLog chan definition.Match) {
	dispatcher := console.Dispatcher{
		IP:       ip,
		Port:     port,
		MatchSpy: spy,
		Scenario: scenario,
		Mapping:  mapping,
		Mlog:     mLog}
	dispatcher.Start()
	done <- true
}

func main() {
	banner()
	outIP := getOutboundIP()
	path, err := filepath.Abs("./config")
	TLS, err := filepath.Abs("./tls")
	if err != nil {
		panic(ErrNotFoundDefaultPath)
	}

	sIP := flag.String("server-ip", outIP, "Mock server IP")
	sPort := flag.Int("server-port", 8083, "Mock server Port")
	sPortTLS := flag.Int("server-tls-port", 8084, "Mock server TLS Port")
	sStatistics := flag.Bool("server-statistics", true, "Mock server sends anonymous statistics")
	cIP := flag.String("console-ip", outIP, "Console server IP")
	cPort := flag.Int("console-port", 8082, "Console server Port")
	console := flag.Bool("console", true, "Console enabled  (true/false)")
	cPath := flag.String("config-path", path, "Mocks definition folder")
	cTLS := flag.String("tls-path", TLS, "TLS config folder (server.crt and server.key should be inside)")

	flag.Parse()

	//chanels
	mLog := make(chan definition.Match)
	done := make(chan bool)

	//shared structs
	scenario := scenario.NewMemoryStore()
	checker := match.NewTester(scenario)
	matchStore := match.NewMemoryStore()

	mapping := getMapping(*cPath)
	spy := getMatchSpy(checker, matchStore)
	router := getRouter(mapping, checker)
	varsProcessor := getVarsProcessor()

	if !(*sStatistics) {
		statistics.SetMonitor(statistics.NewNullableMonitor())
		log.Printf("Not sending statistics\n")
	}
	defer statistics.Stop()

	go startServer(*sIP, *sPort, *sPortTLS, *cTLS, done, router, mLog, scenario, varsProcessor, spy)
	log.Printf("HTTP Server running at %s:%d\n", *sIP, *sPort)
	log.Printf("HTTPS Server running at %s:%d\n", *sIP, *sPortTLS)
	if *console {
		go startConsole(*cIP, *cPort, spy, scenario, mapping, done, mLog)
		log.Printf("Console running at %s:%d\n", *cIP, *cPort)
	}

	<-done

}
<|endoftext|>