<commit_before>package aws

import (
	"fmt"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/iam"

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

func resourceAwsIamPolicy() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsIamPolicyCreate,
		Read:   resourceAwsIamPolicyRead,
		Update: resourceAwsIamPolicyUpdate,
		Delete: resourceAwsIamPolicyDelete,

		Schema: map[string]*schema.Schema{
			"description": &schema.Schema{
				Type:     schema.TypeString,
				ForceNew: true,
				Optional: true,
			},
			"path": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Default:  "/",
				ForceNew: true,
			},
			"policy": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"arn": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

func resourceAwsIamPolicyCreate(d *schema.ResourceData, meta interface{}) error {
	iamconn := meta.(*AWSClient).iamconn
	name := d.Get("name").(string)

	request := &iam.CreatePolicyInput{
		Description:    aws.String(d.Get("description").(string)),
		Path:           aws.String(d.Get("path").(string)),
		PolicyDocument: aws.String(d.Get("policy").(string)),
		PolicyName:     aws.String(name),
	}

	response, err := iamconn.CreatePolicy(request)
	if err != nil {
		return fmt.Errorf("Error creating IAM policy %s: %#v", name, err)
	}

	return readIamPolicy(d, response.Policy)
}

func resourceAwsIamPolicyRead(d *schema.ResourceData, meta interface{}) error {
	iamconn := meta.(*AWSClient).iamconn

	request := &iam.GetPolicyInput{
		PolicyARN: aws.String(d.Id()),
	}

	response, err := iamconn.GetPolicy(request)
	if err != nil {
		if iamerr, ok := err.(awserr.Error); ok && iamerr.Code() == "NoSuchEntity" {
			d.SetId("")
			return nil
		}
		return fmt.Errorf("Error reading IAM policy %s: %#v", d.Id(), err)
	}

	return readIamPolicy(d, response.Policy)
}

func resourceAwsIamPolicyUpdate(d *schema.ResourceData, meta interface{}) error {
	iamconn := meta.(*AWSClient).iamconn

	if err := iamPolicyPruneVersions(d.Id(), iamconn); err != nil {
		return err
	}

	if !d.HasChange("policy") {
		return nil
	}
	request := &iam.CreatePolicyVersionInput{
		PolicyARN:      aws.String(d.Id()),
		PolicyDocument: aws.String(d.Get("policy").(string)),
		SetAsDefault:   aws.Boolean(true),
	}

	if _, err := iamconn.CreatePolicyVersion(request); err != nil {
		return fmt.Errorf("Error updating IAM policy %s: %#v", d.Id(), err)
	}
	return nil
}

func resourceAwsIamPolicyDelete(d *schema.ResourceData, meta interface{}) error {
	iamconn := meta.(*AWSClient).iamconn

	if err := iamPolicyDeleteNondefaultVersions(d.Id(), iamconn); err != nil {
		return err
	}

	request := &iam.DeletePolicyInput{
		PolicyARN: aws.String(d.Id()),
	}

	_, err := iamconn.DeletePolicy(request)
	if err != nil {
		if iamerr, ok := err.(awserr.Error); ok && iamerr.Code() == "NoSuchEntity" {
			return nil
		}
		return fmt.Errorf("Error reading IAM policy %s: %#v", d.Id(), err)
	}
	return nil
}

// iamPolicyPruneVersions deletes the oldest versions.
//
// Old versions are deleted until there are 4 or less remaining, which means at
// least one more can be created before hitting the maximum of 5.
//
// The default version is never deleted.

func iamPolicyPruneVersions(arn string, iamconn *iam.IAM) error {
	versions, err := iamPolicyListVersions(arn, iamconn)
	if err != nil {
		return err
	}
	if len(versions) < 5 {
		return nil
	}

	var oldestVersion *iam.PolicyVersion

	for _, version := range versions {
		if *version.IsDefaultVersion {
			continue
		}
		if oldestVersion == nil ||
			version.CreateDate.Before(*oldestVersion.CreateDate) {
			oldestVersion = version
		}
	}

	if err := iamPolicyDeleteVersion(arn, *oldestVersion.VersionID, iamconn); err != nil {
		return err
	}
	return nil
}

func iamPolicyDeleteNondefaultVersions(arn string, iamconn *iam.IAM) error {
	versions, err := iamPolicyListVersions(arn, iamconn)
	if err != nil {
		return err
	}

	for _, version := range versions {
		if *version.IsDefaultVersion {
			continue
		}
		if err := iamPolicyDeleteVersion(arn, *version.VersionID, iamconn); err != nil {
			return err
		}
	}

	return nil
}

func iamPolicyDeleteVersion(arn, versionID string, iamconn *iam.IAM) error {
	request := &iam.DeletePolicyVersionInput{
		PolicyARN: aws.String(arn),
		VersionID: aws.String(versionID),
	}

	_, err := iamconn.DeletePolicyVersion(request)
	if err != nil {
		return fmt.Errorf("Error deleting version %s from IAM policy %s: %#v", versionID, arn, err)
	}
	return nil
}

func iamPolicyListVersions(arn string, iamconn *iam.IAM) ([]*iam.PolicyVersion, error) {
	request := &iam.ListPolicyVersionsInput{
		PolicyARN: aws.String(arn),
	}

	response, err := iamconn.ListPolicyVersions(request)
	if err != nil {
		return nil, fmt.Errorf("Error listing versions for IAM policy %s: %#v", arn, err)
	}
	return response.Versions, nil
}

func readIamPolicy(d *schema.ResourceData, policy *iam.Policy) error {
	d.SetId(*policy.ARN)
	if policy.Description != nil {
		// the description isn't present in the response to CreatePolicy.
		if err := d.Set("description", *policy.Description); err != nil {
			return err
		}
	}
	if err := d.Set("path", *policy.Path); err != nil {
		return err
	}
	if err := d.Set("name", *policy.PolicyName); err != nil {
		return err
	}
	if err := d.Set("arn", *policy.ARN); err != nil {
		return err
	}
	// TODO: set policy

	return nil
}
<commit_msg>provider/aws: improve iam_policy err msgs<commit_after>package aws

import (
	"fmt"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/iam"

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

func resourceAwsIamPolicy() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsIamPolicyCreate,
		Read:   resourceAwsIamPolicyRead,
		Update: resourceAwsIamPolicyUpdate,
		Delete: resourceAwsIamPolicyDelete,

		Schema: map[string]*schema.Schema{
			"description": &schema.Schema{
				Type:     schema.TypeString,
				ForceNew: true,
				Optional: true,
			},
			"path": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Default:  "/",
				ForceNew: true,
			},
			"policy": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"arn": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

func resourceAwsIamPolicyCreate(d *schema.ResourceData, meta interface{}) error {
	iamconn := meta.(*AWSClient).iamconn
	name := d.Get("name").(string)

	request := &iam.CreatePolicyInput{
		Description:    aws.String(d.Get("description").(string)),
		Path:           aws.String(d.Get("path").(string)),
		PolicyDocument: aws.String(d.Get("policy").(string)),
		PolicyName:     aws.String(name),
	}

	response, err := iamconn.CreatePolicy(request)
	if err != nil {
		return fmt.Errorf("Error creating IAM policy %s: %s", name, err)
	}

	return readIamPolicy(d, response.Policy)
}

func resourceAwsIamPolicyRead(d *schema.ResourceData, meta interface{}) error {
	iamconn := meta.(*AWSClient).iamconn

	request := &iam.GetPolicyInput{
		PolicyARN: aws.String(d.Id()),
	}

	response, err := iamconn.GetPolicy(request)
	if err != nil {
		if iamerr, ok := err.(awserr.Error); ok && iamerr.Code() == "NoSuchEntity" {
			d.SetId("")
			return nil
		}
		return fmt.Errorf("Error reading IAM policy %s: %s", d.Id(), err)
	}

	return readIamPolicy(d, response.Policy)
}

func resourceAwsIamPolicyUpdate(d *schema.ResourceData, meta interface{}) error {
	iamconn := meta.(*AWSClient).iamconn

	if err := iamPolicyPruneVersions(d.Id(), iamconn); err != nil {
		return err
	}

	if !d.HasChange("policy") {
		return nil
	}
	request := &iam.CreatePolicyVersionInput{
		PolicyARN:      aws.String(d.Id()),
		PolicyDocument: aws.String(d.Get("policy").(string)),
		SetAsDefault:   aws.Boolean(true),
	}

	if _, err := iamconn.CreatePolicyVersion(request); err != nil {
		return fmt.Errorf("Error updating IAM policy %s: %s", d.Id(), err)
	}
	return nil
}

func resourceAwsIamPolicyDelete(d *schema.ResourceData, meta interface{}) error {
	iamconn := meta.(*AWSClient).iamconn

	if err := iamPolicyDeleteNondefaultVersions(d.Id(), iamconn); err != nil {
		return err
	}

	request := &iam.DeletePolicyInput{
		PolicyARN: aws.String(d.Id()),
	}

	_, err := iamconn.DeletePolicy(request)
	if err != nil {
		if iamerr, ok := err.(awserr.Error); ok && iamerr.Code() == "NoSuchEntity" {
			return nil
		}
		return fmt.Errorf("Error reading IAM policy %s: %#v", d.Id(), err)
	}
	return nil
}

// iamPolicyPruneVersions deletes the oldest versions.
//
// Old versions are deleted until there are 4 or less remaining, which means at
// least one more can be created before hitting the maximum of 5.
//
// The default version is never deleted.

func iamPolicyPruneVersions(arn string, iamconn *iam.IAM) error {
	versions, err := iamPolicyListVersions(arn, iamconn)
	if err != nil {
		return err
	}
	if len(versions) < 5 {
		return nil
	}

	var oldestVersion *iam.PolicyVersion

	for _, version := range versions {
		if *version.IsDefaultVersion {
			continue
		}
		if oldestVersion == nil ||
			version.CreateDate.Before(*oldestVersion.CreateDate) {
			oldestVersion = version
		}
	}

	if err := iamPolicyDeleteVersion(arn, *oldestVersion.VersionID, iamconn); err != nil {
		return err
	}
	return nil
}

func iamPolicyDeleteNondefaultVersions(arn string, iamconn *iam.IAM) error {
	versions, err := iamPolicyListVersions(arn, iamconn)
	if err != nil {
		return err
	}

	for _, version := range versions {
		if *version.IsDefaultVersion {
			continue
		}
		if err := iamPolicyDeleteVersion(arn, *version.VersionID, iamconn); err != nil {
			return err
		}
	}

	return nil
}

func iamPolicyDeleteVersion(arn, versionID string, iamconn *iam.IAM) error {
	request := &iam.DeletePolicyVersionInput{
		PolicyARN: aws.String(arn),
		VersionID: aws.String(versionID),
	}

	_, err := iamconn.DeletePolicyVersion(request)
	if err != nil {
		return fmt.Errorf("Error deleting version %s from IAM policy %s: %s", versionID, arn, err)
	}
	return nil
}

func iamPolicyListVersions(arn string, iamconn *iam.IAM) ([]*iam.PolicyVersion, error) {
	request := &iam.ListPolicyVersionsInput{
		PolicyARN: aws.String(arn),
	}

	response, err := iamconn.ListPolicyVersions(request)
	if err != nil {
		return nil, fmt.Errorf("Error listing versions for IAM policy %s: %s", arn, err)
	}
	return response.Versions, nil
}

func readIamPolicy(d *schema.ResourceData, policy *iam.Policy) error {
	d.SetId(*policy.ARN)
	if policy.Description != nil {
		// the description isn't present in the response to CreatePolicy.
		if err := d.Set("description", *policy.Description); err != nil {
			return err
		}
	}
	if err := d.Set("path", *policy.Path); err != nil {
		return err
	}
	if err := d.Set("name", *policy.PolicyName); err != nil {
		return err
	}
	if err := d.Set("arn", *policy.ARN); err != nil {
		return err
	}
	// TODO: set policy

	return nil
}
<|endoftext|>


<commit_before>package htlcswitch

import (
	"bytes"
	"encoding/hex"
	"io"

	"github.com/btcsuite/golangcrypto/ripemd160"
	"github.com/roasbeef/btcutil"
)

// HopID represents the id which is used by propagation subsystem in order to
// identify lightning network node.
// TODO(andrew.shvv) remove after switching to the using channel id.
type HopID [ripemd160.Size]byte

// NewHopID creates new instance of hop form node public key.
func NewHopID(pubKey []byte) HopID {
	var routeID HopID
	copy(routeID[:], btcutil.Hash160(pubKey))
	return routeID
}

// String returns string representation of hop id.
func (h HopID) String() string {
	return hex.EncodeToString(h[:])
}

// IsEqual checks does the two hop ids are equal.
func (h HopID) IsEqual(h2 HopID) bool {
	return bytes.Equal(h[:], h2[:])
}

// HopIterator interface represent the entity which is able to give route
// hops one by one. This interface is used to have an abstraction over the
// algorithm which we use to determine the next hope in htlc route.
type HopIterator interface {
	// Next returns next hop if exist and nil if route is ended.
	Next() *HopID

	// Encode encodes iterator and writes it to the writer.
	Encode(w io.Writer) error
}
<commit_msg>htlcswitch: add real sphinx iterator<commit_after>package htlcswitch

import (
	"bytes"
	"encoding/hex"
	"io"

	"github.com/btcsuite/golangcrypto/ripemd160"
	"github.com/go-errors/errors"
	"github.com/lightningnetwork/lightning-onion"
	"github.com/lightningnetwork/lnd/routing"
	"github.com/roasbeef/btcd/btcec"
	"github.com/roasbeef/btcutil"
)

// HopID represents the id which is used by propagation subsystem in order to
// identify lightning network node.
// TODO(andrew.shvv) remove after switching to the using channel id.
type HopID [ripemd160.Size]byte

// NewHopID creates new instance of hop form node public key.
func NewHopID(pubKey []byte) HopID {
	var routeID HopID
	copy(routeID[:], btcutil.Hash160(pubKey))
	return routeID
}

// String returns string representation of hop id.
func (h HopID) String() string {
	return hex.EncodeToString(h[:])
}

// IsEqual checks does the two hop ids are equal.
func (h HopID) IsEqual(h2 HopID) bool {
	return bytes.Equal(h[:], h2[:])
}

// HopIterator interface represent the entity which is able to give route
// hops one by one. This interface is used to have an abstraction over the
// algorithm which we use to determine the next hope in htlc route.
type HopIterator interface {
	// Next returns next hop if exist and nil if route is ended.
	Next() *HopID

	// Encode encodes iterator and writes it to the writer.
	Encode(w io.Writer) error
}

// sphinxHopIterator is the Sphinx implementation of hop iterator which uses
// onion routing to encode route in such a way so that node might see only the
// next hop in the route, after retrieving hop iterator will behave as if
// there is no hop in path.
type sphinxHopIterator struct {
	onionPacket  *sphinx.OnionPacket
	sphinxPacket *sphinx.ProcessedPacket
}

// // A compile time check to ensure sphinxHopIterator implements the HopIterator
// interface.
var _ HopIterator = (*sphinxHopIterator)(nil)

// NewSphinxBlob creates new instance of sphinx hop iterator.
func NewSphinxBlob(route *routing.Route, paymentHash []byte) ([]byte, error) {

	// First obtain all the public keys along the route which are contained
	// in each hop.
	nodes := make([]*btcec.PublicKey, len(route.Hops))
	for i, hop := range route.Hops {
		// We create a new instance of the public key to avoid possibly
		// mutating the curve parameters, which are unset in a higher
		// level in order to avoid spamming the logs.
		pub := btcec.PublicKey{
			Curve: btcec.S256(),
			X:     hop.Channel.Node.PubKey.X,
			Y:     hop.Channel.Node.PubKey.Y,
		}
		nodes[i] = &pub
	}

	// Next we generate the per-hop payload which gives each node within
	// the route the necessary information (fees, CLTV value, etc) to
	// properly forward the payment.
	// TODO(roasbeef): properly set CLTV value, payment amount, and chain
	// within hop payloads.
	var hopPayloads [][]byte
	for i := 0; i < len(route.Hops); i++ {
		payload := bytes.Repeat([]byte{byte('A' + i)},
			sphinx.HopPayloadSize)
		hopPayloads = append(hopPayloads, payload)
	}

	sessionKey, err := btcec.NewPrivateKey(btcec.S256())
	if err != nil {
		return nil, err
	}

	// Next generate the onion routing packet which allows us to perform
	// privacy preserving source routing across the network.
	onionPacket, err := sphinx.NewOnionPacket(nodes, sessionKey,
		hopPayloads, paymentHash)
	if err != nil {
		return nil, err
	}

	// Finally, encode Sphinx packet using it's wire representation to be
	// included within the HTLC add packet.
	var onionBlob bytes.Buffer
	if err := onionPacket.Encode(&onionBlob); err != nil {
		return nil, err
	}

	return onionBlob.Bytes(), nil
}

// Encode encodes iterator and writes it to the writer.
// NOTE: Part of the HopIterator interface.
func (r *sphinxHopIterator) Encode(w io.Writer) error {
	return r.onionPacket.Encode(w)
}

// Next returns next hop if exist and nil if route is ended.
// NOTE: Part of the HopIterator interface.
func (r *sphinxHopIterator) Next() *HopID {
	// If next node was already given than behave as if no hops in route.
	if r.sphinxPacket == nil {
		return nil
	}

	switch r.sphinxPacket.Action {
	case sphinx.ExitNode:
		return nil

	case sphinx.MoreHops:
		id := (*HopID)(&r.sphinxPacket.NextHop)
		r.sphinxPacket = nil
		return id
	}

	return nil
}

// SphinxDecoder is responsible for keeping all sphinx dependent parts inside
// and expose only decoding function. With such approach we give freedom for
// subsystems which wants to decode sphinx path to not be dependable from sphinx
// at all.
//
// NOTE: The reason for keeping decoder separated from hop iterator is too
// maintain the hop iterator abstraction. Without it the structures which using
// the hop iterator should contain sphinx router which makes their
// creations in tests dependent from the sphinx internal parts.
type SphinxDecoder struct {
	router *sphinx.Router
}

// NewSphinxDecoder creates new instance of decoder.
func NewSphinxDecoder(router *sphinx.Router) *SphinxDecoder {
	return &SphinxDecoder{router}
}

// Decode takes byte stream as input and decodes the route/ hop iterator.
func (p *SphinxDecoder) Decode(r io.Reader, rHash []byte) (HopIterator, error) {
	// Before adding the new HTLC to the state machine, parse the
	// onion object in order to obtain the routing information.
	onionPkt := &sphinx.OnionPacket{}
	if err := onionPkt.Decode(r); err != nil {
		return nil, errors.Errorf("unable to decode onion pkt: %v",
			err)

	}

	// Attempt to process the Sphinx packet. We include the payment
	// hash of the HTLC as it's authenticated within the Sphinx
	// packet itself as associated data in order to thwart attempts
	// a replay attacks. In the case of a replay, an attacker is
	// *forced* to use the same payment hash twice, thereby losing
	// their money entirely.
	sphinxPacket, err := p.router.ProcessOnionPacket(onionPkt, rHash)
	if err != nil {
		return nil, errors.Errorf("unable to process onion pkt: "+
			"%v", err)
	}

	return HopIterator(&sphinxHopIterator{
		onionPacket:  sphinxPacket.Packet,
		sphinxPacket: sphinxPacket,
	}), nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"

	"github.com/paulcull/go-webbrick"

	"github.com/ninjasphere/go-ninja/api"
	"github.com/ninjasphere/go-ninja/channels"
	"github.com/ninjasphere/go-ninja/logger"
	"github.com/ninjasphere/go-ninja/model"

	//	"log"
	"regexp"
	"strings"
)

const (
	UNKNOWN = -1 + iota // UNKNOWN is obviously a device that isn't implemented or is unknown. iota means add 1 to the next const, so SOCKET = 0, ALLONE = 1 etc.

	LIGHT     // LIGHT - is possibly a dimmer
	PIR       // PIR - Trigger
	BUTTON    // Pushbutton - Trigger
	TEMP      // Temp sensor
	STATE     // State
	HEARTBEAT // Heartbeat

)

// webBrickDevice holds info about our socket.
type WebbrickDevice struct {
	driver             ninja.Driver
	info               *model.Device
	sendEvent          func(event string, payload interface{}) error
	onOffChannel       *channels.OnOffChannel
	brightnessChannel  *channels.BrightnessChannel
	motionChannel      *channels.MotionChannel
	temperatureChannel *channels.TemperatureChannel
	Device             webbrick.Device
	log                *logger.Logger
}

func NewWebbrickDevice(driver ninja.Driver, id webbrick.Device) *WebbrickDevice {
	//name := id.Name

	log.Infof("In creating NewWebbrickDevie", id.Name)

	var devProductType, devThingType string

	switch id.Type {
	case LIGHT:
		devProductType = "light"
		devThingType = "Light"
	case PIR:
		devProductType = "motion"
		devThingType = "Motion"
	case STATE:
		devProductType = "state"
		devThingType = "State"
	case BUTTON:
		devProductType = "button"
		devThingType = "Button"
	case TEMP:
		devProductType = "temperature"
		devThingType = "Temperature"
	default:
		devProductType = "unknown"
		devThingType = "Unknown"
	}

	// LIGHT     // LIGHT - is possibly a dimmer
	// PIR       // PIR - Trigger
	// BUTTON    // Pushbutton - Trigger
	// TEMP      // Temp sensor
	// STATE     // State
	// HEARTBEAT // Heartbeat

	log.Infof("Creating a new Device, type: %s. Name now: %s", devThingType, id.Name)

	device := &WebbrickDevice{
		driver: driver,
		Device: id,
		info: &model.Device{
			NaturalID:     fmt.Sprintf("%s", id.DevID),
			NaturalIDType: devProductType,
			Name:          &id.Name,
			Signatures: &map[string]string{
				"ninja:manufacturer": "Webbrick",
				"ninja:productName":  "Webbrick" + devThingType + "Device",
				"ninja:productType":  devProductType,
				"ninja:thingType":    devThingType,
			},
		},
		log: logger.GetLogger(devThingType + " Device - " + id.Name),
	}

	log.Debugf(" ******* GOING TO CHECK FOR THE devProductType : %s", devProductType)

	if devProductType == "light" {
		device.onOffChannel = channels.NewOnOffChannel(device)
		device.brightnessChannel = channels.NewBrightnessChannel(device)
	}
	if devProductType == "state" || devProductType == "button" {
		device.onOffChannel = channels.NewOnOffChannel(device)
	}
	if devProductType == "temperature" {
		log.Debugf(" ** CREATING Temp Channel **")
		device.temperatureChannel = channels.NewTemperatureChannel(device)
	}
	if devProductType == "motion" {
		device.motionChannel = channels.NewMotionChannel()
	}
	return device
}

func (d *WebbrickDevice) GetDeviceInfo() *model.Device {
	return d.info
}

func (d *WebbrickDevice) GetDriver() ninja.Driver {
	return d.driver
}

func (d *WebbrickDevice) SetBrightness(level float64) error {
	log.Debugf(" - Setting Brightness level to", level)
	webbrick.SetLightLevel(d.Device.DevID, level)
	d.brightnessChannel.SendState(level)
	return nil
}

func (d *WebbrickDevice) SetOnOff(state bool) error {
	log.Debugf("Setting state to", state)
	webbrick.SetState(d.Device.DevID, state)
	d.onOffChannel.SendState(state)
	return nil
}

func (d *WebbrickDevice) ToggleOnOff() error {
	log.Debugf("Toggling state")
	webbrick.ToggleState(d.Device.DevID)
	d.onOffChannel.SendState(d.Device.State)
	return nil
}

func (d *WebbrickDevice) PushButton() error {
	log.Debugf("Pushing Button")
	webbrick.PushButton(d.Device.DevID)
	return nil
}

func (d *WebbrickDevice) SetEventHandler(sendEvent func(event string, payload interface{}) error) {
	d.sendEvent = sendEvent
}

var reg, _ = regexp.Compile("[^a-z0-9]")

//Exported by service/device schema
func (d *WebbrickDevice) SetName(name *string) (*string, error) {

	log.Infof("Setting device name to %s", *name)

	safe := reg.ReplaceAllString(strings.ToLower(*name), "")
	if len(safe) > 16 {
		safe = safe[0:16]
	}

	log.Warningf("We can only set 5 lowercase alphanum. Name now: %s", safe)
	d.Device.Name = safe
	d.sendEvent("renamed", safe)

	return &safe, nil
}
<commit_msg>debugging<commit_after>package main

import (
	"fmt"

	"github.com/paulcull/go-webbrick"

	"github.com/ninjasphere/go-ninja/api"
	"github.com/ninjasphere/go-ninja/channels"
	"github.com/ninjasphere/go-ninja/logger"
	"github.com/ninjasphere/go-ninja/model"

	//	"log"
	"regexp"
	"strings"
)

const (
	UNKNOWN = -1 + iota // UNKNOWN is obviously a device that isn't implemented or is unknown. iota means add 1 to the next const, so SOCKET = 0, ALLONE = 1 etc.

	LIGHT     // LIGHT - is possibly a dimmer
	PIR       // PIR - Trigger
	BUTTON    // Pushbutton - Trigger
	TEMP      // Temp sensor
	STATE     // State
	HEARTBEAT // Heartbeat

)

// webBrickDevice holds info about our socket.
type WebbrickDevice struct {
	driver             ninja.Driver
	info               *model.Device
	sendEvent          func(event string, payload interface{}) error
	onOffChannel       *channels.OnOffChannel
	brightnessChannel  *channels.BrightnessChannel
	motionChannel      *channels.MotionChannel
	temperatureChannel *channels.TemperatureChannel
	Device             webbrick.Device
	log                *logger.Logger
}

func NewWebbrickDevice(driver ninja.Driver, id webbrick.Device) *WebbrickDevice {
	//name := id.Name

	log.Infof("In creating NewWebbrickDevie", id.Name)

	var devProductType, devThingType string

	switch id.Type {
	case LIGHT:
		devProductType = "light"
		devThingType = "Light"
	case PIR:
		devProductType = "motion"
		devThingType = "Motion"
	case STATE:
		devProductType = "state"
		devThingType = "State"
	case BUTTON:
		devProductType = "button"
		devThingType = "Button"
	case TEMP:
		devProductType = "temperature"
		devThingType = "Temperature"
	default:
		devProductType = "unknown"
		devThingType = "Unknown"
	}

	// LIGHT     // LIGHT - is possibly a dimmer
	// PIR       // PIR - Trigger
	// BUTTON    // Pushbutton - Trigger
	// TEMP      // Temp sensor
	// STATE     // State
	// HEARTBEAT // Heartbeat

	log.Infof("Creating a new Device, type: %s. Name now: %s", devThingType, id.Name)

	if id.Name == "" {
		id.Name = fmt.Sprintf("%s", id.DevID)
	}

	device := &WebbrickDevice{
		driver: driver,
		Device: id,
		info: &model.Device{
			NaturalID:     fmt.Sprintf("%s", id.DevID),
			NaturalIDType: devProductType,
			Name:          &id.Name,
			Signatures: &map[string]string{
				"ninja:manufacturer": "Webbrick",
				"ninja:productName":  "Webbrick" + devThingType + "Device",
				"ninja:productType":  devProductType,
				"ninja:thingType":    devThingType,
			},
		},
		log: logger.GetLogger(devThingType + " Device - " + id.Name),
	}

	log.Debugf(" ******* GOING TO CHECK FOR THE devProductType : %s", devProductType)

	if devProductType == "light" {
		device.onOffChannel = channels.NewOnOffChannel(device)
		device.brightnessChannel = channels.NewBrightnessChannel(device)
	}
	if devProductType == "state" || devProductType == "button" {
		device.onOffChannel = channels.NewOnOffChannel(device)
	}
	if devProductType == "temperature" {
		log.Debugf(" ** CREATING Temp Channel **")
		device.temperatureChannel = channels.NewTemperatureChannel(device)
	}
	if devProductType == "motion" {
		device.motionChannel = channels.NewMotionChannel()
	}
	return device
}

func (d *WebbrickDevice) GetDeviceInfo() *model.Device {
	return d.info
}

func (d *WebbrickDevice) GetDriver() ninja.Driver {
	return d.driver
}

func (d *WebbrickDevice) SetBrightness(level float64) error {
	fmt.Printf(" - Setting Brightness level to", level)
	log.Debugf(" - Setting Brightness level to", level)
	webbrick.SetLightLevel(d.Device.DevID, level)

	d.brightnessChannel.SendState(level)
	return nil
}

func (d *WebbrickDevice) SetOnOff(state bool) error {
	log.Debugf("Setting state to", state)
	webbrick.SetState(d.Device.DevID, state)
	d.onOffChannel.SendState(state)
	return nil
}

func (d *WebbrickDevice) ToggleOnOff() error {
	log.Debugf("Toggling state")
	webbrick.ToggleState(d.Device.DevID)
	d.onOffChannel.SendState(d.Device.State)
	return nil
}

func (d *WebbrickDevice) PushButton() error {
	log.Debugf("Pushing Button")
	webbrick.PushButton(d.Device.DevID)
	return nil
}

func (d *WebbrickDevice) SetEventHandler(sendEvent func(event string, payload interface{}) error) {
	d.sendEvent = sendEvent
}

var reg, _ = regexp.Compile("[^a-z0-9]")

//Exported by service/device schema
func (d *WebbrickDevice) SetName(name *string) (*string, error) {

	log.Infof("Setting device name to %s", *name)

	safe := reg.ReplaceAllString(strings.ToLower(*name), "")
	if len(safe) > 16 {
		safe = safe[0:16]
	}

	log.Warningf("We can only set 5 lowercase alphanum. Name now: %s", safe)
	d.Device.Name = safe
	d.sendEvent("renamed", safe)

	return &safe, nil
}
<|endoftext|>


<commit_before>package qb

import (
	"fmt"
	"github.com/fatih/structs"
	"github.com/serenize/snaker"
	"strings"
)

const tagPrefix = "qb"

// NewMapper instantiates a new mapper object and returns it as a mapper pointer
func NewMapper(driver string) *Mapper {
	return &Mapper{
		driver: driver,
	}
}

// Mapper is the generic struct for struct to table mapping
type Mapper struct {
	driver string
}

func (m *Mapper) extractValue(value string) string {

	hasParams := strings.Contains(value, "(") && strings.Contains(value, ")")

	if hasParams {
		startIndex := strings.Index(value, "(")
		endIndex := strings.Index(value, ")")
		return value[startIndex+1 : endIndex]
	}

	return ""
}

// ConvertType returns the type mapping of column.
// If tagType is, then colType would automatically be resolved.
// If tagType is not "", then automatic type resolving would be overridden by tagType
func (m *Mapper) ConvertType(colType string, tagType string) *Type {

	// convert tagType
	if tagType != "" {
		tagType = strings.ToUpper(tagType)
		return &Type{func() string { return tagType }}
	}

	// convert default type
	switch colType {
	case "string":
		return VarChar()
	case "int":
		return Int()
	case "int64":
		return BigInt()
	case "float32":
		return Float()
	case "float64":
		return Float()
	case "bool":
		return Boolean()
	case "time.Time":
		return Timestamp()
	case "*time.Time":
		return Timestamp()
	default:
		return VarChar()
	}
}

// Convert parses struct and converts it to a new table
func (m *Mapper) Convert(model interface{}) (*Table, error) {

	modelName := snaker.CamelToSnake(structs.Name(model))

	table := &Table{
		name:        modelName,
		columns:     []Column{},
		constraints: []Constraint{},
		builder:     NewBuilder(),
	}

	fmt.Printf("model name: %s\n\n", modelName)

	var col Column
	var rawTag string

	for _, f := range structs.Fields(model) {

		colName := snaker.CamelToSnake(f.Name())
		colType := fmt.Sprintf("%T", f.Value())

		rawTag = f.Tag(tagPrefix)

		constraints := []Constraint{}
		fmt.Printf("field name: %s\n", colName)
		fmt.Printf("field raw tag: %s\n", rawTag)
		fmt.Printf("field type name: %T\n", f.Value())
		fmt.Printf("field constraints: %v\n", constraints)

		// clean trailing spaces of tag
		rawTag = strings.Replace(f.Tag(tagPrefix), " ", "", 1)

		// parse tag
		tag, err := ParseTag(rawTag)
		if err != nil {
			return nil, err
		}

		// convert tag into constraints
		var constraint Constraint
		for _, v := range tag.Constraints {
			if v == "null" {
				constraint = Null()
			} else if v == "notnull" {
				constraint = NotNull()
			} else if v == "unique" {
				constraint = Constraint{
					Name: "UNIQUE",
				}
			} else if strings.Contains(v, "default") {
				constraint = Default(m.extractValue(v))
			} else if strings.Contains(v, "primary_key") {
				table.AddPrimary(colName)
				continue
			} else if strings.Contains(v, "ref") && strings.Contains(v, "(") && strings.Contains(v, ")") {
				tc := strings.Split(m.extractValue(v), ".")
				table.AddRef(colName, tc[0], tc[1])
				continue
			} else {
				return nil, fmt.Errorf("Invalid constraint: %s", v)
			}
			constraints = append(constraints, constraint)
		}

		fmt.Printf("field tag.Type: %s\n", tag.Type)
		fmt.Printf("field tag.Constraints: %v\n", tag.Constraints)

		col = Column{
			Name:        colName,
			Constraints: constraints,
			Type:        m.ConvertType(colType, tag.Type),
		}

		table.AddColumn(col)

		fmt.Println()
	}

	return table, nil
}
<commit_msg>add support for postgres mapper definitions<commit_after>package qb

import (
	"fmt"
	"github.com/fatih/structs"
	"github.com/serenize/snaker"
	"strings"
)

const tagPrefix = "qb"

// NewMapper instantiates a new mapper object and returns it as a mapper pointer
func NewMapper(driver string) *Mapper {
	return &Mapper{
		driver: driver,
	}
}

// Mapper is the generic struct for struct to table mapping
type Mapper struct {
	driver string
}

func (m *Mapper) extractValue(value string) string {

	hasParams := strings.Contains(value, "(") && strings.Contains(value, ")")

	if hasParams {
		startIndex := strings.Index(value, "(")
		endIndex := strings.Index(value, ")")
		return value[startIndex+1 : endIndex]
	}

	return ""
}

// ConvertType returns the type mapping of column.
// If tagType is, then colType would automatically be resolved.
// If tagType is not "", then automatic type resolving would be overridden by tagType
func (m *Mapper) ConvertType(colType string, tagType string) *Type {

	// convert tagType
	if tagType != "" {
		tagType = strings.ToUpper(tagType)
		return &Type{func() string { return tagType }}
	}

	// convert default type
	switch colType {
	case "string":
		return VarChar()
	case "int":
		return Int()
	case "int64":
		return BigInt()
	case "float32":
		return Float()
	case "float64":
		return Float()
	case "bool":
		return Boolean()
	case "time.Time":
		return Timestamp()
	case "*time.Time":
		return Timestamp()
	default:
		return VarChar()
	}
}

// Convert parses struct and converts it to a new table
func (m *Mapper) Convert(model interface{}) (*Table, error) {

	modelName := snaker.CamelToSnake(structs.Name(model))
	if m.driver != "postgres" {
		modelName = fmt.Sprintf("`%s`", modelName)
	}

	table := &Table{
		name:        modelName,
		columns:     []Column{},
		constraints: []Constraint{},
		builder:     NewBuilder(m.driver),
	}

	fmt.Printf("model name: %s\n\n", modelName)

	var col Column
	var rawTag string

	for _, f := range structs.Fields(model) {

		colName := snaker.CamelToSnake(f.Name())
		if m.driver != "postgres" {
			colName = fmt.Sprintf("`%s`", colName)
		}
		colType := fmt.Sprintf("%T", f.Value())

		rawTag = f.Tag(tagPrefix)

		constraints := []Constraint{}
		fmt.Printf("field name: %s\n", colName)
		fmt.Printf("field raw tag: %s\n", rawTag)
		fmt.Printf("field type name: %T\n", f.Value())
		fmt.Printf("field constraints: %v\n", constraints)

		// clean trailing spaces of tag
		rawTag = strings.Replace(f.Tag(tagPrefix), " ", "", 1)

		// parse tag
		tag, err := ParseTag(rawTag)
		if err != nil {
			return nil, err
		}

		// convert tag into constraints
		var constraint Constraint
		for _, v := range tag.Constraints {
			if v == "null" {
				constraint = Null()
			} else if v == "notnull" {
				constraint = NotNull()
			} else if v == "unique" {
				constraint = Constraint{
					Name: "UNIQUE",
				}
			} else if strings.Contains(v, "default") {
				constraint = Default(m.extractValue(v))
			} else if strings.Contains(v, "primary_key") {
				table.AddPrimary(colName)
				continue
			} else if strings.Contains(v, "ref") && strings.Contains(v, "(") && strings.Contains(v, ")") {
				tc := strings.Split(m.extractValue(v), ".")
				table.AddRef(colName, tc[0], tc[1])
				continue
			} else {
				return nil, fmt.Errorf("Invalid constraint: %s", v)
			}
			constraints = append(constraints, constraint)
		}

		fmt.Printf("field tag.Type: %s\n", tag.Type)
		fmt.Printf("field tag.Constraints: %v\n", tag.Constraints)

		col = Column{
			Name:        colName,
			Constraints: constraints,
			Type:        m.ConvertType(colType, tag.Type),
		}

		table.AddColumn(col)

		fmt.Println()
	}

	return table, nil
}
<|endoftext|>


<commit_before>// TODO: For structs that define a ResourceURI, add a method to fetch those
//       contents and parse them into the correct response struct.
//       e.g., Series(123).Data.Results[0].Characters.Items[0].Get()...
// TODO: Add a test to fetch a resource, serialize it into JSON and compare
//       it against the response JSON to catch missing fields
// TODO: Figure out the correct incantation to let Series not have to take an
//       empty struct and instead take nil
// TODO: Find/write Swagger Go client generator?

package marvel

import (
	"crypto/md5"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"time"

	"github.com/google/go-querystring/query"
)

type Client struct {
	public, private string
}

func NewClient(public, private string) Client {
	return Client{public, private}
}

// See http://developer.marvel.com/documentation/authorization
func (c Client) hash() (int64, string) {
	ts := time.Now().Unix()
	hash := md5.New()
	io.WriteString(hash, fmt.Sprintf("%d%s%s", ts, c.private, c.public))
	return ts, fmt.Sprintf("%x", hash.Sum(nil))
}

func (c Client) baseURL(req interface{}) url.URL {
	u := url.URL{
		Scheme: "https",
		Host:   "gateway.marvel.com",
		Path:   "/v1/public/",
	}
	ts, hash := c.hash()
	u.RawQuery = url.Values(map[string][]string{
		"ts":     []string{fmt.Sprintf("%d", ts)},
		"apikey": []string{c.public},
		"hash":   []string{hash},
	}).Encode()
	if req != nil {
		q, _ := query.Values(req)
		u.RawQuery += "&" + q.Encode()
	}
	return u
}

// Fields common to all response entities
type commonResponse struct {
	Code            int
	ETag            string
	Status          string
	Copyright       string
	AttributionText string
	AttributionHTML string
}

type CommonRequest struct {
	Offset int `url:"offset,omitempty"`
	Limit  int `url:"limit,omitempty"`
}

// Fields common to data that lists entities, with pagination
type commonList struct {
	Offset int
	Limit  int
	Total  int
	Count  int
}

type Image struct {
	Path      string
	Extension string
}

type Variant string

var (
	PortraitSmall       = Variant("portrait_small")
	PortraitMedium      = Variant("portrait_medium")
	PortraitXLarge      = Variant("portrait_xlarge")
	PortraitFantastic   = Variant("portrait_fantastic")
	PortraitUncanny     = Variant("portrait_uncanny")
	PortraitIncredible  = Variant("portrait_incredible")
	StandardSmall       = Variant("standard_small")
	StandardMedium      = Variant("standard_medium")
	StandardXLarge      = Variant("standard_xlarge")
	StandardFantastic   = Variant("standard_fantastic")
	StandardUncanny     = Variant("standard_uncanny")
	StandardIncredible  = Variant("standard_incredible")
	LandscapeSmall      = Variant("landscape_small")
	LandscapeMedium     = Variant("landscape_medium")
	LandscapeXLarge     = Variant("landscape_xlarge")
	LandscapeFantastic  = Variant("landscape_fantastic")
	LandscapeUncanny    = Variant("landscape_uncanny")
	LandscapeIncredible = Variant("landscape_incredible")
)

func (i Image) URL(v Variant) string {
	return fmt.Sprintf("%s/%s.%s", i.Path, string(v), i.Extension)
}

type Date string

const dateLayout = "2006-01-02T15:04:05-0700"

func (d Date) Parse() time.Time {
	t, err := time.Parse(dateLayout, string(d))
	if err != nil {
		panic(err)
	}
	return t
}

func (c Client) Series(id int64, req CommonRequest) (resp struct {
	commonResponse
	Data struct {
		commonList
		Results []Series
	}
}, err error) {
	u := c.baseURL(req)
	u.Path += fmt.Sprintf("series/%d/comics", id)
	r, err := c.fetch(u)
	if err != nil {
		return
	}
	defer r.Close()
	err = json.NewDecoder(r).Decode(&resp)
	return
}

func (c Client) fetch(u url.URL) (io.ReadCloser, error) {
	resp, err := http.Get(u.String())
	if err != nil {
		return nil, err
	}
	if resp.StatusCode >= http.StatusBadRequest {
		slurp, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return nil, err
		}
		return nil, fmt.Errorf("error response from API: %d\n%s", resp.StatusCode, slurp)
	}
	return resp.Body, nil
}

type resourceList struct {
	Available     int
	Returned      int
	CollectionURI string
}

type Character struct {
	ResourceURI string
	ID          *int           `json:"id,omitempty"`
	Name        *string        `json:"name,omitempty"`
	Description *string        `json:"description,omitempty"`
	Modified    *Date          `json:"modified,omitempty"`
	URLs        []CharacterURL `json:"urls,omitempty"`
	Thumbnail   *Image         `json:"thumbnail,omitempty"`
	Comics      *ComicsList    `json:"comics,omitempty"`
	Stories     *StoriesList   `json:"stories,omitempty"`
	Events      *EventsList    `json:"events,omitempty"`
	Series      *SeriesList    `json:"series,omitempty"`
}

type CharacterURL URL

func (cu CharacterURL) Get() Character {
	return Character{} // TODO
}

type CharactersList struct {
	resourceList
	Items []Character
}

type Comic struct {
	ResourceURI        string
	ID                 int             `json:"id,omitempty"`
	Name               string          `json:"id,omitempty"`
	DigitalID          *int            `json:"digitalId,omitempty"`
	Title              *string         `json:"title,omitempty"`
	IssueNumber        *int            `json:"issueNumber,omitempty"`
	VariantDescription *string         `json:"variantDescription,omitEmpty"`
	Description        *string         `json:"description,omitempty"`
	Modified           *Date           `json:"modified,omitempty"`
	ISBN               *string         `json:"isbn,omitempty"`
	UPC                *string         `json:"upc,omitempty"`
	DiamondCode        *string         `json:"diamondCode,omitempty"`
	EAN                *string         `json:"ean,omitempty"`
	ISSN               *string         `json:"issn,omitempty"`
	Format             *string         `json:"format,omitempty"`
	PageCount          *int            `json:"pageCount,omitEmpty"`
	TextObjects        []TextObject    `json:"textObjects,omitempty"`
	URLs               []URL           `json:"urls,omitempty"`
	Series             *Series         `json:"series,omitempty"`
	Variants           []Comic         `json:"variants,omitempty"`
	Collections        []Comic         `json:"collections,omitempty"`
	CollectedIssues    []Comic         `json:"collectedIssues,omitempty"`
	Dates              []ComicDate     `json:"dates,omitempty"`
	Prices             []ComicPrice    `json:"prices,omitempty"`
	Thumbnail          *Image          `json:"thumbnail,omitempty"`
	Images             []Image         `json:"images,omitempty"`
	Creators           *CreatorsList   `json:"creators,omitempty"`
	Characters         *CharactersList `json:"characters,omitempty"`
	Stories            *StoriesList    `json:"stories,omitempty"`
	Events             *EventsList     `json:"events,omitempty"`
}

type TextObject struct {
	Type     string
	Language string
	Text     string
}

type ComicDate struct {
	Type string
	Date Date
}

type ComicPrice struct {
	Type  string
	Price float64
}

type ComicsList struct {
	resourceList
	Items []Comic
}

type Story struct {
	ResourceURI   string
	ID            *int            `json:"id,omitempty"`
	Name          *string         `json:"name,omitempty"`
	Title         *string         `json:"title,omitempty"`
	Description   *string         `json:"description,omitempty"`
	Type          *string         `json:"type,omitempty"`
	Modified      *Date           `json:"date,omitempty"`
	Thumbnail     *Image          `json:"image,omitempty"`
	Comics        *ComicsList     `json:"comics,omitempty"`
	Series        *SeriesList     `json:"series,omitempty"`
	Events        *EventsList     `json:"events,omitempty"`
	Characters    *CharactersList `json:"characters,omitempty"`
	Creators      *CreatorsList   `json:"creators,omitempty"`
	OriginalIssue Comic
}

type StoriesList struct {
	resourceList
	Items []Story
}

type Event struct {
	ResourceURI string
	ID          *int            `json:"id,omitempty"`
	Title       *string         `json:"title,omitempty"`
	Description *string         `json:"description,omitempty"`
	URLs        []URL           `json:"urls,omitempty"`
	Modified    *Date           `json:"modified,omitempty"`
	Start       *Date           `json:"start,omitempty"`
	End         *Date           `json:"end,omitempty"`
	Thumbnail   *Image          `json:"thumbnail,omitempty"`
	Comics      *ComicsList     `json:"comics,omitempty"`
	Stories     *StoriesList    `json:"stories,omitempty"`
	Series      *SeriesList     `json:"series,omitempty"`
	Characters  *CharactersList `json:"characters,omitempty"`
	Creators    *CreatorsList   `json:"creators,omitempty"`
	Next        *Event          `json:"next,omitempty"`
	Previous    *Event          `json:"next,omitempty"`
}

type EventsList struct {
	resourceList
	Items []Event
}

type Series struct {
	ResourceURI string
	ID          *int            `json:"id,omitempty"`
	Name        *string         `json:"name,omitempty"`
	Title       *string         `json:"title,omitempty"`
	Description *string         `json:"description,omitempty"`
	URLs        []URL           `json:"urls,omitempty"`
	StartYear   *int            `json:"startYear,omitempty"`
	EndYear     *int            `json:"endYear,omitempty"`
	Rating      *string         `json:"rating,omitempty"`
	Modified    *Date           `json:"modified,omitempty"`
	Thumbnail   *Image          `json:"thumbnail,omitempty"`
	Comics      *ComicsList     `json:"comics,omitempty"`
	Stories     *StoriesList    `json:"stories,omitempty"`
	Events      *EventsList     `json:"events,omitempty"`
	Characters  *CharactersList `json:"characters,omitempty"`
	Creators    *CreatorsList   `json:"creators,omitempty"`
	Next        *Series         `json:"next,omitempty"`
	Previous    *Series         `json:"next,omitempty"`
}

type SeriesList struct {
	resourceList
	Items []Series
}

type Creator struct {
	ResourceURI string
	ID          *int         `json:"id,omitempty"`
	Name        *string      `json:"name,omitempty"`
	FirstName   *string      `json:"firstName,omitempty"`
	MiddleName  *string      `json:"middleName,omitempty"`
	LastName    *string      `json:"lastName,omitempty"`
	Suffix      *string      `json:"suffix,omitempty"`
	FullName    *string      `json:"fullName,omitempty"`
	Modified    *Date        `json:"modified,omitempty"`
	URLs        []URL        `json:"urls,omitempty"`
	Thumbnail   *Image       `json:"thumbnail,omitempty"`
	Series      *SeriesList  `json:"series,omitempty"`
	Stories     *StoriesList `json:"stories,omitempty"`
	Comics      *ComicsList  `json:"comics,omitempty"`
	Events      *EventsList  `json:"events,omitempty"`
}

type CreatorsList struct {
	resourceList
	Items []Creator
}

// TODO: Replace with subtypes that know what their response will be, with a Fetch() method to pull down and deserialize correctly.
type URL struct {
	Type, URL string
}
<commit_msg>Remove CharacterURL for now<commit_after>// TODO: For structs that define a ResourceURI, add a method to fetch those
//       contents and parse them into the correct response struct.
//       e.g., Series(123).Data.Results[0].Characters.Items[0].Get()...
// TODO: Add a test to fetch a resource, serialize it into JSON and compare
//       it against the response JSON to catch missing fields
// TODO: Figure out the correct incantation to let Series not have to take an
//       empty struct and instead take nil
// TODO: Find/write Swagger Go client generator?

package marvel

import (
	"crypto/md5"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"time"

	"github.com/google/go-querystring/query"
)

type Client struct {
	public, private string
}

func NewClient(public, private string) Client {
	return Client{public, private}
}

// See http://developer.marvel.com/documentation/authorization
func (c Client) hash() (int64, string) {
	ts := time.Now().Unix()
	hash := md5.New()
	io.WriteString(hash, fmt.Sprintf("%d%s%s", ts, c.private, c.public))
	return ts, fmt.Sprintf("%x", hash.Sum(nil))
}

func (c Client) baseURL(req interface{}) url.URL {
	u := url.URL{
		Scheme: "https",
		Host:   "gateway.marvel.com",
		Path:   "/v1/public/",
	}
	ts, hash := c.hash()
	u.RawQuery = url.Values(map[string][]string{
		"ts":     []string{fmt.Sprintf("%d", ts)},
		"apikey": []string{c.public},
		"hash":   []string{hash},
	}).Encode()
	if req != nil {
		q, _ := query.Values(req)
		u.RawQuery += "&" + q.Encode()
	}
	return u
}

// Fields common to all response entities
type commonResponse struct {
	Code            int
	ETag            string
	Status          string
	Copyright       string
	AttributionText string
	AttributionHTML string
}

type CommonRequest struct {
	Offset int `url:"offset,omitempty"`
	Limit  int `url:"limit,omitempty"`
}

// Fields common to data that lists entities, with pagination
type commonList struct {
	Offset int
	Limit  int
	Total  int
	Count  int
}

type Image struct {
	Path      string
	Extension string
}

type Variant string

var (
	PortraitSmall       = Variant("portrait_small")
	PortraitMedium      = Variant("portrait_medium")
	PortraitXLarge      = Variant("portrait_xlarge")
	PortraitFantastic   = Variant("portrait_fantastic")
	PortraitUncanny     = Variant("portrait_uncanny")
	PortraitIncredible  = Variant("portrait_incredible")
	StandardSmall       = Variant("standard_small")
	StandardMedium      = Variant("standard_medium")
	StandardXLarge      = Variant("standard_xlarge")
	StandardFantastic   = Variant("standard_fantastic")
	StandardUncanny     = Variant("standard_uncanny")
	StandardIncredible  = Variant("standard_incredible")
	LandscapeSmall      = Variant("landscape_small")
	LandscapeMedium     = Variant("landscape_medium")
	LandscapeXLarge     = Variant("landscape_xlarge")
	LandscapeFantastic  = Variant("landscape_fantastic")
	LandscapeUncanny    = Variant("landscape_uncanny")
	LandscapeIncredible = Variant("landscape_incredible")
)

func (i Image) URL(v Variant) string {
	return fmt.Sprintf("%s/%s.%s", i.Path, string(v), i.Extension)
}

type Date string

const dateLayout = "2006-01-02T15:04:05-0700"

func (d Date) Parse() time.Time {
	t, err := time.Parse(dateLayout, string(d))
	if err != nil {
		panic(err)
	}
	return t
}

func (c Client) Series(id int64, req CommonRequest) (resp struct {
	commonResponse
	Data struct {
		commonList
		Results []Series
	}
}, err error) {
	u := c.baseURL(req)
	u.Path += fmt.Sprintf("series/%d/comics", id)
	r, err := c.fetch(u)
	if err != nil {
		return
	}
	defer r.Close()
	err = json.NewDecoder(r).Decode(&resp)
	return
}

func (c Client) fetch(u url.URL) (io.ReadCloser, error) {
	resp, err := http.Get(u.String())
	if err != nil {
		return nil, err
	}
	if resp.StatusCode >= http.StatusBadRequest {
		slurp, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return nil, err
		}
		return nil, fmt.Errorf("error response from API: %d\n%s", resp.StatusCode, slurp)
	}
	return resp.Body, nil
}

