<commit_before>/*----------------------------------------------------------------
 *  Copyright (c) ThoughtWorks, Inc.
 *  Licensed under the Apache License, Version 2.0
 *  See LICENSE in the project root for license information.
 *----------------------------------------------------------------*/

package execution

import (
	"fmt"

	"errors"

	"github.com/getgauge/gauge/execution/event"
	"github.com/getgauge/gauge/execution/result"
	"github.com/getgauge/gauge/gauge"
	"github.com/getgauge/gauge/gauge_messages"
	"github.com/getgauge/gauge/logger"
	"github.com/getgauge/gauge/plugin"
	"github.com/getgauge/gauge/runner"
	"github.com/getgauge/gauge/validation"
)

type scenarioExecutor struct {
	runner               runner.Runner
	pluginHandler        plugin.Handler
	currentExecutionInfo *gauge_messages.ExecutionInfo
	errMap               *gauge.BuildErrors
	stream               int
	contexts             []*gauge.Step
	teardowns            []*gauge.Step
}

func newScenarioExecutor(r runner.Runner, ph plugin.Handler, ei *gauge_messages.ExecutionInfo, errMap *gauge.BuildErrors, contexts []*gauge.Step, teardowns []*gauge.Step, stream int) *scenarioExecutor {
	return &scenarioExecutor{
		runner:               r,
		pluginHandler:        ph,
		currentExecutionInfo: ei,
		errMap:               errMap,
		stream:               stream,
		contexts:             contexts,
		teardowns:            teardowns,
	}
}

func (e *scenarioExecutor) execute(i gauge.Item, r result.Result) {
	scenario := i.(*gauge.Scenario)
	scenarioResult := r.(*result.ScenarioResult)
	scenarioResult.ProtoScenario.ExecutionStatus = gauge_messages.ExecutionStatus_PASSED
	scenarioResult.ProtoScenario.Skipped = false
	if scenario.SpecDataTableRow.IsInitialized() && !shouldExecuteForRow(scenario.SpecDataTableRowIndex) {
		e.errMap.ScenarioErrs[scenario] = append([]error{errors.New("skipped Reason: Doesn't satisfy --table-rows flag condition")}, e.errMap.ScenarioErrs[scenario]...)
		setSkipInfoInResult(scenarioResult, scenario, e.errMap)
		return
	}
	if _, ok := e.errMap.ScenarioErrs[scenario]; ok {
		setSkipInfoInResult(scenarioResult, scenario, e.errMap)
		event.Notify(event.NewExecutionEvent(event.ScenarioStart, scenario, scenarioResult, e.stream, *e.currentExecutionInfo))
		event.Notify(event.NewExecutionEvent(event.ScenarioEnd, scenario, scenarioResult, e.stream, *e.currentExecutionInfo))
		return
	}
	event.Notify(event.NewExecutionEvent(event.ScenarioStart, scenario, scenarioResult, e.stream, *e.currentExecutionInfo))
	defer event.Notify(event.NewExecutionEvent(event.ScenarioEnd, scenario, scenarioResult, e.stream, *e.currentExecutionInfo))

	res := e.initScenarioDataStore()
	if res.GetFailed() {
		e.handleScenarioDataStoreFailure(scenarioResult, scenario, fmt.Errorf("Failed to initialize scenario datastore. Error: %s", res.GetErrorMessage()))
		return
	}
	e.notifyBeforeScenarioHook(scenarioResult)

	if !scenarioResult.GetFailed() {
		protoContexts := scenarioResult.ProtoScenario.GetContexts()
		protoScenItems := scenarioResult.ProtoScenario.GetScenarioItems()
		e.executeSteps(append(e.contexts, scenario.Steps...), append(protoContexts, protoScenItems...), scenarioResult)
		// teardowns are not appended to previous call to executeSteps to ensure they are run irrespective of context/step failures
		e.executeSteps(e.teardowns, scenarioResult.ProtoScenario.GetTearDownSteps(), scenarioResult)
	}

	e.notifyAfterScenarioHook(scenarioResult)
	scenarioResult.UpdateExecutionTime()
}

func (e *scenarioExecutor) initScenarioDataStore() *gauge_messages.ProtoExecutionResult {
	msg := &gauge_messages.Message{MessageType: gauge_messages.Message_ScenarioDataStoreInit,
		ScenarioDataStoreInitRequest: &gauge_messages.ScenarioDataStoreInitRequest{Stream: int32(e.stream)}}
	return e.runner.ExecuteAndGetStatus(msg)
}

func (e *scenarioExecutor) handleScenarioDataStoreFailure(scenarioResult *result.ScenarioResult, scenario *gauge.Scenario, err error) {
	logger.Errorf(true, err.Error())
	validationError := validation.NewStepValidationError(&gauge.Step{LineNo: scenario.Heading.LineNo, LineText: scenario.Heading.Value},
		err.Error(), e.currentExecutionInfo.CurrentSpec.GetFileName(), nil, "")
	e.errMap.ScenarioErrs[scenario] = []error{validationError}
	setSkipInfoInResult(scenarioResult, scenario, e.errMap)
}

func setSkipInfoInResult(scenarioResult *result.ScenarioResult, scenario *gauge.Scenario, errMap *gauge.BuildErrors) {
	scenarioResult.ProtoScenario.ExecutionStatus = gauge_messages.ExecutionStatus_SKIPPED
	scenarioResult.ProtoScenario.Skipped = true
	var errs []string
	for _, err := range errMap.ScenarioErrs[scenario] {
		errs = append(errs, err.Error())
	}
	scenarioResult.ProtoScenario.SkipErrors = errs
}

func (e *scenarioExecutor) notifyBeforeScenarioHook(scenarioResult *result.ScenarioResult) {
	message := &gauge_messages.Message{MessageType: gauge_messages.Message_ScenarioExecutionStarting,
		ScenarioExecutionStartingRequest: &gauge_messages.ScenarioExecutionStartingRequest{CurrentExecutionInfo: e.currentExecutionInfo, Stream: int32(e.stream)}}
	e.pluginHandler.NotifyPlugins(message)
	res := executeHook(message, scenarioResult, e.runner)
	scenarioResult.ProtoScenario.PreHookMessages = res.Message
	scenarioResult.ProtoScenario.PreHookScreenshotFiles = res.ScreenshotFiles
	scenarioResult.ProtoScenario.PreHookScreenshots = res.Screenshots
	if res.GetFailed() {
		setScenarioFailure(e.currentExecutionInfo)
		handleHookFailure(scenarioResult, res, result.AddPreHook)
	}
	message.ScenarioExecutionStartingRequest.ScenarioResult = gauge.ConvertToProtoScenarioResult(scenarioResult)
	e.pluginHandler.NotifyPlugins(message)
}

func (e *scenarioExecutor) notifyAfterScenarioHook(scenarioResult *result.ScenarioResult) {
	message := &gauge_messages.Message{MessageType: gauge_messages.Message_ScenarioExecutionEnding,
		ScenarioExecutionEndingRequest: &gauge_messages.ScenarioExecutionEndingRequest{CurrentExecutionInfo: e.currentExecutionInfo, Stream: int32(e.stream)}}
	res := executeHook(message, scenarioResult, e.runner)
	scenarioResult.ProtoScenario.PostHookMessages = res.Message
	scenarioResult.ProtoScenario.PostHookScreenshotFiles = res.ScreenshotFiles
	scenarioResult.ProtoScenario.PostHookScreenshots = res.Screenshots
	if res.GetFailed() {
		setScenarioFailure(e.currentExecutionInfo)
		handleHookFailure(scenarioResult, res, result.AddPostHook)
	}
	message.ScenarioExecutionEndingRequest.ScenarioResult = gauge.ConvertToProtoScenarioResult(scenarioResult)
	e.pluginHandler.NotifyPlugins(message)
}

func (e *scenarioExecutor) executeSteps(steps []*gauge.Step, protoItems []*gauge_messages.ProtoItem, scenarioResult *result.ScenarioResult) {
	var stepsIndex int
	for _, protoItem := range protoItems {
		if protoItem.GetItemType() == gauge_messages.ProtoItem_Concept || protoItem.GetItemType() == gauge_messages.ProtoItem_Step {
			failed, recoverable := e.executeStep(steps[stepsIndex], protoItem, scenarioResult)
			stepsIndex++
			if failed {
				scenarioResult.SetFailure()
				if !recoverable {
					return
				}
			}
		}
	}
}

func (e *scenarioExecutor) executeStep(step *gauge.Step, protoItem *gauge_messages.ProtoItem, scenarioResult *result.ScenarioResult) (bool, bool) {
	var failed, recoverable bool
	if protoItem.GetItemType() == gauge_messages.ProtoItem_Concept {
		protoConcept := protoItem.GetConcept()
		res := e.executeConcept(step, protoConcept, scenarioResult)
		failed = res.GetFailed()
		recoverable = res.GetRecoverable()

	} else if protoItem.GetItemType() == gauge_messages.ProtoItem_Step {
		se := &stepExecutor{runner: e.runner, pluginHandler: e.pluginHandler, currentExecutionInfo: e.currentExecutionInfo, stream: e.stream}
		res := se.executeStep(step, protoItem.GetStep())
		protoItem.GetStep().StepExecutionResult = res.ProtoStepExecResult()
		failed = res.GetFailed()
		recoverable = res.ProtoStepExecResult().GetExecutionResult().GetRecoverableError()
	}
	return failed, recoverable
}

func (e *scenarioExecutor) executeConcept(item *gauge.Step, protoConcept *gauge_messages.ProtoConcept, scenarioResult *result.ScenarioResult) *result.ConceptResult {
	cptResult := result.NewConceptResult(protoConcept)
	event.Notify(event.NewExecutionEvent(event.ConceptStart, item, nil, e.stream, *e.currentExecutionInfo))
	defer event.Notify(event.NewExecutionEvent(event.ConceptEnd, nil, cptResult, e.stream, *e.currentExecutionInfo))

	var conceptStepIndex int
	for _, protoStep := range protoConcept.Steps {
		if protoStep.GetItemType() == gauge_messages.ProtoItem_Concept || protoStep.GetItemType() == gauge_messages.ProtoItem_Step {
			failed, recoverable := e.executeStep(item.ConceptSteps[conceptStepIndex], protoStep, scenarioResult)
			conceptStepIndex++
			if failed {
				scenarioResult.SetFailure()
				cptResult.UpdateConceptExecResult()
				if recoverable {
					continue
				}
				return cptResult
			}
		}
	}
	cptResult.UpdateConceptExecResult()
	return cptResult
}

func setStepFailure(executionInfo *gauge_messages.ExecutionInfo) {
	setScenarioFailure(executionInfo)
	executionInfo.CurrentStep.IsFailed = true
}

func getParameters(fragments []*gauge_messages.Fragment) []*gauge_messages.Parameter {
	var parameters []*gauge_messages.Parameter
	for _, fragment := range fragments {
		if fragment.GetFragmentType() == gauge_messages.Fragment_Parameter {
			parameters = append(parameters, fragment.GetParameter())
		}
	}
	return parameters
}

func setScenarioFailure(executionInfo *gauge_messages.ExecutionInfo) {
	setSpecFailure(executionInfo)
	executionInfo.CurrentScenario.IsFailed = true
}
<commit_msg>Separate context execution from steps #1629 (#1722)<commit_after>/*----------------------------------------------------------------
 *  Copyright (c) ThoughtWorks, Inc.
 *  Licensed under the Apache License, Version 2.0
 *  See LICENSE in the project root for license information.
 *----------------------------------------------------------------*/

package execution

