<commit_before>// Copyright 2012 The Gorilla 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 securecookie

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha256"
	"crypto/subtle"
	"encoding/base64"
	"encoding/gob"
	"errors"
	"fmt"
	"hash"
	"strconv"
	"time"
)

// Codec defines an interface to encode and decode cookie values.
type Codec interface {
	Encode(name string, value interface{}) (string, error)
	Decode(name, value string, dst interface{}) error
}

// New returns a new SecureCookie.
//
// hashKey is required, used to authenticate values using HMAC. Create it using
// GenerateRandomKey(). It is recommended to use a key with 32 or 64 bytes.
//
// blockKey is optional, used to encrypt values. Create it using
// GenerateRandomKey(). The key length must correspond to the block size
// of the encryption algorithm. For AES, used by default, valid lengths are
// 16, 24, or 32 bytes to select AES-128, AES-192, or AES-256.
func New(hashKey, blockKey []byte) *SecureCookie {
	s := &SecureCookie{
		hashKey:   hashKey,
		blockKey:  blockKey,
		hashFunc:  sha256.New,
		maxAge:    86400 * 30,
		maxLength: 4096,
	}
	if hashKey == nil {
		s.err = errors.New("securecookie: hash key is not set")
	}
	if blockKey != nil {
		s.BlockFunc(aes.NewCipher)
	}
	return s
}

// SecureCookie encodes and decodes authenticated and optionally encrypted
// cookie values.
type SecureCookie struct {
	hashKey   []byte
	hashFunc  func() hash.Hash
	blockKey  []byte
	block     cipher.Block
	maxLength int
	maxAge    int64
	minAge    int64
	err       error
	// For testing purposes, the function that returns the current timestamp.
	// If not set, it will use time.Now().UTC().Unix().
	timeFunc func() int64
}

// MaxLength restricts the maximum length, in bytes, for the cookie value.
//
// Default is 4096, which is the maximum value accepted by Internet Explorer.
func (s *SecureCookie) MaxLength(value int) *SecureCookie {
	s.maxLength = value
	return s
}

// MaxAge restricts the maximum age, in seconds, for the cookie value.
//
// Default is 86400 * 30. Set it to 0 for no restriction.
func (s *SecureCookie) MaxAge(value int) *SecureCookie {
	s.maxAge = int64(value)
	return s
}

// MinAge restricts the minimum age, in seconds, for the cookie value.
//
// Default is 0 (no restriction).
func (s *SecureCookie) MinAge(value int) *SecureCookie {
	s.minAge = int64(value)
	return s
}

// HashFunc sets the hash function used to create HMAC.
//
// Default is crypto/sha256.New.
func (s *SecureCookie) HashFunc(f func() hash.Hash) *SecureCookie {
	s.hashFunc = f
	return s
}

// BlockFunc sets the encryption function used to create a cipher.Block.
//
// Default is crypto/aes.New.
func (s *SecureCookie) BlockFunc(f func([]byte) (cipher.Block, error)) *SecureCookie {
	if s.blockKey == nil {
		s.err = errors.New("securecookie: block key is not set")
	} else if block, err := f(s.blockKey); err == nil {
		s.block = block
	} else {
		s.err = err
	}
	return s
}

// Encode encodes a cookie value.
//
// It decodes, verifies a message authentication code, optionally decrypts and
// finally deserializes the value.
//
// The name argument is the cookie name. It is stored with the encoded value.
// The value argument is the value to be encoded. It can be any value that can
// be encoded using encoding/gob. To store special structures, they must be
// registered first using gob.Register().
func (s *SecureCookie) Encode(name string, value interface{}) (string, error) {
	if s.err != nil {
		return "", s.err
	}
	if s.hashKey == nil {
		s.err = errors.New("securecookie: hash key is not set")
		return "", s.err
	}
	var err error
	var b []byte
	// 1. Serialize.
	if b, err = serialize(value); err != nil {
		return "", err
	}
	// 2. Create MAC for "name|date|value". Extra pipe to be used later.
	b = []byte(fmt.Sprintf("%s|%d|%s|", name, s.timestamp(), b))
	mac := createMac(hmac.New(s.hashFunc, s.hashKey), b[:len(b)-1])
	// 3. Append mac, remove name.
	b = append(b, mac...)[len(name)+1:]
	// 4. Encrypt (optional).
	if s.block != nil {
		if b, err = encrypt(s.block, b); err != nil {
			return "", err
		}
	}
	// 5. Encode to base64.
	b = encode(b)
	// 6. Check length.
	if s.maxLength != 0 && len(b) > s.maxLength {
		return "", errors.New("securecookie: the value is too long")
	}
	// Done.
	return string(b), nil
}

// Decode decodes a cookie value.
//
// It decodes, verifies a message authentication code, optionally decrypts and
// finally deserializes the value.
//
// The name argument is the cookie name. It must be the same name used when
// it was stored. The value argument is the encoded cookie value. The dst
// argument is where the cookie will be decoded. It must be a pointer.
func (s *SecureCookie) Decode(name, value string, dst interface{}) error {
	if s.err != nil {
		return s.err
	}
	if s.hashKey == nil {
		s.err = errors.New("securecookie: hash key is not set")
		return s.err
	}
	// 1. Check length.
	if s.maxLength != 0 && len(value) > s.maxLength {
		return errors.New("securecookie: the value is too long")
	}
	// 2. Decode from base64.
	b, err := decode([]byte(value))
	if err != nil {
		return err
	}
	// 3. Decrypt (optional).
	if s.block != nil {
		if b, err = decrypt(s.block, b); err != nil {
			return err
		}
	}
	// 4. Value is "date|serialized|mac". Split.
	parts := bytes.SplitN(b, []byte("|"), 3)
	if len(parts) != 3 {
		return errors.New("securecookie: invalid value %v")
	}
	// 5. Verify MAC: "name|date|serialized" against mac.
	h := hmac.New(s.hashFunc, s.hashKey)
	b = append([]byte(name+"|"), b[:len(b)-len(parts[2])-1]...)
	if err = verifyMac(h, b, parts[2]); err != nil {
		return err
	}
	// 6. Verify date ranges.
	var t1 int64
	if t1, err = strconv.ParseInt(string(parts[0]), 10, 64); err != nil {
		return errors.New("securecookie: invalid timestamp")
	}
	t2 := s.timestamp()
	if s.minAge != 0 && t1 > t2-s.minAge {
		return errors.New("securecookie: timestamp is too new")
	}
	if s.maxAge != 0 && t1 < t2-s.maxAge {
		return errors.New("securecookie: expired timestamp")
	}
	// 7. Deserialize.
	if err = deserialize(parts[1], dst); err != nil {
		return err
	}
	// Done.
	return nil
}

// timestamp returns the current timestamp, in seconds.
//
// For testing purposes, the function that generates the timestamp can be
// overridden. If not set, it will return time.Now().UTC().Unix().
func (s *SecureCookie) timestamp() int64 {
	if s.timeFunc == nil {
		return time.Now().UTC().Unix()
	}
	return s.timeFunc()
}

// Authentication -------------------------------------------------------------

// createMac creates a message authentication code (MAC).
func createMac(h hash.Hash, value []byte) []byte {
	h.Write(value)
	return h.Sum(nil)
}

// verifyMac verifies that a message authentication code (MAC) is valid.
func verifyMac(h hash.Hash, value []byte, mac []byte) error {
	mac2 := createMac(h, value)
	if len(mac) == len(mac2) && subtle.ConstantTimeCompare(mac, mac2) == 1 {
		return nil
	}
	return errors.New("securecookie: the value is not valid")
}

// Encryption -----------------------------------------------------------------

// encrypt encrypts a value using the given block in counter mode.
//
// A random initialization vector (http://goo.gl/zF67k) with the length of the
// block size is prepended to the resulting ciphertext.
func encrypt(block cipher.Block, value []byte) ([]byte, error) {
	iv := GenerateRandomKey(block.BlockSize())
	if iv == nil {
		return nil, errors.New("securecookie: failed to generate random iv")
	}
	// Encrypt it.
	stream := cipher.NewCTR(block, iv)
	stream.XORKeyStream(value, value)
	// Return iv + ciphertext.
	return append(iv, value...), nil
}

// decrypt decrypts a value using the given block in counter mode.
//
// The value to be decrypted must be prepended by a initialization vector
// (http://goo.gl/zF67k) with the length of the block size.
func decrypt(block cipher.Block, value []byte) ([]byte, error) {
	size := block.BlockSize()
	if len(value) > size {
		// Extract iv.
		iv := value[:size]
		// Extract ciphertext.
		value = value[size:]
		// Decrypt it.
		stream := cipher.NewCTR(block, iv)
		stream.XORKeyStream(value, value)
		return value, nil
	}
	return nil, errors.New("securecookie: the value could not be decrypted")
}

// Serialization --------------------------------------------------------------

// serialize encodes a value using gob.
func serialize(src interface{}) ([]byte, error) {
	buf := new(bytes.Buffer)
	enc := gob.NewEncoder(buf)
	if err := enc.Encode(src); err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

// deserialize decodes a value using gob.
func deserialize(src []byte, dst interface{}) error {
	dec := gob.NewDecoder(bytes.NewBuffer(src))
	if err := dec.Decode(dst); err != nil {
		return err
	}
	return nil
}

// Encoding -------------------------------------------------------------------

// encode encodes a value using base64.
func encode(value []byte) []byte {
	encoded := make([]byte, base64.URLEncoding.EncodedLen(len(value)))
	base64.URLEncoding.Encode(encoded, value)
	return encoded
}

// decode decodes a cookie using base64.
func decode(value []byte) ([]byte, error) {
	decoded := make([]byte, base64.URLEncoding.DecodedLen(len(value)))
	b, err := base64.URLEncoding.Decode(decoded, value)
	if err != nil {
		return nil, err
	}
	return decoded[:b], nil
}

// Helpers --------------------------------------------------------------------

// GenerateRandomKey returns a random key created using crypto/rand.
func GenerateRandomKey(length int) []byte {
	k := make([]byte, length)
	if _, err := rand.Read(k); err != nil {
		return nil
	}
	return k
}
<commit_msg>securecookie: added EncodeMulti()/DecodeMulti().<commit_after>// Copyright 2012 The Gorilla 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 securecookie

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha256"
	"crypto/subtle"
	"encoding/base64"
	"encoding/gob"
	"errors"
	"fmt"
	"hash"
	"strconv"
	"time"
)

// Codec defines an interface to encode and decode cookie values.
type Codec interface {
	Encode(name string, value interface{}) (string, error)
	Decode(name, value string, dst interface{}) error
}

// New returns a new SecureCookie.
//
// hashKey is required, used to authenticate values using HMAC. Create it using
// GenerateRandomKey(). It is recommended to use a key with 32 or 64 bytes.
//
// blockKey is optional, used to encrypt values. Create it using
// GenerateRandomKey(). The key length must correspond to the block size
// of the encryption algorithm. For AES, used by default, valid lengths are
// 16, 24, or 32 bytes to select AES-128, AES-192, or AES-256.
func New(hashKey, blockKey []byte) *SecureCookie {
	s := &SecureCookie{
		hashKey:   hashKey,
		blockKey:  blockKey,
		hashFunc:  sha256.New,
		maxAge:    86400 * 30,
		maxLength: 4096,
	}
	if hashKey == nil {
		s.err = errors.New("securecookie: hash key is not set")
	}
	if blockKey != nil {
		s.BlockFunc(aes.NewCipher)
	}
	return s
}

// SecureCookie encodes and decodes authenticated and optionally encrypted
// cookie values.
type SecureCookie struct {
	hashKey   []byte
	hashFunc  func() hash.Hash
	blockKey  []byte
	block     cipher.Block
	maxLength int
	maxAge    int64
	minAge    int64
	err       error
	// For testing purposes, the function that returns the current timestamp.
	// If not set, it will use time.Now().UTC().Unix().
	timeFunc func() int64
}

// MaxLength restricts the maximum length, in bytes, for the cookie value.
//
// Default is 4096, which is the maximum value accepted by Internet Explorer.
func (s *SecureCookie) MaxLength(value int) *SecureCookie {
	s.maxLength = value
	return s
}

// MaxAge restricts the maximum age, in seconds, for the cookie value.
//
// Default is 86400 * 30. Set it to 0 for no restriction.
func (s *SecureCookie) MaxAge(value int) *SecureCookie {
	s.maxAge = int64(value)
	return s
}

// MinAge restricts the minimum age, in seconds, for the cookie value.
//
// Default is 0 (no restriction).
func (s *SecureCookie) MinAge(value int) *SecureCookie {
	s.minAge = int64(value)
	return s
}

// HashFunc sets the hash function used to create HMAC.
//
// Default is crypto/sha256.New.
func (s *SecureCookie) HashFunc(f func() hash.Hash) *SecureCookie {
	s.hashFunc = f
	return s
}

