<commit_before>package dpn

import (
	"fmt"
	"github.com/APTrust/bagman/bagman"
	"github.com/op/go-logging"
	"net/url"
	"time"
)

type DPNSync struct {
	LocalClient    *DPNRestClient
	RemoteClients  map[string]*DPNRestClient
	Logger         *logging.Logger
	Config         *DPNConfig
}

// SyncResult describes the result of an operation where we pull
// info about all updated bags, replication requests and restore
// requests from a remote node and copy that data into our own
// local DPN registry.
type SyncResult struct {
	// Node is the node we are pulling information from.
	RemoteNode            *DPNNode
	// Bags is a list of bags successfully synched.
	Bags                  []*DPNBag
	// ReplicationTransfers successfully synched.
	ReplicationTransfers  []*DPNReplicationTransfer
	// RestoreTransfers successfully synched.
	RestoreTransfers      []*DPNRestoreTransfer
	// BagSyncError contains the error (if any) that occurred
	// during the bag sync process. The first error will stop
	// the synching of all subsquent bags.
	BagSyncError          error
	// ReplicationSyncError contains the error (if any) that occurred
	// during the synching of Replication Transfers. The first error
	// will stop the synching of all subsquent replication requests.
	ReplicationSyncError  error
	// RestoreSyncError contains the error (if any) that occurred
	// during the synching of Restore Transfers. The first error
	// will stop the synching of all subsquent restore requests.
	RestoreSyncError      error
	// TimestampError contains the error (if any) that occurred when
	// trying to update the LastPullDate timestamp for the node.
	TimestampError        error
}

func (syncResult *SyncResult) HasSyncErrors() (bool) {
	return (syncResult.BagSyncError != nil ||
		syncResult.ReplicationSyncError != nil ||
		syncResult.RestoreSyncError != nil)
}

func NewDPNSync(config *DPNConfig) (*DPNSync, error) {
	logger := initLogger(config)
	localClient, err := NewDPNRestClient(
		config.RestClient.LocalServiceURL,
		config.RestClient.LocalAPIRoot,
		config.RestClient.LocalAuthToken,
		logger)
	if err != nil {
		return nil, fmt.Errorf("Error creating local DPN REST client: %v", err)
	}
	remoteClients, err := GetRemoteClients(localClient, config, logger)
	if err != nil {
		return nil, fmt.Errorf("Error creating remote DPN REST client: %v", err)
	}
	sync := DPNSync{
		LocalClient: localClient,
		RemoteClients: remoteClients,
		Logger: logger,
		Config: config,
	}
	return &sync, nil
}

func initLogger(config *DPNConfig) (*logging.Logger) {
	// bagman has a nice InitLogger function, but it
	// needs a bagman.Config object, which does a lot
	// of handy internal work. Create a throw-away
	// bagman config so we can get our logger.
	bagmanConfig := bagman.Config{
		LogDirectory: config.LogDirectory,
		LogLevel: config.LogLevel,
		LogToStderr: config.LogToStderr,
	}
	return bagman.InitLogger(bagmanConfig)
}

// Returns a list of all the nodes that our node knows about.
func (dpnSync *DPNSync) GetAllNodes()([]*DPNNode, error) {
	result, err := dpnSync.LocalClient.DPNNodeListGet(nil)
	if err != nil {
		return nil, err
	}
	return result.Results, nil
}

func (dpnSync *DPNSync) UpdateLastPullDate(node *DPNNode, lastPullDate time.Time) (*DPNNode, error) {
	dpnSync.Logger.Debug("Setting last pull date on %s to %s", node.Namespace, lastPullDate)
	node.LastPullDate = lastPullDate  // Don't set this until you're ready to send!
	return dpnSync.LocalClient.DPNNodeUpdate(node)
}

// Sync all bags, replication requests and restore requests from
// the specified remote node. Note that this is a pull-only sync.
// We are not writing any data to other nodes, just reading what
// they have and updating our own registry with their info.
func (dpnSync *DPNSync) SyncEverythingFromNode(remoteNode *DPNNode) (*SyncResult) {
	nextTimeStamp := time.Now().UTC()
	syncResult := &SyncResult {
		RemoteNode: remoteNode,
	}

	bags, err := dpnSync.SyncBags(remoteNode)
	syncResult.Bags = bags
	syncResult.BagSyncError = err

	replXfers, err := dpnSync.SyncReplicationRequests(remoteNode)
	syncResult.ReplicationTransfers = replXfers
	syncResult.ReplicationSyncError = err

	restoreXfers, err := dpnSync.SyncRestoreRequests(remoteNode)
	syncResult.RestoreTransfers = restoreXfers
	syncResult.RestoreSyncError = err

	if !syncResult.HasSyncErrors() {
		updatedNode, err := dpnSync.UpdateLastPullDate(remoteNode, nextTimeStamp)
		syncResult.RemoteNode = updatedNode
		syncResult.TimestampError = err
	} else {
		syncResult.TimestampError = fmt.Errorf(
			"LastPullDate was not updated because of errors during the synchronization process.")
	}

	return syncResult
}

// Syncs bags from the specified node to our own local DPN registry
// if the bags match these critieria:
//
// 1. The node we are querying is the admin node for the bag.
// 2. The bag was updated since the last time we queried the node.
//
// Returns a list of the bags that were successfully updated.
// Even on error, this may still return a list with whatever bags
// were updated before the error occurred.
func (dpnSync *DPNSync) SyncBags(remoteNode *DPNNode) ([]*DPNBag, error) {
	pageNumber := 1
	bagsUpdated := make([]*DPNBag, 0)
	remoteClient := dpnSync.RemoteClients[remoteNode.Namespace]
	for {
		dpnSync.Logger.Debug("Getting page %d of bags from %s", pageNumber, remoteNode.Namespace)
		result, err := dpnSync.getBags(remoteClient, remoteNode, pageNumber)
		if err != nil {
			return bagsUpdated, err
		}
		dpnSync.Logger.Debug("Got %d bags from %s", len(result.Results), remoteNode.Namespace)
		updated, err := dpnSync.syncBags(result.Results)
		if err != nil {
			return bagsUpdated, err
		}
		bagsUpdated = append(bagsUpdated, updated...)
		if result.Next == "" {
			dpnSync.Logger.Debug("No more bags to get from %s", remoteNode.Namespace)
			break
		}
		pageNumber += 1
	}
	dpnSync.Logger.Debug("Updated %d bags in local registry", len(bagsUpdated))
	return bagsUpdated, nil
}

func (dpnSync *DPNSync) syncBags(bags []*DPNBag) ([]*DPNBag, error) {
	bagsUpdated := make([]*DPNBag, 0)
	for _, bag := range(bags) {
		dpnSync.Logger.Debug("Updating bag %s in local registry", bag.UUID)
		existingBag, _ := dpnSync.LocalClient.DPNBagGet(bag.UUID)
		var err error
		var updatedBag *DPNBag
		if existingBag != nil {
			dpnSync.Logger.Debug("Bag %s exists... updating", bag.UUID)
			updatedBag, err = dpnSync.LocalClient.DPNBagUpdate(bag)
		} else {
			dpnSync.Logger.Debug("Bag %s not in local registry... creating", bag.UUID)
			updatedBag, err = dpnSync.LocalClient.DPNBagCreate(bag)
		}
		if err != nil {
			dpnSync.Logger.Debug("Oops! Bag %s: %v", bag.UUID, err)
			return bagsUpdated, err
		}
		bagsUpdated = append(bagsUpdated, updatedBag)
	}
	return bagsUpdated, nil
}

func (dpnSync *DPNSync) getBags(remoteClient *DPNRestClient, remoteNode *DPNNode, pageNumber int) (*BagListResult, error) {
	// We want to get all bags updated since the last time we pulled
	// from this node, and only those bags for which the node we're
	// querying is the admin node.
	params := url.Values{}
	params.Set("after", remoteNode.LastPullDate.Format(time.RFC3339Nano))
	params.Set("admin_node", remoteNode.Namespace)
	params.Set("page", fmt.Sprintf("%d", pageNumber))
	return remoteClient.DPNBagListGet(&params)
}

func (dpnSync *DPNSync) SyncReplicationRequests(remoteNode *DPNNode) ([]*DPNReplicationTransfer, error) {
	xfersUpdated := make([]*DPNReplicationTransfer, 0)
	pageNumber := 1
	remoteClient := dpnSync.RemoteClients[remoteNode.Namespace]
	for {
		dpnSync.Logger.Debug("Getting page %d of replication requests from %s", pageNumber, remoteNode.Namespace)
		result, err := dpnSync.getReplicationRequests(remoteClient, remoteNode, pageNumber)
		if err != nil {
			return xfersUpdated, err
		}
		dpnSync.Logger.Debug("Got %d replication requests from %s", len(result.Results), remoteNode.Namespace)
		updated, err := dpnSync.syncReplicationRequests(result.Results)
		if err != nil {
			return xfersUpdated, err
		}
		xfersUpdated = append(xfersUpdated, updated...)
		if result.Next == "" {
			dpnSync.Logger.Debug("No more replication requests to get from %s", remoteNode.Namespace)
			break
		}
		pageNumber += 1
	}
	dpnSync.Logger.Debug("Updated %d replication requests in local registry", len(xfersUpdated))
	return xfersUpdated, nil
}

func (dpnSync *DPNSync) syncReplicationRequests(xfers []*DPNReplicationTransfer) ([]*DPNReplicationTransfer, error) {
	xfersUpdated := make([]*DPNReplicationTransfer, 0)
	for _, xfer := range(xfers) {
		dpnSync.Logger.Debug("Updating transfer %s in local registry", xfer.ReplicationId)
		existingXfer, _ := dpnSync.LocalClient.ReplicationTransferGet(xfer.ReplicationId)
		var err error
		var updatedXfer *DPNReplicationTransfer
		if existingXfer != nil {
			dpnSync.Logger.Debug("Replication request %s exists... updating", xfer.ReplicationId)
			updatedXfer, err = dpnSync.LocalClient.ReplicationTransferUpdate(xfer)
		} else {
			dpnSync.Logger.Debug("Replication request %s not in local registry... creating", xfer.ReplicationId)
			updatedXfer, err = dpnSync.LocalClient.ReplicationTransferCreate(xfer)
		}
		if err != nil {
			dpnSync.Logger.Debug("Oops! Replication request %s: %v", xfer.ReplicationId, err)
			return xfersUpdated, err
		}
		xfersUpdated = append(xfersUpdated, updatedXfer)
	}
	return xfersUpdated, nil
}

func (dpnSync *DPNSync) getReplicationRequests(remoteClient *DPNRestClient, remoteNode *DPNNode, pageNumber int) (*ReplicationListResult, error) {
	// Get requests updated since the last time we pulled
	// from this node, where this node is the from_node.
	params := url.Values{}
	params.Set("after", remoteNode.LastPullDate.Format(time.RFC3339Nano))
	params.Set("from_node", remoteNode.Namespace)
	params.Set("page", fmt.Sprintf("%d", pageNumber))
	return remoteClient.DPNReplicationListGet(&params)
}

func (dpnSync *DPNSync) SyncRestoreRequests(remoteNode *DPNNode) ([]*DPNRestoreTransfer, error) {
	xfersUpdated := make([]*DPNRestoreTransfer, 0)
	pageNumber := 1
	remoteClient := dpnSync.RemoteClients[remoteNode.Namespace]
	for {
		dpnSync.Logger.Debug("Getting page %d of restore requests from %s", pageNumber, remoteNode.Namespace)
		result, err := dpnSync.getRestoreRequests(remoteClient, remoteNode, pageNumber)
		if err != nil {
			return xfersUpdated, err
		}
		dpnSync.Logger.Debug("Got %d restore requests from %s", len(result.Results), remoteNode.Namespace)
		updated, err := dpnSync.syncRestoreRequests(result.Results)
		if err != nil {
			return xfersUpdated, err
		}
		xfersUpdated = append(xfersUpdated, updated...)
		if result.Next == "" {
			dpnSync.Logger.Debug("No more restore requests to get from %s", remoteNode.Namespace)
			break
		}
		pageNumber += 1
	}
	dpnSync.Logger.Debug("Updated %d restore requests in local registry", len(xfersUpdated))
	return xfersUpdated, nil
}

func (dpnSync *DPNSync) syncRestoreRequests(xfers []*DPNRestoreTransfer) ([]*DPNRestoreTransfer, error) {
	xfersUpdated := make([]*DPNRestoreTransfer, 0)
	for _, xfer := range(xfers) {
		dpnSync.Logger.Debug("Updating transfer %s in local registry", xfer.RestoreId)
		existingXfer, _ := dpnSync.LocalClient.RestoreTransferGet(xfer.RestoreId)
		var err error
		var updatedXfer *DPNRestoreTransfer
		if existingXfer != nil {
			dpnSync.Logger.Debug("Restore request %s exists... updating", xfer.RestoreId)
			updatedXfer, err = dpnSync.LocalClient.RestoreTransferUpdate(xfer)
		} else {
			dpnSync.Logger.Debug("Restore request %s not in local registry... creating", xfer.RestoreId)
			updatedXfer, err = dpnSync.LocalClient.RestoreTransferCreate(xfer)
		}
		if err != nil {
			dpnSync.Logger.Debug("Oops! Restore request %s: %v", xfer.RestoreId, err)
			return xfersUpdated, err
		}
		xfersUpdated = append(xfersUpdated, updatedXfer)
	}
	return xfersUpdated, nil
}

