<commit_before>package report

import (
	"fmt"
	"strings"
)

// Topology describes a specific view of a network. It consists of nodes and
// edges, and metadata about those nodes and edges, represented by
// EdgeMetadatas and Nodes respectively. Edges are directional, and embedded
// in the Node struct.
type Topology struct {
	Nodes
}

// MakeTopology gives you a Topology.
func MakeTopology() Topology {
	return Topology{
		Nodes: map[string]Node{},
	}
}

// WithNode produces a topology from t, with nmd added under key nodeID; if a
// node already exists for this key, nmd is merged with that node. Note that a
// fresh topology is returned.
func (t Topology) WithNode(nodeID string, nmd Node) Topology {
	if existing, ok := t.Nodes[nodeID]; ok {
		nmd = nmd.Merge(existing)
	}
	result := t.Copy()
	result.Nodes[nodeID] = nmd
	return result
}

// Copy returns a value copy of the Topology.
func (t Topology) Copy() Topology {
	return Topology{
		Nodes: t.Nodes.Copy(),
	}
}

// Merge merges the other object into this one, and returns the result object.
// The original is not modified.
func (t Topology) Merge(other Topology) Topology {
	return Topology{
		Nodes: t.Nodes.Merge(other.Nodes),
	}
}

// Nodes is a collection of nodes in a topology. Keys are node IDs.
type Nodes map[string]Node

// Copy returns a value copy of the Nodes.
func (n Nodes) Copy() Nodes {
	cp := make(Nodes, len(n))
	for k, v := range n {
		cp[k] = v.Copy()
	}
	return cp
}

// Merge merges the other object into this one, and returns the result object.
// The original is not modified.
func (n Nodes) Merge(other Nodes) Nodes {
	cp := n.Copy()
	for k, v := range other {
		if _, ok := cp[k]; !ok { // don't overwrite
			cp[k] = v.Copy()
		}
	}
	return cp
}

// Node describes a superset of the metadata that probes can collect about a
// given node in a given topology, along with the edges emanating from the
// node and metadata about those edges.
type Node struct {
	Metadata  `json:"-"`
	Counters  `json:"-"`
	Adjacency IDList        `json:"adjacency"`
	Edges     EdgeMetadatas `json:"-"`
}

// MakeNode creates a new Node with no initial metadata.
func MakeNode() Node {
	return Node{
		Metadata:  Metadata{},
		Counters:  Counters{},
		Adjacency: MakeIDList(),
		Edges:     EdgeMetadatas{},
	}
}

// MakeNodeWith creates a new Node with the supplied map.
func MakeNodeWith(m map[string]string) Node {
	return MakeNode().WithMetadata(m)
}

// WithMetadata returns a fresh copy of n, with Metadata set to m
func (n Node) WithMetadata(m map[string]string) Node {
	result := n.Copy()
	result.Metadata = m
	return result
}

// AddMetadata returns a fresh copy of n, with Metadata set to the merge of n
// and the metadata provided.
func (n Node) AddMetadata(m map[string]string) Node {
	additional := MakeNodeWith(m)
	return n.Merge(additional)
}

// WithCounters returns a fresh copy of n, with Counters set to c.
func (n Node) WithCounters(c map[string]int) Node {
	result := n.Copy()
	result.Counters = c
	return result
}

// WithAdjacency returns a fresh copy of n, with Adjacency set to a.
func (n Node) WithAdjacency(a IDList) Node {
	result := n.Copy()
	result.Adjacency = a
	return result
}

// WithAdjacent returns a fresh copy of n, with 'a' added to Adjacency
func (n Node) WithAdjacent(a string) Node {
	result := n.Copy()
	result.Adjacency = result.Adjacency.Add(a)
	return result
}

// WithEdge returns a fresh copy of n, with 'dst' added to Adjacency and md
// added to EdgeMetadata.
func (n Node) WithEdge(dst string, md EdgeMetadata) Node {
	result := n.Copy()
	result.Adjacency = result.Adjacency.Add(dst)
	result.Edges[dst] = md
	return result
}

// Copy returns a value copy of the Node.
func (n Node) Copy() Node {
	cp := MakeNode()
	cp.Metadata = n.Metadata.Copy()
	cp.Counters = n.Counters.Copy()
	cp.Adjacency = n.Adjacency.Copy()
	cp.Edges = n.Edges.Copy()
	return cp
}

// Merge mergses the individual components of a node and returns a
// fresh node.
func (n Node) Merge(other Node) Node {
	cp := n.Copy()
	cp.Metadata = cp.Metadata.Merge(other.Metadata)
	cp.Counters = cp.Counters.Merge(other.Counters)
	cp.Adjacency = cp.Adjacency.Merge(other.Adjacency)
	cp.Edges = cp.Edges.Merge(other.Edges)
	return cp
}

// Metadata is a string->string map.
type Metadata map[string]string

// Merge merges two node metadata maps together. In case of conflict, the
// other (right-hand) side wins. Always reassign the result of merge to the
// destination. Merge does not modify the receiver.
func (m Metadata) Merge(other Metadata) Metadata {
	result := m.Copy()
	for k, v := range other {
		result[k] = v // other takes precedence
	}
	return result
}

// Copy creates a deep copy of the Metadata.
func (m Metadata) Copy() Metadata {
	result := Metadata{}
	for k, v := range m {
		result[k] = v
	}
	return result
}

// Counters is a string->int map.
type Counters map[string]int

// Merge merges two sets of counters into a fresh set of counters, summing
// values where appropriate.
func (c Counters) Merge(other Counters) Counters {
	result := c.Copy()
	for k, v := range other {
		result[k] = result[k] + v
	}
	return result
}

// Copy creates a deep copy of the Counters.
func (c Counters) Copy() Counters {
	result := Counters{}
	for k, v := range c {
		result[k] = v
	}
	return result
}

// EdgeMetadatas collect metadata about each edge in a topology. Keys are the
// remote node IDs, as in Adjacency.
type EdgeMetadatas map[string]EdgeMetadata

// Copy returns a value copy of the EdgeMetadatas.
func (e EdgeMetadatas) Copy() EdgeMetadatas {
	cp := make(EdgeMetadatas, len(e))
	for k, v := range e {
		cp[k] = v.Copy()
	}
	return cp
}

// Merge merges the other object into this one, and returns the result object.
// The original is not modified.
func (e EdgeMetadatas) Merge(other EdgeMetadatas) EdgeMetadatas {
	cp := e.Copy()
	for k, v := range other {
		cp[k] = cp[k].Merge(v)
	}
	return cp
}

// Flatten flattens all the EdgeMetadatas in this set and returns the result.
// The original is not modified.
func (e EdgeMetadatas) Flatten() EdgeMetadata {
	result := EdgeMetadata{}
	for _, v := range e {
		result = result.Flatten(v)
	}
	return result
}

// EdgeMetadata describes a superset of the metadata that probes can possibly
// collect about a directed edge between two nodes in any topology.
type EdgeMetadata struct {
	EgressPacketCount  *uint64 `json:"egress_packet_count,omitempty"`
	IngressPacketCount *uint64 `json:"ingress_packet_count,omitempty"`
	EgressByteCount    *uint64 `json:"egress_byte_count,omitempty"`  // Transport layer
	IngressByteCount   *uint64 `json:"ingress_byte_count,omitempty"` // Transport layer
	MaxConnCountTCP    *uint64 `json:"max_conn_count_tcp,omitempty"`
}

// Copy returns a value copy of the EdgeMetadata.
func (e EdgeMetadata) Copy() EdgeMetadata {
	return EdgeMetadata{
		EgressPacketCount:  cpu64ptr(e.EgressPacketCount),
		IngressPacketCount: cpu64ptr(e.IngressPacketCount),
		EgressByteCount:    cpu64ptr(e.EgressByteCount),
		IngressByteCount:   cpu64ptr(e.IngressByteCount),
		MaxConnCountTCP:    cpu64ptr(e.MaxConnCountTCP),
	}
}

func cpu64ptr(u *uint64) *uint64 {
	if u == nil {
		return nil
	}
	value := *u   // oh man
	return &value // this sucks
}

// Merge merges another EdgeMetadata into the receiver and returns the result.
// The receiver is not modified. The two edge metadatas should represent the
// same edge on different times.
func (e EdgeMetadata) Merge(other EdgeMetadata) EdgeMetadata {
	cp := e.Copy()
	cp.EgressPacketCount = merge(cp.EgressPacketCount, other.EgressPacketCount, sum)
	cp.IngressPacketCount = merge(cp.IngressPacketCount, other.IngressPacketCount, sum)
	cp.EgressByteCount = merge(cp.EgressByteCount, other.EgressByteCount, sum)
	cp.IngressByteCount = merge(cp.IngressByteCount, other.IngressByteCount, sum)
	cp.MaxConnCountTCP = merge(cp.MaxConnCountTCP, other.MaxConnCountTCP, max)
	return cp
}

// Flatten sums two EdgeMetadatas and returns the result. The receiver is not
// modified. The two edge metadata windows should be the same duration; they
// should represent different edges at the same time.
func (e EdgeMetadata) Flatten(other EdgeMetadata) EdgeMetadata {
	cp := e.Copy()
	cp.EgressPacketCount = merge(cp.EgressPacketCount, other.EgressPacketCount, sum)
	cp.IngressPacketCount = merge(cp.IngressPacketCount, other.IngressPacketCount, sum)
	cp.EgressByteCount = merge(cp.EgressByteCount, other.EgressByteCount, sum)
	cp.IngressByteCount = merge(cp.IngressByteCount, other.IngressByteCount, sum)
	// Note that summing of two maximums doesn't always give us the true
	// maximum. But it's a best effort.
	cp.MaxConnCountTCP = merge(cp.MaxConnCountTCP, other.MaxConnCountTCP, sum)
	return cp
}

// Validate checks the topology for various inconsistencies.
func (t Topology) Validate() error {
	errs := []string{}

	// Check all node metadatas are valid, and the keys are parseable, i.e.
	// contain a scope.
	for nodeID, nmd := range t.Nodes {
		if nmd.Metadata == nil {
			errs = append(errs, fmt.Sprintf("node ID %q has nil metadata", nodeID))
		}
		if _, _, ok := ParseNodeID(nodeID); !ok {
			errs = append(errs, fmt.Sprintf("invalid node ID %q", nodeID))
		}

		// Check all adjancency keys has entries in Node.
		for _, dstNodeID := range nmd.Adjacency {
			if _, ok := t.Nodes[dstNodeID]; !ok {
				errs = append(errs, fmt.Sprintf("node metadata missing from adjacency %q -> %q", nodeID, dstNodeID))
			}
		}

		// Check all the edge metadatas have entries in adjacencies
		for dstNodeID := range nmd.Edges {
			if _, ok := t.Nodes[dstNodeID]; !ok {
				errs = append(errs, fmt.Sprintf("node %s metadatas missing for edge %q", dstNodeID, nodeID))
			}
		}
	}

	if len(errs) > 0 {
		return fmt.Errorf("%d error(s): %s", len(errs), strings.Join(errs, "; "))
	}

	return nil
}

func merge(dst, src *uint64, op func(uint64, uint64) uint64) *uint64 {
	if src == nil {
		return dst
	}
	if dst == nil {
		dst = new(uint64)
	}
	(*dst) = op(*dst, *src)
	return dst
}

func sum(dst, src uint64) uint64 {
	return dst + src
}

func max(dst, src uint64) uint64 {
	if dst > src {
		return dst
	}
	return src
}
<commit_msg>Don't omit Node fields from JSON rendering<commit_after>package report

import (
	"fmt"
	"strings"
)

// Topology describes a specific view of a network. It consists of nodes and
// edges, and metadata about those nodes and edges, represented by
// EdgeMetadatas and Nodes respectively. Edges are directional, and embedded
// in the Node struct.
type Topology struct {
	Nodes // TODO(pb): remove Nodes intermediate type
}

// MakeTopology gives you a Topology.
func MakeTopology() Topology {
	return Topology{
		Nodes: map[string]Node{},
	}
}

// WithNode produces a topology from t, with nmd added under key nodeID; if a
// node already exists for this key, nmd is merged with that node. Note that a
// fresh topology is returned.
func (t Topology) WithNode(nodeID string, nmd Node) Topology {
	if existing, ok := t.Nodes[nodeID]; ok {
		nmd = nmd.Merge(existing)
	}
	result := t.Copy()
	result.Nodes[nodeID] = nmd
	return result
}

// Copy returns a value copy of the Topology.
func (t Topology) Copy() Topology {
	return Topology{
		Nodes: t.Nodes.Copy(),
	}
}

// Merge merges the other object into this one, and returns the result object.
// The original is not modified.
func (t Topology) Merge(other Topology) Topology {
	return Topology{
		Nodes: t.Nodes.Merge(other.Nodes),
	}
}

// Nodes is a collection of nodes in a topology. Keys are node IDs.
// TODO(pb): type Topology map[string]Node
type Nodes map[string]Node

// Copy returns a value copy of the Nodes.
func (n Nodes) Copy() Nodes {
	cp := make(Nodes, len(n))
	for k, v := range n {
		cp[k] = v.Copy()
	}
	return cp
}

