<commit_before>// +build integration

package integration

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"

	"github.com/opentable/sous/dev_support/sous_qa_setup/desc"
	"github.com/opentable/sous/util/shelltest"
)

type templatedConfigs struct {
	desc.EnvDesc
	TestDir, Workdir, Homedir, Statedir string
	XDGConfig, SSHWrapper               string
	GitSSH, GitLocation, GitRemoteBase  string
	SSHExec                             string
	GoPath, ShellPath                   []string
}

func setupConfig(t *testing.T) templatedConfigs {
	descPath := os.Getenv("SOUS_QA_DESC")
	if descPath == "" {
		t.Fatalf("SOUS_QA_DESC is empty - you need to run sous_qa_setup and set that env var")
	}

	pwd := filepath.Dir(descPath)

	envDesc, err := desc.LoadDesc(descPath)
	if err != nil {
		t.Fatalf("Couldn't load a QA env description from SOUS_QA_DESC(%q): %s", descPath, err)
	}
	if !envDesc.Complete() {
		t.Fatal("Incomplete QA env description. Re-run sous_qa_setup?")
	}

	realworkdir, err := ioutil.TempDir("", "sous-cli-testing")
	if err != nil {
		t.Fatalf("Couldn't create temporary working directory: %s", err)
	}

	workdir := "/tmp/sous-work"

	_, err = os.Stat(workdir)
	if !os.IsExist(err) {
		os.Remove(workdir)
	}
	os.Symlink(realworkdir, workdir)

	sousExeDir := filepath.Join(workdir, "sous", "bin")
	sousExe := filepath.Join(sousExeDir, "sous")
	if out, err := exec.Command("go", "build", "-o", sousExe, "..").CombinedOutput(); err != nil {
		t.Fatal(err, string(out))
	}

	stateDir := filepath.Join(workdir, "gdm")

	exePATH, err := shelltest.BuildPath("go", "git", "ssh", "cp", "egrep", "bash")
	if err != nil {
		t.Fatal(err)
	}

	sshExecPath, err := exec.LookPath("ssh")
	if err != nil {
		t.Fatal(err)
	}

	testHome := filepath.Join(workdir, "home")

	gitLocation := fmt.Sprintf("%s/%d/repos", envDesc.Git.Host, envDesc.Git.Port)
	gitRemoteBase := fmt.Sprintf("ssh://root@%s/repos", envDesc.GitOrigin())
	gitSSH := envDesc.AgentIP.String()

	sshWrapper := filepath.Join(testHome, "bin/ssh_wrapper")
	firstGoPath := filepath.Join(testHome, "go")

	shellPath := []string{sousExeDir, "~/bin", exePATH, filepath.Join(firstGoPath, "bin")}

	goPath := []string{firstGoPath}

	if userGopath := os.Getenv("GOPATH"); userGopath != "" {
		for _, userGo := range strings.Split(userGopath, ":") {
			goPath = append(goPath, userGo)
			shellPath = append(shellPath, filepath.Join(userGo, "bin"))
		}
	}

	// Speculation: the size of this struct is a metric we should consider.
	return templatedConfigs{
		TestDir:       pwd,
		EnvDesc:       envDesc,
		Workdir:       workdir,
		Homedir:       testHome,
		Statedir:      stateDir,
		XDGConfig:     filepath.Join(testHome, "dot-config"),
		SSHWrapper:    sshWrapper,
		GoPath:        goPath,
		GitSSH:        gitSSH,
		SSHExec:       sshExecPath,
		GitLocation:   gitLocation,
		GitRemoteBase: gitRemoteBase,
		ShellPath:     shellPath,
	}
}

func buildShell(name string, t *testing.T) *shelltest.ShellTest {
	cfg := setupConfig(t)

	os.MkdirAll(cfg.Homedir, os.ModePerm)
	err := shelltest.TemplateConfigs(filepath.Join(cfg.TestDir, "integration/test-homedir"), cfg.Homedir, cfg)
	if err != nil {
		t.Fatalf("Templating configuration files: %+v", err)
	}

	shell := shelltest.New(t, name, cfg,
		shelltest.WithHostEnv([]string{"DOCKER_HOST", "DOCKER_TLS_VERIFY", "DOCKER_CERT_PATH", "GOROOT"},
			map[string]string{
				"HOME":       cfg.Homedir,
				"XDG_CONFIG": cfg.XDGConfig,
				"GIT_SSH":    cfg.SSHWrapper,
				"GOPATH":     strings.Join(cfg.GoPath, ":"),
				"PATH":       strings.Join(cfg.ShellPath, ":"),
			}))

	shell.WriteTo("../doc/shellexamples")
	//shell.DebugPrefix("shell") //useful especially if test timeout interrupts

	return shell
}

func TestShellLevelIntegration(t *testing.T) {
	shell := buildShell("happypath", t)

	defaultCheck := func(name string, res shelltest.Result, t *testing.T) {
		if len(res.Errs) > 0 {
			t.Errorf("Error in %s: \n\t%s", name, res.Errs)
		}
	}

	preconditions := shell.Block("Preconditions for CLI integration tests", `
	if [ -n "$GOROOT" ]; then
		mkdir -p $GOROOT
	fi
	go get github.com/nyarly/cygnus # cygnus lets us inspect Singularity for ports
	if echo "{{.EnvDesc.SingularityURL}}" | egrep -q '192.168|127.0.0'; then
		{{.TestDir}}/integration/test-registry/clean-singularity.sh {{.EnvDesc.SingularityURL}}
	fi
	cygnus -H {{.EnvDesc.SingularityURL}}
	ls {{.TestDir}}/dev_support
	`, func(name string, res shelltest.Result, t *testing.T) {
		if len(res.Errs) > 0 {
			t.Errorf("Error in %s: \n\t%s", name, res.Errs)
		}
		if res.Matches("repos>sous-server.*0_0_2") {
			msg, err := shell.Template("clean-sing", "Running sous-server already - try `./integration/test-registry/clean-singularity.sh {{.EnvDesc.SingularityURL}}`")
			if err != nil {
				t.Error("Running sous-server already - try `./integration/test-registry/clean-singularity.sh <singularity-url>`")
			}
			t.Error(msg)
		}
		if !res.Matches("sous_linux") {
			t.Error("No sous_linux available - run `make linux_build`")
		}
	})

	prologue := preconditions.Block("Test environment setup", `
	# These steps are required by the Sous integration tests
	# They're analogous to run-of-the-mill workstation maintenance.

	cd {{.TestDir}}
	env
	export SOUS_EXTRA_DOCKER_CA={{.TestDir}}/integration/test-registry/docker-registry/testing.crt
	mkdir -p {{index .GoPath 0}}/{src,bin}

	### This build gives me trouble in tests...
	### xgo does something weird and different with it's dep-cache dir
	# GOPATH={{index .GoPath 0}} make linux_build # we need Sous built for linux for the server
	go install . #install the current sous project
	cp integration/test-registry/git-server/git_pubkey_rsa* ~/dot-ssh/

	cd {{.Workdir}}
	chmod go-rwx -R ~/dot-ssh
	chmod +x -R ~/bin/*
	ssh -o ConnectTimeout=1 -o PasswordAuthentication=no -F "${HOME}/dot-ssh/config" root@{{.GitSSH}} -p 2222 /reset-repos < /dev/null
	`,
		defaultCheck)

	createGDM := prologue.Block("create the GDM", `
	git clone {{.GitRemoteBase}}/gdm
	cp ~/templated-configs/defs.yaml gdm/defs.yaml
	cat gdm/defs.yaml
	pushd gdm
	cat ~/.config/git/config >> .git/config # Eh?
	git add defs.yaml
	git commit -am "Adding defs.yaml"
	git push
	popd
	`, defaultCheck)

	// XXX There should be a `-cluster left,right` syntax, instead of two deploy commands
	setup := createGDM.Block("deploy sous server", `
	sous config
	cat ~/.config/sous/config.yaml
	git clone {{.GitRemoteBase}}/sous-server
	pushd sous-server
	export SOUS_USER_NAME=test SOUS_USER_EMAIL=test@test.com
	export SOUS_SERVER= SOUS_STATE_LOCATION={{.Statedir}}

	sous init
	sous manifest get
	sous manifest set < ~/templated-configs/sous-server.yaml
	sous manifest get # demonstrating this got to GDM

	# Last minute config
	cat Dockerfile
	cp ~/dot-ssh/git_pubkey_rsa key_sous@example.com
	cp {{.TestDir}}/dev_support/$(readlink {{.TestDir}}/dev_support/sous_linux) .
	cp {{.TestDir}}/integration/test-registry/docker-registry/testing.crt docker.crt

	ls -a
	ssh-keyscan -p 2222 {{.GitSSH}} > known_hosts

	git add key_sous@example.com known_hosts sous
	git commit -am "Adding ephemeral files"
	git tag -am "0.0.2" 0.0.2
	git push
	git push --tags

	sous build
	sous deploy -cluster left # We expect to see 'Sous is running ... in workstation mode' here:
	sous deploy -cluster right
	unset SOUS_SERVER
	unset SOUS_STATE_LOCATION
	popd
	`,
		func(name string, res shelltest.Result, t *testing.T) {
			if len(res.Errs) > 0 {
				t.Errorf("Trouble building GDM: \n\t%s", res.Errs)
			}
		})

	// This is where regular use starts
	config := setup.Block("configuration", `
	# This is kind of a hack - in normal operation, Sous would block until its
	# services had been accepted, but when bootstrapping, we need to wait for them
	# to come up.
	while [ $(cygnus -H {{.EnvDesc.SingularityURL}} | grep sous-server | wc -l) -lt 2 ]; do
	  sleep 0.1
	done

	cygnus --env TASK_HOST --env PORT0 {{.EnvDesc.SingularityURL}}
	leftport=$(cygnus --env PORT0 {{.EnvDesc.SingularityURL}} | grep 'sous-server.*left' | awk '{ print $3 }')
	rightport=$(cygnus --env PORT0 {{.EnvDesc.SingularityURL}} | grep 'sous-server.*right' | awk '{ print $3 }')
	cygnus --env TASK_HOST --env PORT0 {{.EnvDesc.SingularityURL}}

	serverURL=http://{{.EnvDesc.AgentIP}}:$leftport
	echo "Determined server url as $serverURL"

	until curl -s -I $serverURL; do
	  sleep 0.1
	done
	sous config Server "$serverURL"
	echo "Set server URL to: $(sous config Server)"

	ETAG=$(curl -s -v http://192.168.99.100:$leftport/servers 2>&1 | sed -n '/Etag:/{s/.*: //; P; }')
	echo $ETAG
	sed "s/LEFTPORT/$leftport/; s/RIGHTPORT/$rightport/" < ~/templated-configs/servers.json > ~/servers.json
	cat ~/servers.json
	curl -v -X PUT -H "If-Match: ${ETAG//[$'\t\r\n ']}" -H "Content-Type: application/json" "${serverURL}/servers" --data "$(< ~/servers.json)"
	curl -s "${serverURL}/servers"
	`,
		func(name string, res shelltest.Result, t *testing.T) {
			if len(res.Errs) > 0 {
				t.Errorf("Trouble building GDM: \n\t%s", res.Errs)
			}

			if !res.Matches(`URL is: http`) {
				t.Fatalf("Sous server not running!")
			}
		})

	deploy := config.Block("deploy project", `
	cat $XDG_CONFIG/sous/config.yaml
	sous config
	git clone {{.GitRemoteBase}}/sous-demo
	cd sous-demo
	git tag -am 'Release!' 0.0.23
	git push --tags
	sous init
	sous build
	sous deploy -cluster left
	`, defaultCheck)

	//check :=
	deploy.Block("confirm deployment", `
	cygnus -x 1 {{.EnvDesc.SingularityURL}}
	`, func(name string, res shelltest.Result, t *testing.T) {
		defaultCheck(name, res, t)
		if !res.Matches("sous-demo") {
			t.Error("No sous-demo request running!")
		}
	})
}
<commit_msg>Travis fixes<commit_after>// +build integration

