<commit_before>package hugolib

import (
	"strings"
	"testing"

	"github.com/BurntSushi/toml"
	"github.com/spf13/afero"
	"github.com/spf13/hugo/hugofs"
	"github.com/spf13/hugo/source"
	"github.com/spf13/viper"
	"github.com/stretchr/testify/assert"
)

const (
	CONF_MENU1 = `
[[menu.main]]
    name = "Go Home"
    url = "/"
	weight = 1
	pre = "
"
	post = "
"
[[menu.main]]
    name = "Blog"
    url = "/posts"
[[menu.grandparent]]
	name = "grandparent"
	url = "/grandparent"
	identifier = "grandparentId"
[[menu.grandparent]]
	name = "parent"
	url = "/parent"
	identifier = "parentId"
	parent = "grandparentId"
[[menu.grandparent]]
	name = "Go Home3"
    url = "/"
	identifier = "grandchildId"
	parent = "parentId"
[[menu.tax]]
	name = "Tax1"
    url = "/two/key/"
	identifier="1"
[[menu.tax]]
	name = "Tax2"
    url = "/two/key"
	identifier="2"
[[menu.tax]]
	name = "Tax RSS"
    url = "/two/key.xml"
	identifier="xml"
[[menu.unicode]]
   name = "Unicode Russian"
   identifier = "unicode-russian"
   url = "/новости-проекта"` // Russian => "news-project"
)

var MENU_PAGE_1 = []byte(`+++
title = "One"
[menu]
	[menu.p_one]
weight = 1
+++
Front Matter with Menu Pages`)

var MENU_PAGE_2 = []byte(`+++
title = "Two"
weight = 2
[menu]
	[menu.p_one]
	[menu.p_two]
		Identity = "Two"

+++
Front Matter with Menu Pages`)

var MENU_PAGE_3 = []byte(`+++
title = "Three"
weight = 3
[menu]
	[menu.p_two]
		Name = "Three"
		Parent = "Two"
+++
Front Matter with Menu Pages`)

var MENU_PAGE_SOURCES = []source.ByteSource{
	{"sect/doc1.md", MENU_PAGE_1},
	{"sect/doc2.md", MENU_PAGE_2},
	{"sect/doc3.md", MENU_PAGE_3},
}

type testMenuState struct {
	site       *Site
	oldMenu    interface{}
	oldBaseUrl interface{}
}

// temporarily disabled
// will be enabled once the cast related build issues is fixed
// todo bep
func _TestPageMenu(t *testing.T) {
	ts := setupMenuTests(t)
	defer resetMenuTestState(ts)

	if len(ts.site.Pages) != 3 {
		t.Fatalf("Posts not created, expected 3 got %d", len(ts.site.Pages))
	}

	first := ts.site.Pages[0]
	second := ts.site.Pages[1]
	third := ts.site.Pages[2]

	pOne := ts.findTestMenuEntryByName("p_one", "One")
	pTwo := ts.findTestMenuEntryByName("p_two", "Two")

	for i, this := range []struct {
		menu           string
		page           *Page
		menuItem       *MenuEntry
		isMenuCurrent  bool
		hasMenuCurrent bool
	}{
		{"p_one", first, pOne, true, false},
		{"p_one", first, pTwo, false, false},
		{"p_one", second, pTwo, true, false},
		{"p_two", second, pTwo, true, false},
		{"p_two", third, pTwo, false, true},
		{"p_one", third, pTwo, false, false},
	} {

		isMenuCurrent := this.page.IsMenuCurrent(this.menu, this.menuItem)
		hasMenuCurrent := this.page.HasMenuCurrent(this.menu, this.menuItem)

		if isMenuCurrent != this.isMenuCurrent {
			t.Errorf("[%d] Wrong result from IsMenuCurrent: %v", i, isMenuCurrent)
		}

		if hasMenuCurrent != this.hasMenuCurrent {
			t.Errorf("[%d] Wrong result for menuItem %v for HasMenuCurrent: %v", i, this.menuItem, hasMenuCurrent)
		}

	}

}

// issue #719
func TestMenuWithUnicodeUrls(t *testing.T) {
	for _, uglyUrls := range []bool{true, false} {
		doTestMenuWithUnicodeUrls(t, uglyUrls)
	}
}

func doTestMenuWithUnicodeUrls(t *testing.T, uglyUrls bool) {
	viper.Set("UglyUrls", uglyUrls)
	ts := setupMenuTests(t)
	defer resetMenuTestState(ts)

	unicodeRussian := ts.findTestMenuEntryById("unicode", "unicode-russian")

	expectedBase := "http://foo.local/zoo/%D0%BD%D0%BE%D0%B2%D0%BE%D1%81%D1%82%D0%B8-%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B0"
	var expected string
	if uglyUrls {
		expected = expectedBase + ".html"
	} else {
		expected = expectedBase + "/"
	}

	assert.Equal(t, expected, unicodeRussian.Url, "uglyUrls[%t]", uglyUrls)
}

func TestTaxonomyNodeMenu(t *testing.T) {
	ts := setupMenuTests(t)
	defer resetMenuTestState(ts)

	for i, this := range []struct {
		menu           string
		taxInfo        taxRenderInfo
		menuItem       *MenuEntry
		isMenuCurrent  bool
		hasMenuCurrent bool
	}{
		{"tax", taxRenderInfo{key: "key", singular: "one", plural: "two"},
			ts.findTestMenuEntryById("tax", "1"), true, false},
		{"tax", taxRenderInfo{key: "key", singular: "one", plural: "two"},
			ts.findTestMenuEntryById("tax", "2"), true, false},
		{"tax", taxRenderInfo{key: "key", singular: "one", plural: "two"},
			&MenuEntry{Name: "Somewhere else", Url: "/somewhereelse"}, false, false},
	} {

		n, _ := ts.site.newTaxonomyNode(this.taxInfo)

		isMenuCurrent := n.IsMenuCurrent(this.menu, this.menuItem)
		hasMenuCurrent := n.HasMenuCurrent(this.menu, this.menuItem)

		if isMenuCurrent != this.isMenuCurrent {
			t.Errorf("[%d] Wrong result from IsMenuCurrent: %v", i, isMenuCurrent)
		}

		if hasMenuCurrent != this.hasMenuCurrent {
			t.Errorf("[%d] Wrong result for menuItem %v for HasMenuCurrent: %v", i, this.menuItem, hasMenuCurrent)
		}

	}

	menuEntryXml := ts.findTestMenuEntryById("tax", "xml")

	if strings.HasSuffix(menuEntryXml.Url, "/") {
		t.Error("RSS menu item should not be padded with trailing slash")
	}
}

func TestHomeNodeMenu(t *testing.T) {
	ts := setupMenuTests(t)
	defer resetMenuTestState(ts)

	home := ts.site.newHomeNode()
	homeMenuEntry := &MenuEntry{Name: home.Title, Url: string(home.Permalink)}

	for i, this := range []struct {
		menu           string
		menuItem       *MenuEntry
		isMenuCurrent  bool
		hasMenuCurrent bool
	}{
		{"main", homeMenuEntry, true, false},
		{"doesnotexist", homeMenuEntry, false, false},
		{"main", &MenuEntry{Name: "Somewhere else", Url: "/somewhereelse"}, false, false},
		{"grandparent", ts.findTestMenuEntryById("grandparent", "grandparentId"), false, false},
		{"grandparent", ts.findTestMenuEntryById("grandparent", "parentId"), false, true},
		{"grandparent", ts.findTestMenuEntryById("grandparent", "grandchildId"), true, false},
	} {

		isMenuCurrent := home.IsMenuCurrent(this.menu, this.menuItem)
		hasMenuCurrent := home.HasMenuCurrent(this.menu, this.menuItem)

		if isMenuCurrent != this.isMenuCurrent {
			t.Errorf("[%d] Wrong result from IsMenuCurrent: %v", i, isMenuCurrent)
		}

		if hasMenuCurrent != this.hasMenuCurrent {
			t.Errorf("[%d] Wrong result for menuItem %v for HasMenuCurrent: %v", i, this.menuItem, hasMenuCurrent)
		}
	}
}

var testMenuIdentityMatcher = func(me *MenuEntry, id string) bool { return me.Identifier == id }
var testMenuNameMatcher = func(me *MenuEntry, id string) bool { return me.Name == id }

func (ts testMenuState) findTestMenuEntryById(mn string, id string) *MenuEntry {
	return ts.findTestMenuEntry(mn, id, testMenuIdentityMatcher)
}
func (ts testMenuState) findTestMenuEntryByName(mn string, id string) *MenuEntry {
	return ts.findTestMenuEntry(mn, id, testMenuNameMatcher)
}

func (ts testMenuState) findTestMenuEntry(mn string, id string, matcher func(me *MenuEntry, id string) bool) *MenuEntry {
	if menu, ok := ts.site.Menus[mn]; ok {
		for _, me := range *menu {

			if matcher(me, id) {
				return me
			}

			descendant := ts.findDescendantTestMenuEntry(me, id, matcher)
			if descendant != nil {
				return descendant
			}
		}
	}
	return nil
}

func (ts testMenuState) findDescendantTestMenuEntry(parent *MenuEntry, id string, matcher func(me *MenuEntry, id string) bool) *MenuEntry {
	if parent.HasChildren() {
		for _, child := range parent.Children {

			if matcher(child, id) {
				return child
			}

			descendant := ts.findDescendantTestMenuEntry(child, id, matcher)
			if descendant != nil {
				return descendant
			}
		}
	}
	return nil
}

func getTestMenuState(s *Site, t *testing.T) *testMenuState {
	menuState := &testMenuState{site: s, oldBaseUrl: viper.Get("baseurl"), oldMenu: viper.Get("menu")}

	menus, err := tomlToMap(CONF_MENU1)

	if err != nil {
		t.Fatalf("Unable to Read menus: %v", err)
	}

	viper.Set("menu", menus["menu"])
	viper.Set("baseurl", "http://foo.local/zoo/")

	return menuState
}

func setupMenuTests(t *testing.T) *testMenuState {
	s := createTestSite()
	testState := getTestMenuState(s, t)
	testSiteSetup(s, t)

	return testState
}

func resetMenuTestState(state *testMenuState) {
	viper.Set("menu", state.oldMenu)
	viper.Set("baseurl", state.oldBaseUrl)
}

func createTestSite() *Site {
	hugofs.DestinationFS = new(afero.MemMapFs)

	s := &Site{
		Source: &source.InMemorySource{ByteSource: MENU_PAGE_SOURCES},
	}
	return s
}

func testSiteSetup(s *Site, t *testing.T) {

	s.Menus = Menus{}
	s.initializeSiteInfo()
	s.Shortcodes = make(map[string]ShortcodeFunc)

	if err := s.CreatePages(); err != nil {
		t.Fatalf("Unable to create pages: %s", err)
	}

	if err := s.BuildSiteMeta(); err != nil {
		t.Fatalf("Unable to build site metadata: %s", err)
	}

}

func tomlToMap(s string) (map[string]interface{}, error) {
	var data map[string]interface{} = make(map[string]interface{})
	if _, err := toml.Decode(s, &data); err != nil {
		return nil, err
	}

	return data, nil

}
<commit_msg>Revert "Temporarily disable failing menu test"<commit_after>package hugolib

import (
	"strings"
	"testing"

	"github.com/BurntSushi/toml"
	"github.com/spf13/afero"
	"github.com/spf13/hugo/hugofs"
	"github.com/spf13/hugo/source"
	"github.com/spf13/viper"
	"github.com/stretchr/testify/assert"
)

const (
	CONF_MENU1 = `
[[menu.main]]
    name = "Go Home"
    url = "/"
	weight = 1
	pre = "
"
	post = "
"
[[menu.main]]
    name = "Blog"
    url = "/posts"
[[menu.grandparent]]
	name = "grandparent"
	url = "/grandparent"
	identifier = "grandparentId"
[[menu.grandparent]]
	name = "parent"
	url = "/parent"
	identifier = "parentId"
	parent = "grandparentId"
[[menu.grandparent]]
	name = "Go Home3"
    url = "/"
	identifier = "grandchildId"
	parent = "parentId"
[[menu.tax]]
	name = "Tax1"
    url = "/two/key/"
	identifier="1"
[[menu.tax]]
	name = "Tax2"
    url = "/two/key"
	identifier="2"
[[menu.tax]]
	name = "Tax RSS"
    url = "/two/key.xml"
	identifier="xml"
[[menu.unicode]]
   name = "Unicode Russian"
   identifier = "unicode-russian"
   url = "/новости-проекта"` // Russian => "news-project"
)

var MENU_PAGE_1 = []byte(`+++
title = "One"
[menu]
	[menu.p_one]
weight = 1
+++
Front Matter with Menu Pages`)

var MENU_PAGE_2 = []byte(`+++
title = "Two"
weight = 2
[menu]
	[menu.p_one]
	[menu.p_two]
		Identity = "Two"

+++
Front Matter with Menu Pages`)

var MENU_PAGE_3 = []byte(`+++
title = "Three"
weight = 3
[menu]
	[menu.p_two]
		Name = "Three"
		Parent = "Two"
+++
Front Matter with Menu Pages`)

var MENU_PAGE_SOURCES = []source.ByteSource{
	{"sect/doc1.md", MENU_PAGE_1},
	{"sect/doc2.md", MENU_PAGE_2},
	{"sect/doc3.md", MENU_PAGE_3},
}

type testMenuState struct {
	site       *Site
	oldMenu    interface{}
	oldBaseUrl interface{}
}