// Merge merges the other object into this one, and returns the result object.
// The original is not modified.
func (n Nodes) Merge(other Nodes) Nodes {
	cp := n.Copy()
	for k, v := range other {
		if _, ok := cp[k]; !ok { // don't overwrite
			cp[k] = v.Copy()
		}
	}
	return cp
}

// Node describes a superset of the metadata that probes can collect about a
// given node in a given topology, along with the edges emanating from the
// node and metadata about those edges.
type Node struct {
	Metadata  `json:"metadata"`
	Counters  `json:"counters"`
	Adjacency IDList        `json:"adjacency"`
	Edges     EdgeMetadatas `json:"edges"`
}

// MakeNode creates a new Node with no initial metadata.
func MakeNode() Node {
	return Node{
		Metadata:  Metadata{},
		Counters:  Counters{},
		Adjacency: MakeIDList(),
		Edges:     EdgeMetadatas{},
	}
}

// MakeNodeWith creates a new Node with the supplied map.
func MakeNodeWith(m map[string]string) Node {
	return MakeNode().WithMetadata(m)
}

// WithMetadata returns a fresh copy of n, with Metadata set to m
func (n Node) WithMetadata(m map[string]string) Node {
	result := n.Copy()
	result.Metadata = m
	return result
}

// AddMetadata returns a fresh copy of n, with Metadata set to the merge of n
// and the metadata provided.
func (n Node) AddMetadata(m map[string]string) Node {
	additional := MakeNodeWith(m)
	return n.Merge(additional)
}

// WithCounters returns a fresh copy of n, with Counters set to c.
func (n Node) WithCounters(c map[string]int) Node {
	result := n.Copy()
	result.Counters = c
	return result
}

// WithAdjacency returns a fresh copy of n, with Adjacency set to a.
func (n Node) WithAdjacency(a IDList) Node {
	result := n.Copy()
	result.Adjacency = a
	return result
}

// WithAdjacent returns a fresh copy of n, with 'a' added to Adjacency
func (n Node) WithAdjacent(a string) Node {
	result := n.Copy()
	result.Adjacency = result.Adjacency.Add(a)
	return result
}

// WithEdge returns a fresh copy of n, with 'dst' added to Adjacency and md
// added to EdgeMetadata.
func (n Node) WithEdge(dst string, md EdgeMetadata) Node {
	result := n.Copy()
	result.Adjacency = result.Adjacency.Add(dst)
	result.Edges[dst] = md
	return result
}

// Copy returns a value copy of the Node.
func (n Node) Copy() Node {
	cp := MakeNode()
	cp.Metadata = n.Metadata.Copy()
	cp.Counters = n.Counters.Copy()
	cp.Adjacency = n.Adjacency.Copy()
	cp.Edges = n.Edges.Copy()
	return cp
}

// Merge mergses the individual components of a node and returns a
// fresh node.
func (n Node) Merge(other Node) Node {
	cp := n.Copy()
	cp.Metadata = cp.Metadata.Merge(other.Metadata)
	cp.Counters = cp.Counters.Merge(other.Counters)
	cp.Adjacency = cp.Adjacency.Merge(other.Adjacency)
	cp.Edges = cp.Edges.Merge(other.Edges)
	return cp
}

// Metadata is a string->string map.
type Metadata map[string]string

// Merge merges two node metadata maps together. In case of conflict, the
// other (right-hand) side wins. Always reassign the result of merge to the
// destination. Merge does not modify the receiver.
func (m Metadata) Merge(other Metadata) Metadata {
	result := m.Copy()
	for k, v := range other {
		result[k] = v // other takes precedence
	}
	return result
}

// Copy creates a deep copy of the Metadata.
func (m Metadata) Copy() Metadata {
	result := Metadata{}
	for k, v := range m {
		result[k] = v
	}
	return result
}

// Counters is a string->int map.
type Counters map[string]int

// Merge merges two sets of counters into a fresh set of counters, summing
// values where appropriate.
func (c Counters) Merge(other Counters) Counters {
	result := c.Copy()
	for k, v := range other {
		result[k] = result[k] + v
	}
	return result
}

// Copy creates a deep copy of the Counters.
func (c Counters) Copy() Counters {
	result := Counters{}
	for k, v := range c {
		result[k] = v
	}
	return result
}

// EdgeMetadatas collect metadata about each edge in a topology. Keys are the
// remote node IDs, as in Adjacency.
type EdgeMetadatas map[string]EdgeMetadata

// Copy returns a value copy of the EdgeMetadatas.
func (e EdgeMetadatas) Copy() EdgeMetadatas {
	cp := make(EdgeMetadatas, len(e))
	for k, v := range e {
		cp[k] = v.Copy()
	}
	return cp
}

// Merge merges the other object into this one, and returns the result object.
// The original is not modified.
func (e EdgeMetadatas) Merge(other EdgeMetadatas) EdgeMetadatas {
	cp := e.Copy()
	for k, v := range other {
		cp[k] = cp[k].Merge(v)
	}
	return cp
}

// Flatten flattens all the EdgeMetadatas in this set and returns the result.
// The original is not modified.
func (e EdgeMetadatas) Flatten() EdgeMetadata {
	result := EdgeMetadata{}
	for _, v := range e {
		result = result.Flatten(v)
	}
	return result
}

// EdgeMetadata describes a superset of the metadata that probes can possibly
// collect about a directed edge between two nodes in any topology.
type EdgeMetadata struct {
	EgressPacketCount  *uint64 `json:"egress_packet_count,omitempty"`
	IngressPacketCount *uint64 `json:"ingress_packet_count,omitempty"`
	EgressByteCount    *uint64 `json:"egress_byte_count,omitempty"`  // Transport layer
	IngressByteCount   *uint64 `json:"ingress_byte_count,omitempty"` // Transport layer
	MaxConnCountTCP    *uint64 `json:"max_conn_count_tcp,omitempty"`
}

// Copy returns a value copy of the EdgeMetadata.
func (e EdgeMetadata) Copy() EdgeMetadata {
	return EdgeMetadata{
		EgressPacketCount:  cpu64ptr(e.EgressPacketCount),
		IngressPacketCount: cpu64ptr(e.IngressPacketCount),
		EgressByteCount:    cpu64ptr(e.EgressByteCount),
		IngressByteCount:   cpu64ptr(e.IngressByteCount),
		MaxConnCountTCP:    cpu64ptr(e.MaxConnCountTCP),
	}
}

func cpu64ptr(u *uint64) *uint64 {
	if u == nil {
		return nil
	}
	value := *u   // oh man
	return &value // this sucks
}

// Merge merges another EdgeMetadata into the receiver and returns the result.
// The receiver is not modified. The two edge metadatas should represent the
// same edge on different times.
func (e EdgeMetadata) Merge(other EdgeMetadata) EdgeMetadata {
	cp := e.Copy()
	cp.EgressPacketCount = merge(cp.EgressPacketCount, other.EgressPacketCount, sum)
	cp.IngressPacketCount = merge(cp.IngressPacketCount, other.IngressPacketCount, sum)
	cp.EgressByteCount = merge(cp.EgressByteCount, other.EgressByteCount, sum)
	cp.IngressByteCount = merge(cp.IngressByteCount, other.IngressByteCount, sum)
	cp.MaxConnCountTCP = merge(cp.MaxConnCountTCP, other.MaxConnCountTCP, max)
	return cp
}

// Flatten sums two EdgeMetadatas and returns the result. The receiver is not
// modified. The two edge metadata windows should be the same duration; they
// should represent different edges at the same time.
func (e EdgeMetadata) Flatten(other EdgeMetadata) EdgeMetadata {
	cp := e.Copy()
	cp.EgressPacketCount = merge(cp.EgressPacketCount, other.EgressPacketCount, sum)
	cp.IngressPacketCount = merge(cp.IngressPacketCount, other.IngressPacketCount, sum)
	cp.EgressByteCount = merge(cp.EgressByteCount, other.EgressByteCount, sum)
	cp.IngressByteCount = merge(cp.IngressByteCount, other.IngressByteCount, sum)
	// Note that summing of two maximums doesn't always give us the true
	// maximum. But it's a best effort.
	cp.MaxConnCountTCP = merge(cp.MaxConnCountTCP, other.MaxConnCountTCP, sum)
	return cp
}

// Validate checks the topology for various inconsistencies.
func (t Topology) Validate() error {
	errs := []string{}

	// Check all node metadatas are valid, and the keys are parseable, i.e.
	// contain a scope.
	for nodeID, nmd := range t.Nodes {
		if nmd.Metadata == nil {
			errs = append(errs, fmt.Sprintf("node ID %q has nil metadata", nodeID))
		}
		if _, _, ok := ParseNodeID(nodeID); !ok {
			errs = append(errs, fmt.Sprintf("invalid node ID %q", nodeID))
		}

		// Check all adjancency keys has entries in Node.
		for _, dstNodeID := range nmd.Adjacency {
			if _, ok := t.Nodes[dstNodeID]; !ok {
				errs = append(errs, fmt.Sprintf("node metadata missing from adjacency %q -> %q", nodeID, dstNodeID))
			}
		}

		// Check all the edge metadatas have entries in adjacencies
		for dstNodeID := range nmd.Edges {
			if _, ok := t.Nodes[dstNodeID]; !ok {
				errs = append(errs, fmt.Sprintf("node %s metadatas missing for edge %q", dstNodeID, nodeID))
			}
		}
	}

	if len(errs) > 0 {
		return fmt.Errorf("%d error(s): %s", len(errs), strings.Join(errs, "; "))
	}

	return nil
}

func merge(dst, src *uint64, op func(uint64, uint64) uint64) *uint64 {
	if src == nil {
		return dst
	}
	if dst == nil {
		dst = new(uint64)
	}
	(*dst) = op(*dst, *src)
	return dst
}

func sum(dst, src uint64) uint64 {
	return dst + src
}

func max(dst, src uint64) uint64 {
	if dst > src {
		return dst
	}
	return src
}
<|endoftext|>


<commit_before>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package mfg

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

	"mynewt.apache.org/newt/newt/builder"
	"mynewt.apache.org/newt/newt/flash"
	"mynewt.apache.org/newt/newt/pkg"
	"mynewt.apache.org/newt/newt/target"
	"mynewt.apache.org/newt/util"
)

type mfgManifest struct {
	BuildTime string `json:"build_time"`
	MfgHash   string `json:"mfg_hash"`
}

func insertPartIntoBlob(blob []byte, part mfgPart) {
	partEnd := part.offset + len(part.data)

	if len(blob) < partEnd {
		panic("internal error; mfg blob too small")
	}

	copy(blob[part.offset:partEnd], part.data)
}

func (mi *MfgImage) partFromImage(
	imgPath string, flashAreaName string) (mfgPart, error) {

	part := mfgPart{}

	area, ok := mi.bsp.FlashMap.Areas[flashAreaName]
	if !ok {
		return part, util.FmtNewtError(
			"Image at \"%s\" requires undefined flash area \"%s\"",
			imgPath, flashAreaName)
	}

	part.name = fmt.Sprintf("%s (%s)", flashAreaName, filepath.Base(imgPath))
	part.offset = area.Offset

	var err error

	part.data, err = ioutil.ReadFile(imgPath)
	if err != nil {
		return part, util.ChildNewtError(err)
	}

	overflow := len(part.data) - area.Size
	if overflow > 0 {
		return part, util.FmtNewtError(
			"Image \"%s\" is too large to fit in flash area \"%s\"; "+
				"image-size=%d flash-area-size=%d overflow=%d",
			imgPath, flashAreaName, len(part.data), area.Size, overflow)
	}

	return part, nil
}

func (mi *MfgImage) section0Size() int {
	greatest := 0

	bootArea := mi.bsp.FlashMap.Areas[flash.FLASH_AREA_NAME_BOOTLOADER]
	image0Area := mi.bsp.FlashMap.Areas[flash.FLASH_AREA_NAME_IMAGE_0]
	image1Area := mi.bsp.FlashMap.Areas[flash.FLASH_AREA_NAME_IMAGE_1]

	if mi.boot != nil {
		greatest = util.IntMax(greatest, bootArea.Offset+bootArea.Size)
	}
	if len(mi.images) >= 1 {
		greatest = util.IntMax(greatest, image0Area.Offset+image0Area.Size)
	}
	if len(mi.images) >= 2 {
		greatest = util.IntMax(greatest, image1Area.Offset+image1Area.Size)
	}

	for _, section := range mi.rawSections {
		greatest = util.IntMax(greatest, section.offset+len(section.data))
	}

	return greatest
}

// @return						section-0-blob, hash-offset, error
func (mi *MfgImage) section0Data(parts []mfgPart) ([]byte, int, error) {
	blobSize := mi.section0Size()
	blob := make([]byte, blobSize)

	// Initialize section 0's data as unwritten flash (0xff).
	for i, _ := range blob {
		blob[i] = 0xff
	}

	for _, part := range parts {
		insertPartIntoBlob(blob, part)
	}

	hashOffset, err := insertMeta(blob, mi.bsp.FlashMap)
	if err != nil {
		return nil, 0, err
	}

	return blob, hashOffset, nil

}

func createImageHeader(hashOffset int) ([]byte, error) {
	buf := &bytes.Buffer{}

	hdr := mfgImageHeader{
		Version:    uint8(MFG_IMAGE_VERSION),
		HashOffset: uint32(hashOffset),
	}
	if err := binary.Write(buf, binary.BigEndian, hdr); err != nil {
		return nil, util.ChildNewtError(err)
	}

	return buf.Bytes(), nil
}