type resourceList struct {
	Available     int
	Returned      int
	CollectionURI string
}

type Character struct {
	ResourceURI string
	ID          *int         `json:"id,omitempty"`
	Name        *string      `json:"name,omitempty"`
	Description *string      `json:"description,omitempty"`
	Modified    *Date        `json:"modified,omitempty"`
	URLs        []URL        `json:"urls,omitempty"`
	Thumbnail   *Image       `json:"thumbnail,omitempty"`
	Comics      *ComicsList  `json:"comics,omitempty"`
	Stories     *StoriesList `json:"stories,omitempty"`
	Events      *EventsList  `json:"events,omitempty"`
	Series      *SeriesList  `json:"series,omitempty"`
}

type CharactersList struct {
	resourceList
	Items []Character
}

type Comic struct {
	ResourceURI        string
	ID                 int             `json:"id,omitempty"`
	Name               string          `json:"id,omitempty"`
	DigitalID          *int            `json:"digitalId,omitempty"`
	Title              *string         `json:"title,omitempty"`
	IssueNumber        *int            `json:"issueNumber,omitempty"`
	VariantDescription *string         `json:"variantDescription,omitEmpty"`
	Description        *string         `json:"description,omitempty"`
	Modified           *Date           `json:"modified,omitempty"`
	ISBN               *string         `json:"isbn,omitempty"`
	UPC                *string         `json:"upc,omitempty"`
	DiamondCode        *string         `json:"diamondCode,omitempty"`
	EAN                *string         `json:"ean,omitempty"`
	ISSN               *string         `json:"issn,omitempty"`
	Format             *string         `json:"format,omitempty"`
	PageCount          *int            `json:"pageCount,omitEmpty"`
	TextObjects        []TextObject    `json:"textObjects,omitempty"`
	URLs               []URL           `json:"urls,omitempty"`
	Series             *Series         `json:"series,omitempty"`
	Variants           []Comic         `json:"variants,omitempty"`
	Collections        []Comic         `json:"collections,omitempty"`
	CollectedIssues    []Comic         `json:"collectedIssues,omitempty"`
	Dates              []ComicDate     `json:"dates,omitempty"`
	Prices             []ComicPrice    `json:"prices,omitempty"`
	Thumbnail          *Image          `json:"thumbnail,omitempty"`
	Images             []Image         `json:"images,omitempty"`
	Creators           *CreatorsList   `json:"creators,omitempty"`
	Characters         *CharactersList `json:"characters,omitempty"`
	Stories            *StoriesList    `json:"stories,omitempty"`
	Events             *EventsList     `json:"events,omitempty"`
}

