<commit_before>package security_groups_test

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"time"

	. "github.com/cloudfoundry/cf-acceptance-tests/cats_suite_helpers"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"

	"github.com/cloudfoundry-incubator/cf-test-helpers/cf"
	"github.com/cloudfoundry-incubator/cf-test-helpers/helpers"
	"github.com/cloudfoundry-incubator/cf-test-helpers/workflowhelpers"
	"github.com/cloudfoundry/cf-acceptance-tests/helpers/app_helpers"
	"github.com/cloudfoundry/cf-acceptance-tests/helpers/assets"
	"github.com/cloudfoundry/cf-acceptance-tests/helpers/random_name"
)

type AppsResponse struct {
	Resources []struct {
		Metadata struct {
			Url string
		}
	}
}

type StatsResponse map[string]struct {
	Stats struct {
		Host string
		Port int
	}
}

type DoraCurlResponse struct {
	Stdout     string
	Stderr     string
	ReturnCode int `json:"return_code"`
}

func pushApp(appName, buildpack string) {
	Expect(cf.Cf("push",
		appName,
		"--no-start",
		"-b", buildpack,
		"-m", DEFAULT_MEMORY_LIMIT,
		"-p", assets.NewAssets().Dora,
		"-d", Config.GetAppsDomain()).Wait(Config.CfPushTimeoutDuration())).To(Exit(0))
	app_helpers.SetBackend(appName)
}

func getAppHostIpAndPort(appName string) (string, int) {
	var appsResponse AppsResponse
	cfResponse := cf.Cf("curl", fmt.Sprintf("/v2/apps?q=name:%s", appName)).Wait(Config.DefaultTimeoutDuration()).Out.Contents()
	json.Unmarshal(cfResponse, &appsResponse)
	serverAppUrl := appsResponse.Resources[0].Metadata.Url

	var statsResponse StatsResponse
	cfResponse = cf.Cf("curl", fmt.Sprintf("%s/stats", serverAppUrl)).Wait(Config.DefaultTimeoutDuration()).Out.Contents()
	json.Unmarshal(cfResponse, &statsResponse)

	return statsResponse["0"].Stats.Host, statsResponse["0"].Stats.Port
}

func testAppConnectivity(clientAppName string, privateHost string, privatePort int) DoraCurlResponse {
	var doraCurlResponse DoraCurlResponse
	curlResponse := helpers.CurlApp(Config, clientAppName, fmt.Sprintf("/curl/%s/%d", privateHost, privatePort))
	json.Unmarshal([]byte(curlResponse), &doraCurlResponse)
	return doraCurlResponse
}

func getAppContainerIpAndPort(appName string) (string, int) {
	curlResponse := helpers.CurlApp(Config, appName, "/myip")
	containerIp := strings.TrimSpace(curlResponse)

	curlResponse = helpers.CurlApp(Config, appName, "/env/VCAP_APPLICATION")
	var env map[string]interface{}
	err := json.Unmarshal([]byte(curlResponse), &env)
	Expect(err).NotTo(HaveOccurred())
	containerPort := int(env["port"].(float64))

	return containerIp, containerPort
}

func createSecurityGroup(privateHost string, privatePort int, containerIp string, containerPort int) string {
	rules := fmt.Sprintf(
		`[{"destination":"%s","ports":"%d","protocol":"tcp"},
			{"destination":"%s","ports":"%d","protocol":"tcp"}]`,
		privateHost, privatePort, containerIp, containerPort)

	file, _ := ioutil.TempFile(os.TempDir(), "CATS-sg-rules")
	defer os.Remove(file.Name())
	file.WriteString(rules)

	rulesPath := file.Name()
	securityGroupName := random_name.CATSRandomName("SG")

	workflowhelpers.AsUser(TestSetup.AdminUserContext(), Config.DefaultTimeoutDuration(), func() {
		Expect(cf.Cf("create-security-group", securityGroupName, rulesPath).Wait(Config.DefaultTimeoutDuration())).To(Exit(0))
	})

	return securityGroupName
}

func bindSecurityGroup(securityGroupName, orgName, spaceName string) {
	By("Applying security group")
	workflowhelpers.AsUser(TestSetup.AdminUserContext(), Config.DefaultTimeoutDuration(), func() {
		Expect(cf.Cf("bind-security-group", securityGroupName, orgName, spaceName).Wait(Config.DefaultTimeoutDuration())).To(Exit(0))
	})
}

func unbindSecurityGroup(securityGroupName, orgName, spaceName string) {
	By("Unapplying security group")
	workflowhelpers.AsUser(TestSetup.AdminUserContext(), Config.DefaultTimeoutDuration(), func() {
		Expect(cf.Cf("unbind-security-group", securityGroupName, orgName, spaceName).Wait(Config.DefaultTimeoutDuration())).To(Exit(0))
	})
}

func deleteSecurityGroup(securityGroupName string) {
	workflowhelpers.AsUser(TestSetup.AdminUserContext(), Config.DefaultTimeoutDuration(), func() {
		Expect(cf.Cf("delete-security-group", securityGroupName, "-f").Wait(Config.DefaultTimeoutDuration())).To(Exit(0))
	})
}

func createDummyBuildpack() string {
	buildpack := random_name.CATSRandomName("BPK")
	buildpackZip := assets.NewAssets().SecurityGroupBuildpack

	workflowhelpers.AsUser(TestSetup.AdminUserContext(), Config.DefaultTimeoutDuration(), func() {
		Expect(cf.Cf("create-buildpack", buildpack, buildpackZip, "999").Wait(Config.DefaultTimeoutDuration())).To(Exit(0))
	})
	return buildpack
}

func deleteBuildpack(buildpack string) {
	workflowhelpers.AsUser(TestSetup.AdminUserContext(), Config.DefaultTimeoutDuration(), func() {
		Expect(cf.Cf("delete-buildpack", buildpack, "-f").Wait(Config.DefaultTimeoutDuration())).To(Exit(0))
	})
}

func getStagingOutput(appName string) func() *Session {
	return func() *Session {
		appLogsSession := cf.Cf("logs", "--recent", appName)
		Expect(appLogsSession.Wait(Config.DefaultTimeoutDuration())).To(Exit(0))
		return appLogsSession
	}
}

var _ = SecurityGroupsDescribe("Security Groups", func() {
	var serverAppName, privateHost string
	var privatePort int

	BeforeEach(func() {
		serverAppName = random_name.CATSRandomName("APP")
		pushApp(serverAppName, Config.GetRubyBuildpackName())
		Expect(cf.Cf("start", serverAppName).Wait(Config.CfPushTimeoutDuration())).To(Exit(0))

		privateHost, privatePort = getAppHostIpAndPort(serverAppName)
	})

	Describe("Running security-groups", func() {
		var clientAppName, securityGroupName string

		BeforeEach(func() {
			clientAppName = random_name.CATSRandomName("APP")
			pushApp(clientAppName, Config.GetRubyBuildpackName())
			Expect(cf.Cf("start", clientAppName).Wait(Config.CfPushTimeoutDuration())).To(Exit(0))

			By("Asserting default running security group configuration for traffic between containers")
			doraCurlResponse := testAppConnectivity(clientAppName, privateHost, privatePort)
			Expect(doraCurlResponse.ReturnCode).NotTo(Equal(0), "Expected default running security groups not to allow internal communication between app containers. Configure your running security groups to not allow traffic on internal networks, or disable this test by setting 'include_security_groups' to 'false' in '"+os.Getenv("CONFIG")+"'.")

			By("Asserting default running security group configuration from a running container to an external destination")
			doraCurlResponse = testAppConnectivity(clientAppName, "www.google.com", 80)
			Expect(doraCurlResponse.ReturnCode).To(Equal(0), "Expected default running security groups to allow external traffic from app containers. Configure your running security groups to not allow traffic on internal networks, or disable this test by setting 'include_security_groups' to 'false' in '"+os.Getenv("CONFIG")+"'.")
		})

		AfterEach(func() {
			app_helpers.AppReport(serverAppName, Config.DefaultTimeoutDuration())
			Expect(cf.Cf("delete", serverAppName, "-f", "-r").Wait(Config.CfPushTimeoutDuration())).To(Exit(0))

			app_helpers.AppReport(clientAppName, Config.DefaultTimeoutDuration())
			Expect(cf.Cf("delete", clientAppName, "-f", "-r").Wait(Config.CfPushTimeoutDuration())).To(Exit(0))

			deleteSecurityGroup(securityGroupName)
		})

		It("allows ip traffic between containers after applying a security group and blocks it when the security group is removed", func() {
			if Config.GetIncludeContainerNetworking() {
				Skip("Skipping this test because Config.ContainerNetworking is set to 'true'.")
			}

			containerIp, containerPort := getAppContainerIpAndPort(serverAppName)
			securityGroupName = createSecurityGroup(privateHost, privatePort, containerIp, containerPort)
			By("binding new security group")
			bindSecurityGroup(securityGroupName, TestSetup.RegularUserContext().Org, TestSetup.RegularUserContext().Space)

			Expect(cf.Cf("restart", clientAppName).Wait(Config.CfPushTimeoutDuration())).To(Exit(0))

			By("Testing that app can connect")
			doraCurlResponse := testAppConnectivity(clientAppName, privateHost, privatePort)
			Expect(doraCurlResponse.ReturnCode).To(Equal(0))

			By("unbinding security group")
			unbindSecurityGroup(securityGroupName, TestSetup.RegularUserContext().Org, TestSetup.RegularUserContext().Space)
			Expect(cf.Cf("restart", clientAppName).Wait(Config.CfPushTimeoutDuration())).To(Exit(0))

			By("Testing that app can no longer connect")
			doraCurlResponse = testAppConnectivity(clientAppName, privateHost, privatePort)
			Expect(doraCurlResponse.ReturnCode).NotTo(Equal(0))
		})

		It("allows ip traffic between containers after applying a policy and blocks it when the policy is removed", func() {
			if !Config.GetIncludeContainerNetworking() {
				Skip("Skipping this test because Config.ContainerNetworking is set to 'false'.")
			}

			containerIp, containerPort := getAppContainerIpAndPort(serverAppName)
			orgName := TestSetup.RegularUserContext().Org
			spaceName := TestSetup.RegularUserContext().Space

			By("Testing that app cannot connect")
			doraCurlResponse := testAppConnectivity(clientAppName, containerIp, containerPort)
			Expect(doraCurlResponse.ReturnCode).NotTo(Equal(0))

			By("adding policy")
			workflowhelpers.AsUser(TestSetup.AdminUserContext(), Config.DefaultTimeoutDuration(), func() {
				Expect(cf.Cf("target", "-o", orgName, "-s", spaceName).Wait(Config.DefaultTimeoutDuration())).To(Exit(0))
				Expect(cf.Cf("allow-access", clientAppName, serverAppName, "--port", fmt.Sprintf("%d", containerPort), "--protocol", "tcp").Wait(Config.CfPushTimeoutDuration())).To(Exit(0))
			})

			By("waiting for policy to be added on cell")
			time.Sleep(10 * time.Second)

			By("Testing that app can connect")
			doraCurlResponse = testAppConnectivity(clientAppName, containerIp, containerPort)
			Expect(doraCurlResponse.ReturnCode).To(Equal(0))

			By("removing policy")
			workflowhelpers.AsUser(TestSetup.AdminUserContext(), Config.DefaultTimeoutDuration(), func() {
				Expect(cf.Cf("target", "-o", orgName, "-s", spaceName).Wait(Config.DefaultTimeoutDuration())).To(Exit(0))
				Expect(cf.Cf("deny-access", clientAppName, serverAppName, "--port", fmt.Sprintf("%d", containerPort), "--protocol", "tcp").Wait(Config.CfPushTimeoutDuration())).To(Exit(0))
			})

			By("waiting for policy to be removed on cell")
			time.Sleep(10 * time.Second)

			By("Testing that app can no longer connect")
			doraCurlResponse = testAppConnectivity(clientAppName, containerIp, containerPort)
			Expect(doraCurlResponse.ReturnCode).NotTo(Equal(0))
		})
	})

	Describe("staging security groups", func() {
		var testAppName, buildpack string

		BeforeEach(func() {
			By("Asserting default staging security group configuration")
			testAppName = random_name.CATSRandomName("APP")
			buildpack = createDummyBuildpack()
			pushApp(testAppName, buildpack)

			privateUri := fmt.Sprintf("%s:%d", privateHost, privatePort)
			Expect(cf.Cf("set-env", testAppName, "TESTURI", privateUri).Wait(Config.DefaultTimeoutDuration())).To(Exit(0))

			Expect(cf.Cf("start", testAppName).Wait(Config.CfPushTimeoutDuration())).To(Exit(1))
			Eventually(getStagingOutput(testAppName), 5).Should(Say("CURL_EXIT=[^0]"), "Expected staging security groups not to allow internal communication between app containers. Configure your staging security groups to not allow traffic on internal networks, or disable this test by setting 'include_security_groups' to 'false' in '"+os.Getenv("CONFIG")+"'.")
		})

		AfterEach(func() {
			app_helpers.AppReport(serverAppName, Config.DefaultTimeoutDuration())
			Expect(cf.Cf("delete", serverAppName, "-f", "-r").Wait(Config.CfPushTimeoutDuration())).To(Exit(0))

			app_helpers.AppReport(testAppName, Config.DefaultTimeoutDuration())
			Expect(cf.Cf("delete", testAppName, "-f", "-r").Wait(Config.CfPushTimeoutDuration())).To(Exit(0))

			deleteBuildpack(buildpack)
		})

		It("allows external and denies internal traffic during staging based on default staging security rules", func() {
			Expect(cf.Cf("set-env", testAppName, "TESTURI", "www.google.com").Wait(Config.DefaultTimeoutDuration())).To(Exit(0))
			Expect(cf.Cf("restart", testAppName).Wait(Config.CfPushTimeoutDuration())).To(Exit(1))
			Eventually(getStagingOutput(testAppName), 5).Should(Say("CURL_EXIT=0"))
		})
	})
})
<commit_msg>Running Security Groups coverage for connecting to non-container destinations<commit_after>package security_groups_test

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"time"

	. "github.com/cloudfoundry/cf-acceptance-tests/cats_suite_helpers"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"

	"github.com/cloudfoundry-incubator/cf-test-helpers/cf"
	"github.com/cloudfoundry-incubator/cf-test-helpers/helpers"
	"github.com/cloudfoundry-incubator/cf-test-helpers/workflowhelpers"
	"github.com/cloudfoundry/cf-acceptance-tests/helpers/app_helpers"
	"github.com/cloudfoundry/cf-acceptance-tests/helpers/assets"
	"github.com/cloudfoundry/cf-acceptance-tests/helpers/random_name"
)