package integration

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"

	"github.com/opentable/sous/dev_support/sous_qa_setup/desc"
	"github.com/opentable/sous/util/shelltest"
)

type templatedConfigs struct {
	desc.EnvDesc
	TestDir, Workdir, Homedir, Statedir string
	XDGConfig, SSHWrapper               string
	GitSSH, GitLocation, GitRemoteBase  string
	SSHExec                             string
	GoPath, ShellPath                   []string
}

func setupConfig(t *testing.T) templatedConfigs {
	descPath := os.Getenv("SOUS_QA_DESC")
	if descPath == "" {
		t.Fatalf("SOUS_QA_DESC is empty - you need to run sous_qa_setup and set that env var")
	}

	pwd := filepath.Dir(descPath)

	envDesc, err := desc.LoadDesc(descPath)
	if err != nil {
		t.Fatalf("Couldn't load a QA env description from SOUS_QA_DESC(%q): %s", descPath, err)
	}
	if !envDesc.Complete() {
		t.Fatal("Incomplete QA env description. Re-run sous_qa_setup?")
	}

	realworkdir, err := ioutil.TempDir("", "sous-cli-testing")
	if err != nil {
		t.Fatalf("Couldn't create temporary working directory: %s", err)
	}

	workdir := "/tmp/sous-work"

	_, err = os.Stat(workdir)
	if !os.IsExist(err) {
		os.Remove(workdir)
	}
	os.Symlink(realworkdir, workdir)

	sousExeDir := filepath.Join(workdir, "sous", "bin")
	sousExe := filepath.Join(sousExeDir, "sous")
	if out, err := exec.Command("go", "build", "-o", sousExe, "..").CombinedOutput(); err != nil {
		t.Fatal(err, string(out))
	}

	stateDir := filepath.Join(workdir, "gdm")

	exePATH, err := shelltest.BuildPath("go", "git", "ssh", "cp", "egrep", "bash")
	if err != nil {
		t.Fatal(err)
	}

	sshExecPath, err := exec.LookPath("ssh")
	if err != nil {
		t.Fatal(err)
	}

	testHome := filepath.Join(workdir, "home")

	gitLocation := fmt.Sprintf("%s/%d/repos", envDesc.Git.Host, envDesc.Git.Port)
	gitRemoteBase := fmt.Sprintf("ssh://root@%s/repos", envDesc.GitOrigin())
	gitSSH := envDesc.AgentIP.String()

	sshWrapper := filepath.Join(testHome, "bin/ssh_wrapper")
	firstGoPath := filepath.Join(testHome, "go")

	shellPath := []string{sousExeDir, "~/bin", exePATH, filepath.Join(firstGoPath, "bin")}

	goPath := []string{firstGoPath}

	if userGopath := os.Getenv("GOPATH"); userGopath != "" {
		for _, userGo := range strings.Split(userGopath, ":") {
			goPath = append(goPath, userGo)
			shellPath = append(shellPath, filepath.Join(userGo, "bin"))
		}
	}

	// Speculation: the size of this struct is a metric we should consider.
	return templatedConfigs{
		TestDir:       pwd,
		EnvDesc:       envDesc,
		Workdir:       workdir,
		Homedir:       testHome,
		Statedir:      stateDir,
		XDGConfig:     filepath.Join(testHome, "dot-config"),
		SSHWrapper:    sshWrapper,
		GoPath:        goPath,
		GitSSH:        gitSSH,
		SSHExec:       sshExecPath,
		GitLocation:   gitLocation,
		GitRemoteBase: gitRemoteBase,
		ShellPath:     shellPath,
	}
}

func buildShell(name string, t *testing.T) *shelltest.ShellTest {
	cfg := setupConfig(t)

	os.MkdirAll(cfg.Homedir, os.ModePerm)
	err := shelltest.TemplateConfigs(filepath.Join(cfg.TestDir, "integration/test-homedir"), cfg.Homedir, cfg)
	if err != nil {
		t.Fatalf("Templating configuration files: %+v", err)
	}

	shell := shelltest.New(t, name, cfg,
		shelltest.WithHostEnv([]string{"DOCKER_HOST", "DOCKER_TLS_VERIFY", "DOCKER_CERT_PATH", "GOROOT"},
			map[string]string{
				"HOME":       cfg.Homedir,
				"XDG_CONFIG": cfg.XDGConfig,
				"GIT_SSH":    cfg.SSHWrapper,
				"GOPATH":     strings.Join(cfg.GoPath, ":"),
				"PATH":       strings.Join(cfg.ShellPath, ":"),
			}))

	shell.WriteTo("../doc/shellexamples")
	//shell.DebugPrefix("shell") //useful especially if test timeout interrupts

	return shell
}

func TestShellLevelIntegration(t *testing.T) {
	shell := buildShell("happypath", t)

	defaultCheck := func(name string, res shelltest.Result, t *testing.T) {
		if len(res.Errs) > 0 {
			t.Errorf("Error in %s: \n\t%s", name, res.Errs)
		}
	}

	preconditions := shell.Block("Preconditions for CLI integration tests", `
	if [ -n "$GOROOT" ]; then
		mkdir -p $GOROOT
	fi
	go get github.com/nyarly/cygnus # cygnus lets us inspect Singularity for ports
	if echo "{{.EnvDesc.SingularityURL}}" | egrep -q '192.168|127.0.0'; then
		{{.TestDir}}/integration/test-registry/clean-singularity.sh {{.EnvDesc.SingularityURL}}
	fi
	cygnus -H {{.EnvDesc.SingularityURL}}
	ls {{.TestDir}}/dev_support
	`, func(name string, res shelltest.Result, t *testing.T) {
		if len(res.Errs) > 0 {
			t.Errorf("Error in %s: \n\t%s", name, res.Errs)
		}
		if res.Matches("repos>sous-server.*0_0_2") {
			msg, err := shell.Template("clean-sing", "Running sous-server already - try `./integration/test-registry/clean-singularity.sh {{.EnvDesc.SingularityURL}}`")
			if err != nil {
				t.Error("Running sous-server already - try `./integration/test-registry/clean-singularity.sh <singularity-url>`")
			}
			t.Error(msg)
		}
		if !res.Matches("sous_linux") {
			t.Error("No sous_linux available - run `make linux_build`")
		}
	})

	prologue := preconditions.Block("Test environment setup", `
	# These steps are required by the Sous integration tests
	# They're analogous to run-of-the-mill workstation maintenance.

	cd {{.TestDir}}
	env
	export SOUS_EXTRA_DOCKER_CA={{.TestDir}}/integration/test-registry/docker-registry/testing.crt
	mkdir -p {{index .GoPath 0}}/{src,bin}

	### This build gives me trouble in tests...
	### xgo does something weird and different with it's dep-cache dir
	# GOPATH={{index .GoPath 0}} make linux_build # we need Sous built for linux for the server
	go install . #install the current sous project
	cp integration/test-registry/git-server/git_pubkey_rsa* ~/dot-ssh/

	cd {{.Workdir}}
	chmod go-rwx -R ~/dot-ssh
	chmod +x -R ~/bin/*
	ssh -o ConnectTimeout=1 -o PasswordAuthentication=no -F "${HOME}/dot-ssh/config" root@{{.GitSSH}} -p 2222 /reset-repos < /dev/null
	`,
		defaultCheck)

	createGDM := prologue.Block("create the GDM", `
	git clone {{.GitRemoteBase}}/gdm
	cp ~/templated-configs/defs.yaml gdm/defs.yaml
	cat gdm/defs.yaml
	pushd gdm
	cat ~/.config/git/config >> .git/config # Eh?
	git add defs.yaml
	git commit -am "Adding defs.yaml"
	git push
	popd
	`, defaultCheck)

	// XXX There should be a `-cluster left,right` syntax, instead of two deploy commands
	setup := createGDM.Block("deploy sous server", `
	sous config
	cat ~/.config/sous/config.yaml
	git clone {{.GitRemoteBase}}/sous-server
	pushd sous-server
	export SOUS_USER_NAME=test SOUS_USER_EMAIL=test@test.com
	export SOUS_SERVER= SOUS_STATE_LOCATION={{.Statedir}}

	sous init
	sous manifest get
	sous manifest set < ~/templated-configs/sous-server.yaml
	sous manifest get # demonstrating this got to GDM

	# Last minute config
	cat Dockerfile
	cp ~/dot-ssh/git_pubkey_rsa key_sous@example.com
	cp {{.TestDir}}/dev_support/$(readlink {{.TestDir}}/dev_support/sous_linux) .
	cp {{.TestDir}}/integration/test-registry/docker-registry/testing.crt docker.crt

	ls -a
	ssh-keyscan -p 2222 {{.GitSSH}} > known_hosts

	git add key_sous@example.com known_hosts sous
	git commit -am "Adding ephemeral files"
	git tag -am "0.0.2" 0.0.2
	git push
	git push --tags

	sous build
	sous deploy -cluster left # We expect to see 'Sous is running ... in workstation mode' here:
	sous deploy -cluster right
	unset SOUS_SERVER
	unset SOUS_STATE_LOCATION
	popd
	`,
		func(name string, res shelltest.Result, t *testing.T) {
			if len(res.Errs) > 0 {
				t.Errorf("Trouble building GDM: \n\t%s", res.Errs)
			}
		})

	// This is where regular use starts
	config := setup.Block("configuration", `
	# This is kind of a hack - in normal operation, Sous would block until its
	# services had been accepted, but when bootstrapping, we need to wait for them
	# to come up.
	while [ $(cygnus -H {{.EnvDesc.SingularityURL}} | grep sous-server | wc -l) -lt 2 ]; do
	  sleep 0.1
	done

	cygnus --env TASK_HOST --env PORT0 {{.EnvDesc.SingularityURL}}
	leftport=$(cygnus --env PORT0 {{.EnvDesc.SingularityURL}} | grep 'sous-server.*left' | awk '{ print $3 }')
	rightport=$(cygnus --env PORT0 {{.EnvDesc.SingularityURL}} | grep 'sous-server.*right' | awk '{ print $3 }')
	cygnus --env TASK_HOST --env PORT0 -K -s {{.EnvDesc.SingularityURL}}

	serverURL=http://{{.EnvDesc.AgentIP}}:$leftport
	echo "Determined server url as $serverURL"

	until curl -s -I $serverURL; do
	  sleep 0.1
	done
	sous config Server "$serverURL"
	echo "Set server URL to: $(sous config Server)"

	ETAG=$(curl -s -v http://192.168.99.100:$leftport/servers 2>&1 | sed -n '/Etag:/{s/.*: //; P; }')
	echo $ETAG
	sed "s/LEFTPORT/$leftport/; s/RIGHTPORT/$rightport/" < ~/templated-configs/servers.json > ~/servers.json
	cat ~/servers.json
	curl -v -X PUT -H "If-Match: ${ETAG//[$'\t\r\n ']}" -H "Content-Type: application/json" "${serverURL}/servers" --data "$(< ~/servers.json)"
	curl -s "${serverURL}/servers"
	`,
		func(name string, res shelltest.Result, t *testing.T) {
			if len(res.Errs) > 0 {
				t.Errorf("Trouble building GDM: \n\t%s", res.Errs)
			}

			if !res.Matches(`URL is: http`) {
				t.Fatalf("Sous server not running!")
			}
		})

	deploy := config.Block("deploy project", `
	cat $XDG_CONFIG/sous/config.yaml
	sous config
	git clone {{.GitRemoteBase}}/sous-demo
	cd sous-demo
	git tag -am 'Release!' 0.0.23
	git push --tags
	sous init
	sous build
	sous deploy -cluster left
	`, defaultCheck)

	//check :=
	deploy.Block("confirm deployment", `
	cygnus -x 1 {{.EnvDesc.SingularityURL}}
	`, func(name string, res shelltest.Result, t *testing.T) {
		defaultCheck(name, res, t)
		if !res.Matches("sous-demo") {
			t.Error("No sous-demo request running!")
		}
	})
}
<|endoftext|>