type TextObject struct {
	Type     string
	Language string
	Text     string
}

type ComicDate struct {
	Type string
	Date Date
}

type ComicPrice struct {
	Type  string
	Price float64
}

type ComicsList struct {
	resourceList
	Items []Comic
}

type Story struct {
	ResourceURI   string
	ID            *int            `json:"id,omitempty"`
	Name          *string         `json:"name,omitempty"`
	Title         *string         `json:"title,omitempty"`
	Description   *string         `json:"description,omitempty"`
	Type          *string         `json:"type,omitempty"`
	Modified      *Date           `json:"date,omitempty"`
	Thumbnail     *Image          `json:"image,omitempty"`
	Comics        *ComicsList     `json:"comics,omitempty"`
	Series        *SeriesList     `json:"series,omitempty"`
	Events        *EventsList     `json:"events,omitempty"`
	Characters    *CharactersList `json:"characters,omitempty"`
	Creators      *CreatorsList   `json:"creators,omitempty"`
	OriginalIssue Comic
}

type StoriesList struct {
	resourceList
	Items []Story
}

type Event struct {
	ResourceURI string
	ID          *int            `json:"id,omitempty"`
	Title       *string         `json:"title,omitempty"`
	Description *string         `json:"description,omitempty"`
	URLs        []URL           `json:"urls,omitempty"`
	Modified    *Date           `json:"modified,omitempty"`
	Start       *Date           `json:"start,omitempty"`
	End         *Date           `json:"end,omitempty"`
	Thumbnail   *Image          `json:"thumbnail,omitempty"`
	Comics      *ComicsList     `json:"comics,omitempty"`
	Stories     *StoriesList    `json:"stories,omitempty"`
	Series      *SeriesList     `json:"series,omitempty"`
	Characters  *CharactersList `json:"characters,omitempty"`
	Creators    *CreatorsList   `json:"creators,omitempty"`
	Next        *Event          `json:"next,omitempty"`
	Previous    *Event          `json:"next,omitempty"`
}

