<commit_before>package estafette

import (
	"testing"

	"github.com/stretchr/testify/assert"

	manifest "github.com/estafette/estafette-ci-manifest"
)

func TestInjectSteps(t *testing.T) {

	t.Run("PrependParallelGitCloneStepInInitStage", func(t *testing.T) {

		mft := getManifestWithoutBuildStatusSteps()

		// act
		injectedManifest, err := InjectSteps(mft, "beta", "github")

		assert.Nil(t, err)
		if assert.Equal(t, 3, len(injectedManifest.Stages)) {
			assert.Equal(t, "initialize", injectedManifest.Stages[0].Name)
			assert.Equal(t, "git-clone", injectedManifest.Stages[0].ParallelStages[1].Name)
			assert.Equal(t, "extensions/git-clone:beta", injectedManifest.Stages[0].ParallelStages[1].ContainerImage)
		}
	})

	t.Run("PrependGitCloneStepAsNormalStageWhenBuildStatusExists", func(t *testing.T) {

		mft := getManifestWithBuildStatusSteps()

		// act
		injectedManifest, err := InjectSteps(mft, "beta", "github")

		assert.Nil(t, err)
		if assert.Equal(t, 4, len(injectedManifest.Stages)) {
			assert.Equal(t, "git-clone", injectedManifest.Stages[0].Name)
			assert.Equal(t, "extensions/git-clone:beta", injectedManifest.Stages[0].ContainerImage)
		}
	})

	t.Run("PrependGitCloneStepToReleaseStagesIfCloneRepositoryIsTrue", func(t *testing.T) {

		mft := getManifestWithoutBuildStatusSteps()

		// act
		injectedManifest, err := InjectSteps(mft, "beta", "github")

		assert.Nil(t, err)
		if assert.Equal(t, 2, len(injectedManifest.Releases[1].Stages)) {
			assert.Equal(t, "git-clone", injectedManifest.Releases[1].Stages[0].Name)
			assert.Equal(t, "extensions/git-clone:beta", injectedManifest.Releases[1].Stages[0].ContainerImage)
		}
	})

	t.Run("DoNotPrependGitCloneStepToReleaseStagesIfCloneRepositoryIsFalse", func(t *testing.T) {

		mft := getManifestWithoutBuildStatusSteps()

		// act
		injectedManifest, err := InjectSteps(mft, "beta", "github")

		assert.Nil(t, err)
		if assert.Equal(t, 1, len(injectedManifest.Releases[0].Stages)) {
			assert.Equal(t, "deploy", injectedManifest.Releases[0].Stages[0].Name)
			assert.Equal(t, "extensions/gke", injectedManifest.Releases[0].Stages[0].ContainerImage)
		}
	})

	t.Run("DoNotPrependGitCloneStepToReleaseStagesIfCloneRepositoryIsTrueButStageAlreadyExists", func(t *testing.T) {

		mft := getManifestWithBuildStatusSteps()

		// act
		injectedManifest, err := InjectSteps(mft, "beta", "github")

		assert.Nil(t, err)
		if assert.Equal(t, 2, len(injectedManifest.Releases[0].Stages)) {
			assert.Equal(t, "git-clone", injectedManifest.Releases[0].Stages[0].Name)
			assert.Equal(t, "extensions/git-clone:stable", injectedManifest.Releases[0].Stages[0].ContainerImage)
		}
	})

	t.Run("PrependParallelSetPendingBuildStatusStepInInitStage", func(t *testing.T) {

		mft := getManifestWithoutBuildStatusSteps()

		// act
		injectedManifest, err := InjectSteps(mft, "beta", "github")

		assert.Nil(t, err)
		if assert.Equal(t, 3, len(injectedManifest.Stages)) {
			assert.Equal(t, "init", injectedManifest.Stages[0].Name)
			assert.Equal(t, "set-pending-build-status", injectedManifest.Stages[0].ParallelStages[0].Name)
			assert.Equal(t, "extensions/github-status:beta", injectedManifest.Stages[0].ParallelStages[0].ContainerImage)
			assert.Equal(t, "pending", injectedManifest.Stages[0].ParallelStages[0].CustomProperties["status"])
		}
	})

	t.Run("PrependSetPendingBuildStatusAsNormalStageWhenGitCloneStageAlreadyExists", func(t *testing.T) {

		mft := getManifestWithoutBuildStatusStepsAndWithGitClone()

		// act
		injectedManifest, err := InjectSteps(mft, "beta", "github")

		assert.Nil(t, err)
		if assert.Equal(t, 4, len(injectedManifest.Stages)) {
			assert.Equal(t, "set-pending-build-status", injectedManifest.Stages[0].Name)
			assert.Equal(t, "extensions/github-status:beta", injectedManifest.Stages[0].ContainerImage)
			assert.Equal(t, "pending", injectedManifest.Stages[0].CustomProperties["status"])
		}
	})

	t.Run("AppendSetBuildStatusStep", func(t *testing.T) {

		mft := getManifestWithoutBuildStatusSteps()

		// act
		injectedManifest, err := InjectSteps(mft, "beta", "github")

		assert.Nil(t, err)
		if assert.Equal(t, 3, len(injectedManifest.Stages)) {
			assert.Equal(t, "set-build-status", injectedManifest.Stages[2].Name)
			assert.Equal(t, "extensions/github-status:beta", injectedManifest.Stages[2].ContainerImage)
		}
	})

	t.Run("PrependGitCloneStepIfBuildStatusStepsExist", func(t *testing.T) {

		mft := getManifestWithBuildStatusSteps()

		// act
		injectedManifest, err := InjectSteps(mft, "dev", "github")

		assert.Nil(t, err)
		if assert.Equal(t, 4, len(injectedManifest.Stages)) {
			assert.Equal(t, "git-clone", injectedManifest.Stages[0].Name)
			assert.Equal(t, "extensions/git-clone:dev", injectedManifest.Stages[0].ContainerImage)
		}
	})

	t.Run("DoNotPrependSetPendingBuildStatusStepIfPendingBuildStatusStepExists", func(t *testing.T) {

		mft := getManifestWithBuildStatusSteps()

		// act
		injectedManifest, err := InjectSteps(mft, "dev", "github")

		assert.Nil(t, err)
		if assert.Equal(t, 4, len(injectedManifest.Stages)) {
			assert.Equal(t, "set-pending-build-status", injectedManifest.Stages[1].Name)
			assert.Equal(t, "extensions/github-status:stable", injectedManifest.Stages[1].ContainerImage)
			assert.Equal(t, "pending", injectedManifest.Stages[1].CustomProperties["status"])
		}
	})

	t.Run("DoNotAppendSetBuildStatusStepIfSetBuildStatusStepExists", func(t *testing.T) {

		mft := getManifestWithBuildStatusSteps()

		// act
		injectedManifest, err := InjectSteps(mft, "dev", "github")

		assert.Nil(t, err)
		if assert.Equal(t, 4, len(injectedManifest.Stages)) {
			assert.Equal(t, "set-build-status", injectedManifest.Stages[3].Name)
			assert.Equal(t, "extensions/github-status:stable", injectedManifest.Stages[3].ContainerImage)
		}
	})
}

func getManifestWithoutBuildStatusSteps() manifest.EstafetteManifest {
	return manifest.EstafetteManifest{
		Builder: manifest.EstafetteBuilder{
			Track: "stable",
		},
		Version: manifest.EstafetteVersion{
			SemVer: &manifest.EstafetteSemverVersion{
				Major:         1,
				Minor:         0,
				Patch:         "234",
				LabelTemplate: "{{branch}}",
				ReleaseBranch: manifest.StringOrStringArray{Values: []string{"master"}},
			},
		},
		Labels:        map[string]string{},
		GlobalEnvVars: map[string]string{},
		Stages: []*manifest.EstafetteStage{
			&manifest.EstafetteStage{
				Name:             "build",
				ContainerImage:   "golang:1.10.2-alpine3.7",
				WorkingDirectory: "/go/src/github.com/estafette/${ESTAFETTE_GIT_NAME}",
			},
		},
		Releases: []*manifest.EstafetteRelease{
			&manifest.EstafetteRelease{
				Name:            "staging",
				CloneRepository: false,
				Stages: []*manifest.EstafetteStage{
					&manifest.EstafetteStage{
						Name:           "deploy",
						ContainerImage: "extensions/gke",
					},
				},
			},
			&manifest.EstafetteRelease{
				Name:            "production",
				CloneRepository: true,
				Stages: []*manifest.EstafetteStage{
					&manifest.EstafetteStage{
						Name:           "deploy",
						ContainerImage: "extensions/gke",
						Shell:          "/bin/sh",
						When:           "status == 'succeeded'",
					},
				},
			},
		},
	}
}

func getManifestWithoutBuildStatusStepsAndWithGitClone() manifest.EstafetteManifest {
	return manifest.EstafetteManifest{
		Builder: manifest.EstafetteBuilder{
			Track: "stable",
		},
		Version: manifest.EstafetteVersion{
			SemVer: &manifest.EstafetteSemverVersion{
				Major:         1,
				Minor:         0,
				Patch:         "234",
				LabelTemplate: "{{branch}}",
				ReleaseBranch: manifest.StringOrStringArray{Values: []string{"master"}},
			},
		},
		Labels:        map[string]string{},
		GlobalEnvVars: map[string]string{},
		Stages: []*manifest.EstafetteStage{
			&manifest.EstafetteStage{
				Name:           "git-clone",
				ContainerImage: "extensions/git-clone:stable",
				Shell:          "/bin/sh",
				When:           "status == 'succeeded'",
			},
			&manifest.EstafetteStage{
				Name:             "build",
				ContainerImage:   "golang:1.10.2-alpine3.7",
				WorkingDirectory: "/go/src/github.com/estafette/${ESTAFETTE_GIT_NAME}",
			},
		},
		Releases: []*manifest.EstafetteRelease{
			&manifest.EstafetteRelease{
				Name:            "staging",
				CloneRepository: false,
				Stages: []*manifest.EstafetteStage{
					&manifest.EstafetteStage{
						Name:           "deploy",
						ContainerImage: "extensions/gke",
					},
				},
			},
			&manifest.EstafetteRelease{
				Name:            "production",
				CloneRepository: true,
				Stages: []*manifest.EstafetteStage{
					&manifest.EstafetteStage{
						Name:           "deploy",
						ContainerImage: "extensions/gke",
						Shell:          "/bin/sh",
						When:           "status == 'succeeded'",
					},
				},
			},
		},
	}
}

func getManifestWithBuildStatusSteps() manifest.EstafetteManifest {
	return manifest.EstafetteManifest{
		Builder: manifest.EstafetteBuilder{
			Track: "stable",
		},
		Version: manifest.EstafetteVersion{
			SemVer: &manifest.EstafetteSemverVersion{
				Major:         1,
				Minor:         0,
				Patch:         "234",
				LabelTemplate: "{{branch}}",
				ReleaseBranch: manifest.StringOrStringArray{Values: []string{"master"}},
			},
		},
		Labels:        map[string]string{},
		GlobalEnvVars: map[string]string{},
		Stages: []*manifest.EstafetteStage{
			&manifest.EstafetteStage{
				Name:           "set-pending-build-status",
				ContainerImage: "extensions/github-status:stable",
				CustomProperties: map[string]interface{}{
					"status": "pending",
				},
				Shell:            "/bin/sh",
				WorkingDirectory: "/estafette-work",
				When:             "status == 'succeeded'",
			},
			&manifest.EstafetteStage{
				Name:             "build",
				ContainerImage:   "golang:1.10.2-alpine3.7",
				Shell:            "/bin/sh",
				WorkingDirectory: "/go/src/github.com/estafette/${ESTAFETTE_GIT_NAME}",
				When:             "status == 'succeeded'",
			},
			&manifest.EstafetteStage{
				Name:             "set-build-status",
				ContainerImage:   "extensions/github-status:stable",
				Shell:            "/bin/sh",
				WorkingDirectory: "/estafette-work",
				When:             "status == 'succeeded' || status == 'failed'",
			},
		},
		Releases: []*manifest.EstafetteRelease{
			&manifest.EstafetteRelease{
				Name:            "production",
				CloneRepository: true,
				Stages: []*manifest.EstafetteStage{
					&manifest.EstafetteStage{
						Name:           "git-clone",
						ContainerImage: "extensions/git-clone:stable",
						Shell:          "/bin/sh",
						When:           "status == 'succeeded'",
					},
					&manifest.EstafetteStage{
						Name:           "deploy",
						ContainerImage: "extensions/gke",
						Shell:          "/bin/sh",
						When:           "status == 'succeeded'",
					},
				},
			},
		},
	}
}
<commit_msg>fix initialize stage name in test<commit_after>package estafette