import (
	"fmt"

	"errors"

	"github.com/getgauge/gauge/execution/event"
	"github.com/getgauge/gauge/execution/result"
	"github.com/getgauge/gauge/gauge"
	"github.com/getgauge/gauge/gauge_messages"
	"github.com/getgauge/gauge/logger"
	"github.com/getgauge/gauge/plugin"
	"github.com/getgauge/gauge/runner"
	"github.com/getgauge/gauge/validation"
)

type scenarioExecutor struct {
	runner               runner.Runner
	pluginHandler        plugin.Handler
	currentExecutionInfo *gauge_messages.ExecutionInfo
	errMap               *gauge.BuildErrors
	stream               int
	contexts             []*gauge.Step
	teardowns            []*gauge.Step
}

func newScenarioExecutor(r runner.Runner, ph plugin.Handler, ei *gauge_messages.ExecutionInfo, errMap *gauge.BuildErrors, contexts []*gauge.Step, teardowns []*gauge.Step, stream int) *scenarioExecutor {
	return &scenarioExecutor{
		runner:               r,
		pluginHandler:        ph,
		currentExecutionInfo: ei,
		errMap:               errMap,
		stream:               stream,
		contexts:             contexts,
		teardowns:            teardowns,
	}
}

func (e *scenarioExecutor) execute(i gauge.Item, r result.Result) {
	scenario := i.(*gauge.Scenario)
	scenarioResult := r.(*result.ScenarioResult)
	scenarioResult.ProtoScenario.ExecutionStatus = gauge_messages.ExecutionStatus_PASSED
	scenarioResult.ProtoScenario.Skipped = false
	if scenario.SpecDataTableRow.IsInitialized() && !shouldExecuteForRow(scenario.SpecDataTableRowIndex) {
		e.errMap.ScenarioErrs[scenario] = append([]error{errors.New("skipped Reason: Doesn't satisfy --table-rows flag condition")}, e.errMap.ScenarioErrs[scenario]...)
		setSkipInfoInResult(scenarioResult, scenario, e.errMap)
		return
	}
	if _, ok := e.errMap.ScenarioErrs[scenario]; ok {
		setSkipInfoInResult(scenarioResult, scenario, e.errMap)
		event.Notify(event.NewExecutionEvent(event.ScenarioStart, scenario, scenarioResult, e.stream, *e.currentExecutionInfo))
		event.Notify(event.NewExecutionEvent(event.ScenarioEnd, scenario, scenarioResult, e.stream, *e.currentExecutionInfo))
		return
	}
	event.Notify(event.NewExecutionEvent(event.ScenarioStart, scenario, scenarioResult, e.stream, *e.currentExecutionInfo))
	defer event.Notify(event.NewExecutionEvent(event.ScenarioEnd, scenario, scenarioResult, e.stream, *e.currentExecutionInfo))

	res := e.initScenarioDataStore()
	if res.GetFailed() {
		e.handleScenarioDataStoreFailure(scenarioResult, scenario, fmt.Errorf("Failed to initialize scenario datastore. Error: %s", res.GetErrorMessage()))
		return
	}
	e.notifyBeforeScenarioHook(scenarioResult)

	if !scenarioResult.GetFailed() {
		protoContexts := scenarioResult.ProtoScenario.GetContexts()
		protoScenItems := scenarioResult.ProtoScenario.GetScenarioItems()
		// context and steps are not appended together since sometime it cause the issue and the steps in step list and proto step list differs.
		// This is done to fix https://github.com/getgauge/gauge/issues/1629
		if e.executeSteps(e.contexts, protoContexts, scenarioResult) {
			e.executeSteps(scenario.Steps, protoScenItems, scenarioResult)
		}
		// teardowns are not appended to previous call to executeSteps to ensure they are run irrespective of context/step failure
		e.executeSteps(e.teardowns, scenarioResult.ProtoScenario.GetTearDownSteps(), scenarioResult)
	}

	e.notifyAfterScenarioHook(scenarioResult)
	scenarioResult.UpdateExecutionTime()
}

func (e *scenarioExecutor) initScenarioDataStore() *gauge_messages.ProtoExecutionResult {
	msg := &gauge_messages.Message{MessageType: gauge_messages.Message_ScenarioDataStoreInit,
		ScenarioDataStoreInitRequest: &gauge_messages.ScenarioDataStoreInitRequest{Stream: int32(e.stream)}}
	return e.runner.ExecuteAndGetStatus(msg)
}

func (e *scenarioExecutor) handleScenarioDataStoreFailure(scenarioResult *result.ScenarioResult, scenario *gauge.Scenario, err error) {
	logger.Errorf(true, err.Error())
	validationError := validation.NewStepValidationError(&gauge.Step{LineNo: scenario.Heading.LineNo, LineText: scenario.Heading.Value},
		err.Error(), e.currentExecutionInfo.CurrentSpec.GetFileName(), nil, "")
	e.errMap.ScenarioErrs[scenario] = []error{validationError}
	setSkipInfoInResult(scenarioResult, scenario, e.errMap)
}

func setSkipInfoInResult(scenarioResult *result.ScenarioResult, scenario *gauge.Scenario, errMap *gauge.BuildErrors) {
	scenarioResult.ProtoScenario.ExecutionStatus = gauge_messages.ExecutionStatus_SKIPPED
	scenarioResult.ProtoScenario.Skipped = true
	var errs []string
	for _, err := range errMap.ScenarioErrs[scenario] {
		errs = append(errs, err.Error())
	}
	scenarioResult.ProtoScenario.SkipErrors = errs
}

func (e *scenarioExecutor) notifyBeforeScenarioHook(scenarioResult *result.ScenarioResult) {
	message := &gauge_messages.Message{MessageType: gauge_messages.Message_ScenarioExecutionStarting,
		ScenarioExecutionStartingRequest: &gauge_messages.ScenarioExecutionStartingRequest{CurrentExecutionInfo: e.currentExecutionInfo, Stream: int32(e.stream)}}
	e.pluginHandler.NotifyPlugins(message)
	res := executeHook(message, scenarioResult, e.runner)
	scenarioResult.ProtoScenario.PreHookMessages = res.Message
	scenarioResult.ProtoScenario.PreHookScreenshotFiles = res.ScreenshotFiles
	scenarioResult.ProtoScenario.PreHookScreenshots = res.Screenshots
	if res.GetFailed() {
		setScenarioFailure(e.currentExecutionInfo)
		handleHookFailure(scenarioResult, res, result.AddPreHook)
	}
	message.ScenarioExecutionStartingRequest.ScenarioResult = gauge.ConvertToProtoScenarioResult(scenarioResult)
	e.pluginHandler.NotifyPlugins(message)
}

func (e *scenarioExecutor) notifyAfterScenarioHook(scenarioResult *result.ScenarioResult) {
	message := &gauge_messages.Message{MessageType: gauge_messages.Message_ScenarioExecutionEnding,
		ScenarioExecutionEndingRequest: &gauge_messages.ScenarioExecutionEndingRequest{CurrentExecutionInfo: e.currentExecutionInfo, Stream: int32(e.stream)}}
	res := executeHook(message, scenarioResult, e.runner)
	scenarioResult.ProtoScenario.PostHookMessages = res.Message
	scenarioResult.ProtoScenario.PostHookScreenshotFiles = res.ScreenshotFiles
	scenarioResult.ProtoScenario.PostHookScreenshots = res.Screenshots
	if res.GetFailed() {
		setScenarioFailure(e.currentExecutionInfo)
		handleHookFailure(scenarioResult, res, result.AddPostHook)
	}
	message.ScenarioExecutionEndingRequest.ScenarioResult = gauge.ConvertToProtoScenarioResult(scenarioResult)
	e.pluginHandler.NotifyPlugins(message)
}

func (e *scenarioExecutor) executeSteps(steps []*gauge.Step, protoItems []*gauge_messages.ProtoItem, scenarioResult *result.ScenarioResult) bool {
	var stepsIndex int
	for _, protoItem := range protoItems {
		if protoItem.GetItemType() == gauge_messages.ProtoItem_Concept || protoItem.GetItemType() == gauge_messages.ProtoItem_Step {
			failed, recoverable := e.executeStep(steps[stepsIndex], protoItem, scenarioResult)
			stepsIndex++
			if failed {
				scenarioResult.SetFailure()
				if !recoverable {
					return false
				}
			}
		}
	}
	return true
}

func (e *scenarioExecutor) executeStep(step *gauge.Step, protoItem *gauge_messages.ProtoItem, scenarioResult *result.ScenarioResult) (bool, bool) {
	var failed, recoverable bool
	if protoItem.GetItemType() == gauge_messages.ProtoItem_Concept {
		protoConcept := protoItem.GetConcept()
		res := e.executeConcept(step, protoConcept, scenarioResult)
		failed = res.GetFailed()
		recoverable = res.GetRecoverable()

	} else if protoItem.GetItemType() == gauge_messages.ProtoItem_Step {
		se := &stepExecutor{runner: e.runner, pluginHandler: e.pluginHandler, currentExecutionInfo: e.currentExecutionInfo, stream: e.stream}
		res := se.executeStep(step, protoItem.GetStep())
		protoItem.GetStep().StepExecutionResult = res.ProtoStepExecResult()
		failed = res.GetFailed()
		recoverable = res.ProtoStepExecResult().GetExecutionResult().GetRecoverableError()
	}
	return failed, recoverable
}

func (e *scenarioExecutor) executeConcept(item *gauge.Step, protoConcept *gauge_messages.ProtoConcept, scenarioResult *result.ScenarioResult) *result.ConceptResult {
	cptResult := result.NewConceptResult(protoConcept)
	event.Notify(event.NewExecutionEvent(event.ConceptStart, item, nil, e.stream, *e.currentExecutionInfo))
	defer event.Notify(event.NewExecutionEvent(event.ConceptEnd, nil, cptResult, e.stream, *e.currentExecutionInfo))

	var conceptStepIndex int
	for _, protoStep := range protoConcept.Steps {
		if protoStep.GetItemType() == gauge_messages.ProtoItem_Concept || protoStep.GetItemType() == gauge_messages.ProtoItem_Step {
			failed, recoverable := e.executeStep(item.ConceptSteps[conceptStepIndex], protoStep, scenarioResult)
			conceptStepIndex++
			if failed {
				scenarioResult.SetFailure()
				cptResult.UpdateConceptExecResult()
				if recoverable {
					continue
				}
				return cptResult
			}
		}
	}
	cptResult.UpdateConceptExecResult()
	return cptResult
}

func setStepFailure(executionInfo *gauge_messages.ExecutionInfo) {
	setScenarioFailure(executionInfo)
	executionInfo.CurrentStep.IsFailed = true
}

func getParameters(fragments []*gauge_messages.Fragment) []*gauge_messages.Parameter {
	var parameters []*gauge_messages.Parameter
	for _, fragment := range fragments {
		if fragment.GetFragmentType() == gauge_messages.Fragment_Parameter {
			parameters = append(parameters, fragment.GetParameter())
		}
	}
	return parameters
}

func setScenarioFailure(executionInfo *gauge_messages.ExecutionInfo) {
	setSpecFailure(executionInfo)
	executionInfo.CurrentScenario.IsFailed = true
}
<|endoftext|>


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

package test

import (
	"fmt"
	"os"
	"path/filepath"

	"v.io/jiri/collect"
	"v.io/jiri/project"
	"v.io/jiri/tool"
	"v.io/x/devtools/internal/test"
	"v.io/x/devtools/internal/xunit"
)