type EventsList struct {
	resourceList
	Items []Event
}

type Series struct {
	ResourceURI string
	ID          *int            `json:"id,omitempty"`
	Name        *string         `json:"name,omitempty"`
	Title       *string         `json:"title,omitempty"`
	Description *string         `json:"description,omitempty"`
	URLs        []URL           `json:"urls,omitempty"`
	StartYear   *int            `json:"startYear,omitempty"`
	EndYear     *int            `json:"endYear,omitempty"`
	Rating      *string         `json:"rating,omitempty"`
	Modified    *Date           `json:"modified,omitempty"`
	Thumbnail   *Image          `json:"thumbnail,omitempty"`
	Comics      *ComicsList     `json:"comics,omitempty"`
	Stories     *StoriesList    `json:"stories,omitempty"`
	Events      *EventsList     `json:"events,omitempty"`
	Characters  *CharactersList `json:"characters,omitempty"`
	Creators    *CreatorsList   `json:"creators,omitempty"`
	Next        *Series         `json:"next,omitempty"`
	Previous    *Series         `json:"next,omitempty"`
}

type SeriesList struct {
	resourceList
	Items []Series
}

type Creator struct {
	ResourceURI string
	ID          *int         `json:"id,omitempty"`
	Name        *string      `json:"name,omitempty"`
	FirstName   *string      `json:"firstName,omitempty"`
	MiddleName  *string      `json:"middleName,omitempty"`
	LastName    *string      `json:"lastName,omitempty"`
	Suffix      *string      `json:"suffix,omitempty"`
	FullName    *string      `json:"fullName,omitempty"`
	Modified    *Date        `json:"modified,omitempty"`
	URLs        []URL        `json:"urls,omitempty"`
	Thumbnail   *Image       `json:"thumbnail,omitempty"`
	Series      *SeriesList  `json:"series,omitempty"`
	Stories     *StoriesList `json:"stories,omitempty"`
	Comics      *ComicsList  `json:"comics,omitempty"`
	Events      *EventsList  `json:"events,omitempty"`
}

