<commit_before>package player

import "testing"

type testPlayer string

func (p testPlayer) Key() string           { return string(p) }
func (testPlayer) Do(a Action) error       { return nil }
func (testPlayer) SetMute(bool) error      { return nil }
func (testPlayer) SetRepeat(bool) error    { return nil }
func (testPlayer) SetVolume(float64) error { return nil }
func (testPlayer) SetTime(float64) error   { return nil }

func TestPlayers(t *testing.T) {
	oneKey := "one"
	onePl := testPlayer(oneKey)

	ps := NewPlayers()

	list := ps.List()
	if len(list) != 0 {
		t.Errorf("len(ps.List()) = %d, expected: %d", len(list), 0)
	}

	ps.Add(onePl)

	p := ps.Get(oneKey)
	if p != onePl {
		t.Errorf("Get(%#v) = %#v, expected: %#v", oneKey, p, onePl)
	}

	list = ps.List()
	if len(list) != 1 {
		t.Errorf("len(ps.List()) = %d, expected: %d", len(list), 1)
	}

	ps.Remove(oneKey)

	p = ps.Get(oneKey)
	if p != nil {
		t.Errorf("Get(%#v) = %#v, expected: %#v", oneKey, p, nil)
	}

	list = ps.List()
	if len(list) != 0 {
		t.Errorf("len(ps.List()) = %d, expected: %d", len(list), 0)
	}
}
<commit_msg>Added test for Players.MarshalJSON<commit_after>package player

import (
	"encoding/json"
	"reflect"
	"testing"
)

type testPlayer string

func (p testPlayer) Key() string           { return string(p) }
func (testPlayer) Do(a Action) error       { return nil }
func (testPlayer) SetMute(bool) error      { return nil }
func (testPlayer) SetRepeat(bool) error    { return nil }
func (testPlayer) SetVolume(float64) error { return nil }
func (testPlayer) SetTime(float64) error   { return nil }

func TestPlayers(t *testing.T) {
	oneKey := "one"
	onePl := testPlayer(oneKey)

	ps := NewPlayers()

	list := ps.List()
	if len(list) != 0 {
		t.Errorf("len(ps.List()) = %d, expected: %d", len(list), 0)
	}

	ps.Add(onePl)

	p := ps.Get(oneKey)
	if p != onePl {
		t.Errorf("Get(%#v) = %#v, expected: %#v", oneKey, p, onePl)
	}

	list = ps.List()
	if len(list) != 1 {
		t.Errorf("len(ps.List()) = %d, expected: %d", len(list), 1)
	}

	jsonExpected := []byte("{\"keys\":[\"one\"]}")
	jsonGot, err := json.Marshal(ps)
	if err != nil {
		t.Errorf("unexpected error marshalling Playlists: %v", err)
	}
	if !reflect.DeepEqual(jsonExpected, jsonGot) {
		t.Errorf("json.Marshal(...) = %#v, expected: %#v", string(jsonGot), string(jsonExpected))
	}

	ps.Remove(oneKey)
	p = ps.Get(oneKey)
	if p != nil {
		t.Errorf("Get(%#v) = %#v, expected: %#v", oneKey, p, nil)
	}

	list = ps.List()
	if len(list) != 0 {
		t.Errorf("len(ps.List()) = %d, expected: %d", len(list), 0)
	}
}
<|endoftext|>


<commit_before>// Copyright 2017 HenryLee. 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 plugin

import (
	tp "github.com/henrylee2cn/teleport"
)

// NewRouterRootSetting creates a plugin to set the peer router root.
func NewRouterRootSetting(routerRoot string) tp.PostNewPeerPlugin {
	return &routerRootSetting{routerRoot}
}

type routerRootSetting struct {
	routerRoot string
}

func (r *routerRootSetting) Name() string {
	return "RouterRootSetting"
}

func (r *routerRootSetting) PostNewPeer(peer tp.EarlyPeer) error {
	peer.RootRoute(r.routerRoot)
	return nil
}
<commit_msg>NewRouterRootSetting -> RootRoute<commit_after>// Copyright 2017 HenryLee. 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 plugin

import (
	tp "github.com/henrylee2cn/teleport"
)

// RootRoute creates a plugin to set the peer router root.
func RootRoute(routerRoot string) tp.Plugin {
	return &rootRoute{routerRoot}
}

type rootRoute struct {
	routerRoot string
}

var (
	_ tp.PostNewPeerPlugin = new(rootRoute)
)

func (r *rootRoute) Name() string {
	return "RootRoute"
}

func (r *rootRoute) PostNewPeer(peer tp.EarlyPeer) error {
	peer.RootRoute(r.routerRoot)
	return nil
}
<|endoftext|>


<commit_before>// The MIT License (MIT)
//
// Copyright (c) 2015 Arnaud Vazard
//
// See LICENSE file.
package memo

import (
	"fmt"
	// "github.com/fatih/color"
	"github.com/romainletendart/goxxx/core"
	"github.com/romainletendart/goxxx/database"
	"github.com/thoj/go-ircevent"
	"io/ioutil"
	"log"
	"regexp"
	"testing"
)

func TestHandleMemoCmd(t *testing.T) {
	log.SetOutput(ioutil.Discard)

	db := database.InitDatabase("tests.sqlite", true)
	defer db.Close()
	Init(db)

	// --- --- --- Supposed to pass
	var (
		message string = "  \t  !memo Receiver this is a memo      "

		// For the Arguments field I checked how it worked from a real function call (Not documented)
		event irc.Event = irc.Event{
			Nick:      "Sender",
			Arguments: []string{"#test_channel", message}}

		replyCallbackDataTest      core.ReplyCallbackData
		replyCallbackDataReference core.ReplyCallbackData = core.ReplyCallbackData{Nick: "Sender", Message: "Sender: memo for Receiver saved"}
	)

	HandleMemoCmd(&event, func(data *core.ReplyCallbackData) {
		replyCallbackDataTest = *data
	})

	if replyCallbackDataTest != replyCallbackDataReference {
		t.Errorf("Test data differ from reference data:\nTest data:\t%#v\nReference data: %#v\n\n", replyCallbackDataTest, replyCallbackDataReference)
	}
	// --- --- --- --- --- ---

	// --- --- --- Not supposed to pass
	message = " this is not a command "
	event = irc.Event{
		Nick:      "Sender",
		Arguments: []string{"#test_channel", message}}

	// There is no memo command in the message, the callback should not be called
	HandleMemoCmd(&event, func(data *core.ReplyCallbackData) {
		t.Errorf("Callback function not supposed to be called, the message does not contain the !memo command (Message: %q)\n\n", message)
	})
	// --- --- --- --- --- ---
}

func TestSendMemo(t *testing.T) {
	log.SetOutput(ioutil.Discard)

	db := database.InitDatabase("tests.sqlite", true)
	defer db.Close()
	Init(db)

	var (
		message               string    = "!memo Receiver this is a memo"
		expectedNick          string    = "Receiver"
		event                 irc.Event = irc.Event{Nick: "Sender", Arguments: []string{"#test_channel", message}}
		replyCallbackDataTest core.ReplyCallbackData
	)

	// Create Memo
	HandleMemoCmd(&event, nil)

	message = " this is a message to trigger the memo "
	event = irc.Event{Nick: expectedNick, Arguments: []string{"#test_channel", message}}
	re := regexp.MustCompile(fmt.Sprintf(`^%s: memo from Sender => "this is a memo" \(\d{2}/\d{2}/\d{4} @ \d{2}:\d{2}\)$`, expectedNick))

	SendMemo(&event, func(data *core.ReplyCallbackData) {
		replyCallbackDataTest = *data
	})

	if !re.MatchString(replyCallbackDataTest.Message) {
		t.Errorf("Regexp %q not matching %q", re.String(), replyCallbackDataTest.Message)
	}
	if replyCallbackDataTest.Nick != expectedNick {
		t.Errorf("Incorrect Nick: should be %q, is %q", expectedNick, replyCallbackDataTest.Nick)
	}
}
<commit_msg>Refactored memo_test.go<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2015 Arnaud Vazard
//
// See LICENSE file.
package memo

import (
	"fmt"
	// "github.com/fatih/color"
	"github.com/romainletendart/goxxx/core"
	"github.com/romainletendart/goxxx/database"
	"github.com/thoj/go-ircevent"
	"io/ioutil"
	"log"
	"regexp"
	"testing"
)

var (
	validMessage   string = "  \t  !memo   Receiver this is a memo      "
	invalidMessage string = "this is not a memo command"
	expectedNick   string = "Receiver"

	// For the Arguments field I checked how it worked from a real function call (Not documented)
	validEvent irc.Event = irc.Event{
		Nick:      "Sender",
		Arguments: []string{"#test_channel", validMessage}}

	invalidEvent irc.Event = irc.Event{
		Nick:      "Sender",
		Arguments: []string{"#test_channel", invalidMessage}}

	replyCallbackDataReference core.ReplyCallbackData = core.ReplyCallbackData{Nick: "Sender", Message: "Sender: memo for Receiver saved"}
)

func Test_HandleMemoCmd(t *testing.T) {
	log.SetOutput(ioutil.Discard)

	db := database.InitDatabase("tests.sqlite", true)
	defer db.Close()
	Init(db)

	// --- --- --- Supposed to pass
	var replyCallbackDataTest core.ReplyCallbackData
	HandleMemoCmd(&validEvent, func(data *core.ReplyCallbackData) {
		replyCallbackDataTest = *data
	})
	if replyCallbackDataTest != replyCallbackDataReference {
		t.Errorf("Test data differ from reference data:\nTest data:\t%#v\nReference data: %#v\n\n", replyCallbackDataTest, replyCallbackDataReference)
	}
	// --- --- --- --- --- ---

	// --- --- --- Not supposed to pass
	HandleMemoCmd(&invalidEvent, func(data *core.ReplyCallbackData) {
		// There is no memo command in the message, the callback should not be called
		t.Errorf("Callback function not supposed to be called, the message does not contain the !memo command (Message: %q)\n\n", invalidMessage)
	})
	// --- --- --- --- --- ---
}

func Test_SendMemo(t *testing.T) {
	log.SetOutput(ioutil.Discard)

	db := database.InitDatabase("tests.sqlite", true)
	defer db.Close()
	Init(db)

	// Create Memo
	HandleMemoCmd(&validEvent, nil)

	message := " this is a message to trigger the memo "
	event := irc.Event{Nick: expectedNick, Arguments: []string{"#test_channel", message}}
	re := regexp.MustCompile(fmt.Sprintf(`^%s: memo from Sender => "this is a memo" \(\d{2}/\d{2}/\d{4} @ \d{2}:\d{2}\)$`, expectedNick))

	var replyCallbackDataTest core.ReplyCallbackData
	SendMemo(&event, func(data *core.ReplyCallbackData) {
		replyCallbackDataTest = *data
	})

	if !re.MatchString(replyCallbackDataTest.Message) {
		t.Errorf("Regexp %q not matching %q", re.String(), replyCallbackDataTest.Message)
	}
	if replyCallbackDataTest.Nick != expectedNick {
		t.Errorf("Incorrect Nick: should be %q, is %q", expectedNick, replyCallbackDataTest.Nick)
	}
}
<|endoftext|>


<commit_before>package queue_test

import (
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"cred-alert/queue"
)

var _ = Describe("Plans", func() {
	Describe("PushEventPlan", func() {
		It("can be encoded into a task", func() {
			task := queue.PushEventPlan{
				Owner:      "owner",
				Repository: "repository",
				Private:    true,
				From:       "sha-1",
				To:         "sha-2",
			}.Task("id-1")

			Expect(task.ID()).To(Equal("id-1"))
			Expect(task.Type()).To(Equal(queue.TaskTypePushEvent))
			Expect(task.Payload()).To(MatchJSON(`
				{
						"owner": "owner",
						"repository": "repository",
						"private": true,
						"from": "sha-1",
						"to": "sha-2"
				}`))
		})

		It("is a queueable plan", func() {
			var _ queue.Plan = queue.PushEventPlan{}
		})
	})
})
<commit_msg>Remove non-test<commit_after>package queue_test

import (
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"cred-alert/queue"
)