var (
	mirrors = []Mirror{
		Mirror{
			name:         "browser",
			googlesource: "https://vanadium.googlesource.com/release.projects.browser",
			github:       "git@github.com:vanadium/browser.git",
		},
		Mirror{
			name:         "go.v23",
			googlesource: "https://vanadium.googlesource.com/release.go.v23",
			github:       "git@github.com:vanadium/go.v23.git",
		},
		Mirror{
			name:         "go.devtools",
			googlesource: "https://vanadium.googlesource.com/release.go.x.devtools",
			github:       "git@github.com:vanadium/go.devtools.git",
		},
		Mirror{
			name:         "go.lib",
			googlesource: "https://vanadium.googlesource.com/release.go.x.lib",
			github:       "git@github.com:vanadium/go.lib.git",
		},
		Mirror{
			name:         "go.ref",
			googlesource: "https://vanadium.googlesource.com/release.go.x.ref",
			github:       "git@github.com:vanadium/go.ref.git",
		},
		Mirror{
			name:         "js",
			googlesource: "https://vanadium.googlesource.com/release.js.core",
			github:       "git@github.com:vanadium/js.git",
		},
		Mirror{
			name:         "js.syncbase",
			googlesource: "https://vanadium.googlesource.com/release.js.syncbase",
			github:       "git@github.com:vanadium/js.syncbase.git",
		},
		Mirror{
			name:         "chat",
			googlesource: "https://vanadium.googlesource.com/release.projects.chat",
			github:       "git@github.com:vanadium/chat.git",
		},
		Mirror{
			name:         "media-sharing",
			googlesource: "https://vanadium.googlesource.com/release.projects.media-sharing",
			github:       "git@github.com:vanadium/media-sharing.git",
		},
		Mirror{
			name:         "pipe2browser",
			googlesource: "https://vanadium.googlesource.com/release.projects.pipe2browser",
			github:       "git@github.com:vanadium/pipe2browser.git",
		},
		Mirror{
			name:         "playground",
			googlesource: "https://vanadium.googlesource.com/release.projects.playground",
			github:       "git@github.com:vanadium/playground.git",
		},
		Mirror{
			name:         "physical-lock",
			googlesource: "https://vanadium.googlesource.com/release.projects.physical-lock",
			github:       "git@github.com:vanadium/physical-lock.git",
		},
		Mirror{
			name:         "reader",
			googlesource: "https://vanadium.googlesource.com/release.projects.reader",
			github:       "git@github.com:vanadium/reader.git",
		},
		Mirror{
			name:         "todos",
			googlesource: "https://vanadium.googlesource.com/release.projects.todos",
			github:       "git@github.com:vanadium/todos.git",
		},
		Mirror{
			name:         "third_party",
			googlesource: "https://vanadium.googlesource.com/third_party",
			github:       "git@github.com:vanadium/third_party.git",
		},
		Mirror{
			name:         "www",
			googlesource: "https://vanadium.googlesource.com/www",
			github:       "git@github.com:vanadium/www.git",
		},
	}
)

type Mirror struct {
	name, googlesource, github string
}

// vanadiumGitHubMirror mirrors googlesource.com vanadium projects to
// github.com.
func vanadiumGitHubMirror(ctx *tool.Context, testName string, _ ...Opt) (_ *test.Result, e error) {
	// Initialize the test/task.
	cleanup, err := initTest(ctx, testName, nil)
	if err != nil {
		return nil, internalTestError{err, "Init"}
	}
	defer collect.Error(func() error { return cleanup() }, &e)

	root, err := project.V23Root()
	if err != nil {
		return nil, internalTestError{err, "V23Root"}
	}

	projects := filepath.Join(root, "projects")
	mode := os.FileMode(0755)
	if err := ctx.Run().MkdirAll(projects, mode); err != nil {
		return nil, internalTestError{err, "MkdirAll"}
	}

	allPassed := true
	suites := []xunit.TestSuite{}
	for _, mirror := range mirrors {
		suite, err := sync(ctx, mirror, projects)
		if err != nil {
			return nil, internalTestError{err, "sync"}
		}

		allPassed = allPassed && (suite.Failures == 0)
		suites = append(suites, *suite)
	}

	if err := xunit.CreateReport(ctx, testName, suites); err != nil {
		return nil, err
	}

	if !allPassed {
		return &test.Result{Status: test.Failed}, nil
	}

	return &test.Result{Status: test.Passed}, nil
}

func sync(ctx *tool.Context, mirror Mirror, projects string) (*xunit.TestSuite, error) {
	suite := xunit.TestSuite{Name: mirror.name}
	dirname := filepath.Join(projects, mirror.name)

	// If dirname does not exist `git clone` otherwise `git pull`.
	if _, err := ctx.Run().Stat(dirname); err != nil {
		if !os.IsNotExist(err) {
			return nil, internalTestError{err, "stat"}
		}

		err := clone(ctx, mirror, projects)
		testCase := makeTestCase("clone", err)
		if err != nil {
			suite.Failures++
		}
		suite.Cases = append(suite.Cases, *testCase)
	} else {
		err := pull(ctx, mirror, projects)
		testCase := makeTestCase("pull", err)
		if err != nil {
			suite.Failures++
		}
		suite.Cases = append(suite.Cases, *testCase)
	}

	err := push(ctx, mirror, projects)
	testCase := makeTestCase("push", err)
	if err != nil {
		suite.Failures++
	}
	suite.Cases = append(suite.Cases, *testCase)

	return &suite, nil
}

func makeTestCase(action string, err error) *xunit.TestCase {
	c := xunit.TestCase{
		Classname: "git",
		Name:      action,
	}

	if err != nil {
		f := xunit.Failure{
			Message: "git error",
			Data:    fmt.Sprintf("%v", err),
		}
		c.Failures = append(c.Failures, f)
	}

	return &c
}

func clone(ctx *tool.Context, mirror Mirror, projects string) error {
	dirname := filepath.Join(projects, mirror.name)
	return ctx.Git().Clone(mirror.googlesource, dirname)
}

func pull(ctx *tool.Context, mirror Mirror, projects string) error {
	dirname := filepath.Join(projects, mirror.name)
	opts := tool.RootDirOpt(dirname)
	return ctx.Git(opts).Pull("origin", "master")
}

func push(ctx *tool.Context, mirror Mirror, projects string) error {
	dirname := filepath.Join(projects, mirror.name)
	opts := tool.RootDirOpt(dirname)
	return ctx.Git(opts).Push(mirror.github, "master")
}
<commit_msg>TBR devtools/jiri-test: Update github mirror repos.<commit_after>// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package test

import (
	"fmt"
	"os"
	"path/filepath"

	"v.io/jiri/collect"
	"v.io/jiri/project"
	"v.io/jiri/tool"
	"v.io/x/devtools/internal/test"
	"v.io/x/devtools/internal/xunit"
)

var (
	mirrors = []Mirror{
		Mirror{
			name:         "go.jiri",
			googlesource: "https://vanadium.googlesource.com/release.go.jiri",
			github:       "git@github.com:vanadium/go.jiri.git",
		},
		Mirror{
			name:         "go.v23",
			googlesource: "https://vanadium.googlesource.com/release.go.v23",
			github:       "git@github.com:vanadium/go.v23.git",
		},
		Mirror{
			name:         "go.devtools",
			googlesource: "https://vanadium.googlesource.com/release.go.x.devtools",
			github:       "git@github.com:vanadium/go.devtools.git",
		},
		Mirror{
			name:         "go.lib",
			googlesource: "https://vanadium.googlesource.com/release.go.x.lib",
			github:       "git@github.com:vanadium/go.lib.git",
		},
		Mirror{
			name:         "go.ref",
			googlesource: "https://vanadium.googlesource.com/release.go.x.ref",
			github:       "git@github.com:vanadium/go.ref.git",
		},
		Mirror{
			name:         "js",
			googlesource: "https://vanadium.googlesource.com/release.js.core",
			github:       "git@github.com:vanadium/js.git",
		},
		Mirror{
			name:         "js.syncbase",
			googlesource: "https://vanadium.googlesource.com/release.js.syncbase",
			github:       "git@github.com:vanadium/js.syncbase.git",
		},
		Mirror{
			name:         "mojo.syncbase",
			googlesource: "https://vanadium.googlesource.com/release.mojo.syncbase",
			github:       "git@github.com:vanadium/mojo.syncbase.git",
		},
		Mirror{
			name:         "browser",
			googlesource: "https://vanadium.googlesource.com/release.projects.browser",
			github:       "git@github.com:vanadium/browser.git",
		},
		Mirror{
			name:         "chat",
			googlesource: "https://vanadium.googlesource.com/release.projects.chat",
			github:       "git@github.com:vanadium/chat.git",
		},
		Mirror{
			name:         "pipe2browser",
			googlesource: "https://vanadium.googlesource.com/release.projects.pipe2browser",
			github:       "git@github.com:vanadium/pipe2browser.git",
		},
		Mirror{
			name:         "playground",
			googlesource: "https://vanadium.googlesource.com/release.projects.playground",
			github:       "git@github.com:vanadium/playground.git",
		},
		Mirror{
			name:         "reader",
			googlesource: "https://vanadium.googlesource.com/release.projects.reader",
			github:       "git@github.com:vanadium/reader.git",
		},
		Mirror{
			name:         "todos",
			googlesource: "https://vanadium.googlesource.com/release.projects.todos",
			github:       "git@github.com:vanadium/todos.git",
		},
		Mirror{
			name:         "third_party",
			googlesource: "https://vanadium.googlesource.com/third_party",
			github:       "git@github.com:vanadium/third_party.git",
		},
		Mirror{
			name:         "www",
			googlesource: "https://vanadium.googlesource.com/www",
			github:       "git@github.com:vanadium/www.git",
		},
	}
)

type Mirror struct {
	name, googlesource, github string
}

// vanadiumGitHubMirror mirrors googlesource.com vanadium projects to
// github.com.
func vanadiumGitHubMirror(ctx *tool.Context, testName string, _ ...Opt) (_ *test.Result, e error) {
	// Initialize the test/task.
	cleanup, err := initTest(ctx, testName, nil)
	if err != nil {
		return nil, internalTestError{err, "Init"}
	}
	defer collect.Error(func() error { return cleanup() }, &e)

	root, err := project.V23Root()
	if err != nil {
		return nil, internalTestError{err, "V23Root"}
	}

	projects := filepath.Join(root, "projects")
	mode := os.FileMode(0755)
	if err := ctx.Run().MkdirAll(projects, mode); err != nil {
		return nil, internalTestError{err, "MkdirAll"}
	}

	allPassed := true
	suites := []xunit.TestSuite{}
	for _, mirror := range mirrors {
		suite, err := sync(ctx, mirror, projects)
		if err != nil {
			return nil, internalTestError{err, "sync"}
		}

		allPassed = allPassed && (suite.Failures == 0)
		suites = append(suites, *suite)
	}

	if err := xunit.CreateReport(ctx, testName, suites); err != nil {
		return nil, err
	}

	if !allPassed {
		return &test.Result{Status: test.Failed}, nil
	}

	return &test.Result{Status: test.Passed}, nil
}

func sync(ctx *tool.Context, mirror Mirror, projects string) (*xunit.TestSuite, error) {
	suite := xunit.TestSuite{Name: mirror.name}
	dirname := filepath.Join(projects, mirror.name)

	// If dirname does not exist `git clone` otherwise `git pull`.
	if _, err := ctx.Run().Stat(dirname); err != nil {
		if !os.IsNotExist(err) {
			return nil, internalTestError{err, "stat"}
		}

		err := clone(ctx, mirror, projects)
		testCase := makeTestCase("clone", err)
		if err != nil {
			suite.Failures++
		}
		suite.Cases = append(suite.Cases, *testCase)
	} else {
		err := pull(ctx, mirror, projects)
		testCase := makeTestCase("pull", err)
		if err != nil {
			suite.Failures++
		}
		suite.Cases = append(suite.Cases, *testCase)
	}

	err := push(ctx, mirror, projects)
	testCase := makeTestCase("push", err)
	if err != nil {
		suite.Failures++
	}
	suite.Cases = append(suite.Cases, *testCase)

	return &suite, nil
}

