<commit_before>/*
 * Copyright (C) 2015 Red Hat, Inc.
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 ovsdb

import (
	"errors"
	"reflect"
	"sync"
	"sync/atomic"
	"time"

	"github.com/socketplane/libovsdb"

	"github.com/skydive-project/skydive/logging"
)

type OvsClient struct {
	ovsdb     *libovsdb.OvsdbClient
	connected uint64
}

type OvsMonitorHandler interface {
	OnOvsBridgeAdd(monitor *OvsMonitor, uuid string, row *libovsdb.RowUpdate)
	OnOvsBridgeDel(monitor *OvsMonitor, uuid string, row *libovsdb.RowUpdate)
	OnOvsBridgeUpdate(monitor *OvsMonitor, uuid string, row *libovsdb.RowUpdate)
	OnOvsInterfaceAdd(monitor *OvsMonitor, uuid string, row *libovsdb.RowUpdate)
	OnOvsInterfaceDel(monitor *OvsMonitor, uuid string, row *libovsdb.RowUpdate)
	OnOvsInterfaceUpdate(monitor *OvsMonitor, uuid string, row *libovsdb.RowUpdate)
	OnOvsPortAdd(monitor *OvsMonitor, uuid string, row *libovsdb.RowUpdate)
	OnOvsPortDel(monitor *OvsMonitor, uuid string, row *libovsdb.RowUpdate)
	OnOvsPortUpdate(monitor *OvsMonitor, uuid string, row *libovsdb.RowUpdate)
}

type OvsMonitor struct {
	sync.RWMutex
	Protocol        string
	Target          string
	OvsClient       *OvsClient
	MonitorHandlers []OvsMonitorHandler
	bridgeCache     map[string]string
	interfaceCache  map[string]string
	portCache       map[string]string
	columnsExcluded map[string]bool
	ticker          *time.Ticker
	done            chan struct{}
}

const ConnectionPollInterval time.Duration = 4 * time.Second

type Notifier struct {
	monitor *OvsMonitor
}

func (n Notifier) Update(context interface{}, tableUpdates libovsdb.TableUpdates) {
	n.monitor.updateHandler(&tableUpdates)
}

func (n Notifier) Locked([]interface{}) {
}

func (n Notifier) Stolen([]interface{}) {
}

func (n Notifier) Echo([]interface{}) {
}

func (n Notifier) Disconnected(c *libovsdb.OvsdbClient) {
	/* trigger re-connection */
	atomic.StoreUint64(&n.monitor.OvsClient.connected, 0)
}

func (o *OvsClient) Exec(operations ...libovsdb.Operation) ([]libovsdb.OperationResult, error) {
	if atomic.LoadUint64(&o.connected) == 0 {
		return nil, errors.New("OVSDB client is not connected")
	}

	result, err := o.ovsdb.Transact("Open_vSwitch", operations...)
	if err != nil {
		return nil, errors.New(
			"Replies number should be atleast equal to number of Operations ")
	}

	if len(result) < len(operations) {
		return nil, errors.New(
			"Replies number should be atleast equal to number of Operations ")
	}

	for i, o := range result {
		if o.Error != "" && i < len(operations) {
			return nil, errors.New(
				"Transaction Failed due to an error :" +
					o.Error + " details:" + o.Details)
		} else if o.Error != "" {
			return nil, errors.New(
				"Transaction Failed due to an error :" + o.Error)
		}
	}

	return result, nil
}

func (o *OvsMonitor) isConnected() bool {
	return atomic.LoadUint64(&o.OvsClient.connected) == 1
}

func (o *OvsMonitor) bridgeUpdated(bridgeUUID string, row *libovsdb.RowUpdate) {
	logging.GetLogger().Infof("Bridge \"%s(%s)\" updated",
		row.New.Fields["name"], bridgeUUID)

	for _, handler := range o.MonitorHandlers {
		handler.OnOvsBridgeUpdate(o, bridgeUUID, row)
	}
}

func (o *OvsMonitor) bridgeAdded(bridgeUUID string, row *libovsdb.RowUpdate) {
	o.bridgeCache[bridgeUUID] = bridgeUUID

	logging.GetLogger().Infof("New bridge \"%s(%s)\" added",
		row.New.Fields["name"], bridgeUUID)

	for _, handler := range o.MonitorHandlers {
		handler.OnOvsBridgeAdd(o, bridgeUUID, row)
	}
}

func (o *OvsMonitor) bridgeDeleted(bridgeUUID string, row *libovsdb.RowUpdate) {
	delete(o.bridgeCache, bridgeUUID)

	logging.GetLogger().Infof("Bridge \"%s(%s)\" got deleted",
		row.Old.Fields["name"], bridgeUUID)

	for _, handler := range o.MonitorHandlers {
		handler.OnOvsBridgeDel(o, bridgeUUID, row)
	}
}

func (o *OvsMonitor) bridgeUpdateHandler(updates *libovsdb.TableUpdate) {
	empty := libovsdb.Row{}

	o.Lock()
	defer o.Unlock()

	for bridgeUUID, row := range updates.Rows {
		if !reflect.DeepEqual(row.New, empty) {
			if _, ok := o.bridgeCache[bridgeUUID]; ok {
				o.bridgeUpdated(bridgeUUID, &row)

			} else {
				o.bridgeAdded(bridgeUUID, &row)
			}
		} else {
			o.bridgeDeleted(bridgeUUID, &row)
		}
	}
}

func (o *OvsMonitor) interfaceUpdated(interfaceUUID string, row *libovsdb.RowUpdate) {
	logging.GetLogger().Infof("Interface \"%s(%s)\" updated",
		row.New.Fields["name"], interfaceUUID)

	for _, handler := range o.MonitorHandlers {
		handler.OnOvsInterfaceUpdate(o, interfaceUUID, row)
	}
}

func (o *OvsMonitor) interfaceAdded(interfaceUUID string, row *libovsdb.RowUpdate) {
	o.interfaceCache[interfaceUUID] = interfaceUUID

	logging.GetLogger().Infof("New interface \"%s(%s)\" added",
		row.New.Fields["name"], interfaceUUID)

	for _, handler := range o.MonitorHandlers {
		handler.OnOvsInterfaceAdd(o, interfaceUUID, row)
	}
}

func (o *OvsMonitor) interfaceDeleted(interfaceUUID string, row *libovsdb.RowUpdate) {
	delete(o.interfaceCache, interfaceUUID)

	logging.GetLogger().Infof("Interface \"%s(%s)\" got deleted",
		row.Old.Fields["name"], interfaceUUID)

	for _, handler := range o.MonitorHandlers {
		handler.OnOvsInterfaceDel(o, interfaceUUID, row)
	}
}

func (o *OvsMonitor) interfaceUpdateHandler(updates *libovsdb.TableUpdate) {
	empty := libovsdb.Row{}

	o.Lock()
	defer o.Unlock()

	for interfaceUUID, row := range updates.Rows {
		if !reflect.DeepEqual(row.New, empty) {
			if _, ok := o.interfaceCache[interfaceUUID]; ok {
				o.interfaceUpdated(interfaceUUID, &row)
			} else {
				o.interfaceAdded(interfaceUUID, &row)
			}
		} else {
			o.interfaceDeleted(interfaceUUID, &row)
		}
	}
}

func (o *OvsMonitor) portUpdated(portUUID string, row *libovsdb.RowUpdate) {
	logging.GetLogger().Infof("Port \"%s(%s)\" updated",
		row.New.Fields["name"], portUUID)

	for _, handler := range o.MonitorHandlers {
		handler.OnOvsPortUpdate(o, portUUID, row)
	}
}

func (o *OvsMonitor) portAdded(portUUID string, row *libovsdb.RowUpdate) {
	o.portCache[portUUID] = portUUID

	logging.GetLogger().Infof("New port \"%s(%s)\" added",
		row.New.Fields["name"], portUUID)

	for _, handler := range o.MonitorHandlers {
		handler.OnOvsPortAdd(o, portUUID, row)
	}
}

func (o *OvsMonitor) portDeleted(portUUID string, row *libovsdb.RowUpdate) {
	delete(o.portCache, portUUID)

	logging.GetLogger().Infof("Port \"%s(%s)\" got deleted",
		row.Old.Fields["name"], portUUID)

	for _, handler := range o.MonitorHandlers {
		handler.OnOvsPortDel(o, portUUID, row)
	}
}

func (o *OvsMonitor) portUpdateHandler(updates *libovsdb.TableUpdate) {
	empty := libovsdb.Row{}

	o.Lock()
	defer o.Unlock()

	for portUUID, row := range updates.Rows {
		if !reflect.DeepEqual(row.New, empty) {
			if _, ok := o.portCache[portUUID]; ok {
				o.portUpdated(portUUID, &row)
			} else {
				o.portAdded(portUUID, &row)
			}
		} else {
			o.portDeleted(portUUID, &row)
		}
	}
}

func (o *OvsMonitor) updateHandler(updates *libovsdb.TableUpdates) {
	for name, tableUpdate := range updates.Updates {
		switch name {
		case "Interface":
			o.interfaceUpdateHandler(&tableUpdate)
		case "Bridge":
			o.bridgeUpdateHandler(&tableUpdate)
		case "Port":
			o.portUpdateHandler(&tableUpdate)
		}
	}
}

func (o *OvsMonitor) setMonitorRequests(table string, r *map[string]libovsdb.MonitorRequest) error {
	schema, ok := o.OvsClient.ovsdb.Schema["Open_vSwitch"]
	if !ok {
		return errors.New("invalid Database Schema")
	}

	var columns []string
	for column := range schema.Tables[table].Columns {
		if _, found := o.columnsExcluded[column]; !found {
			columns = append(columns, column)
		}
	}

	requests := *r
	requests[table] = libovsdb.MonitorRequest{
		Columns: columns,
		Select: libovsdb.MonitorSelect{
			Initial: true,
			Insert:  true,
			Delete:  true,
			Modify:  true,
		},
	}

	return nil
}

func (o *OvsMonitor) AddMonitorHandler(handler OvsMonitorHandler) {
	o.Lock()
	defer o.Unlock()

	o.MonitorHandlers = append(o.MonitorHandlers, handler)
}

func (o *OvsMonitor) ExcludeColumn(column string) {
	o.columnsExcluded[column] = true
}

func (o *OvsMonitor) monitorOvsdb() error {
	ovsdb, err := libovsdb.ConnectUsingProtocol(o.Protocol, o.Target)
	if err != nil {
		return err
	}
	o.OvsClient.ovsdb = ovsdb
	atomic.StoreUint64(&o.OvsClient.connected, 1)

	notifier := Notifier{monitor: o}
	ovsdb.Register(notifier)

	requests := make(map[string]libovsdb.MonitorRequest)
	err = o.setMonitorRequests("Bridge", &requests)
	if err != nil {
		return err
	}

	err = o.setMonitorRequests("Interface", &requests)
	if err != nil {
		return err
	}

	err = o.setMonitorRequests("Port", &requests)
	if err != nil {
		return err
	}

	updates, err := ovsdb.Monitor("Open_vSwitch", "", requests)
	if err != nil {
		return err
	}

	o.updateHandler(updates)

	return nil
}

// startMonitoring() is a watchdog of OvsClient connection with ConnectionPollInterval
func (o *OvsMonitor) startMonitoring() error {
	o.ticker = time.NewTicker(ConnectionPollInterval)

	for {
		select {
		case <-o.ticker.C:
			if o.isConnected() {
				continue
			}
			if err := o.monitorOvsdb(); err != nil {
				logging.GetLogger().Errorf(": re-connect error %v, will try again", err)
			}
		case <-o.done:
			break
		}
	}
}

func (o *OvsMonitor) StartMonitoring() {
	o.monitorOvsdb()
	go o.startMonitoring()
}

func (o *OvsMonitor) StopMonitoring() {
	if o.OvsClient != nil {
		o.done <- struct{}{}
		o.OvsClient.ovsdb.Disconnect()
	}
}