// BlockFunc sets the encryption function used to create a cipher.Block.
//
// Default is crypto/aes.New.
func (s *SecureCookie) BlockFunc(f func([]byte) (cipher.Block, error)) *SecureCookie {
	if s.blockKey == nil {
		s.err = errors.New("securecookie: block key is not set")
	} else if block, err := f(s.blockKey); err == nil {
		s.block = block
	} else {
		s.err = err
	}
	return s
}

// Encode encodes a cookie value.
//
// It decodes, verifies a message authentication code, optionally decrypts and
// finally deserializes the value.
//
// The name argument is the cookie name. It is stored with the encoded value.
// The value argument is the value to be encoded. It can be any value that can
// be encoded using encoding/gob. To store special structures, they must be
// registered first using gob.Register().
func (s *SecureCookie) Encode(name string, value interface{}) (string, error) {
	if s.err != nil {
		return "", s.err
	}
	if s.hashKey == nil {
		s.err = errors.New("securecookie: hash key is not set")
		return "", s.err
	}
	var err error
	var b []byte
	// 1. Serialize.
	if b, err = serialize(value); err != nil {
		return "", err
	}
	// 2. Create MAC for "name|date|value". Extra pipe to be used later.
	b = []byte(fmt.Sprintf("%s|%d|%s|", name, s.timestamp(), b))
	mac := createMac(hmac.New(s.hashFunc, s.hashKey), b[:len(b)-1])
	// 3. Append mac, remove name.
	b = append(b, mac...)[len(name)+1:]
	// 4. Encrypt (optional).
	if s.block != nil {
		if b, err = encrypt(s.block, b); err != nil {
			return "", err
		}
	}
	// 5. Encode to base64.
	b = encode(b)
	// 6. Check length.
	if s.maxLength != 0 && len(b) > s.maxLength {
		return "", errors.New("securecookie: the value is too long")
	}
	// Done.
	return string(b), nil
}

// Decode decodes a cookie value.
//
// It decodes, verifies a message authentication code, optionally decrypts and
// finally deserializes the value.
//
// The name argument is the cookie name. It must be the same name used when
// it was stored. The value argument is the encoded cookie value. The dst
// argument is where the cookie will be decoded. It must be a pointer.
func (s *SecureCookie) Decode(name, value string, dst interface{}) error {
	if s.err != nil {
		return s.err
	}
	if s.hashKey == nil {
		s.err = errors.New("securecookie: hash key is not set")
		return s.err
	}
	// 1. Check length.
	if s.maxLength != 0 && len(value) > s.maxLength {
		return errors.New("securecookie: the value is too long")
	}
	// 2. Decode from base64.
	b, err := decode([]byte(value))
	if err != nil {
		return err
	}
	// 3. Decrypt (optional).
	if s.block != nil {
		if b, err = decrypt(s.block, b); err != nil {
			return err
		}
	}
	// 4. Value is "date|serialized|mac". Split.
	parts := bytes.SplitN(b, []byte("|"), 3)
	if len(parts) != 3 {
		return errors.New("securecookie: invalid value %v")
	}
	// 5. Verify MAC: "name|date|serialized" against mac.
	h := hmac.New(s.hashFunc, s.hashKey)
	b = append([]byte(name+"|"), b[:len(b)-len(parts[2])-1]...)
	if err = verifyMac(h, b, parts[2]); err != nil {
		return err
	}
	// 6. Verify date ranges.
	var t1 int64
	if t1, err = strconv.ParseInt(string(parts[0]), 10, 64); err != nil {
		return errors.New("securecookie: invalid timestamp")
	}
	t2 := s.timestamp()
	if s.minAge != 0 && t1 > t2-s.minAge {
		return errors.New("securecookie: timestamp is too new")
	}
	if s.maxAge != 0 && t1 < t2-s.maxAge {
		return errors.New("securecookie: expired timestamp")
	}
	// 7. Deserialize.
	if err = deserialize(parts[1], dst); err != nil {
		return err
	}
	// Done.
	return nil
}

// timestamp returns the current timestamp, in seconds.
//
// For testing purposes, the function that generates the timestamp can be
// overridden. If not set, it will return time.Now().UTC().Unix().
func (s *SecureCookie) timestamp() int64 {
	if s.timeFunc == nil {
		return time.Now().UTC().Unix()
	}
	return s.timeFunc()
}

// Authentication -------------------------------------------------------------

// createMac creates a message authentication code (MAC).
func createMac(h hash.Hash, value []byte) []byte {
	h.Write(value)
	return h.Sum(nil)
}

// verifyMac verifies that a message authentication code (MAC) is valid.
func verifyMac(h hash.Hash, value []byte, mac []byte) error {
	mac2 := createMac(h, value)
	if len(mac) == len(mac2) && subtle.ConstantTimeCompare(mac, mac2) == 1 {
		return nil
	}
	return errors.New("securecookie: the value is not valid")
}

// Encryption -----------------------------------------------------------------

// encrypt encrypts a value using the given block in counter mode.
//
// A random initialization vector (http://goo.gl/zF67k) with the length of the
// block size is prepended to the resulting ciphertext.
func encrypt(block cipher.Block, value []byte) ([]byte, error) {
	iv := GenerateRandomKey(block.BlockSize())
	if iv == nil {
		return nil, errors.New("securecookie: failed to generate random iv")
	}
	// Encrypt it.
	stream := cipher.NewCTR(block, iv)
	stream.XORKeyStream(value, value)
	// Return iv + ciphertext.
	return append(iv, value...), nil
}

// decrypt decrypts a value using the given block in counter mode.
//
// The value to be decrypted must be prepended by a initialization vector
// (http://goo.gl/zF67k) with the length of the block size.
func decrypt(block cipher.Block, value []byte) ([]byte, error) {
	size := block.BlockSize()
	if len(value) > size {
		// Extract iv.
		iv := value[:size]
		// Extract ciphertext.
		value = value[size:]
		// Decrypt it.
		stream := cipher.NewCTR(block, iv)
		stream.XORKeyStream(value, value)
		return value, nil
	}
	return nil, errors.New("securecookie: the value could not be decrypted")
}

// Serialization --------------------------------------------------------------