func makeTestCase(action string, err error) *xunit.TestCase {
	c := xunit.TestCase{
		Classname: "git",
		Name:      action,
	}

	if err != nil {
		f := xunit.Failure{
			Message: "git error",
			Data:    fmt.Sprintf("%v", err),
		}
		c.Failures = append(c.Failures, f)
	}

	return &c
}

func clone(ctx *tool.Context, mirror Mirror, projects string) error {
	dirname := filepath.Join(projects, mirror.name)
	return ctx.Git().Clone(mirror.googlesource, dirname)
}

func pull(ctx *tool.Context, mirror Mirror, projects string) error {
	dirname := filepath.Join(projects, mirror.name)
	opts := tool.RootDirOpt(dirname)
	return ctx.Git(opts).Pull("origin", "master")
}

func push(ctx *tool.Context, mirror Mirror, projects string) error {
	dirname := filepath.Join(projects, mirror.name)
	opts := tool.RootDirOpt(dirname)
	return ctx.Git(opts).Push(mirror.github, "master")
}
<|endoftext|>


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

package extra25519

import (
	"bytes"
	"crypto/rand"
	"testing"

	"code.google.com/p/go.crypto/curve25519"
	"github.com/tendermint/ed25519"
)

func TestCurve25519Conversion(t *testing.T) {
	public, private, _ := ed25519.GenerateKey(rand.Reader)

	var curve25519Public, curve25519Public2, curve25519Private [32]byte
	PrivateKeyToCurve25519(&curve25519Private, private)
	curve25519.ScalarBaseMult(&curve25519Public, &curve25519Private)

	if !PublicKeyToCurve25519(&curve25519Public2, public) {
		t.Fatalf("PublicKeyToCurve25519 failed")
	}

	if !bytes.Equal(curve25519Public[:], curve25519Public2[:]) {
		t.Errorf("Values didn't match: curve25519 produced %x, conversion produced %x", curve25519Public[:], curve25519Public2[:])
	}
}

func TestElligator(t *testing.T) {
	var publicKey, publicKey2, publicKey3, representative, privateKey [32]byte

	for i := 0; i < 1000; i++ {
		rand.Reader.Read(privateKey[:])

		if !ScalarBaseMult(&publicKey, &representative, &privateKey) {
			continue
		}
		RepresentativeToPublicKey(&publicKey2, &representative)
		if !bytes.Equal(publicKey[:], publicKey2[:]) {
			t.Fatal("The resulting public key doesn't match the initial one.")
		}

		curve25519.ScalarBaseMult(&publicKey3, &privateKey)
		if !bytes.Equal(publicKey[:], publicKey3[:]) {
			t.Fatal("The public key doesn't match the value that curve25519 produced.")
		}
	}
}

func BenchmarkKeyGeneration(b *testing.B) {
	var publicKey, representative, privateKey [32]byte

	// Find the private key that results in a point that's in the image of the map.
	for {
		rand.Reader.Read(privateKey[:])
		if ScalarBaseMult(&publicKey, &representative, &privateKey) {
			break
		}
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		ScalarBaseMult(&publicKey, &representative, &privateKey)
	}
}

func BenchmarkMap(b *testing.B) {
	var publicKey, representative [32]byte
	rand.Reader.Read(representative[:])

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		RepresentativeToPublicKey(&publicKey, &representative)
	}
}
<commit_msg>code.google.com -> golang.org/x<commit_after>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package extra25519

import (
	"bytes"
	"crypto/rand"
	"testing"

	"github.com/tendermint/ed25519"
	"golang.org/x/crypto/curve25519"
)

func TestCurve25519Conversion(t *testing.T) {
	public, private, _ := ed25519.GenerateKey(rand.Reader)

	var curve25519Public, curve25519Public2, curve25519Private [32]byte
	PrivateKeyToCurve25519(&curve25519Private, private)
	curve25519.ScalarBaseMult(&curve25519Public, &curve25519Private)

	if !PublicKeyToCurve25519(&curve25519Public2, public) {
		t.Fatalf("PublicKeyToCurve25519 failed")
	}

	if !bytes.Equal(curve25519Public[:], curve25519Public2[:]) {
		t.Errorf("Values didn't match: curve25519 produced %x, conversion produced %x", curve25519Public[:], curve25519Public2[:])
	}
}

func TestElligator(t *testing.T) {
	var publicKey, publicKey2, publicKey3, representative, privateKey [32]byte

	for i := 0; i < 1000; i++ {
		rand.Reader.Read(privateKey[:])

		if !ScalarBaseMult(&publicKey, &representative, &privateKey) {
			continue
		}
		RepresentativeToPublicKey(&publicKey2, &representative)
		if !bytes.Equal(publicKey[:], publicKey2[:]) {
			t.Fatal("The resulting public key doesn't match the initial one.")
		}

		curve25519.ScalarBaseMult(&publicKey3, &privateKey)
		if !bytes.Equal(publicKey[:], publicKey3[:]) {
			t.Fatal("The public key doesn't match the value that curve25519 produced.")
		}
	}
}

func BenchmarkKeyGeneration(b *testing.B) {
	var publicKey, representative, privateKey [32]byte

	// Find the private key that results in a point that's in the image of the map.
	for {
		rand.Reader.Read(privateKey[:])
		if ScalarBaseMult(&publicKey, &representative, &privateKey) {
			break
		}
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		ScalarBaseMult(&publicKey, &representative, &privateKey)
	}
}

func BenchmarkMap(b *testing.B) {
	var publicKey, representative [32]byte
	rand.Reader.Read(representative[:])

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		RepresentativeToPublicKey(&publicKey, &representative)
	}
}
<|endoftext|>


<commit_before>package topology

import (
	"fmt"
	"net"
	"os"
	"path/filepath"
	"sort"

	"github.com/Cloud-Foundations/Dominator/lib/fsutil"
	"github.com/Cloud-Foundations/Dominator/lib/json"
	"github.com/Cloud-Foundations/Dominator/lib/tags"
	proto "github.com/Cloud-Foundations/Dominator/proto/fleetmanager"
)

type commonStateType struct {
	hostnames    map[string]struct{}
	ipAddresses  map[string]struct{}
	macAddresses map[string]struct{}
}

type inheritingState struct {
	owners    *ownersType
	subnetIds map[string]struct{}
	tags      tags.Tags
}

func checkMacAddressIsZero(macAddr proto.HardwareAddr) bool {
	for _, b := range macAddr {
		if b != 0 {
			return false
		}
	}
	return true
}

func cloneSet(set map[string]struct{}) map[string]struct{} {
	clone := make(map[string]struct{}, len(set))
	for key := range set {
		clone[key] = struct{}{}
	}
	return clone
}

func load(topologyDir string) (*Topology, error) {
	topology := &Topology{
		machineParents:  make(map[string]*Directory),
		reservedIpAddrs: make(map[string]struct{}),
	}
	directory, err := topology.readDirectory(topologyDir, "",
		newInheritingState(),
		&commonStateType{
			hostnames:    make(map[string]struct{}),
			ipAddresses:  make(map[string]struct{}),
			macAddresses: make(map[string]struct{}),
		},
	)
	if err != nil {
		return nil, err
	}
	topology.Root = directory
	return topology, nil
}

func loadMachines(filename string) ([]*proto.Machine, error) {
	var machines []*proto.Machine
	if err := json.ReadFromFile(filename, &machines); err != nil {
		if os.IsNotExist(err) {
			return nil, nil
		}
		return nil, fmt.Errorf("error reading: %s: %s", filename, err)
	}
	for _, machine := range machines {
		if len(machine.HostIpAddress) == 0 {
			if addrs, err := net.LookupIP(machine.Hostname); err != nil {
				return nil, err
			} else if len(addrs) != 1 {
				return nil, fmt.Errorf("num addresses for: %s: %d!=1",
					machine.Hostname, len(addrs))
			} else {
				machine.HostIpAddress = addrs[0]
			}
		}
		if len(machine.HostIpAddress) == 16 {
			machine.HostIpAddress = machine.HostIpAddress.To4()
		}
	}
	return machines, nil
}

func loadOwners(filename string) (*ownersType, error) {
	var owners ownersType
	if err := json.ReadFromFile(filename, &owners); err != nil {
		if os.IsNotExist(err) {
			return nil, nil
		}
		return nil, fmt.Errorf("error reading: %s: %s", filename, err)
	}
	return &owners, nil
}

func loadSubnets(filename string) ([]*Subnet, error) {
	var subnets []*Subnet
	if err := json.ReadFromFile(filename, &subnets); err != nil {
		if os.IsNotExist(err) {
			return nil, nil
		}
		return nil, fmt.Errorf("error reading: %s: %s", filename, err)
	}
	gatewayIPs := make(map[string]struct{}, len(subnets))
	for _, subnet := range subnets {
		subnet.Shrink()
		gatewayIp := subnet.IpGateway.String()
		if _, ok := gatewayIPs[gatewayIp]; ok {
			return nil, fmt.Errorf("duplicate gateway IP: %s", gatewayIp)
		} else {
			gatewayIPs[gatewayIp] = struct{}{}
		}
		subnet.reservedIpAddrs = make(map[string]struct{})
		for _, ipAddr := range subnet.ReservedIPs {
			subnet.reservedIpAddrs[ipAddr.String()] = struct{}{}
		}
	}
	return subnets, nil
}

func loadTags(filename string) (tags.Tags, error) {
	var loadedTags tags.Tags
	if err := json.ReadFromFile(filename, &loadedTags); err != nil {
		if os.IsNotExist(err) {
			return nil, nil
		}
		return nil, fmt.Errorf("error reading: %s: %s", filename, err)
	}
	return loadedTags, nil
}

func (state *commonStateType) addHostname(name string) error {
	if name == "" {
		return nil
	}
	if _, ok := state.hostnames[name]; ok {
		return fmt.Errorf("duplicate hostname: %s", name)
	}
	state.hostnames[name] = struct{}{}
	return nil
}

func (state *commonStateType) addIpAddress(ipAddr net.IP) error {
	if len(ipAddr) < 1 {
		return nil
	}
	name := ipAddr.String()
	if _, ok := state.ipAddresses[name]; ok {
		return fmt.Errorf("duplicate IP address: %s", name)
	}
	state.ipAddresses[name] = struct{}{}
	return nil
}

func (state *commonStateType) addMacAddress(macAddr proto.HardwareAddr) error {
	if len(macAddr) < 1 {
		return nil
	}
	if checkMacAddressIsZero(macAddr) {
		return nil
	}
	name := macAddr.String()
	if _, ok := state.macAddresses[name]; ok {
		return fmt.Errorf("duplicate MAC address: %s", name)
	}
	state.macAddresses[name] = struct{}{}
	return nil
}

func (state *commonStateType) addMachine(machine *proto.Machine,
	subnets map[string]*Subnet) error {
	if err := state.addNetworkEntry(machine.NetworkEntry, subnets); err != nil {
		return err
	}
	if err := state.addNetworkEntry(machine.IPMI, nil); err != nil {
		return err
	}
	for _, entry := range machine.SecondaryNetworkEntries {
		if err := state.addNetworkEntry(entry, subnets); err != nil {
			return err
		}
	}
	return nil
}