func NewOvsMonitor(protcol string, target string) *OvsMonitor {
	return &OvsMonitor{
		Protocol:        protcol,
		Target:          target,
		OvsClient:       &OvsClient{ovsdb: nil, connected: 0},
		bridgeCache:     make(map[string]string),
		interfaceCache:  make(map[string]string),
		portCache:       make(map[string]string),
		columnsExcluded: make(map[string]bool),
		ticker:          nil,
		done:            make(chan struct{}),
	}
}
<commit_msg>Do not crash when exiting agent not connected to ovs<commit_after>/*
 * Copyright (C) 2015 Red Hat, Inc.
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 ovsdb

import (
	"errors"
	"reflect"
	"sync"
	"sync/atomic"
	"time"

	"github.com/socketplane/libovsdb"

	"github.com/skydive-project/skydive/logging"
)

type OvsClient struct {
	ovsdb     *libovsdb.OvsdbClient
	connected uint64
}

type OvsMonitorHandler interface {
	OnOvsBridgeAdd(monitor *OvsMonitor, uuid string, row *libovsdb.RowUpdate)
	OnOvsBridgeDel(monitor *OvsMonitor, uuid string, row *libovsdb.RowUpdate)
	OnOvsBridgeUpdate(monitor *OvsMonitor, uuid string, row *libovsdb.RowUpdate)
	OnOvsInterfaceAdd(monitor *OvsMonitor, uuid string, row *libovsdb.RowUpdate)
	OnOvsInterfaceDel(monitor *OvsMonitor, uuid string, row *libovsdb.RowUpdate)
	OnOvsInterfaceUpdate(monitor *OvsMonitor, uuid string, row *libovsdb.RowUpdate)
	OnOvsPortAdd(monitor *OvsMonitor, uuid string, row *libovsdb.RowUpdate)
	OnOvsPortDel(monitor *OvsMonitor, uuid string, row *libovsdb.RowUpdate)
	OnOvsPortUpdate(monitor *OvsMonitor, uuid string, row *libovsdb.RowUpdate)
}

type OvsMonitor struct {
	sync.RWMutex
	Protocol        string
	Target          string
	OvsClient       *OvsClient
	MonitorHandlers []OvsMonitorHandler
	bridgeCache     map[string]string
	interfaceCache  map[string]string
	portCache       map[string]string
	columnsExcluded map[string]bool
	ticker          *time.Ticker
	done            chan struct{}
}

const ConnectionPollInterval time.Duration = 4 * time.Second

type Notifier struct {
	monitor *OvsMonitor
}

func (n Notifier) Update(context interface{}, tableUpdates libovsdb.TableUpdates) {
	n.monitor.updateHandler(&tableUpdates)
}

func (n Notifier) Locked([]interface{}) {
}

func (n Notifier) Stolen([]interface{}) {
}

func (n Notifier) Echo([]interface{}) {
}

func (n Notifier) Disconnected(c *libovsdb.OvsdbClient) {
	/* trigger re-connection */
	atomic.StoreUint64(&n.monitor.OvsClient.connected, 0)
}

func (o *OvsClient) Exec(operations ...libovsdb.Operation) ([]libovsdb.OperationResult, error) {
	if atomic.LoadUint64(&o.connected) == 0 {
		return nil, errors.New("OVSDB client is not connected")
	}

	result, err := o.ovsdb.Transact("Open_vSwitch", operations...)
	if err != nil {
		return nil, errors.New(
			"Replies number should be atleast equal to number of Operations ")
	}

	if len(result) < len(operations) {
		return nil, errors.New(
			"Replies number should be atleast equal to number of Operations ")
	}

	for i, o := range result {
		if o.Error != "" && i < len(operations) {
			return nil, errors.New(
				"Transaction Failed due to an error :" +
					o.Error + " details:" + o.Details)
		} else if o.Error != "" {
			return nil, errors.New(
				"Transaction Failed due to an error :" + o.Error)
		}
	}

	return result, nil
}

func (o *OvsMonitor) isConnected() bool {
	return atomic.LoadUint64(&o.OvsClient.connected) == 1
}

func (o *OvsMonitor) bridgeUpdated(bridgeUUID string, row *libovsdb.RowUpdate) {
	logging.GetLogger().Infof("Bridge \"%s(%s)\" updated",
		row.New.Fields["name"], bridgeUUID)

	for _, handler := range o.MonitorHandlers {
		handler.OnOvsBridgeUpdate(o, bridgeUUID, row)
	}
}

func (o *OvsMonitor) bridgeAdded(bridgeUUID string, row *libovsdb.RowUpdate) {
	o.bridgeCache[bridgeUUID] = bridgeUUID

	logging.GetLogger().Infof("New bridge \"%s(%s)\" added",
		row.New.Fields["name"], bridgeUUID)

	for _, handler := range o.MonitorHandlers {
		handler.OnOvsBridgeAdd(o, bridgeUUID, row)
	}
}

func (o *OvsMonitor) bridgeDeleted(bridgeUUID string, row *libovsdb.RowUpdate) {
	delete(o.bridgeCache, bridgeUUID)

	logging.GetLogger().Infof("Bridge \"%s(%s)\" got deleted",
		row.Old.Fields["name"], bridgeUUID)

	for _, handler := range o.MonitorHandlers {
		handler.OnOvsBridgeDel(o, bridgeUUID, row)
	}
}

func (o *OvsMonitor) bridgeUpdateHandler(updates *libovsdb.TableUpdate) {
	empty := libovsdb.Row{}

	o.Lock()
	defer o.Unlock()

	for bridgeUUID, row := range updates.Rows {
		if !reflect.DeepEqual(row.New, empty) {
			if _, ok := o.bridgeCache[bridgeUUID]; ok {
				o.bridgeUpdated(bridgeUUID, &row)

			} else {
				o.bridgeAdded(bridgeUUID, &row)
			}
		} else {
			o.bridgeDeleted(bridgeUUID, &row)
		}
	}
}

func (o *OvsMonitor) interfaceUpdated(interfaceUUID string, row *libovsdb.RowUpdate) {
	logging.GetLogger().Infof("Interface \"%s(%s)\" updated",
		row.New.Fields["name"], interfaceUUID)

	for _, handler := range o.MonitorHandlers {
		handler.OnOvsInterfaceUpdate(o, interfaceUUID, row)
	}
}

func (o *OvsMonitor) interfaceAdded(interfaceUUID string, row *libovsdb.RowUpdate) {
	o.interfaceCache[interfaceUUID] = interfaceUUID

	logging.GetLogger().Infof("New interface \"%s(%s)\" added",
		row.New.Fields["name"], interfaceUUID)

	for _, handler := range o.MonitorHandlers {
		handler.OnOvsInterfaceAdd(o, interfaceUUID, row)
	}
}

func (o *OvsMonitor) interfaceDeleted(interfaceUUID string, row *libovsdb.RowUpdate) {
	delete(o.interfaceCache, interfaceUUID)

	logging.GetLogger().Infof("Interface \"%s(%s)\" got deleted",
		row.Old.Fields["name"], interfaceUUID)

	for _, handler := range o.MonitorHandlers {
		handler.OnOvsInterfaceDel(o, interfaceUUID, row)
	}
}

func (o *OvsMonitor) interfaceUpdateHandler(updates *libovsdb.TableUpdate) {
	empty := libovsdb.Row{}

	o.Lock()
	defer o.Unlock()

	for interfaceUUID, row := range updates.Rows {
		if !reflect.DeepEqual(row.New, empty) {
			if _, ok := o.interfaceCache[interfaceUUID]; ok {
				o.interfaceUpdated(interfaceUUID, &row)
			} else {
				o.interfaceAdded(interfaceUUID, &row)
			}
		} else {
			o.interfaceDeleted(interfaceUUID, &row)
		}
	}
}

func (o *OvsMonitor) portUpdated(portUUID string, row *libovsdb.RowUpdate) {
	logging.GetLogger().Infof("Port \"%s(%s)\" updated",
		row.New.Fields["name"], portUUID)

	for _, handler := range o.MonitorHandlers {
		handler.OnOvsPortUpdate(o, portUUID, row)
	}
}

func (o *OvsMonitor) portAdded(portUUID string, row *libovsdb.RowUpdate) {
	o.portCache[portUUID] = portUUID

	logging.GetLogger().Infof("New port \"%s(%s)\" added",
		row.New.Fields["name"], portUUID)

	for _, handler := range o.MonitorHandlers {
		handler.OnOvsPortAdd(o, portUUID, row)
	}
}

func (o *OvsMonitor) portDeleted(portUUID string, row *libovsdb.RowUpdate) {
	delete(o.portCache, portUUID)

	logging.GetLogger().Infof("Port \"%s(%s)\" got deleted",
		row.Old.Fields["name"], portUUID)

	for _, handler := range o.MonitorHandlers {
		handler.OnOvsPortDel(o, portUUID, row)
	}
}

func (o *OvsMonitor) portUpdateHandler(updates *libovsdb.TableUpdate) {
	empty := libovsdb.Row{}

	o.Lock()
	defer o.Unlock()

	for portUUID, row := range updates.Rows {
		if !reflect.DeepEqual(row.New, empty) {
			if _, ok := o.portCache[portUUID]; ok {
				o.portUpdated(portUUID, &row)
			} else {
				o.portAdded(portUUID, &row)
			}
		} else {
			o.portDeleted(portUUID, &row)
		}
	}
}

func (o *OvsMonitor) updateHandler(updates *libovsdb.TableUpdates) {
	for name, tableUpdate := range updates.Updates {
		switch name {
		case "Interface":
			o.interfaceUpdateHandler(&tableUpdate)
		case "Bridge":
			o.bridgeUpdateHandler(&tableUpdate)
		case "Port":
			o.portUpdateHandler(&tableUpdate)
		}
	}
}

func (o *OvsMonitor) setMonitorRequests(table string, r *map[string]libovsdb.MonitorRequest) error {
	schema, ok := o.OvsClient.ovsdb.Schema["Open_vSwitch"]
	if !ok {
		return errors.New("invalid Database Schema")
	}

	var columns []string
	for column := range schema.Tables[table].Columns {
		if _, found := o.columnsExcluded[column]; !found {
			columns = append(columns, column)
		}
	}

	requests := *r
	requests[table] = libovsdb.MonitorRequest{
		Columns: columns,
		Select: libovsdb.MonitorSelect{
			Initial: true,
			Insert:  true,
			Delete:  true,
			Modify:  true,
		},
	}

	return nil
}

func (o *OvsMonitor) AddMonitorHandler(handler OvsMonitorHandler) {
	o.Lock()
	defer o.Unlock()

	o.MonitorHandlers = append(o.MonitorHandlers, handler)
}

func (o *OvsMonitor) ExcludeColumn(column string) {
	o.columnsExcluded[column] = true
}

func (o *OvsMonitor) monitorOvsdb() error {
	ovsdb, err := libovsdb.ConnectUsingProtocol(o.Protocol, o.Target)
	if err != nil {
		return err
	}
	o.OvsClient.ovsdb = ovsdb
	atomic.StoreUint64(&o.OvsClient.connected, 1)

	notifier := Notifier{monitor: o}
	ovsdb.Register(notifier)

	requests := make(map[string]libovsdb.MonitorRequest)
	err = o.setMonitorRequests("Bridge", &requests)
	if err != nil {
		return err
	}

	err = o.setMonitorRequests("Interface", &requests)
	if err != nil {
		return err
	}

	err = o.setMonitorRequests("Port", &requests)
	if err != nil {
		return err
	}

	updates, err := ovsdb.Monitor("Open_vSwitch", "", requests)
	if err != nil {
		return err
	}

	o.updateHandler(updates)

	return nil
}

// startMonitoring() is a watchdog of OvsClient connection with ConnectionPollInterval
func (o *OvsMonitor) startMonitoring() error {
	o.ticker = time.NewTicker(ConnectionPollInterval)

	for {
		select {
		case <-o.ticker.C:
			if o.isConnected() {
				continue
			}
			if err := o.monitorOvsdb(); err != nil {
				logging.GetLogger().Errorf(": re-connect error %v, will try again", err)
			}
		case <-o.done:
			break
		}
	}
}

func (o *OvsMonitor) StartMonitoring() {
	o.monitorOvsdb()
	go o.startMonitoring()
}

func (o *OvsMonitor) StopMonitoring() {
	if o.OvsClient != nil {
		o.done <- struct{}{}
		if o.OvsClient.ovsdb != nil {
			o.OvsClient.ovsdb.Disconnect()
		}
	}
}

func NewOvsMonitor(protcol string, target string) *OvsMonitor {
	return &OvsMonitor{
		Protocol:        protcol,
		Target:          target,
		OvsClient:       &OvsClient{ovsdb: nil, connected: 0},
		bridgeCache:     make(map[string]string),
		interfaceCache:  make(map[string]string),
		portCache:       make(map[string]string),
		columnsExcluded: make(map[string]bool),
		ticker:          nil,
		done:            make(chan struct{}),
	}
}
<|endoftext|>


<commit_before>package glob

import (
	"reflect"
	"testing"
)