func TestPageMenu(t *testing.T) {
	ts := setupMenuTests(t)
	defer resetMenuTestState(ts)

	if len(ts.site.Pages) != 3 {
		t.Fatalf("Posts not created, expected 3 got %d", len(ts.site.Pages))
	}

	first := ts.site.Pages[0]
	second := ts.site.Pages[1]
	third := ts.site.Pages[2]

	pOne := ts.findTestMenuEntryByName("p_one", "One")
	pTwo := ts.findTestMenuEntryByName("p_two", "Two")

	for i, this := range []struct {
		menu           string
		page           *Page
		menuItem       *MenuEntry
		isMenuCurrent  bool
		hasMenuCurrent bool
	}{
		{"p_one", first, pOne, true, false},
		{"p_one", first, pTwo, false, false},
		{"p_one", second, pTwo, true, false},
		{"p_two", second, pTwo, true, false},
		{"p_two", third, pTwo, false, true},
		{"p_one", third, pTwo, false, false},
	} {

		isMenuCurrent := this.page.IsMenuCurrent(this.menu, this.menuItem)
		hasMenuCurrent := this.page.HasMenuCurrent(this.menu, this.menuItem)

		if isMenuCurrent != this.isMenuCurrent {
			t.Errorf("[%d] Wrong result from IsMenuCurrent: %v", i, isMenuCurrent)
		}

		if hasMenuCurrent != this.hasMenuCurrent {
			t.Errorf("[%d] Wrong result for menuItem %v for HasMenuCurrent: %v", i, this.menuItem, hasMenuCurrent)
		}

	}

}

// issue #719
func TestMenuWithUnicodeUrls(t *testing.T) {
	for _, uglyUrls := range []bool{true, false} {
		doTestMenuWithUnicodeUrls(t, uglyUrls)
	}
}

func doTestMenuWithUnicodeUrls(t *testing.T, uglyUrls bool) {
	viper.Set("UglyUrls", uglyUrls)
	ts := setupMenuTests(t)
	defer resetMenuTestState(ts)

	unicodeRussian := ts.findTestMenuEntryById("unicode", "unicode-russian")

	expectedBase := "http://foo.local/zoo/%D0%BD%D0%BE%D0%B2%D0%BE%D1%81%D1%82%D0%B8-%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B0"
	var expected string
	if uglyUrls {
		expected = expectedBase + ".html"
	} else {
		expected = expectedBase + "/"
	}

	assert.Equal(t, expected, unicodeRussian.Url, "uglyUrls[%t]", uglyUrls)
}

func TestTaxonomyNodeMenu(t *testing.T) {
	ts := setupMenuTests(t)
	defer resetMenuTestState(ts)

	for i, this := range []struct {
		menu           string
		taxInfo        taxRenderInfo
		menuItem       *MenuEntry
		isMenuCurrent  bool
		hasMenuCurrent bool
	}{
		{"tax", taxRenderInfo{key: "key", singular: "one", plural: "two"},
			ts.findTestMenuEntryById("tax", "1"), true, false},
		{"tax", taxRenderInfo{key: "key", singular: "one", plural: "two"},
			ts.findTestMenuEntryById("tax", "2"), true, false},
		{"tax", taxRenderInfo{key: "key", singular: "one", plural: "two"},
			&MenuEntry{Name: "Somewhere else", Url: "/somewhereelse"}, false, false},
	} {

		n, _ := ts.site.newTaxonomyNode(this.taxInfo)

		isMenuCurrent := n.IsMenuCurrent(this.menu, this.menuItem)
		hasMenuCurrent := n.HasMenuCurrent(this.menu, this.menuItem)

		if isMenuCurrent != this.isMenuCurrent {
			t.Errorf("[%d] Wrong result from IsMenuCurrent: %v", i, isMenuCurrent)
		}

		if hasMenuCurrent != this.hasMenuCurrent {
			t.Errorf("[%d] Wrong result for menuItem %v for HasMenuCurrent: %v", i, this.menuItem, hasMenuCurrent)
		}

	}

	menuEntryXml := ts.findTestMenuEntryById("tax", "xml")

	if strings.HasSuffix(menuEntryXml.Url, "/") {
		t.Error("RSS menu item should not be padded with trailing slash")
	}
}

func TestHomeNodeMenu(t *testing.T) {
	ts := setupMenuTests(t)
	defer resetMenuTestState(ts)

	home := ts.site.newHomeNode()
	homeMenuEntry := &MenuEntry{Name: home.Title, Url: string(home.Permalink)}

	for i, this := range []struct {
		menu           string
		menuItem       *MenuEntry
		isMenuCurrent  bool
		hasMenuCurrent bool
	}{
		{"main", homeMenuEntry, true, false},
		{"doesnotexist", homeMenuEntry, false, false},
		{"main", &MenuEntry{Name: "Somewhere else", Url: "/somewhereelse"}, false, false},
		{"grandparent", ts.findTestMenuEntryById("grandparent", "grandparentId"), false, false},
		{"grandparent", ts.findTestMenuEntryById("grandparent", "parentId"), false, true},
		{"grandparent", ts.findTestMenuEntryById("grandparent", "grandchildId"), true, false},
	} {

		isMenuCurrent := home.IsMenuCurrent(this.menu, this.menuItem)
		hasMenuCurrent := home.HasMenuCurrent(this.menu, this.menuItem)

		if isMenuCurrent != this.isMenuCurrent {
			t.Errorf("[%d] Wrong result from IsMenuCurrent: %v", i, isMenuCurrent)
		}

		if hasMenuCurrent != this.hasMenuCurrent {
			t.Errorf("[%d] Wrong result for menuItem %v for HasMenuCurrent: %v", i, this.menuItem, hasMenuCurrent)
		}
	}
}

var testMenuIdentityMatcher = func(me *MenuEntry, id string) bool { return me.Identifier == id }
var testMenuNameMatcher = func(me *MenuEntry, id string) bool { return me.Name == id }

func (ts testMenuState) findTestMenuEntryById(mn string, id string) *MenuEntry {
	return ts.findTestMenuEntry(mn, id, testMenuIdentityMatcher)
}
func (ts testMenuState) findTestMenuEntryByName(mn string, id string) *MenuEntry {
	return ts.findTestMenuEntry(mn, id, testMenuNameMatcher)
}

func (ts testMenuState) findTestMenuEntry(mn string, id string, matcher func(me *MenuEntry, id string) bool) *MenuEntry {
	if menu, ok := ts.site.Menus[mn]; ok {
		for _, me := range *menu {

			if matcher(me, id) {
				return me
			}

			descendant := ts.findDescendantTestMenuEntry(me, id, matcher)
			if descendant != nil {
				return descendant
			}
		}
	}
	return nil
}

func (ts testMenuState) findDescendantTestMenuEntry(parent *MenuEntry, id string, matcher func(me *MenuEntry, id string) bool) *MenuEntry {
	if parent.HasChildren() {
		for _, child := range parent.Children {

			if matcher(child, id) {
				return child
			}

			descendant := ts.findDescendantTestMenuEntry(child, id, matcher)
			if descendant != nil {
				return descendant
			}
		}
	}
	return nil
}

func getTestMenuState(s *Site, t *testing.T) *testMenuState {
	menuState := &testMenuState{site: s, oldBaseUrl: viper.Get("baseurl"), oldMenu: viper.Get("menu")}

	menus, err := tomlToMap(CONF_MENU1)

	if err != nil {
		t.Fatalf("Unable to Read menus: %v", err)
	}

	viper.Set("menu", menus["menu"])
	viper.Set("baseurl", "http://foo.local/zoo/")

	return menuState
}

func setupMenuTests(t *testing.T) *testMenuState {
	s := createTestSite()
	testState := getTestMenuState(s, t)
	testSiteSetup(s, t)

	return testState
}

func resetMenuTestState(state *testMenuState) {
	viper.Set("menu", state.oldMenu)
	viper.Set("baseurl", state.oldBaseUrl)
}

func createTestSite() *Site {
	hugofs.DestinationFS = new(afero.MemMapFs)

	s := &Site{
		Source: &source.InMemorySource{ByteSource: MENU_PAGE_SOURCES},
	}
	return s
}

func testSiteSetup(s *Site, t *testing.T) {

	s.Menus = Menus{}
	s.initializeSiteInfo()
	s.Shortcodes = make(map[string]ShortcodeFunc)

	if err := s.CreatePages(); err != nil {
		t.Fatalf("Unable to create pages: %s", err)
	}

	if err := s.BuildSiteMeta(); err != nil {
		t.Fatalf("Unable to build site metadata: %s", err)
	}

}

func tomlToMap(s string) (map[string]interface{}, error) {
	var data map[string]interface{} = make(map[string]interface{})
	if _, err := toml.Decode(s, &data); err != nil {
		return nil, err
	}

	return data, nil

}
<|endoftext|>


<commit_before>// Copyright (c) 2015 SUSE LLC. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package containersuseconnect

import (
	"crypto/tls"
	"encoding/json"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
)

// All the information we need from repositories as given by the registration
// server.
type Repository struct {
	Name        string `json:"name"`
	Description string `json:"description"`
	URL         string `json:"url"`
	Autorefresh bool   `json:"autorefresh"`
	Enabled     bool   `json:"enabled"`
}

// All the information we need from product as given by the registration
// server. It contains a slice of repositories in it.
type Product struct {
	ProductType  string       `json:"product_type"`
	Identifier   string       `json:"identifier"`
	Version      string       `json:"version"`
	Arch         string       `json:"arch"`
	Repositories []Repository `json:"repositories"`
	Extensions   []Product    `json:"extensions"`
	Recommended  bool         `json:"recommended"`
	Name         string       `json:"name"`
	Description  string       `json:"description"`
}

// Take the "Product" as returned from an RMT and adjust the repository URLs
// to include a "credentials" parameter. This is somewhat specific to the RMT
// instances available for Public Cloud on-demand instances, as they need to
// authenticate to be able to access repositories.
func fixRepoUrlsForRMT(p *Product) error {
	for i := range p.Repositories {
		repourl, err := url.Parse(p.Repositories[i].URL)
		if err != nil {
			loggedError("Unable to parse repository URL: %s - %v", p.Repositories[i].URL, err)
			return err
		}
		params := repourl.Query()
		if params.Get("credentials") == "" {
			params.Add("credentials", "SCCcredentials")
		}
		repourl.RawQuery = params.Encode()
		p.Repositories[i].URL = repourl.String()
	}

	// Products can have nested Products (Extensions) handle those recursively
	for i := range p.Extensions {
		if err := fixRepoUrlsForRMT(&p.Extensions[i]); err != nil {
			return err
		}
	}
	return nil
}

// Parse the product as expected from the given reader. This function already
// checks whether the given reader is valid or not.
func parseProducts(reader io.Reader) ([]Product, error) {
	var products []Product

	data, err := ioutil.ReadAll(reader)
	if err != nil {
		return products,
			loggedError("Can't read product information: %v", err.Error())
	}

	// Depending on which API was used the JSON we get passed contains
	// either a list of products ("/connect/subscriptions/products") with
	// a single element in it or a single product
	// ("/connect/systems/products").  So we need to Unmarshall() slightly
	// different for both cases.
	err = json.Unmarshal(data, &products)
	if err != nil {
		products = nil
		// When connected to RMT we only got a single "Product", so let's try
		// to unmarshall that. (And add a "credential" parameter to it if that
		// is not present)
		var product Product
		err = json.Unmarshal(data, &product)
		if err == nil {
			fixRepoUrlsForRMT(&product)
			products = append(products, product)
		}
	}
	if err != nil {
		return products,
			loggedError("Can't read product information: %v - %s", err.Error(), data)
	}
	return products, nil
}

// Request product information to the registration server. The `regCode`
// parameters is used to establish the connection with
// the registration server. The `installed` parameter contains the product to
// be requested.
// This function relies on [/connect/subscriptions/products](https://github.com/SUSE/connect/wiki/SCC-API-%28Implemented%29#product) API.
func requestProductsFromRegCodeOrSystem(data SUSEConnectData, regCode string,
	credentials Credentials, installed InstalledProduct) ([]Product, error) {
	var products []Product
	var err error

	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: data.Insecure},
		Proxy:           http.ProxyFromEnvironment,
	}
	client := &http.Client{Transport: tr}
	req, err := http.NewRequest("GET", data.SccURL, nil)
	if err != nil {
		return products,
			loggedError("Could not connect with registration server: %v\n", err)
	}

	values := req.URL.Query()
	values.Add("identifier", installed.Identifier)
	values.Add("version", installed.Version)
	values.Add("arch", installed.Arch)
	req.URL.RawQuery = values.Encode()
	if len(regCode) > 0 {
		req.Header.Add("Authorization", `Token token=`+regCode)
		req.URL.Path = "/connect/subscriptions/products"
	} else {
		// we're connected to a RMT, which does not provide the /connect/subscriptions/products
		// endpoint. Fallback to "/connect/systems/products" here.
		req.URL.Path = "/connect/systems/products"
		auth := url.UserPassword(credentials.Username, credentials.Password)
		req.URL.User = auth
	}

	resp, err := client.Do(req)
	if err != nil {
		return products, err
	}
	if resp.StatusCode != 200 {
		dec := json.NewDecoder(resp.Body)
		var payload map[string]interface{}
		if err := dec.Decode(&payload); err == nil {
			if err, ok := payload["error"]; ok {
				log.Println(err)
			}
		}
		return products,
			loggedError("Unexpected error while retrieving products with regCode %s: %s", regCode, resp.Status)
	}

	return parseProducts(resp.Body)
}