func createSectionHeader(deviceId int, offset int, size int) ([]byte, error) {
	buf := &bytes.Buffer{}

	sectionHdr := mfgImageSectionHeader{
		DeviceId: uint8(deviceId),
		Offset:   uint32(offset),
		Size:     uint32(size),
	}
	if err := binary.Write(buf, binary.BigEndian, sectionHdr); err != nil {
		return nil, util.ChildNewtError(err)
	}

	return buf.Bytes(), nil
}

// @return						[section0blob, section1blob,...], hash, err
func (mi *MfgImage) createDeviceSections(parts []mfgPart) (
	[][]byte, []byte, error) {

	section0Data, hashOff, err := mi.section0Data(parts)
	if err != nil {
		return nil, nil, err
	}

	// XXX: Append additional flash device sections.

	// Calculate manufacturing has.
	sections := [][]byte{section0Data}
	hash := calcMetaHash(sections)

	// Write hash to meta region in section 0.
	copy(section0Data[hashOff:hashOff+META_HASH_SZ], hash)

	return sections, hash, nil
}

func areaNameFromImgIdx(imgIdx int) (string, error) {
	switch imgIdx {
	case 0:
		return flash.FLASH_AREA_NAME_IMAGE_0, nil
	case 1:
		return flash.FLASH_AREA_NAME_IMAGE_1, nil
	default:
		return "", util.FmtNewtError("invalid image index: %d", imgIdx)
	}
}

func (mi *MfgImage) rawSectionParts() []mfgPart {
	parts := make([]mfgPart, len(mi.rawSections))
	for i, section := range mi.rawSections {
		parts[i].name = fmt.Sprintf("section-%d (%s)", i, section.filename)
		parts[i].offset = section.offset
		parts[i].data = section.data
	}

	return parts
}

func bootLoaderBinPaths(t *target.Target) []string {
	return []string{
		/* boot.elf */
		builder.AppElfPath(t.Name(), builder.BUILD_NAME_APP, t.App().Name()),

		/* boot.elf.bin */
		builder.AppBinPath(t.Name(), builder.BUILD_NAME_APP, t.App().Name()),

		/* manifest.json */
		builder.ManifestPath(t.Name(), builder.BUILD_NAME_APP, t.App().Name()),
	}
}

func loaderBinPaths(t *target.Target) []string {
	if t.LoaderName == "" {
		return nil
	}

	return []string{
		/* <loader>.elf */
		builder.AppElfPath(t.Name(), builder.BUILD_NAME_LOADER,
			t.Loader().Name()),
	}
}

func appBinPaths(t *target.Target) []string {
	return []string{
		/* <app>.elf */
		builder.AppElfPath(t.Name(), builder.BUILD_NAME_APP, t.App().Name()),

		/* <app>.img */
		builder.AppImgPath(t.Name(), builder.BUILD_NAME_APP, t.App().Name()),

		/* manifest.json */
		builder.ManifestPath(t.Name(), builder.BUILD_NAME_APP, t.App().Name()),
	}
}

func imageBinPaths(t *target.Target) []string {
	paths := loaderBinPaths(t)
	paths = append(paths, appBinPaths(t)...)
	return paths
}

func (mi *MfgImage) copyBinFile(srcPath string, dstDir string) error {
	dstPath := dstDir + "/" + filepath.Base(srcPath)

	util.StatusMessage(util.VERBOSITY_VERBOSE, "copying file %s --> %s\n",
		srcPath, dstPath)

	if err := util.CopyFile(srcPath, dstPath); err != nil {
		return err
	}

	return nil
}

func (mi *MfgImage) copyBinFiles() error {
	dstPath := builder.MfgBinDir(mi.basePkg.Name())
	if err := os.MkdirAll(filepath.Dir(dstPath), 0755); err != nil {
		return util.ChildNewtError(err)
	}

	bootPaths := bootLoaderBinPaths(mi.boot)
	for _, path := range bootPaths {
		dstDir := builder.MfgBinBootDir(mi.basePkg.Name())
		if err := mi.copyBinFile(path, dstDir); err != nil {
			return err
		}
	}

	for i, imgTarget := range mi.images {
		imgPaths := imageBinPaths(imgTarget)
		dstDir := builder.MfgBinImageDir(mi.basePkg.Name(), i)
		for _, path := range imgPaths {
			if err := mi.copyBinFile(path, dstDir); err != nil {
				return err
			}
		}
	}

	return nil
}

func (mi *MfgImage) dstBootBinPath() string {
	if mi.boot == nil {
		return ""
	}

	return fmt.Sprintf("%s/%s.elf.bin",
		builder.MfgBinBootDir(mi.basePkg.Name()),
		pkg.ShortName(mi.boot.App()))
}

func (mi *MfgImage) dstImgPath(imgIdx int) string {
	var pack *pkg.LocalPackage

	if len(mi.images) >= 1 {
		switch imgIdx {
		case 0:
			if mi.images[0].LoaderName != "" {
				pack = mi.images[0].Loader()
			} else {
				pack = mi.images[0].App()
			}

		case 1:
			if mi.images[0].LoaderName != "" {
				pack = mi.images[0].App()
			} else {
				if len(mi.images) >= 2 {
					pack = mi.images[1].App()
				}
			}

		default:
			panic(fmt.Sprintf("invalid image index: %d", imgIdx))
		}
	}

	if pack == nil {
		return ""
	}

	return fmt.Sprintf("%s/%s.img",
		builder.MfgBinImageDir(mi.basePkg.Name(), imgIdx), pkg.ShortName(pack))
}

func (mi *MfgImage) targetParts() ([]mfgPart, error) {
	parts := []mfgPart{}

	bootPath := mi.dstBootBinPath()
	if bootPath != "" {
		bootPart, err := mi.partFromImage(
			bootPath, flash.FLASH_AREA_NAME_BOOTLOADER)
		if err != nil {
			return nil, err
		}

		parts = append(parts, bootPart)
	}

	for i := 0; i < 2; i++ {
		imgPath := mi.dstImgPath(i)
		if imgPath != "" {
			areaName, err := areaNameFromImgIdx(i)
			if err != nil {
				return nil, err
			}

			part, err := mi.partFromImage(imgPath, areaName)
			if err != nil {
				return nil, err
			}
			parts = append(parts, part)
		}
	}

	return parts, nil
}

// Returns a slice containing the path of each file required to build the
// manufacturing image.
func (mi *MfgImage) SrcPaths() []string {
	paths := []string{}

	if mi.boot != nil {
		paths = append(paths, bootLoaderBinPaths(mi.boot)...)
	}
	if len(mi.images) >= 1 {
		paths = append(paths, imageBinPaths(mi.images[0])...)
	}
	if len(mi.images) >= 2 {
		paths = append(paths, imageBinPaths(mi.images[1])...)
	}

	for _, raw := range mi.rawSections {
		paths = append(paths, raw.filename)
	}

	return paths
}

// @return						[section0blob, section1blob,...], hash, err
func (mi *MfgImage) build() ([][]byte, []byte, error) {
	if err := mi.copyBinFiles(); err != nil {
		return nil, nil, err
	}

	targetParts, err := mi.targetParts()
	if err != nil {
		return nil, nil, err
	}

	rawParts := mi.rawSectionParts()

	parts := append(targetParts, rawParts...)
	sortParts(parts)

	deviceSections, hash, err := mi.createDeviceSections(parts)
	if err != nil {
		return nil, nil, err
	}

	return deviceSections, hash, nil
}

func (mi *MfgImage) createManifest(hash []byte) ([]byte, error) {
	manifest := mfgManifest{
		BuildTime: time.Now().Format(time.RFC3339),
		MfgHash:   fmt.Sprintf("%x", hash),
	}
	buffer, err := json.MarshalIndent(manifest, "", "  ")
	if err != nil {
		return nil, util.FmtNewtError("Failed to encode mfg manifest: %s",
			err.Error())
	}

	return buffer, nil
}

// @return                      [paths-of-sections], error
func (mi *MfgImage) CreateMfgImage() ([]string, error) {
	sections, hash, err := mi.build()
	if err != nil {
		return nil, err
	}

	sectionDir := builder.MfgSectionDir(mi.basePkg.Name())
	if err := os.MkdirAll(sectionDir, 0755); err != nil {
		return nil, util.ChildNewtError(err)
	}

	sectionPaths := make([]string, len(sections))
	for i, section := range sections {
		sectionPath := builder.MfgSectionPath(mi.basePkg.Name(), i)
		if err := ioutil.WriteFile(sectionPath, section, 0644); err != nil {
			return nil, util.ChildNewtError(err)
		}
		sectionPaths[i] = sectionPath
	}

	manifest, err := mi.createManifest(hash)
	if err != nil {
		return nil, err
	}

	manifestPath := builder.MfgManifestPath(mi.basePkg.Name())
	if err := ioutil.WriteFile(manifestPath, manifest, 0644); err != nil {
		return nil, util.FmtNewtError("Failed to write mfg manifest file: %s",
			err.Error())
	}

	return sectionPaths, nil
}
<commit_msg>mfg - loader and app are both in image0 dir.<commit_after>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package mfg

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

	"mynewt.apache.org/newt/newt/builder"
	"mynewt.apache.org/newt/newt/flash"
	"mynewt.apache.org/newt/newt/pkg"
	"mynewt.apache.org/newt/newt/target"
	"mynewt.apache.org/newt/util"
)

type mfgManifest struct {
	BuildTime string `json:"build_time"`
	MfgHash   string `json:"mfg_hash"`
}

func insertPartIntoBlob(blob []byte, part mfgPart) {
	partEnd := part.offset + len(part.data)

	if len(blob) < partEnd {
		panic("internal error; mfg blob too small")
	}

	copy(blob[part.offset:partEnd], part.data)
}

func (mi *MfgImage) partFromImage(
	imgPath string, flashAreaName string) (mfgPart, error) {

	part := mfgPart{}

	area, ok := mi.bsp.FlashMap.Areas[flashAreaName]
	if !ok {
		return part, util.FmtNewtError(
			"Image at \"%s\" requires undefined flash area \"%s\"",
			imgPath, flashAreaName)
	}

	part.name = fmt.Sprintf("%s (%s)", flashAreaName, filepath.Base(imgPath))
	part.offset = area.Offset

	var err error

	part.data, err = ioutil.ReadFile(imgPath)
	if err != nil {
		return part, util.ChildNewtError(err)
	}

	overflow := len(part.data) - area.Size
	if overflow > 0 {
		return part, util.FmtNewtError(
			"Image \"%s\" is too large to fit in flash area \"%s\"; "+
				"image-size=%d flash-area-size=%d overflow=%d",
			imgPath, flashAreaName, len(part.data), area.Size, overflow)
	}

	return part, nil
}

func (mi *MfgImage) section0Size() int {
	greatest := 0

	bootArea := mi.bsp.FlashMap.Areas[flash.FLASH_AREA_NAME_BOOTLOADER]
	image0Area := mi.bsp.FlashMap.Areas[flash.FLASH_AREA_NAME_IMAGE_0]
	image1Area := mi.bsp.FlashMap.Areas[flash.FLASH_AREA_NAME_IMAGE_1]

	if mi.boot != nil {
		greatest = util.IntMax(greatest, bootArea.Offset+bootArea.Size)
	}
	if len(mi.images) >= 1 {
		greatest = util.IntMax(greatest, image0Area.Offset+image0Area.Size)
	}
	if len(mi.images) >= 2 {
		greatest = util.IntMax(greatest, image1Area.Offset+image1Area.Size)
	}

	for _, section := range mi.rawSections {
		greatest = util.IntMax(greatest, section.offset+len(section.data))
	}

	return greatest
}

// @return						section-0-blob, hash-offset, error
func (mi *MfgImage) section0Data(parts []mfgPart) ([]byte, int, error) {
	blobSize := mi.section0Size()
	blob := make([]byte, blobSize)

	// Initialize section 0's data as unwritten flash (0xff).
	for i, _ := range blob {
		blob[i] = 0xff
	}

	for _, part := range parts {
		insertPartIntoBlob(blob, part)
	}

	hashOffset, err := insertMeta(blob, mi.bsp.FlashMap)
	if err != nil {
		return nil, 0, err
	}

	return blob, hashOffset, nil

}

func createImageHeader(hashOffset int) ([]byte, error) {
	buf := &bytes.Buffer{}

	hdr := mfgImageHeader{
		Version:    uint8(MFG_IMAGE_VERSION),
		HashOffset: uint32(hashOffset),
	}
	if err := binary.Write(buf, binary.BigEndian, hdr); err != nil {
		return nil, util.ChildNewtError(err)
	}

	return buf.Bytes(), nil
}

func createSectionHeader(deviceId int, offset int, size int) ([]byte, error) {
	buf := &bytes.Buffer{}

	sectionHdr := mfgImageSectionHeader{
		DeviceId: uint8(deviceId),
		Offset:   uint32(offset),
		Size:     uint32(size),
	}
	if err := binary.Write(buf, binary.BigEndian, sectionHdr); err != nil {
		return nil, util.ChildNewtError(err)
	}

	return buf.Bytes(), nil
}