// serialize encodes a value using gob.
func serialize(src interface{}) ([]byte, error) {
	buf := new(bytes.Buffer)
	enc := gob.NewEncoder(buf)
	if err := enc.Encode(src); err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

// deserialize decodes a value using gob.
func deserialize(src []byte, dst interface{}) error {
	dec := gob.NewDecoder(bytes.NewBuffer(src))
	if err := dec.Decode(dst); err != nil {
		return err
	}
	return nil
}

// Encoding -------------------------------------------------------------------

// encode encodes a value using base64.
func encode(value []byte) []byte {
	encoded := make([]byte, base64.URLEncoding.EncodedLen(len(value)))
	base64.URLEncoding.Encode(encoded, value)
	return encoded
}

// decode decodes a cookie using base64.
func decode(value []byte) ([]byte, error) {
	decoded := make([]byte, base64.URLEncoding.DecodedLen(len(value)))
	b, err := base64.URLEncoding.Decode(decoded, value)
	if err != nil {
		return nil, err
	}
	return decoded[:b], nil
}

// Helpers --------------------------------------------------------------------

// GenerateRandomKey returns a random key created using crypto/rand.
func GenerateRandomKey(length int) []byte {
	k := make([]byte, length)
	if _, err := rand.Read(k); err != nil {
		return nil
	}
	return k
}

// EncodeMulti encodes a cookie value using a group of codecs.
//
// The codecs are tried in order. Multiple codecs are accepted to allow
// key rotation.
func EncodeMulti(name string, value interface{},
	codecs ...Codec) (string, error) {
	for _, codec := range codecs {
		if encoded, err := codec.Encode(name, value); err == nil {
			return encoded, nil
		}
	}
	return "", errors.New("securecookie: the value could not be encoded")
}

// DecodeMulti decodes a cookie value using a group of codecs.
//
// The codecs are tried in order. Multiple codecs are accepted to allow
// key rotation.
func DecodeMulti(name string, value string, dst interface{},
	codecs ...Codec) error {
	for _, codec := range codecs {
		if err := codec.Decode(name, value, dst); err == nil {
			return nil
		}
	}
	return errors.New("securecookie: the value could not be decoded")
}
<|endoftext|>


<commit_before>// Copyright 2020 Cisco Systems, Inc.
//
// 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 controller

import (
	"github.com/sirupsen/logrus"
	"strconv"

	qospolicy "github.com/noironetworks/aci-containers/pkg/qospolicy/apis/aci.qos/v1"
	qosclientset "github.com/noironetworks/aci-containers/pkg/qospolicy/clientset/versioned"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/client-go/tools/cache"

	"github.com/noironetworks/aci-containers/pkg/apicapi"
)

func QosPolicyLogger(log *logrus.Logger, qos *qospolicy.QosPolicy) *logrus.Entry {
	return log.WithFields(logrus.Fields{
		"namespace": qos.ObjectMeta.Namespace,
		"name":      qos.ObjectMeta.Name,
		"spec":      qos.Spec,
	})
}

func qosInit(cont *AciController, stopCh <-chan struct{}) {
	cont.log.Debug("Initializing qos client")
	restconfig := cont.env.RESTConfig()
	qosClient, err := qosclientset.NewForConfig(restconfig)
	if err != nil {
		cont.log.Errorf("Failed to intialize qos client")
		return
	}
	cont.initQosInformerFromClient(qosClient)
	go cont.qosInformer.Run(stopCh)
	go cont.processQueue(cont.qosQueue, cont.qosIndexer,
		func(obj interface{}) bool {
			return cont.handleQosPolUpdate(obj)
		}, stopCh)
	cache.WaitForCacheSync(stopCh, cont.qosInformer.HasSynced)
}

func (cont *AciController) initQosInformerFromClient(
	qosClient *qosclientset.Clientset) {
	cont.initQosInformerBase(
		cache.NewListWatchFromClient(
			qosClient.AciV1().RESTClient(), "qospolicies",
			metav1.NamespaceAll, fields.Everything()))
}

func (cont *AciController) initQosInformerBase(listWatch *cache.ListWatch) {
	cont.qosIndexer, cont.qosInformer = cache.NewIndexerInformer(
		listWatch,
		&qospolicy.QosPolicy{}, 0,
		cache.ResourceEventHandlerFuncs{
			AddFunc: func(obj interface{}) {
				cont.qosPolicyUpdated(obj)
			},
			UpdateFunc: func(_, obj interface{}) {
				cont.qosPolicyUpdated(obj)
			},
			DeleteFunc: func(obj interface{}) {
				cont.qosPolicyDelete(obj)
			},
		},
		cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
	)
	cont.log.Debug("Initializing Qos Policy Informers")

}

func (cont *AciController) qosPolicyUpdated(obj interface{}) {
	qosPolicy, ok := obj.(*qospolicy.QosPolicy)
	if !ok {
		cont.log.Error("qosPolicyUpdated: Bad object type")
		return
	}
	key, err := cache.MetaNamespaceKeyFunc(qosPolicy)
	if err != nil {
		QosPolicyLogger(cont.log, qosPolicy).
			Error("Could not create key:" + err.Error())
		return
	}
	cont.queueQosUpdateByKey(key)
	cont.log.Infof("qos policy updated: %s", qosPolicy.ObjectMeta.Name)

}

func (cont *AciController) queueQosUpdateByKey(key string) {
	cont.qosQueue.Add(key)
}

func (cont *AciController) qosPolicyDelete(qosobj interface{}) {
	qr := qosobj.(*qospolicy.QosPolicy)
	qrkey, err := cache.MetaNamespaceKeyFunc(qr)

	if err != nil {
		QosPolicyLogger(cont.log, qr).
			Error("Could not create qos policy key: ", err)
		return
	}

	cont.apicConn.ClearApicObjects(cont.aciNameForKey("qp", qrkey))

}

func (cont *AciController) handleQosPolUpdate(obj interface{}) bool {
	qp, ok := obj.(*qospolicy.QosPolicy)
	if !ok {
		cont.log.Error("handleQosPolUpdate: Bad object type")
		return false
	}
	logger := QosPolicyLogger(cont.log, qp)
	key, err := cache.MetaNamespaceKeyFunc(qp)
	if err != nil {
		logger.Error("Could not create qos policy key: ", err)
		return false
	}
	labelKey := cont.aciNameForKey("qp", key)
	cont.log.Infof("Creating qos policy: %s", qp.ObjectMeta.Name)
	qr := apicapi.NewQosRequirement(cont.config.AciPolicyTenant, labelKey)
	qrDn := qr.GetDn()
	apicSlice := apicapi.ApicSlice{qr}

	DscpMarking := apicapi.NewQosEpDscpMarking(qrDn, "EpDscpMarking")
	DscpMarking.SetAttr("mark", strconv.Itoa(qp.Spec.Mark))
	qr.AddChild(DscpMarking)

	// Generate ingress policies
	if qp.Spec.Ingress.PolicingRate != 0 && qp.Spec.Ingress.PolicingBurst != 0 {

		DppPolIngressName := labelKey + "_ingress"
		DppPolIngress := apicapi.NewQosDppPol(cont.config.AciPolicyTenant, DppPolIngressName)
		DppPolIngress.SetAttr("rate", strconv.Itoa(qp.Spec.Ingress.PolicingRate))
		DppPolIngress.SetAttr("burst", strconv.Itoa(qp.Spec.Ingress.PolicingBurst))

		RsIngressDppPol := apicapi.NewRsIngressDppPol(qrDn, DppPolIngressName)
		qr.AddChild(RsIngressDppPol)
		apicSlice = append(apicSlice, DppPolIngress)
	}

	// Generate egress policies
	if qp.Spec.Egress.PolicingRate != 0 && qp.Spec.Egress.PolicingBurst != 0 {

		DppPolEgressName := labelKey + "_egress"
		DppPolEgress := apicapi.NewQosDppPol(cont.config.AciPolicyTenant, DppPolEgressName)
		DppPolEgress.SetAttr("rate", strconv.Itoa(qp.Spec.Egress.PolicingRate))
		DppPolEgress.SetAttr("burst", strconv.Itoa(qp.Spec.Egress.PolicingBurst))

		RsEgressDppPol := apicapi.NewRsEgressDppPol(qrDn, DppPolEgressName)
		qr.AddChild(RsEgressDppPol)
		apicSlice = append(apicSlice, DppPolEgress)
	}
	cont.log.Info("qos APIC slice: ", apicSlice)
	cont.apicConn.WriteApicObjects(labelKey, apicSlice)

	return false

}
<commit_msg>Qos Policy is configurable in APIC versions > 5.1 (#776)<commit_after>// Copyright 2020 Cisco Systems, Inc.
//
// 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 controller

import (
	"github.com/sirupsen/logrus"
	"strconv"

	qospolicy "github.com/noironetworks/aci-containers/pkg/qospolicy/apis/aci.qos/v1"
	qosclientset "github.com/noironetworks/aci-containers/pkg/qospolicy/clientset/versioned"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/client-go/tools/cache"

	"github.com/noironetworks/aci-containers/pkg/apicapi"
)

func QosPolicyLogger(log *logrus.Logger, qos *qospolicy.QosPolicy) *logrus.Entry {
	return log.WithFields(logrus.Fields{
		"namespace": qos.ObjectMeta.Namespace,
		"name":      qos.ObjectMeta.Name,
		"spec":      qos.Spec,
	})
}

func qosInit(cont *AciController, stopCh <-chan struct{}) {
	cont.log.Debug("Initializing qos client")
	restconfig := cont.env.RESTConfig()
	qosClient, err := qosclientset.NewForConfig(restconfig)
	if err != nil {
		cont.log.Errorf("Failed to intialize qos client")
		return
	}
	cont.initQosInformerFromClient(qosClient)
	go cont.qosInformer.Run(stopCh)
	go cont.processQueue(cont.qosQueue, cont.qosIndexer,
		func(obj interface{}) bool {
			return cont.handleQosPolUpdate(obj)
		}, stopCh)
	cache.WaitForCacheSync(stopCh, cont.qosInformer.HasSynced)
}

func (cont *AciController) initQosInformerFromClient(
	qosClient *qosclientset.Clientset) {
	cont.initQosInformerBase(
		cache.NewListWatchFromClient(
			qosClient.AciV1().RESTClient(), "qospolicies",
			metav1.NamespaceAll, fields.Everything()))
}

func (cont *AciController) initQosInformerBase(listWatch *cache.ListWatch) {
	cont.qosIndexer, cont.qosInformer = cache.NewIndexerInformer(
		listWatch,
		&qospolicy.QosPolicy{}, 0,
		cache.ResourceEventHandlerFuncs{
			AddFunc: func(obj interface{}) {
				cont.qosPolicyUpdated(obj)
			},
			UpdateFunc: func(_, obj interface{}) {
				cont.qosPolicyUpdated(obj)
			},
			DeleteFunc: func(obj interface{}) {
				cont.qosPolicyDeleted(obj)
			},
		},
		cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
	)
	cont.log.Debug("Initializing Qos Policy Informers")

}

func (cont *AciController) qosPolicyUpdated(obj interface{}) {
	qosPolicy, ok := obj.(*qospolicy.QosPolicy)
	if !ok {
		cont.log.Error("qosPolicyUpdated: Bad object type")
		return
	}
	key, err := cache.MetaNamespaceKeyFunc(qosPolicy)
	if err != nil {
		QosPolicyLogger(cont.log, qosPolicy).
			Error("Could not create key:" + err.Error())
		return
	}
	cont.queueQosUpdateByKey(key)
	cont.log.Infof("qos policy updated: %s", qosPolicy.ObjectMeta.Name)

}

func (cont *AciController) queueQosUpdateByKey(key string) {
	cont.qosQueue.Add(key)
}

func (cont *AciController) qosPolicyDeleted(qosobj interface{}) {
	qr, isQr := qosobj.(*qospolicy.QosPolicy)
	if !isQr {
		deletedState, ok := qosobj.(cache.DeletedFinalStateUnknown)
		if !ok {
			QosPolicyLogger(cont.log, qr).
				Error("Received unexpected object: ", qosobj)
			return
		}
		qr, ok = deletedState.Obj.(*qospolicy.QosPolicy)
		if !ok {
			QosPolicyLogger(cont.log, qr).
				Error("DeletedFinalStateUnknown contained non-qos object: ", deletedState.Obj)
			return
		}
	}
	qrkey, err := cache.MetaNamespaceKeyFunc(qr)
	if err != nil {
		QosPolicyLogger(cont.log, qr).
			Error("Could not create qos policy key: ", err)
		return
	}

	cont.apicConn.ClearApicObjects(cont.aciNameForKey("qp", qrkey))

}

func (cont *AciController) handleQosPolUpdate(obj interface{}) bool {

	// Ability to configure Qos Policy is available only in APIC versions >= 5.1(x).
	if apicapi.ApicVersion < "5.1" {
		cont.log.Error("Cannot create Qos Policy in APIC versions < 5.1(x). Actual APIC version: ",
			apicapi.ApicVersion)
		return false
	}

	qp, ok := obj.(*qospolicy.QosPolicy)
	if !ok {
		cont.log.Error("handleQosPolUpdate: Bad object type")
		return false
	}
	logger := QosPolicyLogger(cont.log, qp)
	key, err := cache.MetaNamespaceKeyFunc(qp)
	if err != nil {
		logger.Error("Could not create qos policy key: ", err)
		return false
	}
	labelKey := cont.aciNameForKey("qp", key)
	cont.log.Infof("Creating qos policy: %s", qp.ObjectMeta.Name)
	qr := apicapi.NewQosRequirement(cont.config.AciPolicyTenant, labelKey)
	qrDn := qr.GetDn()
	apicSlice := apicapi.ApicSlice{qr}

	DscpMarking := apicapi.NewQosEpDscpMarking(qrDn, "EpDscpMarking")
	DscpMarking.SetAttr("mark", strconv.Itoa(qp.Spec.Mark))
	qr.AddChild(DscpMarking)

	// Generate ingress policies
	if qp.Spec.Ingress.PolicingRate != 0 && qp.Spec.Ingress.PolicingBurst != 0 {

		DppPolIngressName := labelKey + "_ingress"
		DppPolIngress := apicapi.NewQosDppPol(cont.config.AciPolicyTenant, DppPolIngressName)
		DppPolIngress.SetAttr("rate", strconv.Itoa(qp.Spec.Ingress.PolicingRate))
		DppPolIngress.SetAttr("burst", strconv.Itoa(qp.Spec.Ingress.PolicingBurst))

		RsIngressDppPol := apicapi.NewRsIngressDppPol(qrDn, DppPolIngressName)
		qr.AddChild(RsIngressDppPol)
		apicSlice = append(apicSlice, DppPolIngress)
	}

	// Generate egress policies
	if qp.Spec.Egress.PolicingRate != 0 && qp.Spec.Egress.PolicingBurst != 0 {

		DppPolEgressName := labelKey + "_egress"
		DppPolEgress := apicapi.NewQosDppPol(cont.config.AciPolicyTenant, DppPolEgressName)
		DppPolEgress.SetAttr("rate", strconv.Itoa(qp.Spec.Egress.PolicingRate))
		DppPolEgress.SetAttr("burst", strconv.Itoa(qp.Spec.Egress.PolicingBurst))

		RsEgressDppPol := apicapi.NewRsEgressDppPol(qrDn, DppPolEgressName)
		qr.AddChild(RsEgressDppPol)
		apicSlice = append(apicSlice, DppPolEgress)
	}
	cont.log.Info("qos APIC slice: ", apicSlice)
	cont.apicConn.WriteApicObjects(labelKey, apicSlice)

	return false

}
<|endoftext|>


<commit_before>// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cors

import (
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
	"time"

	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/go-martini/martini"
)

func Test_AllowAll(t *testing.T) {
	recorder := httptest.NewRecorder()
	m := martini.New()
	m.Use(Allow(&Options{
		AllowAllOrigins: true,
	}))

	origin := "https://bar.foo.com"
	r, _ := http.NewRequest("PUT", "foo", nil)
	r.Header.Add("Origin", origin)
	m.ServeHTTP(recorder, r)

	headerValue := recorder.HeaderMap.Get(headerAllowOrigin)
	if headerValue != origin {
		t.Errorf("Allow-Origin header should be %v, found %v", origin, headerValue)
	}
}

func Test_AllowRegexMatch(t *testing.T) {
	recorder := httptest.NewRecorder()
	m := martini.New()
	m.Use(Allow(&Options{
		AllowOrigins: []string{"https://aaa.com", "https://*.foo.com"},
	}))

	origin := "https://bar.foo.com"
	r, _ := http.NewRequest("PUT", "foo", nil)
	r.Header.Add("Origin", origin)
	m.ServeHTTP(recorder, r)

	headerValue := recorder.HeaderMap.Get(headerAllowOrigin)
	if headerValue != origin {
		t.Errorf("Allow-Origin header should be %v, found %v", origin, headerValue)
	}
}

func Test_AllowRegexNoMatch(t *testing.T) {
	recorder := httptest.NewRecorder()
	m := martini.New()
	m.Use(Allow(&Options{
		AllowOrigins: []string{"https://*.foo.com"},
	}))

	origin := "https://ww.foo.com.evil.com"
	r, _ := http.NewRequest("PUT", "foo", nil)
	r.Header.Add("Origin", origin)
	m.ServeHTTP(recorder, r)

	headerValue := recorder.HeaderMap.Get(headerAllowOrigin)
	if headerValue != "" {
		t.Errorf("Allow-Origin header should not exist, found %v", headerValue)
	}
}

func Test_OtherHeaders(t *testing.T) {
	recorder := httptest.NewRecorder()
	m := martini.New()
	m.Use(Allow(&Options{
		AllowAllOrigins:  true,
		AllowCredentials: true,
		AllowMethods:     []string{"PATCH", "GET"},
		AllowHeaders:     []string{"Origin", "X-whatever"},
		ExposeHeaders:    []string{"Content-Length", "Hello"},
		MaxAge:           5 * time.Minute,
	}))

	r, _ := http.NewRequest("PUT", "foo", nil)
	origin := "https://ww.foo.com.evil.com"
	r.Header.Add("Origin", origin)
	m.ServeHTTP(recorder, r)

	credentialsVal := recorder.HeaderMap.Get(headerAllowCredentials)
	methodsVal := recorder.HeaderMap.Get(headerAllowMethods)
	headersVal := recorder.HeaderMap.Get(headerAllowHeaders)
	exposedHeadersVal := recorder.HeaderMap.Get(headerExposeHeaders)
	maxAgeVal := recorder.HeaderMap.Get(headerMaxAge)

	if credentialsVal != "true" {
		t.Errorf("Allow-Credentials is expected to be true, found %v", credentialsVal)
	}

	if methodsVal != "PATCH,GET" {
		t.Errorf("Allow-Methods is expected to be PATCH,GET; found %v", methodsVal)
	}

	if headersVal != "Origin,X-whatever" {
		t.Errorf("Allow-Headers is expected to be Origin,X-whatever; found %v", headersVal)
	}

	if exposedHeadersVal != "Content-Length,Hello" {
		t.Errorf("Expose-Headers are expected to be Content-Length,Hello. Found %v", exposedHeadersVal)
	}

	if maxAgeVal != "300" {
		t.Errorf("Max-Age is expected to be 300, found %v", maxAgeVal)
	}
}

func Test_DefaultAllowHeaders(t *testing.T) {
	recorder := httptest.NewRecorder()
	m := martini.New()
	m.Use(Allow(&Options{
		AllowAllOrigins: true,
	}))

	r, _ := http.NewRequest("PUT", "foo", nil)
	origin := "https://ww.foo.com.evil.com"
	r.Header.Add("Origin", origin)
	m.ServeHTTP(recorder, r)

	headersVal := recorder.HeaderMap.Get(headerAllowHeaders)
	if headersVal != "Origin,Accept,Content-Type,Authorization" {
		t.Errorf("Allow-Headers is expected to be Origin,Accept,Content-Type,Authorization; found %v", headersVal)
	}
}

func Test_Preflight(t *testing.T) {
	recorder := httptest.NewRecorder()
	m := martini.New()
	m.Use(Allow(&Options{
		AllowAllOrigins: true,
		AllowMethods:    []string{"PUT", "PATCH"},
		AllowHeaders:    []string{"Origin", "X-whatever", "X-CaseSensitive"},
	}))

	r, _ := http.NewRequest("OPTIONS", "foo", nil)
	r.Header.Add(headerRequestMethod, "PUT")
	r.Header.Add(headerRequestHeaders, "X-whatever, x-casesensitive")
	origin := "https://bar.foo.com"
	r.Header.Add("Origin", origin)
	m.ServeHTTP(recorder, r)

	methodsVal := recorder.HeaderMap.Get(headerAllowMethods)
	headersVal := recorder.HeaderMap.Get(headerAllowHeaders)
	originVal := recorder.HeaderMap.Get(headerAllowOrigin)

	if methodsVal != "PUT,PATCH" {
		t.Errorf("Allow-Methods is expected to be PUT,PATCH, found %v", methodsVal)
	}

	if !strings.Contains(headersVal, "X-whatever") {
		t.Errorf("Allow-Headers is expected to contain X-whatever, found %v", headersVal)
	}

	if !strings.Contains(headersVal, "X-CaseSensitive") {
		t.Errorf("Allow-Headers is expected to contain x-casesensitive, found %v", headersVal)
	}

	if originVal != origin {
		t.Errorf("Allow-Origin is expected to be %v, found %v", origin, originVal)
	}
}

func Benchmark_WithoutCORS(b *testing.B) {
	recorder := httptest.NewRecorder()
	m := martini.New()

	b.ResetTimer()
	for i := 0; i < 100; i++ {
		r, _ := http.NewRequest("PUT", "foo", nil)
		m.ServeHTTP(recorder, r)
	}
}

func Benchmark_WithCORS(b *testing.B) {
	recorder := httptest.NewRecorder()
	m := martini.New()
	m.Use(Allow(&Options{
		AllowAllOrigins:  true,
		AllowCredentials: true,
		AllowMethods:     []string{"PATCH", "GET"},
		AllowHeaders:     []string{"Origin", "X-whatever"},
		MaxAge:           5 * time.Minute,
	}))

	b.ResetTimer()
	for i := 0; i < 100; i++ {
		r, _ := http.NewRequest("PUT", "foo", nil)
		m.ServeHTTP(recorder, r)
	}
}
<commit_msg>pkg/cors: Remove martini from test suite<commit_after>// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cors

import (
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
	"time"
)

func serveHTTP(w http.ResponseWriter, req *http.Request, opts *Options) {
	Allow(opts)(w, req)
	w.WriteHeader(200)
}

func Test_AllowAll(t *testing.T) {
	recorder := httptest.NewRecorder()
	origin := "https://bar.foo.com"
	r, _ := http.NewRequest("PUT", "foo", nil)
	r.Header.Add("Origin", origin)
	serveHTTP(recorder, r, &Options{
		AllowAllOrigins: true,
	})

	headerValue := recorder.HeaderMap.Get(headerAllowOrigin)
	if headerValue != origin {
		t.Errorf("Allow-Origin header should be %v, found %v", origin, headerValue)
	}
}

func Test_AllowRegexMatch(t *testing.T) {
	recorder := httptest.NewRecorder()
	origin := "https://bar.foo.com"
	r, _ := http.NewRequest("PUT", "foo", nil)
	r.Header.Add("Origin", origin)
	serveHTTP(recorder, r, &Options{
		AllowOrigins: []string{"https://aaa.com", "https://*.foo.com"},
	})

	headerValue := recorder.HeaderMap.Get(headerAllowOrigin)
	if headerValue != origin {
		t.Errorf("Allow-Origin header should be %v, found %v", origin, headerValue)
	}
}

func Test_AllowRegexNoMatch(t *testing.T) {
	recorder := httptest.NewRecorder()
	origin := "https://ww.foo.com.evil.com"
	r, _ := http.NewRequest("PUT", "foo", nil)
	r.Header.Add("Origin", origin)
	serveHTTP(recorder, r, &Options{
		AllowOrigins: []string{"https://*.foo.com"},
	})

	headerValue := recorder.HeaderMap.Get(headerAllowOrigin)
	if headerValue != "" {
		t.Errorf("Allow-Origin header should not exist, found %v", headerValue)
	}
}

func Test_OtherHeaders(t *testing.T) {
	recorder := httptest.NewRecorder()
	r, _ := http.NewRequest("PUT", "foo", nil)
	origin := "https://ww.foo.com.evil.com"
	r.Header.Add("Origin", origin)
	serveHTTP(recorder, r, &Options{
		AllowAllOrigins:  true,
		AllowCredentials: true,
		AllowMethods:     []string{"PATCH", "GET"},
		AllowHeaders:     []string{"Origin", "X-whatever"},
		ExposeHeaders:    []string{"Content-Length", "Hello"},
		MaxAge:           5 * time.Minute,
	})

	credentialsVal := recorder.HeaderMap.Get(headerAllowCredentials)
	methodsVal := recorder.HeaderMap.Get(headerAllowMethods)
	headersVal := recorder.HeaderMap.Get(headerAllowHeaders)
	exposedHeadersVal := recorder.HeaderMap.Get(headerExposeHeaders)
	maxAgeVal := recorder.HeaderMap.Get(headerMaxAge)

	if credentialsVal != "true" {
		t.Errorf("Allow-Credentials is expected to be true, found %v", credentialsVal)
	}

	if methodsVal != "PATCH,GET" {
		t.Errorf("Allow-Methods is expected to be PATCH,GET; found %v", methodsVal)
	}

	if headersVal != "Origin,X-whatever" {
		t.Errorf("Allow-Headers is expected to be Origin,X-whatever; found %v", headersVal)
	}

	if exposedHeadersVal != "Content-Length,Hello" {
		t.Errorf("Expose-Headers are expected to be Content-Length,Hello. Found %v", exposedHeadersVal)
	}

	if maxAgeVal != "300" {
		t.Errorf("Max-Age is expected to be 300, found %v", maxAgeVal)
	}
}

func Test_DefaultAllowHeaders(t *testing.T) {
	recorder := httptest.NewRecorder()
	r, _ := http.NewRequest("PUT", "foo", nil)
	origin := "https://ww.foo.com.evil.com"
	r.Header.Add("Origin", origin)
	serveHTTP(recorder, r, &Options{
		AllowAllOrigins: true,
	})

	headersVal := recorder.HeaderMap.Get(headerAllowHeaders)
	if headersVal != "Origin,Accept,Content-Type,Authorization" {
		t.Errorf("Allow-Headers is expected to be Origin,Accept,Content-Type,Authorization; found %v", headersVal)
	}
}

func Test_Preflight(t *testing.T) {
	recorder := httptest.NewRecorder()
	r, _ := http.NewRequest("OPTIONS", "foo", nil)
	r.Header.Add(headerRequestMethod, "PUT")
	r.Header.Add(headerRequestHeaders, "X-whatever, x-casesensitive")
	origin := "https://bar.foo.com"
	r.Header.Add("Origin", origin)
	serveHTTP(recorder, r, &Options{
		AllowAllOrigins: true,
		AllowMethods:    []string{"PUT", "PATCH"},
		AllowHeaders:    []string{"Origin", "X-whatever", "X-CaseSensitive"},
	})

	methodsVal := recorder.HeaderMap.Get(headerAllowMethods)
	headersVal := recorder.HeaderMap.Get(headerAllowHeaders)
	originVal := recorder.HeaderMap.Get(headerAllowOrigin)

	if methodsVal != "PUT,PATCH" {
		t.Errorf("Allow-Methods is expected to be PUT,PATCH, found %v", methodsVal)
	}

	if !strings.Contains(headersVal, "X-whatever") {
		t.Errorf("Allow-Headers is expected to contain X-whatever, found %v", headersVal)
	}

	if !strings.Contains(headersVal, "X-CaseSensitive") {
		t.Errorf("Allow-Headers is expected to contain x-casesensitive, found %v", headersVal)
	}

	if originVal != origin {
		t.Errorf("Allow-Origin is expected to be %v, found %v", origin, originVal)
	}
}

func Benchmark_WithoutCORS(b *testing.B) {
	recorder := httptest.NewRecorder()
	b.ResetTimer()
	for i := 0; i < 100; i++ {
		http.NewRequest("PUT", "foo", nil)
		recorder.WriteHeader(200)
	}
}

func Benchmark_WithCORS(b *testing.B) {
	recorder := httptest.NewRecorder()
	b.ResetTimer()
	for i := 0; i < 100; i++ {
		r, _ := http.NewRequest("PUT", "foo", nil)
		serveHTTP(recorder, r, &Options{
			AllowAllOrigins:  true,
			AllowCredentials: true,
			AllowMethods:     []string{"PATCH", "GET"},
			AllowHeaders:     []string{"Origin", "X-whatever"},
			MaxAge:           5 * time.Minute,
		})
	}
}
<|endoftext|>


<commit_before>package events

import (
	"bytes"
	"io/ioutil"
	"log"
	"net/http"
	"sync"
	"time"
)

// Emitter types are used by the transporter pipeline to consume events from a pipeline's event channel
// and process them.
// Start() will start the emitter and being consuming events
// Init() serves to set the Emitter's listening channel
// Stop() stops the event loop and releases any resources.  Stop is expected to shut down the process cleanly,
// the pipeline process will block until Stop() returns
type Emitter interface {
	Start()
	Init(chan Event)
	Stop()
}

// HTTPPostEmitter listens on the event channel and posts the events to an http server
// Events are serialized into json, and sent via a POST request to the given Uri
// http errors are logged as warnings to the console, and won't stop the Emitter
type HTTPPostEmitter struct {
	uri string
	key string
	pid string

	inflight *sync.WaitGroup
	ch       chan Event
	chstop   chan chan bool
}

// NewHTTPPostEmitter creates a new HTTPPostEmitter
func NewHTTPPostEmitter(uri, key, pid string) *HTTPPostEmitter {
	return &HTTPPostEmitter{
		uri:      uri,
		key:      key,
		pid:      pid,
		chstop:   make(chan chan bool),
		inflight: &sync.WaitGroup{},
	}
}

// Start the emitter
func (e *HTTPPostEmitter) Start() {
	go e.startEventListener()
}

// Init sets the event channel
func (e *HTTPPostEmitter) Init(ch chan Event) {
	e.ch = ch
}

// Stop sends a stop signal and waits for the inflight posts to complete before exiting
func (e *HTTPPostEmitter) Stop() {
	s := make(chan bool)
	e.chstop <- s
	<-s
	e.inflight.Wait()
}

func (e *HTTPPostEmitter) startEventListener() {
	for {
		select {
		case s := <-e.chstop:
			s <- true
			return
		case event := <-e.ch:
			e.inflight.Add(1)
			go func(event Event) {
				defer e.inflight.Done()

				ba, err := event.Emit()
				if err != err {
					log.Printf("EventEmitter Error: %s", err)
					return
				}

				req, err := http.NewRequest("POST", e.uri, bytes.NewBuffer(ba))
				if err != nil {
					log.Printf("EventEmitter Error: %s", err)
					return
				}
				req.Header.Set("Content-Type", "application/json")
				if len(e.pid) > 0 && len(e.key) > 0 {
					req.SetBasicAuth(e.pid, e.key)
				}
				cli := &http.Client{}
				resp, err := cli.Do(req)

				if err != nil {
					log.Printf("EventEmitter Error: %s", err)
					return
				}
				_, err = ioutil.ReadAll(resp.Body)
				if err != nil {
					log.Printf("Response read error: %s", err)
					return
				}
				defer resp.Body.Close()
				if resp.StatusCode != 200 && resp.StatusCode != 201 {
					log.Printf("EventEmitter Error: http error code, expected 200 or 201, got %d, (%s)", resp.StatusCode, ba)
					return
				}
				// fmt.Printf("EventEmitter, got http statuscode:%d for event: %s", resp.StatusCode, event)
			}(event)
		case <-time.After(100 * time.Millisecond):
			continue
			// noop
		}
	}
}

// NewNoopEmitter constructs a NoopEmitter to use with a transporter pipeline.
// a NoopEmitter consumes the events from the listening channel and does nothing with them
// this is useful for cli utilities that dump output to stdout in any case, and don't want
// to clutter the program's output with metrics
func NewNoopEmitter() *NoopEmitter {
	return &NoopEmitter{chstop: make(chan chan bool)}
}

// NoopEmitter consumes the events from the listening channel and does nothing with them
// this is useful for cli utilities that dump output to stdout in any case, and don't want
// to clutter the program's output with metrics
type NoopEmitter struct {
	chstop chan chan bool
	ch     chan Event
}

// Start the event consumer
func (e *NoopEmitter) Start() {
	go func() {
		for {
			select {
			case s := <-e.chstop:
				s <- true
				return
			case <-e.ch:
				continue
			case <-time.After(100 * time.Millisecond):
				continue
			}
		}
	}()
}

// Init sets the event channel
func (e *NoopEmitter) Init(ch chan Event) {
	e.ch = ch
}

// Stop the event consumer
func (e *NoopEmitter) Stop() {
	s := make(chan bool)
	e.chstop <- s
	<-s
}

// NewLogEmitter creates a new LogEmitter
func NewLogEmitter() *LogEmitter {
	return &LogEmitter{
		chstop: make(chan chan bool),
	}
}

// LogEmitter constructs a LogEmitter to use with a transporter pipeline.
// A LogEmitter listens on the event channel and uses go's log package to emit the event,
// eg.
//   2014/11/28 16:56:58 boot map[source:mongo out:mongo]
//   2014/11/28 16:56:58 metrics source recordsIn: 0, recordsOut: 203
//   2014/11/28 16:56:58 exit
//   2014/11/28 16:56:58 metrics source/out recordsIn: 203, recordsOut: 0
type LogEmitter struct {
	chstop chan chan bool
	ch     chan Event
}

// Start the emitter
func (e *LogEmitter) Start() {
	go e.startEventListener()
}

// Init sets the event channel
func (e *LogEmitter) Init(ch chan Event) {
	e.ch = ch
}

// Stop the emitter
func (e *LogEmitter) Stop() {
	s := make(chan bool)
	e.chstop <- s
	<-s
}

func (e *LogEmitter) startEventListener() {
	for {
		select {
		case s := <-e.chstop:
			s <- true
			return
		case event := <-e.ch:
			log.Println(event.String())
		case <-time.After(100 * time.Millisecond):
			continue
			// noop
		}
	}
}

// NewJSONLogEmitter creates a new LogEmitter
func NewJSONLogEmitter() *JSONLogEmitter {
	return &JSONLogEmitter{
		chstop: make(chan chan bool),
	}
}

// JSONLogEmitter constructs a LogEmitter to use with a transporter pipeline.
// A JsonLogEmitter listens on the event channel and uses go's log package to emit the event,
// eg.
// 2015/07/14 11:52:01 {"ts":1436889121,"name":"metrics","path":"source-development.jobs/dest-x.jobs","records":121}
// 2015/07/14 11:52:01 {"ts":1436889121,"name":"exit","version":"0.0.4","endpoints":{"dest-x.jobs":"mongo","source-development.jobs":"mongo"}}
type JSONLogEmitter struct {
	chstop chan chan bool
	ch     chan Event
}

// Start the emitter
func (e *JSONLogEmitter) Start() {
	go e.startEventListener()
}

// Init sets the event channel
func (e *JSONLogEmitter) Init(ch chan Event) {
	e.ch = ch
}

// Stop the emitter
func (e *JSONLogEmitter) Stop() {
	s := make(chan bool)
	e.chstop <- s
	<-s
}

func (e *JSONLogEmitter) startEventListener() {
	for {
		select {
		case s := <-e.chstop:
			s <- true
			return
		case event := <-e.ch:
			j, err := event.Emit()
			if err != nil {
				continue
			}
			log.Println(string(j))
		case <-time.After(100 * time.Millisecond):
			continue
			// noop
		}
	}
}
<commit_msg>use fmt package for json logger because ts in json<commit_after>package events

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"sync"
	"time"
)

// Emitter types are used by the transporter pipeline to consume events from a pipeline's event channel
// and process them.
// Start() will start the emitter and being consuming events
// Init() serves to set the Emitter's listening channel
// Stop() stops the event loop and releases any resources.  Stop is expected to shut down the process cleanly,
// the pipeline process will block until Stop() returns
type Emitter interface {
	Start()
	Init(chan Event)
	Stop()
}

// HTTPPostEmitter listens on the event channel and posts the events to an http server
// Events are serialized into json, and sent via a POST request to the given Uri
// http errors are logged as warnings to the console, and won't stop the Emitter
type HTTPPostEmitter struct {
	uri string
	key string
	pid string

	inflight *sync.WaitGroup
	ch       chan Event
	chstop   chan chan bool
}

// NewHTTPPostEmitter creates a new HTTPPostEmitter
func NewHTTPPostEmitter(uri, key, pid string) *HTTPPostEmitter {
	return &HTTPPostEmitter{
		uri:      uri,
		key:      key,
		pid:      pid,
		chstop:   make(chan chan bool),
		inflight: &sync.WaitGroup{},
	}
}

// Start the emitter
func (e *HTTPPostEmitter) Start() {
	go e.startEventListener()
}

// Init sets the event channel
func (e *HTTPPostEmitter) Init(ch chan Event) {
	e.ch = ch
}

// Stop sends a stop signal and waits for the inflight posts to complete before exiting
func (e *HTTPPostEmitter) Stop() {
	s := make(chan bool)
	e.chstop <- s
	<-s
	e.inflight.Wait()
}

func (e *HTTPPostEmitter) startEventListener() {
	for {
		select {
		case s := <-e.chstop:
			s <- true
			return
		case event := <-e.ch:
			e.inflight.Add(1)
			go func(event Event) {
				defer e.inflight.Done()

				ba, err := event.Emit()
				if err != err {
					log.Printf("EventEmitter Error: %s", err)
					return
				}

				req, err := http.NewRequest("POST", e.uri, bytes.NewBuffer(ba))
				if err != nil {
					log.Printf("EventEmitter Error: %s", err)
					return
				}
				req.Header.Set("Content-Type", "application/json")
				if len(e.pid) > 0 && len(e.key) > 0 {
					req.SetBasicAuth(e.pid, e.key)
				}
				cli := &http.Client{}
				resp, err := cli.Do(req)

				if err != nil {
					log.Printf("EventEmitter Error: %s", err)
					return
				}
				_, err = ioutil.ReadAll(resp.Body)
				if err != nil {
					log.Printf("Response read error: %s", err)
					return
				}
				defer resp.Body.Close()
				if resp.StatusCode != 200 && resp.StatusCode != 201 {
					log.Printf("EventEmitter Error: http error code, expected 200 or 201, got %d, (%s)", resp.StatusCode, ba)
					return
				}
				// fmt.Printf("EventEmitter, got http statuscode:%d for event: %s", resp.StatusCode, event)
			}(event)
		case <-time.After(100 * time.Millisecond):
			continue
			// noop
		}
	}
}

// NewNoopEmitter constructs a NoopEmitter to use with a transporter pipeline.
// a NoopEmitter consumes the events from the listening channel and does nothing with them
// this is useful for cli utilities that dump output to stdout in any case, and don't want
// to clutter the program's output with metrics
func NewNoopEmitter() *NoopEmitter {
	return &NoopEmitter{chstop: make(chan chan bool)}
}

// NoopEmitter consumes the events from the listening channel and does nothing with them
// this is useful for cli utilities that dump output to stdout in any case, and don't want
// to clutter the program's output with metrics
type NoopEmitter struct {
	chstop chan chan bool
	ch     chan Event
}

// Start the event consumer
func (e *NoopEmitter) Start() {
	go func() {
		for {
			select {
			case s := <-e.chstop:
				s <- true
				return
			case <-e.ch:
				continue
			case <-time.After(100 * time.Millisecond):
				continue
			}
		}
	}()
}

// Init sets the event channel
func (e *NoopEmitter) Init(ch chan Event) {
	e.ch = ch
}

// Stop the event consumer
func (e *NoopEmitter) Stop() {
	s := make(chan bool)
	e.chstop <- s
	<-s
}

// NewLogEmitter creates a new LogEmitter
func NewLogEmitter() *LogEmitter {
	return &LogEmitter{
		chstop: make(chan chan bool),
	}
}

// LogEmitter constructs a LogEmitter to use with a transporter pipeline.
// A LogEmitter listens on the event channel and uses go's log package to emit the event,
// eg.
//   2014/11/28 16:56:58 boot map[source:mongo out:mongo]
//   2014/11/28 16:56:58 metrics source recordsIn: 0, recordsOut: 203
//   2014/11/28 16:56:58 exit
//   2014/11/28 16:56:58 metrics source/out recordsIn: 203, recordsOut: 0
type LogEmitter struct {
	chstop chan chan bool
	ch     chan Event
}

// Start the emitter
func (e *LogEmitter) Start() {
	go e.startEventListener()
}

// Init sets the event channel
func (e *LogEmitter) Init(ch chan Event) {
	e.ch = ch
}

// Stop the emitter
func (e *LogEmitter) Stop() {
	s := make(chan bool)
	e.chstop <- s
	<-s
}

func (e *LogEmitter) startEventListener() {
	for {
		select {
		case s := <-e.chstop:
			s <- true
			return
		case event := <-e.ch:
			log.Println(event.String())
		case <-time.After(100 * time.Millisecond):
			continue
			// noop
		}
	}
}

// NewJSONLogEmitter creates a new LogEmitter
func NewJSONLogEmitter() *JSONLogEmitter {
	return &JSONLogEmitter{
		chstop: make(chan chan bool),
	}
}

// JSONLogEmitter constructs a LogEmitter to use with a transporter pipeline.
// A JsonLogEmitter listens on the event channel and uses go's fmt package to emit the event,
// eg.
// {"ts":1436889121,"name":"metrics","path":"source-development.jobs/dest-x.jobs","records":121}
// {"ts":1436889121,"name":"exit","version":"0.0.4","endpoints":{"dest-x.jobs":"mongo","source-development.jobs":"mongo"}}
type JSONLogEmitter struct {
	chstop chan chan bool
	ch     chan Event
}

// Start the emitter
func (e *JSONLogEmitter) Start() {
	go e.startEventListener()
}

// Init sets the event channel
func (e *JSONLogEmitter) Init(ch chan Event) {
	e.ch = ch
}

// Stop the emitter
func (e *JSONLogEmitter) Stop() {
	s := make(chan bool)
	e.chstop <- s
	<-s
}

func (e *JSONLogEmitter) startEventListener() {
	for {
		select {
		case s := <-e.chstop:
			s <- true
			return
		case event := <-e.ch:
			j, err := event.Emit()
			if err != nil {
				continue
			}
			fmt.Println(string(j))
		case <-time.After(100 * time.Millisecond):
			continue
			// noop
		}
	}
}
<|endoftext|>


<commit_before>package middleware

import (
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/aerogo/aero"
	"github.com/aerogo/log"
	"github.com/animenotifier/notify.moe/utils"
)

var request = log.New()
var err = log.New()

// Initialize log files
func init() {
	request.AddOutput(log.File("logs/request.log"))

	err.AddOutput(log.File("logs/error.log"))
	err.AddOutput(os.Stderr)
}

// Log middleware logs every request into logs/request.log and errors into logs/error.log.
func Log() aero.Middleware {
	return func(ctx *aero.Context, next func()) {
		start := time.Now()
		next()
		responseTime := time.Since(start)

		go logRequest(ctx, responseTime)
	}
}

// Logs a single request
func logRequest(ctx *aero.Context, responseTime time.Duration) {
	responseTimeString := strconv.Itoa(int(responseTime.Nanoseconds()/1000000)) + " ms"
	responseTimeString = strings.Repeat(" ", 8-len(responseTimeString)) + responseTimeString

	user := utils.GetUser(ctx)
	ip := ctx.RealIP()

	hostName := "<unknown host>"
	hostNames := GetHostsForIP(ip)

	if len(hostNames) != 0 {
		hostName = hostNames[0]
	}

	// Log every request
	if user != nil {
		request.Info(user.Nick, ip, hostName, responseTimeString, ctx.StatusCode, ctx.URI())
	} else {
		request.Info("[guest]", ip, hostName, responseTimeString, ctx.StatusCode, ctx.URI())
	}

	// Log all requests that failed
	switch ctx.StatusCode {
	case http.StatusOK, http.StatusFound, http.StatusMovedPermanently, http.StatusPermanentRedirect, http.StatusTemporaryRedirect:
		// Ok.

	default:
		err.Error(http.StatusText(ctx.StatusCode), ip, hostName, responseTimeString, ctx.StatusCode, ctx.URI())
	}

	// Notify us about long requests.
	// However ignore requests under /auth/ because those depend on 3rd party servers.
	if responseTime >= 200*time.Millisecond && !strings.HasPrefix(ctx.URI(), "/auth/") {
		err.Error("Long response time", ip, hostName, responseTimeString, ctx.StatusCode, ctx.URI())
	}
}
<commit_msg>Minor changes<commit_after>package middleware

import (
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/aerogo/aero"
	"github.com/aerogo/log"
	"github.com/animenotifier/notify.moe/utils"
)

var request = log.New()
var err = log.New()

// Initialize log files
func init() {
	request.AddOutput(log.File("logs/request.log"))

	err.AddOutput(log.File("logs/error.log"))
	err.AddOutput(os.Stderr)
}

// Log middleware logs every request into logs/request.log and errors into logs/error.log.
func Log() aero.Middleware {
	return func(ctx *aero.Context, next func()) {
		start := time.Now()
		next()
		responseTime := time.Since(start)

		go logRequest(ctx, responseTime)
	}
}

// Logs a single request
func logRequest(ctx *aero.Context, responseTime time.Duration) {
	responseTimeString := strconv.Itoa(int(responseTime.Nanoseconds()/1000000)) + " ms"
	responseTimeString = strings.Repeat(" ", 8-len(responseTimeString)) + responseTimeString

	user := utils.GetUser(ctx)
	ip := ctx.RealIP()

	hostName := "<unknown host>"
	hostNames := GetHostsForIP(ip)

	if len(hostNames) != 0 {
		hostName = hostNames[0]
		hostName = strings.TrimSuffix(hostName, ".")
	}

	// Log every request
	if user != nil {
		request.Info(user.Nick, ip, hostName, responseTimeString, ctx.StatusCode, ctx.URI())
	} else {
		request.Info("[guest]", ip, hostName, responseTimeString, ctx.StatusCode, ctx.URI())
	}

	// Log all requests that failed
	switch ctx.StatusCode {
	case http.StatusOK, http.StatusFound, http.StatusMovedPermanently, http.StatusPermanentRedirect, http.StatusTemporaryRedirect:
		// Ok.

	default:
		err.Error(http.StatusText(ctx.StatusCode), ip, hostName, responseTimeString, ctx.StatusCode, ctx.URI())
	}

	// Notify us about long requests.
	// However ignore requests under /auth/ because those depend on 3rd party servers.
	if responseTime >= 200*time.Millisecond && !strings.HasPrefix(ctx.URI(), "/auth/") {
		err.Error("Long response time", ip, hostName, responseTimeString, ctx.StatusCode, ctx.URI())
	}
}
<|endoftext|>


<commit_before>package haveibeenpwned

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"net/url"
)