type AppsResponse struct {
	Resources []struct {
		Metadata struct {
			Url string
		}
	}
}

type StatsResponse map[string]struct {
	Stats struct {
		Host string
		Port int
	}
}

type DoraCurlResponse struct {
	Stdout     string
	Stderr     string
	ReturnCode int `json:"return_code"`
}

func pushApp(appName, buildpack string) {
	Expect(cf.Cf("push",
		appName,
		"--no-start",
		"-b", buildpack,
		"-m", DEFAULT_MEMORY_LIMIT,
		"-p", assets.NewAssets().Dora,
		"-d", Config.GetAppsDomain()).Wait(Config.CfPushTimeoutDuration())).To(Exit(0))
	app_helpers.SetBackend(appName)
}

func getAppHostIpAndPort(appName string) (string, int) {
	var appsResponse AppsResponse
	cfResponse := cf.Cf("curl", fmt.Sprintf("/v2/apps?q=name:%s", appName)).Wait(Config.DefaultTimeoutDuration()).Out.Contents()
	json.Unmarshal(cfResponse, &appsResponse)
	serverAppUrl := appsResponse.Resources[0].Metadata.Url

	var statsResponse StatsResponse
	cfResponse = cf.Cf("curl", fmt.Sprintf("%s/stats", serverAppUrl)).Wait(Config.DefaultTimeoutDuration()).Out.Contents()
	json.Unmarshal(cfResponse, &statsResponse)

	return statsResponse["0"].Stats.Host, statsResponse["0"].Stats.Port
}

func testAppConnectivity(clientAppName string, privateHost string, privatePort int) DoraCurlResponse {
	var doraCurlResponse DoraCurlResponse
	curlResponse := helpers.CurlApp(Config, clientAppName, fmt.Sprintf("/curl/%s/%d", privateHost, privatePort))
	json.Unmarshal([]byte(curlResponse), &doraCurlResponse)
	return doraCurlResponse
}

func getAppContainerIpAndPort(appName string) (string, int) {
	curlResponse := helpers.CurlApp(Config, appName, "/myip")
	containerIp := strings.TrimSpace(curlResponse)

	curlResponse = helpers.CurlApp(Config, appName, "/env/VCAP_APPLICATION")
	var env map[string]interface{}
	err := json.Unmarshal([]byte(curlResponse), &env)
	Expect(err).NotTo(HaveOccurred())
	containerPort := int(env["port"].(float64))

	return containerIp, containerPort
}

type Destination struct {
	IP       string `json:"destination"`
	Port     int    `json:"ports,string"`
	Protocol string `json:"protocol"`
}

func createSecurityGroup(allowedDestinations ...Destination) string {
	file, _ := ioutil.TempFile(os.TempDir(), "CATS-sg-rules")
	defer os.Remove(file.Name())
	Expect(json.NewEncoder(file).Encode(allowedDestinations)).To(Succeed())

	rulesPath := file.Name()
	securityGroupName := random_name.CATSRandomName("SG")

	workflowhelpers.AsUser(TestSetup.AdminUserContext(), Config.DefaultTimeoutDuration(), func() {
		Expect(cf.Cf("create-security-group", securityGroupName, rulesPath).Wait(Config.DefaultTimeoutDuration())).To(Exit(0))
	})

	return securityGroupName
}

func bindSecurityGroup(securityGroupName, orgName, spaceName string) {
	By("Applying security group")
	workflowhelpers.AsUser(TestSetup.AdminUserContext(), Config.DefaultTimeoutDuration(), func() {
		Expect(cf.Cf("bind-security-group", securityGroupName, orgName, spaceName).Wait(Config.DefaultTimeoutDuration())).To(Exit(0))
	})
}

func unbindSecurityGroup(securityGroupName, orgName, spaceName string) {
	By("Unapplying security group")
	workflowhelpers.AsUser(TestSetup.AdminUserContext(), Config.DefaultTimeoutDuration(), func() {
		Expect(cf.Cf("unbind-security-group", securityGroupName, orgName, spaceName).Wait(Config.DefaultTimeoutDuration())).To(Exit(0))
	})
}

func deleteSecurityGroup(securityGroupName string) {
	workflowhelpers.AsUser(TestSetup.AdminUserContext(), Config.DefaultTimeoutDuration(), func() {
		Expect(cf.Cf("delete-security-group", securityGroupName, "-f").Wait(Config.DefaultTimeoutDuration())).To(Exit(0))
	})
}

func createDummyBuildpack() string {
	buildpack := random_name.CATSRandomName("BPK")
	buildpackZip := assets.NewAssets().SecurityGroupBuildpack

	workflowhelpers.AsUser(TestSetup.AdminUserContext(), Config.DefaultTimeoutDuration(), func() {
		Expect(cf.Cf("create-buildpack", buildpack, buildpackZip, "999").Wait(Config.DefaultTimeoutDuration())).To(Exit(0))
	})
	return buildpack
}

func deleteBuildpack(buildpack string) {
	workflowhelpers.AsUser(TestSetup.AdminUserContext(), Config.DefaultTimeoutDuration(), func() {
		Expect(cf.Cf("delete-buildpack", buildpack, "-f").Wait(Config.DefaultTimeoutDuration())).To(Exit(0))
	})
}

func getStagingOutput(appName string) func() *Session {
	return func() *Session {
		appLogsSession := cf.Cf("logs", "--recent", appName)
		Expect(appLogsSession.Wait(Config.DefaultTimeoutDuration())).To(Exit(0))
		return appLogsSession
	}
}

var _ = SecurityGroupsDescribe("Security Groups", func() {
	var serverAppName, privateHost string
	var privatePort int

	BeforeEach(func() {
		serverAppName = random_name.CATSRandomName("APP")
		pushApp(serverAppName, Config.GetRubyBuildpackName())
		Expect(cf.Cf("start", serverAppName).Wait(Config.CfPushTimeoutDuration())).To(Exit(0))

		privateHost, privatePort = getAppHostIpAndPort(serverAppName)
	})

	Describe("Running security-groups", func() {
		var clientAppName, securityGroupName string

		BeforeEach(func() {
			clientAppName = random_name.CATSRandomName("APP")
			pushApp(clientAppName, Config.GetRubyBuildpackName())
			Expect(cf.Cf("start", clientAppName).Wait(Config.CfPushTimeoutDuration())).To(Exit(0))

			By("Asserting default running security group configuration for traffic between containers")
			doraCurlResponse := testAppConnectivity(clientAppName, privateHost, privatePort)
			Expect(doraCurlResponse.ReturnCode).NotTo(Equal(0), "Expected default running security groups not to allow internal communication between app containers. Configure your running security groups to not allow traffic on internal networks, or disable this test by setting 'include_security_groups' to 'false' in '"+os.Getenv("CONFIG")+"'.")

			By("Asserting default running security group configuration from a running container to an external destination")
			doraCurlResponse = testAppConnectivity(clientAppName, "www.google.com", 80)
			Expect(doraCurlResponse.ReturnCode).To(Equal(0), "Expected default running security groups to allow external traffic from app containers. Configure your running security groups to not allow traffic on internal networks, or disable this test by setting 'include_security_groups' to 'false' in '"+os.Getenv("CONFIG")+"'.")
		})

		AfterEach(func() {
			app_helpers.AppReport(serverAppName, Config.DefaultTimeoutDuration())
			Expect(cf.Cf("delete", serverAppName, "-f", "-r").Wait(Config.CfPushTimeoutDuration())).To(Exit(0))

			app_helpers.AppReport(clientAppName, Config.DefaultTimeoutDuration())
			Expect(cf.Cf("delete", clientAppName, "-f", "-r").Wait(Config.CfPushTimeoutDuration())).To(Exit(0))

			deleteSecurityGroup(securityGroupName)
		})

		It("allows ip traffic between containers after applying a security group and blocks it when the security group is removed", func() {
			if Config.GetIncludeContainerNetworking() {
				Skip("Skipping this test because Config.ContainerNetworking is set to 'true'.")
			}

			containerIp, containerPort := getAppContainerIpAndPort(serverAppName)
			securityGroupName = createSecurityGroup(
				Destination{
					IP:       privateHost,
					Port:     privatePort,
					Protocol: "tcp",
				},
				Destination{
					IP:       containerIp,
					Port:     containerPort,
					Protocol: "tcp",
				},
			)

			By("binding new security group")
			bindSecurityGroup(securityGroupName, TestSetup.RegularUserContext().Org, TestSetup.RegularUserContext().Space)

			Expect(cf.Cf("restart", clientAppName).Wait(Config.CfPushTimeoutDuration())).To(Exit(0))

			By("Testing that app can connect")
			doraCurlResponse := testAppConnectivity(clientAppName, privateHost, privatePort)
			Expect(doraCurlResponse.ReturnCode).To(Equal(0))

			By("unbinding security group")
			unbindSecurityGroup(securityGroupName, TestSetup.RegularUserContext().Org, TestSetup.RegularUserContext().Space)
			Expect(cf.Cf("restart", clientAppName).Wait(Config.CfPushTimeoutDuration())).To(Exit(0))

			By("Testing that app can no longer connect")
			doraCurlResponse = testAppConnectivity(clientAppName, privateHost, privatePort)
			Expect(doraCurlResponse.ReturnCode).NotTo(Equal(0))
		})

		It("allows ip traffic between containers after applying a policy and blocks it when the policy is removed", func() {
			if !Config.GetIncludeContainerNetworking() {
				Skip("Skipping this test because Config.ContainerNetworking is set to 'false'.")
			}

			containerIp, containerPort := getAppContainerIpAndPort(serverAppName)
			orgName := TestSetup.RegularUserContext().Org
			spaceName := TestSetup.RegularUserContext().Space

			By("Testing that app cannot connect")
			doraCurlResponse := testAppConnectivity(clientAppName, containerIp, containerPort)
			Expect(doraCurlResponse.ReturnCode).NotTo(Equal(0))

			By("adding policy")
			workflowhelpers.AsUser(TestSetup.AdminUserContext(), Config.DefaultTimeoutDuration(), func() {
				Expect(cf.Cf("target", "-o", orgName, "-s", spaceName).Wait(Config.DefaultTimeoutDuration())).To(Exit(0))
				Expect(cf.Cf("allow-access", clientAppName, serverAppName, "--port", fmt.Sprintf("%d", containerPort), "--protocol", "tcp").Wait(Config.CfPushTimeoutDuration())).To(Exit(0))
			})

			By("waiting for policy to be added on cell")
			time.Sleep(10 * time.Second)

			By("Testing that app can connect")
			doraCurlResponse = testAppConnectivity(clientAppName, containerIp, containerPort)
			Expect(doraCurlResponse.ReturnCode).To(Equal(0))

			By("removing policy")
			workflowhelpers.AsUser(TestSetup.AdminUserContext(), Config.DefaultTimeoutDuration(), func() {
				Expect(cf.Cf("target", "-o", orgName, "-s", spaceName).Wait(Config.DefaultTimeoutDuration())).To(Exit(0))
				Expect(cf.Cf("deny-access", clientAppName, serverAppName, "--port", fmt.Sprintf("%d", containerPort), "--protocol", "tcp").Wait(Config.CfPushTimeoutDuration())).To(Exit(0))
			})

			By("waiting for policy to be removed on cell")
			time.Sleep(10 * time.Second)

			By("Testing that app can no longer connect")
			doraCurlResponse = testAppConnectivity(clientAppName, containerIp, containerPort)
			Expect(doraCurlResponse.ReturnCode).NotTo(Equal(0))
		})

		It("allows ip traffic from a container to a private, non-container destination after binding a security group and refuses it after unbinding the security group", func() {
			dest := Destination{
				IP:       "10.0.244.255", // some random IP that isn't covered by an existing Security Group rule
				Port:     80,
				Protocol: "tcp",
			}
			securityGroupName = createSecurityGroup(dest)
			By("binding new security group")
			bindSecurityGroup(securityGroupName, TestSetup.RegularUserContext().Org, TestSetup.RegularUserContext().Space)

			Expect(cf.Cf("restart", clientAppName).Wait(Config.CfPushTimeoutDuration())).To(Exit(0))

			By("Testing that connection is not refused (but may be unreachable for other reasons)")
			doraCurlResponse := testAppConnectivity(clientAppName, dest.IP, dest.Port)
			const CurlExitCode_FailedToConnectToHost = 7
			Expect(doraCurlResponse.ReturnCode).NotTo(Equal(CurlExitCode_FailedToConnectToHost))

			By("unbinding security group")
			unbindSecurityGroup(securityGroupName, TestSetup.RegularUserContext().Org, TestSetup.RegularUserContext().Space)
			Expect(cf.Cf("restart", clientAppName).Wait(Config.CfPushTimeoutDuration())).To(Exit(0))

			By("Testing the connect is refused")
			doraCurlResponse = testAppConnectivity(clientAppName, dest.IP, dest.Port)
			Expect(doraCurlResponse.ReturnCode).To(Equal(CurlExitCode_FailedToConnectToHost))
		})
	})

	Describe("staging security groups", func() {
		var testAppName, buildpack string

		BeforeEach(func() {
			By("Asserting default staging security group configuration")
			testAppName = random_name.CATSRandomName("APP")
			buildpack = createDummyBuildpack()
			pushApp(testAppName, buildpack)

			privateUri := fmt.Sprintf("%s:%d", privateHost, privatePort)
			Expect(cf.Cf("set-env", testAppName, "TESTURI", privateUri).Wait(Config.DefaultTimeoutDuration())).To(Exit(0))

			Expect(cf.Cf("start", testAppName).Wait(Config.CfPushTimeoutDuration())).To(Exit(1))
			Eventually(getStagingOutput(testAppName), 5).Should(Say("CURL_EXIT=[^0]"), "Expected staging security groups not to allow internal communication between app containers. Configure your staging security groups to not allow traffic on internal networks, or disable this test by setting 'include_security_groups' to 'false' in '"+os.Getenv("CONFIG")+"'.")
		})

		AfterEach(func() {
			app_helpers.AppReport(serverAppName, Config.DefaultTimeoutDuration())
			Expect(cf.Cf("delete", serverAppName, "-f", "-r").Wait(Config.CfPushTimeoutDuration())).To(Exit(0))

			app_helpers.AppReport(testAppName, Config.DefaultTimeoutDuration())
			Expect(cf.Cf("delete", testAppName, "-f", "-r").Wait(Config.CfPushTimeoutDuration())).To(Exit(0))

			deleteBuildpack(buildpack)
		})

		It("allows external and denies internal traffic during staging based on default staging security rules", func() {
			Expect(cf.Cf("set-env", testAppName, "TESTURI", "www.google.com").Wait(Config.DefaultTimeoutDuration())).To(Exit(0))
			Expect(cf.Cf("restart", testAppName).Wait(Config.CfPushTimeoutDuration())).To(Exit(1))
			Eventually(getStagingOutput(testAppName), 5).Should(Say("CURL_EXIT=0"))
		})
	})
})
<|endoftext|>