type CreatorsList struct {
	resourceList
	Items []Creator
}

// TODO: Replace with subtypes that know what their response will be, with a Fetch() method to pull down and deserialize correctly.
type URL struct {
	Type, URL string
}
<|endoftext|>


<commit_before>package assetmatrix

import (
	"crypto/md5"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
	"sync"
	"time"
)

type Config struct {
	Paths          []*AssetRoot
	Outputs        []string
	OutputDir      string
	AssetURLPrefix string
}

type Matrix struct {
	config            *Config
	cacheBreaker      string
	transformerJSPath string
	scssJSPath        string
	erbRBPath         string
	prevManifest      *Manifest
	Manifest          *Manifest
}

type Manifest struct {
	mtx    sync.Mutex
	Assets map[string]string `json:"assets"`
}

func New(config *Config) *Matrix {
	m := &Matrix{
		config: config,
	}
	for _, r := range config.Paths {
		r.findAsset = m.findAsset
		r.assetURLPrefix = m.config.AssetURLPrefix
	}
	return m
}

func (m *Matrix) Build() error {
	defer m.cleanupTempfiles()

	startedAt := time.Now()
	hashData, err := time.Now().MarshalBinary()
	if err != nil {
		return err
	}
	m.cacheBreaker = fmt.Sprintf("%x", md5.Sum(hashData))

	m.prevManifest = m.parsePrevManifest()
	m.Manifest = &Manifest{
		Assets: make(map[string]string, 0),
	}

	log.Println("Installing dependencies...")
	if err := m.installDeps(); err != nil {
		return err
	}
	if err := m.createTempfiles(); err != nil {
		return err
	}
	for _, r := range m.config.Paths {
		r.transformerJSPath = m.transformerJSPath
		r.scssJSPath = m.scssJSPath
		r.erbRBPath = m.erbRBPath
	}

	log.Println("Cloning external repos...")
	for _, r := range m.config.Paths {
		if r.GitRepo != "" {
			if err := r.CloneRepo(); err != nil {
				return err
			}
		}
	}

	log.Println("Validating asset roots...")
	for _, r := range m.config.Paths {
		if _, err := os.Stat(r.Path); err != nil {
			return err
		}
	}

	log.Println("Enumerating assets...")
	if err := m.enumerateAssets(); err != nil {
		return err
	}
	log.Println("Building output trees...")
	trees, err := m.buildOutputTrees()
	if err != nil {
		return err
	}
	if len(m.config.Outputs) != 0 {
		log.Println("Filtering output trees...")
		matchers := make([]*regexp.Regexp, 0, len(m.config.Outputs))
		for _, pattern := range m.config.Outputs {
			pattern = "^" + strings.Replace(strings.Replace(pattern, ".", "\\.", -1), "*", ".*", -1) + "$"
			matchers = append(matchers, regexp.MustCompile(pattern))
		}
		filteredTrees := make([][]Asset, 0, len(matchers))
		for _, t := range trees {
			a := t[len(t)-1]
			name, err := a.RelPath()
			if err != nil {
				return err
			}
			for _, r := range matchers {
				if r.MatchString(name) {
					log.Println(name)
					filteredTrees = append(filteredTrees, t)
					break
				}
			}
		}
		trees = filteredTrees
	}
	log.Println("Compiling output trees...")
	if err := m.compileTrees(trees); err != nil {
		return err
	}

	log.Println("Writing manifest.json...")
	manifestJSONPath := filepath.Join(m.config.OutputDir, "manifest.json")
	f, err := os.Create(manifestJSONPath)
	defer f.Close()
	if err != nil {
		return err
	}
	e := json.NewEncoder(f)
	if err := e.Encode(m.Manifest); err != nil {
		return err
	}

	duration := time.Since(startedAt)
	log.Printf("Completed in %s", duration)
	return nil
}