var parseCases = []struct {
	src  string
	want []Segment
}{
	{``, []Segment{}},
	{`foo`, []Segment{{Literal, "foo"}}},
	{`*foo*bar`, []Segment{
		{Star, ""}, {Literal, "foo"}, {Star, ""}, {Literal, "bar"}}},
	{`foo**bar`, []Segment{
		{Literal, "foo"}, {StarStar, ""}, {Literal, "bar"}}},
	{`/usr/a**b/c`, []Segment{
		{Slash, ""}, {Literal, "usr"}, {Slash, ""}, {Literal, "a"},
		{StarStar, ""}, {Literal, "b"}, {Slash, ""}, {Literal, "c"}}},
	{`??b`, []Segment{
		{Question, ""}, {Question, ""}, {Literal, "b"}}},
	// Multiple slashes should be parsed as one.
	{`//a//b`, []Segment{
		{Slash, ""}, {Literal, "a"}, {Slash, ""}, {Literal, "b"}}},
	// Escaping.
	{`\*\?b`, []Segment{
		{Literal, "*?b"},
	}},
}

func TestParse(t *testing.T) {
	for _, tc := range parseCases {
		p := Parse(tc.src)
		if !reflect.DeepEqual(p.Segments, tc.want) {
			t.Errorf("Parse(%q) => %v, want %v", p, tc.want)
		}
	}
}
<commit_msg>glob/parse_test.go: Fix a wrong call to Errorf.<commit_after>package glob

import (
	"reflect"
	"testing"
)

var parseCases = []struct {
	src  string
	want []Segment
}{
	{``, []Segment{}},
	{`foo`, []Segment{{Literal, "foo"}}},
	{`*foo*bar`, []Segment{
		{Star, ""}, {Literal, "foo"}, {Star, ""}, {Literal, "bar"}}},
	{`foo**bar`, []Segment{
		{Literal, "foo"}, {StarStar, ""}, {Literal, "bar"}}},
	{`/usr/a**b/c`, []Segment{
		{Slash, ""}, {Literal, "usr"}, {Slash, ""}, {Literal, "a"},
		{StarStar, ""}, {Literal, "b"}, {Slash, ""}, {Literal, "c"}}},
	{`??b`, []Segment{
		{Question, ""}, {Question, ""}, {Literal, "b"}}},
	// Multiple slashes should be parsed as one.
	{`//a//b`, []Segment{
		{Slash, ""}, {Literal, "a"}, {Slash, ""}, {Literal, "b"}}},
	// Escaping.
	{`\*\?b`, []Segment{
		{Literal, "*?b"},
	}},
}

func TestParse(t *testing.T) {
	for _, tc := range parseCases {
		p := Parse(tc.src)
		if !reflect.DeepEqual(p.Segments, tc.want) {
			t.Errorf("Parse(%q) => %v, want %v", tc.src, p, tc.want)
		}
	}
}
<|endoftext|>


<commit_before>/* Copyright 2020 Google Inc.

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

    https://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 main

import (
	"context"
	"flag"
	"time"

	log "github.com/golang/glog"
	gnoiOS "github.com/google/gnxi/gnoi/os"
	"github.com/google/gnxi/utils/credentials"
	"google.golang.org/grpc"
)

var (
	targetAddr = flag.String("target_addr", ":9339", "The target address in the format of host:port")
	targetName = flag.String("target_name", "", "The target name used to verify the hostname returned by TLS handshake")
	version    = flag.String("version", "", "Version of the OS required when using the activate operation")
	osFile     = flag.String("os", "", "Path to the OS image for the install operation")
	op         = flag.String("op", "", "OS service operation. Can be one of: install, activate, verify")
	timeOut    = flag.Duration("time_out", 5*time.Second, "Timeout for the operation, 5 seconds by default")

	client *gnoiOS.Client
	ctx    context.Context
	cancel func()
)

func main() {
	flag.Parse()

	if *targetName == "" {
		flag.Usage()
		log.Exit("-target_name must be specified")
	}
	opts := credentials.ClientCredentials(*targetName)
	conn, err := grpc.Dial(*targetAddr, opts...)
	if err != nil {
		log.Exitf("Dialing to %s failed: %v", *targetAddr, err)
	}
	defer conn.Close()

	client = gnoiOS.NewClient(conn)
	ctx, cancel = context.WithTimeout(context.Background(), *timeOut)
	defer cancel()

	switch *op {
	case "install":
		install()
	case "activate":
		activate()
	case "verify":
		verify()
	default:
		log.Error("No operation provided. Provide one with -op")
	}
}

// install the OS image onto the target.
func install() {
	if *osFile == "" {
		log.Error("No OS image path provided. Provide one with -os")
		return
	}
	// TODO: Add Install RPC call
}

// activate the OS version to be used upon next reboot on the target.
func activate() {
	if *version == "" {
		log.Exit("No version provided. Provide one with -version")
	}
	err := client.Activate(ctx, *version)
	if err != nil {
		log.Exit("Failed Activate:", err)
	}
}

// verify the version of the OS running on the target.
func verify() {
	// TODO: Add Verify RPC call
}
<commit_msg>Clarify invalid operation<commit_after>/* Copyright 2020 Google Inc.

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

    https://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 main

import (
	"context"
	"flag"
	"time"

	log "github.com/golang/glog"
	gnoiOS "github.com/google/gnxi/gnoi/os"
	"github.com/google/gnxi/utils/credentials"
	"google.golang.org/grpc"
)

var (
	targetAddr = flag.String("target_addr", ":9339", "The target address in the format of host:port")
	targetName = flag.String("target_name", "", "The target name used to verify the hostname returned by TLS handshake")
	version    = flag.String("version", "", "Version of the OS required when using the activate operation")
	osFile     = flag.String("os", "", "Path to the OS image for the install operation")
	op         = flag.String("op", "", "OS service operation. Can be one of: install, activate, verify")
	timeOut    = flag.Duration("time_out", 5*time.Second, "Timeout for the operation, 5 seconds by default")

	client *gnoiOS.Client
	ctx    context.Context
	cancel func()
)

func main() {
	flag.Parse()

	if *targetName == "" {
		flag.Usage()
		log.Exit("-target_name must be specified")
	}
	opts := credentials.ClientCredentials(*targetName)
	conn, err := grpc.Dial(*targetAddr, opts...)
	if err != nil {
		log.Exitf("Dialing to %s failed: %v", *targetAddr, err)
	}
	defer conn.Close()

	client = gnoiOS.NewClient(conn)
	ctx, cancel = context.WithTimeout(context.Background(), *timeOut)
	defer cancel()

	switch *op {
	case "install":
		install()
	case "activate":
		activate()
	case "verify":
		verify()
	default:
		flag.Usage()
		log.Error("Invalid operation provided. Provide one with -op")
	}
}

// install the OS image onto the target.
func install() {
	if *osFile == "" {
		log.Error("No OS image path provided. Provide one with -os")
		return
	}
	// TODO: Add Install RPC call
}

// activate the OS version to be used upon next reboot on the target.
func activate() {
	if *version == "" {
		log.Exit("No version provided. Provide one with -version")
	}
	err := client.Activate(ctx, *version)
	if err != nil {
		log.Exit("Failed Activate:", err)
	}
}

// verify the version of the OS running on the target.
func verify() {
	// TODO: Add Verify RPC call
}
<|endoftext|>


<commit_before>package py

/*
#include "Python.h"

PyObject* getPyNone() {
  return Py_BuildValue("");
}
*/
import "C"
import (
	"fmt"
	"pfi/sensorbee/sensorbee/data"
	"unsafe"
)

func getNewPyDic(m map[string]interface{}) Object {
	return Object{}
}

func newPyObj(v data.Value) (Object, error) {
	var pyobj *C.PyObject
	var err error
	switch v.Type() {
	case data.TypeBool:
		b, _ := data.ToInt(v)
		pyobj = C.PyBool_FromLong(C.long(b))
	case data.TypeInt:
		i, _ := data.AsInt(v)
		pyobj = C.PyInt_FromLong(C.long(i))
	case data.TypeFloat:
		f, _ := data.AsFloat(v)
		pyobj = C.PyFloat_FromDouble(C.double(f))
	case data.TypeString:
		s, _ := data.AsString(v)
		pyobj = newPyString(s)
	case data.TypeBlob:
		b, _ := data.AsBlob(v)
		cb := (*C.char)(unsafe.Pointer(&b[0]))
		pyobj = C.PyByteArray_FromStringAndSize(cb, C.Py_ssize_t(len(b)))
	case data.TypeTimestamp:
		t, _ := data.AsTimestamp(v)
		pyobj = getPyDateTime(t)
	case data.TypeArray:
		innerArray, _ := data.AsArray(v)
		pyobj, err = newPyArray(innerArray)
	case data.TypeMap:
		innerMap, _ := data.AsMap(v)
		pyobj, err = newPyMap(innerMap)
	case data.TypeNull:
		pyobj = C.getPyNone()
	default:
		err = fmt.Errorf("not supported type in sensorbee/py: %s", v.Type())
	}
	return Object{p: pyobj}, err
}

func newPyString(s string) *C.PyObject {
	cs := C.CString(s)
	defer C.free(unsafe.Pointer(cs))
	return C.PyString_FromString(cs)
}

func newPyArray(a data.Array) (*C.PyObject, error) {
	pylist := C.PyList_New(C.Py_ssize_t(len(a)))
	if pylist == nil {
		return nil, getPyErr()
	}
	for i, v := range a {
		value, err := newPyObj(v)
		if err != nil {
			return nil, err
		}
		// PyList object takes over the value's reference, and not need to
		// decrease reference counter.
		C.PyList_SetItem(pylist, C.Py_ssize_t(i), value.p)
	}
	return pylist, nil
}

func newPyMap(m data.Map) (*C.PyObject, error) {
	pydict := C.PyDict_New()
	if pydict == nil {
		return nil, getPyErr()
	}
	for k, v := range m {
		err := func() error {
			key := newPyString(k)
			value, err := newPyObj(v)
			if err != nil {
				return err
			}
			defer value.decRef()
			C.PyDict_SetItem(pydict, key, value.p)
			return nil
		}()
		if err != nil {
			return nil, err
		}
	}
	return pydict, nil
}
<commit_msg>Add check for errors in python.<commit_after>package py

/*
#include "Python.h"

PyObject* getPyNone() {
  return Py_BuildValue("");
}
*/
import "C"
import (
	"fmt"
	"pfi/sensorbee/sensorbee/data"
	"unsafe"
)

func getNewPyDic(m map[string]interface{}) Object {
	return Object{}
}

func newPyObj(v data.Value) (Object, error) {
	var pyobj *C.PyObject
	var err error
	switch v.Type() {
	case data.TypeBool:
		b, _ := data.ToInt(v)
		pyobj = C.PyBool_FromLong(C.long(b))
	case data.TypeInt:
		i, _ := data.AsInt(v)
		pyobj = C.PyInt_FromLong(C.long(i))
	case data.TypeFloat:
		f, _ := data.AsFloat(v)
		pyobj = C.PyFloat_FromDouble(C.double(f))
	case data.TypeString:
		s, _ := data.AsString(v)
		pyobj = newPyString(s)
	case data.TypeBlob:
		b, _ := data.AsBlob(v)
		cb := (*C.char)(unsafe.Pointer(&b[0]))
		pyobj = C.PyByteArray_FromStringAndSize(cb, C.Py_ssize_t(len(b)))
	case data.TypeTimestamp:
		t, _ := data.AsTimestamp(v)
		pyobj = getPyDateTime(t)
	case data.TypeArray:
		innerArray, _ := data.AsArray(v)
		pyobj, err = newPyArray(innerArray)
	case data.TypeMap:
		innerMap, _ := data.AsMap(v)
		pyobj, err = newPyMap(innerMap)
	case data.TypeNull:
		pyobj = C.getPyNone()
	default:
		err = fmt.Errorf("not supported type in sensorbee/py: %s", v.Type())
	}

	if pyobj == nil && err == nil {
		return Object{}, getPyErr()
	}
	return Object{p: pyobj}, err
}

func newPyString(s string) *C.PyObject {
	cs := C.CString(s)
	defer C.free(unsafe.Pointer(cs))
	return C.PyString_FromString(cs)
}

func newPyArray(a data.Array) (*C.PyObject, error) {
	pylist := C.PyList_New(C.Py_ssize_t(len(a)))
	if pylist == nil {
		return nil, getPyErr()
	}
	for i, v := range a {
		value, err := newPyObj(v)
		if err != nil {
			return nil, err
		}
		// PyList object takes over the value's reference, and not need to
		// decrease reference counter.
		C.PyList_SetItem(pylist, C.Py_ssize_t(i), value.p)
	}
	return pylist, nil
}

func newPyMap(m data.Map) (*C.PyObject, error) {
	pydict := C.PyDict_New()
	if pydict == nil {
		return nil, getPyErr()
	}
	for k, v := range m {
		err := func() error {
			key := newPyString(k)
			value, err := newPyObj(v)
			if err != nil {
				return err
			}
			defer value.decRef()
			C.PyDict_SetItem(pydict, key, value.p)
			return nil
		}()
		if err != nil {
			return nil, err
		}
	}
	return pydict, nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/user"
	"runtime"
	"sort"
	"strings"
	"sync/atomic"
	"time"
)

