<commit_before>package provider

import (
	"fmt"
	"net/http"
	"os"
	"strings"

	log "github.com/sirupsen/logrus"

	api "gopkg.in/ns1/ns1-go.v2/rest"
	"gopkg.in/ns1/ns1-go.v2/rest/model/dns"

	"github.com/kubernetes-incubator/external-dns/endpoint"
	"github.com/kubernetes-incubator/external-dns/plan"
)

const (
	// ns1Create is a ChangeAction enum value
	ns1Create = "CREATE"
	// ns1Delete is a ChangeAction enum value
	ns1Delete = "DELETE"
	// ns1Update is a ChangeAction enum value
	ns1Update = "UPDATE"
	// ns1DefaultTTL is the default ttl for ttls that are not set
	ns1DefaultTTL = 10
)

// NS1DomainClient is a subset of the NS1 API the the provider uses, to ease testing
type NS1DomainClient interface {
	CreateRecord(r *dns.Record) (*http.Response, error)
	DeleteRecord(zone string, domain string, t string) (*http.Response, error)
	UpdateRecord(r *dns.Record) (*http.Response, error)
	GetZone(zone string) (*dns.Zone, *http.Response, error)
	ListZones() ([]*dns.Zone, *http.Response, error)
}

// NS1DomainService wraps the API and fulfills the NS1DomainClient interface
type NS1DomainService struct {
	service *api.Client
}

// CreateRecord wraps the Create method of the API's Record service
func (n NS1DomainService) CreateRecord(r *dns.Record) (*http.Response, error) {
	return n.service.Records.Create(r)
}

// DeleteRecord wraps the Delete method of the API's Record service
func (n NS1DomainService) DeleteRecord(zone string, domain string, t string) (*http.Response, error) {
	return n.service.Records.Delete(zone, domain, t)
}

// UpdateRecord wraps the Update method of the API's Record service
func (n NS1DomainService) UpdateRecord(r *dns.Record) (*http.Response, error) {
	return n.service.Records.Update(r)
}

// GetZone wraps the Get method of the API's Zones service
func (n NS1DomainService) GetZone(zone string) (*dns.Zone, *http.Response, error) {
	return n.service.Zones.Get(zone)
}

// ListZones wraps the List method of the API's Zones service
func (n NS1DomainService) ListZones() ([]*dns.Zone, *http.Response, error) {
	return n.service.Zones.List()
}

// NS1Config passes cli args to the NS1Provider
type NS1Config struct {
	DomainFilter DomainFilter
	ZoneIDFilter ZoneIDFilter
	DryRun       bool
}

// NS1Provider is the NS1 provider
type NS1Provider struct {
	client       NS1DomainClient
	domainFilter DomainFilter
	zoneIDFilter ZoneIDFilter
	dryRun       bool
}

// NewNS1Provider creates a new NS1 Provider
func NewNS1Provider(config NS1Config) (*NS1Provider, error) {
	return newNS1ProviderWithHTTPClient(config, http.DefaultClient)
}

func newNS1ProviderWithHTTPClient(config NS1Config, client *http.Client) (*NS1Provider, error) {
	token, ok := os.LookupEnv("NS1_APIKEY")
	if !ok {
		return nil, fmt.Errorf("NS1_APIKEY environment variable is not set")
	}

	apiClient := api.NewClient(client, api.SetAPIKey(token))

	provider := &NS1Provider{
		//client:       apiClient,
		client:       NS1DomainService{apiClient},
		domainFilter: config.DomainFilter,
		zoneIDFilter: config.ZoneIDFilter,
	}
	return provider, nil
}

// Records returns the endpoints this provider knows about
func (p *NS1Provider) Records() ([]*endpoint.Endpoint, error) {
	zones, err := p.zonesFiltered()
	if err != nil {
		return nil, err
	}

	var endpoints []*endpoint.Endpoint

	for _, zone := range zones {

		// TODO handle Header Codes
		zoneData, _, err := p.client.GetZone(zone.String())
		if err != nil {
			return nil, err
		}

		for _, record := range zoneData.Records {
			if supportedRecordType(record.Type) {
				endpoints = append(endpoints, endpoint.NewEndpointWithTTL(
					record.Domain,
					record.Type,
					endpoint.TTL(record.TTL),
					record.ShortAns...,
				),
				)
			}
		}
	}

	return endpoints, nil
}

// ns1BuildRecord returns a dns.Record for a change set
func ns1BuildRecord(zoneName string, change *ns1Change) *dns.Record {
	record := dns.NewRecord(zoneName, change.Endpoint.DNSName, change.Endpoint.RecordType)
	for _, v := range change.Endpoint.Targets {
		record.AddAnswer(dns.NewAnswer(strings.Split(v, " ")))
	}
	// set detault ttl
	var ttl = ns1DefaultTTL
	if change.Endpoint.RecordTTL.IsConfigured() {
		ttl = int(change.Endpoint.RecordTTL)
	}
	record.TTL = ttl

	return record
}

// ns1SubmitChanges takes an array of changes and sends them to NS1
func (p *NS1Provider) ns1SubmitChanges(changes []*ns1Change) error {
	// return early if there is nothing to change
	if len(changes) == 0 {
		return nil
	}

	zones, err := p.zonesFiltered()
	if err != nil {
		return err
	}

	// separate into per-zone change sets to be passed to the API.
	changesByZone := ns1ChangesByZone(zones, changes)
	for zoneName, changes := range changesByZone {
		for _, change := range changes {
			record := ns1BuildRecord(zoneName, change)
			logFields := log.Fields{
				"record": record.Domain,
				"type":   record.Type,
				"ttl":    record.TTL,
				"action": change.Action,
				"zone":   zoneName,
			}

			log.WithFields(logFields).Info("Changing record.")

			if p.dryRun {
				continue
			}

			switch change.Action {
			case ns1Create:
				_, err := p.client.CreateRecord(record)
				if err != nil {
					return err
				}
			case ns1Delete:
				_, err := p.client.DeleteRecord(zoneName, record.Domain, record.Type)
				if err != nil {
					return err
				}
			case ns1Update:
				_, err := p.client.UpdateRecord(record)
				if err != nil {
					return err
				}
			}
		}
	}
	return nil
}

// Zones returns the list of hosted zones.
func (p *NS1Provider) zonesFiltered() ([]*dns.Zone, error) {
	// TODO handle Header Codes
	zones, _, err := p.client.ListZones()
	if err != nil {
		return nil, err
	}

	toReturn := []*dns.Zone{}

	for _, z := range zones {
		if p.domainFilter.Match(z.Zone) && p.zoneIDFilter.Match(z.ID) {
			toReturn = append(toReturn, z)
			log.Debugf("Matched %s", z.Zone)
		} else {
			log.Debugf("Filtered %s", z.Zone)
		}
	}

	return toReturn, nil
}

// ns1Change differentiates between ChangeActions
type ns1Change struct {
	Action   string
	Endpoint *endpoint.Endpoint
}

// ApplyChanges applies a given set of changes in a given zone.
func (p *NS1Provider) ApplyChanges(changes *plan.Changes) error {
	combinedChanges := make([]*ns1Change, 0, len(changes.Create)+len(changes.UpdateNew)+len(changes.Delete))

	combinedChanges = append(combinedChanges, newNS1Changes(ns1Create, changes.Create)...)
	combinedChanges = append(combinedChanges, newNS1Changes(ns1Update, changes.UpdateNew)...)
	combinedChanges = append(combinedChanges, newNS1Changes(ns1Delete, changes.Delete)...)

	return p.ns1SubmitChanges(combinedChanges)
}

// newNS1Changes returns a collection of Changes based on the given records and action.
func newNS1Changes(action string, endpoints []*endpoint.Endpoint) []*ns1Change {
	changes := make([]*ns1Change, 0, len(endpoints))

	for _, endpoint := range endpoints {
		changes = append(changes, &ns1Change{
			Action:   action,
			Endpoint: endpoint,
		},
		)
	}

	return changes
}

// ns1ChangesByZone separates a multi-zone change into a single change per zone.
func ns1ChangesByZone(zones []*dns.Zone, changeSets []*ns1Change) map[string][]*ns1Change {
	changes := make(map[string][]*ns1Change)
	zoneNameIDMapper := zoneIDName{}
	for _, z := range zones {
		zoneNameIDMapper.Add(z.Zone, z.Zone)
		changes[z.Zone] = []*ns1Change{}
	}

	for _, c := range changeSets {
		zone, _ := zoneNameIDMapper.FindZone(c.Endpoint.DNSName)
		if zone == "" {
			log.Debugf("Skipping record %s because no hosted zone matching record DNS Name was detected ", c.Endpoint.DNSName)
			continue
		}
		changes[zone] = append(changes[zone], c)
	}

	return changes
}
<commit_msg>Remove comment<commit_after>package provider

import (
	"fmt"
	"net/http"
	"os"
	"strings"

	log "github.com/sirupsen/logrus"

	api "gopkg.in/ns1/ns1-go.v2/rest"
	"gopkg.in/ns1/ns1-go.v2/rest/model/dns"

	"github.com/kubernetes-incubator/external-dns/endpoint"
	"github.com/kubernetes-incubator/external-dns/plan"
)

const (
	// ns1Create is a ChangeAction enum value
	ns1Create = "CREATE"
	// ns1Delete is a ChangeAction enum value
	ns1Delete = "DELETE"
	// ns1Update is a ChangeAction enum value
	ns1Update = "UPDATE"
	// ns1DefaultTTL is the default ttl for ttls that are not set
	ns1DefaultTTL = 10
)

// NS1DomainClient is a subset of the NS1 API the the provider uses, to ease testing
type NS1DomainClient interface {
	CreateRecord(r *dns.Record) (*http.Response, error)
	DeleteRecord(zone string, domain string, t string) (*http.Response, error)
	UpdateRecord(r *dns.Record) (*http.Response, error)
	GetZone(zone string) (*dns.Zone, *http.Response, error)
	ListZones() ([]*dns.Zone, *http.Response, error)
}

// NS1DomainService wraps the API and fulfills the NS1DomainClient interface
type NS1DomainService struct {
	service *api.Client
}

// CreateRecord wraps the Create method of the API's Record service
func (n NS1DomainService) CreateRecord(r *dns.Record) (*http.Response, error) {
	return n.service.Records.Create(r)
}

// DeleteRecord wraps the Delete method of the API's Record service
func (n NS1DomainService) DeleteRecord(zone string, domain string, t string) (*http.Response, error) {
	return n.service.Records.Delete(zone, domain, t)
}

// UpdateRecord wraps the Update method of the API's Record service
func (n NS1DomainService) UpdateRecord(r *dns.Record) (*http.Response, error) {
	return n.service.Records.Update(r)
}

// GetZone wraps the Get method of the API's Zones service
func (n NS1DomainService) GetZone(zone string) (*dns.Zone, *http.Response, error) {
	return n.service.Zones.Get(zone)
}

// ListZones wraps the List method of the API's Zones service
func (n NS1DomainService) ListZones() ([]*dns.Zone, *http.Response, error) {
	return n.service.Zones.List()
}

// NS1Config passes cli args to the NS1Provider
type NS1Config struct {
	DomainFilter DomainFilter
	ZoneIDFilter ZoneIDFilter
	DryRun       bool
}

// NS1Provider is the NS1 provider
type NS1Provider struct {
	client       NS1DomainClient
	domainFilter DomainFilter
	zoneIDFilter ZoneIDFilter
	dryRun       bool
}

// NewNS1Provider creates a new NS1 Provider
func NewNS1Provider(config NS1Config) (*NS1Provider, error) {
	return newNS1ProviderWithHTTPClient(config, http.DefaultClient)
}

func newNS1ProviderWithHTTPClient(config NS1Config, client *http.Client) (*NS1Provider, error) {
	token, ok := os.LookupEnv("NS1_APIKEY")
	if !ok {
		return nil, fmt.Errorf("NS1_APIKEY environment variable is not set")
	}

	apiClient := api.NewClient(client, api.SetAPIKey(token))

	provider := &NS1Provider{
		client:       NS1DomainService{apiClient},
		domainFilter: config.DomainFilter,
		zoneIDFilter: config.ZoneIDFilter,
	}
	return provider, nil
}

// Records returns the endpoints this provider knows about
func (p *NS1Provider) Records() ([]*endpoint.Endpoint, error) {
	zones, err := p.zonesFiltered()
	if err != nil {
		return nil, err
	}

	var endpoints []*endpoint.Endpoint

	for _, zone := range zones {

		// TODO handle Header Codes
		zoneData, _, err := p.client.GetZone(zone.String())
		if err != nil {
			return nil, err
		}

		for _, record := range zoneData.Records {
			if supportedRecordType(record.Type) {
				endpoints = append(endpoints, endpoint.NewEndpointWithTTL(
					record.Domain,
					record.Type,
					endpoint.TTL(record.TTL),
					record.ShortAns...,
				),
				)
			}
		}
	}

	return endpoints, nil
}