func (m *Matrix) parsePrevManifest() *Manifest {
	prevManifest := &Manifest{
		Assets: make(map[string]string, 0),
	}
	file, err := os.Open(filepath.Join(m.config.OutputDir, "manifest.json"))
	if err != nil {
		return prevManifest
	}
	defer file.Close()
	json.NewDecoder(file).Decode(&prevManifest)
	return prevManifest
}

func (m *Matrix) RemoveOldAssets() {
	log.Println("Removing old assets...")
	for logicalPath, path := range m.prevManifest.Assets {
		if m.Manifest.Assets[logicalPath] == path {
			continue
		}
		p := filepath.Join(m.config.OutputDir, path)
		os.Remove(p)
	}
}

func installNpmPackages(names []string) error {
	for _, n := range names {
		if _, err := os.Stat("node_modules/" + n); err == nil {
			continue
		}
		cmd := exec.Command("npm", "install", n)
		cmd.Stderr = os.Stderr
		cmd.Stdout = os.Stdout
		if err := cmd.Run(); err != nil {
			return err
		}
	}
	return nil
}

func (m *Matrix) installDeps() error {
	return installNpmPackages([]string{"recast@0.10.30", "es6-promise@3.0.2", "node-sass@3.2.0", "react-tools@0.13.3"})
}

func (m *Matrix) createTempfiles() error {
	f, err := os.Create("transformer.js")
	if err != nil {
		return err
	}
	m.transformerJSPath = f.Name()
	if _, err := f.WriteString(transformerJS); err != nil {
		return err
	}

	f, err = os.Create("scss.js")
	if err != nil {
		return err
	}
	m.scssJSPath = f.Name()
	if _, err := f.WriteString(scssJS); err != nil {
		return err
	}

	f, err = os.Create("erb.rb")
	if err != nil {
		return err
	}
	m.erbRBPath = f.Name()
	if _, err := f.WriteString(erbRB); err != nil {
		return err
	}

	return nil
}

func (m *Matrix) cleanupTempfiles() {
	os.Remove(m.transformerJSPath)
	os.Remove(m.scssJSPath)
	os.Remove(m.erbRBPath)
}

func (m *Matrix) enumerateAssets() error {
	errChan := make(chan error)
	enumerateAssets := func(r *AssetRoot) {
		errChan <- r.enumerateAssets()
	}
	for _, r := range m.config.Paths {
		r.SetCacheBreaker(m.cacheBreaker)
		go enumerateAssets(r)
	}
	for _ = range m.config.Paths {
		if err := <-errChan; err != nil {
			return err
		}
	}
	return nil
}

func (m *Matrix) buildOutputTrees() ([][]Asset, error) {
	errChan := make(chan error)
	treesChan := make(chan [][]Asset)
	trees := [][]Asset{}
	buildOutputTrees := func(r *AssetRoot) {
		trees, err := r.buildOutputTrees()
		go func() {
			errChan <- err
		}()
		go func() {
			treesChan <- trees
		}()
	}
	for _, r := range m.config.Paths {
		go buildOutputTrees(r)
	}
	for _ = range m.config.Paths {
		if err := <-errChan; err != nil {
			return nil, err
		}
		for _, t := range <-treesChan {
			trees = append(trees, t)
		}
	}
	return trees, nil
}

func (m *Matrix) compileTrees(trees [][]Asset) error {
	errChan := make(chan error)
	compileTree := func(t []Asset) {
		errChan <- m.compileTree(t)
	}
	for _, t := range trees {
		go compileTree(t)
	}
	for _ = range trees {
		if err := <-errChan; err != nil {
			return err
		}
	}
	return nil
}

var AssetNotFoundError = errors.New("Asset not found")

func (m *Matrix) findAsset(key string) (Asset, error) {
	for _, r := range m.config.Paths {
		if a, ok := r.assetIndex[key]; ok {
			return a, nil
		}
	}
	log.Printf("Asset not found: %#v", key)
	return nil, AssetNotFoundError
}

func (m *Matrix) compileTree(tree []Asset) error {
	type result struct {
		io.Reader
		error
	}
	results := make(chan result)
	compileAsset := func(i int, a Asset) {
		r, err := a.Compile()
		results <- result{r, err}
	}
	for i, a := range tree {
		go compileAsset(i, a)
	}
	readers := make([]io.Reader, len(tree))
	for i := range tree {
		res := <-results
		if res.error != nil {
			return res.error
		}
		readers[i] = res.Reader
	}
	outputPath := tree[len(tree)-1].OutputPath()
	manifestKey := outputPath
	ext := filepath.Ext(outputPath)
	outputPath = strings.TrimSuffix(outputPath, ext) + "-" + m.cacheBreaker + ext
	manifestVal := outputPath
	outputPath = filepath.Join(m.config.OutputDir, outputPath)
	if err := os.MkdirAll(filepath.Dir(outputPath), os.ModePerm); err != nil {
		return err
	}
	file, err := os.Create(outputPath)
	if err != nil {
		return err
	}
	m.Manifest.mtx.Lock()
	m.Manifest.Assets[manifestKey] = manifestVal
	m.Manifest.mtx.Unlock()
	log.Printf("Writing %s", outputPath)
	defer file.Close()
	var offset int64
	for i, r := range readers {
		if i > 0 {
			n, err := file.WriteAt([]byte("\n"), offset)
			if err != nil {
				return err
			}
			offset += int64(n)
		}
		n, err := io.Copy(file, r)
		if err != nil {
			return err
		}
		offset += n
	}
	return nil
}
<commit_msg>Fix detecting installed npm deps<commit_after>package assetmatrix

import (
	"crypto/md5"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
	"sync"
	"time"
)

type Config struct {
	Paths          []*AssetRoot
	Outputs        []string
	OutputDir      string
	AssetURLPrefix string
}

type Matrix struct {
	config            *Config
	cacheBreaker      string
	transformerJSPath string
	scssJSPath        string
	erbRBPath         string
	prevManifest      *Manifest
	Manifest          *Manifest
}

type Manifest struct {
	mtx    sync.Mutex
	Assets map[string]string `json:"assets"`
}

func New(config *Config) *Matrix {
	m := &Matrix{
		config: config,
	}
	for _, r := range config.Paths {
		r.findAsset = m.findAsset
		r.assetURLPrefix = m.config.AssetURLPrefix
	}
	return m
}

func (m *Matrix) Build() error {
	defer m.cleanupTempfiles()

	startedAt := time.Now()
	hashData, err := time.Now().MarshalBinary()
	if err != nil {
		return err
	}
	m.cacheBreaker = fmt.Sprintf("%x", md5.Sum(hashData))

	m.prevManifest = m.parsePrevManifest()
	m.Manifest = &Manifest{
		Assets: make(map[string]string, 0),
	}

	log.Println("Installing dependencies...")
	if err := m.installDeps(); err != nil {
		return err
	}
	if err := m.createTempfiles(); err != nil {
		return err
	}
	for _, r := range m.config.Paths {
		r.transformerJSPath = m.transformerJSPath
		r.scssJSPath = m.scssJSPath
		r.erbRBPath = m.erbRBPath
	}

	log.Println("Cloning external repos...")
	for _, r := range m.config.Paths {
		if r.GitRepo != "" {
			if err := r.CloneRepo(); err != nil {
				return err
			}
		}
	}

	log.Println("Validating asset roots...")
	for _, r := range m.config.Paths {
		if _, err := os.Stat(r.Path); err != nil {
			return err
		}
	}

	log.Println("Enumerating assets...")
	if err := m.enumerateAssets(); err != nil {
		return err
	}
	log.Println("Building output trees...")
	trees, err := m.buildOutputTrees()
	if err != nil {
		return err
	}
	if len(m.config.Outputs) != 0 {
		log.Println("Filtering output trees...")
		matchers := make([]*regexp.Regexp, 0, len(m.config.Outputs))
		for _, pattern := range m.config.Outputs {
			pattern = "^" + strings.Replace(strings.Replace(pattern, ".", "\\.", -1), "*", ".*", -1) + "$"
			matchers = append(matchers, regexp.MustCompile(pattern))
		}
		filteredTrees := make([][]Asset, 0, len(matchers))
		for _, t := range trees {
			a := t[len(t)-1]
			name, err := a.RelPath()
			if err != nil {
				return err
			}
			for _, r := range matchers {
				if r.MatchString(name) {
					log.Println(name)
					filteredTrees = append(filteredTrees, t)
					break
				}
			}
		}
		trees = filteredTrees
	}
	log.Println("Compiling output trees...")
	if err := m.compileTrees(trees); err != nil {
		return err
	}

	log.Println("Writing manifest.json...")
	manifestJSONPath := filepath.Join(m.config.OutputDir, "manifest.json")
	f, err := os.Create(manifestJSONPath)
	defer f.Close()
	if err != nil {
		return err
	}
	e := json.NewEncoder(f)
	if err := e.Encode(m.Manifest); err != nil {
		return err
	}

	duration := time.Since(startedAt)
	log.Printf("Completed in %s", duration)
	return nil
}

func (m *Matrix) parsePrevManifest() *Manifest {
	prevManifest := &Manifest{
		Assets: make(map[string]string, 0),
	}
	file, err := os.Open(filepath.Join(m.config.OutputDir, "manifest.json"))
	if err != nil {
		return prevManifest
	}
	defer file.Close()
	json.NewDecoder(file).Decode(&prevManifest)
	return prevManifest
}

func (m *Matrix) RemoveOldAssets() {
	log.Println("Removing old assets...")
	for logicalPath, path := range m.prevManifest.Assets {
		if m.Manifest.Assets[logicalPath] == path {
			continue
		}
		p := filepath.Join(m.config.OutputDir, path)
		os.Remove(p)
	}
}

func installNpmPackages(names []string) error {
	for _, n := range names {
		if _, err := os.Stat("node_modules/" + strings.Split(n, "@")[0]); err == nil {
			continue
		}
		cmd := exec.Command("npm", "install", n)
		cmd.Stderr = os.Stderr
		cmd.Stdout = os.Stdout
		if err := cmd.Run(); err != nil {
			return err
		}
	}
	return nil
}

func (m *Matrix) installDeps() error {
	return installNpmPackages([]string{"recast@0.10.30", "es6-promise@3.0.2", "node-sass@3.2.0", "react-tools@0.13.3"})
}

func (m *Matrix) createTempfiles() error {
	f, err := os.Create("transformer.js")
	if err != nil {
		return err
	}
	m.transformerJSPath = f.Name()
	if _, err := f.WriteString(transformerJS); err != nil {
		return err
	}

	f, err = os.Create("scss.js")
	if err != nil {
		return err
	}
	m.scssJSPath = f.Name()
	if _, err := f.WriteString(scssJS); err != nil {
		return err
	}

	f, err = os.Create("erb.rb")
	if err != nil {
		return err
	}
	m.erbRBPath = f.Name()
	if _, err := f.WriteString(erbRB); err != nil {
		return err
	}

	return nil
}

func (m *Matrix) cleanupTempfiles() {
	os.Remove(m.transformerJSPath)
	os.Remove(m.scssJSPath)
	os.Remove(m.erbRBPath)
}

func (m *Matrix) enumerateAssets() error {
	errChan := make(chan error)
	enumerateAssets := func(r *AssetRoot) {
		errChan <- r.enumerateAssets()
	}
	for _, r := range m.config.Paths {
		r.SetCacheBreaker(m.cacheBreaker)
		go enumerateAssets(r)
	}
	for _ = range m.config.Paths {
		if err := <-errChan; err != nil {
			return err
		}
	}
	return nil
}

