<commit_before>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package monitoring

import (
	"fmt"
	"os/exec"
	"strings"

	appsv1 "k8s.io/api/apps/v1"
	v1 "k8s.io/api/core/v1"
	rbacv1 "k8s.io/api/rbac/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/kubernetes/test/e2e/framework"
	imageutils "k8s.io/kubernetes/test/utils/image"

	gcm "google.golang.org/api/monitoring/v3"
)

var (
	// CustomMetricName is the metrics name used in test cases.
	CustomMetricName = "foo"
	// UnusedMetricName is the unused metrics name used in test cases.
	UnusedMetricName = "unused"
	// CustomMetricValue is the value for CustomMetricName.
	CustomMetricValue = int64(448)
	// UnusedMetricValue is the value for UnusedMetricName.
	UnusedMetricValue = int64(446)
	// StackdriverExporter is exporter name.
	StackdriverExporter = "stackdriver-exporter"
	// HPAPermissions is a ClusterRoleBinding that grants unauthenticated user permissions granted for
	// HPA for testing purposes, i.e. it should grant permission to read custom metrics.
	HPAPermissions = &rbacv1.ClusterRoleBinding{
		ObjectMeta: metav1.ObjectMeta{
			Name: "custom-metrics-reader",
		},
		RoleRef: rbacv1.RoleRef{
			APIGroup: "rbac.authorization.k8s.io",
			Kind:     "ClusterRole",
			Name:     "system:controller:horizontal-pod-autoscaler",
		},
		Subjects: []rbacv1.Subject{
			{
				APIGroup: "rbac.authorization.k8s.io",
				Kind:     "Group",
				Name:     "system:unauthenticated",
			},
		},
	}
	// StagingDeploymentsLocation is the location where the adapter deployment files are stored.
	StagingDeploymentsLocation = "https://raw.githubusercontent.com/GoogleCloudPlatform/k8s-stackdriver/master/custom-metrics-stackdriver-adapter/deploy/staging/"
	// AdapterForOldResourceModel is file name for the old resource model.
	AdapterForOldResourceModel = "adapter_old_resource_model.yaml"
	// AdapterForNewResourceModel is file name for the new resource model.
	AdapterForNewResourceModel = "adapter_new_resource_model.yaml"
	// AdapterDefault is the default model.
	AdapterDefault = AdapterForOldResourceModel
	// ClusterAdminBinding is the cluster rolebinding name for test cases.
	ClusterAdminBinding = "e2e-test-cluster-admin-binding"
)

// CustomMetricContainerSpec allows to specify a config for StackdriverExporterDeployment
// with multiple containers exporting different metrics.
type CustomMetricContainerSpec struct {
	Name        string
	MetricName  string
	MetricValue int64
}

// SimpleStackdriverExporterDeployment is a Deployment of simple application that exports a metric of
// fixed value to Stackdriver in a loop.
func SimpleStackdriverExporterDeployment(name, namespace string, replicas int32, metricValue int64) *appsv1.Deployment {
	return StackdriverExporterDeployment(name, namespace, replicas,
		[]CustomMetricContainerSpec{
			{
				Name:        StackdriverExporter,
				MetricName:  CustomMetricName,
				MetricValue: metricValue,
			},
		})
}

// StackdriverExporterDeployment is a Deployment of an application that can expose
// an arbitrary amount of metrics of fixed value to Stackdriver in a loop. Each metric
// is exposed by a different container in one pod.
// The metric names and values are configured via the containers parameter.
func StackdriverExporterDeployment(name, namespace string, replicas int32, containers []CustomMetricContainerSpec) *appsv1.Deployment {
	podSpec := v1.PodSpec{Containers: []v1.Container{}}
	for _, containerSpec := range containers {
		podSpec.Containers = append(podSpec.Containers, stackdriverExporterContainerSpec(containerSpec.Name, namespace, containerSpec.MetricName, containerSpec.MetricValue))
	}

	return &appsv1.Deployment{
		ObjectMeta: metav1.ObjectMeta{
			Name:      name,
			Namespace: namespace,
		},
		Spec: appsv1.DeploymentSpec{
			Selector: &metav1.LabelSelector{
				MatchLabels: map[string]string{"name": name},
			},
			Template: v1.PodTemplateSpec{
				ObjectMeta: metav1.ObjectMeta{
					Labels: map[string]string{
						"name": name,
					},
				},
				Spec: podSpec,
			},
			Replicas: &replicas,
		},
	}
}

// StackdriverExporterPod is a Pod of simple application that exports a metric of fixed value to
// Stackdriver in a loop.
func StackdriverExporterPod(podName, namespace, podLabel, metricName string, metricValue int64) *v1.Pod {
	return &v1.Pod{
		ObjectMeta: metav1.ObjectMeta{
			Name:      podName,
			Namespace: namespace,
			Labels: map[string]string{
				"name": podLabel,
			},
		},
		Spec: v1.PodSpec{
			Containers: []v1.Container{stackdriverExporterContainerSpec(StackdriverExporter, namespace, metricName, metricValue)},
		},
	}
}

func stackdriverExporterContainerSpec(name string, namespace string, metricName string, metricValue int64) v1.Container {
	return v1.Container{
		Name:            name,
		Image:           imageutils.GetE2EImage(imageutils.SdDummyExporter),
		ImagePullPolicy: v1.PullPolicy("Always"),
		Command: []string{
			"/bin/sh",
			"-c",
			strings.Join([]string{
				"./sd_dummy_exporter",
				"--pod-id=$(POD_ID)",
				"--pod-name=$(POD_NAME)",
				"--namespace=" + namespace,
				"--metric-name=" + metricName,
				fmt.Sprintf("--metric-value=%v", metricValue),
				"--use-old-resource-model",
				"--use-new-resource-model",
			}, " "),
		},
		Env: []v1.EnvVar{
			{
				Name: "POD_ID",
				ValueFrom: &v1.EnvVarSource{
					FieldRef: &v1.ObjectFieldSelector{
						FieldPath: "metadata.uid",
					},
				},
			},
			{
				Name: "POD_NAME",
				ValueFrom: &v1.EnvVarSource{
					FieldRef: &v1.ObjectFieldSelector{
						FieldPath: "metadata.name",
					},
				},
			},
		},
		Ports: []v1.ContainerPort{{ContainerPort: 80}},
	}
}

// PrometheusExporterDeployment is a Deployment of simple application with two containers
// one exposing a metric in prometheus format and second a prometheus-to-sd container
// that scrapes the metric and pushes it to stackdriver.
func PrometheusExporterDeployment(name, namespace string, replicas int32, metricValue int64) *appsv1.Deployment {
	return &appsv1.Deployment{
		ObjectMeta: metav1.ObjectMeta{
			Name:      name,
			Namespace: namespace,
		},
		Spec: appsv1.DeploymentSpec{
			Selector: &metav1.LabelSelector{
				MatchLabels: map[string]string{"name": name},
			},
			Template: v1.PodTemplateSpec{
				ObjectMeta: metav1.ObjectMeta{
					Labels: map[string]string{
						"name": name,
					},
				},
				Spec: prometheusExporterPodSpec(CustomMetricName, metricValue, 8080),
			},
			Replicas: &replicas,
		},
	}
}

func prometheusExporterPodSpec(metricName string, metricValue int64, port int32) v1.PodSpec {
	return v1.PodSpec{
		Containers: []v1.Container{
			{
				Name:            "prometheus-exporter",
				Image:           imageutils.GetE2EImage(imageutils.PrometheusDummyExporter),
				ImagePullPolicy: v1.PullPolicy("Always"),
				Command: []string{"/prometheus_dummy_exporter", "--metric-name=" + metricName,
					fmt.Sprintf("--metric-value=%v", metricValue), fmt.Sprintf("=--port=%d", port)},
				Ports: []v1.ContainerPort{{ContainerPort: port}},
			},
			{
				Name:            "prometheus-to-sd",
				Image:           imageutils.GetE2EImage(imageutils.PrometheusToSd),
				ImagePullPolicy: v1.PullPolicy("Always"),
				Command: []string{"/monitor", fmt.Sprintf("--source=:http://localhost:%d", port),
					"--stackdriver-prefix=custom.googleapis.com", "--pod-id=$(POD_ID)", "--namespace-id=$(POD_NAMESPACE)"},
				Env: []v1.EnvVar{
					{
						Name: "POD_ID",
						ValueFrom: &v1.EnvVarSource{
							FieldRef: &v1.ObjectFieldSelector{
								FieldPath: "metadata.uid",
							},
						},
					},
					{
						Name: "POD_NAMESPACE",
						ValueFrom: &v1.EnvVarSource{
							FieldRef: &v1.ObjectFieldSelector{
								FieldPath: "metadata.namespace",
							},
						},
					},
				},
			},
		},
	}
}

// CreateAdapter creates Custom Metrics - Stackdriver adapter
// adapterDeploymentFile should be a filename for adapter deployment located in StagingDeploymentLocation
func CreateAdapter(adapterDeploymentFile string) error {
	// A workaround to make the work on GKE. GKE doesn't normally allow to create cluster roles,
	// which the adapter deployment does. The solution is to create cluster role binding for
	// cluster-admin role and currently used service account.
	err := createClusterAdminBinding()
	if err != nil {
		return err
	}
	adapterURL := StagingDeploymentsLocation + adapterDeploymentFile
	err = exec.Command("wget", adapterURL).Run()
	if err != nil {
		return err
	}
	stat, err := framework.RunKubectl("", "create", "-f", adapterURL)
	framework.Logf(stat)
	return err
}

func createClusterAdminBinding() error {
	stdout, stderr, err := framework.RunCmd("gcloud", "config", "get-value", "core/account")
	if err != nil {
		framework.Logf(stderr)
		return err
	}
	serviceAccount := strings.TrimSpace(stdout)
	framework.Logf("current service account: %q", serviceAccount)
	stat, err := framework.RunKubectl("", "create", "clusterrolebinding", ClusterAdminBinding, "--clusterrole=cluster-admin", "--user="+serviceAccount)
	framework.Logf(stat)
	return err
}

// CreateDescriptors creates descriptors for metrics: CustomMetricName and UnusedMetricName.
func CreateDescriptors(service *gcm.Service, projectID string) error {
	_, err := service.Projects.MetricDescriptors.Create(fmt.Sprintf("projects/%s", projectID), &gcm.MetricDescriptor{
		Name:       CustomMetricName,
		ValueType:  "INT64",
		Type:       "custom.googleapis.com/" + CustomMetricName,
		MetricKind: "GAUGE",
	}).Do()
	if err != nil {
		return err
	}
	_, err = service.Projects.MetricDescriptors.Create(fmt.Sprintf("projects/%s", projectID), &gcm.MetricDescriptor{
		Name:       UnusedMetricName,
		ValueType:  "INT64",
		Type:       "custom.googleapis.com/" + UnusedMetricName,
		MetricKind: "GAUGE",
	}).Do()
	return err
}

// CleanupDescriptors deletes descriptors for metrics: CustomMetricName and UnusedMetricName.
// TODO: Cleanup time series as well
func CleanupDescriptors(service *gcm.Service, projectID string) {
	_, err := service.Projects.MetricDescriptors.Delete(fmt.Sprintf("projects/%s/metricDescriptors/custom.googleapis.com/%s", projectID, CustomMetricName)).Do()
	if err != nil {
		framework.Logf("Failed to delete descriptor for metric '%s': %v", CustomMetricName, err)
	}
	_, err = service.Projects.MetricDescriptors.Delete(fmt.Sprintf("projects/%s/metricDescriptors/custom.googleapis.com/%s", projectID, UnusedMetricName)).Do()
	if err != nil {
		framework.Logf("Failed to delete descriptor for metric '%s': %v", CustomMetricName, err)
	}
}

// CleanupAdapter deletes Custom Metrics - Stackdriver adapter deployments.
func CleanupAdapter(adapterDeploymentFile string) {
	stat, err := framework.RunKubectl("", "delete", "-f", adapterDeploymentFile)
	framework.Logf(stat)
	if err != nil {
		framework.Logf("Failed to delete adapter deployments: %s", err)
	}
	err = exec.Command("rm", adapterDeploymentFile).Run()
	if err != nil {
		framework.Logf("Failed to delete adapter deployment file: %s", err)
	}
	cleanupClusterAdminBinding()
}

func cleanupClusterAdminBinding() {
	stat, err := framework.RunKubectl("", "delete", "clusterrolebinding", ClusterAdminBinding)
	framework.Logf(stat)
	if err != nil {
		framework.Logf("Failed to delete cluster admin binding: %s", err)
	}
}
<commit_msg>using kubectl apply to create metric adapter<commit_after>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package monitoring

import (
	"fmt"
	"os/exec"
	"strings"

	appsv1 "k8s.io/api/apps/v1"
	v1 "k8s.io/api/core/v1"
	rbacv1 "k8s.io/api/rbac/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/kubernetes/test/e2e/framework"
	imageutils "k8s.io/kubernetes/test/utils/image"

	gcm "google.golang.org/api/monitoring/v3"
)