<commit_before>package validator

import (
	"encoding/json"
	"errors"
	"fmt"

	mona "github.com/appscode/kube-mon/api"
	api "github.com/kubedb/apimachinery/apis/kubedb/v1alpha1"
	"github.com/kubedb/apimachinery/pkg/storage"
	core "k8s.io/api/core/v1"
	kerr "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
)

func ValidateStorage(client kubernetes.Interface, spec *core.PersistentVolumeClaimSpec) error {
	if spec == nil {
		return nil
	}

	if spec.StorageClassName != nil {
		if _, err := client.StorageV1beta1().StorageClasses().Get(*spec.StorageClassName, metav1.GetOptions{}); err != nil {
			if kerr.IsNotFound(err) {
				return fmt.Errorf(`spec.storage.storageClassName "%v" not found`, *spec.StorageClassName)
			}
			return err
		}
	}

	if val, found := spec.Resources.Requests[core.ResourceStorage]; found {
		if val.Value() <= 0 {
			return errors.New("invalid ResourceStorage request")
		}
	} else {
		return errors.New("missing ResourceStorage request")
	}

	return nil
}

func ValidateBackupSchedule(client kubernetes.Interface, spec *api.BackupScheduleSpec, namespace string) error {
	if spec == nil {
		return nil
	}
	// CronExpression can't be empty
	if spec.CronExpression == "" {
		return errors.New("invalid cron expression")
	}

	return ValidateSnapshotSpec(client, spec.SnapshotStorageSpec, namespace)
}

func ValidateSnapshotSpec(client kubernetes.Interface, spec api.SnapshotStorageSpec, namespace string) error {
	// BucketName can't be empty
	if spec.S3 == nil && spec.GCS == nil && spec.Azure == nil && spec.Swift == nil && spec.Local == nil {
		return errors.New("no storage provider is configured")
	}

	if spec.Local != nil {
		return nil
	}

	// Need to provide Storage credential secret
	if spec.StorageSecretName == "" {
		return fmt.Errorf(`object 'SecretName' is missing in '%v'`, spec)
	}

	if err := storage.CheckBucketAccess(client, spec, namespace); err != nil {
		return err
	}

	return nil
}

func ValidateMonitorSpec(monitorSpec *mona.AgentSpec) error {
	specData, err := json.Marshal(monitorSpec)
	if err != nil {
		return err
	}

	if monitorSpec.Agent == "" {
		return fmt.Errorf(`object 'Agent' is missing in '%v'`, string(specData))
	}

	if monitorSpec.Agent.Vendor() == mona.VendorPrometheus {
		if monitorSpec.Agent == mona.AgentCoreOSPrometheus {
			if monitorSpec.Prometheus == nil {
				return fmt.Errorf(`invalid 'Agent' in '%v'`, string(specData))
			}
		} else if monitorSpec.Agent != mona.AgentPrometheusBuiltin {
			return fmt.Errorf(`invalid 'Agent' in '%v'`, string(specData))
		}
	}

	return nil
}
<commit_msg> Fixed Validation of Monitoring Spec (#180)<commit_after>package validator

import (
	"encoding/json"
	"errors"
	"fmt"

	mona "github.com/appscode/kube-mon/api"
	api "github.com/kubedb/apimachinery/apis/kubedb/v1alpha1"
	"github.com/kubedb/apimachinery/pkg/storage"
	core "k8s.io/api/core/v1"
	kerr "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
)

func ValidateStorage(client kubernetes.Interface, spec *core.PersistentVolumeClaimSpec) error {
	if spec == nil {
		return nil
	}

	if spec.StorageClassName != nil {
		if _, err := client.StorageV1beta1().StorageClasses().Get(*spec.StorageClassName, metav1.GetOptions{}); err != nil {
			if kerr.IsNotFound(err) {
				return fmt.Errorf(`spec.storage.storageClassName "%v" not found`, *spec.StorageClassName)
			}
			return err
		}
	}

	if val, found := spec.Resources.Requests[core.ResourceStorage]; found {
		if val.Value() <= 0 {
			return errors.New("invalid ResourceStorage request")
		}
	} else {
		return errors.New("missing ResourceStorage request")
	}

	return nil
}

func ValidateBackupSchedule(client kubernetes.Interface, spec *api.BackupScheduleSpec, namespace string) error {
	if spec == nil {
		return nil
	}
	// CronExpression can't be empty
	if spec.CronExpression == "" {
		return errors.New("invalid cron expression")
	}

	return ValidateSnapshotSpec(client, spec.SnapshotStorageSpec, namespace)
}

func ValidateSnapshotSpec(client kubernetes.Interface, spec api.SnapshotStorageSpec, namespace string) error {
	// BucketName can't be empty
	if spec.S3 == nil && spec.GCS == nil && spec.Azure == nil && spec.Swift == nil && spec.Local == nil {
		return errors.New("no storage provider is configured")
	}

	if spec.Local != nil {
		return nil
	}

	// Need to provide Storage credential secret
	if spec.StorageSecretName == "" {
		return fmt.Errorf(`object 'SecretName' is missing in '%v'`, spec)
	}

	if err := storage.CheckBucketAccess(client, spec, namespace); err != nil {
		return err
	}

	return nil
}

func ValidateMonitorSpec(monitorSpec *mona.AgentSpec) error {
	specData, err := json.Marshal(monitorSpec)
	if err != nil {
		return err
	}

	if monitorSpec.Agent == "" {
		return fmt.Errorf(`object 'Agent' is missing in '%v'`, string(specData))
	}

	if monitorSpec.Agent.Vendor() == mona.VendorPrometheus {
		if monitorSpec.Agent == mona.AgentPrometheusBuiltin ||
			(monitorSpec.Agent == mona.AgentCoreOSPrometheus && monitorSpec.Prometheus != nil) {
			return nil
		}
	}

	return fmt.Errorf(`invalid 'Agent' in '%v'`, string(specData))
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor: Aaron Meihm ameihm@mozilla.com [:alm]

// Package dispatch implements alert dispatching for the agent as a module.
// Persistent modules which generate alerts will have these alerts forwarded
// to this module if the dispatch module is active. The dispatch module can then
// forward the alerts on based on it's configuration.
package dispatch /* import "mig.ninja/mig/modules/dispatch" */

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/jvehent/gozdef"
	"net/http"
	"runtime"
	"time"

	"mig.ninja/mig"
	"mig.ninja/mig/modules"
)

type module struct {
}

// NewRun returns a new instance of a modules.Runner for this module.
func (m *module) NewRun() modules.Runner {
	return new(run)
}

func init() {
	modules.Register("dispatch", new(module))
}

type run struct {
	Parameters Parameters
	Results    modules.Result
}

func buildResults(e elements, r *modules.Result) (buf []byte, err error) {
	r.Success = true
	r.Elements = e
	r.FoundAnything = true
	buf, err = json.Marshal(r)
	return
}

var logChan chan string
var alertChan chan string
var handlerErrChan chan error
var configChan chan modules.ConfigParams

// We keep a copy of the agent environment, hostname, and tags here so we can
// use them in the dispatched output
var env mig.AgentEnv
var tags map[string]string
var agtHostname string

// messageBuf is a queue used to store incoming messages, and is drained by
// runDispatch
var messageBuf chan string

// lastDrop stores the last time a message was dropped by the dispatch module
var lastDrop time.Time
var dropCounter int

// Dispatch record describes the formatting of JSON data submitted from the dispatch
// module.
type DispatchRecord struct {
	Hostname string            `json:"hostname"`
	Env      mig.AgentEnv      `json:"environment"`
	Tags     map[string]string `json:"tags"`
	Details  interface{}       `json:"details"`
}

func (d *DispatchRecord) fromString(msg string) error {
	d.Hostname = agtHostname
	d.Env = env
	d.Tags = tags
	err := json.Unmarshal([]byte(msg), &d.Details)
	return err
}

func moduleMain() {
	var cfg config

	incfg := <-configChan
	agtHostname = incfg.Hostname
	tags = incfg.Tags
	buf, err := json.Marshal(incfg.Env)
	if err != nil {
		handlerErrChan <- err
		return
	}
	err = json.Unmarshal(buf, &env)
	if err != nil {
		handlerErrChan <- err
		return
	}
	buf, err = json.Marshal(incfg.Config)
	if err != nil {
		handlerErrChan <- err
		return
	}
	err = json.Unmarshal(buf, &cfg)
	if err != nil {
		handlerErrChan <- err
		return
	}
	logChan <- "module received configuration"
	if cfg.Dispatch.ChannelSize == 0 {
		cfg.Dispatch.ChannelSize = 1024
		logChan <- "channel size not specified, defaulting to 1024"
	}

	messageBuf = make(chan string, cfg.Dispatch.ChannelSize)

	// Register the dispatch function, which will be called when the module
	// recieves an alert message from the agent.
	modules.RegisterDispatchFunction(dispatchIn)

	// Start the dispatch dequeue function.
	err = runDispatch(cfg)
	if err != nil {
		handlerErrChan <- err
		return
	}
}

func dispatchIn(msg string) {
	select {
	case messageBuf <- msg:
	default:
		dropCounter++
		// If we can't queue the message it is just dropped
		now := time.Now()
		if now.After(lastDrop.Add(time.Duration(time.Minute * 5))) {
			logChan <- fmt.Sprintf("warning, dispatch module dropping messages "+
				"(buffer full, %v dropped since last warning)", dropCounter)
			lastDrop = now
			dropCounter = 0
		}
	}
}

func runDispatch(cfg config) error {
	var (
		httpClient http.Client
		dr         DispatchRecord
	)

	if cfg.Dispatch.SNSTopic != "" {
		err := initSNS(cfg)
		if err != nil {
			return err
		}
	}

	for {
		msg := <-messageBuf
		dr.fromString(msg)
		var buf []byte
		var err error
		if cfg.Dispatch.OutputMozdef {
			// If we are set to output records to MozDef, convert the
			// dispatch record into a gozdef event
			ge, err := gozdef.NewEvent()
			if err != nil {
				logChan <- fmt.Sprintf("create gozdef event: %v", err)
				continue
			}
			ge.Info()
			ge.Summary = "mig dispatch event"
			ge.Category = "mig"
			ge.Details = dr
			ge.Tags = append(ge.Tags, "mig-dispatch")
			buf, err = json.Marshal(ge)
			if err != nil {
				logChan <- fmt.Sprintf("populate gozdef event: %v", err)
				continue
			}
		} else {
			buf, err = json.Marshal(dr)
			if err != nil {
				logChan <- fmt.Sprintf("create dispatch record: %v", err)
				continue
			}
		}
		if cfg.Dispatch.SNSTopic != "" {
			err := dispatchSNS(buf)
			if err != nil {
				logChan <- fmt.Sprintf("sns dispatch: %v", err)
				continue
			}
		} else {
			// Default to HTTP POST
			b := bytes.NewBuffer(buf)
			resp, err := httpClient.Post(cfg.Dispatch.HTTPURL, "application/json", b)
			if err != nil {
				logChan <- fmt.Sprintf("http post: %v", err)
				continue
			}
			resp.Body.Close()
		}
	}
	return nil
}

func requestHandler(p interface{}) (ret string) {
	var results modules.Result
	defer func() {
		if e := recover(); e != nil {
			results.Errors = append(results.Errors, fmt.Sprintf("%v", e))
			results.Success = false
			err, _ := json.Marshal(results)
			ret = string(err)
			return
		}
	}()
	e := elements{Ok: true}
	resp, err := buildResults(e, &results)
	if err != nil {
		panic(err)
	}
	return string(resp)
}

type config struct {
	Dispatch struct {
		OutputMozdef bool   `json:"outputmozdef"`
		HTTPURL      string `json:"httpurl"`
		SNSTopic     string `json:"snstopic"`
		Region       string `json:"region"`
		ChannelSize  int    `json:"channelsize"`
	} `json:"dispatch"`
}

// PersistModConfig returns a new configuration structure for this module.
func (r *run) PersistModConfig() interface{} {
	return &config{}
}

// RunPersist is the entry point for persistent execution of the module.
func (r *run) RunPersist(in modules.ModuleReader, out modules.ModuleWriter) {
	alertChan = make(chan string, 64)
	logChan = make(chan string, 64)
	regChan := make(chan string, 64)
	handlerErrChan = make(chan error, 64)
	configChan = make(chan modules.ConfigParams, 1)

	go moduleMain()
	l, spec, err := modules.GetPersistListener("dispatch")
	if err != nil {
		handlerErrChan <- err
	} else {
		regChan <- spec
	}
	go modules.HandlePersistRequest(l, requestHandler, handlerErrChan)
	modules.DefaultPersistHandlers(in, out, logChan, handlerErrChan, regChan,
		alertChan, configChan)
}

// Run is the entry point for a standard (e.g., query) based invocation of the module.
func (r *run) Run(in modules.ModuleReader) (resStr string) {
	defer func() {
		if e := recover(); e != nil {
			// return error in json
			r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("%v", e))
			r.Results.Success = false
			err, _ := json.Marshal(r.Results)
			resStr = string(err)
			return
		}
	}()
	runtime.GOMAXPROCS(1)
	sockspec, err := modules.ReadPersistInputParameters(in, &r.Parameters)
	if err != nil {
		panic(err)
	}
	err = r.ValidateParameters()
	if err != nil {
		panic(err)
	}
	resStr = modules.SendPersistRequest(r.Parameters, sockspec)
	return
}