func (m *Matrix) buildOutputTrees() ([][]Asset, error) {
	errChan := make(chan error)
	treesChan := make(chan [][]Asset)
	trees := [][]Asset{}
	buildOutputTrees := func(r *AssetRoot) {
		trees, err := r.buildOutputTrees()
		go func() {
			errChan <- err
		}()
		go func() {
			treesChan <- trees
		}()
	}
	for _, r := range m.config.Paths {
		go buildOutputTrees(r)
	}
	for _ = range m.config.Paths {
		if err := <-errChan; err != nil {
			return nil, err
		}
		for _, t := range <-treesChan {
			trees = append(trees, t)
		}
	}
	return trees, nil
}

func (m *Matrix) compileTrees(trees [][]Asset) error {
	errChan := make(chan error)
	compileTree := func(t []Asset) {
		errChan <- m.compileTree(t)
	}
	for _, t := range trees {
		go compileTree(t)
	}
	for _ = range trees {
		if err := <-errChan; err != nil {
			return err
		}
	}
	return nil
}

var AssetNotFoundError = errors.New("Asset not found")

func (m *Matrix) findAsset(key string) (Asset, error) {
	for _, r := range m.config.Paths {
		if a, ok := r.assetIndex[key]; ok {
			return a, nil
		}
	}
	log.Printf("Asset not found: %#v", key)
	return nil, AssetNotFoundError
}