// Request product information to the registration server. The `data` and the
// `credentials` parameters are used in order to establish the connection with
// the registration server. The `installed` parameter contains the product to
// be requested.
func RequestProducts(data SUSEConnectData, credentials Credentials,
	installed InstalledProduct) ([]Product, error) {
	var products []Product
	var regCodes []string
	var err error

	regCodes, err = requestRegcodes(data, credentials)
	if err != nil {
		return products, err
	}

	for _, regCode := range regCodes {
		p, err := requestProductsFromRegCodeOrSystem(data, regCode, credentials, installed)
		if err != nil {
			var emptyProducts []Product
			return emptyProducts, err
		}
		products = append(products, p...)
	}

	return products, nil
}
<commit_msg>Avoid adding multiple credential values to the URL<commit_after>// Copyright (c) 2015 SUSE LLC. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package containersuseconnect

import (
	"crypto/tls"
	"encoding/json"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
)

// All the information we need from repositories as given by the registration
// server.
type Repository struct {
	Name        string `json:"name"`
	Description string `json:"description"`
	URL         string `json:"url"`
	Autorefresh bool   `json:"autorefresh"`
	Enabled     bool   `json:"enabled"`
}

// All the information we need from product as given by the registration
// server. It contains a slice of repositories in it.
type Product struct {
	ProductType  string       `json:"product_type"`
	Identifier   string       `json:"identifier"`
	Version      string       `json:"version"`
	Arch         string       `json:"arch"`
	Repositories []Repository `json:"repositories"`
	Extensions   []Product    `json:"extensions"`
	Recommended  bool         `json:"recommended"`
	Name         string       `json:"name"`
	Description  string       `json:"description"`
}

// Take the "Product" as returned from an RMT and adjust the repository URLs
// to include a "credentials" parameter. This is somewhat specific to the RMT
// instances available for Public Cloud on-demand instances, as they need to
// authenticate to be able to access repositories.
func fixRepoUrlsForRMT(p *Product) error {
	for i := range p.Repositories {
		repourl, err := url.Parse(p.Repositories[i].URL)
		if err != nil {
			loggedError("Unable to parse repository URL: %s - %v", p.Repositories[i].URL, err)
			return err
		}
		params := repourl.Query()
		if params.Get("credentials") == "" {
			params["credentials"] = []string{"SCCcredentials"}
		}
		repourl.RawQuery = params.Encode()
		p.Repositories[i].URL = repourl.String()
	}

	// Products can have nested Products (Extensions) handle those recursively
	for i := range p.Extensions {
		if err := fixRepoUrlsForRMT(&p.Extensions[i]); err != nil {
			return err
		}
	}
	return nil
}

// Parse the product as expected from the given reader. This function already
// checks whether the given reader is valid or not.
func parseProducts(reader io.Reader) ([]Product, error) {
	var products []Product

	data, err := ioutil.ReadAll(reader)
	if err != nil {
		return products,
			loggedError("Can't read product information: %v", err.Error())
	}

	// Depending on which API was used the JSON we get passed contains
	// either a list of products ("/connect/subscriptions/products") with
	// a single element in it or a single product
	// ("/connect/systems/products").  So we need to Unmarshall() slightly
	// different for both cases.
	err = json.Unmarshal(data, &products)
	if err != nil {
		products = nil
		// When connected to RMT we only got a single "Product", so let's try
		// to unmarshall that. (And add a "credential" parameter to it if that
		// is not present)
		var product Product
		err = json.Unmarshal(data, &product)
		if err == nil {
			fixRepoUrlsForRMT(&product)
			products = append(products, product)
		}
	}
	if err != nil {
		return products,
			loggedError("Can't read product information: %v - %s", err.Error(), data)
	}
	return products, nil
}

// Request product information to the registration server. The `regCode`
// parameters is used to establish the connection with
// the registration server. The `installed` parameter contains the product to
// be requested.
// This function relies on [/connect/subscriptions/products](https://github.com/SUSE/connect/wiki/SCC-API-%28Implemented%29#product) API.
func requestProductsFromRegCodeOrSystem(data SUSEConnectData, regCode string,
	credentials Credentials, installed InstalledProduct) ([]Product, error) {
	var products []Product
	var err error

	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: data.Insecure},
		Proxy:           http.ProxyFromEnvironment,
	}
	client := &http.Client{Transport: tr}
	req, err := http.NewRequest("GET", data.SccURL, nil)
	if err != nil {
		return products,
			loggedError("Could not connect with registration server: %v\n", err)
	}

	values := req.URL.Query()
	values.Add("identifier", installed.Identifier)
	values.Add("version", installed.Version)
	values.Add("arch", installed.Arch)
	req.URL.RawQuery = values.Encode()
	if len(regCode) > 0 {
		req.Header.Add("Authorization", `Token token=`+regCode)
		req.URL.Path = "/connect/subscriptions/products"
	} else {
		// we're connected to a RMT, which does not provide the /connect/subscriptions/products
		// endpoint. Fallback to "/connect/systems/products" here.
		req.URL.Path = "/connect/systems/products"
		auth := url.UserPassword(credentials.Username, credentials.Password)
		req.URL.User = auth
	}

	resp, err := client.Do(req)
	if err != nil {
		return products, err
	}
	if resp.StatusCode != 200 {
		dec := json.NewDecoder(resp.Body)
		var payload map[string]interface{}
		if err := dec.Decode(&payload); err == nil {
			if err, ok := payload["error"]; ok {
				log.Println(err)
			}
		}
		return products,
			loggedError("Unexpected error while retrieving products with regCode %s: %s", regCode, resp.Status)
	}

	return parseProducts(resp.Body)
}

// Request product information to the registration server. The `data` and the
// `credentials` parameters are used in order to establish the connection with
// the registration server. The `installed` parameter contains the product to
// be requested.
func RequestProducts(data SUSEConnectData, credentials Credentials,
	installed InstalledProduct) ([]Product, error) {
	var products []Product
	var regCodes []string
	var err error

	regCodes, err = requestRegcodes(data, credentials)
	if err != nil {
		return products, err
	}

	for _, regCode := range regCodes {
		p, err := requestProductsFromRegCodeOrSystem(data, regCode, credentials, installed)
		if err != nil {
			var emptyProducts []Product
			return emptyProducts, err
		}
		products = append(products, p...)
	}

	return products, nil
}
<|endoftext|>


<commit_before>package device_test

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"syscall"
	"testing"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"
	"github.com/tedsuo/ifrit"

	"github.com/cloudfoundry-incubator/garden"
	"github.com/cloudfoundry-incubator/garden-linux/integration/runner"
)

var binPath = "../../old/linux_backend/bin" // relative to test suite directory
var rootFSPath = os.Getenv("GARDEN_TEST_ROOTFS")
var fuseRootFSPath = os.Getenv("GARDEN_FUSE_TEST_ROOTFS")
var graphPath = os.Getenv("GARDEN_TEST_GRAPHPATH")

var gardenBin string
var gardenRunner *runner.Runner
var gardenProcess ifrit.Process

var client garden.Client

func startGarden(argv ...string) garden.Client {
	gardenAddr := fmt.Sprintf("/tmp/garden_%d.sock", GinkgoParallelNode())

	{ // Check this test suite is in the correct directory
		b, err := os.Open(binPath)
		Expect(err).ToNot(HaveOccurred())
		b.Close()
	}

	gardenRunner = runner.New("unix", gardenAddr, gardenBin, binPath, rootFSPath, graphPath, argv...)

	gardenProcess = ifrit.Invoke(gardenRunner)

	return gardenRunner.NewClient()
}

func restartGarden(argv ...string) {
	gardenProcess.Signal(syscall.SIGINT)
	Eventually(gardenProcess.Wait(), "10s").Should(Receive())

	startGarden(argv...)
}

func TestDevice(t *testing.T) {
	if fuseRootFSPath == "" {
		log.Println("GARDEN_FUSE_TEST_ROOTFS undefined; skipping")
		return
	}

	var beforeSuite struct {
		GardenPath string
	}

	SynchronizedBeforeSuite(func() []byte {
		var err error
		beforeSuite.GardenPath, err = gexec.Build("github.com/cloudfoundry-incubator/garden-linux", "-a", "-race", "-tags", "daemon")
		Expect(err).ToNot(HaveOccurred())

		b, err := json.Marshal(beforeSuite)
		Expect(err).ToNot(HaveOccurred())

		return b
	}, func(paths []byte) {
		err := json.Unmarshal(paths, &beforeSuite)
		Expect(err).ToNot(HaveOccurred())

		gardenBin = beforeSuite.GardenPath
	})

	AfterEach(func() {
		gardenProcess.Signal(syscall.SIGQUIT)
		Eventually(gardenProcess.Wait(), "20s").Should(Receive())
	})

	SynchronizedAfterSuite(func() {
		//noop
	}, func() {
		gexec.CleanupBuildArtifacts()
	})

	RegisterFailHandler(Fail)
	RunSpecs(t, "Device Suite")
}
<commit_msg>Make AfterEach more reliable<commit_after>package device_test

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"syscall"
	"testing"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"
	"github.com/tedsuo/ifrit"

	"github.com/cloudfoundry-incubator/garden"
	"github.com/cloudfoundry-incubator/garden-linux/integration/runner"
)

var binPath = "../../old/linux_backend/bin" // relative to test suite directory
var rootFSPath = os.Getenv("GARDEN_TEST_ROOTFS")
var fuseRootFSPath = os.Getenv("GARDEN_FUSE_TEST_ROOTFS")
var graphPath = os.Getenv("GARDEN_TEST_GRAPHPATH")

var gardenBin string
var gardenRunner *runner.Runner
var gardenProcess ifrit.Process

var client garden.Client

func startGarden(argv ...string) garden.Client {
	gardenAddr := fmt.Sprintf("/tmp/garden_%d.sock", GinkgoParallelNode())

	{ // Check this test suite is in the correct directory
		b, err := os.Open(binPath)
		Expect(err).ToNot(HaveOccurred())
		b.Close()
	}

	gardenRunner = runner.New("unix", gardenAddr, gardenBin, binPath, rootFSPath, graphPath, argv...)

	gardenProcess = ifrit.Invoke(gardenRunner)

	return gardenRunner.NewClient()
}

func restartGarden(argv ...string) {
	gardenProcess.Signal(syscall.SIGINT)
	Eventually(gardenProcess.Wait(), "10s").Should(Receive())

	startGarden(argv...)
}

func TestDevice(t *testing.T) {
	if fuseRootFSPath == "" {
		log.Println("GARDEN_FUSE_TEST_ROOTFS undefined; skipping")
		return
	}

	var beforeSuite struct {
		GardenPath string
	}

	SynchronizedBeforeSuite(func() []byte {
		var err error
		beforeSuite.GardenPath, err = gexec.Build("github.com/cloudfoundry-incubator/garden-linux", "-a", "-race", "-tags", "daemon")
		Expect(err).ToNot(HaveOccurred())

		b, err := json.Marshal(beforeSuite)
		Expect(err).ToNot(HaveOccurred())

		return b
	}, func(paths []byte) {
		err := json.Unmarshal(paths, &beforeSuite)
		Expect(err).ToNot(HaveOccurred())

		gardenBin = beforeSuite.GardenPath
	})

	AfterEach(func() {
		ensureGardenRunning()
		gardenProcess.Signal(syscall.SIGQUIT)
		Eventually(gardenProcess.Wait(), "30s").Should(Receive())
	})

	SynchronizedAfterSuite(func() {
		//noop
	}, func() {
		gexec.CleanupBuildArtifacts()
	})

	RegisterFailHandler(Fail)
	RunSpecs(t, "Device Suite")
}

func ensureGardenRunning() {
	if err := client.Ping(); err != nil {
		client = startGarden()
	}
	Expect(client.Ping()).ToNot(HaveOccurred())
}
<|endoftext|>


<commit_before>// Copyright 2016 Marc-Antoine Ruel. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.

// mixers is all the patterns that are constructions of other patterns.

package anim1d

// Gradient does a gradient between 2 patterns.
//
// A good example is using two colors but it can also be animations.
//
// TODO(maruel): Support N colors at M positions.
type Gradient struct {
	Left  SPattern
	Right SPattern
	Curve Curve
	buf   Frame
}

// Render implements Pattern.
func (g *Gradient) Render(pixels Frame, timeMS uint32) {
	l := len(pixels)
	if l == 0 {
		return
	}
	g.buf.reset(l)
	g.Left.Render(pixels, timeMS)
	g.Right.Render(g.buf, timeMS)
	if l == 1 {
		pixels.Mix(g.buf, g.Curve.Scale8(65535>>1))
	} else {
		max := l - 1
		for i := range pixels {
			intensity := uint16(i * 65535 / max)
			pixels[i].Mix(g.buf[i], g.Curve.Scale8(intensity))
		}
	}
}

// Split splits the strip in two.
//
// Unlike gradient, this create 2 logical independent subsets.
type Split struct {
	Left   SPattern
	Right  SPattern
	Offset SValue // Point to split between both sides.
}

// Render implements Pattern.
func (s *Split) Render(pixels Frame, timeMS uint32) {
	offset := MinMax(int(s.Offset.Eval(timeMS, len(pixels))), 0, len(pixels))
	if s.Left.Pattern != nil && offset != 0 {
		s.Left.Render(pixels[:offset], timeMS)
	}
	if s.Right.Pattern != nil && offset != len(pixels) {
		s.Right.Render(pixels[offset:], timeMS)
	}
}