const (
	helloVersion = "0.4"
)

var knownPaths []string
var boottime time.Time
var banner string
var requests int64
var quota int64
var usr *user.User

func inc() int64 {
	return atomic.AddInt64(&requests, 1)
}

func get() int64 {
	return atomic.LoadInt64(&requests)
}

func getVersion() string {
	return fmt.Sprintf("version=%s runtime=%s pid=%d host=%s GOMAXPROCS=%d", helloVersion, runtime.Version(), os.Getpid(), getHostname(), runtime.GOMAXPROCS(0))
}

func getHostname() string {
	host, errHost := os.Hostname()
	if errHost != nil {
		host = host + " (host error: " + errHost.Error() + ")"
	}
	return host
}

func dumpVersion(path string) {
	log.Printf("dumpVersion: writing to touch=%s", path)
	v := getVersion()
	err := ioutil.WriteFile(path, []byte(v), 0640)
	if err != nil {
		log.Printf("dumpVersion: touch=%s: %v", path, err)
	}
}

func main() {

	boottime = time.Now()

	tls := true

	log.Print(getVersion())

	defaultBanner := "banner default"
	if envBanner := os.Getenv("GWH_BANNER"); envBanner != "" {
		defaultBanner = envBanner
	}

	var errUser error
	usr, errUser = user.Current()
	if errUser != nil {
		log.Printf("current user error: %v", errUser)
	}
	if usr != nil {
		log.Printf("user: %s (uid: %s)", usr.Username, usr.Uid)
	}

	currDir, err := os.Getwd()
	if err != nil {
		log.Fatalf("Getwd: %s", err)
	}

	var addr, httpsAddr, key, cert string
	var disableKeepalive bool
	var touch string
	var showVersion bool

	flag.StringVar(&key, "key", "key.pem", "TLS key file")
	flag.StringVar(&cert, "cert", "cert.pem", "TLS cert file")
	flag.StringVar(&addr, "addr", ":8080", "HTTP listen address")
	flag.StringVar(&httpsAddr, "httpsAddr", ":8443", "HTTPS listen address")
	flag.StringVar(&banner, "banner", defaultBanner, "banner will be displayed (env var GWH_BANNER sets default banner)")
	flag.StringVar(&touch, "touch", "", "write version info into this file, if specified")
	flag.BoolVar(&showVersion, "version", false, "does not actually run")
	flag.BoolVar(&disableKeepalive, "disableKeepalive", false, "disable keepalive")
	flag.Int64Var(&quota, "quota", 0, "if defined, service is terminated after serving that amount of requests")
	flag.Parse()

	if touch != "" {
		dumpVersion(touch)
	}

	keepalive := !disableKeepalive

	log.Print("banner: ", banner)
	log.Print("keepalive: ", keepalive)

	if !fileExists(key) {
		log.Printf("TLS key file not found: %s - disabling TLS", key)
		tls = false
	}

	if !fileExists(cert) {
		log.Printf("TLS cert file not found: %s - disabling TLS", cert)
		tls = false
	}

	if showVersion {
		return
	}

	// default handler
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { rootHandler(w, r, keepalive) })

	registerStatic("/www/", currDir)

	log.Printf("using TCP ports HTTP=%s HTTPS=%s TLS=%v", addr, httpsAddr, tls)

	log.Printf("requests quota: %d (0=unlimited)", quota)

	if tls {
		serveHTTPS(addr, httpsAddr, cert, key, keepalive)
		return
	}

	log.Printf("serving HTTP on TCP %s", addr)
	if err := listenAndServe(addr, nil, keepalive); err != nil {
		log.Fatalf("listenAndServe: %s: %v", addr, err)
	}
}

func serveHTTPS(addr, httpsAddr, cert, key string, keepalive bool) {
	httpPort := "80"
	h := strings.Split(addr, ":")
	if len(h) > 1 {
		httpPort = h[1]
	}

	httpsPort := "443"
	hs := strings.Split(httpsAddr, ":")
	if len(hs) > 1 {
		httpsPort = hs[1]
	}

	if httpPort != httpsPort {
		log.Printf("installing redirect from HTTP=%s to HTTPS=%s", addr, httpsPort)

		redirectTLS := func(w http.ResponseWriter, r *http.Request) {
			host := strings.Split(r.Host, ":")[0]
			//code := http.StatusMovedPermanently // 301 permanent
			code := http.StatusFound // 302 temporary
			http.Redirect(w, r, "https://"+host+":"+httpsPort+r.RequestURI, code)
		}

		// http-to-https redirect server
		go func() {
			if err := listenAndServe(addr, http.HandlerFunc(redirectTLS), keepalive); err != nil {
				log.Fatalf("redirect: listenAndServe: %s: %v", addr, err)
			}
		}()
	}

	log.Printf("serving HTTPS on TCP %s", httpsAddr)
	if err := listenAndServeTLS(httpsAddr, cert, key, nil, keepalive); err != nil {
		log.Fatalf("listenAndServeTLS: %s: %v", httpsAddr, err)
	}
}

func listenAndServe(addr string, handler http.Handler, keepalive bool) error {
	server := &http.Server{Addr: addr, Handler: handler}
	server.SetKeepAlivesEnabled(keepalive)
	return server.ListenAndServe()
}

func listenAndServeTLS(addr, certFile, keyFile string, handler http.Handler, keepalive bool) error {
	server := &http.Server{Addr: addr, Handler: handler}
	server.SetKeepAlivesEnabled(keepalive)
	return server.ListenAndServeTLS(certFile, keyFile)
}

func fileExists(path string) bool {
	_, err := os.Stat(path)
	return err == nil
}

type staticHandler struct {
	innerHandler http.Handler
}

func registerStatic(path, dir string) {
	http.Handle(path, staticHandler{http.StripPrefix(path, http.FileServer(http.Dir(dir)))})
	knownPaths = append(knownPaths, path)
	log.Printf("mapping www path %s to directory %s", path, dir)
}

func (handler staticHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	count := inc()
	log.Printf("staticHandler.ServeHTTP req=%d url=%s from=%s", count, r.URL.Path, r.RemoteAddr)
	handler.innerHandler.ServeHTTP(w, r)
	checkQuota("staticHandler.ServeHTTP", count)
}

func checkQuota(label string, count int64) {
	if quota > 0 && count > quota {
		log.Printf("%s req=%d quota=%d EXITING", label, count, quota)
		os.Exit(0)
	}
}