//API URL of haveibeenpwned.com
const API = "https://haveibeenpwned.com/api/v2/"

//BreachModel model
type BreachModel struct {
	Name         string   `json:"Name,omitempty"`
	Title        string   `json:"Title,omitempty"`
	Domain       string   `json:"Domain,omitempty"`
	BreachDate   string   `json:"BreachDate,omitempty"`
	AddedDate    string   `json:"AddedDate,omitempty"`
	ModifiedDate string   `json:"ModifiedDate,omitempty"`
	PwnCount     int      `json:"PwnCount,omitempty"`
	Description  string   `json:"Description,omitempty"`
	DataClasses  []string `json:"DataClasses,omitempty"`
	IsVerified   bool     `json:"IsVerified,omitempty"`
	IsFabricated bool     `json:"IsFabricated,omitempty"`
	IsSensitive  bool     `json:"IsSensitive,omitempty"`
	IsRetired    bool     `json:"IsRetired,omitempty"`
	IsSpamList   bool     `json:"IsSpamList,omitempty"`
}

//PasteModel model
type PasteModel struct {
	Source     string `json:"Source,omitempty"`
	ID         string `json:"Id,omitempty"`
	Title      string `json:"Title,omitempty"`
	Date       string `json:"Date,omitempty"`
	EmailCount int    `json:"EmailCount,omitempty"`
}