// ns1BuildRecord returns a dns.Record for a change set
func ns1BuildRecord(zoneName string, change *ns1Change) *dns.Record {
	record := dns.NewRecord(zoneName, change.Endpoint.DNSName, change.Endpoint.RecordType)
	for _, v := range change.Endpoint.Targets {
		record.AddAnswer(dns.NewAnswer(strings.Split(v, " ")))
	}
	// set detault ttl
	var ttl = ns1DefaultTTL
	if change.Endpoint.RecordTTL.IsConfigured() {
		ttl = int(change.Endpoint.RecordTTL)
	}
	record.TTL = ttl

	return record
}

// ns1SubmitChanges takes an array of changes and sends them to NS1
func (p *NS1Provider) ns1SubmitChanges(changes []*ns1Change) error {
	// return early if there is nothing to change
	if len(changes) == 0 {
		return nil
	}

	zones, err := p.zonesFiltered()
	if err != nil {
		return err
	}

	// separate into per-zone change sets to be passed to the API.
	changesByZone := ns1ChangesByZone(zones, changes)
	for zoneName, changes := range changesByZone {
		for _, change := range changes {
			record := ns1BuildRecord(zoneName, change)
			logFields := log.Fields{
				"record": record.Domain,
				"type":   record.Type,
				"ttl":    record.TTL,
				"action": change.Action,
				"zone":   zoneName,
			}

			log.WithFields(logFields).Info("Changing record.")

			if p.dryRun {
				continue
			}

			switch change.Action {
			case ns1Create:
				_, err := p.client.CreateRecord(record)
				if err != nil {
					return err
				}
			case ns1Delete:
				_, err := p.client.DeleteRecord(zoneName, record.Domain, record.Type)
				if err != nil {
					return err
				}
			case ns1Update:
				_, err := p.client.UpdateRecord(record)
				if err != nil {
					return err
				}
			}
		}
	}
	return nil
}

// Zones returns the list of hosted zones.
func (p *NS1Provider) zonesFiltered() ([]*dns.Zone, error) {
	// TODO handle Header Codes
	zones, _, err := p.client.ListZones()
	if err != nil {
		return nil, err
	}

	toReturn := []*dns.Zone{}

	for _, z := range zones {
		if p.domainFilter.Match(z.Zone) && p.zoneIDFilter.Match(z.ID) {
			toReturn = append(toReturn, z)
			log.Debugf("Matched %s", z.Zone)
		} else {
			log.Debugf("Filtered %s", z.Zone)
		}
	}

	return toReturn, nil
}

// ns1Change differentiates between ChangeActions
type ns1Change struct {
	Action   string
	Endpoint *endpoint.Endpoint
}

// ApplyChanges applies a given set of changes in a given zone.
func (p *NS1Provider) ApplyChanges(changes *plan.Changes) error {
	combinedChanges := make([]*ns1Change, 0, len(changes.Create)+len(changes.UpdateNew)+len(changes.Delete))

	combinedChanges = append(combinedChanges, newNS1Changes(ns1Create, changes.Create)...)
	combinedChanges = append(combinedChanges, newNS1Changes(ns1Update, changes.UpdateNew)...)
	combinedChanges = append(combinedChanges, newNS1Changes(ns1Delete, changes.Delete)...)

	return p.ns1SubmitChanges(combinedChanges)
}

// newNS1Changes returns a collection of Changes based on the given records and action.
func newNS1Changes(action string, endpoints []*endpoint.Endpoint) []*ns1Change {
	changes := make([]*ns1Change, 0, len(endpoints))

	for _, endpoint := range endpoints {
		changes = append(changes, &ns1Change{
			Action:   action,
			Endpoint: endpoint,
		},
		)
	}

	return changes
}

// ns1ChangesByZone separates a multi-zone change into a single change per zone.
func ns1ChangesByZone(zones []*dns.Zone, changeSets []*ns1Change) map[string][]*ns1Change {
	changes := make(map[string][]*ns1Change)
	zoneNameIDMapper := zoneIDName{}
	for _, z := range zones {
		zoneNameIDMapper.Add(z.Zone, z.Zone)
		changes[z.Zone] = []*ns1Change{}
	}

	for _, c := range changeSets {
		zone, _ := zoneNameIDMapper.FindZone(c.Endpoint.DNSName)
		if zone == "" {
			log.Debugf("Skipping record %s because no hosted zone matching record DNS Name was detected ", c.Endpoint.DNSName)
			continue
		}
		changes[zone] = append(changes[zone], c)
	}

	return changes
}
<|endoftext|>


<commit_before>package mapper

import (
	"encoding/json"
	"fmt"
	"net"
	"net/http"
	"net/http/httptest"
	"net/url"
	"reflect"
	"sort"
	"testing"
)

func TestJSON(t *testing.T) {
	wantSM := smJSON{
		Nodes: []nodeJSON{
			nodeJSON{ID: "/", Label: "/"},
			nodeJSON{ID: "/hello-world", Label: "/hello-world"},
			nodeJSON{ID: "/values", Label: "/values"},
			nodeJSON{ID: "/variables", Label: "/variables"},
			nodeJSON{ID: "/constants", Label: "/constants", Color: failColor},
		},
		Edges: []edgeJSON{
			edgeJSON{ID: "/->/hello-world", Source: "/", Target: "/hello-world"},
			edgeJSON{ID: "/->/values", Source: "/", Target: "/values"},
			edgeJSON{ID: "/->/variables", Source: "/", Target: "/variables"},
			edgeJSON{ID: "/hello-world->/", Source: "/hello-world", Target: "/"},
			edgeJSON{ID: "/hello-world->/values", Source: "/hello-world", Target: "/values"},
			edgeJSON{ID: "/values->/", Source: "/values", Target: "/"},
			edgeJSON{ID: "/values->/variables", Source: "/values", Target: "/variables"},
			edgeJSON{ID: "/variables->/", Source: "/variables", Target: "/"},
			edgeJSON{ID: "/variables->/constants", Source: "/variables", Target: "/constants"},
		},
	}

	server := httptest.NewServer(http.FileServer(http.Dir("testdata")))
	defer server.Close()

	u, err := url.Parse(server.URL + "/hello-world")
	if err != nil {
		t.Fatal(err)
	}

	sm, err := NewSiteMap(u.String(), 2)
	if err != nil {
		t.Fatal(err)
	}

	if err := sm.Start(); err != nil {
		t.Errorf("Start error: %v", err)
	}

	ln, err := net.Listen("tcp", "")
	http.Handle("/json", sm)
	go http.Serve(ln, nil)
	resp, err := http.Get(fmt.Sprintf("http://%s/json", ln.Addr()))
	if err != nil {
		t.Fatalf("Failed to retrieve JSON: %v", err)
	}

	gotSM := &smJSON{}
	d := json.NewDecoder(resp.Body)
	if err := d.Decode(gotSM); err != nil {
		t.Fatal(err)
	}

	if got, want := len(gotSM.Edges), len(wantSM.Edges); got != want {
		t.Errorf("Got %d edges, want %d", got, want)
	}
	sort.Slice(gotSM.Edges, func(i, j int) bool { return gotSM.Edges[i].ID < gotSM.Edges[j].ID })
	sort.Slice(wantSM.Edges, func(i, j int) bool { return wantSM.Edges[i].ID < wantSM.Edges[j].ID })
	if !reflect.DeepEqual(gotSM.Edges, wantSM.Edges) {
		t.Errorf("Got edges\n%v\nwant\n%v\n", gotSM.Edges, wantSM.Edges)
	}

	if got, want := len(gotSM.Nodes), len(wantSM.Nodes); got != want {
		t.Fatalf("Got %d nodes, want %d", got, want)
	}
	sort.Slice(gotSM.Nodes, func(i, j int) bool { return gotSM.Nodes[i].ID < gotSM.Nodes[j].ID })
	sort.Slice(wantSM.Nodes, func(i, j int) bool { return wantSM.Nodes[i].ID < wantSM.Nodes[j].ID })
	for i, got := range gotSM.Nodes {
		want := wantSM.Nodes[i]
		// note this does not validate x/y positions as they are randomized
		if got.ID != want.ID || got.Label != want.Label || got.Color != want.Color {
			t.Errorf("Node %d - got %#v, want %#v", i, got, want)
		}
	}
}
<commit_msg>Fix the tests on Travis CI<commit_after>package mapper

import (
	"encoding/json"
	"fmt"
	"net"
	"net/http"
	"net/http/httptest"
	"net/url"
	"reflect"
	"sort"
	"testing"
)

func TestJSON(t *testing.T) {
	wantSM := smJSON{
		Nodes: []nodeJSON{
			nodeJSON{ID: "/", Label: "/"},
			nodeJSON{ID: "/hello-world", Label: "/hello-world"},
			nodeJSON{ID: "/values", Label: "/values"},
			nodeJSON{ID: "/variables", Label: "/variables"},
			nodeJSON{ID: "/constants", Label: "/constants", Color: failColor},
		},
		Edges: []edgeJSON{
			edgeJSON{ID: "/->/hello-world", Source: "/", Target: "/hello-world"},
			edgeJSON{ID: "/->/values", Source: "/", Target: "/values"},
			edgeJSON{ID: "/->/variables", Source: "/", Target: "/variables"},
			edgeJSON{ID: "/hello-world->/", Source: "/hello-world", Target: "/"},
			edgeJSON{ID: "/hello-world->/values", Source: "/hello-world", Target: "/values"},
			edgeJSON{ID: "/values->/", Source: "/values", Target: "/"},
			edgeJSON{ID: "/values->/variables", Source: "/values", Target: "/variables"},
			edgeJSON{ID: "/variables->/", Source: "/variables", Target: "/"},
			edgeJSON{ID: "/variables->/constants", Source: "/variables", Target: "/constants"},
		},
	}

	server := httptest.NewServer(http.FileServer(http.Dir("testdata")))
	defer server.Close()

	u, err := url.Parse(server.URL + "/hello-world")
	if err != nil {
		t.Fatal(err)
	}

	sm, err := NewSiteMap(u.String(), 2)
	if err != nil {
		t.Fatal(err)
	}

	if err := sm.Start(); err != nil {
		t.Errorf("Start error: %v", err)
	}

	ln, err := net.Listen("tcp", "127.0.0.1:0")
	http.Handle("/json", sm)
	go http.Serve(ln, nil)
	resp, err := http.Get(fmt.Sprintf("http://%s/json", ln.Addr()))
	if err != nil {
		t.Fatalf("Failed to retrieve JSON: %v", err)
	}

	gotSM := &smJSON{}
	d := json.NewDecoder(resp.Body)
	if err := d.Decode(gotSM); err != nil {
		t.Fatal(err)
	}

	if got, want := len(gotSM.Edges), len(wantSM.Edges); got != want {
		t.Errorf("Got %d edges, want %d", got, want)
	}
	sort.Slice(gotSM.Edges, func(i, j int) bool { return gotSM.Edges[i].ID < gotSM.Edges[j].ID })
	sort.Slice(wantSM.Edges, func(i, j int) bool { return wantSM.Edges[i].ID < wantSM.Edges[j].ID })
	if !reflect.DeepEqual(gotSM.Edges, wantSM.Edges) {
		t.Errorf("Got edges\n%v\nwant\n%v\n", gotSM.Edges, wantSM.Edges)
	}

	if got, want := len(gotSM.Nodes), len(wantSM.Nodes); got != want {
		t.Fatalf("Got %d nodes, want %d", got, want)
	}
	sort.Slice(gotSM.Nodes, func(i, j int) bool { return gotSM.Nodes[i].ID < gotSM.Nodes[j].ID })
	sort.Slice(wantSM.Nodes, func(i, j int) bool { return wantSM.Nodes[i].ID < wantSM.Nodes[j].ID })
	for i, got := range gotSM.Nodes {
		want := wantSM.Nodes[i]
		// note this does not validate x/y positions as they are randomized
		if got.ID != want.ID || got.Label != want.Label || got.Color != want.Color {
			t.Errorf("Node %d - got %#v, want %#v", i, got, want)
		}
	}
}
<|endoftext|>


<commit_before>package sdm630

import (
	"fmt"
	"log"
	"sort"
	"sync"
	"time"

	. "github.com/gonium/gosdm630/internal/meters"
)

type MeasurementCache struct {
	in      QuerySnipChannel
	meters  map[uint8]MeasurementCacheItem
	maxAge  time.Duration
	verbose bool
}

type MeasurementCacheItem struct {
	*Meter
	*MeterReadings
}

func NewMeasurementCache(
	meters map[uint8]*Meter,
	inChannel QuerySnipChannel,
	scheduler *MeterScheduler,
	maxAge time.Duration,
	isVerbose bool,
) *MeasurementCache {
	items := make(map[uint8]MeasurementCacheItem)

	for _, meter := range meters {
		items[meter.DeviceId] = MeasurementCacheItem{
			meter,
			NewMeterReadings(meter.DeviceId, maxAge),
		}
	}

	cache := &MeasurementCache{
		in:      inChannel,
		meters:  items,
		maxAge:  maxAge,
		verbose: isVerbose,
	}

	scheduler.SetCache(cache)
	return cache
}