// ValidateParameters validates the parameters set in the runner for the module.
func (r *run) ValidateParameters() (err error) {
	return
}

// PrintResults returns the results of a query of this module in human readable form.
func (r *run) PrintResults(result modules.Result, foundOnly bool) (prints []string, err error) {
	var (
		elem elements
	)

	err = result.GetElements(&elem)
	if err != nil {
		panic(err)
	}
	resStr := fmt.Sprintf("ok:%v", elem.Ok)
	prints = append(prints, resStr)
	if !foundOnly {
		for _, we := range result.Errors {
			prints = append(prints, we)
		}
	}
	return
}

type elements struct {
	Ok bool `json:"ok"`
}

// Parameters defines any query parameters used in this module.
type Parameters struct {
}

func newParameters() *Parameters {
	return &Parameters{}
}
<commit_msg>dispatch: remove region configuration parameter<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor: Aaron Meihm ameihm@mozilla.com [:alm]

// Package dispatch implements alert dispatching for the agent as a module.
// Persistent modules which generate alerts will have these alerts forwarded
// to this module if the dispatch module is active. The dispatch module can then
// forward the alerts on based on it's configuration.
package dispatch /* import "mig.ninja/mig/modules/dispatch" */

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/jvehent/gozdef"
	"net/http"
	"runtime"
	"time"

	"mig.ninja/mig"
	"mig.ninja/mig/modules"
)

type module struct {
}

// NewRun returns a new instance of a modules.Runner for this module.
func (m *module) NewRun() modules.Runner {
	return new(run)
}

func init() {
	modules.Register("dispatch", new(module))
}

type run struct {
	Parameters Parameters
	Results    modules.Result
}

func buildResults(e elements, r *modules.Result) (buf []byte, err error) {
	r.Success = true
	r.Elements = e
	r.FoundAnything = true
	buf, err = json.Marshal(r)
	return
}

var logChan chan string
var alertChan chan string
var handlerErrChan chan error
var configChan chan modules.ConfigParams

// We keep a copy of the agent environment, hostname, and tags here so we can
// use them in the dispatched output
var env mig.AgentEnv
var tags map[string]string
var agtHostname string

// messageBuf is a queue used to store incoming messages, and is drained by
// runDispatch
var messageBuf chan string

// lastDrop stores the last time a message was dropped by the dispatch module
var lastDrop time.Time
var dropCounter int

// Dispatch record describes the formatting of JSON data submitted from the dispatch
// module.
type DispatchRecord struct {
	Hostname string            `json:"hostname"`
	Env      mig.AgentEnv      `json:"environment"`
	Tags     map[string]string `json:"tags"`
	Details  interface{}       `json:"details"`
}

func (d *DispatchRecord) fromString(msg string) error {
	d.Hostname = agtHostname
	d.Env = env
	d.Tags = tags
	err := json.Unmarshal([]byte(msg), &d.Details)
	return err
}

func moduleMain() {
	var cfg config

	incfg := <-configChan
	agtHostname = incfg.Hostname
	tags = incfg.Tags
	buf, err := json.Marshal(incfg.Env)
	if err != nil {
		handlerErrChan <- err
		return
	}
	err = json.Unmarshal(buf, &env)
	if err != nil {
		handlerErrChan <- err
		return
	}
	buf, err = json.Marshal(incfg.Config)
	if err != nil {
		handlerErrChan <- err
		return
	}
	err = json.Unmarshal(buf, &cfg)
	if err != nil {
		handlerErrChan <- err
		return
	}
	logChan <- "module received configuration"
	if cfg.Dispatch.ChannelSize == 0 {
		cfg.Dispatch.ChannelSize = 1024
		logChan <- "channel size not specified, defaulting to 1024"
	}

	messageBuf = make(chan string, cfg.Dispatch.ChannelSize)

	// Register the dispatch function, which will be called when the module
	// recieves an alert message from the agent.
	modules.RegisterDispatchFunction(dispatchIn)

	// Start the dispatch dequeue function.
	err = runDispatch(cfg)
	if err != nil {
		handlerErrChan <- err
		return
	}
}

func dispatchIn(msg string) {
	select {
	case messageBuf <- msg:
	default:
		dropCounter++
		// If we can't queue the message it is just dropped
		now := time.Now()
		if now.After(lastDrop.Add(time.Duration(time.Minute * 5))) {
			logChan <- fmt.Sprintf("warning, dispatch module dropping messages "+
				"(buffer full, %v dropped since last warning)", dropCounter)
			lastDrop = now
			dropCounter = 0
		}
	}
}

func runDispatch(cfg config) error {
	var (
		httpClient http.Client
		dr         DispatchRecord
	)

	if cfg.Dispatch.SNSTopic != "" {
		err := initSNS(cfg)
		if err != nil {
			return err
		}
	}

	for {
		msg := <-messageBuf
		dr.fromString(msg)
		var buf []byte
		var err error
		if cfg.Dispatch.OutputMozdef {
			// If we are set to output records to MozDef, convert the
			// dispatch record into a gozdef event
			ge, err := gozdef.NewEvent()
			if err != nil {
				logChan <- fmt.Sprintf("create gozdef event: %v", err)
				continue
			}
			ge.Info()
			ge.Summary = "mig dispatch event"
			ge.Category = "mig"
			ge.Details = dr
			ge.Tags = append(ge.Tags, "mig-dispatch")
			buf, err = json.Marshal(ge)
			if err != nil {
				logChan <- fmt.Sprintf("populate gozdef event: %v", err)
				continue
			}
		} else {
			buf, err = json.Marshal(dr)
			if err != nil {
				logChan <- fmt.Sprintf("create dispatch record: %v", err)
				continue
			}
		}
		if cfg.Dispatch.SNSTopic != "" {
			err := dispatchSNS(buf)
			if err != nil {
				logChan <- fmt.Sprintf("sns dispatch: %v", err)
				continue
			}
		} else {
			// Default to HTTP POST
			b := bytes.NewBuffer(buf)
			resp, err := httpClient.Post(cfg.Dispatch.HTTPURL, "application/json", b)
			if err != nil {
				logChan <- fmt.Sprintf("http post: %v", err)
				continue
			}
			resp.Body.Close()
		}
	}
	return nil
}

func requestHandler(p interface{}) (ret string) {
	var results modules.Result
	defer func() {
		if e := recover(); e != nil {
			results.Errors = append(results.Errors, fmt.Sprintf("%v", e))
			results.Success = false
			err, _ := json.Marshal(results)
			ret = string(err)
			return
		}
	}()
	e := elements{Ok: true}
	resp, err := buildResults(e, &results)
	if err != nil {
		panic(err)
	}
	return string(resp)
}

type config struct {
	Dispatch struct {
		OutputMozdef bool   `json:"outputmozdef"`
		HTTPURL      string `json:"httpurl"`
		SNSTopic     string `json:"snstopic"`
		ChannelSize  int    `json:"channelsize"`
	} `json:"dispatch"`
}

// PersistModConfig returns a new configuration structure for this module.
func (r *run) PersistModConfig() interface{} {
	return &config{}
}

// RunPersist is the entry point for persistent execution of the module.
func (r *run) RunPersist(in modules.ModuleReader, out modules.ModuleWriter) {
	alertChan = make(chan string, 64)
	logChan = make(chan string, 64)
	regChan := make(chan string, 64)
	handlerErrChan = make(chan error, 64)
	configChan = make(chan modules.ConfigParams, 1)

	go moduleMain()
	l, spec, err := modules.GetPersistListener("dispatch")
	if err != nil {
		handlerErrChan <- err
	} else {
		regChan <- spec
	}
	go modules.HandlePersistRequest(l, requestHandler, handlerErrChan)
	modules.DefaultPersistHandlers(in, out, logChan, handlerErrChan, regChan,
		alertChan, configChan)
}