func (dpnSync *DPNSync) getRestoreRequests(remoteClient *DPNRestClient, remoteNode *DPNNode, pageNumber int) (*RestoreListResult, error) {
	// Get requests updated since the last time we pulled
	// from this node, where this node is the to_node.
	// E.g. We ask TDR for restore requests going TO TDR.
	params := url.Values{}
	params.Set("after", remoteNode.LastPullDate.Format(time.RFC3339Nano))
	params.Set("to_node", remoteNode.Namespace)
	params.Set("page", fmt.Sprintf("%d", pageNumber))
	return remoteClient.DPNRestoreListGet(&params)
}
<commit_msg>If REST client for node is nil, log and continue with other nodes<commit_after>package dpn

import (
	"fmt"
	"github.com/APTrust/bagman/bagman"
	"github.com/op/go-logging"
	"net/url"
	"time"
)

type DPNSync struct {
	LocalClient    *DPNRestClient
	RemoteClients  map[string]*DPNRestClient
	Logger         *logging.Logger
	Config         *DPNConfig
}

// SyncResult describes the result of an operation where we pull
// info about all updated bags, replication requests and restore
// requests from a remote node and copy that data into our own
// local DPN registry.
type SyncResult struct {
	// Node is the node we are pulling information from.
	RemoteNode            *DPNNode
	// Bags is a list of bags successfully synched.
	Bags                  []*DPNBag
	// ReplicationTransfers successfully synched.
	ReplicationTransfers  []*DPNReplicationTransfer
	// RestoreTransfers successfully synched.
	RestoreTransfers      []*DPNRestoreTransfer
	// BagSyncError contains the error (if any) that occurred
	// during the bag sync process. The first error will stop
	// the synching of all subsquent bags.
	BagSyncError          error
	// ReplicationSyncError contains the error (if any) that occurred
	// during the synching of Replication Transfers. The first error
	// will stop the synching of all subsquent replication requests.
	ReplicationSyncError  error
	// RestoreSyncError contains the error (if any) that occurred
	// during the synching of Restore Transfers. The first error
	// will stop the synching of all subsquent restore requests.
	RestoreSyncError      error
	// TimestampError contains the error (if any) that occurred when
	// trying to update the LastPullDate timestamp for the node.
	TimestampError        error
}

func (syncResult *SyncResult) HasSyncErrors() (bool) {
	return (syncResult.BagSyncError != nil ||
		syncResult.ReplicationSyncError != nil ||
		syncResult.RestoreSyncError != nil)
}

func NewDPNSync(config *DPNConfig) (*DPNSync, error) {
	logger := initLogger(config)
	localClient, err := NewDPNRestClient(
		config.RestClient.LocalServiceURL,
		config.RestClient.LocalAPIRoot,
		config.RestClient.LocalAuthToken,
		logger)
	if err != nil {
		return nil, fmt.Errorf("Error creating local DPN REST client: %v", err)
	}
	remoteClients, err := GetRemoteClients(localClient, config, logger)
	if err != nil {
		return nil, fmt.Errorf("Error creating remote DPN REST client: %v", err)
	}
	sync := DPNSync{
		LocalClient: localClient,
		RemoteClients: remoteClients,
		Logger: logger,
		Config: config,
	}
	return &sync, nil
}

func initLogger(config *DPNConfig) (*logging.Logger) {
	// bagman has a nice InitLogger function, but it
	// needs a bagman.Config object, which does a lot
	// of handy internal work. Create a throw-away
	// bagman config so we can get our logger.
	bagmanConfig := bagman.Config{
		LogDirectory: config.LogDirectory,
		LogLevel: config.LogLevel,
		LogToStderr: config.LogToStderr,
	}
	return bagman.InitLogger(bagmanConfig)
}

// Returns a list of all the nodes that our node knows about.
func (dpnSync *DPNSync) GetAllNodes()([]*DPNNode, error) {
	result, err := dpnSync.LocalClient.DPNNodeListGet(nil)
	if err != nil {
		return nil, err
	}
	return result.Results, nil
}

func (dpnSync *DPNSync) UpdateLastPullDate(node *DPNNode, lastPullDate time.Time) (*DPNNode, error) {
	dpnSync.Logger.Debug("Setting last pull date on %s to %s", node.Namespace, lastPullDate)
	node.LastPullDate = lastPullDate  // Don't set this until you're ready to send!
	return dpnSync.LocalClient.DPNNodeUpdate(node)
}

// Sync all bags, replication requests and restore requests from
// the specified remote node. Note that this is a pull-only sync.
// We are not writing any data to other nodes, just reading what
// they have and updating our own registry with their info.
func (dpnSync *DPNSync) SyncEverythingFromNode(remoteNode *DPNNode) (*SyncResult) {
	nextTimeStamp := time.Now().UTC()
	syncResult := &SyncResult {
		RemoteNode: remoteNode,
	}

	bags, err := dpnSync.SyncBags(remoteNode)
	syncResult.Bags = bags
	syncResult.BagSyncError = err

	replXfers, err := dpnSync.SyncReplicationRequests(remoteNode)
	syncResult.ReplicationTransfers = replXfers
	syncResult.ReplicationSyncError = err

	restoreXfers, err := dpnSync.SyncRestoreRequests(remoteNode)
	syncResult.RestoreTransfers = restoreXfers
	syncResult.RestoreSyncError = err

	if !syncResult.HasSyncErrors() {
		updatedNode, err := dpnSync.UpdateLastPullDate(remoteNode, nextTimeStamp)
		syncResult.RemoteNode = updatedNode
		syncResult.TimestampError = err
	} else {
		syncResult.TimestampError = fmt.Errorf(
			"LastPullDate was not updated because of errors during the synchronization process.")
	}

	return syncResult
}

// Syncs bags from the specified node to our own local DPN registry
// if the bags match these critieria:
//
// 1. The node we are querying is the admin node for the bag.
// 2. The bag was updated since the last time we queried the node.
//
// Returns a list of the bags that were successfully updated.
// Even on error, this may still return a list with whatever bags
// were updated before the error occurred.
func (dpnSync *DPNSync) SyncBags(remoteNode *DPNNode) ([]*DPNBag, error) {
	pageNumber := 1
	bagsUpdated := make([]*DPNBag, 0)
	remoteClient := dpnSync.RemoteClients[remoteNode.Namespace]
	if remoteClient == nil {
		dpnSync.Logger.Error("Skipping sync for node %s: REST client for that node is nil",
			remoteNode.Namespace)
		return bagsUpdated, fmt.Errorf("No client available for node %s", remoteNode.Namespace)
	}
	for {
		dpnSync.Logger.Debug("Getting page %d of bags from %s", pageNumber, remoteNode.Namespace)
		result, err := dpnSync.getBags(remoteClient, remoteNode, pageNumber)
		if err != nil {
			return bagsUpdated, err
		}
		dpnSync.Logger.Debug("Got %d bags from %s", len(result.Results), remoteNode.Namespace)
		updated, err := dpnSync.syncBags(result.Results)
		if err != nil {
			return bagsUpdated, err
		}
		bagsUpdated = append(bagsUpdated, updated...)
		if result.Next == "" {
			dpnSync.Logger.Debug("No more bags to get from %s", remoteNode.Namespace)
			break
		}
		pageNumber += 1
	}
	dpnSync.Logger.Debug("Updated %d bags in local registry", len(bagsUpdated))
	return bagsUpdated, nil
}

func (dpnSync *DPNSync) syncBags(bags []*DPNBag) ([]*DPNBag, error) {
	bagsUpdated := make([]*DPNBag, 0)
	for _, bag := range(bags) {
		dpnSync.Logger.Debug("Updating bag %s in local registry", bag.UUID)
		existingBag, _ := dpnSync.LocalClient.DPNBagGet(bag.UUID)
		var err error
		var updatedBag *DPNBag
		if existingBag != nil {
			dpnSync.Logger.Debug("Bag %s exists... updating", bag.UUID)
			updatedBag, err = dpnSync.LocalClient.DPNBagUpdate(bag)
		} else {
			dpnSync.Logger.Debug("Bag %s not in local registry... creating", bag.UUID)
			updatedBag, err = dpnSync.LocalClient.DPNBagCreate(bag)
		}
		if err != nil {
			dpnSync.Logger.Debug("Oops! Bag %s: %v", bag.UUID, err)
			return bagsUpdated, err
		}
		bagsUpdated = append(bagsUpdated, updatedBag)
	}
	return bagsUpdated, nil
}

func (dpnSync *DPNSync) getBags(remoteClient *DPNRestClient, remoteNode *DPNNode, pageNumber int) (*BagListResult, error) {
	// We want to get all bags updated since the last time we pulled
	// from this node, and only those bags for which the node we're
	// querying is the admin node.
	params := url.Values{}
	params.Set("after", remoteNode.LastPullDate.Format(time.RFC3339Nano))
	params.Set("admin_node", remoteNode.Namespace)
	params.Set("page", fmt.Sprintf("%d", pageNumber))
	return remoteClient.DPNBagListGet(&params)
}

func (dpnSync *DPNSync) SyncReplicationRequests(remoteNode *DPNNode) ([]*DPNReplicationTransfer, error) {
	xfersUpdated := make([]*DPNReplicationTransfer, 0)
	pageNumber := 1
	remoteClient := dpnSync.RemoteClients[remoteNode.Namespace]
	for {
		dpnSync.Logger.Debug("Getting page %d of replication requests from %s", pageNumber, remoteNode.Namespace)
		result, err := dpnSync.getReplicationRequests(remoteClient, remoteNode, pageNumber)
		if err != nil {
			return xfersUpdated, err
		}
		dpnSync.Logger.Debug("Got %d replication requests from %s", len(result.Results), remoteNode.Namespace)
		updated, err := dpnSync.syncReplicationRequests(result.Results)
		if err != nil {
			return xfersUpdated, err
		}
		xfersUpdated = append(xfersUpdated, updated...)
		if result.Next == "" {
			dpnSync.Logger.Debug("No more replication requests to get from %s", remoteNode.Namespace)
			break
		}
		pageNumber += 1
	}
	dpnSync.Logger.Debug("Updated %d replication requests in local registry", len(xfersUpdated))
	return xfersUpdated, nil
}

func (dpnSync *DPNSync) syncReplicationRequests(xfers []*DPNReplicationTransfer) ([]*DPNReplicationTransfer, error) {
	xfersUpdated := make([]*DPNReplicationTransfer, 0)
	for _, xfer := range(xfers) {
		dpnSync.Logger.Debug("Updating transfer %s in local registry", xfer.ReplicationId)
		existingXfer, _ := dpnSync.LocalClient.ReplicationTransferGet(xfer.ReplicationId)
		var err error
		var updatedXfer *DPNReplicationTransfer
		if existingXfer != nil {
			dpnSync.Logger.Debug("Replication request %s exists... updating", xfer.ReplicationId)
			updatedXfer, err = dpnSync.LocalClient.ReplicationTransferUpdate(xfer)
		} else {
			dpnSync.Logger.Debug("Replication request %s not in local registry... creating", xfer.ReplicationId)
			updatedXfer, err = dpnSync.LocalClient.ReplicationTransferCreate(xfer)
		}
		if err != nil {
			dpnSync.Logger.Debug("Oops! Replication request %s: %v", xfer.ReplicationId, err)
			return xfersUpdated, err
		}
		xfersUpdated = append(xfersUpdated, updatedXfer)
	}
	return xfersUpdated, nil
}

func (dpnSync *DPNSync) getReplicationRequests(remoteClient *DPNRestClient, remoteNode *DPNNode, pageNumber int) (*ReplicationListResult, error) {
	// Get requests updated since the last time we pulled
	// from this node, where this node is the from_node.
	params := url.Values{}
	params.Set("after", remoteNode.LastPullDate.Format(time.RFC3339Nano))
	params.Set("from_node", remoteNode.Namespace)
	params.Set("page", fmt.Sprintf("%d", pageNumber))
	return remoteClient.DPNReplicationListGet(&params)
}

func (dpnSync *DPNSync) SyncRestoreRequests(remoteNode *DPNNode) ([]*DPNRestoreTransfer, error) {
	xfersUpdated := make([]*DPNRestoreTransfer, 0)
	pageNumber := 1
	remoteClient := dpnSync.RemoteClients[remoteNode.Namespace]
	for {
		dpnSync.Logger.Debug("Getting page %d of restore requests from %s", pageNumber, remoteNode.Namespace)
		result, err := dpnSync.getRestoreRequests(remoteClient, remoteNode, pageNumber)
		if err != nil {
			return xfersUpdated, err
		}
		dpnSync.Logger.Debug("Got %d restore requests from %s", len(result.Results), remoteNode.Namespace)
		updated, err := dpnSync.syncRestoreRequests(result.Results)
		if err != nil {
			return xfersUpdated, err
		}
		xfersUpdated = append(xfersUpdated, updated...)
		if result.Next == "" {
			dpnSync.Logger.Debug("No more restore requests to get from %s", remoteNode.Namespace)
			break
		}
		pageNumber += 1
	}
	dpnSync.Logger.Debug("Updated %d restore requests in local registry", len(xfersUpdated))
	return xfersUpdated, nil
}