// Transition changes from Before to After over time. It doesn't repeat.
//
// In gets timeMS that is subtracted by OffsetMS.
type Transition struct {
	Before       SPattern // Old pattern that is disappearing
	After        SPattern // New pattern to show
	OffsetMS     uint32   // Offset at which the transiton from Before->In starts
	TransitionMS uint32   // Duration of the transition while both are rendered
	Curve        Curve    // Type of transition, defaults to EaseOut if not set
	buf          Frame
}

// Render implements Pattern.
func (t *Transition) Render(pixels Frame, timeMS uint32) {
	if timeMS <= t.OffsetMS {
		// Before transition.
		t.Before.Render(pixels, timeMS)
		return
	}
	t.After.Render(pixels, timeMS-t.OffsetMS)
	if timeMS >= t.OffsetMS+t.TransitionMS {
		// After transition.
		t.buf = nil
		return
	}
	t.buf.reset(len(pixels))

	// TODO(maruel): Add lateral animation and others.
	t.Before.Render(t.buf, timeMS)
	intensity := uint16((timeMS - t.OffsetMS) * 65535 / (t.TransitionMS))
	pixels.Mix(t.buf, 255.-t.Curve.Scale8(intensity))
}

// Loop rotates between all the animations.
//
// Display starts with one ShowMS for Patterns[0], then starts looping.
// timeMS is not modified so it's like as all animations continued animating
// behind.
// TODO(maruel): Add lateral transition and others.
type Loop struct {
	Patterns     []SPattern
	ShowMS       uint32 // Duration for each pattern to be shown as pure
	TransitionMS uint32 // Duration of the transition between two patterns, can be 0
	Curve        Curve  // Type of transition, defaults to EaseOut if not set
	buf          Frame
}

// Render implements Pattern.
func (l *Loop) Render(pixels Frame, timeMS uint32) {
	lp := uint32(len(l.Patterns))
	if lp == 0 {
		return
	}
	cycleDuration := l.ShowMS + l.TransitionMS
	if cycleDuration == 0 {
		// Misconfigured. Lock to the first pattern.
		l.Patterns[0].Render(pixels, timeMS)
		return
	}

	base := timeMS / cycleDuration
	index := base % lp
	a := l.Patterns[index]
	a.Render(pixels, timeMS)
	offset := timeMS - (base * cycleDuration)
	if offset <= l.ShowMS {
		return
	}

	// Transition.
	l.buf.reset(len(pixels))
	b := l.Patterns[(index+1)%lp]
	b.Render(l.buf, timeMS)
	offset -= l.ShowMS
	intensity := uint16((l.TransitionMS - offset) * 65535 / l.TransitionMS)
	pixels.Mix(l.buf, l.Curve.Scale8(65535-intensity))
}

// Rotate rotates a pattern that can also cycle either way.
//
// Use negative to go left. Can be used for 'candy bar'.
//
// Similar to PingPong{} except that it doesn't bounce.
//
// Use 5x oversampling with Scale{} to create smoother animation.
type Rotate struct {
	Child       SPattern
	MovePerHour MovePerHour // Expressed in number of light jumps per hour.
	buf         Frame
}

// Render implements Pattern.
func (r *Rotate) Render(pixels Frame, timeMS uint32) {
	l := len(pixels)
	r.buf.reset(l)
	r.Child.Render(r.buf, timeMS)
	offset := r.MovePerHour.Eval(timeMS, len(pixels), l)
	if offset < 0 {
		// Reverse direction.
		offset = l + offset
	}
	copy(pixels[offset:], r.buf)
	copy(pixels[:offset], r.buf[l-offset:])
}

// Chronometer moves 3 lights to the right, each indicating second, minute, and
// hour passed since the start.
//
// Child has 4 pixels used in this order: [default, second, minute, hour].
type Chronometer struct {
	Child SPattern
	buf   Frame
}

// Render implements Pattern.
func (r *Chronometer) Render(pixels Frame, timeMS uint32) {
	l := uint32(len(pixels))
	if l == 0 {
		return
	}
	r.buf.reset(4)
	r.Child.Render(r.buf, timeMS)

	seconds := timeMS / 1000
	mins := seconds / 60
	hours := mins / 60

	secPos := (l*(seconds%60) + 30) / 60
	minPos := (l*(mins%60) + 30) / 60
	hourPos := hours % l

	for i := range pixels {
		switch uint32(i) {
		case secPos:
			pixels[i] = r.buf[1]
		case minPos:
			pixels[i] = r.buf[2]
		case hourPos:
			pixels[i] = r.buf[3]
		default:
			pixels[i] = r.buf[0]
		}
	}
}

// PingPong shows a 'ball' with a trail that bounces from one side to
// the other.
//
// Can be used for a ball, a water wave or K2000 (Knight Rider) style light.
// The trail can be a Frame or a dynamic pattern.
//
// To get smoothed movement, use Scale{} with a 5x factor or so.
// TODO(maruel): That's a bit inefficient, enable Interpolation here.
type PingPong struct {
	Child       SPattern    // [0] is the front pixel so the pixels are effectively drawn in reverse order
	MovePerHour MovePerHour // Expressed in number of light jumps per hour
	buf         Frame
}

// Render implements Pattern.
func (p *PingPong) Render(pixels Frame, timeMS uint32) {
	if len(pixels) == 0 {
		return
	}
	p.buf.reset(len(pixels)*2 - 1)
	p.Child.Render(p.buf, timeMS)
	// The last point of each extremity is only lit on one tick but every other
	// points are lit twice during a full cycle. This means the full cycle is
	// 2*(len(pixels)-1). For a 3 pixels line, the cycle is: x00, 0x0, 00x, 0x0.
	//
	// For Child being Frame "01234567":
	//   move == 0  -> "01234567"
	//   move == 2  -> "21056789"
	//   move == 5  -> "543210ab"
	//   move == 7  -> "76543210"
	//   move == 9  -> "98765012"
	//   move == 11 -> "ba901234"
	//   move == 13 -> "d0123456"
	//   move 14 -> move 0; "2*(8-1)"
	cycle := 2 * (len(pixels) - 1)
	// TODO(maruel): Smoothing with Curve, defaults to Step.
	pos := p.MovePerHour.Eval(timeMS, len(pixels), cycle)

	// Once it works the following code looks trivial but everytime it takes me
	// an absurd amount of time to rewrite it.
	if pos >= len(pixels)-1 {
		// Head runs left.
		// pos2 is the position from the right.
		pos2 := pos + 1 - len(pixels)
		// limit is the offset at which order change.
		limit := len(pixels) - pos2 - 1
		for i := range pixels {
			if i < limit {
				// Going right.
				pixels[i] = p.buf[len(pixels)-i+pos2-1]
			} else {
				// Going left.
				pixels[i] = p.buf[i-limit]
			}
		}
	} else {
		// Head runs right.
		for i := range pixels {
			if i <= pos {
				// Going right.
				pixels[i] = p.buf[pos-i]
			} else {
				// Going left.
				pixels[i] = p.buf[pos+i]
			}
		}
	}
}

// Crop skips the beginning and the end of the source.
type Crop struct {
	Child  SPattern
	Before SValue // Starting pixels to skip
	After  SValue // Ending pixels to skip
	buf    Frame
}

// Render implements Pattern.
func (c *Crop) Render(pixels Frame, timeMS uint32) {
	b := int(MinMax32(c.Before.Eval(timeMS, len(pixels)), 0, 1000))
	a := int(MinMax32(c.After.Eval(timeMS, len(pixels)), 0, 1000))
	// This is slightly wasteful as pixels are drawn just to be ditched.
	c.buf.reset(len(pixels) + b + a)
	c.Child.Render(c.buf, timeMS)
	copy(pixels, c.buf[b:])
}

// Subset skips the beginning and the end of the destination.
type Subset struct {
	Child  SPattern
	Offset SValue // Starting pixels to skip
	Length SValue // Length of the pixels to carry over
}

// Render implements Pattern.
func (s *Subset) Render(pixels Frame, timeMS uint32) {
	if s.Child.Pattern == nil {
		return
	}
	o := MinMax(int(s.Offset.Eval(timeMS, len(pixels))), 0, len(pixels)-1)
	l := MinMax(int(s.Length.Eval(timeMS, len(pixels))), 0, len(pixels)-1-o)
	s.Child.Render(pixels[o:o+l], timeMS)
}

// Dim is a filter that dim the intensity of a buffer.
type Dim struct {
	Child     SPattern //
	Intensity SValue   // 0 is transparent, 255 is fully opaque with original colors.
}

// Render implements Pattern.
func (d *Dim) Render(pixels Frame, timeMS uint32) {
	d.Child.Render(pixels, timeMS)
	i := MinMax32(d.Intensity.Eval(timeMS, len(pixels)), 0, 255)
	pixels.Dim(uint8(i))
}

// Add is a generic mixer that merges the output from multiple patterns with
// saturation.
type Add struct {
	Patterns []SPattern // It should be a list of Dim{} with their corresponding weight.
	buf      Frame      //
}

// Render implements Pattern.
func (a *Add) Render(pixels Frame, timeMS uint32) {
	// Draw and merge each pattern.
	for i := range a.Patterns {
		a.buf.reset(len(pixels))
		a.Patterns[i].Render(a.buf, timeMS)
		pixels.Add(a.buf)
	}
}

// Scale adapts a larger or smaller patterns to the Strip size
//
// This is useful to create smoother horizontal movement animation or to scale
// up/down images.
type Scale struct {
	Child SPattern
	// Defaults to Linear
	Interpolation Interpolation
	// A buffer of this len(buffer)*RatioMilli/1000 will be provided to Child and
	// will be scaled; 500 means smaller, 2000 is larger.
	//
	// Can be set to 0 when Child is a Frame. In this case it is stretched to the
	// strip size.
	RatioMilli SValue
	buf        Frame
}

// Render implements Pattern.
func (s *Scale) Render(pixels Frame, timeMS uint32) {
	if f, ok := s.Child.Pattern.(Frame); ok {
		if s.RatioMilli.Eval(timeMS, len(pixels)) == 0 {
			s.Interpolation.Scale(f, pixels)
			return
		}
	}
	v := MinMax32(s.RatioMilli.Eval(timeMS, len(pixels)), 1, 1000000)
	s.buf.reset((int(v)*len(pixels) + 500) / 1000)
	s.Child.Render(s.buf, timeMS)
	s.Interpolation.Scale(s.buf, pixels)
}
<commit_msg>anim1d: fix Add (bis)<commit_after>// Copyright 2016 Marc-Antoine Ruel. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.

// mixers is all the patterns that are constructions of other patterns.

package anim1d

// Gradient does a gradient between 2 patterns.
//
// A good example is using two colors but it can also be animations.
//
// TODO(maruel): Support N colors at M positions.
type Gradient struct {
	Left  SPattern
	Right SPattern
	Curve Curve
	buf   Frame
}

// Render implements Pattern.
func (g *Gradient) Render(pixels Frame, timeMS uint32) {
	l := len(pixels)
	if l == 0 {
		return
	}
	g.buf.reset(l)
	g.Left.Render(pixels, timeMS)
	g.Right.Render(g.buf, timeMS)
	if l == 1 {
		pixels.Mix(g.buf, g.Curve.Scale8(65535>>1))
	} else {
		max := l - 1
		for i := range pixels {
			intensity := uint16(i * 65535 / max)
			pixels[i].Mix(g.buf[i], g.Curve.Scale8(intensity))
		}
	}
}

// Split splits the strip in two.
//
// Unlike gradient, this create 2 logical independent subsets.
type Split struct {
	Left   SPattern
	Right  SPattern
	Offset SValue // Point to split between both sides.
}

// Render implements Pattern.
func (s *Split) Render(pixels Frame, timeMS uint32) {
	offset := MinMax(int(s.Offset.Eval(timeMS, len(pixels))), 0, len(pixels))
	if s.Left.Pattern != nil && offset != 0 {
		s.Left.Render(pixels[:offset], timeMS)
	}
	if s.Right.Pattern != nil && offset != len(pixels) {
		s.Right.Render(pixels[offset:], timeMS)
	}
}

// Transition changes from Before to After over time. It doesn't repeat.
//
// In gets timeMS that is subtracted by OffsetMS.
type Transition struct {
	Before       SPattern // Old pattern that is disappearing
	After        SPattern // New pattern to show
	OffsetMS     uint32   // Offset at which the transiton from Before->In starts
	TransitionMS uint32   // Duration of the transition while both are rendered
	Curve        Curve    // Type of transition, defaults to EaseOut if not set
	buf          Frame
}

// Render implements Pattern.
func (t *Transition) Render(pixels Frame, timeMS uint32) {
	if timeMS <= t.OffsetMS {
		// Before transition.
		t.Before.Render(pixels, timeMS)
		return
	}
	t.After.Render(pixels, timeMS-t.OffsetMS)
	if timeMS >= t.OffsetMS+t.TransitionMS {
		// After transition.
		t.buf = nil
		return
	}
	t.buf.reset(len(pixels))

	// TODO(maruel): Add lateral animation and others.
	t.Before.Render(t.buf, timeMS)
	intensity := uint16((timeMS - t.OffsetMS) * 65535 / (t.TransitionMS))
	pixels.Mix(t.buf, 255.-t.Curve.Scale8(intensity))
}

// Loop rotates between all the animations.
//
// Display starts with one ShowMS for Patterns[0], then starts looping.
// timeMS is not modified so it's like as all animations continued animating
// behind.
// TODO(maruel): Add lateral transition and others.
type Loop struct {
	Patterns     []SPattern
	ShowMS       uint32 // Duration for each pattern to be shown as pure
	TransitionMS uint32 // Duration of the transition between two patterns, can be 0
	Curve        Curve  // Type of transition, defaults to EaseOut if not set
	buf          Frame
}