// Run is the entry point for a standard (e.g., query) based invocation of the module.
func (r *run) Run(in modules.ModuleReader) (resStr string) {
	defer func() {
		if e := recover(); e != nil {
			// return error in json
			r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("%v", e))
			r.Results.Success = false
			err, _ := json.Marshal(r.Results)
			resStr = string(err)
			return
		}
	}()
	runtime.GOMAXPROCS(1)
	sockspec, err := modules.ReadPersistInputParameters(in, &r.Parameters)
	if err != nil {
		panic(err)
	}
	err = r.ValidateParameters()
	if err != nil {
		panic(err)
	}
	resStr = modules.SendPersistRequest(r.Parameters, sockspec)
	return
}

// ValidateParameters validates the parameters set in the runner for the module.
func (r *run) ValidateParameters() (err error) {
	return
}

// PrintResults returns the results of a query of this module in human readable form.
func (r *run) PrintResults(result modules.Result, foundOnly bool) (prints []string, err error) {
	var (
		elem elements
	)

	err = result.GetElements(&elem)
	if err != nil {
		panic(err)
	}
	resStr := fmt.Sprintf("ok:%v", elem.Ok)
	prints = append(prints, resStr)
	if !foundOnly {
		for _, we := range result.Errors {
			prints = append(prints, we)
		}
	}
	return
}

type elements struct {
	Ok bool `json:"ok"`
}

// Parameters defines any query parameters used in this module.
type Parameters struct {
}

func newParameters() *Parameters {
	return &Parameters{}
}
<|endoftext|>