var (
	// CustomMetricName is the metrics name used in test cases.
	CustomMetricName = "foo"
	// UnusedMetricName is the unused metrics name used in test cases.
	UnusedMetricName = "unused"
	// CustomMetricValue is the value for CustomMetricName.
	CustomMetricValue = int64(448)
	// UnusedMetricValue is the value for UnusedMetricName.
	UnusedMetricValue = int64(446)
	// StackdriverExporter is exporter name.
	StackdriverExporter = "stackdriver-exporter"
	// HPAPermissions is a ClusterRoleBinding that grants unauthenticated user permissions granted for
	// HPA for testing purposes, i.e. it should grant permission to read custom metrics.
	HPAPermissions = &rbacv1.ClusterRoleBinding{
		ObjectMeta: metav1.ObjectMeta{
			Name: "custom-metrics-reader",
		},
		RoleRef: rbacv1.RoleRef{
			APIGroup: "rbac.authorization.k8s.io",
			Kind:     "ClusterRole",
			Name:     "system:controller:horizontal-pod-autoscaler",
		},
		Subjects: []rbacv1.Subject{
			{
				APIGroup: "rbac.authorization.k8s.io",
				Kind:     "Group",
				Name:     "system:unauthenticated",
			},
		},
	}
	// StagingDeploymentsLocation is the location where the adapter deployment files are stored.
	StagingDeploymentsLocation = "https://raw.githubusercontent.com/GoogleCloudPlatform/k8s-stackdriver/master/custom-metrics-stackdriver-adapter/deploy/staging/"
	// AdapterForOldResourceModel is file name for the old resource model.
	AdapterForOldResourceModel = "adapter_old_resource_model.yaml"
	// AdapterForNewResourceModel is file name for the new resource model.
	AdapterForNewResourceModel = "adapter_new_resource_model.yaml"
	// AdapterDefault is the default model.
	AdapterDefault = AdapterForOldResourceModel
	// ClusterAdminBinding is the cluster rolebinding name for test cases.
	ClusterAdminBinding = "e2e-test-cluster-admin-binding"
)

// CustomMetricContainerSpec allows to specify a config for StackdriverExporterDeployment
// with multiple containers exporting different metrics.
type CustomMetricContainerSpec struct {
	Name        string
	MetricName  string
	MetricValue int64
}

// SimpleStackdriverExporterDeployment is a Deployment of simple application that exports a metric of
// fixed value to Stackdriver in a loop.
func SimpleStackdriverExporterDeployment(name, namespace string, replicas int32, metricValue int64) *appsv1.Deployment {
	return StackdriverExporterDeployment(name, namespace, replicas,
		[]CustomMetricContainerSpec{
			{
				Name:        StackdriverExporter,
				MetricName:  CustomMetricName,
				MetricValue: metricValue,
			},
		})
}

// StackdriverExporterDeployment is a Deployment of an application that can expose
// an arbitrary amount of metrics of fixed value to Stackdriver in a loop. Each metric
// is exposed by a different container in one pod.
// The metric names and values are configured via the containers parameter.
func StackdriverExporterDeployment(name, namespace string, replicas int32, containers []CustomMetricContainerSpec) *appsv1.Deployment {
	podSpec := v1.PodSpec{Containers: []v1.Container{}}
	for _, containerSpec := range containers {
		podSpec.Containers = append(podSpec.Containers, stackdriverExporterContainerSpec(containerSpec.Name, namespace, containerSpec.MetricName, containerSpec.MetricValue))
	}

	return &appsv1.Deployment{
		ObjectMeta: metav1.ObjectMeta{
			Name:      name,
			Namespace: namespace,
		},
		Spec: appsv1.DeploymentSpec{
			Selector: &metav1.LabelSelector{
				MatchLabels: map[string]string{"name": name},
			},
			Template: v1.PodTemplateSpec{
				ObjectMeta: metav1.ObjectMeta{
					Labels: map[string]string{
						"name": name,
					},
				},
				Spec: podSpec,
			},
			Replicas: &replicas,
		},
	}
}

// StackdriverExporterPod is a Pod of simple application that exports a metric of fixed value to
// Stackdriver in a loop.
func StackdriverExporterPod(podName, namespace, podLabel, metricName string, metricValue int64) *v1.Pod {
	return &v1.Pod{
		ObjectMeta: metav1.ObjectMeta{
			Name:      podName,
			Namespace: namespace,
			Labels: map[string]string{
				"name": podLabel,
			},
		},
		Spec: v1.PodSpec{
			Containers: []v1.Container{stackdriverExporterContainerSpec(StackdriverExporter, namespace, metricName, metricValue)},
		},
	}
}

func stackdriverExporterContainerSpec(name string, namespace string, metricName string, metricValue int64) v1.Container {
	return v1.Container{
		Name:            name,
		Image:           imageutils.GetE2EImage(imageutils.SdDummyExporter),
		ImagePullPolicy: v1.PullPolicy("Always"),
		Command: []string{
			"/bin/sh",
			"-c",
			strings.Join([]string{
				"./sd_dummy_exporter",
				"--pod-id=$(POD_ID)",
				"--pod-name=$(POD_NAME)",
				"--namespace=" + namespace,
				"--metric-name=" + metricName,
				fmt.Sprintf("--metric-value=%v", metricValue),
				"--use-old-resource-model",
				"--use-new-resource-model",
			}, " "),
		},
		Env: []v1.EnvVar{
			{
				Name: "POD_ID",
				ValueFrom: &v1.EnvVarSource{
					FieldRef: &v1.ObjectFieldSelector{
						FieldPath: "metadata.uid",
					},
				},
			},
			{
				Name: "POD_NAME",
				ValueFrom: &v1.EnvVarSource{
					FieldRef: &v1.ObjectFieldSelector{
						FieldPath: "metadata.name",
					},
				},
			},
		},
		Ports: []v1.ContainerPort{{ContainerPort: 80}},
	}
}

// PrometheusExporterDeployment is a Deployment of simple application with two containers
// one exposing a metric in prometheus format and second a prometheus-to-sd container
// that scrapes the metric and pushes it to stackdriver.
func PrometheusExporterDeployment(name, namespace string, replicas int32, metricValue int64) *appsv1.Deployment {
	return &appsv1.Deployment{
		ObjectMeta: metav1.ObjectMeta{
			Name:      name,
			Namespace: namespace,
		},
		Spec: appsv1.DeploymentSpec{
			Selector: &metav1.LabelSelector{
				MatchLabels: map[string]string{"name": name},
			},
			Template: v1.PodTemplateSpec{
				ObjectMeta: metav1.ObjectMeta{
					Labels: map[string]string{
						"name": name,
					},
				},
				Spec: prometheusExporterPodSpec(CustomMetricName, metricValue, 8080),
			},
			Replicas: &replicas,
		},
	}
}

func prometheusExporterPodSpec(metricName string, metricValue int64, port int32) v1.PodSpec {
	return v1.PodSpec{
		Containers: []v1.Container{
			{
				Name:            "prometheus-exporter",
				Image:           imageutils.GetE2EImage(imageutils.PrometheusDummyExporter),
				ImagePullPolicy: v1.PullPolicy("Always"),
				Command: []string{"/prometheus_dummy_exporter", "--metric-name=" + metricName,
					fmt.Sprintf("--metric-value=%v", metricValue), fmt.Sprintf("=--port=%d", port)},
				Ports: []v1.ContainerPort{{ContainerPort: port}},
			},
			{
				Name:            "prometheus-to-sd",
				Image:           imageutils.GetE2EImage(imageutils.PrometheusToSd),
				ImagePullPolicy: v1.PullPolicy("Always"),
				Command: []string{"/monitor", fmt.Sprintf("--source=:http://localhost:%d", port),
					"--stackdriver-prefix=custom.googleapis.com", "--pod-id=$(POD_ID)", "--namespace-id=$(POD_NAMESPACE)"},
				Env: []v1.EnvVar{
					{
						Name: "POD_ID",
						ValueFrom: &v1.EnvVarSource{
							FieldRef: &v1.ObjectFieldSelector{
								FieldPath: "metadata.uid",
							},
						},
					},
					{
						Name: "POD_NAMESPACE",
						ValueFrom: &v1.EnvVarSource{
							FieldRef: &v1.ObjectFieldSelector{
								FieldPath: "metadata.namespace",
							},
						},
					},
				},
			},
		},
	}
}

// CreateAdapter creates Custom Metrics - Stackdriver adapter
// adapterDeploymentFile should be a filename for adapter deployment located in StagingDeploymentLocation
func CreateAdapter(adapterDeploymentFile string) error {
	// A workaround to make the work on GKE. GKE doesn't normally allow to create cluster roles,
	// which the adapter deployment does. The solution is to create cluster role binding for
	// cluster-admin role and currently used service account.
	err := createClusterAdminBinding()
	if err != nil {
		return err
	}
	adapterURL := StagingDeploymentsLocation + adapterDeploymentFile
	err = exec.Command("wget", adapterURL).Run()
	if err != nil {
		return err
	}
	stat, err := framework.RunKubectl("", "apply", "-f", adapterURL)
	framework.Logf(stat)
	return err
}

func createClusterAdminBinding() error {
	stdout, stderr, err := framework.RunCmd("gcloud", "config", "get-value", "core/account")
	if err != nil {
		framework.Logf(stderr)
		return err
	}
	serviceAccount := strings.TrimSpace(stdout)
	framework.Logf("current service account: %q", serviceAccount)
	stat, err := framework.RunKubectl("", "create", "clusterrolebinding", ClusterAdminBinding, "--clusterrole=cluster-admin", "--user="+serviceAccount)
	framework.Logf(stat)
	return err
}

// CreateDescriptors creates descriptors for metrics: CustomMetricName and UnusedMetricName.
func CreateDescriptors(service *gcm.Service, projectID string) error {
	_, err := service.Projects.MetricDescriptors.Create(fmt.Sprintf("projects/%s", projectID), &gcm.MetricDescriptor{
		Name:       CustomMetricName,
		ValueType:  "INT64",
		Type:       "custom.googleapis.com/" + CustomMetricName,
		MetricKind: "GAUGE",
	}).Do()
	if err != nil {
		return err
	}
	_, err = service.Projects.MetricDescriptors.Create(fmt.Sprintf("projects/%s", projectID), &gcm.MetricDescriptor{
		Name:       UnusedMetricName,
		ValueType:  "INT64",
		Type:       "custom.googleapis.com/" + UnusedMetricName,
		MetricKind: "GAUGE",
	}).Do()
	return err
}

// CleanupDescriptors deletes descriptors for metrics: CustomMetricName and UnusedMetricName.
// TODO: Cleanup time series as well
func CleanupDescriptors(service *gcm.Service, projectID string) {
	_, err := service.Projects.MetricDescriptors.Delete(fmt.Sprintf("projects/%s/metricDescriptors/custom.googleapis.com/%s", projectID, CustomMetricName)).Do()
	if err != nil {
		framework.Logf("Failed to delete descriptor for metric '%s': %v", CustomMetricName, err)
	}
	_, err = service.Projects.MetricDescriptors.Delete(fmt.Sprintf("projects/%s/metricDescriptors/custom.googleapis.com/%s", projectID, UnusedMetricName)).Do()
	if err != nil {
		framework.Logf("Failed to delete descriptor for metric '%s': %v", CustomMetricName, err)
	}
}

// CleanupAdapter deletes Custom Metrics - Stackdriver adapter deployments.
func CleanupAdapter(adapterDeploymentFile string) {
	stat, err := framework.RunKubectl("", "delete", "-f", adapterDeploymentFile)
	framework.Logf(stat)
	if err != nil {
		framework.Logf("Failed to delete adapter deployments: %s", err)
	}
	err = exec.Command("rm", adapterDeploymentFile).Run()
	if err != nil {
		framework.Logf("Failed to delete adapter deployment file: %s", err)
	}
	cleanupClusterAdminBinding()
}

func cleanupClusterAdminBinding() {
	stat, err := framework.RunKubectl("", "delete", "clusterrolebinding", ClusterAdminBinding)
	framework.Logf(stat)
	if err != nil {
		framework.Logf("Failed to delete cluster admin binding: %s", err)
	}
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"
	"strings"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/elb"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsLBCookieStickinessPolicy() *schema.Resource {
	return &schema.Resource{
		// There is no concept of "updating" an LB Stickiness policy in
		// the AWS API.
		Create: resourceAwsLBCookieStickinessPolicyCreate,
		Read:   resourceAwsLBCookieStickinessPolicyRead,
		Delete: resourceAwsLBCookieStickinessPolicyDelete,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"load_balancer": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"lb_port": &schema.Schema{
				Type:     schema.TypeInt,
				Required: true,
				ForceNew: true,
			},

			"cookie_expiration_period": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				ForceNew: true,
			},
		},
	}
}

func resourceAwsLBCookieStickinessPolicyCreate(d *schema.ResourceData, meta interface{}) error {
	elbconn := meta.(*AWSClient).elbconn

	// Provision the LBStickinessPolicy
	lbspOpts := &elb.CreateLBCookieStickinessPolicyInput{
		LoadBalancerName: aws.String(d.Get("load_balancer").(string)),
		PolicyName:       aws.String(d.Get("name").(string)),
	}

	if v := d.Get("cookie_expiration_period").(int); v > 0 {
		lbspOpts.CookieExpirationPeriod = aws.Int64(int64(v))
	}

	log.Printf("[DEBUG] LB Cookie Stickiness Policy opts: %#v", lbspOpts)
	if _, err := elbconn.CreateLBCookieStickinessPolicy(lbspOpts); err != nil {
		return fmt.Errorf("Error creating LBCookieStickinessPolicy: %s", err)
	}

	setLoadBalancerOpts := &elb.SetLoadBalancerPoliciesOfListenerInput{
		LoadBalancerName: aws.String(d.Get("load_balancer").(string)),
		LoadBalancerPort: aws.Int64(int64(d.Get("lb_port").(int))),
		PolicyNames:      []*string{aws.String(d.Get("name").(string))},
	}

	log.Printf("[DEBUG] LB Cookie Stickiness create configuration: %#v", setLoadBalancerOpts)
	if _, err := elbconn.SetLoadBalancerPoliciesOfListener(setLoadBalancerOpts); err != nil {
		return fmt.Errorf("Error setting LBCookieStickinessPolicy: %s", err)
	}

	d.SetId(fmt.Sprintf("%s:%d:%s",
		*lbspOpts.LoadBalancerName,
		*setLoadBalancerOpts.LoadBalancerPort,
		*lbspOpts.PolicyName))
	return nil
}

