<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/getgauge/common"
	"os"
	"path"
	"runtime"
	"sort"
	"strings"
)

const (
	gaugeRepositoryUrl = "http://raw.github.com/getgauge/gauge-repository/master"
)

type installDescription struct {
	Name        string
	Description string
	Versions    []versionInstallDescription
}

type versionInstallDescription struct {
	Version             string
	GaugeVersionSupport versionSupport
	Install             platformSpecifics
	DownloadUrls        downloadUrls
}

type downloadUrls struct {
	X86 platformSpecifics
	X64 platformSpecifics
}

type platformSpecifics struct {
	Windows string
	Linux   string
	Darwin  string
}

type versionSupport struct {
	Minimum string
	Maximum string
}

func installPlugin(pluginName, version string) {
	installDescription, err := getInstallDescription(pluginName)
	if err != nil {
		fmt.Printf("[Error] Failed to find install description for Plugin: '%s' %s. : %s \n", pluginName, version, err)
		return
	}
	if err := installPluginWithDescription(installDescription, version); err != nil {
		fmt.Printf("[Error] Failed installing Plugin '%s' %s : %s \n", pluginName, version, err)
		return
	}
	fmt.Printf("Successfully installed plugin: %s %s\n", pluginName, version)
}

func installPluginWithDescription(installDescription *installDescription, version string) error {
	var versionInstallDescription *versionInstallDescription
	var err error
	if version != "" {
		versionInstallDescription, err = installDescription.getVersion(version)
		if err != nil {
			return err
		}
		if compatibilityError := checkCompatiblity(currentGaugeVersion, &versionInstallDescription.GaugeVersionSupport); compatibilityError != nil {
			return errors.New(fmt.Sprintf("Plugin Version %s is not supported for gauge %s : %s", installDescription.Name, versionInstallDescription.Version, versionInstallDescription.Version, currentGaugeVersion.String(), compatibilityError.Error()))
		}
	} else {
		versionInstallDescription, err = installDescription.getLatestCompatibleVersionTo(currentGaugeVersion)
		if err != nil {
			return errors.New(fmt.Sprintf("Could not find compatible version for plugin %s. : %s", installDescription.Name, err))
		}
	}
	return installPluginVersion(installDescription, versionInstallDescription)
}

func installPluginVersion(installDesc *installDescription, versionInstallDescription *versionInstallDescription) error {
	if common.IsPluginInstalled(installDesc.Name, versionInstallDescription.Version) {
		return errors.New(fmt.Sprintf("Plugin %s %s is already installed.", installDesc.Name, versionInstallDescription.Version))
	}

	fmt.Printf("Installing Plugin => %s %s\n", installDesc.Name, versionInstallDescription.Version)
	pluginZip, err := downloadPluginZip(versionInstallDescription.DownloadUrls)
	if err != nil {
		return errors.New(fmt.Sprintf("Failed to download plugin zip: %s.", err))
	}
	unzippedPluginDir, err := common.UnzipArchive(pluginZip)
	if err != nil {
		return errors.New(fmt.Sprintf("Failed to Unzip plugin-zip file %s.", err))
	}
	if err := runInstallCommands(versionInstallDescription.Install, unzippedPluginDir); err != nil {
		return errors.New(fmt.Sprintf("Failed to Run install command. %s.", err))
	}
	return copyPluginFilesToGauge(installDesc, versionInstallDescription, unzippedPluginDir)
}

func runInstallCommands(installCommands platformSpecifics, workingDir string) error {
	command := ""
	switch runtime.GOOS {
	case "windows":
		command = installCommands.Windows
		break
	case "darwin":
		command = installCommands.Darwin
		break
	default:
		command = installCommands.Linux
		break
	}

	if command == "" {
		return nil
	}

	fmt.Printf("Running plugin install command => %s", command)
	cmd := common.GetExecutableCommand(command)
	cmd.Dir = workingDir
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	err := cmd.Start()
	if err != nil {
		return err
	}

	return cmd.Wait()
}

func copyPluginFilesToGauge(installDesc *installDescription, versionInstallDesc *versionInstallDescription, pluginContents string) error {
	pluginsDir, err := common.GetPrimaryPluginsInstallDir()
	if err != nil {
		return err
	}
	versionedPluginDir := path.Join(pluginsDir, installDesc.Name, versionInstallDesc.Version)
	if common.DirExists(versionedPluginDir) {
		return errors.New(fmt.Sprintf("Plugin %s %s already installed at %s", installDesc.Name, versionInstallDesc.Version, versionedPluginDir))
	}
	return common.MirrorDir(pluginContents, versionedPluginDir)

}

func downloadPluginZip(downloadUrls downloadUrls) (string, error) {
	var platformLinks *platformSpecifics
	if strings.Contains(runtime.GOARCH, "64") {
		platformLinks = &downloadUrls.X64
	} else {
		platformLinks = &downloadUrls.X86
	}

	var downloadLink string
	switch runtime.GOOS {
	case "windows":
		downloadLink = platformLinks.Windows
		break
	case "darwin":
		downloadLink = platformLinks.Darwin
		break
	default:
		downloadLink = platformLinks.Linux
		break
	}
	if downloadLink == "" {
		return "", errors.New("Plugin download URL not available for current platform.")
	}
	downloadedFile, err := common.DownloadToTempDir(downloadLink)
	if err != nil {
		return "", errors.New(fmt.Sprintf("Failed to download File %s: %s", downloadLink, err.Error()))
	}
	return downloadedFile, err
}

func getInstallDescription(plugin string) (*installDescription, error) {
	installJson, err := getPluginInstallJson(plugin)
	if err != nil {
		return nil, err
	}
	InstallJsonContents, readErr := common.ReadFileContents(installJson)
	if readErr != nil {
		return nil, readErr
	}
	installDescription := &installDescription{}
	if err = json.Unmarshal([]byte(InstallJsonContents), installDescription); err != nil {
		return nil, err
	}
	return installDescription, nil
}

func getPluginInstallJson(plugin string) (string, error) {
	versionInstallDescriptionJsonFile := plugin + "-install.json"
	versionInstallDescriptionJsonUrl := constructPluginInstallJsonUrl(plugin)

	downloadedFile, downloadErr := common.DownloadToTempDir(versionInstallDescriptionJsonUrl)
	if downloadErr != nil {
		return "", errors.New(fmt.Sprintf("Could not find %s file. Check install name and version. %s", versionInstallDescriptionJsonFile, downloadErr.Error()))
	}
	return downloadedFile, nil
}

func constructPluginInstallJsonUrl(plugin string) string {
	installJsonFile := plugin + "-install.json"
	return fmt.Sprintf("%s/%s", gaugeRepositoryUrl, installJsonFile)
}

func (installDesc *installDescription) getVersion(version string) (*versionInstallDescription, error) {
	for _, versionInstallDescription := range installDesc.Versions {
		if versionInstallDescription.Version == version {
			return &versionInstallDescription, nil
		}
	}
	return nil, errors.New("Could not find install description for Version " + version)
}

func (installDesc *installDescription) getLatestCompatibleVersionTo(version *version) (*versionInstallDescription, error) {
	installDesc.sortVersionInstallDescriptions()
	for _, versionInstallDesc := range installDesc.Versions {
		if err := checkCompatiblity(version, &versionInstallDesc.GaugeVersionSupport); err == nil {
			return &versionInstallDesc, nil
		}
	}
	return nil, errors.New(fmt.Sprintf("Compatible version to %s not found", version))

}

func (installDescription *installDescription) sortVersionInstallDescriptions() {
	sort.Sort(ByDecreasingVersion(installDescription.Versions))
}

func checkCompatiblity(version *version, versionSupport *versionSupport) error {
	minSupportVersion, err := parseVersion(versionSupport.Minimum)
	if err != nil {
		return errors.New(fmt.Sprintf("Invalid minimum support version %s. : %s. ", versionSupport.Minimum, err))
	}
	if versionSupport.Maximum != "" {
		maxSupportVersion, err := parseVersion(versionSupport.Maximum)
		if err != nil {
			return errors.New(fmt.Sprintf("Invalid maximum support version %s. : %s. ", versionSupport.Maximum, err))
		}
		if version.isBetween(minSupportVersion, maxSupportVersion) {
			return nil
		} else {
			return errors.New(fmt.Sprintf("Version %s is not between %s and %s", version, minSupportVersion, maxSupportVersion))
		}
	}

	if minSupportVersion.isLesserThanEqualTo(version) {
		return nil
	}
	return errors.New(fmt.Sprintf("Incompatible version. Minimum support version %s is higher than current version %s", minSupportVersion, version))
}

type ByDecreasingVersion []versionInstallDescription

func (a ByDecreasingVersion) Len() int      { return len(a) }
func (a ByDecreasingVersion) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByDecreasingVersion) Less(i, j int) bool {
	version1, _ := parseVersion(a[i].Version)
	version2, _ := parseVersion(a[j].Version)
	return version1.isGreaterThan(version2)
}
<commit_msg>Adding plugin working directory while running install command<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/getgauge/common"
	"os"
	"path"
	"runtime"
	"sort"
	"strings"
)

const (
	gaugeRepositoryUrl = "http://raw.github.com/getgauge/gauge-repository/master"
)

type installDescription struct {
	Name        string
	Description string
	Versions    []versionInstallDescription
}

type versionInstallDescription struct {
	Version             string
	GaugeVersionSupport versionSupport
	Install             platformSpecifics
	DownloadUrls        downloadUrls
}

type downloadUrls struct {
	X86 platformSpecifics
	X64 platformSpecifics
}

type platformSpecifics struct {
	Windows string
	Linux   string
	Darwin  string
}

type versionSupport struct {
	Minimum string
	Maximum string
}

func installPlugin(pluginName, version string) {
	installDescription, err := getInstallDescription(pluginName)
	if err != nil {
		fmt.Printf("[Error] Failed to find install description for Plugin: '%s' %s. : %s \n", pluginName, version, err)
		return
	}
	if err := installPluginWithDescription(installDescription, version); err != nil {
		fmt.Printf("[Error] Failed installing Plugin '%s' %s : %s \n", pluginName, version, err)
		return
	}
	fmt.Printf("Successfully installed plugin: %s %s\n", pluginName, version)
}

func installPluginWithDescription(installDescription *installDescription, version string) error {
	var versionInstallDescription *versionInstallDescription
	var err error
	if version != "" {
		versionInstallDescription, err = installDescription.getVersion(version)
		if err != nil {
			return err
		}
		if compatibilityError := checkCompatiblity(currentGaugeVersion, &versionInstallDescription.GaugeVersionSupport); compatibilityError != nil {
			return errors.New(fmt.Sprintf("Plugin Version %s is not supported for gauge %s : %s", installDescription.Name, versionInstallDescription.Version, versionInstallDescription.Version, currentGaugeVersion.String(), compatibilityError.Error()))
		}
	} else {
		versionInstallDescription, err = installDescription.getLatestCompatibleVersionTo(currentGaugeVersion)
		if err != nil {
			return errors.New(fmt.Sprintf("Could not find compatible version for plugin %s. : %s", installDescription.Name, err))
		}
	}
	return installPluginVersion(installDescription, versionInstallDescription)
}

func installPluginVersion(installDesc *installDescription, versionInstallDescription *versionInstallDescription) error {
	if common.IsPluginInstalled(installDesc.Name, versionInstallDescription.Version) {
		return errors.New(fmt.Sprintf("Plugin %s %s is already installed.", installDesc.Name, versionInstallDescription.Version))
	}

	fmt.Printf("Installing Plugin => %s %s\n", installDesc.Name, versionInstallDescription.Version)
	pluginZip, err := downloadPluginZip(versionInstallDescription.DownloadUrls)
	if err != nil {
		return errors.New(fmt.Sprintf("Failed to download plugin zip: %s.", err))
	}
	unzippedPluginDir, err := common.UnzipArchive(pluginZip)
	if err != nil {
		return errors.New(fmt.Sprintf("Failed to Unzip plugin-zip file %s.", err))
	}
	fmt.Printf("Plugin unzipped to => %s\n", unzippedPluginDir)
	if err := runInstallCommands(versionInstallDescription.Install, unzippedPluginDir); err != nil {
		return errors.New(fmt.Sprintf("Failed to Run install command. %s.", err))
	}
	return copyPluginFilesToGauge(installDesc, versionInstallDescription, unzippedPluginDir)
}