import (
	"testing"

	"github.com/stretchr/testify/assert"

	manifest "github.com/estafette/estafette-ci-manifest"
)

func TestInjectSteps(t *testing.T) {

	t.Run("PrependParallelGitCloneStepInInitStage", func(t *testing.T) {

		mft := getManifestWithoutBuildStatusSteps()

		// act
		injectedManifest, err := InjectSteps(mft, "beta", "github")

		assert.Nil(t, err)
		if assert.Equal(t, 3, len(injectedManifest.Stages)) {
			assert.Equal(t, "initialize", injectedManifest.Stages[0].Name)
			assert.Equal(t, "git-clone", injectedManifest.Stages[0].ParallelStages[1].Name)
			assert.Equal(t, "extensions/git-clone:beta", injectedManifest.Stages[0].ParallelStages[1].ContainerImage)
		}
	})

	t.Run("PrependGitCloneStepAsNormalStageWhenBuildStatusExists", func(t *testing.T) {

		mft := getManifestWithBuildStatusSteps()

		// act
		injectedManifest, err := InjectSteps(mft, "beta", "github")

		assert.Nil(t, err)
		if assert.Equal(t, 4, len(injectedManifest.Stages)) {
			assert.Equal(t, "git-clone", injectedManifest.Stages[0].Name)
			assert.Equal(t, "extensions/git-clone:beta", injectedManifest.Stages[0].ContainerImage)
		}
	})

	t.Run("PrependGitCloneStepToReleaseStagesIfCloneRepositoryIsTrue", func(t *testing.T) {

		mft := getManifestWithoutBuildStatusSteps()

		// act
		injectedManifest, err := InjectSteps(mft, "beta", "github")

		assert.Nil(t, err)
		if assert.Equal(t, 2, len(injectedManifest.Releases[1].Stages)) {
			assert.Equal(t, "git-clone", injectedManifest.Releases[1].Stages[0].Name)
			assert.Equal(t, "extensions/git-clone:beta", injectedManifest.Releases[1].Stages[0].ContainerImage)
		}
	})

	t.Run("DoNotPrependGitCloneStepToReleaseStagesIfCloneRepositoryIsFalse", func(t *testing.T) {

		mft := getManifestWithoutBuildStatusSteps()

		// act
		injectedManifest, err := InjectSteps(mft, "beta", "github")

		assert.Nil(t, err)
		if assert.Equal(t, 1, len(injectedManifest.Releases[0].Stages)) {
			assert.Equal(t, "deploy", injectedManifest.Releases[0].Stages[0].Name)
			assert.Equal(t, "extensions/gke", injectedManifest.Releases[0].Stages[0].ContainerImage)
		}
	})

	t.Run("DoNotPrependGitCloneStepToReleaseStagesIfCloneRepositoryIsTrueButStageAlreadyExists", func(t *testing.T) {

		mft := getManifestWithBuildStatusSteps()

		// act
		injectedManifest, err := InjectSteps(mft, "beta", "github")

		assert.Nil(t, err)
		if assert.Equal(t, 2, len(injectedManifest.Releases[0].Stages)) {
			assert.Equal(t, "git-clone", injectedManifest.Releases[0].Stages[0].Name)
			assert.Equal(t, "extensions/git-clone:stable", injectedManifest.Releases[0].Stages[0].ContainerImage)
		}
	})

	t.Run("PrependParallelSetPendingBuildStatusStepInInitStage", func(t *testing.T) {

		mft := getManifestWithoutBuildStatusSteps()

		// act
		injectedManifest, err := InjectSteps(mft, "beta", "github")

		assert.Nil(t, err)
		if assert.Equal(t, 3, len(injectedManifest.Stages)) {
			assert.Equal(t, "initialize", injectedManifest.Stages[0].Name)
			assert.Equal(t, "set-pending-build-status", injectedManifest.Stages[0].ParallelStages[0].Name)
			assert.Equal(t, "extensions/github-status:beta", injectedManifest.Stages[0].ParallelStages[0].ContainerImage)
			assert.Equal(t, "pending", injectedManifest.Stages[0].ParallelStages[0].CustomProperties["status"])
		}
	})

	t.Run("PrependSetPendingBuildStatusAsNormalStageWhenGitCloneStageAlreadyExists", func(t *testing.T) {

		mft := getManifestWithoutBuildStatusStepsAndWithGitClone()

		// act
		injectedManifest, err := InjectSteps(mft, "beta", "github")

		assert.Nil(t, err)
		if assert.Equal(t, 4, len(injectedManifest.Stages)) {
			assert.Equal(t, "set-pending-build-status", injectedManifest.Stages[0].Name)
			assert.Equal(t, "extensions/github-status:beta", injectedManifest.Stages[0].ContainerImage)
			assert.Equal(t, "pending", injectedManifest.Stages[0].CustomProperties["status"])
		}
	})

	t.Run("AppendSetBuildStatusStep", func(t *testing.T) {

		mft := getManifestWithoutBuildStatusSteps()

		// act
		injectedManifest, err := InjectSteps(mft, "beta", "github")

		assert.Nil(t, err)
		if assert.Equal(t, 3, len(injectedManifest.Stages)) {
			assert.Equal(t, "set-build-status", injectedManifest.Stages[2].Name)
			assert.Equal(t, "extensions/github-status:beta", injectedManifest.Stages[2].ContainerImage)
		}
	})

	t.Run("PrependGitCloneStepIfBuildStatusStepsExist", func(t *testing.T) {

		mft := getManifestWithBuildStatusSteps()

		// act
		injectedManifest, err := InjectSteps(mft, "dev", "github")

		assert.Nil(t, err)
		if assert.Equal(t, 4, len(injectedManifest.Stages)) {
			assert.Equal(t, "git-clone", injectedManifest.Stages[0].Name)
			assert.Equal(t, "extensions/git-clone:dev", injectedManifest.Stages[0].ContainerImage)
		}
	})

	t.Run("DoNotPrependSetPendingBuildStatusStepIfPendingBuildStatusStepExists", func(t *testing.T) {

		mft := getManifestWithBuildStatusSteps()

		// act
		injectedManifest, err := InjectSteps(mft, "dev", "github")

		assert.Nil(t, err)
		if assert.Equal(t, 4, len(injectedManifest.Stages)) {
			assert.Equal(t, "set-pending-build-status", injectedManifest.Stages[1].Name)
			assert.Equal(t, "extensions/github-status:stable", injectedManifest.Stages[1].ContainerImage)
			assert.Equal(t, "pending", injectedManifest.Stages[1].CustomProperties["status"])
		}
	})

	t.Run("DoNotAppendSetBuildStatusStepIfSetBuildStatusStepExists", func(t *testing.T) {

		mft := getManifestWithBuildStatusSteps()

		// act
		injectedManifest, err := InjectSteps(mft, "dev", "github")

		assert.Nil(t, err)
		if assert.Equal(t, 4, len(injectedManifest.Stages)) {
			assert.Equal(t, "set-build-status", injectedManifest.Stages[3].Name)
			assert.Equal(t, "extensions/github-status:stable", injectedManifest.Stages[3].ContainerImage)
		}
	})
}

func getManifestWithoutBuildStatusSteps() manifest.EstafetteManifest {
	return manifest.EstafetteManifest{
		Builder: manifest.EstafetteBuilder{
			Track: "stable",
		},
		Version: manifest.EstafetteVersion{
			SemVer: &manifest.EstafetteSemverVersion{
				Major:         1,
				Minor:         0,
				Patch:         "234",
				LabelTemplate: "{{branch}}",
				ReleaseBranch: manifest.StringOrStringArray{Values: []string{"master"}},
			},
		},
		Labels:        map[string]string{},
		GlobalEnvVars: map[string]string{},
		Stages: []*manifest.EstafetteStage{
			&manifest.EstafetteStage{
				Name:             "build",
				ContainerImage:   "golang:1.10.2-alpine3.7",
				WorkingDirectory: "/go/src/github.com/estafette/${ESTAFETTE_GIT_NAME}",
			},
		},
		Releases: []*manifest.EstafetteRelease{
			&manifest.EstafetteRelease{
				Name:            "staging",
				CloneRepository: false,
				Stages: []*manifest.EstafetteStage{
					&manifest.EstafetteStage{
						Name:           "deploy",
						ContainerImage: "extensions/gke",
					},
				},
			},
			&manifest.EstafetteRelease{
				Name:            "production",
				CloneRepository: true,
				Stages: []*manifest.EstafetteStage{
					&manifest.EstafetteStage{
						Name:           "deploy",
						ContainerImage: "extensions/gke",
						Shell:          "/bin/sh",
						When:           "status == 'succeeded'",
					},
				},
			},
		},
	}
}

func getManifestWithoutBuildStatusStepsAndWithGitClone() manifest.EstafetteManifest {
	return manifest.EstafetteManifest{
		Builder: manifest.EstafetteBuilder{
			Track: "stable",
		},
		Version: manifest.EstafetteVersion{
			SemVer: &manifest.EstafetteSemverVersion{
				Major:         1,
				Minor:         0,
				Patch:         "234",
				LabelTemplate: "{{branch}}",
				ReleaseBranch: manifest.StringOrStringArray{Values: []string{"master"}},
			},
		},
		Labels:        map[string]string{},
		GlobalEnvVars: map[string]string{},
		Stages: []*manifest.EstafetteStage{
			&manifest.EstafetteStage{
				Name:           "git-clone",
				ContainerImage: "extensions/git-clone:stable",
				Shell:          "/bin/sh",
				When:           "status == 'succeeded'",
			},
			&manifest.EstafetteStage{
				Name:             "build",
				ContainerImage:   "golang:1.10.2-alpine3.7",
				WorkingDirectory: "/go/src/github.com/estafette/${ESTAFETTE_GIT_NAME}",
			},
		},
		Releases: []*manifest.EstafetteRelease{
			&manifest.EstafetteRelease{
				Name:            "staging",
				CloneRepository: false,
				Stages: []*manifest.EstafetteStage{
					&manifest.EstafetteStage{
						Name:           "deploy",
						ContainerImage: "extensions/gke",
					},
				},
			},
			&manifest.EstafetteRelease{
				Name:            "production",
				CloneRepository: true,
				Stages: []*manifest.EstafetteStage{
					&manifest.EstafetteStage{
						Name:           "deploy",
						ContainerImage: "extensions/gke",
						Shell:          "/bin/sh",
						When:           "status == 'succeeded'",
					},
				},
			},
		},
	}
}

func getManifestWithBuildStatusSteps() manifest.EstafetteManifest {
	return manifest.EstafetteManifest{
		Builder: manifest.EstafetteBuilder{
			Track: "stable",
		},
		Version: manifest.EstafetteVersion{
			SemVer: &manifest.EstafetteSemverVersion{
				Major:         1,
				Minor:         0,
				Patch:         "234",
				LabelTemplate: "{{branch}}",
				ReleaseBranch: manifest.StringOrStringArray{Values: []string{"master"}},
			},
		},
		Labels:        map[string]string{},
		GlobalEnvVars: map[string]string{},
		Stages: []*manifest.EstafetteStage{
			&manifest.EstafetteStage{
				Name:           "set-pending-build-status",
				ContainerImage: "extensions/github-status:stable",
				CustomProperties: map[string]interface{}{
					"status": "pending",
				},
				Shell:            "/bin/sh",
				WorkingDirectory: "/estafette-work",
				When:             "status == 'succeeded'",
			},
			&manifest.EstafetteStage{
				Name:             "build",
				ContainerImage:   "golang:1.10.2-alpine3.7",
				Shell:            "/bin/sh",
				WorkingDirectory: "/go/src/github.com/estafette/${ESTAFETTE_GIT_NAME}",
				When:             "status == 'succeeded'",
			},
			&manifest.EstafetteStage{
				Name:             "set-build-status",
				ContainerImage:   "extensions/github-status:stable",
				Shell:            "/bin/sh",
				WorkingDirectory: "/estafette-work",
				When:             "status == 'succeeded' || status == 'failed'",
			},
		},
		Releases: []*manifest.EstafetteRelease{
			&manifest.EstafetteRelease{
				Name:            "production",
				CloneRepository: true,
				Stages: []*manifest.EstafetteStage{
					&manifest.EstafetteStage{
						Name:           "git-clone",
						ContainerImage: "extensions/git-clone:stable",
						Shell:          "/bin/sh",
						When:           "status == 'succeeded'",
					},
					&manifest.EstafetteStage{
						Name:           "deploy",
						ContainerImage: "extensions/gke",
						Shell:          "/bin/sh",
						When:           "status == 'succeeded'",
					},
				},
			},
		},
	}
}
<|endoftext|>