func (state *commonStateType) addNetworkEntry(entry proto.NetworkEntry,
	subnets map[string]*Subnet) error {
	if entry.SubnetId != "" {
		if _, ok := subnets[entry.SubnetId]; !ok {
			return fmt.Errorf("unknown subnetId: %s", entry.SubnetId)
		}
		if entry.Hostname != "" {
			return fmt.Errorf(
				"cannot specify SubnetId(%s) and Hostname(%s) together",
				entry.SubnetId, entry.Hostname)
		}
		if len(entry.HostIpAddress) > 0 {
			return fmt.Errorf(
				"cannot specify SubnetId(%s) and HostIpAddress(%s) together",
				entry.SubnetId, entry.HostIpAddress)
		}
	}
	if err := state.addHostname(entry.Hostname); err != nil {
		return err
	}
	if err := state.addIpAddress(entry.HostIpAddress); err != nil {
		return err
	}
	if err := state.addMacAddress(entry.HostMacAddress); err != nil {
		return err
	}
	return nil
}

func newInheritingState() *inheritingState {
	return &inheritingState{
		owners:    &ownersType{},
		subnetIds: cloneSet(nil),
		tags:      make(tags.Tags),
	}
}

func (state *inheritingState) copy() *inheritingState {
	return &inheritingState{
		owners:    state.owners.copy(),
		subnetIds: cloneSet(state.subnetIds),
		tags:      state.tags.Copy(),
	}
}

func (t *Topology) loadSubnets(directory *Directory, dirpath string,
	subnetIds map[string]struct{}) error {
	if err := directory.loadSubnets(dirpath, subnetIds); err != nil {
		return err
	}
	for _, subnet := range directory.Subnets {
		for ipAddr := range subnet.reservedIpAddrs {
			t.reservedIpAddrs[ipAddr] = struct{}{}
		}
	}
	return nil
}

func (t *Topology) readDirectory(topDir, dirname string,
	state *inheritingState, commonState *commonStateType) (*Directory, error) {
	directory := &Directory{
		nameToDirectory:  make(map[string]*Directory),
		path:             dirname,
		subnetIdToSubnet: make(map[string]*Subnet),
	}
	dirpath := filepath.Join(topDir, dirname)
	if err := directory.loadOwners(dirpath, state.owners); err != nil {
		return nil, err
	}
	if err := t.loadSubnets(directory, dirpath, state.subnetIds); err != nil {
		return nil, err
	}
	if err := directory.loadTags(dirpath, state.tags); err != nil {
		return nil, err
	}
	if err := t.loadMachines(directory, dirpath, commonState); err != nil {
		return nil, err
	}
	dirnames, err := fsutil.ReadDirnames(dirpath, false)
	if err != nil {
		return nil, err
	}
	for _, name := range dirnames {
		if name == ".git" {
			continue
		}
		path := filepath.Join(dirname, name)
		fi, err := os.Lstat(filepath.Join(topDir, path))
		if err != nil {
			return nil, err
		}
		if !fi.IsDir() {
			continue
		}
		state := state.copy()
		subdir, err := t.readDirectory(topDir, path, state, commonState)
		if err != nil {
			return nil, err
		} else {
			subdir.Name = name
			subdir.parent = directory
			directory.Directories = append(directory.Directories, subdir)
			directory.nameToDirectory[name] = subdir
		}
	}
	return directory, nil
}

func (directory *Directory) loadMachines(dirname string) error {
	var err error
	directory.Machines, err = loadMachines(
		filepath.Join(dirname, "machines.json"))
	if err != nil {
		return err
	}
	for _, machine := range directory.Machines {
		mergedOwners := ownersType{
			OwnerGroups: machine.OwnerGroups,
			OwnerUsers:  machine.OwnerUsers,
		}
		mergedOwners.merge(directory.owners)
		machine.OwnerGroups = mergedOwners.OwnerGroups
		machine.OwnerUsers = mergedOwners.OwnerUsers
		if machine.Tags == nil {
			machine.Tags = directory.Tags
		} else if directory.Tags != nil {
			mergedTags := directory.Tags.Copy()
			mergedTags.Merge(machine.Tags)
			machine.Tags = mergedTags
		}
	}
	return nil
}

func (directory *Directory) loadOwners(dirname string,
	parentOwners *ownersType) error {
	owners, err := loadOwners(filepath.Join(dirname, "owners.json"))
	if err != nil {
		return err
	}
	parentOwners.merge(owners)
	directory.owners = parentOwners
	return nil
}

func (directory *Directory) loadSubnets(dirname string,
	subnetIds map[string]struct{}) error {
	var err error
	directory.Subnets, err = loadSubnets(filepath.Join(dirname, "subnets.json"))
	if err != nil {
		return err
	}
	for _, subnet := range directory.Subnets {
		if _, ok := subnetIds[subnet.Id]; ok {
			return fmt.Errorf("duplicate subnet ID: %s", subnet.Id)
		} else {
			subnetIds[subnet.Id] = struct{}{}
			directory.subnetIdToSubnet[subnet.Id] = subnet
		}
	}
	return nil
}

func (directory *Directory) loadTags(dirname string,
	parentTags tags.Tags) error {
	loadedTags, err := loadTags(filepath.Join(dirname, "tags.json"))
	if err != nil {
		return err
	}
	parentTags.Merge(loadedTags)
	if len(parentTags) > 0 {
		directory.Tags = parentTags
	}
	return nil
}

func (owners *ownersType) copy() *ownersType {
	newOwners := ownersType{
		OwnerGroups: make([]string, 0, len(owners.OwnerGroups)),
		OwnerUsers:  make([]string, 0, len(owners.OwnerUsers)),
	}
	for _, group := range owners.OwnerGroups {
		newOwners.OwnerGroups = append(newOwners.OwnerGroups, group)
	}
	for _, user := range owners.OwnerUsers {
		newOwners.OwnerUsers = append(newOwners.OwnerUsers, user)
	}
	return &newOwners
}

func (to *ownersType) merge(from *ownersType) {
	if from == nil {
		return
	}
	ownerGroups := make(map[string]struct{}, len(to.OwnerGroups))
	for _, group := range to.OwnerGroups {
		ownerGroups[group] = struct{}{}
	}
	changedOwnerGroups := false
	for _, group := range from.OwnerGroups {
		if _, ok := ownerGroups[group]; !ok {
			to.OwnerGroups = append(to.OwnerGroups, group)
			changedOwnerGroups = true
		}
	}
	if changedOwnerGroups {
		sort.Strings(to.OwnerGroups)
	}
	ownerUsers := make(map[string]struct{}, len(to.OwnerUsers))
	for _, group := range to.OwnerUsers {
		ownerUsers[group] = struct{}{}
	}
	changedOwnerUsers := false
	for _, group := range from.OwnerUsers {
		if _, ok := ownerUsers[group]; !ok {
			to.OwnerUsers = append(to.OwnerUsers, group)
			changedOwnerUsers = true
		}
	}
	if changedOwnerUsers {
		sort.Strings(to.OwnerUsers)
	}
}

func (t *Topology) loadMachines(directory *Directory, dirname string,
	commonState *commonStateType) error {
	if err := directory.loadMachines(dirname); err != nil {
		return err
	}
	for _, machine := range directory.Machines {
		err := commonState.addMachine(machine, directory.subnetIdToSubnet)
		if err != nil {
			return fmt.Errorf("error adding: %s: %s", machine.Hostname, err)
		}
		t.machineParents[machine.Hostname] = directory
	}
	return nil
}
<commit_msg>fleet-manager: add valididty check for Machine.GatewaySubnetId.<commit_after>package topology

import (
	"fmt"
	"net"
	"os"
	"path/filepath"
	"sort"

	"github.com/Cloud-Foundations/Dominator/lib/fsutil"
	"github.com/Cloud-Foundations/Dominator/lib/json"
	"github.com/Cloud-Foundations/Dominator/lib/tags"
	proto "github.com/Cloud-Foundations/Dominator/proto/fleetmanager"
)

type commonStateType struct {
	hostnames    map[string]struct{}
	ipAddresses  map[string]struct{}
	macAddresses map[string]struct{}
}

type inheritingState struct {
	owners    *ownersType
	subnetIds map[string]struct{}
	tags      tags.Tags
}

func checkMacAddressIsZero(macAddr proto.HardwareAddr) bool {
	for _, b := range macAddr {
		if b != 0 {
			return false
		}
	}
	return true
}

func cloneSet(set map[string]struct{}) map[string]struct{} {
	clone := make(map[string]struct{}, len(set))
	for key := range set {
		clone[key] = struct{}{}
	}
	return clone
}

func load(topologyDir string) (*Topology, error) {
	topology := &Topology{
		machineParents:  make(map[string]*Directory),
		reservedIpAddrs: make(map[string]struct{}),
	}
	directory, err := topology.readDirectory(topologyDir, "",
		newInheritingState(),
		&commonStateType{
			hostnames:    make(map[string]struct{}),
			ipAddresses:  make(map[string]struct{}),
			macAddresses: make(map[string]struct{}),
		},
	)
	if err != nil {
		return nil, err
	}
	topology.Root = directory
	return topology, nil
}

func loadMachines(filename string) ([]*proto.Machine, error) {
	var machines []*proto.Machine
	if err := json.ReadFromFile(filename, &machines); err != nil {
		if os.IsNotExist(err) {
			return nil, nil
		}
		return nil, fmt.Errorf("error reading: %s: %s", filename, err)
	}
	for _, machine := range machines {
		if len(machine.HostIpAddress) == 0 {
			if addrs, err := net.LookupIP(machine.Hostname); err != nil {
				return nil, err
			} else if len(addrs) != 1 {
				return nil, fmt.Errorf("num addresses for: %s: %d!=1",
					machine.Hostname, len(addrs))
			} else {
				machine.HostIpAddress = addrs[0]
			}
		}
		if len(machine.HostIpAddress) == 16 {
			machine.HostIpAddress = machine.HostIpAddress.To4()
		}
	}
	return machines, nil
}

func loadOwners(filename string) (*ownersType, error) {
	var owners ownersType
	if err := json.ReadFromFile(filename, &owners); err != nil {
		if os.IsNotExist(err) {
			return nil, nil
		}
		return nil, fmt.Errorf("error reading: %s: %s", filename, err)
	}
	return &owners, nil
}

func loadSubnets(filename string) ([]*Subnet, error) {
	var subnets []*Subnet
	if err := json.ReadFromFile(filename, &subnets); err != nil {
		if os.IsNotExist(err) {
			return nil, nil
		}
		return nil, fmt.Errorf("error reading: %s: %s", filename, err)
	}
	gatewayIPs := make(map[string]struct{}, len(subnets))
	for _, subnet := range subnets {
		subnet.Shrink()
		gatewayIp := subnet.IpGateway.String()
		if _, ok := gatewayIPs[gatewayIp]; ok {
			return nil, fmt.Errorf("duplicate gateway IP: %s", gatewayIp)
		} else {
			gatewayIPs[gatewayIp] = struct{}{}
		}
		subnet.reservedIpAddrs = make(map[string]struct{})
		for _, ipAddr := range subnet.ReservedIPs {
			subnet.reservedIpAddrs[ipAddr.String()] = struct{}{}
		}
	}
	return subnets, nil
}

func loadTags(filename string) (tags.Tags, error) {
	var loadedTags tags.Tags
	if err := json.ReadFromFile(filename, &loadedTags); err != nil {
		if os.IsNotExist(err) {
			return nil, nil
		}
		return nil, fmt.Errorf("error reading: %s: %s", filename, err)
	}
	return loadedTags, nil
}

func (state *commonStateType) addHostname(name string) error {
	if name == "" {
		return nil
	}
	if _, ok := state.hostnames[name]; ok {
		return fmt.Errorf("duplicate hostname: %s", name)
	}
	state.hostnames[name] = struct{}{}
	return nil
}