func (mc *MeasurementCache) Consume() {
	for {
		snip := <-mc.in
		devid := snip.DeviceId
		// Search corresponding meter
		if meter, ok := mc.meters[devid]; ok {
			// add the snip to the cache
			meter.AddSnip(snip)
			if mc.verbose {
				log.Printf("%s\r\n", meter.Current)
			}
		} else {
			log.Fatal("Snip for unknown meter received - this should not happen.")
		}
	}
}

// Purge removes accumulated data for specified device
func (mc *MeasurementCache) Purge(deviceId byte) error {
	if meter, ok := mc.meters[deviceId]; ok {
		meter.Purge(deviceId)
		return nil
	}

	return fmt.Errorf("Device with id %d does not exist.", deviceId)
}

func (mc *MeasurementCache) GetSortedIDs() []byte {
	var keys ByteSlice
	for k, _ := range mc.meters {
		keys = append(keys, k)
	}
	sort.Sort(keys)
	return keys
}

func (mc *MeasurementCache) GetCurrent(deviceId byte) (*Readings, error) {
	if meter, ok := mc.meters[deviceId]; ok {
		if meter.GetState() == AVAILABLE {
			return &meter.Current, nil
		}
		return nil, fmt.Errorf("Device %d is not available.", deviceId)
	}
	return nil, fmt.Errorf("Device %d does not exist.", deviceId)
}

func (mc *MeasurementCache) GetMinuteAvg(deviceId byte) (*Readings, error) {
	if meter, ok := mc.meters[deviceId]; ok {
		if meter.GetState() == AVAILABLE {
			measurements := meter.Historic
			lastminute := measurements.NotOlderThan(time.Now().Add(-1 * time.Minute))

			res, err := lastminute.Average()
			if err != nil {
				return nil, err
			}

			if mc.verbose {
				log.Printf("Averaging over %d measurements:\r\n%s\r\n",
					len(measurements), res.String())
			}
			return res, nil
		}
		return nil, fmt.Errorf("Device %d is not available.", deviceId)
	}
	return nil, fmt.Errorf("Device %d does not exist.", deviceId)
}

// ByteSlice attaches the methods of sort.Interface to []byte, sorting in increasing order.
type ByteSlice []byte

func (s ByteSlice) Len() int           { return len(s) }
func (s ByteSlice) Less(i, j int) bool { return s[i] < s[j] }
func (s ByteSlice) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }

type MeterReadings struct {
	Historic ReadingSlice
	Current  Readings
	mux      sync.Mutex
}

func NewMeterReadings(devid uint8, maxAge time.Duration) *MeterReadings {
	res := &MeterReadings{
		Historic: ReadingSlice{},
		Current: Readings{
			UniqueId: fmt.Sprintf(UniqueIdFormat, devid),
			DeviceId: devid,
		},
	}
	go func(mr *MeterReadings) {
		for {
			time.Sleep(maxAge)
			mr.mux.Lock()
			mr.Historic = mr.Historic.NotOlderThan(time.Now().Add(-1 * maxAge))
			mr.mux.Unlock()
		}
	}(res)
	return res
}

func (mr *MeterReadings) Purge(devid uint8) {
	mr.mux.Lock()
	defer mr.mux.Unlock()

	mr.Historic = ReadingSlice{}
	mr.Current = Readings{
		UniqueId: fmt.Sprintf(UniqueIdFormat, devid),
		DeviceId: devid,
	}
}

func (mr *MeterReadings) AddSnip(snip QuerySnip) {
	mr.mux.Lock()
	defer mr.mux.Unlock()

	// 1. Merge the snip to the last values.
	reading := mr.Current
	reading.MergeSnip(snip)

	// 2. store it
	mr.Current = reading
	mr.Historic = append(mr.Historic, reading)
}
<commit_msg>Fix string conversion<commit_after>package sdm630

import (
	"fmt"
	"log"
	"sort"
	"sync"
	"time"

	. "github.com/gonium/gosdm630/internal/meters"
)

type MeasurementCache struct {
	in      QuerySnipChannel
	meters  map[uint8]MeasurementCacheItem
	maxAge  time.Duration
	verbose bool
}

type MeasurementCacheItem struct {
	*Meter
	*MeterReadings
}

func NewMeasurementCache(
	meters map[uint8]*Meter,
	inChannel QuerySnipChannel,
	scheduler *MeterScheduler,
	maxAge time.Duration,
	isVerbose bool,
) *MeasurementCache {
	items := make(map[uint8]MeasurementCacheItem)

	for _, meter := range meters {
		items[meter.DeviceId] = MeasurementCacheItem{
			meter,
			NewMeterReadings(meter.DeviceId, maxAge),
		}
	}

	cache := &MeasurementCache{
		in:      inChannel,
		meters:  items,
		maxAge:  maxAge,
		verbose: isVerbose,
	}

	scheduler.SetCache(cache)
	return cache
}

func (mc *MeasurementCache) Consume() {
	for {
		snip := <-mc.in
		devid := snip.DeviceId
		// Search corresponding meter
		if meter, ok := mc.meters[devid]; ok {
			// add the snip to the cache
			meter.AddSnip(snip)
			if mc.verbose {
				log.Printf("%s\r\n", meter.Current.String())
			}
		} else {
			log.Fatal("Snip for unknown meter received - this should not happen.")
		}
	}
}

// Purge removes accumulated data for specified device
func (mc *MeasurementCache) Purge(deviceId byte) error {
	if meter, ok := mc.meters[deviceId]; ok {
		meter.Purge(deviceId)
		return nil
	}

	return fmt.Errorf("Device with id %d does not exist.", deviceId)
}

func (mc *MeasurementCache) GetSortedIDs() []byte {
	var keys ByteSlice
	for k, _ := range mc.meters {
		keys = append(keys, k)
	}
	sort.Sort(keys)
	return keys
}

func (mc *MeasurementCache) GetCurrent(deviceId byte) (*Readings, error) {
	if meter, ok := mc.meters[deviceId]; ok {
		if meter.GetState() == AVAILABLE {
			return &meter.Current, nil
		}
		return nil, fmt.Errorf("Device %d is not available.", deviceId)
	}
	return nil, fmt.Errorf("Device %d does not exist.", deviceId)
}

func (mc *MeasurementCache) GetMinuteAvg(deviceId byte) (*Readings, error) {
	if meter, ok := mc.meters[deviceId]; ok {
		if meter.GetState() == AVAILABLE {
			measurements := meter.Historic
			lastminute := measurements.NotOlderThan(time.Now().Add(-1 * time.Minute))

			res, err := lastminute.Average()
			if err != nil {
				return nil, err
			}

			if mc.verbose {
				log.Printf("Averaging over %d measurements:\r\n%s\r\n",
					len(measurements), res.String())
			}
			return res, nil
		}
		return nil, fmt.Errorf("Device %d is not available.", deviceId)
	}
	return nil, fmt.Errorf("Device %d does not exist.", deviceId)
}

// ByteSlice attaches the methods of sort.Interface to []byte, sorting in increasing order.
type ByteSlice []byte

func (s ByteSlice) Len() int           { return len(s) }
func (s ByteSlice) Less(i, j int) bool { return s[i] < s[j] }
func (s ByteSlice) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }

type MeterReadings struct {
	Historic ReadingSlice
	Current  Readings
	mux      sync.Mutex
}

func NewMeterReadings(devid uint8, maxAge time.Duration) *MeterReadings {
	res := &MeterReadings{
		Historic: ReadingSlice{},
		Current: Readings{
			UniqueId: fmt.Sprintf(UniqueIdFormat, devid),
			DeviceId: devid,
		},
	}
	go func(mr *MeterReadings) {
		for {
			time.Sleep(maxAge)
			mr.mux.Lock()
			mr.Historic = mr.Historic.NotOlderThan(time.Now().Add(-1 * maxAge))
			mr.mux.Unlock()
		}
	}(res)
	return res
}

func (mr *MeterReadings) Purge(devid uint8) {
	mr.mux.Lock()
	defer mr.mux.Unlock()

	mr.Historic = ReadingSlice{}
	mr.Current = Readings{
		UniqueId: fmt.Sprintf(UniqueIdFormat, devid),
		DeviceId: devid,
	}
}

func (mr *MeterReadings) AddSnip(snip QuerySnip) {
	mr.mux.Lock()
	defer mr.mux.Unlock()

	// 1. Merge the snip to the last values.
	reading := mr.Current
	reading.MergeSnip(snip)

	// 2. store it
	mr.Current = reading
	mr.Historic = append(mr.Historic, reading)
}
<|endoftext|>


<commit_before>package messages

import (
	"code.google.com/p/go-uuid/uuid"
	"github.com/qp/go/utils"
)

// Message is the standard QP messaging object.
// It is used to facilitate all communication between
// QP nodes, as well as containing the metadata
// necessary to implement the pipeline functionality.
type Message struct {
	To   utils.StringDES // array of destination addresses
	From utils.StringDES // array of addresses encountered thus far
	ID   string          // a UUID identifying this message
	Data interface{}     // arbitrary data payload
	Err  interface{}     // arbitrary error payload. nil if no error
}

// NewMessage creates a new Message object with appropriate fields set.
func NewMessage(serviceName string, data interface{}, to ...string) *Message {
	id := uuid.New()
	return &Message{To: to, From: []string{serviceName + "/" + id}, ID: id, Data: data}
}
<commit_msg>Add json tags<commit_after>package messages

import (
	"code.google.com/p/go-uuid/uuid"
	"github.com/qp/go/utils"
)

// Message is the standard QP messaging object.
// It is used to facilitate all communication between
// QP nodes, as well as containing the metadata
// necessary to implement the pipeline functionality.
type Message struct {
	To   utils.StringDES `json:"to"`            // array of destination addresses
	From utils.StringDES `json:"from"`          // array of addresses encountered thus far
	ID   string          `json:"id"`            // a UUID identifying this message
	Data interface{}     `json:"data"`          // arbitrary data payload
	Err  interface{}     `json:"err,omitempty"` // arbitrary error payload. nil if no error
}

// NewMessage creates a new Message object with appropriate fields set.
func NewMessage(serviceName string, data interface{}, to ...string) *Message {
	id := uuid.New()
	return &Message{To: to, From: []string{serviceName + "/" + id}, ID: id, Data: data}
}
<|endoftext|>


<commit_before>package logging

import (
	"runtime"
)

type logger struct {
	name     string
	parent   *logger
	handlers []handler
}

var rootLogger logger
var defaultLoggerLevel string

func init() {
	rootLogger = logger{
		"rootLogger",
		nil,
		make([]handler, 0, 5),
	}

	defaultLoggerLevel = "debug"

	rootLogger.handlers = append(rootLogger.handlers, &SimpleHandler{})
}

func SetLoggerLevel(level string) {
	switch level {
	case "debug":
		fallthrough
	case "DEBUG":
		defaultLoggerLevel = "debug"
	case "info":
		fallthrough
	case "INFO":
		defaultLoggerLevel = "info"
	case "warn":
		fallthrough
	case "WARN":
		defaultLoggerLevel = "warn"
	case "error":
		fallthrough
	case "ERROR":
		defaultLoggerLevel = "error"
	default:
		defaultLoggerLevel = "debug"
	}
}

func GetLogger(name string) logger {
	l := logger{
		name,
		&rootLogger,
		make([]handler, 0, 5),
	}

	return l
}

func (l *logger) record(level string, s string, file string, line int) {
	for _, h := range l.handlers {
		h.record(level, s, file, line)
	}
	if l.parent != nil {
		l.parent.record(level, s, file, line)
	}
}

func (l *logger) Debug(s string) {
	_, file, line, _ := runtime.Caller(1)
	switch defaultLoggerLevel {
	case "debug":
		{
			l.record("debug", s, file, line)
		}
	}
}

func (l *logger) Info(s string) {
	_, file, line, _ := runtime.Caller(1)
	switch defaultLoggerLevel {
	case "debug":
		fallthrough
	case "info":
		{
			l.record("info", s, file, line)
		}
	}
}

func (l *logger) Warn(s string) {
	_, file, line, _ := runtime.Caller(1)
	switch defaultLoggerLevel {
	case "debug":
		fallthrough
	case "info":
		fallthrough
	case "warn":
		{
			l.record("warn", s, file, line)
		}
	}
}

func (l *logger) Error(s string) {
	_, file, line, _ := runtime.Caller(1)
	switch defaultLoggerLevel {
	case "debug":
		fallthrough
	case "info":
		fallthrough
	case "warn":
		fallthrough
	case "error":
		{
			l.record("error", s, file, line)
		}
	}
}
<commit_msg>using Logger instead of logger<commit_after>package logging

import (
	"runtime"
)

type Logger struct {
	name     string
	parent   *Logger
	handlers []handler
}