func (dpnSync *DPNSync) syncRestoreRequests(xfers []*DPNRestoreTransfer) ([]*DPNRestoreTransfer, error) {
	xfersUpdated := make([]*DPNRestoreTransfer, 0)
	for _, xfer := range(xfers) {
		dpnSync.Logger.Debug("Updating transfer %s in local registry", xfer.RestoreId)
		existingXfer, _ := dpnSync.LocalClient.RestoreTransferGet(xfer.RestoreId)
		var err error
		var updatedXfer *DPNRestoreTransfer
		if existingXfer != nil {
			dpnSync.Logger.Debug("Restore request %s exists... updating", xfer.RestoreId)
			updatedXfer, err = dpnSync.LocalClient.RestoreTransferUpdate(xfer)
		} else {
			dpnSync.Logger.Debug("Restore request %s not in local registry... creating", xfer.RestoreId)
			updatedXfer, err = dpnSync.LocalClient.RestoreTransferCreate(xfer)
		}
		if err != nil {
			dpnSync.Logger.Debug("Oops! Restore request %s: %v", xfer.RestoreId, err)
			return xfersUpdated, err
		}
		xfersUpdated = append(xfersUpdated, updatedXfer)
	}
	return xfersUpdated, nil
}

func (dpnSync *DPNSync) getRestoreRequests(remoteClient *DPNRestClient, remoteNode *DPNNode, pageNumber int) (*RestoreListResult, error) {
	// Get requests updated since the last time we pulled
	// from this node, where this node is the to_node.
	// E.g. We ask TDR for restore requests going TO TDR.
	params := url.Values{}
	params.Set("after", remoteNode.LastPullDate.Format(time.RFC3339Nano))
	params.Set("to_node", remoteNode.Namespace)
	params.Set("page", fmt.Sprintf("%d", pageNumber))
	return remoteClient.DPNRestoreListGet(&params)
}
<|endoftext|>


<commit_before>package cache

import (
	"bytes"
	"crypto/sha1"
	"errors"
	"github.com/gin-gonic/gin"
	"io"
	"net/http"
	"net/url"
	"time"
)

const (
	DEFAULT              = time.Duration(0)
	FOREVER              = time.Duration(-1)
	CACHE_MIDDLEWARE_KEY = "gincontrib.cache"
)

var (
	PageCachePrefix = "gincontrib.page.cahe"
	ErrCacheMiss    = errors.New("cache: key not found.")
	ErrNotStored    = errors.New("cache: not stored.")
	ErrNotSupport   = errors.New("cache: not support.")
)

type CacheStore interface {
	Get(key string, value interface{}) error
	Set(key string, value interface{}, expire time.Duration) error
	Add(key string, value interface{}, expire time.Duration) error
	Replace(key string, data interface{}, expire time.Duration) error
	Delete(key string) error
	Increment(key string, data uint64) (uint64, error)
	Decrement(key string, data uint64) (uint64, error)
	Flush() error
}

type responseCache struct {
	status int
	header http.Header
	data   []byte
}

type cachedWriter struct {
	gin.ResponseWriter
	status  int
	written bool
	store   CacheStore
	expire  time.Duration
	key     string
}

func urlEscape(prefix string, u string) string {
	key := url.QueryEscape(u)
	if len(key) > 200 {
		h := sha1.New()
		io.WriteString(h, u)
		key = string(h.Sum(nil))
	}
	var buffer bytes.Buffer
	buffer.WriteString(prefix)
	buffer.WriteString(":")
	buffer.WriteString(key)
	return buffer.String()
}

func newCachedWriter(store CacheStore, expire time.Duration, writer gin.ResponseWriter, key string) *cachedWriter {
	return &cachedWriter{writer, 0, false, store, expire, key}
}

func (w *cachedWriter) WriteHeader(code int) {
	w.status = code
	w.written = true
	w.ResponseWriter.WriteHeader(code)
}

func (w *cachedWriter) Status() int {
	return w.status
}

func (w *cachedWriter) Written() bool {
	return w.written
}

func (w *cachedWriter) Write(data []byte) (int, error) {
	ret, err := w.ResponseWriter.Write(data)
	if err == nil {
		//cache response
		store := w.store
		val := responseCache{
			w.status,
			w.Header(),
			data,
		}
		err = store.Set(w.key, val, w.expire)
		if err != nil {
			// need logger
		}
	}
	return ret, err
}

// Cache Middleware
func Cache(store *CacheStore) gin.HandlerFunc {
	return func(c *gin.Context) {
		c.Set(CACHE_MIDDLEWARE_KEY, store)
		c.Next()
	}
}

func SiteCache(store CacheStore, expire time.Duration) gin.HandlerFunc {

	return func(c *gin.Context) {
		var cache responseCache
		url := c.Request.URL
		key := urlEscape(PageCachePrefix, url.RequestURI())
		if err := store.Get(key, &cache); err != nil {
			c.Next()
		} else {
			c.Writer.WriteHeader(cache.status)
			for k, vals := range cache.header {
				for _, v := range vals {
					c.Writer.Header().Add(k, v)
				}
			}
			c.Writer.Write(cache.data)
		}
	}
}

// Cache Decorator
func CachePage(store CacheStore, expire time.Duration, handle gin.HandlerFunc) gin.HandlerFunc {

	return func(c *gin.Context) {
		var cache responseCache
		url := c.Request.URL
		key := urlEscape(PageCachePrefix, url.RequestURI())
		if err := store.Get(key, &cache); err != nil {
			// replace writer
			writer := newCachedWriter(store, expire, c.Writer, key)
			c.Writer = writer
			handle(c)
		} else {
			c.Writer.WriteHeader(cache.status)
			for k, vals := range cache.header {
				for _, v := range vals {
					c.Writer.Header().Add(k, v)
				}
			}
			c.Writer.Write(cache.data)
		}
	}
}
<commit_msg>Fix typo in cache.PageCachePrefix<commit_after>package cache

import (
	"bytes"
	"crypto/sha1"
	"errors"
	"github.com/gin-gonic/gin"
	"io"
	"net/http"
	"net/url"
	"time"
)

const (
	DEFAULT              = time.Duration(0)
	FOREVER              = time.Duration(-1)
	CACHE_MIDDLEWARE_KEY = "gincontrib.cache"
)

var (
	PageCachePrefix = "gincontrib.page.cache"
	ErrCacheMiss    = errors.New("cache: key not found.")
	ErrNotStored    = errors.New("cache: not stored.")
	ErrNotSupport   = errors.New("cache: not support.")
)

type CacheStore interface {
	Get(key string, value interface{}) error
	Set(key string, value interface{}, expire time.Duration) error
	Add(key string, value interface{}, expire time.Duration) error
	Replace(key string, data interface{}, expire time.Duration) error
	Delete(key string) error
	Increment(key string, data uint64) (uint64, error)
	Decrement(key string, data uint64) (uint64, error)
	Flush() error
}

type responseCache struct {
	status int
	header http.Header
	data   []byte
}

type cachedWriter struct {
	gin.ResponseWriter
	status  int
	written bool
	store   CacheStore
	expire  time.Duration
	key     string
}

func urlEscape(prefix string, u string) string {
	key := url.QueryEscape(u)
	if len(key) > 200 {
		h := sha1.New()
		io.WriteString(h, u)
		key = string(h.Sum(nil))
	}
	var buffer bytes.Buffer
	buffer.WriteString(prefix)
	buffer.WriteString(":")
	buffer.WriteString(key)
	return buffer.String()
}

func newCachedWriter(store CacheStore, expire time.Duration, writer gin.ResponseWriter, key string) *cachedWriter {
	return &cachedWriter{writer, 0, false, store, expire, key}
}

func (w *cachedWriter) WriteHeader(code int) {
	w.status = code
	w.written = true
	w.ResponseWriter.WriteHeader(code)
}

func (w *cachedWriter) Status() int {
	return w.status
}

func (w *cachedWriter) Written() bool {
	return w.written
}

func (w *cachedWriter) Write(data []byte) (int, error) {
	ret, err := w.ResponseWriter.Write(data)
	if err == nil {
		//cache response
		store := w.store
		val := responseCache{
			w.status,
			w.Header(),
			data,
		}
		err = store.Set(w.key, val, w.expire)
		if err != nil {
			// need logger
		}
	}
	return ret, err
}

// Cache Middleware
func Cache(store *CacheStore) gin.HandlerFunc {
	return func(c *gin.Context) {
		c.Set(CACHE_MIDDLEWARE_KEY, store)
		c.Next()
	}
}

func SiteCache(store CacheStore, expire time.Duration) gin.HandlerFunc {

	return func(c *gin.Context) {
		var cache responseCache
		url := c.Request.URL
		key := urlEscape(PageCachePrefix, url.RequestURI())
		if err := store.Get(key, &cache); err != nil {
			c.Next()
		} else {
			c.Writer.WriteHeader(cache.status)
			for k, vals := range cache.header {
				for _, v := range vals {
					c.Writer.Header().Add(k, v)
				}
			}
			c.Writer.Write(cache.data)
		}
	}
}

// Cache Decorator
func CachePage(store CacheStore, expire time.Duration, handle gin.HandlerFunc) gin.HandlerFunc {

	return func(c *gin.Context) {
		var cache responseCache
		url := c.Request.URL
		key := urlEscape(PageCachePrefix, url.RequestURI())
		if err := store.Get(key, &cache); err != nil {
			// replace writer
			writer := newCachedWriter(store, expire, c.Writer, key)
			c.Writer = writer
			handle(c)
		} else {
			c.Writer.WriteHeader(cache.status)
			for k, vals := range cache.header {
				for _, v := range vals {
					c.Writer.Header().Add(k, v)
				}
			}
			c.Writer.Write(cache.data)
		}
	}
}
<|endoftext|>


<commit_before>package ricecore

import (
    "os"
    "io/ioutil"
    "testing"
)

func TestCreate(t *testing.T) {
    InitCore()

    //Setup
    os.Mkdir(homeDir + "/test/", 0755)
	testFileCont := []byte("le test")
    if err := ioutil.WriteFile(homeDir +"/test/test1", testFileCont, 0755); err != nil {
        t.Error(err.Error())
    }

    var files []*RiceFile
    files = make([]*RiceFile, 1)
    f1 := &RiceFile{Location:"./", File:"test1"}
    files[0] = f1
    _, err := CreateRice("test", "test-prog", "~/test/", files)
    if err != nil {
        t.Error(err.Error())
    }
    r, err := GetRice("test", "test-prog")
    if err != nil {
        t.Error(err.Error())
    }
    if err = r.LocalInit(); err != nil {
        t.Error(err.Error())
    }
}
<commit_msg>Added in more testing functions<commit_after>package ricecore

import (
    "os"
    "io/ioutil"
    "testing"
)

func TestCreate(t *testing.T) {
    InitCore()

    //Setup
    os.Mkdir(homeDir + "/test/", 0755)
	testFileCont := []byte("le test")
    if err := ioutil.WriteFile(homeDir +"/test/test1", testFileCont, 0755); err != nil {
        t.Error(err.Error())
    }

    var files []*RiceFile
    files = make([]*RiceFile, 1)
    f1 := &RiceFile{Location:"./", File:"test1"}
    files[0] = f1
    _, err := CreateRice("test", "test-prog", "~/test/", files)
    if err != nil {
        t.Error(err.Error())
    }
    r, err := GetRice("test", "test-prog")
    if err != nil {
        t.Error(err.Error())
    }
    if err = r.LocalInit(); err != nil {
        t.Error(err.Error())
    }
}