//BreachedAccount The most common use of the API is to return a list of all breaches a particular account has been involved in. The API takes a single parameter which is the account to be searched for. The account is not case sensitive and will be trimmed of leading or trailing white spaces. The account should always be URL encoded.
func BreachedAccount(account, domainFilter string, truncate, unverified bool) ([]BreachModel, error) {

	res, err := callService("breachedaccount", account, domainFilter, truncate, unverified)
	if err != nil {
		return nil, err
	}
	if res.StatusCode == http.StatusNotFound {
		return nil, nil
	}

	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	breaches := make([]BreachModel, 0)
	if err := json.Unmarshal(body, &breaches); err != nil {
		return nil, err
	}

	return breaches, nil
}

//Breaches return all breaches
func Breaches(domainFilter string) ([]BreachModel, error) {

	res, err := callService("breaches", "", "", false, false)
	if err != nil {
		return nil, err
	}
	if res.StatusCode == http.StatusNotFound {
		return nil, nil
	}

	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	breaches := make([]BreachModel, 0)
	if err := json.Unmarshal(body, &breaches); err != nil {
		return nil, err
	}

	return breaches, nil

}

//Breach return an specific breach by name
func Breach(name string) ([]BreachModel, error) {

	res, err := callService("breach", name, "", false, false)
	if err != nil {
		return nil, err
	}
	if res.StatusCode == http.StatusNotFound {
		return nil, nil
	}

	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	breaches := make([]BreachModel, 0)
	if err := json.Unmarshal(body, &breaches); err != nil {
		return nil, err
	}

	return breaches, nil
}