// @return						[section0blob, section1blob,...], hash, err
func (mi *MfgImage) createDeviceSections(parts []mfgPart) (
	[][]byte, []byte, error) {

	section0Data, hashOff, err := mi.section0Data(parts)
	if err != nil {
		return nil, nil, err
	}

	// XXX: Append additional flash device sections.

	// Calculate manufacturing has.
	sections := [][]byte{section0Data}
	hash := calcMetaHash(sections)

	// Write hash to meta region in section 0.
	copy(section0Data[hashOff:hashOff+META_HASH_SZ], hash)

	return sections, hash, nil
}

func areaNameFromImgIdx(imgIdx int) (string, error) {
	switch imgIdx {
	case 0:
		return flash.FLASH_AREA_NAME_IMAGE_0, nil
	case 1:
		return flash.FLASH_AREA_NAME_IMAGE_1, nil
	default:
		return "", util.FmtNewtError("invalid image index: %d", imgIdx)
	}
}

func (mi *MfgImage) rawSectionParts() []mfgPart {
	parts := make([]mfgPart, len(mi.rawSections))
	for i, section := range mi.rawSections {
		parts[i].name = fmt.Sprintf("section-%d (%s)", i, section.filename)
		parts[i].offset = section.offset
		parts[i].data = section.data
	}

	return parts
}

func bootLoaderBinPaths(t *target.Target) []string {
	return []string{
		/* boot.elf */
		builder.AppElfPath(t.Name(), builder.BUILD_NAME_APP, t.App().Name()),

		/* boot.elf.bin */
		builder.AppBinPath(t.Name(), builder.BUILD_NAME_APP, t.App().Name()),

		/* manifest.json */
		builder.ManifestPath(t.Name(), builder.BUILD_NAME_APP, t.App().Name()),
	}
}

func loaderBinPaths(t *target.Target) []string {
	if t.LoaderName == "" {
		return nil
	}

	return []string{
		/* <loader>.elf */
		builder.AppElfPath(t.Name(), builder.BUILD_NAME_LOADER,
			t.Loader().Name()),

		/* <app>.img */
		builder.AppImgPath(t.Name(), builder.BUILD_NAME_LOADER,
			t.Loader().Name()),
	}
}

func appBinPaths(t *target.Target) []string {
	return []string{
		/* <app>.elf */
		builder.AppElfPath(t.Name(), builder.BUILD_NAME_APP, t.App().Name()),

		/* <app>.img */
		builder.AppImgPath(t.Name(), builder.BUILD_NAME_APP, t.App().Name()),

		/* manifest.json */
		builder.ManifestPath(t.Name(), builder.BUILD_NAME_APP, t.App().Name()),
	}
}

func imageBinPaths(t *target.Target) []string {
	paths := loaderBinPaths(t)
	paths = append(paths, appBinPaths(t)...)
	return paths
}

func (mi *MfgImage) copyBinFile(srcPath string, dstDir string) error {
	dstPath := dstDir + "/" + filepath.Base(srcPath)

	util.StatusMessage(util.VERBOSITY_VERBOSE, "copying file %s --> %s\n",
		srcPath, dstPath)

	if err := util.CopyFile(srcPath, dstPath); err != nil {
		return err
	}

	return nil
}

func (mi *MfgImage) copyBinFiles() error {
	dstPath := builder.MfgBinDir(mi.basePkg.Name())
	if err := os.MkdirAll(filepath.Dir(dstPath), 0755); err != nil {
		return util.ChildNewtError(err)
	}

	bootPaths := bootLoaderBinPaths(mi.boot)
	for _, path := range bootPaths {
		dstDir := builder.MfgBinBootDir(mi.basePkg.Name())
		if err := mi.copyBinFile(path, dstDir); err != nil {
			return err
		}
	}

	for i, imgTarget := range mi.images {
		imgPaths := imageBinPaths(imgTarget)
		dstDir := builder.MfgBinImageDir(mi.basePkg.Name(), i)
		for _, path := range imgPaths {
			if err := mi.copyBinFile(path, dstDir); err != nil {
				return err
			}
		}
	}

	return nil
}

func (mi *MfgImage) dstBootBinPath() string {
	if mi.boot == nil {
		return ""
	}

	return fmt.Sprintf("%s/%s.elf.bin",
		builder.MfgBinBootDir(mi.basePkg.Name()),
		pkg.ShortName(mi.boot.App()))
}

func (mi *MfgImage) dstImgPath(slotIdx int) string {
	var pack *pkg.LocalPackage
	var imgIdx int

	if len(mi.images) >= 1 {
		switch slotIdx {
		case 0:
			if mi.images[0].LoaderName != "" {
				pack = mi.images[0].Loader()
			} else {
				pack = mi.images[0].App()
			}
			imgIdx = 0

		case 1:
			if mi.images[0].LoaderName != "" {
				pack = mi.images[0].App()
				imgIdx = 0
			} else {
				if len(mi.images) >= 2 {
					pack = mi.images[1].App()
				}
				imgIdx = 1
			}

		default:
			panic(fmt.Sprintf("invalid image index: %d", imgIdx))
		}
	}

	if pack == nil {
		return ""
	}

	return fmt.Sprintf("%s/%s.img",
		builder.MfgBinImageDir(mi.basePkg.Name(), imgIdx), pkg.ShortName(pack))
}

func (mi *MfgImage) targetParts() ([]mfgPart, error) {
	parts := []mfgPart{}

	bootPath := mi.dstBootBinPath()
	if bootPath != "" {
		bootPart, err := mi.partFromImage(
			bootPath, flash.FLASH_AREA_NAME_BOOTLOADER)
		if err != nil {
			return nil, err
		}

		parts = append(parts, bootPart)
	}

	for i := 0; i < 2; i++ {
		imgPath := mi.dstImgPath(i)
		if imgPath != "" {
			areaName, err := areaNameFromImgIdx(i)
			if err != nil {
				return nil, err
			}

			part, err := mi.partFromImage(imgPath, areaName)
			if err != nil {
				return nil, err
			}
			parts = append(parts, part)
		}
	}

	return parts, nil
}

// Returns a slice containing the path of each file required to build the
// manufacturing image.
func (mi *MfgImage) SrcPaths() []string {
	paths := []string{}

	if mi.boot != nil {
		paths = append(paths, bootLoaderBinPaths(mi.boot)...)
	}
	if len(mi.images) >= 1 {
		paths = append(paths, imageBinPaths(mi.images[0])...)
	}
	if len(mi.images) >= 2 {
		paths = append(paths, imageBinPaths(mi.images[1])...)
	}

	for _, raw := range mi.rawSections {
		paths = append(paths, raw.filename)
	}

	return paths
}

// @return						[section0blob, section1blob,...], hash, err
func (mi *MfgImage) build() ([][]byte, []byte, error) {
	if err := mi.copyBinFiles(); err != nil {
		return nil, nil, err
	}

	targetParts, err := mi.targetParts()
	if err != nil {
		return nil, nil, err
	}

	rawParts := mi.rawSectionParts()

	parts := append(targetParts, rawParts...)
	sortParts(parts)

	deviceSections, hash, err := mi.createDeviceSections(parts)
	if err != nil {
		return nil, nil, err
	}

	return deviceSections, hash, nil
}

func (mi *MfgImage) createManifest(hash []byte) ([]byte, error) {
	manifest := mfgManifest{
		BuildTime: time.Now().Format(time.RFC3339),
		MfgHash:   fmt.Sprintf("%x", hash),
	}
	buffer, err := json.MarshalIndent(manifest, "", "  ")
	if err != nil {
		return nil, util.FmtNewtError("Failed to encode mfg manifest: %s",
			err.Error())
	}

	return buffer, nil
}

// @return                      [paths-of-sections], error
func (mi *MfgImage) CreateMfgImage() ([]string, error) {
	sections, hash, err := mi.build()
	if err != nil {
		return nil, err
	}

	sectionDir := builder.MfgSectionDir(mi.basePkg.Name())
	if err := os.MkdirAll(sectionDir, 0755); err != nil {
		return nil, util.ChildNewtError(err)
	}

	sectionPaths := make([]string, len(sections))
	for i, section := range sections {
		sectionPath := builder.MfgSectionPath(mi.basePkg.Name(), i)
		if err := ioutil.WriteFile(sectionPath, section, 0644); err != nil {
			return nil, util.ChildNewtError(err)
		}
		sectionPaths[i] = sectionPath
	}

	manifest, err := mi.createManifest(hash)
	if err != nil {
		return nil, err
	}

	manifestPath := builder.MfgManifestPath(mi.basePkg.Name())
	if err := ioutil.WriteFile(manifestPath, manifest, 0644); err != nil {
		return nil, util.FmtNewtError("Failed to write mfg manifest file: %s",
			err.Error())
	}

	return sectionPaths, nil
}
<|endoftext|>


<commit_before>// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     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.

// sample-bar demonstrates a sample i3bar built using barista.
package main

import (
	"fmt"
	"os/exec"
	"os/user"
	"path/filepath"
	"time"

	"github.com/soumya92/barista/bar"
	"github.com/soumya92/barista/colors"
	"github.com/soumya92/barista/modules/clock"
	"github.com/soumya92/barista/modules/cputemp"
	"github.com/soumya92/barista/modules/group"
	"github.com/soumya92/barista/modules/media"
	"github.com/soumya92/barista/modules/meminfo"
	"github.com/soumya92/barista/modules/netspeed"
	"github.com/soumya92/barista/modules/sysinfo"
	"github.com/soumya92/barista/modules/volume"
	"github.com/soumya92/barista/modules/weather"
	"github.com/soumya92/barista/modules/weather/openweathermap"
	"github.com/soumya92/barista/outputs"
	"github.com/soumya92/barista/pango"
	"github.com/soumya92/barista/pango/icons/fontawesome"
	"github.com/soumya92/barista/pango/icons/ionicons"
	"github.com/soumya92/barista/pango/icons/material"
	"github.com/soumya92/barista/pango/icons/material_community"
	"github.com/soumya92/barista/pango/icons/typicons"
)

var spacer = pango.Span(" ", pango.XXSmall)

func truncate(in string, l int) string {
	if len([]rune(in)) <= l {
		return in
	}
	return string([]rune(in)[:l-1]) + "⋯"
}

func hms(d time.Duration) (h int, m int, s int) {
	h = int(d.Hours())
	m = int(d.Minutes()) % 60
	s = int(d.Seconds()) % 60
	return
}

func formatMediaTime(d time.Duration) string {
	h, m, s := hms(d)
	if h > 0 {
		return fmt.Sprintf("%d:%02d:%02d", h, m, s)
	}
	return fmt.Sprintf("%d:%02d", m, s)
}

func mediaFormatFunc(m media.Info) bar.Output {
	if m.PlaybackStatus == media.Stopped || m.PlaybackStatus == media.Disconnected {
		return nil
	}
	artist := truncate(m.Artist, 20)
	title := truncate(m.Title, 40-len(artist))
	if len(title) < 20 {
		artist = truncate(m.Artist, 40-len(title))
	}
	var iconAndPosition pango.Node
	if m.PlaybackStatus == media.Playing {
		iconAndPosition = pango.Span(
			colors.Hex("#f70"),
			fontawesome.Icon("music"),
			spacer,
			formatMediaTime(m.Position()),
			"/",
			formatMediaTime(m.Length),
		)
	} else {
		iconAndPosition = fontawesome.Icon("music", colors.Hex("#f70"))
	}
	return outputs.Pango(iconAndPosition, spacer, title, " - ", artist)
}

func startTaskManager(e bar.Event) {
	if e.Button == bar.ButtonLeft {
		exec.Command("xfce4-taskmanager").Run()
	}
}

func home(path string) string {
	usr, err := user.Current()
	if err != nil {
		panic(err)
	}
	return filepath.Join(usr.HomeDir, path)
}