<commit_before>package main

import (
	. "github.com/xiaonanln/vacuum/ext/gameserver"
	"github.com/xiaonanln/vacuum/vlog"
)

type Account struct {
	GSEntityKind
}

func (a *Account) Login_OwnClient(username string, password string) {
	vlog.Info("%s.Login %s %s", a, username, password)
	if password != "123456" {
		a.Entity.CallClient("OnLogin", false)
		return
	}

	a.Entity.CallClient("OnLogin", true) // tell client that login ok
	// create the new Avatar entity

	CreateGSEntityAnywhere("Avatar")

}
<commit_msg>working<commit_after>package main

import (
	. "github.com/xiaonanln/vacuum/ext/gameserver"
	"github.com/xiaonanln/vacuum/vlog"
)

type Account struct {
	GSEntityKind
}

func (a *Account) Login_OwnClient(username string, password string) {
	vlog.Info("%s.Login %s %s", a, username, password)
	if password != "123456" {
		a.Entity.CallClient("OnLogin", false)
		return
	}

	a.Entity.CallClient("OnLogin", true) // tell client that login ok
	// create the new Avatar entity

	avatarID := CreateGSEntityAnywhere("Avatar")
	vlog.Debug("%s.Login: create Avatar %s", a, avatarID)
}
<|endoftext|>


<commit_before>//   Copyright 2017 MSolution.IO
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

package routes

import (
	"errors"
	"fmt"
	"net/http"
	"strconv"
	"strings"
)

type (
	// QueryArgInt denotes an int query argument. It fulfills the QueryParser
	// interface.
	QueryArgInt struct{}

	// QueryArgUint denotes an uint query argument. It fulfills the QueryParser
	// interface.
	QueryArgUint struct{}

	// QueryArgString denotes a string query argument. It fulfills the QueryParser
	// interface.
	QueryArgString struct{}

	// QueryArgIntSlice denotes a []int query argument. It fulfills the QueryParser
	// interface.
	QueryArgIntSlice struct{}

	// QueryArgUintSlice denotes a []uint query argument. It fulfills the QueryParser
	// interface.
	QueryArgUintSlice struct{}

	// QueryParser parses a string and returns a typed value.
	// An error can be returned if the value could not be parse. The error's
	// message contains %s which has to be replaced by the argument's name
	// before being displayed.
	QueryParser interface {
		QueryParse(string) (interface{}, error)
	}

	// QueryArg defines an argument by its name and its type.
	QueryArg struct {
		Name string
		Type QueryParser
	}

	// WithQueryArg contains all the arguments to parse in the URL.
	// WithQueryArg has a method Decorate called to apply the decorators
	// on an endpoint.
	WithQueryArg []QueryArg
)

// QueryParse parses an int. A nil error indicates a success. With this func,
// QueryArgInt fulfills QueryArgType.
func (d QueryArgInt) QueryParse(val string) (interface{}, error) {
	maxInt := int(^uint(0) >> 1)
	minInt := -maxInt - 1
	fmt.Printf("%v\n", minInt)
	if i, err := strconv.ParseInt(val, 10, 64); err == nil &&
		i <= int64(maxInt) && i >= int64(minInt) {
		return int(i), nil
	}
	return nil, errors.New("argument \"%s\" must be an int")
}

// QueryParse parses an uint. A nil error indicates a success. With this func,
// QueryArgInt fulfills QueryArgType.
func (d QueryArgUint) QueryParse(val string) (interface{}, error) {
	maxUint := ^uint(0)
	if i, err := strconv.ParseUint(val, 10, 64); err == nil &&
		i <= uint64(maxUint) {
		return uint(i), nil
	}
	return nil, errors.New("argument \"%s\" must be an uint")
}

// QueryParse parses a string. A nil error indicates a success. With this func,
// QueryArgInt fulfills QueryArgType.
func (d QueryArgString) QueryParse(val string) (interface{}, error) {
	return val, nil
}

// QueryParse parses an []int. A nil error indicates a success. With this func,
// QueryArgInt fulfills QueryArgType.
func (d QueryArgIntSlice) QueryParse(val string) (interface{}, error) {
	vals := strings.Split(val, ",")
	res := make([]int, 0, len(vals))
	for _, v := range vals {
		if i, err := strconv.ParseInt(v, 10, 64); err == nil {
			res = append(res, int(i))
		} else {
			return nil, errors.New("argument \"%s\" must be a slice of int")
		}
	}
	return res, nil
}

// QueryParse parses an []uint. A nil error indicates a success. With this func,
// QueryArgInt fulfills QueryArgType.
func (d QueryArgUintSlice) QueryParse(val string) (interface{}, error) {
	vals := strings.Split(val, ",")
	res := make([]uint, 0, len(vals))
	for _, v := range vals {
		if i, err := strconv.ParseUint(v, 10, 64); err == nil {
			res = append(res, uint(i))
		} else {
			return nil, errors.New("argument \"%s\" must be a slice of uint")
		}
	}
	return res, nil
}

func parseArg(arg QueryArg, r *http.Request, a Arguments) (int, ErrorBody) {
	if rawVal := r.URL.Query().Get(arg.Name); rawVal != "" {
		if val, err := arg.Type.QueryParse(rawVal); err == nil {
			a[arg] = val
		} else {
			msg := fmt.Sprintf(err.Error(), arg.Name)
			return 400, ErrorBody{msg}
		}
	} else {
		msg := fmt.Sprintf("argument \"%s\" not found", arg.Name)
		return 400, ErrorBody{msg}
	}
	return 200, ErrorBody{}
}

// Decorate is the function called to apply the decorators to an endpoint. It returns
// a function. This function produces a 400 error code with a json error message or
// calls the next IntermediateHandler.
// The goal of this function is to get the URL parameters to store them in
// the Arguments.
func (d WithQueryArg) Decorate(h IntermediateHandler) IntermediateHandler {
	return func(w http.ResponseWriter, r *http.Request, a Arguments) (status int, output interface{}) {
		for _, arg := range d {
			if code, err := parseArg(arg, r, a); code != 200 {
				return code, err
			}
		}
		return h(w, r, a)
	}
}
<commit_msg>Change way to handle overflow on Int & Uint Query Args.<commit_after>//   Copyright 2017 MSolution.IO
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

package routes

import (
	"errors"
	"fmt"
	"net/http"
	"strconv"
	"strings"
)

const (
	maxUint = ^uint(0)
	maxInt  = int(maxUint >> 1)
	minInt  = -maxInt - 1
)

type (
	// QueryArgInt denotes an int query argument. It fulfills the QueryParser
	// interface.
	QueryArgInt struct{}

	// QueryArgUint denotes an uint query argument. It fulfills the QueryParser
	// interface.
	QueryArgUint struct{}

	// QueryArgString denotes a string query argument. It fulfills the QueryParser
	// interface.
	QueryArgString struct{}

	// QueryArgIntSlice denotes a []int query argument. It fulfills the QueryParser
	// interface.
	QueryArgIntSlice struct{}

	// QueryArgUintSlice denotes a []uint query argument. It fulfills the QueryParser
	// interface.
	QueryArgUintSlice struct{}

	// QueryParser parses a string and returns a typed value.
	// An error can be returned if the value could not be parse. The error's
	// message contains %s which has to be replaced by the argument's name
	// before being displayed.
	QueryParser interface {
		QueryParse(string) (interface{}, error)
	}

	// QueryArg defines an argument by its name and its type.
	QueryArg struct {
		Name string
		Type QueryParser
	}

	// WithQueryArg contains all the arguments to parse in the URL.
	// WithQueryArg has a method Decorate called to apply the decorators
	// on an endpoint.
	WithQueryArg []QueryArg
)

// QueryParse parses an int. A nil error indicates a success. With this func,
// QueryArgInt fulfills QueryArgType.
func (d QueryArgInt) QueryParse(val string) (interface{}, error) {
	if i, err := strconv.ParseInt(val, 10, 64); err == nil &&
		i <= int64(maxInt) && i >= int64(minInt) {
		return int(i), nil
	}
	return nil, errors.New("argument \"%s\" must be an int")
}

// QueryParse parses an uint. A nil error indicates a success. With this func,
// QueryArgInt fulfills QueryArgType.
func (d QueryArgUint) QueryParse(val string) (interface{}, error) {
	if i, err := strconv.ParseUint(val, 10, 64); err == nil &&
		i <= uint64(maxUint) {
		return uint(i), nil
	}
	return nil, errors.New("argument \"%s\" must be an uint")
}

// QueryParse parses a string. A nil error indicates a success. With this func,
// QueryArgInt fulfills QueryArgType.
func (d QueryArgString) QueryParse(val string) (interface{}, error) {
	return val, nil
}

// QueryParse parses an []int. A nil error indicates a success. With this func,
// QueryArgInt fulfills QueryArgType.
func (d QueryArgIntSlice) QueryParse(val string) (interface{}, error) {
	vals := strings.Split(val, ",")
	res := make([]int, 0, len(vals))
	for _, v := range vals {
		if i, err := strconv.ParseInt(v, 10, 64); err == nil &&
			i <= int64(maxInt) && i >= int64(minInt) {
			res = append(res, int(i))
		} else {
			return nil, errors.New("argument \"%s\" must be a slice of int")
		}
	}
	return res, nil
}

// QueryParse parses an []uint. A nil error indicates a success. With this func,
// QueryArgInt fulfills QueryArgType.
func (d QueryArgUintSlice) QueryParse(val string) (interface{}, error) {
	vals := strings.Split(val, ",")
	res := make([]uint, 0, len(vals))
	for _, v := range vals {
		if i, err := strconv.ParseUint(v, 10, 64); err == nil &&
			i <= uint64(maxUint) {
			res = append(res, uint(i))
		} else {
			return nil, errors.New("argument \"%s\" must be a slice of uint")
		}
	}
	return res, nil
}