<commit_before>/*
Copyright 2022 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 checker

import (
	"strings"

	"github.com/blang/semver"
	git "github.com/go-git/go-git/v5"
	gitconfig "github.com/go-git/go-git/v5/config"
	"github.com/go-git/go-git/v5/plumbing"
	gitmemory "github.com/go-git/go-git/v5/storage/memory"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

// Checker is the main structure of checking if we're in Test Freeze.
type Checker struct {
	checker checker
	log     *logrus.Entry
}

// Result is the result returned by `InTestFreeze`.
type Result struct {
	// InTestFreeze is true if we're in Test Freeze.
	InTestFreeze bool

	// Branch is the found latest release branch.
	Branch string

	// Tag is the latest minor release tag to be expected.
	Tag string
}

//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 -generate
//counterfeiter:generate . checker
type checker interface {
	ListRefs(*git.Remote) ([]*plumbing.Reference, error)
}

type defaultChecker struct{}

// New creates a new Checker instance.
func New(log *logrus.Entry) *Checker {
	return &Checker{
		checker: &defaultChecker{},
		log:     log,
	}
}

// InTestFreeze returns if we're in Test Freeze:
// https://github.com/kubernetes/sig-release/blob/2d8a1cc/releases/release_phases.md#test-freeze
// It errors in case of any issue.
func (c *Checker) InTestFreeze() (*Result, error) {
	remote := git.NewRemote(gitmemory.NewStorage(), &gitconfig.RemoteConfig{
		Name: "origin",
		URLs: []string{"https://github.com/kubernetes/kubernetes"},
	})

	refs, err := c.checker.ListRefs(remote)
	if err != nil {
		c.log.Errorf("Unable to list git remote: %v", err)
		return nil, errors.Wrap(err, "list git remote")
	}

	const releaseBranchPrefix = "release-"
	var (
		latestSemver semver.Version
		latestBranch string
	)

	for _, ref := range refs {
		if ref.Name().IsBranch() {
			branch := ref.Name().Short()

			// Filter for release branches
			if !strings.HasPrefix(branch, releaseBranchPrefix) {
				continue
			}

			// Try to parse the latest minor version
			version := strings.TrimPrefix(branch, releaseBranchPrefix) + ".0"

			parsed, err := semver.Parse(version)
			if err != nil {
				c.log.Warnf("Unable parse version %s: %v", version, err)
				continue
			}

			if parsed.GT(latestSemver) {
				latestSemver = parsed
				latestBranch = branch
			}
		}
	}

	if latestBranch == "" {
		return nil, errors.New("no latest release branch found")
	}

	for _, ref := range refs {
		if ref.Name().IsTag() {
			tag := strings.TrimPrefix(ref.Name().Short(), "v")

			parsed, err := semver.Parse(tag)
			if err != nil {
				c.log.Warnf("Unable parse tag %s: %v", tag, err)
				continue
			}

			// Found the latest minor version on the latest release branch,
			// which means we're not in Test Freeze.
			if latestSemver.EQ(parsed) {
				return &Result{
					InTestFreeze: false,
					Branch:       latestBranch,
					Tag:          "v" + tag,
				}, nil
			}
		}
	}

	// Latest minor version not found in latest release branch,
	// we're in Test Freeze.
	return &Result{
		InTestFreeze: true,
		Branch:       latestBranch,
		Tag:          "v" + latestSemver.String(),
	}, nil
}

func (*defaultChecker) ListRefs(r *git.Remote) ([]*plumbing.Reference, error) {
	return r.List(&git.ListOptions{})
}
<commit_msg>Testfreeze plugin doesn't log warning when it failed parsing tag<commit_after>/*
Copyright 2022 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 checker

import (
	"strings"

	"github.com/blang/semver"
	git "github.com/go-git/go-git/v5"
	gitconfig "github.com/go-git/go-git/v5/config"
	"github.com/go-git/go-git/v5/plumbing"
	gitmemory "github.com/go-git/go-git/v5/storage/memory"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

// Checker is the main structure of checking if we're in Test Freeze.
type Checker struct {
	checker checker
	log     *logrus.Entry
}

// Result is the result returned by `InTestFreeze`.
type Result struct {
	// InTestFreeze is true if we're in Test Freeze.
	InTestFreeze bool

	// Branch is the found latest release branch.
	Branch string

	// Tag is the latest minor release tag to be expected.
	Tag string
}

//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 -generate
//counterfeiter:generate . checker
type checker interface {
	ListRefs(*git.Remote) ([]*plumbing.Reference, error)
}

type defaultChecker struct{}

// New creates a new Checker instance.
func New(log *logrus.Entry) *Checker {
	return &Checker{
		checker: &defaultChecker{},
		log:     log,
	}
}

// InTestFreeze returns if we're in Test Freeze:
// https://github.com/kubernetes/sig-release/blob/2d8a1cc/releases/release_phases.md#test-freeze
// It errors in case of any issue.
func (c *Checker) InTestFreeze() (*Result, error) {
	remote := git.NewRemote(gitmemory.NewStorage(), &gitconfig.RemoteConfig{
		Name: "origin",
		URLs: []string{"https://github.com/kubernetes/kubernetes"},
	})

	refs, err := c.checker.ListRefs(remote)
	if err != nil {
		c.log.Errorf("Unable to list git remote: %v", err)
		return nil, errors.Wrap(err, "list git remote")
	}

	const releaseBranchPrefix = "release-"
	var (
		latestSemver semver.Version
		latestBranch string
	)

	for _, ref := range refs {
		if ref.Name().IsBranch() {
			branch := ref.Name().Short()

			// Filter for release branches
			if !strings.HasPrefix(branch, releaseBranchPrefix) {
				continue
			}

			// Try to parse the latest minor version
			version := strings.TrimPrefix(branch, releaseBranchPrefix) + ".0"

			parsed, err := semver.Parse(version)
			if err != nil {
				c.log.WithField("version", version).WithError(err).Debug("Unable parse version.")
				continue
			}

			if parsed.GT(latestSemver) {
				latestSemver = parsed
				latestBranch = branch
			}
		}
	}

	if latestBranch == "" {
		return nil, errors.New("no latest release branch found")
	}

	for _, ref := range refs {
		if ref.Name().IsTag() {
			tag := strings.TrimPrefix(ref.Name().Short(), "v")

			parsed, err := semver.Parse(tag)
			if err != nil {
				c.log.WithField("tag", tag).WithError(err).Debug("Unable parse tag.")
				continue
			}

			// Found the latest minor version on the latest release branch,
			// which means we're not in Test Freeze.
			if latestSemver.EQ(parsed) {
				return &Result{
					InTestFreeze: false,
					Branch:       latestBranch,
					Tag:          "v" + tag,
				}, nil
			}
		}
	}

	// Latest minor version not found in latest release branch,
	// we're in Test Freeze.
	return &Result{
		InTestFreeze: true,
		Branch:       latestBranch,
		Tag:          "v" + latestSemver.String(),
	}, nil
}

func (*defaultChecker) ListRefs(r *git.Remote) ([]*plumbing.Reference, error) {
	return r.List(&git.ListOptions{})
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"time"

	"github.com/TingYunAPM/go"
	"github.com/TingYunAPM/go/framework/beego"
	"github.com/astaxie/beego"
	"github.com/TingYunAPM/routinelocal"
)

type MainController struct {
	tingyun_beego.Controller
}

func getTingyunAction() *tingyun.Action {
	return tingyun_beego.RoutineLocalGetAction()
}
func db_component() {
	action := getTingyunAction()
	component := action.CreateDBComponent(tingyun.ComponentMysql, "192.168.100.2", "mydb", "mytable", "select", "db_component")
	time.Sleep(2 * time.Second)
	component.Finish()
}
func (this *MainController) Get() {
	db_component()
	this.Ctx.WriteString("hello world")
}

func main() {
	err := tingyun.AppInit("tingyun.json")
	tingyun_beego.RoutineLocalInit(routinelocal.Get())
	if err != nil {
		fmt.Println(err)
	}
	defer tingyun.AppStop()

	beego.Router("/", &MainController{})
	tingyun_beego.Run()
}
<commit_msg>添加tingyun_beego框架使用示例说明注释<commit_after>package main

import (
	"fmt"
	"time"

	"github.com/TingYunAPM/go"
	"github.com/TingYunAPM/go/framework/beego"
	"github.com/astaxie/beego"
	"github.com/TingYunAPM/routinelocal"
//如果是通过修改golang代码内建支持协程局部存储的版本,请注释上一行,打开下面一行的注释
//	"github.com/TingYunAPM/routinelocal/native"
)

type MainController struct {
	tingyun_beego.Controller
}

func getTingyunAction() *tingyun.Action {
	return tingyun_beego.RoutineLocalGetAction()
}
func db_component() {
	action := getTingyunAction()
	component := action.CreateDBComponent(tingyun.ComponentMysql, "192.168.100.2", "mydb", "mytable", "select", "db_component")
	time.Sleep(2 * time.Second)
	component.Finish()
}
func (this *MainController) Get() {
	db_component()
	this.Ctx.WriteString("hello world")
}

func main() {
	err := tingyun.AppInit("tingyun.json")
	//注意: 如果要使用 tingyun_beego.RoutineLocalGetAction(),下边这行必须添加
	tingyun_beego.RoutineLocalInit(routinelocal.Get())
	if err != nil {
		fmt.Println(err)
	}
	defer tingyun.AppStop()

	beego.Router("/", &MainController{})
	tingyun_beego.Run()
}
<|endoftext|>


<commit_before>/*
Copyright IBM Corp. 2016 All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

		 http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package util

import (
	"bytes"
	"strings"
	"testing"
	"time"
)

func TestComputeCryptoHash(t *testing.T) {
	if bytes.Compare(ComputeCryptoHash([]byte("foobar")), ComputeCryptoHash([]byte("foobar"))) != 0 {
		t.Fatalf("Expected hashes to match, but they did not match")
	}
	if bytes.Compare(ComputeCryptoHash([]byte("foobar1")), ComputeCryptoHash([]byte("foobar2"))) == 0 {
		t.Fatalf("Expected hashes to be different, but they match")
	}
}

func TestUUIDGeneration(t *testing.T) {
	uuid := GenerateUUID()
	if len(uuid) != 36 {
		t.Fatalf("UUID length is not correct. Expected = 36, Got = %d", len(uuid))
	}
	uuid2 := GenerateUUID()
	if uuid == uuid2 {
		t.Fatalf("Two UUIDs are equal. This should never occur")
	}
}

func TestIntUUIDGeneration(t *testing.T) {
	uuid := GenerateIntUUID()

	uuid2 := GenerateIntUUID()
	if uuid == uuid2 {
		t.Fatalf("Two UUIDs are equal. This should never occur")
	}
}
func TestTimestamp(t *testing.T) {
	for i := 0; i < 10; i++ {
		t.Logf("timestamp now: %v", CreateUtcTimestamp())
		time.Sleep(200 * time.Millisecond)
	}
}

func TestGenerateHashFromSignature(t *testing.T) {
	if bytes.Compare(GenerateHashFromSignature("aPath", "aCtor", []string{"1", "2"}),
		GenerateHashFromSignature("aPath", "aCtor", []string{"1", "2"})) != 0 {
		t.Fatalf("Expected hashes to match, but they did not match")
	}
	if bytes.Compare(GenerateHashFromSignature("aPath", "aCtor", []string{"1", "2"}),
		GenerateHashFromSignature("bPath", "bCtor", []string{"3", "4"})) == 0 {
		t.Fatalf("Expected hashes to be different, but they match")
	}
}

func TestFindMissingElements(t *testing.T) {
	all := []string{"a", "b", "c", "d"}
	some := []string{"b", "c"}
	expectedDelta := []string{"a", "d"}
	actualDelta := FindMissingElements(all, some)
	if len(expectedDelta) != len(actualDelta) {
		t.Fatalf("Got %v, expected %v", actualDelta, expectedDelta)
	}
	for i := range expectedDelta {
		if strings.Compare(expectedDelta[i], actualDelta[i]) != 0 {
			t.Fatalf("Got %v, expected %v", actualDelta, expectedDelta)
		}
	}
}
<commit_msg>Add test case in core/util/utils_test.go<commit_after>/*
Copyright IBM Corp. 2016 All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

		 http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package util

import (
	"bytes"
	"strings"
	"testing"
	"time"
)

func TestComputeCryptoHash(t *testing.T) {
	if bytes.Compare(ComputeCryptoHash([]byte("foobar")), ComputeCryptoHash([]byte("foobar"))) != 0 {
		t.Fatalf("Expected hashes to match, but they did not match")
	}
	if bytes.Compare(ComputeCryptoHash([]byte("foobar1")), ComputeCryptoHash([]byte("foobar2"))) == 0 {
		t.Fatalf("Expected hashes to be different, but they match")
	}
}

func TestUUIDGeneration(t *testing.T) {
	uuid := GenerateUUID()
	if len(uuid) != 36 {
		t.Fatalf("UUID length is not correct. Expected = 36, Got = %d", len(uuid))
	}
	uuid2 := GenerateUUID()
	if uuid == uuid2 {
		t.Fatalf("Two UUIDs are equal. This should never occur")
	}
}

func TestIntUUIDGeneration(t *testing.T) {
	uuid := GenerateIntUUID()

	uuid2 := GenerateIntUUID()
	if uuid == uuid2 {
		t.Fatalf("Two UUIDs are equal. This should never occur")
	}
}
func TestTimestamp(t *testing.T) {
	for i := 0; i < 10; i++ {
		t.Logf("timestamp now: %v", CreateUtcTimestamp())
		time.Sleep(200 * time.Millisecond)
	}
}

func TestGenerateHashFromSignature(t *testing.T) {
	if bytes.Compare(GenerateHashFromSignature("aPath", "aCtor", []string{"1", "2"}),
		GenerateHashFromSignature("aPath", "aCtor", []string{"1", "2"})) != 0 {
		t.Fatalf("Expected hashes to match, but they did not match")
	}
	if bytes.Compare(GenerateHashFromSignature("aPath", "aCtor", []string{"1", "2"}),
		GenerateHashFromSignature("bPath", "bCtor", []string{"3", "4"})) == 0 {
		t.Fatalf("Expected hashes to be different, but they match")
	}
}

func TestGenerateUUIDfromTxSHAHash(t *testing.T) {
	uuid := GenerateUUIDfromTxSHAHash([]byte("foobar"))
	if len(uuid) != 36 {
		t.Fatalf("UUID length is not correct. Expected = 36, Got = %d", len(uuid))
	}
	uuid2 := GenerateUUIDfromTxSHAHash([]byte("foobar1"))
	if uuid == uuid2 {
		t.Fatalf("Two UUIDs are equal. This should never occur")
	}
}

func TestGenerateIDWithAlg(t *testing.T) {
	_, err := GenerateIDWithAlg("sha256base64", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
	if err != nil {
		t.Fatalf("Decoder failure: %v", err)
	}
}

func TestFindMissingElements(t *testing.T) {
	all := []string{"a", "b", "c", "d"}
	some := []string{"b", "c"}
	expectedDelta := []string{"a", "d"}
	actualDelta := FindMissingElements(all, some)
	if len(expectedDelta) != len(actualDelta) {
		t.Fatalf("Got %v, expected %v", actualDelta, expectedDelta)
	}
	for i := range expectedDelta {
		if strings.Compare(expectedDelta[i], actualDelta[i]) != 0 {
			t.Fatalf("Got %v, expected %v", actualDelta, expectedDelta)
		}
	}
}
<|endoftext|>


<commit_before>// Package tests creates tests files for the given schema
package tests

import (
	"bytes"
	"fmt"
	"strings"
	"text/template"

	"go/format"

	"github.com/cihangir/gene/config"
	"github.com/cihangir/gene/generators/common"
	"github.com/cihangir/schema"
)

type generator struct{}

func New() *generator {
	return &generator{}
}

var PathForTests = "%smodels/%s_statements.go"

func (g *generator) Name() string {
	return "statements"
}

// Generate generates the tests for the schema
func (g *generator) Generate(context *config.Context, s *schema.Schema) ([]common.Output, error) {
	moduleName := context.ModuleNameFunc(s.Title)
	outputs := make([]common.Output, 0)

	// Generate test functions
	testFuncs, err := GenerateTestFuncs(s)
	if err != nil {
		return nil, err
	}

	path := fmt.Sprintf("%stests/testfuncs.go", context.Config.Target)

	outputs = append(outputs, common.Output{
		Content: testFuncs,
		Path:    path,
	})

	// generate module test file
	mainTest, err := GenerateMainTestFileForModule(s)
	if err != nil {
		return nil, err
	}

	path = fmt.Sprintf(
		"%sworkers/%s/tests/common_test.go",
		context.Config.Target,
		moduleName,
	)

	outputs = append(outputs, common.Output{
		Content: mainTest,
		Path:    path,
	})

	// generate tests for the schema
	for _, def := range s.Definitions {
		testFile, err := GenerateTests(s.Title, def)
		if err != nil {
			return nil, err
		}
		path = fmt.Sprintf(
			"%sworkers/%s/tests/%s_test.go",
			context.Config.Target,
			moduleName,
			strings.ToLower(def.Title),
		)

		outputs = append(outputs, common.Output{
			Content: testFile,
			Path:    path,
		})

	}

	return outputs, nil
}

// GenerateMainTestFileForModule generates the main test file for the module
// which will be used by other test files
func GenerateMainTestFileForModule(s *schema.Schema) ([]byte, error) {
	// TODO check if file is there, no need to continue
	temp := template.New("mainTestFile.tmpl").Funcs(common.TemplateFuncs)

	if _, err := temp.Parse(MainTestsTemplate); err != nil {
		return nil, err
	}

	var buf bytes.Buffer

	if err := temp.ExecuteTemplate(&buf, "mainTestFile.tmpl", s); err != nil {
		return nil, err
	}

	return format.Source(buf.Bytes())
}

// GenerateTestFuncs generates tests functions
func GenerateTestFuncs(s *schema.Schema) ([]byte, error) {
	// TODO check if file is there, no need to continue
	temp := template.New("testFuncs.tmpl")

	if _, err := temp.Parse(TestFuncs); err != nil {
		return nil, err
	}

	var buf bytes.Buffer

	if err := temp.ExecuteTemplate(&buf, "testFuncs.tmpl", nil); err != nil {
		return nil, err
	}

	return format.Source(buf.Bytes())
}

// GenerateTests generates the actual tests for the schema
func GenerateTests(moduleName string, s *schema.Schema) ([]byte, error) {
	temp := template.New("tests.tmpl").Funcs(common.TemplateFuncs)

	if _, err := temp.Parse(TestsTemplate); err != nil {
		return nil, err
	}

	var buf bytes.Buffer

	data := struct {
		ModuleName string
		Schema     *schema.Schema
	}{
		ModuleName: strings.ToLower(moduleName),
		Schema:     s,
	}

	if err := temp.ExecuteTemplate(&buf, "tests.tmpl", data); err != nil {
		return nil, err
	}
	return format.Source(buf.Bytes())
}
<commit_msg>Gene: use common.Context<commit_after>// Package tests creates tests files for the given schema
package tests

import (
	"bytes"
	"fmt"
	"strings"
	"text/template"

	"go/format"

	"github.com/cihangir/gene/generators/common"
	"github.com/cihangir/schema"
)

type generator struct{}

func New() *generator {
	return &generator{}
}

var PathForTests = "%smodels/%s_statements.go"

func (g *generator) Name() string {
	return "statements"
}

// Generate generates the tests for the schema
func (g *generator) Generate(context *common.Context, s *schema.Schema) ([]common.Output, error) {
	moduleName := context.ModuleNameFunc(s.Title)
	outputs := make([]common.Output, 0)

	// Generate test functions
	testFuncs, err := GenerateTestFuncs(s)
	if err != nil {
		return nil, err
	}

	path := fmt.Sprintf("%stests/testfuncs.go", context.Config.Target)

	outputs = append(outputs, common.Output{
		Content: testFuncs,
		Path:    path,
	})

	// generate module test file
	mainTest, err := GenerateMainTestFileForModule(s)
	if err != nil {
		return nil, err
	}

	path = fmt.Sprintf(
		"%sworkers/%s/tests/common_test.go",
		context.Config.Target,
		moduleName,
	)

	outputs = append(outputs, common.Output{
		Content: mainTest,
		Path:    path,
	})

	// generate tests for the schema
	for _, def := range s.Definitions {
		testFile, err := GenerateTests(s.Title, def)
		if err != nil {
			return nil, err
		}
		path = fmt.Sprintf(
			"%sworkers/%s/tests/%s_test.go",
			context.Config.Target,
			moduleName,
			strings.ToLower(def.Title),
		)

		outputs = append(outputs, common.Output{
			Content: testFile,
			Path:    path,
		})

	}

	return outputs, nil
}

// GenerateMainTestFileForModule generates the main test file for the module
// which will be used by other test files
func GenerateMainTestFileForModule(s *schema.Schema) ([]byte, error) {
	// TODO check if file is there, no need to continue
	temp := template.New("mainTestFile.tmpl").Funcs(common.TemplateFuncs)

	if _, err := temp.Parse(MainTestsTemplate); err != nil {
		return nil, err
	}

	var buf bytes.Buffer

	if err := temp.ExecuteTemplate(&buf, "mainTestFile.tmpl", s); err != nil {
		return nil, err
	}

	return format.Source(buf.Bytes())
}

// GenerateTestFuncs generates tests functions
func GenerateTestFuncs(s *schema.Schema) ([]byte, error) {
	// TODO check if file is there, no need to continue
	temp := template.New("testFuncs.tmpl")

	if _, err := temp.Parse(TestFuncs); err != nil {
		return nil, err
	}

	var buf bytes.Buffer

	if err := temp.ExecuteTemplate(&buf, "testFuncs.tmpl", nil); err != nil {
		return nil, err
	}

	return format.Source(buf.Bytes())
}

// GenerateTests generates the actual tests for the schema
func GenerateTests(moduleName string, s *schema.Schema) ([]byte, error) {
	temp := template.New("tests.tmpl").Funcs(common.TemplateFuncs)

	if _, err := temp.Parse(TestsTemplate); err != nil {
		return nil, err
	}

	var buf bytes.Buffer

	data := struct {
		ModuleName string
		Schema     *schema.Schema
	}{
		ModuleName: strings.ToLower(moduleName),
		Schema:     s,
	}

	if err := temp.ExecuteTemplate(&buf, "tests.tmpl", data); err != nil {
		return nil, err
	}
	return format.Source(buf.Bytes())
}
<|endoftext|>


<commit_before>package platform

import (
	"bufio"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
)

func uname(opts ...string) (string, error) {

	cmd := exec.Command("uname", opts...)
	bout, err := cmd.Output()
	if err != nil {
		return "", err
	}
	out := strings.Trim(string(bout), " \r\n")
	return out, err
}

func infos() (Platform, error) {
	var err error
	var plat Platform

	table := map[string]string{
		"System":    "-s",
		"Node":      "-n",
		"Release":   "-r",
		"Version":   "-v",
		"Machine":   "-m",
		"Processor": "-p",
	}

	for n, arg := range table {
		var v string
		v, err = uname(arg)
		if err != nil {
			return plat, err
		}
		table[n] = v
	}

	plat.System = table["System"]
	plat.Node = table["Node"]
	plat.Release = table["Release"]
	plat.Version = table["Version"]
	plat.Machine = table["Machine"]
	plat.Processor = table["Processor"]

	err = plat.init_dist()

	return plat, err
}

func (p *Platform) init_dist() error {
	var err error

	distname := strings.ToLower(p.System)
	distvers := ""

	re_release_filename := regexp.MustCompile(`(\w+)[-_](release|version)`)
	re_lsb_release_version := regexp.MustCompile(`(.+) release ([\d.]+)[^(]*(?:\((.+)\))?`)
	//re_release_version := regexp.MustCompile(`([^0-9]+)(?: release )?([\d.]+)[^(]*(?:\((.+)\))?`)

	switch p.System {
	case "Linux":
		distfullname := ""
		distfile := ""
		var files []string
		files, err = filepath.Glob(filepath.Join("/etc", "*"))
		if err != nil {
			return err
		}
		for _, fname := range files {
			m := re_release_filename.FindStringSubmatch(fname)
			if m == nil {
				continue
			}
			distname = m[1]
			//fmt.Printf(">>> [%s]\n    [%v] => %q\n", fname, m, distname)
			distfile = fname
			break
		}
		if distname == "" {
			return fmt.Errorf("platform: unsupported linux distribution")
		}
		var f *os.File
		f, err = os.Open(distfile)
		if err != nil {
			return err
		}
		defer f.Close()

		var line string
		line, err = bufio.NewReader(f).ReadString('\n')
		if err != nil {
			return err
		}
		//distid := ""
		m := re_lsb_release_version.FindStringSubmatch(line)
		if m == nil {
			// pre-LSB. not supported.
			return fmt.Errorf("platform: unsupported linux distribution")
		}
		// LSB format: "distro release x.x (codename)"
		distfullname = m[1]
		distvers = m[2]
		//distid = m[3]
		//fmt.Printf(">>> %v (%q %q %q)\n", m, distfullname, distvers, distid)
		match := func(pattern string) bool {
			matched, err := regexp.MatchString(pattern, distfullname)
			if err != nil {
				return false
			}
			return matched
		}

		if match(`Red.*?Hat.*?Enterprise.*?Linux.*?`) {
			// rel := strings.Split(distvers, ".")
			// major := rel[0]
			// //minor := rel[1]
			// distver = major
			distname = "rhel"

		} else if match(`Red.*?Hat.*?Linux.*?`) {
			// rel := strings.Split(distvers, ".")
			// major := rel[0]
			// //minor := rel[1]
			// distver = major
			distname = "rh"

		} else if match(`CERN.*?E.*?Linux.*?`) {
			distname = "cel"

		} else if match(`Scientific.*?Linux.*?CERN.*?`) {

			// rel := strings.Split(distvers, ".")
			// major := rel[0]
			// //minor := rel[1]
			// distver = major
			distname = "slc"
		} else if match(`Scientific.*?Linux.*?`) {

			// rel := strings.Split(distvers, ".")
			// major := rel[0]
			// //minor := rel[1]
			// distver = major
			distname = "sl"

		} else if match(`Fedora.*?`) {
			distname = "fedora"

		} else if match(``) {

		} else {

		}

	case "Darwin":
		distvers = p.Release
		vers, err := exec.Command("sw_vers", "-productVersion").Output()
		if err == nil {
			distvers = strings.Trim(string(vers), " \r\n")
		}
		distname = "darwin"

	default:
		panic(fmt.Sprintf("platform: unknown platform [%s]", p.System))
	}

	p.DistName = distname
	p.DistVers = distvers
	return err
}

// EOF
<commit_msg>platform: better osname detection<commit_after>package platform

import (
	"bufio"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"sort"
	"strings"

	gocfg "github.com/sbinet/go-config/config"
)

func uname(opts ...string) (string, error) {

	cmd := exec.Command("uname", opts...)
	bout, err := cmd.Output()
	if err != nil {
		return "", err
	}
	out := strings.Trim(string(bout), " \r\n")
	return out, err
}

func infos() (Platform, error) {
	var err error
	var plat Platform

	table := map[string]string{
		"System":    "-s",
		"Node":      "-n",
		"Release":   "-r",
		"Version":   "-v",
		"Machine":   "-m",
		"Processor": "-p",
	}

	for n, arg := range table {
		var v string
		v, err = uname(arg)
		if err != nil {
			return plat, err
		}
		table[n] = v
	}

	plat.System = table["System"]
	plat.Node = table["Node"]
	plat.Release = table["Release"]
	plat.Version = table["Version"]
	plat.Machine = table["Machine"]
	plat.Processor = table["Processor"]

	err = plat.init_dist()

	return plat, err
}

func parse_dist_files(fnames []string) (distname, distvers string, err error) {
	distname = ""
	distvers = ""

	re_release_filename := regexp.MustCompile(`(\w+)[-_](release|version)`)
	re_lsb_release_version := regexp.MustCompile(`(.+) release ([\d.]+)[^(]*(?:\((.+)\))?`)
	re_release_version := regexp.MustCompile(`([^0-9]+)(?: release )?([\d.]+)[^(]*(?:\((.+)\))?`)

	if !sort.StringsAreSorted(fnames) {
		sort.Strings(fnames)
	}

	for _, args := range [][]string{
		{"/etc/os-release", "ID", "VERSION_ID"},
		{"/etc/lsb-release", "DISTRIB_ID", "DISTRIB_RELEASE"},
	} {
		fname := args[0]
		id_str := args[1]
		vers_str := args[2]
		//fmt.Printf("--> [%s]\n", fname)
		idx := sort.SearchStrings(fnames, fname)
		if !(idx < len(fnames) && fnames[idx] == fname) {
			continue
		}

		cfg, err := gocfg.ReadDefault(fname)
		if err != nil {
			//fmt.Printf("++ %v\n", err)
			continue
		}

		d_name, err := cfg.RawString("DEFAULT", id_str)
		if err == nil {
			distname = strings.Trim(d_name, `"'`)
		} else {
			//fmt.Printf("++ %v\n", err)
			continue
		}
		d_vers, err := cfg.RawString("DEFAULT", vers_str)
		if err == nil {
			distvers = strings.Trim(d_vers, `"'`)
		} else {
			// distvers is optional...

			//fmt.Printf("++ %v\n", err)
			//continue
		}
		return distname, distvers, nil
	}

	for _, fname := range fnames {
		m := re_release_filename.FindStringSubmatch(fname)
		if m == nil {
			continue
		}
		distname = m[1]
		//fmt.Printf(">>> [%s]\n    [%v] => %q\n", fname, m, distname)
		distfile := fname

		f, err2 := os.Open(distfile)
		defer f.Close()
		if err2 != nil {
			continue
		}

		var line string
		line, err2 = bufio.NewReader(f).ReadString('\n')
		if err2 != nil {
			continue
		}
		//distid := ""

		m = re_lsb_release_version.FindStringSubmatch(line)
		if m == nil {
			// pre-LSB.
			m = re_release_version.FindStringSubmatch(line)
			if m == nil {
				// not supported.
				//fmt.Printf("** [%s] wrong format (%v)\n", distfile, strings.Trim(line, " \r\n"))
				continue
			}
		}
		// LSB format: "distro release x.x (codename)"
		distfullname := m[1]
		distvers = m[2]
		//distid = m[3]
		//fmt.Printf(">>> %v (%q %q %q)\n", m, distfullname, distvers, distid)
		match := func(pattern string) bool {
			matched, err := regexp.MatchString(pattern, distfullname)
			if err != nil {
				return false
			}
			return matched
		}

		if match(`Red.*?Hat.*?Enterprise.*?Linux.*?`) {
			// rel := strings.Split(distvers, ".")
			// major := rel[0]
			// //minor := rel[1]
			// distver = major
			distname = "rhel"

		} else if match(`Red.*?Hat.*?Linux.*?`) {
			// rel := strings.Split(distvers, ".")
			// major := rel[0]
			// //minor := rel[1]
			// distver = major
			distname = "rh"

		} else if match(`CERN.*?E.*?Linux.*?`) {
			distname = "cel"

		} else if match(`Scientific.*?Linux.*?CERN.*?`) {

			// rel := strings.Split(distvers, ".")
			// major := rel[0]
			// //minor := rel[1]
			// distver = major
			distname = "slc"
		} else if match(`Scientific.*?Linux.*?`) {

			// rel := strings.Split(distvers, ".")
			// major := rel[0]
			// //minor := rel[1]
			// distver = major
			distname = "sl"

		} else if match(`Fedora.*?`) {
			distname = "fedora"

		} else {

		}
		return distname, distvers, nil
	}
	return "", "", fmt.Errorf("platform: unsupported linux distribution")
}

func (p *Platform) init_dist() error {
	var err error

	distname := strings.ToLower(p.System)
	distvers := ""

	switch p.System {
	case "Linux":
		var files []string
		files, err = filepath.Glob(filepath.Join("/etc", "*"))
		if err != nil {
			return err
		}
		sort.Strings(files)
		distname, distvers, err = parse_dist_files(files)
		if err != nil {
			return err
		}
		if distname == "" {
			return fmt.Errorf("platform: unsupported linux distribution")
		}

	case "Darwin":
		distvers = p.Release
		vers, err := exec.Command("sw_vers", "-productVersion").Output()
		if err == nil {
			distvers = strings.Trim(string(vers), " \r\n")
		}
		distname = "darwin"

	default:
		panic(fmt.Sprintf("platform: unknown platform [%s]", p.System))
	}

	p.DistName = distname
	p.DistVers = distvers
	return err
}

// EOF
<|endoftext|>


<commit_before>// Copyright 2019 The Terraformer 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 azure

import (
	"errors"
	"fmt"
	"os"
	"strings"

	"github.com/Azure/go-autorest/autorest"
	"github.com/GoogleCloudPlatform/terraformer/terraformutils"
	"github.com/GoogleCloudPlatform/terraformer/terraformutils/providerwrapper"
	"github.com/hashicorp/go-azure-helpers/authentication"
	"github.com/hashicorp/go-azure-helpers/sender"
)

type AzureProvider struct { //nolint
	terraformutils.Provider
	config     authentication.Config
	authorizer autorest.Authorizer
}

func (p *AzureProvider) setEnvConfig() error {
	subscriptionID := os.Getenv("ARM_SUBSCRIPTION_ID")
	if subscriptionID == "" {
		return errors.New("set ARM_SUBSCRIPTION_ID env var")
	}
	builder := &authentication.Builder{
		ClientID:                 os.Getenv("ARM_CLIENT_ID"),
		SubscriptionID:           subscriptionID,
		TenantID:                 os.Getenv("ARM_TENANT_ID"),
		Environment:              os.Getenv("ARM_ENVIRONMENT"),
		ClientSecret:             os.Getenv("ARM_CLIENT_SECRET"),
		SupportsAzureCliToken:    true,
		SupportsClientSecretAuth: true,
		SupportsClientCertAuth:   true,
		ClientCertPath:           os.Getenv("ARM_CLIENT_CERTIFICATE_PATH"),
		ClientCertPassword:       os.Getenv("ARM_CLIENT_CERTIFICATE_PASSWORD"),

		/*
		   // Managed Service Identity Auth
		   SupportsManagedServiceIdentity bool
		   MsiEndpoint                    string
		*/
	}

	if builder.Environment == "" {
		builder.Environment = "public"
	}
	config, err := builder.Build()
	if err != nil {
		return nil
	}
	p.config = *config

	return nil
}