func rootHandler(w http.ResponseWriter, r *http.Request, keepalive bool) {
	count := inc()
	log.Printf("rootHandler: req=%d from=%s", count, r.RemoteAddr)
	log.Printf("rootHandler: req=%d method=%s host=%s path=%s", count, r.Method, r.Host, r.URL.Path)
	log.Printf("rootHandler: req=%d query=%s", count, r.URL.RawQuery)

	var paths string
	for _, p := range knownPaths {
		paths += fmt.Sprintf("%s
", p, p)
	}

	var errMsg string
	if r.URL.Path != "/" {
		format := `

Path not found!

Method: %s

Host: %s

Path not found: [%s]

Query: [%s]

`
		errMsg = fmt.Sprintf(format, r.Method, r.Host, r.URL.Path, r.URL.RawQuery)
	}

	header :=
		`<!DOCTYPE html>

    <title>gowebhello root page</title>

gowebhello root page

    https://github.com/udhos/gowebhello is a simple golang replacement for 'python -m SimpleHTTPServer'.

`
	bodyTempl :=
		`
Welcome!

	gowebhello version %s runtime %s

        Keepalive: %v

	Application banner: %s

	Application arguments: %v

	Application dir: %s

	Process: %d

	User: %s (uid: %s)

	Server hostname: %s

	Your address: %s

	Request method=%s host=%s path=[%s] query=[%s]

	Current time: %s

	Uptime: %s

	Requests: %d

    %s

All known paths:

    %s
`

	footer :=
		`

`

	cwd, errCwd := os.Getwd()
	if errCwd != nil {
		cwd = cwd + " (error: " + errCwd.Error() + ")"
	}

	host := getHostname()

	now := time.Now()

	username := "?"
	uid := "?"

	if usr != nil {
		username = usr.Username
		uid = usr.Uid
	}

	body := fmt.Sprintf(bodyTempl, helloVersion, runtime.Version(), keepalive, banner, os.Args, cwd, os.Getpid(), username, uid, host, r.RemoteAddr, r.Method, r.Host, r.URL.Path, r.URL.RawQuery, now, time.Since(boottime), get(), errMsg, paths)

	if !keepalive {
		w.Header().Set("Connection", "close")
	}

	io.WriteString(w, header)
	io.WriteString(w, body)
	showHeaders(w, r)
	showReqBody(w, r)
	io.WriteString(w, footer)

	checkQuota("rootHandler", count)
}

func showReqBody(w http.ResponseWriter, r *http.Request) {

	io.WriteString(w, "
Request Body
\n")

	buf, err := ioutil.ReadAll(r.Body)
	if err != nil {
		m := fmt.Sprintf("request body: %v", err)
		log.Print(m)
		io.WriteString(w, m)
		return
	}

	bodySize := len(buf)

	log.Printf("body size: %d", bodySize)

	io.WriteString(w, fmt.Sprintf("
request body size: %d
\n", bodySize))

	io.WriteString(w, "
\n")
	io.WriteString(w, string(buf))
	io.WriteString(w, "\n")
	io.WriteString(w, "
\n")
}

func showHeaders(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, "
Headers
\n")

	var headers []string
	for k, v := range r.Header {
		for _, vv := range v {
			headers = append(headers, k+": "+vv+"
\n")
		}
	}

	sort.Strings(headers)

	for _, h := range headers {
		io.WriteString(w, h)
	}
}
<commit_msg>Version up.<commit_after>package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/user"
	"runtime"
	"sort"
	"strings"
	"sync/atomic"
	"time"
)

const (
	helloVersion = "0.5"
)

var knownPaths []string
var boottime time.Time
var banner string
var requests int64
var quota int64
var usr *user.User

func inc() int64 {
	return atomic.AddInt64(&requests, 1)
}

func get() int64 {
	return atomic.LoadInt64(&requests)
}

func getVersion() string {
	return fmt.Sprintf("version=%s runtime=%s pid=%d host=%s GOMAXPROCS=%d", helloVersion, runtime.Version(), os.Getpid(), getHostname(), runtime.GOMAXPROCS(0))
}

func getHostname() string {
	host, errHost := os.Hostname()
	if errHost != nil {
		host = host + " (host error: " + errHost.Error() + ")"
	}
	return host
}

func dumpVersion(path string) {
	log.Printf("dumpVersion: writing to touch=%s", path)
	v := getVersion()
	err := ioutil.WriteFile(path, []byte(v), 0640)
	if err != nil {
		log.Printf("dumpVersion: touch=%s: %v", path, err)
	}
}

func main() {

	boottime = time.Now()

	tls := true

	log.Print(getVersion())

	defaultBanner := "banner default"
	if envBanner := os.Getenv("GWH_BANNER"); envBanner != "" {
		defaultBanner = envBanner
	}

	var errUser error
	usr, errUser = user.Current()
	if errUser != nil {
		log.Printf("current user error: %v", errUser)
	}
	if usr != nil {
		log.Printf("user: %s (uid: %s)", usr.Username, usr.Uid)
	}

	currDir, err := os.Getwd()
	if err != nil {
		log.Fatalf("Getwd: %s", err)
	}

	var addr, httpsAddr, key, cert string
	var disableKeepalive bool
	var touch string
	var showVersion bool

	flag.StringVar(&key, "key", "key.pem", "TLS key file")
	flag.StringVar(&cert, "cert", "cert.pem", "TLS cert file")
	flag.StringVar(&addr, "addr", ":8080", "HTTP listen address")
	flag.StringVar(&httpsAddr, "httpsAddr", ":8443", "HTTPS listen address")
	flag.StringVar(&banner, "banner", defaultBanner, "banner will be displayed (env var GWH_BANNER sets default banner)")
	flag.StringVar(&touch, "touch", "", "write version info into this file, if specified")
	flag.BoolVar(&showVersion, "version", false, "does not actually run")
	flag.BoolVar(&disableKeepalive, "disableKeepalive", false, "disable keepalive")
	flag.Int64Var(&quota, "quota", 0, "if defined, service is terminated after serving that amount of requests")
	flag.Parse()

	if touch != "" {
		dumpVersion(touch)
	}

	keepalive := !disableKeepalive

	log.Print("banner: ", banner)
	log.Print("keepalive: ", keepalive)

	if !fileExists(key) {
		log.Printf("TLS key file not found: %s - disabling TLS", key)
		tls = false
	}

	if !fileExists(cert) {
		log.Printf("TLS cert file not found: %s - disabling TLS", cert)
		tls = false
	}

	if showVersion {
		return
	}

	// default handler
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { rootHandler(w, r, keepalive) })

	registerStatic("/www/", currDir)

	log.Printf("using TCP ports HTTP=%s HTTPS=%s TLS=%v", addr, httpsAddr, tls)

	log.Printf("requests quota: %d (0=unlimited)", quota)

	if tls {
		serveHTTPS(addr, httpsAddr, cert, key, keepalive)
		return
	}

	log.Printf("serving HTTP on TCP %s", addr)
	if err := listenAndServe(addr, nil, keepalive); err != nil {
		log.Fatalf("listenAndServe: %s: %v", addr, err)
	}
}

func serveHTTPS(addr, httpsAddr, cert, key string, keepalive bool) {
	httpPort := "80"
	h := strings.Split(addr, ":")
	if len(h) > 1 {
		httpPort = h[1]
	}

	httpsPort := "443"
	hs := strings.Split(httpsAddr, ":")
	if len(hs) > 1 {
		httpsPort = hs[1]
	}

	if httpPort != httpsPort {
		log.Printf("installing redirect from HTTP=%s to HTTPS=%s", addr, httpsPort)

		redirectTLS := func(w http.ResponseWriter, r *http.Request) {
			host := strings.Split(r.Host, ":")[0]
			//code := http.StatusMovedPermanently // 301 permanent
			code := http.StatusFound // 302 temporary
			http.Redirect(w, r, "https://"+host+":"+httpsPort+r.RequestURI, code)
		}

		// http-to-https redirect server
		go func() {
			if err := listenAndServe(addr, http.HandlerFunc(redirectTLS), keepalive); err != nil {
				log.Fatalf("redirect: listenAndServe: %s: %v", addr, err)
			}
		}()
	}

	log.Printf("serving HTTPS on TCP %s", httpsAddr)
	if err := listenAndServeTLS(httpsAddr, cert, key, nil, keepalive); err != nil {
		log.Fatalf("listenAndServeTLS: %s: %v", httpsAddr, err)
	}
}

func listenAndServe(addr string, handler http.Handler, keepalive bool) error {
	server := &http.Server{Addr: addr, Handler: handler}
	server.SetKeepAlivesEnabled(keepalive)
	return server.ListenAndServe()
}

func listenAndServeTLS(addr, certFile, keyFile string, handler http.Handler, keepalive bool) error {
	server := &http.Server{Addr: addr, Handler: handler}
	server.SetKeepAlivesEnabled(keepalive)
	return server.ListenAndServeTLS(certFile, keyFile)
}

func fileExists(path string) bool {
	_, err := os.Stat(path)
	return err == nil
}

type staticHandler struct {
	innerHandler http.Handler
}

func registerStatic(path, dir string) {
	http.Handle(path, staticHandler{http.StripPrefix(path, http.FileServer(http.Dir(dir)))})
	knownPaths = append(knownPaths, path)
	log.Printf("mapping www path %s to directory %s", path, dir)
}

func (handler staticHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	count := inc()
	log.Printf("staticHandler.ServeHTTP req=%d url=%s from=%s", count, r.URL.Path, r.RemoteAddr)
	handler.innerHandler.ServeHTTP(w, r)
	checkQuota("staticHandler.ServeHTTP", count)
}

func checkQuota(label string, count int64) {
	if quota > 0 && count > quota {
		log.Printf("%s req=%d quota=%d EXITING", label, count, quota)
		os.Exit(0)
	}
}

func rootHandler(w http.ResponseWriter, r *http.Request, keepalive bool) {
	count := inc()
	log.Printf("rootHandler: req=%d from=%s", count, r.RemoteAddr)
	log.Printf("rootHandler: req=%d method=%s host=%s path=%s", count, r.Method, r.Host, r.URL.Path)
	log.Printf("rootHandler: req=%d query=%s", count, r.URL.RawQuery)

	var paths string
	for _, p := range knownPaths {
		paths += fmt.Sprintf("%s
", p, p)
	}

	var errMsg string
	if r.URL.Path != "/" {
		format := `

Path not found!

Method: %s

Host: %s

Path not found: [%s]

Query: [%s]

`
		errMsg = fmt.Sprintf(format, r.Method, r.Host, r.URL.Path, r.URL.RawQuery)
	}

	header :=
		`<!DOCTYPE html>

    <title>gowebhello root page</title>

gowebhello root page

    https://github.com/udhos/gowebhello is a simple golang replacement for 'python -m SimpleHTTPServer'.

`
	bodyTempl :=
		`
Welcome!

	gowebhello version %s runtime %s

        Keepalive: %v

	Application banner: %s

	Application arguments: %v

	Application dir: %s

	Process: %d

	User: %s (uid: %s)

	Server hostname: %s

	Your address: %s

	Request method=%s host=%s path=[%s] query=[%s]

	Current time: %s

	Uptime: %s

	Requests: %d

    %s

All known paths:

    %s
`

	footer :=
		`

`

	cwd, errCwd := os.Getwd()
	if errCwd != nil {
		cwd = cwd + " (error: " + errCwd.Error() + ")"
	}

	host := getHostname()

	now := time.Now()

	username := "?"
	uid := "?"

	if usr != nil {
		username = usr.Username
		uid = usr.Uid
	}

	body := fmt.Sprintf(bodyTempl, helloVersion, runtime.Version(), keepalive, banner, os.Args, cwd, os.Getpid(), username, uid, host, r.RemoteAddr, r.Method, r.Host, r.URL.Path, r.URL.RawQuery, now, time.Since(boottime), get(), errMsg, paths)

	if !keepalive {
		w.Header().Set("Connection", "close")
	}

	io.WriteString(w, header)
	io.WriteString(w, body)
	showHeaders(w, r)
	showReqBody(w, r)
	io.WriteString(w, footer)

	checkQuota("rootHandler", count)
}

func showReqBody(w http.ResponseWriter, r *http.Request) {

	io.WriteString(w, "
Request Body
\n")

	buf, err := ioutil.ReadAll(r.Body)
	if err != nil {
		m := fmt.Sprintf("request body: %v", err)
		log.Print(m)
		io.WriteString(w, m)
		return
	}

	bodySize := len(buf)

	log.Printf("body size: %d", bodySize)

	io.WriteString(w, fmt.Sprintf("
request body size: %d
\n", bodySize))

	io.WriteString(w, "
\n")
	io.WriteString(w, string(buf))
	io.WriteString(w, "\n")
	io.WriteString(w, "
\n")
}

func showHeaders(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, "
Headers
\n")

	var headers []string
	for k, v := range r.Header {
		for _, vv := range v {
			headers = append(headers, k+": "+vv+"
\n")
		}
	}

	sort.Strings(headers)

	for _, h := range headers {
		io.WriteString(w, h)
	}
}
<|endoftext|>


<commit_before>package main

import (
    "testing"
)

func TestPacketAdd(t *testing.T) {
    pq := PacketQueue{}

    if pq.Length() != 0 {
        t.Errorf("Length should be 0 but was %d", pq.Length())
    }

    i, err := pq.Add([]byte{1,2,3,4,5})

    if err != nil {
        t.Error(err)
    }

    if i != 0 {
        t.Errorf("Serial no. should be 0 but was %d", i)
    }

    if pq.Length() != 1 {
        t.Errorf("Length should be 1 but was %d", pq.Length())
    }

    i, err = pq.Add([]byte{6, 7, 8, 9, 0})

    if err != nil {
        t.Error(err)
    }

    if pq.Length() != 2 {
        t.Errorf("Length should be 2 but was %d", pq.Length())
    }

}

<commit_msg>Add some more queue tests<commit_after>package main

import (
    "testing"
)

func TestPacketAdd(t *testing.T) {
    pq := PacketQueue{}

    if pq.Length() != 0 {
        t.Errorf("Length should be 0 but was %d", pq.Length())
    }

    i, err := pq.Add([]byte{1,2,3,4,5})

    if err != nil {
        t.Error(err)
    }

    if i != 0 {
        t.Errorf("Serial no. should be 0 but was %d", i)
    }

    if pq.Length() != 1 {
        t.Errorf("Length should be 1 but was %d", pq.Length())
    }

    i, err = pq.Add([]byte{6, 7, 8, 9, 0})

    if err != nil {
        t.Error(err)
    }

    if pq.Length() != 2 {
        t.Errorf("Length should be 2 but was %d", pq.Length())
    }

}

func TestQueueCapacity(t *testing.T) {
    pq := PacketQueue{}
    for i := 0; i < PACKET_BUFFER_SIZE; i++ {
        serial, err := pq.Add([]byte{0, 0, byte(i)})
        if err != nil {
            t.Error(err)
        }
        if serial != uint64(i) {
            t.Errorf("Serial %d did not match %d", serial, i)
        }
    }

    _, err := pq.Add([]byte{0})
    if err == nil {
        t.Errorf("Queue did not error when adding past capacity")
    }

    const trimNum uint64 = 5
    pq.TrimUpTo(trimNum)

    for i := 0; uint64(i) < trimNum; i++ {
        serial, err := pq.Add([]byte{1, byte(i)})
        if err != nil {
            t.Error(err)
        }
        if serial != uint64(PACKET_BUFFER_SIZE + i) {
            t.Errorf("Serial %d did not match %d", serial, i)
        }
    }

    _, err = pq.Add([]byte{0})
    if err == nil {
        t.Errorf("Queue did not error when adding past capacity")
    }
}
<|endoftext|>


<commit_before>package structs

import (
	"reflect"
	"strings"
	"testing"

	"github.com/hashicorp/go-multierror"
)

func TestJob_Validate(t *testing.T) {
	j := &Job{}
	err := j.Validate()
	mErr := err.(*multierror.Error)
	if !strings.Contains(mErr.Errors[0].Error(), "job region") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[1].Error(), "job ID") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[2].Error(), "job name") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[3].Error(), "job type") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[4].Error(), "priority") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[5].Error(), "datacenters") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[6].Error(), "task groups") {
		t.Fatalf("err: %s", err)
	}

	j = &Job{
		Region:      "global",
		ID:          GenerateUUID(),
		Name:        "my-job",
		Type:        JobTypeService,
		Priority:    50,
		Datacenters: []string{"dc1"},
		TaskGroups: []*TaskGroup{
			&TaskGroup{
				Name: "web",
			},
			&TaskGroup{
				Name: "web",
			},
			&TaskGroup{},
		},
	}
	err = j.Validate()
	mErr = err.(*multierror.Error)
	if !strings.Contains(mErr.Errors[0].Error(), "2 redefines 'web' from group 1") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[1].Error(), "group 3 missing name") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[2].Error(), "Task group 1 validation failed") {
		t.Fatalf("err: %s", err)
	}
}

func TestTaskGroup_Validate(t *testing.T) {
	tg := &TaskGroup{}
	err := tg.Validate()
	mErr := err.(*multierror.Error)
	if !strings.Contains(mErr.Errors[0].Error(), "group name") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[1].Error(), "count must be positive") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[2].Error(), "Missing tasks") {
		t.Fatalf("err: %s", err)
	}

	tg = &TaskGroup{
		Name:  "web",
		Count: 1,
		Tasks: []*Task{
			&Task{Name: "web"},
			&Task{Name: "web"},
			&Task{},
		},
	}
	err = tg.Validate()
	mErr = err.(*multierror.Error)
	if !strings.Contains(mErr.Errors[0].Error(), "2 redefines 'web' from task 1") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[1].Error(), "Task 3 missing name") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[2].Error(), "Task 1 validation failed") {
		t.Fatalf("err: %s", err)
	}
}

func TestTask_Validate(t *testing.T) {
	task := &Task{}
	err := task.Validate()
	mErr := err.(*multierror.Error)
	if !strings.Contains(mErr.Errors[0].Error(), "task name") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[1].Error(), "task driver") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[2].Error(), "task resources") {
		t.Fatalf("err: %s", err)
	}

	task = &Task{
		Name:      "web",
		Driver:    "docker",
		Resources: &Resources{},
	}
	err = task.Validate()
	if err != nil {
		t.Fatalf("err: %s", err)
	}
}