<commit_before>package subdomains

import (
	"strings"

	"github.com/webx-top/echo"
	"github.com/webx-top/echo/engine"
	"github.com/webx-top/echo/engine/fasthttp"
	"github.com/webx-top/echo/engine/standard"
)

func New() *Subdomains {
	s := &Subdomains{
		Hosts:   map[string]*echo.Echo{},
		Alias:   map[string]*Info{},
		Default: ``,
	}
	return s
}

type Info struct {
	Name string
	Host string
	*echo.Echo
}

type Subdomains struct {
	Hosts    map[string]*echo.Echo
	Alias    map[string]*Info
	Default  string //default name
	Protocol string //http/https
}

func (s *Subdomains) Add(name string, e *echo.Echo) *Subdomains {
	r := strings.SplitN(name, `@`, 2) //blog@www.blog.com
	var host string
	if len(r) > 1 {
		name = r[0]
		host = r[1]
	}
	s.Hosts[host] = e
	s.Alias[name] = &Info{Name: name, Host: host, Echo: e}
	return s
}

func (s *Subdomains) Get(args ...string) *Info {
	name := s.Default
	if len(args) > 0 {
		name = args[0]
	}
	if e, ok := s.Alias[name]; ok {
		return e
	}
	return nil
}

func (s *Subdomains) URL(purl string, args ...string) string {
	info := s.Get(args...)
	if info == nil {
		return purl
	}
	if len(s.Protocol) < 1 {
		return `http://` + info.Host + purl
	}
	return s.Protocol + `://` + info.Host + purl
}

func (s *Subdomains) FindByDomain(host string) (*echo.Echo, bool) {
	handler, exists := s.Hosts[host]
	if !exists {
		if p := strings.LastIndexByte(host, ':'); p > -1 {
			handler, exists = s.Hosts[host[0:p]]
		}
		if !exists {
			var info *Info
			info, exists = s.Alias[s.Default]
			if exists {
				handler = info.Echo
			}
		}
	}
	return handler, exists
}

func (s *Subdomains) ServeHTTP(r engine.Request, w engine.Response) {
	domain := r.Host()
	handler, exists := s.FindByDomain(domain)
	if exists && handler != nil {
		handler.ServeHTTP(r, w)
	} else {
		w.NotFound()
	}
}

func (s *Subdomains) Run(args ...interface{}) {
	var eng engine.Engine
	var arg interface{}
	size := len(args)
	if size > 0 {
		arg = args[0]
	}
	if size > 1 {
		if conf, ok := arg.(*engine.Config); ok {
			if v, ok := args[1].(string); ok {
				if v == `fast` {
					eng = fasthttp.NewWithConfig(conf)
				} else {
					eng = standard.NewWithConfig(conf)
				}
			} else {
				eng = fasthttp.NewWithConfig(conf)
			}
		} else {
			addr := `:80`
			if v, ok := arg.(string); ok && len(v) > 0 {
				addr = v
			}
			if v, ok := args[1].(string); ok {
				if v == `fast` {
					eng = fasthttp.New(addr)
				} else {
					eng = standard.New(addr)
				}
			} else {
				eng = fasthttp.New(addr)
			}
		}
	} else {
		switch v := arg.(type) {
		case string:
			eng = fasthttp.New(v)
		case engine.Engine:
			eng = v
		default:
			eng = fasthttp.New(`:80`)
		}
	}
	e := s.Get()
	if e == nil {
		for _, info := range s.Alias {
			e = info
			break
		}
	}
	e.Logger().Info(`Server has been launched.`)
	e.Run(eng, s)
	e.Logger().Info(`Server has been closed.`)
}
<commit_msg>update<commit_after>package subdomains

import (
	"strings"

	"github.com/webx-top/echo"
	"github.com/webx-top/echo/engine"
	"github.com/webx-top/echo/engine/fasthttp"
	"github.com/webx-top/echo/engine/standard"
)

var Default = New()

func New() *Subdomains {
	s := &Subdomains{
		Hosts:   map[string]*echo.Echo{},
		Alias:   map[string]*Info{},
		Default: ``,
	}
	return s
}

type Info struct {
	Name string
	Host string
	*echo.Echo
}

type Subdomains struct {
	Hosts    map[string]*echo.Echo
	Alias    map[string]*Info
	Default  string //default name
	Protocol string //http/https
}

func (s *Subdomains) Add(name string, e *echo.Echo) *Subdomains {
	r := strings.SplitN(name, `@`, 2) //blog@www.blog.com
	var host string
	if len(r) > 1 {
		name = r[0]
		host = r[1]
	}
	s.Hosts[host] = e
	s.Alias[name] = &Info{Name: name, Host: host, Echo: e}
	return s
}

func (s *Subdomains) Get(args ...string) *Info {
	name := s.Default
	if len(args) > 0 {
		name = args[0]
	}
	if e, ok := s.Alias[name]; ok {
		return e
	}
	return nil
}

func (s *Subdomains) URL(purl string, args ...string) string {
	info := s.Get(args...)
	if info == nil {
		return purl
	}
	if len(s.Protocol) < 1 {
		return `http://` + info.Host + purl
	}
	return s.Protocol + `://` + info.Host + purl
}

func (s *Subdomains) FindByDomain(host string) (*echo.Echo, bool) {
	handler, exists := s.Hosts[host]
	if !exists {
		if p := strings.LastIndexByte(host, ':'); p > -1 {
			handler, exists = s.Hosts[host[0:p]]
		}
		if !exists {
			var info *Info
			info, exists = s.Alias[s.Default]
			if exists {
				handler = info.Echo
			}
		}
	}
	return handler, exists
}

func (s *Subdomains) ServeHTTP(r engine.Request, w engine.Response) {
	domain := r.Host()
	handler, exists := s.FindByDomain(domain)
	if exists && handler != nil {
		handler.ServeHTTP(r, w)
	} else {
		w.NotFound()
	}
}

func (s *Subdomains) Run(args ...interface{}) {
	var eng engine.Engine
	var arg interface{}
	size := len(args)
	if size > 0 {
		arg = args[0]
	}
	if size > 1 {
		if conf, ok := arg.(*engine.Config); ok {
			if v, ok := args[1].(string); ok {
				if v == `fast` {
					eng = fasthttp.NewWithConfig(conf)
				} else {
					eng = standard.NewWithConfig(conf)
				}
			} else {
				eng = fasthttp.NewWithConfig(conf)
			}
		} else {
			addr := `:80`
			if v, ok := arg.(string); ok && len(v) > 0 {
				addr = v
			}
			if v, ok := args[1].(string); ok {
				if v == `fast` {
					eng = fasthttp.New(addr)
				} else {
					eng = standard.New(addr)
				}
			} else {
				eng = fasthttp.New(addr)
			}
		}
	} else {
		switch v := arg.(type) {
		case string:
			eng = fasthttp.New(v)
		case engine.Engine:
			eng = v
		default:
			eng = fasthttp.New(`:80`)
		}
	}
	e := s.Get()
	if e == nil {
		for _, info := range s.Alias {
			e = info
			break
		}
	}
	e.Logger().Info(`Server has been launched.`)
	e.Run(eng, s)
	e.Logger().Info(`Server has been closed.`)
}
<|endoftext|>


<commit_before>package subdomains

import (
	"strings"

	"github.com/webx-top/echo"
	"github.com/webx-top/echo/engine"
	"github.com/webx-top/echo/engine/fasthttp"
	"github.com/webx-top/echo/engine/standard"
)

func New() *Subdomains {
	s := &Subdomains{
		Hosts:   map[string]*echo.Echo{},
		Alias:   map[string]*Info{},
		Default: ``,
	}
	return s
}

type Info struct {
	Name string
	Host string
	*echo.Echo
}

type Subdomains struct {
	Hosts   map[string]*echo.Echo
	Alias   map[string]*Info
	Default string //default name
}

func (s *Subdomains) Add(name string, e *echo.Echo) *Subdomains {
	r := strings.SplitN(name, `@`, 2) //blog@www.blog.com
	var host string
	if len(r) > 1 {
		name = r[0]
		host = r[1]
	}
	s.Hosts[host] = e
	s.Alias[name] = &Info{Name: name, Host: host, Echo: e}
	return s
}

func (s *Subdomains) Get(args ...string) *Info {
	name := s.Default
	if len(args) > 0 {
		name = args[0]
		if e, ok := s.Alias[name]; ok {
			return e
		}
	}
	return nil
}

func (s *Subdomains) FindByDomain(host string) (*echo.Echo, bool) {
	handler, exists := s.Hosts[host]
	if !exists {
		if p := strings.LastIndexByte(host, ':'); p > -1 {
			handler, exists = s.Hosts[host[0:p]]
		}
		if !exists {
			var info *Info
			info, exists = s.Alias[s.Default]
			if exists {
				handler = info.Echo
			}
		}
	}
	return handler, exists
}

func (s *Subdomains) ServeHTTP(r engine.Request, w engine.Response) {
	domain := r.Host()
	handler, exists := s.FindByDomain(domain)
	if exists && handler != nil {
		handler.ServeHTTP(r, w)
	} else {
		w.NotFound()
	}
}

func (s *Subdomains) Run(args ...interface{}) {
	var eng engine.Engine
	var arg interface{}
	size := len(args)
	if size > 0 {
		arg = args[0]
	}
	if size > 1 {
		if conf, ok := arg.(*engine.Config); ok {
			if v, ok := args[1].(string); ok {
				if v == `fast` {
					eng = fasthttp.NewWithConfig(conf)
				} else {
					eng = standard.NewWithConfig(conf)
				}
			} else {
				eng = fasthttp.NewWithConfig(conf)
			}
		} else {
			addr := `:80`
			if v, ok := arg.(string); ok && len(v) > 0 {
				addr = v
			}
			if v, ok := args[1].(string); ok {
				if v == `fast` {
					eng = fasthttp.New(addr)
				} else {
					eng = standard.New(addr)
				}
			} else {
				eng = fasthttp.New(addr)
			}
		}
	} else {
		switch v := arg.(type) {
		case string:
			eng = fasthttp.New(v)
		case engine.Engine:
			eng = v
		default:
			eng = fasthttp.New(`:80`)
		}
	}
	e := s.Get()
	if e == nil {
		for _, info := range s.Alias {
			e = info
			break
		}
	}
	e.Logger().Info(`Server has been launched.`)
	e.Run(eng, s)
	e.Logger().Info(`Server has been closed.`)
}
<commit_msg>update<commit_after>package subdomains

import (
	"strings"

	"github.com/webx-top/echo"
	"github.com/webx-top/echo/engine"
	"github.com/webx-top/echo/engine/fasthttp"
	"github.com/webx-top/echo/engine/standard"
)

func New() *Subdomains {
	s := &Subdomains{
		Hosts:   map[string]*echo.Echo{},
		Alias:   map[string]*Info{},
		Default: ``,
	}
	return s
}

type Info struct {
	Name string
	Host string
	*echo.Echo
}

type Subdomains struct {
	Hosts    map[string]*echo.Echo
	Alias    map[string]*Info
	Default  string //default name
	Protocol string //http/https
}

func (s *Subdomains) Add(name string, e *echo.Echo) *Subdomains {
	r := strings.SplitN(name, `@`, 2) //blog@www.blog.com
	var host string
	if len(r) > 1 {
		name = r[0]
		host = r[1]
	}
	s.Hosts[host] = e
	s.Alias[name] = &Info{Name: name, Host: host, Echo: e}
	return s
}

func (s *Subdomains) Get(args ...string) *Info {
	name := s.Default
	if len(args) > 0 {
		name = args[0]
		if e, ok := s.Alias[name]; ok {
			return e
		}
	}
	return nil
}

func (s *Subdomains) URL(purl string, args ...string) string {
	info := s.Get(args...)
	if info == nil {
		return purl
	}
	if len(s.Protocol) < 1 {
		return `http://` + info.Host + purl
	}
	return s.Protocol + `://` + info.Host + purl
}