func parseArg(arg QueryArg, r *http.Request, a Arguments) (int, ErrorBody) {
	if rawVal := r.URL.Query().Get(arg.Name); rawVal != "" {
		if val, err := arg.Type.QueryParse(rawVal); err == nil {
			a[arg] = val
		} else {
			msg := fmt.Sprintf(err.Error(), arg.Name)
			return 400, ErrorBody{msg}
		}
	} else {
		msg := fmt.Sprintf("argument \"%s\" not found", arg.Name)
		return 400, ErrorBody{msg}
	}
	return 200, ErrorBody{}
}

// Decorate is the function called to apply the decorators to an endpoint. It returns
// a function. This function produces a 400 error code with a json error message or
// calls the next IntermediateHandler.
// The goal of this function is to get the URL parameters to store them in
// the Arguments.
func (d WithQueryArg) Decorate(h IntermediateHandler) IntermediateHandler {
	return func(w http.ResponseWriter, r *http.Request, a Arguments) (status int, output interface{}) {
		for _, arg := range d {
			if code, err := parseArg(arg, r, a); code != 200 {
				return code, err
			}
		}
		return h(w, r, a)
	}
}
<|endoftext|>


<commit_before>package clover

import (
	"crypto/sha512"
	"encoding/hex"
	"errors"
	"github.com/plimble/unik/mock_unik"
	"github.com/stretchr/testify/assert"
	"testing"
)

type mockResponseType struct {
	store *Mockallstore
}

func setUpCodeResponseType() (*codeRespType, *mockResponseType) {
	store := NewMockallstore()
	mock := &mockResponseType{store}

	config := NewAuthConfig(store)
	config.AddAuthCodeGrant(store)

	mu := mock_unik.NewMockGenerator()
	mu.On("Generate").Return("1")
	rt := newCodeRespType(config, mu)
	return rt, mock
}

func generateAuthRequest(resType string) *authorizeRequest {
	ar := &authorizeRequest{
		state:        "0",
		redirectURI:  "http://localhost",
		responseType: resType,
		scope:        "email",
		clientID:     "1001",
		client: &DefaultClient{
			ClientID:     "1",
			RedirectURI:  "http://localhost",
			ClientSecret: "xyz",
			GrantType:    []string{AUTHORIZATION_CODE},
			UserID:       "1",
			Scope:        []string{"email"},
		},
	}

	return ar
}

func genTestAuthCode(rt *codeRespType, ar *authorizeRequest) *AuthorizeCode {
	return &AuthorizeCode{
		Code:        hashCode("1"),
		ClientID:    ar.client.GetClientID(),
		UserID:      ar.client.GetUserID(),
		Expires:     addSecondUnix(rt.config.AuthCodeLifetime),
		Scope:       []string{"email"},
		RedirectURI: ar.redirectURI,
	}
}

func hashCode(code string) string {
	hasher := sha512.New()
	hasher.Write([]byte(code))
	return hex.EncodeToString(hasher.Sum(nil))
}

func TestGetAuthResponse(t *testing.T) {
	rt, mock := setUpCodeResponseType()
	ar := generateAuthRequest("code")
	ac := genTestAuthCode(rt, ar)

	mock.store.On("SetAuthorizeCode", ac).Return(nil)
	resp := rt.GetAuthResponse(ar, ar.client, ar.client.GetScope())

	assert.Equal(t, 302, resp.code)
	assert.False(t, resp.IsError())
}

func TestGetAuthResponseError(t *testing.T) {
	rt, mock := setUpCodeResponseType()
	ar := generateAuthRequest("code")
	ac := genTestAuthCode(rt, ar)

	mock.store.On("SetAuthorizeCode", ac).Return(errors.New("test"))
	resp := rt.GetAuthResponse(ar, ar.client, ar.client.GetScope())

	assert.Equal(t, 500, resp.code)
	assert.True(t, resp.IsError())
}

func TestCreateAuthCode(t *testing.T) {
	rt, mock := setUpCodeResponseType()
	ar := generateAuthRequest("code")
	ac := genTestAuthCode(rt, ar)

	mock.store.On("SetAuthorizeCode", ac).Return(nil)
	code, resp := rt.createAuthCode(ar.client, ar.client.GetScope(), ar.client.GetRedirectURI())

	assert.Nil(t, resp)
	assert.Equal(t, ac.Code, code.Code)
}

func TestCreateAuthCodeError(t *testing.T) {
	rt, mock := setUpCodeResponseType()
	ar := generateAuthRequest("code")
	ac := genTestAuthCode(rt, ar)

	mock.store.On("SetAuthorizeCode", ac).Return(errors.New("test"))
	code, resp := rt.createAuthCode(ar.client, ar.client.GetScope(), ar.client.GetRedirectURI())

	assert.Nil(t, code)
	assert.True(t, resp.IsError())
}

func TestGenerateAuthCode(t *testing.T) {
	rt, _ := setUpCodeResponseType()
	code := rt.generateAuthCode()
	assert.NotEmpty(t, code)
}
<commit_msg>update<commit_after>package clover

import (
	"errors"
	"github.com/plimble/unik/mock_unik"
	"github.com/stretchr/testify/assert"
	"testing"
)

type mockCodeRespType struct {
	store *Mockallstore
	unik  *mock_unik.MockGenerator
}

func setupCodeRespType() (*codeRespType, *mockCodeRespType) {
	store := NewMockallstore()
	unik := mock_unik.NewMockGenerator()

	config := NewAuthConfig(store)
	config.AuthCodeStore = store

	mock := &mockCodeRespType{store, unik}
	rt := newCodeRespType(config, unik)
	return rt, mock
}

func TestCodeRespType_GetAuthResponse(t *testing.T) {
	rt, m := setupCodeRespType()
	rt.config.AuthCodeLifetime = 60

	ar := &authorizeRequest{
		redirectURI:  "http://localhost/redirect",
		responseType: RESP_TYPE_CODE,
		state:        "123",
	}

	client := &DefaultClient{
		ClientID: "123",
		UserID:   "abc",
	}

	scopes := []string{"1", "2"}

	m.unik.On("Generate").Return("xyz")
	expAc := &AuthorizeCode{
		Code:        rt.generateAuthCode(),
		ClientID:    client.ClientID,
		UserID:      client.UserID,
		Expires:     addSecondUnix(60),
		Scope:       scopes,
		RedirectURI: ar.redirectURI,
	}

	m.unik.On("Generate").Return("xyz")
	m.store.On("SetAuthorizeCode", expAc).Return(nil)

	resp := rt.GetAuthResponse(ar, client, scopes)
	assert.False(t, resp.IsError())
	assert.Equal(t, 302, resp.code)
	assert.Equal(t, "http://localhost/redirect", resp.redirectURI)
	assert.Equal(t, map[string]interface{}{"code": expAc.Code, "state": "123"}, resp.data)
	assert.False(t, resp.isFragment)
}

func TestCodeRespType_CreateAuthCode(t *testing.T) {
	rt, m := setupCodeRespType()
	rt.config.AuthCodeLifetime = 60

	client := &DefaultClient{
		ClientID: "123",
		UserID:   "abc",
	}

	m.unik.On("Generate").Return("xyz")
	expAc := &AuthorizeCode{
		Code:        rt.generateAuthCode(),
		ClientID:    client.ClientID,
		UserID:      client.UserID,
		Expires:     addSecondUnix(60),
		Scope:       []string{"1", "2"},
		RedirectURI: "http://localhost/redirect",
	}

	m.unik.On("Generate").Return("xyz")
	m.store.On("SetAuthorizeCode", expAc).Return(nil)

	ac, resp := rt.createAuthCode(client, []string{"1", "2"}, "http://localhost/redirect")
	assert.Equal(t, expAc, ac)
	assert.Nil(t, resp)
}

func TestCodeRespType_CreateAuthCode_WithError(t *testing.T) {
	rt, m := setupCodeRespType()
	rt.config.AuthCodeLifetime = 60

	client := &DefaultClient{
		ClientID: "123",
		UserID:   "abc",
	}

	m.unik.On("Generate").Return("xyz")
	expAc := &AuthorizeCode{
		Code:        rt.generateAuthCode(),
		ClientID:    client.ClientID,
		UserID:      client.UserID,
		Expires:     addSecondUnix(60),
		Scope:       []string{"1", "2"},
		RedirectURI: "http://localhost/redirect",
	}

	m.unik.On("Generate").Return("xyz")
	m.store.On("SetAuthorizeCode", expAc).Return(errors.New("error"))

	ac, resp := rt.createAuthCode(client, []string{"1", "2"}, "http://localhost/redirect")
	assert.Nil(t, ac)
	assert.Equal(t, errInternal("error"), resp)
}

func TestCodeRespType_CreateRespData(t *testing.T) {
	rt, _ := setupCodeRespType()

	testCases := []struct {
		code     string
		state    string
		respData respData
	}{
		//with out state
		{code: "123", state: "", respData: respData{"code": "123"}},
		//with state
		{code: "123", state: "123", respData: respData{"code": "123", "state": "123"}},
	}

	for _, testCase := range testCases {
		data := rt.createRespData(testCase.code, testCase.state)
		assert.Equal(t, testCase.respData, data)
	}
}
<|endoftext|>


<commit_before>// +build linux
//
// Copyright (c) 2017 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//

package main

import (
	"io/ioutil"
	"os"
	"testing"

	"github.com/stretchr/testify/assert"
	"golang.org/x/sys/unix"
)

const (
	RawModeErr      = "should be properly set in raw mode"
	FlagRawModeErr  = "flag " + RawModeErr
	ValueRawModeErr = "value " + RawModeErr
)

func newTestTerminal(t *testing.T) (*os.File, error) {
	if os.Getuid() != 0 {
		t.Skip("Skipping this test: Requires to be root")
		return nil, nil
	}

	return os.OpenFile("/dev/tty", os.O_RDWR, os.ModeDevice)
}

func TestSetupTerminalOnNonTerminalFailure(t *testing.T) {
	file, err := ioutil.TempFile("", "tmp")
	assert.Nil(t, err, "Failed to create temporary file")
	defer file.Close()

	_, err = setupTerminal(int(file.Fd()))
	assert.NotNil(t, err, "Should fail because the file is not a terminal")
}

func TestSetupTerminalSuccess(t *testing.T) {
	file, err := newTestTerminal(t)
	assert.Nil(t, err, "Failed to create terminal")
	defer file.Close()

	savedTermios, err := setupTerminal(int(file.Fd()))
	assert.Nil(t, err, "Should not fail because the file is a terminal")

	termios, err := unix.IoctlGetTermios(int(file.Fd()), unix.TCGETS)
	assert.Nil(t, err, "Failed to get terminal information")
	assert.True(t, (termios.Iflag&termiosIFlagRawTermInvMask) == 0, "Termios I %s", FlagRawModeErr)
	assert.True(t, (termios.Oflag&termiosOFlagRawTermInvMask) == 0, "Termios O %s", FlagRawModeErr)
	assert.True(t, (termios.Lflag&termiosLFlagRawTermInvMask) == 0, "Termios L %s", FlagRawModeErr)
	assert.True(t, (termios.Cflag&termiosCFlagRawTermInvMask) == 0, "Termios C %s", FlagRawModeErr)
	assert.True(t, (termios.Cflag&termiosCFlagRawTermMask) == termiosCFlagRawTermMask, "Termios C %s", FlagRawModeErr)
	assert.True(t, termios.Cc[unix.VMIN] == termiosCcVMinRawTermVal, "Termios CC VMIN %s", ValueRawModeErr)
	assert.True(t, termios.Cc[unix.VTIME] == termiosCcVTimeRawTermVal, "Termios CC VTIME %s", ValueRawModeErr)

	err = restoreTerminal(int(file.Fd()), savedTermios)
	assert.Nil(t, err, "Terminal should be properly restored")
}
<commit_msg>tests: skip test if not tty<commit_after>// +build linux
//
// Copyright (c) 2017 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//

package main

import (
	"io/ioutil"
	"os"
	"syscall"
	"testing"

	"github.com/stretchr/testify/assert"
	"golang.org/x/sys/unix"
)

const (
	RawModeErr      = "should be properly set in raw mode"
	FlagRawModeErr  = "flag " + RawModeErr
	ValueRawModeErr = "value " + RawModeErr
)

func newTestTerminal(t *testing.T) (*os.File, error) {
	if os.Getuid() != 0 {
		t.Skip("Skipping this test: Requires to be root")
		return nil, nil
	}

	return os.OpenFile("/dev/tty", os.O_RDWR, os.ModeDevice)
}

func TestSetupTerminalOnNonTerminalFailure(t *testing.T) {
	file, err := ioutil.TempFile("", "tmp")
	assert.Nil(t, err, "Failed to create temporary file")
	defer file.Close()

	_, err = setupTerminal(int(file.Fd()))
	assert.NotNil(t, err, "Should fail because the file is not a terminal")
}