func TestResource_NetIndex(t *testing.T) {
	r := &Resources{
		Networks: []*NetworkResource{
			&NetworkResource{Device: "eth0"},
			&NetworkResource{Device: "lo0"},
			&NetworkResource{Device: ""},
		},
	}
	if idx := r.NetIndex(&NetworkResource{Device: "eth0"}); idx != 0 {
		t.Fatalf("Bad: %d", idx)
	}
	if idx := r.NetIndex(&NetworkResource{Device: "lo0"}); idx != 1 {
		t.Fatalf("Bad: %d", idx)
	}
	if idx := r.NetIndex(&NetworkResource{Device: "eth1"}); idx != -1 {
		t.Fatalf("Bad: %d", idx)
	}
}

func TestResource_Superset(t *testing.T) {
	r1 := &Resources{
		CPU:      2000,
		MemoryMB: 2048,
		DiskMB:   10000,
		IOPS:     100,
	}
	r2 := &Resources{
		CPU:      2000,
		MemoryMB: 1024,
		DiskMB:   5000,
		IOPS:     50,
	}

	if s, _ := r1.Superset(r1); !s {
		t.Fatalf("bad")
	}
	if s, _ := r1.Superset(r2); !s {
		t.Fatalf("bad")
	}
	if s, _ := r2.Superset(r1); s {
		t.Fatalf("bad")
	}
	if s, _ := r2.Superset(r2); !s {
		t.Fatalf("bad")
	}
}

func TestResource_Add(t *testing.T) {
	r1 := &Resources{
		CPU:      2000,
		MemoryMB: 2048,
		DiskMB:   10000,
		IOPS:     100,
		Networks: []*NetworkResource{
			&NetworkResource{
				CIDR:          "10.0.0.0/8",
				MBits:         100,
				ReservedPorts: []int{22},
			},
		},
	}
	r2 := &Resources{
		CPU:      2000,
		MemoryMB: 1024,
		DiskMB:   5000,
		IOPS:     50,
		Networks: []*NetworkResource{
			&NetworkResource{
				IP:            "10.0.0.1",
				MBits:         50,
				ReservedPorts: []int{80},
			},
		},
	}

	err := r1.Add(r2)
	if err != nil {
		t.Fatalf("Err: %v", err)
	}

	expect := &Resources{
		CPU:      3000,
		MemoryMB: 3072,
		DiskMB:   15000,
		IOPS:     150,
		Networks: []*NetworkResource{
			&NetworkResource{
				CIDR:          "10.0.0.0/8",
				MBits:         150,
				ReservedPorts: []int{22, 80},
			},
		},
	}

	if !reflect.DeepEqual(expect.Networks, r1.Networks) {
		t.Fatalf("bad: %#v %#v", expect, r1)
	}
}

func TestResource_Add_Network(t *testing.T) {
	r1 := &Resources{}
	r2 := &Resources{
		Networks: []*NetworkResource{
			&NetworkResource{
				MBits:        50,
				DynamicPorts: []string{"http", "https"},
			},
		},
	}
	r3 := &Resources{
		Networks: []*NetworkResource{
			&NetworkResource{
				MBits:        25,
				DynamicPorts: []string{"admin"},
			},
		},
	}

	err := r1.Add(r2)
	if err != nil {
		t.Fatalf("Err: %v", err)
	}
	err = r1.Add(r3)
	if err != nil {
		t.Fatalf("Err: %v", err)
	}

	expect := &Resources{
		Networks: []*NetworkResource{
			&NetworkResource{
				MBits:        75,
				DynamicPorts: []string{"http", "https", "admin"},
			},
		},
	}

	if !reflect.DeepEqual(expect.Networks, r1.Networks) {
		t.Fatalf("bad: %#v %#v", expect.Networks[0], r1.Networks[0])
	}
}

func TestListDynamicPorts(t *testing.T) {
	resources := &NetworkResource{
		ReservedPorts: []int{80, 443, 3306, 8080},
		DynamicPorts:  []string{"mysql", "admin"},
	}

	expected := []int{3306, 8080}
	actual := resources.ListDynamicPorts()

	if !reflect.DeepEqual(expected, actual) {
		t.Fatalf("Expected %#v; found %#v", expected, actual)
	}
}

func TestMapDynamicPorts(t *testing.T) {
	resources := &NetworkResource{
		ReservedPorts: []int{80, 443, 3306, 8080},
		DynamicPorts:  []string{"mysql", "admin"},
	}

	expected := map[string]int{
		"mysql": 3306,
		"admin": 8080,
	}
	actual := resources.MapDynamicPorts()

	if !reflect.DeepEqual(expected, actual) {
		t.Fatalf("Expected %#v; found %#v", expected, actual)
	}
}

func TestEncodeDecode(t *testing.T) {
	type FooRequest struct {
		Foo string
		Bar int
		Baz bool
	}
	arg := &FooRequest{
		Foo: "test",
		Bar: 42,
		Baz: true,
	}
	buf, err := Encode(1, arg)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	var out FooRequest
	err = Decode(buf[1:], &out)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	if !reflect.DeepEqual(arg, &out) {
		t.Fatalf("bad: %#v %#v", arg, out)
	}
}
<commit_msg>That method is gone<commit_after>package structs

import (
	"reflect"
	"strings"
	"testing"

	"github.com/hashicorp/go-multierror"
)

func TestJob_Validate(t *testing.T) {
	j := &Job{}
	err := j.Validate()
	mErr := err.(*multierror.Error)
	if !strings.Contains(mErr.Errors[0].Error(), "job region") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[1].Error(), "job ID") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[2].Error(), "job name") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[3].Error(), "job type") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[4].Error(), "priority") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[5].Error(), "datacenters") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[6].Error(), "task groups") {
		t.Fatalf("err: %s", err)
	}

	j = &Job{
		Region:      "global",
		ID:          GenerateUUID(),
		Name:        "my-job",
		Type:        JobTypeService,
		Priority:    50,
		Datacenters: []string{"dc1"},
		TaskGroups: []*TaskGroup{
			&TaskGroup{
				Name: "web",
			},
			&TaskGroup{
				Name: "web",
			},
			&TaskGroup{},
		},
	}
	err = j.Validate()
	mErr = err.(*multierror.Error)
	if !strings.Contains(mErr.Errors[0].Error(), "2 redefines 'web' from group 1") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[1].Error(), "group 3 missing name") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[2].Error(), "Task group 1 validation failed") {
		t.Fatalf("err: %s", err)
	}
}

func TestTaskGroup_Validate(t *testing.T) {
	tg := &TaskGroup{}
	err := tg.Validate()
	mErr := err.(*multierror.Error)
	if !strings.Contains(mErr.Errors[0].Error(), "group name") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[1].Error(), "count must be positive") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[2].Error(), "Missing tasks") {
		t.Fatalf("err: %s", err)
	}

	tg = &TaskGroup{
		Name:  "web",
		Count: 1,
		Tasks: []*Task{
			&Task{Name: "web"},
			&Task{Name: "web"},
			&Task{},
		},
	}
	err = tg.Validate()
	mErr = err.(*multierror.Error)
	if !strings.Contains(mErr.Errors[0].Error(), "2 redefines 'web' from task 1") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[1].Error(), "Task 3 missing name") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[2].Error(), "Task 1 validation failed") {
		t.Fatalf("err: %s", err)
	}
}

func TestTask_Validate(t *testing.T) {
	task := &Task{}
	err := task.Validate()
	mErr := err.(*multierror.Error)
	if !strings.Contains(mErr.Errors[0].Error(), "task name") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[1].Error(), "task driver") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[2].Error(), "task resources") {
		t.Fatalf("err: %s", err)
	}

	task = &Task{
		Name:      "web",
		Driver:    "docker",
		Resources: &Resources{},
	}
	err = task.Validate()
	if err != nil {
		t.Fatalf("err: %s", err)
	}
}

func TestResource_NetIndex(t *testing.T) {
	r := &Resources{
		Networks: []*NetworkResource{
			&NetworkResource{Device: "eth0"},
			&NetworkResource{Device: "lo0"},
			&NetworkResource{Device: ""},
		},
	}
	if idx := r.NetIndex(&NetworkResource{Device: "eth0"}); idx != 0 {
		t.Fatalf("Bad: %d", idx)
	}
	if idx := r.NetIndex(&NetworkResource{Device: "lo0"}); idx != 1 {
		t.Fatalf("Bad: %d", idx)
	}
	if idx := r.NetIndex(&NetworkResource{Device: "eth1"}); idx != -1 {
		t.Fatalf("Bad: %d", idx)
	}
}

func TestResource_Superset(t *testing.T) {
	r1 := &Resources{
		CPU:      2000,
		MemoryMB: 2048,
		DiskMB:   10000,
		IOPS:     100,
	}
	r2 := &Resources{
		CPU:      2000,
		MemoryMB: 1024,
		DiskMB:   5000,
		IOPS:     50,
	}

	if s, _ := r1.Superset(r1); !s {
		t.Fatalf("bad")
	}
	if s, _ := r1.Superset(r2); !s {
		t.Fatalf("bad")
	}
	if s, _ := r2.Superset(r1); s {
		t.Fatalf("bad")
	}
	if s, _ := r2.Superset(r2); !s {
		t.Fatalf("bad")
	}
}

func TestResource_Add(t *testing.T) {
	r1 := &Resources{
		CPU:      2000,
		MemoryMB: 2048,
		DiskMB:   10000,
		IOPS:     100,
		Networks: []*NetworkResource{
			&NetworkResource{
				CIDR:          "10.0.0.0/8",
				MBits:         100,
				ReservedPorts: []int{22},
			},
		},
	}
	r2 := &Resources{
		CPU:      2000,
		MemoryMB: 1024,
		DiskMB:   5000,
		IOPS:     50,
		Networks: []*NetworkResource{
			&NetworkResource{
				IP:            "10.0.0.1",
				MBits:         50,
				ReservedPorts: []int{80},
			},
		},
	}

	err := r1.Add(r2)
	if err != nil {
		t.Fatalf("Err: %v", err)
	}

	expect := &Resources{
		CPU:      3000,
		MemoryMB: 3072,
		DiskMB:   15000,
		IOPS:     150,
		Networks: []*NetworkResource{
			&NetworkResource{
				CIDR:          "10.0.0.0/8",
				MBits:         150,
				ReservedPorts: []int{22, 80},
			},
		},
	}

	if !reflect.DeepEqual(expect.Networks, r1.Networks) {
		t.Fatalf("bad: %#v %#v", expect, r1)
	}
}

func TestResource_Add_Network(t *testing.T) {
	r1 := &Resources{}
	r2 := &Resources{
		Networks: []*NetworkResource{
			&NetworkResource{
				MBits:        50,
				DynamicPorts: []string{"http", "https"},
			},
		},
	}
	r3 := &Resources{
		Networks: []*NetworkResource{
			&NetworkResource{
				MBits:        25,
				DynamicPorts: []string{"admin"},
			},
		},
	}

	err := r1.Add(r2)
	if err != nil {
		t.Fatalf("Err: %v", err)
	}
	err = r1.Add(r3)
	if err != nil {
		t.Fatalf("Err: %v", err)
	}

	expect := &Resources{
		Networks: []*NetworkResource{
			&NetworkResource{
				MBits:        75,
				DynamicPorts: []string{"http", "https", "admin"},
			},
		},
	}

	if !reflect.DeepEqual(expect.Networks, r1.Networks) {
		t.Fatalf("bad: %#v %#v", expect.Networks[0], r1.Networks[0])
	}
}

func TestMapDynamicPorts(t *testing.T) {
	resources := &NetworkResource{
		ReservedPorts: []int{80, 443, 3306, 8080},
		DynamicPorts:  []string{"mysql", "admin"},
	}

	expected := map[string]int{
		"mysql": 3306,
		"admin": 8080,
	}
	actual := resources.MapDynamicPorts()

	if !reflect.DeepEqual(expected, actual) {
		t.Fatalf("Expected %#v; found %#v", expected, actual)
	}
}

func TestEncodeDecode(t *testing.T) {
	type FooRequest struct {
		Foo string
		Bar int
		Baz bool
	}
	arg := &FooRequest{
		Foo: "test",
		Bar: 42,
		Baz: true,
	}
	buf, err := Encode(1, arg)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	var out FooRequest
	err = Decode(buf[1:], &out)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	if !reflect.DeepEqual(arg, &out) {
		t.Fatalf("bad: %#v %#v", arg, out)
	}
}
<|endoftext|>


<commit_before>package nat

import (
	"errors"
	"fmt"
	"strconv"
	"strings"
	"sync"
	"time"

	nat "github.com/fd/go-nat"
	logging "github.com/ipfs/go-log"
	ma "github.com/jbenet/go-multiaddr"
	manet "github.com/jbenet/go-multiaddr-net"
	goprocess "github.com/jbenet/goprocess"
	periodic "github.com/jbenet/goprocess/periodic"
	notifier "github.com/whyrusleeping/go-notifier"
)