func (s *Subdomains) FindByDomain(host string) (*echo.Echo, bool) {
	handler, exists := s.Hosts[host]
	if !exists {
		if p := strings.LastIndexByte(host, ':'); p > -1 {
			handler, exists = s.Hosts[host[0:p]]
		}
		if !exists {
			var info *Info
			info, exists = s.Alias[s.Default]
			if exists {
				handler = info.Echo
			}
		}
	}
	return handler, exists
}

func (s *Subdomains) ServeHTTP(r engine.Request, w engine.Response) {
	domain := r.Host()
	handler, exists := s.FindByDomain(domain)
	if exists && handler != nil {
		handler.ServeHTTP(r, w)
	} else {
		w.NotFound()
	}
}

func (s *Subdomains) Run(args ...interface{}) {
	var eng engine.Engine
	var arg interface{}
	size := len(args)
	if size > 0 {
		arg = args[0]
	}
	if size > 1 {
		if conf, ok := arg.(*engine.Config); ok {
			if v, ok := args[1].(string); ok {
				if v == `fast` {
					eng = fasthttp.NewWithConfig(conf)
				} else {
					eng = standard.NewWithConfig(conf)
				}
			} else {
				eng = fasthttp.NewWithConfig(conf)
			}
		} else {
			addr := `:80`
			if v, ok := arg.(string); ok && len(v) > 0 {
				addr = v
			}
			if v, ok := args[1].(string); ok {
				if v == `fast` {
					eng = fasthttp.New(addr)
				} else {
					eng = standard.New(addr)
				}
			} else {
				eng = fasthttp.New(addr)
			}
		}
	} else {
		switch v := arg.(type) {
		case string:
			eng = fasthttp.New(v)
		case engine.Engine:
			eng = v
		default:
			eng = fasthttp.New(`:80`)
		}
	}
	e := s.Get()
	if e == nil {
		for _, info := range s.Alias {
			e = info
			break
		}
	}
	e.Logger().Info(`Server has been launched.`)
	e.Run(eng, s)
	e.Logger().Info(`Server has been closed.`)
}
<|endoftext|>


<commit_before>package models_test
<commit_msg>added tests for NewDrill<commit_after>package models_test

import (
	. "taap_project/models"
	"testing"
)

func TestNewDrill(t *testing.T) {
	drill := Drill{"title", "category", 0}
	if drill.Title != "title" && drill.Category != "category" && drill.ImageId != 0 {
		t.Fail()
	}
}
<|endoftext|>


<commit_before>// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://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 client

import (
	"context"
	"crypto/rand"
	"errors"
	"fmt"
	"io"
	"sync"
	"time"

	log "github.com/golang/glog"
	"github.com/golang/protobuf/proto"
	"github.com/google/fleetspeak/fleetspeak/src/client/service"
	"github.com/google/fleetspeak/fleetspeak/src/common"

	fspb "github.com/google/fleetspeak/fleetspeak/src/common/proto/fleetspeak"
)

const inboxSize = 100

// A serviceConfiguration manages and communicates the services installed on a
// client. In normal use it is a singleton.
type serviceConfiguration struct {
	services  map[string]*serviceData
	lock      sync.RWMutex // Protects the structure of services.
	client    *Client
	factories map[string]service.Factory // Used to look up correct factory when configuring services.
}

func (c *serviceConfiguration) ProcessMessage(ctx context.Context, m *fspb.Message) error {
	c.lock.RLock()
	target := c.services[m.Destination.ServiceName]
	c.lock.RUnlock()

	if target == nil {
		return fmt.Errorf("destination service not installed")
	}
	select {
	case target.inbox <- m:

		target.countLock.Lock()
		target.acceptCount++
		target.countLock.Unlock()

		return nil
	case <-ctx.Done():
		return ctx.Err()
	}
}

func (c *serviceConfiguration) InstallSignedService(sd *fspb.SignedClientServiceConfig) error {
	if err := c.client.config.ValidateServiceConfig(sd); err != nil {
		return fmt.Errorf("Unable to verify signature of service config %v, ignoring: %v", sd.Signature, err)
	}

	var cfg fspb.ClientServiceConfig
	if err := proto.Unmarshal(sd.ServiceConfig, &cfg); err != nil {
		return fmt.Errorf("Unable to parse service config [%v], ignoring: %v", sd.Signature, err)
	}

ll:
	for _, l := range cfg.RequiredLabels {
		if l.ServiceName == "client" {
			for _, cl := range c.client.cfg.ClientLabels {
				if cl.Label == l.Label {
					continue ll
				}
			}
			return fmt.Errorf("service config requires label %v", l)
		}
	}

	return c.InstallService(&cfg, sd.Signature)
}

func (c *serviceConfiguration) InstallService(cfg *fspb.ClientServiceConfig, sig []byte) error {

	if cfg.Name == "" || cfg.Name == "system" || cfg.Name == "client" {
		return fmt.Errorf("illegal service name [%v]", cfg.Name)
	}

	f := c.factories[cfg.Factory]
	if f == nil {
		return fmt.Errorf("factory not found [%v]", cfg.Factory)
	}
	s, err := f(cfg)
	if err != nil {
		return fmt.Errorf("unable to create service: %v", err)
	}

	d := serviceData{
		config:  c,
		name:    cfg.Name,
		service: s,
		inbox:   make(chan *fspb.Message, inboxSize),
	}
	if err := d.start(); err != nil {
		return fmt.Errorf("unable to start service: %v", err)
	}

	d.working.Add(1)
	go d.processingLoop()

	c.lock.Lock()
	old := c.services[cfg.Name]
	c.services[cfg.Name] = &d
	c.client.config.RecordRunningService(cfg.Name, sig)
	c.lock.Unlock()

	if old != nil {
		old.stop()
	}

	log.Infof("Started service %v with config:\n%v", cfg.Name, cfg)
	return nil
}

// Counts returns the number of accepted and processed messages for each
// service.
func (c *serviceConfiguration) Counts() (accepted, processed map[string]uint64) {
	am := make(map[string]uint64)
	pm := make(map[string]uint64)
	c.lock.RLock()
	defer c.lock.RUnlock()

	for _, sd := range c.services {
		sd.countLock.Lock()
		a, p := sd.acceptCount, sd.processedCount
		sd.countLock.Unlock()
		am[sd.name] = a
		pm[sd.name] = p
	}
	return am, pm
}

func (c *serviceConfiguration) Stop() {
	c.lock.Lock()
	defer c.lock.Unlock()
	for _, sd := range c.services {
		sd.stop()
	}
	c.services = make(map[string]*serviceData)
}

// A serviceData contains the data we have about a configured service, wrapping
// a Service interface and mediating communication between it and the rest of
// the Fleetspeak client.
type serviceData struct {
	config  *serviceConfiguration
	name    string
	working sync.WaitGroup
	service service.Service
	inbox   chan *fspb.Message

	countLock                   sync.Mutex // Protects acceptCount, processCount
	acceptCount, processedCount uint64
}

// Send implements service.Context.
func (d *serviceData) Send(ctx context.Context, am service.AckMessage) error {
	m := am.M
	id := d.config.client.config.ClientID().Bytes()

	m.Source = &fspb.Address{
		ClientId:    id,
		ServiceName: d.name,
	}

	if len(m.SourceMessageId) == 0 {
		b := make([]byte, 16)
		if _, err := rand.Read(b); err != nil {
			return fmt.Errorf("unable to create random source message id: %v", err)
		}
		m.SourceMessageId = b
	}

	return d.config.client.ProcessMessage(ctx, am)
}

// GetLocalInfo implements service.Context.
func (d *serviceData) GetLocalInfo() *service.LocalInfo {
	ret := &service.LocalInfo{
		ClientID: d.config.client.config.ClientID(),
		Labels:   d.config.client.config.Labels(),
	}

	d.config.lock.RLock()
	defer d.config.lock.RUnlock()
	for s := range d.config.services {
		if s != "system" {
			ret.Services = append(ret.Services, s)
		}
	}
	return ret
}

// GetFileIfModified implements service.Context.
func (d *serviceData) GetFileIfModified(ctx context.Context, name string, modSince time.Time) (io.ReadCloser, time.Time, error) {
	if d.config.client.com == nil {
		// happens during tests
		return nil, time.Time{}, errors.New("file not found")
	}
	return d.config.client.com.GetFileIfModified(ctx, d.name, name, modSince)
}

func (d *serviceData) processingLoop() {
	for {
		m, ok := <-d.inbox

		d.countLock.Lock()
		d.processedCount++
		cnt := d.processedCount
		d.countLock.Unlock()

		log.Errorf("%s: cnt: %d", d.name, cnt)
		if cnt&0x1f == 0 {
			log.Error("beaconing")
			select {
			case d.config.client.processingBeacon <- struct{}{}:
			default:
			}
		}

		if !ok {
			d.working.Done()
			return
		}
		id, err := common.BytesToMessageID(m.MessageId)
		if err != nil {
			log.Errorf("ignoring message with bad message id: [%v]", m.MessageId)
			continue
		}
		if err := d.service.ProcessMessage(context.TODO(), m); err != nil {
			d.config.client.errs <- &fspb.MessageErrorData{
				MessageId: id.Bytes(),
				Error:     err.Error(),
			}
		} else {
			d.config.client.acks <- id
		}

	}
}

func (d *serviceData) start() error {
	return d.service.Start(d)
}

func (d *serviceData) stop() {
	close(d.inbox)
	d.working.Wait()
	d.service.Stop()
}
<commit_msg>Remove some accidentally added log calls.<commit_after>// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://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 client

import (
	"context"
	"crypto/rand"
	"errors"
	"fmt"
	"io"
	"sync"
	"time"

	log "github.com/golang/glog"
	"github.com/golang/protobuf/proto"
	"github.com/google/fleetspeak/fleetspeak/src/client/service"
	"github.com/google/fleetspeak/fleetspeak/src/common"

	fspb "github.com/google/fleetspeak/fleetspeak/src/common/proto/fleetspeak"
)

const inboxSize = 100

// A serviceConfiguration manages and communicates the services installed on a
// client. In normal use it is a singleton.
type serviceConfiguration struct {
	services  map[string]*serviceData
	lock      sync.RWMutex // Protects the structure of services.
	client    *Client
	factories map[string]service.Factory // Used to look up correct factory when configuring services.
}

func (c *serviceConfiguration) ProcessMessage(ctx context.Context, m *fspb.Message) error {
	c.lock.RLock()
	target := c.services[m.Destination.ServiceName]
	c.lock.RUnlock()

	if target == nil {
		return fmt.Errorf("destination service not installed")
	}
	select {
	case target.inbox <- m:

		target.countLock.Lock()
		target.acceptCount++
		target.countLock.Unlock()

		return nil
	case <-ctx.Done():
		return ctx.Err()
	}
}

func (c *serviceConfiguration) InstallSignedService(sd *fspb.SignedClientServiceConfig) error {
	if err := c.client.config.ValidateServiceConfig(sd); err != nil {
		return fmt.Errorf("Unable to verify signature of service config %v, ignoring: %v", sd.Signature, err)
	}

	var cfg fspb.ClientServiceConfig
	if err := proto.Unmarshal(sd.ServiceConfig, &cfg); err != nil {
		return fmt.Errorf("Unable to parse service config [%v], ignoring: %v", sd.Signature, err)
	}

ll:
	for _, l := range cfg.RequiredLabels {
		if l.ServiceName == "client" {
			for _, cl := range c.client.cfg.ClientLabels {
				if cl.Label == l.Label {
					continue ll
				}
			}
			return fmt.Errorf("service config requires label %v", l)
		}
	}

	return c.InstallService(&cfg, sd.Signature)
}

func (c *serviceConfiguration) InstallService(cfg *fspb.ClientServiceConfig, sig []byte) error {

	if cfg.Name == "" || cfg.Name == "system" || cfg.Name == "client" {
		return fmt.Errorf("illegal service name [%v]", cfg.Name)
	}

	f := c.factories[cfg.Factory]
	if f == nil {
		return fmt.Errorf("factory not found [%v]", cfg.Factory)
	}
	s, err := f(cfg)
	if err != nil {
		return fmt.Errorf("unable to create service: %v", err)
	}

	d := serviceData{
		config:  c,
		name:    cfg.Name,
		service: s,
		inbox:   make(chan *fspb.Message, inboxSize),
	}
	if err := d.start(); err != nil {
		return fmt.Errorf("unable to start service: %v", err)
	}

	d.working.Add(1)
	go d.processingLoop()

	c.lock.Lock()
	old := c.services[cfg.Name]
	c.services[cfg.Name] = &d
	c.client.config.RecordRunningService(cfg.Name, sig)
	c.lock.Unlock()

	if old != nil {
		old.stop()
	}

	log.Infof("Started service %v with config:\n%v", cfg.Name, cfg)
	return nil
}