func (p *AzureProvider) getAuthorizer() (autorest.Authorizer, error) {
	env, err := authentication.DetermineEnvironment(p.config.Environment)
	if err != nil {
		return nil, err
	}

	oauthConfig, err := p.config.BuildOAuthConfig(env.ActiveDirectoryEndpoint)
	if err != nil {
		return nil, err
	}

	if oauthConfig == nil {
		return nil, fmt.Errorf("Unable to configure OAuthConfig for tenant %s", p.config.TenantID)
	}

	sender := sender.BuildSender("AzureRM")

	auth, err := p.config.GetAuthorizationToken(sender, oauthConfig, env.ResourceManagerEndpoint)
	if err != nil {
		return nil, err
	}

	return auth, nil
}

func (p *AzureProvider) Init(args []string) error {
	err := p.setEnvConfig()
	if err != nil {
		return err
	}

	authorizer, err := p.getAuthorizer()
	if err != nil {
		return err
	}
	p.authorizer = authorizer

	return nil
}

func (p *AzureProvider) GetName() string {
	return "azurerm"
}

func (p *AzureProvider) GetProviderData(arg ...string) map[string]interface{} {
	version := providerwrapper.GetProviderVersion(p.GetName())
	if strings.Contains(version, "v2.") {
		return map[string]interface{}{
			"provider": map[string]interface{}{
				"azurerm": map[string]interface{}{
					"version": providerwrapper.GetProviderVersion(p.GetName()),
					// NOTE:
					// Workaround for azurerm v2 provider changes
					// Tested with azurerm_resource_group under v2.17.0
					// https://github.com/terraform-providers/terraform-provider-azurerm/issues/5866#issuecomment-594239342
					// https://github.com/hashicorp/terraform/issues/24200#issuecomment-594745861
					"features": map[string]interface{}{},
				},
			},
		}
	} else {
		return map[string]interface{}{
			"provider": map[string]interface{}{
				"azurerm": map[string]interface{}{
					"version": providerwrapper.GetProviderVersion(p.GetName()),
				},
			},
		}
	}
}