var (
	// ErrNoMapping signals no mapping exists for an address
	ErrNoMapping = errors.New("mapping not established")
)

var log = logging.Logger("nat")

// MappingDuration is a default port mapping duration.
// Port mappings are renewed every (MappingDuration / 3)
const MappingDuration = time.Second * 60

// CacheTime is the time a mapping will cache an external address for
const CacheTime = time.Second * 15

// DiscoverNAT looks for a NAT device in the network and
// returns an object that can manage port mappings.
func DiscoverNAT() *NAT {
	nat, err := nat.DiscoverGateway()
	if err != nil {
		log.Debug("DiscoverGateway error:", err)
		return nil
	}
	addr, err := nat.GetDeviceAddress()
	if err != nil {
		log.Debug("DiscoverGateway address error:", err)
	} else {
		log.Debug("DiscoverGateway address:", addr)
	}
	return newNAT(nat)
}

// NAT is an object that manages address port mappings in
// NATs (Network Address Translators). It is a long-running
// service that will periodically renew port mappings,
// and keep an up-to-date list of all the external addresses.
type NAT struct {
	nat  nat.NAT
	proc goprocess.Process // manages nat mappings lifecycle

	mappingmu sync.RWMutex // guards mappings
	mappings  map[*mapping]struct{}

	Notifier
}

func newNAT(realNAT nat.NAT) *NAT {
	return &NAT{
		nat:      realNAT,
		proc:     goprocess.WithParent(goprocess.Background()),
		mappings: make(map[*mapping]struct{}),
	}
}

// Close shuts down all port mappings. NAT can no longer be used.
func (nat *NAT) Close() error {
	return nat.proc.Close()
}

// Process returns the nat's life-cycle manager, for making it listen
// to close signals.
func (nat *NAT) Process() goprocess.Process {
	return nat.proc
}

// Notifier is an object that assists NAT in notifying listeners.
// It is implemented using thirdparty/notifier
type Notifier struct {
	n notifier.Notifier
}

func (n *Notifier) notifyAll(notify func(n Notifiee)) {
	n.n.NotifyAll(func(n notifier.Notifiee) {
		notify(n.(Notifiee))
	})
}

// Notify signs up notifiee to listen to NAT events.
func (n *Notifier) Notify(notifiee Notifiee) {
	n.n.Notify(n)
}

// StopNotify stops signaling events to notifiee.
func (n *Notifier) StopNotify(notifiee Notifiee) {
	n.n.StopNotify(notifiee)
}

// Notifiee is an interface objects must implement to listen to NAT events.
type Notifiee interface {

	// Called every time a successful mapping happens
	// Warning: the port mapping may have changed. If that is the
	// case, both MappingSuccess and MappingChanged are called.
	MappingSuccess(nat *NAT, m Mapping)

	// Called when mapping a port succeeds, but the mapping is
	// with a different port than an earlier success.
	MappingChanged(nat *NAT, m Mapping, oldport int, newport int)

	// Called when a port mapping fails. NAT will continue attempting after
	// the next period. To stop trying, use: mapping.Close(). After this failure,
	// mapping.ExternalPort() will be zero, and nat.ExternalAddrs() will not
	// return the address for this mapping. With luck, the next attempt will
	// succeed, without the client needing to do anything.
	MappingFailed(nat *NAT, m Mapping, oldport int, err error)
}

// Mapping represents a port mapping in a NAT.
type Mapping interface {
	// NAT returns the NAT object this Mapping belongs to.
	NAT() *NAT

	// Protocol returns the protocol of this port mapping. This is either
	// "tcp" or "udp" as no other protocols are likely to be NAT-supported.
	Protocol() string

	// InternalPort returns the internal device port. Mapping will continue to
	// try to map InternalPort() to an external facing port.
	InternalPort() int

	// ExternalPort returns the external facing port. If the mapping is not
	// established, port will be 0
	ExternalPort() int

	// InternalAddr returns the internal address.
	InternalAddr() ma.Multiaddr

	// ExternalAddr returns the external facing address. If the mapping is not
	// established, addr will be nil, and and ErrNoMapping will be returned.
	ExternalAddr() (addr ma.Multiaddr, err error)

	// Close closes the port mapping
	Close() error
}

// keeps republishing
type mapping struct {
	sync.Mutex // guards all fields

	nat     *NAT
	proto   string
	intport int
	extport int
	intaddr ma.Multiaddr
	proc    goprocess.Process

	cached    ma.Multiaddr
	cacheTime time.Time
}

func (m *mapping) NAT() *NAT {
	m.Lock()
	defer m.Unlock()
	return m.nat
}

func (m *mapping) Protocol() string {
	m.Lock()
	defer m.Unlock()
	return m.proto
}

func (m *mapping) InternalPort() int {
	m.Lock()
	defer m.Unlock()
	return m.intport
}

func (m *mapping) ExternalPort() int {
	m.Lock()
	defer m.Unlock()
	return m.extport
}

func (m *mapping) setExternalPort(p int) {
	m.Lock()
	defer m.Unlock()
	m.extport = p
}

func (m *mapping) InternalAddr() ma.Multiaddr {
	m.Lock()
	defer m.Unlock()
	return m.intaddr
}

func (m *mapping) ExternalAddr() (ma.Multiaddr, error) {
	if time.Now().Sub(m.cacheTime) < CacheTime {
		return m.cached, nil
	}

	if m.ExternalPort() == 0 { // dont even try right now.
		return nil, ErrNoMapping
	}

	ip, err := m.nat.nat.GetExternalAddress()
	if err != nil {
		return nil, err
	}

	ipmaddr, err := manet.FromIP(ip)
	if err != nil {
		return nil, fmt.Errorf("error parsing ip")
	}

	// call m.ExternalPort again, as mapping may have changed under our feet. (tocttou)
	extport := m.ExternalPort()
	if extport == 0 {
		return nil, ErrNoMapping
	}

	tcp, err := ma.NewMultiaddr(fmt.Sprintf("/%s/%d", m.Protocol(), extport))
	if err != nil {
		return nil, err
	}

	maddr2 := ipmaddr.Encapsulate(tcp)

	m.cached = maddr2
	m.cacheTime = time.Now()
	return maddr2, nil
}

func (m *mapping) Close() error {
	return m.proc.Close()
}

// Mappings returns a slice of all NAT mappings
func (nat *NAT) Mappings() []Mapping {
	nat.mappingmu.Lock()
	maps2 := make([]Mapping, 0, len(nat.mappings))
	for m := range nat.mappings {
		maps2 = append(maps2, m)
	}
	nat.mappingmu.Unlock()
	return maps2
}

func (nat *NAT) addMapping(m *mapping) {
	// make mapping automatically close when nat is closed.
	nat.proc.AddChild(m.proc)

	nat.mappingmu.Lock()
	nat.mappings[m] = struct{}{}
	nat.mappingmu.Unlock()
}

func (nat *NAT) rmMapping(m *mapping) {
	nat.mappingmu.Lock()
	delete(nat.mappings, m)
	nat.mappingmu.Unlock()
}

// NewMapping attemps to construct a mapping on protocol and internal port
// It will also periodically renew the mapping until the returned Mapping
// -- or its parent NAT -- is Closed.
//
// May not succeed, and mappings may change over time;
// NAT devices may not respect our port requests, and even lie.
// Clients should not store the mapped results, but rather always
// poll our object for the latest mappings.
func (nat *NAT) NewMapping(maddr ma.Multiaddr) (Mapping, error) {
	if nat == nil {
		return nil, fmt.Errorf("no nat available")
	}

	network, addr, err := manet.DialArgs(maddr)
	if err != nil {
		return nil, fmt.Errorf("DialArgs failed on addr:", maddr.String())
	}

	switch network {
	case "tcp", "tcp4", "tcp6":
		network = "tcp"
	case "udp", "udp4", "udp6":
		network = "udp"
	default:
		return nil, fmt.Errorf("transport not supported by NAT: %s", network)
	}

	intports := strings.Split(addr, ":")[1]
	intport, err := strconv.Atoi(intports)
	if err != nil {
		return nil, err
	}

	m := &mapping{
		nat:     nat,
		proto:   network,
		intport: intport,
		intaddr: maddr,
	}
	m.proc = goprocess.WithTeardown(func() error {
		nat.rmMapping(m)
		return nil
	})
	nat.addMapping(m)

	m.proc.AddChild(periodic.Every(MappingDuration/3, func(worker goprocess.Process) {
		nat.establishMapping(m)
	}))

	// do it once synchronously, so first mapping is done right away, and before exiting,
	// allowing users -- in the optimistic case -- to use results right after.
	nat.establishMapping(m)
	return m, nil
}

func (nat *NAT) establishMapping(m *mapping) {
	oldport := m.ExternalPort()
	log.Debugf("Attempting port map: %s/%d", m.Protocol(), m.InternalPort())
	newport, err := nat.nat.AddPortMapping(m.Protocol(), m.InternalPort(), "http", MappingDuration)

	failure := func() {
		m.setExternalPort(0) // clear mapping
		// TODO: log.Event
		log.Debugf("failed to establish port mapping: %s", err)
		nat.Notifier.notifyAll(func(n Notifiee) {
			n.MappingFailed(nat, m, oldport, err)
		})

		// we do not close if the mapping failed,
		// because it may work again next time.
	}

	if err != nil || newport == 0 {
		failure()
		return
	}

	m.setExternalPort(newport)
	ext, err := m.ExternalAddr()
	if err != nil {
		log.Debugf("NAT Mapping addr error: %s %s", m.InternalAddr(), err)
		failure()
		return
	}

	log.Debugf("NAT Mapping: %s --> %s", m.InternalAddr(), ext)
	if oldport != 0 && newport != oldport {
		log.Debugf("failed to renew same port mapping: ch %d -> %d", oldport, newport)
		nat.Notifier.notifyAll(func(n Notifiee) {
			n.MappingChanged(nat, m, oldport, newport)
		})
	}

	nat.Notifier.notifyAll(func(n Notifiee) {
		n.MappingSuccess(nat, m)
	})
}

// PortMapAddrs attempts to open (and continue to keep open)
// port mappings for given addrs. This function blocks until
// all  addresses have been tried. This allows clients to
// retrieve results immediately after:
//
//   nat.PortMapAddrs(addrs)
//   mapped := nat.ExternalAddrs()
//
// Some may not succeed, and mappings may change over time;
// NAT devices may not respect our port requests, and even lie.
// Clients should not store the mapped results, but rather always
// poll our object for the latest mappings.
func (nat *NAT) PortMapAddrs(addrs []ma.Multiaddr) {
	// spin off addr mappings independently.
	var wg sync.WaitGroup
	for _, addr := range addrs {
		// do all of them concurrently
		wg.Add(1)
		go func() {
			defer wg.Done()
			nat.NewMapping(addr)
		}()
	}
	wg.Wait()
}

// MappedAddrs returns address mappings NAT believes have been
// successfully established. Unsuccessful mappings are nil. This is:
//
// 		map[internalAddr]externalAddr
//
// This set of mappings _may not_ be correct, as NAT devices are finicky.
// Consider this with _best effort_ semantics.
func (nat *NAT) MappedAddrs() map[ma.Multiaddr]ma.Multiaddr {

	mappings := nat.Mappings()
	addrmap := make(map[ma.Multiaddr]ma.Multiaddr, len(mappings))

	for _, m := range mappings {
		i := m.InternalAddr()
		e, err := m.ExternalAddr()
		if err != nil {
			addrmap[i] = nil
		} else {
			addrmap[i] = e
		}
	}
	return addrmap
}

// ExternalAddrs returns a list of addresses that NAT believes have
// been successfully established. Unsuccessful mappings are omitted,
// so nat.ExternalAddrs() may return less addresses than nat.InternalAddrs().
// To see which addresses are mapped, use nat.MappedAddrs().
//
// This set of mappings _may not_ be correct, as NAT devices are finicky.
// Consider this with _best effort_ semantics.
func (nat *NAT) ExternalAddrs() []ma.Multiaddr {
	mappings := nat.Mappings()
	addrs := make([]ma.Multiaddr, 0, len(mappings))
	for _, m := range mappings {
		a, err := m.ExternalAddr()
		if err != nil {
			continue // this mapping not currently successful.
		}
		addrs = append(addrs, a)
	}
	return addrs
}
<commit_msg>fix minor race condition in nat detection code<commit_after>package nat

import (
	"errors"
	"fmt"
	"strconv"
	"strings"
	"sync"
	"time"

	nat "github.com/fd/go-nat"
	logging "github.com/ipfs/go-log"
	ma "github.com/jbenet/go-multiaddr"
	manet "github.com/jbenet/go-multiaddr-net"
	goprocess "github.com/jbenet/goprocess"
	periodic "github.com/jbenet/goprocess/periodic"
	notifier "github.com/whyrusleeping/go-notifier"
)