func resourceAwsLBCookieStickinessPolicyRead(d *schema.ResourceData, meta interface{}) error {
	elbconn := meta.(*AWSClient).elbconn

	lbName, lbPort, policyName := resourceAwsLBCookieStickinessPolicyParseId(d.Id())

	request := &elb.DescribeLoadBalancerPoliciesInput{
		LoadBalancerName: aws.String(lbName),
		PolicyNames:      []*string{aws.String(policyName)},
	}

	getResp, err := elbconn.DescribeLoadBalancerPolicies(request)
	if err != nil {
		if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "PolicyNotFound" {
			// The policy is gone.
			d.SetId("")
			return nil
		}
		return fmt.Errorf("Error retrieving policy: %s", err)
	}

	if len(getResp.PolicyDescriptions) != 1 {
		return fmt.Errorf("Unable to find policy %#v", getResp.PolicyDescriptions)
	}

	// We can get away with this because there's only one attribute, the
	// cookie expiration, in these descriptions.
	policyDesc := getResp.PolicyDescriptions[0]
	cookieAttr := policyDesc.PolicyAttributeDescriptions[0]
	if *cookieAttr.AttributeName != "CookieExpirationPeriod" {
		return fmt.Errorf("Unable to find cookie expiration period.")
	}
	d.Set("cookie_expiration_period", cookieAttr.AttributeValue)

	d.Set("name", policyName)
	d.Set("load_balancer", lbName)
	d.Set("lb_port", lbPort)

	return nil
}

func resourceAwsLBCookieStickinessPolicyDelete(d *schema.ResourceData, meta interface{}) error {
	elbconn := meta.(*AWSClient).elbconn

	lbName, _, policyName := resourceAwsLBCookieStickinessPolicyParseId(d.Id())

	// Perversely, if we Set an empty list of PolicyNames, we detach the
	// policies attached to a listener, which is required to delete the
	// policy itself.
	setLoadBalancerOpts := &elb.SetLoadBalancerPoliciesOfListenerInput{
		LoadBalancerName: aws.String(d.Get("load_balancer").(string)),
		LoadBalancerPort: aws.Int64(int64(d.Get("lb_port").(int))),
		PolicyNames:      []*string{},
	}

	if _, err := elbconn.SetLoadBalancerPoliciesOfListener(setLoadBalancerOpts); err != nil {
		return fmt.Errorf("Error removing LBCookieStickinessPolicy: %s", err)
	}

	request := &elb.DeleteLoadBalancerPolicyInput{
		LoadBalancerName: aws.String(lbName),
		PolicyName:       aws.String(policyName),
	}

	if _, err := elbconn.DeleteLoadBalancerPolicy(request); err != nil {
		return fmt.Errorf("Error deleting LB stickiness policy %s: %s", d.Id(), err)
	}
	return nil
}

// resourceAwsLBCookieStickinessPolicyParseId takes an ID and parses it into
// it's constituent parts. You need three axes (LB name, policy name, and LB
// port) to create or identify a stickiness policy in AWS's API.
func resourceAwsLBCookieStickinessPolicyParseId(id string) (string, string, string) {
	parts := strings.SplitN(id, ":", 3)
	return parts[0], parts[1], parts[2]
}
<commit_msg>add validation for cookie stickiness<commit_after>package aws

import (
	"fmt"
	"log"
	"strings"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/elb"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsLBCookieStickinessPolicy() *schema.Resource {
	return &schema.Resource{
		// There is no concept of "updating" an LB Stickiness policy in
		// the AWS API.
		Create: resourceAwsLBCookieStickinessPolicyCreate,
		Read:   resourceAwsLBCookieStickinessPolicyRead,
		Delete: resourceAwsLBCookieStickinessPolicyDelete,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"load_balancer": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"lb_port": &schema.Schema{
				Type:     schema.TypeInt,
				Required: true,
				ForceNew: true,
			},

			"cookie_expiration_period": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				ForceNew: true,
				ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
					value := v.(int)
					if value <= 0 {
						es = append(es, fmt.Errorf(
							"LB Cookie Expiration Period must be greater than zero if specified"))
					}
					return
				},
			},
		},
	}
}

func resourceAwsLBCookieStickinessPolicyCreate(d *schema.ResourceData, meta interface{}) error {
	elbconn := meta.(*AWSClient).elbconn

	// Provision the LBStickinessPolicy
	lbspOpts := &elb.CreateLBCookieStickinessPolicyInput{
		LoadBalancerName: aws.String(d.Get("load_balancer").(string)),
		PolicyName:       aws.String(d.Get("name").(string)),
	}

	if v := d.Get("cookie_expiration_period").(int); v > 0 {
		lbspOpts.CookieExpirationPeriod = aws.Int64(int64(v))
	}

	log.Printf("[DEBUG] LB Cookie Stickiness Policy opts: %#v", lbspOpts)
	if _, err := elbconn.CreateLBCookieStickinessPolicy(lbspOpts); err != nil {
		return fmt.Errorf("Error creating LBCookieStickinessPolicy: %s", err)
	}

	setLoadBalancerOpts := &elb.SetLoadBalancerPoliciesOfListenerInput{
		LoadBalancerName: aws.String(d.Get("load_balancer").(string)),
		LoadBalancerPort: aws.Int64(int64(d.Get("lb_port").(int))),
		PolicyNames:      []*string{aws.String(d.Get("name").(string))},
	}

	log.Printf("[DEBUG] LB Cookie Stickiness create configuration: %#v", setLoadBalancerOpts)
	if _, err := elbconn.SetLoadBalancerPoliciesOfListener(setLoadBalancerOpts); err != nil {
		return fmt.Errorf("Error setting LBCookieStickinessPolicy: %s", err)
	}

	d.SetId(fmt.Sprintf("%s:%d:%s",
		*lbspOpts.LoadBalancerName,
		*setLoadBalancerOpts.LoadBalancerPort,
		*lbspOpts.PolicyName))
	return nil
}

func resourceAwsLBCookieStickinessPolicyRead(d *schema.ResourceData, meta interface{}) error {
	elbconn := meta.(*AWSClient).elbconn

	lbName, lbPort, policyName := resourceAwsLBCookieStickinessPolicyParseId(d.Id())

	request := &elb.DescribeLoadBalancerPoliciesInput{
		LoadBalancerName: aws.String(lbName),
		PolicyNames:      []*string{aws.String(policyName)},
	}

	getResp, err := elbconn.DescribeLoadBalancerPolicies(request)
	if err != nil {
		if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "PolicyNotFound" {
			// The policy is gone.
			d.SetId("")
			return nil
		}
		return fmt.Errorf("Error retrieving policy: %s", err)
	}

	if len(getResp.PolicyDescriptions) != 1 {
		return fmt.Errorf("Unable to find policy %#v", getResp.PolicyDescriptions)
	}

	// We can get away with this because there's only one attribute, the
	// cookie expiration, in these descriptions.
	policyDesc := getResp.PolicyDescriptions[0]
	cookieAttr := policyDesc.PolicyAttributeDescriptions[0]
	if *cookieAttr.AttributeName != "CookieExpirationPeriod" {
		return fmt.Errorf("Unable to find cookie expiration period.")
	}
	d.Set("cookie_expiration_period", cookieAttr.AttributeValue)

	d.Set("name", policyName)
	d.Set("load_balancer", lbName)
	d.Set("lb_port", lbPort)

	return nil
}

func resourceAwsLBCookieStickinessPolicyDelete(d *schema.ResourceData, meta interface{}) error {
	elbconn := meta.(*AWSClient).elbconn

	lbName, _, policyName := resourceAwsLBCookieStickinessPolicyParseId(d.Id())

	// Perversely, if we Set an empty list of PolicyNames, we detach the
	// policies attached to a listener, which is required to delete the
	// policy itself.
	setLoadBalancerOpts := &elb.SetLoadBalancerPoliciesOfListenerInput{
		LoadBalancerName: aws.String(d.Get("load_balancer").(string)),
		LoadBalancerPort: aws.Int64(int64(d.Get("lb_port").(int))),
		PolicyNames:      []*string{},
	}

	if _, err := elbconn.SetLoadBalancerPoliciesOfListener(setLoadBalancerOpts); err != nil {
		return fmt.Errorf("Error removing LBCookieStickinessPolicy: %s", err)
	}

	request := &elb.DeleteLoadBalancerPolicyInput{
		LoadBalancerName: aws.String(lbName),
		PolicyName:       aws.String(policyName),
	}

	if _, err := elbconn.DeleteLoadBalancerPolicy(request); err != nil {
		return fmt.Errorf("Error deleting LB stickiness policy %s: %s", d.Id(), err)
	}
	return nil
}

// resourceAwsLBCookieStickinessPolicyParseId takes an ID and parses it into
// it's constituent parts. You need three axes (LB name, policy name, and LB
// port) to create or identify a stickiness policy in AWS's API.
func resourceAwsLBCookieStickinessPolicyParseId(id string) (string, string, string) {
	parts := strings.SplitN(id, ":", 3)
	return parts[0], parts[1], parts[2]
}
<|endoftext|>


<commit_before>package spec

import (
	"bytes"
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"net"
	"syscall"
	"time"

	"golang.org/x/net/http2"
	"golang.org/x/net/http2/hpack"

	"github.com/summerwind/h2spec/config"
	"github.com/summerwind/h2spec/log"
)

const (
	// DefaultWindowSize is the value of default connection window size.
	DefaultWindowSize = 65535
	// DefaultFrameSize is the value of default frame size.
	DefaultFrameSize = 16384
)

// Conn represent a HTTP/2 connection.
// This struct contains settings information, current window size,
// encoder of HPACK and frame encoder.
type Conn struct {
	net.Conn

	Settings map[http2.SettingID]uint32
	Timeout  time.Duration
	Verbose  bool
	Closed   bool

	WindowUpdate bool
	WindowSize   map[uint32]int

	framer     *http2.Framer
	encoder    *hpack.Encoder
	encoderBuf *bytes.Buffer

	debugFramer    *http2.Framer
	debugFramerBuf *bytes.Buffer
}

// Dial connects to the server based on configuration.
func Dial(c *config.Config) (*Conn, error) {
	var baseConn net.Conn
	var err error

	if c.TLS {
		dialer := &net.Dialer{}
		dialer.Timeout = c.Timeout

		tlsConn, err := tls.DialWithDialer(dialer, "tcp", c.Addr(), c.TLSConfig())
		if err != nil {
			return nil, err
		}

		cs := tlsConn.ConnectionState()
		if !cs.NegotiatedProtocolIsMutual {
			return nil, errors.New("Protocol negotiation failed")
		}

		baseConn = tlsConn
	} else {
		baseConn, err = net.DialTimeout("tcp", c.Addr(), c.Timeout)
		if err != nil {
			return nil, err
		}
	}

	settings := map[http2.SettingID]uint32{}

	framer := http2.NewFramer(baseConn, baseConn)
	framer.AllowIllegalWrites = true
	framer.AllowIllegalReads = true

	var encoderBuf bytes.Buffer
	encoder := hpack.NewEncoder(&encoderBuf)

	conn := Conn{
		Conn:     baseConn,
		Settings: settings,
		Timeout:  c.Timeout,
		Verbose:  c.Verbose,
		Closed:   false,

		WindowUpdate: true,
		WindowSize:   map[uint32]int{0: DefaultWindowSize},

		framer:     framer,
		encoder:    encoder,
		encoderBuf: &encoderBuf,
	}

	if conn.Verbose {
		conn.debugFramerBuf = new(bytes.Buffer)
		conn.debugFramer = http2.NewFramer(conn.debugFramerBuf, conn.debugFramerBuf)
		conn.debugFramer.AllowIllegalWrites = true
		conn.debugFramer.AllowIllegalReads = true
	}

	return &conn, nil
}

// Handshake performs HTTP/2 handshake with the server.
func (conn *Conn) Handshake() error {
	done := make(chan error)

	fmt.Fprintf(conn, "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")

	go func() {
		local := false
		remote := false

		setting := http2.Setting{
			ID:  http2.SettingInitialWindowSize,
			Val: DefaultWindowSize,
		}
		conn.WriteSettings(setting)

		for !(local && remote) {
			f, err := conn.framer.ReadFrame()
			if err != nil {
				done <- err
				return
			}

			ev := getEventByFrame(f)
			conn.vlog(ev, false)

			sf, ok := f.(*http2.SettingsFrame)
			if !ok {
				continue
			}

			if sf.IsAck() {
				local = true
			} else {
				remote = true
				sf.ForeachSetting(func(setting http2.Setting) error {
					conn.Settings[setting.ID] = setting.Val
					return nil
				})
				conn.WriteSettingsAck()
			}
		}

		done <- nil
	}()

	select {
	case err := <-done:
		if err != nil {
			return err
		}
	case <-time.After(conn.Timeout):
		return ErrTimeout
	}

	return nil
}

// MaxFrameSize returns value of Handshake performs HTTP/2 handshake
// with the server.
func (conn *Conn) MaxFrameSize() int {
	val, ok := conn.Settings[http2.SettingMaxFrameSize]
	if !ok {
		return DefaultFrameSize
	}
	return int(val)
}

// EncodeHeaders encodes header and returns encoded bytes. Conn
// retains encoding context and next call of EncodeHeaders will be
// performed using the same encoding context.
func (conn *Conn) EncodeHeaders(headers []hpack.HeaderField) []byte {
	conn.encoderBuf.Reset()

	for _, hf := range headers {
		conn.encoder.WriteField(hf)
	}

	dst := make([]byte, conn.encoderBuf.Len())
	copy(dst, conn.encoderBuf.Bytes())

	return dst
}

// SetMaxDynamicTableSize changes the dynamic header table size to v.
func (conn *Conn) SetMaxDynamicTableSize(v uint32) {
	conn.encoder.SetMaxDynamicTableSize(v)
}