func (AzureProvider) GetResourceConnections() map[string]map[string][]string {
	return map[string]map[string][]string{}
}

func (p *AzureProvider) GetSupportedService() map[string]terraformutils.ServiceGenerator {
	return map[string]terraformutils.ServiceGenerator{
		"analysis":               &AnalysisGenerator{},
		"database":               &DatabasesGenerator{},
		"disk":                   &DiskGenerator{},
		"network_interface":      &NetworkInterfaceGenerator{},
		"network_security_group": &NetworkSecurityGroupGenerator{},
		"resource_group":         &ResourceGroupGenerator{},
		"storage_account":        &StorageAccountGenerator{},
		"virtual_machine":        &VirtualMachineGenerator{},
		"virtual_network":        &VirtualNetworkGenerator{},
	}
}

func (p *AzureProvider) InitService(serviceName string, verbose bool) error {
	var isSupported bool
	if _, isSupported = p.GetSupportedService()[serviceName]; !isSupported {
		return errors.New("azurerm: " + serviceName + " not supported service")
	}
	p.Service = p.GetSupportedService()[serviceName]
	p.Service.SetName(serviceName)
	p.Service.SetVerbose(verbose)
	p.Service.SetProviderName(p.GetName())
	p.Service.SetArgs(map[string]interface{}{
		"config":     p.config,
		"authorizer": p.authorizer,
	})
	return nil
}
<commit_msg>Simplify var<commit_after>// Copyright 2019 The Terraformer 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 azure

import (
	"errors"
	"fmt"
	"os"
	"strings"

	"github.com/Azure/go-autorest/autorest"
	"github.com/GoogleCloudPlatform/terraformer/terraformutils"
	"github.com/GoogleCloudPlatform/terraformer/terraformutils/providerwrapper"
	"github.com/hashicorp/go-azure-helpers/authentication"
	"github.com/hashicorp/go-azure-helpers/sender"
)

type AzureProvider struct { //nolint
	terraformutils.Provider
	config     authentication.Config
	authorizer autorest.Authorizer
}

func (p *AzureProvider) setEnvConfig() error {
	subscriptionID := os.Getenv("ARM_SUBSCRIPTION_ID")
	if subscriptionID == "" {
		return errors.New("set ARM_SUBSCRIPTION_ID env var")
	}
	builder := &authentication.Builder{
		ClientID:                 os.Getenv("ARM_CLIENT_ID"),
		SubscriptionID:           subscriptionID,
		TenantID:                 os.Getenv("ARM_TENANT_ID"),
		Environment:              os.Getenv("ARM_ENVIRONMENT"),
		ClientSecret:             os.Getenv("ARM_CLIENT_SECRET"),
		SupportsAzureCliToken:    true,
		SupportsClientSecretAuth: true,
		SupportsClientCertAuth:   true,
		ClientCertPath:           os.Getenv("ARM_CLIENT_CERTIFICATE_PATH"),
		ClientCertPassword:       os.Getenv("ARM_CLIENT_CERTIFICATE_PASSWORD"),

		/*
		   // Managed Service Identity Auth
		   SupportsManagedServiceIdentity bool
		   MsiEndpoint                    string
		*/
	}

	if builder.Environment == "" {
		builder.Environment = "public"
	}
	config, err := builder.Build()
	if err != nil {
		return nil
	}
	p.config = *config

	return nil
}

func (p *AzureProvider) getAuthorizer() (autorest.Authorizer, error) {
	env, err := authentication.DetermineEnvironment(p.config.Environment)
	if err != nil {
		return nil, err
	}

	oauthConfig, err := p.config.BuildOAuthConfig(env.ActiveDirectoryEndpoint)
	if err != nil {
		return nil, err
	}

	if oauthConfig == nil {
		return nil, fmt.Errorf("Unable to configure OAuthConfig for tenant %s", p.config.TenantID)
	}

	sender := sender.BuildSender("AzureRM")

	auth, err := p.config.GetAuthorizationToken(sender, oauthConfig, env.ResourceManagerEndpoint)
	if err != nil {
		return nil, err
	}

	return auth, nil
}

func (p *AzureProvider) Init(args []string) error {
	err := p.setEnvConfig()
	if err != nil {
		return err
	}

	authorizer, err := p.getAuthorizer()
	if err != nil {
		return err
	}
	p.authorizer = authorizer

	return nil
}

func (p *AzureProvider) GetName() string {
	return "azurerm"
}

func (p *AzureProvider) GetProviderData(arg ...string) map[string]interface{} {
	version := providerwrapper.GetProviderVersion(p.GetName())
	if strings.Contains(version, "v2.") {
		return map[string]interface{}{
			"provider": map[string]interface{}{
				"azurerm": map[string]interface{}{
					"version": version,
					// NOTE:
					// Workaround for azurerm v2 provider changes
					// Tested with azurerm_resource_group under v2.17.0
					// https://github.com/terraform-providers/terraform-provider-azurerm/issues/5866#issuecomment-594239342
					// https://github.com/hashicorp/terraform/issues/24200#issuecomment-594745861
					"features": map[string]interface{}{},
				},
			},
		}
	} else {
		return map[string]interface{}{
			"provider": map[string]interface{}{
				"azurerm": map[string]interface{}{
					"version": version,
				},
			},
		}
	}
}

func (AzureProvider) GetResourceConnections() map[string]map[string][]string {
	return map[string]map[string][]string{}
}

func (p *AzureProvider) GetSupportedService() map[string]terraformutils.ServiceGenerator {
	return map[string]terraformutils.ServiceGenerator{
		"analysis":               &AnalysisGenerator{},
		"database":               &DatabasesGenerator{},
		"disk":                   &DiskGenerator{},
		"network_interface":      &NetworkInterfaceGenerator{},
		"network_security_group": &NetworkSecurityGroupGenerator{},
		"resource_group":         &ResourceGroupGenerator{},
		"storage_account":        &StorageAccountGenerator{},
		"virtual_machine":        &VirtualMachineGenerator{},
		"virtual_network":        &VirtualNetworkGenerator{},
	}
}

func (p *AzureProvider) InitService(serviceName string, verbose bool) error {
	var isSupported bool
	if _, isSupported = p.GetSupportedService()[serviceName]; !isSupported {
		return errors.New("azurerm: " + serviceName + " not supported service")
	}
	p.Service = p.GetSupportedService()[serviceName]
	p.Service.SetName(serviceName)
	p.Service.SetVerbose(verbose)
	p.Service.SetProviderName(p.GetName())
	p.Service.SetArgs(map[string]interface{}{
		"config":     p.config,
		"authorizer": p.authorizer,
	})
	return nil
}
<|endoftext|>