func TestSetupTerminalSuccess(t *testing.T) {
	file, err := newTestTerminal(t)

	if perr, ok := err.(*os.PathError); ok {
		switch perr.Err.(syscall.Errno) {
		case syscall.ENXIO:
			t.Skip("Skipping this test: Failed to open tty, make sure test is running in a tty")
		default:
			t.Fatalf("could not open tty %s", err)
		}
	}

	assert.Nil(t, err, "Failed to create terminal")
	defer file.Close()

	savedTermios, err := setupTerminal(int(file.Fd()))
	assert.Nil(t, err, "Should not fail because the file is a terminal")

	termios, err := unix.IoctlGetTermios(int(file.Fd()), unix.TCGETS)
	assert.Nil(t, err, "Failed to get terminal information")
	assert.True(t, (termios.Iflag&termiosIFlagRawTermInvMask) == 0, "Termios I %s", FlagRawModeErr)
	assert.True(t, (termios.Oflag&termiosOFlagRawTermInvMask) == 0, "Termios O %s", FlagRawModeErr)
	assert.True(t, (termios.Lflag&termiosLFlagRawTermInvMask) == 0, "Termios L %s", FlagRawModeErr)
	assert.True(t, (termios.Cflag&termiosCFlagRawTermInvMask) == 0, "Termios C %s", FlagRawModeErr)
	assert.True(t, (termios.Cflag&termiosCFlagRawTermMask) == termiosCFlagRawTermMask, "Termios C %s", FlagRawModeErr)
	assert.True(t, termios.Cc[unix.VMIN] == termiosCcVMinRawTermVal, "Termios CC VMIN %s", ValueRawModeErr)
	assert.True(t, termios.Cc[unix.VTIME] == termiosCcVTimeRawTermVal, "Termios CC VTIME %s", ValueRawModeErr)

	err = restoreTerminal(int(file.Fd()), savedTermios)
	assert.Nil(t, err, "Terminal should be properly restored")
}
<|endoftext|>


<commit_before>package lastfm

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"strconv"
	"time"
)

// Lastfm represents the Recenttracks json
// api.
type Lastfm struct {
	Recenttracks struct {
		Track []struct {
			Artist struct {
				Text string `json:"#text"`
				Mbid string `json:"mbid"`
			} `json:"artist"`
			Streamable string `json:"streamable"`
			Album      struct {
				Text string `json:"#text"`
				Mbid string `json:"mbid"`
			} `json:"album"`
			Image []struct {
				Text string `json:"#text"`
				Size string `json:"size"`
			} `json:"image"`
			Name string `json:"name"`
			Mbid string `json:"mbid"`
			Url  string `json:"url"`
			Attr struct {
				Nowplaying string `json:"nowplaying"`
			} `json:"@attr"`
			Date struct {
				Text string `json:"#text"`
				Uts  string `json:"uts"`
			} `json:"date"`
		} `json:"track"`
		Attr struct {
			User       string `json:"user"`
			Page       string `json:"page"`
			PerPage    string `json:"perPage"`
			TotalPages string `json:"totalPages"`
			Total      string `json:"total"`
		} `json:"@attr"`
	} `json:"recenttracks"`
}

// NewLastfm returns a new Lastfm instance with the given
// LastfmUser and LastfmKey.
func NewLastfm(LastfmUser, LastfmKey string) (*Lastfm, error) {
	var track *Lastfm

	url := "http://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=" + LastfmUser + "&api_key=" + LastfmKey + "&format=json"
	res, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}
	if err := json.Unmarshal(body, &track); err != nil {
		return nil, err
	}
	return track, nil
}

// IsNowPlaying returns the current track
// that is playing.
func (fm *Lastfm) IsNowPlaying() bool {
	return fm.Recenttracks.Track[0].Attr.Nowplaying != ""
}

// GetCurrentArtistAndTrackName returns the current artist name
// and track name of the track that is currently playing.
func (fm *Lastfm) GetCurrentArtistAndTrackName() (string, string) {
	return fm.Recenttracks.Track[0].Artist.Text, fm.Recenttracks.Track[0].Name
}

// GetLastPlayedDate returns the date of the last song that
// was played.
func (fm *Lastfm) GetLastPlayedDate() (string, error) {
	val, err := strconv.ParseInt(fm.Recenttracks.Track[0].Date.Uts, 10, 64)
	if err != nil {
		return "", err
	}
	tracktime := time.Unix(val, 0).UTC()
	return time.Since(tracktime).String(), nil
}
<commit_msg>update to api change<commit_after>package lastfm

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"strconv"
	"time"
)

// lastfmData represents the Recenttracks json
// api.
type lastfmData struct {
	Recenttracks struct {
		Track []struct {
			Artist struct {
				Text string `json:"#text"`
				Mbid string `json:"mbid"`
			} `json:"artist"`
			Streamable string `json:"streamable"`
			Album      struct {
				Text string `json:"#text"`
				Mbid string `json:"mbid"`
			} `json:"album"`
			Image []struct {
				Text string `json:"#text"`
				Size string `json:"size"`
			} `json:"image"`
			Name string `json:"name"`
			Mbid string `json:"mbid"`
			Url  string `json:"url"`
			Attr struct {
				Nowplaying string `json:"nowplaying"`
			} `json:"@attr"`
			Date struct {
				Text string `json:"#text"`
				Uts  string `json:"uts"`
			} `json:"date"`
		} `json:"track"`
		Attr struct {
			User       string `json:"user"`
			Page       string `json:"page"`
			PerPage    string `json:"perPage"`
			TotalPages string `json:"totalPages"`
			Total      string `json:"total"`
		} `json:"@attr"`
	} `json:"recenttracks"`
}

// LastfmApi struct for the api.
type LastfmApi struct {
	LastfmUser string
	LastfmKey  string
}

// New returns a new Lastfm instance with the given
// LastfmUser and LastfmKey.
func New(lastfmUser, lastfmKey string) *LastfmApi {
	return &LastfmApi{
		LastfmUser: lastfmUser,
		LastfmKey:  lastfmKey,
	}
}

// IsNowPlaying returns the current track
// that is playing.
func (fm *LastfmApi) IsNowPlaying() (bool, error) {
	var track *lastfmData
	url := "http://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=" + fm.LastfmUser +
		"&api_key=" + fm.LastfmKey + "&format=json"
	res, err := http.Get(url)
	if err != nil {
		return false, err
	}
	defer res.Body.Close()
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return false, err
	}
	if err := json.Unmarshal(body, &track); err != nil {
		return false, err
	}

	return track.Recenttracks.Track[0].Attr.Nowplaying != "", nil
}

// GetCurrentArtistAndTrackName returns the current artist name
// and track name of the track that is currently playing.
func (fm *LastfmApi) GetCurrentArtistAndTrackName() (string, string, error) {
	var track *lastfmData
	url := "http://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=" + fm.LastfmUser +
		"&api_key=" + fm.LastfmKey + "&format=json"
	res, err := http.Get(url)
	if err != nil {
		return "", "", err
	}
	defer res.Body.Close()
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return "", "", err
	}
	if err := json.Unmarshal(body, &track); err != nil {
		return "", "", err
	}

	return track.Recenttracks.Track[0].Artist.Text, track.Recenttracks.Track[0].Name, nil
}

// GetLastPlayedDate returns the date of the last song that
// was played.
func (fm *LastfmApi) GetLastPlayedDate() (string, error) {
	var track *lastfmData
	url := "http://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=" + fm.LastfmUser +
		"&api_key=" + fm.LastfmKey + "&format=json"
	res, err := http.Get(url)
	if err != nil {
		return "", err
	}
	defer res.Body.Close()
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return "", err
	}
	if err := json.Unmarshal(body, &track); err != nil {
		return "", err
	}

	val, err := strconv.ParseInt(track.Recenttracks.Track[0].Date.Uts, 10, 64)
	if err != nil {
		return "", err
	}
	tracktime := time.Unix(val, 0).UTC()

	return time.Since(tracktime).String(), nil
}
<|endoftext|>


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

package main

import (
	"fmt"
	"io/ioutil"
)

type Page struct {
	Title string
	Body []byte
}

func (p *Page) save() error {
	filename := p.Title + ".txt"
	return ioutil.WriteFile(filename, p.Body, 0600)
}

func loadPage(title string) (*Page, error) {
	filename := title + ".txt"
	body, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	return &Page{Title: title, Body: body}, nil
}

func main() {
	p1 := &Page{Title: "TestPage", Body: []byte("This is a sample Page.")}
	p1.save()
	p2, _ := loadPage("TestPage")
	fmt.Println(string(p2.Body))
}
<commit_msg>Implement simple viewHandler to serve wiki page<commit_after>// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
)

type Page struct {
	Title string
	Body []byte
}

func (p *Page) save() error {
	filename := p.Title + ".txt"
	return ioutil.WriteFile(filename, p.Body, 0600)
}

func loadPage(title string) (*Page, error) {
	filename := title + ".txt"
	body, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	return &Page{Title: title, Body: body}, nil
}