func runInstallCommands(installCommands platformSpecifics, workingDir string) error {
	command := ""
	switch runtime.GOOS {
	case "windows":
		command = installCommands.Windows
		break
	case "darwin":
		command = installCommands.Darwin
		break
	default:
		command = installCommands.Linux
		break
	}

	if command == "" {
		return nil
	}

	fmt.Printf("Running plugin install command => %s\n", command)
	cmd := common.GetExecutableCommand(path.Join(workingDir, command))
	cmd.Dir = workingDir
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	err := cmd.Start()
	if err != nil {
		return err
	}

	return cmd.Wait()
}

func copyPluginFilesToGauge(installDesc *installDescription, versionInstallDesc *versionInstallDescription, pluginContents string) error {
	pluginsDir, err := common.GetPrimaryPluginsInstallDir()
	if err != nil {
		return err
	}
	versionedPluginDir := path.Join(pluginsDir, installDesc.Name, versionInstallDesc.Version)
	if common.DirExists(versionedPluginDir) {
		return errors.New(fmt.Sprintf("Plugin %s %s already installed at %s", installDesc.Name, versionInstallDesc.Version, versionedPluginDir))
	}
	return common.MirrorDir(pluginContents, versionedPluginDir)

}

func downloadPluginZip(downloadUrls downloadUrls) (string, error) {
	var platformLinks *platformSpecifics
	if strings.Contains(runtime.GOARCH, "64") {
		platformLinks = &downloadUrls.X64
	} else {
		platformLinks = &downloadUrls.X86
	}

	var downloadLink string
	switch runtime.GOOS {
	case "windows":
		downloadLink = platformLinks.Windows
		break
	case "darwin":
		downloadLink = platformLinks.Darwin
		break
	default:
		downloadLink = platformLinks.Linux
		break
	}
	if downloadLink == "" {
		return "", errors.New("Plugin download URL not available for current platform.")
	}
	downloadedFile, err := common.DownloadToTempDir(downloadLink)
	if err != nil {
		return "", errors.New(fmt.Sprintf("Failed to download File %s: %s", downloadLink, err.Error()))
	}
	return downloadedFile, err
}

func getInstallDescription(plugin string) (*installDescription, error) {
	installJson, err := getPluginInstallJson(plugin)
	if err != nil {
		return nil, err
	}
	InstallJsonContents, readErr := common.ReadFileContents(installJson)
	if readErr != nil {
		return nil, readErr
	}
	installDescription := &installDescription{}
	if err = json.Unmarshal([]byte(InstallJsonContents), installDescription); err != nil {
		return nil, err
	}
	return installDescription, nil
}

func getPluginInstallJson(plugin string) (string, error) {
	versionInstallDescriptionJsonFile := plugin + "-install.json"
	versionInstallDescriptionJsonUrl := constructPluginInstallJsonUrl(plugin)

	downloadedFile, downloadErr := common.DownloadToTempDir(versionInstallDescriptionJsonUrl)
	if downloadErr != nil {
		return "", errors.New(fmt.Sprintf("Could not find %s file. Check install name and version. %s", versionInstallDescriptionJsonFile, downloadErr.Error()))
	}
	return downloadedFile, nil
}

func constructPluginInstallJsonUrl(plugin string) string {
	installJsonFile := plugin + "-install.json"
	return fmt.Sprintf("%s/%s", gaugeRepositoryUrl, installJsonFile)
}

func (installDesc *installDescription) getVersion(version string) (*versionInstallDescription, error) {
	for _, versionInstallDescription := range installDesc.Versions {
		if versionInstallDescription.Version == version {
			return &versionInstallDescription, nil
		}
	}
	return nil, errors.New("Could not find install description for Version " + version)
}

func (installDesc *installDescription) getLatestCompatibleVersionTo(version *version) (*versionInstallDescription, error) {
	installDesc.sortVersionInstallDescriptions()
	for _, versionInstallDesc := range installDesc.Versions {
		if err := checkCompatiblity(version, &versionInstallDesc.GaugeVersionSupport); err == nil {
			return &versionInstallDesc, nil
		}
	}
	return nil, errors.New(fmt.Sprintf("Compatible version to %s not found", version))

}

func (installDescription *installDescription) sortVersionInstallDescriptions() {
	sort.Sort(ByDecreasingVersion(installDescription.Versions))
}

func checkCompatiblity(version *version, versionSupport *versionSupport) error {
	minSupportVersion, err := parseVersion(versionSupport.Minimum)
	if err != nil {
		return errors.New(fmt.Sprintf("Invalid minimum support version %s. : %s. ", versionSupport.Minimum, err))
	}
	if versionSupport.Maximum != "" {
		maxSupportVersion, err := parseVersion(versionSupport.Maximum)
		if err != nil {
			return errors.New(fmt.Sprintf("Invalid maximum support version %s. : %s. ", versionSupport.Maximum, err))
		}
		if version.isBetween(minSupportVersion, maxSupportVersion) {
			return nil
		} else {
			return errors.New(fmt.Sprintf("Version %s is not between %s and %s", version, minSupportVersion, maxSupportVersion))
		}
	}

	if minSupportVersion.isLesserThanEqualTo(version) {
		return nil
	}
	return errors.New(fmt.Sprintf("Incompatible version. Minimum support version %s is higher than current version %s", minSupportVersion, version))
}

type ByDecreasingVersion []versionInstallDescription

func (a ByDecreasingVersion) Len() int      { return len(a) }
func (a ByDecreasingVersion) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByDecreasingVersion) Less(i, j int) bool {
	version1, _ := parseVersion(a[i].Version)
	version2, _ := parseVersion(a[j].Version)
	return version1.isGreaterThan(version2)
}
<|endoftext|>


<commit_before>package types

import (
	"testing"
	"time"
)

/*
	Проверяет функции ToDateTime(), String() и StringToDateTime()
*/
func TestDateTimeStringConversion(t *testing.T) {
	timeMonthAgo := time.Now().AddDate(0, -1, 0)
	dateTime := ToDateTime(timeMonthAgo)
	dateTimeString := dateTime.String()

	dateTimeFromString, err := StringToDateTime(dateTimeString)
	if err != nil {
		t.Fatal(err)
	}

	expectedString := dateTimeFromString.String()
	if expectedString != dateTimeString {
		t.Fatalf("Ошибка %v. Ожидалось %v, получено %v", err, expectedString, dateTimeString)
	}
}

/*
	Проверяет функции ToDate(), String() и StringToDate()
*/
func TestDateStringConversion(t *testing.T) {
	timeMonthAgo := time.Now().AddDate(0, -1, 0)
	date := ToDate(timeMonthAgo)
	dateString := date.String()

	dateFromString, err := StringToDate(dateString)
	if err != nil {
		t.Fatal(err)
	}

	expectedString := dateFromString.String()
	if expectedString != dateString {
		t.Fatalf("Ошибка %v. Ожидалось %v, получено %v", err, expectedString, dateString)
	}
}

func TestDaysBefore(t *testing.T) {
	dateToday := DateNow()
	dateYesterday := ToDate(time.Now().AddDate(0, 0, -1))
	dateTomorrow := ToDate(time.Now().AddDate(0, 0, 1))

	if d := dateToday.DaysBefore(dateToday); d != 0 {
		t.Fatalf("Ожидалось 0, получено %d дней", d)
	}

	if d := dateToday.DaysBefore(dateYesterday); d != -1 {
		t.Fatalf("Ожидалось -1, получено %d дней", d)
	}

	if d := dateToday.DaysBefore(dateTomorrow); d != 1 {
		t.Fatalf("Ожидалось 1, получено %d дней", d)
	}

	if d := dateYesterday.DaysBefore(dateTomorrow); d != 2 {
		t.Fatalf("Ожидалось 2, получено %d дней", d)
	}
}

func TestStringToDateTimeHMS(t *testing.T) {
	s := "2015-07-30"
	dt, err := StringDateToDateTimeHMS(s, 5, 6, 7)
	if err != nil {
		t.Fatal(err)
	}
	sExpected := "2015-07-30 05:06:07"
	sReceived := dt.String()
	if sExpected != sReceived {
		t.Fatalf("Ожидалось получить строку %s, получена строка %s", sExpected, sReceived)
	}
}

func TestNeverDate(t *testing.T) {
	nd := NeverDate()
	sExpected := "1990-01-01"
	sReceived := nd.String()
	if sExpected != sReceived {
		t.Fatalf("Ожидалось получить строку %s, получена строка %s", sExpected, sReceived)
	}
}

func TestNeverTime(t *testing.T) {
	ndt := NeverTime()
	sExpected := "1990-01-01 00:00:00"
	sReceived := ndt.String()
	if sExpected != sReceived {
		t.Fatalf("Ожидалось получить строку %s, получена строка %s", sExpected, sReceived)
	}
}

/*
	Проверяет возврат текущих даты, даты-времени и
	преобразование Date -> DateTime с учётом HMS
*/
func TestNow(t *testing.T) {
	dateTimeNow := DateTimeNow()
	dateNow := dateTimeNow.ConvertToDate()
	dateTimeAfterConversion := dateNow.ConvertToDateTimeHMS(
		dateTimeNow.Hour(), dateTimeNow.Minute(), dateTimeNow.Second())
	dateTimeAfterConversion.Layout = DateTimeLayout

	sExpected := dateTimeNow.String()
	sReceived := dateTimeAfterConversion.String()
	if sExpected != sReceived {
		t.Fatalf("Ожидалось получить строку %s, получена строка %s", sExpected, sReceived)
	}
}

func TestDateTimeTodayHMS(t *testing.T) {
	dt := DateTimeTodayHMS(23, 59, 59)
	sExpected := DateNow().String() + " 23:59:59"
	sReceived := dt.String()
	if sExpected != sReceived {
		t.Fatalf("Ожидалось получить строку %s, получена строка %s", sExpected, sReceived)
	}
}

func TestDateSetDefaultLayoutIfEmpty(t *testing.T) {
	d := DateNow()
	d.Layout = ""
	d.setDefaultLayoutIfEmpty()
	if d.Layout != DateLayout {
		t.Fatal("Установлен неправильный Layout")
	}
}

func TestDateTimeSetDefaultLayoutIfEmpty(t *testing.T) {
	dt := DateTimeNow()
	dt.Layout = ""
	dt.setDefaultLayoutIfEmpty()
	if dt.Layout != DateTimeLayout {
		t.Fatal("Установлен неправильный Layout")
	}
}
<commit_msg>Тест на After, Before, Between для Date<commit_after>package types

import (
	"testing"
	"time"
)

/*
	Проверяет функции ToDateTime(), String() и StringToDateTime()
*/
func TestDateTimeStringConversion(t *testing.T) {
	timeMonthAgo := time.Now().AddDate(0, -1, 0)
	dateTime := ToDateTime(timeMonthAgo)
	dateTimeString := dateTime.String()

	dateTimeFromString, err := StringToDateTime(dateTimeString)
	if err != nil {
		t.Fatal(err)
	}

	expectedString := dateTimeFromString.String()
	if expectedString != dateTimeString {
		t.Fatalf("Ошибка %v. Ожидалось %v, получено %v", err, expectedString, dateTimeString)
	}
}

/*
	Проверяет функции ToDate(), String() и StringToDate()
*/
func TestDateStringConversion(t *testing.T) {
	timeMonthAgo := time.Now().AddDate(0, -1, 0)
	date := ToDate(timeMonthAgo)
	dateString := date.String()

	dateFromString, err := StringToDate(dateString)
	if err != nil {
		t.Fatal(err)
	}

	expectedString := dateFromString.String()
	if expectedString != dateString {
		t.Fatalf("Ошибка %v. Ожидалось %v, получено %v", err, expectedString, dateString)
	}
}

func TestDaysBefore(t *testing.T) {
	dateToday := DateNow()
	dateYesterday := ToDate(time.Now().AddDate(0, 0, -1))
	dateTomorrow := ToDate(time.Now().AddDate(0, 0, 1))

	if d := dateToday.DaysBefore(dateToday); d != 0 {
		t.Fatalf("Ожидалось 0, получено %d дней", d)
	}

	if d := dateToday.DaysBefore(dateYesterday); d != -1 {
		t.Fatalf("Ожидалось -1, получено %d дней", d)
	}

	if d := dateToday.DaysBefore(dateTomorrow); d != 1 {
		t.Fatalf("Ожидалось 1, получено %d дней", d)
	}

	if d := dateYesterday.DaysBefore(dateTomorrow); d != 2 {
		t.Fatalf("Ожидалось 2, получено %d дней", d)
	}
}