func (state *commonStateType) addIpAddress(ipAddr net.IP) error {
	if len(ipAddr) < 1 {
		return nil
	}
	name := ipAddr.String()
	if _, ok := state.ipAddresses[name]; ok {
		return fmt.Errorf("duplicate IP address: %s", name)
	}
	state.ipAddresses[name] = struct{}{}
	return nil
}

func (state *commonStateType) addMacAddress(macAddr proto.HardwareAddr) error {
	if len(macAddr) < 1 {
		return nil
	}
	if checkMacAddressIsZero(macAddr) {
		return nil
	}
	name := macAddr.String()
	if _, ok := state.macAddresses[name]; ok {
		return fmt.Errorf("duplicate MAC address: %s", name)
	}
	state.macAddresses[name] = struct{}{}
	return nil
}

func (state *commonStateType) addMachine(machine *proto.Machine,
	subnets map[string]*Subnet) error {
	if machine.GatewaySubnetId != "" {
		if _, ok := subnets[machine.GatewaySubnetId]; !ok {
			return fmt.Errorf("unknown subnetId: %s", machine.GatewaySubnetId)
		}
	}
	if err := state.addNetworkEntry(machine.NetworkEntry, subnets); err != nil {
		return err
	}
	if err := state.addNetworkEntry(machine.IPMI, nil); err != nil {
		return err
	}
	for _, entry := range machine.SecondaryNetworkEntries {
		if err := state.addNetworkEntry(entry, subnets); err != nil {
			return err
		}
	}
	return nil
}

func (state *commonStateType) addNetworkEntry(entry proto.NetworkEntry,
	subnets map[string]*Subnet) error {
	if entry.SubnetId != "" {
		if _, ok := subnets[entry.SubnetId]; !ok {
			return fmt.Errorf("unknown subnetId: %s", entry.SubnetId)
		}
		if entry.Hostname != "" {
			return fmt.Errorf(
				"cannot specify SubnetId(%s) and Hostname(%s) together",
				entry.SubnetId, entry.Hostname)
		}
		if len(entry.HostIpAddress) > 0 {
			return fmt.Errorf(
				"cannot specify SubnetId(%s) and HostIpAddress(%s) together",
				entry.SubnetId, entry.HostIpAddress)
		}
	}
	if err := state.addHostname(entry.Hostname); err != nil {
		return err
	}
	if err := state.addIpAddress(entry.HostIpAddress); err != nil {
		return err
	}
	if err := state.addMacAddress(entry.HostMacAddress); err != nil {
		return err
	}
	return nil
}

func newInheritingState() *inheritingState {
	return &inheritingState{
		owners:    &ownersType{},
		subnetIds: cloneSet(nil),
		tags:      make(tags.Tags),
	}
}

func (state *inheritingState) copy() *inheritingState {
	return &inheritingState{
		owners:    state.owners.copy(),
		subnetIds: cloneSet(state.subnetIds),
		tags:      state.tags.Copy(),
	}
}

func (t *Topology) loadSubnets(directory *Directory, dirpath string,
	subnetIds map[string]struct{}) error {
	if err := directory.loadSubnets(dirpath, subnetIds); err != nil {
		return err
	}
	for _, subnet := range directory.Subnets {
		for ipAddr := range subnet.reservedIpAddrs {
			t.reservedIpAddrs[ipAddr] = struct{}{}
		}
	}
	return nil
}

func (t *Topology) readDirectory(topDir, dirname string,
	state *inheritingState, commonState *commonStateType) (*Directory, error) {
	directory := &Directory{
		nameToDirectory:  make(map[string]*Directory),
		path:             dirname,
		subnetIdToSubnet: make(map[string]*Subnet),
	}
	dirpath := filepath.Join(topDir, dirname)
	if err := directory.loadOwners(dirpath, state.owners); err != nil {
		return nil, err
	}
	if err := t.loadSubnets(directory, dirpath, state.subnetIds); err != nil {
		return nil, err
	}
	if err := directory.loadTags(dirpath, state.tags); err != nil {
		return nil, err
	}
	if err := t.loadMachines(directory, dirpath, commonState); err != nil {
		return nil, err
	}
	dirnames, err := fsutil.ReadDirnames(dirpath, false)
	if err != nil {
		return nil, err
	}
	for _, name := range dirnames {
		if name == ".git" {
			continue
		}
		path := filepath.Join(dirname, name)
		fi, err := os.Lstat(filepath.Join(topDir, path))
		if err != nil {
			return nil, err
		}
		if !fi.IsDir() {
			continue
		}
		state := state.copy()
		subdir, err := t.readDirectory(topDir, path, state, commonState)
		if err != nil {
			return nil, err
		} else {
			subdir.Name = name
			subdir.parent = directory
			directory.Directories = append(directory.Directories, subdir)
			directory.nameToDirectory[name] = subdir
		}
	}
	return directory, nil
}

func (directory *Directory) loadMachines(dirname string) error {
	var err error
	directory.Machines, err = loadMachines(
		filepath.Join(dirname, "machines.json"))
	if err != nil {
		return err
	}
	for _, machine := range directory.Machines {
		mergedOwners := ownersType{
			OwnerGroups: machine.OwnerGroups,
			OwnerUsers:  machine.OwnerUsers,
		}
		mergedOwners.merge(directory.owners)
		machine.OwnerGroups = mergedOwners.OwnerGroups
		machine.OwnerUsers = mergedOwners.OwnerUsers
		if machine.Tags == nil {
			machine.Tags = directory.Tags
		} else if directory.Tags != nil {
			mergedTags := directory.Tags.Copy()
			mergedTags.Merge(machine.Tags)
			machine.Tags = mergedTags
		}
	}
	return nil
}

func (directory *Directory) loadOwners(dirname string,
	parentOwners *ownersType) error {
	owners, err := loadOwners(filepath.Join(dirname, "owners.json"))
	if err != nil {
		return err
	}
	parentOwners.merge(owners)
	directory.owners = parentOwners
	return nil
}

func (directory *Directory) loadSubnets(dirname string,
	subnetIds map[string]struct{}) error {
	var err error
	directory.Subnets, err = loadSubnets(filepath.Join(dirname, "subnets.json"))
	if err != nil {
		return err
	}
	for _, subnet := range directory.Subnets {
		if _, ok := subnetIds[subnet.Id]; ok {
			return fmt.Errorf("duplicate subnet ID: %s", subnet.Id)
		} else {
			subnetIds[subnet.Id] = struct{}{}
			directory.subnetIdToSubnet[subnet.Id] = subnet
		}
	}
	return nil
}

func (directory *Directory) loadTags(dirname string,
	parentTags tags.Tags) error {
	loadedTags, err := loadTags(filepath.Join(dirname, "tags.json"))
	if err != nil {
		return err
	}
	parentTags.Merge(loadedTags)
	if len(parentTags) > 0 {
		directory.Tags = parentTags
	}
	return nil
}

func (owners *ownersType) copy() *ownersType {
	newOwners := ownersType{
		OwnerGroups: make([]string, 0, len(owners.OwnerGroups)),
		OwnerUsers:  make([]string, 0, len(owners.OwnerUsers)),
	}
	for _, group := range owners.OwnerGroups {
		newOwners.OwnerGroups = append(newOwners.OwnerGroups, group)
	}
	for _, user := range owners.OwnerUsers {
		newOwners.OwnerUsers = append(newOwners.OwnerUsers, user)
	}
	return &newOwners
}

func (to *ownersType) merge(from *ownersType) {
	if from == nil {
		return
	}
	ownerGroups := make(map[string]struct{}, len(to.OwnerGroups))
	for _, group := range to.OwnerGroups {
		ownerGroups[group] = struct{}{}
	}
	changedOwnerGroups := false
	for _, group := range from.OwnerGroups {
		if _, ok := ownerGroups[group]; !ok {
			to.OwnerGroups = append(to.OwnerGroups, group)
			changedOwnerGroups = true
		}
	}
	if changedOwnerGroups {
		sort.Strings(to.OwnerGroups)
	}
	ownerUsers := make(map[string]struct{}, len(to.OwnerUsers))
	for _, group := range to.OwnerUsers {
		ownerUsers[group] = struct{}{}
	}
	changedOwnerUsers := false
	for _, group := range from.OwnerUsers {
		if _, ok := ownerUsers[group]; !ok {
			to.OwnerUsers = append(to.OwnerUsers, group)
			changedOwnerUsers = true
		}
	}
	if changedOwnerUsers {
		sort.Strings(to.OwnerUsers)
	}
}