// Counts returns the number of accepted and processed messages for each
// service.
func (c *serviceConfiguration) Counts() (accepted, processed map[string]uint64) {
	am := make(map[string]uint64)
	pm := make(map[string]uint64)
	c.lock.RLock()
	defer c.lock.RUnlock()

	for _, sd := range c.services {
		sd.countLock.Lock()
		a, p := sd.acceptCount, sd.processedCount
		sd.countLock.Unlock()
		am[sd.name] = a
		pm[sd.name] = p
	}
	return am, pm
}

func (c *serviceConfiguration) Stop() {
	c.lock.Lock()
	defer c.lock.Unlock()
	for _, sd := range c.services {
		sd.stop()
	}
	c.services = make(map[string]*serviceData)
}

// A serviceData contains the data we have about a configured service, wrapping
// a Service interface and mediating communication between it and the rest of
// the Fleetspeak client.
type serviceData struct {
	config  *serviceConfiguration
	name    string
	working sync.WaitGroup
	service service.Service
	inbox   chan *fspb.Message

	countLock                   sync.Mutex // Protects acceptCount, processCount
	acceptCount, processedCount uint64
}

// Send implements service.Context.
func (d *serviceData) Send(ctx context.Context, am service.AckMessage) error {
	m := am.M
	id := d.config.client.config.ClientID().Bytes()

	m.Source = &fspb.Address{
		ClientId:    id,
		ServiceName: d.name,
	}

	if len(m.SourceMessageId) == 0 {
		b := make([]byte, 16)
		if _, err := rand.Read(b); err != nil {
			return fmt.Errorf("unable to create random source message id: %v", err)
		}
		m.SourceMessageId = b
	}

	return d.config.client.ProcessMessage(ctx, am)
}

// GetLocalInfo implements service.Context.
func (d *serviceData) GetLocalInfo() *service.LocalInfo {
	ret := &service.LocalInfo{
		ClientID: d.config.client.config.ClientID(),
		Labels:   d.config.client.config.Labels(),
	}

	d.config.lock.RLock()
	defer d.config.lock.RUnlock()
	for s := range d.config.services {
		if s != "system" {
			ret.Services = append(ret.Services, s)
		}
	}
	return ret
}

// GetFileIfModified implements service.Context.
func (d *serviceData) GetFileIfModified(ctx context.Context, name string, modSince time.Time) (io.ReadCloser, time.Time, error) {
	if d.config.client.com == nil {
		// happens during tests
		return nil, time.Time{}, errors.New("file not found")
	}
	return d.config.client.com.GetFileIfModified(ctx, d.name, name, modSince)
}

func (d *serviceData) processingLoop() {
	for {
		m, ok := <-d.inbox

		d.countLock.Lock()
		d.processedCount++
		cnt := d.processedCount
		d.countLock.Unlock()

		if cnt&0x1f == 0 {
			select {
			case d.config.client.processingBeacon <- struct{}{}:
			default:
			}
		}

		if !ok {
			d.working.Done()
			return
		}
		id, err := common.BytesToMessageID(m.MessageId)
		if err != nil {
			log.Errorf("ignoring message with bad message id: [%v]", m.MessageId)
			continue
		}
		if err := d.service.ProcessMessage(context.TODO(), m); err != nil {
			d.config.client.errs <- &fspb.MessageErrorData{
				MessageId: id.Bytes(),
				Error:     err.Error(),
			}
		} else {
			d.config.client.acks <- id
		}

	}
}

func (d *serviceData) start() error {
	return d.service.Start(d)
}

func (d *serviceData) stop() {
	close(d.inbox)
	d.working.Wait()
	d.service.Stop()
}
<|endoftext|>


<commit_before>// Copyright 2016 The Gitea 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"
	"os"
	"path/filepath"
	"testing"

	"code.gitea.io/gitea/modules/setting"

	"github.com/Unknwon/com"
	"github.com/go-xorm/core"
	"github.com/go-xorm/xorm"
	"github.com/stretchr/testify/assert"
	"gopkg.in/testfixtures.v2"
	"net/url"
)

// NonexistentID an ID that will never exist
const NonexistentID = 9223372036854775807

// giteaRoot a path to the gitea root
var giteaRoot string

// MainTest a reusable TestMain(..) function for unit tests that need to use a
// test database. Creates the test database, and sets necessary settings.
func MainTest(m *testing.M, pathToGiteaRoot string) {
	var err error
	giteaRoot = pathToGiteaRoot
	fixturesDir := filepath.Join(pathToGiteaRoot, "models", "fixtures")
	if err = createTestEngine(fixturesDir); err != nil {
		fmt.Fprintf(os.Stderr, "Error creating test engine: %v\n", err)
		os.Exit(1)
	}

	setting.AppURL = "https://try.gitea.io/"
	setting.RunUser = "runuser"
	setting.SSH.Port = 3000
	setting.SSH.Domain = "try.gitea.io"
	setting.RepoRootPath = filepath.Join(os.TempDir(), "repos")
	setting.AppDataPath = filepath.Join(os.TempDir(), "appdata")
	setting.AppWorkPath = pathToGiteaRoot
	setting.StaticRootPath = pathToGiteaRoot
	setting.GravatarSourceURL, err = url.Parse("https://secure.gravatar.com/avatar/")
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error url.Parse: %v\n", err)
		os.Exit(1)
	}

	os.Exit(m.Run())
}

func createTestEngine(fixturesDir string) error {
	var err error
	x, err = xorm.NewEngine("sqlite3", "file::memory:?cache=shared")
	if err != nil {
		return err
	}
	x.SetMapper(core.GonicMapper{})
	if err = x.StoreEngine("InnoDB").Sync2(tables...); err != nil {
		return err
	}
	switch os.Getenv("GITEA_UNIT_TESTS_VERBOSE") {
	case "true", "1":
		x.ShowSQL(true)
	}

	return InitFixtures(&testfixtures.SQLite{}, fixturesDir)
}

// PrepareTestDatabase load test fixtures into test database
func PrepareTestDatabase() error {
	return LoadFixtures()
}

// PrepareTestEnv prepares the environment for unit tests. Can only be called
// by tests that use the above MainTest(..) function.
func PrepareTestEnv(t testing.TB) {
	assert.NoError(t, PrepareTestDatabase())
	assert.NoError(t, os.RemoveAll(setting.RepoRootPath))
	metaPath := filepath.Join(giteaRoot, "integrations", "gitea-repositories-meta")
	assert.NoError(t, com.CopyDir(metaPath, setting.RepoRootPath))
}

type testCond struct {
	query interface{}
	args  []interface{}
}

// Cond create a condition with arguments for a test
func Cond(query interface{}, args ...interface{}) interface{} {
	return &testCond{query: query, args: args}
}

func whereConditions(sess *xorm.Session, conditions []interface{}) {
	for _, condition := range conditions {
		switch cond := condition.(type) {
		case *testCond:
			sess.Where(cond.query, cond.args...)
		default:
			sess.Where(cond)
		}
	}
}

func loadBeanIfExists(bean interface{}, conditions ...interface{}) (bool, error) {
	sess := x.NewSession()
	defer sess.Close()
	whereConditions(sess, conditions)
	return sess.Get(bean)
}

// BeanExists for testing, check if a bean exists
func BeanExists(t testing.TB, bean interface{}, conditions ...interface{}) bool {
	exists, err := loadBeanIfExists(bean, conditions...)
	assert.NoError(t, err)
	return exists
}

// AssertExistsAndLoadBean assert that a bean exists and load it from the test
// database
func AssertExistsAndLoadBean(t testing.TB, bean interface{}, conditions ...interface{}) interface{} {
	exists, err := loadBeanIfExists(bean, conditions...)
	assert.NoError(t, err)
	assert.True(t, exists,
		"Expected to find %+v (of type %T, with conditions %+v), but did not",
		bean, bean, conditions)
	return bean
}

// GetCount get the count of a bean
func GetCount(t testing.TB, bean interface{}, conditions ...interface{}) int {
	sess := x.NewSession()
	defer sess.Close()
	whereConditions(sess, conditions)
	count, err := sess.Count(bean)
	assert.NoError(t, err)
	return int(count)
}

// AssertNotExistsBean assert that a bean does not exist in the test database
func AssertNotExistsBean(t testing.TB, bean interface{}, conditions ...interface{}) {
	exists, err := loadBeanIfExists(bean, conditions...)
	assert.NoError(t, err)
	assert.False(t, exists)
}

// AssertExistsIf asserts that a bean exists or does not exist, depending on
// what is expected.
func AssertExistsIf(t *testing.T, expected bool, bean interface{}, conditions ...interface{}) {
	exists, err := loadBeanIfExists(bean, conditions...)
	assert.NoError(t, err)
	assert.Equal(t, expected, exists)
}

// AssertSuccessfulInsert assert that beans is successfully inserted
func AssertSuccessfulInsert(t testing.TB, beans ...interface{}) {
	_, err := x.Insert(beans...)
	assert.NoError(t, err)
}

// AssertCount assert the count of a bean
func AssertCount(t testing.TB, bean interface{}, expected interface{}) {
	assert.EqualValues(t, expected, GetCount(t, bean))
}

// AssertInt64InRange assert value is in range [low, high]
func AssertInt64InRange(t testing.TB, low, high, value int64) {
	assert.True(t, value >= low && value <= high,
		"Expected value in range [%d, %d], found %d", low, high, value)
}
<commit_msg>Force remove test repo root path in case previous test is still locking it (#3528)<commit_after>// Copyright 2016 The Gitea 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"
	"os"
	"path/filepath"
	"testing"
	"time"

	"code.gitea.io/gitea/modules/setting"

	"github.com/Unknwon/com"
	"github.com/go-xorm/core"
	"github.com/go-xorm/xorm"
	"github.com/stretchr/testify/assert"
	"gopkg.in/testfixtures.v2"
	"net/url"
)

// NonexistentID an ID that will never exist
const NonexistentID = 9223372036854775807

// giteaRoot a path to the gitea root
var giteaRoot string

// MainTest a reusable TestMain(..) function for unit tests that need to use a
// test database. Creates the test database, and sets necessary settings.
func MainTest(m *testing.M, pathToGiteaRoot string) {
	var err error
	giteaRoot = pathToGiteaRoot
	fixturesDir := filepath.Join(pathToGiteaRoot, "models", "fixtures")
	if err = createTestEngine(fixturesDir); err != nil {
		fmt.Fprintf(os.Stderr, "Error creating test engine: %v\n", err)
		os.Exit(1)
	}

	setting.AppURL = "https://try.gitea.io/"
	setting.RunUser = "runuser"
	setting.SSH.Port = 3000
	setting.SSH.Domain = "try.gitea.io"
	setting.RepoRootPath = filepath.Join(os.TempDir(), "repos")
	setting.AppDataPath = filepath.Join(os.TempDir(), "appdata")
	setting.AppWorkPath = pathToGiteaRoot
	setting.StaticRootPath = pathToGiteaRoot
	setting.GravatarSourceURL, err = url.Parse("https://secure.gravatar.com/avatar/")
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error url.Parse: %v\n", err)
		os.Exit(1)
	}

	os.Exit(m.Run())
}

func createTestEngine(fixturesDir string) error {
	var err error
	x, err = xorm.NewEngine("sqlite3", "file::memory:?cache=shared")
	if err != nil {
		return err
	}
	x.SetMapper(core.GonicMapper{})
	if err = x.StoreEngine("InnoDB").Sync2(tables...); err != nil {
		return err
	}
	switch os.Getenv("GITEA_UNIT_TESTS_VERBOSE") {
	case "true", "1":
		x.ShowSQL(true)
	}

	return InitFixtures(&testfixtures.SQLite{}, fixturesDir)
}

func removeAllWithRetry(dir string) error {
	var err error
	for i := 0; i < 20; i++ {
		err = os.RemoveAll(dir)
		if err == nil {
			break
		}
		time.Sleep(100 * time.Millisecond)
	}
	return err
}