// Send sends a byte sequense. This function is used to send a raw
// data in tests.
func (conn *Conn) Send(payload []byte) error {
	conn.vlog(RawDataEvent{payload}, true)
	_, err := conn.Write(payload)
	return err
}

// WriteData sends a DATA frame.
func (conn *Conn) WriteData(streamID uint32, endStream bool, data []byte) error {
	if conn.Verbose {
		conn.debugFramer.WriteData(streamID, endStream, data)
		conn.logFrameSend()
	}

	return conn.framer.WriteData(streamID, endStream, data)
}

// WriteDataPadded sends a DATA frame with padding.
func (conn *Conn) WriteDataPadded(streamID uint32, endStream bool, data, pad []byte) error {
	if conn.Verbose {
		conn.debugFramer.WriteDataPadded(streamID, endStream, data, pad)
		conn.logFrameSend()
	}

	return conn.framer.WriteDataPadded(streamID, endStream, data, pad)
}

// WriteHeaders sends a HEADERS frame.
func (conn *Conn) WriteHeaders(p http2.HeadersFrameParam) error {
	if conn.Verbose {
		conn.debugFramer.WriteHeaders(p)
		conn.logFrameSend()
	}

	return conn.framer.WriteHeaders(p)
}

// WritePriority sends a PRIORITY frame.
func (conn *Conn) WritePriority(streamID uint32, p http2.PriorityParam) error {
	if conn.Verbose {
		conn.debugFramer.WritePriority(streamID, p)
		conn.logFrameSend()
	}

	return conn.framer.WritePriority(streamID, p)
}

// WriteRSTStream sends a RST_STREAM frame.
func (conn *Conn) WriteRSTStream(streamID uint32, code http2.ErrCode) error {
	if conn.Verbose {
		conn.debugFramer.WriteRSTStream(streamID, code)
		conn.logFrameSend()
	}

	return conn.framer.WriteRSTStream(streamID, code)
}

// WriteSettings sends a SETTINGS frame.
func (conn *Conn) WriteSettings(settings ...http2.Setting) error {
	if conn.Verbose {
		conn.debugFramer.WriteSettings(settings...)
		conn.logFrameSend()
	}

	return conn.framer.WriteSettings(settings...)
}

// WriteSettingsAck sends a SETTINGS frame with ACK flag.
func (conn *Conn) WriteSettingsAck() error {
	if conn.Verbose {
		conn.debugFramer.WriteSettingsAck()
		conn.logFrameSend()
	}

	return conn.framer.WriteSettingsAck()
}

// WritePushPromise sends a PUSH_PROMISE frame.
func (conn *Conn) WritePushPromise(p http2.PushPromiseParam) error {
	if conn.Verbose {
		conn.debugFramer.WritePushPromise(p)
		conn.logFrameSend()
	}

	return conn.framer.WritePushPromise(p)
}

// WritePing sends a PING frame.
func (conn *Conn) WritePing(ack bool, data [8]byte) error {
	if conn.Verbose {
		conn.debugFramer.WritePing(ack, data)
		conn.logFrameSend()
	}

	return conn.framer.WritePing(ack, data)
}

// WritePing sends a PING frame.
func (conn *Conn) WriteGoAway(maxStreamID uint32, code http2.ErrCode, debugData []byte) error {
	if conn.Verbose {
		conn.debugFramer.WriteGoAway(maxStreamID, code, debugData)
		conn.logFrameSend()
	}

	return conn.framer.WriteGoAway(maxStreamID, code, debugData)
}

// WriteWindowUpdate sends a WINDOW_UPDATE frame.
func (conn *Conn) WriteWindowUpdate(streamID, incr uint32) error {
	if conn.Verbose {
		conn.debugFramer.WriteWindowUpdate(streamID, incr)
		conn.logFrameSend()
	}

	return conn.framer.WriteWindowUpdate(streamID, incr)
}

// WriteContinuation sends a CONTINUATION frame.
func (conn *Conn) WriteContinuation(streamID uint32, endHeaders bool, headerBlockFragment []byte) error {
	if conn.Verbose {
		conn.debugFramer.WriteContinuation(streamID, endHeaders, headerBlockFragment)
		conn.logFrameSend()
	}

	return conn.framer.WriteContinuation(streamID, endHeaders, headerBlockFragment)
}

// WaitEvent returns a event occured on connection. This function is
// used to wait the next event on the connection.
func (conn *Conn) WaitEvent() Event {
	var ev Event

	rd := time.Now().Add(conn.Timeout)
	conn.SetReadDeadline(rd)

	f, err := conn.framer.ReadFrame()
	if err != nil {
		if err == io.EOF {
			ev = ConnectionClosedEvent{}
			conn.vlog(ev, false)
			conn.Closed = true
			return ev
		}

		opErr, ok := err.(*net.OpError)
		if ok {
			if opErr.Err == syscall.ECONNRESET {
				ev = ConnectionClosedEvent{}
				conn.vlog(ev, false)
				conn.Closed = true
				return ev
			}

			if opErr.Timeout() {
				ev = TimeoutEvent{}
				conn.vlog(ev, false)
				conn.Closed = true
				return ev
			}
		}

		ev = ErrorEvent{err}
		conn.vlog(ev, false)
		return ev
	}

	_, ok := f.(*http2.DataFrame)
	if ok {
		conn.updateWindowSize(f)
	}

	ev = getEventByFrame(f)
	conn.vlog(ev, false)

	return ev
}

// WaitEventByType returns a specified event occured on connection.
// This function is used to wait the next event that has specified
// type on the connection.
func (conn *Conn) WaitEventByType(evt EventType) (Event, bool) {
	var lastEvent Event

	for !conn.Closed {
		ev := conn.WaitEvent()

		if ev.Type() == evt {
			return ev, true
		}

		if ev.Type() == EventTimeout && lastEvent != nil {
			break
		}

		lastEvent = ev
	}

	return lastEvent, false
}

// updateWindowSize calculates the current window size based on the
// information in the given HTTP/2 frame.
func (conn *Conn) updateWindowSize(f http2.Frame) {
	if !conn.WindowUpdate {
		return
	}

	len := int(f.Header().Length)
	streamID := f.Header().StreamID

	_, ok := conn.WindowSize[streamID]
	if !ok {
		conn.WindowSize[streamID] = DefaultWindowSize
	}

	conn.WindowSize[streamID] -= len
	if conn.WindowSize[streamID] <= 0 {
		incr := DefaultWindowSize + (conn.WindowSize[streamID] * -1)
		conn.WriteWindowUpdate(streamID, uint32(incr))
		conn.WindowSize[streamID] += incr
	}

	conn.WindowSize[0] -= len
	if conn.WindowSize[0] <= 0 {
		incr := DefaultWindowSize + (conn.WindowSize[0] * -1)
		conn.WriteWindowUpdate(0, uint32(incr))
		conn.WindowSize[0] += incr
	}
}

// logFrameSend writes a log of the frame to be sent.
func (conn *Conn) logFrameSend() {
	f, err := conn.debugFramer.ReadFrame()
	if err != nil {
		// http2 package does not parse DATA frame with stream ID: 0x0.
		// So we are going to log the information that sent some frame.
		if conn.Verbose {
			log.Println(gray(fmt.Sprintf("     <-- [send] ??? Frame (Failed to parse the frame)")))
		}
		return
	}

	ev := getEventByFrame(f)
	conn.vlog(ev, true)
}

// vlog writes a verbose log.
func (conn *Conn) vlog(ev Event, send bool) {
	if !conn.Verbose {
		return
	}

	if send {
		log.Println(gray(fmt.Sprintf("     <-- [send] %s", ev)))
	} else {
		log.Println(gray(fmt.Sprintf("     --> [recv] %s", ev)))
	}
}

// getEventByFrame returns an event based on given HTTP/2 frame.
func getEventByFrame(f http2.Frame) Event {
	var ev Event

	switch f := f.(type) {
	case *http2.DataFrame:
		ev = DataFrameEvent{*f}
	case *http2.HeadersFrame:
		ev = HeadersFrameEvent{*f}
	case *http2.PriorityFrame:
		ev = PriorityFrameEvent{*f}
	case *http2.RSTStreamFrame:
		ev = RSTStreamFrameEvent{*f}
	case *http2.SettingsFrame:
		ev = SettingsFrameEvent{*f}
	case *http2.PushPromiseFrame:
		ev = PushPromiseFrameEvent{*f}
	case *http2.PingFrame:
		ev = PingFrameEvent{*f}
	case *http2.GoAwayFrame:
		ev = GoAwayFrameEvent{*f}
	case *http2.WindowUpdateFrame:
		ev = WindowUpdateFrameEvent{*f}
	case *http2.ContinuationFrame:
		ev = ContinuationFrameEvent{*f}
		//default:
		//	ev = EventUnknownFrame(f)
	}

	return ev
}
<commit_msg>Remove arrow in verbose log<commit_after>package spec

import (
	"bytes"
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"net"
	"syscall"
	"time"

	"golang.org/x/net/http2"
	"golang.org/x/net/http2/hpack"

	"github.com/summerwind/h2spec/config"
	"github.com/summerwind/h2spec/log"
)

const (
	// DefaultWindowSize is the value of default connection window size.
	DefaultWindowSize = 65535
	// DefaultFrameSize is the value of default frame size.
	DefaultFrameSize = 16384
)

// Conn represent a HTTP/2 connection.
// This struct contains settings information, current window size,
// encoder of HPACK and frame encoder.
type Conn struct {
	net.Conn

	Settings map[http2.SettingID]uint32
	Timeout  time.Duration
	Verbose  bool
	Closed   bool

	WindowUpdate bool
	WindowSize   map[uint32]int

	framer     *http2.Framer
	encoder    *hpack.Encoder
	encoderBuf *bytes.Buffer

	debugFramer    *http2.Framer
	debugFramerBuf *bytes.Buffer
}

// Dial connects to the server based on configuration.
func Dial(c *config.Config) (*Conn, error) {
	var baseConn net.Conn
	var err error

	if c.TLS {
		dialer := &net.Dialer{}
		dialer.Timeout = c.Timeout

		tlsConn, err := tls.DialWithDialer(dialer, "tcp", c.Addr(), c.TLSConfig())
		if err != nil {
			return nil, err
		}

		cs := tlsConn.ConnectionState()
		if !cs.NegotiatedProtocolIsMutual {
			return nil, errors.New("Protocol negotiation failed")
		}

		baseConn = tlsConn
	} else {
		baseConn, err = net.DialTimeout("tcp", c.Addr(), c.Timeout)
		if err != nil {
			return nil, err
		}
	}

	settings := map[http2.SettingID]uint32{}

	framer := http2.NewFramer(baseConn, baseConn)
	framer.AllowIllegalWrites = true
	framer.AllowIllegalReads = true

	var encoderBuf bytes.Buffer
	encoder := hpack.NewEncoder(&encoderBuf)

	conn := Conn{
		Conn:     baseConn,
		Settings: settings,
		Timeout:  c.Timeout,
		Verbose:  c.Verbose,
		Closed:   false,

		WindowUpdate: true,
		WindowSize:   map[uint32]int{0: DefaultWindowSize},

		framer:     framer,
		encoder:    encoder,
		encoderBuf: &encoderBuf,
	}

	if conn.Verbose {
		conn.debugFramerBuf = new(bytes.Buffer)
		conn.debugFramer = http2.NewFramer(conn.debugFramerBuf, conn.debugFramerBuf)
		conn.debugFramer.AllowIllegalWrites = true
		conn.debugFramer.AllowIllegalReads = true
	}

	return &conn, nil
}

// Handshake performs HTTP/2 handshake with the server.
func (conn *Conn) Handshake() error {
	done := make(chan error)

	fmt.Fprintf(conn, "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")

	go func() {
		local := false
		remote := false

		setting := http2.Setting{
			ID:  http2.SettingInitialWindowSize,
			Val: DefaultWindowSize,
		}
		conn.WriteSettings(setting)

		for !(local && remote) {
			f, err := conn.framer.ReadFrame()
			if err != nil {
				done <- err
				return
			}

			ev := getEventByFrame(f)
			conn.vlog(ev, false)

			sf, ok := f.(*http2.SettingsFrame)
			if !ok {
				continue
			}

			if sf.IsAck() {
				local = true
			} else {
				remote = true
				sf.ForeachSetting(func(setting http2.Setting) error {
					conn.Settings[setting.ID] = setting.Val
					return nil
				})
				conn.WriteSettingsAck()
			}
		}

		done <- nil
	}()

	select {
	case err := <-done:
		if err != nil {
			return err
		}
	case <-time.After(conn.Timeout):
		return ErrTimeout
	}

	return nil
}

// MaxFrameSize returns value of Handshake performs HTTP/2 handshake
// with the server.
func (conn *Conn) MaxFrameSize() int {
	val, ok := conn.Settings[http2.SettingMaxFrameSize]
	if !ok {
		return DefaultFrameSize
	}
	return int(val)
}

// EncodeHeaders encodes header and returns encoded bytes. Conn
// retains encoding context and next call of EncodeHeaders will be
// performed using the same encoding context.
func (conn *Conn) EncodeHeaders(headers []hpack.HeaderField) []byte {
	conn.encoderBuf.Reset()

	for _, hf := range headers {
		conn.encoder.WriteField(hf)
	}

	dst := make([]byte, conn.encoderBuf.Len())
	copy(dst, conn.encoderBuf.Bytes())

	return dst
}

// SetMaxDynamicTableSize changes the dynamic header table size to v.
func (conn *Conn) SetMaxDynamicTableSize(v uint32) {
	conn.encoder.SetMaxDynamicTableSize(v)
}

// Send sends a byte sequense. This function is used to send a raw
// data in tests.
func (conn *Conn) Send(payload []byte) error {
	conn.vlog(RawDataEvent{payload}, true)
	_, err := conn.Write(payload)
	return err
}