//PasteAccount return an slice of Pastes for the specific account
func PasteAccount(email string) ([]PasteModel, error) {
	res, err := callService("pasteaccount", email, "", false, false)
	if err != nil {
		return nil, err
	}
	if res.StatusCode == http.StatusNotFound {
		return nil, nil
	}

	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	pastes := make([]PasteModel, 0)
	if err := json.Unmarshal(body, &pastes); err != nil {
		return nil, err
	}

	return pastes, nil

}

func callService(service, account, domainFilter string, truncate, unverified bool) (*http.Response, error) {
	client := &http.Client{}

	u, err := url.Parse(API)
	if err != nil {
		return nil, err
	}

	u.Path += service + "/" + account
	parameters := url.Values{}
	if domainFilter != "" {
		parameters.Add("domain", domainFilter)
	}
	if truncate != false {
		parameters.Add("truncateResponse", "true")
	}
	if unverified != false {
		parameters.Add("includeUnverified", "true")
	}
	u.RawQuery = parameters.Encode()

	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return nil, err
	}

	req.Header.Set("User-Agent", "Go/1.8")
	res, err := client.Do(req)

	if err != nil {
		return nil, err
	}

	switch res.StatusCode {
	case http.StatusBadRequest:
		return nil, errors.New("error: the account does not comply with an acceptable format")
	case http.StatusForbidden:
		return nil, errors.New("error: no user agent has been specified in the request")
	case http.StatusTooManyRequests:
		return nil, errors.New("error: too many requests — the rate limit has been exceeded")
	}

	return res, nil
}
<commit_msg>remove error: from the return, we all know it's an error<commit_after>package haveibeenpwned

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"net/url"
)

//API URL of haveibeenpwned.com
const API = "https://haveibeenpwned.com/api/v2/"

//BreachModel model
type BreachModel struct {
	Name         string   `json:"Name,omitempty"`
	Title        string   `json:"Title,omitempty"`
	Domain       string   `json:"Domain,omitempty"`
	BreachDate   string   `json:"BreachDate,omitempty"`
	AddedDate    string   `json:"AddedDate,omitempty"`
	ModifiedDate string   `json:"ModifiedDate,omitempty"`
	PwnCount     int      `json:"PwnCount,omitempty"`
	Description  string   `json:"Description,omitempty"`
	DataClasses  []string `json:"DataClasses,omitempty"`
	IsVerified   bool     `json:"IsVerified,omitempty"`
	IsFabricated bool     `json:"IsFabricated,omitempty"`
	IsSensitive  bool     `json:"IsSensitive,omitempty"`
	IsRetired    bool     `json:"IsRetired,omitempty"`
	IsSpamList   bool     `json:"IsSpamList,omitempty"`
}

//PasteModel model
type PasteModel struct {
	Source     string `json:"Source,omitempty"`
	ID         string `json:"Id,omitempty"`
	Title      string `json:"Title,omitempty"`
	Date       string `json:"Date,omitempty"`
	EmailCount int    `json:"EmailCount,omitempty"`
}

//BreachedAccount The most common use of the API is to return a list of all breaches a particular account has been involved in. The API takes a single parameter which is the account to be searched for. The account is not case sensitive and will be trimmed of leading or trailing white spaces. The account should always be URL encoded.
func BreachedAccount(account, domainFilter string, truncate, unverified bool) ([]BreachModel, error) {

	res, err := callService("breachedaccount", account, domainFilter, truncate, unverified)
	if err != nil {
		return nil, err
	}
	if res.StatusCode == http.StatusNotFound {
		return nil, nil
	}

	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	breaches := make([]BreachModel, 0)
	if err := json.Unmarshal(body, &breaches); err != nil {
		return nil, err
	}

	return breaches, nil
}

//Breaches return all breaches
func Breaches(domainFilter string) ([]BreachModel, error) {

	res, err := callService("breaches", "", "", false, false)
	if err != nil {
		return nil, err
	}
	if res.StatusCode == http.StatusNotFound {
		return nil, nil
	}

	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	breaches := make([]BreachModel, 0)
	if err := json.Unmarshal(body, &breaches); err != nil {
		return nil, err
	}

	return breaches, nil

}

//Breach return an specific breach by name
func Breach(name string) ([]BreachModel, error) {

	res, err := callService("breach", name, "", false, false)
	if err != nil {
		return nil, err
	}
	if res.StatusCode == http.StatusNotFound {
		return nil, nil
	}

	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	breaches := make([]BreachModel, 0)
	if err := json.Unmarshal(body, &breaches); err != nil {
		return nil, err
	}

	return breaches, nil
}

//PasteAccount return an slice of Pastes for the specific account
func PasteAccount(email string) ([]PasteModel, error) {
	res, err := callService("pasteaccount", email, "", false, false)
	if err != nil {
		return nil, err
	}
	if res.StatusCode == http.StatusNotFound {
		return nil, nil
	}

	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	pastes := make([]PasteModel, 0)
	if err := json.Unmarshal(body, &pastes); err != nil {
		return nil, err
	}

	return pastes, nil

}

func callService(service, account, domainFilter string, truncate, unverified bool) (*http.Response, error) {
	client := &http.Client{}

	u, err := url.Parse(API)
	if err != nil {
		return nil, err
	}

	u.Path += service + "/" + account
	parameters := url.Values{}
	if domainFilter != "" {
		parameters.Add("domain", domainFilter)
	}
	if truncate != false {
		parameters.Add("truncateResponse", "true")
	}
	if unverified != false {
		parameters.Add("includeUnverified", "true")
	}
	u.RawQuery = parameters.Encode()

	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return nil, err
	}

	req.Header.Set("User-Agent", "Go/1.8")
	res, err := client.Do(req)

	if err != nil {
		return nil, err
	}

	switch res.StatusCode {
	case http.StatusBadRequest:
		return nil, errors.New("the account does not comply with an acceptable format")
	case http.StatusForbidden:
		return nil, errors.New("no user agent has been specified in the request")
	case http.StatusTooManyRequests:
		return nil, errors.New("too many requests — the rate limit has been exceeded")
	}

	return res, nil
}
<|endoftext|>


<commit_before>package syslog_test