func (t *Topology) loadMachines(directory *Directory, dirname string,
	commonState *commonStateType) error {
	if err := directory.loadMachines(dirname); err != nil {
		return err
	}
	for _, machine := range directory.Machines {
		err := commonState.addMachine(machine, directory.subnetIdToSubnet)
		if err != nil {
			return fmt.Errorf("error adding: %s: %s", machine.Hostname, err)
		}
		t.machineParents[machine.Hostname] = directory
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"time"

	"github.com/StreamBoat/kodi_jsonrpc"
	"github.com/pdf/kodi-callback-daemon/config"
	"github.com/pdf/kodi-callback-daemon/hyperion"
	"github.com/pdf/kodi-callback-daemon/kodi"
	"github.com/pdf/kodi-callback-daemon/shell"

	log "github.com/Sirupsen/logrus"
)

const (
	VERSION = "0.4.3"
)

var (
	cfg config.Config
	k   kodi_jsonrpc.Connection
)

// usage simply prints the invocation requirements.
func usage() {
	fmt.Fprintf(os.Stderr, "\nKodi Callback Daemon v%s\n\nUsage: %s [configFile]\n\n", VERSION, os.Args[0])
	os.Exit(1)
}

// init ensures we have a config path argument, and loads the configuration.
func init() {
	if len(os.Args) < 2 {
		usage()
	}

	// Initialize logger
	cfg = config.Load(os.Args[1])
	if cfg.Debug != nil && *cfg.Debug == true {
		log.SetLevel(log.DebugLevel)
	} else {
		log.SetLevel(log.InfoLevel)
	}
}

// execute iterates through a list of callbacks, and sends them to the backend
// defined in the `backend` property.
func execute(callbacks []interface{}) {
	for i := range callbacks {
		m := callbacks[i].(map[string]interface{})

		switch m[`backend`] {
		case `hyperion`:
			if cfg.Hyperion != nil {
				hyperion.Execute(m)
			}

		case `kodi`, `xbmc`:
			kodi.Execute(&k, m)

		case `shell`:
			shell.Execute(m)

		default:
			log.WithField(`backend`, m[`backend`]).Warn(`Unknown backend`)
		}
	}
}

// callbacksByType takes a type to match, and a list of callbacks. If the
// callback has a `types` property, and that contains a matching type, the
// callback is added to the returned list.  A callback without a `types`
// property will always be returned.
func callbacksByType(matchType string, callbacks []interface{}) []interface{} {
	result := make([]interface{}, 0)
	var cb map[string]interface{}

	for i := range callbacks {
		// Access internal callback map.
		cb = callbacks[i].(map[string]interface{})

		switch cb[`types`].(type) {
		// We have a list of types.
		case []interface{}:
			// Access internal types slice.
			cbTypes, ok := cb[`types`].([]interface{})
			if ok == false {
				log.Fatal(`Couldn't understand 'types' array, check your configuration.`)
			}
			for j := range cbTypes {
				if cbTypes[j].(string) == matchType {
					// Matched the required type, add this callback to the results.
					result = append(result, cb)
				}
			}
		// If there is no valid `types` property, add this callback to the results.
		default:
			result = append(result, cb)
		}
	}

	return result
}

// main program loop.
func main() {
	var host config.Host
	// Connect to Kodi, this is required.
	kodi_timeout := time.Duration(0)
	if cfg.Kodi != nil {
		if cfg.Kodi.Timeout != nil {
			kodi_timeout = *cfg.Kodi.Timeout
		}
		host = *cfg.Kodi
	} else if cfg.XBMC != nil {
		if cfg.XBMC.Timeout != nil {
			kodi_timeout = *cfg.XBMC.Timeout
		}
		host = *cfg.XBMC
	} else {
		log.Fatal(`You must provide Kodi/XBMC connection details in your configuration`)
	}
	k, err := kodi_jsonrpc.New(
		fmt.Sprintf(`%s:%d`, host.Address, host.Port),
		kodi_timeout,
	)

	defer k.Close()
	if err != nil {
		log.WithField(`error`, err).Fatal(`Failed to obtain Kodi connection`)
	}

	// If the configuration specifies a Hyperion connection, use it.
	if cfg.Hyperion != nil {
		hyperion.Connect(fmt.Sprintf(`%s:%d`, cfg.Hyperion.Address, cfg.Hyperion.Port))
		defer hyperion.Close()
	}

	// Get callbacks from configuration.
	callbacks := cfg.Callbacks.(map[string]interface{})

	// Execute callbacks for the special `Startup` notification.
	if callbacks[`Startup`] != nil {
		execute(callbacks[`Startup`].([]interface{}))
	}

	// Loop while reading from Kodi.
	for {
		// Read from Kodi.
		notification := <-k.Notifications

		// Match Kodi notification to our configured callbacks.
		if callbacks[notification.Method] != nil {
			cbs := callbacks[notification.Method].([]interface{})
			// The Player.OnPlay notification supports an filtering by item type.
			if notification.Method == `Player.OnPlay` {
				cbs = callbacksByType(notification.Params.Data.Item.Type, cbs)
			}
			execute(cbs)
		}
	}
}
<commit_msg>Bump to version :100: for kodi name change<commit_after>package main

import (
	"fmt"
	"os"
	"time"

	"github.com/StreamBoat/kodi_jsonrpc"
	"github.com/pdf/kodi-callback-daemon/config"
	"github.com/pdf/kodi-callback-daemon/hyperion"
	"github.com/pdf/kodi-callback-daemon/kodi"
	"github.com/pdf/kodi-callback-daemon/shell"

	log "github.com/Sirupsen/logrus"
)

const (
	VERSION = "1.0.0"
)

var (
	cfg config.Config
	k   kodi_jsonrpc.Connection
)

// usage simply prints the invocation requirements.
func usage() {
	fmt.Fprintf(os.Stderr, "\nKodi Callback Daemon v%s\n\nUsage: %s [configFile]\n\n", VERSION, os.Args[0])
	os.Exit(1)
}

// init ensures we have a config path argument, and loads the configuration.
func init() {
	if len(os.Args) < 2 {
		usage()
	}

	// Initialize logger
	cfg = config.Load(os.Args[1])
	if cfg.Debug != nil && *cfg.Debug == true {
		log.SetLevel(log.DebugLevel)
	} else {
		log.SetLevel(log.InfoLevel)
	}
}

// execute iterates through a list of callbacks, and sends them to the backend
// defined in the `backend` property.
func execute(callbacks []interface{}) {
	for i := range callbacks {
		m := callbacks[i].(map[string]interface{})

		switch m[`backend`] {
		case `hyperion`:
			if cfg.Hyperion != nil {
				hyperion.Execute(m)
			}

		case `kodi`, `xbmc`:
			kodi.Execute(&k, m)

		case `shell`:
			shell.Execute(m)

		default:
			log.WithField(`backend`, m[`backend`]).Warn(`Unknown backend`)
		}
	}
}

// callbacksByType takes a type to match, and a list of callbacks. If the
// callback has a `types` property, and that contains a matching type, the
// callback is added to the returned list.  A callback without a `types`
// property will always be returned.
func callbacksByType(matchType string, callbacks []interface{}) []interface{} {
	result := make([]interface{}, 0)
	var cb map[string]interface{}

	for i := range callbacks {
		// Access internal callback map.
		cb = callbacks[i].(map[string]interface{})

		switch cb[`types`].(type) {
		// We have a list of types.
		case []interface{}:
			// Access internal types slice.
			cbTypes, ok := cb[`types`].([]interface{})
			if ok == false {
				log.Fatal(`Couldn't understand 'types' array, check your configuration.`)
			}
			for j := range cbTypes {
				if cbTypes[j].(string) == matchType {
					// Matched the required type, add this callback to the results.
					result = append(result, cb)
				}
			}
		// If there is no valid `types` property, add this callback to the results.
		default:
			result = append(result, cb)
		}
	}

	return result
}

// main program loop.
func main() {
	var host config.Host
	// Connect to Kodi, this is required.
	kodi_timeout := time.Duration(0)
	if cfg.Kodi != nil {
		if cfg.Kodi.Timeout != nil {
			kodi_timeout = *cfg.Kodi.Timeout
		}
		host = *cfg.Kodi
	} else if cfg.XBMC != nil {
		if cfg.XBMC.Timeout != nil {
			kodi_timeout = *cfg.XBMC.Timeout
		}
		host = *cfg.XBMC
	} else {
		log.Fatal(`You must provide Kodi/XBMC connection details in your configuration`)
	}
	k, err := kodi_jsonrpc.New(
		fmt.Sprintf(`%s:%d`, host.Address, host.Port),
		kodi_timeout,
	)

	defer k.Close()
	if err != nil {
		log.WithField(`error`, err).Fatal(`Failed to obtain Kodi connection`)
	}

	// If the configuration specifies a Hyperion connection, use it.
	if cfg.Hyperion != nil {
		hyperion.Connect(fmt.Sprintf(`%s:%d`, cfg.Hyperion.Address, cfg.Hyperion.Port))
		defer hyperion.Close()
	}

	// Get callbacks from configuration.
	callbacks := cfg.Callbacks.(map[string]interface{})

	// Execute callbacks for the special `Startup` notification.
	if callbacks[`Startup`] != nil {
		execute(callbacks[`Startup`].([]interface{}))
	}

	// Loop while reading from Kodi.
	for {
		// Read from Kodi.
		notification := <-k.Notifications

		// Match Kodi notification to our configured callbacks.
		if callbacks[notification.Method] != nil {
			cbs := callbacks[notification.Method].([]interface{})
			// The Player.OnPlay notification supports an filtering by item type.
			if notification.Method == `Player.OnPlay` {
				cbs = callbacksByType(notification.Params.Data.Item.Type, cbs)
			}
			execute(cbs)
		}
	}
}
<|endoftext|>


<commit_before>package gomol

import (
	"fmt"
	"time"

	"github.com/aphistic/sweet"
	. "github.com/onsi/gomega"
)

func (s *GomolSuite) TestLeakRegressionTest(t sweet.T) {
	var (
		blocker = make(chan struct{})
		l1      = newDefaultMemLogger()
		l2      = &BlockingLogger{ch: blocker}
		ch      = make(chan error)
		errors  = make(chan int)
	)

	go func() {
		defer close(errors)

		count := 0
		for range ch {
			count++
		}

		errors <- count
	}()

	testBase = NewBase()
	testBase.SetConfig(&Config{MaxQueueSize: TestMaxQueueSize})
	testBase.SetErrorChan(ch)
	testBase.AddLogger(l1)
	testBase.AddLogger(l2)
	testBase.InitLoggers()

	for i := 0; i < TestMaxQueueSize; i++ {
		testBase.Infof("test %d", i)
	}

	// l1 gets a message, but then l2 blocks immediately
	// after. We should not have any additional messages
	// sent to the first logger.
	Expect(l1.Messages()).To(HaveLen(1))
	Expect(l1.Messages()[0].Message).To(Equal("test 0"))

	// Send additional messages while the loggers are blocked
	// and the queue is full. This should NOT block the main
	// app routine.

	for i := TestMaxQueueSize; i < TestMaxQueueSize*3; i++ {
		testBase.Infof("test %d", i)
	}

	// Now, unblock the logger, publish another chunk, and
	// wait for the messages to drain so we can inspect what
	// messages made it through during the time the logger
	// was naughty.

	close(blocker)
	<-time.After(time.Millisecond * 100)

	for i := TestMaxQueueSize * 3; i < TestMaxQueueSize*4; i++ {
		testBase.Infof("test %d", i)
	}

	testBase.ShutdownLoggers()

	// We ran into a situation where in order to keep the bound
	// of the queue some messages had to be dropped - these must
	// be the _oldest_ messages and should. In this case, it is
	// the first two chunks (except the first message). The next
	// message we should see that wasn't dropped should be the
	// first message in the third chunk.

	Expect(l1.Messages()).To(HaveLen(2*TestMaxQueueSize + 1))

	// Skip checking "test 0" message

	for i := 0; i < len(l1.Messages())-1; i++ {
		Expect(l1.Messages()[i+1].Message).To(Equal(fmt.Sprintf("test %d", i+2*TestMaxQueueSize)))
	}

	Eventually(errors).Should(Receive(Equal(2*TestMaxQueueSize - 1)))
}

//
// Logger that blocks all messages

type BlockingLogger struct {
	ch chan struct{}
}

func (l *BlockingLogger) SetBase(base *Base)    {}
func (l *BlockingLogger) InitLogger() error     { return nil }
func (l *BlockingLogger) IsInitialized() bool   { return true }
func (l *BlockingLogger) ShutdownLogger() error { return nil }

func (l *BlockingLogger) Logm(timestamp time.Time, level LogLevel, attrs map[string]interface{}, msg string) error {
	<-l.ch
	return nil
}
<commit_msg>Change test to eventually have a value instead of expecting one because it was racy<commit_after>package gomol

import (
	"fmt"
	"time"

	"github.com/aphistic/sweet"
	. "github.com/onsi/gomega"
)

func (s *GomolSuite) TestLeakRegressionTest(t sweet.T) {
	var (
		blocker = make(chan struct{})
		l1      = newDefaultMemLogger()
		l2      = &BlockingLogger{ch: blocker}
		ch      = make(chan error)
		errors  = make(chan int)
	)

	go func() {
		defer close(errors)

		count := 0
		for range ch {
			count++
		}

		errors <- count
	}()

	testBase = NewBase()
	testBase.SetConfig(&Config{MaxQueueSize: TestMaxQueueSize})
	testBase.SetErrorChan(ch)
	testBase.AddLogger(l1)
	testBase.AddLogger(l2)
	testBase.InitLoggers()

	for i := 0; i < TestMaxQueueSize; i++ {
		testBase.Infof("test %d", i)
	}

	// l1 gets a message, but then l2 blocks immediately
	// after. We should not have any additional messages
	// sent to the first logger.
	Eventually(l1.Messages()).Should(HaveLen(1))
	Expect(l1.Messages()[0].Message).To(Equal("test 0"))

	// Send additional messages while the loggers are blocked
	// and the queue is full. This should NOT block the main
	// app routine.

	for i := TestMaxQueueSize; i < TestMaxQueueSize*3; i++ {
		testBase.Infof("test %d", i)
	}

	// Now, unblock the logger, publish another chunk, and
	// wait for the messages to drain so we can inspect what
	// messages made it through during the time the logger
	// was naughty.

	close(blocker)
	<-time.After(time.Millisecond * 100)

	for i := TestMaxQueueSize * 3; i < TestMaxQueueSize*4; i++ {
		testBase.Infof("test %d", i)
	}

	testBase.ShutdownLoggers()

	// We ran into a situation where in order to keep the bound
	// of the queue some messages had to be dropped - these must
	// be the _oldest_ messages and should. In this case, it is
	// the first two chunks (except the first message). The next
	// message we should see that wasn't dropped should be the
	// first message in the third chunk.

	Expect(l1.Messages()).To(HaveLen(2*TestMaxQueueSize + 1))

	// Skip checking "test 0" message

	for i := 0; i < len(l1.Messages())-1; i++ {
		Expect(l1.Messages()[i+1].Message).To(Equal(fmt.Sprintf("test %d", i+2*TestMaxQueueSize)))
	}

	Eventually(errors).Should(Receive(Equal(2*TestMaxQueueSize - 1)))
}

//
// Logger that blocks all messages

type BlockingLogger struct {
	ch chan struct{}
}

func (l *BlockingLogger) SetBase(base *Base)    {}
func (l *BlockingLogger) InitLogger() error     { return nil }
func (l *BlockingLogger) IsInitialized() bool   { return true }
func (l *BlockingLogger) ShutdownLogger() error { return nil }

func (l *BlockingLogger) Logm(timestamp time.Time, level LogLevel, attrs map[string]interface{}, msg string) error {
	<-l.ch
	return nil
}
<|endoftext|>


<commit_before><commit_msg>Clarified log message.<commit_after><|endoftext|>


<commit_before>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package cloudimagemetadata

import (
	"fmt"

	"github.com/juju/errors"
	"github.com/juju/loggo"
	jujutxn "github.com/juju/txn"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"gopkg.in/mgo.v2/txn"

	"github.com/juju/juju/environs/imagemetadata"
)

var logger = loggo.GetLogger("juju.state.cloudimagemetadata")

type cloudImageStorage struct {
	envUUID            string
	metadataCollection *mgo.Collection
	txnRunner          jujutxn.Runner
}

var _ Storage = (*cloudImageStorage)(nil)

// NewStorage constructs a new Storage that stores  image metadata
// in the provided collection using the provided transaction runner.
func NewStorage(
	envUUID string,
	metadataCollection *mgo.Collection,
	runner jujutxn.Runner,
) Storage {
	return &cloudImageStorage{
		envUUID:            envUUID,
		metadataCollection: metadataCollection,
		txnRunner:          runner,
	}
}

// AddMetadata implements Storage.AddMetadata.
func (s *cloudImageStorage) AddMetadata(metadata Metadata) error {
	newDoc := metadata.mongoDoc()
	buildTxn := func(attempt int) ([]txn.Op, error) {
		op := txn.Op{
			C:  s.metadataCollection.Name,
			Id: newDoc.Id,
		}
		if attempt == 0 {
			// On the first attempt we assume we're adding new cloud image metadata.
			op.Assert = txn.DocMissing
			op.Insert = &newDoc
			logger.Debugf("inserting cloud image metadata for %v", newDoc.Id)
		} else {
			// Subsequent attempts to add metadata will update the fields.
			op.Assert = txn.DocExists
			op.Update = bson.D{{"$set", newDoc.updates()}}
			logger.Debugf("updating cloud image metadata for %v", newDoc.Id)
		}
		return []txn.Op{op}, nil
	}

	err := s.txnRunner.Run(buildTxn)
	if err != nil {
		return errors.Annotatef(err, "cannot add cloud image metadata for %v", newDoc.Id)
	}
	return nil
}

// FindMetadata implements Storage.FindMetadata.
func (s *cloudImageStorage) FindMetadata(series, arch, stream string) (Metadata, error) {
	desiredID := createKey(series, arch, stream)
	doc, err := s.imagesMetadata(desiredID)
	if err != nil {
		return Metadata{}, errors.Trace(err)
	}
	return doc.metadata(), nil
}

// AllMetadata implements Storage.AllMetadata.
func (s *cloudImageStorage) AllMetadata() ([]Metadata, error) {
	var docs []imagesMetadataDoc
	if err := s.metadataCollection.Find(nil).All(&docs); err != nil {
		return nil, errors.Trace(err)
	}
	metadata := make([]Metadata, len(docs))
	for i, doc := range docs {
		metadata[i] = doc.metadata()
	}
	return metadata, nil
}

func (c *cloudImageStorage) imagesMetadata(desiredID string) (imagesMetadataDoc, error) {
	var doc imagesMetadataDoc
	logger.Infof("looking for cloud image metadata with id %v", desiredID)
	err := c.metadataCollection.Find(bson.D{{"_id", desiredID}}).One(&doc)
	if err == mgo.ErrNotFound {
		return doc, errors.NotFoundf("cloud image metadata with id %v", desiredID)
	}
	return doc, err
}

type imagesMetadataDoc struct {
	Id          string `bson:"_id"`
	Series      string `bson:"series"`
	Arch        string `bson:"arch"`
	Stream      string `bson:"stream,omitempty"`
	Storage     string `bson:"root_store,omitempty"`
	VirtType    string `bson:"virt,omitempty"`
	RegionAlias string `bson:"crsn,omitempty"`
	RegionName  string `bson:"region,omitempty"`
	Endpoint    string `bson:"endpoint,omitempty"`
}

func (m imagesMetadataDoc) metadata() Metadata {
	return Metadata{
		Series:      m.Series,
		Arch:        m.Arch,
		Stream:      m.Stream,
		Storage:     m.Storage,
		VirtType:    m.VirtType,
		RegionAlias: m.RegionAlias,
		RegionName:  m.RegionName,
		Endpoint:    m.Endpoint,
	}
}

func (m imagesMetadataDoc) updates() bson.D {
	return bson.D{
		{"series", m.Series},
		{"arch", m.Arch},
		{"stream", m.Stream},
		{"root_store", m.Storage},
		{"virt", m.VirtType},
		{"crsn", m.RegionAlias},
		{"region", m.RegionName},
		{"endpoint", m.Endpoint},
	}
}

func (m *Metadata) mongoDoc() imagesMetadataDoc {
	return imagesMetadataDoc{
		Id:          m.key(),
		Series:      m.Series,
		Arch:        m.Arch,
		Stream:      m.Stream,
		Storage:     m.Storage,
		VirtType:    m.VirtType,
		RegionAlias: m.RegionAlias,
		RegionName:  m.RegionName,
		Endpoint:    m.Endpoint,
	}
}

func streamKey(stream string) string {
	if stream != "" {
		return stream
	}
	// Since stream is optional,when omitted, assume "released" is desired.
	return imagemetadata.ReleasedStream
}

var createKey = func(series, arch, stream string) string {
	return fmt.Sprintf("%s-%s-%s", series, arch, streamKey(stream))
}

func (im *Metadata) key() string {
	return createKey(im.Series, im.Arch, im.Stream)
}
<commit_msg>Renamed internal var to "storage".<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package cloudimagemetadata

import (
	"fmt"

	"github.com/juju/errors"
	"github.com/juju/loggo"
	jujutxn "github.com/juju/txn"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"gopkg.in/mgo.v2/txn"

	"github.com/juju/juju/environs/imagemetadata"
)

var logger = loggo.GetLogger("juju.state.cloudimagemetadata")

type storage struct {
	envUUID            string
	metadataCollection *mgo.Collection
	txnRunner          jujutxn.Runner
}

var _ Storage = (*storage)(nil)

// NewStorage constructs a new Storage that stores  image metadata
// in the provided collection using the provided transaction runner.
func NewStorage(
	envUUID string,
	metadataCollection *mgo.Collection,
	runner jujutxn.Runner,
) Storage {
	return &storage{
		envUUID:            envUUID,
		metadataCollection: metadataCollection,
		txnRunner:          runner,
	}
}

// AddMetadata implements Storage.AddMetadata.
func (s *storage) AddMetadata(metadata Metadata) error {
	newDoc := metadata.mongoDoc()
	buildTxn := func(attempt int) ([]txn.Op, error) {
		op := txn.Op{
			C:  s.metadataCollection.Name,
			Id: newDoc.Id,
		}
		if attempt == 0 {
			// On the first attempt we assume we're adding new cloud image metadata.
			op.Assert = txn.DocMissing
			op.Insert = &newDoc
			logger.Debugf("inserting cloud image metadata for %v", newDoc.Id)
		} else {
			// Subsequent attempts to add metadata will update the fields.
			op.Assert = txn.DocExists
			op.Update = bson.D{{"$set", newDoc.updates()}}
			logger.Debugf("updating cloud image metadata for %v", newDoc.Id)
		}
		return []txn.Op{op}, nil
	}

	err := s.txnRunner.Run(buildTxn)
	if err != nil {
		return errors.Annotatef(err, "cannot add cloud image metadata for %v", newDoc.Id)
	}
	return nil
}

// FindMetadata implements Storage.FindMetadata.
func (s *storage) FindMetadata(series, arch, stream string) (Metadata, error) {
	desiredID := createKey(series, arch, stream)
	doc, err := s.imagesMetadata(desiredID)
	if err != nil {
		return Metadata{}, errors.Trace(err)
	}
	return doc.metadata(), nil
}

// AllMetadata implements Storage.AllMetadata.
func (s *storage) AllMetadata() ([]Metadata, error) {
	var docs []imagesMetadataDoc
	if err := s.metadataCollection.Find(nil).All(&docs); err != nil {
		return nil, errors.Trace(err)
	}
	metadata := make([]Metadata, len(docs))
	for i, doc := range docs {
		metadata[i] = doc.metadata()
	}
	return metadata, nil
}

func (c *storage) imagesMetadata(desiredID string) (imagesMetadataDoc, error) {
	var doc imagesMetadataDoc
	logger.Infof("looking for cloud image metadata with id %v", desiredID)
	err := c.metadataCollection.Find(bson.D{{"_id", desiredID}}).One(&doc)
	if err == mgo.ErrNotFound {
		return doc, errors.NotFoundf("cloud image metadata with id %v", desiredID)
	}
	return doc, err
}

type imagesMetadataDoc struct {
	Id          string `bson:"_id"`
	Series      string `bson:"series"`
	Arch        string `bson:"arch"`
	Stream      string `bson:"stream,omitempty"`
	Storage     string `bson:"root_store,omitempty"`
	VirtType    string `bson:"virt,omitempty"`
	RegionAlias string `bson:"crsn,omitempty"`
	RegionName  string `bson:"region,omitempty"`
	Endpoint    string `bson:"endpoint,omitempty"`
}

func (m imagesMetadataDoc) metadata() Metadata {
	return Metadata{
		Series:      m.Series,
		Arch:        m.Arch,
		Stream:      m.Stream,
		Storage:     m.Storage,
		VirtType:    m.VirtType,
		RegionAlias: m.RegionAlias,
		RegionName:  m.RegionName,
		Endpoint:    m.Endpoint,
	}
}

func (m imagesMetadataDoc) updates() bson.D {
	return bson.D{
		{"series", m.Series},
		{"arch", m.Arch},
		{"stream", m.Stream},
		{"root_store", m.Storage},
		{"virt", m.VirtType},
		{"crsn", m.RegionAlias},
		{"region", m.RegionName},
		{"endpoint", m.Endpoint},
	}
}

func (m *Metadata) mongoDoc() imagesMetadataDoc {
	return imagesMetadataDoc{
		Id:          m.key(),
		Series:      m.Series,
		Arch:        m.Arch,
		Stream:      m.Stream,
		Storage:     m.Storage,
		VirtType:    m.VirtType,
		RegionAlias: m.RegionAlias,
		RegionName:  m.RegionName,
		Endpoint:    m.Endpoint,
	}
}

func streamKey(stream string) string {
	if stream != "" {
		return stream
	}
	// Since stream is optional,when omitted, assume "released" is desired.
	return imagemetadata.ReleasedStream
}

var createKey = func(series, arch, stream string) string {
	return fmt.Sprintf("%s-%s-%s", series, arch, streamKey(stream))
}

func (im *Metadata) key() string {
	return createKey(im.Series, im.Arch, im.Stream)
}
<|endoftext|>