// PrepareTestDatabase load test fixtures into test database
func PrepareTestDatabase() error {
	return LoadFixtures()
}

// PrepareTestEnv prepares the environment for unit tests. Can only be called
// by tests that use the above MainTest(..) function.
func PrepareTestEnv(t testing.TB) {
	assert.NoError(t, PrepareTestDatabase())
	assert.NoError(t, removeAllWithRetry(setting.RepoRootPath))
	metaPath := filepath.Join(giteaRoot, "integrations", "gitea-repositories-meta")
	assert.NoError(t, com.CopyDir(metaPath, setting.RepoRootPath))
}

type testCond struct {
	query interface{}
	args  []interface{}
}

// Cond create a condition with arguments for a test
func Cond(query interface{}, args ...interface{}) interface{} {
	return &testCond{query: query, args: args}
}

func whereConditions(sess *xorm.Session, conditions []interface{}) {
	for _, condition := range conditions {
		switch cond := condition.(type) {
		case *testCond:
			sess.Where(cond.query, cond.args...)
		default:
			sess.Where(cond)
		}
	}
}

func loadBeanIfExists(bean interface{}, conditions ...interface{}) (bool, error) {
	sess := x.NewSession()
	defer sess.Close()
	whereConditions(sess, conditions)
	return sess.Get(bean)
}

// BeanExists for testing, check if a bean exists
func BeanExists(t testing.TB, bean interface{}, conditions ...interface{}) bool {
	exists, err := loadBeanIfExists(bean, conditions...)
	assert.NoError(t, err)
	return exists
}

// AssertExistsAndLoadBean assert that a bean exists and load it from the test
// database
func AssertExistsAndLoadBean(t testing.TB, bean interface{}, conditions ...interface{}) interface{} {
	exists, err := loadBeanIfExists(bean, conditions...)
	assert.NoError(t, err)
	assert.True(t, exists,
		"Expected to find %+v (of type %T, with conditions %+v), but did not",
		bean, bean, conditions)
	return bean
}

// GetCount get the count of a bean
func GetCount(t testing.TB, bean interface{}, conditions ...interface{}) int {
	sess := x.NewSession()
	defer sess.Close()
	whereConditions(sess, conditions)
	count, err := sess.Count(bean)
	assert.NoError(t, err)
	return int(count)
}

// AssertNotExistsBean assert that a bean does not exist in the test database
func AssertNotExistsBean(t testing.TB, bean interface{}, conditions ...interface{}) {
	exists, err := loadBeanIfExists(bean, conditions...)
	assert.NoError(t, err)
	assert.False(t, exists)
}

// AssertExistsIf asserts that a bean exists or does not exist, depending on
// what is expected.
func AssertExistsIf(t *testing.T, expected bool, bean interface{}, conditions ...interface{}) {
	exists, err := loadBeanIfExists(bean, conditions...)
	assert.NoError(t, err)
	assert.Equal(t, expected, exists)
}

// AssertSuccessfulInsert assert that beans is successfully inserted
func AssertSuccessfulInsert(t testing.TB, beans ...interface{}) {
	_, err := x.Insert(beans...)
	assert.NoError(t, err)
}

// AssertCount assert the count of a bean
func AssertCount(t testing.TB, bean interface{}, expected interface{}) {
	assert.EqualValues(t, expected, GetCount(t, bean))
}

// AssertInt64InRange assert value is in range [low, high]
func AssertInt64InRange(t testing.TB, low, high, value int64) {
	assert.True(t, value >= low && value <= high,
		"Expected value in range [%d, %d], found %d", low, high, value)
}
<|endoftext|>


<commit_before>// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// 		https://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 lint

import (
	"fmt"
	"reflect"
	"strings"
	"testing"

	"google.golang.org/protobuf/proto"
	"google.golang.org/protobuf/types/descriptorpb"
)

func TestLinter_run(t *testing.T) {
	fileName := "protofile"
	req, _ := NewProtoRequest(
		&descriptorpb.FileDescriptorProto{
			Name: &fileName,
		}, nil)

	defaultConfigs := Configs{
		{[]string{"**"}, []string{}, map[string]RuleConfig{}},
	}

	ruleProblems := []Problem{{Message: "rule1_problem", Category: "", RuleID: "test::rule1"}}

	tests := []struct {
		desc    string
		configs Configs
		resp    Response
	}{
		{"empty config empty response", Configs{}, Response{FilePath: req.ProtoFile().Path()}},
		{
			"config with non-matching file has no effect",
			append(
				defaultConfigs,
				Config{
					IncludedPaths: []string{"nofile"},
					RuleConfigs:   map[string]RuleConfig{"": {Disabled: true}},
				},
			),
			Response{Problems: ruleProblems, FilePath: req.ProtoFile().Path()},
		},
		{
			"config with non-matching rule has no effect",
			append(
				defaultConfigs,
				Config{
					IncludedPaths: []string{"*"},
					RuleConfigs:   map[string]RuleConfig{"foo::bar": {Disabled: true}},
				},
			),
			Response{Problems: ruleProblems, FilePath: req.ProtoFile().Path()},
		},
		{
			"matching config can disable rule",
			append(
				defaultConfigs,
				Config{
					IncludedPaths: []string{"*"},
					RuleConfigs: map[string]RuleConfig{
						"test::rule1": {Disabled: true},
					},
				},
			),
			Response{FilePath: req.ProtoFile().Path()},
		},
		{
			"matching config can override Category",
			append(
				defaultConfigs,
				Config{
					IncludedPaths: []string{"*"},
					RuleConfigs: map[string]RuleConfig{
						"test::rule1": {Category: "error"},
					},
				},
			),
			Response{
				Problems: []Problem{{Message: "rule1_problem", Category: "error", RuleID: "test::rule1"}},
				FilePath: req.ProtoFile().Path(),
			},
		},
	}

	for ind, test := range tests {
		rules, err := NewRules(&mockRule{
			info:     RuleInfo{Name: "test::rule1"},
			lintResp: ruleProblems,
		})
		if err != nil {
			t.Fatal(err)
		}
		l := New(rules, test.configs)

		resp, _ := l.run(req)
		if !reflect.DeepEqual(resp, test.resp) {
			t.Errorf("Test #%d (%s): Linter.run()=%v; want %v", ind+1, test.desc, resp, test.resp)
		}
	}
}

type panickingRule struct{}

func (r *panickingRule) Info() RuleInfo                    { return RuleInfo{Name: "panic"} }
func (r *panickingRule) Lint(_ Request) ([]Problem, error) { panic("panic") }

type panickingErrorRule struct{}

func (r *panickingErrorRule) Info() RuleInfo                    { return RuleInfo{Name: "panic"} }
func (r *panickingErrorRule) Lint(_ Request) ([]Problem, error) { panic(fmt.Errorf("panic")) }

func TestLinter_LintProtos_RulePanics(t *testing.T) {
	tests := []struct {
		rule Rule
	}{{&panickingRule{}}, {&panickingErrorRule{}}}

	for _, test := range tests {
		r, err := NewRules(test.rule)
		if err != nil {
			t.Fatalf("Failed to create Rules: %q", err)
		}

		fd := new(descriptorpb.FileDescriptorProto)
		fd.SourceCodeInfo = new(descriptorpb.SourceCodeInfo)
		fd.Name = proto.String("test.proto")

		l := New(r, []Config{{
			IncludedPaths: []string{"**"},
			RuleConfigs:   map[string]RuleConfig{"": {}},
		}})

		_, err = l.LintProtos([]*descriptorpb.FileDescriptorProto{fd})
		if err == nil || !strings.Contains(err.Error(), "panic") {
			t.Fatalf("Expected error with panic, got %q", err)
		}
	}
}
<commit_msg>Removed usage of function that's not available in google3 (#112)<commit_after>// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// 		https://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 lint

import (
	"fmt"
	"reflect"
	"strings"
	"testing"

	"google.golang.org/protobuf/types/descriptorpb"
)

func TestLinter_run(t *testing.T) {
	fileName := "protofile"
	req, _ := NewProtoRequest(
		&descriptorpb.FileDescriptorProto{
			Name: &fileName,
		}, nil)

	defaultConfigs := Configs{
		{[]string{"**"}, []string{}, map[string]RuleConfig{}},
	}

	ruleProblems := []Problem{{Message: "rule1_problem", Category: "", RuleID: "test::rule1"}}

	tests := []struct {
		desc    string
		configs Configs
		resp    Response
	}{
		{"empty config empty response", Configs{}, Response{FilePath: req.ProtoFile().Path()}},
		{
			"config with non-matching file has no effect",
			append(
				defaultConfigs,
				Config{
					IncludedPaths: []string{"nofile"},
					RuleConfigs:   map[string]RuleConfig{"": {Disabled: true}},
				},
			),
			Response{Problems: ruleProblems, FilePath: req.ProtoFile().Path()},
		},
		{
			"config with non-matching rule has no effect",
			append(
				defaultConfigs,
				Config{
					IncludedPaths: []string{"*"},
					RuleConfigs:   map[string]RuleConfig{"foo::bar": {Disabled: true}},
				},
			),
			Response{Problems: ruleProblems, FilePath: req.ProtoFile().Path()},
		},
		{
			"matching config can disable rule",
			append(
				defaultConfigs,
				Config{
					IncludedPaths: []string{"*"},
					RuleConfigs: map[string]RuleConfig{
						"test::rule1": {Disabled: true},
					},
				},
			),
			Response{FilePath: req.ProtoFile().Path()},
		},
		{
			"matching config can override Category",
			append(
				defaultConfigs,
				Config{
					IncludedPaths: []string{"*"},
					RuleConfigs: map[string]RuleConfig{
						"test::rule1": {Category: "error"},
					},
				},
			),
			Response{
				Problems: []Problem{{Message: "rule1_problem", Category: "error", RuleID: "test::rule1"}},
				FilePath: req.ProtoFile().Path(),
			},
		},
	}

	for ind, test := range tests {
		rules, err := NewRules(&mockRule{
			info:     RuleInfo{Name: "test::rule1"},
			lintResp: ruleProblems,
		})
		if err != nil {
			t.Fatal(err)
		}
		l := New(rules, test.configs)

		resp, _ := l.run(req)
		if !reflect.DeepEqual(resp, test.resp) {
			t.Errorf("Test #%d (%s): Linter.run()=%v; want %v", ind+1, test.desc, resp, test.resp)
		}
	}
}

type panickingRule struct{}

func (r *panickingRule) Info() RuleInfo                    { return RuleInfo{Name: "panic"} }
func (r *panickingRule) Lint(_ Request) ([]Problem, error) { panic("panic") }

type panickingErrorRule struct{}

func (r *panickingErrorRule) Info() RuleInfo                    { return RuleInfo{Name: "panic"} }
func (r *panickingErrorRule) Lint(_ Request) ([]Problem, error) { panic(fmt.Errorf("panic")) }

func TestLinter_LintProtos_RulePanics(t *testing.T) {
	tests := []struct {
		rule Rule
	}{{&panickingRule{}}, {&panickingErrorRule{}}}

	for _, test := range tests {
		r, err := NewRules(test.rule)
		if err != nil {
			t.Fatalf("Failed to create Rules: %q", err)
		}

		fd := new(descriptorpb.FileDescriptorProto)
		fd.SourceCodeInfo = new(descriptorpb.SourceCodeInfo)
		filename := "test.proto"
		fd.Name = &filename

		l := New(r, []Config{{
			IncludedPaths: []string{"**"},
			RuleConfigs:   map[string]RuleConfig{"": {}},
		}})

		_, err = l.LintProtos([]*descriptorpb.FileDescriptorProto{fd})
		if err == nil || !strings.Contains(err.Error(), "panic") {
			t.Fatalf("Expected error with panic, got %q", err)
		}
	}
}
<|endoftext|>


<commit_before>package listener

import (
	"fmt"
	"log"
	"regexp"
	"strings"

	"github.com/bwmarrin/discordgo"
	"github.com/xackery/discordeq/discord"
	"github.com/xackery/eqemuconfig"
)

var disco *discord.Discord

func ListenToDiscord(config *eqemuconfig.Config, disc *discord.Discord) (err error) {
	var session *discordgo.Session
	disco = disc
	if session, err = disco.GetSession(); err != nil {
		log.Printf("[Discord] Failed to get instance %s: %s (Make sure bot is part of server)", config.Discord.ServerID, err.Error())
		return
	}

	session.StateEnabled = true
	session.AddHandler(onMessageEvent)
	log.Printf("[Discord] Connected\n")
	if err = session.Open(); err != nil {
		log.Printf("[Discord] Session closed: %s", err.Error())
		return
	}
	select {}
}