func main() {
	material.Load(home("Github/material-design-icons"))
	materialCommunity.Load(home("Github/MaterialDesign-Webfont"))
	typicons.Load(home("Github/typicons.font"))
	ionicons.Load(home("Github/ionicons"))
	fontawesome.Load(home("Github/Font-Awesome"))

	colors.LoadFromMap(map[string]string{
		"good":     "#6d6",
		"degraded": "#dd6",
		"bad":      "#d66",
		"dim-icon": "#777",
	})

	localtime := clock.New().OutputFunc(func(now time.Time) bar.Output {
		return outputs.Pango(
			material.Icon("today", colors.Scheme("dim-icon")),
			now.Format("Mon Jan 2 "),
			material.Icon("access-time", colors.Scheme("dim-icon")),
			now.Format("15:04:05"),
		)
	}).OnClick(func(e bar.Event) {
		if e.Button == bar.ButtonLeft {
			exec.Command("gsimplecal").Run()
		}
	})

	// Weather information comes from OpenWeatherMap.
	// https://openweathermap.org/api.
	wthr := weather.New(
		openweathermap.Zipcode("94043", "US").Build(),
	).OutputFunc(func(w weather.Weather) bar.Output {
		iconName := ""
		switch w.Condition {
		case weather.Thunderstorm,
			weather.TropicalStorm,
			weather.Hurricane:
			iconName = "stormy"
		case weather.Drizzle,
			weather.Hail:
			iconName = "shower"
		case weather.Rain:
			iconName = "downpour"
		case weather.Snow,
			weather.Sleet:
			iconName = "snow"
		case weather.Mist,
			weather.Smoke,
			weather.Whirls,
			weather.Haze,
			weather.Fog:
			iconName = "windy-cloudy"
		case weather.Clear:
			if !w.Sunset.IsZero() && time.Now().After(w.Sunset) {
				iconName = "night"
			} else {
				iconName = "sunny"
			}
		case weather.PartlyCloudy:
			iconName = "partly-sunny"
		case weather.Cloudy, weather.Overcast:
			iconName = "cloudy"
		case weather.Tornado,
			weather.Windy:
			iconName = "windy"
		}
		if iconName == "" {
			iconName = "warning-outline"
		} else {
			iconName = "weather-" + iconName
		}
		return outputs.Pango(
			typicons.Icon(iconName), spacer,
			pango.Textf("%d℃", w.Temperature.C()),
			pango.Span(" (provided by ", w.Attribution, ")", pango.XSmall),
		)
	})

	vol := volume.DefaultMixer().OutputFunc(func(v volume.Volume) bar.Output {
		if v.Mute {
			return outputs.
				Pango(ionicons.Icon("volume-mute"), "MUT").
				Color(colors.Scheme("degraded"))
		}
		iconName := "low"
		pct := v.Pct()
		if pct > 66 {
			iconName = "high"
		} else if pct > 33 {
			iconName = "medium"
		}
		return outputs.Pango(
			ionicons.Icon("volume-"+iconName),
			spacer,
			pango.Textf("%2d%%", pct),
		)
	})

	loadAvg := sysinfo.New().OutputFunc(func(s sysinfo.Info) bar.Output {
		out := outputs.Textf("%0.2f %0.2f", s.Loads[0], s.Loads[2])
		// Load averages are unusually high for a few minutes after boot.
		if s.Uptime < 10*time.Minute {
			// so don't add colours until 10 minutes after system start.
			return out
		}
		switch {
		case s.Loads[0] > 128, s.Loads[2] > 64:
			out.Urgent(true)
		case s.Loads[0] > 64, s.Loads[2] > 32:
			out.Color(colors.Scheme("bad"))
		case s.Loads[0] > 32, s.Loads[2] > 16:
			out.Color(colors.Scheme("degraded"))
		}
		return out
	}).OnClick(startTaskManager)

	freeMem := meminfo.New().OutputFunc(func(m meminfo.Info) bar.Output {
		out := outputs.Pango(material.Icon("memory"), m.Available().IEC())
		freeGigs := m.Available().In("GiB")
		switch {
		case freeGigs < 0.5:
			out.Urgent(true)
		case freeGigs < 1:
			out.Color(colors.Scheme("bad"))
		case freeGigs < 2:
			out.Color(colors.Scheme("degraded"))
		case freeGigs > 12:
			out.Color(colors.Scheme("good"))
		}
		return out
	}).OnClick(startTaskManager)

	temp := cputemp.DefaultZone().
		RefreshInterval(2 * time.Second).
		UrgentWhen(func(temp cputemp.Temperature) bool {
			return temp.C() > 90
		}).
		OutputColor(func(temp cputemp.Temperature) bar.Color {
			switch {
			case temp.C() > 70:
				return colors.Scheme("bad")
			case temp.C() > 60:
				return colors.Scheme("degraded")
			default:
				return colors.Empty()
			}
		}).
		OutputFunc(func(temp cputemp.Temperature) bar.Output {
			return outputs.Pango(
				materialCommunity.Icon("fan"), spacer,
				pango.Textf("%2d℃", temp.C()),
			)
		})

	net := netspeed.New("eno1").
		RefreshInterval(2 * time.Second).
		OutputFunc(func(s netspeed.Speeds) bar.Output {
			return outputs.Pango(
				fontawesome.Icon("upload"), spacer, pango.Textf("%5s", s.Tx.SI()),
				pango.Span(" ", pango.Small),
				fontawesome.Icon("download"), spacer, pango.Textf("%5s", s.Rx.SI()),
			)
		})

	rhythmbox := media.New("rhythmbox").OutputFunc(mediaFormatFunc)

	g := group.Collapsing()

	panic(bar.Run(
		rhythmbox,
		g.Add(net),
		g.Add(temp),
		g.Add(freeMem),
		g.Add(loadAvg),
		g.Button(outputs.Text("+"), outputs.Text("-")),
		vol,
		wthr,
		localtime,
	))
}
<commit_msg>Use outputs.Empty() instead of nil<commit_after>// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     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.

// sample-bar demonstrates a sample i3bar built using barista.
package main

import (
	"fmt"
	"os/exec"
	"os/user"
	"path/filepath"
	"time"

	"github.com/soumya92/barista/bar"
	"github.com/soumya92/barista/colors"
	"github.com/soumya92/barista/modules/clock"
	"github.com/soumya92/barista/modules/cputemp"
	"github.com/soumya92/barista/modules/group"
	"github.com/soumya92/barista/modules/media"
	"github.com/soumya92/barista/modules/meminfo"
	"github.com/soumya92/barista/modules/netspeed"
	"github.com/soumya92/barista/modules/sysinfo"
	"github.com/soumya92/barista/modules/volume"
	"github.com/soumya92/barista/modules/weather"
	"github.com/soumya92/barista/modules/weather/openweathermap"
	"github.com/soumya92/barista/outputs"
	"github.com/soumya92/barista/pango"
	"github.com/soumya92/barista/pango/icons/fontawesome"
	"github.com/soumya92/barista/pango/icons/ionicons"
	"github.com/soumya92/barista/pango/icons/material"
	"github.com/soumya92/barista/pango/icons/material_community"
	"github.com/soumya92/barista/pango/icons/typicons"
)

var spacer = pango.Span(" ", pango.XXSmall)

func truncate(in string, l int) string {
	if len([]rune(in)) <= l {
		return in
	}
	return string([]rune(in)[:l-1]) + "⋯"
}

func hms(d time.Duration) (h int, m int, s int) {
	h = int(d.Hours())
	m = int(d.Minutes()) % 60
	s = int(d.Seconds()) % 60
	return
}

func formatMediaTime(d time.Duration) string {
	h, m, s := hms(d)
	if h > 0 {
		return fmt.Sprintf("%d:%02d:%02d", h, m, s)
	}
	return fmt.Sprintf("%d:%02d", m, s)
}

func mediaFormatFunc(m media.Info) bar.Output {
	if m.PlaybackStatus == media.Stopped || m.PlaybackStatus == media.Disconnected {
		return outputs.Empty()
	}
	artist := truncate(m.Artist, 20)
	title := truncate(m.Title, 40-len(artist))
	if len(title) < 20 {
		artist = truncate(m.Artist, 40-len(title))
	}
	var iconAndPosition pango.Node
	if m.PlaybackStatus == media.Playing {
		iconAndPosition = pango.Span(
			colors.Hex("#f70"),
			fontawesome.Icon("music"),
			spacer,
			formatMediaTime(m.Position()),
			"/",
			formatMediaTime(m.Length),
		)
	} else {
		iconAndPosition = fontawesome.Icon("music", colors.Hex("#f70"))
	}
	return outputs.Pango(iconAndPosition, spacer, title, " - ", artist)
}

func startTaskManager(e bar.Event) {
	if e.Button == bar.ButtonLeft {
		exec.Command("xfce4-taskmanager").Run()
	}
}

func home(path string) string {
	usr, err := user.Current()
	if err != nil {
		panic(err)
	}
	return filepath.Join(usr.HomeDir, path)
}

func main() {
	material.Load(home("Github/material-design-icons"))
	materialCommunity.Load(home("Github/MaterialDesign-Webfont"))
	typicons.Load(home("Github/typicons.font"))
	ionicons.Load(home("Github/ionicons"))
	fontawesome.Load(home("Github/Font-Awesome"))

	colors.LoadFromMap(map[string]string{
		"good":     "#6d6",
		"degraded": "#dd6",
		"bad":      "#d66",
		"dim-icon": "#777",
	})

	localtime := clock.New().OutputFunc(func(now time.Time) bar.Output {
		return outputs.Pango(
			material.Icon("today", colors.Scheme("dim-icon")),
			now.Format("Mon Jan 2 "),
			material.Icon("access-time", colors.Scheme("dim-icon")),
			now.Format("15:04:05"),
		)
	}).OnClick(func(e bar.Event) {
		if e.Button == bar.ButtonLeft {
			exec.Command("gsimplecal").Run()
		}
	})

	// Weather information comes from OpenWeatherMap.
	// https://openweathermap.org/api.
	wthr := weather.New(
		openweathermap.Zipcode("94043", "US").Build(),
	).OutputFunc(func(w weather.Weather) bar.Output {
		iconName := ""
		switch w.Condition {
		case weather.Thunderstorm,
			weather.TropicalStorm,
			weather.Hurricane:
			iconName = "stormy"
		case weather.Drizzle,
			weather.Hail:
			iconName = "shower"
		case weather.Rain:
			iconName = "downpour"
		case weather.Snow,
			weather.Sleet:
			iconName = "snow"
		case weather.Mist,
			weather.Smoke,
			weather.Whirls,
			weather.Haze,
			weather.Fog:
			iconName = "windy-cloudy"
		case weather.Clear:
			if !w.Sunset.IsZero() && time.Now().After(w.Sunset) {
				iconName = "night"
			} else {
				iconName = "sunny"
			}
		case weather.PartlyCloudy:
			iconName = "partly-sunny"
		case weather.Cloudy, weather.Overcast:
			iconName = "cloudy"
		case weather.Tornado,
			weather.Windy:
			iconName = "windy"
		}
		if iconName == "" {
			iconName = "warning-outline"
		} else {
			iconName = "weather-" + iconName
		}
		return outputs.Pango(
			typicons.Icon(iconName), spacer,
			pango.Textf("%d℃", w.Temperature.C()),
			pango.Span(" (provided by ", w.Attribution, ")", pango.XSmall),
		)
	})

	vol := volume.DefaultMixer().OutputFunc(func(v volume.Volume) bar.Output {
		if v.Mute {
			return outputs.
				Pango(ionicons.Icon("volume-mute"), "MUT").
				Color(colors.Scheme("degraded"))
		}
		iconName := "low"
		pct := v.Pct()
		if pct > 66 {
			iconName = "high"
		} else if pct > 33 {
			iconName = "medium"
		}
		return outputs.Pango(
			ionicons.Icon("volume-"+iconName),
			spacer,
			pango.Textf("%2d%%", pct),
		)
	})

	loadAvg := sysinfo.New().OutputFunc(func(s sysinfo.Info) bar.Output {
		out := outputs.Textf("%0.2f %0.2f", s.Loads[0], s.Loads[2])
		// Load averages are unusually high for a few minutes after boot.
		if s.Uptime < 10*time.Minute {
			// so don't add colours until 10 minutes after system start.
			return out
		}
		switch {
		case s.Loads[0] > 128, s.Loads[2] > 64:
			out.Urgent(true)
		case s.Loads[0] > 64, s.Loads[2] > 32:
			out.Color(colors.Scheme("bad"))
		case s.Loads[0] > 32, s.Loads[2] > 16:
			out.Color(colors.Scheme("degraded"))
		}
		return out
	}).OnClick(startTaskManager)

	freeMem := meminfo.New().OutputFunc(func(m meminfo.Info) bar.Output {
		out := outputs.Pango(material.Icon("memory"), m.Available().IEC())
		freeGigs := m.Available().In("GiB")
		switch {
		case freeGigs < 0.5:
			out.Urgent(true)
		case freeGigs < 1:
			out.Color(colors.Scheme("bad"))
		case freeGigs < 2:
			out.Color(colors.Scheme("degraded"))
		case freeGigs > 12:
			out.Color(colors.Scheme("good"))
		}
		return out
	}).OnClick(startTaskManager)

	temp := cputemp.DefaultZone().
		RefreshInterval(2 * time.Second).
		UrgentWhen(func(temp cputemp.Temperature) bool {
			return temp.C() > 90
		}).
		OutputColor(func(temp cputemp.Temperature) bar.Color {
			switch {
			case temp.C() > 70:
				return colors.Scheme("bad")
			case temp.C() > 60:
				return colors.Scheme("degraded")
			default:
				return colors.Empty()
			}
		}).
		OutputFunc(func(temp cputemp.Temperature) bar.Output {
			return outputs.Pango(
				materialCommunity.Icon("fan"), spacer,
				pango.Textf("%2d℃", temp.C()),
			)
		})

	net := netspeed.New("eno1").
		RefreshInterval(2 * time.Second).
		OutputFunc(func(s netspeed.Speeds) bar.Output {
			return outputs.Pango(
				fontawesome.Icon("upload"), spacer, pango.Textf("%5s", s.Tx.SI()),
				pango.Span(" ", pango.Small),
				fontawesome.Icon("download"), spacer, pango.Textf("%5s", s.Rx.SI()),
			)
		})

	rhythmbox := media.New("rhythmbox").OutputFunc(mediaFormatFunc)

	g := group.Collapsing()

	panic(bar.Run(
		rhythmbox,
		g.Add(net),
		g.Add(temp),
		g.Add(freeMem),
		g.Add(loadAvg),
		g.Button(outputs.Text("+"), outputs.Text("-")),
		vol,
		wthr,
		localtime,
	))
}
<|endoftext|>


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