var rootLogger Logger
var defaultLoggerLevel string

func init() {
	rootLogger = Logger{
		"rootLogger",
		nil,
		make([]handler, 0, 5),
	}

	defaultLoggerLevel = "debug"

	rootLogger.handlers = append(rootLogger.handlers, &SimpleHandler{})
}

func SetLoggerLevel(level string) {
	switch level {
	case "debug":
		fallthrough
	case "DEBUG":
		defaultLoggerLevel = "debug"
	case "info":
		fallthrough
	case "INFO":
		defaultLoggerLevel = "info"
	case "warn":
		fallthrough
	case "WARN":
		defaultLoggerLevel = "warn"
	case "error":
		fallthrough
	case "ERROR":
		defaultLoggerLevel = "error"
	default:
		defaultLoggerLevel = "debug"
	}
}

func GetLogger(name string) Logger {
	l := Logger{
		name,
		&rootLogger,
		make([]handler, 0, 5),
	}

	return l
}

func (l *Logger) record(level string, s string, file string, line int) {
	for _, h := range l.handlers {
		h.record(level, s, file, line)
	}
	if l.parent != nil {
		l.parent.record(level, s, file, line)
	}
}

func (l *Logger) Debug(s string) {
	_, file, line, _ := runtime.Caller(1)
	switch defaultLoggerLevel {
	case "debug":
		{
			l.record("debug", s, file, line)
		}
	}
}

func (l *Logger) Info(s string) {
	_, file, line, _ := runtime.Caller(1)
	switch defaultLoggerLevel {
	case "debug":
		fallthrough
	case "info":
		{
			l.record("info", s, file, line)
		}
	}
}

func (l *Logger) Warn(s string) {
	_, file, line, _ := runtime.Caller(1)
	switch defaultLoggerLevel {
	case "debug":
		fallthrough
	case "info":
		fallthrough
	case "warn":
		{
			l.record("warn", s, file, line)
		}
	}
}

func (l *Logger) Error(s string) {
	_, file, line, _ := runtime.Caller(1)
	switch defaultLoggerLevel {
	case "debug":
		fallthrough
	case "info":
		fallthrough
	case "warn":
		fallthrough
	case "error":
		{
			l.record("error", s, file, line)
		}
	}
}
<|endoftext|>


<commit_before>package core

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"sync"

	"github.com/tinzenite/model"
	"github.com/tinzenite/shared"
)

/*
onEncryptedMessage is called for messages from encrypted peers. Will redestribute
the message according to its type.

TODO describe order of operations (successful lock -> request model -> sync -> push / pull difference)
*/
func (c *chaninterface) onEncryptedMessage(address string, msgType shared.MsgType, message string) {
	// TODO switch and handle messages NOTE FIXME implement
	switch msgType {
	case shared.MsgLock:
		msg := &shared.LockMessage{}
		err := json.Unmarshal([]byte(message), msg)
		if err != nil {
			log.Println(err.Error())
			return
		}
		c.onEncLockMessage(address, *msg)
	case shared.MsgNotify:
		msg := &shared.NotifyMessage{}
		err := json.Unmarshal([]byte(message), msg)
		if err != nil {
			log.Println(err.Error())
			return
		}
		c.onEncNotifyMessage(address, *msg)
	case shared.MsgRequest:
		msg := &shared.RequestMessage{}
		err := json.Unmarshal([]byte(message), msg)
		if err != nil {
			log.Println(err.Error())
			return
		}
		c.onEncRequestMessage(address, *msg)
	default:
		c.warn("Unknown object received:", msgType.String())
	}
}

/*
onEncLockMessage handles lock messages. Notably it requests a model on a
successful lock, starting a synchronization.

FIXME: peer never sends a release request to locked encrypted peers! For now we
just await the timeout... TODO
*/
func (c *chaninterface) onEncLockMessage(address string, msg shared.LockMessage) {
	switch msg.Action {
	case shared.LoAccept:
		// remember that this peer is locked
		_, exists := c.tin.peers[address]
		if !exists {
			c.warn("Can not set peer to locked as peer doesn't exist!")
			return
		}
		c.tin.peers[address].SetLocked(true)
		// if LOCKED request model file to begin sync
		rm := shared.CreateRequestMessage(shared.OtModel, shared.IDMODEL)
		c.requestFile(address, rm, c.encModelReceived)
	case shared.LoRelease:
		// unset lock of this peer
		_, exists := c.tin.peers[address]
		if !exists {
			c.warn("Can not release peer as peer doesn't exist!")
			return
		}
		c.tin.peers[address].SetLocked(false)
	default:
		c.warn("Unknown lock action received:", msg.Action.String())
	}
}

/*
onEncNotifyMessage handles the reception of notification messages.
*/
func (c *chaninterface) onEncNotifyMessage(address string, msg shared.NotifyMessage) {
	switch msg.Notify {
	case shared.NoMissing:
		// remove transfer as no file will come
		delete(c.inTransfers, c.buildKey(address, msg.Identification))
		// if model --> create it
		if msg.Identification == shared.IDMODEL {
			// log that encrypted was empty and that we'll just upload our current state
			c.log("Encrypted is empty, nothing to merge, uploading directly.")
			// so send push for model and all objects
			c.sendCompletePushes(address)
			return
		}
		// TODO: if object --> error... maybe "reset" the encrypted peer?
		log.Println("DEBUG: object missing!", msg.Identification, msg.Notify)
		log.Println("DEBUG: TODO: reset encrypted peer?")
	default:
		c.warn("Unknown notify type received:", msg.Notify.String())
	}
}

/*
onEncRequestMessage handles the reception of a request message following a push
message. Triggers the sending of the requested file.
*/
func (c *chaninterface) onEncRequestMessage(address string, msg shared.RequestMessage) {
	var path string
	// if model has been requested --> path is different as not tracked itself
	if msg.Identification == shared.IDMODEL {
		path = c.tin.Path + "/" + shared.TINZENITEDIR + "/" + shared.SENDINGDIR + "/" + shared.MODELJSON
		// get model info
		model, err := c.tin.model.Read()
		if err != nil {
			c.warn("Failed to read model:", err.Error())
			return
		}
		// convert to json
		data, err := json.Marshal(model)
		if err != nil {
			c.warn("Failed to marshal model to JSON:", err.Error())
			return
		}
		// write json to temp file
		err = ioutil.WriteFile(path, data, shared.FILEPERMISSIONMODE)
		if err != nil {
			c.warn("Failed to write model info to temp file:", err.Error())
			return
		}
	} else {
		// get subPath for file
		subPath, err := c.tin.model.GetSubPath(msg.Identification)
		if err != nil {
			c.warn("Failed to locate subpath for request message!", msg.Identification)
			return
		}
		path = c.tin.Path + "/" + subPath
	}
	// and send file (concurrent because of encryption)
	log.Println("DEBUG: sending", path, "as", msg.ObjType)
	go c.encSendFile(address, msg.Identification, path, msg.ObjType)
	// TODO: shouldn't we reread the msg.ObjType from disk too?
}

/*
encSendFile handles uploading a file to the encrypted peer. This function is MADE
to run concurrently. Path is the path where the file CURRENTLY resides: the method
will copy all its data to SENDINGDIR, encrypt it there, and then send it.
*/
func (c *chaninterface) encSendFile(address, identification, path string, ot shared.ObjectType) {
	// read file data
	data, err := ioutil.ReadFile(path)
	if err != nil {
		c.warn("Failed to read data:", err.Error())
		return
	}
	// TODO encrypt here? FIXME
	// log.Println("DEBUG: encrypt here")
	// write to temp file
	sendPath := c.tin.Path + "/" + shared.TINZENITEDIR + "/" + shared.SENDINGDIR + "/" + identification
	err = ioutil.WriteFile(sendPath, data, shared.FILEPERMISSIONMODE)
	if err != nil {
		c.warn("Failed to write (encrypted) data to sending file:", err.Error())
		return
	}
	// send file
	err = c.tin.channel.SendFile(address, sendPath, identification, func(success bool) {
		if !success {
			c.log("Failed to upload file!", ot.String(), identification)
		}
		// remove sending temp file always
		err := os.Remove(sendPath)
		if err != nil {
			c.warn("Failed to remove sending file!", err.Error())
		}
	})
	if err != nil {
		c.warn("Failed to send file:", err.Error())
		return
	}
	// done
}

/*
sendCompletePushes sends push models for everything, starting with the model.
This will result in the encrypted peer requesting all objects.
*/
func (c *chaninterface) sendCompletePushes(address string) {
	// vars we'll use
	var pm shared.PushMessage
	// start by sending push for model
	pm = shared.CreatePushMessage(shared.IDMODEL, shared.OtModel)
	c.tin.channel.Send(address, pm.JSON())
	for path, stin := range c.tin.model.StaticInfos {
		// if directory, skip
		if stin.Directory {
			continue
		}
		c.encSendPush(address, path, stin.Identification)
	}
	// and done
}

/*
encModelReceived is called when a model is received from an encrypted peer. It
triggers the complete sync with the encrypted state, concluding with updating
the encrypted peer to be up to date with this peer.

NOTE: this is a long method because it blocks until ALL operations required for
the complete model sync have completed. FIXME: make this work better.
*/
func (c *chaninterface) encModelReceived(address, path string) {
	// no matter what: remove temp file
	defer func() {
		err := os.Remove(path)
		if err != nil {
			c.warn("encModelReceived: failed to remove received temp model file!", err.Error())
		}
	}()
	// read model
	data, err := ioutil.ReadFile(path)
	if err != nil {
		c.log("encModelReceived: failed to read received model file:", err.Error())
		return
	}
	// TODO decrypt file!
	// log.Println("DEBUG: TODO: decrypt model here!")
	// unmarshal
	foreignModel := &shared.ObjectInfo{}
	err = json.Unmarshal(data, foreignModel)
	if err != nil {
		c.log("encModelReceived: failed to parse JSON:", err.Error())
		return
	}
	// get updates we have to apply to ourselves before updating encrypted
	updateLists, err := c.tin.model.Sync(foreignModel)
	if err != nil {
		c.log("encModelReceived: failed to sync models:", err.Error())
		return
	}
	// apply updates to bring our model to a merged state
	var wg sync.WaitGroup
	for _, um := range updateLists {
		wg.Add(1) // wait for each subroutine
		// pass by value, not reference
		go func(um shared.UpdateMessage) {
			defer func() { wg.Done() }() // no matter what unlock sync
			err := c.handleEncryptedMessage(address, &um)
			if err != nil {
				c.log("encModelReceived: handleMessage failed with:", err.Error())
			}
		}(*um) // pass UpdateMessage because otherwise the pointer will change on each for iteration
	}
	wg.Wait() // wait for all updates to have been applied
	// when completely done, start uploading current state
	log.Println("DEBUG: now upload my current state to encrypted!")
	// build path maps and associated object maps of foreign model
	foreignPaths := make(map[string]bool)
	foreignObjs := make(map[string]*shared.ObjectInfo)
	foreignModel.ForEach(func(obj shared.ObjectInfo) {
		foreignPaths[obj.Path] = true
		// strip of children and write to objects
		obj.Objects = nil
		foreignObjs[obj.Path] = &obj
	})
	// get what must be changed
	created, remained, removed := shared.Difference(foreignPaths, c.tin.model.TrackedPaths)
	// for each path: check and create messages accordingly
	for _, create := range created {
		stin, exists := c.tin.model.StaticInfos[create]
		if !exists {
			// continue because this means it doesn't actually exist
			c.warn("encModelReceived: missing stin for locally created object!")
			continue
		}
		if stin.Directory {
			continue
		}
		log.Println("Send push for created", create)
		c.encSendPush(address, create, stin.Identification)
	}
	for _, remains := range remained {
		stin, exists := c.tin.model.StaticInfos[remains]
		if !exists {
			// continue because this means it doesn't actually exist
			c.warn("encModelReceived: missing stin for locally held object!")
			continue
		}
		if stin.Directory {
			continue
		}
		// fetch foreign object
		fObj, exists := foreignObjs[remains]
		if !exists {
			c.warn("encModelReceived: missing foreign object for locally held object!")
			continue
		}
		// if remote version already includes all known changes of local version, no need to send update, so continue
		if fObj.Version.Includes(stin.Version) {
			continue
		}
		// this means something has changed so reupload the object, overwritting the old version.
		log.Println("Send push for modified", remains)
		c.encSendPush(address, remains, stin.Identification)
	}
	// removed objects: use notify to have encrypted delete them
	for _, remove := range removed {
		stin, exists := foreignObjs[remove]
		if !exists {
			c.warn("encModelReceived: missing stin from foreign objects!")
			continue
		}
		if stin.Directory {
			continue
		}
		log.Println("Send notify for removal of", remove)
		nm := shared.CreateNotifyMessage(shared.NoRemoved, stin.Identification)
		c.tin.channel.Send(address, nm.JSON())
	}
	// TODO wg wait here FIXME: waiting to unlock means waiting for requests. Abstract messages away into channels?
	log.Println("DEBUG: done encrypted sync")
	return
	// finally unlock when done
	lm := shared.CreateLockMessage(shared.LoRelease)
	c.tin.channel.Send(address, lm.JSON())
}