func TestActivate(t *testing.T) {
    r, err := GetRice("test", "test-prog")
    if err != nil {
        t.Error(err.Error())
    }

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

func TestGetActiveRice(t *testing.T) {
    _, err := GetActiveRice("test-prog")
    if err != nil {
        t.Error(err.Error())
    }
}

func TestDeactivate(t *testing.T) {
    if err := DeactivateCurrentRice("test-prog"); err != nil {
        t.Error(err.Error())
    }
}

func TestUninstall(t *testing.T) {
    r, err := GetRice("test", "test-prog")
    if err != nil {
        t.Error(err.Error())
    }

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

func TestQuery(t *testing.T) {
    _, err := QueryPackages("logos")
    if err != nil {
        t.Error(err.Error())
    }
    //TODO: Validate results
}
<|endoftext|>


<commit_before>package gaurun

import (
	"log"
	"math"
	"sync/atomic"
	"time"

	"github.com/client9/reopen"
	"github.com/uber-go/zap"
)

type LogReq struct {
	Type          string `json:"type"`
	Time          string `json:"time"`
	URI           string `json:"uri"`
	Method        string `json:"method"`
	Proto         string `json:"proto"`
	ContentLength int64  `json:"content_length"`
}

type LogPushEntry struct {
	Type     string  `json:"type"`
	Time     string  `json:"time"`
	ID       uint64  `json:"id"`
	Platform string  `json:"platform"`
	Token    string  `json:"token"`
	Message  string  `json:"message"`
	Ptime    float64 `json:"ptime"`
	Error    string  `json:"error"`
	// Android
	CollapseKey    string `json:"collapse_key,omitempty"`
	DelayWhileIdle bool   `json:"delay_while_idle,omitempty"`
	TimeToLive     int    `json:"time_to_live,omitempty"`
	// iOS
	Badge            int    `json:"badge,omitempty"`
	Sound            string `json:"sound,omitempty"`
	ContentAvailable bool   `json:"content_available,omitempty"`
	Expiry           int    `json:"expiry,omitempty"`
}

type Reopener interface {
	Reopen() error
}

func InitLog(outString, levelString string) (zap.Logger, Reopener, error) {
	var writer reopen.Writer
	switch outString {
	case "stdout":
		writer = reopen.Stdout
	case "stderr":
		writer = reopen.Stderr
	default:
		f, err := reopen.NewFileWriterMode(outString, 0644)
		if err != nil {
			return nil, nil, err
		}
		writer = f
	}

	encoder := zap.NewJSONEncoder(
		zap.MessageKey("message"),
		zap.TimeFormatter(func(t time.Time) zap.Field {
			return zap.String("time", t.Local().Format("2006/01/02 15:04:05 MST"))
		}),
	)

	var level zap.Level
	if err := level.UnmarshalText([]byte(levelString)); err != nil {
		return nil, nil, err
	}

	writeSyncer := zap.AddSync(writer)
	return zap.New(encoder, level, zap.Output(writeSyncer), zap.ErrorOutput(writeSyncer)), writer, nil
}

// LogSetupFatal output error log with log package and exit immediately.
func LogSetupFatal(err error) {
	log.Fatal(err)
}

func LogAcceptedRequest(uri, method, proto string, length int64) {
	LogAccess.Info("",
		zap.String("type", "accepted-request"),
		zap.String("uri", uri),
		zap.String("method", method),
		zap.String("proto", proto),
		zap.Int64("content_length", length),
	)
}

func LogPush(id uint64, status, token string, ptime float64, req RequestGaurunNotification, errPush error) {
	var plat string
	switch req.Platform {
	case PlatFormIos:
		plat = "ios"
	case PlatFormAndroid:
		plat = "android"
	}

	ptime = math.Floor(ptime*1000) / 1000 // %.3f conversion

	errMsg := ""
	if errPush != nil {
		errMsg = errPush.Error()
	}

	var logger func(string, ...zap.Field)
	switch status {
	case StatusAcceptedPush:
		fallthrough
	case StatusSucceededPush:
		logger = LogAccess.Info
	case StatusFailedPush:
		logger = LogError.Error
	}

	// omitempty handling for device dependent values
	collapseKey := zap.Skip()
	if req.CollapseKey != "" {
		collapseKey = zap.String("collapse_key", req.CollapseKey)
	}
	delayWhileIdle := zap.Skip()
	if req.DelayWhileIdle {
		delayWhileIdle = zap.Bool("delay_while_idle", req.DelayWhileIdle)
	}
	timeToLive := zap.Skip()
	if req.TimeToLive != 0 {
		timeToLive = zap.Int("time_to_live", req.TimeToLive)
	}
	badge := zap.Skip()
	if req.Badge != 0 {
		badge = zap.Int("badge", req.Badge)
	}
	sound := zap.Skip()
	if req.Sound != "" {
		sound = zap.String("sound", req.Sound)
	}
	contentAvailable := zap.Skip()
	if req.ContentAvailable {
		contentAvailable = zap.Bool("content_available", req.ContentAvailable)
	}
	expiry := zap.Skip()
	if req.Expiry != 0 {
		expiry = zap.Int("expiry", req.Expiry)
	}

	logger(req.Message,
		zap.Uint64("id", id),
		zap.String("platform", plat),
		zap.String("token", token),
		zap.String("type", status),
		zap.Float64("ptime", ptime),
		zap.String("error", errMsg),
		collapseKey,
		delayWhileIdle,
		timeToLive,
		badge,
		sound,
		contentAvailable,
		expiry,
	)
}

func numberingPush() uint64 {
	return atomic.AddUint64(&SeqID, 1)
}
<commit_msg>add mutable_content log field<commit_after>package gaurun

import (
	"log"
	"math"
	"sync/atomic"
	"time"

	"github.com/client9/reopen"
	"github.com/uber-go/zap"
)

type LogReq struct {
	Type          string `json:"type"`
	Time          string `json:"time"`
	URI           string `json:"uri"`
	Method        string `json:"method"`
	Proto         string `json:"proto"`
	ContentLength int64  `json:"content_length"`
}

type LogPushEntry struct {
	Type     string  `json:"type"`
	Time     string  `json:"time"`
	ID       uint64  `json:"id"`
	Platform string  `json:"platform"`
	Token    string  `json:"token"`
	Message  string  `json:"message"`
	Ptime    float64 `json:"ptime"`
	Error    string  `json:"error"`
	// Android
	CollapseKey    string `json:"collapse_key,omitempty"`
	DelayWhileIdle bool   `json:"delay_while_idle,omitempty"`
	TimeToLive     int    `json:"time_to_live,omitempty"`
	// iOS
	Badge            int    `json:"badge,omitempty"`
	Sound            string `json:"sound,omitempty"`
	ContentAvailable bool   `json:"content_available,omitempty"`
	MutableContent   bool   `json:"mutable_content,omitempty"`
	Expiry           int    `json:"expiry,omitempty"`
}

type Reopener interface {
	Reopen() error
}

func InitLog(outString, levelString string) (zap.Logger, Reopener, error) {
	var writer reopen.Writer
	switch outString {
	case "stdout":
		writer = reopen.Stdout
	case "stderr":
		writer = reopen.Stderr
	default:
		f, err := reopen.NewFileWriterMode(outString, 0644)
		if err != nil {
			return nil, nil, err
		}
		writer = f
	}

	encoder := zap.NewJSONEncoder(
		zap.MessageKey("message"),
		zap.TimeFormatter(func(t time.Time) zap.Field {
			return zap.String("time", t.Local().Format("2006/01/02 15:04:05 MST"))
		}),
	)

	var level zap.Level
	if err := level.UnmarshalText([]byte(levelString)); err != nil {
		return nil, nil, err
	}

	writeSyncer := zap.AddSync(writer)
	return zap.New(encoder, level, zap.Output(writeSyncer), zap.ErrorOutput(writeSyncer)), writer, nil
}

// LogSetupFatal output error log with log package and exit immediately.
func LogSetupFatal(err error) {
	log.Fatal(err)
}

func LogAcceptedRequest(uri, method, proto string, length int64) {
	LogAccess.Info("",
		zap.String("type", "accepted-request"),
		zap.String("uri", uri),
		zap.String("method", method),
		zap.String("proto", proto),
		zap.Int64("content_length", length),
	)
}

func LogPush(id uint64, status, token string, ptime float64, req RequestGaurunNotification, errPush error) {
	var plat string
	switch req.Platform {
	case PlatFormIos:
		plat = "ios"
	case PlatFormAndroid:
		plat = "android"
	}

	ptime = math.Floor(ptime*1000) / 1000 // %.3f conversion

	errMsg := ""
	if errPush != nil {
		errMsg = errPush.Error()
	}

	var logger func(string, ...zap.Field)
	switch status {
	case StatusAcceptedPush:
		fallthrough
	case StatusSucceededPush:
		logger = LogAccess.Info
	case StatusFailedPush:
		logger = LogError.Error
	}

	// omitempty handling for device dependent values
	collapseKey := zap.Skip()
	if req.CollapseKey != "" {
		collapseKey = zap.String("collapse_key", req.CollapseKey)
	}
	delayWhileIdle := zap.Skip()
	if req.DelayWhileIdle {
		delayWhileIdle = zap.Bool("delay_while_idle", req.DelayWhileIdle)
	}
	timeToLive := zap.Skip()
	if req.TimeToLive != 0 {
		timeToLive = zap.Int("time_to_live", req.TimeToLive)
	}
	badge := zap.Skip()
	if req.Badge != 0 {
		badge = zap.Int("badge", req.Badge)
	}
	sound := zap.Skip()
	if req.Sound != "" {
		sound = zap.String("sound", req.Sound)
	}
	contentAvailable := zap.Skip()
	if req.ContentAvailable {
		contentAvailable = zap.Bool("content_available", req.ContentAvailable)
	}
	mutableContent := zap.Skip()
	if req.MutableContent {
		mutableContent = zap.Bool("mutable_content", req.MutableContent)
	}
	expiry := zap.Skip()
	if req.Expiry != 0 {
		expiry = zap.Int("expiry", req.Expiry)
	}

	logger(req.Message,
		zap.Uint64("id", id),
		zap.String("platform", plat),
		zap.String("token", token),
		zap.String("type", status),
		zap.Float64("ptime", ptime),
		zap.String("error", errMsg),
		collapseKey,
		delayWhileIdle,
		timeToLive,
		badge,
		sound,
		contentAvailable,
		mutableContent,
		expiry,
	)
}

func numberingPush() uint64 {
	return atomic.AddUint64(&SeqID, 1)
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Pani Networks
// 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 kubernetes implements kubernetes API specific
// helper functions.
package kubernetes

import (
	"fmt"
	"github.com/golang/glog"
	"github.com/romana/core/common"
	"github.com/romana/core/tenant"
	"net/http"
	"strings"
	"sync"
)

// TODO
// 1. Make interface for translate
// 2. implemet caching for resolving tenant/segments
// 3. Decide what to do with getOrAddSegment,
//    ( translation might not be possible unless all
//      tenants/segments created )

type PolicyTranslator interface {
	Init(*common.RestClient, string)

	// Translates kubernetes policy into romana format.
	Kube2Romana(KubeObject) (common.Policy, error)

	// Translates number of kubernetes policies into romana format.
	// Returns a list of translated policies, list of original policies
	// that failed to translate and an error.
	Kube2RomanaBulk([]KubeObject) ([]common.Policy, []KubeObject, error)
}

type Translator struct {
	listener         *kubeListener
	restClient       *common.RestClient
	tenantsCache     []TenantCacheEntry
	cacheMu          sync.Mutex
	segmentLabelName string
}

// resolveTenantByName retrieves tenant information from romana.
func (l *Translator) resolveTenantByName(tenantName string) (*tenant.Tenant, error) {
	t := &tenant.Tenant{Name: tenantName}
	err := l.restClient.Find(t, common.FindLast)
	if err != nil {
		return t, err
	}
	return t, nil
}

// getOrAddSegment finds a segment (based on segment selector).
// If not found, it adds one.
func (l *Translator) getOrAddSegment(namespace string, kubeSegmentName string) (*tenant.Segment, error) {
	tenantCacheEntry := l.checkTenantInCahce(namespace)
	if tenantCacheEntry == nil {
		return nil, TranslatorError{ErrorTenantNotIntCache, fmt.Errorf("Tenant not found in cache while resolving segment")}
	}

	segment := l.checkSegmentInCahce(tenantCacheEntry, kubeSegmentName)
	if segment != nil {
		// Stop right here, we have what we
		// came for.
		return segment, nil

	}

	// This branch corresponds to a situation when
	// tenant found in the cache but segment isn't.
	// We will try to create a segment and update the cache.
	defer func() {
		err := l.updateCache()
		if err != nil {
			glog.Error("Failed to update cache in translator during getOrAddSegment().")
		}
	}()

	ten := tenantCacheEntry.Tenant
	seg := &tenant.Segment{}
	seg.Name = kubeSegmentName
	seg.TenantID = ten.ID
	err := l.restClient.Find(seg, common.FindExactlyOne)
	if err == nil {
		return seg, nil
	}

	switch err := err.(type) {
	case common.HttpError:
		if err.StatusCode == http.StatusNotFound {
			// Not found, so let's create a segment.
			segreq := tenant.Segment{Name: kubeSegmentName, TenantID: ten.ID}
			segURL, err2 := l.restClient.GetServiceUrl("tenant")
			if err2 != nil {
				return nil, err2
			}
			segURL = fmt.Sprintf("%s/tenants/%d/segments", segURL, ten.ID)
			err2 = l.restClient.Post(segURL, segreq, seg)
			if err2 == nil {
				// Successful creation.
				return seg, nil
			}
			// Creation of non-existing segment gave an error.
			switch err2 := err2.(type) {
			case common.HttpError:
				// Maybe someone else just created a segment between the original
				// lookup and now?
				if err2.StatusCode == http.StatusConflict {
					switch details := err2.Details.(type) {
					case tenant.Segment:
						// We expect the existing segment to be returned in the details field.
						return &details, nil
					default:
						// This is unexpected...
						return nil, err
					}
				}
				// Any other HTTP error other than a Conflict here - return it.
				return nil, err2
			default:
				// Any other error - return it
				return nil, err2
			}
		}
		// Any other HTTP error other than a Not found here - return it
		return nil, err
	default:
		// Any other error - return it
		return nil, err
	}
}

// translateNetworkPolicy translates a Kubernetes policy into
// Romana policy (see common.Policy) with the following rules:
// 1. Kubernetes Namespace corresponds to Romana Tenant
// 2. If Romana Tenant does not exist it is an error (a tenant should
//    automatically have been created when the namespace was added)
func (l *Translator) translateNetworkPolicy(kubePolicy *KubeObject) (common.Policy, error) {
	policyName := kubePolicy.Metadata.Name
	romanaPolicy := &common.Policy{Direction: common.PolicyDirectionIngress, Name: policyName, ExternalID: kubePolicy.Metadata.Uid}
	ns := kubePolicy.Metadata.Namespace
	// TODO actually look up tenant K8S ID.
	tenantCacheEntry := l.checkTenantInCahce(ns)
	if tenantCacheEntry == nil {
		return common.Policy{}, TranslatorError{ErrorTenantNotIntCache, nil}
	}

	t := tenantCacheEntry.Tenant
	glog.Infof("translateNetworkPolicy(): For namespace %s got %+v", ns, t)
	tenantID := t.ID
	tenantExternalID := t.ExternalID

	kubeSegmentID := kubePolicy.Spec.PodSelector.MatchLabels[l.segmentLabelName]
	if kubeSegmentID == "" {
		return *romanaPolicy, common.NewError("Expected segment to be specified in podSelector part as '%s'", l.segmentLabelName)
	}

	segment, err := l.getOrAddSegment(ns, kubeSegmentID)
	//	log.Printf("XXXX getOrAddSegment %s %s: %+v %v", ns, kubeSegmentID, segment, err)
	if err != nil {
		return *romanaPolicy, err
	}
	segmentID := segment.ID
	appliedTo := common.Endpoint{TenantID: tenantID, SegmentID: segmentID}
	//	log.Printf("XXXX 0 %+v %d %d", appliedTo, tenantID, segmentID)
	//	log.Printf("XXXX 1 %+v", romanaPolicy.AppliedTo)
	romanaPolicy.AppliedTo = make([]common.Endpoint, 1)
	romanaPolicy.AppliedTo[0] = appliedTo
	//	log.Printf("XXXX 2 %+v %d expecting %+v", romanaPolicy.AppliedTo, len(romanaPolicy.AppliedTo), appliedTo)
	romanaPolicy.Peers = make([]common.Endpoint, 0)
	romanaPolicy.Rules = make([]common.Rule, 0)
	// TODO range
	// from := kubePolicy.Spec.Ingress[0].From
	// This is subject to change once the network specification in Kubernetes is finalized.
	// Right now it is a work in progress.
	glog.Infof("YYYYY For %s processing %+v", kubePolicy.Metadata.Name, kubePolicy.Spec.Ingress)
	for _, ingress := range kubePolicy.Spec.Ingress {
		for _, entry := range ingress.From {
			pods := entry.Pods
			fromKubeSegmentID := pods.MatchLabels[l.segmentLabelName]
			if fromKubeSegmentID == "" {
				return *romanaPolicy, common.NewError("Expected segment to be specified in podSelector part as '%s'", l.segmentLabelName)
			}
			fromSegment, err := l.getOrAddSegment(ns, fromKubeSegmentID)
			if err != nil {
				glog.Errorf("Error in policy translator getOrAddSegment() exited with %s", err)
				return *romanaPolicy, err
			}
			peer := common.Endpoint{TenantID: tenantID, TenantExternalID: tenantExternalID, SegmentID: fromSegment.ID, SegmentExternalID: fromSegment.ExternalID}
			romanaPolicy.Peers = append(romanaPolicy.Peers, peer)
		}
		for _, toPort := range ingress.ToPorts {
			proto := strings.ToLower(toPort.Protocol)
			ports := []uint{toPort.Port}
			rule := common.Rule{Protocol: proto, Ports: ports}
			romanaPolicy.Rules = append(romanaPolicy.Rules, rule)
			glog.Infof("YYYYY %+v", romanaPolicy.Rules)
		}
	}
	glog.Infof("translateNetworkPolicy(): Validating %+v", romanaPolicy)
	err = romanaPolicy.Validate()
	if err != nil {
		glog.Errorf("Error in policy translator failed to validate resulting policy %v - %s", romanaPolicy, err)
		return *romanaPolicy, err
	}
	return *romanaPolicy, nil
}

type TenantCacheEntry struct {
	Tenant   tenant.Tenant
	Segments []tenant.Segment
}

func (t Translator) checkTenantInCahce(tenantName string) *TenantCacheEntry {
	t.cacheMu.Lock()
	defer func() {
		t.cacheMu.Unlock()
	}()

	for tn, currentTenant := range t.tenantsCache {
		if currentTenant.Tenant.Name == tenantName {
			return &t.tenantsCache[tn]
		}
	}
	return nil
}

func (t Translator) Kube2Romana(kubePolicy KubeObject) (common.Policy, error) {
	return common.Policy{}, nil
}

func (t Translator) Kube2RomanaBulk(kubePolicies []KubeObject) ([]common.Policy, []KubeObject, error) {
	glog.Info("In Kube2RomanaBulk")
	var returnRomanaPolicy []common.Policy
	var returnKubePolicy []KubeObject

	err := t.updateCache()
	if err != nil {
		return returnRomanaPolicy, returnKubePolicy, TranslatorError{ErrorCacheUpdate, err}
	}

	for kubePolicyNumber, _ := range kubePolicies {
		romanaPolicy, err := t.translateNetworkPolicy(&kubePolicies[kubePolicyNumber])
		if err != nil {
			returnKubePolicy = append(returnKubePolicy, kubePolicies[kubePolicyNumber])
		} else {
			returnRomanaPolicy = append(returnRomanaPolicy, romanaPolicy)
		}
	}

	return returnRomanaPolicy, returnKubePolicy, nil

}

func (t Translator) checkSegmentInCahce(cacheEntry *TenantCacheEntry, segmentId string) *tenant.Segment {
	t.cacheMu.Lock()
	defer func() {
		t.cacheMu.Unlock()
	}()

	for segn, currentSegment := range cacheEntry.Segments {
		if currentSegment.Name == currentSegment.Name {
			return &cacheEntry.Segments[segn]
		}
	}
	return nil
}

func (t *Translator) updateCache() error {
	glog.Info("In updateCache")

	tenantURL, err := t.restClient.GetServiceUrl("tenant")
	if err != nil {
		return TranslatorError{ErrorCacheUpdate, err}
	}
	glog.Info("In updateCache 2")

	tenants := []tenant.Tenant{}
	err = t.restClient.Get(tenantURL+"/tenants", &tenants)
	if err != nil {
		return TranslatorError{ErrorCacheUpdate, err}
	}
	glog.Info("In updateCache 3")

	if t.restClient == nil {
		glog.Fatal("REST client is nil")
	}

	// tenants := []tenant.Tenant{}
	// _ = t.restClient.Find(&tenants, common.FindAll)

	t.cacheMu.Lock()
	defer func() {
		glog.Infof("Exiting updateCahce with %d tenants", len(t.tenantsCache))
		t.cacheMu.Unlock()
	}()

	t.tenantsCache = nil
	for _, ten := range tenants {
		segments := []tenant.Segment{}
		// err = t.restClient.Get(tenantURL+"/tenants/"+ten.ExternalID+"/segments", &segments)
		// err = t.restClient.Get(tenantURL+"/tenants/"+string(ten.ID)+"/segments", &segments)
		fullUrl := fmt.Sprintf("%s/tenants/%d/segments", tenantURL, ten.ID)
		err = t.restClient.Get(fullUrl, &segments)
		// err := t.restClient.Find(&segments, common.FindAll)
		if err != nil {
			return TranslatorError{ErrorCacheUpdate, err}
		}

		t.tenantsCache = append(t.tenantsCache, TenantCacheEntry{ten, segments})
	}
	return nil
}

func (t *Translator) Init(client *common.RestClient, segmentLabelName string) {

	t.restClient = client
	err := t.updateCache()
	if err == nil {
		glog.Infof("Translator cache updated - have %d tenant entries", len(t.tenantsCache))
	} else {
		glog.Errorf("Translator cache update failed, %s", err)
	}

	t.cacheMu = sync.Mutex{}
	t.segmentLabelName = segmentLabelName
}

func (t Translator) GetClient() *common.RestClient {
	return t.restClient
}

type TranslatorError struct {
	Code    TranslatorErrorType
	Details error
}

func (t TranslatorError) Error() string {
	return fmt.Sprintf("Translator error code %d, %s", t.Code, t.Details)
}

type TranslatorErrorType int

const (
	ErrorCacheUpdate TranslatorErrorType = iota
	ErrorTenantNotIntCache
)
<commit_msg>Fix policy translator update cache issue on http 404<commit_after>// Copyright (c) 2016 Pani Networks
// 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 kubernetes implements kubernetes API specific
// helper functions.
package kubernetes

import (
	"fmt"
	"github.com/golang/glog"
	"github.com/romana/core/common"
	"github.com/romana/core/tenant"
	"net/http"
	"strings"
	"sync"
)

// TODO
// 1. Make interface for translate
// 2. implemet caching for resolving tenant/segments
// 3. Decide what to do with getOrAddSegment,
//    ( translation might not be possible unless all
//      tenants/segments created )

type PolicyTranslator interface {
	Init(*common.RestClient, string)

	// Translates kubernetes policy into romana format.
	Kube2Romana(KubeObject) (common.Policy, error)

	// Translates number of kubernetes policies into romana format.
	// Returns a list of translated policies, list of original policies
	// that failed to translate and an error.
	Kube2RomanaBulk([]KubeObject) ([]common.Policy, []KubeObject, error)
}

type Translator struct {
	listener         *kubeListener
	restClient       *common.RestClient
	tenantsCache     []TenantCacheEntry
	cacheMu          sync.Mutex
	segmentLabelName string
}

// resolveTenantByName retrieves tenant information from romana.
func (l *Translator) resolveTenantByName(tenantName string) (*tenant.Tenant, error) {
	t := &tenant.Tenant{Name: tenantName}
	err := l.restClient.Find(t, common.FindLast)
	if err != nil {
		return t, err
	}
	return t, nil
}

// getOrAddSegment finds a segment (based on segment selector).
// If not found, it adds one.
func (l *Translator) getOrAddSegment(namespace string, kubeSegmentName string) (*tenant.Segment, error) {
	tenantCacheEntry := l.checkTenantInCahce(namespace)
	if tenantCacheEntry == nil {
		return nil, TranslatorError{ErrorTenantNotIntCache, fmt.Errorf("Tenant not found in cache while resolving segment")}
	}

	segment := l.checkSegmentInCahce(tenantCacheEntry, kubeSegmentName)
	if segment != nil {
		// Stop right here, we have what we
		// came for.
		return segment, nil

	}

	// This branch corresponds to a situation when
	// tenant found in the cache but segment isn't.
	// We will try to create a segment and update the cache.
	defer func() {
		err := l.updateCache()
		if err != nil {
			glog.Error("Failed to update cache in translator during getOrAddSegment().")
		}
	}()

	ten := tenantCacheEntry.Tenant
	seg := &tenant.Segment{}
	seg.Name = kubeSegmentName
	seg.TenantID = ten.ID
	err := l.restClient.Find(seg, common.FindExactlyOne)
	if err == nil {
		return seg, nil
	}

	switch err := err.(type) {
	case common.HttpError:
		if err.StatusCode == http.StatusNotFound {
			// Not found, so let's create a segment.
			segreq := tenant.Segment{Name: kubeSegmentName, TenantID: ten.ID}
			segURL, err2 := l.restClient.GetServiceUrl("tenant")
			if err2 != nil {
				return nil, err2
			}
			segURL = fmt.Sprintf("%s/tenants/%d/segments", segURL, ten.ID)
			err2 = l.restClient.Post(segURL, segreq, seg)
			if err2 == nil {
				// Successful creation.
				return seg, nil
			}
			// Creation of non-existing segment gave an error.
			switch err2 := err2.(type) {
			case common.HttpError:
				// Maybe someone else just created a segment between the original
				// lookup and now?
				if err2.StatusCode == http.StatusConflict {
					switch details := err2.Details.(type) {
					case tenant.Segment:
						// We expect the existing segment to be returned in the details field.
						return &details, nil
					default:
						// This is unexpected...
						return nil, err
					}
				}
				// Any other HTTP error other than a Conflict here - return it.
				return nil, err2
			default:
				// Any other error - return it
				return nil, err2
			}
		}
		// Any other HTTP error other than a Not found here - return it
		return nil, err
	default:
		// Any other error - return it
		return nil, err
	}
}

// translateNetworkPolicy translates a Kubernetes policy into
// Romana policy (see common.Policy) with the following rules:
// 1. Kubernetes Namespace corresponds to Romana Tenant
// 2. If Romana Tenant does not exist it is an error (a tenant should
//    automatically have been created when the namespace was added)
func (l *Translator) translateNetworkPolicy(kubePolicy *KubeObject) (common.Policy, error) {
	policyName := kubePolicy.Metadata.Name
	romanaPolicy := &common.Policy{Direction: common.PolicyDirectionIngress, Name: policyName, ExternalID: kubePolicy.Metadata.Uid}
	ns := kubePolicy.Metadata.Namespace
	// TODO actually look up tenant K8S ID.
	tenantCacheEntry := l.checkTenantInCahce(ns)
	if tenantCacheEntry == nil {
		return common.Policy{}, TranslatorError{ErrorTenantNotIntCache, nil}
	}

	t := tenantCacheEntry.Tenant
	glog.Infof("translateNetworkPolicy(): For namespace %s got %+v", ns, t)
	tenantID := t.ID
	tenantExternalID := t.ExternalID

	kubeSegmentID := kubePolicy.Spec.PodSelector.MatchLabels[l.segmentLabelName]
	if kubeSegmentID == "" {
		return *romanaPolicy, common.NewError("Expected segment to be specified in podSelector part as '%s'", l.segmentLabelName)
	}

	segment, err := l.getOrAddSegment(ns, kubeSegmentID)
	//	log.Printf("XXXX getOrAddSegment %s %s: %+v %v", ns, kubeSegmentID, segment, err)
	if err != nil {
		return *romanaPolicy, err
	}
	segmentID := segment.ID
	appliedTo := common.Endpoint{TenantID: tenantID, SegmentID: segmentID}
	//	log.Printf("XXXX 0 %+v %d %d", appliedTo, tenantID, segmentID)
	//	log.Printf("XXXX 1 %+v", romanaPolicy.AppliedTo)
	romanaPolicy.AppliedTo = make([]common.Endpoint, 1)
	romanaPolicy.AppliedTo[0] = appliedTo
	//	log.Printf("XXXX 2 %+v %d expecting %+v", romanaPolicy.AppliedTo, len(romanaPolicy.AppliedTo), appliedTo)
	romanaPolicy.Peers = make([]common.Endpoint, 0)
	romanaPolicy.Rules = make([]common.Rule, 0)
	// TODO range
	// from := kubePolicy.Spec.Ingress[0].From
	// This is subject to change once the network specification in Kubernetes is finalized.
	// Right now it is a work in progress.
	glog.Infof("YYYYY For %s processing %+v", kubePolicy.Metadata.Name, kubePolicy.Spec.Ingress)
	for _, ingress := range kubePolicy.Spec.Ingress {
		for _, entry := range ingress.From {
			pods := entry.Pods
			fromKubeSegmentID := pods.MatchLabels[l.segmentLabelName]
			if fromKubeSegmentID == "" {
				return *romanaPolicy, common.NewError("Expected segment to be specified in podSelector part as '%s'", l.segmentLabelName)
			}
			fromSegment, err := l.getOrAddSegment(ns, fromKubeSegmentID)
			if err != nil {
				glog.Errorf("Error in policy translator getOrAddSegment() exited with %s", err)
				return *romanaPolicy, err
			}
			peer := common.Endpoint{TenantID: tenantID, TenantExternalID: tenantExternalID, SegmentID: fromSegment.ID, SegmentExternalID: fromSegment.ExternalID}
			romanaPolicy.Peers = append(romanaPolicy.Peers, peer)
		}
		for _, toPort := range ingress.ToPorts {
			proto := strings.ToLower(toPort.Protocol)
			ports := []uint{toPort.Port}
			rule := common.Rule{Protocol: proto, Ports: ports}
			romanaPolicy.Rules = append(romanaPolicy.Rules, rule)
			glog.Infof("YYYYY %+v", romanaPolicy.Rules)
		}
	}
	glog.Infof("translateNetworkPolicy(): Validating %+v", romanaPolicy)
	err = romanaPolicy.Validate()
	if err != nil {
		glog.Errorf("Error in policy translator failed to validate resulting policy %v - %s", romanaPolicy, err)
		return *romanaPolicy, err
	}
	return *romanaPolicy, nil
}

type TenantCacheEntry struct {
	Tenant   tenant.Tenant
	Segments []tenant.Segment
}

func (t Translator) checkTenantInCahce(tenantName string) *TenantCacheEntry {
	t.cacheMu.Lock()
	defer func() {
		t.cacheMu.Unlock()
	}()

	for tn, currentTenant := range t.tenantsCache {
		if currentTenant.Tenant.Name == tenantName {
			return &t.tenantsCache[tn]
		}
	}
	return nil
}

func (t Translator) Kube2Romana(kubePolicy KubeObject) (common.Policy, error) {
	return common.Policy{}, nil
}

func (t Translator) Kube2RomanaBulk(kubePolicies []KubeObject) ([]common.Policy, []KubeObject, error) {
	glog.Info("In Kube2RomanaBulk")
	var returnRomanaPolicy []common.Policy
	var returnKubePolicy []KubeObject

	err := t.updateCache()
	if err != nil {
		return returnRomanaPolicy, returnKubePolicy, TranslatorError{ErrorCacheUpdate, err}
	}

	for kubePolicyNumber, _ := range kubePolicies {
		romanaPolicy, err := t.translateNetworkPolicy(&kubePolicies[kubePolicyNumber])
		if err != nil {
			returnKubePolicy = append(returnKubePolicy, kubePolicies[kubePolicyNumber])
		} else {
			returnRomanaPolicy = append(returnRomanaPolicy, romanaPolicy)
		}
	}

	return returnRomanaPolicy, returnKubePolicy, nil

}

func (t Translator) checkSegmentInCahce(cacheEntry *TenantCacheEntry, segmentId string) *tenant.Segment {
	t.cacheMu.Lock()
	defer func() {
		t.cacheMu.Unlock()
	}()

	for segn, currentSegment := range cacheEntry.Segments {
		if currentSegment.Name == currentSegment.Name {
			return &cacheEntry.Segments[segn]
		}
	}
	return nil
}

func (t *Translator) updateCache() error {
	glog.Info("In updateCache")

	tenantURL, err := t.restClient.GetServiceUrl("tenant")
	if err != nil {
		return TranslatorError{ErrorCacheUpdate, err}
	}
	glog.Info("In updateCache 2")

	tenants := []tenant.Tenant{}
	err = t.restClient.Get(tenantURL+"/tenants", &tenants)
	if err != nil {
		return TranslatorError{ErrorCacheUpdate, err}
	}
	glog.Info("In updateCache 3")

	if t.restClient == nil {
		glog.Fatal("REST client is nil")
	}

	// tenants := []tenant.Tenant{}
	// _ = t.restClient.Find(&tenants, common.FindAll)

	t.cacheMu.Lock()
	defer func() {
		glog.Infof("Exiting updateCahce with %d tenants", len(t.tenantsCache))
		t.cacheMu.Unlock()
	}()

	t.tenantsCache = nil
	for _, ten := range tenants {
		segments := []tenant.Segment{}
		// err = t.restClient.Get(tenantURL+"/tenants/"+ten.ExternalID+"/segments", &segments)
		// err = t.restClient.Get(tenantURL+"/tenants/"+string(ten.ID)+"/segments", &segments)
		fullUrl := fmt.Sprintf("%s/tenants/%d/segments", tenantURL, ten.ID)
		err = t.restClient.Get(fullUrl, &segments)
		// err := t.restClient.Find(&segments, common.FindAll)

		// ignore 404 error here which means no segments
		// considered to be a zero segments rather then
		// an error.
		if err != nil && !checkHttp404(err) {
			return TranslatorError{ErrorCacheUpdate, err}
		}

		t.tenantsCache = append(t.tenantsCache, TenantCacheEntry{ten, segments})
	}
	return nil
}

func checkHttp404(err error) bool {
	var ret bool
	switch e := err.(type) {
	case common.HttpError:
		if e.StatusCode == 404 {
			ret = true
		}
	}

	return ret
}

func (t *Translator) Init(client *common.RestClient, segmentLabelName string) {

	t.restClient = client
	err := t.updateCache()
	if err == nil {
		glog.Infof("Translator cache updated - have %d tenant entries", len(t.tenantsCache))
	} else {
		glog.Errorf("Translator cache update failed, %s", err)
	}

	t.cacheMu = sync.Mutex{}
	t.segmentLabelName = segmentLabelName
}

func (t Translator) GetClient() *common.RestClient {
	return t.restClient
}

type TranslatorError struct {
	Code    TranslatorErrorType
	Details error
}

func (t TranslatorError) Error() string {
	return fmt.Sprintf("Translator error code %d, %s", t.Code, t.Details)
}

type TranslatorErrorType int

const (
	ErrorCacheUpdate TranslatorErrorType = iota
	ErrorTenantNotIntCache
)
<|endoftext|>


<commit_before><commit_msg>fix: null pointer error and show parsing progress<commit_after><|endoftext|>


<commit_before>// Copyright 2015 Dorival Pedroso and Raul Durand. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package rnd

import (
	"math"

	"github.com/cpmech/gosl/chk"
)

// DistType indicates the distribution to which a random variable appears to belong to
type DistType int

const (
	D_Nrm DistType = iota + 1
	D_Log
	D_Bet
	D_Gev
)

// VarData implements data defining one random variable
type VarData struct {

	// input
	D     DistType // type of distribution: "nrm", "log", "beta"
	M     float64  // mean-type parameter
	S     float64  // deviation-type parameter
	MSlog bool     // M and S parameters are m and s of log distribution and not μ and σ from standard (default)
	K     float64  // shape parameter
	A     float64  // lower limit
	B     float64  // upper limit
	Q     float64  // q-parameter (e.g. for Beta distribution)
	R     float64  // r-parameter (e.g. for Beta distribution)

	// derived
	distr Distribution // pointer to distribution
	m, s  float64      // auxiliary: parameters of lognormal distribution
}

// CalcEquiv computes equivalent normal parameters at check/design point
func (o *VarData) CalcEquiv(x float64) (μN, σN float64, invalid bool) {
	switch o.D {
	case D_Nrm:
		return o.M, o.S, false
	case D_Log:
		if x < TOLMINLOG {
			//err = chk.Err("x must be ≥ 0 for lognormal distribution. %g is invalid", x)
			invalid = true
			return
		}
		σN = o.s * x
		μN = (1.0 - math.Log(x) + o.m) * x
		if μN < 0 { // TODO: check this
			F := o.distr.Cdf(x)
			z := StdInvPhi(F)
			μN = 0
			σN = x / z
		}
	case D_Gev:
		// using algorithm from:
		//  Rackwitz R, Fiessler B. An algorithm for calculation of structural reliability
		//  under combined loading. Berichte zur Sicherheitstheorie der Bauwerke,
		//  Lab. f. Konstr. Ingb. Munich, Germany; 1977
		F := o.distr.Cdf(x)
		if F == 0 || F == 1 { // z = Φ⁻¹(F) → -∞ or +∞   TODO: check this
			//io.Pfred("cannot compute equivalent normal parameters @ %g because F=%g", x, F)
			μN = 0
			σN = o.S
			return
		}
		f := o.distr.Pdf(x)
		z := StdInvPhi(F)
		σN = Stdphi(z) / f
		μN = x - σN*z
		if μN < 0 { // TODO: check if this is neccessary for all GEVs
			//io.Pfred("warning: fixing μN and σN to avoid μN<0\n")
			μN = 0
			σN = x / z
		}
	default:
		chk.Panic("cannot handle <%v> distribution yet", o.D)
	}
	return
}

// Transform transform x into standard normal space
func (o *VarData) Transform(x float64) (y float64, invalid bool) {
	μ, σ, invalid := o.CalcEquiv(x)
	if invalid {
		return
	}
	y = (x - μ) / σ
	return
}

// InvTransform transform y from standard normal space into original space
func (o *VarData) InvTransform(y float64) (x float64, invalid bool) {
	chk.Panic("InvTransform is not implemented yet")
	return
}

// Variables implements a set of random variables
type Variables []*VarData

// Init initialises distributions in Variables
func (o *Variables) Init() (err error) {
	for _, d := range *o {
		d.distr, err = GetDistrib(d.D)
		if err != nil {
			chk.Err("cannot find distribution:\n%v", err)
			return
		}
		err = d.distr.Init(d)
		if err != nil {
			chk.Err("cannot initialise variables:\n%v", err)
			return
		}
	}
	return
}

// Transform transform all variables into standard normal space
func (o *Variables) Transform(x []float64) (y []float64, invalid bool) {
	y = make([]float64, len(x))
	for i, d := range *o {
		y[i], invalid = d.Transform(x[i])
		if invalid {
			return
		}
	}
	return
}
<commit_msg>check for invalid F added to CalcEquiv<commit_after>// Copyright 2015 Dorival Pedroso and Raul Durand. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package rnd

import (
	"math"

	"github.com/cpmech/gosl/chk"
)

// DistType indicates the distribution to which a random variable appears to belong to
type DistType int

const (
	D_Nrm DistType = iota + 1
	D_Log
	D_Bet
	D_Gev
)

// VarData implements data defining one random variable
type VarData struct {

	// input
	D     DistType // type of distribution: "nrm", "log", "beta"
	M     float64  // mean-type parameter
	S     float64  // deviation-type parameter
	MSlog bool     // M and S parameters are m and s of log distribution and not μ and σ from standard (default)
	K     float64  // shape parameter
	A     float64  // lower limit
	B     float64  // upper limit
	Q     float64  // q-parameter (e.g. for Beta distribution)
	R     float64  // r-parameter (e.g. for Beta distribution)

	// derived
	distr Distribution // pointer to distribution
	m, s  float64      // auxiliary: parameters of lognormal distribution
}

// CalcEquiv computes equivalent normal parameters at check/design point
func (o *VarData) CalcEquiv(x float64) (μN, σN float64, invalid bool) {
	switch o.D {
	case D_Nrm:
		return o.M, o.S, false
	case D_Log:
		if x < TOLMINLOG {
			//err = chk.Err("x must be ≥ 0 for lognormal distribution. %g is invalid", x)
			invalid = true
			return
		}
		σN = o.s * x
		μN = (1.0 - math.Log(x) + o.m) * x
		//io.Pforan("s=%v m=%v σN=%v μN=%v\n", o.s, o.m, σN, μN)
		if μN < 0 { // TODO: check this
			F := o.distr.Cdf(x)
			if F == 0 || F == 1 { // z = Φ⁻¹(F) → -∞ or +∞   TODO: check this
				//io.Pfred("cannot compute equivalent normal parameters @ %g because F=%g", x, F)
				μN = 0
				σN = o.S
				return
			}
			z := StdInvPhi(F)
			μN = 0
			σN = x / z
		}
	case D_Gev:
		// using algorithm from:
		//  Rackwitz R, Fiessler B. An algorithm for calculation of structural reliability
		//  under combined loading. Berichte zur Sicherheitstheorie der Bauwerke,
		//  Lab. f. Konstr. Ingb. Munich, Germany; 1977
		F := o.distr.Cdf(x)
		if F == 0 || F == 1 { // z = Φ⁻¹(F) → -∞ or +∞   TODO: check this
			//io.Pfred("cannot compute equivalent normal parameters @ %g because F=%g", x, F)
			μN = 0
			σN = o.S
			return
		}
		f := o.distr.Pdf(x)
		z := StdInvPhi(F)
		σN = Stdphi(z) / f
		μN = x - σN*z
		if μN < 0 { // TODO: check if this is neccessary for all GEVs
			//io.Pfred("warning: fixing μN and σN to avoid μN<0\n")
			μN = 0
			σN = x / z
		}
	default:
		chk.Panic("cannot handle <%v> distribution yet", o.D)
	}
	return
}

// Transform transform x into standard normal space
func (o *VarData) Transform(x float64) (y float64, invalid bool) {
	μ, σ, invalid := o.CalcEquiv(x)
	if invalid {
		return
	}
	y = (x - μ) / σ
	return
}

// InvTransform transform y from standard normal space into original space
func (o *VarData) InvTransform(y float64) (x float64, invalid bool) {
	chk.Panic("InvTransform is not implemented yet")
	return
}

// Variables implements a set of random variables
type Variables []*VarData

// Init initialises distributions in Variables
func (o *Variables) Init() (err error) {
	for _, d := range *o {
		d.distr, err = GetDistrib(d.D)
		if err != nil {
			chk.Err("cannot find distribution:\n%v", err)
			return
		}
		err = d.distr.Init(d)
		if err != nil {
			chk.Err("cannot initialise variables:\n%v", err)
			return
		}
	}
	return
}

// Transform transform all variables into standard normal space
func (o *Variables) Transform(x []float64) (y []float64, invalid bool) {
	y = make([]float64, len(x))
	for i, d := range *o {
		y[i], invalid = d.Transform(x[i])
		if invalid {
			return
		}
	}
	return
}
<|endoftext|>


<commit_before>// Package alice provides a convenient way to chain http handlers.
package alice

import "net/http"

// A constructor for a piece of middleware.
// Some middleware use this constructor out of the box,
// so in most cases you can just pass somepackage.New
type Constructor func(http.Handler) http.Handler

// Chain acts as a list of http.Handler constructors.
// Chain is effectively immutable:
// once created, it will always hold
// the same set of constructors in the same order.
type Chain struct {
	constructors []Constructor
}

// New creates a new chain,
// memorizing the given list of middleware constructors.
// New serves no other function,
// constructors are only called upon a call to Then().
func New(constructors ...Constructor) Chain {
	return Chain{append(([]Constructor)(nil), constructors...)}
}

// Then chains the middleware and returns the final http.Handler.
//     New(m1, m2, m3).Then(h)
// is equivalent to:
//     m1(m2(m3(h)))
// When the request comes in, it will be passed to m1, then m2, then m3
// and finally, the given handler
// (assuming every middleware calls the following one).
//
// A chain can be safely reused by calling Then() several times.
//     stdStack := alice.New(ratelimitHandler, csrfHandler)
//     indexPipe = stdStack.Then(indexHandler)
//     authPipe = stdStack.Then(authHandler)
// Note that constructors are called on every call to Then()
// and thus several instances of the same middleware will be created
// when a chain is reused in this way.
// For proper middleware, this should cause no problems.
//
// Then() treats nil as http.DefaultServeMux.
func (c Chain) Then(h http.Handler) http.Handler {
	if h == nil {
		h = http.DefaultServeMux
	}

	for i := range c.constructors {
		h = c.constructors[len(c.constructors)-1-i](h)
	}

	return h
}

// ThenFunc works identically to Then, but takes
// a HandlerFunc instead of a Handler.
//
// The following two statements are equivalent:
//     c.Then(http.HandlerFunc(fn))
//     c.ThenFunc(fn)
//
// ThenFunc provides all the guarantees of Then.
func (c Chain) ThenFunc(fn http.HandlerFunc) http.Handler {
	if fn == nil {
		return c.Then(nil)
	}
	return c.Then(fn)
}

// Append extends a chain, adding the specified constructors
// as the last ones in the request flow.
//
// Append returns a new chain, leaving the original one untouched.
//
//     stdChain := alice.New(m1, m2)
//     extChain := stdChain.Append(m3, m4)
//     // requests in stdChain go m1 -> m2
//     // requests in extChain go m1 -> m2 -> m3 -> m4
func (c Chain) Append(constructors ...Constructor) Chain {
	newCons := make([]Constructor, 0, len(c.constructors)+len(constructors))
	newCons = append(newCons, c.constructors...)
	newCons = append(newCons, constructors...)

	return New(newCons...)
}

// Extend extends a chain by adding the specified chain
// as the last one in the request flow.
//
// Extend returns a new chain, leaving the original one untouched.
//
//     stdChain := alice.New(m1, m2)
//     ext1Chain := alice.New(m3, m4)
//     ext2Chain := stdChain.Extend(ext1Chain)
//     // requests in stdChain go  m1 -> m2
//     // requests in ext1Chain go m3 -> m4
//     // requests in ext2Chain go m1 -> m2 -> m3 -> m4
//
// Another example:
//  aHtmlAfterNosurf := alice.New(m2)
// 	aHtml := alice.New(m1, func(h http.Handler) http.Handler {
// 		csrf := nosurf.New(h)
// 		csrf.SetFailureHandler(aHtmlAfterNosurf.ThenFunc(csrfFail))
// 		return csrf
// 	}).Extend(aHtmlAfterNosurf)
//		// requests to aHtml hitting nosurfs success handler go m1 -> nosurf -> m2 -> target-handler
//		// requests to aHtml hitting nosurfs failure handler go m1 -> nosurf -> m2 -> csrfFail
func (c Chain) Extend(chain Chain) Chain {
	return c.Append(chain.constructors...)
}
<commit_msg>avoid unnecessary allocation with New() in Append()<commit_after>// Package alice provides a convenient way to chain http handlers.
package alice

import "net/http"

// A constructor for a piece of middleware.
// Some middleware use this constructor out of the box,
// so in most cases you can just pass somepackage.New
type Constructor func(http.Handler) http.Handler

// Chain acts as a list of http.Handler constructors.
// Chain is effectively immutable:
// once created, it will always hold
// the same set of constructors in the same order.
type Chain struct {
	constructors []Constructor
}

// New creates a new chain,
// memorizing the given list of middleware constructors.
// New serves no other function,
// constructors are only called upon a call to Then().
func New(constructors ...Constructor) Chain {
	return Chain{append(([]Constructor)(nil), constructors...)}
}

// Then chains the middleware and returns the final http.Handler.
//     New(m1, m2, m3).Then(h)
// is equivalent to:
//     m1(m2(m3(h)))
// When the request comes in, it will be passed to m1, then m2, then m3
// and finally, the given handler
// (assuming every middleware calls the following one).
//
// A chain can be safely reused by calling Then() several times.
//     stdStack := alice.New(ratelimitHandler, csrfHandler)
//     indexPipe = stdStack.Then(indexHandler)
//     authPipe = stdStack.Then(authHandler)
// Note that constructors are called on every call to Then()
// and thus several instances of the same middleware will be created
// when a chain is reused in this way.
// For proper middleware, this should cause no problems.
//
// Then() treats nil as http.DefaultServeMux.
func (c Chain) Then(h http.Handler) http.Handler {
	if h == nil {
		h = http.DefaultServeMux
	}

	for i := range c.constructors {
		h = c.constructors[len(c.constructors)-1-i](h)
	}

	return h
}

// ThenFunc works identically to Then, but takes
// a HandlerFunc instead of a Handler.
//
// The following two statements are equivalent:
//     c.Then(http.HandlerFunc(fn))
//     c.ThenFunc(fn)
//
// ThenFunc provides all the guarantees of Then.
func (c Chain) ThenFunc(fn http.HandlerFunc) http.Handler {
	if fn == nil {
		return c.Then(nil)
	}
	return c.Then(fn)
}

// Append extends a chain, adding the specified constructors
// as the last ones in the request flow.
//
// Append returns a new chain, leaving the original one untouched.
//
//     stdChain := alice.New(m1, m2)
//     extChain := stdChain.Append(m3, m4)
//     // requests in stdChain go m1 -> m2
//     // requests in extChain go m1 -> m2 -> m3 -> m4
func (c Chain) Append(constructors ...Constructor) Chain {
	newCons := make([]Constructor, 0, len(c.constructors)+len(constructors))
	newCons = append(newCons, c.constructors...)
	newCons = append(newCons, constructors...)

	return Chain{newCons}
}

// Extend extends a chain by adding the specified chain
// as the last one in the request flow.
//
// Extend returns a new chain, leaving the original one untouched.
//
//     stdChain := alice.New(m1, m2)
//     ext1Chain := alice.New(m3, m4)
//     ext2Chain := stdChain.Extend(ext1Chain)
//     // requests in stdChain go  m1 -> m2
//     // requests in ext1Chain go m3 -> m4
//     // requests in ext2Chain go m1 -> m2 -> m3 -> m4
//
// Another example:
//  aHtmlAfterNosurf := alice.New(m2)
// 	aHtml := alice.New(m1, func(h http.Handler) http.Handler {
// 		csrf := nosurf.New(h)
// 		csrf.SetFailureHandler(aHtmlAfterNosurf.ThenFunc(csrfFail))
// 		return csrf
// 	}).Extend(aHtmlAfterNosurf)
//		// requests to aHtml hitting nosurfs success handler go m1 -> nosurf -> m2 -> target-handler
//		// requests to aHtml hitting nosurfs failure handler go m1 -> nosurf -> m2 -> csrfFail
func (c Chain) Extend(chain Chain) Chain {
	return c.Append(chain.constructors...)
}
<|endoftext|>


<commit_before>// Package alice provides a convenient way to chain http handlers.
package alice

import "net/http"

// A constructor for a piece of middleware.
// Some middleware use this constructor out of the box,
// so in most cases you can just pass somepackage.New
type Constructor func(http.Handler) http.Handler

// Chain acts as a list of http.Handler constructors.
// Chain is effectively immutable:
// once created, it will always hold
// the same set of constructors in the same order.
type Chain struct {
	constructors []Constructor
}

// New creates a new chain,
// memorizing the given list of middleware constructors.
// New serves no other function,
// constructors are only called upon a call to Then().
func New(constructors ...Constructor) Chain {
	return Chain{append(([]Constructor)(nil), constructors...)}
}

// Then chains the middleware and returns the final http.Handler.
//     New(m1, m2, m3).Then(h)
// is equivalent to:
//     m1(m2(m3(h)))
// When the request comes in, it will be passed to m1, then m2, then m3
// and finally, the given handler
// (assuming every middleware calls the following one).
//
// A chain can be safely reused by calling Then() several times.
//     stdStack := alice.New(ratelimitHandler, csrfHandler)
//     indexPipe = stdStack.Then(indexHandler)
//     authPipe = stdStack.Then(authHandler)
// Note that constructors are called on every call to Then()
// and thus several instances of the same middleware will be created
// when a chain is reused in this way.
// For proper middleware, this should cause no problems.
//
// Then() treats nil as http.DefaultServeMux.
func (c Chain) Then(h http.Handler) http.Handler {
	if h == nil {
		h = http.DefaultServeMux
	}

	for i := range c.constructors {
		h = c.constructors[len(c.constructors)-1-i](h)
	}

	return h
}

// ThenFunc works identically to Then, but takes
// a HandlerFunc instead of a Handler.
//
// The following two statements are equivalent:
//     c.Then(http.HandlerFunc(fn))
//     c.ThenFunc(fn)
//
// ThenFunc provides all the guarantees of Then.
func (c Chain) ThenFunc(fn http.HandlerFunc) http.Handler {
	if fn == nil {
		return c.Then(nil)
	}
	return c.Then(fn)
}

// Append extends a chain, adding the specified constructors
// as the last ones in the request flow.
//
// Append returns a new chain, leaving the original one untouched.
//
//     stdChain := alice.New(m1, m2)
//     extChain := stdChain.Append(m3, m4)
//     // requests in stdChain go m1 -> m2
//     // requests in extChain go m1 -> m2 -> m3 -> m4
func (c Chain) Append(constructors ...Constructor) Chain {
	newCons := make([]Constructor, 0, len(c.constructors)+len(constructors))
	newCons = append(newCons, c.constructors...)
	newCons = append(newCons, constructors...)

	return Chain{newCons}
}

// Extend extends a chain by adding the specified chain
// as the last one in the request flow.
//
// Extend returns a new chain, leaving the original one untouched.
//
//     stdChain := alice.New(m1, m2)
//     ext1Chain := alice.New(m3, m4)
//     ext2Chain := stdChain.Extend(ext1Chain)
//     // requests in stdChain go  m1 -> m2
//     // requests in ext1Chain go m3 -> m4
//     // requests in ext2Chain go m1 -> m2 -> m3 -> m4
//
// Another example:
//  aHtmlAfterNosurf := alice.New(m2)
// 	aHtml := alice.New(m1, func(h http.Handler) http.Handler {
// 		csrf := nosurf.New(h)
// 		csrf.SetFailureHandler(aHtmlAfterNosurf.ThenFunc(csrfFail))
// 		return csrf
// 	}).Extend(aHtmlAfterNosurf)
//		// requests to aHtml hitting nosurfs success handler go m1 -> nosurf -> m2 -> target-handler
//		// requests to aHtml hitting nosurfs failure handler go m1 -> nosurf -> m2 -> csrfFail
func (c Chain) Extend(chain Chain) Chain {
	return c.Append(chain.constructors...)
}
<commit_msg>chore: add comment to avoid others not understanding the code<commit_after>// Package alice provides a convenient way to chain http handlers.
package alice

import "net/http"

// A constructor for a piece of middleware.
// Some middleware use this constructor out of the box,
// so in most cases you can just pass somepackage.New
type Constructor func(http.Handler) http.Handler

// Chain acts as a list of http.Handler constructors.
// Chain is effectively immutable:
// once created, it will always hold
// the same set of constructors in the same order.
type Chain struct {
	constructors []Constructor
}

// New creates a new chain,
// memorizing the given list of middleware constructors.
// New serves no other function,
// constructors are only called upon a call to Then().
func New(constructors ...Constructor) Chain {
	return Chain{append(([]Constructor)(nil), constructors...)}
}

// Then chains the middleware and returns the final http.Handler.
//     New(m1, m2, m3).Then(h)
// is equivalent to:
//     m1(m2(m3(h)))
// When the request comes in, it will be passed to m1, then m2, then m3
// and finally, the given handler
// (assuming every middleware calls the following one).
//
// A chain can be safely reused by calling Then() several times.
//     stdStack := alice.New(ratelimitHandler, csrfHandler)
//     indexPipe = stdStack.Then(indexHandler)
//     authPipe = stdStack.Then(authHandler)
// Note that constructors are called on every call to Then()
// and thus several instances of the same middleware will be created
// when a chain is reused in this way.
// For proper middleware, this should cause no problems.
//
// Then() treats nil as http.DefaultServeMux.
func (c Chain) Then(h http.Handler) http.Handler {
	if h == nil {
		h = http.DefaultServeMux
	}

	for i := range c.constructors {
		h = c.constructors[len(c.constructors)-1-i](h)
	}

	return h
}

// ThenFunc works identically to Then, but takes
// a HandlerFunc instead of a Handler.
//
// The following two statements are equivalent:
//     c.Then(http.HandlerFunc(fn))
//     c.ThenFunc(fn)
//
// ThenFunc provides all the guarantees of Then.
func (c Chain) ThenFunc(fn http.HandlerFunc) http.Handler {
	// This nil check cannot be removed due to the "nil is not nil"
	// common mistake in Go.
	if fn == nil {
		return c.Then(nil)
	}
	return c.Then(fn)
}

// Append extends a chain, adding the specified constructors
// as the last ones in the request flow.
//
// Append returns a new chain, leaving the original one untouched.
//
//     stdChain := alice.New(m1, m2)
//     extChain := stdChain.Append(m3, m4)
//     // requests in stdChain go m1 -> m2
//     // requests in extChain go m1 -> m2 -> m3 -> m4
func (c Chain) Append(constructors ...Constructor) Chain {
	newCons := make([]Constructor, 0, len(c.constructors)+len(constructors))
	newCons = append(newCons, c.constructors...)
	newCons = append(newCons, constructors...)

	return Chain{newCons}
}

// Extend extends a chain by adding the specified chain
// as the last one in the request flow.
//
// Extend returns a new chain, leaving the original one untouched.
//
//     stdChain := alice.New(m1, m2)
//     ext1Chain := alice.New(m3, m4)
//     ext2Chain := stdChain.Extend(ext1Chain)
//     // requests in stdChain go  m1 -> m2
//     // requests in ext1Chain go m3 -> m4
//     // requests in ext2Chain go m1 -> m2 -> m3 -> m4
//
// Another example:
//  aHtmlAfterNosurf := alice.New(m2)
// 	aHtml := alice.New(m1, func(h http.Handler) http.Handler {
// 		csrf := nosurf.New(h)
// 		csrf.SetFailureHandler(aHtmlAfterNosurf.ThenFunc(csrfFail))
// 		return csrf
// 	}).Extend(aHtmlAfterNosurf)
//		// requests to aHtml hitting nosurfs success handler go m1 -> nosurf -> m2 -> target-handler
//		// requests to aHtml hitting nosurfs failure handler go m1 -> nosurf -> m2 -> csrfFail
func (c Chain) Extend(chain Chain) Chain {
	return c.Append(chain.constructors...)
}
<|endoftext|>


<commit_before>package main

import (
	"time"

	"github.com/MustWin/baremtlclient"
	"github.com/hashicorp/terraform/helper/schema"
)

type ShapeReader struct {
	resourceData      *schema.ResourceData
	client            BareMetalClient
	shapeListResponse *baremtlsdk.ShapeList
}

func ResourceCoreShape() *schema.Resource {
	return &schema.Resource{
		Read: readShape,
		Schema: map[string]*schema.Schema{
			"shapes": &schema.Schema{
				Type:     schema.TypeList,
				Computed: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},
			"compartment_id": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"availability_domain": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},
			"image_id": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},
		},
	}
}

func readShape(d *schema.ResourceData, m interface{}) (e error) {
	client := m.(BareMetalClient)
	reader := &ShapeReader{
		resourceData: d,
		client:       client,
	}

	if e = readResource(reader); e != nil {
		return
	}

	reader.SetData()

	return
}

func (r *ShapeReader) getOptions() (opts []baremtlsdk.CoreOptions) {
	keys := []string{
		"availability_domain",
		"image_id",
	}

	opts = []baremtlsdk.CoreOptions{}

	for _, key := range keys {
		if val, ok := r.resourceData.GetOk(key); ok {
			if len(opts) == 0 {
				opts = append(opts, baremtlsdk.CoreOptions{})
			}

			switch key {
			case "availability_domain":
				opts[0].AvailabilityDomain = val.(string)
			case "image_id":
				opts[0].ImageID = val.(string)

			}
		}
	}

	return

}

func (r *ShapeReader) Get() (e error) {
	compartmentID := r.resourceData.Get("compartment_id").(string)
	opts := r.getOptions()

	if r.shapeListResponse, e = r.client.ListShapes(compartmentID, opts...); e != nil {
		return
	}

	return
}

func (r *ShapeReader) SetData() {
	if r.shapeListResponse != nil {
		// Important, if you don't have an ID, make one up for your datasource
		// or things will end in tears
		r.resourceData.SetId(time.Now().UTC().String())
		r.resourceData.Set("shapes", r.shapeListResponse.Shapes)
	}
	return
}
<commit_msg>removed redundent SetData call<commit_after>package main

import (
	"time"

	"github.com/MustWin/baremtlclient"
	"github.com/hashicorp/terraform/helper/schema"
)

type ShapeReader struct {
	resourceData      *schema.ResourceData
	client            BareMetalClient
	shapeListResponse *baremtlsdk.ShapeList
}

func ResourceCoreShape() *schema.Resource {
	return &schema.Resource{
		Read: readShape,
		Schema: map[string]*schema.Schema{
			"shapes": &schema.Schema{
				Type:     schema.TypeList,
				Computed: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},
			"compartment_id": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"availability_domain": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},
			"image_id": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},
		},
	}
}