package minio

import (
	"bufio"
	"encoding/json"
	"net/http"
	"net/url"
)

// GetBucketNotification - get bucket notification at a given path.
func (c Client) GetBucketNotification(bucketName string) (bucketNotification BucketNotification, err error) {
	// Input validation.
	if err := isValidBucketName(bucketName); err != nil {
		return BucketNotification{}, err
	}
	notification, err := c.getBucketNotification(bucketName)
	if err != nil {
		return BucketNotification{}, err
	}
	return notification, nil
}

// Request server for notification rules.
func (c Client) getBucketNotification(bucketName string) (BucketNotification, error) {
	urlValues := make(url.Values)
	urlValues.Set("notification", "")

	// Execute GET on bucket to list objects.
	resp, err := c.executeMethod("GET", requestMetadata{
		bucketName:  bucketName,
		queryValues: urlValues,
	})

	defer closeResponse(resp)
	if err != nil {
		return BucketNotification{}, err
	}
	return processBucketNotificationResponse(bucketName, resp)

}

// processes the GetNotification http response from the server.
func processBucketNotificationResponse(bucketName string, resp *http.Response) (BucketNotification, error) {
	if resp.StatusCode != http.StatusOK {
		errResponse := httpRespToErrorResponse(resp, bucketName, "")
		return BucketNotification{}, errResponse
	}
	var bucketNotification BucketNotification
	err := xmlDecoder(resp.Body, &bucketNotification)
	if err != nil {
		return BucketNotification{}, err
	}
	return bucketNotification, nil
}

// Indentity represents the user id, this is a compliance field.
type identity struct {
	PrincipalID string `json:"principalId"`
}

// Notification event bucket metadata.
type bucketMeta struct {
	Name          string   `json:"name"`
	OwnerIdentity identity `json:"ownerIdentity"`
	ARN           string   `json:"arn"`
}

// Notification event object metadata.
type objectMeta struct {
	Key       string `json:"key"`
	Size      int64  `json:"size,omitempty"`
	ETag      string `json:"eTag,omitempty"`
	VersionID string `json:"versionId,omitempty"`
	Sequencer string `json:"sequencer"`
}

// Notification event server specific metadata.
type eventMeta struct {
	SchemaVersion   string     `json:"s3SchemaVersion"`
	ConfigurationID string     `json:"configurationId"`
	Bucket          bucketMeta `json:"bucket"`
	Object          objectMeta `json:"object"`
}

// NotificationEvent represents an Amazon an S3 bucket notification event.
type NotificationEvent struct {
	EventVersion      string            `json:"eventVersion"`
	EventSource       string            `json:"eventSource"`
	AwsRegion         string            `json:"awsRegion"`
	EventTime         string            `json:"eventTime"`
	EventName         string            `json:"eventName"`
	UserIdentity      identity          `json:"userIdentity"`
	RequestParameters map[string]string `json:"requestParameters"`
	ResponseElements  map[string]string `json:"responseElements"`
	S3                eventMeta         `json:"s3"`
}

// NotificationInfo - represents the collection of notification events, additionally
// also reports errors if any while listening on bucket notifications.
type NotificationInfo struct {
	Records []NotificationEvent
	Err     error
}

// ListenBucketNotification - listen on bucket notifications.
func (c Client) ListenBucketNotification(bucketName string, accountArn Arn, doneCh <-chan struct{}) <-chan NotificationInfo {
	notificationInfoCh := make(chan NotificationInfo, 1)
	// Only success, start a routine to start reading line by line.
	go func(notificationInfoCh chan<- NotificationInfo) {
		defer close(notificationInfoCh)

		if err := isValidBucketName(bucketName); err != nil {
			notificationInfoCh <- NotificationInfo{
				Err: err,
			}
			return
		}

		urlValues := make(url.Values)
		urlValues.Set("notificationARN", accountArn.String())

		// Execute GET on bucket to list objects.
		resp, err := c.executeMethod("GET", requestMetadata{
			bucketName:  bucketName,
			queryValues: urlValues,
		})
		if err != nil {
			notificationInfoCh <- NotificationInfo{
				Err: err,
			}
			return
		}

		// Validate http response, upon error return quickly.
		if resp.StatusCode != http.StatusOK {
			errResponse := httpRespToErrorResponse(resp, bucketName, "")
			notificationInfoCh <- NotificationInfo{
				Err: errResponse,
			}
			return
		}

		// Initialize a new bufio scanner, to read line by line.
		bio := bufio.NewScanner(resp.Body)

		// Close the response body.
		defer resp.Body.Close()

		// Unmarshal each line, returns marshalled values.
		for bio.Scan() {
			var notificationInfo NotificationInfo
			buf := bio.Bytes()
			if err = json.Unmarshal(buf, &notificationInfo); err != nil {
				notificationInfoCh <- NotificationInfo{
					Err: err,
				}
				return
			}
			// Send notifications on channel only if there are events received.
			if len(notificationInfo.Records) > 0 {
				select {
				case notificationInfoCh <- notificationInfo:
				case <-doneCh:
					return
				}
			}
		}
	}(notificationInfoCh)

	// Returns the notification info channel, for caller to start reading from.
	return notificationInfoCh
}
<commit_msg>api: ListenBucketNotification should retry upon io.ErrUnexpectedEOF. (#486)<commit_after>/*
 * Minio Go Library for Amazon S3 Compatible Cloud Storage (C) 2016 Minio, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package minio

import (
	"bufio"
	"encoding/json"
	"io"
	"net/http"
	"net/url"
)

// GetBucketNotification - get bucket notification at a given path.
func (c Client) GetBucketNotification(bucketName string) (bucketNotification BucketNotification, err error) {
	// Input validation.
	if err := isValidBucketName(bucketName); err != nil {
		return BucketNotification{}, err
	}
	notification, err := c.getBucketNotification(bucketName)
	if err != nil {
		return BucketNotification{}, err
	}
	return notification, nil
}

// Request server for notification rules.
func (c Client) getBucketNotification(bucketName string) (BucketNotification, error) {
	urlValues := make(url.Values)
	urlValues.Set("notification", "")

	// Execute GET on bucket to list objects.
	resp, err := c.executeMethod("GET", requestMetadata{
		bucketName:  bucketName,
		queryValues: urlValues,
	})

	defer closeResponse(resp)
	if err != nil {
		return BucketNotification{}, err
	}
	return processBucketNotificationResponse(bucketName, resp)

}

// processes the GetNotification http response from the server.
func processBucketNotificationResponse(bucketName string, resp *http.Response) (BucketNotification, error) {
	if resp.StatusCode != http.StatusOK {
		errResponse := httpRespToErrorResponse(resp, bucketName, "")
		return BucketNotification{}, errResponse
	}
	var bucketNotification BucketNotification
	err := xmlDecoder(resp.Body, &bucketNotification)
	if err != nil {
		return BucketNotification{}, err
	}
	return bucketNotification, nil
}

// Indentity represents the user id, this is a compliance field.
type identity struct {
	PrincipalID string `json:"principalId"`
}

// Notification event bucket metadata.
type bucketMeta struct {
	Name          string   `json:"name"`
	OwnerIdentity identity `json:"ownerIdentity"`
	ARN           string   `json:"arn"`
}

// Notification event object metadata.
type objectMeta struct {
	Key       string `json:"key"`
	Size      int64  `json:"size,omitempty"`
	ETag      string `json:"eTag,omitempty"`
	VersionID string `json:"versionId,omitempty"`
	Sequencer string `json:"sequencer"`
}

// Notification event server specific metadata.
type eventMeta struct {
	SchemaVersion   string     `json:"s3SchemaVersion"`
	ConfigurationID string     `json:"configurationId"`
	Bucket          bucketMeta `json:"bucket"`
	Object          objectMeta `json:"object"`
}

// NotificationEvent represents an Amazon an S3 bucket notification event.
type NotificationEvent struct {
	EventVersion      string            `json:"eventVersion"`
	EventSource       string            `json:"eventSource"`
	AwsRegion         string            `json:"awsRegion"`
	EventTime         string            `json:"eventTime"`
	EventName         string            `json:"eventName"`
	UserIdentity      identity          `json:"userIdentity"`
	RequestParameters map[string]string `json:"requestParameters"`
	ResponseElements  map[string]string `json:"responseElements"`
	S3                eventMeta         `json:"s3"`
}

// NotificationInfo - represents the collection of notification events, additionally
// also reports errors if any while listening on bucket notifications.
type NotificationInfo struct {
	Records []NotificationEvent
	Err     error
}

// ListenBucketNotification - listen on bucket notifications.
func (c Client) ListenBucketNotification(bucketName string, accountArn Arn, doneCh <-chan struct{}) <-chan NotificationInfo {
	notificationInfoCh := make(chan NotificationInfo, 1)
	// Only success, start a routine to start reading line by line.
	go func(notificationInfoCh chan<- NotificationInfo) {
		defer close(notificationInfoCh)

		if err := isValidBucketName(bucketName); err != nil {
			notificationInfoCh <- NotificationInfo{
				Err: err,
			}
			return
		}

		// Continously run and listen on bucket notification.
		for {
			urlValues := make(url.Values)
			urlValues.Set("notificationARN", accountArn.String())

			// Execute GET on bucket to list objects.
			resp, err := c.executeMethod("GET", requestMetadata{
				bucketName:  bucketName,
				queryValues: urlValues,
			})
			if err != nil {
				notificationInfoCh <- NotificationInfo{
					Err: err,
				}
				return
			}

			// Validate http response, upon error return quickly.
			if resp.StatusCode != http.StatusOK {
				errResponse := httpRespToErrorResponse(resp, bucketName, "")
				notificationInfoCh <- NotificationInfo{
					Err: errResponse,
				}
				return
			}

			// Initialize a new bufio scanner, to read line by line.
			bio := bufio.NewScanner(resp.Body)

			// Close the response body.
			defer resp.Body.Close()

			// Unmarshal each line, returns marshalled values.
			for bio.Scan() {
				var notificationInfo NotificationInfo
				if err = json.Unmarshal(bio.Bytes(), &notificationInfo); err != nil {
					notificationInfoCh <- NotificationInfo{
						Err: err,
					}
					return
				}
				// Send notifications on channel only if there are events received.
				if len(notificationInfo.Records) > 0 {
					select {
					case notificationInfoCh <- notificationInfo:
					case <-doneCh:
						return
					}
				}
			}
			// Look for any underlying errors.
			if err = bio.Err(); err != nil {
				// For an unexpected connection drop from server, we close the body
				// and re-connect.
				if err == io.ErrUnexpectedEOF {
					resp.Body.Close()
					continue
				}
				notificationInfoCh <- NotificationInfo{
					Err: err,
				}
				return
			}
		}
	}(notificationInfoCh)

	// Returns the notification info channel, for caller to start reading from.
	return notificationInfoCh
}
<|endoftext|>


<commit_before>package cluster

import (
	"fmt"
	"net"
	"net/url"

	"github.com/openshift/origin/pkg/util/netutils"
	"github.com/openvswitch/ovn-kubernetes/go-controller/pkg/config"
	"github.com/openvswitch/ovn-kubernetes/go-controller/pkg/factory"
	"github.com/openvswitch/ovn-kubernetes/go-controller/pkg/kube"
	"github.com/openvswitch/ovn-kubernetes/go-controller/pkg/util"
	"k8s.io/client-go/kubernetes"
)

// OvnClusterController is the object holder for utilities meant for cluster management
type OvnClusterController struct {
	Kube                  kube.Interface
	watchFactory          *factory.WatchFactory
	masterSubnetAllocator *netutils.SubnetAllocator

	ClusterIPNet          *net.IPNet
	ClusterServicesSubnet string
	HostSubnetLength      uint32

	GatewayInit      bool
	GatewayIntf      string
	GatewayBridge    string
	GatewayNextHop   string
	GatewaySpareIntf bool
	NodePortEnable   bool
	OvnHA            bool
}

const (
	// OvnHostSubnet is the constant string representing the annotation key
	OvnHostSubnet = "ovn_host_subnet"
	// DefaultNamespace is the name of the default namespace
	DefaultNamespace = "default"
	// MasterOverlayIP is the overlay IP address on master node
	MasterOverlayIP = "master_overlay_ip"
)

// NewClusterController creates a new controller for IP subnet allocation to
// a given resource type (either Namespace or Node)
func NewClusterController(kubeClient kubernetes.Interface, wf *factory.WatchFactory) *OvnClusterController {
	return &OvnClusterController{
		Kube:         &kube.Kube{KClient: kubeClient},
		watchFactory: wf,
	}
}

func setOVSExternalIDs(nodeName string, ids ...string) error {
	var err error

	nodeIP := config.Default.EncapIP
	if nodeIP == "" {
		nodeIP, err = netutils.GetNodeIP(nodeName)
		if err != nil {
			return fmt.Errorf("failed to obtain local IP from hostname %q: %v", nodeName, err)
		}
	} else {
		if ip := net.ParseIP(nodeIP); ip == nil {
			return fmt.Errorf("invalid encapsulation IP provided %q", nodeIP)
		}
	}

	args := []string{
		"set",
		"Open_vSwitch",
		".",
		fmt.Sprintf("external_ids:ovn-encap-type=%s", config.Default.EncapType),
		fmt.Sprintf("external_ids:ovn-encap-ip=%s", nodeIP),
	}
	for _, str := range ids {
		args = append(args, "external_ids:"+str)
	}
	_, stderr, err := util.RunOVSVsctl(args...)
	if err != nil {
		return fmt.Errorf("error setting OVS external IDs: %v\n  %q", err, stderr)
	}
	return nil
}

func setupOVNNode(nodeName, kubeServer, kubeToken, kubeCACert string) error {
	// Tell ovn-*bctl how to talk to the database
	for _, auth := range []*config.OvnDBAuth{
		config.OvnNorth.ClientAuth,
		config.OvnSouth.ClientAuth} {
		if err := auth.SetDBAuth(); err != nil {
			return err
		}
	}

	// Tell other utilities (ovn-k8s-cni-overlay, etc) how to talk to Kubernetes
	if _, err := url.Parse(kubeServer); err != nil {
		return fmt.Errorf("error parsing k8s server %q: %v", kubeServer, err)
	}
	return setOVSExternalIDs(
		nodeName,
		fmt.Sprintf("k8s-api-server=\"%s\"", kubeServer),
		fmt.Sprintf("k8s-api-token=\"%s\"", kubeToken),
		fmt.Sprintf("k8s-ca-certificate=\"%s\"", kubeCACert))
}

func setupOVNMaster(nodeName string) error {
	// Configure both server and client of OVN databases, since master uses both
	for _, auth := range []*config.OvnDBAuth{
		config.OvnNorth.ServerAuth,
		config.OvnNorth.ClientAuth,
		config.OvnSouth.ServerAuth,
		config.OvnSouth.ClientAuth} {
		if err := auth.SetDBAuth(); err != nil {
			return err
		}
	}

	return setOVSExternalIDs(nodeName)
}
<commit_msg>k8s tags not needed in external_ids now<commit_after>package cluster

import (
	"fmt"
	"net"

	"github.com/openshift/origin/pkg/util/netutils"
	"github.com/openvswitch/ovn-kubernetes/go-controller/pkg/config"
	"github.com/openvswitch/ovn-kubernetes/go-controller/pkg/factory"
	"github.com/openvswitch/ovn-kubernetes/go-controller/pkg/kube"
	"github.com/openvswitch/ovn-kubernetes/go-controller/pkg/util"
	"k8s.io/client-go/kubernetes"
)

// OvnClusterController is the object holder for utilities meant for cluster management
type OvnClusterController struct {
	Kube                  kube.Interface
	watchFactory          *factory.WatchFactory
	masterSubnetAllocator *netutils.SubnetAllocator

	ClusterIPNet          *net.IPNet
	ClusterServicesSubnet string
	HostSubnetLength      uint32

	GatewayInit      bool
	GatewayIntf      string
	GatewayBridge    string
	GatewayNextHop   string
	GatewaySpareIntf bool
	NodePortEnable   bool
	OvnHA            bool
}

const (
	// OvnHostSubnet is the constant string representing the annotation key
	OvnHostSubnet = "ovn_host_subnet"
	// DefaultNamespace is the name of the default namespace
	DefaultNamespace = "default"
	// MasterOverlayIP is the overlay IP address on master node
	MasterOverlayIP = "master_overlay_ip"
)

// NewClusterController creates a new controller for IP subnet allocation to
// a given resource type (either Namespace or Node)
func NewClusterController(kubeClient kubernetes.Interface, wf *factory.WatchFactory) *OvnClusterController {
	return &OvnClusterController{
		Kube:         &kube.Kube{KClient: kubeClient},
		watchFactory: wf,
	}
}

func setOVSExternalIDs(nodeName string, ids ...string) error {
	var err error

	nodeIP := config.Default.EncapIP
	if nodeIP == "" {
		nodeIP, err = netutils.GetNodeIP(nodeName)
		if err != nil {
			return fmt.Errorf("failed to obtain local IP from hostname %q: %v", nodeName, err)
		}
	} else {
		if ip := net.ParseIP(nodeIP); ip == nil {
			return fmt.Errorf("invalid encapsulation IP provided %q", nodeIP)
		}
	}

	args := []string{
		"set",
		"Open_vSwitch",
		".",
		fmt.Sprintf("external_ids:ovn-encap-type=%s", config.Default.EncapType),
		fmt.Sprintf("external_ids:ovn-encap-ip=%s", nodeIP),
	}
	for _, str := range ids {
		args = append(args, "external_ids:"+str)
	}
	_, stderr, err := util.RunOVSVsctl(args...)
	if err != nil {
		return fmt.Errorf("error setting OVS external IDs: %v\n  %q", err, stderr)
	}
	return nil
}

func setupOVNNode(nodeName, kubeServer, kubeToken, kubeCACert string) error {
	// Tell ovn-*bctl how to talk to the database
	for _, auth := range []*config.OvnDBAuth{
		config.OvnNorth.ClientAuth,
		config.OvnSouth.ClientAuth} {
		if err := auth.SetDBAuth(); err != nil {
			return err
		}
	}

	return setOVSExternalIDs(nodeName)
}

func setupOVNMaster(nodeName string) error {
	// Configure both server and client of OVN databases, since master uses both
	for _, auth := range []*config.OvnDBAuth{
		config.OvnNorth.ServerAuth,
		config.OvnNorth.ClientAuth,
		config.OvnSouth.ServerAuth,
		config.OvnSouth.ClientAuth} {
		if err := auth.SetDBAuth(); err != nil {
			return err
		}
	}

	return setOVSExternalIDs(nodeName)
}
<|endoftext|>


<commit_before>// Copyright 2011 Arne Roomann-Kurrik.
//
// 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 oauth1a

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

// Container for user-specific keys and secrets related to the OAuth process.
// This struct is intended to be serialized and stored for future use.
// Request and Access tokens are each stored separately, so that the current
// position in the auth flow may be inferred.
type UserConfig struct {
	RequestTokenSecret string
	RequestTokenKey    string
	AccessTokenSecret  string
	AccessTokenKey     string
	Verifier           string
	AccessValues       url.Values
}

// Creates a UserConfig object with existing access token credentials.  For
// users where an access token has been obtained through other means than
// the authz flows provided by this library.
func NewAuthorizedConfig(token string, secret string) *UserConfig {
	return &UserConfig{AccessTokenKey: token, AccessTokenSecret: secret}
}

// Sign and send a Request using the current configuration.
func (c *UserConfig) send(request *http.Request, service *Service, client *http.Client) (*http.Response, error) {
	if err := service.Sign(request, c); err != nil {
		return nil, err
	}
	response, err := client.Do(request)
	if err != nil {
		return nil, err
	}
	if response.StatusCode != 200 {
		return nil, errors.New("Endpoint response: " + response.Status)
	}
	return response, nil
}

// Issue a request to obtain a Request token.
func (c *UserConfig) GetRequestToken(service *Service, client *http.Client) error {
	request, err := http.NewRequest("POST", service.RequestURL, nil)
	if err != nil {
		return err
	}
	request.Form = make(url.Values)
	request.Form.Add("oauth_callback", service.ClientConfig.CallbackURL)
	response, err := c.send(request, service, client)
	if err != nil {
		return err
	}
	err = c.parseRequestToken(response)
	return err
}

// Given the returned response from a Request token request, parse out the
// appropriate request token and secret fields.
func (c *UserConfig) parseRequestToken(response *http.Response) error {
	defer response.Body.Close()
	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return err
	}
	params, err := url.ParseQuery(string(body))
	tokenKey := params.Get("oauth_token")
	tokenSecret := params.Get("oauth_token_secret")
	if tokenKey == "" || tokenSecret == "" {
		return errors.New("No token or secret found")
	}
	c.RequestTokenKey = tokenKey
	c.RequestTokenSecret = tokenSecret
	if params.Get("oauth_callback_confirmed") == "false" {
		return errors.New("OAuth callback not confirmed")
	}
	return nil
}