/*
handleEncryptedMessage looks at the message, fetches files if required, and correctly
applies it to the model.

TODO handle how we update the encrypted peer – upload created and modified files, remove deleted files.
*/
func (c *chaninterface) handleEncryptedMessage(address string, msg *shared.UpdateMessage) error {
	// use check message to prepare message and check for special cases
	msg, err := c.tin.model.CheckMessage(msg)
	// if update known --> ignore it
	if err == model.ErrIgnoreUpdate {
		return nil
	}
	// if encrypted has a removal that we have registered as done, remove it
	if err == model.ErrObjectRemovalDone {
		log.Println("DEBUG: remove removal from encrypted.", msg.String())
		// TODO what exactly do I have to tell encrypted to remove? FIXME
		nm := shared.CreateNotifyMessage(shared.NoRemoved, msg.Object.Identification)
		c.tin.channel.Send(address, nm.JSON())
		// done
		return nil
	}
	// if still error, return it
	if err != nil {
		return err
	}
	// --> IF CheckMessage was ok, we can now handle applying the message
	// apply directories directly
	if msg.Object.Directory {
		// no merge because it should never happen for directories
		return c.tin.model.ApplyUpdateMessage(msg)
	}
	op := msg.Operation
	// create and modify must first fetch the file
	if op == shared.OpCreate || op == shared.OpModify {
		rm := shared.CreateRequestMessage(shared.OtObject, msg.Object.Identification)
		var wg sync.WaitGroup
		wg.Add(1)
		c.requestFile(address, rm, func(address, path string) {
			// force calling function to wait until this has been handled
			defer func() { wg.Done() }()
			// rename to correct name for model
			err := os.Rename(path, c.temppath+"/"+rm.Identification)
			if err != nil {
				c.log("Failed to move file to temp: " + err.Error())
				return
			}
			// TODO decrypt file!
			// log.Println("DEBUG: TODO: decrypt file here!")
			// apply
			err = c.mergeUpdate(*msg)
			if err != nil {
				c.log("File application error: " + err.Error())
			}
			// done
		})
		// wait for file to be received before returning
		wg.Wait()
		// errors may turn up but only when the file has been received, so done here
		return nil
	} else if op == shared.OpRemove {
		// remove is without file transfer, so directly apply
		return c.mergeUpdate(*msg)
	}
	c.warn("Unknown operation received, ignoring update message!")
	return shared.ErrIllegalParameters
}

/*
encSendPush sends a PushMessage for the given parameters, setting the ObjType
according to the path.
*/
func (c *chaninterface) encSendPush(address, path, identification string) {
	peerDir := shared.TINZENITEDIR + "/" + shared.ORGDIR + "/" + shared.PEERSDIR
	authPath := shared.TINZENITEDIR + "/" + shared.ORGDIR + "/" + shared.AUTHJSON
	// default object type is OtObject
	objectType := shared.OtObject
	// if peer --> update objectType
	if strings.HasPrefix(path, peerDir) {
		objectType = shared.OtPeer
	}
	// if auth file --> update objectType
	if path == authPath {
		objectType = shared.OtAuth
	}
	pm := shared.CreatePushMessage(identification, objectType)
	c.tin.channel.Send(address, pm.JSON())
}
<commit_msg>started fixing encrypted application to avoid model.Sync as that can not work :P<commit_after>package core

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"sync"

	"github.com/tinzenite/model"
	"github.com/tinzenite/shared"
)

/*
onEncryptedMessage is called for messages from encrypted peers. Will redestribute
the message according to its type.

TODO describe order of operations (successful lock -> request model -> sync -> push / pull difference)
*/
func (c *chaninterface) onEncryptedMessage(address string, msgType shared.MsgType, message string) {
	// TODO switch and handle messages NOTE FIXME implement
	switch msgType {
	case shared.MsgLock:
		msg := &shared.LockMessage{}
		err := json.Unmarshal([]byte(message), msg)
		if err != nil {
			log.Println(err.Error())
			return
		}
		c.onEncLockMessage(address, *msg)
	case shared.MsgNotify:
		msg := &shared.NotifyMessage{}
		err := json.Unmarshal([]byte(message), msg)
		if err != nil {
			log.Println(err.Error())
			return
		}
		c.onEncNotifyMessage(address, *msg)
	case shared.MsgRequest:
		msg := &shared.RequestMessage{}
		err := json.Unmarshal([]byte(message), msg)
		if err != nil {
			log.Println(err.Error())
			return
		}
		c.onEncRequestMessage(address, *msg)
	default:
		c.warn("Unknown object received:", msgType.String())
	}
}

/*
onEncLockMessage handles lock messages. Notably it requests a model on a
successful lock, starting a synchronization.

FIXME: peer never sends a release request to locked encrypted peers! For now we
just await the timeout... TODO
*/
func (c *chaninterface) onEncLockMessage(address string, msg shared.LockMessage) {
	switch msg.Action {
	case shared.LoAccept:
		// remember that this peer is locked
		_, exists := c.tin.peers[address]
		if !exists {
			c.warn("Can not set peer to locked as peer doesn't exist!")
			return
		}
		c.tin.peers[address].SetLocked(true)
		// if LOCKED request model file to begin sync
		rm := shared.CreateRequestMessage(shared.OtModel, shared.IDMODEL)
		c.requestFile(address, rm, c.encModelReceived)
	case shared.LoRelease:
		// unset lock of this peer
		_, exists := c.tin.peers[address]
		if !exists {
			c.warn("Can not release peer as peer doesn't exist!")
			return
		}
		c.tin.peers[address].SetLocked(false)
	default:
		c.warn("Unknown lock action received:", msg.Action.String())
	}
}

/*
onEncNotifyMessage handles the reception of notification messages.
*/
func (c *chaninterface) onEncNotifyMessage(address string, msg shared.NotifyMessage) {
	switch msg.Notify {
	case shared.NoMissing:
		// remove transfer as no file will come
		delete(c.inTransfers, c.buildKey(address, msg.Identification))
		// if model --> create it
		if msg.Identification == shared.IDMODEL {
			// log that encrypted was empty and that we'll just upload our current state
			c.log("Encrypted is empty, nothing to merge, uploading directly.")
			// so send push for model and all objects
			c.sendCompletePushes(address)
			return
		}
		// TODO: if object --> error... maybe "reset" the encrypted peer?
		log.Println("DEBUG: object missing!", msg.Identification, msg.Notify)
		log.Println("DEBUG: TODO: reset encrypted peer?")
	default:
		c.warn("Unknown notify type received:", msg.Notify.String())
	}
}

/*
onEncRequestMessage handles the reception of a request message following a push
message. Triggers the sending of the requested file.
*/
func (c *chaninterface) onEncRequestMessage(address string, msg shared.RequestMessage) {
	var path string
	// if model has been requested --> path is different as not tracked itself
	if msg.Identification == shared.IDMODEL {
		path = c.tin.Path + "/" + shared.TINZENITEDIR + "/" + shared.SENDINGDIR + "/" + shared.MODELJSON
		// get model info
		model, err := c.tin.model.Read()
		if err != nil {
			c.warn("Failed to read model:", err.Error())
			return
		}
		// convert to json
		data, err := json.Marshal(model)
		if err != nil {
			c.warn("Failed to marshal model to JSON:", err.Error())
			return
		}
		// write json to temp file
		err = ioutil.WriteFile(path, data, shared.FILEPERMISSIONMODE)
		if err != nil {
			c.warn("Failed to write model info to temp file:", err.Error())
			return
		}
	} else {
		// get subPath for file
		subPath, err := c.tin.model.GetSubPath(msg.Identification)
		if err != nil {
			c.warn("Failed to locate subpath for request message!", msg.Identification)
			return
		}
		path = c.tin.Path + "/" + subPath
	}
	// and send file (concurrent because of encryption)
	log.Println("DEBUG: sending", path, "as", msg.ObjType)
	go c.encSendFile(address, msg.Identification, path, msg.ObjType)
	// TODO: shouldn't we reread the msg.ObjType from disk too?
}

/*
encSendFile handles uploading a file to the encrypted peer. This function is MADE
to run concurrently. Path is the path where the file CURRENTLY resides: the method
will copy all its data to SENDINGDIR, encrypt it there, and then send it.
*/
func (c *chaninterface) encSendFile(address, identification, path string, ot shared.ObjectType) {
	// read file data
	data, err := ioutil.ReadFile(path)
	if err != nil {
		c.warn("Failed to read data:", err.Error())
		return
	}
	// TODO encrypt here? FIXME
	// log.Println("DEBUG: encrypt here")
	// write to temp file
	sendPath := c.tin.Path + "/" + shared.TINZENITEDIR + "/" + shared.SENDINGDIR + "/" + identification
	err = ioutil.WriteFile(sendPath, data, shared.FILEPERMISSIONMODE)
	if err != nil {
		c.warn("Failed to write (encrypted) data to sending file:", err.Error())
		return
	}
	// send file
	err = c.tin.channel.SendFile(address, sendPath, identification, func(success bool) {
		if !success {
			c.log("Failed to upload file!", ot.String(), identification)
		}
		// remove sending temp file always
		err := os.Remove(sendPath)
		if err != nil {
			c.warn("Failed to remove sending file!", err.Error())
		}
	})
	if err != nil {
		c.warn("Failed to send file:", err.Error())
		return
	}
	// done
}

/*
sendCompletePushes sends push models for everything, starting with the model.
This will result in the encrypted peer requesting all objects.
*/
func (c *chaninterface) sendCompletePushes(address string) {
	// vars we'll use
	var pm shared.PushMessage
	// start by sending push for model
	pm = shared.CreatePushMessage(shared.IDMODEL, shared.OtModel)
	c.tin.channel.Send(address, pm.JSON())
	for path, stin := range c.tin.model.StaticInfos {
		// if directory, skip
		if stin.Directory {
			continue
		}
		c.encSendPush(address, path, stin.Identification)
	}
	// and done
}

/*
encModelReceived is called when a model is received from an encrypted peer. It
triggers the complete sync with the encrypted state, concluding with updating
the encrypted peer to be up to date with this peer.

NOTE: this is a long method because it blocks until ALL operations required for
the complete model sync have completed. FIXME: make this work better.
*/
func (c *chaninterface) encModelReceived(address, path string) {
	// no matter what: remove temp file
	defer func() {
		err := os.Remove(path)
		if err != nil {
			c.warn("encModelReceived: failed to remove received temp model file!", err.Error())
		}
	}()
	// read model
	data, err := ioutil.ReadFile(path)
	if err != nil {
		c.log("encModelReceived: failed to read received model file:", err.Error())
		return
	}
	// TODO decrypt file!
	// log.Println("DEBUG: TODO: decrypt model here!")
	// unmarshal
	foreignModel := &shared.ObjectInfo{}
	err = json.Unmarshal(data, foreignModel)
	if err != nil {
		c.log("encModelReceived: failed to parse JSON:", err.Error())
		return
	}
	// build path maps and associated object maps of foreign model
	foreignPaths := make(map[string]bool)
	foreignObjs := make(map[string]*shared.ObjectInfo)
	foreignModel.ForEach(func(obj shared.ObjectInfo) {
		foreignPaths[obj.Path] = true
		// strip of children and write to objects
		obj.Objects = nil
		foreignObjs[obj.Path] = &obj
	})
	// STEP ONE: get differences that THIS must get and apply from FOREIGN
	// get differences that foreignPaths may be ahead of
	created, remained, removed := shared.Difference(c.tin.model.TrackedPaths, foreignPaths)
	for _, create := range created {
		log.Println("FOREIGN CREATED", create)
	}
	for _, remains := range remained {
		log.Println("FOREIGN REMAINED", remains)
	}
	for _, remove := range removed {
		log.Println("FOREIGN REMOVED", remove)
	}
	// STEP TWO: get difference that must be UPLOADED to foreign to make it equal to THIS
	created, remained, removed = shared.Difference(foreignPaths, c.tin.model.TrackedPaths)
	// for each path: check and create messages accordingly
	for _, create := range created {
		stin, exists := c.tin.model.StaticInfos[create]
		if !exists {
			// continue because this means it doesn't actually exist
			c.warn("encModelReceived: missing stin for locally created object!")
			continue
		}
		if stin.Directory {
			continue
		}
		log.Println("Send push for created", create)
		c.encSendPush(address, create, stin.Identification)
	}
	for _, remains := range remained {
		stin, exists := c.tin.model.StaticInfos[remains]
		if !exists {
			// continue because this means it doesn't actually exist
			c.warn("encModelReceived: missing stin for locally held object!")
			continue
		}
		if stin.Directory {
			continue
		}
		// fetch foreign object
		fObj, exists := foreignObjs[remains]
		if !exists {
			c.warn("encModelReceived: missing foreign object for locally held object!")
			continue
		}
		// if remote version already includes all known changes of local version, no need to send update, so continue
		if fObj.Version.Includes(stin.Version) {
			continue
		}
		// this means something has changed so reupload the object, overwritting the old version.
		log.Println("Send push for modified", remains)
		c.encSendPush(address, remains, stin.Identification)
	}
	// removed objects: use notify to have encrypted delete them
	for _, remove := range removed {
		stin, exists := foreignObjs[remove]
		if !exists {
			c.warn("encModelReceived: missing stin from foreign objects!")
			continue
		}
		if stin.Directory {
			continue
		}
		log.Println("Send notify for removal of", remove)
		nm := shared.CreateNotifyMessage(shared.NoRemoved, stin.Identification)
		c.tin.channel.Send(address, nm.JSON())
	}
	log.Println("DEBUG: done encrypted sync, awaiting transfer completion")
	// NOTE encrypted will be unlocked once all transfers are complete, see tinzenite.SyncEncrypted
}