// Render implements Pattern.
func (l *Loop) Render(pixels Frame, timeMS uint32) {
	lp := uint32(len(l.Patterns))
	if lp == 0 {
		return
	}
	cycleDuration := l.ShowMS + l.TransitionMS
	if cycleDuration == 0 {
		// Misconfigured. Lock to the first pattern.
		l.Patterns[0].Render(pixels, timeMS)
		return
	}

	base := timeMS / cycleDuration
	index := base % lp
	a := l.Patterns[index]
	a.Render(pixels, timeMS)
	offset := timeMS - (base * cycleDuration)
	if offset <= l.ShowMS {
		return
	}

	// Transition.
	l.buf.reset(len(pixels))
	b := l.Patterns[(index+1)%lp]
	b.Render(l.buf, timeMS)
	offset -= l.ShowMS
	intensity := uint16((l.TransitionMS - offset) * 65535 / l.TransitionMS)
	pixels.Mix(l.buf, l.Curve.Scale8(65535-intensity))
}

// Rotate rotates a pattern that can also cycle either way.
//
// Use negative to go left. Can be used for 'candy bar'.
//
// Similar to PingPong{} except that it doesn't bounce.
//
// Use 5x oversampling with Scale{} to create smoother animation.
type Rotate struct {
	Child       SPattern
	MovePerHour MovePerHour // Expressed in number of light jumps per hour.
	buf         Frame
}

// Render implements Pattern.
func (r *Rotate) Render(pixels Frame, timeMS uint32) {
	l := len(pixels)
	r.buf.reset(l)
	r.Child.Render(r.buf, timeMS)
	offset := r.MovePerHour.Eval(timeMS, len(pixels), l)
	if offset < 0 {
		// Reverse direction.
		offset = l + offset
	}
	copy(pixels[offset:], r.buf)
	copy(pixels[:offset], r.buf[l-offset:])
}

// Chronometer moves 3 lights to the right, each indicating second, minute, and
// hour passed since the start.
//
// Child has 4 pixels used in this order: [default, second, minute, hour].
type Chronometer struct {
	Child SPattern
	buf   Frame
}

// Render implements Pattern.
func (r *Chronometer) Render(pixels Frame, timeMS uint32) {
	l := uint32(len(pixels))
	if l == 0 {
		return
	}
	r.buf.reset(4)
	r.Child.Render(r.buf, timeMS)

	seconds := timeMS / 1000
	mins := seconds / 60
	hours := mins / 60

	secPos := (l*(seconds%60) + 30) / 60
	minPos := (l*(mins%60) + 30) / 60
	hourPos := hours % l

	for i := range pixels {
		switch uint32(i) {
		case secPos:
			pixels[i] = r.buf[1]
		case minPos:
			pixels[i] = r.buf[2]
		case hourPos:
			pixels[i] = r.buf[3]
		default:
			pixels[i] = r.buf[0]
		}
	}
}

// PingPong shows a 'ball' with a trail that bounces from one side to
// the other.
//
// Can be used for a ball, a water wave or K2000 (Knight Rider) style light.
// The trail can be a Frame or a dynamic pattern.
//
// To get smoothed movement, use Scale{} with a 5x factor or so.
// TODO(maruel): That's a bit inefficient, enable Interpolation here.
type PingPong struct {
	Child       SPattern    // [0] is the front pixel so the pixels are effectively drawn in reverse order
	MovePerHour MovePerHour // Expressed in number of light jumps per hour
	buf         Frame
}

// Render implements Pattern.
func (p *PingPong) Render(pixels Frame, timeMS uint32) {
	if len(pixels) == 0 {
		return
	}
	p.buf.reset(len(pixels)*2 - 1)
	p.Child.Render(p.buf, timeMS)
	// The last point of each extremity is only lit on one tick but every other
	// points are lit twice during a full cycle. This means the full cycle is
	// 2*(len(pixels)-1). For a 3 pixels line, the cycle is: x00, 0x0, 00x, 0x0.
	//
	// For Child being Frame "01234567":
	//   move == 0  -> "01234567"
	//   move == 2  -> "21056789"
	//   move == 5  -> "543210ab"
	//   move == 7  -> "76543210"
	//   move == 9  -> "98765012"
	//   move == 11 -> "ba901234"
	//   move == 13 -> "d0123456"
	//   move 14 -> move 0; "2*(8-1)"
	cycle := 2 * (len(pixels) - 1)
	// TODO(maruel): Smoothing with Curve, defaults to Step.
	pos := p.MovePerHour.Eval(timeMS, len(pixels), cycle)

	// Once it works the following code looks trivial but everytime it takes me
	// an absurd amount of time to rewrite it.
	if pos >= len(pixels)-1 {
		// Head runs left.
		// pos2 is the position from the right.
		pos2 := pos + 1 - len(pixels)
		// limit is the offset at which order change.
		limit := len(pixels) - pos2 - 1
		for i := range pixels {
			if i < limit {
				// Going right.
				pixels[i] = p.buf[len(pixels)-i+pos2-1]
			} else {
				// Going left.
				pixels[i] = p.buf[i-limit]
			}
		}
	} else {
		// Head runs right.
		for i := range pixels {
			if i <= pos {
				// Going right.
				pixels[i] = p.buf[pos-i]
			} else {
				// Going left.
				pixels[i] = p.buf[pos+i]
			}
		}
	}
}

// Crop skips the beginning and the end of the source.
type Crop struct {
	Child  SPattern
	Before SValue // Starting pixels to skip
	After  SValue // Ending pixels to skip
	buf    Frame
}

// Render implements Pattern.
func (c *Crop) Render(pixels Frame, timeMS uint32) {
	b := int(MinMax32(c.Before.Eval(timeMS, len(pixels)), 0, 1000))
	a := int(MinMax32(c.After.Eval(timeMS, len(pixels)), 0, 1000))
	// This is slightly wasteful as pixels are drawn just to be ditched.
	c.buf.reset(len(pixels) + b + a)
	c.Child.Render(c.buf, timeMS)
	copy(pixels, c.buf[b:])
}

// Subset skips the beginning and the end of the destination.
type Subset struct {
	Child  SPattern
	Offset SValue // Starting pixels to skip
	Length SValue // Length of the pixels to carry over
}

// Render implements Pattern.
func (s *Subset) Render(pixels Frame, timeMS uint32) {
	if s.Child.Pattern == nil {
		return
	}
	o := MinMax(int(s.Offset.Eval(timeMS, len(pixels))), 0, len(pixels)-1)
	l := MinMax(int(s.Length.Eval(timeMS, len(pixels))), 0, len(pixels)-1-o)
	s.Child.Render(pixels[o:o+l], timeMS)
}

// Dim is a filter that dim the intensity of a buffer.
type Dim struct {
	Child     SPattern //
	Intensity SValue   // 0 is transparent, 255 is fully opaque with original colors.
}

// Render implements Pattern.
func (d *Dim) Render(pixels Frame, timeMS uint32) {
	d.Child.Render(pixels, timeMS)
	i := MinMax32(d.Intensity.Eval(timeMS, len(pixels)), 0, 255)
	pixels.Dim(uint8(i))
}

// Add is a generic mixer that merges the output from multiple patterns with
// saturation.
type Add struct {
	Patterns []SPattern // It should be a list of Dim{} with their corresponding weight.
	buf      Frame      //
}

// Render implements Pattern.
func (a *Add) Render(pixels Frame, timeMS uint32) {
	// Draw and merge each pattern.
	a.buf.reset(len(pixels))
	for i := range pixels {
		pixels[i] = Color{}
	}
	for i := range a.Patterns {
		a.Patterns[i].Render(a.buf, timeMS)
		pixels.Add(a.buf)
	}
}

// Scale adapts a larger or smaller patterns to the Strip size
//
// This is useful to create smoother horizontal movement animation or to scale
// up/down images.
type Scale struct {
	Child SPattern
	// Defaults to Linear
	Interpolation Interpolation
	// A buffer of this len(buffer)*RatioMilli/1000 will be provided to Child and
	// will be scaled; 500 means smaller, 2000 is larger.
	//
	// Can be set to 0 when Child is a Frame. In this case it is stretched to the
	// strip size.
	RatioMilli SValue
	buf        Frame
}

// Render implements Pattern.
func (s *Scale) Render(pixels Frame, timeMS uint32) {
	if f, ok := s.Child.Pattern.(Frame); ok {
		if s.RatioMilli.Eval(timeMS, len(pixels)) == 0 {
			s.Interpolation.Scale(f, pixels)
			return
		}
	}
	v := MinMax32(s.RatioMilli.Eval(timeMS, len(pixels)), 1, 1000000)
	s.buf.reset((int(v)*len(pixels) + 500) / 1000)
	s.Child.Render(s.buf, timeMS)
	s.Interpolation.Scale(s.buf, pixels)
}
<|endoftext|>


<commit_before>package imgscale

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestMiddlewareConfigure(t *testing.T) {
	handler := Configure("../config/formats.json")
	assert.NotNil(t, handler, nil)
}
<commit_msg>Assertin implementation of interface Handler<commit_after>package imgscale

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestMiddlewareConfigure(t *testing.T) {
	handler := Configure("../config/formats.json")
	assert.NotNil(t, handler, nil)
	assert.Implements(t, new(Handler), handler)
}
<|endoftext|>


<commit_before>package sdm630

import (
	"fmt"
	"log"
	"regexp"
	"time"

	. "github.com/gonium/gosdm630/meters"
	"github.com/grid-x/modbus"
)

const (
	maxRetry = 5
)

const (
	_ = iota
	ModbusComset2400_8N1
	ModbusComset9600_8N1
	ModbusComset19200_8N1
	ModbusComset2400_8E1
	ModbusComset9600_8E1
	ModbusComset19200_8E1
)

type ModbusEngine struct {
	client  modbus.Client
	handler modbus.ClientHandler
	verbose bool
	status  *Status
}

// injectable logger for grid-x modbus implementation
type modbusLogger struct{}

func (l *modbusLogger) Printf(format string, v ...interface{}) {
	log.Printf(format, v...)
}

// NewRTUClientHandler creates a serial line  RTU/ASCII modbus handler
func NewRTUClientHandler(rtuDevice string, comset int, verbose bool) *modbus.RTUClientHandler {
	handler := modbus.NewRTUClientHandler(rtuDevice)

	handler.Parity = "N"
	handler.DataBits = 8
	handler.StopBits = 1

	switch comset {
	case ModbusComset2400_8N1:
		handler.BaudRate = 2400
	case ModbusComset9600_8N1:
		handler.BaudRate = 9600
	case ModbusComset19200_8N1:
		handler.BaudRate = 19200
	case ModbusComset2400_8E1:
		handler.BaudRate = 2400
		handler.Parity = "E"
	case ModbusComset9600_8E1:
		handler.BaudRate = 9600
		handler.Parity = "E"
	case ModbusComset19200_8E1:
		handler.BaudRate = 19200
		handler.Parity = "E"
	default:
		log.Fatal("Invalid communication set specified. See -h for help.")
	}

	handler.Timeout = 300 * time.Millisecond
	if verbose {
		logger := &modbusLogger{}
		handler.Logger = logger
		log.Printf("Connecting to RTU via %s, %d %d%s%d\r\n", rtuDevice,
			handler.BaudRate, handler.DataBits, handler.Parity,
			handler.StopBits)
	}

	return handler
}

// NewTCPClientHandler creates a TCP modbus handler
func NewTCPClientHandler(rtuDevice string, verbose bool) *modbus.TCPClientHandler {
	handler := modbus.NewTCPClientHandler(rtuDevice)
	handler.Timeout = 1 * time.Second
	handler.ProtocolRecoveryTimeout = 10 * time.Second
	handler.LinkRecoveryTimeout = 15 * time.Second

	if verbose {
		logger := &modbusLogger{}
		handler.Logger = logger
	}
	return handler
}

func NewModbusEngine(
	rtuDevice string,
	comset int,
	simulate bool,
	verbose bool,
	status *Status,
) *ModbusEngine {
	var handler modbus.ClientHandler
	var mbclient modbus.Client

	if simulate {
		log.Println("*** Simulation mode ***")
		mbclient = NewMockClient(20) // error rate for testing
	} else {
		// parse adapter string
		re := regexp.MustCompile(":[0-9]+$")
		if re.MatchString(rtuDevice) {
			// tcp connection
			handler = NewTCPClientHandler(rtuDevice, verbose)
		} else {
			// serial connection
			handler = NewRTUClientHandler(rtuDevice, comset, verbose)
		}

		mbclient = modbus.NewClient(handler)
		if err := handler.Connect(); err != nil {
			log.Fatal("Failed to connect: ", err)
		}
	}

	return &ModbusEngine{
		client:  mbclient,
		handler: handler,
		verbose: verbose,
		status:  status,
	}
}

func (q *ModbusEngine) setTimeout(timeout time.Duration) time.Duration {
	// update the slave id in the handler
	if handler, ok := q.handler.(*modbus.RTUClientHandler); ok {
		t := handler.Timeout
		handler.Timeout = timeout
		return t
	} else if handler, ok := q.handler.(*modbus.TCPClientHandler); ok {
		t := handler.Timeout
		handler.Timeout = timeout
		return t
	} else if handler != nil {
		log.Fatal("Unsupported modbus handler")
	}
	return 0
}

func (q *ModbusEngine) setSlave(deviceId uint8) {
	// update the slave id in the handler
	if handler, ok := q.handler.(*modbus.RTUClientHandler); ok {
		handler.SetSlave(deviceId)
	} else if handler, ok := q.handler.(*modbus.TCPClientHandler); ok {
		handler.SetSlave(deviceId)
	} else if handler != nil {
		log.Fatal("Unsupported modbus handler")
	}
}