// Obtain a URL which will allow the current user to authorize access to their
// OAuth-protected data.
func (c *UserConfig) GetAuthorizeURL(service *Service) (string, error) {
	if c.RequestTokenKey == "" || c.RequestTokenSecret == "" {
		return "", errors.New("No configured request token")
	}
	token := url.QueryEscape(c.RequestTokenKey)
	return service.AuthorizeURL + "?oauth_token=" + token, nil
}

// Parses an access token and verifier from a redirected authorize reqeust.
func (c *UserConfig) ParseAuthorize(request *http.Request, service *Service) (string, string, error) {
	request.ParseForm()
	urlParts := request.URL.Query()
	token := urlParts.Get("oauth_token")
	verifier := urlParts.Get("oauth_verifier")
	if token == "" {
		token = request.Form.Get("oauth_token")
	}
	if verifier == "" {
		verifier = request.Form.Get("oauth_verifier")
	}
	if token == "" || verifier == "" {
		return "", "", errors.New("Token or verifier were missing from response")
	}
	return token, verifier, nil
}

// Issue a request to exchange the current request token for an access token.
func (c *UserConfig) GetAccessToken(token string, verifier string, service *Service, client *http.Client) error {
	if c.RequestTokenKey == "" || c.RequestTokenSecret == "" {
		return errors.New("No configured request token")
	}
	if c.RequestTokenKey != token {
		return errors.New("Returned token did not match request token")
	}
	c.Verifier = verifier
	request, err := http.NewRequest("POST", service.AccessURL, nil)
	if err != nil {
		return err
	}
	request.Form = make(url.Values)
	request.Form.Add("oauth_verifier", verifier)
	response, err := c.send(request, service, client)
	if err != nil {
		return err
	}
	err = c.parseAccessToken(response)
	return err
}

// Given the returned response from the access token request, pull out the
// access token and token secret.  Store a copy of any other values returned,
// too, since some services (like Twitter) return handy information such
// as the username.
func (c *UserConfig) parseAccessToken(response *http.Response) error {
	defer response.Body.Close()
	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return err
	}
	params, err := url.ParseQuery(string(body))
	tokenKey := params.Get("oauth_token")
	tokenSecret := params.Get("oauth_token_secret")
	if tokenKey == "" || tokenSecret == "" {
		return errors.New("No token or secret found")
	}
	c.AccessTokenKey = tokenKey
	c.AccessTokenSecret = tokenSecret
	c.AccessValues = params
	return nil
}

// Returns a token and secret corresponding to where in the OAuth flow this
// config is currently in.  The priority is Access token, Request token, empty
// string.
func (c *UserConfig) GetToken() (string, string) {
	if c.AccessTokenKey != "" {
		return c.AccessTokenKey, c.AccessTokenSecret
	}
	if c.RequestTokenKey != "" {
		return c.RequestTokenKey, c.RequestTokenSecret
	}
	return "", ""
}
<commit_msg>Include request parameters in bodies of POST requests.<commit_after>// Copyright 2011 Arne Roomann-Kurrik.
//
// 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 oauth1a

import (
	"errors"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
)

// Container for user-specific keys and secrets related to the OAuth process.
// This struct is intended to be serialized and stored for future use.
// Request and Access tokens are each stored separately, so that the current
// position in the auth flow may be inferred.
type UserConfig struct {
	RequestTokenSecret string
	RequestTokenKey    string
	AccessTokenSecret  string
	AccessTokenKey     string
	Verifier           string
	AccessValues       url.Values
}

// Creates a UserConfig object with existing access token credentials.  For
// users where an access token has been obtained through other means than
// the authz flows provided by this library.
func NewAuthorizedConfig(token string, secret string) *UserConfig {
	return &UserConfig{AccessTokenKey: token, AccessTokenSecret: secret}
}

// Sign and send a Request using the current configuration.
func (c *UserConfig) send(request *http.Request, service *Service, client *http.Client) (*http.Response, error) {
	if err := service.Sign(request, c); err != nil {
		return nil, err
	}
	response, err := client.Do(request)
	if err != nil {
		return nil, err
	}
	if response.StatusCode != 200 {
		return nil, errors.New("Endpoint response: " + response.Status)
	}
	return response, nil
}

// Issue a request to obtain a Request token.
func (c *UserConfig) GetRequestToken(service *Service, client *http.Client) error {
	data := url.Values{}
	if service.ClientConfig.CallbackURL != "" {
		data.Set("oauth_callback", service.ClientConfig.CallbackURL)
	}
	body := strings.NewReader(data.Encode())
	request, err := http.NewRequest("POST", service.RequestURL, body)
	if err != nil {
		return err
	}
	request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	response, err := c.send(request, service, client)
	if err != nil {
		return err
	}
	err = c.parseRequestToken(response)
	return err
}

// Given the returned response from a Request token request, parse out the
// appropriate request token and secret fields.
func (c *UserConfig) parseRequestToken(response *http.Response) error {
	defer response.Body.Close()
	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return err
	}
	params, err := url.ParseQuery(string(body))
	tokenKey := params.Get("oauth_token")
	tokenSecret := params.Get("oauth_token_secret")
	if tokenKey == "" || tokenSecret == "" {
		return errors.New("No token or secret found")
	}
	c.RequestTokenKey = tokenKey
	c.RequestTokenSecret = tokenSecret
	if params.Get("oauth_callback_confirmed") == "false" {
		return errors.New("OAuth callback not confirmed")
	}
	return nil
}

