<commit_before>/*
Copyright 2019 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 main

import (
	"context"
	"testing"

	"k8s.io/api/networking/v1beta1"
	"k8s.io/apimachinery/pkg/util/intstr"
	"k8s.io/ingress-gce/pkg/e2e"
	"k8s.io/ingress-gce/pkg/fuzz"
	"k8s.io/ingress-gce/pkg/fuzz/features"
	"k8s.io/ingress-gce/pkg/utils"
)

// TestFinalizer asserts that finalizer is added/ removed appropriately during the life cycle
// of an ingress resource.
func TestFinalizer(t *testing.T) {
	t.Parallel()
	ctx := context.Background()
	port80 := intstr.FromInt(80)
	numForwardingRules := 1
	numBackendServices := 2
	svcName := "service-1"
	ing := fuzz.NewIngressBuilder("", "ingress-1", "").
		AddPath("foo.com", "/", svcName, port80).
		SetIngressClass("gce").
		Build()

	Framework.RunWithSandbox("finalizer", t, func(t *testing.T, s *e2e.Sandbox) {
		_, err := e2e.CreateEchoService(s, svcName, nil)
		if err != nil {
			t.Fatalf("CreateEchoService(_, %q, nil): %v, want nil", svcName, err)
		}
		t.Logf("Echo service created (%s/%s)", s.Namespace, svcName)

		crud := e2e.IngressCRUD{C: Framework.Clientset}
		ing.Namespace = s.Namespace
		if _, err := crud.Create(ing); err != nil {
			t.Fatalf("create(%s/%s) = %v, want nil; Ingress: %v", ing.Namespace, ing.Name, err, ing)
		}
		t.Logf("Ingress created (%s/%s)", s.Namespace, ing.Name)
		ing = waitForStableIngress(true, ing, s, t)

		ingFinalizers := ing.GetFinalizers()
		if len(ingFinalizers) != 1 || ingFinalizers[0] != utils.FinalizerKey {
			t.Fatalf("GetFinalizers() = %+v, want [%q]", ingFinalizers, utils.FinalizerKey)
		}

		gclb := checkGCLB(t, s, ing, numForwardingRules, numBackendServices)

		deleteOptions := &fuzz.GCLBDeleteOptions{
			SkipDefaultBackend: true,
		}
		if err := e2e.WaitForIngressDeletion(ctx, gclb, s, ing, deleteOptions); err != nil {
			t.Errorf("e2e.WaitForIngressDeletion(..., %q, nil) = %v, want nil", ing.Name, err)
		}
	})
}

// TestFinalizerIngressClassChange asserts that finalizer is removed from ingress after its
// associated resources are cleaned up when ingress class is updated to a non glbc type.
func TestFinalizerIngressClassChange(t *testing.T) {
	t.Parallel()
	ctx := context.Background()
	port80 := intstr.FromInt(80)
	numForwardingRules := 1
	numBackendServices := 2
	svcName := "service-1"
	ing := fuzz.NewIngressBuilder("", "ingress-1", "").
		AddPath("foo.com", "/", svcName, port80).
		SetIngressClass("gce").
		Build()

	Framework.RunWithSandbox("finalizer-ingress-class-change", t, func(t *testing.T, s *e2e.Sandbox) {
		_, err := e2e.CreateEchoService(s, svcName, nil)
		if err != nil {
			t.Fatalf("CreateEchoService(_, %q, nil): %v, want nil", svcName, err)
		}
		t.Logf("Echo service created (%s/%s)", s.Namespace, svcName)

		crud := e2e.IngressCRUD{C: Framework.Clientset}
		ing.Namespace = s.Namespace
		if _, err := crud.Create(ing); err != nil {
			t.Fatalf("create(%s/%s) = %v, want nil; Ingress: %v", ing.Namespace, ing.Name, err, ing)
		}
		t.Logf("Ingress created (%s/%s)", s.Namespace, ing.Name)
		ing = waitForStableIngress(true, ing, s, t)

		ingFinalizers := ing.GetFinalizers()
		if len(ingFinalizers) != 1 || ingFinalizers[0] != utils.FinalizerKey {
			t.Fatalf("GetFinalizers() = %+v, want [%q]", ingFinalizers, utils.FinalizerKey)
		}

		gclb := checkGCLB(t, s, ing, numForwardingRules, numBackendServices)

		// Change Ingress class
		newIngClass := "nginx"
		ing = fuzz.NewIngressBuilderFromExisting(ing).SetIngressClass(newIngClass).Build()

		if _, err := crud.Update(ing); err != nil {
			t.Fatalf("update(%s/%s) = %v, want nil; ingress: %v", ing.Namespace, ing.Name, err, ing)
		}
		t.Logf("Ingress (%s/%s) class changed to %s", s.Namespace, ing.Name, newIngClass)

		deleteOptions := &fuzz.GCLBDeleteOptions{
			SkipDefaultBackend: true,
		}
		if err := e2e.WaitForFinalizerDeletion(ctx, gclb, s, ing.Name, deleteOptions); err != nil {
			t.Errorf("e2e.WaitForFinalizerDeletion(..., %q, _) = %v, want nil", ing.Name, err)
		}
		t.Logf("Finalizer for Ingress (%s/%s) deleted", s.Namespace, ing.Name)
	})
}

// TestFinalizerIngressesWithSharedResources asserts that sync does not return error with finalizer
// when multiple ingresses with shared resources are created or deleted.
func TestFinalizerIngressesWithSharedResources(t *testing.T) {
	t.Parallel()
	ctx := context.Background()
	port80 := intstr.FromInt(80)
	numForwardingRules := 1
	numBackendServices := 2
	svcName := "service-1"
	ing := fuzz.NewIngressBuilder("", "ingress-1", "").
		AddPath("foo.com", "/", svcName, port80).
		SetIngressClass("gce").
		Build()
	otherIng := fuzz.NewIngressBuilder("", "ingress-2", "").
		AddPath("foo.com", "/", svcName, port80).
		SetIngressClass("gce").
		Build()

	Framework.RunWithSandbox("finalizer-ingress-class-change", t, func(t *testing.T, s *e2e.Sandbox) {
		_, err := e2e.CreateEchoService(s, svcName, nil)
		if err != nil {
			t.Fatalf("CreateEchoService(_, %q, nil): %v, want nil", svcName, err)
		}
		t.Logf("Echo service created (%s/%s)", s.Namespace, svcName)

		crud := e2e.IngressCRUD{C: Framework.Clientset}
		ing.Namespace = s.Namespace
		if _, err := crud.Create(ing); err != nil {
			t.Fatalf("create(%s/%s) = %v, want nil; Ingress: %v", ing.Namespace, ing.Name, err, ing)
		}
		t.Logf("Ingress created (%s/%s)", s.Namespace, ing.Name)

		otherIng.Namespace = s.Namespace
		if _, err := crud.Create(otherIng); err != nil {
			t.Fatalf("create(%s/%s) = %v, want nil; Ingress: %v", otherIng.Namespace, otherIng.Name, err, otherIng)
		}
		t.Logf("Ingress created (%s/%s)", s.Namespace, otherIng.Name)

		ing = waitForStableIngress(true, ing, s, t)
		otherIng = waitForStableIngress(true, otherIng, s, t)

		ingFinalizers := ing.GetFinalizers()
		if len(ingFinalizers) != 1 || ingFinalizers[0] != utils.FinalizerKey {
			t.Fatalf("GetFinalizers() = %+v, want [%q]", ingFinalizers, utils.FinalizerKey)
		}
		otherIngFinalizers := otherIng.GetFinalizers()
		if len(otherIngFinalizers) != 1 || otherIngFinalizers[0] != utils.FinalizerKey {
			t.Fatalf("GetFinalizers() = %+v, want [%q]", otherIngFinalizers, utils.FinalizerKey)
		}

		gclb := checkGCLB(t, s, ing, numForwardingRules, numBackendServices)
		otherGclb := checkGCLB(t, s, otherIng, numForwardingRules, numBackendServices)

		// SkipBackends ensure that we dont wait on deletion of shared backends.
		deleteOptions := &fuzz.GCLBDeleteOptions{
			SkipDefaultBackend: true,
			SkipBackends:       true,
		}
		if err := e2e.WaitForIngressDeletion(ctx, gclb, s, ing, deleteOptions); err != nil {
			t.Errorf("e2e.WaitForIngressDeletion(..., %q, nil) = %v, want nil", ing.Name, err)
		}
		deleteOptions = &fuzz.GCLBDeleteOptions{
			SkipDefaultBackend: true,
		}
		if err := e2e.WaitForIngressDeletion(ctx, otherGclb, s, otherIng, deleteOptions); err != nil {
			t.Errorf("e2e.WaitForIngressDeletion(..., %q, nil) = %v, want nil", otherIng.Name, err)
		}
	})
}

// TestUpdateTo1dot7 asserts that finalizer is added to an ingress when upgraded from a version
// without finalizer to version 1.7. Also, verifies that ingress is deleted with finalizer enabled.
// Note: The test is named in such a way that it does run as a normal test or an upgrade test for
// other versions.
func TestUpdateTo1dot7(t *testing.T) {
	port80 := intstr.FromInt(80)
	numForwardingRules := 1
	numBackendServices := 2
	svcName := "service-1"
	ing := fuzz.NewIngressBuilder("", "ingress-1", "").
		AddPath("foo.com", "/", svcName, port80).
		SetIngressClass("gce").
		Build()
	Framework.RunWithSandbox("finalizer-master-upgrade", t, func(t *testing.T, s *e2e.Sandbox) {
		t.Parallel()

		_, err := e2e.CreateEchoService(s, svcName, nil)
		if err != nil {
			t.Fatalf("CreateEchoService(_, %q, nil): %v, want nil", svcName, err)
		}
		t.Logf("Echo service created (%s/%s)", s.Namespace, svcName)

		crud := e2e.IngressCRUD{C: Framework.Clientset}
		ing.Namespace = s.Namespace
		if _, err := crud.Create(ing); err != nil {
			t.Fatalf("create(%s/%s) = %v, want nil; Ingress: %v", ing.Namespace, ing.Name, err, ing)
		}
		t.Logf("Ingress created (%s/%s)", s.Namespace, ing.Name)
		waitForStableIngress(true, ing, s, t)

		// Check that finalizer is not added in old version in which finalizer add is not enabled.
		ingFinalizers := ing.GetFinalizers()
		if l := len(ingFinalizers); l != 0 {
			t.Fatalf("GetFinalizers() = %d, want 0", l)
		}

		checkGCLB(t, s, ing, numForwardingRules, numBackendServices)

		for {
			// While k8s master is upgrading, it will return a connection refused
			// error for any k8s resource we try to hit. We loop until the
			// master upgrade has finished.
			if s.MasterUpgrading() {
				continue
			}

			if s.MasterUpgraded() {
				t.Logf("Detected master upgrade")
				break
			}
		}

		// Wait for finalizer to be added and verify that correct finalizer is added to the ingress after the upgrade.
		if err := e2e.WaitForFinalizer(s, ing.Name); err != nil {
			t.Errorf("e2e.WaitForFinalizer(_, %q) = %v, want nil", ing.Name, err)
		}

		gclb := checkGCLB(t, s, ing, numForwardingRules, numBackendServices)

		// If the Master has upgraded and the Ingress is stable,
		// we delete the Ingress and exit out of the loop to indicate that
		// the test is done.
		deleteOptions := &fuzz.GCLBDeleteOptions{
			SkipDefaultBackend: true,
		}
		if err := e2e.WaitForIngressDeletion(context.Background(), gclb, s, ing, deleteOptions); err != nil {
			t.Errorf("e2e.WaitForIngressDeletion(..., %q, nil) = %v, want nil", ing.Name, err)
		}
	})
}

func checkGCLB(t *testing.T, s *e2e.Sandbox, ing *v1beta1.Ingress, numForwardingRules, numBackendServices int) *fuzz.GCLB {
	// Perform whitebox testing.
	if len(ing.Status.LoadBalancer.Ingress) < 1 {
		t.Fatalf("Ingress does not have an IP: %+v", ing.Status)
	}
	vip := ing.Status.LoadBalancer.Ingress[0].IP
	t.Logf("Ingress %s/%s VIP = %s", s.Namespace, ing.Name, vip)
	gclb, err := fuzz.GCLBForVIP(context.Background(), Framework.Cloud, vip, fuzz.FeatureValidators(features.All))
	if err != nil {
		t.Fatalf("GCLBForVIP(..., %q, _) = %v, want nil; error getting GCP resources for LB with IP", vip, err)
	}

	if err = e2e.CheckGCLB(gclb, numForwardingRules, numBackendServices); err != nil {
		t.Error(err)
	}
	return gclb
}
<commit_msg>Pass updated ingress for validation<commit_after>/*
Copyright 2019 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 main

import (
	"context"
	"testing"

	"k8s.io/api/networking/v1beta1"
	"k8s.io/apimachinery/pkg/util/intstr"
	"k8s.io/ingress-gce/pkg/e2e"
	"k8s.io/ingress-gce/pkg/fuzz"
	"k8s.io/ingress-gce/pkg/fuzz/features"
	"k8s.io/ingress-gce/pkg/utils"
)

// TestFinalizer asserts that finalizer is added/ removed appropriately during the life cycle
// of an ingress resource.
func TestFinalizer(t *testing.T) {
	t.Parallel()
	ctx := context.Background()
	port80 := intstr.FromInt(80)
	numForwardingRules := 1
	numBackendServices := 2
	svcName := "service-1"
	ing := fuzz.NewIngressBuilder("", "ingress-1", "").
		AddPath("foo.com", "/", svcName, port80).
		SetIngressClass("gce").
		Build()

	Framework.RunWithSandbox("finalizer", t, func(t *testing.T, s *e2e.Sandbox) {
		_, err := e2e.CreateEchoService(s, svcName, nil)
		if err != nil {
			t.Fatalf("CreateEchoService(_, %q, nil): %v, want nil", svcName, err)
		}
		t.Logf("Echo service created (%s/%s)", s.Namespace, svcName)

		crud := e2e.IngressCRUD{C: Framework.Clientset}
		ing.Namespace = s.Namespace
		if _, err := crud.Create(ing); err != nil {
			t.Fatalf("create(%s/%s) = %v, want nil; Ingress: %v", ing.Namespace, ing.Name, err, ing)
		}
		t.Logf("Ingress created (%s/%s)", s.Namespace, ing.Name)
		ing = waitForStableIngress(true, ing, s, t)

		ingFinalizers := ing.GetFinalizers()
		if len(ingFinalizers) != 1 || ingFinalizers[0] != utils.FinalizerKey {
			t.Fatalf("GetFinalizers() = %+v, want [%q]", ingFinalizers, utils.FinalizerKey)
		}

		gclb := checkGCLB(t, s, ing, numForwardingRules, numBackendServices)

		deleteOptions := &fuzz.GCLBDeleteOptions{
			SkipDefaultBackend: true,
		}
		if err := e2e.WaitForIngressDeletion(ctx, gclb, s, ing, deleteOptions); err != nil {
			t.Errorf("e2e.WaitForIngressDeletion(..., %q, nil) = %v, want nil", ing.Name, err)
		}
	})
}

// TestFinalizerIngressClassChange asserts that finalizer is removed from ingress after its
// associated resources are cleaned up when ingress class is updated to a non glbc type.
func TestFinalizerIngressClassChange(t *testing.T) {
	t.Parallel()
	ctx := context.Background()
	port80 := intstr.FromInt(80)
	numForwardingRules := 1
	numBackendServices := 2
	svcName := "service-1"
	ing := fuzz.NewIngressBuilder("", "ingress-1", "").
		AddPath("foo.com", "/", svcName, port80).
		SetIngressClass("gce").
		Build()

	Framework.RunWithSandbox("finalizer-ingress-class-change", t, func(t *testing.T, s *e2e.Sandbox) {
		_, err := e2e.CreateEchoService(s, svcName, nil)
		if err != nil {
			t.Fatalf("CreateEchoService(_, %q, nil): %v, want nil", svcName, err)
		}
		t.Logf("Echo service created (%s/%s)", s.Namespace, svcName)

		crud := e2e.IngressCRUD{C: Framework.Clientset}
		ing.Namespace = s.Namespace
		if _, err := crud.Create(ing); err != nil {
			t.Fatalf("create(%s/%s) = %v, want nil; Ingress: %v", ing.Namespace, ing.Name, err, ing)
		}
		t.Logf("Ingress created (%s/%s)", s.Namespace, ing.Name)
		ing = waitForStableIngress(true, ing, s, t)

		ingFinalizers := ing.GetFinalizers()
		if len(ingFinalizers) != 1 || ingFinalizers[0] != utils.FinalizerKey {
			t.Fatalf("GetFinalizers() = %+v, want [%q]", ingFinalizers, utils.FinalizerKey)
		}

		gclb := checkGCLB(t, s, ing, numForwardingRules, numBackendServices)

		// Change Ingress class
		newIngClass := "nginx"
		ing = fuzz.NewIngressBuilderFromExisting(ing).SetIngressClass(newIngClass).Build()

		if _, err := crud.Update(ing); err != nil {
			t.Fatalf("update(%s/%s) = %v, want nil; ingress: %v", ing.Namespace, ing.Name, err, ing)
		}
		t.Logf("Ingress (%s/%s) class changed to %s", s.Namespace, ing.Name, newIngClass)

		deleteOptions := &fuzz.GCLBDeleteOptions{
			SkipDefaultBackend: true,
		}
		if err := e2e.WaitForFinalizerDeletion(ctx, gclb, s, ing.Name, deleteOptions); err != nil {
			t.Errorf("e2e.WaitForFinalizerDeletion(..., %q, _) = %v, want nil", ing.Name, err)
		}
		t.Logf("Finalizer for Ingress (%s/%s) deleted", s.Namespace, ing.Name)
	})
}

// TestFinalizerIngressesWithSharedResources asserts that sync does not return error with finalizer
// when multiple ingresses with shared resources are created or deleted.
func TestFinalizerIngressesWithSharedResources(t *testing.T) {
	t.Parallel()
	ctx := context.Background()
	port80 := intstr.FromInt(80)
	numForwardingRules := 1
	numBackendServices := 2
	svcName := "service-1"
	ing := fuzz.NewIngressBuilder("", "ingress-1", "").
		AddPath("foo.com", "/", svcName, port80).
		SetIngressClass("gce").
		Build()
	otherIng := fuzz.NewIngressBuilder("", "ingress-2", "").
		AddPath("foo.com", "/", svcName, port80).
		SetIngressClass("gce").
		Build()

	Framework.RunWithSandbox("finalizer-ingress-class-change", t, func(t *testing.T, s *e2e.Sandbox) {
		_, err := e2e.CreateEchoService(s, svcName, nil)
		if err != nil {
			t.Fatalf("CreateEchoService(_, %q, nil): %v, want nil", svcName, err)
		}
		t.Logf("Echo service created (%s/%s)", s.Namespace, svcName)

		crud := e2e.IngressCRUD{C: Framework.Clientset}
		ing.Namespace = s.Namespace
		if _, err := crud.Create(ing); err != nil {
			t.Fatalf("create(%s/%s) = %v, want nil; Ingress: %v", ing.Namespace, ing.Name, err, ing)
		}
		t.Logf("Ingress created (%s/%s)", s.Namespace, ing.Name)

		otherIng.Namespace = s.Namespace
		if _, err := crud.Create(otherIng); err != nil {
			t.Fatalf("create(%s/%s) = %v, want nil; Ingress: %v", otherIng.Namespace, otherIng.Name, err, otherIng)
		}
		t.Logf("Ingress created (%s/%s)", s.Namespace, otherIng.Name)

		ing = waitForStableIngress(true, ing, s, t)
		otherIng = waitForStableIngress(true, otherIng, s, t)

		ingFinalizers := ing.GetFinalizers()
		if len(ingFinalizers) != 1 || ingFinalizers[0] != utils.FinalizerKey {
			t.Fatalf("GetFinalizers() = %+v, want [%q]", ingFinalizers, utils.FinalizerKey)
		}
		otherIngFinalizers := otherIng.GetFinalizers()
		if len(otherIngFinalizers) != 1 || otherIngFinalizers[0] != utils.FinalizerKey {
			t.Fatalf("GetFinalizers() = %+v, want [%q]", otherIngFinalizers, utils.FinalizerKey)
		}

		gclb := checkGCLB(t, s, ing, numForwardingRules, numBackendServices)
		otherGclb := checkGCLB(t, s, otherIng, numForwardingRules, numBackendServices)

		// SkipBackends ensure that we dont wait on deletion of shared backends.
		deleteOptions := &fuzz.GCLBDeleteOptions{
			SkipDefaultBackend: true,
			SkipBackends:       true,
		}
		if err := e2e.WaitForIngressDeletion(ctx, gclb, s, ing, deleteOptions); err != nil {
			t.Errorf("e2e.WaitForIngressDeletion(..., %q, nil) = %v, want nil", ing.Name, err)
		}
		deleteOptions = &fuzz.GCLBDeleteOptions{
			SkipDefaultBackend: true,
		}
		if err := e2e.WaitForIngressDeletion(ctx, otherGclb, s, otherIng, deleteOptions); err != nil {
			t.Errorf("e2e.WaitForIngressDeletion(..., %q, nil) = %v, want nil", otherIng.Name, err)
		}
	})
}

// TestUpdateTo1dot7 asserts that finalizer is added to an ingress when upgraded from a version
// without finalizer to version 1.7. Also, verifies that ingress is deleted with finalizer enabled.
// Note: The test is named in such a way that it does run as a normal test or an upgrade test for
// other versions.
func TestUpdateTo1dot7(t *testing.T) {
	port80 := intstr.FromInt(80)
	numForwardingRules := 1
	numBackendServices := 2
	svcName := "service-1"
	ing := fuzz.NewIngressBuilder("", "ingress-1", "").
		AddPath("foo.com", "/", svcName, port80).
		SetIngressClass("gce").
		Build()
	Framework.RunWithSandbox("finalizer-master-upgrade", t, func(t *testing.T, s *e2e.Sandbox) {
		t.Parallel()

		_, err := e2e.CreateEchoService(s, svcName, nil)
		if err != nil {
			t.Fatalf("CreateEchoService(_, %q, nil): %v, want nil", svcName, err)
		}
		t.Logf("Echo service created (%s/%s)", s.Namespace, svcName)

		crud := e2e.IngressCRUD{C: Framework.Clientset}
		ing.Namespace = s.Namespace
		if _, err := crud.Create(ing); err != nil {
			t.Fatalf("create(%s/%s) = %v, want nil; Ingress: %v", ing.Namespace, ing.Name, err, ing)
		}
		t.Logf("Ingress created (%s/%s)", s.Namespace, ing.Name)
		ing = waitForStableIngress(true, ing, s, t)

		// Check that finalizer is not added in old version in which finalizer add is not enabled.
		ingFinalizers := ing.GetFinalizers()
		if l := len(ingFinalizers); l != 0 {
			t.Fatalf("GetFinalizers() = %d, want 0", l)
		}

		checkGCLB(t, s, ing, numForwardingRules, numBackendServices)

		for {
			// While k8s master is upgrading, it will return a connection refused
			// error for any k8s resource we try to hit. We loop until the
			// master upgrade has finished.
			if s.MasterUpgrading() {
				continue
			}

			if s.MasterUpgraded() {
				t.Logf("Detected master upgrade")
				break
			}
		}

		// Wait for finalizer to be added and verify that correct finalizer is added to the ingress after the upgrade.
		if err := e2e.WaitForFinalizer(s, ing.Name); err != nil {
			t.Errorf("e2e.WaitForFinalizer(_, %q) = %v, want nil", ing.Name, err)
		}

		gclb := checkGCLB(t, s, ing, numForwardingRules, numBackendServices)

		// If the Master has upgraded and the Ingress is stable,
		// we delete the Ingress and exit out of the loop to indicate that
		// the test is done.
		deleteOptions := &fuzz.GCLBDeleteOptions{
			SkipDefaultBackend: true,
		}
		if err := e2e.WaitForIngressDeletion(context.Background(), gclb, s, ing, deleteOptions); err != nil {
			t.Errorf("e2e.WaitForIngressDeletion(..., %q, nil) = %v, want nil", ing.Name, err)
		}
	})
}

func checkGCLB(t *testing.T, s *e2e.Sandbox, ing *v1beta1.Ingress, numForwardingRules, numBackendServices int) *fuzz.GCLB {
	// Perform whitebox testing.
	if len(ing.Status.LoadBalancer.Ingress) < 1 {
		t.Fatalf("Ingress does not have an IP: %+v", ing.Status)
	}
	vip := ing.Status.LoadBalancer.Ingress[0].IP
	t.Logf("Ingress %s/%s VIP = %s", s.Namespace, ing.Name, vip)
	gclb, err := fuzz.GCLBForVIP(context.Background(), Framework.Cloud, vip, fuzz.FeatureValidators(features.All))
	if err != nil {
		t.Fatalf("GCLBForVIP(..., %q, _) = %v, want nil; error getting GCP resources for LB with IP", vip, err)
	}

	if err = e2e.CheckGCLB(gclb, numForwardingRules, numBackendServices); err != nil {
		t.Error(err)
	}
	return gclb
}
<|endoftext|>


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

package main

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"strings"
	"sync"

	"github.com/googleapis/gapic-showcase/server"
	pb "github.com/googleapis/gapic-showcase/server/genproto"
	"github.com/googleapis/gapic-showcase/server/genrest"
	"github.com/googleapis/gapic-showcase/server/services"
	fallback "github.com/googleapis/grpc-fallback-go/server"
	gmux "github.com/gorilla/mux"
	"github.com/soheilhy/cmux"
	"golang.org/x/sync/errgroup"
	locpb "google.golang.org/genproto/googleapis/cloud/location"
	iampb "google.golang.org/genproto/googleapis/iam/v1"
	lropb "google.golang.org/genproto/googleapis/longrunning"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/reflection"
)

// RuntimeConfig has the run-time settings necessary to run the
// Showcase servers.
type RuntimeConfig struct {
	port         string
	fallbackPort string
	tlsCaCert    string
	tlsCert      string
	tlsKey       string
}

// Endpoint defines common operations for any of the various types of
// transport-specific network endpoints Showcase supports
type Endpoint interface {
	// Serve beings the listen-and-serve loop for this
	// Endpoint. It typically blocks until the server is shut
	// down. The error it returns depends on the underlying
	// implementation.
	Serve() error

	// Shutdown causes the currently running Endpoint to
	// terminate. The error it returns depends on the underlying
	// implementation.
	Shutdown() error
}

// CreateAllEndpoints returns an Endpoint that can serve gRPC and
// HTTP/REST connections (on config.port) and gRPC-fallback
// connections (on config.fallbackPort)
func CreateAllEndpoints(config RuntimeConfig) Endpoint {
	// Ensure port is of the right form.
	if !strings.HasPrefix(config.port, ":") {
		config.port = ":" + config.port
	}

	// Start listening.
	lis, err := net.Listen("tcp", config.port)
	if err != nil {
		log.Fatalf("Showcase failed to listen on port '%s': %v", config.port, err)
	}
	stdLog.Printf("Showcase listening on port: %s", config.port)

	m := cmux.New(lis)
	grpcListener := m.MatchWithWriters(cmux.HTTP2MatchHeaderFieldSendSettings("content-type", "application/grpc"))
	httpListener := m.Match(cmux.HTTP1Fast())

	backend := createBackends()
	gRPCServer := newEndpointGRPC(grpcListener, config, backend)
	restServer := newEndpointREST(httpListener, backend)
	cmuxServer := newEndpointMux(m, gRPCServer, restServer)
	return cmuxServer
}

// endpointMux is an Endpoint for cmux, the connection multiplexer
// allowing different types of connections on the same port.
//
// We choose not to use grpc.Server.ServeHTTP because it is
// experimental and does not support some gRPC features available
// through grpc.Server.Serve. (cf
// https://godoc.org/google.golang.org/grpc#Server.ServeHTTP)
type endpointMux struct {
	endpoints []Endpoint
	cmux      cmux.CMux
	mux       sync.Mutex
}

func newEndpointMux(cmuxEndpoint cmux.CMux, endpoints ...Endpoint) Endpoint {
	return &endpointMux{
		endpoints: endpoints,
		cmux:      cmuxEndpoint,
	}
}

func (em *endpointMux) String() string {
	return "endpoint multiplexer"
}

func (em *endpointMux) Serve() error {
	g := new(errgroup.Group)
	for idx, endpt := range em.endpoints {
		if endpt != nil {
			stdLog.Printf("Starting endpoint %d: %s", idx, endpt)
			endpoint := endpt
			g.Go(func() error {
				err := endpoint.Serve()
				err2 := em.Shutdown()
				if err != nil {
					return err
				}
				return err2
			})
		}
	}
	if em.cmux != nil {
		stdLog.Printf("Starting %s", em)

		g.Go(func() error {
			err := em.cmux.Serve()
			err2 := em.Shutdown()
			if err != nil {
				return err
			}
			return err2

		})
	}
	return g.Wait()
}

func (em *endpointMux) Shutdown() error {
	em.mux.Lock()
	defer em.mux.Unlock()

	var err error
	if em.cmux != nil {
		// TODO: Wait for https://github.com/soheilhy/cmux/pull/69 (due to
		// https://github.com/soheilhy/cmux/pull/69#issuecomment-712928041.)
		//
		// err = em.mux.Close()
		em.cmux = nil
	}

	for idx, endpt := range em.endpoints {
		if endpt != nil {
			// TODO: Wait for https://github.com/soheilhy/cmux/pull/69
			// newErr := endpt.Shutdown()
			// if err==nil {
			// 	err = newErr
			// }
			em.endpoints[idx] = nil
		}
	}
	return err
}

// endpointGRPC is an Endpoint for gRPC connections to the Showcase
// server.
type endpointGRPC struct {
	server         *grpc.Server
	fallbackServer *fallback.FallbackServer
	listener       net.Listener
	mux            sync.Mutex
}

// createBackends creates services used by both the gRPC and REST servers.
func createBackends() *services.Backend {
	logger := &loggerObserver{}
	observerRegistry := server.ShowcaseObserverRegistry()
	observerRegistry.RegisterUnaryObserver(logger)
	observerRegistry.RegisterStreamRequestObserver(logger)
	observerRegistry.RegisterStreamResponseObserver(logger)

	identityServer := services.NewIdentityServer()
	messagingServer := services.NewMessagingServer(identityServer)
	return &services.Backend{
		EchoServer:            services.NewEchoServer(),
		SequenceServiceServer: services.NewSequenceServer(),
		IdentityServer:        identityServer,
		MessagingServer:       messagingServer,
		ComplianceServer:      services.NewComplianceServer(),
		TestingServer:         services.NewTestingServer(observerRegistry),
		OperationsServer:      services.NewOperationsServer(messagingServer),
		LocationsServer:       services.NewLocationsServer(),
		IAMPolicyServer:       services.NewIAMPolicyServer(),
		StdLog:                stdLog,
		ErrLog:                errLog,
		ObserverRegistry:      observerRegistry,
	}
}

func newEndpointGRPC(lis net.Listener, config RuntimeConfig, backend *services.Backend) Endpoint {
	opts := []grpc.ServerOption{
		grpc.StreamInterceptor(backend.ObserverRegistry.StreamInterceptor),
		grpc.UnaryInterceptor(backend.ObserverRegistry.UnaryInterceptor),
	}

	// load mutual TLS cert/key and root CA cert
	if config.tlsCaCert != "" && config.tlsCert != "" && config.tlsKey != "" {
		keyPair, err := tls.LoadX509KeyPair(config.tlsCert, config.tlsKey)
		if err != nil {
			log.Fatalf("Failed to load server TLS cert/key with error:%v", err)
		}

		cert, err := ioutil.ReadFile(config.tlsCaCert)
		if err != nil {
			log.Fatalf("Failed to load root CA cert file with error:%v", err)
		}

		pool := x509.NewCertPool()
		pool.AppendCertsFromPEM(cert)

		ta := credentials.NewTLS(&tls.Config{
			Certificates: []tls.Certificate{keyPair},
			ClientCAs:    pool,
			ClientAuth:   tls.RequireAndVerifyClientCert,
		})

		opts = append(opts, grpc.Creds(ta))
	}
	s := grpc.NewServer(opts...)

	// Register Services to the server.
	pb.RegisterEchoServer(s, backend.EchoServer)
	pb.RegisterSequenceServiceServer(s, backend.SequenceServiceServer)
	pb.RegisterIdentityServer(s, backend.IdentityServer)
	pb.RegisterMessagingServer(s, backend.MessagingServer)
	pb.RegisterComplianceServer(s, backend.ComplianceServer)
	pb.RegisterTestingServer(s, backend.TestingServer)
	lropb.RegisterOperationsServer(s, backend.OperationsServer)
	locpb.RegisterLocationsServer(s, backend.LocationsServer)
	iampb.RegisterIAMPolicyServer(s, backend.IAMPolicyServer)

	fb := fallback.NewServer(config.fallbackPort, "localhost"+config.port)

	// Register reflection service on gRPC server.
	reflection.Register(s)

	return &endpointGRPC{
		server:         s,
		fallbackServer: fb,
		listener:       lis,
	}
}

func (eg *endpointGRPC) String() string {
	return "gRPC endpoint"
}

func (eg *endpointGRPC) Serve() error {
	defer eg.Shutdown()
	if eg.fallbackServer != nil {
		stdLog.Printf("Listening for gRPC-fallback connections")
		eg.fallbackServer.StartBackground()
	}
	if eg.server != nil {
		stdLog.Printf("Listening for gRPC connections")
		return eg.server.Serve(eg.listener)
	}
	return fmt.Errorf("gRPC server not set up")
}

func (eg *endpointGRPC) Shutdown() error {
	eg.mux.Lock()
	defer eg.mux.Unlock()

	if eg.fallbackServer != nil {
		stdLog.Printf("Stopping gRPC-fallback connections")
		eg.fallbackServer.Shutdown()
		eg.fallbackServer = nil
	}

	if eg.server != nil {
		stdLog.Printf("Stopping gRPC connections")
		eg.server.GracefulStop()
		eg.server = nil
	}
	stdLog.Printf("Stopped gRPC")
	return nil
}

// endpointREST is an Endpoint for HTTP/REST connections to the Showcase
// server.
type endpointREST struct {
	server   *http.Server
	listener net.Listener
	mux      sync.Mutex
}

func newEndpointREST(lis net.Listener, backend *services.Backend) *endpointREST {
	router := gmux.NewRouter()
	router.HandleFunc("/hello", func(w http.ResponseWriter, _ *http.Request) {
		w.Write([]byte("GAPIC Showcase: HTTP/REST endpoint using gorilla/mux\n"))
	})
	genrest.RegisterHandlers(router, backend)
	return &endpointREST{
		server:   &http.Server{Handler: router},
		listener: lis,
	}
}

func (er *endpointREST) String() string {
	return "HTTP/REST endpoint"
}

func (er *endpointREST) Serve() error {
	defer er.Shutdown()
	if er.server != nil {
		stdLog.Printf("Listening for REST connections")
		return er.server.Serve(er.listener)
	}
	return fmt.Errorf("REST server not set up")
}

func (er *endpointREST) Shutdown() error {
	er.mux.Lock()
	defer er.mux.Unlock()
	var err error
	if er.server != nil {
		stdLog.Printf("Stopping REST connections")
		err = er.server.Shutdown(context.Background())
		er.server = nil
	}
	stdLog.Printf("Stopped REST")
	return err
}
<commit_msg>fix: Allow mTLS to work over gRPC using cmux. (#755)<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"strings"
	"sync"

	"github.com/googleapis/gapic-showcase/server"
	pb "github.com/googleapis/gapic-showcase/server/genproto"
	"github.com/googleapis/gapic-showcase/server/genrest"
	"github.com/googleapis/gapic-showcase/server/services"
	fallback "github.com/googleapis/grpc-fallback-go/server"
	gmux "github.com/gorilla/mux"
	"github.com/soheilhy/cmux"
	"golang.org/x/sync/errgroup"
	locpb "google.golang.org/genproto/googleapis/cloud/location"
	iampb "google.golang.org/genproto/googleapis/iam/v1"
	lropb "google.golang.org/genproto/googleapis/longrunning"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/reflection"
)

// RuntimeConfig has the run-time settings necessary to run the
// Showcase servers.
type RuntimeConfig struct {
	port         string
	fallbackPort string
	tlsCaCert    string
	tlsCert      string
	tlsKey       string
}

// Endpoint defines common operations for any of the various types of
// transport-specific network endpoints Showcase supports
type Endpoint interface {
	// Serve beings the listen-and-serve loop for this
	// Endpoint. It typically blocks until the server is shut
	// down. The error it returns depends on the underlying
	// implementation.
	Serve() error

	// Shutdown causes the currently running Endpoint to
	// terminate. The error it returns depends on the underlying
	// implementation.
	Shutdown() error
}

// CreateAllEndpoints returns an Endpoint that can serve gRPC and
// HTTP/REST connections (on config.port) and gRPC-fallback
// connections (on config.fallbackPort)
func CreateAllEndpoints(config RuntimeConfig) Endpoint {
	// Ensure port is of the right form.
	if !strings.HasPrefix(config.port, ":") {
		config.port = ":" + config.port
	}

	// Start listening.
	lis, err := net.Listen("tcp", config.port)
	if err != nil {
		log.Fatalf("Showcase failed to listen on port '%s': %v", config.port, err)
	}
	stdLog.Printf("Showcase listening on port: %s", config.port)

	m := cmux.New(lis)
	httpListener := m.Match(cmux.HTTP1Fast())
	// cmux.Any() is needed below to get mTLS to work for
	// gRPC, and that in turn means the order of the matchers matters. See
	// https://github.com/open-telemetry/opentelemetry-collector/issues/2732
	grpcListener := m.Match(cmux.Any())

	backend := createBackends()
	gRPCServer := newEndpointGRPC(grpcListener, config, backend)
	restServer := newEndpointREST(httpListener, backend)
	cmuxServer := newEndpointMux(m, gRPCServer, restServer)
	return cmuxServer
}

// endpointMux is an Endpoint for cmux, the connection multiplexer
// allowing different types of connections on the same port.
//
// We choose not to use grpc.Server.ServeHTTP because it is
// experimental and does not support some gRPC features available
// through grpc.Server.Serve. (cf
// https://godoc.org/google.golang.org/grpc#Server.ServeHTTP)
type endpointMux struct {
	endpoints []Endpoint
	cmux      cmux.CMux
	mux       sync.Mutex
}

func newEndpointMux(cmuxEndpoint cmux.CMux, endpoints ...Endpoint) Endpoint {
	return &endpointMux{
		endpoints: endpoints,
		cmux:      cmuxEndpoint,
	}
}

func (em *endpointMux) String() string {
	return "endpoint multiplexer"
}

func (em *endpointMux) Serve() error {
	g := new(errgroup.Group)
	for idx, endpt := range em.endpoints {
		if endpt != nil {
			stdLog.Printf("Starting endpoint %d: %s", idx, endpt)
			endpoint := endpt
			g.Go(func() error {
				err := endpoint.Serve()
				err2 := em.Shutdown()
				if err != nil {
					return err
				}
				return err2
			})
		}
	}
	if em.cmux != nil {
		stdLog.Printf("Starting %s", em)

		g.Go(func() error {
			err := em.cmux.Serve()
			err2 := em.Shutdown()
			if err != nil {
				return err
			}
			return err2

		})
	}
	return g.Wait()
}

func (em *endpointMux) Shutdown() error {
	em.mux.Lock()
	defer em.mux.Unlock()

	var err error
	if em.cmux != nil {
		// TODO: Wait for https://github.com/soheilhy/cmux/pull/69 (due to
		// https://github.com/soheilhy/cmux/pull/69#issuecomment-712928041.)
		//
		// err = em.mux.Close()
		em.cmux = nil
	}

	for idx, endpt := range em.endpoints {
		if endpt != nil {
			// TODO: Wait for https://github.com/soheilhy/cmux/pull/69
			// newErr := endpt.Shutdown()
			// if err==nil {
			// 	err = newErr
			// }
			em.endpoints[idx] = nil
		}
	}
	return err
}

// endpointGRPC is an Endpoint for gRPC connections to the Showcase
// server.
type endpointGRPC struct {
	server         *grpc.Server
	fallbackServer *fallback.FallbackServer
	listener       net.Listener
	mux            sync.Mutex
}

// createBackends creates services used by both the gRPC and REST servers.
func createBackends() *services.Backend {
	logger := &loggerObserver{}
	observerRegistry := server.ShowcaseObserverRegistry()
	observerRegistry.RegisterUnaryObserver(logger)
	observerRegistry.RegisterStreamRequestObserver(logger)
	observerRegistry.RegisterStreamResponseObserver(logger)

	identityServer := services.NewIdentityServer()
	messagingServer := services.NewMessagingServer(identityServer)
	return &services.Backend{
		EchoServer:            services.NewEchoServer(),
		SequenceServiceServer: services.NewSequenceServer(),
		IdentityServer:        identityServer,
		MessagingServer:       messagingServer,
		ComplianceServer:      services.NewComplianceServer(),
		TestingServer:         services.NewTestingServer(observerRegistry),
		OperationsServer:      services.NewOperationsServer(messagingServer),
		LocationsServer:       services.NewLocationsServer(),
		IAMPolicyServer:       services.NewIAMPolicyServer(),
		StdLog:                stdLog,
		ErrLog:                errLog,
		ObserverRegistry:      observerRegistry,
	}
}

func newEndpointGRPC(lis net.Listener, config RuntimeConfig, backend *services.Backend) Endpoint {
	opts := []grpc.ServerOption{
		grpc.StreamInterceptor(backend.ObserverRegistry.StreamInterceptor),
		grpc.UnaryInterceptor(backend.ObserverRegistry.UnaryInterceptor),
	}

	// load mutual TLS cert/key and root CA cert
	if config.tlsCaCert != "" && config.tlsCert != "" && config.tlsKey != "" {
		keyPair, err := tls.LoadX509KeyPair(config.tlsCert, config.tlsKey)
		if err != nil {
			log.Fatalf("Failed to load server TLS cert/key with error:%v", err)
		}

		cert, err := ioutil.ReadFile(config.tlsCaCert)
		if err != nil {
			log.Fatalf("Failed to load root CA cert file with error:%v", err)
		}

		pool := x509.NewCertPool()
		pool.AppendCertsFromPEM(cert)

		ta := credentials.NewTLS(&tls.Config{
			Certificates: []tls.Certificate{keyPair},
			ClientCAs:    pool,
			ClientAuth:   tls.RequireAndVerifyClientCert,
		})

		opts = append(opts, grpc.Creds(ta))
	}
	s := grpc.NewServer(opts...)

	// Register Services to the server.
	pb.RegisterEchoServer(s, backend.EchoServer)
	pb.RegisterSequenceServiceServer(s, backend.SequenceServiceServer)
	pb.RegisterIdentityServer(s, backend.IdentityServer)
	pb.RegisterMessagingServer(s, backend.MessagingServer)
	pb.RegisterComplianceServer(s, backend.ComplianceServer)
	pb.RegisterTestingServer(s, backend.TestingServer)
	lropb.RegisterOperationsServer(s, backend.OperationsServer)
	locpb.RegisterLocationsServer(s, backend.LocationsServer)
	iampb.RegisterIAMPolicyServer(s, backend.IAMPolicyServer)

	fb := fallback.NewServer(config.fallbackPort, "localhost"+config.port)

	// Register reflection service on gRPC server.
	reflection.Register(s)

	return &endpointGRPC{
		server:         s,
		fallbackServer: fb,
		listener:       lis,
	}
}

func (eg *endpointGRPC) String() string {
	return "gRPC endpoint"
}

func (eg *endpointGRPC) Serve() error {
	defer eg.Shutdown()
	if eg.fallbackServer != nil {
		stdLog.Printf("Listening for gRPC-fallback connections")
		eg.fallbackServer.StartBackground()
	}
	if eg.server != nil {
		stdLog.Printf("Listening for gRPC connections")
		return eg.server.Serve(eg.listener)
	}
	return fmt.Errorf("gRPC server not set up")
}

func (eg *endpointGRPC) Shutdown() error {
	eg.mux.Lock()
	defer eg.mux.Unlock()

	if eg.fallbackServer != nil {
		stdLog.Printf("Stopping gRPC-fallback connections")
		eg.fallbackServer.Shutdown()
		eg.fallbackServer = nil
	}

	if eg.server != nil {
		stdLog.Printf("Stopping gRPC connections")
		eg.server.GracefulStop()
		eg.server = nil
	}
	stdLog.Printf("Stopped gRPC")
	return nil
}

// endpointREST is an Endpoint for HTTP/REST connections to the Showcase
// server.
type endpointREST struct {
	server   *http.Server
	listener net.Listener
	mux      sync.Mutex
}

func newEndpointREST(lis net.Listener, backend *services.Backend) *endpointREST {
	router := gmux.NewRouter()
	router.HandleFunc("/hello", func(w http.ResponseWriter, _ *http.Request) {
		w.Write([]byte("GAPIC Showcase: HTTP/REST endpoint using gorilla/mux\n"))
	})
	genrest.RegisterHandlers(router, backend)
	return &endpointREST{
		server:   &http.Server{Handler: router},
		listener: lis,
	}
}

func (er *endpointREST) String() string {
	return "HTTP/REST endpoint"
}

func (er *endpointREST) Serve() error {
	defer er.Shutdown()
	if er.server != nil {
		stdLog.Printf("Listening for REST connections")
		return er.server.Serve(er.listener)
	}
	return fmt.Errorf("REST server not set up")
}

func (er *endpointREST) Shutdown() error {
	er.mux.Lock()
	defer er.mux.Unlock()
	var err error
	if er.server != nil {
		stdLog.Printf("Stopping REST connections")
		err = er.server.Shutdown(context.Background())
		er.server = nil
	}
	stdLog.Printf("Stopped REST")
	return err
}
<|endoftext|>


<commit_before>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package storage

import (
	"github.com/juju/cmd"
	"github.com/juju/errors"
	"github.com/juju/utils/keyvalues"
	"launchpad.net/gnuflag"
)

const PoolCreateCommandDoc = `
Create or define a storage pool.

A pool describes the underlying characteristics of the storage,
such as the credentials, location of storage and performance or
durability characteristics of that storage
(tagged disks/volume groups in MAAS,
EBS with ssd/magnetic/iops characteristics).

For many providers, there will be a shared resource
where storage can be requested (e.g. EBS in amazon).
Creating pools there maps provider specific settings
into named resources that can be used during deployment.

Pools defined at the environment level are easily reused across services.

options:
    -e, --environment (= "")
        juju environment to operate in
    -o, --output (= "")
        specify an output file
    <name>
        pool name
    <provider type>
        pool provider type
    [<key>=<value>]+
        pool configuration attributes
`

// PoolCreateCommand lists storage pools.
type PoolCreateCommand struct {
	PoolCommandBase
	poolName string
	// TODO(anastasiamac 2015-01-29) type will need to become optional
	// if type is unspecified, use the environment's default provider type
	provider string
	attrs    map[string]interface{}
}

// Init implements Command.Init.
func (c *PoolCreateCommand) Init(args []string) (err error) {
	if len(args) < 3 {
		return errors.New("pool creation requires names, provider type and attrs for configuration")
	}

	c.poolName = args[0]
	c.provider = args[1]

	options, err := keyvalues.Parse(args[2:], true)
	if err != nil {
		return err
	}

	c.attrs = make(map[string]interface{})
	for key, value := range options {
		c.attrs[key] = value
	}
	return nil
}

// Info implements Command.Info.
func (c *PoolCreateCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "create",
		Purpose: "create storage pool",
		Doc:     PoolCreateCommandDoc,
	}
}

// SetFlags implements Command.SetFlags.
func (c *PoolCreateCommand) SetFlags(f *gnuflag.FlagSet) {
	c.StorageCommandBase.SetFlags(f)
}

// Run implements Command.Run.
func (c *PoolCreateCommand) Run(ctx *cmd.Context) (err error) {
	api, err := getPoolCreateAPI(c)
	if err != nil {
		return err
	}
	defer api.Close()

	return api.CreatePool(c.poolName, c.provider, c.attrs)
}

var (
	getPoolCreateAPI = (*PoolCreateCommand).getPoolCreateAPI
)

// PoolCreateAPI defines the API methods that pool create command uses.
type PoolCreateAPI interface {
	Close() error
	CreatePool(pname, ptype string, pconfig map[string]interface{}) error
}

func (c *PoolCreateCommand) getPoolCreateAPI() (PoolCreateAPI, error) {
	return c.NewStorageAPI()
}
<commit_msg>Doc rephrasing.<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package storage

import (
	"github.com/juju/cmd"
	"github.com/juju/errors"
	"github.com/juju/utils/keyvalues"
	"launchpad.net/gnuflag"
)

const PoolCreateCommandDoc = `
Create or define a storage pool.

Pools are a mechanism for administrators to define sources of storage that
they will use to satisfy service storage requirements.

A single pool might be used for storage from units of many different services -
it is a resource from which different stores may be drawn.

A pool describes the underlying characteristics of the storage,
such as the credentials, location of storage and performance or
durability characteristics of that storage
(tagged disks/volume groups in MAAS,
EBS with ssd/magnetic/iops characteristics).

For many providers, there will be a shared resource
where storage can be requested (e.g. EBS in amazon).
Creating pools there maps provider specific settings
into named resources that can be used during deployment.

Pools defined at the environment level are easily reused across services.

options:
    -e, --environment (= "")
        juju environment to operate in
    -o, --output (= "")
        specify an output file
    <name>
        pool name
    <provider type>
        pool provider type
    [<key>=<value>]+
        pool configuration attributes, for e.g. tags, size, path, etc...
`

// PoolCreateCommand lists storage pools.
type PoolCreateCommand struct {
	PoolCommandBase
	poolName string
	// TODO(anastasiamac 2015-01-29) type will need to become optional
	// if type is unspecified, use the environment's default provider type
	provider string
	attrs    map[string]interface{}
}

// Init implements Command.Init.
func (c *PoolCreateCommand) Init(args []string) (err error) {
	if len(args) < 3 {
		return errors.New("pool creation requires names, provider type and attrs for configuration")
	}

	c.poolName = args[0]
	c.provider = args[1]

	options, err := keyvalues.Parse(args[2:], true)
	if err != nil {
		return err
	}

	c.attrs = make(map[string]interface{})
	for key, value := range options {
		c.attrs[key] = value
	}
	return nil
}

// Info implements Command.Info.
func (c *PoolCreateCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "create",
		Purpose: "create storage pool",
		Doc:     PoolCreateCommandDoc,
	}
}

// SetFlags implements Command.SetFlags.
func (c *PoolCreateCommand) SetFlags(f *gnuflag.FlagSet) {
	c.StorageCommandBase.SetFlags(f)
}

// Run implements Command.Run.
func (c *PoolCreateCommand) Run(ctx *cmd.Context) (err error) {
	api, err := getPoolCreateAPI(c)
	if err != nil {
		return err
	}
	defer api.Close()

	return api.CreatePool(c.poolName, c.provider, c.attrs)
}

var (
	getPoolCreateAPI = (*PoolCreateCommand).getPoolCreateAPI
)

// PoolCreateAPI defines the API methods that pool create command uses.
type PoolCreateAPI interface {
	Close() error
	CreatePool(pname, ptype string, pconfig map[string]interface{}) error
}

func (c *PoolCreateCommand) getPoolCreateAPI() (PoolCreateAPI, error) {
	return c.NewStorageAPI()
}
<|endoftext|>


<commit_before>package gateway

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"runtime"
	"strings"
	"sync"
	"syscall"
	"time"

	_ "expvar"

	"github.com/funkygao/gafka"
	"github.com/funkygao/gafka/cmd/kateway/manager"
	mandummy "github.com/funkygao/gafka/cmd/kateway/manager/dummy"
	mandb "github.com/funkygao/gafka/cmd/kateway/manager/mysql"
	"github.com/funkygao/gafka/cmd/kateway/meta"
	"github.com/funkygao/gafka/cmd/kateway/meta/zkmeta"
	"github.com/funkygao/gafka/cmd/kateway/store"
	storedummy "github.com/funkygao/gafka/cmd/kateway/store/dummy"
	storekfk "github.com/funkygao/gafka/cmd/kateway/store/kafka"
	"github.com/funkygao/gafka/ctx"
	"github.com/funkygao/gafka/registry"
	"github.com/funkygao/gafka/registry/zk"
	"github.com/funkygao/gafka/telementry"
	"github.com/funkygao/gafka/telementry/influxdb"
	gzk "github.com/funkygao/gafka/zk"
	"github.com/funkygao/go-metrics"
	"github.com/funkygao/golib/signal"
	"github.com/funkygao/golib/timewheel"
	log "github.com/funkygao/log4go"
	zklib "github.com/samuel/go-zookeeper/zk"
)

// Gateway is a distributed Pub/Sub HTTP endpoint.
//
// Working with ehaproxy, it can form a Pub/Sub cluster system.
type Gateway struct {
	id string // must be unique across the zone

	zkzone       *gzk.ZkZone // load/resume/flush counter metrics to zk
	svrMetrics   *serverMetrics
	accessLogger *AccessLogger
	guard        *guard
	timer        *timewheel.TimeWheel

	shutdownOnce sync.Once
	shutdownCh   chan struct{}
	wg           sync.WaitGroup

	certFile string
	keyFile  string

	pubServer *pubServer
	subServer *subServer
	manServer *manServer
	debugMux  *http.ServeMux

	clientStates *ClientStates
}

func New(id string) *Gateway {
	this := &Gateway{
		id:           id,
		shutdownCh:   make(chan struct{}),
		certFile:     Options.CertFile,
		keyFile:      Options.KeyFile,
		clientStates: NewClientStates(),
	}

	this.zkzone = gzk.NewZkZone(gzk.DefaultConfig(Options.Zone, ctx.ZoneZkAddrs(Options.Zone)))
	if err := this.zkzone.Ping(); err != nil {
		panic(err)
	}

	if Options.EnableRegistry {
		registry.Default = zk.New(this.zkzone, this.id, this.InstanceInfo())
	}
	metaConf := zkmeta.DefaultConfig()
	metaConf.Refresh = Options.MetaRefresh
	meta.Default = zkmeta.New(metaConf, this.zkzone)
	this.guard = newGuard(this)
	this.timer = timewheel.NewTimeWheel(time.Second, 120)
	this.accessLogger = NewAccessLogger("access_log", 100)
	this.svrMetrics = NewServerMetrics(Options.ReporterInterval, this)
	rc, err := influxdb.NewConfig(Options.InfluxServer, Options.InfluxDbName, "", "", Options.ReporterInterval)
	if err != nil {
		log.Error("telementry: %v", err)
	} else {
		telementry.Default = influxdb.New(metrics.DefaultRegistry, rc)
	}

	// initialize the manager store
	switch Options.ManagerStore {
	case "mysql":
		cf := mandb.DefaultConfig(Options.Zone)
		cf.Refresh = Options.ManagerRefresh
		manager.Default = mandb.New(cf)
		manager.Default.AllowSubWithUnregisteredGroup(Options.PermitUnregisteredGroup)

	case "dummy":
		manager.Default = mandummy.New(Options.DummyCluster)

	default:
		panic("invalid manager store:" + Options.ManagerStore)
	}

	// initialize the servers on demand
	if Options.DebugHttpAddr != "" {
		this.debugMux = http.NewServeMux()
	}
	if Options.ManHttpAddr != "" || Options.ManHttpsAddr != "" {
		this.manServer = newManServer(Options.ManHttpAddr, Options.ManHttpsAddr,
			Options.MaxClients, this)
	}
	if Options.PubHttpAddr != "" || Options.PubHttpsAddr != "" {
		this.pubServer = newPubServer(Options.PubHttpAddr, Options.PubHttpsAddr,
			Options.MaxClients, this)

		switch Options.Store {
		case "kafka":
			store.DefaultPubStore = storekfk.NewPubStore(Options.PubPoolCapcity, Options.PubPoolIdleTimeout,
				Options.UseCompress, &this.wg, Options.Debug, Options.DryRun)

		case "dummy":
			store.DefaultPubStore = storedummy.NewPubStore(&this.wg, Options.Debug)

		default:
			panic("invalid store")
		}
	}
	if Options.SubHttpAddr != "" || Options.SubHttpsAddr != "" {
		this.subServer = newSubServer(Options.SubHttpAddr, Options.SubHttpsAddr,
			Options.MaxClients, this)

		switch Options.Store {
		case "kafka":
			store.DefaultSubStore = storekfk.NewSubStore(&this.wg,
				this.subServer.closedConnCh, Options.Debug)

		case "dummy":
			store.DefaultSubStore = storedummy.NewSubStore(&this.wg,
				this.subServer.closedConnCh, Options.Debug)

		default:
			panic("invalid store")

		}
	}

	return this
}

func (this *Gateway) InstanceInfo() []byte {
	ip, err := ctx.LocalIP()
	if err != nil {
		panic(err)
	}
	info := gzk.KatewayMeta{
		Id:        this.id,
		Zone:      Options.Zone,
		Ver:       gafka.Version,
		Build:     gafka.BuildId,
		BuiltAt:   gafka.BuiltAt,
		Host:      ctx.Hostname(),
		Ip:        ip.String(),
		Cpu:       ctx.NumCPUStr(),
		Arch:      fmt.Sprintf("%s:%s-%s/%s", runtime.Compiler, runtime.Version(), runtime.GOOS, runtime.GOARCH),
		PubAddr:   Options.PubHttpAddr,
		SPubAddr:  Options.PubHttpsAddr,
		SubAddr:   Options.SubHttpAddr,
		SSubAddr:  Options.SubHttpsAddr,
		ManAddr:   Options.ManHttpAddr,
		SManAddr:  Options.ManHttpsAddr,
		DebugAddr: Options.DebugHttpAddr,
	}
	d, _ := json.Marshal(info)
	return d
}

func (this *Gateway) Start() (err error) {
	log.Info("starting gateway[%s@%s]...", gafka.BuildId, gafka.BuiltAt)

	signal.RegisterSignalsHandler(func(sig os.Signal) {
		log.Info("gateway[%s@%s] received signal: %s", gafka.BuildId, gafka.BuiltAt, strings.ToUpper(sig.String()))
		this.stop()
	}, syscall.SIGINT, syscall.SIGTERM) // yes we ignore HUP

	// keep watch on zk connection jitter
	go func() {
		evtCh, ok := this.zkzone.SessionEvents()
		if !ok {
			log.Error("someone else is consuming my zk events?")
			return
		}

		// during connecting phase, the following events are fired:
		// StateConnecting -> StateConnected -> StateHasSession
		firstHandShaked := false
		for {
			select {
			case <-this.shutdownCh:
				return

			case evt, ok := <-evtCh:
				if !ok {
					return
				}

				if !firstHandShaked {
					if evt.State == zklib.StateHasSession {
						firstHandShaked = true
					}

					continue
				}

				log.Warn("zk jitter: %+v", evt)

				if evt.State == zklib.StateHasSession {
					log.Warn("zk reconnected after session lost")
				}
			}
		}
	}()

	meta.Default.Start()
	log.Trace("meta store[%s] started", meta.Default.Name())

	if err = manager.Default.Start(); err != nil {
		return
	}
	log.Trace("manager store[%s] started", manager.Default.Name())

	go this.guard.Start()
	log.Trace("guard started")

	if telementry.Default != nil {
		go func() {
			log.Trace("telementry[%s] started", telementry.Default.Name())

			if err = telementry.Default.Start(); err != nil {
				log.Error("telementry[%s]: %v", telementry.Default.Name(), err)
			}
		}()
	}

	if Options.EnableAccessLog {
		if err = this.accessLogger.Start(); err != nil {
			log.Error("access logger: %s", err)
		}
	}

	this.buildRouting()

	this.svrMetrics.Load()
	go startRuntimeMetrics(Options.ReporterInterval)

	// start up the servers
	if this.manServer != nil {
		this.manServer.Start()
	}
	if this.pubServer != nil {
		if err := store.DefaultPubStore.Start(); err != nil {
			panic(err)
		}
		log.Trace("pub store[%s] started", store.DefaultPubStore.Name())

		this.pubServer.Start()
	}
	if this.subServer != nil {
		if err := store.DefaultSubStore.Start(); err != nil {
			panic(err)
		}
		log.Trace("sub store[%s] started", store.DefaultSubStore.Name())

		this.subServer.Start()
	}

	// the last thing is to register: notify others: come on baby!
	if registry.Default != nil {
		if err := registry.Default.Register(); err != nil {
			panic(err)
		}

		log.Info("gateway[%s:%s] ready, registered in %s", ctx.Hostname(), this.id,
			registry.Default.Name())
	} else {
		log.Info("gateway[%s:%s] ready, unregistered", ctx.Hostname(), this.id)
	}

	return nil
}

func (this *Gateway) stop() {
	this.shutdownOnce.Do(func() {
		log.Info("stopping gateway...")

		close(this.shutdownCh)
	})
}

func (this *Gateway) ServeForever() {
	select {
	case <-this.shutdownCh:
		// the 1st thing is to deregister
		if registry.Default != nil {
			if err := registry.Default.Deregister(); err != nil {
				log.Error("Deregister: %v", err)
			}

			log.Info("deregistered from %s", registry.Default.Name())
		}

		log.Info("waiting for servers shutdown...")
		this.wg.Wait()
		log.Info("<----- all servers shutdown ----->")

		this.accessLogger.Stop()

		log.Trace("stopping pub store")
		if store.DefaultPubStore != nil {
			store.DefaultPubStore.Stop()
		}
		log.Trace("stopping sub store")
		if store.DefaultSubStore != nil {
			store.DefaultSubStore.Stop()
		}

		this.svrMetrics.Flush()
		log.Trace("svr metrics flushed")

		if telementry.Default != nil {
			telementry.Default.Stop()
			log.Trace("telementry stopped")
		}

		meta.Default.Stop()
		log.Trace("meta store stopped")
		manager.Default.Stop()
		log.Trace("manager store stopped")

		if this.zkzone != nil {
			this.zkzone.Close()
			log.Trace("zkzone stopped")
		}

		this.timer.Stop()
	}

}
<commit_msg>store can only be closed after web server closed<commit_after>package gateway

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"runtime"
	"strings"
	"sync"
	"syscall"
	"time"

	_ "expvar"

	"github.com/funkygao/gafka"
	"github.com/funkygao/gafka/cmd/kateway/manager"
	mandummy "github.com/funkygao/gafka/cmd/kateway/manager/dummy"
	mandb "github.com/funkygao/gafka/cmd/kateway/manager/mysql"
	"github.com/funkygao/gafka/cmd/kateway/meta"
	"github.com/funkygao/gafka/cmd/kateway/meta/zkmeta"
	"github.com/funkygao/gafka/cmd/kateway/store"
	storedummy "github.com/funkygao/gafka/cmd/kateway/store/dummy"
	storekfk "github.com/funkygao/gafka/cmd/kateway/store/kafka"
	"github.com/funkygao/gafka/ctx"
	"github.com/funkygao/gafka/registry"
	"github.com/funkygao/gafka/registry/zk"
	"github.com/funkygao/gafka/telementry"
	"github.com/funkygao/gafka/telementry/influxdb"
	gzk "github.com/funkygao/gafka/zk"
	"github.com/funkygao/go-metrics"
	"github.com/funkygao/golib/signal"
	"github.com/funkygao/golib/timewheel"
	log "github.com/funkygao/log4go"
	zklib "github.com/samuel/go-zookeeper/zk"
)

// Gateway is a distributed Pub/Sub HTTP endpoint.
//
// Working with ehaproxy, it can form a Pub/Sub cluster system.
type Gateway struct {
	id string // must be unique across the zone

	zkzone       *gzk.ZkZone // load/resume/flush counter metrics to zk
	svrMetrics   *serverMetrics
	accessLogger *AccessLogger
	guard        *guard
	timer        *timewheel.TimeWheel

	shutdownOnce sync.Once
	shutdownCh   chan struct{}
	wg           sync.WaitGroup

	certFile string
	keyFile  string

	pubServer *pubServer
	subServer *subServer
	manServer *manServer
	debugMux  *http.ServeMux

	clientStates *ClientStates
}

func New(id string) *Gateway {
	this := &Gateway{
		id:           id,
		shutdownCh:   make(chan struct{}),
		certFile:     Options.CertFile,
		keyFile:      Options.KeyFile,
		clientStates: NewClientStates(),
	}

	this.zkzone = gzk.NewZkZone(gzk.DefaultConfig(Options.Zone, ctx.ZoneZkAddrs(Options.Zone)))
	if err := this.zkzone.Ping(); err != nil {
		panic(err)
	}

	if Options.EnableRegistry {
		registry.Default = zk.New(this.zkzone, this.id, this.InstanceInfo())
	}
	metaConf := zkmeta.DefaultConfig()
	metaConf.Refresh = Options.MetaRefresh
	meta.Default = zkmeta.New(metaConf, this.zkzone)
	this.guard = newGuard(this)
	this.timer = timewheel.NewTimeWheel(time.Second, 120)
	this.accessLogger = NewAccessLogger("access_log", 100)
	this.svrMetrics = NewServerMetrics(Options.ReporterInterval, this)
	rc, err := influxdb.NewConfig(Options.InfluxServer, Options.InfluxDbName, "", "", Options.ReporterInterval)
	if err != nil {
		log.Error("telementry: %v", err)
	} else {
		telementry.Default = influxdb.New(metrics.DefaultRegistry, rc)
	}

	// initialize the manager store
	switch Options.ManagerStore {
	case "mysql":
		cf := mandb.DefaultConfig(Options.Zone)
		cf.Refresh = Options.ManagerRefresh
		manager.Default = mandb.New(cf)
		manager.Default.AllowSubWithUnregisteredGroup(Options.PermitUnregisteredGroup)

	case "dummy":
		manager.Default = mandummy.New(Options.DummyCluster)

	default:
		panic("invalid manager store:" + Options.ManagerStore)
	}

	// initialize the servers on demand
	if Options.DebugHttpAddr != "" {
		this.debugMux = http.NewServeMux()
	}
	if Options.ManHttpAddr != "" || Options.ManHttpsAddr != "" {
		this.manServer = newManServer(Options.ManHttpAddr, Options.ManHttpsAddr,
			Options.MaxClients, this)
	}
	if Options.PubHttpAddr != "" || Options.PubHttpsAddr != "" {
		this.pubServer = newPubServer(Options.PubHttpAddr, Options.PubHttpsAddr,
			Options.MaxClients, this)

		switch Options.Store {
		case "kafka":
			store.DefaultPubStore = storekfk.NewPubStore(Options.PubPoolCapcity, Options.PubPoolIdleTimeout,
				Options.UseCompress, &this.wg, Options.Debug, Options.DryRun)

		case "dummy":
			store.DefaultPubStore = storedummy.NewPubStore(&this.wg, Options.Debug)

		default:
			panic("invalid store")
		}
	}
	if Options.SubHttpAddr != "" || Options.SubHttpsAddr != "" {
		this.subServer = newSubServer(Options.SubHttpAddr, Options.SubHttpsAddr,
			Options.MaxClients, this)

		switch Options.Store {
		case "kafka":
			store.DefaultSubStore = storekfk.NewSubStore(&this.wg,
				this.subServer.closedConnCh, Options.Debug)

		case "dummy":
			store.DefaultSubStore = storedummy.NewSubStore(&this.wg,
				this.subServer.closedConnCh, Options.Debug)

		default:
			panic("invalid store")

		}
	}

	return this
}

func (this *Gateway) InstanceInfo() []byte {
	ip, err := ctx.LocalIP()
	if err != nil {
		panic(err)
	}
	info := gzk.KatewayMeta{
		Id:        this.id,
		Zone:      Options.Zone,
		Ver:       gafka.Version,
		Build:     gafka.BuildId,
		BuiltAt:   gafka.BuiltAt,
		Host:      ctx.Hostname(),
		Ip:        ip.String(),
		Cpu:       ctx.NumCPUStr(),
		Arch:      fmt.Sprintf("%s:%s-%s/%s", runtime.Compiler, runtime.Version(), runtime.GOOS, runtime.GOARCH),
		PubAddr:   Options.PubHttpAddr,
		SPubAddr:  Options.PubHttpsAddr,
		SubAddr:   Options.SubHttpAddr,
		SSubAddr:  Options.SubHttpsAddr,
		ManAddr:   Options.ManHttpAddr,
		SManAddr:  Options.ManHttpsAddr,
		DebugAddr: Options.DebugHttpAddr,
	}
	d, _ := json.Marshal(info)
	return d
}

func (this *Gateway) Start() (err error) {
	log.Info("starting gateway[%s@%s]...", gafka.BuildId, gafka.BuiltAt)

	signal.RegisterSignalsHandler(func(sig os.Signal) {
		log.Info("gateway[%s@%s] received signal: %s", gafka.BuildId, gafka.BuiltAt, strings.ToUpper(sig.String()))
		this.stop()
	}, syscall.SIGINT, syscall.SIGTERM) // yes we ignore HUP

	// keep watch on zk connection jitter
	go func() {
		evtCh, ok := this.zkzone.SessionEvents()
		if !ok {
			log.Error("someone else is consuming my zk events?")
			return
		}

		// during connecting phase, the following events are fired:
		// StateConnecting -> StateConnected -> StateHasSession
		firstHandShaked := false
		for {
			select {
			case <-this.shutdownCh:
				return

			case evt, ok := <-evtCh:
				if !ok {
					return
				}

				if !firstHandShaked {
					if evt.State == zklib.StateHasSession {
						firstHandShaked = true
					}

					continue
				}

				log.Warn("zk jitter: %+v", evt)

				if evt.State == zklib.StateHasSession {
					log.Warn("zk reconnected after session lost")
				}
			}
		}
	}()

	meta.Default.Start()
	log.Trace("meta store[%s] started", meta.Default.Name())

	if err = manager.Default.Start(); err != nil {
		return
	}
	log.Trace("manager store[%s] started", manager.Default.Name())

	go this.guard.Start()
	log.Trace("guard started")

	if telementry.Default != nil {
		go func() {
			log.Trace("telementry[%s] started", telementry.Default.Name())

			if err = telementry.Default.Start(); err != nil {
				log.Error("telementry[%s]: %v", telementry.Default.Name(), err)
			}
		}()
	}

	if Options.EnableAccessLog {
		if err = this.accessLogger.Start(); err != nil {
			log.Error("access logger: %s", err)
		}
	}

	this.buildRouting()

	this.svrMetrics.Load()
	go startRuntimeMetrics(Options.ReporterInterval)

	// start up the servers
	if this.manServer != nil {
		this.manServer.Start()
	}
	if this.pubServer != nil {
		if err := store.DefaultPubStore.Start(); err != nil {
			panic(err)
		}
		log.Trace("pub store[%s] started", store.DefaultPubStore.Name())

		this.pubServer.Start()
	}
	if this.subServer != nil {
		if err := store.DefaultSubStore.Start(); err != nil {
			panic(err)
		}
		log.Trace("sub store[%s] started", store.DefaultSubStore.Name())

		this.subServer.Start()
	}

	// the last thing is to register: notify others: come on baby!
	if registry.Default != nil {
		if err := registry.Default.Register(); err != nil {
			panic(err)
		}

		log.Info("gateway[%s:%s] ready, registered in %s", ctx.Hostname(), this.id,
			registry.Default.Name())
	} else {
		log.Info("gateway[%s:%s] ready, unregistered", ctx.Hostname(), this.id)
	}

	return nil
}

func (this *Gateway) stop() {
	this.shutdownOnce.Do(func() {
		log.Info("stopping gateway...")

		close(this.shutdownCh)
	})
}

func (this *Gateway) ServeForever() {
	select {
	case <-this.shutdownCh:
		// the 1st thing is to deregister
		if registry.Default != nil {
			if err := registry.Default.Deregister(); err != nil {
				log.Error("Deregister: %v", err)
			}

			log.Info("deregistered from %s", registry.Default.Name())
		}

		// store can only be closed after web server closed
		<-this.pubServer.Closed()
		<-this.subServer.Closed()
		<-this.manServer.Closed()

		this.accessLogger.Stop()

		log.Trace("stopping pub store")
		if store.DefaultPubStore != nil {
			go store.DefaultPubStore.Stop()
		}
		log.Trace("stopping sub store")
		if store.DefaultSubStore != nil {
			go store.DefaultSubStore.Stop()
		}

		log.Info("...waiting for services shutdown...")
		this.wg.Wait()
		log.Info("<----- all services shutdown ----->")

		this.svrMetrics.Flush()
		log.Trace("svr metrics flushed")

		if telementry.Default != nil {
			telementry.Default.Stop()
			log.Trace("telementry stopped")
		}

		meta.Default.Stop()
		log.Trace("meta store stopped")
		manager.Default.Stop()
		log.Trace("manager store stopped")

		if this.zkzone != nil {
			this.zkzone.Close()
			log.Trace("zkzone stopped")
		}

		this.timer.Stop()
	}

}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"net/http"
	"strconv"

	"github.com/bakape/r-a-d.io/templates"
	"gopkg.in/olivere/elastic.v5"
)

var (
	elasticClient *elastic.Client
)

// Connect to Elastic Search
func initElastic() (err error) {
	elasticClient, err = elastic.NewClient()
	return
}

// Query Elastic Search for matching songs
func querySearch(query string, page int) (buf []byte, err error) {
	q := elastic.NewQueryStringQuery(query).
		DefaultOperator("AND")
	for _, s := range [...]string{"title", "artist", "album", "tags", "_id"} {
		q.Field(s)
	}
	res, err := elasticClient.Search().
		Index("song-database").
		Type("track").
		Query(q).
		SortBy(
			elastic.NewFieldSort("priority").Desc(),
			elastic.NewFieldSort("_score").Desc(),
		).
		PostFilter(
			elastic.NewBoolQuery().
				Must(elastic.NewTermQuery("usable", 1)),
		).
		From(page * 20).
		Size(20).
		Do(nil)
	if err != nil {
		return
	}
	buf, err = json.Marshal(res)
	return
}

// Serve search result page
func serveSearch(w http.ResponseWriter, r *http.Request) {
	q := r.URL.Query()
	page, err := strconv.ParseUint(q.Get("page"), 10, 64)
	if err != nil {
		text400(w, err)
		return
	}
	buf, err := querySearch(q.Get("q"), int(page))
	if err != nil {
		text500(w, r, err)
		return
	}

	w.Write([]byte(templates.Search(buf)))
}
<commit_msg>Default to page 0<commit_after>package main

import (
	"encoding/json"
	"net/http"
	"strconv"

	"github.com/bakape/r-a-d.io/templates"
	"gopkg.in/olivere/elastic.v5"
)

var (
	elasticClient *elastic.Client
)

// Connect to Elastic Search
func initElastic() (err error) {
	elasticClient, err = elastic.NewClient()
	return
}

// Query Elastic Search for matching songs
func querySearch(query string, page int) (buf []byte, err error) {
	q := elastic.NewQueryStringQuery(query).
		DefaultOperator("AND")
	for _, s := range [...]string{"title", "artist", "album", "tags", "_id"} {
		q.Field(s)
	}
	res, err := elasticClient.Search().
		Index("song-database").
		Type("track").
		Query(q).
		SortBy(
			elastic.NewFieldSort("priority").Desc(),
			elastic.NewFieldSort("_score").Desc(),
		).
		PostFilter(
			elastic.NewBoolQuery().
				Must(elastic.NewTermQuery("usable", 1)),
		).
		From(page * 20).
		Size(20).
		Do(nil)
	if err != nil {
		return
	}
	buf, err = json.Marshal(res)
	return
}

// Serve search result page
func serveSearch(w http.ResponseWriter, r *http.Request) {
	q := r.URL.Query()
	page, _ := strconv.ParseUint(q.Get("page"), 10, 64)
	buf, err := querySearch(q.Get("q"), int(page))
	if err != nil {
		text500(w, r, err)
		return
	}

	w.Write([]byte(templates.Search(buf)))
}
<|endoftext|>


<commit_before>package solr

import (
	"fmt"
	"net/url"
	"strings"
)

type Query struct {
	params url.Values
}

func NewQuery() *Query {
	q := new(Query)
	q.params = url.Values{}
	return q
}

func (q *Query) AddParam(k string, v string) {
	q.params.Add(k, v)
}

func (q *Query) RemoveParam(k string) {
	q.params.Del(k)
}

func (q *Query) GetParam(k string) string {
	return q.params.Get(k)
}

func (q *Query) SetParam(k string, v string) {
	q.params.Set(k, v)
}

func (q *Query) String() string {
	return q.params.Encode()
}

type Search struct {
	query *Query
	conn  *Connection
	Start int
	Rows  int
	Debug string
}

// NewSearch takes c and q as optional
func NewSearch(c *Connection, q *Query) *Search {
	s := new(Search)
	if q != nil {
		s.SetQuery(q)
	}
	if c != nil {
		s.conn = c
	}
	return s
}

// SetQuery will replace old query with new query q
func (s *Search) SetQuery(q *Query) {
	s.query = q
}

// QueryString return a query string of all queries, including start, rows, debug and wt=json.
// wt is always json
func (s *Search) QueryString() string {

	query := []string{"wt=json"}

	if s.Start > 0 {
		query = append(query, fmt.Sprintf("start=%d", s.Start))
	}

	if s.Rows > 0 {
		query = append(query, fmt.Sprintf("rows=%d", s.Rows))
	}

	if s.Debug != "" {
		query = append(query, fmt.Sprintf("debug=%s&indent=true", s.Debug))
	}

	if s.query != nil {
		query = append(query, s.query.String())
	}

	return strings.Join(query, "&")
}

// Result will create a StandardResultParser if no parser specified.
// parser must be an implementation of ResultParser interface
func (s *Search) Result(parser ResultParser) (*SolrResult, error) {
	if s.conn == nil {
		return nil, fmt.Errorf("No connection found for making request to solr")
	}
	resp, err := s.conn.Select(s.QueryString())
	if err != nil {
		return nil, err
	}
	if parser == nil {
		parser = new(StandardResultParser)
	}
	return parser.Parse(resp)
}
<commit_msg>Make max capacity for query<commit_after>package solr

import (
	"fmt"
	"net/url"
	"strings"
)

type Query struct {
	params url.Values
}

func NewQuery() *Query {
	q := new(Query)
	q.params = url.Values{}
	return q
}

func (q *Query) AddParam(k string, v string) {
	q.params.Add(k, v)
}

func (q *Query) RemoveParam(k string) {
	q.params.Del(k)
}

func (q *Query) GetParam(k string) string {
	return q.params.Get(k)
}

func (q *Query) SetParam(k string, v string) {
	q.params.Set(k, v)
}

func (q *Query) String() string {
	return q.params.Encode()
}

type Search struct {
	query *Query
	conn  *Connection
	Start int
	Rows  int
	Debug string
}

// NewSearch takes c and q as optional
func NewSearch(c *Connection, q *Query) *Search {
	s := new(Search)
	if q != nil {
		s.SetQuery(q)
	}
	if c != nil {
		s.conn = c
	}
	return s
}

// SetQuery will replace old query with new query q
func (s *Search) SetQuery(q *Query) {
	s.query = q
}

// QueryString return a query string of all queries, including start, rows, debug and wt=json.
// wt is always json
func (s *Search) QueryString() string {

	query := make([]string, 0, 5)

	query = append(query, "wt=json")

	if s.Start > 0 {
		query = append(query, fmt.Sprintf("start=%d", s.Start))
	}

	if s.Rows > 0 {
		query = append(query, fmt.Sprintf("rows=%d", s.Rows))
	}

	if s.Debug != "" {
		query = append(query, fmt.Sprintf("debug=%s&indent=true", s.Debug))
	}

	if s.query != nil {
		query = append(query, s.query.String())
	}

	return strings.Join(query, "&")
}

// Result will create a StandardResultParser if no parser specified.
// parser must be an implementation of ResultParser interface
func (s *Search) Result(parser ResultParser) (*SolrResult, error) {
	if s.conn == nil {
		return nil, fmt.Errorf("No connection found for making request to solr")
	}
	resp, err := s.conn.Select(s.QueryString())
	if err != nil {
		return nil, err
	}
	if parser == nil {
		parser = new(StandardResultParser)
	}
	return parser.Parse(resp)
}
<|endoftext|>


<commit_before>package secure

import (
	"crypto/rand"
	"encoding/binary"
	"errors"
	"fmt"
	"io"

	"golang.org/x/crypto/nacl/box"
)

// Size (in bytes) of the nonce for NaCl box seal/open
const NonceSize = 24

// Size (in bytes) of the key for NaCl box seal/open
const KeySize = 32

// Size (in bytes) of the max message size supported by this package
const MaxMessageSize = 32 * 1024

// ErrNonceSize means that the source of randomness did not provide
// enough bytes for a complete nonce
var ErrNonceSize = errors.New("not enough bytes read for nonce")

// ErrDecrypt means that there was a problem during decryption
var ErrDecrypt = errors.New("decrypt error")

// ErrEncWrite means that a complete encrypted message was unable to be written
var ErrEncWrite = errors.New("failed to write complete encrypted message")

// A Reader is an io.Reader that can be used to read streams
// of encrypted data that was encrypted using the Writer from this package.
// The Reader will decrypt and return the plaintext from
// the provided io.Reader.
type Reader struct {
	r         io.Reader
	priv, pub *[KeySize]byte
	shared    [KeySize]byte
}

// Read decrypts a stream encrypted with box.Seal.
// It expects the nonce used to be prepended
// to the ciphertext
func (s Reader) Read(p []byte) (int, error) {
	// Read the nonce from the stream
	var nonce [NonceSize]byte
	if n, err := io.ReadFull(s.r, nonce[:]); err != nil {
		fmt.Println(err)
		fmt.Println(n)
		return 0, errors.New("nonce")
	}

	// Read the ciphertext size
	var size uint16
	if err := binary.Read(s.r, binary.LittleEndian, &size); err != nil {
		return 0, errors.New("size")
	}

	// Ensure buffer is large enough for ciphertext
	if uint16(len(p)) < size-box.Overhead {
		return 0, errors.New("wrong size")
	}

	// make a buffer large enough to handle
	// the overhead associated with an encrypted message
	enc := make([]byte, size)
	if _, err := io.ReadFull(s.r, enc); err != nil {
		return 0, errors.New("msg")
	}

	decrypt, auth := box.OpenAfterPrecomputation(p[0:0], enc, &nonce, &s.shared)
	// if authentication failed, output bottom
	if !auth {
		return 0, ErrDecrypt
	}

	return len(decrypt), nil
}

// A Writer is an io.Writer which will encrypt the provided data
// and write it to the provided wrapped io.Writer
type Writer struct {
	w         io.Writer
	priv, pub *[KeySize]byte
	shared    [KeySize]byte
}

// Write encrypts a plaintext stream using box.Seal
func (s Writer) Write(p []byte) (int, error) {
	var nonce [24]byte
	if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil {
		return 0, errors.New("secureWriter: cant generate random nonce: " + err.Error())
	}

	// write nonce
	_, err := s.w.Write(nonce[:])
	if err != nil {
		return 0, ErrEncWrite
	}

	enc := box.SealAfterPrecomputation(nil, p, &nonce, &s.shared)

	// write ciphertext length
	if err := binary.Write(s.w, binary.LittleEndian, uint16(len(enc))); err != nil {
		return 0, ErrEncWrite
	}

	// write ciphertext
	if _, err = s.w.Write(enc); err != nil {
		return 0, ErrEncWrite
	}

	return len(p), err
}

// NewReader instantiates a new secure Reader
// priv and pub should be keys generated with box.GenerateKey
func NewReader(r io.Reader, priv, pub *[KeySize]byte) Reader {
	sr := Reader{r: r, priv: priv, pub: pub}
	box.Precompute(&sr.shared, pub, priv)
	return sr
}

// NewWriter instantiates a new secure Writer
// priv and pub should be keys generated with box.GenerateKey
func NewWriter(w io.Writer, priv, pub *[KeySize]byte) Writer {
	sw := Writer{w: w, priv: priv, pub: pub}
	box.Precompute(&sw.shared, pub, priv)
	return sw
}
<commit_msg>change to use generic error<commit_after>package secure

import (
	"crypto/rand"
	"encoding/binary"
	"errors"
	"fmt"
	"io"

	"golang.org/x/crypto/nacl/box"
)

// Size (in bytes) of the nonce for NaCl box seal/open
const NonceSize = 24

// Size (in bytes) of the key for NaCl box seal/open
const KeySize = 32

// Size (in bytes) of the max message size supported by this package
const MaxMessageSize = 32 * 1024

// ErrNonceSize means that the source of randomness did not provide
// enough bytes for a complete nonce
var ErrNonceSize = errors.New("not enough bytes read for nonce")

// ErrDecrypt means that there was a problem during decryption
var ErrDecrypt = errors.New("decrypt error")

// ErrEncWrite means that a complete encrypted message was unable to be written
var ErrEncWrite = errors.New("failed to write complete encrypted message")

// A Reader is an io.Reader that can be used to read streams
// of encrypted data that was encrypted using the Writer from this package.
// The Reader will decrypt and return the plaintext from
// the provided io.Reader.
type Reader struct {
	r         io.Reader
	priv, pub *[KeySize]byte
	shared    [KeySize]byte
}

// Read decrypts a stream encrypted with box.Seal.
// It expects the nonce used to be prepended
// to the ciphertext
func (s Reader) Read(p []byte) (int, error) {
	// Read the nonce from the stream
	var nonce [NonceSize]byte
	if n, err := io.ReadFull(s.r, nonce[:]); err != nil {
		fmt.Println(err)
		fmt.Println(n)
		return 0, ErrDecrypt
	}

	// Read the ciphertext size
	var size uint16
	if err := binary.Read(s.r, binary.LittleEndian, &size); err != nil {
		return 0, ErrDecrypt
	}

	// Ensure buffer is large enough for ciphertext
	if uint16(len(p)) < size-box.Overhead {
		return 0, ErrDecrypt
	}

	// make a buffer large enough to handle
	// the overhead associated with an encrypted message
	enc := make([]byte, size)
	if _, err := io.ReadFull(s.r, enc); err != nil {
		return 0, ErrDecrypt
	}

	decrypt, auth := box.OpenAfterPrecomputation(p[0:0], enc, &nonce, &s.shared)
	// if authentication failed, output bottom
	if !auth {
		return 0, ErrDecrypt
	}

	return len(decrypt), nil
}

// A Writer is an io.Writer which will encrypt the provided data
// and write it to the provided wrapped io.Writer
type Writer struct {
	w         io.Writer
	priv, pub *[KeySize]byte
	shared    [KeySize]byte
}

// Write encrypts a plaintext stream using box.Seal
func (s Writer) Write(p []byte) (int, error) {
	var nonce [24]byte
	if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil {
		return 0, errors.New("secureWriter: cant generate random nonce: " + err.Error())
	}

	// write nonce
	_, err := s.w.Write(nonce[:])
	if err != nil {
		return 0, ErrEncWrite
	}

	enc := box.SealAfterPrecomputation(nil, p, &nonce, &s.shared)

	// write ciphertext length
	if err := binary.Write(s.w, binary.LittleEndian, uint16(len(enc))); err != nil {
		return 0, ErrEncWrite
	}

	// write ciphertext
	if _, err = s.w.Write(enc); err != nil {
		return 0, ErrEncWrite
	}

	return len(p), err
}

// NewReader instantiates a new secure Reader
// priv and pub should be keys generated with box.GenerateKey
func NewReader(r io.Reader, priv, pub *[KeySize]byte) Reader {
	sr := Reader{r: r, priv: priv, pub: pub}
	box.Precompute(&sr.shared, pub, priv)
	return sr
}

// NewWriter instantiates a new secure Writer
// priv and pub should be keys generated with box.GenerateKey
func NewWriter(w io.Writer, priv, pub *[KeySize]byte) Writer {
	sw := Writer{w: w, priv: priv, pub: pub}
	box.Precompute(&sw.shared, pub, priv)
	return sw
}
<|endoftext|>


<commit_before>package semver

import (
	"fmt"
	"regexp"
	"strings"
)

var (
	baseRe    *regexp.Regexp
	extReList []*regexp.Regexp
)

// Version represents a SemVer 2.0.0 version and implements some high-level
// methods to manage multiple versions.
type SemVer struct {
	Major  string // Backward-incompatible changes
	Minor  string // New functionality
	Patch  string // Bug fixes
	PreRel string // Optional pre-release tag
	Build  string // Optional build metadata
}

func init() {
	baseRe = regexp.MustCompile("^(([1-9][0-9]+)|[0-9])?$")
	extReList = []*regexp.Regexp{
		regexp.MustCompile("^([1-9a-zA-Z]([0-9a-zA-Z-]+)?)?$"),
		regexp.MustCompile("^([1-9]([0-9]+)?)?$"),
	}
}

// String will return a flat string representing the semantic version
func (s *SemVer) String() string {
	res := s.BaseString()
	if s.PreRel != "" {
		res = fmt.Sprintf("%s-%s", res, s.PreRel)
	}
	if s.Build != "" {
		res = fmt.Sprintf("%s+%s", res, s.Build)
	}
	return res
}

// BaseString will return the base version number (sans pre-release and build)
// as a formatted string.
func (s *SemVer) BaseString() string {
	return fmt.Sprintf("%s.%s.%s", s.Major, s.Minor, s.Patch)
}

// parts will return all version components as a slice of strings.
func (s *SemVer) parts() []string {
	return []string{s.Major, s.Minor, s.Patch, s.PreRel, s.Build}
}

// New creates a new semver object from individual version components.
func New(major, minor, patch, preRel, build string) (*SemVer, error) {
	s := &SemVer{
		Major:  major,
		Minor:  minor,
		Patch:  patch,
		PreRel: preRel,
		Build:  build,
	}
	if err := s.verify(); err != nil {
		return nil, err
	}
	return s, nil
}

// New will create a new semantic versioning object from a flat
// string and populate all struct fields.
func NewFromString(vstr string) (*SemVer, error) {
	var major, minor, patch, pre, build string

	parts := strings.SplitN(vstr, ".", 3)
	if len(parts) < 3 {
		return nil, fmt.Errorf("semver: version too short: %s", vstr)
	}
	major, minor, patch = parts[0], parts[1], parts[2]

	parts = strings.SplitN(patch, "+", 2)
	patch = parts[0]
	if len(parts) > 1 {
		build = parts[1]
	}

	parts = strings.SplitN(patch, "-", 2)
	patch = parts[0]
	if len(parts) > 1 {
		pre = parts[1]
	}

	return New(major, minor, patch, pre, build)
}

// matchAny simplifies iterating over a slice of regexp patterns and testing if
// any of them match a subject text.
func matchAny(patterns []*regexp.Regexp, subj string) bool {
	for _, pattern := range patterns {
		if pattern.MatchString(subj) {
			return true
		}
	}
	return false
}

// verify is used to ensure that a semver object complies with the format
// defined by semver.org.
func (s *SemVer) verify() error {
	if !(baseRe.MatchString(s.Major) &&
		baseRe.MatchString(s.Minor) &&
		baseRe.MatchString(s.Patch)) {
		return fmt.Errorf("semver: invalid base version: %s", s.BaseString())
	}

	for _, subj := range strings.Split(s.PreRel, ".") {
		if !matchAny(extReList, subj) {
			return fmt.Errorf("semver: invalid pre-release tag: %s", s.PreRel)
		}
	}

	for _, subj := range strings.Split(s.Build, ".") {
		if !matchAny(extReList, subj) {
			return fmt.Errorf("semver: invalid build metadata: %s", s.Build)
		}
	}

	return nil
}
<commit_msg>Refactored verify method<commit_after>package semver

import (
	"fmt"
	"regexp"
	"strings"
)

var (
	baseRe    *regexp.Regexp
	extReList []*regexp.Regexp
)

// Version represents a SemVer 2.0.0 version and implements some high-level
// methods to manage multiple versions.
type SemVer struct {
	Major  string // Backward-incompatible changes
	Minor  string // New functionality
	Patch  string // Bug fixes
	PreRel string // Optional pre-release tag
	Build  string // Optional build metadata
}

func init() {
	baseRe = regexp.MustCompile("^(([1-9][0-9]+)|[0-9])?$")
	extReList = []*regexp.Regexp{
		regexp.MustCompile("^([1-9a-zA-Z]([0-9a-zA-Z-]+)?)?$"),
		regexp.MustCompile("^([1-9]([0-9]+)?)?$"),
	}
}

// String will return a flat string representing the semantic version
func (s *SemVer) String() string {
	res := fmt.Sprintf("%s.%s.%s", s.Major, s.Minor, s.Patch)
	if s.PreRel != "" {
		res = fmt.Sprintf("%s-%s", res, s.PreRel)
	}
	if s.Build != "" {
		res = fmt.Sprintf("%s+%s", res, s.Build)
	}
	return res
}

// parts will return all version components as a slice of strings.
func (s *SemVer) parts() []string {
	return []string{s.Major, s.Minor, s.Patch, s.PreRel, s.Build}
}

// New creates a new semver object from individual version components.
func New(major, minor, patch, preRel, build string) (*SemVer, error) {
	s := &SemVer{
		Major:  major,
		Minor:  minor,
		Patch:  patch,
		PreRel: preRel,
		Build:  build,
	}
	if err := s.verify(); err != nil {
		return nil, err
	}
	return s, nil
}

// New will create a new semantic versioning object from a flat
// string and populate all struct fields.
func NewFromString(vstr string) (*SemVer, error) {
	var major, minor, patch, pre, build string

	parts := strings.SplitN(vstr, ".", 3)
	if len(parts) < 3 {
		return nil, fmt.Errorf("semver: version too short: %s", vstr)
	}
	major, minor, patch = parts[0], parts[1], parts[2]

	parts = strings.SplitN(patch, "+", 2)
	patch = parts[0]
	if len(parts) > 1 {
		build = parts[1]
	}

	parts = strings.SplitN(patch, "-", 2)
	patch = parts[0]
	if len(parts) > 1 {
		pre = parts[1]
	}

	return New(major, minor, patch, pre, build)
}

// matchAny simplifies iterating over a slice of regexp patterns and testing if
// any of them match a subject text.
func matchAny(patterns []*regexp.Regexp, subj string) bool {
	for _, pattern := range patterns {
		if pattern.MatchString(subj) {
			return true
		}
	}
	return false
}

// verify is used to ensure that a semver object complies with the format
// defined by semver.org.
func (s *SemVer) verify() error {
	if !baseRe.MatchString(s.Major) {
		return fmt.Errorf("semver: invalid major version: %s", s.Major)
	}

	if !baseRe.MatchString(s.Minor) {
		return fmt.Errorf("semver: invalid minor version: %s", s.Minor)
	}

	if !baseRe.MatchString(s.Patch) {
		return fmt.Errorf("semver: invalid patch version: %s", s.Patch)
	}

	for _, subj := range strings.Split(s.PreRel, ".") {
		if !matchAny(extReList, subj) {
			return fmt.Errorf("semver: invalid pre-release tag: %s", s.PreRel)
		}
	}

	for _, subj := range strings.Split(s.Build, ".") {
		if !matchAny(extReList, subj) {
			return fmt.Errorf("semver: invalid build metadata: %s", s.Build)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package rpc

type Server struct {
	xp        Transporter
	wrapError WrapErrorFunc
}

func NewServer(xp Transporter, f WrapErrorFunc) *Server {
	return &Server{xp, f}
}

func (s *Server) Register(p Protocol) error {
	p.WrapError = s.wrapError
	receiver, err := s.xp.getReceiver()
	if err != nil {
		return err
	}
	return receiver.RegisterProtocol(p)
}

// AddCloseListener supplies a channel listener to which
// the server will send an error when a connection closes
func (s *Server) AddCloseListener(ch chan error) error {
	rec, err := s.xp.getReceiver()
	if err != nil {
		return err
	}
	rec.AddCloseListener(ch)
	return nil
}

// TODO: Split into Run and RunAsync, and update callers. See
// https://github.com/keybase/go-framed-msgpack-rpc/issues/39 .
func (s *Server) Run(bg bool) error {
	if bg {
		return s.xp.Run()
	}
	return s.xp.RunAsync()
}
<commit_msg>Fix stupid bug with previous commit<commit_after>package rpc

type Server struct {
	xp        Transporter
	wrapError WrapErrorFunc
}

func NewServer(xp Transporter, f WrapErrorFunc) *Server {
	return &Server{xp, f}
}

func (s *Server) Register(p Protocol) error {
	p.WrapError = s.wrapError
	receiver, err := s.xp.getReceiver()
	if err != nil {
		return err
	}
	return receiver.RegisterProtocol(p)
}

// AddCloseListener supplies a channel listener to which
// the server will send an error when a connection closes
func (s *Server) AddCloseListener(ch chan error) error {
	rec, err := s.xp.getReceiver()
	if err != nil {
		return err
	}
	rec.AddCloseListener(ch)
	return nil
}

// TODO: Split into Run and RunAsync, and update callers. See
// https://github.com/keybase/go-framed-msgpack-rpc/issues/39 .
func (s *Server) Run(bg bool) error {
	if bg {
		return s.xp.RunAsync()
	}
	return s.xp.Run()
}
<|endoftext|>


<commit_before>// Copyright 2015 The Prometheus 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.

// +build !nofilesystem
// +build linux freebsd openbsd darwin,amd64 dragonfly

package collector

import (
	"flag"
	"regexp"

	"github.com/prometheus/client_golang/prometheus"
)

// Arch-dependent implementation must define:
// * defIgnoredMountPoints
// * defIgnoredFSTypes
// * filesystemLabelNames
// * filesystemCollector.GetStats

var (
	ignoredMountPoints = flag.String(
		"collector.filesystem.ignored-mount-points",
		defIgnoredMountPoints,
		"Regexp of mount points to ignore for filesystem collector.")

	ignoredFSTypes = flag.String(
		"collector.filesystem.ignored-fs-types",
		defIgnoredFSTypes,
		"Regexp of filesystem types to ignore for filesystem collector.")

	filesystemLabelNames = []string{"device", "mountpoint", "fstype"}
)

type filesystemCollector struct {
	ignoredMountPointsPattern     *regexp.Regexp
	ignoredFSTypesPattern         *regexp.Regexp
	sizeDesc, freeDesc, availDesc *prometheus.Desc
	filesDesc, filesFreeDesc      *prometheus.Desc
	roDesc, deviceErrorDesc       *prometheus.Desc
}

type filesystemLabels struct {
	device, mountPoint, fsType string
}

type filesystemStats struct {
	labels            filesystemLabels
	size, free, avail float64
	files, filesFree  float64
	ro, deviceError   float64
}

func init() {
	Factories["filesystem"] = NewFilesystemCollector
}

// NewFilesystemCollector returns a new Collector exposing filesystems stats.
func NewFilesystemCollector() (Collector, error) {
	subsystem := "filesystem"
	mountPointPattern := regexp.MustCompile(*ignoredMountPoints)
	filesystemsTypesPattern := regexp.MustCompile(*ignoredFSTypes)

	sizeDesc := prometheus.NewDesc(
		prometheus.BuildFQName(Namespace, subsystem, "size"),
		"Filesystem size in bytes.",
		filesystemLabelNames, nil,
	)

	freeDesc := prometheus.NewDesc(
		prometheus.BuildFQName(Namespace, subsystem, "free"),
		"Filesystem free space in bytes.",
		filesystemLabelNames, nil,
	)

	availDesc := prometheus.NewDesc(
		prometheus.BuildFQName(Namespace, subsystem, "avail"),
		"Filesystem space available to non-root users in bytes.",
		filesystemLabelNames, nil,
	)

	filesDesc := prometheus.NewDesc(
		prometheus.BuildFQName(Namespace, subsystem, "files"),
		"Filesystem total file nodes.",
		filesystemLabelNames, nil,
	)

	filesFreeDesc := prometheus.NewDesc(
		prometheus.BuildFQName(Namespace, subsystem, "files_free"),
		"Filesystem total free file nodes.",
		filesystemLabelNames, nil,
	)

	roDesc := prometheus.NewDesc(
		prometheus.BuildFQName(Namespace, subsystem, "readonly"),
		"Filesystem read-only status.",
		filesystemLabelNames, nil,
	)

	deviceErrorDesc := prometheus.NewDesc(
		prometheus.BuildFQName(Namespace, subsystem, "device_error"),
		"Whether an error occured while getting statistics for the given device.",
		filesystemLabelNames, nil,
	)

	return &filesystemCollector{
		ignoredMountPointsPattern: mountPointPattern,
		ignoredFSTypesPattern:     filesystemsTypesPattern,
		sizeDesc:                  sizeDesc,
		freeDesc:                  freeDesc,
		availDesc:                 availDesc,
		filesDesc:                 filesDesc,
		filesFreeDesc:             filesFreeDesc,
		roDesc:                    roDesc,
		deviceErrorDesc:           deviceErrorDesc,
	}, nil
}

func (c *filesystemCollector) Update(ch chan<- prometheus.Metric) error {
	stats, err := c.GetStats()
	if err != nil {
		return err
	}
	// Make sure we expose a metric once, even if there are multiple mounts
	seen := map[filesystemLabels]bool{}
	for _, s := range stats {
		if seen[s.labels] {
			continue
		}
		seen[s.labels] = true

		ch <- prometheus.MustNewConstMetric(
			c.deviceErrorDesc, prometheus.GaugeValue,
			s.deviceError, s.labels.device, s.labels.mountPoint, s.labels.fsType,
		)
		if s.deviceError > 0 {
			continue
		}

		ch <- prometheus.MustNewConstMetric(
			c.sizeDesc, prometheus.GaugeValue,
			s.size, s.labels.device, s.labels.mountPoint, s.labels.fsType,
		)
		ch <- prometheus.MustNewConstMetric(
			c.freeDesc, prometheus.GaugeValue,
			s.free, s.labels.device, s.labels.mountPoint, s.labels.fsType,
		)
		ch <- prometheus.MustNewConstMetric(
			c.availDesc, prometheus.GaugeValue,
			s.avail, s.labels.device, s.labels.mountPoint, s.labels.fsType,
		)
		ch <- prometheus.MustNewConstMetric(
			c.filesDesc, prometheus.GaugeValue,
			s.files, s.labels.device, s.labels.mountPoint, s.labels.fsType,
		)
		ch <- prometheus.MustNewConstMetric(
			c.filesFreeDesc, prometheus.GaugeValue,
			s.filesFree, s.labels.device, s.labels.mountPoint, s.labels.fsType,
		)
		ch <- prometheus.MustNewConstMetric(
			c.roDesc, prometheus.GaugeValue,
			s.ro, s.labels.device, s.labels.mountPoint, s.labels.fsType,
		)
	}
	return nil
}
<commit_msg>Correct typo (#582)<commit_after>// Copyright 2015 The Prometheus 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.

// +build !nofilesystem
// +build linux freebsd openbsd darwin,amd64 dragonfly

package collector

import (
	"flag"
	"regexp"

	"github.com/prometheus/client_golang/prometheus"
)

// Arch-dependent implementation must define:
// * defIgnoredMountPoints
// * defIgnoredFSTypes
// * filesystemLabelNames
// * filesystemCollector.GetStats

var (
	ignoredMountPoints = flag.String(
		"collector.filesystem.ignored-mount-points",
		defIgnoredMountPoints,
		"Regexp of mount points to ignore for filesystem collector.")

	ignoredFSTypes = flag.String(
		"collector.filesystem.ignored-fs-types",
		defIgnoredFSTypes,
		"Regexp of filesystem types to ignore for filesystem collector.")

	filesystemLabelNames = []string{"device", "mountpoint", "fstype"}
)

type filesystemCollector struct {
	ignoredMountPointsPattern     *regexp.Regexp
	ignoredFSTypesPattern         *regexp.Regexp
	sizeDesc, freeDesc, availDesc *prometheus.Desc
	filesDesc, filesFreeDesc      *prometheus.Desc
	roDesc, deviceErrorDesc       *prometheus.Desc
}

type filesystemLabels struct {
	device, mountPoint, fsType string
}

type filesystemStats struct {
	labels            filesystemLabels
	size, free, avail float64
	files, filesFree  float64
	ro, deviceError   float64
}

func init() {
	Factories["filesystem"] = NewFilesystemCollector
}

// NewFilesystemCollector returns a new Collector exposing filesystems stats.
func NewFilesystemCollector() (Collector, error) {
	subsystem := "filesystem"
	mountPointPattern := regexp.MustCompile(*ignoredMountPoints)
	filesystemsTypesPattern := regexp.MustCompile(*ignoredFSTypes)

	sizeDesc := prometheus.NewDesc(
		prometheus.BuildFQName(Namespace, subsystem, "size"),
		"Filesystem size in bytes.",
		filesystemLabelNames, nil,
	)

	freeDesc := prometheus.NewDesc(
		prometheus.BuildFQName(Namespace, subsystem, "free"),
		"Filesystem free space in bytes.",
		filesystemLabelNames, nil,
	)

	availDesc := prometheus.NewDesc(
		prometheus.BuildFQName(Namespace, subsystem, "avail"),
		"Filesystem space available to non-root users in bytes.",
		filesystemLabelNames, nil,
	)

	filesDesc := prometheus.NewDesc(
		prometheus.BuildFQName(Namespace, subsystem, "files"),
		"Filesystem total file nodes.",
		filesystemLabelNames, nil,
	)

	filesFreeDesc := prometheus.NewDesc(
		prometheus.BuildFQName(Namespace, subsystem, "files_free"),
		"Filesystem total free file nodes.",
		filesystemLabelNames, nil,
	)

	roDesc := prometheus.NewDesc(
		prometheus.BuildFQName(Namespace, subsystem, "readonly"),
		"Filesystem read-only status.",
		filesystemLabelNames, nil,
	)

	deviceErrorDesc := prometheus.NewDesc(
		prometheus.BuildFQName(Namespace, subsystem, "device_error"),
		"Whether an error occurred while getting statistics for the given device.",
		filesystemLabelNames, nil,
	)

	return &filesystemCollector{
		ignoredMountPointsPattern: mountPointPattern,
		ignoredFSTypesPattern:     filesystemsTypesPattern,
		sizeDesc:                  sizeDesc,
		freeDesc:                  freeDesc,
		availDesc:                 availDesc,
		filesDesc:                 filesDesc,
		filesFreeDesc:             filesFreeDesc,
		roDesc:                    roDesc,
		deviceErrorDesc:           deviceErrorDesc,
	}, nil
}

func (c *filesystemCollector) Update(ch chan<- prometheus.Metric) error {
	stats, err := c.GetStats()
	if err != nil {
		return err
	}
	// Make sure we expose a metric once, even if there are multiple mounts
	seen := map[filesystemLabels]bool{}
	for _, s := range stats {
		if seen[s.labels] {
			continue
		}
		seen[s.labels] = true

		ch <- prometheus.MustNewConstMetric(
			c.deviceErrorDesc, prometheus.GaugeValue,
			s.deviceError, s.labels.device, s.labels.mountPoint, s.labels.fsType,
		)
		if s.deviceError > 0 {
			continue
		}

		ch <- prometheus.MustNewConstMetric(
			c.sizeDesc, prometheus.GaugeValue,
			s.size, s.labels.device, s.labels.mountPoint, s.labels.fsType,
		)
		ch <- prometheus.MustNewConstMetric(
			c.freeDesc, prometheus.GaugeValue,
			s.free, s.labels.device, s.labels.mountPoint, s.labels.fsType,
		)
		ch <- prometheus.MustNewConstMetric(
			c.availDesc, prometheus.GaugeValue,
			s.avail, s.labels.device, s.labels.mountPoint, s.labels.fsType,
		)
		ch <- prometheus.MustNewConstMetric(
			c.filesDesc, prometheus.GaugeValue,
			s.files, s.labels.device, s.labels.mountPoint, s.labels.fsType,
		)
		ch <- prometheus.MustNewConstMetric(
			c.filesFreeDesc, prometheus.GaugeValue,
			s.filesFree, s.labels.device, s.labels.mountPoint, s.labels.fsType,
		)
		ch <- prometheus.MustNewConstMetric(
			c.roDesc, prometheus.GaugeValue,
			s.ro, s.labels.device, s.labels.mountPoint, s.labels.fsType,
		)
	}
	return nil
}
<|endoftext|>