func (m *Matrix) compileTree(tree []Asset) error {
	type result struct {
		io.Reader
		error
	}
	results := make(chan result)
	compileAsset := func(i int, a Asset) {
		r, err := a.Compile()
		results <- result{r, err}
	}
	for i, a := range tree {
		go compileAsset(i, a)
	}
	readers := make([]io.Reader, len(tree))
	for i := range tree {
		res := <-results
		if res.error != nil {
			return res.error
		}
		readers[i] = res.Reader
	}
	outputPath := tree[len(tree)-1].OutputPath()
	manifestKey := outputPath
	ext := filepath.Ext(outputPath)
	outputPath = strings.TrimSuffix(outputPath, ext) + "-" + m.cacheBreaker + ext
	manifestVal := outputPath
	outputPath = filepath.Join(m.config.OutputDir, outputPath)
	if err := os.MkdirAll(filepath.Dir(outputPath), os.ModePerm); err != nil {
		return err
	}
	file, err := os.Create(outputPath)
	if err != nil {
		return err
	}
	m.Manifest.mtx.Lock()
	m.Manifest.Assets[manifestKey] = manifestVal
	m.Manifest.mtx.Unlock()
	log.Printf("Writing %s", outputPath)
	defer file.Close()
	var offset int64
	for i, r := range readers {
		if i > 0 {
			n, err := file.WriteAt([]byte("\n"), offset)
			if err != nil {
				return err
			}
			offset += int64(n)
		}
		n, err := io.Copy(file, r)
		if err != nil {
			return err
		}
		offset += n
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/Luzifer/dockermanager/config"
	"github.com/Luzifer/go_helpers/str"
	"github.com/cenkalti/backoff"
	"github.com/fsouza/go-dockerclient"
)

const (
	labelIsManaged       = "io.luzifer.dockermanager.managed"
	labelConfigHash      = "io.luzifer.dockermanager.cfghash"
	labelIsScheduled     = "io.luzifer.dockermanager.scheduler"
	labelDockerProxySlug = "io.luzifer.dockerproxy.slug"
	labelDockerProxyPort = "io.luzifer.dockerproxy.port"
)

var (
	lastStartContainerCall = time.Now()
)

func getImages(dangling bool) []docker.APIImages {
	images, err := dockerClient.ListImages(docker.ListImagesOptions{
		All: false,
	})
	orFail(err)

	nowSeconds := time.Now().Unix()
	response := []docker.APIImages{}

	for _, v := range images {
		if dangling {
			if len(v.RepoTags) == 1 && v.RepoTags[0] == "<none>:<none>" && (nowSeconds-v.Created) > 3600 {
				response = append(response, v)
			}
		} else {
			if len(v.RepoTags) != 1 || v.RepoTags[0] != "<none>:<none>" {
				response = append(response, v)
			}
		}
	}

	return response
}

func cleanDangling() {
	images := getImages(true)
	for _, v := range images {
		log.Printf("Removing dangling image: %s", v.ID)
		dockerClient.RemoveImage(v.ID)
	}
}

func removeNotRequiredImages() {
	if !params.ManageFullHost {
		// If we're not responsible for the full host don't remove images
		return
	}

	required := []string{}
	for _, v := range *cfg {
		if str.StringInSlice(serfElector.MyName, v.Hosts) || str.StringInSlice("ALL", v.Hosts) {
			required = append(required, fmt.Sprintf("%s:%s", v.Image, v.Tag))
		}
	}

	currentImages := getImages(false)
	for _, i := range currentImages {
		found := false
		for _, t := range i.RepoTags {
			for _, r := range required {
				if r == t {
					found = true
				}
			}
		}

		if !found {
			log.Printf("Removing not required image: %s", i.ID)
			dockerClient.RemoveImage(i.ID)
		}
	}
}

func refreshImages() {
	for _, v := range *cfg {
		if str.StringInSlice(serfElector.MyName, v.Hosts) || str.StringInSlice("ALL", v.Hosts) {
			pullImage(v.Image, v.Tag)
		}
	}
}

func pullImage(image, tag string) {
	auth := docker.AuthConfiguration{}

	reginfo := strings.SplitN(image, "/", 2)
	if len(reginfo) == 2 {
		for s, a := range authConfig.Configs {
			if strings.Contains(s, reginfo[0]) {
				auth = a
			}
		}
	}

	log.Printf("Refreshing repo %s:%s...", image, tag)
	err := dockerClient.PullImage(docker.PullImageOptions{
		Repository: image,
		Tag:        tag,
	}, auth)
	orLog(err)
}

func bootContainer(name string, cfg config.ContainerConfig) {
	var (
		container *docker.Container
		err       error
		bo        = backoff.NewExponentialBackOff()
	)

	cs, err := cfg.Checksum()
	orFail(err)
	if err != nil {
		return
	}

	labels := cfg.Labels
	if labels == nil {
		labels = map[string]string{}
	}
	labels[labelConfigHash] = cs
	labels[labelIsManaged] = "true"

	if cfg.StartTimes != "" {
		labels[labelIsScheduled] = "true"
	}

	if cfg.DockerProxy.Slug != "" {
		labels[labelDockerProxySlug] = cfg.DockerProxy.Slug
		labels[labelDockerProxyPort] = strconv.FormatInt(int64(cfg.DockerProxy.Port), 10)
	}

	mounts, volumes, binds := parseMounts(cfg.Volumes)

	newcfg := &docker.Config{
		AttachStdin:  false,
		AttachStdout: true,
		AttachStderr: true,
		Image:        strings.Join([]string{cfg.Image, cfg.Tag}, ":"),
		Env:          cfg.Environment,
		Cmd:          cfg.Command,
		Labels:       labels,
		Volumes:      volumes,
		Mounts:       mounts,
	}

	hostConfig := &docker.HostConfig{
		Binds:        binds,
		Links:        cfg.Links,
		Privileged:   false,
		PortBindings: make(map[docker.Port][]docker.PortBinding),
		CapAdd:       cfg.AddCapabilities,
	}

	for _, v := range cfg.Ports {
		s := strings.Split(v.Local, ":")
		hostConfig.PortBindings[docker.Port(v.Container)] = []docker.PortBinding{{
			HostIP:   s[0],
			HostPort: s[1],
		}}
	}

	bo.MaxElapsedTime = time.Minute
	err = backoff.Retry(func() error {
		log.Printf("Creating container %s", name)
		container, err = dockerClient.CreateContainer(docker.CreateContainerOptions{
			Name:       name,
			Config:     newcfg,
			HostConfig: hostConfig,
		})
		orLog(err)
		if err != nil {
			if strings.Contains(err.Error(), "no such image") {
				pullImage(cfg.Image, cfg.Tag)
			}
			if strings.Contains(err.Error(), "is already in use by container") {
				cleanContainers()
			}
		}
		return err
	}, bo)

	if err != nil {
		log.Printf("Unable to start container '%s': %s", name, err)
		return
	}

	log.Printf("Starting container %s", container.Name)
	dockerClient.StartContainer(container.Name, nil)
}

func listRunningContainers() ([]docker.APIContainers, error) {
	containers, err := dockerClient.ListContainers(docker.ListContainersOptions{
		All: false,
	})
	orLog(err)
	return containers, err
}

func getExpectedRunningNames() []string {
	expectedRunning := []string{}
	for name, containerCfg := range *cfg {
		if containerCfg.ShouldBeRunning(serfElector.MyName, lastStartContainerCall) {
			expectedRunning = append(expectedRunning, name)
		}
	}
	return expectedRunning
}

func stopUnexpectedContainers() {
	expectedRunning := getExpectedRunningNames()

	// Stop containers not expected to be running
	currentRunning, err := listRunningContainers()
	orLog(err)
	if err != nil {
		return
	}
	for _, v := range currentRunning {
		allowed := false

		containerDetails, err := dockerClient.InspectContainer(v.ID)
		orFail(err)

		_, isManaged := containerDetails.Config.Labels[labelIsManaged]
		if !params.ManageFullHost && !isManaged {
			allowed = true
		}

		if containerDetails.Config.Labels[labelIsScheduled] == "true" {
			// If it's a scheduled container keep it running
			allowed = true
		}

		for _, n := range v.Names {
			if str.StringInSlice(strings.Trim(n, "/"), expectedRunning) {
				allowed = true
			}
		}
		if !allowed {
			log.Printf("Stopping container %s as it is not expected to be running.", v.ID)
			err := dockerClient.StopContainer(v.ID, 5)
			orFail(err)
			time.Sleep(time.Second * 5)
		}
	}
}

func removeDeprecatedContainers() {
	expectedRunning := getExpectedRunningNames()

	// Stop containers who are of deprecated images
	currentRunning, err := listRunningContainers()
	orLog(err)
	if err != nil {
		return
	}
	images := getImages(false)
	for _, n := range expectedRunning {
		repoName := strings.Join([]string{(*cfg)[n].Image, (*cfg)[n].Tag}, ":")
		currentImageID := "0"
		for _, i := range images {
			if str.StringInSlice(repoName, i.RepoTags) {
				currentImageID = i.ID
			}
		}
		if currentImageID == "0" {
			log.Printf("Found no image ID for repo %s", strings.Join([]string{(*cfg)[n].Image, (*cfg)[n].Tag}, ":"))
			continue
		}
		for _, v := range currentRunning {
			containerDetails, err := dockerClient.InspectContainer(v.ID)
			orFail(err)

			cs, err := (*cfg)[n].Checksum()
			orFail(err)

			if str.StringInSlice(fmt.Sprintf("/%s", n), v.Names) {
				needsUpdate := false
				if !strings.HasPrefix(currentImageID, containerDetails.Image) {
					log.Printf("Container %s has a new image version.", n)
					needsUpdate = true
				}
				if containerDetails.Config.Labels[labelConfigHash] != cs {
					log.Printf("Container %s has a configuration update.", n)
					needsUpdate = true
				}
				if needsUpdate && !timeAllowed((*cfg)[n].UpdateTimes) {
					log.Printf("Image %s has update but container %s (%s) is not allowed to update now.", v.Image, n, v.ID)
					needsUpdate = false
				}

				if needsUpdate {
					stopWaitTime := (*cfg)[n].StopTimeout
					if stopWaitTime == 0 {
						stopWaitTime = 5
					}

					log.Printf("Image: %s Current: %s", containerDetails.Image, currentImageID)
					log.Printf("Stopping deprecated container %s", v.ID)
					err := dockerClient.StopContainer(v.ID, stopWaitTime)
					orFail(err)

					log.Printf("Removing deprecated container %s", v.ID)
					dockerClient.RemoveContainer(docker.RemoveContainerOptions{
						ID: v.ID,
					})

					currentRunning, err = listRunningContainers()
					orFail(err)
				}
			}
		}
	}
}

func startExpectedContainers() {
	expectedRunning := getExpectedRunningNames()
	lastStartContainerCall = time.Now()

	// Start expected containers
	currentRunning, err := listRunningContainers()
	orLog(err)
	if err != nil {
		return
	}
	runningNames := []string{}
	for _, v := range currentRunning {
		for _, n := range v.Names {
			runningNames = append(runningNames, strings.Trim(n, "/"))
		}
	}
	for _, n := range expectedRunning {
		if !str.StringInSlice(n, runningNames) {
			bootContainer(n, (*cfg)[n])
		}
	}
}

func cleanContainers() {
	runningContainers, err := dockerClient.ListContainers(docker.ListContainersOptions{
		All: true,
	})
	orLog(err)
	if err != nil {
		return
	}

	for _, v := range runningContainers {
		_, isManaged := v.Labels[labelIsManaged]

		if strings.HasPrefix(v.Status, "Exited") || strings.HasPrefix(v.Status, "Dead") || (strings.HasPrefix(v.Status, "Created") && isManaged) {
			log.Printf("Removing container %s (Status %s)", v.ID, v.Status)
			err := dockerClient.RemoveContainer(docker.RemoveContainerOptions{
				ID:    v.ID,
				Force: true,
			})
			orLog(err)
		}
	}
}

func parseMounts(mountIn []string) (mounts []docker.Mount, volumes map[string]struct{}, binds []string) {
	volumes = make(map[string]struct{})
	for _, m := range mountIn {
		if len(m) == 0 {
			continue
		}

		parts := strings.Split(m, ":")
		if len(parts) != 2 && len(parts) != 3 {
			log.Printf("[ERRO] Invalid default mount: %s", m)
			continue
		}

		if stat, err := os.Stat(parts[0]); err == nil && !stat.IsDir() {
			binds = append(binds, m)
			continue
		}

		mo := docker.Mount{
			Source:      parts[0],
			Destination: parts[1],
		}

		if len(parts) == 3 {
			mo.RW = (parts[3] != "ro")
		}

		mounts = append(mounts, mo)
		volumes[mo.Destination] = struct{}{}
	}

	return
}
<commit_msg>Fix: Volumes behaves strange<commit_after>package main

import (
	"fmt"
	"log"
	"strconv"
	"strings"
	"time"

	"github.com/Luzifer/dockermanager/config"
	"github.com/Luzifer/go_helpers/str"
	"github.com/cenkalti/backoff"
	"github.com/fsouza/go-dockerclient"
)

const (
	labelIsManaged       = "io.luzifer.dockermanager.managed"
	labelConfigHash      = "io.luzifer.dockermanager.cfghash"
	labelIsScheduled     = "io.luzifer.dockermanager.scheduler"
	labelDockerProxySlug = "io.luzifer.dockerproxy.slug"
	labelDockerProxyPort = "io.luzifer.dockerproxy.port"
)

var (
	lastStartContainerCall = time.Now()
)

func getImages(dangling bool) []docker.APIImages {
	images, err := dockerClient.ListImages(docker.ListImagesOptions{
		All: false,
	})
	orFail(err)

	nowSeconds := time.Now().Unix()
	response := []docker.APIImages{}

	for _, v := range images {
		if dangling {
			if len(v.RepoTags) == 1 && v.RepoTags[0] == "<none>:<none>" && (nowSeconds-v.Created) > 3600 {
				response = append(response, v)
			}
		} else {
			if len(v.RepoTags) != 1 || v.RepoTags[0] != "<none>:<none>" {
				response = append(response, v)
			}
		}
	}

	return response
}

func cleanDangling() {
	images := getImages(true)
	for _, v := range images {
		log.Printf("Removing dangling image: %s", v.ID)
		dockerClient.RemoveImage(v.ID)
	}
}

func removeNotRequiredImages() {
	if !params.ManageFullHost {
		// If we're not responsible for the full host don't remove images
		return
	}

	required := []string{}
	for _, v := range *cfg {
		if str.StringInSlice(serfElector.MyName, v.Hosts) || str.StringInSlice("ALL", v.Hosts) {
			required = append(required, fmt.Sprintf("%s:%s", v.Image, v.Tag))
		}
	}

	currentImages := getImages(false)
	for _, i := range currentImages {
		found := false
		for _, t := range i.RepoTags {
			for _, r := range required {
				if r == t {
					found = true
				}
			}
		}

		if !found {
			log.Printf("Removing not required image: %s", i.ID)
			dockerClient.RemoveImage(i.ID)
		}
	}
}

func refreshImages() {
	for _, v := range *cfg {
		if str.StringInSlice(serfElector.MyName, v.Hosts) || str.StringInSlice("ALL", v.Hosts) {
			pullImage(v.Image, v.Tag)
		}
	}
}

func pullImage(image, tag string) {
	auth := docker.AuthConfiguration{}

	reginfo := strings.SplitN(image, "/", 2)
	if len(reginfo) == 2 {
		for s, a := range authConfig.Configs {
			if strings.Contains(s, reginfo[0]) {
				auth = a
			}
		}
	}

	log.Printf("Refreshing repo %s:%s...", image, tag)
	err := dockerClient.PullImage(docker.PullImageOptions{
		Repository: image,
		Tag:        tag,
	}, auth)
	orLog(err)
}

func bootContainer(name string, cfg config.ContainerConfig) {
	var (
		container *docker.Container
		err       error
		bo        = backoff.NewExponentialBackOff()
	)

	cs, err := cfg.Checksum()
	orFail(err)
	if err != nil {
		return
	}

	labels := cfg.Labels
	if labels == nil {
		labels = map[string]string{}
	}
	labels[labelConfigHash] = cs
	labels[labelIsManaged] = "true"

	if cfg.StartTimes != "" {
		labels[labelIsScheduled] = "true"
	}

	if cfg.DockerProxy.Slug != "" {
		labels[labelDockerProxySlug] = cfg.DockerProxy.Slug
		labels[labelDockerProxyPort] = strconv.FormatInt(int64(cfg.DockerProxy.Port), 10)
	}

	volumes, binds := parseMounts(cfg.Volumes)

	newcfg := &docker.Config{
		AttachStdin:  false,
		AttachStdout: true,
		AttachStderr: true,
		Image:        strings.Join([]string{cfg.Image, cfg.Tag}, ":"),
		Env:          cfg.Environment,
		Cmd:          cfg.Command,
		Labels:       labels,
		Volumes:      volumes,
	}

	hostConfig := &docker.HostConfig{
		Binds:        binds,
		Links:        cfg.Links,
		Privileged:   false,
		PortBindings: make(map[docker.Port][]docker.PortBinding),
		CapAdd:       cfg.AddCapabilities,
	}

	for _, v := range cfg.Ports {
		s := strings.Split(v.Local, ":")
		hostConfig.PortBindings[docker.Port(v.Container)] = []docker.PortBinding{{
			HostIP:   s[0],
			HostPort: s[1],
		}}
	}

	bo.MaxElapsedTime = time.Minute
	err = backoff.Retry(func() error {
		log.Printf("Creating container %s", name)
		container, err = dockerClient.CreateContainer(docker.CreateContainerOptions{
			Name:       name,
			Config:     newcfg,
			HostConfig: hostConfig,
		})
		orLog(err)
		if err != nil {
			if strings.Contains(err.Error(), "no such image") {
				pullImage(cfg.Image, cfg.Tag)
			}
			if strings.Contains(err.Error(), "is already in use by container") {
				cleanContainers()
			}
		}
		return err
	}, bo)

	if err != nil {
		log.Printf("Unable to start container '%s': %s", name, err)
		return
	}

	log.Printf("Starting container %s", container.Name)
	dockerClient.StartContainer(container.Name, nil)
}

func listRunningContainers() ([]docker.APIContainers, error) {
	containers, err := dockerClient.ListContainers(docker.ListContainersOptions{
		All: false,
	})
	orLog(err)
	return containers, err
}

func getExpectedRunningNames() []string {
	expectedRunning := []string{}
	for name, containerCfg := range *cfg {
		if containerCfg.ShouldBeRunning(serfElector.MyName, lastStartContainerCall) {
			expectedRunning = append(expectedRunning, name)
		}
	}
	return expectedRunning
}

func stopUnexpectedContainers() {
	expectedRunning := getExpectedRunningNames()

	// Stop containers not expected to be running
	currentRunning, err := listRunningContainers()
	orLog(err)
	if err != nil {
		return
	}
	for _, v := range currentRunning {
		allowed := false

		containerDetails, err := dockerClient.InspectContainer(v.ID)
		orFail(err)

		_, isManaged := containerDetails.Config.Labels[labelIsManaged]
		if !params.ManageFullHost && !isManaged {
			allowed = true
		}

		if containerDetails.Config.Labels[labelIsScheduled] == "true" {
			// If it's a scheduled container keep it running
			allowed = true
		}

		for _, n := range v.Names {
			if str.StringInSlice(strings.Trim(n, "/"), expectedRunning) {
				allowed = true
			}
		}
		if !allowed {
			log.Printf("Stopping container %s as it is not expected to be running.", v.ID)
			err := dockerClient.StopContainer(v.ID, 5)
			orFail(err)
			time.Sleep(time.Second * 5)
		}
	}
}

func removeDeprecatedContainers() {
	expectedRunning := getExpectedRunningNames()

	// Stop containers who are of deprecated images
	currentRunning, err := listRunningContainers()
	orLog(err)
	if err != nil {
		return
	}
	images := getImages(false)
	for _, n := range expectedRunning {
		repoName := strings.Join([]string{(*cfg)[n].Image, (*cfg)[n].Tag}, ":")
		currentImageID := "0"
		for _, i := range images {
			if str.StringInSlice(repoName, i.RepoTags) {
				currentImageID = i.ID
			}
		}
		if currentImageID == "0" {
			log.Printf("Found no image ID for repo %s", strings.Join([]string{(*cfg)[n].Image, (*cfg)[n].Tag}, ":"))
			continue
		}
		for _, v := range currentRunning {
			containerDetails, err := dockerClient.InspectContainer(v.ID)
			orFail(err)

			cs, err := (*cfg)[n].Checksum()
			orFail(err)

			if str.StringInSlice(fmt.Sprintf("/%s", n), v.Names) {
				needsUpdate := false
				if !strings.HasPrefix(currentImageID, containerDetails.Image) {
					log.Printf("Container %s has a new image version.", n)
					needsUpdate = true
				}
				if containerDetails.Config.Labels[labelConfigHash] != cs {
					log.Printf("Container %s has a configuration update.", n)
					needsUpdate = true
				}
				if needsUpdate && !timeAllowed((*cfg)[n].UpdateTimes) {
					log.Printf("Image %s has update but container %s (%s) is not allowed to update now.", v.Image, n, v.ID)
					needsUpdate = false
				}

				if needsUpdate {
					stopWaitTime := (*cfg)[n].StopTimeout
					if stopWaitTime == 0 {
						stopWaitTime = 5
					}

					log.Printf("Image: %s Current: %s", containerDetails.Image, currentImageID)
					log.Printf("Stopping deprecated container %s", v.ID)
					err := dockerClient.StopContainer(v.ID, stopWaitTime)
					orFail(err)

					log.Printf("Removing deprecated container %s", v.ID)
					dockerClient.RemoveContainer(docker.RemoveContainerOptions{
						ID: v.ID,
					})

					currentRunning, err = listRunningContainers()
					orFail(err)
				}
			}
		}
	}
}

func startExpectedContainers() {
	expectedRunning := getExpectedRunningNames()
	lastStartContainerCall = time.Now()

	// Start expected containers
	currentRunning, err := listRunningContainers()
	orLog(err)
	if err != nil {
		return
	}
	runningNames := []string{}
	for _, v := range currentRunning {
		for _, n := range v.Names {
			runningNames = append(runningNames, strings.Trim(n, "/"))
		}
	}
	for _, n := range expectedRunning {
		if !str.StringInSlice(n, runningNames) {
			bootContainer(n, (*cfg)[n])
		}
	}
}

func cleanContainers() {
	runningContainers, err := dockerClient.ListContainers(docker.ListContainersOptions{
		All: true,
	})
	orLog(err)
	if err != nil {
		return
	}

	for _, v := range runningContainers {
		_, isManaged := v.Labels[labelIsManaged]

		if strings.HasPrefix(v.Status, "Exited") || strings.HasPrefix(v.Status, "Dead") || (strings.HasPrefix(v.Status, "Created") && isManaged) {
			log.Printf("Removing container %s (Status %s)", v.ID, v.Status)
			err := dockerClient.RemoveContainer(docker.RemoveContainerOptions{
				ID:    v.ID,
				Force: true,
			})
			orLog(err)
		}
	}
}

func parseMounts(mountIn []string) (volumes map[string]struct{}, binds []string) {
	volumes = make(map[string]struct{})
	for _, m := range mountIn {
		if len(m) == 0 {
			continue
		}

		parts := strings.Split(m, ":")
		if len(parts) != 2 && len(parts) != 3 {
			log.Printf("[ERRO] Invalid default mount: %s", m)
			continue
		}

		binds = append(binds, m)
		volumes[parts[1]] = struct{}{}
	}

	return
}
<|endoftext|>