func readShape(d *schema.ResourceData, m interface{}) (e error) {
	client := m.(BareMetalClient)
	reader := &ShapeReader{
		resourceData: d,
		client:       client,
	}

	return readResource(reader)

}

func (r *ShapeReader) getOptions() (opts []baremtlsdk.CoreOptions) {
	keys := []string{
		"availability_domain",
		"image_id",
	}

	opts = []baremtlsdk.CoreOptions{}

	for _, key := range keys {
		if val, ok := r.resourceData.GetOk(key); ok {
			if len(opts) == 0 {
				opts = append(opts, baremtlsdk.CoreOptions{})
			}

			switch key {
			case "availability_domain":
				opts[0].AvailabilityDomain = val.(string)
			case "image_id":
				opts[0].ImageID = val.(string)

			}
		}
	}

	return

}

func (r *ShapeReader) Get() (e error) {
	compartmentID := r.resourceData.Get("compartment_id").(string)
	opts := r.getOptions()

	if r.shapeListResponse, e = r.client.ListShapes(compartmentID, opts...); e != nil {
		return
	}

	return
}

func (r *ShapeReader) SetData() {
	if r.shapeListResponse != nil {
		// Important, if you don't have an ID, make one up for your datasource
		// or things will end in tears
		r.resourceData.SetId(time.Now().UTC().String())
		r.resourceData.Set("shapes", r.shapeListResponse.Shapes)
	}
	return
}
<|endoftext|>