func TestStringToDateTimeHMS(t *testing.T) {
	s := "2015-07-30"
	dt, err := StringDateToDateTimeHMS(s, 5, 6, 7)
	if err != nil {
		t.Fatal(err)
	}
	sExpected := "2015-07-30 05:06:07"
	sReceived := dt.String()
	if sExpected != sReceived {
		t.Fatalf("Ожидалось получить строку %s, получена строка %s", sExpected, sReceived)
	}
}

func TestNeverDate(t *testing.T) {
	nd := NeverDate()
	sExpected := "1990-01-01"
	sReceived := nd.String()
	if sExpected != sReceived {
		t.Fatalf("Ожидалось получить строку %s, получена строка %s", sExpected, sReceived)
	}
}

func TestNeverTime(t *testing.T) {
	ndt := NeverTime()
	sExpected := "1990-01-01 00:00:00"
	sReceived := ndt.String()
	if sExpected != sReceived {
		t.Fatalf("Ожидалось получить строку %s, получена строка %s", sExpected, sReceived)
	}
}

/*
	Проверяет возврат текущих даты, даты-времени и
	преобразование Date -> DateTime с учётом HMS
*/
func TestNow(t *testing.T) {
	dateTimeNow := DateTimeNow()
	dateNow := dateTimeNow.ConvertToDate()
	dateTimeAfterConversion := dateNow.ConvertToDateTimeHMS(
		dateTimeNow.Hour(), dateTimeNow.Minute(), dateTimeNow.Second())
	dateTimeAfterConversion.Layout = DateTimeLayout

	sExpected := dateTimeNow.String()
	sReceived := dateTimeAfterConversion.String()
	if sExpected != sReceived {
		t.Fatalf("Ожидалось получить строку %s, получена строка %s", sExpected, sReceived)
	}
}

func TestDateTimeTodayHMS(t *testing.T) {
	dt := DateTimeTodayHMS(23, 59, 59)
	sExpected := DateNow().String() + " 23:59:59"
	sReceived := dt.String()
	if sExpected != sReceived {
		t.Fatalf("Ожидалось получить строку %s, получена строка %s", sExpected, sReceived)
	}
}

func TestDateSetDefaultLayoutIfEmpty(t *testing.T) {
	d := DateNow()
	d.Layout = ""
	d.setDefaultLayoutIfEmpty()
	if d.Layout != DateLayout {
		t.Fatal("Установлен неправильный Layout")
	}
}

func TestDateTimeSetDefaultLayoutIfEmpty(t *testing.T) {
	dt := DateTimeNow()
	dt.Layout = ""
	dt.setDefaultLayoutIfEmpty()
	if dt.Layout != DateTimeLayout {
		t.Fatal("Установлен неправильный Layout")
	}
}

func TestDateBeforeAfterBetween(t *testing.T) {
	dateToday := DateNow()
	dateYesterday := ToDate(time.Now().AddDate(0, 0, -1))
	dateTomorrow := ToDate(time.Now().AddDate(0, 0, 1))

	if dateToday.Before(dateToday) {
		t.Fatal("Сегодня не может быть раньше чем сегодня")
	}
	if dateToday.After(dateToday) {
		t.Fatal("Сегодня не может быть позже чем сегодня")
	}
	if dateToday.Between(dateToday, dateTomorrow) {
		t.Fatal("Сегодня не может быть между сегодня и завтра")
	}
	if dateToday.Between(dateYesterday, dateToday) {
		t.Fatal("Сегодня не может быть между вчера и сегодня")
	}

	if dateToday.Before(dateYesterday) {
		t.Fatal("Сегодня не может быть раньше чем вчера")
	}
	if dateYesterday.After(dateToday) {
		t.Fatal("Вчера не может быть позже чем сегодня")
	}
	if dateYesterday.Between(dateToday, dateTomorrow) {
		t.Fatal("Вчера не может быть между сегодня и завтра")
	}

	if !dateToday.Before(dateTomorrow) {
		t.Fatal("Сегодня должно быть раньше чем завтра")
	}
	if !dateToday.After(dateYesterday) {
		t.Fatal("Сегодня должно быть позже чем вчера")
	}
	if !dateToday.Between(dateYesterday, dateTomorrow) {
		t.Fatal("Сегодня должно быть между вчера и завтра")
	}
}
<|endoftext|>


<commit_before>package proxy