var _ = Describe("Plans", func() {
	Describe("PushEventPlan", func() {
		It("can be encoded into a task", func() {
			task := queue.PushEventPlan{
				Owner:      "owner",
				Repository: "repository",
				Private:    true,
				From:       "sha-1",
				To:         "sha-2",
			}.Task("id-1")

			Expect(task.ID()).To(Equal("id-1"))
			Expect(task.Type()).To(Equal(queue.TaskTypePushEvent))
			Expect(task.Payload()).To(MatchJSON(`
				{
						"owner": "owner",
						"repository": "repository",
						"private": true,
						"from": "sha-1",
						"to": "sha-2"
				}`))
		})
	})
})
<|endoftext|>


<commit_before>package metrics

import (
	"bufio"
	"encoding/json"
	"fmt"
	"os"
	"regexp"
	"strconv"
	"strings"

	"github.com/mackerelio/mackerel-agent/config"
	"github.com/mackerelio/mackerel-agent/logging"
	"github.com/mackerelio/mackerel-agent/mackerel"
	"github.com/mackerelio/mackerel-agent/util"
)

// pluginGenerator collects user-defined metrics.
// mackerel-agent runs specified command and parses the result for the metric names and values.
type pluginGenerator struct {
	Config config.PluginConfig
	Meta   *pluginMeta
}

// pluginMeta is generated from plugin command. (not the configuration file)
type pluginMeta struct {
	Graphs map[string]customGraphDef
}

type customGraphDef struct {
	Label   string
	Unit    string
	Metrics []customGraphMetricDef
}

type customGraphMetricDef struct {
	Name    string
	Label   string
	Stacked bool
}

var pluginLogger = logging.GetLogger("metrics.plugin")

const pluginPrefix = "custom."

var pluginConfigurationEnvName = "MACKEREL_AGENT_PLUGIN_META"

// NewPluginGenerator XXX
func NewPluginGenerator(conf config.PluginConfig) PluginGenerator {
	return &pluginGenerator{Config: conf}
}

func (g *pluginGenerator) Generate() (Values, error) {
	results, err := g.collectValues()
	if err != nil {
		return nil, err
	}
	return results, nil
}

func (g *pluginGenerator) PrepareGraphDefs() ([]mackerel.CreateGraphDefsPayload, error) {
	err := g.loadPluginMeta()
	if err != nil {
		return nil, err
	}

	payload := g.makeCreateGraphDefsPayload()
	return payload, nil
}

// loadPluginMeta obtains plugin information (e.g. graph visuals, metric
// namespaces, etc) from the command specified.
// mackerel-agent runs the command with MACKEREL_AGENT_PLUGIN_META
// environment variable set.  The command is supposed to output like below:
//
// 	# mackerel-agent-plugin
// 	{
// 	  "graphs": {
// 	    GRAPH_NAME: {
// 	      "label": GRAPH_LABEL,
// 	      "unit": UNIT_TYPE
// 	      "metrics": [
// 	        {
// 	          "name": METRIC_NAME,
// 	          "label": METRIC_LABEL
// 	        },
// 	        ...
// 	      ]
// 	    },
// 	    GRAPH_NAME: ...
// 	  }
// 	}
//
// Valid UNIT_TYPEs are: "float", "integer", "percentage", "bytes", "bytes/sec", "iops"
//
// The output should start with a line beginning with '#', which contains
// meta-info of the configuration. (eg. plugin schema version)
//
// Below is a working example where the plugin emits metrics named "dice.d6" and "dice.d20":
//
// 	{
// 	  "graphs": {
// 	    "dice": {
// 	      "metrics": [
// 	        {
// 	          "name": "d6",
// 	          "label": "Die (d6)"
// 	        },
// 	        {
// 	          "name": "d20",
// 	          "label": "Die (d20)"
// 	        }
// 	      ],
// 	      "unit": "integer",
// 	      "label": "My Dice"
// 	    }
// 	  }
// 	}
func (g *pluginGenerator) loadPluginMeta() error {
	command := g.Config.Command
	pluginLogger.Debugf("Obtaining plugin configuration: %q", command)

	// Set environment variable to make the plugin command generate its configuration
	os.Setenv(pluginConfigurationEnvName, "1")
	defer os.Setenv(pluginConfigurationEnvName, "")

	stdout, stderr, exitCode, err := util.RunCommand(command)
	if err != nil {
		return fmt.Errorf("running %q failed: %s, exit=%d stderr=%q", command, err, exitCode, stderr)
	}

	outBuffer := bufio.NewReader(strings.NewReader(stdout))
	// Read the plugin configuration meta (version etc)

	headerLine, err := outBuffer.ReadString('\n')
	if err != nil {
		return fmt.Errorf("while reading the first line of command %q: %s", command, err)
	}

	// Parse the header line of format:
	// # mackerel-agent-plugin [key=value]...
	pluginMetaHeader := map[string]string{}

	re := regexp.MustCompile(`^#\s*mackerel-agent-plugin\b(.*)`)
	m := re.FindStringSubmatch(headerLine)
	if m == nil {
		return fmt.Errorf("bad format of first line: %q", headerLine)
	}

	for _, field := range strings.Fields(m[1]) {
		keyValue := strings.Split(field, "=")
		var value string
		if len(keyValue) > 1 {
			value = keyValue[1]
		} else {
			value = ""
		}
		pluginMetaHeader[keyValue[0]] = value
	}

	// Check schema version
	version, ok := pluginMetaHeader["version"]
	if !ok {
		version = "1"
	}

	if version != "1" {
		return fmt.Errorf("unsupported plugin meta version: %q", version)
	}

	conf := &pluginMeta{}
	err = json.NewDecoder(outBuffer).Decode(conf)

	if err != nil {
		return fmt.Errorf("while reading plugin configuration: %s", err)
	}

	g.Meta = conf

	return nil
}

func (g *pluginGenerator) makeCreateGraphDefsPayload() []mackerel.CreateGraphDefsPayload {
	if g.Meta == nil {
		return nil
	}

	payloads := []mackerel.CreateGraphDefsPayload{}

	for key, graph := range g.Meta.Graphs {
		payload := mackerel.CreateGraphDefsPayload{
			Name:        pluginPrefix + key,
			DisplayName: graph.Label,
			Unit:        graph.Unit,
		}
		if payload.Unit == "" {
			payload.Unit = "float"
		}

		for _, metric := range graph.Metrics {
			metricPayload := mackerel.CreateGraphDefsPayloadMetric{
				Name:        pluginPrefix + key + "." + metric.Name,
				DisplayName: metric.Label,
				IsStacked:   metric.Stacked,
			}
			payload.Metrics = append(payload.Metrics, metricPayload)
		}

		payloads = append(payloads, payload)
	}

	return payloads
}

var delimReg = regexp.MustCompile(`[\s\t]+`)

func (g *pluginGenerator) collectValues() (Values, error) {
	command := g.Config.Command
	pluginLogger.Debugf("Executing plugin: command = \"%s\"", command)

	os.Setenv(pluginConfigurationEnvName, "")
	stdout, stderr, _, err := util.RunCommand(command)

	if err != nil {
		pluginLogger.Errorf("Failed to execute command \"%s\" (skip these metrics):\n%s", command, stderr)
		return nil, err
	}

	results := make(map[string]float64, 0)
	for _, line := range strings.Split(stdout, "\n") {
		// Key, value, timestamp
		// ex.) tcp.CLOSING 0 1397031808
		items := delimReg.Split(line, 3)
		if len(items) != 3 {
			continue
		}
		value, err := strconv.ParseFloat(items[1], 64)
		if err != nil {
			pluginLogger.Warningf("Failed to parse values: %s", err)
			continue
		}

		key := items[0]

		results[pluginPrefix+key] = value
	}

	return results, nil
}
<commit_msg>output plugin error<commit_after>package metrics

import (
	"bufio"
	"encoding/json"
	"fmt"
	"os"
	"regexp"
	"strconv"
	"strings"

	"github.com/mackerelio/mackerel-agent/config"
	"github.com/mackerelio/mackerel-agent/logging"
	"github.com/mackerelio/mackerel-agent/mackerel"
	"github.com/mackerelio/mackerel-agent/util"
)

// pluginGenerator collects user-defined metrics.
// mackerel-agent runs specified command and parses the result for the metric names and values.
type pluginGenerator struct {
	Config config.PluginConfig
	Meta   *pluginMeta
}

// pluginMeta is generated from plugin command. (not the configuration file)
type pluginMeta struct {
	Graphs map[string]customGraphDef
}

type customGraphDef struct {
	Label   string
	Unit    string
	Metrics []customGraphMetricDef
}

type customGraphMetricDef struct {
	Name    string
	Label   string
	Stacked bool
}

var pluginLogger = logging.GetLogger("metrics.plugin")

const pluginPrefix = "custom."

var pluginConfigurationEnvName = "MACKEREL_AGENT_PLUGIN_META"

// NewPluginGenerator XXX
func NewPluginGenerator(conf config.PluginConfig) PluginGenerator {
	return &pluginGenerator{Config: conf}
}

func (g *pluginGenerator) Generate() (Values, error) {
	results, err := g.collectValues()
	if err != nil {
		return nil, err
	}
	return results, nil
}

func (g *pluginGenerator) PrepareGraphDefs() ([]mackerel.CreateGraphDefsPayload, error) {
	err := g.loadPluginMeta()
	if err != nil {
		return nil, err
	}

	payload := g.makeCreateGraphDefsPayload()
	return payload, nil
}

// loadPluginMeta obtains plugin information (e.g. graph visuals, metric
// namespaces, etc) from the command specified.
// mackerel-agent runs the command with MACKEREL_AGENT_PLUGIN_META
// environment variable set.  The command is supposed to output like below:
//
// 	# mackerel-agent-plugin
// 	{
// 	  "graphs": {
// 	    GRAPH_NAME: {
// 	      "label": GRAPH_LABEL,
// 	      "unit": UNIT_TYPE
// 	      "metrics": [
// 	        {
// 	          "name": METRIC_NAME,
// 	          "label": METRIC_LABEL
// 	        },
// 	        ...
// 	      ]
// 	    },
// 	    GRAPH_NAME: ...
// 	  }
// 	}
//
// Valid UNIT_TYPEs are: "float", "integer", "percentage", "bytes", "bytes/sec", "iops"
//
// The output should start with a line beginning with '#', which contains
// meta-info of the configuration. (eg. plugin schema version)
//
// Below is a working example where the plugin emits metrics named "dice.d6" and "dice.d20":
//
// 	{
// 	  "graphs": {
// 	    "dice": {
// 	      "metrics": [
// 	        {
// 	          "name": "d6",
// 	          "label": "Die (d6)"
// 	        },
// 	        {
// 	          "name": "d20",
// 	          "label": "Die (d20)"
// 	        }
// 	      ],
// 	      "unit": "integer",
// 	      "label": "My Dice"
// 	    }
// 	  }
// 	}
func (g *pluginGenerator) loadPluginMeta() error {
	command := g.Config.Command
	pluginLogger.Debugf("Obtaining plugin configuration: %q", command)

	// Set environment variable to make the plugin command generate its configuration
	os.Setenv(pluginConfigurationEnvName, "1")
	defer os.Setenv(pluginConfigurationEnvName, "")

	stdout, stderr, exitCode, err := util.RunCommand(command)
	if err != nil {
		return fmt.Errorf("running %q failed: %s, exit=%d stderr=%q", command, err, exitCode, stderr)
	}

	outBuffer := bufio.NewReader(strings.NewReader(stdout))
	// Read the plugin configuration meta (version etc)

	headerLine, err := outBuffer.ReadString('\n')
	if err != nil {
		return fmt.Errorf("while reading the first line of command %q: %s", command, err)
	}

	// Parse the header line of format:
	// # mackerel-agent-plugin [key=value]...
	pluginMetaHeader := map[string]string{}

	re := regexp.MustCompile(`^#\s*mackerel-agent-plugin\b(.*)`)
	m := re.FindStringSubmatch(headerLine)
	if m == nil {
		return fmt.Errorf("bad format of first line: %q", headerLine)
	}

	for _, field := range strings.Fields(m[1]) {
		keyValue := strings.Split(field, "=")
		var value string
		if len(keyValue) > 1 {
			value = keyValue[1]
		} else {
			value = ""
		}
		pluginMetaHeader[keyValue[0]] = value
	}

	// Check schema version
	version, ok := pluginMetaHeader["version"]
	if !ok {
		version = "1"
	}

	if version != "1" {
		return fmt.Errorf("unsupported plugin meta version: %q", version)
	}

	conf := &pluginMeta{}
	err = json.NewDecoder(outBuffer).Decode(conf)

	if err != nil {
		return fmt.Errorf("while reading plugin configuration: %s", err)
	}

	g.Meta = conf

	return nil
}