// WriteData sends a DATA frame.
func (conn *Conn) WriteData(streamID uint32, endStream bool, data []byte) error {
	if conn.Verbose {
		conn.debugFramer.WriteData(streamID, endStream, data)
		conn.logFrameSend()
	}

	return conn.framer.WriteData(streamID, endStream, data)
}

// WriteDataPadded sends a DATA frame with padding.
func (conn *Conn) WriteDataPadded(streamID uint32, endStream bool, data, pad []byte) error {
	if conn.Verbose {
		conn.debugFramer.WriteDataPadded(streamID, endStream, data, pad)
		conn.logFrameSend()
	}

	return conn.framer.WriteDataPadded(streamID, endStream, data, pad)
}

// WriteHeaders sends a HEADERS frame.
func (conn *Conn) WriteHeaders(p http2.HeadersFrameParam) error {
	if conn.Verbose {
		conn.debugFramer.WriteHeaders(p)
		conn.logFrameSend()
	}

	return conn.framer.WriteHeaders(p)
}

// WritePriority sends a PRIORITY frame.
func (conn *Conn) WritePriority(streamID uint32, p http2.PriorityParam) error {
	if conn.Verbose {
		conn.debugFramer.WritePriority(streamID, p)
		conn.logFrameSend()
	}

	return conn.framer.WritePriority(streamID, p)
}

// WriteRSTStream sends a RST_STREAM frame.
func (conn *Conn) WriteRSTStream(streamID uint32, code http2.ErrCode) error {
	if conn.Verbose {
		conn.debugFramer.WriteRSTStream(streamID, code)
		conn.logFrameSend()
	}

	return conn.framer.WriteRSTStream(streamID, code)
}

// WriteSettings sends a SETTINGS frame.
func (conn *Conn) WriteSettings(settings ...http2.Setting) error {
	if conn.Verbose {
		conn.debugFramer.WriteSettings(settings...)
		conn.logFrameSend()
	}

	return conn.framer.WriteSettings(settings...)
}

// WriteSettingsAck sends a SETTINGS frame with ACK flag.
func (conn *Conn) WriteSettingsAck() error {
	if conn.Verbose {
		conn.debugFramer.WriteSettingsAck()
		conn.logFrameSend()
	}

	return conn.framer.WriteSettingsAck()
}

// WritePushPromise sends a PUSH_PROMISE frame.
func (conn *Conn) WritePushPromise(p http2.PushPromiseParam) error {
	if conn.Verbose {
		conn.debugFramer.WritePushPromise(p)
		conn.logFrameSend()
	}

	return conn.framer.WritePushPromise(p)
}

// WritePing sends a PING frame.
func (conn *Conn) WritePing(ack bool, data [8]byte) error {
	if conn.Verbose {
		conn.debugFramer.WritePing(ack, data)
		conn.logFrameSend()
	}

	return conn.framer.WritePing(ack, data)
}

// WritePing sends a PING frame.
func (conn *Conn) WriteGoAway(maxStreamID uint32, code http2.ErrCode, debugData []byte) error {
	if conn.Verbose {
		conn.debugFramer.WriteGoAway(maxStreamID, code, debugData)
		conn.logFrameSend()
	}

	return conn.framer.WriteGoAway(maxStreamID, code, debugData)
}

// WriteWindowUpdate sends a WINDOW_UPDATE frame.
func (conn *Conn) WriteWindowUpdate(streamID, incr uint32) error {
	if conn.Verbose {
		conn.debugFramer.WriteWindowUpdate(streamID, incr)
		conn.logFrameSend()
	}

	return conn.framer.WriteWindowUpdate(streamID, incr)
}

// WriteContinuation sends a CONTINUATION frame.
func (conn *Conn) WriteContinuation(streamID uint32, endHeaders bool, headerBlockFragment []byte) error {
	if conn.Verbose {
		conn.debugFramer.WriteContinuation(streamID, endHeaders, headerBlockFragment)
		conn.logFrameSend()
	}

	return conn.framer.WriteContinuation(streamID, endHeaders, headerBlockFragment)
}

// WaitEvent returns a event occured on connection. This function is
// used to wait the next event on the connection.
func (conn *Conn) WaitEvent() Event {
	var ev Event

	rd := time.Now().Add(conn.Timeout)
	conn.SetReadDeadline(rd)

	f, err := conn.framer.ReadFrame()
	if err != nil {
		if err == io.EOF {
			ev = ConnectionClosedEvent{}
			conn.vlog(ev, false)
			conn.Closed = true
			return ev
		}

		opErr, ok := err.(*net.OpError)
		if ok {
			if opErr.Err == syscall.ECONNRESET {
				ev = ConnectionClosedEvent{}
				conn.vlog(ev, false)
				conn.Closed = true
				return ev
			}

			if opErr.Timeout() {
				ev = TimeoutEvent{}
				conn.vlog(ev, false)
				conn.Closed = true
				return ev
			}
		}

		ev = ErrorEvent{err}
		conn.vlog(ev, false)
		return ev
	}

	_, ok := f.(*http2.DataFrame)
	if ok {
		conn.updateWindowSize(f)
	}

	ev = getEventByFrame(f)
	conn.vlog(ev, false)

	return ev
}

// WaitEventByType returns a specified event occured on connection.
// This function is used to wait the next event that has specified
// type on the connection.
func (conn *Conn) WaitEventByType(evt EventType) (Event, bool) {
	var lastEvent Event

	for !conn.Closed {
		ev := conn.WaitEvent()

		if ev.Type() == evt {
			return ev, true
		}

		if ev.Type() == EventTimeout && lastEvent != nil {
			break
		}

		lastEvent = ev
	}

	return lastEvent, false
}

// updateWindowSize calculates the current window size based on the
// information in the given HTTP/2 frame.
func (conn *Conn) updateWindowSize(f http2.Frame) {
	if !conn.WindowUpdate {
		return
	}

	len := int(f.Header().Length)
	streamID := f.Header().StreamID

	_, ok := conn.WindowSize[streamID]
	if !ok {
		conn.WindowSize[streamID] = DefaultWindowSize
	}

	conn.WindowSize[streamID] -= len
	if conn.WindowSize[streamID] <= 0 {
		incr := DefaultWindowSize + (conn.WindowSize[streamID] * -1)
		conn.WriteWindowUpdate(streamID, uint32(incr))
		conn.WindowSize[streamID] += incr
	}

	conn.WindowSize[0] -= len
	if conn.WindowSize[0] <= 0 {
		incr := DefaultWindowSize + (conn.WindowSize[0] * -1)
		conn.WriteWindowUpdate(0, uint32(incr))
		conn.WindowSize[0] += incr
	}
}

// logFrameSend writes a log of the frame to be sent.
func (conn *Conn) logFrameSend() {
	f, err := conn.debugFramer.ReadFrame()
	if err != nil {
		// http2 package does not parse DATA frame with stream ID: 0x0.
		// So we are going to log the information that sent some frame.
		if conn.Verbose {
			log.Println(gray(fmt.Sprintf("     [send] ??? Frame (Failed to parse the frame)")))
		}
		return
	}

	ev := getEventByFrame(f)
	conn.vlog(ev, true)
}

// vlog writes a verbose log.
func (conn *Conn) vlog(ev Event, send bool) {
	if !conn.Verbose {
		return
	}

	if send {
		log.Println(gray(fmt.Sprintf("     [send] %s", ev)))
	} else {
		log.Println(gray(fmt.Sprintf("     [recv] %s", ev)))
	}
}

// getEventByFrame returns an event based on given HTTP/2 frame.
func getEventByFrame(f http2.Frame) Event {
	var ev Event

	switch f := f.(type) {
	case *http2.DataFrame:
		ev = DataFrameEvent{*f}
	case *http2.HeadersFrame:
		ev = HeadersFrameEvent{*f}
	case *http2.PriorityFrame:
		ev = PriorityFrameEvent{*f}
	case *http2.RSTStreamFrame:
		ev = RSTStreamFrameEvent{*f}
	case *http2.SettingsFrame:
		ev = SettingsFrameEvent{*f}
	case *http2.PushPromiseFrame:
		ev = PushPromiseFrameEvent{*f}
	case *http2.PingFrame:
		ev = PingFrameEvent{*f}
	case *http2.GoAwayFrame:
		ev = GoAwayFrameEvent{*f}
	case *http2.WindowUpdateFrame:
		ev = WindowUpdateFrameEvent{*f}
	case *http2.ContinuationFrame:
		ev = ContinuationFrameEvent{*f}
		//default:
		//	ev = EventUnknownFrame(f)
	}

	return ev
}
<|endoftext|>


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

import (
	"github.com/couchbaselabs/clog"
	cb "github.com/couchbaselabs/go-couchbase"
	"github.com/couchbaselabs/tuqtng/query"
)

const CHANNEL = "NETWORK"

func WalkViewInBatches(result chan cb.ViewRow, errs query.ErrorChannel, bucket *cb.Bucket,
	ddoc string, view string, options map[string]interface{}, batchSize int) {

	defer close(result)
	defer close(errs)

	options["limit"] = batchSize + 1
	logURL, err := bucket.ViewURL(ddoc, view, options)
	if err == nil {
		clog.To(CHANNEL, "Request View: %v", logURL)
	}
	vres, err := bucket.View(ddoc, view, options)

	if err != nil {
		errs <- query.NewError(err, "Unable to access view")
		return
	}

	for i, row := range vres.Rows {
		if i < batchSize {
			// dont process the last row, its just used to see if we
			// need to continue processing
			result <- row
		}
	}

	// as long as we continue to get batchSize + 1 results back we have to keep going
	for len(vres.Rows) > batchSize {
		skey := vres.Rows[batchSize].Key
		skeydocid := vres.Rows[batchSize].ID
		options["startkey"] = skey
		options["startkey_docid"] = cb.DocId(skeydocid)

		logURL, err := bucket.ViewURL(ddoc, view, options)
		if err == nil {
			clog.To(CHANNEL, "Request View: %v", logURL)
		}
		vres, err = bucket.View(ddoc, view, options)
		if err != nil {
			errs <- query.NewError(err, "Unable to access view")
			return
		}

		for i, row := range vres.Rows {
			if i < batchSize {
				// dont process the last row, its just used to see if we
				// need to continue processing
				result <- row
			}
		}
	}
}
<commit_msg>refactored to reuse code block<commit_after>//  Copyright (c) 2013 Couchbase, Inc.
//  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
//  except in compliance with the License. You may obtain a copy of the License at
//    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 couchbase

import (
	"github.com/couchbaselabs/clog"
	cb "github.com/couchbaselabs/go-couchbase"
	"github.com/couchbaselabs/tuqtng/query"
)

const CHANNEL = "NETWORK"

func WalkViewInBatches(result chan cb.ViewRow, errs query.ErrorChannel, bucket *cb.Bucket,
	ddoc string, view string, options map[string]interface{}, batchSize int) {

	defer close(result)
	defer close(errs)

	options["limit"] = batchSize + 1

	ok := true
	for ok {

		logURL, err := bucket.ViewURL(ddoc, view, options)
		if err == nil {
			clog.To(CHANNEL, "Request View: %v", logURL)
		}
		vres, err := bucket.View(ddoc, view, options)
		if err != nil {
			errs <- query.NewError(err, "Unable to access view")
			return
		}

		for i, row := range vres.Rows {
			if i < batchSize {
				// dont process the last row, its just used to see if we
				// need to continue processing
				result <- row
			}
		}

		if len(vres.Rows) > batchSize {
			// prepare for next run
			skey := vres.Rows[batchSize].Key
			skeydocid := vres.Rows[batchSize].ID
			options["startkey"] = skey
			options["startkey_docid"] = cb.DocId(skeydocid)
		} else {
			// stop
			ok = false
		}
	}
}
<|endoftext|>


<commit_before>/*
                       WARNING WARNING WARNING

                Attention all potential contributors

   This testfile is not in the best state. We've been slowly transitioning
   from the built in "testing" package to using Ginkgo. As you can see, we've
   changed the format, but a lot of the setup, test body, descriptions, etc
   are either hardcoded, completely lacking, or misleading.

   For example:

   Describe("Testing with ginkgo"...)      // This is not a great description
   It("TestDoesSoemthing"...)              // This is a horrible description

   Describe("create-user command"...       // Describe the actual object under test
   It("creates a user when provided ..."   // this is more descriptive

   For good examples of writing Ginkgo tests for the cli, refer to

   src/github.com/cloudfoundry/cli/cf/commands/application/delete_app_test.go
   src/github.com/cloudfoundry/cli/cf/terminal/ui_test.go
   src/github.com/cloudfoundry/loggregator_consumer/consumer_test.go
*/

package api_test

import (
	. "github.com/cloudfoundry/cli/cf/api"
	"github.com/cloudfoundry/cli/cf/net"
	testapi "github.com/cloudfoundry/cli/testhelpers/api"
	testconfig "github.com/cloudfoundry/cli/testhelpers/configuration"
	testnet "github.com/cloudfoundry/cli/testhelpers/net"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"net/http"
	"net/http/httptest"
	"time"
)