func viewHandler(w http.ResponseWriter, r *http.Request) {
	title := r.URL.Path[len("/view/"):]
	p, _ := loadPage(title)
	fmt.Fprintf(w, "
%s

%s
", p.Title, p.Body)
}

func main() {
	http.HandleFunc("/view/", viewHandler)
	http.ListenAndServe(":8080", nil)
}
<|endoftext|>


<commit_before>package fscache

import (
	"errors"
	"io"
	"sync"
)

type layeredCache struct {
	layers []Cache
}

// NewLayered returns a Cache which stores its data in all the passed
// caches, when a key is requested it is loaded into all the caches above the first hit.
func NewLayered(caches ...Cache) Cache {
	return &layeredCache{layers: caches}
}

func (l *layeredCache) Get(key string) (r ReadAtCloser, w io.WriteCloser, err error) {
	var last ReadAtCloser
	var writers []io.WriteCloser

	for i, layer := range l.layers {
		r, w, err = layer.Get(key)
		if err != nil {
			if len(writers) > 0 {
				last.Close()
				multiWC(writers...).Close()
			}
			return nil, nil, err
		}

		// hit
		if w == nil {
			if len(writers) > 0 {
				go func(r io.ReadCloser) {
					wc := multiWC(writers...)
					defer r.Close()
					defer wc.Close()
					io.Copy(wc, r)
				}(r)
				return last, nil, nil
			}
			return r, nil, nil
		}

		// miss
		writers = append(writers, w)

		if i == len(l.layers)-1 {
			if last != nil {
				last.Close()
			}
			return r, multiWC(writers...), nil
		}

		if last != nil {
			last.Close()
		}
		last = r
	}

	return nil, nil, errors.New("no caches")
}

func (l *layeredCache) Remove(key string) error {
	var grp sync.WaitGroup
	// walk upwards so that lower layers don't
	// restore upper layers on Get()
	for i := len(l.layers) - 1; i >= 0; i-- {
		grp.Add(1)
		go func(layer Cache) {
			defer grp.Done()
			layer.Remove(key)
		}(l.layers[i])
	}
	grp.Wait()
	return nil
}

func (l *layeredCache) Exists(key string) bool {
	for _, layer := range l.layers {
		if layer.Exists(key) {
			return true
		}
	}
	return false
}

func (l *layeredCache) Clean() (err error) {
	for _, layer := range l.layers {
		er := layer.Clean()
		if er != nil {
			err = er
		}
	}
	return nil
}

func multiWC(wc ...io.WriteCloser) io.WriteCloser {
	if len(wc) == 0 {
		return nil
	}

	return &multiWriteCloser{
		writers: wc,
	}
}

type multiWriteCloser struct {
	writers []io.WriteCloser
}

func (t *multiWriteCloser) Write(p []byte) (n int, err error) {
	for _, w := range t.writers {
		n, err = w.Write(p)
		if err != nil {
			return
		}
	}
	return len(p), nil
}

func (t *multiWriteCloser) Close() error {
	for _, w := range t.writers {
		w.Close()
	}
	return nil
}
<commit_msg>Fix Clean err check<commit_after>package fscache

import (
	"errors"
	"io"
	"sync"
)

type layeredCache struct {
	layers []Cache
}

// NewLayered returns a Cache which stores its data in all the passed
// caches, when a key is requested it is loaded into all the caches above the first hit.
func NewLayered(caches ...Cache) Cache {
	return &layeredCache{layers: caches}
}

func (l *layeredCache) Get(key string) (r ReadAtCloser, w io.WriteCloser, err error) {
	var last ReadAtCloser
	var writers []io.WriteCloser

	for i, layer := range l.layers {
		r, w, err = layer.Get(key)
		if err != nil {
			if len(writers) > 0 {
				last.Close()
				multiWC(writers...).Close()
			}
			return nil, nil, err
		}

		// hit
		if w == nil {
			if len(writers) > 0 {
				go func(r io.ReadCloser) {
					wc := multiWC(writers...)
					defer r.Close()
					defer wc.Close()
					io.Copy(wc, r)
				}(r)
				return last, nil, nil
			}
			return r, nil, nil
		}

		// miss
		writers = append(writers, w)

		if i == len(l.layers)-1 {
			if last != nil {
				last.Close()
			}
			return r, multiWC(writers...), nil
		}

		if last != nil {
			last.Close()
		}
		last = r
	}

	return nil, nil, errors.New("no caches")
}

func (l *layeredCache) Remove(key string) error {
	var grp sync.WaitGroup
	// walk upwards so that lower layers don't
	// restore upper layers on Get()
	for i := len(l.layers) - 1; i >= 0; i-- {
		grp.Add(1)
		go func(layer Cache) {
			defer grp.Done()
			layer.Remove(key)
		}(l.layers[i])
	}
	grp.Wait()
	return nil
}

func (l *layeredCache) Exists(key string) bool {
	for _, layer := range l.layers {
		if layer.Exists(key) {
			return true
		}
	}
	return false
}

func (l *layeredCache) Clean() (error) {
	for _, layer := range l.layers {
		if err := layer.Clean(); err != nil {
			return err
		}
	}
	return nil
}

func multiWC(wc ...io.WriteCloser) io.WriteCloser {
	if len(wc) == 0 {
		return nil
	}

	return &multiWriteCloser{
		writers: wc,
	}
}

type multiWriteCloser struct {
	writers []io.WriteCloser
}

func (t *multiWriteCloser) Write(p []byte) (n int, err error) {
	for _, w := range t.writers {
		n, err = w.Write(p)
		if err != nil {
			return
		}
	}
	return len(p), nil
}

func (t *multiWriteCloser) Close() error {
	for _, w := range t.writers {
		w.Close()
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2016 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package integration

import (
	"bufio"
	"context"
	"io"
	"net"
	"net/http"
	"strconv"
	"strings"
	"testing"
	"time"

	"go.etcd.io/etcd/clientv3"
	"go.etcd.io/etcd/integration"
	"go.etcd.io/etcd/pkg/testutil"
	"go.etcd.io/etcd/pkg/transport"

	grpcprom "github.com/grpc-ecosystem/go-grpc-prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"google.golang.org/grpc"
)

func TestV3ClientMetrics(t *testing.T) {
	defer testutil.AfterTest(t)

	var (
		addr = "localhost:27989"
		ln   net.Listener
		err  error
	)

	// listen for all Prometheus metrics
	donec := make(chan struct{})
	go func() {
		defer close(donec)

		srv := &http.Server{Handler: promhttp.Handler()}
		srv.SetKeepAlivesEnabled(false)

		ln, err = transport.NewUnixListener(addr)
		if err != nil {
			t.Errorf("Error: %v occurred while listening on addr: %v", err, addr)
		}

		err = srv.Serve(ln)
		if err != nil && !transport.IsClosedConnError(err) {
			t.Errorf("Err serving http requests: %v", err)
		}
	}()

	url := "unix://" + addr + "/metrics"

	clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1, SkipCreatingClient: true})
	defer clus.Terminate(t)

	cfg := clientv3.Config{
		Endpoints: []string{clus.Members[0].GRPCAddr()},
		DialOptions: []grpc.DialOption{
			grpc.WithUnaryInterceptor(grpcprom.UnaryClientInterceptor),
			grpc.WithStreamInterceptor(grpcprom.StreamClientInterceptor),
		},
	}
	cli, cerr := clientv3.New(cfg)
	if cerr != nil {
		t.Fatal(cerr)
	}
	defer cli.Close()

	wc := cli.Watch(context.Background(), "foo")

	wBefore := sumCountersForMetricAndLabels(t, url, "grpc_client_msg_received_total", "Watch", "bidi_stream")

	pBefore := sumCountersForMetricAndLabels(t, url, "grpc_client_started_total", "Put", "unary")

	_, err = cli.Put(context.Background(), "foo", "bar")
	if err != nil {
		t.Errorf("Error putting value in key store")
	}

	pAfter := sumCountersForMetricAndLabels(t, url, "grpc_client_started_total", "Put", "unary")
	if pBefore+1 != pAfter {
		t.Errorf("grpc_client_started_total expected %d, got %d", 1, pAfter-pBefore)
	}

	// consume watch response
	select {
	case <-wc:
	case <-time.After(10 * time.Second):
		t.Error("Timeout occurred for getting watch response")
	}

	wAfter := sumCountersForMetricAndLabels(t, url, "grpc_client_msg_received_total", "Watch", "bidi_stream")
	if wBefore+1 != wAfter {
		t.Errorf("grpc_client_msg_received_total expected %d, got %d", 1, wAfter-wBefore)
	}

	ln.Close()
	<-donec
}

func sumCountersForMetricAndLabels(t *testing.T, url string, metricName string, matchingLabelValues ...string) int {
	count := 0
	for _, line := range getHTTPBodyAsLines(t, url) {
		ok := true
		if !strings.HasPrefix(line, metricName) {
			continue
		}

		for _, labelValue := range matchingLabelValues {
			if !strings.Contains(line, `"`+labelValue+`"`) {
				ok = false
				break
			}
		}

		if !ok {
			continue
		}

		valueString := line[strings.LastIndex(line, " ")+1 : len(line)-1]
		valueFloat, err := strconv.ParseFloat(valueString, 32)
		if err != nil {
			t.Fatalf("failed parsing value for line: %v and matchingLabelValues: %v", line, matchingLabelValues)
		}
		count += int(valueFloat)
	}
	return count
}

func getHTTPBodyAsLines(t *testing.T, url string) []string {
	cfgtls := transport.TLSInfo{}
	tr, err := transport.NewTransport(cfgtls, time.Second)
	if err != nil {
		t.Fatalf("Error getting transport: %v", err)
	}

	tr.MaxIdleConns = -1
	tr.DisableKeepAlives = true

	cli := &http.Client{Transport: tr}

	resp, err := cli.Get(url)
	if err != nil {
		t.Fatalf("Error fetching: %v", err)
	}

	reader := bufio.NewReader(resp.Body)
	lines := []string{}
	for {
		line, err := reader.ReadString('\n')
		if err != nil {
			if err == io.EOF {
				break
			} else {
				t.Fatalf("error reading: %v", err)
			}
		}
		lines = append(lines, line)
	}
	resp.Body.Close()
	return lines
}
<commit_msg>integration: fix a data race about `err`<commit_after>// Copyright 2016 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package integration

import (
	"bufio"
	"context"
	"io"
	"net"
	"net/http"
	"strconv"
	"strings"
	"testing"
	"time"

	"go.etcd.io/etcd/clientv3"
	"go.etcd.io/etcd/integration"
	"go.etcd.io/etcd/pkg/testutil"
	"go.etcd.io/etcd/pkg/transport"

	grpcprom "github.com/grpc-ecosystem/go-grpc-prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"google.golang.org/grpc"
)

func TestV3ClientMetrics(t *testing.T) {
	defer testutil.AfterTest(t)

	var (
		addr = "localhost:27989"
		ln   net.Listener
	)

	// listen for all Prometheus metrics
	donec := make(chan struct{})
	go func() {
		var err error

		defer close(donec)

		srv := &http.Server{Handler: promhttp.Handler()}
		srv.SetKeepAlivesEnabled(false)

		ln, err = transport.NewUnixListener(addr)
		if err != nil {
			t.Errorf("Error: %v occurred while listening on addr: %v", err, addr)
		}

		err = srv.Serve(ln)
		if err != nil && !transport.IsClosedConnError(err) {
			t.Errorf("Err serving http requests: %v", err)
		}
	}()

	url := "unix://" + addr + "/metrics"

	clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1, SkipCreatingClient: true})
	defer clus.Terminate(t)

	cfg := clientv3.Config{
		Endpoints: []string{clus.Members[0].GRPCAddr()},
		DialOptions: []grpc.DialOption{
			grpc.WithUnaryInterceptor(grpcprom.UnaryClientInterceptor),
			grpc.WithStreamInterceptor(grpcprom.StreamClientInterceptor),
		},
	}
	cli, cerr := clientv3.New(cfg)
	if cerr != nil {
		t.Fatal(cerr)
	}
	defer cli.Close()

	wc := cli.Watch(context.Background(), "foo")

	wBefore := sumCountersForMetricAndLabels(t, url, "grpc_client_msg_received_total", "Watch", "bidi_stream")

	pBefore := sumCountersForMetricAndLabels(t, url, "grpc_client_started_total", "Put", "unary")

	_, err := cli.Put(context.Background(), "foo", "bar")
	if err != nil {
		t.Errorf("Error putting value in key store")
	}

	pAfter := sumCountersForMetricAndLabels(t, url, "grpc_client_started_total", "Put", "unary")
	if pBefore+1 != pAfter {
		t.Errorf("grpc_client_started_total expected %d, got %d", 1, pAfter-pBefore)
	}

	// consume watch response
	select {
	case <-wc:
	case <-time.After(10 * time.Second):
		t.Error("Timeout occurred for getting watch response")
	}

	wAfter := sumCountersForMetricAndLabels(t, url, "grpc_client_msg_received_total", "Watch", "bidi_stream")
	if wBefore+1 != wAfter {
		t.Errorf("grpc_client_msg_received_total expected %d, got %d", 1, wAfter-wBefore)
	}

	ln.Close()
	<-donec
}

func sumCountersForMetricAndLabels(t *testing.T, url string, metricName string, matchingLabelValues ...string) int {
	count := 0
	for _, line := range getHTTPBodyAsLines(t, url) {
		ok := true
		if !strings.HasPrefix(line, metricName) {
			continue
		}

		for _, labelValue := range matchingLabelValues {
			if !strings.Contains(line, `"`+labelValue+`"`) {
				ok = false
				break
			}
		}

		if !ok {
			continue
		}

		valueString := line[strings.LastIndex(line, " ")+1 : len(line)-1]
		valueFloat, err := strconv.ParseFloat(valueString, 32)
		if err != nil {
			t.Fatalf("failed parsing value for line: %v and matchingLabelValues: %v", line, matchingLabelValues)
		}
		count += int(valueFloat)
	}
	return count
}

func getHTTPBodyAsLines(t *testing.T, url string) []string {
	cfgtls := transport.TLSInfo{}
	tr, err := transport.NewTransport(cfgtls, time.Second)
	if err != nil {
		t.Fatalf("Error getting transport: %v", err)
	}

	tr.MaxIdleConns = -1
	tr.DisableKeepAlives = true

	cli := &http.Client{Transport: tr}

	resp, err := cli.Get(url)
	if err != nil {
		t.Fatalf("Error fetching: %v", err)
	}

	reader := bufio.NewReader(resp.Body)
	lines := []string{}
	for {
		line, err := reader.ReadString('\n')
		if err != nil {
			if err == io.EOF {
				break
			} else {
				t.Fatalf("error reading: %v", err)
			}
		}
		lines = append(lines, line)
	}
	resp.Body.Close()
	return lines
}
<|endoftext|>


<commit_before>package wrphttp

import (
	"net/http"
	"net/url"
	"time"

	"github.com/Comcast/webpa-common/httperror"
	"github.com/Comcast/webpa-common/logging"
	"github.com/Comcast/webpa-common/middleware"
	"github.com/Comcast/webpa-common/tracing"
	"github.com/Comcast/webpa-common/wrp"
	"github.com/go-kit/kit/endpoint"
	"github.com/go-kit/kit/log"
	gokithttp "github.com/go-kit/kit/transport/http"
)

const (
	DefaultMethod                            = "POST"
	DefaultEndpoint                          = "http://localhost:7000/api/v2/device"
	DefaultMaxIdleConnsPerHost               = 20
	DefaultFanoutTimeout       time.Duration = 45 * time.Second
	DefaultClientTimeout       time.Duration = 30 * time.Second
	DefaultMaxClients          int64         = 10000
	DefaultConcurrency                       = 1000
	DefaultEncoderPoolSize                   = 100
	DefaultDecoderPoolSize                   = 100
)

// FanoutOptions describe the options available for a go-kit HTTP server that does fanout via middleware.Fanout.
type FanoutOptions struct {
	// Logger is the go-kit logger to use when creating the service fanout.  If not set, logging.DefaultLogger is used.
	Logger log.Logger `json:"-"`

	// Method is the HTTP method to use for all endpoints.  If not set, DefaultMethod is used.
	Method string `json:"method,omitempty"`

	// Endpoints are the URLs for each endpoint to fan out to.  If not set, DefaultEndpoint is used.
	Endpoints []string `json:"endpoints,omitempty"`

	// Transport is the http.Client transport
	Transport http.Transport `json:"transport"`

	// FanoutTimeout is the timeout for the entire fanout operation.  If not supplied, DefaultFanoutTimeout is used.
	FanoutTimeout time.Duration `json:"timeout"`

	// ClientTimeout is the http.Client Timeout.  If not set, DefaultClientTimeout is used.
	ClientTimeout time.Duration `json:"clientTimeout"`

	// MaxClients is the maximum number of concurrent clients that can be using the fanout.  This should be set to
	// something larger than the Concurrency field.
	MaxClients int64 `json:"maxClients"`

	// Concurrency is the maximum number of concurrent fanouts allowed.  This is enforced via a Concurrent middleware.
	// If this is not set, DefaultConcurrency is used.
	Concurrency int `json:"concurrency"`

	// EncoderPoolSize is the size of the WRP encoder pool.  If not set, DefaultEncoderPoolSize is used.
	EncoderPoolSize int

	// DecoderPoolSize is the size of the WRP encoder pool.  If not set, DefaultDecoderPoolSize is used.
	DecoderPoolSize int

	// Middleware is the extra Middleware to append, which can (and often is) empty
	Middleware []endpoint.Middleware `json:"-"`
}

func (f *FanoutOptions) logger() log.Logger {
	if f != nil && f.Logger != nil {
		return f.Logger
	}

	return logging.DefaultLogger()
}

func (f *FanoutOptions) method() string {
	if f != nil && len(f.Method) > 0 {
		return f.Method
	}

	return DefaultMethod
}

func (f *FanoutOptions) endpoints() []string {
	if f != nil && len(f.Endpoints) > 0 {
		return f.Endpoints
	}

	return []string{DefaultEndpoint}
}