func (g *pluginGenerator) makeCreateGraphDefsPayload() []mackerel.CreateGraphDefsPayload {
	if g.Meta == nil {
		return nil
	}

	payloads := []mackerel.CreateGraphDefsPayload{}

	for key, graph := range g.Meta.Graphs {
		payload := mackerel.CreateGraphDefsPayload{
			Name:        pluginPrefix + key,
			DisplayName: graph.Label,
			Unit:        graph.Unit,
		}
		if payload.Unit == "" {
			payload.Unit = "float"
		}

		for _, metric := range graph.Metrics {
			metricPayload := mackerel.CreateGraphDefsPayloadMetric{
				Name:        pluginPrefix + key + "." + metric.Name,
				DisplayName: metric.Label,
				IsStacked:   metric.Stacked,
			}
			payload.Metrics = append(payload.Metrics, metricPayload)
		}

		payloads = append(payloads, payload)
	}

	return payloads
}

var delimReg = regexp.MustCompile(`[\s\t]+`)

func (g *pluginGenerator) collectValues() (Values, error) {
	command := g.Config.Command
	pluginLogger.Debugf("Executing plugin: command = \"%s\"", command)

	os.Setenv(pluginConfigurationEnvName, "")
	stdout, stderr, _, err := util.RunCommand(command)

	if stderr != "" {
		pluginLogger.Infof("command %q printed out on STDERR: %q", stderr)
	}
	if err != nil {
		pluginLogger.Errorf("Failed to execute command %q (skip these metrics):\n", command)
		return nil, err
	}

	results := make(map[string]float64, 0)
	for _, line := range strings.Split(stdout, "\n") {
		// Key, value, timestamp
		// ex.) tcp.CLOSING 0 1397031808
		items := delimReg.Split(line, 3)
		if len(items) != 3 {
			continue
		}
		value, err := strconv.ParseFloat(items[1], 64)
		if err != nil {
			pluginLogger.Warningf("Failed to parse values: %s", err)
			continue
		}

		key := items[0]

		results[pluginPrefix+key] = value
	}

	return results, nil
}
<|endoftext|>


<commit_before>package postgres

import (
    "errors"
    "strings"
    "fmt"
    "regexp"
    "database/sql/driver"
    "github.com/archsh/go.xql"
    "strconv"
)

type Elemented interface {
    Elem2Strings() []string
    Strings2Elem(...string) error
}

type StringArray []string

func (h StringArray) Declare(props xql.PropertySet) string {
    size,_ := props.GetInt("size", 32)
    return fmt.Sprintf("varchar(%d)[]",size)
}

func (a StringArray) Elem2Strings() []string {
    var ss []string
    for _, x := range a {
        ss = append(ss, fmt.Sprintf("'%s'", strings.Replace(x, "'","\\'",-1)))
    }
    return ss
}

func (a *StringArray) Strings2Elem(ss ...string) error {
    (*a) = ss
    return nil
}

func (p *StringArray) Scan(src interface{}) error {
    return Array_Scan(src, p)
}

func (p StringArray) Value() (driver.Value, error) {
    return Array_Value(&p)
}

type IntegerArray []int

func (h IntegerArray) Declare(props xql.PropertySet) string {
    return "integer[]"
}

func (a IntegerArray) Elem2Strings() []string {
    ss := []string{}
    for _,x := range a {
        ss = append(ss, fmt.Sprintf("%d", x))
    }
    return ss
}

func (a *IntegerArray) Strings2Elem(ss ...string) error {
    for _, s := range ss {
        n, e := strconv.ParseInt(s, 10, 32)
        if nil != e {
            return e
        }else{
            (*a) = append(*a, int(n))
        }
    }
    return nil
}

func (p *IntegerArray) Scan(src interface{}) error {
    return Array_Scan(src, p)
}

func (p IntegerArray) Value() (driver.Value, error) {
    return Array_Value(&p)
}

type SmallIntegerArray []int16

func (h SmallIntegerArray) Declare(props xql.PropertySet) string {
    return "smallint[]"
}

func (a SmallIntegerArray) Elem2Strings() []string {
    ss := []string{}
    for _,x := range a {
        ss = append(ss, fmt.Sprintf("%d", x))
    }
    return ss
}

func (a *SmallIntegerArray) Strings2Elem(ss ...string) error {
    for _, s := range ss {
        n, e := strconv.ParseInt(s, 10, 16)
        if nil != e {
            return e
        }else{
            (*a) = append(*a, int16(n))
        }
    }
    return nil
}

func (p *SmallIntegerArray) Scan(src interface{}) error {
    return Array_Scan(src, p)
}

func (p SmallIntegerArray) Value() (driver.Value, error) {
    return Array_Value(&p)
}

type BigIntegerArray []int64

func (h BigIntegerArray) Declare(props xql.PropertySet) string {
    return "bigint[]"
}

func (a BigIntegerArray) Elem2Strings() []string {
    ss := []string{}
    for _,x := range a {
        ss = append(ss, fmt.Sprintf("%d", x))
    }
    return ss
}

func (a *BigIntegerArray) Strings2Elem(ss ...string) error {
    for _, s := range ss {
        n, e := strconv.ParseInt(s, 10, 64)
        if nil != e {
            return e
        }else{
            (*a) = append(*a, n)
        }
    }
    return nil
}

func (p *BigIntegerArray) Scan(src interface{}) error {
    return Array_Scan(src, p)
}

func (p BigIntegerArray) Value() (driver.Value, error) {
    return Array_Value(&p)
}

type RealArray []float32

func (h RealArray) Declare(props xql.PropertySet) string {
    return "real[]"
}

func (a RealArray) Elem2Strings() []string {
    ss := []string{}
    for _,x := range a {
        ss = append(ss, fmt.Sprintf("%f", x))
    }
    return ss
}

func (a *RealArray) Strings2Elem(ss ...string) error {
    for _, s := range ss {
        n, e := strconv.ParseFloat(s, 32)
        if nil != e {
            return e
        }else{
            (*a) = append(*a, float32(n))
        }
    }
    return nil
}

func (p *RealArray) Scan(src interface{}) error {
    return Array_Scan(src, p)
}

func (p RealArray) Value() (driver.Value, error) {
    return Array_Value(&p)
}

type DoubleArray []float64

func (h DoubleArray) Declare(props xql.PropertySet) string {
    return "double[]"
}

func (a DoubleArray) Elem2Strings() []string {
    ss := []string{}
    for _,x := range a {
        ss = append(ss, fmt.Sprintf("%f", x))
    }
    return ss
}

func (a *DoubleArray) Strings2Elem(ss ...string) error {
    for _, s := range ss {
        n, e := strconv.ParseFloat(s, 64)
        if nil != e {
            return e
        }else{
            (*a) = append(*a, n)
        }
    }
    return nil
}

func (p *DoubleArray) Scan(src interface{}) error {
    return Array_Scan(src, p)
}

func (p DoubleArray) Value() (driver.Value, error) {
    return Array_Value(&p)
}

type BoolArray []bool

func (h BoolArray) Declare(props xql.PropertySet) string {
    return "bool[]"
}

func (a BoolArray) Elem2Strings() []string {
    ss := []string{}
    for _,x := range a {
        ss = append(ss, fmt.Sprintf("%s", x))
    }
    return ss
}

func (a *BoolArray) Strings2Elem(ss ...string) error {
    for _, s := range ss {
        switch strings.ToLower(s) {
        case "y","yes","t","true","ok":
            (*a) = append(*a, true)
        default:
            (*a) = append(*a, false)
        }
    }
    return nil
}

func (p *BoolArray) Scan(src interface{}) error {
    return Array_Scan(src, p)
}

func (p BoolArray) Value() (driver.Value, error) {
    return Array_Value(&p)
}

// PARSING ARRAYS
// SEE http://www.postgresql.org/docs/9.1/static/arrays.html#ARRAYS-IO
// Arrays are output within {} and a delimiter, which is a comma for most
// postgres types (; for box)
//
// Individual values are surrounded by quotes:
// The array output routine will put double quotes around element values if
// they are empty strings, contain curly braces, delimiter characters,
// double quotes, backslashes, or white space, or match the word NULL.
// Double quotes and backslashes embedded in element values will be
// backslash-escaped. For numeric data types it is safe to assume that double
// quotes will never appear, but for textual data types one should be prepared
// to cope with either the presence or absence of quotes.

// construct a regexp to extract values:
var (
    // unquoted array values must not contain: (" , \ { } whitespace NULL)
    // and must be at least one char
    unquotedChar  = `[^",\\{}\s(NULL)]`
    unquotedValue = fmt.Sprintf("(%s)+", unquotedChar)

    // quoted array values are surrounded by double quotes, can be any
    // character except " or \, which must be backslash escaped:
    quotedChar  = `[^"\\]|\\"|\\\\`
    quotedValue = fmt.Sprintf("\"(%s)*\"", quotedChar)

    // an array value may be either quoted or unquoted:
    arrayValue = fmt.Sprintf("(?P<value>(%s|%s))", unquotedValue, quotedValue)

    // Array values are separated with a comma IF there is more than one value:
    arrayExp = regexp.MustCompile(fmt.Sprintf("((%s)(,)?)", arrayValue))

    valueIndex int
)

// Find the index of the 'value' named expression
func init() {
    for i, subexp := range arrayExp.SubexpNames() {
        if subexp == "value" {
            valueIndex = i
            break
        }
    }
}

// Parse the output string from the array type.
// Regex used: (((?P<value>(([^",\\{}\s(NULL)])+|"([^"\\]|\\"|\\\\)*")))(,)?)
func parseArray(array string) []string {
    results := make([]string, 0)
    matches := arrayExp.FindAllStringSubmatch(array, -1)
    for _, match := range matches {
        s := match[valueIndex]
        // the string _might_ be wrapped in quotes, so trim them:
        s = strings.Trim(s, "\"")
        results = append(results, s)
    }
    return results
}

//func (p *StringArray) Scan(src interface{}) error {
//    asBytes, ok := src.([]byte)
//    if !ok {
//        return error(errors.New("Scan source was not []bytes."))
//    }
//
//    asString := string(asBytes)
//    parsed := parseArray(asString)
//    (*p) = StringArray(parsed)
//
//    return nil
//}
//
//func (p StringArray) Value() (driver.Value, error) {
//    var ss []string
//    for _, s := range p {
//        ss = append(ss, fmt.Sprintf(`"%s"`, s))
//    }
//    return strings.Join([]string{"{", strings.Join(ss, ","), "}"}, ""), nil
//}

func Array_Scan(src interface{}, dest interface{}) error {
    if nil == src || dest == nil {
        return nil
    }
    asBytes, ok := src.([]byte)
    if !ok {
        return error(errors.New("Scan source was not []bytes."))
    }

    asString := string(asBytes)
    parsed := parseArray(asString)
    if vv, ok := dest.(Elemented); ok {
        return vv.Strings2Elem(parsed...)
    }
    return errors.New("Elemented should be implemented.")
}

func Array_Value(v interface{}) (driver.Value, error) {
    if nil == v {
        return nil, nil
    }
    if vv, ok := v.(Elemented); ok {
        return strings.Join([]string{"{", strings.Join(vv.Elem2Strings(), ","), "}"}, ""), nil
    }
    return nil, errors.New("Elemented should be implemented.")
}<commit_msg>Improved StringArray.<commit_after>package postgres

import (
    "errors"
    "strings"
    "fmt"
    "regexp"
    "database/sql/driver"
    "github.com/archsh/go.xql"
    "strconv"
)

type Elemented interface {
    Elem2Strings() []string
    Strings2Elem(...string) error
}

type StringArray []string

func (h StringArray) Declare(props xql.PropertySet) string {
    size,_ := props.GetInt("size", 32)
    return fmt.Sprintf("varchar(%d)[]",size)
}

func (a StringArray) Elem2Strings() []string {
    var ss []string
    for _, x := range a {
        ss = append(ss,
            fmt.Sprintf("'%s'",
                strings.Replace(strings.Replace(x, `'`,`\'`,-1),`"`,`\"`,-1)))
    }
    return ss
}

func (a *StringArray) Strings2Elem(ss ...string) error {
    (*a) = ss
    return nil
}

func (p *StringArray) Scan(src interface{}) error {
    return Array_Scan(src, p)
}

func (p StringArray) Value() (driver.Value, error) {
    return Array_Value(&p)
}

type IntegerArray []int