var _ = Describe("Testing with ginkgo", func() {
	var serviceInstanceSummariesResponse testnet.TestResponse

	BeforeEach(func() {
		serviceInstanceSummariesResponse = testnet.TestResponse{Status: http.StatusOK, Body: `
			{
			  "apps":[
				{
				  "name":"app1",
				  "service_names":[
					"my-service-instance"
				  ]
				},{
				  "name":"app2",
				  "service_names":[
					"my-service-instance"
				  ]
				}
			  ],
			  "services": [
				{
				  "guid": "my-service-instance-guid",
				  "name": "my-service-instance",
				  "bound_app_count": 2,
				  "service_plan": {
					"guid": "service-plan-guid",
					"name": "spark",
					"service": {
					  "guid": "service-offering-guid",
					  "label": "cleardb",
					  "provider": "cleardb-provider",
					  "version": "n/a"
					}
				  }
				}
			  ]
			}`,
		}
	})

	It("TestServiceSummaryGetSummariesInCurrentSpace", func() {
		req := testapi.NewCloudControllerTestRequest(testnet.TestRequest{
			Method:   "GET",
			Path:     "/v2/spaces/my-space-guid/summary",
			Response: serviceInstanceSummariesResponse,
		})

		ts, handler, repo := createServiceSummaryRepo(req)
		defer ts.Close()

		serviceInstances, apiErr := repo.GetSummariesInCurrentSpace()
		Expect(handler).To(testnet.HaveAllRequestsCalled())

		Expect(apiErr).NotTo(HaveOccurred())
		Expect(1).To(Equal(len(serviceInstances)))

		instance1 := serviceInstances[0]
		Expect(instance1.Name).To(Equal("my-service-instance"))
		Expect(instance1.ServicePlan.Name).To(Equal("spark"))
		Expect(instance1.ServiceOffering.Label).To(Equal("cleardb"))
		Expect(instance1.ServiceOffering.Label).To(Equal("cleardb"))
		Expect(instance1.ServiceOffering.Provider).To(Equal("cleardb-provider"))
		Expect(instance1.ServiceOffering.Version).To(Equal("n/a"))
		Expect(len(instance1.ApplicationNames)).To(Equal(2))
		Expect(instance1.ApplicationNames[0]).To(Equal("app1"))
		Expect(instance1.ApplicationNames[1]).To(Equal("app2"))
	})
})

func createServiceSummaryRepo(req testnet.TestRequest) (ts *httptest.Server, handler *testnet.TestHandler, repo ServiceSummaryRepository) {
	ts, handler = testnet.NewServer([]testnet.TestRequest{req})
	configRepo := testconfig.NewRepositoryWithDefaults()
	configRepo.SetApiEndpoint(ts.URL)
	gateway := net.NewCloudControllerGateway(configRepo, time.Now)
	repo = NewCloudControllerServiceSummaryRepository(configRepo, gateway)
	return
}
<commit_msg>Cleanup service_summary repo tests<commit_after>package api_test