func onMessageEvent(s *discordgo.Session, m *discordgo.MessageCreate) {

	//Look for messages to be relayed to OOC in game.
	if m.ChannelID == config.Discord.ChannelID &&
		len(m.Message.Content) > 0 &&
		m.Message.Content[0:1] != "!" {
		messageCreate(s, m)
		return
	}

	//Look for any commands.
	if len(m.Message.Content) > 0 &&
		m.Message.Content[0:1] == "!" {
		commandParse(s, m)
	}

}

func commandParse(s *discordgo.Session, m *discordgo.MessageCreate) {
	//Verify user is allowed to send commands
	isAllowed := false
	for _, admin := range config.Discord.Admins {
		if m.Author.ID == admin.Id {
			isAllowed = true
			break
		}
	}
	if !isAllowed {
		if _, err := disco.SendMessage(m.ChannelID, fmt.Sprintf("Sorry %s, access denied.", m.Author.Username)); err != nil {
			fmt.Printf("[Discord] Failed to send discord message: %s\n", err.Error())
			return
		}
		return
	}
	//figure out command, remove the ! bang
	command := strings.ToLower(m.Message.Content[1:])

	switch command {
	case "help":
		if _, err := disco.SendMessage(m.ChannelID, fmt.Sprintf("%s: !help: Available commands:", m.Author.Username)); err != nil {
			fmt.Printf("[Discord] Failed to send discord help command: %s\n", err.Error())
			return
		}
	case "who":

	default:
		if _, err := disco.SendMessage(m.ChannelID, fmt.Sprintf("%s: Invalid command. Use !help to learn commands.", m.Author.Username)); err != nil {
			fmt.Printf("[Discord] Failed to send discord command message: %s\n", err.Error())
			return
		}
	}

}

func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {

	ign := ""
	member, err := s.GuildMember(config.Discord.ServerID, m.Author.ID)
	if err != nil {
		log.Printf("[Discord] Failed to get member: %s (Make sure you have set the bot permissions to see members)", err.Error())
		return
	}

	roles, err := s.GuildRoles(config.Discord.ServerID)
	if err != nil {
		log.Printf("[Discord] Failed to get roles: %s (Make sure you have set the bot permissions to see roles)", err.Error())
		return
	}
	for _, role := range member.Roles {
		if ign != "" {
			break
		}
		for _, gRole := range roles {
			if ign != "" {
				break
			}
			if strings.TrimSpace(gRole.ID) == strings.TrimSpace(role) {
				if strings.Contains(gRole.Name, "IGN:") {
					splitStr := strings.Split(gRole.Name, "IGN:")
					if len(splitStr) > 1 {
						ign = strings.TrimSpace(splitStr[1])
					}
				}
			}
		}
	}
	if ign == "" {
		return
	}
	msg := m.ContentWithMentionsReplaced()
	//Maximum limit of 4k
	if len(msg) > 4000 {
		msg = msg[0:4000]
	}

	if len(msg) < 1 {
		return
	}

	ign = sanitize(ign)
	msg = sanitize(msg)

	//Send message.
	if err = Sendln(fmt.Sprintf("emote world 260 %s says from discord, '%s'", ign, msg)); err != nil {
		log.Printf("[Discord] Error sending message to telnet (%s:%s): %s\n", ign, msg, err.Error())
		return
	}

	log.Printf("[Discord] %s: %s\n", ign, msg)
}

func sanitize(data string) (sData string) {
	sData = data
	sData = strings.Replace(sData, `%`, "&PCT;", -1)
	re := regexp.MustCompile("[^\x00-\x7F]+")
	sData = re.ReplaceAllString(sData, "")
	return
}

func alphanumeric(data string) (sData string) {
	sData = data
	re := regexp.MustCompile("[^a-zA-Z0-9_]+")
	sData = re.ReplaceAllString(sData, "")
	return
}
<commit_msg>removed command parsing support<commit_after>package listener

import (
	"fmt"
	"log"
	"regexp"
	"strings"

	"github.com/bwmarrin/discordgo"
	"github.com/xackery/discordeq/discord"
	"github.com/xackery/eqemuconfig"
)

var disco *discord.Discord

func ListenToDiscord(config *eqemuconfig.Config, disc *discord.Discord) (err error) {
	var session *discordgo.Session
	disco = disc
	if session, err = disco.GetSession(); err != nil {
		log.Printf("[Discord] Failed to get instance %s: %s (Make sure bot is part of server)", config.Discord.ServerID, err.Error())
		return
	}

	session.StateEnabled = true
	session.AddHandler(onMessageEvent)
	log.Printf("[Discord] Connected\n")
	if err = session.Open(); err != nil {
		log.Printf("[Discord] Session closed: %s", err.Error())
		return
	}
	select {}
}

func onMessageEvent(s *discordgo.Session, m *discordgo.MessageCreate) {

	//Look for messages to be relayed to OOC in game.
	if m.ChannelID == config.Discord.ChannelID &&
		len(m.Message.Content) > 0 &&
		m.Message.Content[0:1] != "!" {
		messageCreate(s, m)
		return
	}

	//Look for any commands.
	if len(m.Message.Content) > 0 &&
		m.Message.Content[0:1] == "!" {
		commandParse(s, m)
	}

}

func commandParse(s *discordgo.Session, m *discordgo.MessageCreate) {
	//This feature is currently not supported.
	return

	//Verify user is allowed to send commands
	isAllowed := false
	for _, admin := range config.Discord.Admins {
		if m.Author.ID == admin.Id {
			isAllowed = true
			break
		}
	}
	if !isAllowed {
		if _, err := disco.SendMessage(m.ChannelID, fmt.Sprintf("Sorry %s, access denied.", m.Author.Username)); err != nil {
			fmt.Printf("[Discord] Failed to send discord message: %s\n", err.Error())
			return
		}
		return
	}
	//figure out command, remove the ! bang
	command := strings.ToLower(m.Message.Content[1:])

	switch command {
	case "help":
		if _, err := disco.SendMessage(m.ChannelID, fmt.Sprintf("%s: !help: Available commands:", m.Author.Username)); err != nil {
			fmt.Printf("[Discord] Failed to send discord help command: %s\n", err.Error())
			return
		}
	case "who":

	default:
		if _, err := disco.SendMessage(m.ChannelID, fmt.Sprintf("%s: Invalid command. Use !help to learn commands.", m.Author.Username)); err != nil {
			fmt.Printf("[Discord] Failed to send discord command message: %s\n", err.Error())
			return
		}
	}

}

func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {

	ign := ""
	member, err := s.GuildMember(config.Discord.ServerID, m.Author.ID)
	if err != nil {
		log.Printf("[Discord] Failed to get member: %s (Make sure you have set the bot permissions to see members)", err.Error())
		return
	}

	roles, err := s.GuildRoles(config.Discord.ServerID)
	if err != nil {
		log.Printf("[Discord] Failed to get roles: %s (Make sure you have set the bot permissions to see roles)", err.Error())
		return
	}
	for _, role := range member.Roles {
		if ign != "" {
			break
		}
		for _, gRole := range roles {
			if ign != "" {
				break
			}
			if strings.TrimSpace(gRole.ID) == strings.TrimSpace(role) {
				if strings.Contains(gRole.Name, "IGN:") {
					splitStr := strings.Split(gRole.Name, "IGN:")
					if len(splitStr) > 1 {
						ign = strings.TrimSpace(splitStr[1])
					}
				}
			}
		}
	}
	if ign == "" {
		return
	}
	msg := m.ContentWithMentionsReplaced()
	//Maximum limit of 4k
	if len(msg) > 4000 {
		msg = msg[0:4000]
	}

	if len(msg) < 1 {
		return
	}

	ign = sanitize(ign)
	msg = sanitize(msg)

	//Send message.
	if err = Sendln(fmt.Sprintf("emote world 260 %s says from discord, '%s'", ign, msg)); err != nil {
		log.Printf("[Discord] Error sending message to telnet (%s:%s): %s\n", ign, msg, err.Error())
		return
	}

	log.Printf("[Discord] %s: %s\n", ign, msg)
}

func sanitize(data string) (sData string) {
	sData = data
	sData = strings.Replace(sData, `%`, "&PCT;", -1)
	re := regexp.MustCompile("[^\x00-\x7F]+")
	sData = re.ReplaceAllString(sData, "")
	return
}