import (
	"net"
	"strconv"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

type packetData []byte

var _ = PDescribe("UDP Proxy", func() {
	var serverPort int

	BeforeEach(func() {
		serverPort = 7331
	})

	It("sets up the UDPProxy", func() {
		proxy, err := NewUDPProxy(13370, "localhost", serverPort, nil, nil, 0, 0)
		Expect(err).ToNot(HaveOccurred())
		Expect(proxy.clientDict).To(HaveLen(0))

		// check that port 13370 is in use
		addr, err := net.ResolveUDPAddr("udp", ":13370")
		Expect(err).ToNot(HaveOccurred())
		_, err = net.ListenUDP("udp", addr)
		Expect(err).To(HaveOccurred())
		Expect(err).To(MatchError("listen udp :13370: bind: address already in use"))

		proxy.Stop() // stopping is tested in the next test
	})

	It("stops the UDPProxy", func() {
		proxy, err := NewUDPProxy(13371, "localhost", serverPort, nil, nil, 0, 0)
		Expect(err).ToNot(HaveOccurred())
		proxy.Stop()

		// check that port 13370 is not in use anymore
		addr, err := net.ResolveUDPAddr("udp", ":13371")
		Expect(err).ToNot(HaveOccurred())
		ln, err := net.ListenUDP("udp", addr)
		Expect(err).ToNot(HaveOccurred())
		ln.Close()
	})

	Context("Proxy tests", func() {
		var serverConn *net.UDPConn
		var serverReceivedPackets []packetData
		var serverNumPacketsSent int
		var clientConn *net.UDPConn
		var proxy *UDPProxy

		BeforeEach(func() {
			var err error
			serverReceivedPackets = serverReceivedPackets[:0]
			serverNumPacketsSent = 0

			// setup a UDP server on port 7331
			serverAddr, err := net.ResolveUDPAddr("udp", ":"+strconv.Itoa(serverPort))
			Expect(err).ToNot(HaveOccurred())
			serverConn, err = net.ListenUDP("udp", serverAddr)
			Expect(err).ToNot(HaveOccurred())

			proxyAddr, err := net.ResolveUDPAddr("udp", ":10001")
			Expect(err).ToNot(HaveOccurred())

			go func() {
				defer GinkgoRecover()

				for {
					buf := make([]byte, 1500)
					n, addr, err2 := serverConn.ReadFromUDP(buf)
					if err2 != nil {
						return
					}
					data := buf[0:n]
					serverReceivedPackets = append(serverReceivedPackets, packetData(data))

					// echo each packet received back to the client
					serverConn.WriteToUDP(data, addr)
					serverNumPacketsSent++
				}
			}()

			// setup the client
			localAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0")
			Expect(err).ToNot(HaveOccurred())
			clientConn, err = net.DialUDP("udp", localAddr, proxyAddr)
			Expect(err).ToNot(HaveOccurred())
		})

		AfterEach(func() {
			proxy.Stop()
			serverConn.Close()
			time.Sleep(time.Millisecond)
		})

		Context("no packet drop", func() {
			BeforeEach(func() {
				// setup the proxy
				var err error
				proxy, err = NewUDPProxy(10001, "localhost", serverPort, nil, nil, 0, 0)
				Expect(err).ToNot(HaveOccurred())
			})

			It("relays packets from the client to the server", func() {
				_, err := clientConn.Write([]byte("foobar"))
				Expect(err).ToNot(HaveOccurred())
				time.Sleep(time.Millisecond)
				Expect(proxy.clientDict).To(HaveLen(1))
				var key string
				var conn *connection
				for key, conn = range proxy.clientDict {
					Expect(conn.incomingPacketCounter).To(Equal(PacketNumber(1)))
				}
				_, err = clientConn.Write([]byte("decafbad"))
				Expect(err).ToNot(HaveOccurred())
				time.Sleep(time.Millisecond)
				Expect(proxy.clientDict).To(HaveLen(1))
				Expect(proxy.clientDict[key].incomingPacketCounter).To(Equal(PacketNumber(2)))
				Expect(serverReceivedPackets).To(HaveLen(2))
				Expect(serverReceivedPackets[0]).To(Equal(packetData("foobar")))
				Expect(serverReceivedPackets[1]).To(Equal(packetData("decafbad")))
			})

			It("relays packets from the server to the client", func() {
				_, err := clientConn.Write([]byte("foobar"))
				Expect(err).ToNot(HaveOccurred())
				time.Sleep(time.Millisecond)
				Expect(proxy.clientDict).To(HaveLen(1))
				var key string
				var conn *connection
				for key, conn = range proxy.clientDict {
					Expect(conn.outgoingPacketCounter).To(Equal(PacketNumber(1)))
				}
				_, err = clientConn.Write([]byte("decafbad"))
				Expect(err).ToNot(HaveOccurred())
				time.Sleep(time.Millisecond)
				Expect(proxy.clientDict).To(HaveLen(1))
				Expect(proxy.clientDict[key].outgoingPacketCounter).To(Equal(PacketNumber(2)))

				var clientReceivedPackets []packetData

				// receive the packets echoed by the server on client side
				go func() {
					defer GinkgoRecover()

					for {
						buf := make([]byte, 1500)
						n, _, err2 := clientConn.ReadFromUDP(buf)
						if err2 != nil {
							return
						}
						data := buf[0:n]
						clientReceivedPackets = append(clientReceivedPackets, packetData(data))
					}
				}()

				time.Sleep(time.Millisecond)
				Expect(serverReceivedPackets).To(HaveLen(2))
				Expect(serverNumPacketsSent).To(Equal(2))
				Expect(clientReceivedPackets).To(HaveLen(2))
				Expect(clientReceivedPackets[0]).To(Equal(packetData("foobar")))
				Expect(clientReceivedPackets[1]).To(Equal(packetData("decafbad")))
			})
		})

		Context("Drop Callbacks", func() {
			It("drops incoming packets", func() {
				dropper := func(p PacketNumber) bool {
					return p%2 == 0
				}

				var err error
				proxy, err = NewUDPProxy(10001, "localhost", serverPort, dropper, nil, 0, 0)
				Expect(err).ToNot(HaveOccurred())

				for i := 1; i <= 6; i++ {
					_, err := clientConn.Write([]byte("foobar" + strconv.Itoa(i)))
					Expect(err).ToNot(HaveOccurred())
					time.Sleep(time.Millisecond)
				}
				Expect(serverReceivedPackets).To(HaveLen(3))
				Expect(serverReceivedPackets[0]).To(Equal(packetData("foobar1")))
				Expect(serverReceivedPackets[1]).To(Equal(packetData("foobar3")))
				Expect(serverReceivedPackets[2]).To(Equal(packetData("foobar5")))
			})

			It("drops outgoing packets", func() {
				dropper := func(p PacketNumber) bool {
					return p%2 == 0
				}

				var err error
				proxy, err = NewUDPProxy(10001, "localhost", serverPort, nil, dropper, 0, 0)
				Expect(err).ToNot(HaveOccurred())

				for i := 1; i <= 6; i++ {
					_, err := clientConn.Write([]byte("foobar" + strconv.Itoa(i)))
					Expect(err).ToNot(HaveOccurred())
					time.Sleep(time.Millisecond)
				}

				var clientReceivedPackets []packetData

				// receive the packets echoed by the server on client side
				go func() {
					defer GinkgoRecover()

					for {
						buf := make([]byte, 1500)
						n, _, err2 := clientConn.ReadFromUDP(buf)
						if err2 != nil {
							return
						}
						data := buf[0:n]
						clientReceivedPackets = append(clientReceivedPackets, packetData(data))
					}
				}()

				time.Sleep(time.Millisecond)
				Expect(clientReceivedPackets).To(HaveLen(3))
				Expect(clientReceivedPackets[0]).To(Equal(packetData("foobar1")))
				Expect(clientReceivedPackets[1]).To(Equal(packetData("foobar3")))
				Expect(clientReceivedPackets[2]).To(Equal(packetData("foobar5")))
			})
		})
	})
})
<commit_msg>fix flaky UDP Proxy tests<commit_after>package proxy

import (
	"net"
	"strconv"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

type packetData []byte

var _ = Describe("UDP Proxy", func() {
	var serverPort int

	BeforeEach(func() {
		serverPort = 7331
	})

	It("sets up the UDPProxy", func() {
		proxy, err := NewUDPProxy(13370, "localhost", serverPort, nil, nil, 0, 0)
		Expect(err).ToNot(HaveOccurred())
		Expect(proxy.clientDict).To(HaveLen(0))

		// check that port 13370 is in use
		addr, err := net.ResolveUDPAddr("udp", ":13370")
		Expect(err).ToNot(HaveOccurred())
		_, err = net.ListenUDP("udp", addr)
		Expect(err).To(HaveOccurred())
		Expect(err).To(MatchError("listen udp :13370: bind: address already in use"))

		proxy.Stop() // stopping is tested in the next test
	})

	It("stops the UDPProxy", func() {
		proxy, err := NewUDPProxy(13371, "localhost", serverPort, nil, nil, 0, 0)
		Expect(err).ToNot(HaveOccurred())
		proxy.Stop()

		// check that port 13370 is not in use anymore
		addr, err := net.ResolveUDPAddr("udp", ":13371")
		Expect(err).ToNot(HaveOccurred())
		ln, err := net.ListenUDP("udp", addr)
		Expect(err).ToNot(HaveOccurred())
		ln.Close()
	})

	Context("Proxy tests", func() {
		var serverConn *net.UDPConn
		var serverReceivedPackets []packetData
		var serverNumPacketsSent int
		var clientConn *net.UDPConn
		var proxy *UDPProxy

		BeforeEach(func() {
			var err error
			serverReceivedPackets = serverReceivedPackets[:0]
			serverNumPacketsSent = 0

			// setup a UDP server on port 7331
			serverAddr, err := net.ResolveUDPAddr("udp", ":"+strconv.Itoa(serverPort))
			Expect(err).ToNot(HaveOccurred())
			serverConn, err = net.ListenUDP("udp", serverAddr)
			Expect(err).ToNot(HaveOccurred())

			proxyAddr, err := net.ResolveUDPAddr("udp", ":10001")
			Expect(err).ToNot(HaveOccurred())

			go func() {
				defer GinkgoRecover()

				for {
					buf := make([]byte, 1500)
					n, addr, err2 := serverConn.ReadFromUDP(buf)
					if err2 != nil {
						return
					}
					data := buf[0:n]
					serverReceivedPackets = append(serverReceivedPackets, packetData(data))

					// echo each packet received back to the client
					serverConn.WriteToUDP(data, addr)
					serverNumPacketsSent++
				}
			}()

			// setup the client
			localAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0")
			Expect(err).ToNot(HaveOccurred())
			clientConn, err = net.DialUDP("udp", localAddr, proxyAddr)
			Expect(err).ToNot(HaveOccurred())
		})

		AfterEach(func() {
			proxy.Stop()
			serverConn.Close()
			time.Sleep(time.Millisecond)
		})

		Context("no packet drop", func() {
			BeforeEach(func() {
				// setup the proxy
				var err error
				proxy, err = NewUDPProxy(10001, "localhost", serverPort, nil, nil, 0, 0)
				Expect(err).ToNot(HaveOccurred())
			})

			It("relays packets from the client to the server", func() {
				_, err := clientConn.Write([]byte("foobar"))
				Expect(err).ToNot(HaveOccurred())

				Eventually(func() map[string]*connection { return proxy.clientDict }).Should(HaveLen(1))
				var key string
				var conn *connection
				for key, conn = range proxy.clientDict {
					Expect(conn.incomingPacketCounter).To(Equal(PacketNumber(1)))
				}
				_, err = clientConn.Write([]byte("decafbad"))
				Expect(err).ToNot(HaveOccurred())
				Expect(proxy.clientDict).To(HaveLen(1))
				Eventually(func() []packetData { return serverReceivedPackets }).Should(HaveLen(2))
				Expect(proxy.clientDict[key].incomingPacketCounter).To(Equal(PacketNumber(2)))
				Expect(serverReceivedPackets[0]).To(Equal(packetData("foobar")))
				Expect(serverReceivedPackets[1]).To(Equal(packetData("decafbad")))
			})

			It("relays packets from the server to the client", func() {
				_, err := clientConn.Write([]byte("foobar"))
				Expect(err).ToNot(HaveOccurred())
				Eventually(func() map[string]*connection { return proxy.clientDict }).Should(HaveLen(1))
				var key string
				var conn *connection
				for key, conn = range proxy.clientDict {
					Expect(conn.outgoingPacketCounter).To(Equal(PacketNumber(1)))
				}
				_, err = clientConn.Write([]byte("decafbad"))
				Expect(err).ToNot(HaveOccurred())

				Expect(proxy.clientDict).To(HaveLen(1))
				Eventually(func() PacketNumber { return proxy.clientDict[key].outgoingPacketCounter }).Should(Equal(PacketNumber(2)))

				var clientReceivedPackets []packetData

				// receive the packets echoed by the server on client side
				go func() {
					defer GinkgoRecover()

					for {
						buf := make([]byte, 1500)
						n, _, err2 := clientConn.ReadFromUDP(buf)
						if err2 != nil {
							return
						}
						data := buf[0:n]
						clientReceivedPackets = append(clientReceivedPackets, packetData(data))
					}
				}()

				Eventually(func() []packetData { return serverReceivedPackets }).Should(HaveLen(2))
				Expect(serverReceivedPackets).To(HaveLen(2))
				Expect(serverNumPacketsSent).To(Equal(2))
				Eventually(func() []packetData { return clientReceivedPackets }).Should(HaveLen(2))
				Expect(clientReceivedPackets[0]).To(Equal(packetData("foobar")))
				Expect(clientReceivedPackets[1]).To(Equal(packetData("decafbad")))
			})
		})

		Context("Drop Callbacks", func() {
			It("drops incoming packets", func() {
				dropper := func(p PacketNumber) bool {
					return p%2 == 0
				}

				var err error
				proxy, err = NewUDPProxy(10001, "localhost", serverPort, dropper, nil, 0, 0)
				Expect(err).ToNot(HaveOccurred())

				for i := 1; i <= 6; i++ {
					_, err := clientConn.Write([]byte("foobar" + strconv.Itoa(i)))
					Expect(err).ToNot(HaveOccurred())
					time.Sleep(time.Millisecond)
				}
				Eventually(func() []packetData { return serverReceivedPackets }).Should(HaveLen(3))
				Expect(serverReceivedPackets[0]).To(Equal(packetData("foobar1")))
				Expect(serverReceivedPackets[1]).To(Equal(packetData("foobar3")))
				Expect(serverReceivedPackets[2]).To(Equal(packetData("foobar5")))
			})

			It("drops outgoing packets", func() {
				dropper := func(p PacketNumber) bool {
					return p%2 == 0
				}

				var err error
				proxy, err = NewUDPProxy(10001, "localhost", serverPort, nil, dropper, 0, 0)
				Expect(err).ToNot(HaveOccurred())

				for i := 1; i <= 6; i++ {
					_, err := clientConn.Write([]byte("foobar" + strconv.Itoa(i)))
					Expect(err).ToNot(HaveOccurred())
					time.Sleep(time.Millisecond)
				}

				var clientReceivedPackets []packetData

				// receive the packets echoed by the server on client side
				go func() {
					defer GinkgoRecover()

					for {
						buf := make([]byte, 1500)
						n, _, err2 := clientConn.ReadFromUDP(buf)
						if err2 != nil {
							return
						}
						data := buf[0:n]
						clientReceivedPackets = append(clientReceivedPackets, packetData(data))
					}
				}()

				Eventually(func() []packetData { return clientReceivedPackets }).Should(HaveLen(3))
				Expect(clientReceivedPackets[0]).To(Equal(packetData("foobar1")))
				Expect(clientReceivedPackets[1]).To(Equal(packetData("foobar3")))
				Expect(clientReceivedPackets[2]).To(Equal(packetData("foobar5")))
			})
		})
	})
})
<|endoftext|>


<commit_before>package aadgraph

import (
	"context"
	"encoding/base64"
	"encoding/hex"
	"encoding/pem"
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac"
	"github.com/Azure/go-autorest/autorest/date"
	"github.com/hashicorp/go-uuid"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"

	"github.com/hashicorp/terraform-provider-azuread/internal/utils"
	"github.com/hashicorp/terraform-provider-azuread/internal/validate"
)

// valid types are `application` and `service_principal`
func CertificateResourceSchema(idAttribute string) map[string]*schema.Schema {
	return map[string]*schema.Schema{
		idAttribute: {
			Type:             schema.TypeString,
			Required:         true,
			ForceNew:         true,
			ValidateDiagFunc: validate.UUID,
		},

		"key_id": {
			Type:             schema.TypeString,
			Optional:         true,
			Computed:         true,
			ForceNew:         true,
			ValidateDiagFunc: validate.UUID,
		},

		"type": {
			Type:     schema.TypeString,
			Optional: true,
			ForceNew: true,
			ValidateFunc: validation.StringInSlice([]string{
				"AsymmetricX509Cert",
				"Symmetric",
			}, false),
		},

		"encoding": {
			Type:     schema.TypeString,
			Optional: true,
			ForceNew: true,
			Default:  "pem",
			ValidateFunc: validation.StringInSlice([]string{
				"base64",
				"hex",
				"pem",
			}, false),
		},

		"value": {
			Type:      schema.TypeString,
			Required:  true,
			ForceNew:  true,
			Sensitive: true,
		},

		"start_date": {
			Type:         schema.TypeString,
			Optional:     true,
			Computed:     true,
			ForceNew:     true,
			ValidateFunc: validation.IsRFC3339Time,
		},

		"end_date": {
			Type:          schema.TypeString,
			Optional:      true,
			Computed:      true,
			ForceNew:      true,
			ConflictsWith: []string{"end_date_relative"},
			ValidateFunc:  validation.IsRFC3339Time,
		},

		"end_date_relative": {
			Type:             schema.TypeString,
			Optional:         true,
			ForceNew:         true,
			ConflictsWith:    []string{"end_date"},
			ValidateDiagFunc: validate.NoEmptyStrings,
		},
	}
}

// valid types are `application` and `service_principal`
func PasswordResourceSchema(idAttribute string) map[string]*schema.Schema {
	return map[string]*schema.Schema{
		idAttribute: {
			Type:             schema.TypeString,
			Required:         true,
			ForceNew:         true,
			ValidateDiagFunc: validate.UUID,
		},

		"key_id": {
			Type:             schema.TypeString,
			Optional:         true,
			Computed:         true,
			ForceNew:         true,
			ValidateDiagFunc: validate.UUID,
		},

		"description": {
			Type:     schema.TypeString,
			Optional: true,
			Computed: true,
			ForceNew: true,
		},

		"value": {
			Type:         schema.TypeString,
			Required:     true,
			ForceNew:     true,
			Sensitive:    true,
			ValidateFunc: validation.StringLenBetween(1, 863), // Encrypted secret cannot be empty and can be at most 1024 bytes.
		},

		"start_date": {
			Type:         schema.TypeString,
			Optional:     true,
			Computed:     true,
			ForceNew:     true,
			ValidateFunc: validation.IsRFC3339Time,
		},

		"end_date": {
			Type:         schema.TypeString,
			Optional:     true,
			Computed:     true,
			ForceNew:     true,
			ExactlyOneOf: []string{"end_date_relative"},
			ValidateFunc: validation.IsRFC3339Time,
		},

		"end_date_relative": {
			Type:             schema.TypeString,
			Optional:         true,
			ForceNew:         true,
			ExactlyOneOf:     []string{"end_date"},
			ValidateDiagFunc: validate.NoEmptyStrings,
		},
	}
}

func PasswordCredentialForResource(d *schema.ResourceData) (*graphrbac.PasswordCredential, error) {
	value := d.Get("value").(string)

	// errors should be handled by the validation
	var keyId string
	if v, ok := d.GetOk("key_id"); ok {
		keyId = v.(string)
	} else {
		kid, err := uuid.GenerateUUID()
		if err != nil {
			return nil, err
		}

		keyId = kid
	}

	var endDate time.Time
	if v := d.Get("end_date").(string); v != "" {
		var err error
		endDate, err = time.Parse(time.RFC3339, v)
		if err != nil {
			return nil, CredentialError{str: fmt.Sprintf("Unable to parse the provided end date %q: %+v", v, err), attr: "end_date"}
		}
	} else if v := d.Get("end_date_relative").(string); v != "" {
		d, err := time.ParseDuration(v)
		if err != nil {
			return nil, CredentialError{str: fmt.Sprintf("Unable to parse `end_date_relative` (%q) as a duration", v), attr: "end_date_relative"}
		}
		endDate = time.Now().Add(d)
	} else {
		return nil, CredentialError{str: "One of `end_date` or `end_date_relative` must be specified", attr: "end_date"}
	}

	credential := graphrbac.PasswordCredential{
		KeyID:   utils.String(keyId),
		Value:   utils.String(value),
		EndDate: &date.Time{Time: endDate},
	}

	if v, ok := d.GetOk("display_name"); ok {
		customIdentifier := []byte(v.(string))
		credential.CustomKeyIdentifier = &customIdentifier
	} else if v, ok := d.GetOk("description"); ok {
		customIdentifier := []byte(v.(string))
		credential.CustomKeyIdentifier = &customIdentifier
	}

	if v, ok := d.GetOk("start_date"); ok {
		startDate, err := time.Parse(time.RFC3339, v.(string))
		if err != nil {
			return nil, CredentialError{str: fmt.Sprintf("Unable to parse the provided start date %q: %+v", v, err), attr: "start_date"}
		}
		credential.StartDate = &date.Time{Time: startDate}
	}

	return &credential, nil
}

func PasswordCredentialResultFindByKeyId(creds graphrbac.PasswordCredentialListResult, keyId string) *graphrbac.PasswordCredential {
	var cred *graphrbac.PasswordCredential

	if creds.Value != nil {
		for _, c := range *creds.Value {
			if c.KeyID == nil {
				continue
			}

			if *c.KeyID == keyId {
				cred = &c
				break
			}
		}
	}

	return cred
}

func PasswordCredentialResultAdd(existing graphrbac.PasswordCredentialListResult, cred *graphrbac.PasswordCredential) (*[]graphrbac.PasswordCredential, error) {
	if cred == nil {
		return nil, errors.New("credential to be added is null")
	}

	newCreds := make([]graphrbac.PasswordCredential, 0)

	if existing.Value != nil {
		for _, v := range *existing.Value {
			if v.KeyID == nil {
				continue
			}
			if *v.KeyID == *cred.KeyID {
				return nil, &AlreadyExistsError{"Password Credential", *cred.KeyID}
			}
		}

		newCreds = *existing.Value
	}
	newCreds = append(newCreds, *cred)

	return &newCreds, nil
}

func PasswordCredentialResultRemoveByKeyId(existing graphrbac.PasswordCredentialListResult, keyId string) (*[]graphrbac.PasswordCredential, error) {
	if keyId == "" {
		return nil, errors.New("ID of key to be removed is blank")
	}

	newCreds := make([]graphrbac.PasswordCredential, 0)

	if existing.Value != nil {
		for _, v := range *existing.Value {
			if v.KeyID == nil {
				continue
			}

			if *v.KeyID == keyId {
				continue
			}

			newCreds = append(newCreds, v)
		}
	}

	return &newCreds, nil
}

func WaitForPasswordCredentialReplication(ctx context.Context, keyId string, timeout time.Duration, f func() (graphrbac.PasswordCredentialListResult, error)) (interface{}, error) {
	return (&resource.StateChangeConf{
		Pending:                   []string{"NotFound"},
		Target:                    []string{"Found"},
		Timeout:                   timeout,
		MinTimeout:                1 * time.Second,
		ContinuousTargetOccurence: 10,
		Refresh: func() (interface{}, string, error) {
			creds, err := f()
			if err != nil {
				if utils.ResponseWasNotFound(creds.Response) {
					return creds, "NotFound", nil
				}
				return creds, "Error", fmt.Errorf("unable to retrieve object, received response with status %d: %v", creds.Response.StatusCode, err)
			}

			credential := PasswordCredentialResultFindByKeyId(creds, keyId)
			if credential == nil {
				return creds, "NotFound", nil
			}

			return creds, "Found", nil
		},
	}).WaitForStateContext(ctx)
}

func KeyCredentialForResource(d *schema.ResourceData) (*graphrbac.KeyCredential, error) {
	keyType := d.Get("type").(string)
	value := d.Get("value").(string)

	var encodedValue string
	encoding := d.Get("encoding").(string)
	switch encoding {
	case "base64":
		der, err := base64.StdEncoding.DecodeString(strings.TrimSpace(value))
		if err != nil {
			return nil, fmt.Errorf("failed to decode base64 certificate data")
		}
		block := pem.Block{
			Type:  "CERTIFICATE",
			Bytes: der,
		}
		pemVal := pem.EncodeToMemory(&block)
		if pemVal == nil {
			return nil, fmt.Errorf("failed to PEM-encode certificate")
		}
		encodedValue = base64.StdEncoding.EncodeToString(pemVal)
	case "hex":
		bytesVal := []byte(strings.TrimSpace(value))
		der := make([]byte, hex.DecodedLen(len(bytesVal)))
		_, err := hex.Decode(der, bytesVal)
		if err != nil {
			return nil, fmt.Errorf("failed to decode hexadecimal certificate data: %+v", err)
		}
		block := pem.Block{
			Type:  "CERTIFICATE",
			Bytes: der,
		}
		pemVal := pem.EncodeToMemory(&block)
		if pemVal == nil {
			return nil, fmt.Errorf("failed to PEM-encode certificate")
		}
		encodedValue = base64.StdEncoding.EncodeToString(pemVal)
	case "pem":
		encodedValue = base64.StdEncoding.EncodeToString([]byte(value))
	}

	// errors should be handled by the validation
	var keyId string
	if v, ok := d.GetOk("key_id"); ok {
		keyId = v.(string)
	} else {
		kid, err := uuid.GenerateUUID()
		if err != nil {
			return nil, err
		}

		keyId = kid
	}

	var endDate time.Time
	if v := d.Get("end_date").(string); v != "" {
		var err error
		endDate, err = time.Parse(time.RFC3339, v)
		if err != nil {
			return nil, CredentialError{str: fmt.Sprintf("Unable to parse the provided end date %q: %+v", v, err), attr: "end_date"}
		}
	} else if v := d.Get("end_date_relative").(string); v != "" {
		d, err := time.ParseDuration(v)
		if err != nil {
			return nil, CredentialError{str: fmt.Sprintf("Unable to parse `end_date_relative` (%q) as a duration", v), attr: "end_date_relative"}
		}
		endDate = time.Now().Add(d)
	} else {
		return nil, CredentialError{str: "One of `end_date` or `end_date_relative` must be specified", attr: "end_date"}
	}

	credential := graphrbac.KeyCredential{
		KeyID:   utils.String(keyId),
		Type:    utils.String(keyType),
		Usage:   utils.String("verify"),
		Value:   utils.String(encodedValue),
		EndDate: &date.Time{Time: endDate},
	}

	if v, ok := d.GetOk("start_date"); ok {
		startDate, err := time.Parse(time.RFC3339, v.(string))
		if err != nil {
			return nil, CredentialError{str: fmt.Sprintf("Unable to parse the provided start date %q: %+v", v, err), attr: "start_date"}
		}
		credential.StartDate = &date.Time{Time: startDate}
	}

	return &credential, nil
}

func KeyCredentialResultFindByKeyId(creds graphrbac.KeyCredentialListResult, keyId string) *graphrbac.KeyCredential {
	if creds.Value != nil {
		for _, c := range *creds.Value {
			if c.KeyID == nil {
				continue
			}
			if *c.KeyID == keyId {
				return &c
			}
		}
	}

	return nil
}

func KeyCredentialResultAdd(existing graphrbac.KeyCredentialListResult, cred *graphrbac.KeyCredential) (*[]graphrbac.KeyCredential, error) {
	newCreds := make([]graphrbac.KeyCredential, 0)

	if existing.Value != nil {
		for _, v := range *existing.Value {
			if v.KeyID == nil {
				continue
			}

			if *v.KeyID == *cred.KeyID {
				return nil, &AlreadyExistsError{"Key Credential", *cred.KeyID}
			}
		}

		newCreds = *existing.Value
	}
	newCreds = append(newCreds, *cred)

	return &newCreds, nil
}

func KeyCredentialResultRemoveByKeyId(existing graphrbac.KeyCredentialListResult, keyId string) (*[]graphrbac.KeyCredential, error) {
	if keyId == "" {
		return nil, errors.New("ID of key to be removed is blank")
	}

	newCreds := make([]graphrbac.KeyCredential, 0)

	if existing.Value != nil {
		for _, v := range *existing.Value {
			if v.KeyID == nil {
				continue
			}

			if *v.KeyID == keyId {
				continue
			}

			newCreds = append(newCreds, v)
		}
	}

	return &newCreds, nil
}

func WaitForKeyCredentialReplication(ctx context.Context, keyId string, timeout time.Duration, f func() (graphrbac.KeyCredentialListResult, error)) (interface{}, error) {
	return (&resource.StateChangeConf{
		Pending:                   []string{"NotFound"},
		Target:                    []string{"Found"},
		Timeout:                   timeout,
		MinTimeout:                1 * time.Second,
		ContinuousTargetOccurence: 10,
		Refresh: func() (interface{}, string, error) {
			creds, err := f()
			if err != nil {
				if utils.ResponseWasNotFound(creds.Response) {
					return creds, "NotFound", nil
				}
				return creds, "Error", fmt.Errorf("unable to retrieve object, received response with status %d: %v", creds.Response.StatusCode, err)
			}

			credential := KeyCredentialResultFindByKeyId(creds, keyId)
			if credential == nil {
				return creds, "NotFound", nil
			}

			return creds, "Found", nil
		},
	}).WaitForStateContext(ctx)
}
<commit_msg>Remove unused common schema functions<commit_after>package aadgraph

import (
	"context"
	"encoding/base64"
	"encoding/hex"
	"encoding/pem"
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac"
	"github.com/Azure/go-autorest/autorest/date"
	"github.com/hashicorp/go-uuid"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"

	"github.com/hashicorp/terraform-provider-azuread/internal/utils"
)

func PasswordCredentialForResource(d *schema.ResourceData) (*graphrbac.PasswordCredential, error) {
	value := d.Get("value").(string)

	// errors should be handled by the validation
	var keyId string
	if v, ok := d.GetOk("key_id"); ok {
		keyId = v.(string)
	} else {
		kid, err := uuid.GenerateUUID()
		if err != nil {
			return nil, err
		}

		keyId = kid
	}

	var endDate time.Time
	if v := d.Get("end_date").(string); v != "" {
		var err error
		endDate, err = time.Parse(time.RFC3339, v)
		if err != nil {
			return nil, CredentialError{str: fmt.Sprintf("Unable to parse the provided end date %q: %+v", v, err), attr: "end_date"}
		}
	} else if v := d.Get("end_date_relative").(string); v != "" {
		d, err := time.ParseDuration(v)
		if err != nil {
			return nil, CredentialError{str: fmt.Sprintf("Unable to parse `end_date_relative` (%q) as a duration", v), attr: "end_date_relative"}
		}
		endDate = time.Now().Add(d)
	} else {
		return nil, CredentialError{str: "One of `end_date` or `end_date_relative` must be specified", attr: "end_date"}
	}

	credential := graphrbac.PasswordCredential{
		KeyID:   utils.String(keyId),
		Value:   utils.String(value),
		EndDate: &date.Time{Time: endDate},
	}

	if v, ok := d.GetOk("display_name"); ok {
		customIdentifier := []byte(v.(string))
		credential.CustomKeyIdentifier = &customIdentifier
	} else if v, ok := d.GetOk("description"); ok {
		customIdentifier := []byte(v.(string))
		credential.CustomKeyIdentifier = &customIdentifier
	}

	if v, ok := d.GetOk("start_date"); ok {
		startDate, err := time.Parse(time.RFC3339, v.(string))
		if err != nil {
			return nil, CredentialError{str: fmt.Sprintf("Unable to parse the provided start date %q: %+v", v, err), attr: "start_date"}
		}
		credential.StartDate = &date.Time{Time: startDate}
	}

	return &credential, nil
}

func PasswordCredentialResultFindByKeyId(creds graphrbac.PasswordCredentialListResult, keyId string) *graphrbac.PasswordCredential {
	var cred *graphrbac.PasswordCredential

	if creds.Value != nil {
		for _, c := range *creds.Value {
			if c.KeyID == nil {
				continue
			}

			if *c.KeyID == keyId {
				cred = &c
				break
			}
		}
	}

	return cred
}

func PasswordCredentialResultAdd(existing graphrbac.PasswordCredentialListResult, cred *graphrbac.PasswordCredential) (*[]graphrbac.PasswordCredential, error) {
	if cred == nil {
		return nil, errors.New("credential to be added is null")
	}

	newCreds := make([]graphrbac.PasswordCredential, 0)

	if existing.Value != nil {
		for _, v := range *existing.Value {
			if v.KeyID == nil {
				continue
			}
			if *v.KeyID == *cred.KeyID {
				return nil, &AlreadyExistsError{"Password Credential", *cred.KeyID}
			}
		}

		newCreds = *existing.Value
	}
	newCreds = append(newCreds, *cred)

	return &newCreds, nil
}

func PasswordCredentialResultRemoveByKeyId(existing graphrbac.PasswordCredentialListResult, keyId string) (*[]graphrbac.PasswordCredential, error) {
	if keyId == "" {
		return nil, errors.New("ID of key to be removed is blank")
	}

	newCreds := make([]graphrbac.PasswordCredential, 0)

	if existing.Value != nil {
		for _, v := range *existing.Value {
			if v.KeyID == nil {
				continue
			}

			if *v.KeyID == keyId {
				continue
			}

			newCreds = append(newCreds, v)
		}
	}

	return &newCreds, nil
}

func WaitForPasswordCredentialReplication(ctx context.Context, keyId string, timeout time.Duration, f func() (graphrbac.PasswordCredentialListResult, error)) (interface{}, error) {
	return (&resource.StateChangeConf{
		Pending:                   []string{"NotFound"},
		Target:                    []string{"Found"},
		Timeout:                   timeout,
		MinTimeout:                1 * time.Second,
		ContinuousTargetOccurence: 10,
		Refresh: func() (interface{}, string, error) {
			creds, err := f()
			if err != nil {
				if utils.ResponseWasNotFound(creds.Response) {
					return creds, "NotFound", nil
				}
				return creds, "Error", fmt.Errorf("unable to retrieve object, received response with status %d: %v", creds.Response.StatusCode, err)
			}

			credential := PasswordCredentialResultFindByKeyId(creds, keyId)
			if credential == nil {
				return creds, "NotFound", nil
			}

			return creds, "Found", nil
		},
	}).WaitForStateContext(ctx)
}

func KeyCredentialForResource(d *schema.ResourceData) (*graphrbac.KeyCredential, error) {
	keyType := d.Get("type").(string)
	value := d.Get("value").(string)

	var encodedValue string
	encoding := d.Get("encoding").(string)
	switch encoding {
	case "base64":
		der, err := base64.StdEncoding.DecodeString(strings.TrimSpace(value))
		if err != nil {
			return nil, fmt.Errorf("failed to decode base64 certificate data")
		}
		block := pem.Block{
			Type:  "CERTIFICATE",
			Bytes: der,
		}
		pemVal := pem.EncodeToMemory(&block)
		if pemVal == nil {
			return nil, fmt.Errorf("failed to PEM-encode certificate")
		}
		encodedValue = base64.StdEncoding.EncodeToString(pemVal)
	case "hex":
		bytesVal := []byte(strings.TrimSpace(value))
		der := make([]byte, hex.DecodedLen(len(bytesVal)))
		_, err := hex.Decode(der, bytesVal)
		if err != nil {
			return nil, fmt.Errorf("failed to decode hexadecimal certificate data: %+v", err)
		}
		block := pem.Block{
			Type:  "CERTIFICATE",
			Bytes: der,
		}
		pemVal := pem.EncodeToMemory(&block)
		if pemVal == nil {
			return nil, fmt.Errorf("failed to PEM-encode certificate")
		}
		encodedValue = base64.StdEncoding.EncodeToString(pemVal)
	case "pem":
		encodedValue = base64.StdEncoding.EncodeToString([]byte(value))
	}

	// errors should be handled by the validation
	var keyId string
	if v, ok := d.GetOk("key_id"); ok {
		keyId = v.(string)
	} else {
		kid, err := uuid.GenerateUUID()
		if err != nil {
			return nil, err
		}

		keyId = kid
	}

	var endDate time.Time
	if v := d.Get("end_date").(string); v != "" {
		var err error
		endDate, err = time.Parse(time.RFC3339, v)
		if err != nil {
			return nil, CredentialError{str: fmt.Sprintf("Unable to parse the provided end date %q: %+v", v, err), attr: "end_date"}
		}
	} else if v := d.Get("end_date_relative").(string); v != "" {
		d, err := time.ParseDuration(v)
		if err != nil {
			return nil, CredentialError{str: fmt.Sprintf("Unable to parse `end_date_relative` (%q) as a duration", v), attr: "end_date_relative"}
		}
		endDate = time.Now().Add(d)
	} else {
		return nil, CredentialError{str: "One of `end_date` or `end_date_relative` must be specified", attr: "end_date"}
	}

	credential := graphrbac.KeyCredential{
		KeyID:   utils.String(keyId),
		Type:    utils.String(keyType),
		Usage:   utils.String("verify"),
		Value:   utils.String(encodedValue),
		EndDate: &date.Time{Time: endDate},
	}

	if v, ok := d.GetOk("start_date"); ok {
		startDate, err := time.Parse(time.RFC3339, v.(string))
		if err != nil {
			return nil, CredentialError{str: fmt.Sprintf("Unable to parse the provided start date %q: %+v", v, err), attr: "start_date"}
		}
		credential.StartDate = &date.Time{Time: startDate}
	}

	return &credential, nil
}

func KeyCredentialResultFindByKeyId(creds graphrbac.KeyCredentialListResult, keyId string) *graphrbac.KeyCredential {
	if creds.Value != nil {
		for _, c := range *creds.Value {
			if c.KeyID == nil {
				continue
			}
			if *c.KeyID == keyId {
				return &c
			}
		}
	}

	return nil
}

func KeyCredentialResultAdd(existing graphrbac.KeyCredentialListResult, cred *graphrbac.KeyCredential) (*[]graphrbac.KeyCredential, error) {
	newCreds := make([]graphrbac.KeyCredential, 0)

	if existing.Value != nil {
		for _, v := range *existing.Value {
			if v.KeyID == nil {
				continue
			}

			if *v.KeyID == *cred.KeyID {
				return nil, &AlreadyExistsError{"Key Credential", *cred.KeyID}
			}
		}

		newCreds = *existing.Value
	}
	newCreds = append(newCreds, *cred)

	return &newCreds, nil
}

func KeyCredentialResultRemoveByKeyId(existing graphrbac.KeyCredentialListResult, keyId string) (*[]graphrbac.KeyCredential, error) {
	if keyId == "" {
		return nil, errors.New("ID of key to be removed is blank")
	}

	newCreds := make([]graphrbac.KeyCredential, 0)

	if existing.Value != nil {
		for _, v := range *existing.Value {
			if v.KeyID == nil {
				continue
			}

			if *v.KeyID == keyId {
				continue
			}

			newCreds = append(newCreds, v)
		}
	}

	return &newCreds, nil
}

func WaitForKeyCredentialReplication(ctx context.Context, keyId string, timeout time.Duration, f func() (graphrbac.KeyCredentialListResult, error)) (interface{}, error) {
	return (&resource.StateChangeConf{
		Pending:                   []string{"NotFound"},
		Target:                    []string{"Found"},
		Timeout:                   timeout,
		MinTimeout:                1 * time.Second,
		ContinuousTargetOccurence: 10,
		Refresh: func() (interface{}, string, error) {
			creds, err := f()
			if err != nil {
				if utils.ResponseWasNotFound(creds.Response) {
					return creds, "NotFound", nil
				}
				return creds, "Error", fmt.Errorf("unable to retrieve object, received response with status %d: %v", creds.Response.StatusCode, err)
			}

			credential := KeyCredentialResultFindByKeyId(creds, keyId)
			if credential == nil {
				return creds, "NotFound", nil
			}

			return creds, "Found", nil
		},
	}).WaitForStateContext(ctx)
}
<|endoftext|>


<commit_before>package providers

import (
	"context"
	"net/http"
	"net/url"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/gregjones/httpcache"
	"github.com/gregjones/httpcache/diskcache"
	"github.com/pkg/errors"
	"github.com/wyattjoh/ims/internal/image/provider"
)

// GetUnderlyingTransport checks the url as some clients have specific
// requirements of the underlying transport.
func GetUnderlyingTransport(ctx context.Context, originURL *url.URL) (http.RoundTripper, error) {
	switch originURL.Scheme {
	case "gs":
		return provider.NewGCSTransport(ctx)
	default:
		return http.DefaultTransport, nil
	}
}

// WrapCacheRoundTripper gets the roundtripper if the provider is a remote
// backend type.
func WrapCacheRoundTripper(ctx context.Context, underlyingTransport http.RoundTripper, originCache string) (http.RoundTripper, error) {
	switch originCache {
	case ":memory:":

		// Create the memory cache transport, and add the underlying transport to
		// it.
		mct := httpcache.NewMemoryCacheTransport()
		mct.Transport = underlyingTransport

		logrus.WithField("transport", ":memory:").Debug("origin cache enabled")
		return mct, nil

	case "":

		// No cache was specified, fall back to the underlying transport.
		logrus.Debug("origin cache disabled")
		return underlyingTransport, nil

	default:

		// Create a new disk transport cache.
		ct := httpcache.NewTransport(diskcache.New(originCache))
		ct.Transport = underlyingTransport

		logrus.WithField("transport", originCache).Debug("origin cache enabled")
		return ct, nil

	}
}

// GetRemoteProviderClient gets the remote provider client or errors.
func GetRemoteProviderClient(ctx context.Context, originURL *url.URL, transport http.RoundTripper) (provider.Provider, error) {
	switch originURL.Scheme {
	case "gs":
		return provider.NewGCS(ctx, originURL.Host, transport)
	case "s3":
		return provider.NewS3(originURL.Host, transport)
	case "http", "https":
		return provider.NewOrigin(originURL, transport), nil
	default:
		return nil, errors.New("invalid origin url provided, scheme could not be matched to an available provider")
	}
}

// GetRemoteBackendProvider will get the backend provider based on the scheme of
// the url.
func GetRemoteBackendProvider(ctx context.Context, origin, originCache string) (provider.Provider, error) {
	originURL, err := url.Parse(origin)
	if err != nil {
		return nil, errors.Wrap(err, "cannot parse the origin url")
	}

	// Get the underlying transport to use to fetch the original resource.
	underlyingTrasport, err := GetUnderlyingTransport(ctx, originURL)
	if err != nil {
		return nil, errors.Wrap(err, "cannot get the underlying transport")
	}

	transport, err := WrapCacheRoundTripper(ctx, underlyingTrasport, originCache)
	if err != nil {
		return nil, errors.Wrap(err, "can't get the origin round tripper")
	}

	// Get the remote provider client.
	return GetRemoteProviderClient(ctx, originURL, transport)
}

// ParseBackend parses the backend using the following formats:
//
//   <host>,<origin> OR <origin>
//
// Where if the host is not specified, it falls back to the defaultHost.
func ParseBackend(defaultHost, backend string) (string, string, error) {
	if backend == "" || backend == "," || strings.HasPrefix(backend, ",") || strings.HasSuffix(backend, ",") {
		return "", "", errors.New("cannot be blank")
	}

	backendSplit := strings.Split(backend, ",")

	if len(backendSplit) == 2 {
		return backendSplit[0], backendSplit[1], nil
	} else if len(backendSplit) == 1 {
		return defaultHost, backendSplit[0], nil
	}

	return "", "", errors.New("expected form <host>,<origin> OR <origin>")
}

// New loops over the origins provided, parsing with the specified providers,
// and returns the providers keyed by host and optionally wrapped with an origin
// cache. This will error if the same backend host is extracted more than once.
func New(ctx context.Context, defaultHost string, backends []string, originCache string) (map[string]provider.Provider, error) {
	if len(backends) == 0 {
		return nil, errors.New("no provider selected")
	}

	// Collect all the providers to the map of Host -> provider.Provider.
	providers := make(map[string]provider.Provider)
	for _, backend := range backends {
		host, origin, err := ParseBackend(defaultHost, backend)
		if err != nil {
			return nil, errors.Wrap(err, "cannot parse the backend")
		}

		// Check to see if we've already attached this host.
		if _, ok := providers[host]; ok {
			return nil, errors.Errorf("host %s already has a provider attached to it", host)
		}

		// If the origin contains the "://" then it must be a remote provider, so
		// attempt to create a origin provider.
		if strings.Contains(origin, "://") {

			// This looks a little weird because we aren't passing a constructed
			// origin cache down to the provider that is shared, but each provider
			// will have a different http.RoundTripper anyways, so no need to reuse
			// the cache in the same way.
			p, err := GetRemoteBackendProvider(ctx, origin, originCache)
			if err != nil {
				return nil, errors.Wrap(err, "cannot get the origin provider")
			}

			logrus.WithFields(logrus.Fields{
				"host":   host,
				"origin": origin,
			}).Debug("serving from the origin")
			providers[host] = p

		} else {

			logrus.WithFields(logrus.Fields{
				"host":      host,
				"directory": origin,
			}).Debug("serving from the filesystem")
			providers[host] = &provider.Filesystem{Dir: http.Dir(origin)}

		}
	}

	return providers, nil
}
<commit_msg>spelling<commit_after>package providers

import (
	"context"
	"net/http"
	"net/url"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/gregjones/httpcache"
	"github.com/gregjones/httpcache/diskcache"
	"github.com/pkg/errors"
	"github.com/wyattjoh/ims/internal/image/provider"
)

// GetUnderlyingTransport checks the url as some clients have specific
// requirements of the underlying transport.
func GetUnderlyingTransport(ctx context.Context, originURL *url.URL) (http.RoundTripper, error) {
	switch originURL.Scheme {
	case "gs":
		return provider.NewGCSTransport(ctx)
	default:
		return http.DefaultTransport, nil
	}
}

// WrapCacheRoundTripper gets the roundtripper if the provider is a remote
// backend type.
func WrapCacheRoundTripper(ctx context.Context, underlyingTransport http.RoundTripper, originCache string) (http.RoundTripper, error) {
	switch originCache {
	case ":memory:":

		// Create the memory cache transport, and add the underlying transport to
		// it.
		mct := httpcache.NewMemoryCacheTransport()
		mct.Transport = underlyingTransport

		logrus.WithField("transport", ":memory:").Debug("origin cache enabled")
		return mct, nil

	case "":

		// No cache was specified, fall back to the underlying transport.
		logrus.Debug("origin cache disabled")
		return underlyingTransport, nil

	default:

		// Create a new disk transport cache.
		ct := httpcache.NewTransport(diskcache.New(originCache))
		ct.Transport = underlyingTransport

		logrus.WithField("transport", originCache).Debug("origin cache enabled")
		return ct, nil

	}
}

// GetRemoteProviderClient gets the remote provider client or errors.
func GetRemoteProviderClient(ctx context.Context, originURL *url.URL, transport http.RoundTripper) (provider.Provider, error) {
	switch originURL.Scheme {
	case "gs":
		return provider.NewGCS(ctx, originURL.Host, transport)
	case "s3":
		return provider.NewS3(originURL.Host, transport)
	case "http", "https":
		return provider.NewOrigin(originURL, transport), nil
	default:
		return nil, errors.New("invalid origin url provided, scheme could not be matched to an available provider")
	}
}

// GetRemoteBackendProvider will get the backend provider based on the scheme of
// the url.
func GetRemoteBackendProvider(ctx context.Context, origin, originCache string) (provider.Provider, error) {
	originURL, err := url.Parse(origin)
	if err != nil {
		return nil, errors.Wrap(err, "cannot parse the origin url")
	}

	// Get the underlying transport to use to fetch the original resource.
	underlyingTransport, err := GetUnderlyingTransport(ctx, originURL)
	if err != nil {
		return nil, errors.Wrap(err, "cannot get the underlying transport")
	}

	transport, err := WrapCacheRoundTripper(ctx, underlyingTransport, originCache)
	if err != nil {
		return nil, errors.Wrap(err, "can't get the origin round tripper")
	}

	// Get the remote provider client.
	return GetRemoteProviderClient(ctx, originURL, transport)
}

// ParseBackend parses the backend using the following formats:
//
//   <host>,<origin> OR <origin>
//
// Where if the host is not specified, it falls back to the defaultHost.
func ParseBackend(defaultHost, backend string) (string, string, error) {
	if backend == "" || backend == "," || strings.HasPrefix(backend, ",") || strings.HasSuffix(backend, ",") {
		return "", "", errors.New("cannot be blank")
	}

	backendSplit := strings.Split(backend, ",")

	if len(backendSplit) == 2 {
		return backendSplit[0], backendSplit[1], nil
	} else if len(backendSplit) == 1 {
		return defaultHost, backendSplit[0], nil
	}

	return "", "", errors.New("expected form <host>,<origin> OR <origin>")
}

// New loops over the origins provided, parsing with the specified providers,
// and returns the providers keyed by host and optionally wrapped with an origin
// cache. This will error if the same backend host is extracted more than once.
func New(ctx context.Context, defaultHost string, backends []string, originCache string) (map[string]provider.Provider, error) {
	if len(backends) == 0 {
		return nil, errors.New("no provider selected")
	}

	// Collect all the providers to the map of Host -> provider.Provider.
	providers := make(map[string]provider.Provider)
	for _, backend := range backends {
		host, origin, err := ParseBackend(defaultHost, backend)
		if err != nil {
			return nil, errors.Wrap(err, "cannot parse the backend")
		}

		// Check to see if we've already attached this host.
		if _, ok := providers[host]; ok {
			return nil, errors.Errorf("host %s already has a provider attached to it", host)
		}

		// If the origin contains the "://" then it must be a remote provider, so
		// attempt to create a origin provider.
		if strings.Contains(origin, "://") {

			// This looks a little weird because we aren't passing a constructed
			// origin cache down to the provider that is shared, but each provider
			// will have a different http.RoundTripper anyways, so no need to reuse
			// the cache in the same way.
			p, err := GetRemoteBackendProvider(ctx, origin, originCache)
			if err != nil {
				return nil, errors.Wrap(err, "cannot get the origin provider")
			}

			logrus.WithFields(logrus.Fields{
				"host":   host,
				"origin": origin,
			}).Debug("serving from the origin")
			providers[host] = p

		} else {

			logrus.WithFields(logrus.Fields{
				"host":      host,
				"directory": origin,
			}).Debug("serving from the filesystem")
			providers[host] = &provider.Filesystem{Dir: http.Dir(origin)}

		}
	}

	return providers, nil
}
<|endoftext|>


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

// +build mojo

package main

import (
	"flag"
	"sync"

	"mojo/public/go/application"
	"mojo/public/go/bindings"
	"mojo/public/go/system"

	mojom "mojom/v.io/discovery"

	"v.io/v23"
	"v.io/v23/context"

	idiscovery "v.io/x/ref/lib/discovery"
	fdiscovery "v.io/x/ref/lib/discovery/factory"
	"v.io/x/ref/lib/discovery/plugins/mock"
	_ "v.io/x/ref/runtime/factories/generic"

	"v.io/mojo/discovery/internal"
)

//#include "mojo/public/c/system/types.h"
import "C"

var (
	flagUseMock = flag.Bool("use-mock", false, "Use a mock plugin for mojo apptests.")
)

type delegate struct {
	ctx      *context.T
	shutdown v23.Shutdown

	mu    sync.Mutex
	stubs map[*bindings.Stub]struct{} // GUARDED_BY(mu)
}

func (d *delegate) Initialize(mctx application.Context) {
	// TODO(bjornick): Calling init multiple times in the same process
	// will be bad.  For now, this is ok because this is the only
	// vanadium service that will be used in the demos and each go library
	// will be in its own process.
	d.ctx, d.shutdown = v23.Init(mctx)

	if *flagUseMock {
		df, _ := idiscovery.NewFactory(d.ctx, mock.New())
		fdiscovery.InjectFactory(df)
	}
}

func (d *delegate) Create(request mojom.Discovery_Request) {
	discovery := internal.NewDiscovery(d.ctx)
	stub := mojom.NewDiscoveryStub(request, discovery, bindings.GetAsyncWaiter())
	d.mu.Lock()
	d.stubs[stub] = struct{}{}
	d.mu.Unlock()

	go func() {
		defer discovery.Close()

		for {
			if err := stub.ServeRequest(); err != nil {
				connErr, ok := err.(*bindings.ConnectionError)
				if !ok || !connErr.Closed() {
					d.ctx.Error(err)
				}
				break
			}
		}

		d.mu.Lock()
		delete(d.stubs, stub)
		d.mu.Unlock()
	}()
}

func (d *delegate) AcceptConnection(connection *application.Connection) {
	connection.ProvideServices(&mojom.Discovery_ServiceFactory{d})
}

func (d *delegate) Quit() {
	d.mu.Lock()
	for stub := range d.stubs {
		stub.Close()
	}
	d.mu.Unlock()
	d.shutdown()
}

//export MojoMain
func MojoMain(handle C.MojoHandle) C.MojoResult {
	application.Run(&delegate{stubs: map[*bindings.Stub]struct{}{}}, system.MojoHandle(handle))
	return C.MOJO_RESULT_OK
}

func main() {}
<commit_msg>v23: remove mojo-specific hacks from v23 and move into profiles<commit_after>// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build mojo

package main

import (
	"flag"
	"sync"

	"mojo/public/go/application"
	"mojo/public/go/bindings"
	"mojo/public/go/system"

	mojom "mojom/v.io/discovery"

	"v.io/v23"
	"v.io/v23/context"

	idiscovery "v.io/x/ref/lib/discovery"
	fdiscovery "v.io/x/ref/lib/discovery/factory"
	"v.io/x/ref/lib/discovery/plugins/mock"
	"v.io/x/ref/runtime/factories/roaming"

	"v.io/mojo/discovery/internal"
)

//#include "mojo/public/c/system/types.h"
import "C"

var (
	flagUseMock = flag.Bool("use-mock", false, "Use a mock plugin for mojo apptests.")
)

type delegate struct {
	ctx      *context.T
	shutdown v23.Shutdown

	mu    sync.Mutex
	stubs map[*bindings.Stub]struct{} // GUARDED_BY(mu)
}

func (d *delegate) Initialize(mctx application.Context) {
	// TODO(bjornick): Calling init multiple times in the same process
	// will be bad.  For now, this is ok because this is the only
	// vanadium service that will be used in the demos and each go library
	// will be in its own process.
	roaming.SetArgs(mctx)
	d.ctx, d.shutdown = v23.Init()

	if *flagUseMock {
		df, _ := idiscovery.NewFactory(d.ctx, mock.New())
		fdiscovery.InjectFactory(df)
	}
}

func (d *delegate) Create(request mojom.Discovery_Request) {
	discovery := internal.NewDiscovery(d.ctx)
	stub := mojom.NewDiscoveryStub(request, discovery, bindings.GetAsyncWaiter())
	d.mu.Lock()
	d.stubs[stub] = struct{}{}
	d.mu.Unlock()

	go func() {
		defer discovery.Close()

		for {
			if err := stub.ServeRequest(); err != nil {
				connErr, ok := err.(*bindings.ConnectionError)
				if !ok || !connErr.Closed() {
					d.ctx.Error(err)
				}
				break
			}
		}

		d.mu.Lock()
		delete(d.stubs, stub)
		d.mu.Unlock()
	}()
}

func (d *delegate) AcceptConnection(connection *application.Connection) {
	connection.ProvideServices(&mojom.Discovery_ServiceFactory{d})
}

func (d *delegate) Quit() {
	d.mu.Lock()
	for stub := range d.stubs {
		stub.Close()
	}
	d.mu.Unlock()
	d.shutdown()
}

//export MojoMain
func MojoMain(handle C.MojoHandle) C.MojoResult {
	application.Run(&delegate{stubs: map[*bindings.Stub]struct{}{}}, system.MojoHandle(handle))
	return C.MOJO_RESULT_OK
}

func main() {}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
)

// int の引数をもらって、
// int を返す関数を返す関数。
func adder() func(int) int {
	sum := 0
	return func(x int) int {
		sum += x
		return sum
	}
}

func main() {
	pos, neg := adder(), adder()
	for i := 0; i < 10; i++ {
		fmt.Println(
			pos(i),
			neg(-2*i),
		)
	}
}
<commit_msg>Updated 43_FunctionClousures.<commit_after>package main

import (
	"fmt"
)

// int の引数をもらって、
// int を返す関数を返す関数。
//
// - addr() を呼び出すたびに、
//   addr 空間(環境)が新しく作成される。
// - addr 空間内で作成された関数は、
//   その空間で宣言された変数を使用することが出来る。
// - addr 空間内で宣言された変数は、
//   他の空間から参照・変更されない。
// という感じか？
func adder() func(int) int {
	sum := 0

	innerFunc := func(x int) int {
		sum += x
		return sum
	}

	return innerFunc
}

func main() {
	f1 := adder()
	fmt.Println(f1(1))
	fmt.Println(f1(1))

	f2 := adder()
	fmt.Println(f2(1))
	fmt.Println(f2(1))
}
<|endoftext|>


<commit_before>/*
Copyright 2017 Matthew Lord (mattalord@gmail.com)

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package instances

import (
  "database/sql"
  _ "github.com/go-sql-driver/mysql"
  "errors"
  "fmt"
)

// member variables that start with capital letters are public/exported
type Instance struct {
  Mysql_host string
  Mysql_port string
  Mysql_user string
  mysql_pass string

  // The status related vars can serve as an effective cache
  Group_name string
  Server_uuid string
  Member_state string
  Online_participants uint8
  Has_quorum bool
  Read_only bool
  Applier_queue_size uint16
  db *sql.DB
}

// enable debug logging for all instances
var Debug bool = false

func New( myh string, myp string, myu string, mys string ) * Instance {
  return &Instance{ Mysql_host: myh, Mysql_port: myp, Mysql_user: myu, mysql_pass: mys }
}

func (me *Instance) Connect() error {
  var err error

  if( me.db == nil ){
    conn_string := me.Mysql_user + ":" + me.mysql_pass + "@tcp(" + me.Mysql_host + ":" + me.Mysql_port + ")/performance_schema"

    if( Debug ){
      fmt.Printf( "Making SQL connection using: %s\n", conn_string )
    }

    me.db, err = sql.Open( "mysql", conn_string )

    if( err != nil ){
      fmt.Printf( "Error during sql.Open: %v", err )
    }
  }

  err = me.db.Ping()

  if( err == nil ){
    query_str := "SELECT variable_value FROM global_variables WHERE variable_name='group_replication_group_name'"

    if( Debug ){
      fmt.Printf( "Checking group name on '%s:%s'. Query: %s\n", me.Mysql_host, me.Mysql_port, query_str )
    }

    err = me.db.QueryRow( query_str ).Scan( &me.Group_name )

    if( err != nil || me.Group_name == "" ){
      err = errors.New( "Specified MySQL Instance is not a member of any Group Replication cluster!" )
    }

    query_str = "SELECT variable_value, member_state FROM global_variables gv INNER JOIN replication_group_members rgm ON(gv.variable_value=rgm.member_id) WHERE gv.variable_name='server_uuid'"

    if( Debug ){
      fmt.Printf( "Checking status of '%s:%s'. Query: %s\n", me.Mysql_host, me.Mysql_port, query_str )
    }

    err = me.db.QueryRow( query_str ).Scan( &me.Server_uuid, &me.Member_state )
  }

  return err
}

func (me *Instance) HasQuorum() (bool, error) {
  quorum_query := "SELECT IF( MEMBER_STATE='ONLINE' AND ((SELECT COUNT(*) FROM replication_group_members WHERE MEMBER_STATE != 'ONLINE') >= ((SELECT COUNT(*) FROM replication_group_members)/2) = 0), 'true', 'false' ) FROM replication_group_members JOIN replication_group_member_stats USING(member_id)"

  if( Debug ){
    fmt.Printf( "Checking if '%s:%s' has a quorum. Query: %s\n", me.Mysql_host, me.Mysql_port, quorum_query )
  }

  err := me.db.Ping()

  if( err == nil ){
    err = me.db.QueryRow( quorum_query ).Scan( &me.Has_quorum )
  }

  return me.Has_quorum, err
}

func (me *Instance) IsReadOnly() (bool, error) {
  ro_query := "SELECT variable_value FROM global_variables WHERE variable_name='super_read_only'"

  if( Debug ){
    fmt.Printf( "Checking if '%s:%s' is read only. Query: %s\n", me.Mysql_host, me.Mysql_port, ro_query )
  }

  err := me.db.Ping()

  if( err == nil ){
    err = me.db.QueryRow( ro_query ).Scan( &me.Read_only )
  }

  return me.Read_only, err
}

func (me *Instance) GetMembers() (*[]Instance, error) {
  membership_query := "SELECT member_id, member_host, member_port, member_state FROM replication_group_members"
  member_slice := []Instance{}
  me.Online_participants = 0

  if( Debug ){
    fmt.Printf( "Getting group members from '%s:%s'. Query: %s\n", me.Mysql_host, me.Mysql_port, membership_query )
  }

  err := me.db.Ping()

  if( err == nil ){
    rows, err := me.db.Query( membership_query )

    if( err == nil ){
      defer rows.Close()

      for( rows.Next() ){
        member := New( "", "", me.Mysql_user, me.mysql_pass )
        err = rows.Scan( &member.Server_uuid, &member.Mysql_host, &member.Mysql_port, &member.Member_state )
        if( member.Member_state == "ONLINE" ){
          me.Online_participants++
        }
        member_slice = append( member_slice, *member )
      }

      if( Debug ){
        fmt.Printf( "Group member info found for '%s:%s' -- ONLINE member count: %d, Members: %v\n", me.Mysql_host, me.Mysql_port, me.Online_participants, member_slice )
      }
    }
  }

  return &member_slice, err
}

func (me *Instance) Shutdown() error {
  shutdown_query := "SHUTDOWN"

  if( Debug ){
    fmt.Printf( "Shutting down node '%s:%s'\n", me.Mysql_host, me.Mysql_port )
  }

  err := me.db.Ping()

  if( err == nil ){
    _, err = me.db.Exec( shutdown_query )
  }

  return err
}

func (me *Instance) TransactionsExecuted() (string, error) {
  // since this is such a fast changing metric, I won't cache the value in the struct
  var gtids string
  gtid_query := "SELECT @@global.GTID_EXECUTED"

  if( Debug ){
    fmt.Printf( "Getting the transactions executed on '%s:%s'\n", me.Mysql_host, me.Mysql_port )
  }

  err := me.db.Ping()

  if( err == nil ){
    err = me.db.QueryRow( gtid_query ).Scan( &gtids )
  }

  return gtids, err
}

func (me *Instance) ForceMembers( fms string ) error {
  force_membership_query := "SET GLOBAL group_replication_force_members='" + fms + "'"

  if( Debug ){
    fmt.Printf( "Forcing group membership on '%s:%s'. Query: %s\n", me.Mysql_host, me.Mysql_port, force_membership_query )
  }

  err := me.db.Ping()

  if( err == nil ){
    _, err = me.db.Exec( force_membership_query )
  }

  return err
}
<commit_msg>Improved the logging<commit_after>/*
Copyright 2017 Matthew Lord (mattalord@gmail.com)

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package instances

import (
  "database/sql"
  _ "github.com/go-sql-driver/mysql"
  "errors"
  "log"
  "os"
)

// member variables that start with capital letters are public/exported
type Instance struct {
  Mysql_host string
  Mysql_port string
  Mysql_user string
  mysql_pass string

  // The status related vars can serve as an effective cache
  Group_name string
  Server_uuid string
  Member_state string
  Online_participants uint8
  Has_quorum bool
  Read_only bool
  Applier_queue_size uint16
  db *sql.DB
}

// enable debug logging for all instances
var Debug bool = false

// setup debug logging for all instances
var DebugLog = log.New(os.Stderr,
               "DEBUG: ",
               log.Ldate|log.Ltime|log.Lshortfile)

func New( myh string, myp string, myu string, mys string ) * Instance {
  return &Instance{ Mysql_host: myh, Mysql_port: myp, Mysql_user: myu, mysql_pass: mys }
}

func (me *Instance) Connect() error {
  var err error

  if( me.db == nil ){
    conn_string := me.Mysql_user + ":" + me.mysql_pass + "@tcp(" + me.Mysql_host + ":" + me.Mysql_port + ")/performance_schema"

    if( Debug ){
      DebugLog.Printf( "Making SQL connection using: %s\n", conn_string )
    }

    me.db, err = sql.Open( "mysql", conn_string )

    if( err != nil ){
      DebugLog.Printf( "Error during sql.Open: %v", err )
    }
  }

  err = me.db.Ping()

  if( err == nil ){
    query_str := "SELECT variable_value FROM global_variables WHERE variable_name='group_replication_group_name'"

    if( Debug ){
      DebugLog.Printf( "Checking group name on '%s:%s'. Query: %s\n", me.Mysql_host, me.Mysql_port, query_str )
    }

    err = me.db.QueryRow( query_str ).Scan( &me.Group_name )

    if( err != nil || me.Group_name == "" ){
      err = errors.New( "Specified MySQL Instance is not a member of any Group Replication cluster!" )
    }

    query_str = "SELECT variable_value, member_state FROM global_variables gv INNER JOIN replication_group_members rgm ON(gv.variable_value=rgm.member_id) WHERE gv.variable_name='server_uuid'"

    if( Debug ){
      DebugLog.Printf( "Checking status of '%s:%s'. Query: %s\n", me.Mysql_host, me.Mysql_port, query_str )
    }

    err = me.db.QueryRow( query_str ).Scan( &me.Server_uuid, &me.Member_state )
  }

  return err
}

func (me *Instance) HasQuorum() (bool, error) {
  quorum_query := "SELECT IF( MEMBER_STATE='ONLINE' AND ((SELECT COUNT(*) FROM replication_group_members WHERE MEMBER_STATE != 'ONLINE') >= ((SELECT COUNT(*) FROM replication_group_members)/2) = 0), 'true', 'false' ) FROM replication_group_members JOIN replication_group_member_stats USING(member_id)"

  if( Debug ){
    DebugLog.Printf( "Checking if '%s:%s' has a quorum. Query: %s\n", me.Mysql_host, me.Mysql_port, quorum_query )
  }

  err := me.db.Ping()

  if( err == nil ){
    err = me.db.QueryRow( quorum_query ).Scan( &me.Has_quorum )
  }

  return me.Has_quorum, err
}

func (me *Instance) IsReadOnly() (bool, error) {
  ro_query := "SELECT variable_value FROM global_variables WHERE variable_name='super_read_only'"

  if( Debug ){
    DebugLog.Printf( "Checking if '%s:%s' is read only. Query: %s\n", me.Mysql_host, me.Mysql_port, ro_query )
  }

  err := me.db.Ping()

  if( err == nil ){
    err = me.db.QueryRow( ro_query ).Scan( &me.Read_only )
  }

  return me.Read_only, err
}

func (me *Instance) GetMembers() (*[]Instance, error) {
  membership_query := "SELECT member_id, member_host, member_port, member_state FROM replication_group_members"
  member_slice := []Instance{}
  me.Online_participants = 0

  if( Debug ){
    DebugLog.Printf( "Getting group members from '%s:%s'. Query: %s\n", me.Mysql_host, me.Mysql_port, membership_query )
  }

  err := me.db.Ping()

  if( err == nil ){
    rows, err := me.db.Query( membership_query )

    if( err == nil ){
      defer rows.Close()

      for( rows.Next() ){
        member := New( "", "", me.Mysql_user, me.mysql_pass )
        err = rows.Scan( &member.Server_uuid, &member.Mysql_host, &member.Mysql_port, &member.Member_state )
        if( member.Member_state == "ONLINE" ){
          me.Online_participants++
        }
        member_slice = append( member_slice, *member )
      }

      if( Debug ){
        DebugLog.Printf( "Group member info found for '%s:%s' -- ONLINE member count: %d, Members: %v\n", me.Mysql_host, me.Mysql_port, me.Online_participants, member_slice )
      }
    }
  }

  return &member_slice, err
}

func (me *Instance) Shutdown() error {
  shutdown_query := "SHUTDOWN"

  if( Debug ){
    DebugLog.Printf( "Shutting down node '%s:%s'\n", me.Mysql_host, me.Mysql_port )
  }

  err := me.db.Ping()

  if( err == nil ){
    _, err = me.db.Exec( shutdown_query )
  }

  return err
}

func (me *Instance) TransactionsExecuted() (string, error) {
  // since this is such a fast changing metric, I won't cache the value in the struct
  var gtids string
  gtid_query := "SELECT @@global.GTID_EXECUTED"

  if( Debug ){
    DebugLog.Printf( "Getting the transactions executed on '%s:%s'\n", me.Mysql_host, me.Mysql_port )
  }

  err := me.db.Ping()

  if( err == nil ){
    err = me.db.QueryRow( gtid_query ).Scan( &gtids )
  }

  return gtids, err
}

func (me *Instance) ForceMembers( fms string ) error {
  force_membership_query := "SET GLOBAL group_replication_force_members='" + fms + "'"

  if( Debug ){
    DebugLog.Printf( "Forcing group membership on '%s:%s'. Query: %s\n", me.Mysql_host, me.Mysql_port, force_membership_query )
  }

  err := me.db.Ping()

  if( err == nil ){
    _, err = me.db.Exec( force_membership_query )
  }

  return err
}
<|endoftext|>