func (h IntegerArray) Declare(props xql.PropertySet) string {
    return "integer[]"
}

func (a IntegerArray) Elem2Strings() []string {
    ss := []string{}
    for _,x := range a {
        ss = append(ss, fmt.Sprintf("%d", x))
    }
    return ss
}

func (a *IntegerArray) Strings2Elem(ss ...string) error {
    for _, s := range ss {
        n, e := strconv.ParseInt(s, 10, 32)
        if nil != e {
            return e
        }else{
            (*a) = append(*a, int(n))
        }
    }
    return nil
}

func (p *IntegerArray) Scan(src interface{}) error {
    return Array_Scan(src, p)
}

func (p IntegerArray) Value() (driver.Value, error) {
    return Array_Value(&p)
}

type SmallIntegerArray []int16

func (h SmallIntegerArray) Declare(props xql.PropertySet) string {
    return "smallint[]"
}

func (a SmallIntegerArray) Elem2Strings() []string {
    ss := []string{}
    for _,x := range a {
        ss = append(ss, fmt.Sprintf("%d", x))
    }
    return ss
}

func (a *SmallIntegerArray) Strings2Elem(ss ...string) error {
    for _, s := range ss {
        n, e := strconv.ParseInt(s, 10, 16)
        if nil != e {
            return e
        }else{
            (*a) = append(*a, int16(n))
        }
    }
    return nil
}

func (p *SmallIntegerArray) Scan(src interface{}) error {
    return Array_Scan(src, p)
}

func (p SmallIntegerArray) Value() (driver.Value, error) {
    return Array_Value(&p)
}

type BigIntegerArray []int64

func (h BigIntegerArray) Declare(props xql.PropertySet) string {
    return "bigint[]"
}

func (a BigIntegerArray) Elem2Strings() []string {
    ss := []string{}
    for _,x := range a {
        ss = append(ss, fmt.Sprintf("%d", x))
    }
    return ss
}

func (a *BigIntegerArray) Strings2Elem(ss ...string) error {
    for _, s := range ss {
        n, e := strconv.ParseInt(s, 10, 64)
        if nil != e {
            return e
        }else{
            (*a) = append(*a, n)
        }
    }
    return nil
}

func (p *BigIntegerArray) Scan(src interface{}) error {
    return Array_Scan(src, p)
}

func (p BigIntegerArray) Value() (driver.Value, error) {
    return Array_Value(&p)
}

type RealArray []float32

func (h RealArray) Declare(props xql.PropertySet) string {
    return "real[]"
}

func (a RealArray) Elem2Strings() []string {
    ss := []string{}
    for _,x := range a {
        ss = append(ss, fmt.Sprintf("%f", x))
    }
    return ss
}

func (a *RealArray) Strings2Elem(ss ...string) error {
    for _, s := range ss {
        n, e := strconv.ParseFloat(s, 32)
        if nil != e {
            return e
        }else{
            (*a) = append(*a, float32(n))
        }
    }
    return nil
}

func (p *RealArray) Scan(src interface{}) error {
    return Array_Scan(src, p)
}

func (p RealArray) Value() (driver.Value, error) {
    return Array_Value(&p)
}

type DoubleArray []float64

func (h DoubleArray) Declare(props xql.PropertySet) string {
    return "double[]"
}

func (a DoubleArray) Elem2Strings() []string {
    ss := []string{}
    for _,x := range a {
        ss = append(ss, fmt.Sprintf("%f", x))
    }
    return ss
}

func (a *DoubleArray) Strings2Elem(ss ...string) error {
    for _, s := range ss {
        n, e := strconv.ParseFloat(s, 64)
        if nil != e {
            return e
        }else{
            (*a) = append(*a, n)
        }
    }
    return nil
}

func (p *DoubleArray) Scan(src interface{}) error {
    return Array_Scan(src, p)
}

func (p DoubleArray) Value() (driver.Value, error) {
    return Array_Value(&p)
}

type BoolArray []bool

func (h BoolArray) Declare(props xql.PropertySet) string {
    return "bool[]"
}

func (a BoolArray) Elem2Strings() []string {
    ss := []string{}
    for _,x := range a {
        ss = append(ss, fmt.Sprintf("%s", x))
    }
    return ss
}

func (a *BoolArray) Strings2Elem(ss ...string) error {
    for _, s := range ss {
        switch strings.ToLower(s) {
        case "y","yes","t","true","ok":
            (*a) = append(*a, true)
        default:
            (*a) = append(*a, false)
        }
    }
    return nil
}

func (p *BoolArray) Scan(src interface{}) error {
    return Array_Scan(src, p)
}

func (p BoolArray) Value() (driver.Value, error) {
    return Array_Value(&p)
}

// PARSING ARRAYS
// SEE http://www.postgresql.org/docs/9.1/static/arrays.html#ARRAYS-IO
// Arrays are output within {} and a delimiter, which is a comma for most
// postgres types (; for box)
//
// Individual values are surrounded by quotes:
// The array output routine will put double quotes around element values if
// they are empty strings, contain curly braces, delimiter characters,
// double quotes, backslashes, or white space, or match the word NULL.
// Double quotes and backslashes embedded in element values will be
// backslash-escaped. For numeric data types it is safe to assume that double
// quotes will never appear, but for textual data types one should be prepared
// to cope with either the presence or absence of quotes.

// construct a regexp to extract values:
var (
    // unquoted array values must not contain: (" , \ { } whitespace NULL)
    // and must be at least one char
    unquotedChar  = `[^",\\{}\s(NULL)]`
    unquotedValue = fmt.Sprintf("(%s)+", unquotedChar)

    // quoted array values are surrounded by double quotes, can be any
    // character except " or \, which must be backslash escaped:
    quotedChar  = `[^"\\]|\\"|\\\\`
    quotedValue = fmt.Sprintf("\"(%s)*\"", quotedChar)

    // an array value may be either quoted or unquoted:
    arrayValue = fmt.Sprintf("(?P<value>(%s|%s))", unquotedValue, quotedValue)

    // Array values are separated with a comma IF there is more than one value:
    arrayExp = regexp.MustCompile(fmt.Sprintf("((%s)(,)?)", arrayValue))

    valueIndex int
)

// Find the index of the 'value' named expression
func init() {
    for i, subexp := range arrayExp.SubexpNames() {
        if subexp == "value" {
            valueIndex = i
            break
        }
    }
}

// Parse the output string from the array type.
// Regex used: (((?P<value>(([^",\\{}\s(NULL)])+|"([^"\\]|\\"|\\\\)*")))(,)?)
func parseArray(array string) []string {
    results := make([]string, 0)
    matches := arrayExp.FindAllStringSubmatch(array, -1)
    for _, match := range matches {
        s := match[valueIndex]
        // the string _might_ be wrapped in quotes, so trim them:
        s = strings.Trim(s, "\"")
        results = append(results, s)
    }
    return results
}

//func (p *StringArray) Scan(src interface{}) error {
//    asBytes, ok := src.([]byte)
//    if !ok {
//        return error(errors.New("Scan source was not []bytes."))
//    }
//
//    asString := string(asBytes)
//    parsed := parseArray(asString)
//    (*p) = StringArray(parsed)
//
//    return nil
//}
//
//func (p StringArray) Value() (driver.Value, error) {
//    var ss []string
//    for _, s := range p {
//        ss = append(ss, fmt.Sprintf(`"%s"`, s))
//    }
//    return strings.Join([]string{"{", strings.Join(ss, ","), "}"}, ""), nil
//}

func Array_Scan(src interface{}, dest interface{}) error {
    if nil == src || dest == nil {
        return nil
    }
    asBytes, ok := src.([]byte)
    if !ok {
        return error(errors.New("Scan source was not []bytes."))
    }

    asString := string(asBytes)
    parsed := parseArray(asString)
    if vv, ok := dest.(Elemented); ok {
        return vv.Strings2Elem(parsed...)
    }
    return errors.New("Elemented should be implemented.")
}

func Array_Value(v interface{}) (driver.Value, error) {
    if nil == v {
        return nil, nil
    }
    if vv, ok := v.(Elemented); ok {
        return strings.Join([]string{"{", strings.Join(vv.Elem2Strings(), ","), "}"}, ""), nil
    }
    return nil, errors.New("Elemented should be implemented.")
}<|endoftext|>


<commit_before>package organisations

import (
	"fmt"
	"github.com/Financial-Times/neo-utils-go/neoutils"
	"github.com/jmcvetta/neoism"
)

type relationships []struct {
	RelationshipType string `json:"relationship"`
}

// TransferRelationships is responsible for moving relationships from node with sourceUUID to node with destinationUUID
func CreateTransferRelationshipsQueries(cypherRunner neoutils.CypherRunner, destinationUUID string, sourceUUID string) ([]*neoism.CypherQuery, error) {

	relationshipsFromSourceNode, relationshipsToSourceNode, err := getNodeRelationshipNames(cypherRunner, sourceUUID)
	if err != nil {
		return nil, err
	}

	writeQueries := []*neoism.CypherQuery{}
	for _, rel := range relationshipsFromSourceNode {
		transfQuery := constructTransferRelationshipsWithPlatformVersionFromNodeQuery(sourceUUID, destinationUUID, rel.RelationshipType)
		transfQuery2 := constructTransferRelationshipsFromNodeQuery(sourceUUID, destinationUUID, rel.RelationshipType)
		writeQueries = append(writeQueries, transfQuery, transfQuery2)
	}

	for _, rel := range relationshipsToSourceNode {
		transfQuery := constructTransferRelationshipsWithPlatformVersionToNodeQuery(sourceUUID, destinationUUID, rel.RelationshipType)
		transfQuery2 := constructTransferRelationshipsToNodeQuery(sourceUUID, destinationUUID, rel.RelationshipType)
		writeQueries = append(writeQueries, transfQuery, transfQuery2)
	}

	return writeQueries, nil
}

func getNodeRelationshipNames(cypherRunner neoutils.CypherRunner, uuid string) (relationshipsFromNodeWithUUID relationships, relationshipsToNodeWithUUID relationships, err error) {
	// find all the -> relationships
	relationshipsFromNodeWithUUID = relationships{}
	readRelationshipsFromNodeWithUUIDQuery := &neoism.CypherQuery{
		Statement: `match (a:Thing{uuid:{uuid}})-[r]->(b)
			    return distinct type(r) as relationship`,
		Parameters: map[string]interface{}{
			"uuid": uuid,
		},
		Result: &relationshipsFromNodeWithUUID,
	}

	// find all the <- relationships
	relationshipsToNodeWithUUID = relationships{}
	readRelationshipsToNodeWithUUIDQuery := &neoism.CypherQuery{
		Statement: `match (a:Thing{uuid:{uuid}})<-[r]-(b)
			    return distinct type(r) as relationship`,
		Parameters: map[string]interface{}{
			"uuid": uuid,
		},
		Result: &relationshipsToNodeWithUUID,
	}

	readQueries := []*neoism.CypherQuery{readRelationshipsFromNodeWithUUIDQuery, readRelationshipsToNodeWithUUIDQuery}

	err = cypherRunner.CypherBatch(readQueries)

	if err != nil {
		return nil, nil, err
	}

	return relationshipsFromNodeWithUUID, relationshipsToNodeWithUUID, nil
}

func constructTransferRelationshipsWithPlatformVersionFromNodeQuery(fromUUID string, toUUID string, predicate string) *neoism.CypherQuery {
	transferAnnotationsQuery := &neoism.CypherQuery{
		Statement: fmt.Sprintf(`MATCH (oldNode:Organisation {uuid:{fromUUID}})-[oldRel:%s]->(p)
					WHERE EXISTS(oldRel.platformVersion)
					MATCH (newNode:Organisation {uuid:{toUUID}})
					MERGE (newNode)-[newRel:%s{platformVersion:oldRel.platformVersion}]->(p)
					on create SET newRel = oldRel
					DELETE oldRel`, predicate, predicate),

		Parameters: map[string]interface{}{
			"fromUUID": fromUUID,
			"toUUID":   toUUID,
		},
	}
	return transferAnnotationsQuery
}

func constructTransferRelationshipsFromNodeQuery(fromUUID string, toUUID string, predicate string) *neoism.CypherQuery {
	transferAnnotationsQuery := &neoism.CypherQuery{
		Statement: fmt.Sprintf(`MATCH (oldNode:Organisation {uuid:{fromUUID}})-[oldRel:%s]->(p)
					WHERE NOT EXISTS(oldRel.platformVersion)
					MATCH (newNode:Organisation {uuid:{toUUID}})
					MERGE (newNode)-[newRel:%s]->(p)
					on create SET newRel = oldRel
					DELETE oldRel`, predicate, predicate),

		Parameters: map[string]interface{}{
			"fromUUID": fromUUID,
			"toUUID":   toUUID,
		},
	}
	return transferAnnotationsQuery
}