var (
	// ErrNoMapping signals no mapping exists for an address
	ErrNoMapping = errors.New("mapping not established")
)

var log = logging.Logger("nat")

// MappingDuration is a default port mapping duration.
// Port mappings are renewed every (MappingDuration / 3)
const MappingDuration = time.Second * 60

// CacheTime is the time a mapping will cache an external address for
const CacheTime = time.Second * 15

// DiscoverNAT looks for a NAT device in the network and
// returns an object that can manage port mappings.
func DiscoverNAT() *NAT {
	nat, err := nat.DiscoverGateway()
	if err != nil {
		log.Debug("DiscoverGateway error:", err)
		return nil
	}
	addr, err := nat.GetDeviceAddress()
	if err != nil {
		log.Debug("DiscoverGateway address error:", err)
	} else {
		log.Debug("DiscoverGateway address:", addr)
	}
	return newNAT(nat)
}

// NAT is an object that manages address port mappings in
// NATs (Network Address Translators). It is a long-running
// service that will periodically renew port mappings,
// and keep an up-to-date list of all the external addresses.
type NAT struct {
	nat  nat.NAT
	proc goprocess.Process // manages nat mappings lifecycle

	mappingmu sync.RWMutex // guards mappings
	mappings  map[*mapping]struct{}

	Notifier
}

func newNAT(realNAT nat.NAT) *NAT {
	return &NAT{
		nat:      realNAT,
		proc:     goprocess.WithParent(goprocess.Background()),
		mappings: make(map[*mapping]struct{}),
	}
}

// Close shuts down all port mappings. NAT can no longer be used.
func (nat *NAT) Close() error {
	return nat.proc.Close()
}

// Process returns the nat's life-cycle manager, for making it listen
// to close signals.
func (nat *NAT) Process() goprocess.Process {
	return nat.proc
}

// Notifier is an object that assists NAT in notifying listeners.
// It is implemented using thirdparty/notifier
type Notifier struct {
	n notifier.Notifier
}

func (n *Notifier) notifyAll(notify func(n Notifiee)) {
	n.n.NotifyAll(func(n notifier.Notifiee) {
		notify(n.(Notifiee))
	})
}

// Notify signs up notifiee to listen to NAT events.
func (n *Notifier) Notify(notifiee Notifiee) {
	n.n.Notify(n)
}

// StopNotify stops signaling events to notifiee.
func (n *Notifier) StopNotify(notifiee Notifiee) {
	n.n.StopNotify(notifiee)
}

// Notifiee is an interface objects must implement to listen to NAT events.
type Notifiee interface {

	// Called every time a successful mapping happens
	// Warning: the port mapping may have changed. If that is the
	// case, both MappingSuccess and MappingChanged are called.
	MappingSuccess(nat *NAT, m Mapping)

	// Called when mapping a port succeeds, but the mapping is
	// with a different port than an earlier success.
	MappingChanged(nat *NAT, m Mapping, oldport int, newport int)

	// Called when a port mapping fails. NAT will continue attempting after
	// the next period. To stop trying, use: mapping.Close(). After this failure,
	// mapping.ExternalPort() will be zero, and nat.ExternalAddrs() will not
	// return the address for this mapping. With luck, the next attempt will
	// succeed, without the client needing to do anything.
	MappingFailed(nat *NAT, m Mapping, oldport int, err error)
}

// Mapping represents a port mapping in a NAT.
type Mapping interface {
	// NAT returns the NAT object this Mapping belongs to.
	NAT() *NAT

	// Protocol returns the protocol of this port mapping. This is either
	// "tcp" or "udp" as no other protocols are likely to be NAT-supported.
	Protocol() string

	// InternalPort returns the internal device port. Mapping will continue to
	// try to map InternalPort() to an external facing port.
	InternalPort() int

	// ExternalPort returns the external facing port. If the mapping is not
	// established, port will be 0
	ExternalPort() int

	// InternalAddr returns the internal address.
	InternalAddr() ma.Multiaddr

	// ExternalAddr returns the external facing address. If the mapping is not
	// established, addr will be nil, and and ErrNoMapping will be returned.
	ExternalAddr() (addr ma.Multiaddr, err error)

	// Close closes the port mapping
	Close() error
}

// keeps republishing
type mapping struct {
	sync.Mutex // guards all fields

	nat     *NAT
	proto   string
	intport int
	extport int
	intaddr ma.Multiaddr
	proc    goprocess.Process

	cached    ma.Multiaddr
	cacheTime time.Time
	cacheLk   sync.Mutex
}

func (m *mapping) NAT() *NAT {
	m.Lock()
	defer m.Unlock()
	return m.nat
}

func (m *mapping) Protocol() string {
	m.Lock()
	defer m.Unlock()
	return m.proto
}

func (m *mapping) InternalPort() int {
	m.Lock()
	defer m.Unlock()
	return m.intport
}

func (m *mapping) ExternalPort() int {
	m.Lock()
	defer m.Unlock()
	return m.extport
}

func (m *mapping) setExternalPort(p int) {
	m.Lock()
	defer m.Unlock()
	m.extport = p
}

func (m *mapping) InternalAddr() ma.Multiaddr {
	m.Lock()
	defer m.Unlock()
	return m.intaddr
}

func (m *mapping) ExternalAddr() (ma.Multiaddr, error) {
	m.cacheLk.Lock()
	ctime := m.cacheTime
	cval := m.cached
	m.cacheLk.Unlock()
	if time.Now().Sub(ctime) < CacheTime {
		return cval, nil
	}

	if m.ExternalPort() == 0 { // dont even try right now.
		return nil, ErrNoMapping
	}

	ip, err := m.nat.nat.GetExternalAddress()
	if err != nil {
		return nil, err
	}

	ipmaddr, err := manet.FromIP(ip)
	if err != nil {
		return nil, fmt.Errorf("error parsing ip")
	}

	// call m.ExternalPort again, as mapping may have changed under our feet. (tocttou)
	extport := m.ExternalPort()
	if extport == 0 {
		return nil, ErrNoMapping
	}

	tcp, err := ma.NewMultiaddr(fmt.Sprintf("/%s/%d", m.Protocol(), extport))
	if err != nil {
		return nil, err
	}

	maddr2 := ipmaddr.Encapsulate(tcp)

	m.cacheLk.Lock()
	m.cached = maddr2
	m.cacheTime = time.Now()
	m.cacheLk.Unlock()
	return maddr2, nil
}

func (m *mapping) Close() error {
	return m.proc.Close()
}

// Mappings returns a slice of all NAT mappings
func (nat *NAT) Mappings() []Mapping {
	nat.mappingmu.Lock()
	maps2 := make([]Mapping, 0, len(nat.mappings))
	for m := range nat.mappings {
		maps2 = append(maps2, m)
	}
	nat.mappingmu.Unlock()
	return maps2
}

func (nat *NAT) addMapping(m *mapping) {
	// make mapping automatically close when nat is closed.
	nat.proc.AddChild(m.proc)

	nat.mappingmu.Lock()
	nat.mappings[m] = struct{}{}
	nat.mappingmu.Unlock()
}

func (nat *NAT) rmMapping(m *mapping) {
	nat.mappingmu.Lock()
	delete(nat.mappings, m)
	nat.mappingmu.Unlock()
}

// NewMapping attemps to construct a mapping on protocol and internal port
// It will also periodically renew the mapping until the returned Mapping
// -- or its parent NAT -- is Closed.
//
// May not succeed, and mappings may change over time;
// NAT devices may not respect our port requests, and even lie.
// Clients should not store the mapped results, but rather always
// poll our object for the latest mappings.
func (nat *NAT) NewMapping(maddr ma.Multiaddr) (Mapping, error) {
	if nat == nil {
		return nil, fmt.Errorf("no nat available")
	}

	network, addr, err := manet.DialArgs(maddr)
	if err != nil {
		return nil, fmt.Errorf("DialArgs failed on addr:", maddr.String())
	}

	switch network {
	case "tcp", "tcp4", "tcp6":
		network = "tcp"
	case "udp", "udp4", "udp6":
		network = "udp"
	default:
		return nil, fmt.Errorf("transport not supported by NAT: %s", network)
	}

	intports := strings.Split(addr, ":")[1]
	intport, err := strconv.Atoi(intports)
	if err != nil {
		return nil, err
	}

	m := &mapping{
		nat:     nat,
		proto:   network,
		intport: intport,
		intaddr: maddr,
	}
	m.proc = goprocess.WithTeardown(func() error {
		nat.rmMapping(m)
		return nil
	})
	nat.addMapping(m)

	m.proc.AddChild(periodic.Every(MappingDuration/3, func(worker goprocess.Process) {
		nat.establishMapping(m)
	}))

	// do it once synchronously, so first mapping is done right away, and before exiting,
	// allowing users -- in the optimistic case -- to use results right after.
	nat.establishMapping(m)
	return m, nil
}

func (nat *NAT) establishMapping(m *mapping) {
	oldport := m.ExternalPort()
	log.Debugf("Attempting port map: %s/%d", m.Protocol(), m.InternalPort())
	newport, err := nat.nat.AddPortMapping(m.Protocol(), m.InternalPort(), "http", MappingDuration)

	failure := func() {
		m.setExternalPort(0) // clear mapping
		// TODO: log.Event
		log.Debugf("failed to establish port mapping: %s", err)
		nat.Notifier.notifyAll(func(n Notifiee) {
			n.MappingFailed(nat, m, oldport, err)
		})

		// we do not close if the mapping failed,
		// because it may work again next time.
	}

	if err != nil || newport == 0 {
		failure()
		return
	}

	m.setExternalPort(newport)
	ext, err := m.ExternalAddr()
	if err != nil {
		log.Debugf("NAT Mapping addr error: %s %s", m.InternalAddr(), err)
		failure()
		return
	}

	log.Debugf("NAT Mapping: %s --> %s", m.InternalAddr(), ext)
	if oldport != 0 && newport != oldport {
		log.Debugf("failed to renew same port mapping: ch %d -> %d", oldport, newport)
		nat.Notifier.notifyAll(func(n Notifiee) {
			n.MappingChanged(nat, m, oldport, newport)
		})
	}

	nat.Notifier.notifyAll(func(n Notifiee) {
		n.MappingSuccess(nat, m)
	})
}

// PortMapAddrs attempts to open (and continue to keep open)
// port mappings for given addrs. This function blocks until
// all  addresses have been tried. This allows clients to
// retrieve results immediately after:
//
//   nat.PortMapAddrs(addrs)
//   mapped := nat.ExternalAddrs()
//
// Some may not succeed, and mappings may change over time;
// NAT devices may not respect our port requests, and even lie.
// Clients should not store the mapped results, but rather always
// poll our object for the latest mappings.
func (nat *NAT) PortMapAddrs(addrs []ma.Multiaddr) {
	// spin off addr mappings independently.
	var wg sync.WaitGroup
	for _, addr := range addrs {
		// do all of them concurrently
		wg.Add(1)
		go func() {
			defer wg.Done()
			nat.NewMapping(addr)
		}()
	}
	wg.Wait()
}

// MappedAddrs returns address mappings NAT believes have been
// successfully established. Unsuccessful mappings are nil. This is:
//
// 		map[internalAddr]externalAddr
//
// This set of mappings _may not_ be correct, as NAT devices are finicky.
// Consider this with _best effort_ semantics.
func (nat *NAT) MappedAddrs() map[ma.Multiaddr]ma.Multiaddr {

	mappings := nat.Mappings()
	addrmap := make(map[ma.Multiaddr]ma.Multiaddr, len(mappings))

	for _, m := range mappings {
		i := m.InternalAddr()
		e, err := m.ExternalAddr()
		if err != nil {
			addrmap[i] = nil
		} else {
			addrmap[i] = e
		}
	}
	return addrmap
}

// ExternalAddrs returns a list of addresses that NAT believes have
// been successfully established. Unsuccessful mappings are omitted,
// so nat.ExternalAddrs() may return less addresses than nat.InternalAddrs().
// To see which addresses are mapped, use nat.MappedAddrs().
//
// This set of mappings _may not_ be correct, as NAT devices are finicky.
// Consider this with _best effort_ semantics.
func (nat *NAT) ExternalAddrs() []ma.Multiaddr {
	mappings := nat.Mappings()
	addrs := make([]ma.Multiaddr, 0, len(mappings))
	for _, m := range mappings {
		a, err := m.ExternalAddr()
		if err != nil {
			continue // this mapping not currently successful.
		}
		addrs = append(addrs, a)
	}
	return addrs
}
<|endoftext|>