func (f *FanoutOptions) urls() ([]*url.URL, error) {
	var urls []*url.URL
	for _, endpoint := range f.endpoints() {
		url, err := url.Parse(endpoint)
		if err != nil {
			return nil, err
		}

		urls = append(urls, url)
	}

	return urls, nil
}

func (f *FanoutOptions) transport() *http.Transport {
	transport := new(http.Transport)

	if f != nil {
		*transport = f.Transport
	}

	if transport.MaxIdleConnsPerHost < 1 {
		transport.MaxIdleConnsPerHost = DefaultMaxIdleConnsPerHost
	}

	return transport
}

func (f *FanoutOptions) fanoutTimeout() time.Duration {
	if f != nil && f.FanoutTimeout > 0 {
		return f.FanoutTimeout
	}

	return DefaultFanoutTimeout
}

func (f *FanoutOptions) clientTimeout() time.Duration {
	if f != nil && f.ClientTimeout > 0 {
		return f.ClientTimeout
	}

	return DefaultClientTimeout
}

func (f *FanoutOptions) maxClients() int64 {
	if f != nil && f.MaxClients > 0 {
		return f.MaxClients
	}

	return DefaultMaxClients
}

func (f *FanoutOptions) concurrency() int {
	if f != nil && f.Concurrency > 0 {
		return f.Concurrency
	}

	return DefaultConcurrency
}

func (f *FanoutOptions) encoderPoolSize() int {
	if f != nil && f.EncoderPoolSize > 0 {
		return f.EncoderPoolSize
	}

	return DefaultEncoderPoolSize
}

func (f *FanoutOptions) decoderPoolSize() int {
	if f != nil && f.DecoderPoolSize > 0 {
		return f.DecoderPoolSize
	}

	return DefaultDecoderPoolSize
}

func (f *FanoutOptions) middleware() []endpoint.Middleware {
	if f != nil {
		return f.Middleware
	}

	return nil
}

// NewEncoderPool creates a wrp.EncoderPool using this options, which can be nil to take defaults
func (o *FanoutOptions) NewEncoderPool(format wrp.Format) *wrp.EncoderPool {
	return wrp.NewEncoderPool(o.encoderPoolSize(), format)
}

// NewDecoderPool creates a wrp.DecoderPool using this options, which can be nil to take defaults
func (o *FanoutOptions) NewDecoderPool(format wrp.Format) *wrp.DecoderPool {
	return wrp.NewDecoderPool(o.decoderPoolSize(), format)
}

// NewFanoutEndpoint uses the supplied options to produce a go-kit HTTP server endpoint which
// fans out to the HTTP endpoints specified in the options.  The endpoint returned from this
// can be used to build one or more go-kit transport/http.Server objects.
//
// The FanoutOptions can be nil, in which case a set of defaults is used.
func NewFanoutEndpoint(o *FanoutOptions) (endpoint.Endpoint, error) {
	urls, err := o.urls()
	if err != nil {
		return nil, err
	}

	var (
		encoderPool = o.NewEncoderPool(wrp.Msgpack)
		decoderPool = o.NewDecoderPool(wrp.Msgpack)

		httpClient = &http.Client{
			Transport: o.transport(),
			Timeout:   o.clientTimeout(),
		}

		fanoutEndpoints = make(map[string]endpoint.Endpoint, len(urls))
	)

	for _, url := range urls {
		fanoutEndpoints[url.String()] =
			gokithttp.NewClient(
				o.method(),
				url,
				ClientEncodeRequestBody(encoderPool, http.Header{"Accept": []string{"application/msgpack"}}),
				ClientDecodeResponseBody(decoderPool),
				gokithttp.SetClient(httpClient),
			).Endpoint()
	}

	var (
		middlewareChain = append(
			[]endpoint.Middleware{
				middleware.Logging,
				middleware.Busy(o.maxClients(), &httperror.E{Code: http.StatusServiceUnavailable, Text: "Server Busy"}),
				middleware.Timeout(o.fanoutTimeout()),
				middleware.Concurrent(o.concurrency(), &httperror.E{Code: http.StatusTooManyRequests, Text: "Too Many Requests"}),
			},
			o.middleware()...,
		)
	)

	return endpoint.Chain(
			middlewareChain[0],
			middlewareChain[1:]...,
		)(middleware.Fanout(tracing.NewSpanner(), fanoutEndpoints)),
		nil
}
<commit_msg>Fixed the default endpoint to use talaria's /device/send on the same default port<commit_after>package wrphttp

import (
	"net/http"
	"net/url"
	"time"

	"github.com/Comcast/webpa-common/httperror"
	"github.com/Comcast/webpa-common/logging"
	"github.com/Comcast/webpa-common/middleware"
	"github.com/Comcast/webpa-common/tracing"
	"github.com/Comcast/webpa-common/wrp"
	"github.com/go-kit/kit/endpoint"
	"github.com/go-kit/kit/log"
	gokithttp "github.com/go-kit/kit/transport/http"
)

const (
	DefaultMethod                            = "POST"
	DefaultEndpoint                          = "http://localhost:7000/api/v2/device/send"
	DefaultMaxIdleConnsPerHost               = 20
	DefaultFanoutTimeout       time.Duration = 45 * time.Second
	DefaultClientTimeout       time.Duration = 30 * time.Second
	DefaultMaxClients          int64         = 10000
	DefaultConcurrency                       = 1000
	DefaultEncoderPoolSize                   = 100
	DefaultDecoderPoolSize                   = 100
)

// FanoutOptions describe the options available for a go-kit HTTP server that does fanout via middleware.Fanout.
type FanoutOptions struct {
	// Logger is the go-kit logger to use when creating the service fanout.  If not set, logging.DefaultLogger is used.
	Logger log.Logger `json:"-"`

	// Method is the HTTP method to use for all endpoints.  If not set, DefaultMethod is used.
	Method string `json:"method,omitempty"`

	// Endpoints are the URLs for each endpoint to fan out to.  If not set, DefaultEndpoint is used.
	Endpoints []string `json:"endpoints,omitempty"`

	// Transport is the http.Client transport
	Transport http.Transport `json:"transport"`

	// FanoutTimeout is the timeout for the entire fanout operation.  If not supplied, DefaultFanoutTimeout is used.
	FanoutTimeout time.Duration `json:"timeout"`

	// ClientTimeout is the http.Client Timeout.  If not set, DefaultClientTimeout is used.
	ClientTimeout time.Duration `json:"clientTimeout"`

	// MaxClients is the maximum number of concurrent clients that can be using the fanout.  This should be set to
	// something larger than the Concurrency field.
	MaxClients int64 `json:"maxClients"`

	// Concurrency is the maximum number of concurrent fanouts allowed.  This is enforced via a Concurrent middleware.
	// If this is not set, DefaultConcurrency is used.
	Concurrency int `json:"concurrency"`

	// EncoderPoolSize is the size of the WRP encoder pool.  If not set, DefaultEncoderPoolSize is used.
	EncoderPoolSize int

	// DecoderPoolSize is the size of the WRP encoder pool.  If not set, DefaultDecoderPoolSize is used.
	DecoderPoolSize int

	// Middleware is the extra Middleware to append, which can (and often is) empty
	Middleware []endpoint.Middleware `json:"-"`
}

func (f *FanoutOptions) logger() log.Logger {
	if f != nil && f.Logger != nil {
		return f.Logger
	}

	return logging.DefaultLogger()
}

func (f *FanoutOptions) method() string {
	if f != nil && len(f.Method) > 0 {
		return f.Method
	}

	return DefaultMethod
}

func (f *FanoutOptions) endpoints() []string {
	if f != nil && len(f.Endpoints) > 0 {
		return f.Endpoints
	}

	return []string{DefaultEndpoint}
}

func (f *FanoutOptions) urls() ([]*url.URL, error) {
	var urls []*url.URL
	for _, endpoint := range f.endpoints() {
		url, err := url.Parse(endpoint)
		if err != nil {
			return nil, err
		}

		urls = append(urls, url)
	}

	return urls, nil
}

func (f *FanoutOptions) transport() *http.Transport {
	transport := new(http.Transport)

	if f != nil {
		*transport = f.Transport
	}

	if transport.MaxIdleConnsPerHost < 1 {
		transport.MaxIdleConnsPerHost = DefaultMaxIdleConnsPerHost
	}

	return transport
}

func (f *FanoutOptions) fanoutTimeout() time.Duration {
	if f != nil && f.FanoutTimeout > 0 {
		return f.FanoutTimeout
	}

	return DefaultFanoutTimeout
}

func (f *FanoutOptions) clientTimeout() time.Duration {
	if f != nil && f.ClientTimeout > 0 {
		return f.ClientTimeout
	}

	return DefaultClientTimeout
}

func (f *FanoutOptions) maxClients() int64 {
	if f != nil && f.MaxClients > 0 {
		return f.MaxClients
	}

	return DefaultMaxClients
}

func (f *FanoutOptions) concurrency() int {
	if f != nil && f.Concurrency > 0 {
		return f.Concurrency
	}

	return DefaultConcurrency
}

func (f *FanoutOptions) encoderPoolSize() int {
	if f != nil && f.EncoderPoolSize > 0 {
		return f.EncoderPoolSize
	}

	return DefaultEncoderPoolSize
}

func (f *FanoutOptions) decoderPoolSize() int {
	if f != nil && f.DecoderPoolSize > 0 {
		return f.DecoderPoolSize
	}

	return DefaultDecoderPoolSize
}

func (f *FanoutOptions) middleware() []endpoint.Middleware {
	if f != nil {
		return f.Middleware
	}

	return nil
}

// NewEncoderPool creates a wrp.EncoderPool using this options, which can be nil to take defaults
func (o *FanoutOptions) NewEncoderPool(format wrp.Format) *wrp.EncoderPool {
	return wrp.NewEncoderPool(o.encoderPoolSize(), format)
}

// NewDecoderPool creates a wrp.DecoderPool using this options, which can be nil to take defaults
func (o *FanoutOptions) NewDecoderPool(format wrp.Format) *wrp.DecoderPool {
	return wrp.NewDecoderPool(o.decoderPoolSize(), format)
}

// NewFanoutEndpoint uses the supplied options to produce a go-kit HTTP server endpoint which
// fans out to the HTTP endpoints specified in the options.  The endpoint returned from this
// can be used to build one or more go-kit transport/http.Server objects.
//
// The FanoutOptions can be nil, in which case a set of defaults is used.
func NewFanoutEndpoint(o *FanoutOptions) (endpoint.Endpoint, error) {
	urls, err := o.urls()
	if err != nil {
		return nil, err
	}

	var (
		encoderPool = o.NewEncoderPool(wrp.Msgpack)
		decoderPool = o.NewDecoderPool(wrp.Msgpack)

		httpClient = &http.Client{
			Transport: o.transport(),
			Timeout:   o.clientTimeout(),
		}

		fanoutEndpoints = make(map[string]endpoint.Endpoint, len(urls))
	)

	for _, url := range urls {
		fanoutEndpoints[url.String()] =
			gokithttp.NewClient(
				o.method(),
				url,
				ClientEncodeRequestBody(encoderPool, http.Header{"Accept": []string{"application/msgpack"}}),
				ClientDecodeResponseBody(decoderPool),
				gokithttp.SetClient(httpClient),
			).Endpoint()
	}

	var (
		middlewareChain = append(
			[]endpoint.Middleware{
				middleware.Logging,
				middleware.Busy(o.maxClients(), &httperror.E{Code: http.StatusServiceUnavailable, Text: "Server Busy"}),
				middleware.Timeout(o.fanoutTimeout()),
				middleware.Concurrent(o.concurrency(), &httperror.E{Code: http.StatusTooManyRequests, Text: "Too Many Requests"}),
			},
			o.middleware()...,
		)
	)

	return endpoint.Chain(
			middlewareChain[0],
			middlewareChain[1:]...,
		)(middleware.Fanout(tracing.NewSpanner(), fanoutEndpoints)),
		nil
}
<|endoftext|>


<commit_before>package app

import (
	"sync"

	"github.com/moncho/dry/appui"
	"github.com/moncho/dry/appui/swarm"
	"github.com/moncho/dry/docker"
	"github.com/moncho/dry/ui"
	"github.com/moncho/dry/ui/termui"
)

//widgetRegistry holds two sets of widgets:
// * those registered in the the registry when it was created, that
//   can be reused. These are the individually named widgets found on
//   this struct.
// * a list of widgets to be rendered on the next rendering.
type widgetRegistry struct {
	ContainerList *appui.ContainersWidget
	ContainerMenu *appui.ContainerMenuWidget
	DiskUsage     *appui.DockerDiskUsageRenderer
	DockerInfo    *appui.DockerInfo
	ImageList     *appui.DockerImagesWidget
	Monitor       *appui.Monitor
	Networks      *appui.DockerNetworksWidget
	Nodes         *swarm.NodesWidget
	NodeTasks     *swarm.NodeTasksWidget
	ServiceTasks  *swarm.ServiceTasksWidget
	ServiceList   *swarm.ServicesWidget
	Stacks        *swarm.StacksWidget
	StackTasks    *swarm.StacksTasksWidget
	MessageBar    *ui.ExpiringMessageWidget
	activeWidgets map[string]termui.Widget
	sync.Mutex
}