func alphanumeric(data string) (sData string) {
	sData = data
	re := regexp.MustCompile("[^a-zA-Z0-9_]+")
	sData = re.ReplaceAllString(sData, "")
	return
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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 loader

import (
	"fmt"
	"github.com/golang/glog"
	"k8s.io/kops/util/pkg/vfs"
	"k8s.io/kubernetes/pkg/util/sets"
	"os"
	"path"
	"strings"
)

type TreeWalker struct {
	Contexts       map[string]Handler
	Extensions     map[string]Handler
	DefaultHandler Handler
	Tags           sets.String
}

type TreeWalkItem struct {
	Context      string
	Name         string
	Path         vfs.Path
	RelativePath string
	Meta         string
	Tags         []string
}

func (i *TreeWalkItem) ReadString() (string, error) {
	b, err := i.ReadBytes()
	if err != nil {
		return "", err
	}
	return string(b), nil
}

func (i *TreeWalkItem) ReadBytes() ([]byte, error) {
	b, err := i.Path.ReadFile()
	if err != nil {
		return nil, fmt.Errorf("error reading file %q: %v", i.Path, err)
	}
	return b, nil
}

type Handler func(item *TreeWalkItem) error

func IsTag(name string) bool {
	return len(name) != 0 && name[0] == '_'
}

func (t *TreeWalker) Walk(basedir vfs.Path) error {
	i := &TreeWalkItem{
		Context:      "",
		Path:         basedir,
		RelativePath: "",
		Tags:         nil,
	}

	return t.walkDirectory(i)
}

func (t *TreeWalker) walkDirectory(parent *TreeWalkItem) error {
	files, err := parent.Path.ReadDir()
	if err != nil {
		return fmt.Errorf("error reading directory %q: %v", parent.Path, err)
	}

	for _, f := range files {
		var err error

		fileName := f.Base()

		i := &TreeWalkItem{
			Context:      parent.Context,
			Path:         f,
			RelativePath: path.Join(parent.RelativePath, fileName),
			Name:         fileName,
			Tags:         parent.Tags,
		}

		glog.V(4).Infof("visit %q", f)

		hasMeta := false
		{
			metaPath := parent.Path.Join(fileName + ".meta")
			metaBytes, err := metaPath.ReadFile()
			if err != nil {
				if !os.IsNotExist(err) {
					return fmt.Errorf("error reading file %q: %v", metaPath, err)
				}
				metaBytes = nil
			}
			if metaBytes != nil {
				hasMeta = true
				i.Meta = string(metaBytes)
			}
		}

		if _, err := f.ReadDir(); err == nil {
			if IsTag(fileName) {
				// Only descend into the tag directory if we have the tag
				_, found := t.Tags[fileName]
				if !found {
					glog.V(2).Infof("Skipping directory as tag not present: %q", f)
					continue
				} else {
					i.Tags = append(i.Tags, fileName)
					glog.V(2).Infof("Descending into directory, as tag is present: %q", f)
					err = t.walkDirectory(i)
				}
			} else if _, found := t.Contexts[fileName]; found {
				// Entering a new context (mode of operation)
				if parent.Context != "" {
					return fmt.Errorf("found context %q inside context %q at %q", fileName, parent.Context, f)
				}
				i.Context = fileName
				i.RelativePath = ""
				err = t.walkDirectory(i)
			} else {
				// Simple directory for organization / structure
				err = t.walkDirectory(i)
			}
			if err != nil {
				return err
			}

			// So that we can manage directories, we do not ignore directories which have a .meta file
			if hasMeta {
				glog.V(4).Infof("Found .meta file for directory %q; will process", f)
			} else {
				continue
			}
		}

		if strings.HasSuffix(fileName, ".meta") {
			// We'll read it when we see the actual file
			// But check the actual file is there
			primaryPath := strings.TrimSuffix(f.Base(), ".meta")
			if _, err := parent.Path.Join(primaryPath).ReadFile(); os.IsNotExist(err) {
				return fmt.Errorf("found .meta file without corresponding file: %q", f)
			}

			continue
		}

		var handler Handler
		if i.Context != "" {
			handler = t.Contexts[i.Context]
		} else {
			// TODO: Just remove extensions.... we barely use them!
			// (or remove default handler and replace with lots of small files?)
			extension := path.Ext(fileName)
			handler = t.Extensions[extension]
			if handler == nil {
				handler = t.DefaultHandler
			}
		}

		err = handler(i)
		if err != nil {
			return fmt.Errorf("error handling file %q: %v", f, err)
		}
	}

	return nil
}
<commit_msg>Improve log message when skipping log<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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 loader

import (
	"fmt"
	"github.com/golang/glog"
	"k8s.io/kops/util/pkg/vfs"
	"k8s.io/kubernetes/pkg/util/sets"
	"os"
	"path"
	"strings"
)

type TreeWalker struct {
	Contexts       map[string]Handler
	Extensions     map[string]Handler
	DefaultHandler Handler
	Tags           sets.String
}

type TreeWalkItem struct {
	Context      string
	Name         string
	Path         vfs.Path
	RelativePath string
	Meta         string
	Tags         []string
}

func (i *TreeWalkItem) ReadString() (string, error) {
	b, err := i.ReadBytes()
	if err != nil {
		return "", err
	}
	return string(b), nil
}

func (i *TreeWalkItem) ReadBytes() ([]byte, error) {
	b, err := i.Path.ReadFile()
	if err != nil {
		return nil, fmt.Errorf("error reading file %q: %v", i.Path, err)
	}
	return b, nil
}

type Handler func(item *TreeWalkItem) error

func IsTag(name string) bool {
	return len(name) != 0 && name[0] == '_'
}

func (t *TreeWalker) Walk(basedir vfs.Path) error {
	i := &TreeWalkItem{
		Context:      "",
		Path:         basedir,
		RelativePath: "",
		Tags:         nil,
	}

	return t.walkDirectory(i)
}

func (t *TreeWalker) walkDirectory(parent *TreeWalkItem) error {
	files, err := parent.Path.ReadDir()
	if err != nil {
		return fmt.Errorf("error reading directory %q: %v", parent.Path, err)
	}

	for _, f := range files {
		var err error

		fileName := f.Base()

		i := &TreeWalkItem{
			Context:      parent.Context,
			Path:         f,
			RelativePath: path.Join(parent.RelativePath, fileName),
			Name:         fileName,
			Tags:         parent.Tags,
		}

		glog.V(4).Infof("visit %q", f)

		hasMeta := false
		{
			metaPath := parent.Path.Join(fileName + ".meta")
			metaBytes, err := metaPath.ReadFile()
			if err != nil {
				if !os.IsNotExist(err) {
					return fmt.Errorf("error reading file %q: %v", metaPath, err)
				}
				metaBytes = nil
			}
			if metaBytes != nil {
				hasMeta = true
				i.Meta = string(metaBytes)
			}
		}

		if _, err := f.ReadDir(); err == nil {
			if IsTag(fileName) {
				// Only descend into the tag directory if we have the tag
				_, found := t.Tags[fileName]
				if !found {
					glog.V(2).Infof("Skipping directory %q as tag %q not present", f, fileName)
					continue
				} else {
					i.Tags = append(i.Tags, fileName)
					glog.V(2).Infof("Descending into directory, as tag is present: %q", f)
					err = t.walkDirectory(i)
				}
			} else if _, found := t.Contexts[fileName]; found {
				// Entering a new context (mode of operation)
				if parent.Context != "" {
					return fmt.Errorf("found context %q inside context %q at %q", fileName, parent.Context, f)
				}
				i.Context = fileName
				i.RelativePath = ""
				err = t.walkDirectory(i)
			} else {
				// Simple directory for organization / structure
				err = t.walkDirectory(i)
			}
			if err != nil {
				return err
			}

			// So that we can manage directories, we do not ignore directories which have a .meta file
			if hasMeta {
				glog.V(4).Infof("Found .meta file for directory %q; will process", f)
			} else {
				continue
			}
		}

		if strings.HasSuffix(fileName, ".meta") {
			// We'll read it when we see the actual file
			// But check the actual file is there
			primaryPath := strings.TrimSuffix(f.Base(), ".meta")
			if _, err := parent.Path.Join(primaryPath).ReadFile(); os.IsNotExist(err) {
				return fmt.Errorf("found .meta file without corresponding file: %q", f)
			}

			continue
		}

		var handler Handler
		if i.Context != "" {
			handler = t.Contexts[i.Context]
		} else {
			// TODO: Just remove extensions.... we barely use them!
			// (or remove default handler and replace with lots of small files?)
			extension := path.Ext(fileName)
			handler = t.Extensions[extension]
			if handler == nil {
				handler = t.DefaultHandler
			}
		}

		err = handler(i)
		if err != nil {
			return fmt.Errorf("error handling file %q: %v", f, err)
		}
	}

	return nil
}
<|endoftext|>


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

package rocserv

import (
	"context"
	"fmt"

	"gitlab.pri.ibanyu.com/middleware/seaweed/xconfig"

	etcd "github.com/coreos/etcd/client"
	"github.com/shawnfeng/sutil/dbrouter"
)

type ServInfo struct {
	Type   string `json:"type"`
	Addr   string `json:"addr"`
	Servid int    `json:"-"`
	//Processor string    `json:"processor"`
}

func (m *ServInfo) String() string {
	return fmt.Sprintf("%s://%s", m.Type, m.Addr)
}

type RegData struct {
	Servs map[string]*ServInfo `json:"servs"`
	Lane  string               `json:"lane"`
}

type ServCtrl struct {
	Weight  int      `json:"weight"`
	Disable bool     `json:"disable"`
	Groups  []string `json:"groups"`
}

type ManualData struct {
	Ctrl *ServCtrl `json:"ctrl"`
}

func NewRegData(servs map[string]*ServInfo, lane string) *RegData {
	return &RegData{
		Servs: servs,
		Lane:  lane,
	}
}

func getValue(client etcd.KeysAPI, path string) ([]byte, error) {
	r, err := client.Get(context.Background(), path, &etcd.GetOptions{Recursive: true, Sort: false})
	if err != nil {
		return nil, err
	}

	if r.Node == nil || r.Node.Dir {
		return nil, fmt.Errorf("etcd node value err location:%s", path)
	}

	return []byte(r.Node.Value), nil
}

// ServBase Interface
type ServBase interface {
	// key is processor to ServInfo
	RegisterService(servs map[string]*ServInfo) error
	RegisterBackDoor(servs map[string]*ServInfo) error
	RegisterCrossDCService(servs map[string]*ServInfo) error

	Servname() string
	ServIp() string
	Servid() int
	// 服务副本名称, servename + servid
	Copyname() string

	// 获取服务的配置
	ServConfig(cfg interface{}) error
	// 任意路径的配置信息
	//ArbiConfig(location string) (string, error)

	// 慢id生成器，适合id产生不是非常快的场景,基于毫秒时间戳，每毫秒最多产生2个id，过快会自动阻塞，直到毫秒递增
	// id表示可以再52bit完成，用double表示不会丢失精度，javascript等弱类型语音可以直接使用
	GenSlowId(tp string) (int64, error)
	GetSlowIdStamp(sid int64) int64
	GetSlowIdWithStamp(stamp int64) int64

	// id生成逻辑
	GenSnowFlakeId() (int64, error)
	// 获取snowflakeid生成时间戳，单位ms
	GetSnowFlakeIdStamp(sid int64) int64
	// 按给定的时间点构造一个起始snowflakeid，一般用于区域判断
	GetSnowFlakeIdWithStamp(stamp int64) int64

	GenUuid() (string, error)
	GenUuidSha1() (string, error)
	GenUuidMd5() (string, error)

	// 默认的锁，局部分布式锁，各个服务之间独立不共享

	// 获取到lock立即返回，否则block直到获取到
	Lock(name string) error
	// 没有lock的情况下unlock，程序会直接panic
	Unlock(name string) error
	// 立即返回，如果获取到lock返回true，否则返回false
	Trylock(name string) (bool, error)

	// 全局分布式锁，全局只有一个，需要特殊加global说明

	LockGlobal(name string) error
	UnlockGlobal(name string) error
	TrylockGlobal(name string) (bool, error)

	// db router
	Dbrouter() *dbrouter.Router

	// conf center
	ConfigCenter() xconfig.ConfigCenter

	// reginfos
	RegInfos() map[string]string

	// stop
	Stop()

	// set app shutdown hook
	SetOnShutdown(func())
}
<commit_msg>change RegData.Lane to *string<commit_after>// Copyright 2014 The roc Author. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package rocserv

import (
	"context"
	"fmt"

	"gitlab.pri.ibanyu.com/middleware/seaweed/xconfig"

	etcd "github.com/coreos/etcd/client"
	"github.com/shawnfeng/sutil/dbrouter"
)

type ServInfo struct {
	Type   string `json:"type"`
	Addr   string `json:"addr"`
	Servid int    `json:"-"`
	//Processor string    `json:"processor"`
}

func (m *ServInfo) String() string {
	return fmt.Sprintf("%s://%s", m.Type, m.Addr)
}

type RegData struct {
	Servs map[string]*ServInfo `json:"servs"`
	Lane  *string               `json:"lane"`
}

type ServCtrl struct {
	Weight  int      `json:"weight"`
	Disable bool     `json:"disable"`
	Groups  []string `json:"groups"`
}

type ManualData struct {
	Ctrl *ServCtrl `json:"ctrl"`
}

func NewRegData(servs map[string]*ServInfo, lane string) *RegData {
	return &RegData{
		Servs: servs,
		Lane:  &lane,
	}
}

func (r *RegData) GetLane() (string, bool) {
	if r.Lane == nil {
		return "", false
	}
	return *r.Lane, true
}

func getValue(client etcd.KeysAPI, path string) ([]byte, error) {
	r, err := client.Get(context.Background(), path, &etcd.GetOptions{Recursive: true, Sort: false})
	if err != nil {
		return nil, err
	}

	if r.Node == nil || r.Node.Dir {
		return nil, fmt.Errorf("etcd node value err location:%s", path)
	}

	return []byte(r.Node.Value), nil
}

// ServBase Interface
type ServBase interface {
	// key is processor to ServInfo
	RegisterService(servs map[string]*ServInfo) error
	RegisterBackDoor(servs map[string]*ServInfo) error
	RegisterCrossDCService(servs map[string]*ServInfo) error

	Servname() string
	ServIp() string
	Servid() int
	// 服务副本名称, servename + servid
	Copyname() string

	// 获取服务的配置
	ServConfig(cfg interface{}) error
	// 任意路径的配置信息
	//ArbiConfig(location string) (string, error)

	// 慢id生成器，适合id产生不是非常快的场景,基于毫秒时间戳，每毫秒最多产生2个id，过快会自动阻塞，直到毫秒递增
	// id表示可以再52bit完成，用double表示不会丢失精度，javascript等弱类型语音可以直接使用
	GenSlowId(tp string) (int64, error)
	GetSlowIdStamp(sid int64) int64
	GetSlowIdWithStamp(stamp int64) int64

	// id生成逻辑
	GenSnowFlakeId() (int64, error)
	// 获取snowflakeid生成时间戳，单位ms
	GetSnowFlakeIdStamp(sid int64) int64
	// 按给定的时间点构造一个起始snowflakeid，一般用于区域判断
	GetSnowFlakeIdWithStamp(stamp int64) int64

	GenUuid() (string, error)
	GenUuidSha1() (string, error)
	GenUuidMd5() (string, error)

	// 默认的锁，局部分布式锁，各个服务之间独立不共享

	// 获取到lock立即返回，否则block直到获取到
	Lock(name string) error
	// 没有lock的情况下unlock，程序会直接panic
	Unlock(name string) error
	// 立即返回，如果获取到lock返回true，否则返回false
	Trylock(name string) (bool, error)

	// 全局分布式锁，全局只有一个，需要特殊加global说明

	LockGlobal(name string) error
	UnlockGlobal(name string) error
	TrylockGlobal(name string) (bool, error)

	// db router
	Dbrouter() *dbrouter.Router

	// conf center
	ConfigCenter() xconfig.ConfigCenter

	// reginfos
	RegInfos() map[string]string

	// stop
	Stop()

	// set app shutdown hook
	SetOnShutdown(func())
}
<|endoftext|>