<commit_before>package gcm

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"time"
)

// gcm Client const variables
const (
	GCMClientTimeout = time.Second * 10
	GCMEndpoint      = "https://fcm.googleapis.com/fcm/send"
)

// Client is GCM client
type Client struct {
	apiKey string
	Client *http.Client
}

// Send sends notifications to gcm (TODO: send retry)
func (gc *Client) Send(p Payload) (*Response, error) {
	req, err := gc.NewRequest(p)
	if err != nil {
		return nil, err
	}

	res, err := gc.Client.Do(req)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	resp := &Response{}
	dec := json.NewDecoder(res.Body)
	if err = dec.Decode(&resp.Body); err != nil {
		return nil, err
	}
	resp.Header.StatusCode = res.StatusCode

	if res.StatusCode != http.StatusOK {
		errCode := resp.Body.Error
		eres := ErrorResponse{
			StatusCode: resp.Header.StatusCode,
			ErrCode:    errCode,
		}
		return nil, eres
	}

	return resp, err
}

// NewRequest creates request for gcm
func (gc *Client) NewRequest(p Payload) (*http.Request, error) {
	u, err := url.Parse(GCMEndpoint)
	if err != nil {
		return nil, err
	}

	data, err := json.Marshal(p)
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("POST", u.String(), bytes.NewReader(data))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Authorization", fmt.Sprintf("key=%s", gc.apiKey))
	req.Header.Set("Content-Type", "application/json")

	return req, nil
}