import (
	"net/http"
	"net/http/httptest"
	"time"

	"github.com/cloudfoundry/cli/cf/net"
	testapi "github.com/cloudfoundry/cli/testhelpers/api"
	testconfig "github.com/cloudfoundry/cli/testhelpers/configuration"
	testnet "github.com/cloudfoundry/cli/testhelpers/net"

	. "github.com/cloudfoundry/cli/cf/api"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("ServiceSummaryRepository", func() {
	var serviceInstanceSummariesResponse testnet.TestResponse

	BeforeEach(func() {
		serviceInstanceSummariesResponse = testnet.TestResponse{Status: http.StatusOK, Body: `
			{
			  "apps":[
				{
				  "name":"app1",
				  "service_names":[
					"my-service-instance"
				  ]
				},{
				  "name":"app2",
				  "service_names":[
					"my-service-instance"
				  ]
				}
			  ],
			  "services": [
				{
				  "guid": "my-service-instance-guid",
				  "name": "my-service-instance",
				  "bound_app_count": 2,
				  "service_plan": {
					"guid": "service-plan-guid",
					"name": "spark",
					"service": {
					  "guid": "service-offering-guid",
					  "label": "cleardb",
					  "provider": "cleardb-provider",
					  "version": "n/a"
					}
				  }
				}
			  ]
			}`,
		}
	})

	It("gets a summary of services in the given space", func() {
		req := testapi.NewCloudControllerTestRequest(testnet.TestRequest{
			Method:   "GET",
			Path:     "/v2/spaces/my-space-guid/summary",
			Response: serviceInstanceSummariesResponse,
		})

		ts, handler, repo := createServiceSummaryRepo(req)
		defer ts.Close()

		serviceInstances, apiErr := repo.GetSummariesInCurrentSpace()
		Expect(handler).To(testnet.HaveAllRequestsCalled())

		Expect(apiErr).NotTo(HaveOccurred())
		Expect(1).To(Equal(len(serviceInstances)))

		instance1 := serviceInstances[0]
		Expect(instance1.Name).To(Equal("my-service-instance"))
		Expect(instance1.ServicePlan.Name).To(Equal("spark"))
		Expect(instance1.ServiceOffering.Label).To(Equal("cleardb"))
		Expect(instance1.ServiceOffering.Label).To(Equal("cleardb"))
		Expect(instance1.ServiceOffering.Provider).To(Equal("cleardb-provider"))
		Expect(instance1.ServiceOffering.Version).To(Equal("n/a"))
		Expect(len(instance1.ApplicationNames)).To(Equal(2))
		Expect(instance1.ApplicationNames[0]).To(Equal("app1"))
		Expect(instance1.ApplicationNames[1]).To(Equal("app2"))
	})
})

func createServiceSummaryRepo(req testnet.TestRequest) (ts *httptest.Server, handler *testnet.TestHandler, repo ServiceSummaryRepository) {
	ts, handler = testnet.NewServer([]testnet.TestRequest{req})
	configRepo := testconfig.NewRepositoryWithDefaults()
	configRepo.SetApiEndpoint(ts.URL)
	gateway := net.NewCloudControllerGateway(configRepo, time.Now)
	repo = NewCloudControllerServiceSummaryRepository(configRepo, gateway)
	return
}
<|endoftext|>


<commit_before>package sam3

import (
	"bufio"
	"bytes"
	"errors"
  "io"
	"net"
	"strings"
)

// Represents a streaming session.
type StreamSession struct {
  samAddr     string             // address to the sam bridge (ipv4:port)
	id          string             // tunnel name
	conn        net.Conn           // connection to sam bridge
	keys        I2PKeys            // i2p destination keys
}

// Returns the local tunnel name of the I2P tunnel used for the stream session
func (ss StreamSession) ID() string {
	return ss.id
}

func (ss *StreamSession) Close() error {
  return ss.conn.Close()
}

// Returns the I2P destination (the address) of the stream session
func (ss StreamSession) Addr() I2PAddr {
	return ss.keys.Addr()
}

// Returns the keys associated with the stream session
func (ss StreamSession) Keys() I2PKeys {
	return ss.keys
}

// Creates a new StreamSession with the I2CP- and streaminglib options as
// specified. See the I2P documentation for a full list of options.
func (sam *SAM) NewStreamSession(id string, keys I2PKeys, options []string) (*StreamSession, error) {
	conn, err := sam.newGenericSession("STREAM", id, keys, options, []string{})
	if err != nil {
		return nil, err
	}
	return &StreamSession{sam.address, id, conn, keys}, nil
}

// implement net.Dialer
func (s *StreamSession) Dial(n, addr string) (c net.Conn, err error) {

  var i2paddr I2PAddr
  var host string
  host, _, err = net.SplitHostPort(addr)
  if err == nil {
    // check for name
    if strings.HasSuffix(host, ".b32.i2p") || strings.HasSuffix(host, ".i2p") {
      // name lookup
      var sam *SAM
      sam, err = NewSAM(s.samAddr)
      if err == nil {
        i2paddr, err = sam.Lookup(host)
        sam.Close()
      }
    } else {
      // probably a destination
      i2paddr = I2PAddr(host)
    }
    return s.DialI2P(i2paddr)
  }
  return
}

// Dials to an I2P destination and returns a SAMConn, which implements a net.Conn.
func (s *StreamSession) DialI2P(addr I2PAddr) (*SAMConn, error) {
	sam, err := NewSAM(s.samAddr)
	if err != nil {
		return nil, err
	}
	conn := sam.conn
	_,err = conn.Write([]byte("STREAM CONNECT ID=" + s.id + " DESTINATION=" + addr.Base64() + " SILENT=false\n"))
	if err != nil {
		return nil, err
	}
	buf := make([]byte, 4096)
	n, err := conn.Read(buf)
	if err != nil {
		return nil, err
	}
	scanner := bufio.NewScanner(bytes.NewReader(buf[:n]))
	scanner.Split(bufio.ScanWords)
	for scanner.Scan() {
		switch scanner.Text() {
		case "STREAM" :
			continue
		case "STATUS" :
			continue
		case "RESULT=OK" :
			return &SAMConn{s.keys.addr, addr, conn}, nil
		case "RESULT=CANT_REACH_PEER" :
      sam.Close()
			return nil, errors.New("Can not reach peer")
		case "RESULT=I2P_ERROR" :
      sam.Close()
			return nil, errors.New("I2P internal error")
		case "RESULT=INVALID_KEY" :
      sam.Close()
			return nil, errors.New("Invalid key")
		case "RESULT=INVALID_ID" :
      sam.Close()
			return nil, errors.New("Invalid tunnel ID")
		case "RESULT=TIMEOUT" :
      sam.Close()
			return nil, errors.New("Timeout")
    default:
      sam.Close()
			return nil, errors.New("Unknown error: " + scanner.Text() + " : " + string(buf[:n]))
		}
	}
	panic("sam3 go library error in StreamSession.DialI2P()")
}

// create a new stream listener to accept inbound connections
func (s *StreamSession) Listen() (*StreamListener, error) {
  return &StreamListener{
    session: s,
    id: s.id,
    laddr: s.keys.Addr(),
  }, nil
}

type StreamListener struct {
  // parent stream session
  session *StreamSession
  // our session id
  id string
  // our local address for this sam socket
  laddr I2PAddr
}

// get our address
// implements net.Listener
func (l *StreamListener) Addr() net.Addr {
  return l.laddr
}

// implements net.Listener
func (l *StreamListener) Close() error {
  return l.session.Close()
}

// implements net.Listener
func (l *StreamListener) Accept() (net.Conn, error) {
  return l.AcceptI2P()
}

// accept a new inbound connection
func (l *StreamListener) AcceptI2P() (*SAMConn, error) {
  s, err := NewSAM(l.session.samAddr)
  if err == nil {
    // we connected to sam
    // send accept() command
    _, err = io.WriteString(s.conn, "STREAM ACCEPT ID="+l.id+" SILENT=false\n")
    // read reply
    rd := bufio.NewReader(s.conn)
    // read first line
    line, err := rd.ReadString(10)
    if err == nil {
      if strings.HasPrefix(line, "STREAM STATUS RESULT=OK") {
        // we gud read destination line
        dest, err := rd.ReadString(10)
        if err == nil {
          // return wrapped connection
          dest = strings.Trim(dest, "\n")
          return &SAMConn{
            laddr: l.laddr,
            raddr: I2PAddr(dest),
            conn: s.conn,
          }, nil
        } else {
          s.Close()
          return nil, err
        }
      } else {
        s.Close()
        return nil, errors.New("invalid sam line: "+line)
      }
    } else {
      s.Close()
      return nil, err
    }
  }
  return nil, err
}
<commit_msg>only dial out when name resolution succeedes<commit_after>package sam3

import (
	"bufio"
	"bytes"
	"errors"
  "io"
	"net"
	"strings"
)

// Represents a streaming session.
type StreamSession struct {
  samAddr     string             // address to the sam bridge (ipv4:port)
	id          string             // tunnel name
	conn        net.Conn           // connection to sam bridge
	keys        I2PKeys            // i2p destination keys
}

// Returns the local tunnel name of the I2P tunnel used for the stream session
func (ss StreamSession) ID() string {
	return ss.id
}

func (ss *StreamSession) Close() error {
  return ss.conn.Close()
}

// Returns the I2P destination (the address) of the stream session
func (ss StreamSession) Addr() I2PAddr {
	return ss.keys.Addr()
}

// Returns the keys associated with the stream session
func (ss StreamSession) Keys() I2PKeys {
	return ss.keys
}

// Creates a new StreamSession with the I2CP- and streaminglib options as
// specified. See the I2P documentation for a full list of options.
func (sam *SAM) NewStreamSession(id string, keys I2PKeys, options []string) (*StreamSession, error) {
	conn, err := sam.newGenericSession("STREAM", id, keys, options, []string{})
	if err != nil {
		return nil, err
	}
	return &StreamSession{sam.address, id, conn, keys}, nil
}

// implement net.Dialer
func (s *StreamSession) Dial(n, addr string) (c net.Conn, err error) {

  var i2paddr I2PAddr
  var host string
  host, _, err = net.SplitHostPort(addr)
  if err == nil {
    // check for name
    if strings.HasSuffix(host, ".b32.i2p") || strings.HasSuffix(host, ".i2p") {
      // name lookup
      var sam *SAM
      sam, err = NewSAM(s.samAddr)
      if err == nil {
        i2paddr, err = sam.Lookup(host)
        sam.Close()
      }
    } else {
      // probably a destination
      i2paddr = I2PAddr(host)
    }
    if err == nil {
      return s.DialI2P(i2paddr)
    }
  }
  return
}

// Dials to an I2P destination and returns a SAMConn, which implements a net.Conn.
func (s *StreamSession) DialI2P(addr I2PAddr) (*SAMConn, error) {
	sam, err := NewSAM(s.samAddr)
	if err != nil {
		return nil, err
	}
	conn := sam.conn
	_,err = conn.Write([]byte("STREAM CONNECT ID=" + s.id + " DESTINATION=" + addr.Base64() + " SILENT=false\n"))
	if err != nil {
		return nil, err
	}
	buf := make([]byte, 4096)
	n, err := conn.Read(buf)
	if err != nil {
		return nil, err
	}
	scanner := bufio.NewScanner(bytes.NewReader(buf[:n]))
	scanner.Split(bufio.ScanWords)
	for scanner.Scan() {
		switch scanner.Text() {
		case "STREAM" :
			continue
		case "STATUS" :
			continue
		case "RESULT=OK" :
			return &SAMConn{s.keys.addr, addr, conn}, nil
		case "RESULT=CANT_REACH_PEER" :
      sam.Close()
			return nil, errors.New("Can not reach peer")
		case "RESULT=I2P_ERROR" :
      sam.Close()
			return nil, errors.New("I2P internal error")
		case "RESULT=INVALID_KEY" :
      sam.Close()
			return nil, errors.New("Invalid key")
		case "RESULT=INVALID_ID" :
      sam.Close()
			return nil, errors.New("Invalid tunnel ID")
		case "RESULT=TIMEOUT" :
      sam.Close()
			return nil, errors.New("Timeout")
    default:
      sam.Close()
			return nil, errors.New("Unknown error: " + scanner.Text() + " : " + string(buf[:n]))
		}
	}
	panic("sam3 go library error in StreamSession.DialI2P()")
}

// create a new stream listener to accept inbound connections
func (s *StreamSession) Listen() (*StreamListener, error) {
  return &StreamListener{
    session: s,
    id: s.id,
    laddr: s.keys.Addr(),
  }, nil
}

type StreamListener struct {
  // parent stream session
  session *StreamSession
  // our session id
  id string
  // our local address for this sam socket
  laddr I2PAddr
}

// get our address
// implements net.Listener
func (l *StreamListener) Addr() net.Addr {
  return l.laddr
}

// implements net.Listener
func (l *StreamListener) Close() error {
  return l.session.Close()
}

// implements net.Listener
func (l *StreamListener) Accept() (net.Conn, error) {
  return l.AcceptI2P()
}

// accept a new inbound connection
func (l *StreamListener) AcceptI2P() (*SAMConn, error) {
  s, err := NewSAM(l.session.samAddr)
  if err == nil {
    // we connected to sam
    // send accept() command
    _, err = io.WriteString(s.conn, "STREAM ACCEPT ID="+l.id+" SILENT=false\n")
    // read reply
    rd := bufio.NewReader(s.conn)
    // read first line
    line, err := rd.ReadString(10)
    if err == nil {
      if strings.HasPrefix(line, "STREAM STATUS RESULT=OK") {
        // we gud read destination line
        dest, err := rd.ReadString(10)
        if err == nil {
          // return wrapped connection
          dest = strings.Trim(dest, "\n")
          return &SAMConn{
            laddr: l.laddr,
            raddr: I2PAddr(dest),
            conn: s.conn,
          }, nil
        } else {
          s.Close()
          return nil, err
        }
      } else {
        s.Close()
        return nil, errors.New("invalid sam line: "+line)
      }
    } else {
      s.Close()
      return nil, err
    }
  }
  return nil, err
}
<|endoftext|>


<commit_before>package checkmailq

import (
	"fmt"
	"os"
	"os/exec"
	"regexp"
	"strconv"
	"strings"

	"github.com/jessevdk/go-flags"
	"github.com/mackerelio/checkers"
)

// Do the plugin
func Do() {
	ckr := run(os.Args[1:])
	ckr.Name = "Mailq"
	ckr.Exit()
}

type monitor struct {
	warning  int64
	critical int64
}

func (m monitor) hasWarning() bool {
	return m.warning != 0
}

func (m monitor) checkWarning(queue int64) bool {
	return (m.hasWarning() && m.warning < queue)
}

func (m monitor) hasCritical() bool {
	return m.critical != 0
}

func (m monitor) checkCritical(queue int64) bool {
	return (m.hasCritical() && m.critical < queue)
}

func newMonitor(warning, critical int64) *monitor {
	return &monitor{
		warning:  warning,
		critical: critical,
	}
}

var opts struct {
	Warning  int64  `short:"w" long:"warning" default:"100" description:"number of messages in queue to generate warning"`
	Critical int64  `short:"c" long:"critical" default:"200" description:"number of messages in queue to generate critical alert ( w < c )"`
	Mta      string `short:"M" long:"mta" default:"postfix" description:"target mta"`
}

func run(args []string) *checkers.Checker {
	_, err := flags.ParseArgs(&opts, args)
	if err != nil {
		os.Exit(1)
	}

	var queue int64
	queueStr := "0"
	monitor := newMonitor(opts.Warning, opts.Critical)

	result := checkers.OK

	if opts.Mta == "postfix" {
		out, err := exec.Command("mailq").Output()

		if err != nil {
			return checkers.Unknown(err.Error())
		}

		outs := strings.Split(string(out), "\n")
		line := outs[len(outs)-2]

		re := regexp.MustCompile(`-- \d+ Kbytes in (\d+) Requests.`)
		if re.MatchString(line) {
			queueStr = re.ReplaceAllString(line, "$1")
			queue, err = strconv.ParseInt(queueStr, 10, 64)
			if err != nil {
				return checkers.Unknown(err.Error())
			}
		}
	} else if opts.Mta == "qmail" {
		out, err := exec.Command("qmail-qstat").Output()

		if err != nil {
			return checkers.Unknown(err.Error())
		}

		outs := strings.Split(string(out), "\n")
		line := outs[0]

		re := regexp.MustCompile(`^messages in queue: (\d+)`)
		if re.MatchString(line) {
			queueStr = re.ReplaceAllString(line, "$1")
			queue, err = strconv.ParseInt(queueStr, 10, 64)
			if err != nil {
				return checkers.Unknown(err.Error())
			}
		}
	} else {
		return checkers.Unknown(fmt.Sprintf("%s: specified mta's check is not implemented.", opts.Mta))
	}

	if monitor.checkWarning(queue) {
		result = checkers.WARNING
	}

	if monitor.checkCritical(queue) {
		result = checkers.CRITICAL
	}

	msg := fmt.Sprintf(queueStr)
	return checkers.NewChecker(result, msg)
}
<commit_msg>fix message<commit_after>package checkmailq

import (
	"fmt"
	"os"
	"os/exec"
	"regexp"
	"strconv"
	"strings"

	"github.com/jessevdk/go-flags"
	"github.com/mackerelio/checkers"
)

// Do the plugin
func Do() {
	ckr := run(os.Args[1:])
	ckr.Name = "Mailq"
	ckr.Exit()
}

type monitor struct {
	warning  int64
	critical int64
}

func (m monitor) hasWarning() bool {
	return m.warning != 0
}

func (m monitor) checkWarning(queue int64) bool {
	return (m.hasWarning() && m.warning < queue)
}

func (m monitor) hasCritical() bool {
	return m.critical != 0
}

func (m monitor) checkCritical(queue int64) bool {
	return (m.hasCritical() && m.critical < queue)
}

func newMonitor(warning, critical int64) *monitor {
	return &monitor{
		warning:  warning,
		critical: critical,
	}
}

var opts struct {
	Warning  int64  `short:"w" long:"warning" default:"100" description:"number of messages in queue to generate warning"`
	Critical int64  `short:"c" long:"critical" default:"200" description:"number of messages in queue to generate critical alert ( w < c )"`
	Mta      string `short:"M" long:"mta" default:"postfix" description:"target mta"`
}

func run(args []string) *checkers.Checker {
	_, err := flags.ParseArgs(&opts, args)
	if err != nil {
		os.Exit(1)
	}

	var queue int64
	queueStr := "0"
	monitor := newMonitor(opts.Warning, opts.Critical)

	result := checkers.OK

	if opts.Mta == "postfix" {
		out, err := exec.Command("mailq").Output()

		if err != nil {
			return checkers.Unknown(err.Error())
		}

		outs := strings.Split(string(out), "\n")
		line := outs[len(outs)-2]

		re := regexp.MustCompile(`-- \d+ Kbytes in (\d+) Requests.`)
		if re.MatchString(line) {
			queueStr = re.ReplaceAllString(line, "$1")
			queue, err = strconv.ParseInt(queueStr, 10, 64)
			if err != nil {
				return checkers.Unknown(err.Error())
			}
		}
	} else if opts.Mta == "qmail" {
		out, err := exec.Command("qmail-qstat").Output()

		if err != nil {
			return checkers.Unknown(err.Error())
		}

		outs := strings.Split(string(out), "\n")
		line := outs[0]

		re := regexp.MustCompile(`^messages in queue: (\d+)`)
		if re.MatchString(line) {
			queueStr = re.ReplaceAllString(line, "$1")
			queue, err = strconv.ParseInt(queueStr, 10, 64)
			if err != nil {
				return checkers.Unknown(err.Error())
			}
		}
	} else {
		return checkers.Unknown(fmt.Sprintf("%s: specified mta's check is not implemented.", opts.Mta))
	}

	if monitor.checkWarning(queue) {
		result = checkers.WARNING
	}

	if monitor.checkCritical(queue) {
		result = checkers.CRITICAL
	}

	return checkers.NewChecker(result, queueStr)
}
<|endoftext|>


<commit_before>package auth

import (
	"http"
	"strings"
	"encoding/base64"
	"crypto/sha1"
)

type BasicAuth struct {
	Realm   string
	Secrets SecretProvider
}

/*
 Checks the username/password combination from the request. Returns
 either an empty string (authentication failed) or the name of the
 authenticated user.

 Supports MD5 and SHA1 password entries
*/
func (a *BasicAuth) CheckAuth(r *http.Request) string {
	s := strings.SplitN(r.Header.Get("Authorization"), " ", 2)
	if len(s) != 2 || s[0] != "Basic" {
		return ""
	}

	b, err := base64.StdEncoding.DecodeString(s[1])
	if err != nil {
		return ""
	}
	pair := strings.SplitN(string(b), ":", 2)
	if len(pair) != 2 {
		return ""
	}
	passwd := a.Secrets(pair[0], a.Realm)
	if passwd[:5] == "{SHA}" {
		d := sha1.New()
		d.Write([]byte(pair[1]))
		if passwd[5:] != base64.StdEncoding.EncodeToString(d.Sum()) {
			return ""
		}
	} else {
		e := NewMD5Entry(passwd)
		if e == nil {
			return ""
		}
		if passwd != string(MD5Crypt([]byte(pair[1]), e.Salt, e.Magic)) {
			return ""
		}
	}
	return pair[0]
}

/*
 http.Handler for BasicAuth which initiates the authentication process
 (or requires reauthentication).
*/
func (a *BasicAuth) RequireAuth(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("WWW-Authenticate", `Basic realm="`+a.Realm+`"`)
	w.WriteHeader(401)
	w.Write([]byte("401 Unauthorized\n"))
}

/*
 BasicAuthenticator returns a function, which wraps an
 AuthenticatedHandlerFunc converting it to http.HandlerFunc. This
 wrapper function checks the authentication and either sends back
 required authentication headers, or calls the wrapped function with
 authenticated username in the AuthenticatedRequest.
*/
func BasicAuthenticator(realm string, secrets SecretProvider) Authenticator {
	a := &BasicAuth{Realm: realm, Secrets: secrets}
	return func(wrapped AuthenticatedHandlerFunc) http.HandlerFunc {
		return func(w http.ResponseWriter, r *http.Request) {
			if username := a.CheckAuth(r); username == "" {
				a.RequireAuth(w, r)
			} else {
				ar := &AuthenticatedRequest{Request: *r, Username: username}
				wrapped(w, ar)
			}
		}
	}
}
<commit_msg>minor fix: auth should fail if password for the user is empty (unknown)<commit_after>package auth

import (
	"http"
	"strings"
	"encoding/base64"
	"crypto/sha1"
)

type BasicAuth struct {
	Realm   string
	Secrets SecretProvider
}

/*
 Checks the username/password combination from the request. Returns
 either an empty string (authentication failed) or the name of the
 authenticated user.

 Supports MD5 and SHA1 password entries
*/
func (a *BasicAuth) CheckAuth(r *http.Request) string {
	s := strings.SplitN(r.Header.Get("Authorization"), " ", 2)
	if len(s) != 2 || s[0] != "Basic" {
		return ""
	}

	b, err := base64.StdEncoding.DecodeString(s[1])
	if err != nil {
		return ""
	}
	pair := strings.SplitN(string(b), ":", 2)
	if len(pair) != 2 {
		return ""
	}
	passwd := a.Secrets(pair[0], a.Realm)
	if passwd == "" {
		return ""
	}
	if passwd[:5] == "{SHA}" {
		d := sha1.New()
		d.Write([]byte(pair[1]))
		if passwd[5:] != base64.StdEncoding.EncodeToString(d.Sum()) {
			return ""
		}
	} else {
		e := NewMD5Entry(passwd)
		if e == nil {
			return ""
		}
		if passwd != string(MD5Crypt([]byte(pair[1]), e.Salt, e.Magic)) {
			return ""
		}
	}
	return pair[0]
}

/*
 http.Handler for BasicAuth which initiates the authentication process
 (or requires reauthentication).
*/
func (a *BasicAuth) RequireAuth(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("WWW-Authenticate", `Basic realm="`+a.Realm+`"`)
	w.WriteHeader(401)
	w.Write([]byte("401 Unauthorized\n"))
}

/*
 BasicAuthenticator returns a function, which wraps an
 AuthenticatedHandlerFunc converting it to http.HandlerFunc. This
 wrapper function checks the authentication and either sends back
 required authentication headers, or calls the wrapped function with
 authenticated username in the AuthenticatedRequest.
*/
func BasicAuthenticator(realm string, secrets SecretProvider) Authenticator {
	a := &BasicAuth{Realm: realm, Secrets: secrets}
	return func(wrapped AuthenticatedHandlerFunc) http.HandlerFunc {
		return func(w http.ResponseWriter, r *http.Request) {
			if username := a.CheckAuth(r); username == "" {
				a.RequireAuth(w, r)
			} else {
				ar := &AuthenticatedRequest{Request: *r, Username: username}
				wrapped(w, ar)
			}
		}
	}
}
<|endoftext|>


<commit_before>package dsc

import (
	"compress/gzip"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
)

type batch struct {
	processed      int
	tempDir        string
	tempFile       string
	size           int
	sql            string
	writer         *gzip.Writer
	values         []interface{}
	placeholders   string
	columns        string
	dataIndexes    []int
	firstSeq       int64
	bulkInsertType string
	manager        *AbstractManager
	sqlProvider    func(item interface{}) *ParametrizedSQL
	updateId       func(index int, seq int64)
	connection     Connection
	table          string
}

func (b *batch) flush() (int, error) {
	if b.sql == "" {
		return 0, nil
	}

	var dataIndexes = b.dataIndexes
	b.dataIndexes = []int{}
	switch b.bulkInsertType {
	case CopyLocalInsert:
		defer os.Remove(b.tempFile)
		err := b.writer.Flush()
		if err != nil {
			return 0, err
		}
		err = b.writer.Close()
		if err != nil {
			return 0, err
		}
	case BulkInsertAllType:
		b.sql += " SELECT 1 FROM DUAL"
	}
	result, err := b.manager.ExecuteOnConnection(b.connection, b.sql, b.values)
	b.dataIndexes = []int{}
	b.sql = ""
	b.values = []interface{}{}
	if err != nil {
		return 0, err
	}
	affected, err := result.RowsAffected()
	if err != nil {
		return 0, err
	}
	for _, i := range dataIndexes {
		b.firstSeq++
		b.updateId(i, b.firstSeq)
	}
	b.firstSeq = 0
	return int(affected), nil
}

func (b *batch) expandedValues(parametrizedSQL *ParametrizedSQL) string {
	recordLine := b.manager.ExpandSQL(b.placeholders, parametrizedSQL.Values)
	if breakCount := strings.Count(recordLine, "\n"); breakCount > 0 {
		recordLine = strings.Replace(recordLine, "\n", "", breakCount)
	}
	return recordLine + "\n"
}

func (b *batch) transformFirst(parametrizedSQL *ParametrizedSQL) error {
	b.sql = parametrizedSQL.SQL
	b.values = parametrizedSQL.Values
	fragment := " VALUES"
	valuesIndex := strings.Index(parametrizedSQL.SQL, fragment)
	if beginIndex := strings.Index(parametrizedSQL.SQL, "("); beginIndex != -1 {
		names := string(parametrizedSQL.SQL[beginIndex+1:])
		if endIndex := strings.Index(names, ")"); endIndex != -1 {
			b.columns = string(names[:endIndex])
		}
	}
	b.placeholders = strings.Trim(strings.TrimSpace(string(parametrizedSQL.SQL[valuesIndex+7:])), "()")
	switch b.bulkInsertType {
	case CopyLocalInsert:
		b.tempDir = b.manager.config.GetString("tempDir", os.TempDir())
		if b.columns == "" {
			return fmt.Errorf("columns were empty")
		}
		file, err := ioutil.TempFile(b.tempDir, "temp")
		if err != nil {
			return err
		}
		b.tempFile = file.Name()
		b.writer = gzip.NewWriter(file)
		if _, err := b.writer.Write([]byte(b.expandedValues(parametrizedSQL))); err != nil {
			return err
		}

		dialect := GetDatastoreDialect(b.manager.config.DriverName)
		table := b.table
		if db, _ := dialect.GetCurrentDatastore(b.manager); db != "" {
			table = db + "." + table
		}
		b.sql = fmt.Sprintf(`COPY %v(%v)
FROM LOCAL '%v' GZIP
DELIMITER ','
NULL AS 'null'
ENCLOSED BY ''''
`, table, b.columns, file.Name())
		b.values = make([]interface{}, 0)
	case UnionSelectInsert:
		valuesIndex := strings.Index(parametrizedSQL.SQL, " VALUES")
		selectAll := " SELECT " + b.expandedValues(parametrizedSQL)
		selectAll = b.manager.ExpandSQL(selectAll, parametrizedSQL.Values)
		parametrizedSQL.Values = []interface{}{}
		b.sql = b.sql[:valuesIndex] + " " + selectAll

	case BulkInsertAllType:
		b.sql = strings.Replace(b.sql, "INSERT ", "INSERT ALL ", 1)
	default:

	}
	return nil
}

func (b *batch) transformNext(parametrizedSQL *ParametrizedSQL) error {
	switch b.bulkInsertType {
	case CopyLocalInsert:
		_, err := b.writer.Write([]byte(b.expandedValues(parametrizedSQL)))
		return err
	case UnionSelectInsert:
		b.sql += "\nUNION ALL SELECT " + b.expandedValues(parametrizedSQL)
	case BulkInsertAllType:
		b.sql += fmt.Sprintf("\nINTO %v(%v) VALUES(%v)", b.table, b.columns, b.placeholders)
		b.values = append(b.values, parametrizedSQL.Values...)
	default:
		b.sql += fmt.Sprintf(",(%v)", b.placeholders)
		b.values = append(b.values, parametrizedSQL.Values...)
	}
	return nil
}

func (b *batch) persist(index int, item interface{}) error {
	parametrizedSQL := b.sqlProvider(item)
	if len(parametrizedSQL.Values) == 1 && parametrizedSQL.Type == SQLTypeUpdate {
		//nothing to udpate, one parameter is ID=? without values to update
		return nil
	}
	if parametrizedSQL.Type == SQLTypeInsert && b.size > 0 {
		if len(b.dataIndexes) > b.size {
			if _, err := b.flush(); err != nil {
				return err
			}
		}
		b.dataIndexes = append(b.dataIndexes, index)
		if isFirst := len(b.sql) == 0; isFirst {
			return b.transformFirst(parametrizedSQL)
		}
		return b.transformNext(parametrizedSQL)
	}
	result, err := b.manager.ExecuteOnConnection(b.connection, parametrizedSQL.SQL, parametrizedSQL.Values)
	if err != nil {
		return err
	}
	affected, err := result.RowsAffected()
	if err != nil {
		return err
	}
	b.processed += int(affected)
	seq, _ := result.LastInsertId()
	if b.size > 0 && b.firstSeq == 0 {
		b.firstSeq = seq
	}
	b.updateId(index, seq)
	return nil
}

func newBatch(table string, connection Connection, manager *AbstractManager, sqlProvider func(item interface{}) *ParametrizedSQL, updateId func(index int, seq int64)) *batch {
	dialect := GetDatastoreDialect(manager.Config().DriverName)
	var batchSize = manager.Config().GetInt(BatchSizeKey, defaultBatchSize)
	Logf("batch size: %v\n", batchSize)
	canUseBatch := dialect != nil && dialect.CanPersistBatch() && batchSize > 1
	if !canUseBatch {
		batchSize = 0
	}
	insertType := ""
	if dialect != nil {
		insertType = dialect.BulkInsertType()
	}
	return &batch{
		connection:     connection,
		updateId:       updateId,
		sqlProvider:    sqlProvider,
		size:           batchSize,
		values:         []interface{}{},
		dataIndexes:    []int{},
		bulkInsertType: insertType,
		manager:        manager,
		table:          table,
	}
}
<commit_msg>reverted copy changes<commit_after>package dsc

import (
	"compress/gzip"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
)

type batch struct {
	processed      int
	tempDir        string
	tempFile       string
	size           int
	sql            string
	writer         *gzip.Writer
	values         []interface{}
	placeholders   string
	columns        string
	dataIndexes    []int
	firstSeq       int64
	bulkInsertType string
	manager        *AbstractManager
	sqlProvider    func(item interface{}) *ParametrizedSQL
	updateId       func(index int, seq int64)
	connection     Connection
	table          string
}

func (b *batch) flush() (int, error) {
	if b.sql == "" {
		return 0, nil
	}

	var dataIndexes = b.dataIndexes
	b.dataIndexes = []int{}
	switch b.bulkInsertType {
	case CopyLocalInsert:
		defer os.Remove(b.tempFile)
		err := b.writer.Flush()
		if err != nil {
			return 0, err
		}
		err = b.writer.Close()
		if err != nil {
			return 0, err
		}
	case BulkInsertAllType:
		b.sql += " SELECT 1 FROM DUAL"
	}
	result, err := b.manager.ExecuteOnConnection(b.connection, b.sql, b.values)
	b.dataIndexes = []int{}
	b.sql = ""
	b.values = []interface{}{}
	if err != nil {
		return 0, err
	}
	affected, err := result.RowsAffected()
	if err != nil {
		return 0, err
	}
	for _, i := range dataIndexes {
		b.firstSeq++
		b.updateId(i, b.firstSeq)
	}
	b.firstSeq = 0
	return int(affected), nil
}

func (b *batch) expandedValues(parametrizedSQL *ParametrizedSQL) string {
	recordLine := b.manager.ExpandSQL(b.placeholders, parametrizedSQL.Values)
	if breakCount := strings.Count(recordLine, "\n"); breakCount > 0 {
		recordLine = strings.Replace(recordLine, "\n", "", breakCount)
	}
	return recordLine + "\n"
}

func (b *batch) transformFirst(parametrizedSQL *ParametrizedSQL) error {
	b.sql = parametrizedSQL.SQL
	b.values = parametrizedSQL.Values
	fragment := " VALUES"
	valuesIndex := strings.Index(parametrizedSQL.SQL, fragment)
	if beginIndex := strings.Index(parametrizedSQL.SQL, "("); beginIndex != -1 {
		names := string(parametrizedSQL.SQL[beginIndex+1:])
		if endIndex := strings.Index(names, ")"); endIndex != -1 {
			b.columns = string(names[:endIndex])
		}
	}
	b.placeholders = strings.Trim(strings.TrimSpace(string(parametrizedSQL.SQL[valuesIndex+7:])), "()")
	switch b.bulkInsertType {
	case CopyLocalInsert:
		b.tempDir = b.manager.config.GetString("tempDir", os.TempDir())
		if b.columns == "" {
			return fmt.Errorf("columns were empty")
		}
		file, err := ioutil.TempFile(b.tempDir, "temp")
		if err != nil {
			return err
		}
		b.tempFile = file.Name()
		b.writer = gzip.NewWriter(file)
		if _, err := b.writer.Write([]byte(b.expandedValues(parametrizedSQL))); err != nil {
			return err
		}

		table := b.table
		b.sql = fmt.Sprintf(`COPY %v(%v)
FROM LOCAL '%v' GZIP
DELIMITER ','
NULL AS 'null'
ENCLOSED BY ''''
`, table, b.columns, file.Name())
		b.values = make([]interface{}, 0)
	case UnionSelectInsert:
		valuesIndex := strings.Index(parametrizedSQL.SQL, " VALUES")
		selectAll := " SELECT " + b.expandedValues(parametrizedSQL)
		selectAll = b.manager.ExpandSQL(selectAll, parametrizedSQL.Values)
		parametrizedSQL.Values = []interface{}{}
		b.sql = b.sql[:valuesIndex] + " " + selectAll

	case BulkInsertAllType:
		b.sql = strings.Replace(b.sql, "INSERT ", "INSERT ALL ", 1)
	default:

	}
	return nil
}

func (b *batch) transformNext(parametrizedSQL *ParametrizedSQL) error {
	switch b.bulkInsertType {
	case CopyLocalInsert:
		_, err := b.writer.Write([]byte(b.expandedValues(parametrizedSQL)))
		return err
	case UnionSelectInsert:
		b.sql += "\nUNION ALL SELECT " + b.expandedValues(parametrizedSQL)
	case BulkInsertAllType:
		b.sql += fmt.Sprintf("\nINTO %v(%v) VALUES(%v)", b.table, b.columns, b.placeholders)
		b.values = append(b.values, parametrizedSQL.Values...)
	default:
		b.sql += fmt.Sprintf(",(%v)", b.placeholders)
		b.values = append(b.values, parametrizedSQL.Values...)
	}
	return nil
}

func (b *batch) persist(index int, item interface{}) error {
	parametrizedSQL := b.sqlProvider(item)
	if len(parametrizedSQL.Values) == 1 && parametrizedSQL.Type == SQLTypeUpdate {
		//nothing to udpate, one parameter is ID=? without values to update
		return nil
	}
	if parametrizedSQL.Type == SQLTypeInsert && b.size > 0 {
		if len(b.dataIndexes) > b.size {
			if _, err := b.flush(); err != nil {
				return err
			}
		}
		b.dataIndexes = append(b.dataIndexes, index)
		if isFirst := len(b.sql) == 0; isFirst {
			return b.transformFirst(parametrizedSQL)
		}
		return b.transformNext(parametrizedSQL)
	}
	result, err := b.manager.ExecuteOnConnection(b.connection, parametrizedSQL.SQL, parametrizedSQL.Values)
	if err != nil {
		return err
	}
	affected, err := result.RowsAffected()
	if err != nil {
		return err
	}
	b.processed += int(affected)
	seq, _ := result.LastInsertId()
	if b.size > 0 && b.firstSeq == 0 {
		b.firstSeq = seq
	}
	b.updateId(index, seq)
	return nil
}

func newBatch(table string, connection Connection, manager *AbstractManager, sqlProvider func(item interface{}) *ParametrizedSQL, updateId func(index int, seq int64)) *batch {
	dialect := GetDatastoreDialect(manager.Config().DriverName)
	var batchSize = manager.Config().GetInt(BatchSizeKey, defaultBatchSize)
	Logf("batch size: %v\n", batchSize)
	canUseBatch := dialect != nil && dialect.CanPersistBatch() && batchSize > 1
	if !canUseBatch {
		batchSize = 0
	}
	insertType := ""
	if dialect != nil {
		insertType = dialect.BulkInsertType()
	}
	return &batch{
		connection:     connection,
		updateId:       updateId,
		sqlProvider:    sqlProvider,
		size:           batchSize,
		values:         []interface{}{},
		dataIndexes:    []int{},
		bulkInsertType: insertType,
		manager:        manager,
		table:          table,
	}
}
<|endoftext|>