/*
handleEncryptedMessage looks at the message, fetches files if required, and correctly
applies it to the model.

TODO handle how we update the encrypted peer – upload created and modified files, remove deleted files.
*/
func (c *chaninterface) handleEncryptedMessage(address string, msg *shared.UpdateMessage) error {
	// use check message to prepare message and check for special cases
	msg, err := c.tin.model.CheckMessage(msg)
	// if update known --> ignore it
	if err == model.ErrIgnoreUpdate {
		return nil
	}
	// if encrypted has a removal that we have registered as done, remove it
	if err == model.ErrObjectRemovalDone {
		log.Println("DEBUG: remove removal from encrypted.", msg.String())
		// TODO what exactly do I have to tell encrypted to remove? FIXME
		nm := shared.CreateNotifyMessage(shared.NoRemoved, msg.Object.Identification)
		c.tin.channel.Send(address, nm.JSON())
		// done
		return nil
	}
	// if still error, return it
	if err != nil {
		return err
	}
	// --> IF CheckMessage was ok, we can now handle applying the message
	// apply directories directly
	if msg.Object.Directory {
		// no merge because it should never happen for directories
		return c.tin.model.ApplyUpdateMessage(msg)
	}
	op := msg.Operation
	// create and modify must first fetch the file
	if op == shared.OpCreate || op == shared.OpModify {
		rm := shared.CreateRequestMessage(shared.OtObject, msg.Object.Identification)
		var wg sync.WaitGroup
		wg.Add(1)
		c.requestFile(address, rm, func(address, path string) {
			// force calling function to wait until this has been handled
			defer func() { wg.Done() }()
			// rename to correct name for model
			err := os.Rename(path, c.temppath+"/"+rm.Identification)
			if err != nil {
				c.log("Failed to move file to temp: " + err.Error())
				return
			}
			// TODO decrypt file!
			// log.Println("DEBUG: TODO: decrypt file here!")
			// apply
			err = c.mergeUpdate(*msg)
			if err != nil {
				c.log("File application error: " + err.Error())
			}
			// done
		})
		// wait for file to be received before returning
		wg.Wait()
		// errors may turn up but only when the file has been received, so done here
		return nil
	} else if op == shared.OpRemove {
		// remove is without file transfer, so directly apply
		return c.mergeUpdate(*msg)
	}
	c.warn("Unknown operation received, ignoring update message!")
	return shared.ErrIllegalParameters
}

/*
encSendPush sends a PushMessage for the given parameters, setting the ObjType
according to the path.
*/
func (c *chaninterface) encSendPush(address, path, identification string) {
	peerDir := shared.TINZENITEDIR + "/" + shared.ORGDIR + "/" + shared.PEERSDIR
	authPath := shared.TINZENITEDIR + "/" + shared.ORGDIR + "/" + shared.AUTHJSON
	// default object type is OtObject
	objectType := shared.OtObject
	// if peer --> update objectType
	if strings.HasPrefix(path, peerDir) {
		objectType = shared.OtPeer
	}
	// if auth file --> update objectType
	if path == authPath {
		objectType = shared.OtAuth
	}
	pm := shared.CreatePushMessage(identification, objectType)
	c.tin.channel.Send(address, pm.JSON())
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"net"
	"os"
	"sync"
	"syscall"

	"github.com/lxc/lxd/shared"
)

// Netcat is called with:
//
//    lxd netcat /path/to/unix/socket
//
// and does unbuffered netcatting of to socket to stdin/stdout. Any arguments
// after the path to the unix socket are ignored, so that this can be passed
// directly to rsync as the sync command.
func cmdNetcat(args *Args) error {
	if len(args.Params) < 2 {
		return fmt.Errorf("Bad arguments %q", args.Params)
	}

	logPath := shared.LogPath(args.Params[1], "netcat.log")
	if shared.PathExists(logPath) {
		os.Remove(logPath)
	}

	logFile, err := os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_SYNC, 0644)
	if err != nil {
		return err
	}
	defer logFile.Close()

	uAddr, err := net.ResolveUnixAddr("unix", args.Params[0])
	if err != nil {
		logFile.WriteString(fmt.Sprintf("Could not resolve unix domain socket \"%s\": %s.\n", args.Params[0], err))
		return err
	}

	conn, err := net.DialUnix("unix", nil, uAddr)
	if err != nil {
		logFile.WriteString(fmt.Sprintf("Could not dial unix domain socket \"%s\": %s.\n", args.Params[0], err))
		return err
	}

	wg := sync.WaitGroup{}
	wg.Add(1)

	go func() {
		_, err := io.Copy(eagainWriter{os.Stdout}, eagainReader{conn})
		if err != nil {
			logFile.WriteString(fmt.Sprintf("Error while copying from stdout to unix domain socket \"%s\": %s.\n", args.Params[0], err))
		}
		conn.Close()
		wg.Done()
	}()

	go func() {
		_, err := io.Copy(eagainWriter{conn}, eagainReader{os.Stdin})
		if err != nil {
			logFile.WriteString(fmt.Sprintf("Error while copying from unix domain socket \"%s\" to stdin: %s.\n", args.Params[0], err))
		}
	}()

	wg.Wait()

	return nil
}

type eagainReader struct {
	r io.Reader
}

func (er eagainReader) Read(p []byte) (int, error) {
again:
	n, err := er.r.Read(p)
	if err == nil {
		return n, nil
	}

	// keep retrying on EAGAIN
	errno, ok := shared.GetErrno(err)
	if ok && errno == syscall.EAGAIN {
		goto again
	}

	return n, err
}

type eagainWriter struct {
	w io.Writer
}

func (ew eagainWriter) Write(p []byte) (int, error) {
again:
	n, err := ew.w.Write(p)
	if err == nil {
		return n, nil
	}

	// keep retrying on EAGAIN
	errno, ok := shared.GetErrno(err)
	if ok && errno == syscall.EAGAIN {
		goto again
	}

	return n, err
}
<commit_msg>lxd/netcat: Port to using shared/eagain<commit_after>package main

import (
	"fmt"
	"io"
	"net"
	"os"
	"sync"

	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/eagain"
)

// Netcat is called with:
//
//    lxd netcat /path/to/unix/socket
//
// and does unbuffered netcatting of to socket to stdin/stdout. Any arguments
// after the path to the unix socket are ignored, so that this can be passed
// directly to rsync as the sync command.
func cmdNetcat(args *Args) error {
	if len(args.Params) < 2 {
		return fmt.Errorf("Bad arguments %q", args.Params)
	}

	logPath := shared.LogPath(args.Params[1], "netcat.log")
	if shared.PathExists(logPath) {
		os.Remove(logPath)
	}

	logFile, err := os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_SYNC, 0644)
	if err != nil {
		return err
	}
	defer logFile.Close()

	uAddr, err := net.ResolveUnixAddr("unix", args.Params[0])
	if err != nil {
		logFile.WriteString(fmt.Sprintf("Could not resolve unix domain socket \"%s\": %s.\n", args.Params[0], err))
		return err
	}

	conn, err := net.DialUnix("unix", nil, uAddr)
	if err != nil {
		logFile.WriteString(fmt.Sprintf("Could not dial unix domain socket \"%s\": %s.\n", args.Params[0], err))
		return err
	}

	wg := sync.WaitGroup{}
	wg.Add(1)

	go func() {
		_, err := io.Copy(eagain.Writer{Writer: os.Stdout}, eagain.Reader{Reader: conn})
		if err != nil {
			logFile.WriteString(fmt.Sprintf("Error while copying from stdout to unix domain socket \"%s\": %s.\n", args.Params[0], err))
		}
		conn.Close()
		wg.Done()
	}()

	go func() {
		_, err := io.Copy(eagain.Writer{Writer: conn}, eagain.Reader{Reader: os.Stdin})
		if err != nil {
			logFile.WriteString(fmt.Sprintf("Error while copying from unix domain socket \"%s\" to stdin: %s.\n", args.Params[0], err))
		}
	}()

	wg.Wait()

	return nil
}
<|endoftext|>


<commit_before>package lzma

import "github.com/uli-go/xz/lzb"

// Parameters contain all information required to decode or encode an LZMA
// stream.
//
// The maximum of the dictionary and buffer size is 2^31-1 on 32-bit
// platforms.
type Parameters struct {
	// number of literal context bits
	LC int
	// number of literal position bits
	LP int
	// number of position bits
	PB int
	// size of the dictionary in bytes
	DictSize int64
	// size of uncompressed data in bytes
	Size int64
	// header includes unpacked size
	SizeInHeader bool
	// end-of-stream marker requested
	EOS bool
	// buffer size
	BufferSize int64
}

// Default defines standard parameters.
var Default = Parameters{
	LC:         3,
	LP:         0,
	PB:         2,
	DictSize:   4096,
	BufferSize: 4096,
}

// lzbParamertes converts Parameters in the identical lzb.Parameters.
func lzbParameters(p *Parameters) lzb.Parameters {
	return lzb.Parameters{
		LC:           p.LC,
		LP:           p.LP,
		PB:           p.PB,
		DictSize:     p.DictSize,
		Size:         p.Size,
		SizeInHeader: p.SizeInHeader,
		EOS:          p.EOS,
		BufferSize:   p.BufferSize,
	}
}
<commit_msg>lzma: set default dictionary size to 8 megabyte<commit_after>package lzma

import "github.com/uli-go/xz/lzb"

// Parameters contain all information required to decode or encode an LZMA
// stream.
//
// The maximum of the dictionary and buffer size is 2^31-1 on 32-bit
// platforms.
type Parameters struct {
	// number of literal context bits
	LC int
	// number of literal position bits
	LP int
	// number of position bits
	PB int
	// size of the dictionary in bytes
	DictSize int64
	// size of uncompressed data in bytes
	Size int64
	// header includes unpacked size
	SizeInHeader bool
	// end-of-stream marker requested
	EOS bool
	// buffer size
	BufferSize int64
}

// Default defines standard parameters.
var Default = Parameters{
	LC:         3,
	LP:         0,
	PB:         2,
	DictSize:   8 * 1024 * 1024,
	BufferSize: 4096,
}

// lzbParamertes converts Parameters in the identical lzb.Parameters.
func lzbParameters(p *Parameters) lzb.Parameters {
	return lzb.Parameters{
		LC:           p.LC,
		LP:           p.LP,
		PB:           p.PB,
		DictSize:     p.DictSize,
		Size:         p.Size,
		SizeInHeader: p.SizeInHeader,
		EOS:          p.EOS,
		BufferSize:   p.BufferSize,
	}
}
<|endoftext|>


<commit_before>package proxy

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"os/exec"
	"regexp"
	"strings"

	"github.com/fsouza/go-dockerclient"
	. "github.com/weaveworks/weave/common"
)

var (
	containerIDRegexp   = regexp.MustCompile("^(/v[0-9\\.]*)?/containers/([^/]*)/.*")
	weaveWaitEntrypoint = []string{"/w/w"}
)

func callWeave(args ...string) ([]byte, []byte, error) {
	args = append([]string{"--local"}, args...)
	cmd := exec.Command("./weave", args...)
	cmd.Env = []string{
		"PATH=/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
		"PROCFS=/hostproc",
	}

	propagateEnv := func(key string) {
		if val := os.Getenv(key); val != "" {
			cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", key, val))
		}
	}

	propagateEnv("DOCKER_BRIDGE")

	// Propogage WEAVE_DEBUG, to make debugging easier.
	propagateEnv("WEAVE_DEBUG")

	// This prevents the code coverage contortions in our
	// integration test suite breaking things.
	propagateEnv("COVERAGE")

	Log.Debug("Calling weave args: ", args, "env: ", cmd.Env)
	var stdout, stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	err := cmd.Run()
	return stdout.Bytes(), stderr.Bytes(), err
}