// Reconnect refreshes underlying modbus TCP connection if connected via TCP
func (q *ModbusEngine) Reconnect() {
	if handler, ok := q.handler.(*modbus.TCPClientHandler); ok {
		handler.Close()
	}
}

// Query modbus registers
func (q *ModbusEngine) Query(snip QuerySnip) (retval []byte, err error) {
	q.setSlave(snip.DeviceId)
	q.status.IncreaseRequestCounter()

	if snip.ReadLen <= 0 {
		log.Fatalf("Invalid meter operation %v.", snip)
	}

	switch snip.FuncCode {
	case ReadInputReg:
		retval, err = q.client.ReadInputRegisters(snip.OpCode, snip.ReadLen)
	case ReadHoldingReg:
		retval, err = q.client.ReadHoldingRegisters(snip.OpCode, snip.ReadLen)
	default:
		log.Fatalf("Unknown function code %d - cannot query device.",
			snip.FuncCode)
	}

	if err != nil && q.verbose {
		log.Printf("Device %d failed to retrieve opcode 0x%x, error was: %s\r\n", snip.DeviceId, snip.OpCode, err.Error())
	}

	return retval, err
}

// Transform converts raw query result into one or more Readings
func (q *ModbusEngine) Transform(snip QuerySnip, bytes []byte) []QuerySnip {
	now := time.Now()

	if snip.Splitter != nil {
		// block reading - needs splitting
		snips := snip.Splitter(bytes)
		res := make([]QuerySnip, len(snips))

		for idx, sr := range snips {
			splitSnip := QuerySnip{
				DeviceId: snip.DeviceId,
				Operation: Operation{
					OpCode:   sr.OpCode,
					IEC61850: sr.IEC61850,
				},
				Value:         sr.Value,
				ReadTimestamp: now,
			}
			res[idx] = splitSnip
		}

		return res
	}

	// single reading
	if snip.Transform == nil {
		log.Fatalf("Snip transformation not defined: %v", snip)
	}

	// convert bytes to value
	snip.Value = snip.Transform(bytes)
	snip.ReadTimestamp = now

	return []QuerySnip{snip}
}

// Run consumes device operations and produces operation results
func (q *ModbusEngine) Run(
	snipChannel QuerySnipChannel,
	controlChannel ControlSnipChannel,
	outputChannel QuerySnipChannel,
) {
	defer close(outputChannel)
	defer close(controlChannel)

	var previousDeviceId uint8
	for snip := range snipChannel {
		// The SDM devices need to have a little pause between querying
		// different devices.
		if previousDeviceId != snip.DeviceId {
			time.Sleep(time.Duration(100) * time.Millisecond)
			previousDeviceId = snip.DeviceId
		}

		var bytes []byte
		var err error
		var controlSnip ControlSnip

		for retry := 0; retry < maxRetry; retry++ {
			bytes, err = q.Query(snip)
			if err == nil {
				break
			}

			q.status.IncreaseReconnectCounter()
			log.Printf("Device %d failed to respond (%d/%d)",
				snip.DeviceId, retry+1, maxRetry)
			time.Sleep(time.Duration(100) * time.Millisecond)
		}

		if err == nil {
			// success
			snips := q.Transform(snip, bytes)
			for _, snip := range snips {
				if q.verbose {
					log.Printf("Device %d - %s: %.2f\n", snip.DeviceId, snip.IEC61850.String(), snip.Value)
				}
				outputChannel <- snip
			}

			// signal ok
			controlSnip = ControlSnip{
				Type:     CONTROLSNIP_OK,
				Message:  "OK",
				DeviceId: snip.DeviceId,
			}
		} else {
			// signal error
			controlSnip = ControlSnip{
				Type:     CONTROLSNIP_ERROR,
				Message:  fmt.Sprintf("Device %d did not respond.", snip.DeviceId),
				DeviceId: snip.DeviceId,
			}
		}

		controlChannel <- controlSnip
	}
}

func (q *ModbusEngine) Scan() {
	type DeviceInfo struct {
		DeviceId  uint8
		MeterType string
	}

	var deviceId uint8
	deviceList := make([]DeviceInfo, 0)
	oldtimeout := q.setTimeout(50 * time.Millisecond)
	log.Printf("Starting bus scan")

SCAN:
	// loop over all valid slave adresses
	for deviceId = 1; deviceId <= 247; deviceId++ {
		// give the bus some time to recover before querying the next device
		time.Sleep(time.Duration(40) * time.Millisecond)

		for _, factory := range Producers {
			producer := factory()
			operation := producer.Probe()
			snip := NewQuerySnip(deviceId, operation)

			value, err := q.Query(snip)
			if err == nil {
				log.Printf("Device %d: %s type device found, %s: %.2f\r\n",
					deviceId,
					producer.Type(),
					snip.IEC61850,
					snip.Transform(value))
				dev := DeviceInfo{
					DeviceId:  deviceId,
					MeterType: producer.Type(),
				}
				deviceList = append(deviceList, dev)
				continue SCAN
			}
		}

		log.Printf("Device %d: n/a\r\n", deviceId)
	}

	// restore timeout to old value
	q.setTimeout(oldtimeout)

	log.Printf("Found %d active devices:\r\n", len(deviceList))
	for _, device := range deviceList {
		log.Printf("* slave address %d: type %s\r\n", device.DeviceId,
			device.MeterType)
	}
	log.Println("WARNING: This lists only the devices that responded to " +
		"a known probe request. Devices with different " +
		"function code definitions might not be detected.")
}
<commit_msg>Handle NaN values returned by ABB meters<commit_after>package sdm630

import (
	"fmt"
	"log"
	"math"
	"regexp"
	"time"

	. "github.com/gonium/gosdm630/meters"
	"github.com/grid-x/modbus"
)

const (
	maxRetry = 3
)

const (
	_ = iota
	ModbusComset2400_8N1
	ModbusComset9600_8N1
	ModbusComset19200_8N1
	ModbusComset2400_8E1
	ModbusComset9600_8E1
	ModbusComset19200_8E1
)

type ModbusEngine struct {
	client  modbus.Client
	handler modbus.ClientHandler
	verbose bool
	status  *Status
}

// injectable logger for grid-x modbus implementation
type modbusLogger struct{}

func (l *modbusLogger) Printf(format string, v ...interface{}) {
	log.Printf(format, v...)
}

// NewRTUClientHandler creates a serial line  RTU/ASCII modbus handler
func NewRTUClientHandler(rtuDevice string, comset int, verbose bool) *modbus.RTUClientHandler {
	handler := modbus.NewRTUClientHandler(rtuDevice)

	handler.Parity = "N"
	handler.DataBits = 8
	handler.StopBits = 1

	switch comset {
	case ModbusComset2400_8N1:
		handler.BaudRate = 2400
	case ModbusComset9600_8N1:
		handler.BaudRate = 9600
	case ModbusComset19200_8N1:
		handler.BaudRate = 19200
	case ModbusComset2400_8E1:
		handler.BaudRate = 2400
		handler.Parity = "E"
	case ModbusComset9600_8E1:
		handler.BaudRate = 9600
		handler.Parity = "E"
	case ModbusComset19200_8E1:
		handler.BaudRate = 19200
		handler.Parity = "E"
	default:
		log.Fatal("Invalid communication set specified. See -h for help.")
	}

	handler.Timeout = 300 * time.Millisecond
	if verbose {
		logger := &modbusLogger{}
		handler.Logger = logger
		log.Printf("Connecting to RTU via %s, %d %d%s%d\r\n", rtuDevice,
			handler.BaudRate, handler.DataBits, handler.Parity,
			handler.StopBits)
	}

	return handler
}

// NewTCPClientHandler creates a TCP modbus handler
func NewTCPClientHandler(rtuDevice string, verbose bool) *modbus.TCPClientHandler {
	handler := modbus.NewTCPClientHandler(rtuDevice)
	handler.Timeout = 1 * time.Second
	handler.ProtocolRecoveryTimeout = 10 * time.Second
	handler.LinkRecoveryTimeout = 15 * time.Second

	if verbose {
		logger := &modbusLogger{}
		handler.Logger = logger
	}
	return handler
}

func NewModbusEngine(
	rtuDevice string,
	comset int,
	simulate bool,
	verbose bool,
	status *Status,
) *ModbusEngine {
	var handler modbus.ClientHandler
	var mbclient modbus.Client

	if simulate {
		log.Println("*** Simulation mode ***")
		mbclient = NewMockClient(20) // error rate for testing
	} else {
		// parse adapter string
		re := regexp.MustCompile(":[0-9]+$")
		if re.MatchString(rtuDevice) {
			// tcp connection
			handler = NewTCPClientHandler(rtuDevice, verbose)
		} else {
			// serial connection
			handler = NewRTUClientHandler(rtuDevice, comset, verbose)
		}

		mbclient = modbus.NewClient(handler)
		if err := handler.Connect(); err != nil {
			log.Fatal("Failed to connect: ", err)
		}
	}

	return &ModbusEngine{
		client:  mbclient,
		handler: handler,
		verbose: verbose,
		status:  status,
	}
}

func (q *ModbusEngine) setTimeout(timeout time.Duration) time.Duration {
	// update the slave id in the handler
	if handler, ok := q.handler.(*modbus.RTUClientHandler); ok {
		t := handler.Timeout
		handler.Timeout = timeout
		return t
	} else if handler, ok := q.handler.(*modbus.TCPClientHandler); ok {
		t := handler.Timeout
		handler.Timeout = timeout
		return t
	} else if handler != nil {
		log.Fatal("Unsupported modbus handler")
	}
	return 0
}

func (q *ModbusEngine) setSlave(deviceId uint8) {
	// update the slave id in the handler
	if handler, ok := q.handler.(*modbus.RTUClientHandler); ok {
		handler.SetSlave(deviceId)
	} else if handler, ok := q.handler.(*modbus.TCPClientHandler); ok {
		handler.SetSlave(deviceId)
	} else if handler != nil {
		log.Fatal("Unsupported modbus handler")
	}
}

// Reconnect refreshes underlying modbus TCP connection if connected via TCP
func (q *ModbusEngine) Reconnect() {
	if handler, ok := q.handler.(*modbus.TCPClientHandler); ok {
		handler.Close()
	}
}

// Query modbus registers
func (q *ModbusEngine) Query(snip QuerySnip) (retval []byte, err error) {
	q.setSlave(snip.DeviceId)
	q.status.IncreaseRequestCounter()

	if snip.ReadLen <= 0 {
		log.Fatalf("Invalid meter operation %v.", snip)
	}

	switch snip.FuncCode {
	case ReadInputReg:
		retval, err = q.client.ReadInputRegisters(snip.OpCode, snip.ReadLen)
	case ReadHoldingReg:
		retval, err = q.client.ReadHoldingRegisters(snip.OpCode, snip.ReadLen)
	default:
		log.Fatalf("Unknown function code %d - cannot query device.",
			snip.FuncCode)
	}

	if err != nil && q.verbose {
		log.Printf("Device %d failed to retrieve opcode 0x%x, error was: %s\r\n", snip.DeviceId, snip.OpCode, err.Error())
	}

	return retval, err
}

// Transform converts raw query result into one or more Readings
func (q *ModbusEngine) Transform(snip QuerySnip, bytes []byte) []QuerySnip {
	now := time.Now()

	if snip.Splitter != nil {
		// block reading - needs splitting
		snips := snip.Splitter(bytes)
		res := make([]QuerySnip, len(snips))

		for idx, sr := range snips {
			splitSnip := QuerySnip{
				DeviceId: snip.DeviceId,
				Operation: Operation{
					OpCode:   sr.OpCode,
					IEC61850: sr.IEC61850,
				},
				Value:         sr.Value,
				ReadTimestamp: now,
			}
			res[idx] = splitSnip
		}

		return res
	}

	// single reading
	if snip.Transform == nil {
		log.Fatalf("Snip transformation not defined: %v", snip)
	}

	// convert bytes to value
	snip.Value = snip.Transform(bytes)
	snip.ReadTimestamp = now

	// check if result is valid
	if math.IsNaN(snip.Value) {
		return []QuerySnip{}
	}

	return []QuerySnip{snip}
}

// Run consumes device operations and produces operation results
func (q *ModbusEngine) Run(
	snipChannel QuerySnipChannel,
	controlChannel ControlSnipChannel,
	outputChannel QuerySnipChannel,
) {
	defer close(outputChannel)
	defer close(controlChannel)

	var previousDeviceId uint8
	for snip := range snipChannel {
		// The SDM devices need to have a little pause between querying
		// different devices.
		if previousDeviceId != snip.DeviceId {
			time.Sleep(time.Duration(100) * time.Millisecond)
			previousDeviceId = snip.DeviceId
		}

		var bytes []byte
		var err error
		var controlSnip ControlSnip

		for retry := 0; retry < maxRetry; retry++ {
			bytes, err = q.Query(snip)
			if err == nil {
				break
			}

			q.status.IncreaseReconnectCounter()
			log.Printf("Device %d failed to respond (%d/%d)",
				snip.DeviceId, retry+1, maxRetry)
			time.Sleep(time.Duration(100) * time.Millisecond)
		}

		if err == nil {
			// success
			snips := q.Transform(snip, bytes)
			for _, snip := range snips {
				if q.verbose {
					log.Printf("Device %d - %s: %.2f\n", snip.DeviceId, snip.IEC61850.String(), snip.Value)
				}
				outputChannel <- snip
			}

			// signal ok
			controlSnip = ControlSnip{
				Type:     CONTROLSNIP_OK,
				Message:  "OK",
				DeviceId: snip.DeviceId,
			}
		} else {
			// signal error
			controlSnip = ControlSnip{
				Type:     CONTROLSNIP_ERROR,
				Message:  fmt.Sprintf("Device %d did not respond.", snip.DeviceId),
				DeviceId: snip.DeviceId,
			}
		}

		controlChannel <- controlSnip
	}
}