func constructTransferRelationshipsWithPlatformVersionToNodeQuery(fromUUID string, toUUID string, predicate string) *neoism.CypherQuery {
	transferAnnotationsQuery := &neoism.CypherQuery{
		Statement: fmt.Sprintf(`MATCH (oldNode:Organisation {uuid:{fromUUID}})<-[oldRel:%s]-(p)
					WHERE EXISTS(oldRel.platformVersion)
					MATCH (newNode:Organisation {uuid:{toUUID}})
					MERGE (newNode)<-[newRel:%s{platformVersion:oldRel.platformVersion}]-(p)
					ON create SET newRel = oldRel
					DELETE oldRel`, predicate, predicate),

		Parameters: map[string]interface{}{
			"fromUUID": fromUUID,
			"toUUID":   toUUID,
		},
	}
	return transferAnnotationsQuery
}

func constructTransferRelationshipsToNodeQuery(fromUUID string, toUUID string, predicate string) *neoism.CypherQuery {
	transferAnnotationsQuery := &neoism.CypherQuery{
		Statement: fmt.Sprintf(`MATCH (oldNode:Organisation {uuid:{fromUUID}})<-[oldRel:%s]-(p)
					WHERE not EXISTS(oldRel.platformVersion)
					MATCH (newNode:Organisation {uuid:{toUUID}})
					MERGE (newNode)<-[newRel:%s]-(p)
					ON CREATE SET newRel = oldRel
					DELETE oldRel`, predicate, predicate),

		Parameters: map[string]interface{}{
			"fromUUID": fromUUID,
			"toUUID":   toUUID,
		},
	}
	return transferAnnotationsQuery
}
<commit_msg>Changed Organisation to Thing<commit_after>package organisations

import (
	"fmt"
	"github.com/Financial-Times/neo-utils-go/neoutils"
	"github.com/jmcvetta/neoism"
)

type relationships []struct {
	RelationshipType string `json:"relationship"`
}

// TransferRelationships is responsible for moving relationships from node with sourceUUID to node with destinationUUID
func CreateTransferRelationshipsQueries(cypherRunner neoutils.CypherRunner, destinationUUID string, sourceUUID string) ([]*neoism.CypherQuery, error) {

	relationshipsFromSourceNode, relationshipsToSourceNode, err := getNodeRelationshipNames(cypherRunner, sourceUUID)
	if err != nil {
		return nil, err
	}

	writeQueries := []*neoism.CypherQuery{}
	for _, rel := range relationshipsFromSourceNode {
		transfQuery := constructTransferRelationshipsWithPlatformVersionFromNodeQuery(sourceUUID, destinationUUID, rel.RelationshipType)
		transfQuery2 := constructTransferRelationshipsFromNodeQuery(sourceUUID, destinationUUID, rel.RelationshipType)
		writeQueries = append(writeQueries, transfQuery, transfQuery2)
	}

	for _, rel := range relationshipsToSourceNode {
		transfQuery := constructTransferRelationshipsWithPlatformVersionToNodeQuery(sourceUUID, destinationUUID, rel.RelationshipType)
		transfQuery2 := constructTransferRelationshipsToNodeQuery(sourceUUID, destinationUUID, rel.RelationshipType)
		writeQueries = append(writeQueries, transfQuery, transfQuery2)
	}

	return writeQueries, nil
}

func getNodeRelationshipNames(cypherRunner neoutils.CypherRunner, uuid string) (relationshipsFromNodeWithUUID relationships, relationshipsToNodeWithUUID relationships, err error) {
	// find all the -> relationships
	relationshipsFromNodeWithUUID = relationships{}
	readRelationshipsFromNodeWithUUIDQuery := &neoism.CypherQuery{
		Statement: `match (a:Thing{uuid:{uuid}})-[r]->(b)
			    return distinct type(r) as relationship`,
		Parameters: map[string]interface{}{
			"uuid": uuid,
		},
		Result: &relationshipsFromNodeWithUUID,
	}

	// find all the <- relationships
	relationshipsToNodeWithUUID = relationships{}
	readRelationshipsToNodeWithUUIDQuery := &neoism.CypherQuery{
		Statement: `match (a:Thing{uuid:{uuid}})<-[r]-(b)
			    return distinct type(r) as relationship`,
		Parameters: map[string]interface{}{
			"uuid": uuid,
		},
		Result: &relationshipsToNodeWithUUID,
	}

	readQueries := []*neoism.CypherQuery{readRelationshipsFromNodeWithUUIDQuery, readRelationshipsToNodeWithUUIDQuery}

	err = cypherRunner.CypherBatch(readQueries)

	if err != nil {
		return nil, nil, err
	}

	return relationshipsFromNodeWithUUID, relationshipsToNodeWithUUID, nil
}

func constructTransferRelationshipsWithPlatformVersionFromNodeQuery(fromUUID string, toUUID string, predicate string) *neoism.CypherQuery {
	transferAnnotationsQuery := &neoism.CypherQuery{
		Statement: fmt.Sprintf(`MATCH (oldNode:Thing {uuid:{fromUUID}})-[oldRel:%s]->(p)
					WHERE EXISTS(oldRel.platformVersion)
					MATCH (newNode:Thing {uuid:{toUUID}})
					MERGE (newNode)-[newRel:%s{platformVersion:oldRel.platformVersion}]->(p)
					on create SET newRel = oldRel
					DELETE oldRel`, predicate, predicate),

		Parameters: map[string]interface{}{
			"fromUUID": fromUUID,
			"toUUID":   toUUID,
		},
	}
	return transferAnnotationsQuery
}

func constructTransferRelationshipsFromNodeQuery(fromUUID string, toUUID string, predicate string) *neoism.CypherQuery {
	transferAnnotationsQuery := &neoism.CypherQuery{
		Statement: fmt.Sprintf(`MATCH (oldNode:Thing {uuid:{fromUUID}})-[oldRel:%s]->(p)
					WHERE NOT EXISTS(oldRel.platformVersion)
					MATCH (newNode:Thing {uuid:{toUUID}})
					MERGE (newNode)-[newRel:%s]->(p)
					on create SET newRel = oldRel
					DELETE oldRel`, predicate, predicate),

		Parameters: map[string]interface{}{
			"fromUUID": fromUUID,
			"toUUID":   toUUID,
		},
	}
	return transferAnnotationsQuery
}

func constructTransferRelationshipsWithPlatformVersionToNodeQuery(fromUUID string, toUUID string, predicate string) *neoism.CypherQuery {
	transferAnnotationsQuery := &neoism.CypherQuery{
		Statement: fmt.Sprintf(`MATCH (oldNode:Thing {uuid:{fromUUID}})<-[oldRel:%s]-(p)
					WHERE EXISTS(oldRel.platformVersion)
					MATCH (newNode:Thing {uuid:{toUUID}})
					MERGE (newNode)<-[newRel:%s{platformVersion:oldRel.platformVersion}]-(p)
					ON create SET newRel = oldRel
					DELETE oldRel`, predicate, predicate),

		Parameters: map[string]interface{}{
			"fromUUID": fromUUID,
			"toUUID":   toUUID,
		},
	}
	return transferAnnotationsQuery
}

func constructTransferRelationshipsToNodeQuery(fromUUID string, toUUID string, predicate string) *neoism.CypherQuery {
	transferAnnotationsQuery := &neoism.CypherQuery{
		Statement: fmt.Sprintf(`MATCH (oldNode:Thing {uuid:{fromUUID}})<-[oldRel:%s]-(p)
					WHERE not EXISTS(oldRel.platformVersion)
					MATCH (newNode:Thing {uuid:{toUUID}})
					MERGE (newNode)<-[newRel:%s]-(p)
					ON CREATE SET newRel = oldRel
					DELETE oldRel`, predicate, predicate),

		Parameters: map[string]interface{}{
			"fromUUID": fromUUID,
			"toUUID":   toUUID,
		},
	}
	return transferAnnotationsQuery
}
<|endoftext|>


<commit_before>package file

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/hashicorp/errwrap"
	log "github.com/hashicorp/go-hclog"

	"github.com/hashicorp/vault/helper/consts"
	"github.com/hashicorp/vault/helper/jsonutil"
	"github.com/hashicorp/vault/physical"
)

// Verify FileBackend satisfies the correct interfaces
var _ physical.Backend = (*FileBackend)(nil)
var _ physical.Transactional = (*TransactionalFileBackend)(nil)
var _ physical.PseudoTransactional = (*FileBackend)(nil)

// FileBackend is a physical backend that stores data on disk
// at a given file path. It can be used for durable single server
// situations, or to develop locally where durability is not critical.
//
// WARNING: the file backend implementation is currently extremely unsafe
// and non-performant. It is meant mostly for local testing and development.
// It can be improved in the future.
type FileBackend struct {
	sync.RWMutex
	path       string
	logger     log.Logger
	permitPool *physical.PermitPool
}

type TransactionalFileBackend struct {
	FileBackend
}

type fileEntry struct {
	Value []byte
}

// NewFileBackend constructs a FileBackend using the given directory
func NewFileBackend(conf map[string]string, logger log.Logger) (physical.Backend, error) {
	path, ok := conf["path"]
	if !ok {
		return nil, fmt.Errorf("'path' must be set")
	}

	return &FileBackend{
		path:       path,
		logger:     logger,
		permitPool: physical.NewPermitPool(physical.DefaultParallelOperations),
	}, nil
}

func NewTransactionalFileBackend(conf map[string]string, logger log.Logger) (physical.Backend, error) {
	path, ok := conf["path"]
	if !ok {
		return nil, fmt.Errorf("'path' must be set")
	}

	// Create a pool of size 1 so only one operation runs at a time
	return &TransactionalFileBackend{
		FileBackend: FileBackend{
			path:       path,
			logger:     logger,
			permitPool: physical.NewPermitPool(1),
		},
	}, nil
}

func (b *FileBackend) Delete(ctx context.Context, path string) error {
	b.permitPool.Acquire()
	defer b.permitPool.Release()

	b.Lock()
	defer b.Unlock()

	return b.DeleteInternal(ctx, path)
}

func (b *FileBackend) DeleteInternal(ctx context.Context, path string) error {
	if path == "" {
		return nil
	}

	if err := b.validatePath(path); err != nil {
		return err
	}

	basePath, key := b.expandPath(path)
	fullPath := filepath.Join(basePath, key)

	err := os.Remove(fullPath)
	if err != nil && !os.IsNotExist(err) {
		return errwrap.Wrapf(fmt.Sprintf("failed to remove %q: {{err}}", fullPath), err)
	}

	err = b.cleanupLogicalPath(path)

	return err
}