//NewWidgetRegistry creates the WidgetCatalog
func newWidgetRegistry(daemon docker.ContainerDaemon) *widgetRegistry {
	di := appui.NewDockerInfo(daemon)
	di.SetX(0)
	di.SetY(1)
	di.SetWidth(ui.ActiveScreen.Dimensions.Width)
	w := widgetRegistry{
		DockerInfo:    di,
		ContainerList: appui.NewContainersWidget(daemon, appui.MainScreenHeaderSize),
		ContainerMenu: appui.NewContainerMenuWidget(daemon, appui.MainScreenHeaderSize),
		ImageList:     appui.NewDockerImagesWidget(daemon, appui.MainScreenHeaderSize),
		DiskUsage:     appui.NewDockerDiskUsageRenderer(ui.ActiveScreen.Dimensions.Height),
		Monitor:       appui.NewMonitor(daemon, appui.MainScreenHeaderSize),
		Networks:      appui.NewDockerNetworksWidget(daemon, appui.MainScreenHeaderSize),
		Nodes:         swarm.NewNodesWidget(daemon, appui.MainScreenHeaderSize),
		NodeTasks:     swarm.NewNodeTasksWidget(daemon, appui.MainScreenHeaderSize),
		ServiceTasks:  swarm.NewServiceTasksWidget(daemon, appui.MainScreenHeaderSize),
		ServiceList:   swarm.NewServicesWidget(daemon, appui.MainScreenHeaderSize),
		Stacks:        swarm.NewStacksWidget(daemon, appui.MainScreenHeaderSize),
		StackTasks:    swarm.NewStacksTasksWidget(daemon, appui.MainScreenHeaderSize),
		activeWidgets: make(map[string]termui.Widget),
		MessageBar:    ui.NewExpiringMessageWidget(0, ui.ActiveScreen.Dimensions.Width, appui.DryTheme),
	}

	return &w
}

func (wr *widgetRegistry) add(w termui.Widget) {
	wr.Lock()
	defer wr.Unlock()
	if err := w.Mount(); err == nil {
		wr.activeWidgets[w.Name()] = w
	}
}

func (wr *widgetRegistry) remove(w termui.Widget) {
	wr.Lock()
	defer wr.Unlock()
	if err := w.Unmount(); err == nil {
		delete(wr.activeWidgets, w.Name())
	}
}
<commit_msg>Register widgets for docker notifications<commit_after>package app

import (
	"context"
	"sync"
	"time"

	"github.com/docker/docker/api/types/events"

	"github.com/moncho/dry/appui"
	"github.com/moncho/dry/appui/swarm"
	"github.com/moncho/dry/docker"
	"github.com/moncho/dry/ui"
	"github.com/moncho/dry/ui/termui"
)

//widgetRegistry holds two sets of widgets:
// * those registered in the the registry when it was created, that
//   can be reused. These are the individually named widgets found on
//   this struct.
// * a list of widgets to be rendered on the next rendering.
type widgetRegistry struct {
	ContainerList *appui.ContainersWidget
	ContainerMenu *appui.ContainerMenuWidget
	DiskUsage     *appui.DockerDiskUsageRenderer
	DockerInfo    *appui.DockerInfo
	ImageList     *appui.DockerImagesWidget
	Monitor       *appui.Monitor
	Networks      *appui.DockerNetworksWidget
	Nodes         *swarm.NodesWidget
	NodeTasks     *swarm.NodeTasksWidget
	ServiceTasks  *swarm.ServiceTasksWidget
	ServiceList   *swarm.ServicesWidget
	Stacks        *swarm.StacksWidget
	StackTasks    *swarm.StacksTasksWidget
	MessageBar    *ui.ExpiringMessageWidget
	activeWidgets map[string]termui.Widget
	sync.Mutex
}

//NewWidgetRegistry creates the WidgetCatalog
func newWidgetRegistry(daemon docker.ContainerDaemon) *widgetRegistry {
	di := appui.NewDockerInfo(daemon)
	di.SetX(0)
	di.SetY(1)
	di.SetWidth(ui.ActiveScreen.Dimensions.Width)
	w := widgetRegistry{
		DockerInfo:    di,
		ContainerList: appui.NewContainersWidget(daemon, appui.MainScreenHeaderSize),
		ContainerMenu: appui.NewContainerMenuWidget(daemon, appui.MainScreenHeaderSize),
		ImageList:     appui.NewDockerImagesWidget(daemon, appui.MainScreenHeaderSize),
		DiskUsage:     appui.NewDockerDiskUsageRenderer(ui.ActiveScreen.Dimensions.Height),
		Monitor:       appui.NewMonitor(daemon, appui.MainScreenHeaderSize),
		Networks:      appui.NewDockerNetworksWidget(daemon, appui.MainScreenHeaderSize),
		Nodes:         swarm.NewNodesWidget(daemon, appui.MainScreenHeaderSize),
		NodeTasks:     swarm.NewNodeTasksWidget(daemon, appui.MainScreenHeaderSize),
		ServiceTasks:  swarm.NewServiceTasksWidget(daemon, appui.MainScreenHeaderSize),
		ServiceList:   swarm.NewServicesWidget(daemon, appui.MainScreenHeaderSize),
		Stacks:        swarm.NewStacksWidget(daemon, appui.MainScreenHeaderSize),
		StackTasks:    swarm.NewStacksTasksWidget(daemon, appui.MainScreenHeaderSize),
		activeWidgets: make(map[string]termui.Widget),
		MessageBar:    ui.NewExpiringMessageWidget(0, ui.ActiveScreen.Dimensions.Width, appui.DryTheme),
	}

	refreshOnContainerEvent(w.ContainerList, daemon)
	refreshOnDockerEvent(docker.ImageSource, w.ImageList)
	refreshOnDockerEvent(docker.NetworkSource, w.Networks)
	refreshOnDockerEvent(docker.NodeSource, w.Nodes)
	refreshOnDockerEvent(docker.ServiceSource, w.ServiceList)
	refreshOnDockerEvent(docker.ServiceSource, w.Stacks)

	return &w
}

func (wr *widgetRegistry) add(w termui.Widget) {
	wr.Lock()
	defer wr.Unlock()
	if err := w.Mount(); err == nil {
		wr.activeWidgets[w.Name()] = w
	}
}

func (wr *widgetRegistry) remove(w termui.Widget) {
	wr.Lock()
	defer wr.Unlock()
	if err := w.Unmount(); err == nil {
		delete(wr.activeWidgets, w.Name())
	}
}

var timeBetweenRefresh = 1000 * time.Millisecond

func refreshOnDockerEvent(source docker.SourceType, w termui.Widget) {
	last := time.Now()
	docker.GlobalRegistry.Register(
		source,
		func(ctx context.Context, m events.Message) error {
			if time.Since(last) > timeBetweenRefresh {
				last = time.Now()
				err := w.Unmount()
				if err != nil {
					return err
				}
				return refreshScreen()
			}
			return nil
		})
}
func refreshOnContainerEvent(w termui.Widget, daemon docker.ContainerDaemon) {
	last := time.Now()
	docker.GlobalRegistry.Register(
		docker.ContainerSource,
		func(ctx context.Context, m events.Message) error {
			if time.Since(last) > timeBetweenRefresh {
				last = time.Now()
				daemon.Refresh(func(e error) {
					err := w.Unmount()
					if err != nil {
						return
					}
					refreshScreen()
				})
			}
			return nil
		})
}
<|endoftext|>


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

package templates

import (
	"fmt"
)

var masterTemplate = fmt.Sprintf(`<!DOCTYPE HTML>

	<meta charset="utf-8">
	<title>{{.Title}}</title>

	<link rel="schema.DC" href="http://purl.org/dc/terms/">
	<meta name="DC.date" content="{{.Date}}">

	<link rel="alternate" type="application/rss+xml" title="RSS" href="/rss.xml">

	<link rel="shortcut icon" href="/theme/favicon.ico" />

	<link rel="stylesheet" href="/theme/deck.css" media="screen">
	<link rel="stylesheet" href="/theme/screen.css" media="screen">
	<link rel="stylesheet" href="/theme/print.css" media="print">
	<link rel="stylesheet" href="/theme/codehighlighting/highlight.css" media="screen, print">

{{ if .ToplevelNavigation}}

	{{range .ToplevelNavigation.Entries}}

		{{.Title}}

	{{end}}

{{end}}

{{ if .BreadcrumbNavigation}}

	{{range .BreadcrumbNavigation.Entries}}

		{{.Title}}

	{{end}}

{{end}}

%s

Tags

Sitemap

RSS Feed

`, ChildTemplatePlaceholder)

const repositoryTemplate = `

{{.Title}}

{{.Description}}

{{.Content}}

{{range .Childs}}

	{{.Title}}

{{.Description}}

{{end}}

`

const documentTemplate = `

{{.Title}}

{{.Description}}

{{.Content}}

{{ if .Tags }}

		Tags:

	{{range .Tags}}

		{{.Name}}

	{{end}}

{{end}}

{{range .Childs}}

	{{.Title}}

{{.Description}}

{{end}}

`

const presentationTemplate = `

{{.Title}}

{{.Description}}

		 /

		<button class="deck-prev-link" title="Previous">←</button>
		<button href="#" class="deck-next-link" title="Next">→</button>

		<form action="." method="get" class="goto-form">
			Go to slide:
			<input type="text" name="slidenum" id="goto-slide" list="goto-datalist">
			<datalist id="goto-datalist"></datalist>
			<input type="submit" value="Go">
		</form>

{{.Content}}

`

const messageTemplate = `

{{.Description}}

{{.Content}}

`

const errorTemplate = `

{{.Title}}

{{.Description}}

{{.Content}}

`

var sitemapContentTemplate = fmt.Sprintf(`

	{{.Title}}

	{{ if .Childs }}

	%s

	{{ end }}

`, ChildTemplatePlaceholder)

const sitemapTemplate = `

{{.Title}}

{{.Description}}

{{.Content}}

`

var tagmapContentTemplate = `

{{range .Tags}}

	{{.Name}}

		{{range .Childs}}

			{{.Title}}

{{.Description}}

		{{end}}

{{end}}

`

const tagmapTemplate = `

{{.Title}}

{{.Description}}

{{.Content}}

`
<commit_msg>Default Theme Update: Don't render the childs section if there are no childs<commit_after>// Copyright 2013 Andreas Koch. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package templates

import (
	"fmt"
)

var masterTemplate = fmt.Sprintf(`<!DOCTYPE HTML>

	<meta charset="utf-8">
	<title>{{.Title}}</title>

	<link rel="schema.DC" href="http://purl.org/dc/terms/">
	<meta name="DC.date" content="{{.Date}}">

	<link rel="alternate" type="application/rss+xml" title="RSS" href="/rss.xml">

	<link rel="shortcut icon" href="/theme/favicon.ico" />

	<link rel="stylesheet" href="/theme/deck.css" media="screen">
	<link rel="stylesheet" href="/theme/screen.css" media="screen">
	<link rel="stylesheet" href="/theme/print.css" media="print">
	<link rel="stylesheet" href="/theme/codehighlighting/highlight.css" media="screen, print">

{{ if .ToplevelNavigation}}

	{{range .ToplevelNavigation.Entries}}

		{{.Title}}

	{{end}}

{{end}}

{{ if .BreadcrumbNavigation}}

	{{range .BreadcrumbNavigation.Entries}}

		{{.Title}}

	{{end}}

{{end}}

%s

Tags

Sitemap

RSS Feed

`, ChildTemplatePlaceholder)

const repositoryTemplate = `

{{.Title}}

{{.Description}}

{{.Content}}

{{ if .Childs }}

{{range .Childs}}

	{{.Title}}

{{.Description}}

{{end}}

{{end}}
`

const documentTemplate = `

{{.Title}}

{{.Description}}

{{.Content}}

{{ if .Tags }}

		Tags:

	{{range .Tags}}

		{{.Name}}

	{{end}}

{{end}}

{{ if .Childs }}

{{range .Childs}}

	{{.Title}}

{{.Description}}

{{end}}

{{end}}
`

const presentationTemplate = `

{{.Title}}

{{.Description}}

		 /

		<button class="deck-prev-link" title="Previous">←</button>
		<button href="#" class="deck-next-link" title="Next">→</button>

		<form action="." method="get" class="goto-form">
			Go to slide:
			<input type="text" name="slidenum" id="goto-slide" list="goto-datalist">
			<datalist id="goto-datalist"></datalist>
			<input type="submit" value="Go">
		</form>

{{.Content}}

`

const messageTemplate = `

{{.Description}}

{{.Content}}

`

const errorTemplate = `

{{.Title}}

{{.Description}}

{{.Content}}

`

var sitemapContentTemplate = fmt.Sprintf(`

	{{.Title}}

	{{ if .Childs }}

	%s

	{{ end }}

`, ChildTemplatePlaceholder)

const sitemapTemplate = `

{{.Title}}

{{.Description}}

{{.Content}}

`

var tagmapContentTemplate = `

{{range .Tags}}

	{{.Name}}

		{{range .Childs}}

			{{.Title}}

{{.Description}}

		{{end}}

{{end}}

`

const tagmapTemplate = `

{{.Title}}

{{.Description}}

{{.Content}}

`
<|endoftext|>