func (q *ModbusEngine) Scan() {
	type DeviceInfo struct {
		DeviceId  uint8
		MeterType string
	}

	var deviceId uint8
	deviceList := make([]DeviceInfo, 0)
	oldtimeout := q.setTimeout(50 * time.Millisecond)
	log.Printf("Starting bus scan")

SCAN:
	// loop over all valid slave adresses
	for deviceId = 1; deviceId <= 247; deviceId++ {
		// give the bus some time to recover before querying the next device
		time.Sleep(time.Duration(40) * time.Millisecond)

		for _, factory := range Producers {
			producer := factory()
			operation := producer.Probe()
			snip := NewQuerySnip(deviceId, operation)

			value, err := q.Query(snip)
			if err == nil {
				log.Printf("Device %d: %s type device found, %s: %.2f\r\n",
					deviceId,
					producer.Type(),
					snip.IEC61850,
					snip.Transform(value))
				dev := DeviceInfo{
					DeviceId:  deviceId,
					MeterType: producer.Type(),
				}
				deviceList = append(deviceList, dev)
				continue SCAN
			}
		}

		log.Printf("Device %d: n/a\r\n", deviceId)
	}

	// restore timeout to old value
	q.setTimeout(oldtimeout)

	log.Printf("Found %d active devices:\r\n", len(deviceList))
	for _, device := range deviceList {
		log.Printf("* slave address %d: type %s\r\n", device.DeviceId,
			device.MeterType)
	}
	log.Println("WARNING: This lists only the devices that responded to " +
		"a known probe request. Devices with different " +
		"function code definitions might not be detected.")
}
<|endoftext|>


<commit_before>// Copyright 2018 Google LLC
//
// 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 qemu provides implementations of a net.Conn and net.Listener backed by qemu_pipe
package qemu

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"errors"
	"io"
	"log"
	"net"
	"os"
	"strings"
	"sync"
	"syscall"
	"time"
)

const (
	qemuDriver = "/dev/qemu_pipe"
	qemuSvc    = "pipe:unix:"
	ioErrMsg   = "input/output error"
	rdyMsg     = "rdy"

	// We pick a sufficiently large buffer size to avoid hitting an underlying bug on the emulator.
	// See https://issuetracker.google.com/issues/115894209 for context.
	buffSize = 1 << 16
)

var errClosed = errors.New("error connection closed")
var errNotImplemented = errors.New("error not implemented")

type qemuAddr string

// Network returns the network type of the connection
func (a qemuAddr) Network() string {
	return qemuDriver
}

// String returns the description of the connection
func (a qemuAddr) String() string {
	return string(a)
}

// Conn implements the net.Conn interface on top of a qemu_pipe
type Conn struct {
	// Backed by qemu_pipe
	wb *bufio.Writer
	rb *bufio.Reader
	cl io.Closer

	// Dummy addr
	addr net.Addr

	// Bytes remaining in the connection buffer
	// it would be tempting to simplify the code and use
	// an intermediate buffer to hold overflow buffer
	// but bytes.Buffer does not shrink which causes memory usage
	// to blow up
	left int

	closedReads  bool
	closedWrites bool
	closed       bool

	readsCloseChan  chan struct{}
	writesCloseChan chan struct{}

	readLock  *sync.Mutex
	writeLock *sync.Mutex
	closeLock *sync.Mutex
}

// Read reads from from the Conn connection.
// Note that each message is prepended with the size, so we need to keep
// track of how many bytes we need to read across Read calls
func (q *Conn) Read(b []byte) (int, error) {
	q.readLock.Lock()
	defer q.readLock.Unlock()

	// Normally this could be done by just checking status != io.EOF, however
	// a read on /dev/qemu-pipe associated with a closed connection will return
	// EIO with the string "input/output error". Since errno isn't visible to us,
	// we check the error message and ensure the target string is not present.
	n, err := q.read(b)
	if err != nil {
		if strings.Contains(err.Error(), ioErrMsg) {
			return n, io.EOF
		}
	}
	return n, err

}

func (q *Conn) read(b []byte) (int, error) {
	if q.closedReads {
		return 0, errClosed
	}

	toRead := q.left
	if toRead > len(b) {
		toRead = len(b)
	}

	// read leftovers from previous reads before trying to read the size
	if toRead > 0 {
		n, err := io.ReadFull(q.rb, b[:toRead])
		if err != nil {
			return n, err
		}
		q.left = q.left - n

		// b might be bigger than remaining bytes but conn might be empty and
		// we dont want to block on size read, so return early.
		return n, nil
	}

	var recd uint32
	if err := binary.Read(q.rb, binary.LittleEndian, &recd); err != nil {
		return 0, err
	}

	// The other side has signaled an EOF. Close connection for reads, we might have leftover writes
	if recd == 0 {
		return 0, io.EOF
	}

	toRead = int(recd)
	if toRead > len(b) {
		toRead = len(b)
	}

	n, err := io.ReadFull(q.rb, b[:toRead])
	if err != nil {
		return n, err
	}
	q.left = int(recd) - toRead
	return n, nil
}

// Write writes the contents of b to the underlying connection
func (q *Conn) Write(b []byte) (int, error) {
	q.writeLock.Lock()
	defer q.writeLock.Unlock()

	if q.closedWrites {
		return 0, errClosed
	}

	// Prepend the message with size
	if err := binary.Write(q.wb, binary.LittleEndian, uint32(len(b))); err != nil {
		return 0, err
	}
	n, err := q.wb.Write(b)
	if err != nil {
		return n, err
	}
	return n, q.wb.Flush()
}

// Close closes the connection
func (q *Conn) Close() error {
	q.closeLock.Lock()
	defer q.closeLock.Unlock()

	if q.closed {
		return errClosed
	}

	q.closed = true

	q.CloseWrite()
	q.CloseRead()
	q.cl.Close()
	return nil
}

// CloseRead closes the read side of the connection
func (q *Conn) CloseRead() error {
	q.readLock.Lock()
	defer q.readLock.Unlock()

	if q.closedReads {
		return errClosed
	}
	q.closedReads = true
	close(q.readsCloseChan)
	return nil
}

// CloseWrite closes the write side of the connection
func (q *Conn) CloseWrite() error {
	q.writeLock.Lock()
	defer q.writeLock.Unlock()

	if q.closedWrites == true {
		return errClosed
	}
	q.closedWrites = true

	err := q.sendClose()
	close(q.writesCloseChan)
	return err
}

func (q *Conn) sendClose() error {
	if err := binary.Write(q.wb, binary.LittleEndian, uint32(0)); err != nil {
		return err
	}
	if _, err := q.wb.Write([]byte{}); err != nil {
		return err
	}
	return q.wb.Flush()
}

// LocalAddr returns the qemu address
func (q *Conn) LocalAddr() net.Addr {
	return q.addr
}

// RemoteAddr returns the qemu address
func (q *Conn) RemoteAddr() net.Addr {
	return q.addr
}

// SetDeadline sets the connection deadline
func (q *Conn) SetDeadline(t time.Time) error {
	return errNotImplemented
}

// SetReadDeadline sets the read deadline
func (q *Conn) SetReadDeadline(t time.Time) error {
	return errNotImplemented
}

// SetWriteDeadline sets the write deadline
func (q *Conn) SetWriteDeadline(t time.Time) error {
	return errNotImplemented
}

// closeConn waits for the read end and the write end of th connection
// to be closed and then closes the underlying connection
func (q *Conn) closeConn() {
	// remember the ABC
	<-q.readsCloseChan
	<-q.writesCloseChan
	q.cl.Close()
}

func makeConn(conn io.ReadWriteCloser) *Conn {
	return &Conn{
		wb:              bufio.NewWriterSize(conn, buffSize),
		rb:              bufio.NewReaderSize(conn, buffSize),
		cl:              conn,
		addr:            qemuAddr(""),
		readsCloseChan:  make(chan struct{}),
		writesCloseChan: make(chan struct{}),
		readLock:        &sync.Mutex{},
		writeLock:       &sync.Mutex{},
		closeLock:       &sync.Mutex{},
	}
}

// ConnBuilder implements a qemu connection builder. It wraps around listener
// listening on a qemu pipe. It accepts connectsion and sync with the client
// before returning
type ConnBuilder struct {
	net.Listener
}

// Accept will connect to the guest and return the connection.
func (b *ConnBuilder) Accept() (net.Conn, error) {
	for {
		conn, err := b.Listener.Accept()
		if err != nil {
			return nil, err
		}

		// sync with the server
		rdy := []byte(rdyMsg)
		if _, err := conn.Write(rdy); err != nil {
			conn.Close()
			continue
		}
		if _, err := io.ReadFull(conn, rdy); err != nil {
			conn.Close()
			continue
		}
		if !bytes.Equal([]byte(rdyMsg), rdy) {
			conn.Close()
			continue
		}

		q := makeConn(conn)

		go q.closeConn()
		return q, nil
	}
}

// MakeConnBuilder creates a new ConnBuilder struct
func MakeConnBuilder(emuDir, socket string) (*ConnBuilder, error) {
	wd, err := os.Getwd()
	if err != nil {
		return nil, err
	}

	if err := os.Chdir(emuDir); err != nil {
		return nil, err
	}

	if err := os.Remove(socket); err != nil && !os.IsNotExist(err) {
		return nil, err
	}
	lis, err := net.Listen("unix", socket)
	if err != nil {
		return nil, err
	}

	if err := os.Chdir(wd); err != nil {
		return nil, err
	}

	return &ConnBuilder{Listener: lis}, nil
}

// PipeConnBuilder implements a qemu connection builder. It wraps around listener
// listening on a qemu pipe. It accepts connectsion and sync with the client
// before returning
type PipeConnBuilder struct {
	net.Listener
}

// Accept will connect to the guest and return the connection.
func (b *PipeConnBuilder) Accept() (net.Conn, error) {
	for {
		conn, err := b.Listener.Accept()
		if err != nil {
			return nil, err
		}

		// sync with the server
		rdy := []byte(rdyMsg)
		if _, err := io.ReadFull(conn, rdy); err != nil {
			conn.Close()
			continue
		}
		if !bytes.Equal([]byte(rdyMsg), rdy) {
			conn.Close()
			continue
		}
		if _, err := conn.Write(rdy); err != nil {
			conn.Close()
			continue
		}

		q := makeConn(conn)

		go q.closeConn()
		return q, nil
	}
}

// MakePipeConBuilder returns a PipeConuilder using pipe.
func MakePipeConnBuilder(pipe *Pipe) *PipeConnBuilder {
	return &PipeConnBuilder{Listener: pipe}
}

// QemuConn implements a ReadWriteInterface with a qemu pipe.
type QemuConn struct {
	io.ReadWriteCloser
}

// MakeQemuConn create new qemu control socket backed by a qemu pipe file.
func MakeQemuConn(file *os.File) *QemuConn {
	return &QemuConn{ReadWriteCloser: file}
}

// LocalAddr returns the qemu address
func (q *QemuConn) LocalAddr() net.Addr {
	return &net.UnixAddr{Name: "qemu-pipe"}
}

// RemoteAddr returns the qemu address
func (q *QemuConn) RemoteAddr() net.Addr {
	return &net.UnixAddr{Name: "qemu-pipe"}
}

// SetDeadline sets the connection deadline
func (q *QemuConn) SetDeadline(t time.Time) error {
	return errNotImplemented
}

// SetReadDeadline sets the read deadline
func (q *QemuConn) SetReadDeadline(t time.Time) error {
	return errNotImplemented
}

// SetWriteDeadline sets the write deadline
func (q *QemuConn) SetWriteDeadline(t time.Time) error {
	return errNotImplemented
}

func openQemuDevBlocking() (*os.File, error) {
	// Open device manually in blocking mode. Qemu pipes don't support polling io.
	r, err := syscall.Open(qemuDriver, os.O_RDWR|syscall.O_CLOEXEC, 0600)
	if err != nil {
		return nil, err
	}

	return os.NewFile(uintptr(r), qemuDriver), nil
}

// Pipe implements a net.Listener on top of a guest qemu pipe
type Pipe struct {
	socketName string
	closed     bool
}

// Accept creates a new net.Conn backed by a qemu_pipe connetion
func (q *Pipe) Accept() (net.Conn, error) {

	log.Println("Creating new conn")
	if q.closed {
		return nil, errClosed
	}

	// Each new file descriptor we open will create a new connection
	// We need to wait on the host to be ready:
	// 1) poll the qemu_pipe driver with the desiered socket name
	// 2) wait until the client is read to send/recv, we do this waiting until we read a rdy message
	var conn *os.File
	var err error
	br := false
	for {
		if conn != nil {
			// Got an error, close connection and try again
			conn.Close()
			time.Sleep(20 * time.Millisecond)
		}

		conn, err = openQemuDevBlocking()
		if err != nil {
			return nil, err
		}
		log.Println("File opened")

		svcName := qemuSvc + q.socketName
		buff := make([]byte, len(svcName)+1)
		copy(buff, svcName)

		// retry loop to wait until we can start the service on the qemu_pipe
		for {
			log.Println("Writing service")
			written, err := conn.Write(buff)
			if err != nil {
				// The host has not opened the socket. Sleep and try again
				conn.Close()
				time.Sleep(100 * time.Millisecond)
				break
			}
			buff = buff[written:]
			if len(buff) == 0 {
				log.Println("Wrote service")
				br = true
				break
			}
		}

		if br {
			return MakeQemuConn(conn), nil
		}
	}

}