// cleanupLogicalPath is used to remove all empty nodes, beginning with deepest
// one, aborting on first non-empty one, up to top-level node.
func (b *FileBackend) cleanupLogicalPath(path string) error {
	nodes := strings.Split(path, fmt.Sprintf("%c", os.PathSeparator))
	for i := len(nodes) - 1; i > 0; i-- {
		fullPath := filepath.Join(b.path, filepath.Join(nodes[:i]...))

		dir, err := os.Open(fullPath)
		if err != nil {
			if dir != nil {
				dir.Close()
			}
			if os.IsNotExist(err) {
				return nil
			} else {
				return err
			}
		}

		list, err := dir.Readdir(1)
		dir.Close()
		if err != nil && err != io.EOF {
			return err
		}

		// If we have no entries, it's an empty directory; remove it
		if err == io.EOF || list == nil || len(list) == 0 {
			err = os.Remove(fullPath)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

func (b *FileBackend) Get(ctx context.Context, k string) (*physical.Entry, error) {
	b.permitPool.Acquire()
	defer b.permitPool.Release()

	b.RLock()
	defer b.RUnlock()

	return b.GetInternal(ctx, k)
}

func (b *FileBackend) GetInternal(ctx context.Context, k string) (*physical.Entry, error) {
	if err := b.validatePath(k); err != nil {
		return nil, err
	}

	path, key := b.expandPath(k)
	path = filepath.Join(path, key)

	f, err := os.Open(path)
	if f != nil {
		defer f.Close()
	}
	if err != nil {
		if os.IsNotExist(err) {
			return nil, nil
		}

		return nil, err
	}

	var entry fileEntry
	if err := jsonutil.DecodeJSONFromReader(f, &entry); err != nil {
		return nil, err
	}

	return &physical.Entry{
		Key:   k,
		Value: entry.Value,
	}, nil
}

func (b *FileBackend) Put(ctx context.Context, entry *physical.Entry) error {
	b.permitPool.Acquire()
	defer b.permitPool.Release()

	b.Lock()
	defer b.Unlock()

	return b.PutInternal(ctx, entry)
}

func (b *FileBackend) PutInternal(ctx context.Context, entry *physical.Entry) error {
	if err := b.validatePath(entry.Key); err != nil {
		return err
	}

	path, key := b.expandPath(entry.Key)

	// Make the parent tree
	if err := os.MkdirAll(path, 0700); err != nil {
		return err
	}

	// JSON encode the entry and write it
	f, err := os.OpenFile(
		filepath.Join(path, key),
		os.O_CREATE|os.O_TRUNC|os.O_WRONLY,
		0600)
	if f != nil {
		defer f.Close()
	}
	if err != nil {
		return err
	}
	enc := json.NewEncoder(f)
	return enc.Encode(&fileEntry{
		Value: entry.Value,
	})
}

func (b *FileBackend) List(ctx context.Context, prefix string) ([]string, error) {
	b.permitPool.Acquire()
	defer b.permitPool.Release()

	b.RLock()
	defer b.RUnlock()

	return b.ListInternal(prefix)
}

func (b *FileBackend) ListInternal(prefix string) ([]string, error) {
	if err := b.validatePath(prefix); err != nil {
		return nil, err
	}

	path := b.path
	if prefix != "" {
		path = filepath.Join(path, prefix)
	}

	// Read the directory contents
	f, err := os.Open(path)
	if f != nil {
		defer f.Close()
	}
	if err != nil {
		if os.IsNotExist(err) {
			return nil, nil
		}

		return nil, err
	}

	names, err := f.Readdirnames(-1)
	if err != nil {
		return nil, err
	}

	for i, name := range names {
		fi, err := os.Stat(filepath.Join(path, name))
		if err != nil {
			return nil, err
		}
		if fi.IsDir() {
			names[i] = name + "/"
		} else {
			if name[0] == '_' {
				names[i] = name[1:]
			}
		}
	}

	return names, nil
}

func (b *FileBackend) expandPath(k string) (string, string) {
	path := filepath.Join(b.path, k)
	key := filepath.Base(path)
	path = filepath.Dir(path)
	return path, "_" + key
}

func (b *FileBackend) validatePath(path string) error {
	switch {
	case strings.Contains(path, ".."):
		return consts.ErrPathContainsParentReferences
	}

	return nil
}

func (b *TransactionalFileBackend) Transaction(ctx context.Context, txns []*physical.TxnEntry) error {
	b.permitPool.Acquire()
	defer b.permitPool.Release()

	b.Lock()
	defer b.Unlock()

	return physical.GenericTransactionHandler(ctx, b, txns)
}
<commit_msg>Do some best-effort cleanup in file backend (#4684)<commit_after>package file

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/hashicorp/errwrap"
	log "github.com/hashicorp/go-hclog"

	"github.com/hashicorp/vault/helper/consts"
	"github.com/hashicorp/vault/helper/jsonutil"
	"github.com/hashicorp/vault/physical"
)

// Verify FileBackend satisfies the correct interfaces
var _ physical.Backend = (*FileBackend)(nil)
var _ physical.Transactional = (*TransactionalFileBackend)(nil)
var _ physical.PseudoTransactional = (*FileBackend)(nil)

// FileBackend is a physical backend that stores data on disk
// at a given file path. It can be used for durable single server
// situations, or to develop locally where durability is not critical.
//
// WARNING: the file backend implementation is currently extremely unsafe
// and non-performant. It is meant mostly for local testing and development.
// It can be improved in the future.
type FileBackend struct {
	sync.RWMutex
	path       string
	logger     log.Logger
	permitPool *physical.PermitPool
}

type TransactionalFileBackend struct {
	FileBackend
}

type fileEntry struct {
	Value []byte
}

// NewFileBackend constructs a FileBackend using the given directory
func NewFileBackend(conf map[string]string, logger log.Logger) (physical.Backend, error) {
	path, ok := conf["path"]
	if !ok {
		return nil, fmt.Errorf("'path' must be set")
	}

	return &FileBackend{
		path:       path,
		logger:     logger,
		permitPool: physical.NewPermitPool(physical.DefaultParallelOperations),
	}, nil
}

func NewTransactionalFileBackend(conf map[string]string, logger log.Logger) (physical.Backend, error) {
	path, ok := conf["path"]
	if !ok {
		return nil, fmt.Errorf("'path' must be set")
	}

	// Create a pool of size 1 so only one operation runs at a time
	return &TransactionalFileBackend{
		FileBackend: FileBackend{
			path:       path,
			logger:     logger,
			permitPool: physical.NewPermitPool(1),
		},
	}, nil
}

func (b *FileBackend) Delete(ctx context.Context, path string) error {
	b.permitPool.Acquire()
	defer b.permitPool.Release()

	b.Lock()
	defer b.Unlock()

	return b.DeleteInternal(ctx, path)
}

func (b *FileBackend) DeleteInternal(ctx context.Context, path string) error {
	if path == "" {
		return nil
	}

	if err := b.validatePath(path); err != nil {
		return err
	}

	basePath, key := b.expandPath(path)
	fullPath := filepath.Join(basePath, key)

	err := os.Remove(fullPath)
	if err != nil && !os.IsNotExist(err) {
		return errwrap.Wrapf(fmt.Sprintf("failed to remove %q: {{err}}", fullPath), err)
	}

	err = b.cleanupLogicalPath(path)

	return err
}

// cleanupLogicalPath is used to remove all empty nodes, beginning with deepest
// one, aborting on first non-empty one, up to top-level node.
func (b *FileBackend) cleanupLogicalPath(path string) error {
	nodes := strings.Split(path, fmt.Sprintf("%c", os.PathSeparator))
	for i := len(nodes) - 1; i > 0; i-- {
		fullPath := filepath.Join(b.path, filepath.Join(nodes[:i]...))

		dir, err := os.Open(fullPath)
		if err != nil {
			if dir != nil {
				dir.Close()
			}
			if os.IsNotExist(err) {
				return nil
			} else {
				return err
			}
		}

		list, err := dir.Readdir(1)
		dir.Close()
		if err != nil && err != io.EOF {
			return err
		}

		// If we have no entries, it's an empty directory; remove it
		if err == io.EOF || list == nil || len(list) == 0 {
			err = os.Remove(fullPath)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

func (b *FileBackend) Get(ctx context.Context, k string) (*physical.Entry, error) {
	b.permitPool.Acquire()
	defer b.permitPool.Release()

	b.RLock()
	defer b.RUnlock()

	return b.GetInternal(ctx, k)
}

func (b *FileBackend) GetInternal(ctx context.Context, k string) (*physical.Entry, error) {
	if err := b.validatePath(k); err != nil {
		return nil, err
	}

	path, key := b.expandPath(k)
	path = filepath.Join(path, key)

	// If we stat it and it exists but is size zero, it may be left from some
	// previous FS error like out-of-space. No Vault entry will ever be zero
	// length, so simply remove it and return nil.
	fi, err := os.Stat(path)
	if err == nil {
		if fi.Size() == 0 {
			// Best effort, ignore errors
			os.Remove(path)
			return nil, nil
		}
	}

	f, err := os.Open(path)
	if f != nil {
		defer f.Close()
	}
	if err != nil {
		if os.IsNotExist(err) {
			return nil, nil
		}

		return nil, err
	}

	var entry fileEntry
	if err := jsonutil.DecodeJSONFromReader(f, &entry); err != nil {
		return nil, err
	}

	return &physical.Entry{
		Key:   k,
		Value: entry.Value,
	}, nil
}

func (b *FileBackend) Put(ctx context.Context, entry *physical.Entry) error {
	b.permitPool.Acquire()
	defer b.permitPool.Release()

	b.Lock()
	defer b.Unlock()

	return b.PutInternal(ctx, entry)
}

func (b *FileBackend) PutInternal(ctx context.Context, entry *physical.Entry) error {
	if err := b.validatePath(entry.Key); err != nil {
		return err
	}

	path, key := b.expandPath(entry.Key)

	// Make the parent tree
	if err := os.MkdirAll(path, 0700); err != nil {
		return err
	}

	// JSON encode the entry and write it
	fullPath := filepath.Join(path, key)
	f, err := os.OpenFile(
		fullPath,
		os.O_CREATE|os.O_TRUNC|os.O_WRONLY,
		0600)
	if err != nil {
		if f != nil {
			f.Close()
		}
		return err
	}
	if f == nil {
		return errors.New("could not successfully get a file handle")
	}

	enc := json.NewEncoder(f)
	encErr := enc.Encode(&fileEntry{
		Value: entry.Value,
	})
	f.Close()
	if encErr == nil {
		return nil
	}

	// Everything below is best-effort and will result in encErr being returned

	// See if we ended up with a zero-byte file and if so delete it, might be a
	// case of disk being full but the file info is in metadata that is
	// reserved.
	fi, err := os.Stat(fullPath)
	if err != nil {
		return encErr
	}
	if fi == nil {
		return encErr
	}
	if fi.Size() == 0 {
		os.Remove(fullPath)
	}
	return encErr
}

func (b *FileBackend) List(ctx context.Context, prefix string) ([]string, error) {
	b.permitPool.Acquire()
	defer b.permitPool.Release()

	b.RLock()
	defer b.RUnlock()

	return b.ListInternal(prefix)
}

func (b *FileBackend) ListInternal(prefix string) ([]string, error) {
	if err := b.validatePath(prefix); err != nil {
		return nil, err
	}

	path := b.path
	if prefix != "" {
		path = filepath.Join(path, prefix)
	}

	// Read the directory contents
	f, err := os.Open(path)
	if f != nil {
		defer f.Close()
	}
	if err != nil {
		if os.IsNotExist(err) {
			return nil, nil
		}

		return nil, err
	}

	names, err := f.Readdirnames(-1)
	if err != nil {
		return nil, err
	}

	for i, name := range names {
		fi, err := os.Stat(filepath.Join(path, name))
		if err != nil {
			return nil, err
		}
		if fi.IsDir() {
			names[i] = name + "/"
		} else {
			if name[0] == '_' {
				names[i] = name[1:]
			}
		}
	}

	return names, nil
}

func (b *FileBackend) expandPath(k string) (string, string) {
	path := filepath.Join(b.path, k)
	key := filepath.Base(path)
	path = filepath.Dir(path)
	return path, "_" + key
}

func (b *FileBackend) validatePath(path string) error {
	switch {
	case strings.Contains(path, ".."):
		return consts.ErrPathContainsParentReferences
	}

	return nil
}

func (b *TransactionalFileBackend) Transaction(ctx context.Context, txns []*physical.TxnEntry) error {
	b.permitPool.Acquire()
	defer b.permitPool.Release()

	b.Lock()
	defer b.Unlock()

	return physical.GenericTransactionHandler(ctx, b, txns)
}
<|endoftext|>


<commit_before>package somatree

type SomaTreeAttacher interface {
	Attach(a AttachRequest)
	Destroy()
	Detach()

	SomaTreePropertier
	Checker

	clearParent()
	setFault(f *SomaTreeElemFault)
	setParent(p SomaTreeReceiver)
	updateFaultRecursive(f *SomaTreeElemFault)
	updateParentRecursive(p SomaTreeReceiver)
}

// implemented by: repository
type SomaTreeRootAttacher interface {
	SomaTreeAttacher
	GetName() string
	attachToRoot(a AttachRequest)
}

// implemented by: buckets
type SomaTreeRepositoryAttacher interface {
	SomaTreeAttacher
	GetName() string
	attachToRepository(a AttachRequest)
	CloneRepository() SomaTreeRepositoryAttacher
	setActionDeep(c chan *Action)
}

// implemented by: groups, clusters, nodes
type SomaTreeBucketAttacher interface {
	SomaTreeAttacher
	GetName() string
	attachToBucket(a AttachRequest)
	CloneBucket() SomaTreeBucketAttacher
	ReAttach(a AttachRequest)
	setActionDeep(c chan *Action)
}

// implemented by: groups, clusters, nodes
type SomaTreeGroupAttacher interface {
	SomaTreeAttacher
	GetName() string
	attachToGroup(a AttachRequest)
	CloneGroup() SomaTreeGroupAttacher
	ReAttach(a AttachRequest)
	setActionDeep(c chan *Action)
}

// implemented by: nodes
type SomaTreeClusterAttacher interface {
	SomaTreeAttacher
	GetName() string
	attachToCluster(a AttachRequest)
	CloneCluster() SomaTreeClusterAttacher
	ReAttach(a AttachRequest)
	setActionDeep(c chan *Action)
}

type AttachRequest struct {
	Root       SomaTreeReceiver
	ParentType string
	ParentId   string
	ParentName string
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<commit_msg>Tighten up Attacher interface<commit_after>package somatree

type SomaTreeAttacher interface {
	Attach(a AttachRequest)
	Destroy()
	Detach()

	SomaTreePropertier
	Checker

	GetName() string
	setActionDeep(c chan *Action)

	clearParent()
	setFault(f *SomaTreeElemFault)
	setParent(p SomaTreeReceiver)
	updateFaultRecursive(f *SomaTreeElemFault)
	updateParentRecursive(p SomaTreeReceiver)
}

// implemented by: repository
type SomaTreeRootAttacher interface {
	SomaTreeAttacher
	attachToRoot(a AttachRequest)
}

// implemented by: buckets
type SomaTreeRepositoryAttacher interface {
	SomaTreeAttacher
	attachToRepository(a AttachRequest)
	CloneRepository() SomaTreeRepositoryAttacher
}

// implemented by: groups, clusters, nodes
type SomaTreeBucketAttacher interface {
	SomaTreeAttacher
	attachToBucket(a AttachRequest)
	CloneBucket() SomaTreeBucketAttacher
	ReAttach(a AttachRequest)
}

// implemented by: groups, clusters, nodes
type SomaTreeGroupAttacher interface {
	SomaTreeAttacher
	attachToGroup(a AttachRequest)
	CloneGroup() SomaTreeGroupAttacher
	ReAttach(a AttachRequest)
}

// implemented by: nodes
type SomaTreeClusterAttacher interface {
	SomaTreeAttacher
	attachToCluster(a AttachRequest)
	CloneCluster() SomaTreeClusterAttacher
	ReAttach(a AttachRequest)
}

type AttachRequest struct {
	Root       SomaTreeReceiver
	ParentType string
	ParentId   string
	ParentName string
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<|endoftext|>


<commit_before>// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package models

import (
	"fmt"
	"sort"
	"strings"
	"time"

	"github.com/go-xorm/xorm"

	"github.com/gogits/git-module"

	"github.com/gogits/gogs/modules/process"
)

// Release represents a release of repository.
type Release struct {
	ID               int64 `xorm:"pk autoincr"`
	RepoID           int64
	PublisherID      int64
	Publisher        *User `xorm:"-"`
	TagName          string
	LowerTagName     string
	Target           string
	Title            string
	Sha1             string `xorm:"VARCHAR(40)"`
	NumCommits       int64
	NumCommitsBehind int64  `xorm:"-"`
	Note             string `xorm:"TEXT"`
	IsDraft          bool   `xorm:"NOT NULL DEFAULT false"`
	IsPrerelease     bool

	Created     time.Time `xorm:"-"`
	CreatedUnix int64
}

func (r *Release) BeforeInsert() {
	r.CreatedUnix = time.Now().Unix()
}

func (r *Release) AfterSet(colName string, _ xorm.Cell) {
	switch colName {
	case "created_unix":
		r.Created = time.Unix(r.CreatedUnix, 0).Local()
	}
}

// IsReleaseExist returns true if release with given tag name already exists.
func IsReleaseExist(repoID int64, tagName string) (bool, error) {
	if len(tagName) == 0 {
		return false, nil
	}

	return x.Get(&Release{RepoID: repoID, LowerTagName: strings.ToLower(tagName)})
}

func createTag(gitRepo *git.Repository, rel *Release) error {
	// Only actual create when publish.
	if !rel.IsDraft {
		if !gitRepo.IsTagExist(rel.TagName) {
			commit, err := gitRepo.GetBranchCommit(rel.Target)
			if err != nil {
				return fmt.Errorf("GetBranchCommit: %v", err)
			}

			// Trim '--' prefix to prevent command line argument vulnerability.
			rel.TagName = strings.TrimPrefix(rel.TagName, "--")
			if err = gitRepo.CreateTag(rel.TagName, commit.ID.String()); err != nil {
				if strings.Contains(err.Error(), "is not a valid tag name") {
					return ErrInvalidTagName{rel.TagName}
				}
				return err
			}
		} else {
			commit, err := gitRepo.GetTagCommit(rel.TagName)
			if err != nil {
				return fmt.Errorf("GetTagCommit: %v", err)
			}

			rel.NumCommits, err = commit.CommitsCount()
			if err != nil {
				return fmt.Errorf("CommitsCount: %v", err)
			}
		}
	}
	return nil
}

// CreateRelease creates a new release of repository.
func CreateRelease(gitRepo *git.Repository, rel *Release) error {
	isExist, err := IsReleaseExist(rel.RepoID, rel.TagName)
	if err != nil {
		return err
	} else if isExist {
		return ErrReleaseAlreadyExist{rel.TagName}
	}

	if err = createTag(gitRepo, rel); err != nil {
		return err
	}
	rel.LowerTagName = strings.ToLower(rel.TagName)
	_, err = x.InsertOne(rel)
	return err
}

// GetRelease returns release by given ID.
func GetRelease(repoID int64, tagName string) (*Release, error) {
	isExist, err := IsReleaseExist(repoID, tagName)
	if err != nil {
		return nil, err
	} else if !isExist {
		return nil, ErrReleaseNotExist{0, tagName}
	}

	rel := &Release{RepoID: repoID, LowerTagName: strings.ToLower(tagName)}
	_, err = x.Get(rel)
	return rel, err
}

// GetReleaseByID returns release with given ID.
func GetReleaseByID(id int64) (*Release, error) {
	rel := new(Release)
	has, err := x.Id(id).Get(rel)
	if err != nil {
		return nil, err
	} else if !has {
		return nil, ErrReleaseNotExist{id, ""}
	}

	return rel, nil
}

// GetReleasesByRepoID returns a list of releases of repository.
func GetReleasesByRepoID(repoID int64) (rels []*Release, err error) {
	err = x.Desc("created_unix").Find(&rels, Release{RepoID: repoID})
	return rels, err
}

type ReleaseSorter struct {
	rels []*Release
}

func (rs *ReleaseSorter) Len() int {
	return len(rs.rels)
}

func (rs *ReleaseSorter) Less(i, j int) bool {
	diffNum := rs.rels[i].NumCommits - rs.rels[j].NumCommits
	if diffNum != 0 {
		return diffNum > 0
	}
	return rs.rels[i].Created.After(rs.rels[j].Created)
}

func (rs *ReleaseSorter) Swap(i, j int) {
	rs.rels[i], rs.rels[j] = rs.rels[j], rs.rels[i]
}

// SortReleases sorts releases by number of commits and created time.
func SortReleases(rels []*Release) {
	sorter := &ReleaseSorter{rels: rels}
	sort.Sort(sorter)
}

// UpdateRelease updates information of a release.
func UpdateRelease(gitRepo *git.Repository, rel *Release) (err error) {
	if err = createTag(gitRepo, rel); err != nil {
		return err
	}
	_, err = x.Id(rel.ID).AllCols().Update(rel)
	return err
}

// DeleteReleaseByID deletes a release and corresponding Git tag by given ID.
func DeleteReleaseByID(id int64) error {
	rel, err := GetReleaseByID(id)
	if err != nil {
		return fmt.Errorf("GetReleaseByID: %v", err)
	}

	repo, err := GetRepositoryByID(rel.RepoID)
	if err != nil {
		return fmt.Errorf("GetRepositoryByID: %v", err)
	}

	_, stderr, err := process.ExecDir(-1, repo.RepoPath(),
		fmt.Sprintf("DeleteReleaseByID (git tag -d): %d", rel.ID),
		"git", "tag", "-d", rel.TagName)
	if err != nil && !strings.Contains(stderr, "not found") {
		return fmt.Errorf("git tag -d: %v - %s", err, stderr)
	}

	if _, err = x.Id(rel.ID).Delete(new(Release)); err != nil {
		return fmt.Errorf("Delete: %v", err)
	}

	return nil
}
<commit_msg>models/release: Update Sha1 if tag already exists (#3331)<commit_after>// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package models

import (
	"fmt"
	"sort"
	"strings"
	"time"

	"github.com/go-xorm/xorm"

	"github.com/gogits/git-module"

	"github.com/gogits/gogs/modules/process"
)

// Release represents a release of repository.
type Release struct {
	ID               int64 `xorm:"pk autoincr"`
	RepoID           int64
	PublisherID      int64
	Publisher        *User `xorm:"-"`
	TagName          string
	LowerTagName     string
	Target           string
	Title            string
	Sha1             string `xorm:"VARCHAR(40)"`
	NumCommits       int64
	NumCommitsBehind int64  `xorm:"-"`
	Note             string `xorm:"TEXT"`
	IsDraft          bool   `xorm:"NOT NULL DEFAULT false"`
	IsPrerelease     bool

	Created     time.Time `xorm:"-"`
	CreatedUnix int64
}

func (r *Release) BeforeInsert() {
	r.CreatedUnix = time.Now().Unix()
}

func (r *Release) AfterSet(colName string, _ xorm.Cell) {
	switch colName {
	case "created_unix":
		r.Created = time.Unix(r.CreatedUnix, 0).Local()
	}
}

// IsReleaseExist returns true if release with given tag name already exists.
func IsReleaseExist(repoID int64, tagName string) (bool, error) {
	if len(tagName) == 0 {
		return false, nil
	}

	return x.Get(&Release{RepoID: repoID, LowerTagName: strings.ToLower(tagName)})
}

func createTag(gitRepo *git.Repository, rel *Release) error {
	// Only actual create when publish.
	if !rel.IsDraft {
		if !gitRepo.IsTagExist(rel.TagName) {
			commit, err := gitRepo.GetBranchCommit(rel.Target)
			if err != nil {
				return fmt.Errorf("GetBranchCommit: %v", err)
			}

			// Trim '--' prefix to prevent command line argument vulnerability.
			rel.TagName = strings.TrimPrefix(rel.TagName, "--")
			if err = gitRepo.CreateTag(rel.TagName, commit.ID.String()); err != nil {
				if strings.Contains(err.Error(), "is not a valid tag name") {
					return ErrInvalidTagName{rel.TagName}
				}
				return err
			}
		} else {
			commit, err := gitRepo.GetTagCommit(rel.TagName)
			if err != nil {
				return fmt.Errorf("GetTagCommit: %v", err)
			}

			rel.Sha1 = commit.ID.String()
			rel.NumCommits, err = commit.CommitsCount()
			if err != nil {
				return fmt.Errorf("CommitsCount: %v", err)
			}
		}
	}
	return nil
}

// CreateRelease creates a new release of repository.
func CreateRelease(gitRepo *git.Repository, rel *Release) error {
	isExist, err := IsReleaseExist(rel.RepoID, rel.TagName)
	if err != nil {
		return err
	} else if isExist {
		return ErrReleaseAlreadyExist{rel.TagName}
	}

	if err = createTag(gitRepo, rel); err != nil {
		return err
	}
	rel.LowerTagName = strings.ToLower(rel.TagName)
	_, err = x.InsertOne(rel)
	return err
}

// GetRelease returns release by given ID.
func GetRelease(repoID int64, tagName string) (*Release, error) {
	isExist, err := IsReleaseExist(repoID, tagName)
	if err != nil {
		return nil, err
	} else if !isExist {
		return nil, ErrReleaseNotExist{0, tagName}
	}

	rel := &Release{RepoID: repoID, LowerTagName: strings.ToLower(tagName)}
	_, err = x.Get(rel)
	return rel, err
}

// GetReleaseByID returns release with given ID.
func GetReleaseByID(id int64) (*Release, error) {
	rel := new(Release)
	has, err := x.Id(id).Get(rel)
	if err != nil {
		return nil, err
	} else if !has {
		return nil, ErrReleaseNotExist{id, ""}
	}

	return rel, nil
}

// GetReleasesByRepoID returns a list of releases of repository.
func GetReleasesByRepoID(repoID int64) (rels []*Release, err error) {
	err = x.Desc("created_unix").Find(&rels, Release{RepoID: repoID})
	return rels, err
}

type ReleaseSorter struct {
	rels []*Release
}

func (rs *ReleaseSorter) Len() int {
	return len(rs.rels)
}

func (rs *ReleaseSorter) Less(i, j int) bool {
	diffNum := rs.rels[i].NumCommits - rs.rels[j].NumCommits
	if diffNum != 0 {
		return diffNum > 0
	}
	return rs.rels[i].Created.After(rs.rels[j].Created)
}

func (rs *ReleaseSorter) Swap(i, j int) {
	rs.rels[i], rs.rels[j] = rs.rels[j], rs.rels[i]
}

// SortReleases sorts releases by number of commits and created time.
func SortReleases(rels []*Release) {
	sorter := &ReleaseSorter{rels: rels}
	sort.Sort(sorter)
}

// UpdateRelease updates information of a release.
func UpdateRelease(gitRepo *git.Repository, rel *Release) (err error) {
	if err = createTag(gitRepo, rel); err != nil {
		return err
	}
	_, err = x.Id(rel.ID).AllCols().Update(rel)
	return err
}

// DeleteReleaseByID deletes a release and corresponding Git tag by given ID.
func DeleteReleaseByID(id int64) error {
	rel, err := GetReleaseByID(id)
	if err != nil {
		return fmt.Errorf("GetReleaseByID: %v", err)
	}

	repo, err := GetRepositoryByID(rel.RepoID)
	if err != nil {
		return fmt.Errorf("GetRepositoryByID: %v", err)
	}

	_, stderr, err := process.ExecDir(-1, repo.RepoPath(),
		fmt.Sprintf("DeleteReleaseByID (git tag -d): %d", rel.ID),
		"git", "tag", "-d", rel.TagName)
	if err != nil && !strings.Contains(stderr, "not found") {
		return fmt.Errorf("git tag -d: %v - %s", err, stderr)
	}

	if _, err = x.Id(rel.ID).Delete(new(Release)); err != nil {
		return fmt.Errorf("Delete: %v", err)
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2019 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package module

import (
	"context"
	"fmt"
	"sync"
)

// ServiceFunc 服务实际需要执行的函数
//
// 实现者需要正确处理 ctx.Done 事件，调用者可能会主动取消函数执行；
// 如果是通 ctx 取消的，应该返回其错误信息。
type ServiceFunc func(ctx context.Context) error

// ServiceState 服务的状态值
type ServiceState int8

// 几种可能的状态值
const (
	ServiceStop    ServiceState = iota + 1 // 当前处理停止状态，默认状态
	ServiceRunning                         // 正在运行
	ServiceFailed                          // 出错，不再执行后续操作
)

// Service 服务模型
type Service struct {
	ID    int
	Title string

	state      ServiceState
	f          ServiceFunc
	cancelFunc context.CancelFunc
	locker     sync.Mutex

	err error // 保存上次的出错内容
}

// AddService 添加新的服务
func (m *Module) AddService(f ServiceFunc, title string) {
	m.AddInit(func() error {
		m.ms.services = append(m.ms.services, &Service{
			//TODO ID:0,
			Title: title,
			state: ServiceStop,
			f:     f,
		})
		return nil
	}, "注册服务："+title)
}

// State 获取当前服务的状态
func (srv *Service) State() ServiceState {
	return srv.state
}

// Err 上次的错误信息，不会清空。
func (srv *Service) Err() error {
	return srv.err
}

// Run 开始执行该服务
func (srv *Service) Run() {
	srv.locker.Lock()
	defer srv.locker.Unlock()

	if srv.state != ServiceRunning {
		go srv.serve()
	}
}

func (srv *Service) serve() {
	defer func() {
		if msg := recover(); msg != nil {
			srv.err = fmt.Errorf("panic:%v", msg)
			srv.state = ServiceFailed
		}
	}()

	ctx := context.Background()
	ctx, srv.cancelFunc = context.WithCancel(ctx)
	srv.state = ServiceRunning

	err := srv.f(ctx)
	if err != nil && err != context.Canceled {
		srv.err = err
		srv.state = ServiceFailed
		return
	}

	srv.state = ServiceStop
}

// Stop 停止服务。
func (srv *Service) Stop() {
	srv.locker.Lock()
	defer srv.locker.Unlock()

	if srv.state != ServiceRunning {
		return
	}

	if srv.cancelFunc != nil {
		srv.cancelFunc()
		srv.cancelFunc = nil
	}

	srv.state = ServiceStop
}
<commit_msg>[module] 去掉 Service.ID 不是必须的要素<commit_after>// Copyright 2019 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package module

import (
	"context"
	"fmt"
	"sync"
)

// ServiceFunc 服务实际需要执行的函数
//
// 实现者需要正确处理 ctx.Done 事件，调用者可能会主动取消函数执行；
// 如果是通 ctx 取消的，应该返回其错误信息。
type ServiceFunc func(ctx context.Context) error

// ServiceState 服务的状态值
type ServiceState int8

// 几种可能的状态值
const (
	ServiceStop    ServiceState = iota + 1 // 当前处理停止状态，默认状态
	ServiceRunning                         // 正在运行
	ServiceFailed                          // 出错，不再执行后续操作
)

// Service 服务模型
type Service struct {
	Title string

	state      ServiceState
	f          ServiceFunc
	cancelFunc context.CancelFunc
	locker     sync.Mutex

	err error // 保存上次的出错内容
}

// AddService 添加新的服务
func (m *Module) AddService(f ServiceFunc, title string) {
	m.AddInit(func() error {
		m.ms.services = append(m.ms.services, &Service{
			Title: title,
			state: ServiceStop,
			f:     f,
		})
		return nil
	}, "注册服务："+title)
}

// State 获取当前服务的状态
func (srv *Service) State() ServiceState {
	return srv.state
}

// Err 上次的错误信息，不会清空。
func (srv *Service) Err() error {
	return srv.err
}

// Run 开始执行该服务
func (srv *Service) Run() {
	srv.locker.Lock()
	defer srv.locker.Unlock()

	if srv.state != ServiceRunning {
		go srv.serve()
	}
}

func (srv *Service) serve() {
	defer func() {
		if msg := recover(); msg != nil {
			srv.err = fmt.Errorf("panic:%v", msg)
			srv.state = ServiceFailed
		}
	}()

	ctx := context.Background()
	ctx, srv.cancelFunc = context.WithCancel(ctx)
	srv.state = ServiceRunning

	err := srv.f(ctx)
	if err != nil && err != context.Canceled {
		srv.err = err
		srv.state = ServiceFailed
		return
	}

	srv.state = ServiceStop
}

// Stop 停止服务。
func (srv *Service) Stop() {
	srv.locker.Lock()
	defer srv.locker.Unlock()

	if srv.state != ServiceRunning {
		return
	}

	if srv.cancelFunc != nil {
		srv.cancelFunc()
		srv.cancelFunc = nil
	}

	srv.state = ServiceStop
}
<|endoftext|>


<commit_before>package main

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

type Module3rdTestSuite struct {
	suite.Suite
	modules3rd []Module3rd
}

func (suite *Module3rdTestSuite) SetupTest() {
	modules3rdConf := "./config/modules.cfg.example"
	modules3rd, err := loadModules3rdFile(modules3rdConf)
	if err != nil {
		fmt.Printf("Failed to load %s\n", modules3rdConf)
		os.Exit(1)
	}
	suite.modules3rd = modules3rd
}

func (suite *Module3rdTestSuite) TestModules3rd() {
	for _, m := range suite.modules3rd {
		switch m.Name {
		case "echo-nginx-module":
			assert.Equal(suite.T(), m.Name, "echo-nginx-module")
			assert.Equal(suite.T(), m.Url, "https://github.com/openresty/echo-nginx-module.git")
			assert.Equal(suite.T(), m.Rev, "v0.53")
		case "headers-more-nginx-module":
			assert.Equal(suite.T(), m.Name, "headers-more-nginx-module")
			assert.Equal(suite.T(), m.Url, "https://github.com/openresty/headers-more-nginx-module.git")
			assert.Equal(suite.T(), m.Rev, "v0.25")
		case "ngx_devel_kit":
			assert.Equal(suite.T(), m.Name, "ngx_devel_kit")
			assert.Equal(suite.T(), m.Url, "https://github.com/simpl/ngx_devel_kit")
			assert.Equal(suite.T(), m.Rev, "v0.2.19")
		case "ngx_info":
			assert.Equal(suite.T(), m.Name, "ngx_info")
			assert.Equal(suite.T(), m.Url, "https://github.com/cubicdaiya/ngx_info")
			assert.Equal(suite.T(), m.Rev, "")
		case "ngx_dosdetector":
			assert.Equal(suite.T(), m.Name, "ngx_dosdetector")
			assert.Equal(suite.T(), m.Url, "https://github.com/cubicdaiya/ngx_dosdetector")
			assert.Equal(suite.T(), m.Rev, "")
		case "ngx_access_token":
			assert.Equal(suite.T(), m.Name, "ngx_access_token")
			assert.Equal(suite.T(), m.Url, "https://github.com/cubicdaiya/ngx_access_token")
			assert.Equal(suite.T(), m.Rev, "")
		case "ngx_small_light":
			assert.Equal(suite.T(), m.Name, "ngx_small_light")
			assert.Equal(suite.T(), m.Url, "https://github.com/cubicdaiya/ngx_small_light")
			assert.Equal(suite.T(), m.Rev, "v0.5.2")
			assert.Equal(suite.T(), m.Shprov, "./setup --with-gd")
		}
	}
}

func TestModule3rdTestSuite(t *testing.T) {
	suite.Run(t, new(Module3rdTestSuite))
}
<commit_msg>fixed failure to test<commit_after>package main

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

type Module3rdTestSuite struct {
	suite.Suite
	modules3rd []Module3rd
}

func (suite *Module3rdTestSuite) SetupTest() {
	modules3rdConf := "./config/modules.cfg.example"
	modules3rd, err := loadModules3rdFile(modules3rdConf)
	if err != nil {
		fmt.Printf("Failed to load %s\n", modules3rdConf)
		os.Exit(1)
	}
	suite.modules3rd = modules3rd
}

func (suite *Module3rdTestSuite) TestModules3rd() {
	for _, m := range suite.modules3rd {
		switch m.Name {
		case "echo-nginx-module":
			assert.Equal(suite.T(), m.Name, "echo-nginx-module")
			assert.Equal(suite.T(), m.Url, "https://github.com/openresty/echo-nginx-module.git")
			assert.Equal(suite.T(), m.Rev, "v0.54")
		case "headers-more-nginx-module":
			assert.Equal(suite.T(), m.Name, "headers-more-nginx-module")
			assert.Equal(suite.T(), m.Url, "https://github.com/openresty/headers-more-nginx-module.git")
			assert.Equal(suite.T(), m.Rev, "v0.25")
		case "ngx_devel_kit":
			assert.Equal(suite.T(), m.Name, "ngx_devel_kit")
			assert.Equal(suite.T(), m.Url, "https://github.com/simpl/ngx_devel_kit")
			assert.Equal(suite.T(), m.Rev, "v0.2.19")
		case "ngx_info":
			assert.Equal(suite.T(), m.Name, "ngx_info")
			assert.Equal(suite.T(), m.Url, "https://github.com/cubicdaiya/ngx_info")
			assert.Equal(suite.T(), m.Rev, "")
		case "ngx_dosdetector":
			assert.Equal(suite.T(), m.Name, "ngx_dosdetector")
			assert.Equal(suite.T(), m.Url, "https://github.com/cubicdaiya/ngx_dosdetector")
			assert.Equal(suite.T(), m.Rev, "")
		case "ngx_access_token":
			assert.Equal(suite.T(), m.Name, "ngx_access_token")
			assert.Equal(suite.T(), m.Url, "https://github.com/cubicdaiya/ngx_access_token")
			assert.Equal(suite.T(), m.Rev, "")
		case "ngx_small_light":
			assert.Equal(suite.T(), m.Name, "ngx_small_light")
			assert.Equal(suite.T(), m.Url, "https://github.com/cubicdaiya/ngx_small_light")
			assert.Equal(suite.T(), m.Rev, "v0.5.3")
			assert.Equal(suite.T(), m.Shprov, "./setup --with-gd")
		}
	}
}

func TestModule3rdTestSuite(t *testing.T) {
	suite.Run(t, new(Module3rdTestSuite))
}
<|endoftext|>