// Obtain a URL which will allow the current user to authorize access to their
// OAuth-protected data.
func (c *UserConfig) GetAuthorizeURL(service *Service) (string, error) {
	if c.RequestTokenKey == "" || c.RequestTokenSecret == "" {
		return "", errors.New("No configured request token")
	}
	token := url.QueryEscape(c.RequestTokenKey)
	return service.AuthorizeURL + "?oauth_token=" + token, nil
}

// Parses an access token and verifier from a redirected authorize reqeust.
func (c *UserConfig) ParseAuthorize(request *http.Request, service *Service) (string, string, error) {
	request.ParseForm()
	urlParts := request.URL.Query()
	token := urlParts.Get("oauth_token")
	verifier := urlParts.Get("oauth_verifier")
	if token == "" {
		token = request.Form.Get("oauth_token")
	}
	if verifier == "" {
		verifier = request.Form.Get("oauth_verifier")
	}
	if token == "" || verifier == "" {
		return "", "", errors.New("Token or verifier were missing from response")
	}
	return token, verifier, nil
}

// Issue a request to exchange the current request token for an access token.
func (c *UserConfig) GetAccessToken(token string, verifier string, service *Service, client *http.Client) error {
	// This code used to error out if RequestTokenKey were empty, but
	// in the interest of being able to operate in a stateless manner this
	// has been removed.  If you want to make sure that the request token
	// is validated against what is being returned, populate the UserConfig
	// with a request token stored server-side somewhere, accessed by the
	// user's session.
	if c.RequestTokenKey != "" && c.RequestTokenKey != token {
		return errors.New("Returned token did not match request token")
	}
	c.Verifier = verifier
	data := url.Values{}
	if service.ClientConfig.CallbackURL != "" {
		data.Set("oauth_verifier", verifier)
	}
	body := strings.NewReader(data.Encode())
	request, err := http.NewRequest("POST", service.AccessURL, body)
	if err != nil {
		return err
	}
	request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	response, err := c.send(request, service, client)
	if err != nil {
		return err
	}
	err = c.parseAccessToken(response)
	return err
}

// Given the returned response from the access token request, pull out the
// access token and token secret.  Store a copy of any other values returned,
// too, since some services (like Twitter) return handy information such
// as the username.
func (c *UserConfig) parseAccessToken(response *http.Response) error {
	defer response.Body.Close()
	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return err
	}
	params, err := url.ParseQuery(string(body))
	tokenKey := params.Get("oauth_token")
	tokenSecret := params.Get("oauth_token_secret")
	if tokenKey == "" || tokenSecret == "" {
		return errors.New("No token or secret found")
	}
	c.AccessTokenKey = tokenKey
	c.AccessTokenSecret = tokenSecret
	c.AccessValues = params
	return nil
}

// Returns a token and secret corresponding to where in the OAuth flow this
// config is currently in.  The priority is Access token, Request token, empty
// string.
func (c *UserConfig) GetToken() (string, string) {
	if c.AccessTokenKey != "" {
		return c.AccessTokenKey, c.AccessTokenSecret
	}
	if c.RequestTokenKey != "" {
		return c.RequestTokenKey, c.RequestTokenSecret
	}
	return "", ""
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"io"
	"log"
	"os"
	"time"

	"github.com/ecc1/medtronic"
	"github.com/ecc1/medtronic/packet"
)

const (
	verbose = false
)

type (
	// SPILinkCommand represents a command sent by the client.
	SPILinkCommand struct {
		Command string
		Data    []byte // base64-encoded by json.Marshal
		Repeat  int
		Timeout int // microseconds
	}

	// SPILinkResult represents a result returned to the client.
	SPILinkResult struct {
		Data  []byte // base64-encoded by json.Marshal
		RSSI  int
		Error bool
	}
)

var (
	input  = json.NewDecoder(os.Stdin)
	output = json.NewEncoder(os.Stdout)

	radio = medtronic.Open().Radio
)

func main() {
	if radio.Error() != nil {
		log.Fatal(radio.Error())
	}
	for {
		cmd := readCommand()
		result := cmd.perform()
		err := output.Encode(result)
		radio.SetError(err)
		if radio.Error() != nil {
			log.Print(radio.Error())
			radio.SetError(nil)
		}
	}
}

func readCommand() SPILinkCommand {
	cmd := SPILinkCommand{}
	err := input.Decode(&cmd)
	if err == io.EOF {
		if verbose {
			log.Printf("EOF: exiting")
		}
		os.Exit(0)
	}
	if err != nil {
		log.Print(err)
	}
	return cmd
}

func (cmd SPILinkCommand) perform() SPILinkResult {
	if verbose {
		log.Printf("received %s command", cmd.Command)
	}
	timeout := time.Duration(cmd.Timeout) * time.Microsecond
	result := SPILinkResult{}
	switch cmd.Command {
	case "send_packet":
		result = send(cmd.Data, cmd.Repeat)
	case "get_packet":
		result = receive(timeout)
	case "send_and_listen":
		send(cmd.Data, cmd.Repeat)
		result = receive(timeout)
	default:
		log.Printf("unknown spilink command: %+v", cmd)
		result.Error = true
	}
	if verbose {
		log.Printf("returning %d-byte result", len(result.Data))
	}
	return result
}

func send(data []byte, repeat int) SPILinkResult {
	p := packet.Encode(data)
	if repeat == 0 {
		repeat = 1
	}
	if verbose {
		if repeat == 1 {
			log.Printf("sending %d-byte packet", len(p))
		} else {
			log.Printf("sending %d-byte packet %d times", len(p), repeat)
		}
	}
	for i := 0; i < repeat; i++ {
		radio.Send(p)
	}
	return SPILinkResult{}
}

func receive(timeout time.Duration) SPILinkResult {
	if verbose {
		log.Printf("receiving with timeout = %v", timeout)
	}
	result := SPILinkResult{}
	p, rssi := radio.Receive(timeout)
	data, err := packet.Decode6b4b(p)
	radio.SetError(err)
	result.Data = data
	if radio.Error() != nil {
		log.Print(radio.Error())
		radio.SetError(nil)
		result.Error = true
		return result
	}
	if verbose {
		log.Printf("received %d-byte packet (RSSI = %d)", len(p), rssi)
	}
	return result
}
<commit_msg>Remove spilink<commit_after><|endoftext|>


<commit_before>/*
Copyright 2020 The Vitess Authors.

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

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

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

package vindexes

import (
	"bytes"
	"encoding/binary"
	"encoding/json"
	"io/ioutil"

	"vitess.io/vitess/go/sqltypes"
	"vitess.io/vitess/go/vt/key"
	"vitess.io/vitess/go/vt/log"
)

var (
	_ MultiColumn = (*RegionJson)(nil)
)

func init() {
	Register("region_json", NewRegionJson)
}

// RegionMap is used to store mapping of country to region
type RegionMap map[string]uint64

// RegionJson defines a vindex that uses a lookup table.
// The table is expected to define the id column as unique. It's
// Unique and a Lookup.
type RegionJson struct {
	name        string
	regionMap   RegionMap
	regionBytes int
}

// NewRegionJson creates a RegionJson vindex.
// The supplied map requires all the fields of "region_experimental".
// Additionally, it requires a region_map argument representing the path to a json file
// containing a map of country to region.
func NewRegionJson(name string, m map[string]string) (Vindex, error) {
	rmPath := m["region_map"]
	rmap := make(map[string]uint64)
	data, err := ioutil.ReadFile(rmPath)
	if err != nil {
		return nil, err
	}
	log.Infof("Loaded Region map from: %s", rmPath)
	err = json.Unmarshal(data, &rmap)
	if err != nil {
		return nil, err
	}

	return &RegionJson{
		name:      name,
		regionMap: rmap,
	}, nil
}

// String returns the name of the vindex.
func (rv *RegionJson) String() string {
	return rv.name
}

// Cost returns the cost of this index as 1.
func (rv *RegionJson) Cost() int {
	return 1
}

// IsUnique returns true since the Vindex is unique.
func (rv *RegionJson) IsUnique() bool {
	return true
}

// Map satisfies MultiColumn.
func (rv *RegionJson) Map(vcursor VCursor, rowsColValues [][]sqltypes.Value) ([]key.Destination, error) {
	destinations := make([]key.Destination, 0, len(rowsColValues))
	for _, row := range rowsColValues {
		if len(row) != 2 {
			destinations = append(destinations, key.DestinationNone{})
			continue
		}
		// Compute hash.
		hn, err := sqltypes.ToUint64(row[0])
		if err != nil {
			destinations = append(destinations, key.DestinationNone{})
			continue
		}
		h := vhash(hn)

		rn, ok := rv.regionMap[row[1].ToString()]
		if !ok {
			destinations = append(destinations, key.DestinationNone{})
			continue
		}
		r := make([]byte, 2)
		binary.BigEndian.PutUint16(r, uint16(rn))

		// Concatenate and add to destinations.
		if rv.regionBytes == 1 {
			r = r[1:]
		}
		dest := append(r, h...)
		destinations = append(destinations, key.DestinationKeyspaceID(dest))
	}
	return destinations, nil
}

// Verify satisfies MultiColumn
func (rv *RegionJson) Verify(vcursor VCursor, rowsColValues [][]sqltypes.Value, ksids [][]byte) ([]bool, error) {
	result := make([]bool, len(rowsColValues))
	destinations, _ := rv.Map(vcursor, rowsColValues)
	for i, dest := range destinations {
		destksid, ok := dest.(key.DestinationKeyspaceID)
		if !ok {
			continue
		}
		result[i] = bytes.Equal([]byte(destksid), ksids[i])
	}
	return result, nil
}

// NeedVCursor staisfies the Vindex interface.
func (rv *RegionJson) NeedsVCursor() bool {
	return false
}
<commit_msg>Typo staisfies -> satisfies, nit: region_experimental -> RegionExperimental<commit_after>/*
Copyright 2020 The Vitess Authors.

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

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

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

package vindexes

import (
	"bytes"
	"encoding/binary"
	"encoding/json"
	"io/ioutil"

	"vitess.io/vitess/go/sqltypes"
	"vitess.io/vitess/go/vt/key"
	"vitess.io/vitess/go/vt/log"
)

var (
	_ MultiColumn = (*RegionJson)(nil)
)

func init() {
	Register("region_json", NewRegionJson)
}

// RegionMap is used to store mapping of country to region
type RegionMap map[string]uint64

// RegionJson defines a vindex that uses a lookup table.
// The table is expected to define the id column as unique. It's
// Unique and a Lookup.
type RegionJson struct {
	name        string
	regionMap   RegionMap
	regionBytes int
}

// NewRegionJson creates a RegionJson vindex.
// The supplied map requires all the fields of "RegionExperimental".
// Additionally, it requires a region_map argument representing the path to a json file
// containing a map of country to region.
func NewRegionJson(name string, m map[string]string) (Vindex, error) {
	rmPath := m["region_map"]
	rmap := make(map[string]uint64)
	data, err := ioutil.ReadFile(rmPath)
	if err != nil {
		return nil, err
	}
	log.Infof("Loaded Region map from: %s", rmPath)
	err = json.Unmarshal(data, &rmap)
	if err != nil {
		return nil, err
	}

	return &RegionJson{
		name:      name,
		regionMap: rmap,
	}, nil
}

// String returns the name of the vindex.
func (rv *RegionJson) String() string {
	return rv.name
}

// Cost returns the cost of this index as 1.
func (rv *RegionJson) Cost() int {
	return 1
}

// IsUnique returns true since the Vindex is unique.
func (rv *RegionJson) IsUnique() bool {
	return true
}

// Map satisfies MultiColumn.
func (rv *RegionJson) Map(vcursor VCursor, rowsColValues [][]sqltypes.Value) ([]key.Destination, error) {
	destinations := make([]key.Destination, 0, len(rowsColValues))
	for _, row := range rowsColValues {
		if len(row) != 2 {
			destinations = append(destinations, key.DestinationNone{})
			continue
		}
		// Compute hash.
		hn, err := sqltypes.ToUint64(row[0])
		if err != nil {
			destinations = append(destinations, key.DestinationNone{})
			continue
		}
		h := vhash(hn)

		rn, ok := rv.regionMap[row[1].ToString()]
		if !ok {
			destinations = append(destinations, key.DestinationNone{})
			continue
		}
		r := make([]byte, 2)
		binary.BigEndian.PutUint16(r, uint16(rn))

		// Concatenate and add to destinations.
		if rv.regionBytes == 1 {
			r = r[1:]
		}
		dest := append(r, h...)
		destinations = append(destinations, key.DestinationKeyspaceID(dest))
	}
	return destinations, nil
}

// Verify satisfies MultiColumn
func (rv *RegionJson) Verify(vcursor VCursor, rowsColValues [][]sqltypes.Value, ksids [][]byte) ([]bool, error) {
	result := make([]bool, len(rowsColValues))
	destinations, _ := rv.Map(vcursor, rowsColValues)
	for i, dest := range destinations {
		destksid, ok := dest.(key.DestinationKeyspaceID)
		if !ok {
			continue
		}
		result[i] = bytes.Equal([]byte(destksid), ksids[i])
	}
	return result, nil
}

// NeedVCursor satisfies the Vindex interface.
func (rv *RegionJson) NeedsVCursor() bool {
	return false
}
<|endoftext|>