// Close closes the connection
func (q *Pipe) Close() error {
	q.closed = true
	return nil
}

// Addr returns the connection address
func (q *Pipe) Addr() net.Addr {
	return qemuAddr(q.socketName)
}

// MakePipe will return a new net.Listener
// backed by a qemu pipe. Qemu pipes are implemented as virtual
// devices. To get a handle an open("/dev/qemu_pipe") is issued.
// The virtual driver keeps a map of file descriptors to available
// services. In this case we open a unix socket service and return that.
func MakePipe(socketName string) (*Pipe, error) {
	if _, err := os.Stat(qemuDriver); err != nil {
		return nil, err
	}

	return &Pipe{socketName: socketName}, nil
}
<commit_msg>Fix lint errors<commit_after>// Copyright 2018 Google LLC
//
// 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 qemu provides implementations of a net.Conn and net.Listener backed by qemu_pipe
package qemu

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"errors"
	"io"
	"log"
	"net"
	"os"
	"strings"
	"sync"
	"syscall"
	"time"
)

const (
	qemuDriver = "/dev/qemu_pipe"
	qemuSvc    = "pipe:unix:"
	ioErrMsg   = "input/output error"
	rdyMsg     = "rdy"

	// We pick a sufficiently large buffer size to avoid hitting an underlying bug on the emulator.
	// See https://issuetracker.google.com/issues/115894209 for context.
	buffSize = 1 << 16
)

var errClosed = errors.New("error connection closed")
var errNotImplemented = errors.New("error not implemented")

type qemuAddr string

// Network returns the network type of the connection
func (a qemuAddr) Network() string {
	return qemuDriver
}

// String returns the description of the connection
func (a qemuAddr) String() string {
	return string(a)
}

// Conn implements the net.Conn interface on top of a qemu_pipe
type Conn struct {
	// Backed by qemu_pipe
	wb *bufio.Writer
	rb *bufio.Reader
	cl io.Closer

	// Dummy addr
	addr net.Addr

	// Bytes remaining in the connection buffer
	// it would be tempting to simplify the code and use
	// an intermediate buffer to hold overflow buffer
	// but bytes.Buffer does not shrink which causes memory usage
	// to blow up
	left int

	closedReads  bool
	closedWrites bool
	closed       bool

	readsCloseChan  chan struct{}
	writesCloseChan chan struct{}

	readLock  *sync.Mutex
	writeLock *sync.Mutex
	closeLock *sync.Mutex
}

// Read reads from from the Conn connection.
// Note that each message is prepended with the size, so we need to keep
// track of how many bytes we need to read across Read calls
func (q *Conn) Read(b []byte) (int, error) {
	q.readLock.Lock()
	defer q.readLock.Unlock()

	// Normally this could be done by just checking status != io.EOF, however
	// a read on /dev/qemu-pipe associated with a closed connection will return
	// EIO with the string "input/output error". Since errno isn't visible to us,
	// we check the error message and ensure the target string is not present.
	n, err := q.read(b)
	if err != nil {
		if strings.Contains(err.Error(), ioErrMsg) {
			return n, io.EOF
		}
	}
	return n, err

}

func (q *Conn) read(b []byte) (int, error) {
	if q.closedReads {
		return 0, errClosed
	}

	toRead := q.left
	if toRead > len(b) {
		toRead = len(b)
	}

	// read leftovers from previous reads before trying to read the size
	if toRead > 0 {
		n, err := io.ReadFull(q.rb, b[:toRead])
		if err != nil {
			return n, err
		}
		q.left = q.left - n

		// b might be bigger than remaining bytes but conn might be empty and
		// we dont want to block on size read, so return early.
		return n, nil
	}

	var recd uint32
	if err := binary.Read(q.rb, binary.LittleEndian, &recd); err != nil {
		return 0, err
	}

	// The other side has signaled an EOF. Close connection for reads, we might have leftover writes
	if recd == 0 {
		return 0, io.EOF
	}

	toRead = int(recd)
	if toRead > len(b) {
		toRead = len(b)
	}

	n, err := io.ReadFull(q.rb, b[:toRead])
	if err != nil {
		return n, err
	}
	q.left = int(recd) - toRead
	return n, nil
}

// Write writes the contents of b to the underlying connection
func (q *Conn) Write(b []byte) (int, error) {
	q.writeLock.Lock()
	defer q.writeLock.Unlock()

	if q.closedWrites {
		return 0, errClosed
	}

	// Prepend the message with size
	if err := binary.Write(q.wb, binary.LittleEndian, uint32(len(b))); err != nil {
		return 0, err
	}
	n, err := q.wb.Write(b)
	if err != nil {
		return n, err
	}
	return n, q.wb.Flush()
}

// Close closes the connection
func (q *Conn) Close() error {
	q.closeLock.Lock()
	defer q.closeLock.Unlock()

	if q.closed {
		return errClosed
	}

	q.closed = true

	q.CloseWrite()
	q.CloseRead()
	q.cl.Close()
	return nil
}

// CloseRead closes the read side of the connection
func (q *Conn) CloseRead() error {
	q.readLock.Lock()
	defer q.readLock.Unlock()

	if q.closedReads {
		return errClosed
	}
	q.closedReads = true
	close(q.readsCloseChan)
	return nil
}

// CloseWrite closes the write side of the connection
func (q *Conn) CloseWrite() error {
	q.writeLock.Lock()
	defer q.writeLock.Unlock()

	if q.closedWrites == true {
		return errClosed
	}
	q.closedWrites = true

	err := q.sendClose()
	close(q.writesCloseChan)
	return err
}

func (q *Conn) sendClose() error {
	if err := binary.Write(q.wb, binary.LittleEndian, uint32(0)); err != nil {
		return err
	}
	if _, err := q.wb.Write([]byte{}); err != nil {
		return err
	}
	return q.wb.Flush()
}

// LocalAddr returns the qemu address
func (q *Conn) LocalAddr() net.Addr {
	return q.addr
}

// RemoteAddr returns the qemu address
func (q *Conn) RemoteAddr() net.Addr {
	return q.addr
}

// SetDeadline sets the connection deadline
func (q *Conn) SetDeadline(t time.Time) error {
	return errNotImplemented
}

// SetReadDeadline sets the read deadline
func (q *Conn) SetReadDeadline(t time.Time) error {
	return errNotImplemented
}

// SetWriteDeadline sets the write deadline
func (q *Conn) SetWriteDeadline(t time.Time) error {
	return errNotImplemented
}

// closeConn waits for the read end and the write end of th connection
// to be closed and then closes the underlying connection
func (q *Conn) closeConn() {
	// remember the ABC
	<-q.readsCloseChan
	<-q.writesCloseChan
	q.cl.Close()
}

func makeConn(conn io.ReadWriteCloser) *Conn {
	return &Conn{
		wb:              bufio.NewWriterSize(conn, buffSize),
		rb:              bufio.NewReaderSize(conn, buffSize),
		cl:              conn,
		addr:            qemuAddr(""),
		readsCloseChan:  make(chan struct{}),
		writesCloseChan: make(chan struct{}),
		readLock:        &sync.Mutex{},
		writeLock:       &sync.Mutex{},
		closeLock:       &sync.Mutex{},
	}
}

// ConnBuilder implements a qemu connection builder. It wraps around listener
// listening on a qemu pipe. It accepts connectsion and sync with the client
// before returning
type ConnBuilder struct {
	net.Listener
}

// Accept will connect to the guest and return the connection.
func (b *ConnBuilder) Accept() (net.Conn, error) {
	for {
		conn, err := b.Listener.Accept()
		if err != nil {
			return nil, err
		}

		// sync with the server
		rdy := []byte(rdyMsg)
		if _, err := conn.Write(rdy); err != nil {
			conn.Close()
			continue
		}
		if _, err := io.ReadFull(conn, rdy); err != nil {
			conn.Close()
			continue
		}
		if !bytes.Equal([]byte(rdyMsg), rdy) {
			conn.Close()
			continue
		}

		q := makeConn(conn)

		go q.closeConn()
		return q, nil
	}
}

// MakeConnBuilder creates a new ConnBuilder struct
func MakeConnBuilder(emuDir, socket string) (*ConnBuilder, error) {
	wd, err := os.Getwd()
	if err != nil {
		return nil, err
	}

	if err := os.Chdir(emuDir); err != nil {
		return nil, err
	}

	if err := os.Remove(socket); err != nil && !os.IsNotExist(err) {
		return nil, err
	}
	lis, err := net.Listen("unix", socket)
	if err != nil {
		return nil, err
	}

	if err := os.Chdir(wd); err != nil {
		return nil, err
	}

	return &ConnBuilder{Listener: lis}, nil
}

// PipeConnBuilder implements a qemu connection builder. It wraps around listener
// listening on a qemu pipe. It accepts connectsion and sync with the client
// before returning
type PipeConnBuilder struct {
	net.Listener
}

// Accept will connect to the guest and return the connection.
func (b *PipeConnBuilder) Accept() (net.Conn, error) {
	for {
		conn, err := b.Listener.Accept()
		if err != nil {
			return nil, err
		}

		// sync with the server
		rdy := []byte(rdyMsg)
		if _, err := io.ReadFull(conn, rdy); err != nil {
			conn.Close()
			continue
		}
		if !bytes.Equal([]byte(rdyMsg), rdy) {
			conn.Close()
			continue
		}
		if _, err := conn.Write(rdy); err != nil {
			conn.Close()
			continue
		}

		q := makeConn(conn)

		go q.closeConn()
		return q, nil
	}
}

// MakePipeConnBuilder returns a PipeConuilder using pipe.
func MakePipeConnBuilder(pipe *Pipe) *PipeConnBuilder {
	return &PipeConnBuilder{Listener: pipe}
}

// QemuConn implements a ReadWriteInterface with a qemu pipe.
type QemuConn struct {
	io.ReadWriteCloser
}

// MakeQemuConn create new qemu control socket backed by a qemu pipe file.
func MakeQemuConn(file *os.File) *QemuConn {
	return &QemuConn{ReadWriteCloser: file}
}

// LocalAddr returns the qemu address
func (q *QemuConn) LocalAddr() net.Addr {
	return &net.UnixAddr{Name: "qemu-pipe"}
}

// RemoteAddr returns the qemu address
func (q *QemuConn) RemoteAddr() net.Addr {
	return &net.UnixAddr{Name: "qemu-pipe"}
}

// SetDeadline sets the connection deadline
func (q *QemuConn) SetDeadline(t time.Time) error {
	return errNotImplemented
}

// SetReadDeadline sets the read deadline
func (q *QemuConn) SetReadDeadline(t time.Time) error {
	return errNotImplemented
}

// SetWriteDeadline sets the write deadline
func (q *QemuConn) SetWriteDeadline(t time.Time) error {
	return errNotImplemented
}

func openQemuDevBlocking() (*os.File, error) {
	// Open device manually in blocking mode. Qemu pipes don't support polling io.
	r, err := syscall.Open(qemuDriver, os.O_RDWR|syscall.O_CLOEXEC, 0600)
	if err != nil {
		return nil, err
	}

	return os.NewFile(uintptr(r), qemuDriver), nil
}

// Pipe implements a net.Listener on top of a guest qemu pipe
type Pipe struct {
	socketName string
	closed     bool
}

// Accept creates a new net.Conn backed by a qemu_pipe connetion
func (q *Pipe) Accept() (net.Conn, error) {

	log.Println("Creating new conn")
	if q.closed {
		return nil, errClosed
	}

	// Each new file descriptor we open will create a new connection
	// We need to wait on the host to be ready:
	// 1) poll the qemu_pipe driver with the desiered socket name
	// 2) wait until the client is read to send/recv, we do this waiting until we read a rdy message
	var conn *os.File
	var err error
	br := false
	for {
		if conn != nil {
			// Got an error, close connection and try again
			conn.Close()
			time.Sleep(20 * time.Millisecond)
		}

		conn, err = openQemuDevBlocking()
		if err != nil {
			return nil, err
		}
		log.Println("File opened")

		svcName := qemuSvc + q.socketName
		buff := make([]byte, len(svcName)+1)
		copy(buff, svcName)

		// retry loop to wait until we can start the service on the qemu_pipe
		for {
			log.Println("Writing service")
			written, err := conn.Write(buff)
			if err != nil {
				// The host has not opened the socket. Sleep and try again
				conn.Close()
				time.Sleep(100 * time.Millisecond)
				break
			}
			buff = buff[written:]
			if len(buff) == 0 {
				log.Println("Wrote service")
				br = true
				break
			}
		}

		if br {
			return MakeQemuConn(conn), nil
		}
	}

}

// Close closes the connection
func (q *Pipe) Close() error {
	q.closed = true
	return nil
}

// Addr returns the connection address
func (q *Pipe) Addr() net.Addr {
	return qemuAddr(q.socketName)
}

// MakePipe will return a new net.Listener
// backed by a qemu pipe. Qemu pipes are implemented as virtual
// devices. To get a handle an open("/dev/qemu_pipe") is issued.
// The virtual driver keeps a map of file descriptors to available
// services. In this case we open a unix socket service and return that.
func MakePipe(socketName string) (*Pipe, error) {
	if _, err := os.Stat(qemuDriver); err != nil {
		return nil, err
	}

	return &Pipe{socketName: socketName}, nil
}
<|endoftext|>