// NewClient establishes a http connection with gcm
func NewClient(apikey string) (*Client, error) {
	client := &http.Client{
		Timeout: GCMClientTimeout,
	}

	return &Client{
		apiKey: apikey,
		Client: client,
	}, nil
}
<commit_msg>not necessary return error value<commit_after>package gcm

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"time"
)

// gcm Client const variables
const (
	GCMClientTimeout = time.Second * 10
	GCMEndpoint      = "https://fcm.googleapis.com/fcm/send"
)

// Client is GCM client
type Client struct {
	apiKey string
	Client *http.Client
}

// Send sends notifications to gcm (TODO: send retry)
func (gc *Client) Send(p Payload) (*Response, error) {
	req, err := gc.NewRequest(p)
	if err != nil {
		return nil, err
	}

	res, err := gc.Client.Do(req)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	resp := &Response{}
	dec := json.NewDecoder(res.Body)
	if err = dec.Decode(&resp.Body); err != nil {
		return nil, err
	}
	resp.Header.StatusCode = res.StatusCode

	if res.StatusCode != http.StatusOK {
		errCode := resp.Body.Error
		eres := ErrorResponse{
			StatusCode: resp.Header.StatusCode,
			ErrCode:    errCode,
		}
		return nil, eres
	}

	return resp, err
}

// NewRequest creates request for gcm
func (gc *Client) NewRequest(p Payload) (*http.Request, error) {
	u, err := url.Parse(GCMEndpoint)
	if err != nil {
		return nil, err
	}

	data, err := json.Marshal(p)
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("POST", u.String(), bytes.NewReader(data))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Authorization", fmt.Sprintf("key=%s", gc.apiKey))
	req.Header.Set("Content-Type", "application/json")

	return req, nil
}

// NewClient establishes a http connection with gcm
func NewClient(apikey string) *Client {
	client := &http.Client{
		Timeout: GCMClientTimeout,
	}

	return &Client{
		apiKey: apikey,
		Client: client,
	}
}
<|endoftext|>