<commit_before>/*
Copyright 2016 The Kubernetes Authors 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 service

import (
	"fmt"
	"time"

	federation_release_1_3 "k8s.io/kubernetes/federation/client/clientset_generated/federation_release_1_3"
	"k8s.io/kubernetes/pkg/api/errors"
	v1 "k8s.io/kubernetes/pkg/api/v1"
	cache "k8s.io/kubernetes/pkg/client/cache"
	"k8s.io/kubernetes/pkg/controller"

	"github.com/golang/glog"
	"reflect"
	"sort"
)

// worker runs a worker thread that just dequeues items, processes them, and marks them done.
// It enforces that the syncHandler is never invoked concurrently with the same key.
func (sc *ServiceController) clusterServiceWorker() {
	fedClient := sc.federationClient
	for clusterName, cache := range sc.clusterCache.clientMap {
		go func(cache *clusterCache, clusterName string) {
			for {
				func() {
					key, quit := cache.serviceQueue.Get()
					defer cache.serviceQueue.Done(key)
					if quit {
						return
					}
					err := sc.clusterCache.syncService(key.(string), clusterName, cache, sc.serviceCache, fedClient, sc)
					if err != nil {
						glog.Errorf("Failed to sync service: %+v", err)
					}
				}()
			}
		}(cache, clusterName)
	}
}

// Whenever there is change on service, the federation service should be updated
func (cc *clusterClientCache) syncService(key, clusterName string, clusterCache *clusterCache, serviceCache *serviceCache, fedClient federation_release_1_3.Interface, sc *ServiceController) error {
	// obj holds the latest service info from apiserver, return if there is no federation cache for the service
	cachedService, ok := serviceCache.get(key)
	if !ok {
		// if serviceCache does not exists, that means the service is not created by federation, we should skip it
		return nil
	}
	serviceInterface, exists, err := clusterCache.serviceStore.GetByKey(key)
	if err != nil {
		glog.Infof("Did not successfully get %v from store: %v, will retry later", key, err)
		clusterCache.serviceQueue.Add(key)
		return err
	}
	var needUpdate bool
	if exists {
		service, ok := serviceInterface.(*v1.Service)
		if ok {
			glog.V(4).Infof("Found service for federation service %s/%s from cluster %s", service.Namespace, service.Name, clusterName)
			needUpdate = cc.processServiceUpdate(cachedService, service, clusterName)
		} else {
			_, ok := serviceInterface.(cache.DeletedFinalStateUnknown)
			if !ok {
				return fmt.Errorf("Object contained wasn't a service or a deleted key: %+v", serviceInterface)
			}
			glog.Infof("Found tombstone for %v", key)
			needUpdate = cc.processServiceDeletion(cachedService, clusterName)
		}
	} else {
		glog.Infof("Can not get service %v for cluster %s from serviceStore", key, clusterName)
		needUpdate = cc.processServiceDeletion(cachedService, clusterName)
	}

	if needUpdate {
		for i := 0; i < clientRetryCount; i++ {
			if err := sc.ensureDnsRecords(clusterName, cachedService); err == nil {
				break
			}
			glog.V(4).Infof("Error ensuring DNS Records for service %s on cluster %s: %v", key, clusterName, err)
			time.Sleep(cachedService.nextDNSUpdateDelay())
			clusterCache.serviceQueue.Add(key)
			// did not retry here as we still want to persist federation apiserver even ensure dns records fails
		}
		err := cc.persistFedServiceUpdate(cachedService, fedClient)
		if err == nil {
			cachedService.appliedState = cachedService.lastState
			cachedService.resetFedUpdateDelay()
		} else {
			if err != nil {
				glog.Errorf("Failed to sync service: %+v, put back to service queue", err)
				clusterCache.serviceQueue.Add(key)
			}
		}
	}
	return nil
}

// processServiceDeletion is triggered when a service is delete from underlying k8s cluster
// the deletion function will wip out the cached ingress info of the service from federation service ingress
// the function returns a bool to indicate if actual update happened on federation service cache
// and if the federation service cache is updated, the updated info should be post to federation apiserver
func (cc *clusterClientCache) processServiceDeletion(cachedService *cachedService, clusterName string) bool {
	cachedService.rwlock.Lock()
	defer cachedService.rwlock.Unlock()
	cachedStatus, ok := cachedService.serviceStatusMap[clusterName]
	// cached status found, remove ingress info from federation service cache
	if ok {
		cachedFedServiceStatus := cachedService.lastState.Status.LoadBalancer
		removeIndexes := []int{}
		for i, fed := range cachedFedServiceStatus.Ingress {
			for _, new := range cachedStatus.Ingress {
				// remove if same ingress record found
				if new.IP == fed.IP && new.Hostname == fed.Hostname {
					removeIndexes = append(removeIndexes, i)
				}
			}
		}
		sort.Ints(removeIndexes)
		for i := len(removeIndexes) - 1; i >= 0; i-- {
			cachedFedServiceStatus.Ingress = append(cachedFedServiceStatus.Ingress[:removeIndexes[i]], cachedFedServiceStatus.Ingress[removeIndexes[i]+1:]...)
			glog.V(4).Infof("Remove old ingress %d for service %s/%s", removeIndexes[i], cachedService.lastState.Namespace, cachedService.lastState.Name)
		}
		delete(cachedService.serviceStatusMap, clusterName)
		delete(cachedService.endpointMap, clusterName)
		cachedService.lastState.Status.LoadBalancer = cachedFedServiceStatus
		return true
	} else {
		glog.V(4).Infof("Service removal %s/%s from cluster %s observed.", cachedService.lastState.Namespace, cachedService.lastState.Name, clusterName)
	}
	return false
}

// processServiceUpdate Update ingress info when service updated
// the function returns a bool to indicate if actual update happened on federation service cache
// and if the federation service cache is updated, the updated info should be post to federation apiserver
func (cc *clusterClientCache) processServiceUpdate(cachedService *cachedService, service *v1.Service, clusterName string) bool {
	glog.V(4).Infof("Processing service update for %s/%s, cluster %s", service.Namespace, service.Name, clusterName)
	cachedService.rwlock.Lock()
	defer cachedService.rwlock.Unlock()
	var needUpdate bool
	newServiceLB := service.Status.LoadBalancer
	cachedFedServiceStatus := cachedService.lastState.Status.LoadBalancer
	if len(newServiceLB.Ingress) == 0 {
		// not yet get LB IP
		return false
	}

	cachedStatus, ok := cachedService.serviceStatusMap[clusterName]
	if ok {
		if reflect.DeepEqual(cachedStatus, newServiceLB) {
			glog.V(4).Infof("Same ingress info observed for service %s/%s: %+v ", service.Namespace, service.Name, cachedStatus.Ingress)
		} else {
			glog.V(4).Infof("Ingress info was changed for service %s/%s: cache: %+v, new: %+v ",
				service.Namespace, service.Name, cachedStatus.Ingress, newServiceLB)
			needUpdate = true
		}
	} else {
		glog.V(4).Infof("Cached service status was not found for %s/%s, cluster %s, building one", service.Namespace, service.Name, clusterName)

		// cache is not always reliable(cache will be cleaned when service controller restart)
		// two cases will run into this branch:
		// 1. new service loadbalancer info received -> no info in cache, and no in federation service
		// 2. service controller being restarted -> no info in cache, but it is in federation service

		// check if the lb info is already in federation service

		cachedService.serviceStatusMap[clusterName] = newServiceLB
		needUpdate = false
		// iterate service ingress info
		for _, new := range newServiceLB.Ingress {
			var found bool
			// if it is known by federation service
			for _, fed := range cachedFedServiceStatus.Ingress {
				if new.IP == fed.IP && new.Hostname == fed.Hostname {
					found = true
				}
			}
			if !found {
				needUpdate = true
				break
			}
		}
	}

	if needUpdate {
		// new status = cached federation status - cached status + new status from k8s cluster

		removeIndexes := []int{}
		for i, fed := range cachedFedServiceStatus.Ingress {
			for _, new := range cachedStatus.Ingress {
				// remove if same ingress record found
				if new.IP == fed.IP && new.Hostname == fed.Hostname {
					removeIndexes = append(removeIndexes, i)
				}
			}
		}
		sort.Ints(removeIndexes)
		for i := len(removeIndexes) - 1; i >= 0; i-- {
			cachedFedServiceStatus.Ingress = append(cachedFedServiceStatus.Ingress[:removeIndexes[i]], cachedFedServiceStatus.Ingress[removeIndexes[i]+1:]...)
		}
		cachedFedServiceStatus.Ingress = append(cachedFedServiceStatus.Ingress, service.Status.LoadBalancer.Ingress...)
		cachedService.lastState.Status.LoadBalancer = cachedFedServiceStatus
		glog.V(4).Infof("Add new ingress info %+v for service %s/%s", service.Status.LoadBalancer, service.Namespace, service.Name)
	} else {
		glog.V(4).Infof("Same ingress info found for %s/%s, cluster %s", service.Namespace, service.Name, clusterName)
	}
	return needUpdate
}

func (cc *clusterClientCache) persistFedServiceUpdate(cachedService *cachedService, fedClient federation_release_1_3.Interface) error {
	service := cachedService.lastState
	glog.V(5).Infof("Persist federation service status %s/%s", service.Namespace, service.Name)
	var err error
	for i := 0; i < clientRetryCount; i++ {
		_, err := fedClient.Core().Services(service.Namespace).Get(service.Name)
		if errors.IsNotFound(err) {
			glog.Infof("Not persisting update to service '%s/%s' that no longer exists: %v",
				service.Namespace, service.Name, err)
			return nil
		}
		_, err = fedClient.Core().Services(service.Namespace).UpdateStatus(service)
		if err == nil {
			glog.V(2).Infof("Successfully update service %s/%s to federation apiserver", service.Namespace, service.Name)
			return nil
		}
		if errors.IsNotFound(err) {
			glog.Infof("Not persisting update to service '%s/%s' that no longer exists: %v",
				service.Namespace, service.Name, err)
			return nil
		}
		if errors.IsConflict(err) {
			glog.V(4).Infof("Not persisting update to service '%s/%s' that has been changed since we received it: %v",
				service.Namespace, service.Name, err)
			return err
		}
		time.Sleep(cachedService.nextFedUpdateDelay())
	}
	return err
}

// obj could be an *api.Service, or a DeletionFinalStateUnknown marker item.
func (cc *clusterClientCache) enqueueService(obj interface{}, clusterName string) {
	key, err := controller.KeyFunc(obj)
	if err != nil {
		glog.Errorf("Couldn't get key for object %+v: %v", obj, err)
		return
	}
	_, ok := cc.clientMap[clusterName]
	if ok {
		cc.clientMap[clusterName].serviceQueue.Add(key)
	}
}
<commit_msg>Fix block-local err bug in service_helper.go<commit_after>/*
Copyright 2016 The Kubernetes Authors 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 service

import (
	"fmt"
	"time"

	federation_release_1_3 "k8s.io/kubernetes/federation/client/clientset_generated/federation_release_1_3"
	"k8s.io/kubernetes/pkg/api/errors"
	v1 "k8s.io/kubernetes/pkg/api/v1"
	cache "k8s.io/kubernetes/pkg/client/cache"
	"k8s.io/kubernetes/pkg/controller"

	"github.com/golang/glog"
	"reflect"
	"sort"
)

// worker runs a worker thread that just dequeues items, processes them, and marks them done.
// It enforces that the syncHandler is never invoked concurrently with the same key.
func (sc *ServiceController) clusterServiceWorker() {
	fedClient := sc.federationClient
	for clusterName, cache := range sc.clusterCache.clientMap {
		go func(cache *clusterCache, clusterName string) {
			for {
				func() {
					key, quit := cache.serviceQueue.Get()
					defer cache.serviceQueue.Done(key)
					if quit {
						return
					}
					err := sc.clusterCache.syncService(key.(string), clusterName, cache, sc.serviceCache, fedClient, sc)
					if err != nil {
						glog.Errorf("Failed to sync service: %+v", err)
					}
				}()
			}
		}(cache, clusterName)
	}
}

// Whenever there is change on service, the federation service should be updated
func (cc *clusterClientCache) syncService(key, clusterName string, clusterCache *clusterCache, serviceCache *serviceCache, fedClient federation_release_1_3.Interface, sc *ServiceController) error {
	// obj holds the latest service info from apiserver, return if there is no federation cache for the service
	cachedService, ok := serviceCache.get(key)
	if !ok {
		// if serviceCache does not exists, that means the service is not created by federation, we should skip it
		return nil
	}
	serviceInterface, exists, err := clusterCache.serviceStore.GetByKey(key)
	if err != nil {
		glog.Infof("Did not successfully get %v from store: %v, will retry later", key, err)
		clusterCache.serviceQueue.Add(key)
		return err
	}
	var needUpdate bool
	if exists {
		service, ok := serviceInterface.(*v1.Service)
		if ok {
			glog.V(4).Infof("Found service for federation service %s/%s from cluster %s", service.Namespace, service.Name, clusterName)
			needUpdate = cc.processServiceUpdate(cachedService, service, clusterName)
		} else {
			_, ok := serviceInterface.(cache.DeletedFinalStateUnknown)
			if !ok {
				return fmt.Errorf("Object contained wasn't a service or a deleted key: %+v", serviceInterface)
			}
			glog.Infof("Found tombstone for %v", key)
			needUpdate = cc.processServiceDeletion(cachedService, clusterName)
		}
	} else {
		glog.Infof("Can not get service %v for cluster %s from serviceStore", key, clusterName)
		needUpdate = cc.processServiceDeletion(cachedService, clusterName)
	}

	if needUpdate {
		for i := 0; i < clientRetryCount; i++ {
			err := sc.ensureDnsRecords(clusterName, cachedService)
			if err == nil {
				break
			}
			glog.V(4).Infof("Error ensuring DNS Records for service %s on cluster %s: %v", key, clusterName, err)
			time.Sleep(cachedService.nextDNSUpdateDelay())
			clusterCache.serviceQueue.Add(key)
			// did not retry here as we still want to persist federation apiserver even ensure dns records fails
		}
		err := cc.persistFedServiceUpdate(cachedService, fedClient)
		if err == nil {
			cachedService.appliedState = cachedService.lastState
			cachedService.resetFedUpdateDelay()
		} else {
			if err != nil {
				glog.Errorf("Failed to sync service: %+v, put back to service queue", err)
				clusterCache.serviceQueue.Add(key)
			}
		}
	}
	return nil
}

// processServiceDeletion is triggered when a service is delete from underlying k8s cluster
// the deletion function will wip out the cached ingress info of the service from federation service ingress
// the function returns a bool to indicate if actual update happened on federation service cache
// and if the federation service cache is updated, the updated info should be post to federation apiserver
func (cc *clusterClientCache) processServiceDeletion(cachedService *cachedService, clusterName string) bool {
	cachedService.rwlock.Lock()
	defer cachedService.rwlock.Unlock()
	cachedStatus, ok := cachedService.serviceStatusMap[clusterName]
	// cached status found, remove ingress info from federation service cache
	if ok {
		cachedFedServiceStatus := cachedService.lastState.Status.LoadBalancer
		removeIndexes := []int{}
		for i, fed := range cachedFedServiceStatus.Ingress {
			for _, new := range cachedStatus.Ingress {
				// remove if same ingress record found
				if new.IP == fed.IP && new.Hostname == fed.Hostname {
					removeIndexes = append(removeIndexes, i)
				}
			}
		}
		sort.Ints(removeIndexes)
		for i := len(removeIndexes) - 1; i >= 0; i-- {
			cachedFedServiceStatus.Ingress = append(cachedFedServiceStatus.Ingress[:removeIndexes[i]], cachedFedServiceStatus.Ingress[removeIndexes[i]+1:]...)
			glog.V(4).Infof("Remove old ingress %d for service %s/%s", removeIndexes[i], cachedService.lastState.Namespace, cachedService.lastState.Name)
		}
		delete(cachedService.serviceStatusMap, clusterName)
		delete(cachedService.endpointMap, clusterName)
		cachedService.lastState.Status.LoadBalancer = cachedFedServiceStatus
		return true
	} else {
		glog.V(4).Infof("Service removal %s/%s from cluster %s observed.", cachedService.lastState.Namespace, cachedService.lastState.Name, clusterName)
	}
	return false
}

// processServiceUpdate Update ingress info when service updated
// the function returns a bool to indicate if actual update happened on federation service cache
// and if the federation service cache is updated, the updated info should be post to federation apiserver
func (cc *clusterClientCache) processServiceUpdate(cachedService *cachedService, service *v1.Service, clusterName string) bool {
	glog.V(4).Infof("Processing service update for %s/%s, cluster %s", service.Namespace, service.Name, clusterName)
	cachedService.rwlock.Lock()
	defer cachedService.rwlock.Unlock()
	var needUpdate bool
	newServiceLB := service.Status.LoadBalancer
	cachedFedServiceStatus := cachedService.lastState.Status.LoadBalancer
	if len(newServiceLB.Ingress) == 0 {
		// not yet get LB IP
		return false
	}

	cachedStatus, ok := cachedService.serviceStatusMap[clusterName]
	if ok {
		if reflect.DeepEqual(cachedStatus, newServiceLB) {
			glog.V(4).Infof("Same ingress info observed for service %s/%s: %+v ", service.Namespace, service.Name, cachedStatus.Ingress)
		} else {
			glog.V(4).Infof("Ingress info was changed for service %s/%s: cache: %+v, new: %+v ",
				service.Namespace, service.Name, cachedStatus.Ingress, newServiceLB)
			needUpdate = true
		}
	} else {
		glog.V(4).Infof("Cached service status was not found for %s/%s, cluster %s, building one", service.Namespace, service.Name, clusterName)

		// cache is not always reliable(cache will be cleaned when service controller restart)
		// two cases will run into this branch:
		// 1. new service loadbalancer info received -> no info in cache, and no in federation service
		// 2. service controller being restarted -> no info in cache, but it is in federation service

		// check if the lb info is already in federation service

		cachedService.serviceStatusMap[clusterName] = newServiceLB
		needUpdate = false
		// iterate service ingress info
		for _, new := range newServiceLB.Ingress {
			var found bool
			// if it is known by federation service
			for _, fed := range cachedFedServiceStatus.Ingress {
				if new.IP == fed.IP && new.Hostname == fed.Hostname {
					found = true
				}
			}
			if !found {
				needUpdate = true
				break
			}
		}
	}

	if needUpdate {
		// new status = cached federation status - cached status + new status from k8s cluster

		removeIndexes := []int{}
		for i, fed := range cachedFedServiceStatus.Ingress {
			for _, new := range cachedStatus.Ingress {
				// remove if same ingress record found
				if new.IP == fed.IP && new.Hostname == fed.Hostname {
					removeIndexes = append(removeIndexes, i)
				}
			}
		}
		sort.Ints(removeIndexes)
		for i := len(removeIndexes) - 1; i >= 0; i-- {
			cachedFedServiceStatus.Ingress = append(cachedFedServiceStatus.Ingress[:removeIndexes[i]], cachedFedServiceStatus.Ingress[removeIndexes[i]+1:]...)
		}
		cachedFedServiceStatus.Ingress = append(cachedFedServiceStatus.Ingress, service.Status.LoadBalancer.Ingress...)
		cachedService.lastState.Status.LoadBalancer = cachedFedServiceStatus
		glog.V(4).Infof("Add new ingress info %+v for service %s/%s", service.Status.LoadBalancer, service.Namespace, service.Name)
	} else {
		glog.V(4).Infof("Same ingress info found for %s/%s, cluster %s", service.Namespace, service.Name, clusterName)
	}
	return needUpdate
}

func (cc *clusterClientCache) persistFedServiceUpdate(cachedService *cachedService, fedClient federation_release_1_3.Interface) error {
	service := cachedService.lastState
	glog.V(5).Infof("Persist federation service status %s/%s", service.Namespace, service.Name)
	var err error
	for i := 0; i < clientRetryCount; i++ {
		_, err := fedClient.Core().Services(service.Namespace).Get(service.Name)
		if errors.IsNotFound(err) {
			glog.Infof("Not persisting update to service '%s/%s' that no longer exists: %v",
				service.Namespace, service.Name, err)
			return nil
		}
		_, err = fedClient.Core().Services(service.Namespace).UpdateStatus(service)
		if err == nil {
			glog.V(2).Infof("Successfully update service %s/%s to federation apiserver", service.Namespace, service.Name)
			return nil
		}
		if errors.IsNotFound(err) {
			glog.Infof("Not persisting update to service '%s/%s' that no longer exists: %v",
				service.Namespace, service.Name, err)
			return nil
		}
		if errors.IsConflict(err) {
			glog.V(4).Infof("Not persisting update to service '%s/%s' that has been changed since we received it: %v",
				service.Namespace, service.Name, err)
			return err
		}
		time.Sleep(cachedService.nextFedUpdateDelay())
	}
	return err
}

// obj could be an *api.Service, or a DeletionFinalStateUnknown marker item.
func (cc *clusterClientCache) enqueueService(obj interface{}, clusterName string) {
	key, err := controller.KeyFunc(obj)
	if err != nil {
		glog.Errorf("Couldn't get key for object %+v: %v", obj, err)
		return
	}
	_, ok := cc.clientMap[clusterName]
	if ok {
		cc.clientMap[clusterName].serviceQueue.Add(key)
	}
}
<|endoftext|>


<commit_before>package router

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"net"
	"net/http"
	"reflect"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/ginkgomon"

	cf_tcp_router "github.com/cloudfoundry-incubator/cf-tcp-router"
	"github.com/cloudfoundry-incubator/cf-tcp-router-acceptance-tests/assets/tcp-sample-receiver/testrunner"
	"github.com/cloudfoundry-incubator/cf-tcp-router-acceptance-tests/helpers"
	"github.com/cloudfoundry-incubator/receptor"
	"github.com/cloudfoundry-incubator/tcp-emitter/tcp_routes"
)

const (
	DEFAULT_CONNECT_TIMEOUT = 1 * time.Second
	CONN_TYPE               = "tcp"
)

var _ = Describe("Routing Test", func() {
	var (
		externalPort1       int
		externalPort2       int
		sampleReceiverPort1 int
		sampleReceiverPort2 int
		serverId1           string
		serverId2           string

		receiver1 ifrit.Process
		receiver2 ifrit.Process
	)

	configureMapping := func(externalPort int, backendPorts ...int) {
		backends := cf_tcp_router.BackendHostInfos{}
		for _, backendPort := range backendPorts {
			backends = append(backends, cf_tcp_router.NewBackendHostInfo(externalIP, uint16(backendPort)))
		}

		createMappingRequest := cf_tcp_router.MappingRequests{
			cf_tcp_router.NewMappingRequest(uint16(externalPort), backends),
		}
		payload, err := json.Marshal(createMappingRequest)

		Expect(err).ToNot(HaveOccurred())

		resp, err := http.Post(fmt.Sprintf(
			"http://%s:%d/v0/external_ports",
			routerApiConfig.Address, routerApiConfig.Port),
			"application/json", bytes.NewBuffer(payload))
		Expect(err).ToNot(HaveOccurred())

		Expect(resp.StatusCode).Should(Equal(http.StatusOK))
	}

	checkConnection := func(errChan chan error, address string, serverId string) {
		time.Sleep(2 * time.Second)
		conn, err := net.DialTimeout(CONN_TYPE, address, DEFAULT_CONNECT_TIMEOUT)
		if err != nil {
			errChan <- err
			return
		}

		nanoSeconds := time.Now().Nanosecond()
		message := []byte(fmt.Sprintf("Time is %d", nanoSeconds))
		_, err = conn.Write(message)
		if err != nil {
			errChan <- err
			return
		}

		expectedMessage := []byte(serverId + ":" + string(message))
		buff := make([]byte, len(expectedMessage))
		_, err = conn.Read(buff)
		if err != nil {
			errChan <- err
			return
		}

		if !reflect.DeepEqual(buff, expectedMessage) {
			errChan <- errors.New(fmt.Sprintf("Message mismatch. Actual=[%s], Expected=[%s]", string(buff), string(expectedMessage)))
			return
		}
		errChan <- conn.Close()
	}

	verifyConnection := func(externalPort int, serverId string) {
		errChan := make(chan error, 1)
		address := fmt.Sprintf("%s:%d", routerApiConfig.Address, externalPort)
		go checkConnection(errChan, address, serverId)
		i := 0
	OUTERLOOP:
		for {
			select {
			case err := <-errChan:
				if err != nil {
					logger.Info(fmt.Sprintf("\n%d - Recevied error on errchan:%s\n", i, err.Error()))
					if i < 10 {
						i = i + 1
						go checkConnection(errChan, address, serverId)
					} else {
						Expect(err).ShouldNot(HaveOccurred())
					}
				} else {
					break OUTERLOOP
				}
			}
		}
	}

	spinupTcpReceiver := func(port int, id string) ifrit.Process {
		sampleReceiverArgs := testrunner.Args{
			Address:  fmt.Sprintf("%s:%d", externalIP, port),
			ServerId: id,
		}
		runner1 := testrunner.New(sampleReceiverPath, sampleReceiverArgs)
		return ifrit.Invoke(runner1)
	}

	tearDownTcpReceiver := func(receiverProcess ifrit.Process) {
		ginkgomon.Kill(receiverProcess, 5*time.Second)
	}

	Describe("A sample receiver running as a separate process", func() {
		BeforeEach(func() {
			externalPort1 = 60000 + GinkgoParallelNode()
			sampleReceiverPort1 = 9000 + GinkgoParallelNode()
			sampleReceiverPort2 = 9500 + GinkgoParallelNode()
			serverId1 = "serverId1"
			serverId2 = "serverId2"

			receiver1 = spinupTcpReceiver(sampleReceiverPort1, serverId1)
			receiver2 = spinupTcpReceiver(sampleReceiverPort2, serverId2)
		})

		AfterEach(func() {
			tearDownTcpReceiver(receiver1)
			tearDownTcpReceiver(receiver2)
		})

		It("routes traffic to sample receiver", func() {
			configureMapping(externalPort1, sampleReceiverPort1)
			verifyConnection(externalPort1, serverId1)

			By("altering the mapping it routes to new backend")
			configureMapping(externalPort1, sampleReceiverPort2)
			verifyConnection(externalPort1, serverId2)
		})
	})

	Describe("Multiple sample receivers running as a separate process and mapped to same external port", func() {
		sendAndReceive := func(address string) (net.Conn, string) {
			conn, err := net.DialTimeout(CONN_TYPE, address, DEFAULT_CONNECT_TIMEOUT)
			Expect(err).ShouldNot(HaveOccurred())

			message := "Hello"
			_, err = conn.Write([]byte(message))
			Expect(err).ShouldNot(HaveOccurred())

			response := make([]byte, 1024)
			count, err := conn.Read(response)
			Expect(err).ShouldNot(HaveOccurred())

			return conn, string(response[0:count])
		}

		BeforeEach(func() {
			externalPort1 = 61000 + GinkgoParallelNode()
			sampleReceiverPort1 = 7000 + GinkgoParallelNode()
			sampleReceiverPort2 = 7500 + GinkgoParallelNode()
			serverId1 = "serverId3"
			serverId2 = "serverId4"

			receiver1 = spinupTcpReceiver(sampleReceiverPort1, serverId1)
			receiver2 = spinupTcpReceiver(sampleReceiverPort2, serverId2)
		})

		AfterEach(func() {
			tearDownTcpReceiver(receiver1)
			tearDownTcpReceiver(receiver2)
		})

		It("load balances the connections", func() {
			configureMapping(externalPort1, sampleReceiverPort1, sampleReceiverPort2)
			address := fmt.Sprintf("%s:%d", routerApiConfig.Address, externalPort1)
			Eventually(func() error {
				tmpconn, err := net.Dial(CONN_TYPE, address)
				if err == nil {
					tmpconn.Close()
				}
				return err
			}, 20*time.Second, 1*time.Second).ShouldNot(HaveOccurred())

			conn1, response1 := sendAndReceive(address)
			conn2, response2 := sendAndReceive(address)
			Expect(response1).ShouldNot(Equal(response2))

			err := conn1.Close()
			Expect(err).ShouldNot(HaveOccurred())
			err = conn2.Close()
			Expect(err).ShouldNot(HaveOccurred())
		})
	})

	Describe("A single sample receiver running as a separate process and mapped to multiple external ports", func() {
		var (
			receptorClient receptor.Client
			processGuid    string
		)

		createDesiredLRPTwoExternalPorts := func(
			externalPort1,
			externalPort2,
			ContainerPort uint16,
			serverId string) receptor.DesiredLRPCreateRequest {
			lrp := helpers.CreateDesiredLRP(logger,
				uint16(externalPort1), uint16(sampleReceiverPort1), serverId1, 1)

			route1 := tcp_routes.TCPRoute{
				ExternalPort:  uint16(externalPort1),
				ContainerPort: uint16(sampleReceiverPort1),
			}
			route2 := tcp_routes.TCPRoute{
				ExternalPort:  uint16(externalPort2),
				ContainerPort: uint16(sampleReceiverPort1),
			}
			routes := tcp_routes.TCPRoutes{route1, route2}
			lrp.Routes = routes.RoutingInfo()
			return lrp
		}

		BeforeEach(func() {
			receptorClient = receptor.NewClient(routerApiConfig.DiegoAPIURL)
			externalPort1 = 34500 + GinkgoParallelNode()
			externalPort2 = 12300 + GinkgoParallelNode()

			sampleReceiverPort1 = 7000 + GinkgoParallelNode()
			serverId1 = "serverId6"

			lrp := createDesiredLRPTwoExternalPorts(
				uint16(externalPort1),
				uint16(externalPort2),
				uint16(sampleReceiverPort1),
				serverId1,
			)
			err := receptorClient.CreateDesiredLRP(lrp)
			Expect(err).ShouldNot(HaveOccurred())
			processGuid = lrp.ProcessGuid
		})

		AfterEach(func() {
			err := receptorClient.DeleteDesiredLRP(processGuid)
			Expect(err).ShouldNot(HaveOccurred())
		})

		It("sends traffic on the different external ports to the same container port", func() {
			verifyConnection(externalPort1, serverId1)
			verifyConnection(externalPort2, serverId1)
		})
	})

	Describe("LRP with TCP routing requirements is desired", func() {
		var (
			receptorClient receptor.Client
			processGuid    string
		)

		BeforeEach(func() {

			receptorClient = receptor.NewClient(routerApiConfig.DiegoAPIURL)

			externalPort1 = 62000 + GinkgoParallelNode()
			sampleReceiverPort1 = 8000 + GinkgoParallelNode()
			serverId1 = fmt.Sprintf("serverId-%d", GinkgoParallelNode())

			lrp := helpers.CreateDesiredLRP(logger,
				uint16(externalPort1), uint16(sampleReceiverPort1), serverId1, 1)

			err := receptorClient.CreateDesiredLRP(lrp)
			Expect(err).ShouldNot(HaveOccurred())
			processGuid = lrp.ProcessGuid
		})

		AfterEach(func() {
			err := receptorClient.DeleteDesiredLRP(processGuid)
			Expect(err).ShouldNot(HaveOccurred())
		})

		It("receives TCP traffic on desired external port", func() {
			verifyConnection(externalPort1, serverId1)

			By("updating LRP with new external port it receives traffic on new external port")
			externalPort1 = 63000 + GinkgoParallelNode()
			updatedLrp := helpers.UpdateDesiredLRP(uint16(externalPort1),
				uint16(sampleReceiverPort1), 1)
			err := receptorClient.UpdateDesiredLRP(processGuid, updatedLrp)
			Expect(err).ShouldNot(HaveOccurred())
			verifyConnection(externalPort1, serverId1)
		})
	})
})
<commit_msg>Fix the description of test<commit_after>package router

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"net"
	"net/http"
	"reflect"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/ginkgomon"

	cf_tcp_router "github.com/cloudfoundry-incubator/cf-tcp-router"
	"github.com/cloudfoundry-incubator/cf-tcp-router-acceptance-tests/assets/tcp-sample-receiver/testrunner"
	"github.com/cloudfoundry-incubator/cf-tcp-router-acceptance-tests/helpers"
	"github.com/cloudfoundry-incubator/receptor"
	"github.com/cloudfoundry-incubator/tcp-emitter/tcp_routes"
)

const (
	DEFAULT_CONNECT_TIMEOUT = 1 * time.Second
	CONN_TYPE               = "tcp"
)

var _ = Describe("Routing Test", func() {
	var (
		externalPort1       int
		externalPort2       int
		sampleReceiverPort1 int
		sampleReceiverPort2 int
		serverId1           string
		serverId2           string

		receiver1 ifrit.Process
		receiver2 ifrit.Process
	)

	configureMapping := func(externalPort int, backendPorts ...int) {
		backends := cf_tcp_router.BackendHostInfos{}
		for _, backendPort := range backendPorts {
			backends = append(backends, cf_tcp_router.NewBackendHostInfo(externalIP, uint16(backendPort)))
		}

		createMappingRequest := cf_tcp_router.MappingRequests{
			cf_tcp_router.NewMappingRequest(uint16(externalPort), backends),
		}
		payload, err := json.Marshal(createMappingRequest)

		Expect(err).ToNot(HaveOccurred())

		resp, err := http.Post(fmt.Sprintf(
			"http://%s:%d/v0/external_ports",
			routerApiConfig.Address, routerApiConfig.Port),
			"application/json", bytes.NewBuffer(payload))
		Expect(err).ToNot(HaveOccurred())

		Expect(resp.StatusCode).Should(Equal(http.StatusOK))
	}

	checkConnection := func(errChan chan error, address string, serverId string) {
		time.Sleep(2 * time.Second)
		conn, err := net.DialTimeout(CONN_TYPE, address, DEFAULT_CONNECT_TIMEOUT)
		if err != nil {
			errChan <- err
			return
		}

		nanoSeconds := time.Now().Nanosecond()
		message := []byte(fmt.Sprintf("Time is %d", nanoSeconds))
		_, err = conn.Write(message)
		if err != nil {
			errChan <- err
			return
		}

		expectedMessage := []byte(serverId + ":" + string(message))
		buff := make([]byte, len(expectedMessage))
		_, err = conn.Read(buff)
		if err != nil {
			errChan <- err
			return
		}

		if !reflect.DeepEqual(buff, expectedMessage) {
			errChan <- errors.New(fmt.Sprintf("Message mismatch. Actual=[%s], Expected=[%s]", string(buff), string(expectedMessage)))
			return
		}
		errChan <- conn.Close()
	}

	verifyConnection := func(externalPort int, serverId string) {
		errChan := make(chan error, 1)
		address := fmt.Sprintf("%s:%d", routerApiConfig.Address, externalPort)
		go checkConnection(errChan, address, serverId)
		i := 0
	OUTERLOOP:
		for {
			select {
			case err := <-errChan:
				if err != nil {
					logger.Info(fmt.Sprintf("\n%d - Recevied error on errchan:%s\n", i, err.Error()))
					if i < 10 {
						i = i + 1
						go checkConnection(errChan, address, serverId)
					} else {
						Expect(err).ShouldNot(HaveOccurred())
					}
				} else {
					break OUTERLOOP
				}
			}
		}
	}

	spinupTcpReceiver := func(port int, id string) ifrit.Process {
		sampleReceiverArgs := testrunner.Args{
			Address:  fmt.Sprintf("%s:%d", externalIP, port),
			ServerId: id,
		}
		runner1 := testrunner.New(sampleReceiverPath, sampleReceiverArgs)
		return ifrit.Invoke(runner1)
	}

	tearDownTcpReceiver := func(receiverProcess ifrit.Process) {
		ginkgomon.Kill(receiverProcess, 5*time.Second)
	}

	Describe("A sample receiver running as a separate process", func() {
		BeforeEach(func() {
			externalPort1 = 60000 + GinkgoParallelNode()
			sampleReceiverPort1 = 9000 + GinkgoParallelNode()
			sampleReceiverPort2 = 9500 + GinkgoParallelNode()
			serverId1 = "serverId1"
			serverId2 = "serverId2"

			receiver1 = spinupTcpReceiver(sampleReceiverPort1, serverId1)
			receiver2 = spinupTcpReceiver(sampleReceiverPort2, serverId2)
		})

		AfterEach(func() {
			tearDownTcpReceiver(receiver1)
			tearDownTcpReceiver(receiver2)
		})

		It("routes traffic to sample receiver", func() {
			configureMapping(externalPort1, sampleReceiverPort1)
			verifyConnection(externalPort1, serverId1)

			By("altering the mapping it routes to new backend")
			configureMapping(externalPort1, sampleReceiverPort2)
			verifyConnection(externalPort1, serverId2)
		})
	})

	Describe("Multiple sample receivers running as a separate process and mapped to same external port", func() {
		sendAndReceive := func(address string) (net.Conn, string) {
			conn, err := net.DialTimeout(CONN_TYPE, address, DEFAULT_CONNECT_TIMEOUT)
			Expect(err).ShouldNot(HaveOccurred())

			message := "Hello"
			_, err = conn.Write([]byte(message))
			Expect(err).ShouldNot(HaveOccurred())

			response := make([]byte, 1024)
			count, err := conn.Read(response)
			Expect(err).ShouldNot(HaveOccurred())

			return conn, string(response[0:count])
		}

		BeforeEach(func() {
			externalPort1 = 61000 + GinkgoParallelNode()
			sampleReceiverPort1 = 7000 + GinkgoParallelNode()
			sampleReceiverPort2 = 7500 + GinkgoParallelNode()
			serverId1 = "serverId3"
			serverId2 = "serverId4"

			receiver1 = spinupTcpReceiver(sampleReceiverPort1, serverId1)
			receiver2 = spinupTcpReceiver(sampleReceiverPort2, serverId2)
		})

		AfterEach(func() {
			tearDownTcpReceiver(receiver1)
			tearDownTcpReceiver(receiver2)
		})

		It("load balances the connections", func() {
			configureMapping(externalPort1, sampleReceiverPort1, sampleReceiverPort2)
			address := fmt.Sprintf("%s:%d", routerApiConfig.Address, externalPort1)
			Eventually(func() error {
				tmpconn, err := net.Dial(CONN_TYPE, address)
				if err == nil {
					tmpconn.Close()
				}
				return err
			}, 20*time.Second, 1*time.Second).ShouldNot(HaveOccurred())

			conn1, response1 := sendAndReceive(address)
			conn2, response2 := sendAndReceive(address)
			Expect(response1).ShouldNot(Equal(response2))

			err := conn1.Close()
			Expect(err).ShouldNot(HaveOccurred())
			err = conn2.Close()
			Expect(err).ShouldNot(HaveOccurred())
		})
	})

	Describe("LRP mapped to multiple external ports", func() {
		var (
			receptorClient receptor.Client
			processGuid    string
		)

		createDesiredLRPTwoExternalPorts := func(
			externalPort1,
			externalPort2,
			ContainerPort uint16,
			serverId string) receptor.DesiredLRPCreateRequest {
			lrp := helpers.CreateDesiredLRP(logger,
				uint16(externalPort1), uint16(sampleReceiverPort1), serverId1, 1)

			route1 := tcp_routes.TCPRoute{
				ExternalPort:  uint16(externalPort1),
				ContainerPort: uint16(sampleReceiverPort1),
			}
			route2 := tcp_routes.TCPRoute{
				ExternalPort:  uint16(externalPort2),
				ContainerPort: uint16(sampleReceiverPort1),
			}
			routes := tcp_routes.TCPRoutes{route1, route2}
			lrp.Routes = routes.RoutingInfo()
			return lrp
		}

		BeforeEach(func() {
			receptorClient = receptor.NewClient(routerApiConfig.DiegoAPIURL)
			externalPort1 = 34500 + GinkgoParallelNode()
			externalPort2 = 12300 + GinkgoParallelNode()

			sampleReceiverPort1 = 7000 + GinkgoParallelNode()
			serverId1 = "serverId6"

			lrp := createDesiredLRPTwoExternalPorts(
				uint16(externalPort1),
				uint16(externalPort2),
				uint16(sampleReceiverPort1),
				serverId1,
			)
			err := receptorClient.CreateDesiredLRP(lrp)
			Expect(err).ShouldNot(HaveOccurred())
			processGuid = lrp.ProcessGuid
		})

		AfterEach(func() {
			err := receptorClient.DeleteDesiredLRP(processGuid)
			Expect(err).ShouldNot(HaveOccurred())
		})

		It("sends traffic on the different external ports to the same container port", func() {
			verifyConnection(externalPort1, serverId1)
			verifyConnection(externalPort2, serverId1)
		})
	})

	Describe("LRP with TCP routing requirements is desired", func() {
		var (
			receptorClient receptor.Client
			processGuid    string
		)

		BeforeEach(func() {

			receptorClient = receptor.NewClient(routerApiConfig.DiegoAPIURL)

			externalPort1 = 62000 + GinkgoParallelNode()
			sampleReceiverPort1 = 8000 + GinkgoParallelNode()
			serverId1 = fmt.Sprintf("serverId-%d", GinkgoParallelNode())

			lrp := helpers.CreateDesiredLRP(logger,
				uint16(externalPort1), uint16(sampleReceiverPort1), serverId1, 1)

			err := receptorClient.CreateDesiredLRP(lrp)
			Expect(err).ShouldNot(HaveOccurred())
			processGuid = lrp.ProcessGuid
		})

		AfterEach(func() {
			err := receptorClient.DeleteDesiredLRP(processGuid)
			Expect(err).ShouldNot(HaveOccurred())
		})

		It("receives TCP traffic on desired external port", func() {
			verifyConnection(externalPort1, serverId1)

			By("updating LRP with new external port it receives traffic on new external port")
			externalPort1 = 63000 + GinkgoParallelNode()
			updatedLrp := helpers.UpdateDesiredLRP(uint16(externalPort1),
				uint16(sampleReceiverPort1), 1)
			err := receptorClient.UpdateDesiredLRP(processGuid, updatedLrp)
			Expect(err).ShouldNot(HaveOccurred())
			verifyConnection(externalPort1, serverId1)
		})
	})
})
<|endoftext|>


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

package admin

import (
	"code.gitea.io/gitea/models"
	"code.gitea.io/gitea/modules/base"
	"code.gitea.io/gitea/modules/context"
	"code.gitea.io/gitea/modules/setting"
	"code.gitea.io/gitea/routers"
)

const (
	tplOrgs base.TplName = "admin/org/list"
)

// Organizations show all the organizations
func Organizations(ctx *context.Context) {
	ctx.Data["Title"] = ctx.Tr("admin.organizations")
	ctx.Data["PageIsAdmin"] = true
	ctx.Data["PageIsAdminOrganizations"] = true

	routers.RenderUserSearch(ctx, &models.SearchUserOptions{
		Type:     models.UserTypeOrganization,
		PageSize: setting.UI.Admin.OrgPagingNum,
	}, tplOrgs)
}
<commit_msg>Show private organization for admin, fix #6111 (#6112)<commit_after>// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package admin

import (
	"code.gitea.io/gitea/models"
	"code.gitea.io/gitea/modules/base"
	"code.gitea.io/gitea/modules/context"
	"code.gitea.io/gitea/modules/setting"
	"code.gitea.io/gitea/routers"
)

const (
	tplOrgs base.TplName = "admin/org/list"
)

// Organizations show all the organizations
func Organizations(ctx *context.Context) {
	ctx.Data["Title"] = ctx.Tr("admin.organizations")
	ctx.Data["PageIsAdmin"] = true
	ctx.Data["PageIsAdminOrganizations"] = true

	routers.RenderUserSearch(ctx, &models.SearchUserOptions{
		Type:     models.UserTypeOrganization,
		PageSize: setting.UI.Admin.OrgPagingNum,
		Private:  true,
	}, tplOrgs)
}
<|endoftext|>


<commit_before>package qbit

import (
	"fmt"
	"strings"
)

type Constraint struct {
	Name string
}

func NotNull() *Constraint {
	return &Constraint{"NOT NULL"}
}

func Default(value interface{}) *Constraint {
	return &Constraint{fmt.Sprintf("DEFAULT `%s`", value)}
}

func Unique(cols ...string) *Constraint {
	if len(cols) > 0 {
		return &Constraint{fmt.Sprintf("UNIQUE")}
	}
	return &Constraint{fmt.Sprintf("UNIQUE(%s)", strings.Join(cols, ", "))}
}

func Key() *Constraint {
	return &Constraint{"KEY"}
}

func PrimaryKey(cols ...string) *Constraint {
	if len(cols) > 0 {
		return &Constraint{fmt.Sprintf("PRIMARY KEY")}
	}
	return &Constraint{fmt.Sprintf("PRIMARY KEY(%s)", strings.Join(cols, ", "))}
}

func ForeignKey(cols string, table string, refcols string) *Constraint {
	return &Constraint{fmt.Sprintf(
		"FOREIGN KEY (%s) REFERENCES %s ($s)",
		cols,
		table,
		refcols,
	)}
}
<commit_msg>refactor return type of constraints from *Constraint to Constraint<commit_after>package qbit

import (
	"fmt"
	"strings"
)

type Constraint struct {
	Name string
}

func NotNull() Constraint {
	return Constraint{"NOT NULL"}
}

func Default(value interface{}) Constraint {
	return Constraint{fmt.Sprintf("DEFAULT `%s`", value)}
}

func Unique(cols ...string) Constraint {
	if len(cols) > 0 {
		return Constraint{"UNIQUE"}
	}
	return Constraint{fmt.Sprintf("UNIQUE(%s)", strings.Join(cols, ", "))}
}

func Key() Constraint {
	return Constraint{"KEY"}
}

func PrimaryKey(cols ...string) Constraint {
	if len(cols) == 0 {
		return Constraint{"PRIMARY KEY"}
	}
	return Constraint{fmt.Sprintf("PRIMARY KEY(%s)", strings.Join(cols, ", "))}
}

func ForeignKey(cols string, table string, refcols string) Constraint {
	return Constraint{fmt.Sprintf(
		"FOREIGN KEY (%s) REFERENCES %s ($s)",
		cols,
		table,
		refcols,
	)}
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Kubernetes Authors.

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

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

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

package cloudup

import (
	"context"
	"fmt"
	"net"
	"os"
	"strings"

	"k8s.io/klog"
	"k8s.io/kops/dns-controller/pkg/dns"
	"k8s.io/kops/dnsprovider/pkg/dnsprovider"
	"k8s.io/kops/dnsprovider/pkg/dnsprovider/rrstype"
	"k8s.io/kops/pkg/apis/kops"
	kopsdns "k8s.io/kops/pkg/dns"
	"k8s.io/kops/pkg/featureflag"
	"k8s.io/kops/pkg/model"
	"k8s.io/kops/upup/pkg/fi"
)

const (
	// PlaceholderIP is from TEST-NET-3
	// https://en.wikipedia.org/wiki/Reserved_IP_addresses
	PlaceholderIP  = "203.0.113.123"
	PlaceholderTTL = 10
	// DigitalOcean's DNS servers require a certain minimum TTL (it's 30), keeping 60 here.
	PlaceholderTTLDigitialOcean = 60
)

func findZone(cluster *kops.Cluster, cloud fi.Cloud) (dnsprovider.Zone, error) {
	dns, err := cloud.DNS()
	if err != nil {
		return nil, fmt.Errorf("error building DNS provider: %v", err)
	}

	zonesProvider, ok := dns.Zones()
	if !ok {
		return nil, fmt.Errorf("error getting DNS zones provider")
	}

	zones, err := zonesProvider.List()
	if err != nil {
		return nil, fmt.Errorf("error listing DNS zones: %v", err)
	}

	var matches []dnsprovider.Zone
	findName := strings.TrimSuffix(cluster.Spec.DNSZone, ".")
	for _, zone := range zones {
		id := zone.ID()
		name := strings.TrimSuffix(zone.Name(), ".")
		if id == cluster.Spec.DNSZone || name == findName {
			matches = append(matches, zone)
		}
	}
	if len(matches) == 0 {
		return nil, fmt.Errorf("cannot find DNS Zone %q.  Please pre-create the zone and set up NS records so that it resolves", cluster.Spec.DNSZone)
	}

	if len(matches) > 1 {
		klog.Infof("Found multiple DNS Zones matching %q, please set the cluster's Spec.DNSZone to the desired Zone ID:", cluster.Spec.DNSZone)
		for _, zone := range zones {
			id := zone.ID()
			klog.Infof("\t%s", id)
		}
		return nil, fmt.Errorf("found multiple DNS Zones matching %q", cluster.Spec.DNSZone)
	}

	zone := matches[0]
	return zone, nil
}

func validateDNS(cluster *kops.Cluster, cloud fi.Cloud) error {
	kopsModelContext := &model.KopsModelContext{
		Cluster: cluster,
		// We are not initializing a lot of the fields here; revisit once UsePrivateDNS is "real"
	}

	if kopsModelContext.UsePrivateDNS() {
		klog.Infof("Private DNS: skipping DNS validation")
		return nil
	}

	zone, err := findZone(cluster, cloud)
	if err != nil {
		return err
	}
	dnsName := strings.TrimSuffix(zone.Name(), ".")

	klog.V(2).Infof("Doing DNS lookup to verify NS records for %q", dnsName)
	ns, err := net.LookupNS(dnsName)
	if err != nil {
		return fmt.Errorf("error doing DNS lookup for NS records for %q: %v", dnsName, err)
	}

	if len(ns) == 0 {
		if os.Getenv("DNS_IGNORE_NS_CHECK") == "" {
			return fmt.Errorf("NS records not found for %q - please make sure they are correctly configured", dnsName)
		}
		klog.Warningf("Ignoring failed NS record check because DNS_IGNORE_NS_CHECK is set")
	} else {
		var hosts []string
		for _, n := range ns {
			hosts = append(hosts, n.Host)
		}
		klog.V(2).Infof("Found NS records for %q: %v", dnsName, hosts)
	}

	return nil
}

func precreateDNS(ctx context.Context, cluster *kops.Cluster, cloud fi.Cloud) error {
	// TODO: Move to update
	if !featureflag.DNSPreCreate.Enabled() {
		klog.V(4).Infof("Skipping DNS record pre-creation because feature flag not enabled")
		return nil
	}

	// We precreate some DNS names (where they don't exist), with a dummy IP address
	// This avoids hitting negative TTL on DNS lookups, which tend to be very long
	// If we get the names wrong here, it doesn't really matter (extra DNS name, slower boot)

	dnsHostnames := buildPrecreateDNSHostnames(cluster)

	{
		var filtered []string
		for _, name := range dnsHostnames {
			if !kopsdns.IsGossipHostname(name) {
				filtered = append(filtered, name)
			}
		}
		dnsHostnames = filtered
	}

	if len(dnsHostnames) == 0 {
		klog.Infof("No DNS records to pre-create")
		return nil
	}

	klog.Infof("Pre-creating DNS records")

	zone, err := findZone(cluster, cloud)
	if err != nil {
		return err
	}

	rrs, ok := zone.ResourceRecordSets()
	if !ok {
		return fmt.Errorf("error getting DNS resource records for %q", zone.Name())
	}

	recordsMap := make(map[string]dnsprovider.ResourceRecordSet)
	// TODO: We should change the filter to be a suffix match instead
	//records, err := rrs.List("", "")
	records, err := rrs.List()
	if err != nil {
		return fmt.Errorf("error listing DNS resource records for %q: %v", zone.Name(), err)
	}

	for _, record := range records {
		name := dns.EnsureDotSuffix(record.Name())
		key := string(record.Type()) + "::" + name
		recordsMap[key] = record
	}

	changeset := rrs.StartChangeset()
	// TODO: Add ChangeSet.IsEmpty() method
	var created []string

	for _, dnsHostname := range dnsHostnames {
		dnsHostname = dns.EnsureDotSuffix(dnsHostname)
		found := false
		dnsRecord := recordsMap["A::"+dnsHostname]
		if dnsRecord != nil {
			rrdatas := dnsRecord.Rrdatas()
			if len(rrdatas) > 0 {
				klog.V(4).Infof("Found DNS record %s => %s; won't create", dnsHostname, rrdatas)
				found = true
			} else {
				// This is probably an alias target; leave it alone...
				klog.V(4).Infof("Found DNS record %s, but no records", dnsHostname)
				found = true
			}
		}

		if found {
			continue
		}

		klog.V(2).Infof("Pre-creating DNS record %s => %s", dnsHostname, PlaceholderIP)

		if cloud.ProviderID() == kops.CloudProviderDO {
			changeset.Add(rrs.New(dnsHostname, []string{PlaceholderIP}, PlaceholderTTLDigitialOcean, rrstype.A))
		} else {
			changeset.Add(rrs.New(dnsHostname, []string{PlaceholderIP}, PlaceholderTTL, rrstype.A))
		}

		created = append(created, dnsHostname)
	}

	if len(created) != 0 {
		err := changeset.Apply(ctx)
		if err != nil {
			return fmt.Errorf("error pre-creating DNS records: %v", err)
		}
		klog.V(2).Infof("Pre-created DNS names: %v", created)
	}

	return nil
}

// buildPrecreateDNSHostnames returns the hostnames we should precreate
func buildPrecreateDNSHostnames(cluster *kops.Cluster) []string {
	dnsInternalSuffix := ".internal." + cluster.ObjectMeta.Name

	var dnsHostnames []string

	if cluster.Spec.MasterPublicName != "" {
		dnsHostnames = append(dnsHostnames, cluster.Spec.MasterPublicName)
	} else {
		klog.Warningf("cannot pre-create MasterPublicName - not set")
	}

	if cluster.Spec.MasterInternalName != "" {
		dnsHostnames = append(dnsHostnames, cluster.Spec.MasterInternalName)
	} else {
		klog.Warningf("cannot pre-create MasterInternalName - not set")
	}

	for _, etcdCluster := range cluster.Spec.EtcdClusters {
		if etcdCluster.Provider == kops.EtcdProviderTypeManager {
			continue
		}
		etcClusterName := "etcd-" + etcdCluster.Name
		if etcdCluster.Name == "main" {
			// Special case
			etcClusterName = "etcd"
		}
		for _, etcdClusterMember := range etcdCluster.Members {
			name := etcClusterName + "-" + etcdClusterMember.Name + dnsInternalSuffix
			dnsHostnames = append(dnsHostnames, name)
		}
	}

	return dnsHostnames
}
<commit_msg>Address feedback<commit_after>/*
Copyright 2019 The Kubernetes Authors.

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

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

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

package cloudup

import (
	"context"
	"fmt"
	"net"
	"os"
	"strings"

	"k8s.io/klog"
	"k8s.io/kops/dns-controller/pkg/dns"
	"k8s.io/kops/dnsprovider/pkg/dnsprovider"
	"k8s.io/kops/dnsprovider/pkg/dnsprovider/rrstype"
	"k8s.io/kops/pkg/apis/kops"
	kopsdns "k8s.io/kops/pkg/dns"
	"k8s.io/kops/pkg/featureflag"
	"k8s.io/kops/pkg/model"
	"k8s.io/kops/upup/pkg/fi"
)

const (
	// PlaceholderIP is from TEST-NET-3
	// https://en.wikipedia.org/wiki/Reserved_IP_addresses
	PlaceholderIP  = "203.0.113.123"
	PlaceholderTTL = 10
	// DigitalOcean's DNS servers require a certain minimum TTL (it's 30), keeping 60 here.
	PlaceholderTTLDigitialOcean = 60
)

func findZone(cluster *kops.Cluster, cloud fi.Cloud) (dnsprovider.Zone, error) {
	dns, err := cloud.DNS()
	if err != nil {
		return nil, fmt.Errorf("error building DNS provider: %v", err)
	}

	zonesProvider, ok := dns.Zones()
	if !ok {
		return nil, fmt.Errorf("error getting DNS zones provider")
	}

	zones, err := zonesProvider.List()
	if err != nil {
		return nil, fmt.Errorf("error listing DNS zones: %v", err)
	}

	var matches []dnsprovider.Zone
	findName := strings.TrimSuffix(cluster.Spec.DNSZone, ".")
	for _, zone := range zones {
		id := zone.ID()
		name := strings.TrimSuffix(zone.Name(), ".")
		if id == cluster.Spec.DNSZone || name == findName {
			matches = append(matches, zone)
		}
	}
	if len(matches) == 0 {
		return nil, fmt.Errorf("cannot find DNS Zone %q.  Please pre-create the zone and set up NS records so that it resolves", cluster.Spec.DNSZone)
	}

	if len(matches) > 1 {
		klog.Infof("Found multiple DNS Zones matching %q, please set the cluster's spec.dnsZone to the desired Zone ID:", cluster.Spec.DNSZone)
		for _, zone := range zones {
			id := zone.ID()
			klog.Infof("\t%s", id)
		}
		return nil, fmt.Errorf("found multiple DNS Zones matching %q", cluster.Spec.DNSZone)
	}

	zone := matches[0]
	return zone, nil
}

func validateDNS(cluster *kops.Cluster, cloud fi.Cloud) error {
	kopsModelContext := &model.KopsModelContext{
		Cluster: cluster,
		// We are not initializing a lot of the fields here; revisit once UsePrivateDNS is "real"
	}

	if kopsModelContext.UsePrivateDNS() {
		klog.Infof("Private DNS: skipping DNS validation")
		return nil
	}

	zone, err := findZone(cluster, cloud)
	if err != nil {
		return err
	}
	dnsName := strings.TrimSuffix(zone.Name(), ".")

	klog.V(2).Infof("Doing DNS lookup to verify NS records for %q", dnsName)
	ns, err := net.LookupNS(dnsName)
	if err != nil {
		return fmt.Errorf("error doing DNS lookup for NS records for %q: %v", dnsName, err)
	}

	if len(ns) == 0 {
		if os.Getenv("DNS_IGNORE_NS_CHECK") == "" {
			return fmt.Errorf("NS records not found for %q - please make sure they are correctly configured", dnsName)
		}
		klog.Warningf("Ignoring failed NS record check because DNS_IGNORE_NS_CHECK is set")
	} else {
		var hosts []string
		for _, n := range ns {
			hosts = append(hosts, n.Host)
		}
		klog.V(2).Infof("Found NS records for %q: %v", dnsName, hosts)
	}

	return nil
}

func precreateDNS(ctx context.Context, cluster *kops.Cluster, cloud fi.Cloud) error {
	// TODO: Move to update
	if !featureflag.DNSPreCreate.Enabled() {
		klog.V(4).Infof("Skipping DNS record pre-creation because feature flag not enabled")
		return nil
	}

	// We precreate some DNS names (where they don't exist), with a dummy IP address
	// This avoids hitting negative TTL on DNS lookups, which tend to be very long
	// If we get the names wrong here, it doesn't really matter (extra DNS name, slower boot)

	dnsHostnames := buildPrecreateDNSHostnames(cluster)

	{
		var filtered []string
		for _, name := range dnsHostnames {
			if !kopsdns.IsGossipHostname(name) {
				filtered = append(filtered, name)
			}
		}
		dnsHostnames = filtered
	}

	if len(dnsHostnames) == 0 {
		klog.Infof("No DNS records to pre-create")
		return nil
	}

	klog.Infof("Pre-creating DNS records")

	zone, err := findZone(cluster, cloud)
	if err != nil {
		return err
	}

	rrs, ok := zone.ResourceRecordSets()
	if !ok {
		return fmt.Errorf("error getting DNS resource records for %q", zone.Name())
	}

	recordsMap := make(map[string]dnsprovider.ResourceRecordSet)
	// TODO: We should change the filter to be a suffix match instead
	//records, err := rrs.List("", "")
	records, err := rrs.List()
	if err != nil {
		return fmt.Errorf("error listing DNS resource records for %q: %v", zone.Name(), err)
	}

	for _, record := range records {
		name := dns.EnsureDotSuffix(record.Name())
		key := string(record.Type()) + "::" + name
		recordsMap[key] = record
	}

	changeset := rrs.StartChangeset()
	// TODO: Add ChangeSet.IsEmpty() method
	var created []string

	for _, dnsHostname := range dnsHostnames {
		dnsHostname = dns.EnsureDotSuffix(dnsHostname)
		found := false
		dnsRecord := recordsMap["A::"+dnsHostname]
		if dnsRecord != nil {
			rrdatas := dnsRecord.Rrdatas()
			if len(rrdatas) > 0 {
				klog.V(4).Infof("Found DNS record %s => %s; won't create", dnsHostname, rrdatas)
				found = true
			} else {
				// This is probably an alias target; leave it alone...
				klog.V(4).Infof("Found DNS record %s, but no records", dnsHostname)
				found = true
			}
		}

		if found {
			continue
		}

		klog.V(2).Infof("Pre-creating DNS record %s => %s", dnsHostname, PlaceholderIP)

		if cloud.ProviderID() == kops.CloudProviderDO {
			changeset.Add(rrs.New(dnsHostname, []string{PlaceholderIP}, PlaceholderTTLDigitialOcean, rrstype.A))
		} else {
			changeset.Add(rrs.New(dnsHostname, []string{PlaceholderIP}, PlaceholderTTL, rrstype.A))
		}

		created = append(created, dnsHostname)
	}

	if len(created) != 0 {
		err := changeset.Apply(ctx)
		if err != nil {
			return fmt.Errorf("error pre-creating DNS records: %v", err)
		}
		klog.V(2).Infof("Pre-created DNS names: %v", created)
	}

	return nil
}

// buildPrecreateDNSHostnames returns the hostnames we should precreate
func buildPrecreateDNSHostnames(cluster *kops.Cluster) []string {
	dnsInternalSuffix := ".internal." + cluster.ObjectMeta.Name

	var dnsHostnames []string

	if cluster.Spec.MasterPublicName != "" {
		dnsHostnames = append(dnsHostnames, cluster.Spec.MasterPublicName)
	} else {
		klog.Warningf("cannot pre-create MasterPublicName - not set")
	}

	if cluster.Spec.MasterInternalName != "" {
		dnsHostnames = append(dnsHostnames, cluster.Spec.MasterInternalName)
	} else {
		klog.Warningf("cannot pre-create MasterInternalName - not set")
	}

	for _, etcdCluster := range cluster.Spec.EtcdClusters {
		if etcdCluster.Provider == kops.EtcdProviderTypeManager {
			continue
		}
		etcClusterName := "etcd-" + etcdCluster.Name
		if etcdCluster.Name == "main" {
			// Special case
			etcClusterName = "etcd"
		}
		for _, etcdClusterMember := range etcdCluster.Members {
			name := etcClusterName + "-" + etcdClusterMember.Name + dnsInternalSuffix
			dnsHostnames = append(dnsHostnames, name)
		}
	}

	return dnsHostnames
}
<|endoftext|>


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

package nodes

import (
	"encoding/json"
	"fmt"
	nodeinfomodel "github.com/contiv/vpp/plugins/contiv/model/node"

	"github.com/contiv/vpp/plugins/crd/cache/telemetrymodel"
	k8snodeinfo "github.com/contiv/vpp/plugins/ksr/model/node"
	"github.com/contiv/vpp/plugins/ksr/model/pod"
	"github.com/contiv/vpp/plugins/netctl/http"
	"github.com/coreos/etcd/clientv3"
	"github.com/ligato/cn-infra/db/keyval/etcd"
	"github.com/ligato/cn-infra/logging/logrus"
	"github.com/ligato/vpp-agent/plugins/vpp/model/interfaces"
	"os"
	"regexp"
	"text/tabwriter"
	"time"
)

//PrintNodes will print out all of the nodes in a network in a table format.
func PrintNodes() {
	cfg := &etcd.ClientConfig{
		Config: &clientv3.Config{
			Endpoints: []string{"127.0.0.1:32379"},
		},
		OpTimeout: 1 * time.Second,
	}
	w := tabwriter.NewWriter(os.Stdout, 0, 8, 4, '\t', 0)
	// Create connection to etcd.
	db, err := etcd.NewEtcdConnectionWithBytes(*cfg, logrus.DefaultLogger())
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	itr, err := db.ListValues("/vnf-agent/contiv-ksr/allocatedIDs/")
	if err != nil {
		fmt.Printf("Error getting values")
		return
	}
	fmt.Fprintf(w, "id\tname\t\tip_address\t\tman_ip_addr\tbuild_date\t\t\tbuild_version\t\tstart_time\tstate\n")
	w.Flush()
	for {
		kv, stop := itr.GetNext()
		if stop {
			break
		}
		buf := kv.GetValue()
		nodeInfo := &nodeinfomodel.NodeInfo{}
		err = json.Unmarshal(buf, nodeInfo)
		//fmt.Printf("NodeInfo: %+v\n", nodeInfo)
		// Do whatever processing we need to do
		bytes := http.GetNodeInfo(nodeInfo.ManagementIpAddress, "liveness")
		var liveness telemetrymodel.NodeLiveness
		err = json.Unmarshal(bytes, &liveness)
		if err != nil {
			fmt.Println(err)
			liveness.BuildDate = "Not Available"
		}

		fmt.Fprintf(w, "%+v\t%s\t%s\t%s\t%s\t%s\t%d\t%d\n",
			nodeInfo.Id,
			nodeInfo.Name,
			nodeInfo.IpAddress,
			nodeInfo.ManagementIpAddress,
			liveness.BuildDate,
			liveness.BuildVersion,
			liveness.StartTime,
			liveness.State)

		w.Flush()
	}
	db.Close()
}

//FindIPForNodeName will find an ip address that corresponds to the passed in nodeName
func FindIPForNodeName(nodeName string) string {
	cfg := &etcd.ClientConfig{
		Config: &clientv3.Config{
			Endpoints: []string{"127.0.0.1:32379"},
		},
		OpTimeout: 1 * time.Second,
	}
	// Create connection to etcd.
	db, err := etcd.NewEtcdConnectionWithBytes(*cfg, logrus.DefaultLogger())
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	itr, err := db.ListValues("/vnf-agent/contiv-ksr/allocatedIDs/")
	if err != nil {
		fmt.Printf("Error getting values")
		return ""
	}
	for {
		kv, stop := itr.GetNext()
		if stop {
			break
		}
		buf := kv.GetValue()
		//key := kv.GetKey()
		//fmt.Printf("Key: %s, value: %s\n", key, string(buf))
		nodeInfo := &nodeinfomodel.NodeInfo{}
		err = json.Unmarshal(buf, nodeInfo)
		if nodeInfo.Name == nodeName {
			return nodeInfo.ManagementIpAddress
		}
	}
	db.Close()
	return ""
}

//VppCliCmd will receive a nodeName and a vpp cli command and print it out to the console
func VppCliCmd(nodeName string, vppclicmd string) {

	fmt.Printf("vppcli %s %s\n", nodeName, vppclicmd)

	ipAdr := ResolveNodeOrIP(nodeName)
	cmd := fmt.Sprintf("vpp/command")
	body := fmt.Sprintf("{\"vppclicommand\":\"%s\"}", vppclicmd)
	err := http.SetNodeInfo(ipAdr, cmd, body)
	if err != nil {
		fmt.Println(err)
	}

}

//NodeIPamCmd prints out the ipam information of a specific node
func NodeIPamCmd(nodeName string) {
	fmt.Printf("nodeipam %s\n", nodeName)
	w := tabwriter.NewWriter(os.Stdout, 0, 8, 4, '\t', 0)

	ip := ResolveNodeOrIP(nodeName)
	fmt.Fprintf(w, "id\tname\tip_address\tpod_network_ip\tvpp_host_network\n")
	b := http.GetNodeInfo(ip, "contiv/v1/ipam")
	ipam := telemetrymodel.IPamEntry{}
	err := json.Unmarshal(b, &ipam)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%s\n",
		ipam.NodeID,
		ipam.NodeName,
		ipam.NodeIP,
		ipam.PodNetwork,
		ipam.VppHostNetwork)

	w.Flush()
}

//PrintPodsPerNode will print out all of the non-local pods for a certain pods along with their tap interface ip address
func PrintPodsPerNode(input string) {
	hostIP := ResolveNodeOrIP(input)
	cfg := &etcd.ClientConfig{
		Config: &clientv3.Config{
			Endpoints: []string{"127.0.0.1:32379"},
		},
		OpTimeout: 1 * time.Second,
	}
	w := tabwriter.NewWriter(os.Stdout, 0, 8, 6, '\t', 0)
	// Create connection to etcd.
	db, err := etcd.NewEtcdConnectionWithBytes(*cfg, logrus.DefaultLogger())
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	itr, err := db.ListValues("/vnf-agent/contiv-ksr/k8s/pod/")
	if err != nil {
		fmt.Printf("Error getting values")
		return
	}
	fmt.Fprintf(w, "name\t\t\tip_address\t\thost_ip_addr\ttap_ip\toutgoing_idx\ttag\n")

	for {
		kv, stop := itr.GetNext()
		if stop {
			break
		}
		buf := kv.GetValue()
		podInfo := &pod.Pod{}
		err = json.Unmarshal(buf, podInfo)
		if podInfo.HostIpAddress != hostIP || podInfo.IpAddress == hostIP {
			continue
		}
		ip, idx, tag := printTapInterfaces(podInfo)
		fmt.Fprintf(w, "%s\t\t\t%s\t\t%s\t%s\t%d\t%s\n",
			podInfo.Name,
			podInfo.IpAddress,
			podInfo.HostIpAddress,
			ip[0],
			idx,
			tag)
		for _, str := range ip[1:] {
			fmt.Fprintf(w, "\t\t\t\t\t\t%s\n", str)
		}

	}
	w.Flush()
	db.Close()
}

//ResolveNodeOrIP will take in an input string which is either a node name or string and return the ip for the nodename or
//simply return the ip
func ResolveNodeOrIP(input string) (ipAdr string) {
	re := regexp.MustCompile(`(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}`)
	if re.MatchString(input) {
		return input
	}
	ip := FindIPForNodeName(input)
	return ip
}

func printTapInterfaces(podInfo *pod.Pod) ([]string, uint32, string) {
	var str []string
	cmd := fmt.Sprintf("vpp/dump/v1/interfaces")
	var idx uint32
	var tag string
	b := http.GetNodeInfo(podInfo.HostIpAddress, cmd)
	intfs := make(telemetrymodel.NodeInterfaces)
	json.Unmarshal(b, &intfs)
	for _, intf := range intfs {
		if intf.If.IfType == interfaces.InterfaceType_TAP_INTERFACE {
			for _, ip := range intf.If.IPAddresses {
				str = append(str, ip)
			}
			idx = intf.IfMeta.SwIfIndex
			tag = intf.IfMeta.Tag
		}

	}
	return str, idx, tag
}

func getK8sNode(nodeName string) *k8snodeinfo.Node {
	cfg := &etcd.ClientConfig{
		Config: &clientv3.Config{
			Endpoints: []string{"127.0.0.1:32379"},
		},
		OpTimeout: 1 * time.Second,
	}
	// Create connection to etcd.
	db, err := etcd.NewEtcdConnectionWithBytes(*cfg, logrus.DefaultLogger())
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	b, found, _, err := db.GetValue("/vnf-agent/contiv-ksr/k8s/" + nodeName)
	if err != nil || !found {
		fmt.Printf("Error getting values")
		return nil
	}
	k8sInfo := &k8snodeinfo.Node{}
	json.Unmarshal(b, k8sInfo)
	return k8sInfo
}
<commit_msg>Add code that correlates pod interface with its corresponding VPP tap interface<commit_after>// Copyright (c) 2018 Cisco and/or its affiliates.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//

package nodes

import (
	"encoding/json"
	"fmt"
	nodeinfomodel "github.com/contiv/vpp/plugins/contiv/model/node"

	"github.com/contiv/vpp/plugins/crd/cache/telemetrymodel"
	"github.com/contiv/vpp/plugins/ksr/model/pod"
	"github.com/contiv/vpp/plugins/netctl/http"
	"github.com/coreos/etcd/clientv3"
	"github.com/ligato/cn-infra/db/keyval/etcd"
	"github.com/ligato/cn-infra/logging/logrus"
	"github.com/ligato/vpp-agent/plugins/vpp/model/interfaces"
	"os"
	"regexp"
	"strconv"
	"strings"
	"text/tabwriter"
	"time"
)

//PrintNodes will print out all of the nodes in a network in a table format.
func PrintNodes() {
	cfg := &etcd.ClientConfig{
		Config: &clientv3.Config{
			Endpoints: []string{"127.0.0.1:32379"},
		},
		OpTimeout: 1 * time.Second,
	}
	w := tabwriter.NewWriter(os.Stdout, 0, 8, 4, '\t', 0)
	// Create connection to etcd.
	db, err := etcd.NewEtcdConnectionWithBytes(*cfg, logrus.DefaultLogger())
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	itr, err := db.ListValues("/vnf-agent/contiv-ksr/allocatedIDs/")
	if err != nil {
		fmt.Printf("Error getting values")
		return
	}
	fmt.Fprintf(w, "id\tname\t\tip_address\t\tman_ip_addr\tbuild_date\t\t\tbuild_version\t\tstart_time\tstate\n")
	w.Flush()
	for {
		kv, stop := itr.GetNext()
		if stop {
			break
		}
		buf := kv.GetValue()
		nodeInfo := &nodeinfomodel.NodeInfo{}
		err = json.Unmarshal(buf, nodeInfo)
		//fmt.Printf("NodeInfo: %+v\n", nodeInfo)
		// Do whatever processing we need to do
		bytes := http.GetNodeInfo(nodeInfo.ManagementIpAddress, "liveness")
		var liveness telemetrymodel.NodeLiveness
		err = json.Unmarshal(bytes, &liveness)
		if err != nil {
			fmt.Println(err)
			liveness.BuildDate = "Not Available"
		}

		fmt.Fprintf(w, "%+v\t%s\t%s\t%s\t%s\t%s\t%d\t%d\n",
			nodeInfo.Id,
			nodeInfo.Name,
			nodeInfo.IpAddress,
			nodeInfo.ManagementIpAddress,
			liveness.BuildDate,
			liveness.BuildVersion,
			liveness.StartTime,
			liveness.State)

		w.Flush()
	}
	db.Close()
}

//FindIPForNodeName will find an ip address that corresponds to the passed in nodeName
func FindIPForNodeName(nodeName string) string {
	cfg := &etcd.ClientConfig{
		Config: &clientv3.Config{
			Endpoints: []string{"127.0.0.1:32379"},
		},
		OpTimeout: 1 * time.Second,
	}
	// Create connection to etcd.
	db, err := etcd.NewEtcdConnectionWithBytes(*cfg, logrus.DefaultLogger())
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	itr, err := db.ListValues("/vnf-agent/contiv-ksr/allocatedIDs/")
	if err != nil {
		fmt.Printf("Error getting values")
		return ""
	}
	for {
		kv, stop := itr.GetNext()
		if stop {
			break
		}
		buf := kv.GetValue()
		//key := kv.GetKey()
		//fmt.Printf("Key: %s, value: %s\n", key, string(buf))
		nodeInfo := &nodeinfomodel.NodeInfo{}
		err = json.Unmarshal(buf, nodeInfo)
		if nodeInfo.Name == nodeName {
			return nodeInfo.ManagementIpAddress
		}
	}
	db.Close()
	return ""
}

//VppCliCmd will receive a nodeName and a vpp cli command and print it out to the console
func VppCliCmd(nodeName string, vppclicmd string) {

	fmt.Printf("vppcli %s %s\n", nodeName, vppclicmd)

	ipAdr := ResolveNodeOrIP(nodeName)
	cmd := fmt.Sprintf("vpp/command")
	body := fmt.Sprintf("{\"vppclicommand\":\"%s\"}", vppclicmd)
	err := http.SetNodeInfo(ipAdr, cmd, body)
	if err != nil {
		fmt.Println(err)
	}

}

//NodeIPamCmd prints out the ipam information of a specific node
func NodeIPamCmd(nodeName string) {
	fmt.Printf("nodeipam %s\n", nodeName)
	w := tabwriter.NewWriter(os.Stdout, 0, 8, 4, '\t', 0)

	ip := ResolveNodeOrIP(nodeName)
	fmt.Fprintf(w, "id\tname\tip_address\tpod_network_ip\tvpp_host_network\n")
	b := http.GetNodeInfo(ip, "contiv/v1/ipam")
	ipam := telemetrymodel.IPamEntry{}
	err := json.Unmarshal(b, &ipam)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%s\n",
		ipam.NodeID,
		ipam.NodeName,
		ipam.NodeIP,
		ipam.PodNetwork,
		ipam.VppHostNetwork)

	w.Flush()
}

//PrintPodsPerNode will print out all of the non-local pods for a certain pods along with their tap interface ip address
func PrintPodsPerNode(input string) {
	hostIP := ResolveNodeOrIP(input)
	cfg := &etcd.ClientConfig{
		Config: &clientv3.Config{
			Endpoints: []string{"127.0.0.1:32379"},
		},
		OpTimeout: 1 * time.Second,
	}
	w := tabwriter.NewWriter(os.Stdout, 0, 8, 6, '\t', 0)
	// Create connection to etcd.
	db, err := etcd.NewEtcdConnectionWithBytes(*cfg, logrus.DefaultLogger())
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	itr, err := db.ListValues("/vnf-agent/contiv-ksr/k8s/pod/")
	if err != nil {
		fmt.Printf("Error getting values")
		return
	}
	fmt.Fprintf(w, "name\t\t\tip_address\t\thost_ip_addr\ttap_ip\toutgoing_idx\ttag\n")

	for {
		kv, stop := itr.GetNext()
		if stop {
			break
		}
		buf := kv.GetValue()
		podInfo := &pod.Pod{}
		err = json.Unmarshal(buf, podInfo)
		if podInfo.HostIpAddress != hostIP || podInfo.IpAddress == hostIP {
			continue
		}
		if ipAddress, ifIndex, tag, err := printTapInterfaces(podInfo); err == nil {
			fmt.Fprintf(w, "%s\t\t\t%s\t\t%s\t%s\t%d\t%s\n",
				podInfo.Name,
				podInfo.IpAddress,
				podInfo.HostIpAddress,
				ipAddress,
				ifIndex,
				tag)

		} else {
			fmt.Printf("error %s\n", err)
		}
	}
	w.Flush()
	db.Close()
}

//ResolveNodeOrIP will take in an input string which is either a node name or string and return the ip for the nodename or
//simply return the ip
func ResolveNodeOrIP(input string) (ipAdr string) {
	re := regexp.MustCompile(`(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}`)
	if re.MatchString(input) {
		return input
	}
	ip := FindIPForNodeName(input)
	return ip
}

func printTapInterfaces(podInfo *pod.Pod) (string, uint32, string, error) {
	// Get interface information
	cmd := "vpp/dump/v1/interfaces"
	b := http.GetNodeInfo(podInfo.HostIpAddress, cmd)
	intfs := make(telemetrymodel.NodeInterfaces)
	if err := json.Unmarshal(b, &intfs); err != nil {

		return "", 0, "", fmt.Errorf("could not get pod's interface; pod %s, hostIPAddress %s, err %s",
			podInfo.Name, podInfo.HostIpAddress, err)
	}

	// Get ipam information
	cmd = "contiv/v1/ipam"
	b = http.GetNodeInfo(podInfo.HostIpAddress, cmd)
	ipam := telemetrymodel.IPamEntry{}
	if err := json.Unmarshal(b, &ipam); err != nil {
		return "", 0, "", fmt.Errorf("could not get ipam for host %s, err %s",
			podInfo.HostIpAddress, err)
	}

	podIfIPAddress, podIfIPMask, err := getIPAddressAndMask(ipam.Config.PodIfIPCIDR)
	if err != nil {
		return "", 0, "", fmt.Errorf("invalid PodIfIPCIDR address %s, err %s",
			podInfo.HostIpAddress, err)
	}

	podIfIPPrefix := podIfIPAddress &^ podIfIPMask
	podAddr, err := ip2uint32(podInfo.IpAddress)
	if err != nil {
		return "", 0, "", fmt.Errorf("invalid podInfo.IpAddress %s, err %s",
			podInfo.HostIpAddress, err)
	}
	podAddrSuffix := podAddr & podIfIPMask

	for _, intf := range intfs {
		if intf.If.IfType == interfaces.InterfaceType_TAP_INTERFACE {
			for _, ip := range intf.If.IPAddresses {
				ifIPAddr, iffIPMask, err := getIPAddressAndMask(ip)
				if err != nil {
					continue
				}
				if iffIPMask != 0 {
					// TODO: do spme error handling
					continue
				}

				ifIPAdrPrefix := ifIPAddr &^ podIfIPMask
				ifIPAdrSuffix := ifIPAddr & podIfIPMask
				if (podIfIPPrefix == ifIPAdrPrefix) && (ifIPAdrSuffix == podAddrSuffix) {
					return ip, intf.IfMeta.SwIfIndex, intf.IfMeta.Tag, nil
				}
			}
		}
	}

	return "", 0, "", nil
}

// maskLength2Mask will tank in an int and return the bit mask for the number given
func maskLength2Mask(ml int) uint32 {
	var mask uint32
	for i := 0; i < 32-ml; i++ {
		mask = mask << 1
		mask++
	}
	return mask
}

func ip2uint32(ipAddress string) (uint32, error) {
	var ipu uint32
	parts := strings.Split(ipAddress, ".")
	for _, p := range parts {
		// num, _ := strconv.ParseUint(p, 10, 32)
		num, _ := strconv.Atoi(p)
		ipu = (ipu << 8) + uint32(num)
		//fmt.Printf("%d: num: 0x%x, ipu: 0x%x\n", i, num, ipu)
	}
	return ipu, nil
}

func getIPAddressAndMask(ip string) (uint32, uint32, error) {
	addressParts := strings.Split(ip, "/")
	maskLen, err := strconv.Atoi(addressParts[1])
	if err != nil {
		return 0, 0, fmt.Errorf("invalid mask")
	}

	address, err := ip2uint32(addressParts[0])
	if err != nil {
		return 0, 0, err
	}
	mask := maskLength2Mask(maskLen)

	return address, mask, nil
}
<|endoftext|>


<commit_before>package output

import (
	"time"

	"github.com/Shopify/sarama"
	"github.com/funkygao/dbus/engine"
	"github.com/funkygao/dbus/model"
	"github.com/funkygao/dbus/plugins/input/myslave"
	"github.com/funkygao/gafka/ctx"
	"github.com/funkygao/gafka/zk"
	conf "github.com/funkygao/jsconf"
	log "github.com/funkygao/log4go"
)

type sentPos struct {
	Log string
	Pos uint32
}

type KafkaOutput struct {
	zone, cluster, topic string

	zkzone   *zk.ZkZone
	ack      sarama.RequiredAcks
	async    bool
	compress bool

	p  sarama.SyncProducer
	ap sarama.AsyncProducer

	sendMessage func(row *model.RowsEvent)

	pos *sentPos

	// FIXME should be shared with MysqlbinlogInput
	// currently, KafkaOutput MUST setup master_host/master_port to correctly checkpoint position
	myslave *myslave.MySlave
}

func (this *KafkaOutput) Init(config *conf.Conf) {
	this.zone = config.String("zone", "")
	this.cluster = config.String("cluster", "")
	this.topic = config.String("topic", "")
	if this.cluster == "" || this.zone == "" || this.topic == "" {
		panic("invalid configuration")
	}

	this.initPosition()

	// ack is ignored in async mode
	this.ack = sarama.RequiredAcks(config.Int("ack", int(sarama.WaitForLocal)))
	this.async = config.Bool("async", false)
	if this.async {
		this.sendMessage = this.asyncSendMessage
	} else {
		this.sendMessage = this.syncSendMessage
	}
	this.compress = config.Bool("compress", false)
	this.zkzone = zk.NewZkZone(zk.DefaultConfig(this.zone, ctx.ZoneZkAddrs(this.zone)))
}

func (this *KafkaOutput) Run(r engine.OutputRunner, h engine.PluginHelper) error {
	this.myslave = engine.Globals().Registered("myslave").(*myslave.MySlave)

	if err := this.prepareProducer(); err != nil {
		return err
	}

	defer func() {
		if this.async {
			this.ap.Close()
		} else {
			this.p.Close()
		}
	}()

	for {
		select {
		case pack, ok := <-r.InChan():
			if !ok {
				return nil
			}

			row, ok := pack.Payload.(*model.RowsEvent)
			if !ok {
				log.Error("bad payload: %+v", pack.Payload)
				continue
			}

			// best effort to reduce dup message to kafka
			if row.Log > this.pos.Log || (row.Log == this.pos.Log && row.Position > this.pos.Pos) {
				this.sendMessage(row)
			}

			pack.Recycle()
		}
	}

	return nil
}

func (this *KafkaOutput) prepareProducer() error {
	cf := sarama.NewConfig()
	cf.ChannelBufferSize = 256 * 2 // default was 256
	if this.compress {
		cf.Producer.Compression = sarama.CompressionSnappy
	}

	zkcluster := this.zkzone.NewCluster(this.cluster)

	if !this.async {
		cf.Producer.RequiredAcks = this.ack
		p, err := sarama.NewSyncProducer(zkcluster.BrokerList(), cf)
		if err != nil {
			return err
		}

		this.p = p
		return nil
	}

	// async producer
	cf.Producer.Return.Errors = true
	cf.Producer.Return.Successes = true
	cf.Producer.Retry.Backoff = time.Millisecond * 300
	cf.Producer.Retry.Max = 3
	cf.Producer.RequiredAcks = sarama.NoResponse
	cf.Producer.Flush.Frequency = time.Second
	cf.Producer.Flush.Messages = 2000 // TODO
	cf.Producer.Flush.MaxMessages = 0 // unlimited
	ap, err := sarama.NewAsyncProducer(zkcluster.BrokerList(), cf)
	if err != nil {
		return err
	}

	this.ap = ap
	go func() {
		for {
			select {
			case msg := <-this.ap.Successes():
				row := msg.Value.(*model.RowsEvent)
				this.markAsSent(row)
				if err := this.myslave.MarkAsProcessed(row); err != nil {
					log.Error("[%s.%s.%s] {%s} %v", this.zone, this.cluster, this.topic, row, err)
				}

			case err := <-this.ap.Errors():
				// e,g.
				// kafka: Failed to produce message to topic dbustest: kafka server: Message was too large, server rejected it to avoid allocation error.
				row := err.Msg.Value.(*model.RowsEvent)
				log.Error("[%s.%s.%s] %s %s", this.zone, this.cluster, this.topic, err, row.MetaInfo())
			}
		}

	}()

	return nil
}

func (this *KafkaOutput) syncSendMessage(row *model.RowsEvent) {
	msg := &sarama.ProducerMessage{
		Topic: this.topic,
		Value: row,
	}

	var (
		partition int32
		offset    int64
		err       error
	)
	for {
		if partition, offset, err = this.p.SendMessage(msg); err == nil {
			break
		}

		log.Error("%s.%s.%s {%s} %v", this.zone, this.cluster, this.topic, row, err)
		time.Sleep(time.Second)
	}

	this.markAsSent(row)
	if err = this.myslave.MarkAsProcessed(row); err != nil {
		log.Warn("%s.%s.%s {%s} %v", this.zone, this.cluster, this.topic, row, err)
	}

	log.Debug("sync sent [%d/%d] %s", partition, offset, row)
}

func (this *KafkaOutput) asyncSendMessage(row *model.RowsEvent) {
	log.Debug("async sending: %s", row)

	this.ap.Input() <- &sarama.ProducerMessage{
		Topic: this.topic,
		Value: row,
	}
}

func (this *KafkaOutput) initPosition() {
	b, err := this.zkzone.NewCluster(this.cluster).TailMessage(this.topic, 0, 1) // FIXME 1 partition allowed only
	if err != nil {
		panic(err)
	}

	this.pos = &sentPos{}
	if len(b) == 1 {
		// has checkpoint in kafka
		row := &model.RowsEvent{}
		if err := row.Decode(b[0]); err != nil {
			panic(err)
		}

		this.pos.Log = row.Log
		this.pos.Pos = row.Position
	}

	log.Debug("[%s.%s.%s] %+v", this.zone, this.cluster, this.topic, this.pos)
}

func (this *KafkaOutput) markAsSent(row *model.RowsEvent) {
	this.pos.Log = row.Log
	this.pos.Pos = row.Position
}

func init() {
	engine.RegisterPlugin("KafkaOutput", func() engine.Plugin {
		return new(KafkaOutput)
	})
}
<commit_msg>log the skipped msg<commit_after>package output

import (
	"time"

	"github.com/Shopify/sarama"
	"github.com/funkygao/dbus/engine"
	"github.com/funkygao/dbus/model"
	"github.com/funkygao/dbus/plugins/input/myslave"
	"github.com/funkygao/gafka/ctx"
	"github.com/funkygao/gafka/zk"
	conf "github.com/funkygao/jsconf"
	log "github.com/funkygao/log4go"
)

type sentPos struct {
	Log string
	Pos uint32
}

type KafkaOutput struct {
	zone, cluster, topic string

	zkzone   *zk.ZkZone
	ack      sarama.RequiredAcks
	async    bool
	compress bool

	p  sarama.SyncProducer
	ap sarama.AsyncProducer

	sendMessage func(row *model.RowsEvent)

	pos *sentPos

	// FIXME should be shared with MysqlbinlogInput
	// currently, KafkaOutput MUST setup master_host/master_port to correctly checkpoint position
	myslave *myslave.MySlave
}

func (this *KafkaOutput) Init(config *conf.Conf) {
	this.zone = config.String("zone", "")
	this.cluster = config.String("cluster", "")
	this.topic = config.String("topic", "")
	if this.cluster == "" || this.zone == "" || this.topic == "" {
		panic("invalid configuration")
	}

	this.initPosition()

	// ack is ignored in async mode
	this.ack = sarama.RequiredAcks(config.Int("ack", int(sarama.WaitForLocal)))
	this.async = config.Bool("async", false)
	if this.async {
		this.sendMessage = this.asyncSendMessage
	} else {
		this.sendMessage = this.syncSendMessage
	}
	this.compress = config.Bool("compress", false)
	this.zkzone = zk.NewZkZone(zk.DefaultConfig(this.zone, ctx.ZoneZkAddrs(this.zone)))
}

func (this *KafkaOutput) Run(r engine.OutputRunner, h engine.PluginHelper) error {
	this.myslave = engine.Globals().Registered("myslave").(*myslave.MySlave)

	if err := this.prepareProducer(); err != nil {
		return err
	}

	defer func() {
		if this.async {
			this.ap.Close()
		} else {
			this.p.Close()
		}
	}()

	for {
		select {
		case pack, ok := <-r.InChan():
			if !ok {
				return nil
			}

			row, ok := pack.Payload.(*model.RowsEvent)
			if !ok {
				log.Error("bad payload: %+v", pack.Payload)
				continue
			}

			// best effort to reduce dup message to kafka
			if row.Log > this.pos.Log ||
				(row.Log == this.pos.Log && row.Position > this.pos.Pos) {
				this.sendMessage(row)
			} else {
				log.Trace("[%s.%s.%s] skipped {%s}", this.zone, this.cluster, this.topic, row)
			}

			pack.Recycle()
		}
	}

	return nil
}

func (this *KafkaOutput) prepareProducer() error {
	cf := sarama.NewConfig()
	cf.ChannelBufferSize = 256 * 2 // default was 256
	if this.compress {
		cf.Producer.Compression = sarama.CompressionSnappy
	}

	zkcluster := this.zkzone.NewCluster(this.cluster)

	if !this.async {
		cf.Producer.RequiredAcks = this.ack
		p, err := sarama.NewSyncProducer(zkcluster.BrokerList(), cf)
		if err != nil {
			return err
		}

		this.p = p
		return nil
	}

	// async producer
	cf.Producer.Return.Errors = true
	cf.Producer.Return.Successes = true
	cf.Producer.Retry.Backoff = time.Millisecond * 300
	cf.Producer.Retry.Max = 3
	cf.Producer.RequiredAcks = sarama.NoResponse
	cf.Producer.Flush.Frequency = time.Second
	cf.Producer.Flush.Messages = 2000 // TODO
	cf.Producer.Flush.MaxMessages = 0 // unlimited
	ap, err := sarama.NewAsyncProducer(zkcluster.BrokerList(), cf)
	if err != nil {
		return err
	}

	this.ap = ap
	go func() {
		for {
			select {
			case msg := <-this.ap.Successes():
				row := msg.Value.(*model.RowsEvent)
				this.markAsSent(row)
				if err := this.myslave.MarkAsProcessed(row); err != nil {
					log.Error("[%s.%s.%s] {%s} %v", this.zone, this.cluster, this.topic, row, err)
				}

			case err := <-this.ap.Errors():
				// e,g.
				// kafka: Failed to produce message to topic dbustest: kafka server: Message was too large, server rejected it to avoid allocation error.
				row := err.Msg.Value.(*model.RowsEvent)
				log.Error("[%s.%s.%s] %s %s", this.zone, this.cluster, this.topic, err, row.MetaInfo())
			}
		}

	}()

	return nil
}

func (this *KafkaOutput) syncSendMessage(row *model.RowsEvent) {
	msg := &sarama.ProducerMessage{
		Topic: this.topic,
		Value: row,
	}

	var (
		partition int32
		offset    int64
		err       error
	)
	for {
		if partition, offset, err = this.p.SendMessage(msg); err == nil {
			break
		}

		log.Error("%s.%s.%s {%s} %v", this.zone, this.cluster, this.topic, row, err)
		time.Sleep(time.Second)
	}

	this.markAsSent(row)
	if err = this.myslave.MarkAsProcessed(row); err != nil {
		log.Warn("%s.%s.%s {%s} %v", this.zone, this.cluster, this.topic, row, err)
	}

	log.Debug("sync sent [%d/%d] %s", partition, offset, row)
}

func (this *KafkaOutput) asyncSendMessage(row *model.RowsEvent) {
	log.Debug("async sending: %s", row)

	this.ap.Input() <- &sarama.ProducerMessage{
		Topic: this.topic,
		Value: row,
	}
}

func (this *KafkaOutput) initPosition() {
	b, err := this.zkzone.NewCluster(this.cluster).TailMessage(this.topic, 0, 1) // FIXME 1 partition allowed only
	if err != nil {
		panic(err)
	}

	this.pos = &sentPos{}
	if len(b) == 1 {
		// has checkpoint in kafka
		row := &model.RowsEvent{}
		if err := row.Decode(b[0]); err != nil {
			panic(err)
		}

		this.pos.Log = row.Log
		this.pos.Pos = row.Position
	}

	log.Debug("[%s.%s.%s] %+v", this.zone, this.cluster, this.topic, this.pos)
}

func (this *KafkaOutput) markAsSent(row *model.RowsEvent) {
	this.pos.Log = row.Log
	this.pos.Pos = row.Position
}

func init() {
	engine.RegisterPlugin("KafkaOutput", func() engine.Plugin {
		return new(KafkaOutput)
	})
}
<|endoftext|>


<commit_before>//go:build integration

/*
Copyright 2016 The Kubernetes Authors 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 integration

import (
	"context"
	"os"
	"os/exec"
	"strings"
	"testing"
)

// TestDockerFlags makes sure the --docker-env and --docker-opt parameters are respected
func TestDockerFlags(t *testing.T) {
	if NoneDriver() {
		t.Skip("skipping: none driver does not support ssh or bundle docker")
	}
	if ContainerRuntime() != "docker" {
		t.Skipf("skipping: only runs with docker container runtime, currently testing %s", ContainerRuntime())
	}
	MaybeParallel(t)

	profile := UniqueProfileName("docker-flags")
	ctx, cancel := context.WithTimeout(context.Background(), Minutes(30))
	defer CleanupWithLogs(t, profile, cancel)

	// Use the most verbose logging for the simplest test. If it fails, something is very wrong.
	args := append([]string{"start", "-p", profile, "--cache-images=false", "--memory=2048", "--install-addons=false", "--wait=false", "--docker-env=FOO=BAR", "--docker-env=BAZ=BAT", "--docker-opt=debug", "--docker-opt=icc=true", "--alsologtostderr", "-v=5"}, StartArgs()...)
	rr, err := Run(t, exec.CommandContext(ctx, Target(), args...))
	if err != nil {
		t.Errorf("failed to start minikube with args: %q : %v", rr.Command(), err)
	}

	rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "ssh", "sudo systemctl show docker --property=Environment --no-pager"))
	if err != nil {
		t.Errorf("failed to 'systemctl show docker' inside minikube. args %q: %v", rr.Command(), err)
	}

	for _, envVar := range []string{"FOO=BAR", "BAZ=BAT"} {
		if !strings.Contains(rr.Stdout.String(), envVar) {
			t.Errorf("expected env key/value %q to be passed to minikube's docker and be included in: *%q*.", envVar, rr.Stdout)
		}
	}

	rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "ssh", "sudo systemctl show docker --property=ExecStart --no-pager"))
	if err != nil {
		t.Errorf("failed on the second 'systemctl show docker' inside minikube. args %q: %v", rr.Command(), err)
	}
	for _, opt := range []string{"--debug", "--icc=true"} {
		if !strings.Contains(rr.Stdout.String(), opt) {
			t.Fatalf("expected %q output to have include *%s* . output: %q", rr.Command(), opt, rr.Stdout)
		}
	}
}

// TestForceSystemdFlag tests the --force-systemd flag, as one would expect.
func TestForceSystemdFlag(t *testing.T) {
	if NoneDriver() {
		t.Skip("skipping: none driver does not support ssh or bundle docker")
	}
	MaybeParallel(t)

	profile := UniqueProfileName("force-systemd-flag")
	ctx, cancel := context.WithTimeout(context.Background(), Minutes(30))
	defer CleanupWithLogs(t, profile, cancel)

	// Use the most verbose logging for the simplest test. If it fails, something is very wrong.
	args := append([]string{"start", "-p", profile, "--memory=2048", "--force-systemd", "--alsologtostderr", "-v=5"}, StartArgs()...)
	rr, err := Run(t, exec.CommandContext(ctx, Target(), args...))
	if err != nil {
		t.Errorf("failed to start minikube with args: %q : %v", rr.Command(), err)
	}

	containerRuntime := ContainerRuntime()
	switch containerRuntime {
	case "docker":
		validateDockerSystemd(ctx, t, profile)
	case "containerd":
		validateContainerdSystemd(ctx, t, profile)
	case "crio":
		validateCrioSystemd(ctx, t, profile)
	}

}

// validateDockerSystemd makes sure the --force-systemd flag worked with the docker container runtime
func validateDockerSystemd(ctx context.Context, t *testing.T, profile string) {
	rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "ssh", "docker info --format {{.CgroupDriver}}"))
	if err != nil {
		t.Errorf("failed to get docker cgroup driver. args %q: %v", rr.Command(), err)
	}
	if !strings.Contains(rr.Output(), "systemd") {
		t.Fatalf("expected systemd cgroup driver, got: %v", rr.Output())
	}
}

// validateContainerdSystemd makes sure the --force-systemd flag worked with the containerd container runtime
func validateContainerdSystemd(ctx context.Context, t *testing.T, profile string) {
	rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "ssh", "cat /etc/containerd/config.toml"))
	if err != nil {
		t.Errorf("failed to get containerd cgroup driver. args %q: %v", rr.Command(), err)
	}
	if !strings.Contains(rr.Output(), "SystemdCgroup = true") {
		t.Fatalf("expected systemd cgroup driver, got: %v", rr.Output())
	}
}

// validateCrioSystemd makes sure the --force-systemd flag worked with the cri-o container runtime
func validateCrioSystemd(ctx context.Context, t *testing.T, profile string) {
	rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "ssh", "cat /etc/crio/crio.conf"))
	if err != nil {
		t.Errorf("failed to get cri-o cgroup driver. args %q: %v", rr.Command(), err)
	}
	// cri-o defaults to `systemd` if `cgroup_manager` not set, so we remove `cgroup_manager` on force
	if strings.Contains(rr.Output(), "cgroup_manager = ") {
		t.Fatalf("expected systemd cgroup driver, got: %v", rr.Output())
	}
}

// TestForceSystemdEnv makes sure the MINIKUBE_FORCE_SYSTEMD environment variable works just as well as the --force-systemd flag
func TestForceSystemdEnv(t *testing.T) {
	if NoneDriver() {
		t.Skip("skipping: none driver does not support ssh or bundle docker")
	}
	MaybeParallel(t)

	profile := UniqueProfileName("force-systemd-env")
	ctx, cancel := context.WithTimeout(context.Background(), Minutes(30))
	defer CleanupWithLogs(t, profile, cancel)

	args := append([]string{"start", "-p", profile, "--memory=2048", "--alsologtostderr", "-v=5"}, StartArgs()...)
	cmd := exec.CommandContext(ctx, Target(), args...)
	cmd.Env = append(os.Environ(), "MINIKUBE_FORCE_SYSTEMD=true")
	rr, err := Run(t, cmd)
	if err != nil {
		t.Errorf("failed to start minikube with args: %q : %v", rr.Command(), err)
	}
	containerRuntime := ContainerRuntime()
	switch containerRuntime {
	case "docker":
		validateDockerSystemd(ctx, t, profile)
	case "containerd":
		validateContainerdSystemd(ctx, t, profile)
	}
}
<commit_msg>update docker linux crio test<commit_after>//go:build integration

/*
Copyright 2016 The Kubernetes Authors 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 integration

import (
	"context"
	"os"
	"os/exec"
	"strings"
	"testing"
)

// TestDockerFlags makes sure the --docker-env and --docker-opt parameters are respected
func TestDockerFlags(t *testing.T) {
	if NoneDriver() {
		t.Skip("skipping: none driver does not support ssh or bundle docker")
	}
	if ContainerRuntime() != "docker" {
		t.Skipf("skipping: only runs with docker container runtime, currently testing %s", ContainerRuntime())
	}
	MaybeParallel(t)

	profile := UniqueProfileName("docker-flags")
	ctx, cancel := context.WithTimeout(context.Background(), Minutes(30))
	defer CleanupWithLogs(t, profile, cancel)

	// Use the most verbose logging for the simplest test. If it fails, something is very wrong.
	args := append([]string{"start", "-p", profile, "--cache-images=false", "--memory=2048", "--install-addons=false", "--wait=false", "--docker-env=FOO=BAR", "--docker-env=BAZ=BAT", "--docker-opt=debug", "--docker-opt=icc=true", "--alsologtostderr", "-v=5"}, StartArgs()...)
	rr, err := Run(t, exec.CommandContext(ctx, Target(), args...))
	if err != nil {
		t.Errorf("failed to start minikube with args: %q : %v", rr.Command(), err)
	}

	rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "ssh", "sudo systemctl show docker --property=Environment --no-pager"))
	if err != nil {
		t.Errorf("failed to 'systemctl show docker' inside minikube. args %q: %v", rr.Command(), err)
	}

	for _, envVar := range []string{"FOO=BAR", "BAZ=BAT"} {
		if !strings.Contains(rr.Stdout.String(), envVar) {
			t.Errorf("expected env key/value %q to be passed to minikube's docker and be included in: *%q*.", envVar, rr.Stdout)
		}
	}

	rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "ssh", "sudo systemctl show docker --property=ExecStart --no-pager"))
	if err != nil {
		t.Errorf("failed on the second 'systemctl show docker' inside minikube. args %q: %v", rr.Command(), err)
	}
	for _, opt := range []string{"--debug", "--icc=true"} {
		if !strings.Contains(rr.Stdout.String(), opt) {
			t.Fatalf("expected %q output to have include *%s* . output: %q", rr.Command(), opt, rr.Stdout)
		}
	}
}

// TestForceSystemdFlag tests the --force-systemd flag, as one would expect.
func TestForceSystemdFlag(t *testing.T) {
	if NoneDriver() {
		t.Skip("skipping: none driver does not support ssh or bundle docker")
	}
	MaybeParallel(t)

	profile := UniqueProfileName("force-systemd-flag")
	ctx, cancel := context.WithTimeout(context.Background(), Minutes(30))
	defer CleanupWithLogs(t, profile, cancel)

	// Use the most verbose logging for the simplest test. If it fails, something is very wrong.
	args := append([]string{"start", "-p", profile, "--memory=2048", "--force-systemd", "--alsologtostderr", "-v=5"}, StartArgs()...)
	rr, err := Run(t, exec.CommandContext(ctx, Target(), args...))
	if err != nil {
		t.Errorf("failed to start minikube with args: %q : %v", rr.Command(), err)
	}

	containerRuntime := ContainerRuntime()
	switch containerRuntime {
	case "docker":
		validateDockerSystemd(ctx, t, profile)
	case "containerd":
		validateContainerdSystemd(ctx, t, profile)
	case "crio":
		validateCrioSystemd(ctx, t, profile)
	}

}

// validateDockerSystemd makes sure the --force-systemd flag worked with the docker container runtime
func validateDockerSystemd(ctx context.Context, t *testing.T, profile string) {
	rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "ssh", "docker info --format {{.CgroupDriver}}"))
	if err != nil {
		t.Errorf("failed to get docker cgroup driver. args %q: %v", rr.Command(), err)
	}
	if !strings.Contains(rr.Output(), "systemd") {
		t.Fatalf("expected systemd cgroup driver, got: %v", rr.Output())
	}
}

// validateContainerdSystemd makes sure the --force-systemd flag worked with the containerd container runtime
func validateContainerdSystemd(ctx context.Context, t *testing.T, profile string) {
	rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "ssh", "cat /etc/containerd/config.toml"))
	if err != nil {
		t.Errorf("failed to get containerd cgroup driver. args %q: %v", rr.Command(), err)
	}
	if !strings.Contains(rr.Output(), "SystemdCgroup = true") {
		t.Fatalf("expected systemd cgroup driver, got: %v", rr.Output())
	}
}

// validateCrioSystemd makes sure the --force-systemd flag worked with the cri-o container runtime
func validateCrioSystemd(ctx context.Context, t *testing.T, profile string) {
	rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "ssh", "cat /etc/crio/crio.conf.d/02-crio.conf"))
	if err != nil {
		t.Errorf("failed to get cri-o cgroup driver. args %q: %v", rr.Command(), err)
	}
	if strings.Contains(rr.Output(), "cgroup_manager = systemd") {
		t.Fatalf("expected systemd cgroup driver, got: %v", rr.Output())
	}
}

// TestForceSystemdEnv makes sure the MINIKUBE_FORCE_SYSTEMD environment variable works just as well as the --force-systemd flag
func TestForceSystemdEnv(t *testing.T) {
	if NoneDriver() {
		t.Skip("skipping: none driver does not support ssh or bundle docker")
	}
	MaybeParallel(t)

	profile := UniqueProfileName("force-systemd-env")
	ctx, cancel := context.WithTimeout(context.Background(), Minutes(30))
	defer CleanupWithLogs(t, profile, cancel)

	args := append([]string{"start", "-p", profile, "--memory=2048", "--alsologtostderr", "-v=5"}, StartArgs()...)
	cmd := exec.CommandContext(ctx, Target(), args...)
	cmd.Env = append(os.Environ(), "MINIKUBE_FORCE_SYSTEMD=true")
	rr, err := Run(t, cmd)
	if err != nil {
		t.Errorf("failed to start minikube with args: %q : %v", rr.Command(), err)
	}
	containerRuntime := ContainerRuntime()
	switch containerRuntime {
	case "docker":
		validateDockerSystemd(ctx, t, profile)
	case "containerd":
		validateContainerdSystemd(ctx, t, profile)
	}
}
<|endoftext|>