func unmarshalRequestBody(r *http.Request, target interface{}) error {
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return err
	}
	Log.Debugf("->requestBody: %s", body)
	if err := r.Body.Close(); err != nil {
		return err
	}
	r.Body = ioutil.NopCloser(bytes.NewReader(body))

	d := json.NewDecoder(bytes.NewReader(body))
	d.UseNumber() // don't want large numbers in scientific format
	return d.Decode(&target)
}

func marshalRequestBody(r *http.Request, body interface{}) error {
	newBody, err := json.Marshal(body)
	if err != nil {
		return err
	}
	Log.Debugf("<-requestBody: %s", newBody)
	r.Body = ioutil.NopCloser(bytes.NewReader(newBody))
	r.ContentLength = int64(len(newBody))
	return nil
}

func unmarshalResponseBody(r *http.Response, target interface{}) error {
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return err
	}
	if err := r.Body.Close(); err != nil {
		return err
	}
	r.Body = ioutil.NopCloser(bytes.NewReader(body))

	d := json.NewDecoder(bytes.NewReader(body))
	d.UseNumber() // don't want large numbers in scientific format
	return d.Decode(&target)
}

func marshalResponseBody(r *http.Response, body interface{}) error {
	newBody, err := json.Marshal(body)
	if err != nil {
		return err
	}
	r.Body = ioutil.NopCloser(bytes.NewReader(newBody))
	r.ContentLength = int64(len(newBody))
	// Stop it being chunked, because that hangs
	r.TransferEncoding = nil
	return nil
}

func inspectContainerInPath(client *docker.Client, path string) (*docker.Container, error) {
	subs := containerIDRegexp.FindStringSubmatch(path)
	if subs == nil {
		err := fmt.Errorf("No container id found in request with path %s", path)
		Log.Warningln(err)
		return nil, err
	}
	containerID := subs[2]

	container, err := client.InspectContainer(containerID)
	if err != nil {
		Log.Warningf("Error inspecting container %s: %v", containerID, err)
	}
	return container, err
}

func weaveContainerIPs(containerID string) (mac string, ips []net.IP, nets []*net.IPNet, err error) {
	stdout, stderr, err := callWeave("ps", containerID)
	if err != nil || len(stderr) > 0 {
		err = errors.New(string(stderr))
		return
	}
	if len(stdout) <= 0 {
		return
	}

	fields := strings.Fields(string(stdout))
	if len(fields) < 2 {
		return
	}
	mac = fields[1]

	var ip net.IP
	var ipnet *net.IPNet
	for _, cidr := range fields[2:] {
		ip, ipnet, err = net.ParseCIDR(cidr)
		if err != nil {
			return
		}
		ips = append(ips, ip)
		nets = append(nets, ipnet)
	}
	return
}
<commit_msg>Also print intercepted responses<commit_after>package proxy

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"os/exec"
	"regexp"
	"strings"

	"github.com/fsouza/go-dockerclient"
	. "github.com/weaveworks/weave/common"
)

var (
	containerIDRegexp   = regexp.MustCompile("^(/v[0-9\\.]*)?/containers/([^/]*)/.*")
	weaveWaitEntrypoint = []string{"/w/w"}
)

func callWeave(args ...string) ([]byte, []byte, error) {
	args = append([]string{"--local"}, args...)
	cmd := exec.Command("./weave", args...)
	cmd.Env = []string{
		"PATH=/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
		"PROCFS=/hostproc",
	}

	propagateEnv := func(key string) {
		if val := os.Getenv(key); val != "" {
			cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", key, val))
		}
	}

	propagateEnv("DOCKER_BRIDGE")

	// Propogage WEAVE_DEBUG, to make debugging easier.
	propagateEnv("WEAVE_DEBUG")

	// This prevents the code coverage contortions in our
	// integration test suite breaking things.
	propagateEnv("COVERAGE")

	Log.Debug("Calling weave args: ", args, "env: ", cmd.Env)
	var stdout, stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	err := cmd.Run()
	return stdout.Bytes(), stderr.Bytes(), err
}

func unmarshalRequestBody(r *http.Request, target interface{}) error {
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return err
	}
	Log.Debugf("->requestBody: %s", body)
	if err := r.Body.Close(); err != nil {
		return err
	}
	r.Body = ioutil.NopCloser(bytes.NewReader(body))

	d := json.NewDecoder(bytes.NewReader(body))
	d.UseNumber() // don't want large numbers in scientific format
	return d.Decode(&target)
}

func marshalRequestBody(r *http.Request, body interface{}) error {
	newBody, err := json.Marshal(body)
	if err != nil {
		return err
	}
	Log.Debugf("<-requestBody: %s", newBody)
	r.Body = ioutil.NopCloser(bytes.NewReader(newBody))
	r.ContentLength = int64(len(newBody))
	return nil
}

func unmarshalResponseBody(r *http.Response, target interface{}) error {
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return err
	}
	Log.Debugf("->responseBody: %s", body)
	if err := r.Body.Close(); err != nil {
		return err
	}
	r.Body = ioutil.NopCloser(bytes.NewReader(body))

	d := json.NewDecoder(bytes.NewReader(body))
	d.UseNumber() // don't want large numbers in scientific format
	return d.Decode(&target)
}

func marshalResponseBody(r *http.Response, body interface{}) error {
	newBody, err := json.Marshal(body)
	if err != nil {
		return err
	}
	Log.Debugf("<-responseBody: %s", newBody)
	r.Body = ioutil.NopCloser(bytes.NewReader(newBody))
	r.ContentLength = int64(len(newBody))
	// Stop it being chunked, because that hangs
	r.TransferEncoding = nil
	return nil
}

func inspectContainerInPath(client *docker.Client, path string) (*docker.Container, error) {
	subs := containerIDRegexp.FindStringSubmatch(path)
	if subs == nil {
		err := fmt.Errorf("No container id found in request with path %s", path)
		Log.Warningln(err)
		return nil, err
	}
	containerID := subs[2]

	container, err := client.InspectContainer(containerID)
	if err != nil {
		Log.Warningf("Error inspecting container %s: %v", containerID, err)
	}
	return container, err
}

func weaveContainerIPs(containerID string) (mac string, ips []net.IP, nets []*net.IPNet, err error) {
	stdout, stderr, err := callWeave("ps", containerID)
	if err != nil || len(stderr) > 0 {
		err = errors.New(string(stderr))
		return
	}
	if len(stdout) <= 0 {
		return
	}

	fields := strings.Fields(string(stdout))
	if len(fields) < 2 {
		return
	}
	mac = fields[1]

	var ip net.IP
	var ipnet *net.IPNet
	for _, cidr := range fields[2:] {
		ip, ipnet, err = net.ParseCIDR(cidr)
		if err != nil {
			return
		}
		ips = append(ips, ip)
		nets = append(nets, ipnet)
	}
	return
}
<|endoftext|>


<commit_before>package mock

import (
	"encoding/json"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"strings"
	"time"

	"github.com/kayac/Gunfish/apns"
)

const (
	ApplicationJSON        = "application/json"
	LimitApnsTokenByteSize = 100 // Payload byte size.
)

// StartAPNSMockServer starts HTTP/2 server for mock
func APNsMockServer(verbose bool) *http.ServeMux {
	mux := http.NewServeMux()

	mux.HandleFunc("/3/device/", func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()
		defer func() {
			if verbose {
				log.Printf("reqtime:%f proto:%s method:%s path:%s host:%s", reqtime(start), r.Proto, r.Method, r.URL.Path, r.RemoteAddr)
			}
		}()

		// sets the response time from apns server
		time.Sleep(time.Millisecond*200 + time.Millisecond*(time.Duration(rand.Int63n(200)-100)))

		// only allow path which pattern is '/3/device/:token'
		splitPath := strings.Split(r.URL.Path, "/")
		if len(splitPath) != 4 {
			w.WriteHeader(http.StatusNotFound)
			fmt.Fprintf(w, "404 Not found")
			return
		}

		w.Header().Set("Content-Type", ApplicationJSON)

		token := splitPath[len(splitPath)-1]
		if len(([]byte(token))) > LimitApnsTokenByteSize || token == "baddevicetoken" {
			w.Header().Set("apns-id", "apns-id")
			w.WriteHeader(http.StatusBadRequest)
			fmt.Fprintf(w, createErrorResponse(apns.BadDeviceToken, http.StatusBadRequest))
		} else if token == "missingtopic" {
			w.WriteHeader(http.StatusBadRequest)
			fmt.Fprintf(w, createErrorResponse(apns.MissingTopic, http.StatusBadRequest))
		} else if token == "unregistered" {
			// If the value in the :status header is 410, the value of this key is
			// the last time at which APNs confirmed that the device token was
			// no longer valid for the topic.
			//
			// Stop pushing notifications until the device registers a token with
			// a later timestamp with your provider.
			w.WriteHeader(http.StatusGone)
			fmt.Fprint(w, createErrorResponse(apns.Unregistered, http.StatusGone))
		} else if token == "expiredprovidertoken" {
			w.WriteHeader(http.StatusForbidden)
			fmt.Fprint(w, createErrorResponse(apns.ExpiredProviderToken, http.StatusForbidden))
		} else {
			w.Header().Set("apns-id", "apns-id")
			w.WriteHeader(http.StatusOK)
		}

		return
	})

	return mux
}

func createErrorResponse(ermsg apns.ErrorResponseCode, status int) string {
	var er apns.ErrorResponse
	if status == http.StatusGone {
		er = apns.ErrorResponse{
			Reason:    ermsg.String(),
			Timestamp: time.Now().Unix(),
		}
	} else {
		er = apns.ErrorResponse{
			Reason: ermsg.String(),
		}
	}
	der, _ := json.Marshal(er)
	return string(der)
}

func reqtime(start time.Time) float64 {
	diff := time.Now().Sub(start)
	return diff.Seconds()
}
<commit_msg>write []byte to writer better than string<commit_after>package mock

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"math/rand"
	"net/http"
	"strings"
	"time"

	"github.com/kayac/Gunfish/apns"
)

const (
	ApplicationJSON        = "application/json"
	LimitApnsTokenByteSize = 100 // Payload byte size.
)

// StartAPNSMockServer starts HTTP/2 server for mock
func APNsMockServer(verbose bool) *http.ServeMux {
	mux := http.NewServeMux()

	mux.HandleFunc("/3/device/", func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()
		defer func() {
			if verbose {
				log.Printf("reqtime:%f proto:%s method:%s path:%s host:%s", reqtime(start), r.Proto, r.Method, r.URL.Path, r.RemoteAddr)
			}
		}()

		// sets the response time from apns server
		time.Sleep(time.Millisecond*200 + time.Millisecond*(time.Duration(rand.Int63n(200)-100)))

		// only allow path which pattern is '/3/device/:token'
		splitPath := strings.Split(r.URL.Path, "/")
		if len(splitPath) != 4 {
			w.WriteHeader(http.StatusNotFound)
			fmt.Fprintf(w, "404 Not found")
			return
		}

		w.Header().Set("Content-Type", ApplicationJSON)

		token := splitPath[len(splitPath)-1]
		if len(([]byte(token))) > LimitApnsTokenByteSize || token == "baddevicetoken" {
			w.Header().Set("apns-id", "apns-id")
			w.WriteHeader(http.StatusBadRequest)
			createErrorResponse(w, apns.BadDeviceToken, http.StatusBadRequest)
		} else if token == "missingtopic" {
			w.WriteHeader(http.StatusBadRequest)
			createErrorResponse(w, apns.MissingTopic, http.StatusBadRequest)
		} else if token == "unregistered" {
			// If the value in the :status header is 410, the value of this key is
			// the last time at which APNs confirmed that the device token was
			// no longer valid for the topic.
			//
			// Stop pushing notifications until the device registers a token with
			// a later timestamp with your provider.
			w.WriteHeader(http.StatusGone)
			createErrorResponse(w, apns.Unregistered, http.StatusGone)
		} else if token == "expiredprovidertoken" {
			w.WriteHeader(http.StatusForbidden)
			createErrorResponse(w, apns.ExpiredProviderToken, http.StatusForbidden)
		} else {
			w.Header().Set("apns-id", "apns-id")
			w.WriteHeader(http.StatusOK)
		}

		return
	})

	return mux
}

func createErrorResponse(w io.Writer, ermsg apns.ErrorResponseCode, status int) error {
	enc := json.NewEncoder(w)
	var er apns.ErrorResponse
	if status == http.StatusGone {
		er = apns.ErrorResponse{
			Reason:    ermsg.String(),
			Timestamp: time.Now().Unix(),
		}
	} else {
		er = apns.ErrorResponse{
			Reason: ermsg.String(),
		}
	}
	return enc.Encode(er)
}

func reqtime(start time.Time) float64 {
	diff := time.Now().Sub(start)
	return diff.Seconds()
}
<|endoftext|>


<commit_before>// Copyright 2016 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.

package parts

import (
	"encoding/json"
	"go/format"
	"html/template"
	"log"
	"net/http"
	"strings"

	"github.com/google/shenzhen-go/model/pin"
)

const (
	codePartEditTemplateSrc = `

`
)