import (
	"fmt"
	"time"

	"github.com/jtarchie/syslog/pkg/log"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

const validMessage = `<34>1 2003-10-11T22:14:15.003Z mymachine.example.com su 12345 98765 [exampleSDID@32473 iut="3" eventSource="Application" eventID="1011"] 'su root' failed for lonvick on /dev/pts/8`

func ParseLogMessageTests(message string) {
	var (
		payload []byte
		log     *syslog.Log
	)

	BeforeEach(func() {
		payload = []byte(message)
		log = &syslog.Log{}
	})

	It("parses valid messages", func() {
		_, offset, err := syslog.Parse(payload)
		Expect(offset).ToNot(Equal(0))
		Expect(err).ToNot(HaveOccurred())
	})

	It("renders back to its original format", func() {
		log, _, _ := syslog.Parse(payload)
		Expect(log.String()).To(Equal(validMessage))
	})

	It("sets the version", func() {
		log, _, _ := syslog.Parse(payload)
		Expect(log.Version()).To(Equal(1))
	})

	Context("with the priority", func() {
		It("returns the severity", func() {
			log, _, _ := syslog.Parse(payload)
			Expect(log.Severity()).To(Equal(2))
		})

		It("returns the facility", func() {
			log, _, _ := syslog.Parse(payload)
			Expect(log.Facility()).To(Equal(4))
		})

		It("sets the priority", func() {
			log, _, _ := syslog.Parse(payload)
			Expect(log.Priority()).To(Equal(34))
		})
	})

	It("returns a valid date object", func() {
		log, _, _ := syslog.Parse(payload)
		Expect(log.Timestamp().String()).To(Equal("2003-10-11 22:14:15.003 +0000 UTC"))
	})

	It("sets the hostname", func() {
		log, _, _ := syslog.Parse(payload)
		Expect(log.Hostname()).To(BeEquivalentTo("mymachine.example.com"))
	})

	It("sets the app name", func() {
		log, _, _ := syslog.Parse(payload)
		Expect(log.Appname()).To(BeEquivalentTo("su"))
	})

	It("sets the proc id", func() {
		log, _, _ := syslog.Parse(payload)
		Expect(log.ProcID()).To(BeEquivalentTo("12345"))
	})

	It("sets the log id", func() {
		log, _, _ := syslog.Parse(payload)
		Expect(log.MsgID()).To(BeEquivalentTo("98765"))
	})

	It("sets structure data", func() {
		log, _, _ := syslog.Parse(payload)
		data := log.StructureData()[0]
		Expect(data.ID()).To(BeEquivalentTo("exampleSDID@32473"))
		Expect(data.Properties()).To(BeEquivalentTo([]syslog.Property{
			{"iut", "3"},
			{"eventSource", "Application"},
			{"eventID", "1011"},
		}))
	})

	It("sets the message", func() {
		log, _, _ := syslog.Parse(payload)
		Expect(log.Message()).To(BeEquivalentTo("'su root' failed for lonvick on /dev/pts/8"))
	})

}

var _ = Describe("with a standard payload", func() {
	It("returns the correct offset", func() {
		_, offset, _ := syslog.Parse([]byte(validMessage))
		Expect(offset).To(Equal(len(validMessage)))
	})

	ParseLogMessageTests(validMessage)

	Context("with the hostname", func() {
		It("is empty when '-'", func() {
			payload := []byte("<34>1 2003-10-11T22:14:15.003Z - su - - - 'su root' failed for lonvick on /dev/pts/8")
			log, _, _ := syslog.Parse(payload)
			Expect(log.Hostname()).To(BeEmpty())
			Expect(log.String()).To(BeEquivalentTo(payload))
		})
	})

	Context("with the app name", func() {
		It("is empty when '-'", func() {
			payload := []byte("<34>1 2003-10-11T22:14:15.003Z - - - - - 'su root' failed for lonvick on /dev/pts/8")
			log, _, _ := syslog.Parse(payload)
			Expect(log.Appname()).To(BeEmpty())
			Expect(log.String()).To(BeEquivalentTo(payload))
		})
	})

	Context("with the proc id", func() {
		It("is empty when '-'", func() {
			payload := []byte("<34>1 2003-10-11T22:14:15.003Z - - - - - 'su root' failed for lonvick on /dev/pts/8")
			log, _, _ := syslog.Parse(payload)
			Expect(log.ProcID()).To(BeEmpty())
			Expect(log.String()).To(BeEquivalentTo(payload))
		})
	})

	Context("with the log id", func() {
		It("is empty when '-'", func() {
			payload := []byte("<34>1 2003-10-11T22:14:15.003Z - - - - - 'su root' failed for lonvick on /dev/pts/8")
			log, _, _ := syslog.Parse(payload)
			Expect(log.MsgID()).To(BeEmpty())
			Expect(log.String()).To(BeEquivalentTo(payload))
		})
	})

	Context("with structure data", func() {
		It("is empty when '-'", func() {
			payload := []byte("<34>1 2003-10-11T22:14:15.003Z - - - - - 'su root' failed for lonvick on /dev/pts/8")
			log, _, _ := syslog.Parse(payload)
			Expect(log.StructureData()).To(HaveLen(0))
			Expect(log.String()).To(BeEquivalentTo(payload))
		})

		It("allows character escaping", func() {
			payload := []byte(`<29>50 2016-01-15T01:00:43Z hn S - - [my@id1 a="1" b="\"" c="\\" d="\]" e="\"There are \\many things here[1\]\""]`)
			log, _, err := syslog.Parse(payload)
			Expect(err).ToNot(HaveOccurred())
			data := log.StructureData()[0]
			Expect(data.ID()).To(BeEquivalentTo("my@id1"))
			Expect(data.Properties()).To(BeEquivalentTo([]syslog.Property{
				{"a", "1"},
				{"b", `"`},
				{"c", `\`},
				{"d", `]`},
				{"e", `"There are \many things here[1]"`},
			}))
			Expect(log.String()).To(BeEquivalentTo(payload))
		})
	})

	Context("with a message", func() {
		It("sets empty for no message", func() {
			payload := []byte("<34>1 2003-10-11T22:14:15.003Z - - - - -")
			log, _, _ := syslog.Parse(payload)
			Expect(log.Message()).To(BeEmpty())
			Expect(log.String()).To(BeEquivalentTo(payload))
		})
	})

	Context("with multiple structure data elements", func() {
		It("parses them all", func() {
			payload := []byte(`<29>50 2016-01-15T01:00:43Z hn S - - [my@id1 k="v"][my@id2 c="val"]`)
			log, _, err := syslog.Parse(payload)
			Expect(err).ToNot(HaveOccurred())
			data := log.StructureData()[0]
			Expect(data.ID()).To(BeEquivalentTo("my@id1"))
			Expect(data.Properties()).To(BeEquivalentTo([]syslog.Property{
				{"k", "v"},
			}))
			data = log.StructureData()[1]
			Expect(data.ID()).To(BeEquivalentTo("my@id2"))
			Expect(data.Properties()).To(BeEquivalentTo([]syslog.Property{
				{"c", "val"},
			}))
		})
	})

	Context("with the example timestamps from the RFC", func() {
		It("can parse them all", func() {
			timestamp := func(t string) time.Time {
				payload := []byte("<34>1 " + t + " mymachine.example.com su - - - 'su root' failed for lonvick on /dev/pts/8")
				log, _, err := syslog.Parse(payload)
				Expect(err).ToNot(HaveOccurred())

				return log.Timestamp()
			}

			Expect(timestamp("2003-10-11T22:14:15.00003Z").String()).To(Equal("2003-10-11 22:14:15.00003 +0000 UTC"))
			Expect(timestamp("1985-04-12T23:20:50.52Z").String()).To(Equal("1985-04-12 23:20:50.52 +0000 UTC"))
			Expect(timestamp("1985-04-12T23:20:50.52+00:00").String()).To(Equal("1985-04-12 23:20:50.52 +0000 UTC"))
			Expect(timestamp("1985-04-12T23:20:50.52+02:00").String()).To(Equal("1985-04-12 21:20:50.52 +0000 UTC"))
			Expect(timestamp("1985-04-12T18:20:50.52-02:00").String()).To(Equal("1985-04-12 20:20:50.52 +0000 UTC"))
		})

		It("fails parsing on unsupported formats", func() {
			payload := []byte("<34>1 2003-10-11T22:14:15.003Z07:00 mymachine.example.com su - - - 'su root' failed for lonvick on /dev/pts/8")
			_, n, err := syslog.Parse(payload)
			Expect(n).To(Equal(0))
			Expect(err).To(HaveOccurred())
		})

		It("is sets empty when '-'", func() {
			payload := []byte("<34>1 - - su - - - 'su root' failed for lonvick on /dev/pts/8")
			log, _, _ := syslog.Parse(payload)
			Expect(log.Timestamp().IsZero()).To(BeTrue())
			Expect(log.String()).To(BeEquivalentTo(payload))
		})
	})
})

var _ = Describe("with a TCP payload", func() {
	Context("with a single message", func() {
		payload := fmt.Sprintf("%d %s", len(validMessage), validMessage)

		It("returns the correct offset", func() {
			_, offset, _ := syslog.Parse([]byte(payload))
			Expect(offset).To(Equal(len(validMessage) + 4))
		})

		ParseLogMessageTests(payload)
	})

	Context("with a multiple payload", func() {
		payload := fmt.Sprintf("%d %s%d %s", len(validMessage), validMessage, len(validMessage), validMessage)

		It("returns the correct offset", func() {
			_, offset, _ := syslog.Parse([]byte(payload))
			Expect(offset).To(Equal(len(validMessage) + 4))
		})
		ParseLogMessageTests(payload)
	})

	It("works with these messages", func() {
		_, _, err := syslog.Parse([]byte(`<4>1 2018-02-02T18:30:55.968Z worker-25.example.com flood 64974 937 - This is message 937 from worker`))
		Expect(err).ToNot(HaveOccurred())
	})
})
<commit_msg>fix go vet<commit_after>package syslog_test

import (
	"fmt"
	"time"

	"github.com/jtarchie/syslog/pkg/log"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

const validMessage = `<34>1 2003-10-11T22:14:15.003Z mymachine.example.com su 12345 98765 [exampleSDID@32473 iut="3" eventSource="Application" eventID="1011"] 'su root' failed for lonvick on /dev/pts/8`

func ParseLogMessageTests(message string) {
	var (
		payload []byte
	)

	BeforeEach(func() {
		payload = []byte(message)
	})

	It("parses valid messages", func() {
		_, offset, err := syslog.Parse(payload)
		Expect(offset).ToNot(Equal(0))
		Expect(err).ToNot(HaveOccurred())
	})

	It("renders back to its original format", func() {
		log, _, _ := syslog.Parse(payload)
		Expect(log.String()).To(Equal(validMessage))
	})

	It("sets the version", func() {
		log, _, _ := syslog.Parse(payload)
		Expect(log.Version()).To(Equal(1))
	})

	Context("with the priority", func() {
		It("returns the severity", func() {
			log, _, _ := syslog.Parse(payload)
			Expect(log.Severity()).To(Equal(2))
		})

		It("returns the facility", func() {
			log, _, _ := syslog.Parse(payload)
			Expect(log.Facility()).To(Equal(4))
		})

		It("sets the priority", func() {
			log, _, _ := syslog.Parse(payload)
			Expect(log.Priority()).To(Equal(34))
		})
	})

	It("returns a valid date object", func() {
		log, _, _ := syslog.Parse(payload)
		Expect(log.Timestamp().String()).To(Equal("2003-10-11 22:14:15.003 +0000 UTC"))
	})

	It("sets the hostname", func() {
		log, _, _ := syslog.Parse(payload)
		Expect(log.Hostname()).To(BeEquivalentTo("mymachine.example.com"))
	})

	It("sets the app name", func() {
		log, _, _ := syslog.Parse(payload)
		Expect(log.Appname()).To(BeEquivalentTo("su"))
	})

	It("sets the proc id", func() {
		log, _, _ := syslog.Parse(payload)
		Expect(log.ProcID()).To(BeEquivalentTo("12345"))
	})

	It("sets the log id", func() {
		log, _, _ := syslog.Parse(payload)
		Expect(log.MsgID()).To(BeEquivalentTo("98765"))
	})

	It("sets structure data", func() {
		log, _, _ := syslog.Parse(payload)
		data := log.StructureData()[0]
		Expect(data.ID()).To(BeEquivalentTo("exampleSDID@32473"))
		Expect(data.Properties()).To(BeEquivalentTo([]syslog.Property{
			{"iut", "3"},
			{"eventSource", "Application"},
			{"eventID", "1011"},
		}))
	})

	It("sets the message", func() {
		log, _, _ := syslog.Parse(payload)
		Expect(log.Message()).To(BeEquivalentTo("'su root' failed for lonvick on /dev/pts/8"))
	})

}

var _ = Describe("with a standard payload", func() {
	It("returns the correct offset", func() {
		_, offset, _ := syslog.Parse([]byte(validMessage))
		Expect(offset).To(Equal(len(validMessage)))
	})

	ParseLogMessageTests(validMessage)

	Context("with the hostname", func() {
		It("is empty when '-'", func() {
			payload := []byte("<34>1 2003-10-11T22:14:15.003Z - su - - - 'su root' failed for lonvick on /dev/pts/8")
			log, _, _ := syslog.Parse(payload)
			Expect(log.Hostname()).To(BeEmpty())
			Expect(log.String()).To(BeEquivalentTo(payload))
		})
	})

	Context("with the app name", func() {
		It("is empty when '-'", func() {
			payload := []byte("<34>1 2003-10-11T22:14:15.003Z - - - - - 'su root' failed for lonvick on /dev/pts/8")
			log, _, _ := syslog.Parse(payload)
			Expect(log.Appname()).To(BeEmpty())
			Expect(log.String()).To(BeEquivalentTo(payload))
		})
	})

	Context("with the proc id", func() {
		It("is empty when '-'", func() {
			payload := []byte("<34>1 2003-10-11T22:14:15.003Z - - - - - 'su root' failed for lonvick on /dev/pts/8")
			log, _, _ := syslog.Parse(payload)
			Expect(log.ProcID()).To(BeEmpty())
			Expect(log.String()).To(BeEquivalentTo(payload))
		})
	})

	Context("with the log id", func() {
		It("is empty when '-'", func() {
			payload := []byte("<34>1 2003-10-11T22:14:15.003Z - - - - - 'su root' failed for lonvick on /dev/pts/8")
			log, _, _ := syslog.Parse(payload)
			Expect(log.MsgID()).To(BeEmpty())
			Expect(log.String()).To(BeEquivalentTo(payload))
		})
	})

	Context("with structure data", func() {
		It("is empty when '-'", func() {
			payload := []byte("<34>1 2003-10-11T22:14:15.003Z - - - - - 'su root' failed for lonvick on /dev/pts/8")
			log, _, _ := syslog.Parse(payload)
			Expect(log.StructureData()).To(HaveLen(0))
			Expect(log.String()).To(BeEquivalentTo(payload))
		})

		It("allows character escaping", func() {
			payload := []byte(`<29>50 2016-01-15T01:00:43Z hn S - - [my@id1 a="1" b="\"" c="\\" d="\]" e="\"There are \\many things here[1\]\""]`)
			log, _, err := syslog.Parse(payload)
			Expect(err).ToNot(HaveOccurred())
			data := log.StructureData()[0]
			Expect(data.ID()).To(BeEquivalentTo("my@id1"))
			Expect(data.Properties()).To(BeEquivalentTo([]syslog.Property{
				{"a", "1"},
				{"b", `"`},
				{"c", `\`},
				{"d", `]`},
				{"e", `"There are \many things here[1]"`},
			}))
			Expect(log.String()).To(BeEquivalentTo(payload))
		})
	})

	Context("with a message", func() {
		It("sets empty for no message", func() {
			payload := []byte("<34>1 2003-10-11T22:14:15.003Z - - - - -")
			log, _, _ := syslog.Parse(payload)
			Expect(log.Message()).To(BeEmpty())
			Expect(log.String()).To(BeEquivalentTo(payload))
		})
	})

	Context("with multiple structure data elements", func() {
		It("parses them all", func() {
			payload := []byte(`<29>50 2016-01-15T01:00:43Z hn S - - [my@id1 k="v"][my@id2 c="val"]`)
			log, _, err := syslog.Parse(payload)
			Expect(err).ToNot(HaveOccurred())
			data := log.StructureData()[0]
			Expect(data.ID()).To(BeEquivalentTo("my@id1"))
			Expect(data.Properties()).To(BeEquivalentTo([]syslog.Property{
				{"k", "v"},
			}))
			data = log.StructureData()[1]
			Expect(data.ID()).To(BeEquivalentTo("my@id2"))
			Expect(data.Properties()).To(BeEquivalentTo([]syslog.Property{
				{"c", "val"},
			}))
		})
	})

	Context("with the example timestamps from the RFC", func() {
		It("can parse them all", func() {
			timestamp := func(t string) time.Time {
				payload := []byte("<34>1 " + t + " mymachine.example.com su - - - 'su root' failed for lonvick on /dev/pts/8")
				log, _, err := syslog.Parse(payload)
				Expect(err).ToNot(HaveOccurred())

				return log.Timestamp()
			}

			Expect(timestamp("2003-10-11T22:14:15.00003Z").String()).To(Equal("2003-10-11 22:14:15.00003 +0000 UTC"))
			Expect(timestamp("1985-04-12T23:20:50.52Z").String()).To(Equal("1985-04-12 23:20:50.52 +0000 UTC"))
			Expect(timestamp("1985-04-12T23:20:50.52+00:00").String()).To(Equal("1985-04-12 23:20:50.52 +0000 UTC"))
			Expect(timestamp("1985-04-12T23:20:50.52+02:00").String()).To(Equal("1985-04-12 21:20:50.52 +0000 UTC"))
			Expect(timestamp("1985-04-12T18:20:50.52-02:00").String()).To(Equal("1985-04-12 20:20:50.52 +0000 UTC"))
		})

		It("fails parsing on unsupported formats", func() {
			payload := []byte("<34>1 2003-10-11T22:14:15.003Z07:00 mymachine.example.com su - - - 'su root' failed for lonvick on /dev/pts/8")
			_, n, err := syslog.Parse(payload)
			Expect(n).To(Equal(0))
			Expect(err).To(HaveOccurred())
		})

		It("is sets empty when '-'", func() {
			payload := []byte("<34>1 - - su - - - 'su root' failed for lonvick on /dev/pts/8")
			log, _, _ := syslog.Parse(payload)
			Expect(log.Timestamp().IsZero()).To(BeTrue())
			Expect(log.String()).To(BeEquivalentTo(payload))
		})
	})
})

var _ = Describe("with a TCP payload", func() {
	Context("with a single message", func() {
		payload := fmt.Sprintf("%d %s", len(validMessage), validMessage)

		It("returns the correct offset", func() {
			_, offset, _ := syslog.Parse([]byte(payload))
			Expect(offset).To(Equal(len(validMessage) + 4))
		})

		ParseLogMessageTests(payload)
	})

	Context("with a multiple payload", func() {
		payload := fmt.Sprintf("%d %s%d %s", len(validMessage), validMessage, len(validMessage), validMessage)

		It("returns the correct offset", func() {
			_, offset, _ := syslog.Parse([]byte(payload))
			Expect(offset).To(Equal(len(validMessage) + 4))
		})
		ParseLogMessageTests(payload)
	})

	It("works with these messages", func() {
		_, _, err := syslog.Parse([]byte(`<4>1 2018-02-02T18:30:55.968Z worker-25.example.com flood 64974 937 - This is message 937 from worker`))
		Expect(err).ToNot(HaveOccurred())
	})
})
<|endoftext|>


<commit_before>/*
Copyright 2014 Google Inc. All rights reserved.

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

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

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

package master

import (
	"fmt"
	"time"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"

	"github.com/golang/glog"
)

func (m *Master) serviceWriterLoop(stop chan struct{}) {
	for {
		// Update service & endpoint records.
		// TODO: when it becomes possible to change this stuff,
		// stop polling and start watching.
		// TODO: add endpoints of all replicas, not just the elected master.
		if m.readWriteServer != "" {
			// TODO: the public port should be part of the argument here, port will not always be 443
			if err := m.createMasterServiceIfNeeded("kubernetes", 443); err != nil {
				glog.Errorf("Can't create rw service: %v", err)
			}
			if err := m.ensureEndpointsContain("kubernetes", m.readWriteServer); err != nil {
				glog.Errorf("Can't create rw endpoints: %v", err)
			}
		}

		select {
		case <-stop:
			return
		case <-time.After(10 * time.Second):
		}
	}
}

func (m *Master) roServiceWriterLoop(stop chan struct{}) {
	for {
		// Update service & endpoint records.
		// TODO: when it becomes possible to change this stuff,
		// stop polling and start watching.
		if m.readOnlyServer != "" {
			// TODO: the public port should be part of the argument here, port will not always be 80
			if err := m.createMasterServiceIfNeeded("kubernetes-ro", 80); err != nil {
				glog.Errorf("Can't create ro service: %v", err)
			}
			if err := m.ensureEndpointsContain("kubernetes-ro", m.readOnlyServer); err != nil {
				glog.Errorf("Can't create ro endpoints: %v", err)
			}
		}

		select {
		case <-stop:
			return
		case <-time.After(10 * time.Second):
		}
	}
}

// createMasterServiceIfNeeded will create the specified service if it
// doesn't already exist.
func (m *Master) createMasterServiceIfNeeded(serviceName string, port int) error {
	ctx := api.NewDefaultContext()
	if _, err := m.serviceRegistry.GetService(ctx, serviceName); err == nil {
		// The service already exists.
		return nil
	}
	svc := &api.Service{
		ObjectMeta: api.ObjectMeta{
			Name:      serviceName,
			Namespace: api.NamespaceDefault,
			Labels:    map[string]string{"provider": "kubernetes", "component": "apiserver"},
		},
		Spec: api.ServiceSpec{
			Port: port,
			// maintained by this code, not by the pod selector
			Selector: nil,
		},
	}
	// Kids, don't do this at home: this is a hack. There's no good way to call the business
	// logic which lives in the REST object from here.
	c, err := m.storage["services"].(apiserver.RESTCreater).Create(ctx, svc)
	if err != nil {
		return err
	}
	resp := <-c
	if _, ok := resp.Object.(*api.Service); ok {
		// If all worked, we get back an *api.Service object.
		return nil
	}
	return fmt.Errorf("unexpected response: %#v", resp)
}

// ensureEndpointsContain sets the endpoints for the given service. Also removes
// excess endpoints (as determined by m.masterCount). Extra endpoints could appear
// in the list if, for example, the master starts running on a different machine,
// changing IP addresses.
func (m *Master) ensureEndpointsContain(serviceName string, endpoint string) error {
	ctx := api.NewDefaultContext()
	e, err := m.endpointRegistry.GetEndpoints(ctx, serviceName)
	if err != nil {
		e = &api.Endpoints{
			ObjectMeta: api.ObjectMeta{
				Name:      serviceName,
				Namespace: api.NamespaceDefault,
			},
		}
	}
	found := false
	for i := range e.Endpoints {
		if e.Endpoints[i] == endpoint {
			found = true
			break
		}
	}
	if !found {
		e.Endpoints = append(e.Endpoints, endpoint)
	}
	if len(e.Endpoints) > m.masterCount {
		// We append to the end and remove from the beginning, so this should
		// converge rapidly with all masters performing this operation.
		e.Endpoints = e.Endpoints[len(e.Endpoints)-m.masterCount:]
	} else if found {
		// We didn't make any changes, no need to actually call update.
		return nil
	}
	return m.endpointRegistry.UpdateEndpoints(ctx, e)
}
<commit_msg>Print the returned object in the event of a publish error<commit_after>/*
Copyright 2014 Google Inc. All rights reserved.

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

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

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

package master

import (
	"fmt"
	"time"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"

	"github.com/golang/glog"
)

func (m *Master) serviceWriterLoop(stop chan struct{}) {
	for {
		// Update service & endpoint records.
		// TODO: when it becomes possible to change this stuff,
		// stop polling and start watching.
		// TODO: add endpoints of all replicas, not just the elected master.
		if m.readWriteServer != "" {
			// TODO: the public port should be part of the argument here, port will not always be 443
			if err := m.createMasterServiceIfNeeded("kubernetes", 443); err != nil {
				glog.Errorf("Can't create rw service: %v", err)
			}
			if err := m.ensureEndpointsContain("kubernetes", m.readWriteServer); err != nil {
				glog.Errorf("Can't create rw endpoints: %v", err)
			}
		}

		select {
		case <-stop:
			return
		case <-time.After(10 * time.Second):
		}
	}
}

func (m *Master) roServiceWriterLoop(stop chan struct{}) {
	for {
		// Update service & endpoint records.
		// TODO: when it becomes possible to change this stuff,
		// stop polling and start watching.
		if m.readOnlyServer != "" {
			// TODO: the public port should be part of the argument here, port will not always be 80
			if err := m.createMasterServiceIfNeeded("kubernetes-ro", 80); err != nil {
				glog.Errorf("Can't create ro service: %v", err)
			}
			if err := m.ensureEndpointsContain("kubernetes-ro", m.readOnlyServer); err != nil {
				glog.Errorf("Can't create ro endpoints: %v", err)
			}
		}

		select {
		case <-stop:
			return
		case <-time.After(10 * time.Second):
		}
	}
}

// createMasterServiceIfNeeded will create the specified service if it
// doesn't already exist.
func (m *Master) createMasterServiceIfNeeded(serviceName string, port int) error {
	ctx := api.NewDefaultContext()
	if _, err := m.serviceRegistry.GetService(ctx, serviceName); err == nil {
		// The service already exists.
		return nil
	}
	svc := &api.Service{
		ObjectMeta: api.ObjectMeta{
			Name:      serviceName,
			Namespace: api.NamespaceDefault,
			Labels:    map[string]string{"provider": "kubernetes", "component": "apiserver"},
		},
		Spec: api.ServiceSpec{
			Port: port,
			// maintained by this code, not by the pod selector
			Selector: nil,
		},
	}
	// Kids, don't do this at home: this is a hack. There's no good way to call the business
	// logic which lives in the REST object from here.
	c, err := m.storage["services"].(apiserver.RESTCreater).Create(ctx, svc)
	if err != nil {
		return err
	}
	resp := <-c
	if _, ok := resp.Object.(*api.Service); ok {
		// If all worked, we get back an *api.Service object.
		return nil
	}
	return fmt.Errorf("unexpected response: %#v", resp.Object)
}

// ensureEndpointsContain sets the endpoints for the given service. Also removes
// excess endpoints (as determined by m.masterCount). Extra endpoints could appear
// in the list if, for example, the master starts running on a different machine,
// changing IP addresses.
func (m *Master) ensureEndpointsContain(serviceName string, endpoint string) error {
	ctx := api.NewDefaultContext()
	e, err := m.endpointRegistry.GetEndpoints(ctx, serviceName)
	if err != nil {
		e = &api.Endpoints{
			ObjectMeta: api.ObjectMeta{
				Name:      serviceName,
				Namespace: api.NamespaceDefault,
			},
		}
	}
	found := false
	for i := range e.Endpoints {
		if e.Endpoints[i] == endpoint {
			found = true
			break
		}
	}
	if !found {
		e.Endpoints = append(e.Endpoints, endpoint)
	}
	if len(e.Endpoints) > m.masterCount {
		// We append to the end and remove from the beginning, so this should
		// converge rapidly with all masters performing this operation.
		e.Endpoints = e.Endpoints[len(e.Endpoints)-m.masterCount:]
	} else if found {
		// We didn't make any changes, no need to actually call update.
		return nil
	}
	return m.endpointRegistry.UpdateEndpoints(ctx, e)
}
<|endoftext|>