var (
	// CodePanels are subpanels for editing code-type parts.
	CodePanels = []struct {
		Name   string
		Editor template.HTML
	}{
		{
			Name: "Pins",
			Editor: `

		Direction
		Name
		Type
		Add pin

			<select name="PinDirection">
				<option value="in" {{if eq .Direction "in"}}selected{{end}}>Input</option>
				<option value="out" {{if eq .Direction "out"}}selected{{end}}>Output</option>
			</select>

			<input type="text" name="PinName" required pattern="^[_a-zA-Z][_a-zA-Z0-9]*$" title="Must start with a letter or underscore, and only contain letters, digits, or underscores." value="{{.Name}}">

			<input type="text" name="PinType" required value="{{.Type}}">

			Remove pin

`,
		},
		{
			Name:   "Imports",
			Editor: `<div class="codeedit" id="code-imports">{.Node.FlatImports}</div>`,
		},
		{
			Name:   "Head",
			Editor: `<div class="codeedit" id="code-head">{.Node.ImplHead}</div>`,
		},
		{
			Name:   "Body",
			Editor: `<div class="codeedit" id="code-body">{.Node.ImplBody}</div>`,
		},
		{
			Name:   "Tail",
			Editor: `<div class="codeedit" id="code-tail">{.Node.ImplTail}</div>`,
		},
		{
			Name: "Help",
			Editor: `

		A Code part runs (executes) any Go code that you write.

		It consists of 3 parts: a Head, a Body, and a Tail.
		The Head runs first, and only runs once, no matter what number Multiplicity is set to.
		The Body runs next. The number of concurrent copies of the Body that run is set by Multiplicity.
		Finally, when all copies of the Body return, the Tail runs.

		The Head and Tail are useful for operations that should only be done once. For example, any
		output channels written to in the Body can be correctly closed (if desired) in the Tail.

		Each instance of the Body can use the int parameters instanceNumber and multiplicity
		to distinguish which instance is running and how many are running, if necessary.
		0 <= instanceNumber < multiplicity

		Any channels referred to will automatically be detected and shown in the graph, and
		when channels are renamed, these will be safely updated in the Code where they are
		referred to.

		The return statement is allowed but optional in Code. There are no values that
		need to be returned.
		Using return in the Head will prevent the Body or Tail from executing, but
		using return in the Body won't affect whether the Tail is executed.

	`,
		},
	}
)

// Code is a component containing arbitrary code.
type Code struct {
	imports          []string
	head, body, tail string
	pins             []pin.Definition
}

// NewCode just makes a new *Code.
func NewCode(imports []string, head, body, tail string, pins []pin.Definition) *Code {
	return &Code{
		imports: imports,
		head:    head,
		body:    body,
		tail:    tail,
		pins:    pins,
	}
}

type part interface {
	Imports() []string
	Impl() (head, body, tail string)
	Pins() []pin.Definition
}

// NewCodeFromAny creates a new Code, copying the implementation details
// out of an existing part.
func NewCodeFromAny(p part) *Code {
	h, b, t := p.Impl()
	return &Code{
		imports: p.Imports(),
		head:    h,
		body:    b,
		tail:    t,
		pins:    p.Pins(),
	}
}

type jsonCode struct {
	Imports []string         `json:"imports"`
	Head    []string         `json:"head"`
	Body    []string         `json:"body"`
	Tail    []string         `json:"tail"`
	Pins    []pin.Definition `json:"pins"`
}

// MarshalJSON encodes the Code component as JSON.
func (c *Code) MarshalJSON() ([]byte, error) {
	k := &jsonCode{
		Imports: c.imports,
		Head:    strings.Split(c.head, "\n"),
		Body:    strings.Split(c.body, "\n"),
		Tail:    strings.Split(c.tail, "\n"),
		Pins:    c.pins,
	}
	stripCR(k.Imports)
	stripCR(k.Head)
	stripCR(k.Body)
	stripCR(k.Tail)
	return json.Marshal(k)
}

// UnmarshalJSON decodes the Code component from JSON.
func (c *Code) UnmarshalJSON(j []byte) error {
	var mp jsonCode
	if err := json.Unmarshal(j, &mp); err != nil {
		return err
	}
	h := strings.Join(mp.Head, "\n")
	b := strings.Join(mp.Body, "\n")
	t := strings.Join(mp.Tail, "\n")
	if err := c.refresh(h, b, t); err != nil {
		// TODO: revisit all this
		log.Printf("Couldn't format or determine channels used: %v", err)
	}
	c.imports = mp.Imports
	c.pins = mp.Pins
	return nil
}

// Pins returns pins. These are 100% user-defined.
func (c *Code) Pins() []pin.Definition { return c.pins }

// Clone returns a copy of this Code part.
func (c *Code) Clone() interface{} {
	c2 := &Code{
		imports: c.imports,
		head:    c.head,
		body:    c.body,
		tail:    c.tail,
		pins:    append([]pin.Definition{}, c.pins...),
	}
	return c2
}

// Impl returns the implementation of the goroutine.
func (c *Code) Impl() (Head, Body, Tail string) {
	return c.head, c.body, c.tail
}

// Imports returns a nil slice.
func (c *Code) Imports() []string { return c.imports }

// TypeKey returns "Code".
func (*Code) TypeKey() string { return "Code" }

func (c *Code) refresh(h, b, t string) error {
	// At least save what the user entered.
	c.head, c.body, c.tail = h, b, t

	// Try to format it.
	hf, err := format.Source([]byte(h))
	if err != nil {
		return err
	}
	bf, err := format.Source([]byte(b))
	if err != nil {
		return err
	}
	tf, err := format.Source([]byte(t))
	if err != nil {
		return err
	}

	c.head, c.body, c.tail = string(hf), string(bf), string(tf)
	return nil
}

// Update sets relevant fields based on the given Request.
func (c *Code) Update(r *http.Request) error {
	h, b, t := c.head, c.body, c.tail
	if r != nil {
		h, b, t = r.FormValue("Head"), r.FormValue("Body"), r.FormValue("Tail")
	}
	pd, pn, pt := r.Form["PinDirection"], r.Form["PinName"], r.Form["PinType"]
	c.pins = make([]pin.Definition, 0, len(pd))
	for i, d := range pd {
		c.pins = append(c.pins, pin.Definition{
			Name:      pn[i],
			Direction: pin.Direction(d),
			Type:      pt[i],
		})
	}
	return c.refresh(h, b, t)
}
<commit_msg>Delete unused constant<commit_after>// Copyright 2016 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.

package parts

import (
	"encoding/json"
	"go/format"
	"html/template"
	"log"
	"net/http"
	"strings"

	"github.com/google/shenzhen-go/model/pin"
)

var (
	// CodePanels are subpanels for editing code-type parts.
	CodePanels = []struct {
		Name   string
		Editor template.HTML
	}{
		{
			Name: "Pins",
			Editor: `

		Direction
		Name
		Type
		Add pin

			<select name="PinDirection">
				<option value="in" {{if eq .Direction "in"}}selected{{end}}>Input</option>
				<option value="out" {{if eq .Direction "out"}}selected{{end}}>Output</option>
			</select>

			<input type="text" name="PinName" required pattern="^[_a-zA-Z][_a-zA-Z0-9]*$" title="Must start with a letter or underscore, and only contain letters, digits, or underscores." value="{{.Name}}">

			<input type="text" name="PinType" required value="{{.Type}}">

			Remove pin

`,
		},
		{
			Name:   "Imports",
			Editor: `<div class="codeedit" id="code-imports">{.Node.FlatImports}</div>`,
		},
		{
			Name:   "Head",
			Editor: `<div class="codeedit" id="code-head">{.Node.ImplHead}</div>`,
		},
		{
			Name:   "Body",
			Editor: `<div class="codeedit" id="code-body">{.Node.ImplBody}</div>`,
		},
		{
			Name:   "Tail",
			Editor: `<div class="codeedit" id="code-tail">{.Node.ImplTail}</div>`,
		},
		{
			Name: "Help",
			Editor: `

		A Code part runs (executes) any Go code that you write.

		It consists of 3 parts: a Head, a Body, and a Tail.
		The Head runs first, and only runs once, no matter what number Multiplicity is set to.
		The Body runs next. The number of concurrent copies of the Body that run is set by Multiplicity.
		Finally, when all copies of the Body return, the Tail runs.

		The Head and Tail are useful for operations that should only be done once. For example, any
		output channels written to in the Body can be correctly closed (if desired) in the Tail.

		Each instance of the Body can use the int parameters instanceNumber and multiplicity
		to distinguish which instance is running and how many are running, if necessary.
		0 <= instanceNumber < multiplicity

		Any channels referred to will automatically be detected and shown in the graph, and
		when channels are renamed, these will be safely updated in the Code where they are
		referred to.

		The return statement is allowed but optional in Code. There are no values that
		need to be returned.
		Using return in the Head will prevent the Body or Tail from executing, but
		using return in the Body won't affect whether the Tail is executed.

	`,
		},
	}
)

// Code is a component containing arbitrary code.
type Code struct {
	imports          []string
	head, body, tail string
	pins             []pin.Definition
}

// NewCode just makes a new *Code.
func NewCode(imports []string, head, body, tail string, pins []pin.Definition) *Code {
	return &Code{
		imports: imports,
		head:    head,
		body:    body,
		tail:    tail,
		pins:    pins,
	}
}

type part interface {
	Imports() []string
	Impl() (head, body, tail string)
	Pins() []pin.Definition
}

// NewCodeFromAny creates a new Code, copying the implementation details
// out of an existing part.
func NewCodeFromAny(p part) *Code {
	h, b, t := p.Impl()
	return &Code{
		imports: p.Imports(),
		head:    h,
		body:    b,
		tail:    t,
		pins:    p.Pins(),
	}
}

type jsonCode struct {
	Imports []string         `json:"imports"`
	Head    []string         `json:"head"`
	Body    []string         `json:"body"`
	Tail    []string         `json:"tail"`
	Pins    []pin.Definition `json:"pins"`
}

// MarshalJSON encodes the Code component as JSON.
func (c *Code) MarshalJSON() ([]byte, error) {
	k := &jsonCode{
		Imports: c.imports,
		Head:    strings.Split(c.head, "\n"),
		Body:    strings.Split(c.body, "\n"),
		Tail:    strings.Split(c.tail, "\n"),
		Pins:    c.pins,
	}
	stripCR(k.Imports)
	stripCR(k.Head)
	stripCR(k.Body)
	stripCR(k.Tail)
	return json.Marshal(k)
}

// UnmarshalJSON decodes the Code component from JSON.
func (c *Code) UnmarshalJSON(j []byte) error {
	var mp jsonCode
	if err := json.Unmarshal(j, &mp); err != nil {
		return err
	}
	h := strings.Join(mp.Head, "\n")
	b := strings.Join(mp.Body, "\n")
	t := strings.Join(mp.Tail, "\n")
	if err := c.refresh(h, b, t); err != nil {
		// TODO: revisit all this
		log.Printf("Couldn't format or determine channels used: %v", err)
	}
	c.imports = mp.Imports
	c.pins = mp.Pins
	return nil
}

// Pins returns pins. These are 100% user-defined.
func (c *Code) Pins() []pin.Definition { return c.pins }

// Clone returns a copy of this Code part.
func (c *Code) Clone() interface{} {
	c2 := &Code{
		imports: c.imports,
		head:    c.head,
		body:    c.body,
		tail:    c.tail,
		pins:    append([]pin.Definition{}, c.pins...),
	}
	return c2
}

// Impl returns the implementation of the goroutine.
func (c *Code) Impl() (Head, Body, Tail string) {
	return c.head, c.body, c.tail
}

// Imports returns a nil slice.
func (c *Code) Imports() []string { return c.imports }

// TypeKey returns "Code".
func (*Code) TypeKey() string { return "Code" }

func (c *Code) refresh(h, b, t string) error {
	// At least save what the user entered.
	c.head, c.body, c.tail = h, b, t

	// Try to format it.
	hf, err := format.Source([]byte(h))
	if err != nil {
		return err
	}
	bf, err := format.Source([]byte(b))
	if err != nil {
		return err
	}
	tf, err := format.Source([]byte(t))
	if err != nil {
		return err
	}

	c.head, c.body, c.tail = string(hf), string(bf), string(tf)
	return nil
}

// Update sets relevant fields based on the given Request.
func (c *Code) Update(r *http.Request) error {
	h, b, t := c.head, c.body, c.tail
	if r != nil {
		h, b, t = r.FormValue("Head"), r.FormValue("Body"), r.FormValue("Tail")
	}
	pd, pn, pt := r.Form["PinDirection"], r.Form["PinName"], r.Form["PinType"]
	c.pins = make([]pin.Definition, 0, len(pd))
	for i, d := range pd {
		c.pins = append(c.pins, pin.Definition{
			Name:      pn[i],
			Direction: pin.Direction(d),
			Type:      pt[i],
		})
	}
	return c.refresh(h, b, t)
}
<|endoftext|>