<commit_before>/*
Copyright 2016 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 statefulset

import (
	"fmt"
	"math/rand"
	"sort"
	"strconv"
	"testing"
	"time"

	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"

	"k8s.io/kubernetes/pkg/api/v1"
	podutil "k8s.io/kubernetes/pkg/api/v1/pod"
	apps "k8s.io/kubernetes/pkg/apis/apps/v1beta1"
	"k8s.io/kubernetes/pkg/controller"
	"k8s.io/kubernetes/pkg/controller/history"
)

func TestGetParentNameAndOrdinal(t *testing.T) {
	set := newStatefulSet(3)
	pod := newStatefulSetPod(set, 1)
	if parent, ordinal := getParentNameAndOrdinal(pod); parent != set.Name {
		t.Errorf("Extracted the wrong parent name expected %s found %s", set.Name, parent)
	} else if ordinal != 1 {
		t.Errorf("Extracted the wrong ordinal expected %d found %d", 1, ordinal)
	}
	pod.Name = "1-bar"
	if parent, ordinal := getParentNameAndOrdinal(pod); parent != "" {
		t.Error("Expected empty string for non-member Pod parent")
	} else if ordinal != -1 {
		t.Error("Expected -1 for non member Pod ordinal")
	}
}

func TestIsMemberOf(t *testing.T) {
	set := newStatefulSet(3)
	set2 := newStatefulSet(3)
	set2.Name = "foo2"
	pod := newStatefulSetPod(set, 1)
	if !isMemberOf(set, pod) {
		t.Error("isMemberOf retruned false negative")
	}
	if isMemberOf(set2, pod) {
		t.Error("isMemberOf returned false positive")
	}
}

func TestIdentityMatches(t *testing.T) {
	set := newStatefulSet(3)
	pod := newStatefulSetPod(set, 1)
	if !identityMatches(set, pod) {
		t.Error("Newly created Pod has a bad identity")
	}
	pod.Name = "foo"
	if identityMatches(set, pod) {
		t.Error("identity matches for a Pod with the wrong name")
	}
	pod = newStatefulSetPod(set, 1)
	pod.Namespace = ""
	if identityMatches(set, pod) {
		t.Error("identity matches for a Pod with the wrong namespace")
	}
	pod = newStatefulSetPod(set, 1)
	pod.Spec.Hostname = ""
	if identityMatches(set, pod) {
		t.Error("identity matches for a Pod with no hostname")
	}
	pod = newStatefulSetPod(set, 1)
	pod.Spec.Subdomain = ""
	if identityMatches(set, pod) {
		t.Error("identity matches for a Pod with no subdomain")
	}
}

func TestStorageMatches(t *testing.T) {
	set := newStatefulSet(3)
	pod := newStatefulSetPod(set, 1)
	if !storageMatches(set, pod) {
		t.Error("Newly created Pod has a invalid stroage")
	}
	pod.Spec.Volumes = nil
	if storageMatches(set, pod) {
		t.Error("Pod with invalid Volumes has valid storage")
	}
	pod = newStatefulSetPod(set, 1)
	for i := range pod.Spec.Volumes {
		pod.Spec.Volumes[i].PersistentVolumeClaim = nil
	}
	if storageMatches(set, pod) {
		t.Error("Pod with invalid Volumes claim valid storage")
	}
	pod = newStatefulSetPod(set, 1)
	for i := range pod.Spec.Volumes {
		if pod.Spec.Volumes[i].PersistentVolumeClaim != nil {
			pod.Spec.Volumes[i].PersistentVolumeClaim.ClaimName = "foo"
		}
	}
	if storageMatches(set, pod) {
		t.Error("Pod with invalid Volumes claim valid storage")
	}
	pod = newStatefulSetPod(set, 1)
	pod.Name = "bar"
	if storageMatches(set, pod) {
		t.Error("Pod with invalid ordinal has valid storage")
	}
}

func TestUpdateIdentity(t *testing.T) {
	set := newStatefulSet(3)
	pod := newStatefulSetPod(set, 1)
	if !identityMatches(set, pod) {
		t.Error("Newly created Pod has a bad identity")
	}
	pod.Namespace = ""
	if identityMatches(set, pod) {
		t.Error("identity matches for a Pod with the wrong namespace")
	}
	updateIdentity(set, pod)
	if !identityMatches(set, pod) {
		t.Error("updateIdentity failed to update the Pods namespace")
	}
	pod = newStatefulSetPod(set, 1)
	pod.Spec.Hostname = ""
	if identityMatches(set, pod) {
		t.Error("identity matches for a Pod with no hostname")
	}
	updateIdentity(set, pod)
	if !identityMatches(set, pod) {
		t.Error("updateIdentity failed to update the Pod's hostname")
	}
	pod = newStatefulSetPod(set, 1)
	pod.Spec.Subdomain = ""
	if identityMatches(set, pod) {
		t.Error("identity matches for a Pod with no subdomain")
	}
	updateIdentity(set, pod)
	if !identityMatches(set, pod) {
		t.Error("updateIdentity failed to update the Pod's subdomain")
	}
}

func TestUpdateStorage(t *testing.T) {
	set := newStatefulSet(3)
	pod := newStatefulSetPod(set, 1)
	if !storageMatches(set, pod) {
		t.Error("Newly created Pod has a invalid stroage")
	}
	pod.Spec.Volumes = nil
	if storageMatches(set, pod) {
		t.Error("Pod with invalid Volumes has valid storage")
	}
	updateStorage(set, pod)
	if !storageMatches(set, pod) {
		t.Error("updateStorage failed to recreate volumes")
	}
	pod = newStatefulSetPod(set, 1)
	for i := range pod.Spec.Volumes {
		pod.Spec.Volumes[i].PersistentVolumeClaim = nil
	}
	if storageMatches(set, pod) {
		t.Error("Pod with invalid Volumes claim valid storage")
	}
	updateStorage(set, pod)
	if !storageMatches(set, pod) {
		t.Error("updateStorage failed to recreate volume claims")
	}
	pod = newStatefulSetPod(set, 1)
	for i := range pod.Spec.Volumes {
		if pod.Spec.Volumes[i].PersistentVolumeClaim != nil {
			pod.Spec.Volumes[i].PersistentVolumeClaim.ClaimName = "foo"
		}
	}
	if storageMatches(set, pod) {
		t.Error("Pod with invalid Volumes claim valid storage")
	}
	updateStorage(set, pod)
	if !storageMatches(set, pod) {
		t.Error("updateStorage failed to recreate volume claim names")
	}
}

func TestIsRunningAndReady(t *testing.T) {
	set := newStatefulSet(3)
	pod := newStatefulSetPod(set, 1)
	if isRunningAndReady(pod) {
		t.Error("isRunningAndReady does not respect Pod phase")
	}
	pod.Status.Phase = v1.PodRunning
	if isRunningAndReady(pod) {
		t.Error("isRunningAndReady does not respect Pod condition")
	}
	condition := v1.PodCondition{Type: v1.PodReady, Status: v1.ConditionTrue}
	podutil.UpdatePodCondition(&pod.Status, &condition)
	if !isRunningAndReady(pod) {
		t.Error("Pod should be running and ready")
	}
	pod.Annotations[apps.StatefulSetInitAnnotation] = "true"
	if !isRunningAndReady(pod) {
		t.Error("isRunningAndReady does not respected init annotation set to true")
	}
	pod.Annotations[apps.StatefulSetInitAnnotation] = "false"
	if isRunningAndReady(pod) {
		t.Error("isRunningAndReady does not respected init annotation set to false")
	}
	pod.Annotations[apps.StatefulSetInitAnnotation] = "blah"
	if !isRunningAndReady(pod) {
		t.Error("isRunningAndReady does not erroneous init annotation")
	}
}

func TestAscendingOrdinal(t *testing.T) {
	set := newStatefulSet(10)
	for i := 0; i < 10; i++ {

	}
	pods := make([]*v1.Pod, 10)
	perm := rand.Perm(10)
	for i, v := range perm {
		pods[i] = newStatefulSetPod(set, v)
	}
	sort.Sort(ascendingOrdinal(pods))
	if !sort.IsSorted(ascendingOrdinal(pods)) {
		t.Error("ascendingOrdinal fails to sort Pods")
	}
}

func TestOverlappingStatefulSets(t *testing.T) {
	sets := make([]*apps.StatefulSet, 10)
	perm := rand.Perm(10)
	for i, v := range perm {
		sets[i] = newStatefulSet(10)
		sets[i].CreationTimestamp = metav1.NewTime(sets[i].CreationTimestamp.Add(time.Duration(v) * time.Second))
	}
	sort.Sort(overlappingStatefulSets(sets))
	if !sort.IsSorted(overlappingStatefulSets(sets)) {
		t.Error("ascendingOrdinal fails to sort Pods")
	}
	for i, v := range perm {
		sets[i] = newStatefulSet(10)
		sets[i].Name = strconv.FormatInt(int64(v), 10)
	}
	sort.Sort(overlappingStatefulSets(sets))
	if !sort.IsSorted(overlappingStatefulSets(sets)) {
		t.Error("ascendingOrdinal fails to sort Pods")
	}
}

func TestNewPodControllerRef(t *testing.T) {
	set := newStatefulSet(1)
	pod := newStatefulSetPod(set, 0)
	controllerRef := controller.GetControllerOf(pod)
	if controllerRef == nil {
		t.Fatalf("No ControllerRef found on new pod")
	}
	if got, want := controllerRef.APIVersion, apps.SchemeGroupVersion.String(); got != want {
		t.Errorf("controllerRef.APIVersion = %q, want %q", got, want)
	}
	if got, want := controllerRef.Kind, "StatefulSet"; got != want {
		t.Errorf("controllerRef.Kind = %q, want %q", got, want)
	}
	if got, want := controllerRef.Name, set.Name; got != want {
		t.Errorf("controllerRef.Name = %q, want %q", got, want)
	}
	if got, want := controllerRef.UID, set.UID; got != want {
		t.Errorf("controllerRef.UID = %q, want %q", got, want)
	}
	if got, want := *controllerRef.Controller, true; got != want {
		t.Errorf("controllerRef.Controller = %v, want %v", got, want)
	}
}

func TestCreateApplyRevision(t *testing.T) {
	set := newStatefulSet(1)
	revision, err := newRevision(set, 1)
	if err != nil {
		t.Fatal(err)
	}
	set.Spec.Template.Spec.Containers[0].Name = "foo"
	restoredSet, err := applyRevision(set, revision)
	if err != nil {
		t.Fatal(err)
	}
	restoredRevision, err := newRevision(restoredSet, 2)
	if err != nil {
		t.Fatal(err)
	}
	if !history.EqualRevision(revision, restoredRevision) {
		t.Errorf("wanted %v got %v", string(revision.Data.Raw), string(restoredRevision.Data.Raw))
	}
}

func newPVC(name string) v1.PersistentVolumeClaim {
	return v1.PersistentVolumeClaim{
		ObjectMeta: metav1.ObjectMeta{
			Name: name,
		},
		Spec: v1.PersistentVolumeClaimSpec{
			Resources: v1.ResourceRequirements{
				Requests: v1.ResourceList{
					v1.ResourceStorage: *resource.NewQuantity(1, resource.BinarySI),
				},
			},
		},
	}
}

func newStatefulSetWithVolumes(replicas int, name string, petMounts []v1.VolumeMount, podMounts []v1.VolumeMount) *apps.StatefulSet {
	mounts := append(petMounts, podMounts...)
	claims := []v1.PersistentVolumeClaim{}
	for _, m := range petMounts {
		claims = append(claims, newPVC(m.Name))
	}

	vols := []v1.Volume{}
	for _, m := range podMounts {
		vols = append(vols, v1.Volume{
			Name: m.Name,
			VolumeSource: v1.VolumeSource{
				HostPath: &v1.HostPathVolumeSource{
					Path: fmt.Sprintf("/tmp/%v", m.Name),
				},
			},
		})
	}

	template := v1.PodTemplateSpec{
		Spec: v1.PodSpec{
			Containers: []v1.Container{
				{
					Name:         "nginx",
					Image:        "nginx",
					VolumeMounts: mounts,
				},
			},
			Volumes: vols,
		},
	}

	template.Labels = map[string]string{"foo": "bar"}

	return &apps.StatefulSet{
		TypeMeta: metav1.TypeMeta{
			Kind:       "StatefulSet",
			APIVersion: "apps/v1beta1",
		},
		ObjectMeta: metav1.ObjectMeta{
			Name:      name,
			Namespace: v1.NamespaceDefault,
			UID:       types.UID("test"),
		},
		Spec: apps.StatefulSetSpec{
			Selector: &metav1.LabelSelector{
				MatchLabels: map[string]string{"foo": "bar"},
			},
			Replicas:             func() *int32 { i := int32(replicas); return &i }(),
			Template:             template,
			VolumeClaimTemplates: claims,
			ServiceName:          "governingsvc",
			UpdateStrategy:       apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
			RevisionHistoryLimit: func() *int32 {
				limit := int32(2)
				return &limit
			}(),
		},
	}
}

func newStatefulSet(replicas int) *apps.StatefulSet {
	petMounts := []v1.VolumeMount{
		{Name: "datadir", MountPath: "/tmp/zookeeper"},
	}
	podMounts := []v1.VolumeMount{
		{Name: "home", MountPath: "/home"},
	}
	return newStatefulSetWithVolumes(replicas, "foo", petMounts, podMounts)
}
<commit_msg>delete the for loops that done nothing<commit_after>/*
Copyright 2016 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 statefulset

import (
	"fmt"
	"math/rand"
	"sort"
	"strconv"
	"testing"
	"time"

	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"

	"k8s.io/kubernetes/pkg/api/v1"
	podutil "k8s.io/kubernetes/pkg/api/v1/pod"
	apps "k8s.io/kubernetes/pkg/apis/apps/v1beta1"
	"k8s.io/kubernetes/pkg/controller"
	"k8s.io/kubernetes/pkg/controller/history"
)

func TestGetParentNameAndOrdinal(t *testing.T) {
	set := newStatefulSet(3)
	pod := newStatefulSetPod(set, 1)
	if parent, ordinal := getParentNameAndOrdinal(pod); parent != set.Name {
		t.Errorf("Extracted the wrong parent name expected %s found %s", set.Name, parent)
	} else if ordinal != 1 {
		t.Errorf("Extracted the wrong ordinal expected %d found %d", 1, ordinal)
	}
	pod.Name = "1-bar"
	if parent, ordinal := getParentNameAndOrdinal(pod); parent != "" {
		t.Error("Expected empty string for non-member Pod parent")
	} else if ordinal != -1 {
		t.Error("Expected -1 for non member Pod ordinal")
	}
}

func TestIsMemberOf(t *testing.T) {
	set := newStatefulSet(3)
	set2 := newStatefulSet(3)
	set2.Name = "foo2"
	pod := newStatefulSetPod(set, 1)
	if !isMemberOf(set, pod) {
		t.Error("isMemberOf retruned false negative")
	}
	if isMemberOf(set2, pod) {
		t.Error("isMemberOf returned false positive")
	}
}

func TestIdentityMatches(t *testing.T) {
	set := newStatefulSet(3)
	pod := newStatefulSetPod(set, 1)
	if !identityMatches(set, pod) {
		t.Error("Newly created Pod has a bad identity")
	}
	pod.Name = "foo"
	if identityMatches(set, pod) {
		t.Error("identity matches for a Pod with the wrong name")
	}
	pod = newStatefulSetPod(set, 1)
	pod.Namespace = ""
	if identityMatches(set, pod) {
		t.Error("identity matches for a Pod with the wrong namespace")
	}
	pod = newStatefulSetPod(set, 1)
	pod.Spec.Hostname = ""
	if identityMatches(set, pod) {
		t.Error("identity matches for a Pod with no hostname")
	}
	pod = newStatefulSetPod(set, 1)
	pod.Spec.Subdomain = ""
	if identityMatches(set, pod) {
		t.Error("identity matches for a Pod with no subdomain")
	}
}

func TestStorageMatches(t *testing.T) {
	set := newStatefulSet(3)
	pod := newStatefulSetPod(set, 1)
	if !storageMatches(set, pod) {
		t.Error("Newly created Pod has a invalid stroage")
	}
	pod.Spec.Volumes = nil
	if storageMatches(set, pod) {
		t.Error("Pod with invalid Volumes has valid storage")
	}
	pod = newStatefulSetPod(set, 1)
	for i := range pod.Spec.Volumes {
		pod.Spec.Volumes[i].PersistentVolumeClaim = nil
	}
	if storageMatches(set, pod) {
		t.Error("Pod with invalid Volumes claim valid storage")
	}
	pod = newStatefulSetPod(set, 1)
	for i := range pod.Spec.Volumes {
		if pod.Spec.Volumes[i].PersistentVolumeClaim != nil {
			pod.Spec.Volumes[i].PersistentVolumeClaim.ClaimName = "foo"
		}
	}
	if storageMatches(set, pod) {
		t.Error("Pod with invalid Volumes claim valid storage")
	}
	pod = newStatefulSetPod(set, 1)
	pod.Name = "bar"
	if storageMatches(set, pod) {
		t.Error("Pod with invalid ordinal has valid storage")
	}
}

func TestUpdateIdentity(t *testing.T) {
	set := newStatefulSet(3)
	pod := newStatefulSetPod(set, 1)
	if !identityMatches(set, pod) {
		t.Error("Newly created Pod has a bad identity")
	}
	pod.Namespace = ""
	if identityMatches(set, pod) {
		t.Error("identity matches for a Pod with the wrong namespace")
	}
	updateIdentity(set, pod)
	if !identityMatches(set, pod) {
		t.Error("updateIdentity failed to update the Pods namespace")
	}
	pod = newStatefulSetPod(set, 1)
	pod.Spec.Hostname = ""
	if identityMatches(set, pod) {
		t.Error("identity matches for a Pod with no hostname")
	}
	updateIdentity(set, pod)
	if !identityMatches(set, pod) {
		t.Error("updateIdentity failed to update the Pod's hostname")
	}
	pod = newStatefulSetPod(set, 1)
	pod.Spec.Subdomain = ""
	if identityMatches(set, pod) {
		t.Error("identity matches for a Pod with no subdomain")
	}
	updateIdentity(set, pod)
	if !identityMatches(set, pod) {
		t.Error("updateIdentity failed to update the Pod's subdomain")
	}
}

func TestUpdateStorage(t *testing.T) {
	set := newStatefulSet(3)
	pod := newStatefulSetPod(set, 1)
	if !storageMatches(set, pod) {
		t.Error("Newly created Pod has a invalid stroage")
	}
	pod.Spec.Volumes = nil
	if storageMatches(set, pod) {
		t.Error("Pod with invalid Volumes has valid storage")
	}
	updateStorage(set, pod)
	if !storageMatches(set, pod) {
		t.Error("updateStorage failed to recreate volumes")
	}
	pod = newStatefulSetPod(set, 1)
	for i := range pod.Spec.Volumes {
		pod.Spec.Volumes[i].PersistentVolumeClaim = nil
	}
	if storageMatches(set, pod) {
		t.Error("Pod with invalid Volumes claim valid storage")
	}
	updateStorage(set, pod)
	if !storageMatches(set, pod) {
		t.Error("updateStorage failed to recreate volume claims")
	}
	pod = newStatefulSetPod(set, 1)
	for i := range pod.Spec.Volumes {
		if pod.Spec.Volumes[i].PersistentVolumeClaim != nil {
			pod.Spec.Volumes[i].PersistentVolumeClaim.ClaimName = "foo"
		}
	}
	if storageMatches(set, pod) {
		t.Error("Pod with invalid Volumes claim valid storage")
	}
	updateStorage(set, pod)
	if !storageMatches(set, pod) {
		t.Error("updateStorage failed to recreate volume claim names")
	}
}

func TestIsRunningAndReady(t *testing.T) {
	set := newStatefulSet(3)
	pod := newStatefulSetPod(set, 1)
	if isRunningAndReady(pod) {
		t.Error("isRunningAndReady does not respect Pod phase")
	}
	pod.Status.Phase = v1.PodRunning
	if isRunningAndReady(pod) {
		t.Error("isRunningAndReady does not respect Pod condition")
	}
	condition := v1.PodCondition{Type: v1.PodReady, Status: v1.ConditionTrue}
	podutil.UpdatePodCondition(&pod.Status, &condition)
	if !isRunningAndReady(pod) {
		t.Error("Pod should be running and ready")
	}
	pod.Annotations[apps.StatefulSetInitAnnotation] = "true"
	if !isRunningAndReady(pod) {
		t.Error("isRunningAndReady does not respected init annotation set to true")
	}
	pod.Annotations[apps.StatefulSetInitAnnotation] = "false"
	if isRunningAndReady(pod) {
		t.Error("isRunningAndReady does not respected init annotation set to false")
	}
	pod.Annotations[apps.StatefulSetInitAnnotation] = "blah"
	if !isRunningAndReady(pod) {
		t.Error("isRunningAndReady does not erroneous init annotation")
	}
}

func TestAscendingOrdinal(t *testing.T) {
	set := newStatefulSet(10)
	pods := make([]*v1.Pod, 10)
	perm := rand.Perm(10)
	for i, v := range perm {
		pods[i] = newStatefulSetPod(set, v)
	}
	sort.Sort(ascendingOrdinal(pods))
	if !sort.IsSorted(ascendingOrdinal(pods)) {
		t.Error("ascendingOrdinal fails to sort Pods")
	}
}

func TestOverlappingStatefulSets(t *testing.T) {
	sets := make([]*apps.StatefulSet, 10)
	perm := rand.Perm(10)
	for i, v := range perm {
		sets[i] = newStatefulSet(10)
		sets[i].CreationTimestamp = metav1.NewTime(sets[i].CreationTimestamp.Add(time.Duration(v) * time.Second))
	}
	sort.Sort(overlappingStatefulSets(sets))
	if !sort.IsSorted(overlappingStatefulSets(sets)) {
		t.Error("ascendingOrdinal fails to sort Pods")
	}
	for i, v := range perm {
		sets[i] = newStatefulSet(10)
		sets[i].Name = strconv.FormatInt(int64(v), 10)
	}
	sort.Sort(overlappingStatefulSets(sets))
	if !sort.IsSorted(overlappingStatefulSets(sets)) {
		t.Error("ascendingOrdinal fails to sort Pods")
	}
}

func TestNewPodControllerRef(t *testing.T) {
	set := newStatefulSet(1)
	pod := newStatefulSetPod(set, 0)
	controllerRef := controller.GetControllerOf(pod)
	if controllerRef == nil {
		t.Fatalf("No ControllerRef found on new pod")
	}
	if got, want := controllerRef.APIVersion, apps.SchemeGroupVersion.String(); got != want {
		t.Errorf("controllerRef.APIVersion = %q, want %q", got, want)
	}
	if got, want := controllerRef.Kind, "StatefulSet"; got != want {
		t.Errorf("controllerRef.Kind = %q, want %q", got, want)
	}
	if got, want := controllerRef.Name, set.Name; got != want {
		t.Errorf("controllerRef.Name = %q, want %q", got, want)
	}
	if got, want := controllerRef.UID, set.UID; got != want {
		t.Errorf("controllerRef.UID = %q, want %q", got, want)
	}
	if got, want := *controllerRef.Controller, true; got != want {
		t.Errorf("controllerRef.Controller = %v, want %v", got, want)
	}
}

func TestCreateApplyRevision(t *testing.T) {
	set := newStatefulSet(1)
	revision, err := newRevision(set, 1)
	if err != nil {
		t.Fatal(err)
	}
	set.Spec.Template.Spec.Containers[0].Name = "foo"
	restoredSet, err := applyRevision(set, revision)
	if err != nil {
		t.Fatal(err)
	}
	restoredRevision, err := newRevision(restoredSet, 2)
	if err != nil {
		t.Fatal(err)
	}
	if !history.EqualRevision(revision, restoredRevision) {
		t.Errorf("wanted %v got %v", string(revision.Data.Raw), string(restoredRevision.Data.Raw))
	}
}

func newPVC(name string) v1.PersistentVolumeClaim {
	return v1.PersistentVolumeClaim{
		ObjectMeta: metav1.ObjectMeta{
			Name: name,
		},
		Spec: v1.PersistentVolumeClaimSpec{
			Resources: v1.ResourceRequirements{
				Requests: v1.ResourceList{
					v1.ResourceStorage: *resource.NewQuantity(1, resource.BinarySI),
				},
			},
		},
	}
}

func newStatefulSetWithVolumes(replicas int, name string, petMounts []v1.VolumeMount, podMounts []v1.VolumeMount) *apps.StatefulSet {
	mounts := append(petMounts, podMounts...)
	claims := []v1.PersistentVolumeClaim{}
	for _, m := range petMounts {
		claims = append(claims, newPVC(m.Name))
	}

	vols := []v1.Volume{}
	for _, m := range podMounts {
		vols = append(vols, v1.Volume{
			Name: m.Name,
			VolumeSource: v1.VolumeSource{
				HostPath: &v1.HostPathVolumeSource{
					Path: fmt.Sprintf("/tmp/%v", m.Name),
				},
			},
		})
	}

	template := v1.PodTemplateSpec{
		Spec: v1.PodSpec{
			Containers: []v1.Container{
				{
					Name:         "nginx",
					Image:        "nginx",
					VolumeMounts: mounts,
				},
			},
			Volumes: vols,
		},
	}

	template.Labels = map[string]string{"foo": "bar"}

	return &apps.StatefulSet{
		TypeMeta: metav1.TypeMeta{
			Kind:       "StatefulSet",
			APIVersion: "apps/v1beta1",
		},
		ObjectMeta: metav1.ObjectMeta{
			Name:      name,
			Namespace: v1.NamespaceDefault,
			UID:       types.UID("test"),
		},
		Spec: apps.StatefulSetSpec{
			Selector: &metav1.LabelSelector{
				MatchLabels: map[string]string{"foo": "bar"},
			},
			Replicas:             func() *int32 { i := int32(replicas); return &i }(),
			Template:             template,
			VolumeClaimTemplates: claims,
			ServiceName:          "governingsvc",
			UpdateStrategy:       apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
			RevisionHistoryLimit: func() *int32 {
				limit := int32(2)
				return &limit
			}(),
		},
	}
}

func newStatefulSet(replicas int) *apps.StatefulSet {
	petMounts := []v1.VolumeMount{
		{Name: "datadir", MountPath: "/tmp/zookeeper"},
	}
	podMounts := []v1.VolumeMount{
		{Name: "home", MountPath: "/home"},
	}
	return newStatefulSetWithVolumes(replicas, "foo", petMounts, podMounts)
}
<|endoftext|>


<commit_before>package core

import (
	rpc "github.com/tendermint/go-rpc/server"
	"github.com/tendermint/go-rpc/types"
	ctypes "github.com/tendermint/tendermint/rpc/core/types"
)

// TODO: eliminate redundancy between here and reading code from core/
var Routes = map[string]*rpc.RPCFunc{
	"subscribe":            rpc.NewWSRPCFunc(SubscribeResult, "event"),
	"unsubscribe":          rpc.NewWSRPCFunc(UnsubscribeResult, "event"),
	"status":               rpc.NewRPCFunc(StatusResult, ""),
	"net_info":             rpc.NewRPCFunc(NetInfoResult, ""),
	"dial_seeds":           rpc.NewRPCFunc(DialSeedsResult, "seeds"),
	"blockchain":           rpc.NewRPCFunc(BlockchainInfoResult, "minHeight,maxHeight"),
	"genesis":              rpc.NewRPCFunc(GenesisResult, ""),
	"block":                rpc.NewRPCFunc(BlockResult, "height"),
	"validators":           rpc.NewRPCFunc(ValidatorsResult, ""),
	"dump_consensus_state": rpc.NewRPCFunc(DumpConsensusStateResult, ""),
	"broadcast_tx_sync":    rpc.NewRPCFunc(BroadcastTxSyncResult, "tx"),
	"broadcast_tx_asyn":    rpc.NewRPCFunc(BroadcastTxAsyncResult, "tx"),
	"unconfirmed_txs":      rpc.NewRPCFunc(UnconfirmedTxsResult, ""),
	// subscribe/unsubscribe are reserved for websocket events.
}

func SubscribeResult(wsCtx rpctypes.WSRPCContext, event string) (ctypes.TMResult, error) {
	if r, err := Subscribe(wsCtx, event); err != nil {
		return nil, err
	} else {
		return r, nil
	}
}

func UnsubscribeResult(wsCtx rpctypes.WSRPCContext, event string) (ctypes.TMResult, error) {
	if r, err := Unsubscribe(wsCtx, event); err != nil {
		return nil, err
	} else {
		return r, nil
	}
}

func StatusResult() (ctypes.TMResult, error) {
	if r, err := Status(); err != nil {
		return nil, err
	} else {
		return r, nil
	}
}

func NetInfoResult() (ctypes.TMResult, error) {
	if r, err := NetInfo(); err != nil {
		return nil, err
	} else {
		return r, nil
	}
}

func DialSeedsResult(seeds []string) (ctypes.TMResult, error) {
	if r, err := DialSeeds(seeds); err != nil {
		return nil, err
	} else {
		return r, nil
	}
}

func BlockchainInfoResult(min, max int) (ctypes.TMResult, error) {
	if r, err := BlockchainInfo(min, max); err != nil {
		return nil, err
	} else {
		return r, nil
	}
}

func GenesisResult() (ctypes.TMResult, error) {
	if r, err := Genesis(); err != nil {
		return nil, err
	} else {
		return r, nil
	}
}

func BlockResult(height int) (ctypes.TMResult, error) {
	if r, err := Block(height); err != nil {
		return nil, err
	} else {
		return r, nil
	}
}

func ValidatorsResult() (ctypes.TMResult, error) {
	if r, err := Validators(); err != nil {
		return nil, err
	} else {
		return r, nil
	}
}

func DumpConsensusStateResult() (ctypes.TMResult, error) {
	if r, err := DumpConsensusState(); err != nil {
		return nil, err
	} else {
		return r, nil
	}
}

func UnconfirmedTxsResult() (ctypes.TMResult, error) {
	if r, err := UnconfirmedTxs(); err != nil {
		return nil, err
	} else {
		return r, nil
	}
}

func BroadcastTxSyncResult(tx []byte) (ctypes.TMResult, error) {
	if r, err := BroadcastTxSync(tx); err != nil {
		return nil, err
	} else {
		return r, nil
	}
}

func BroadcastTxAsyncResult(tx []byte) (ctypes.TMResult, error) {
	if r, err := BroadcastTxAsync(tx); err != nil {
		return nil, err
	} else {
		return r, nil
	}
}
<commit_msg>Fix typo, broadcast_tx_asyn(c)<commit_after>package core

import (
	rpc "github.com/tendermint/go-rpc/server"
	"github.com/tendermint/go-rpc/types"
	ctypes "github.com/tendermint/tendermint/rpc/core/types"
)

// TODO: eliminate redundancy between here and reading code from core/
var Routes = map[string]*rpc.RPCFunc{
	"subscribe":            rpc.NewWSRPCFunc(SubscribeResult, "event"),
	"unsubscribe":          rpc.NewWSRPCFunc(UnsubscribeResult, "event"),
	"status":               rpc.NewRPCFunc(StatusResult, ""),
	"net_info":             rpc.NewRPCFunc(NetInfoResult, ""),
	"dial_seeds":           rpc.NewRPCFunc(DialSeedsResult, "seeds"),
	"blockchain":           rpc.NewRPCFunc(BlockchainInfoResult, "minHeight,maxHeight"),
	"genesis":              rpc.NewRPCFunc(GenesisResult, ""),
	"block":                rpc.NewRPCFunc(BlockResult, "height"),
	"validators":           rpc.NewRPCFunc(ValidatorsResult, ""),
	"dump_consensus_state": rpc.NewRPCFunc(DumpConsensusStateResult, ""),
	"broadcast_tx_sync":    rpc.NewRPCFunc(BroadcastTxSyncResult, "tx"),
	"broadcast_tx_async":   rpc.NewRPCFunc(BroadcastTxAsyncResult, "tx"),
	"unconfirmed_txs":      rpc.NewRPCFunc(UnconfirmedTxsResult, ""),
	// subscribe/unsubscribe are reserved for websocket events.
}

func SubscribeResult(wsCtx rpctypes.WSRPCContext, event string) (ctypes.TMResult, error) {
	if r, err := Subscribe(wsCtx, event); err != nil {
		return nil, err
	} else {
		return r, nil
	}
}

func UnsubscribeResult(wsCtx rpctypes.WSRPCContext, event string) (ctypes.TMResult, error) {
	if r, err := Unsubscribe(wsCtx, event); err != nil {
		return nil, err
	} else {
		return r, nil
	}
}

func StatusResult() (ctypes.TMResult, error) {
	if r, err := Status(); err != nil {
		return nil, err
	} else {
		return r, nil
	}
}

func NetInfoResult() (ctypes.TMResult, error) {
	if r, err := NetInfo(); err != nil {
		return nil, err
	} else {
		return r, nil
	}
}

func DialSeedsResult(seeds []string) (ctypes.TMResult, error) {
	if r, err := DialSeeds(seeds); err != nil {
		return nil, err
	} else {
		return r, nil
	}
}

func BlockchainInfoResult(min, max int) (ctypes.TMResult, error) {
	if r, err := BlockchainInfo(min, max); err != nil {
		return nil, err
	} else {
		return r, nil
	}
}

func GenesisResult() (ctypes.TMResult, error) {
	if r, err := Genesis(); err != nil {
		return nil, err
	} else {
		return r, nil
	}
}

func BlockResult(height int) (ctypes.TMResult, error) {
	if r, err := Block(height); err != nil {
		return nil, err
	} else {
		return r, nil
	}
}

func ValidatorsResult() (ctypes.TMResult, error) {
	if r, err := Validators(); err != nil {
		return nil, err
	} else {
		return r, nil
	}
}

func DumpConsensusStateResult() (ctypes.TMResult, error) {
	if r, err := DumpConsensusState(); err != nil {
		return nil, err
	} else {
		return r, nil
	}
}

func UnconfirmedTxsResult() (ctypes.TMResult, error) {
	if r, err := UnconfirmedTxs(); err != nil {
		return nil, err
	} else {
		return r, nil
	}
}

func BroadcastTxSyncResult(tx []byte) (ctypes.TMResult, error) {
	if r, err := BroadcastTxSync(tx); err != nil {
		return nil, err
	} else {
		return r, nil
	}
}

func BroadcastTxAsyncResult(tx []byte) (ctypes.TMResult, error) {
	if r, err := BroadcastTxAsync(tx); err != nil {
		return nil, err
	} else {
		return r, nil
	}
}
<|endoftext|>


<commit_before>package model

import (
	"context"
	"fmt"
	"time"

	"github.com/jinzhu/gorm"
	api_v1 "github.com/oinume/lekcije/proto-gen/go/proto/api/v1"
	"github.com/oinume/lekcije/server/errors"
	"go.opencensus.io/trace"
)

type NotificationTimeSpan struct {
	UserID    uint32
	Number    uint8
	FromTime  string
	ToTime    string
	CreatedAt time.Time
	from      time.Time
	to        time.Time
}

func (*NotificationTimeSpan) TableName() string {
	return "notification_time_span"
}

func (s *NotificationTimeSpan) ParseTime() error {
	f, err := time.Parse("15:04:05", s.FromTime)
	if err != nil {
		return err
	}
	s.from = f

	t, err := time.Parse("15:04:05", s.ToTime)
	if err != nil {
		return err
	}
	s.to = t
	return nil
}

func (s *NotificationTimeSpan) Within(t time.Time) bool {
	if err := s.ParseTime(); err != nil {
		return false
	}
	if s.from.Before(s.to) {
		if (t.After(s.from) || t.Equal(s.from)) && (t.Before(s.to) || t.Equal(s.to)) {
			return true
		}
	} else {
		// Add 24 hour to s.to if from > to. from=04:00, to=03:00 -> from=04:00, to=27:00
		toTime := s.to
		toTime = toTime.Add(time.Hour * 24)
		if (t.After(s.from) || t.Equal(s.from)) && (t.Before(toTime) || t.Equal(toTime)) {
			return true
		}
	}
	return false
}

type NotificationTimeSpanList []*NotificationTimeSpan

func (l NotificationTimeSpanList) Within(t time.Time) bool {
	target := t
	for _, timeSpan := range l {
		if timeSpan.Within(target) {
			return true
		}
	}
	return false
}

type NotificationTimeSpanService struct {
	db *gorm.DB
}

func NewNotificationTimeSpanService(db *gorm.DB) *NotificationTimeSpanService {
	return &NotificationTimeSpanService{db}
}

func (s *NotificationTimeSpanService) NewNotificationTimeSpansFromPB(
	userID uint32, args []*api_v1.NotificationTimeSpan,
) []*NotificationTimeSpan {
	values := make([]*NotificationTimeSpan, 0, len(args))
	for i, v := range args {
		fromTime := fmt.Sprintf("%v:%v", v.FromHour, v.FromMinute)
		toTime := fmt.Sprintf("%v:%v", v.ToHour, v.ToMinute)
		values = append(values, &NotificationTimeSpan{
			UserID:   userID,
			Number:   uint8(i + 1),
			FromTime: fromTime,
			ToTime:   toTime,
		})
	}
	return values
}

func (s *NotificationTimeSpanService) NewNotificationTimeSpansPB(args []*NotificationTimeSpan) ([]*api_v1.NotificationTimeSpan, error) {
	values := make([]*api_v1.NotificationTimeSpan, 0, len(args))
	for _, v := range args {
		fromTime, err := time.Parse("15:04:05", v.FromTime)
		if err != nil {
			return nil, errors.NewInternalError(
				errors.WithError(err),
				errors.WithMessagef("Invalid time format: FromTime=%v", v.FromTime),
			)
		}
		toTime, err := time.Parse("15:04:05", v.ToTime)
		if err != nil {
			return nil, errors.NewInternalError(
				errors.WithError(err),
				errors.WithMessagef("Invalid time format: ToTime=%v", v.ToTime),
			)
		}
		values = append(values, &api_v1.NotificationTimeSpan{
			FromHour:   int32(fromTime.Hour()),
			FromMinute: int32(fromTime.Minute()),
			ToHour:     int32(toTime.Hour()),
			ToMinute:   int32(toTime.Minute()),
		})
	}
	return values, nil
}

func (s *NotificationTimeSpanService) FindByUserID(ctx context.Context, userID uint32) ([]*NotificationTimeSpan, error) {
	_, span := trace.StartSpan(ctx, "NotificationTimeSpanService.FindByUserID")
	defer span.End()
	sql := fmt.Sprintf(`SELECT * FROM %s WHERE user_id = ?`, (&NotificationTimeSpan{}).TableName())
	timeSpans := make([]*NotificationTimeSpan, 0, 10)
	if err := s.db.Raw(sql, userID).Scan(&timeSpans).Error; err != nil {
		return nil, errors.NewInternalError(
			errors.WithError(err),
			errors.WithMessage("FindByUserID select failed"),
			errors.WithResource(errors.NewResource((&NotificationTimeSpan{}).TableName(), "userID", userID)),
		)
	}
	return timeSpans, nil
}

func (s *NotificationTimeSpanService) UpdateAll(userID uint32, timeSpans []*NotificationTimeSpan) error {
	for _, timeSpan := range timeSpans {
		if userID != timeSpan.UserID {
			return errors.NewInvalidArgumentError(
				errors.WithMessage("Given userID and userID of timeSpans must be same"),
			)
		}
	}

	tx := s.db.Begin()
	tableName := (&NotificationTimeSpan{}).TableName()
	sql := fmt.Sprintf(`DELETE FROM %s WHERE user_id = ?`, tableName)
	if err := tx.Exec(sql, userID).Error; err != nil {
		return errors.NewInternalError(
			errors.WithError(err),
			errors.WithMessage("UpdateAll delete failed"),
			errors.WithResource(errors.NewResource(tableName, "userID", userID)),
		)
	}

	for _, timeSpan := range timeSpans {
		if err := tx.Create(timeSpan).Error; err != nil {
			return errors.NewInternalError(
				errors.WithError(err),
				errors.WithMessage("UpdateAll insert failed"),
				errors.WithResource(errors.NewResource(tableName, "userID", userID)),
			)
		}
	}

	if err := tx.Commit().Error; err != nil {
		return errors.NewInternalError(
			errors.WithError(err),
			errors.WithMessage("UpdateAll commit failed"),
			errors.WithResource(errors.NewResource(tableName, "userID", userID)),
		)
	}

	return nil
}
<commit_msg>Add annotation for span<commit_after>package model

import (
	"context"
	"fmt"
	"time"

	"github.com/jinzhu/gorm"
	api_v1 "github.com/oinume/lekcije/proto-gen/go/proto/api/v1"
	"github.com/oinume/lekcije/server/errors"
	"go.opencensus.io/trace"
)

type NotificationTimeSpan struct {
	UserID    uint32
	Number    uint8
	FromTime  string
	ToTime    string
	CreatedAt time.Time
	from      time.Time
	to        time.Time
}

func (*NotificationTimeSpan) TableName() string {
	return "notification_time_span"
}

func (s *NotificationTimeSpan) ParseTime() error {
	f, err := time.Parse("15:04:05", s.FromTime)
	if err != nil {
		return err
	}
	s.from = f

	t, err := time.Parse("15:04:05", s.ToTime)
	if err != nil {
		return err
	}
	s.to = t
	return nil
}

func (s *NotificationTimeSpan) Within(t time.Time) bool {
	if err := s.ParseTime(); err != nil {
		return false
	}
	if s.from.Before(s.to) {
		if (t.After(s.from) || t.Equal(s.from)) && (t.Before(s.to) || t.Equal(s.to)) {
			return true
		}
	} else {
		// Add 24 hour to s.to if from > to. from=04:00, to=03:00 -> from=04:00, to=27:00
		toTime := s.to
		toTime = toTime.Add(time.Hour * 24)
		if (t.After(s.from) || t.Equal(s.from)) && (t.Before(toTime) || t.Equal(toTime)) {
			return true
		}
	}
	return false
}

type NotificationTimeSpanList []*NotificationTimeSpan

func (l NotificationTimeSpanList) Within(t time.Time) bool {
	target := t
	for _, timeSpan := range l {
		if timeSpan.Within(target) {
			return true
		}
	}
	return false
}

type NotificationTimeSpanService struct {
	db *gorm.DB
}

func NewNotificationTimeSpanService(db *gorm.DB) *NotificationTimeSpanService {
	return &NotificationTimeSpanService{db}
}

func (s *NotificationTimeSpanService) NewNotificationTimeSpansFromPB(
	userID uint32, args []*api_v1.NotificationTimeSpan,
) []*NotificationTimeSpan {
	values := make([]*NotificationTimeSpan, 0, len(args))
	for i, v := range args {
		fromTime := fmt.Sprintf("%v:%v", v.FromHour, v.FromMinute)
		toTime := fmt.Sprintf("%v:%v", v.ToHour, v.ToMinute)
		values = append(values, &NotificationTimeSpan{
			UserID:   userID,
			Number:   uint8(i + 1),
			FromTime: fromTime,
			ToTime:   toTime,
		})
	}
	return values
}

func (s *NotificationTimeSpanService) NewNotificationTimeSpansPB(args []*NotificationTimeSpan) ([]*api_v1.NotificationTimeSpan, error) {
	values := make([]*api_v1.NotificationTimeSpan, 0, len(args))
	for _, v := range args {
		fromTime, err := time.Parse("15:04:05", v.FromTime)
		if err != nil {
			return nil, errors.NewInternalError(
				errors.WithError(err),
				errors.WithMessagef("Invalid time format: FromTime=%v", v.FromTime),
			)
		}
		toTime, err := time.Parse("15:04:05", v.ToTime)
		if err != nil {
			return nil, errors.NewInternalError(
				errors.WithError(err),
				errors.WithMessagef("Invalid time format: ToTime=%v", v.ToTime),
			)
		}
		values = append(values, &api_v1.NotificationTimeSpan{
			FromHour:   int32(fromTime.Hour()),
			FromMinute: int32(fromTime.Minute()),
			ToHour:     int32(toTime.Hour()),
			ToMinute:   int32(toTime.Minute()),
		})
	}
	return values, nil
}

func (s *NotificationTimeSpanService) FindByUserID(
	ctx context.Context,
	userID uint32,
) ([]*NotificationTimeSpan, error) {
	_, span := trace.StartSpan(ctx, "NotificationTimeSpanService.FindByUserID")
	defer span.End()
	span.Annotatef([]trace.Attribute{
		trace.Int64Attribute("userID", int64(userID)),
	}, "userID:%d", userID)

	sql := fmt.Sprintf(`SELECT * FROM %s WHERE user_id = ?`, (&NotificationTimeSpan{}).TableName())
	timeSpans := make([]*NotificationTimeSpan, 0, 10)
	if err := s.db.Raw(sql, userID).Scan(&timeSpans).Error; err != nil {
		return nil, errors.NewInternalError(
			errors.WithError(err),
			errors.WithMessage("FindByUserID select failed"),
			errors.WithResource(errors.NewResource((&NotificationTimeSpan{}).TableName(), "userID", userID)),
		)
	}
	return timeSpans, nil
}

func (s *NotificationTimeSpanService) UpdateAll(userID uint32, timeSpans []*NotificationTimeSpan) error {
	for _, timeSpan := range timeSpans {
		if userID != timeSpan.UserID {
			return errors.NewInvalidArgumentError(
				errors.WithMessage("Given userID and userID of timeSpans must be same"),
			)
		}
	}

	tx := s.db.Begin()
	tableName := (&NotificationTimeSpan{}).TableName()
	sql := fmt.Sprintf(`DELETE FROM %s WHERE user_id = ?`, tableName)
	if err := tx.Exec(sql, userID).Error; err != nil {
		return errors.NewInternalError(
			errors.WithError(err),
			errors.WithMessage("UpdateAll delete failed"),
			errors.WithResource(errors.NewResource(tableName, "userID", userID)),
		)
	}

	for _, timeSpan := range timeSpans {
		if err := tx.Create(timeSpan).Error; err != nil {
			return errors.NewInternalError(
				errors.WithError(err),
				errors.WithMessage("UpdateAll insert failed"),
				errors.WithResource(errors.NewResource(tableName, "userID", userID)),
			)
		}
	}

	if err := tx.Commit().Error; err != nil {
		return errors.NewInternalError(
			errors.WithError(err),
			errors.WithMessage("UpdateAll commit failed"),
			errors.WithResource(errors.NewResource(tableName, "userID", userID)),
		)
	}

	return nil
}
<|endoftext|>


<commit_before>package usecase

import (
	"html/template"
	"net/http"

	"github.com/matsu-chara/gol/operations"
)

var dumpTemplate = template.Must(template.New("gol").Parse(`
<!DOCTYPE html>

    <meta charset="utf-8">
    <title>gol</title>

   {{ range $key, $value := . }}

{{ $key }} :{{ $value }}

   {{ end }}

`))

// DumpAsHTML dumps all links in kvs as html
func DumpAsHTML(filepath string, w http.ResponseWriter) {
	dumped, err := operations.RunDump(filepath)
	if err != nil {
		respondInternalServerError(err, w)
		return
	}

	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	err = dumpTemplate.ExecuteTemplate(w, "gol", dumped)
	if err != nil {
		respondInternalServerError(err, w)
		return
	}
	return
}
<commit_msg>add post/delete ui<commit_after>package usecase

import (
	"html/template"
	"net/http"

	"github.com/matsu-chara/gol/operations"
)

var dumpTemplate = template.Must(template.New("gol").Parse(`
<!DOCTYPE html>

    <meta charset="utf-8">
    <title>gol</title>

current links

      {{ range $key, $value := . }}

{{ $key }} :{{ $value }}

      {{ end }}

register new link

    <form id="register-form">
      key: <input id="register-form-key" name="key" type="text" />
      url: <input id="register-form-value" name="value" type="text" />
      <input type="button" value="register" onclick="doRegister()">
	</form>

delete link

    <form id="delete-form">
      key: <input id="delete-form-key" name="key" type="text" />
      <input type="button" value="delete" onclick="doDelete()">
	</form>

`))

// DumpAsHTML dumps all links in kvs as html
func DumpAsHTML(filepath string, w http.ResponseWriter) {
	dumped, err := operations.RunDump(filepath)
	if err != nil {
		respondInternalServerError(err, w)
		return
	}

	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	err = dumpTemplate.ExecuteTemplate(w, "gol", dumped)
	if err != nil {
		respondInternalServerError(err, w)
		return
	}
	return
}
<|endoftext|>


<commit_before>package runtime

// Version is the current version of the buffalo binary
const Version = "v0.12.0"

// const Version = "development"
<commit_msg>version bump<commit_after>package runtime

// Version is the current version of the buffalo binary
const Version = "v0.12.1"

// const Version = "development"
<|endoftext|>


<commit_before>package cli

import (
	"encoding/json"
	"fmt"
	"os"

	log "github.com/Sirupsen/logrus"
	"github.com/bitrise-io/bitrise/models"
	"github.com/bitrise-io/go-utils/colorstring"
	"github.com/codegangsta/cli"
	"github.com/ryanuber/go-glob"
)

func registerFatal(errorMsg, format string) {
	msg := map[string]string{
		"error": errorMsg,
	}

	if format == OutputFormatRaw {
		log.Fatal(msg["error"])
	} else {
		bytes, err := json.Marshal(msg)
		if err != nil {
			log.Fatalf("Faild to parse error model, err: %s", err)
		}

		fmt.Println(string(bytes))
		os.Exit(1)
	}
}

// GetWorkflowIDByPattern ...
func GetWorkflowIDByPattern(config models.BitriseDataModel, pattern string) (string, error) {
	// Check for workflow ID in trigger map
	for _, item := range config.TriggerMap {
		if glob.Glob(item.Pattern, pattern) {
			if !item.IsPullRequestAllowed && IsPullRequestMode {
				return "", fmt.Errorf("Trigger pattern (%s) match found, but pull request is not enabled", pattern)
			}
			return item.WorkflowID, nil
		}
	}

	// Check for direct workflow selection
	_, exist := config.Workflows[pattern]
	if !exist {
		return "", fmt.Errorf("Specified Workflow (%s) does not exist!", pattern)
	} else if IsPullRequestMode {
		return "", fmt.Errorf("Run triggered by pull request (pattern: %s), but no matching pattern found", pattern)
	}

	return pattern, nil
}

func triggerCheck(c *cli.Context) {
	format := c.String(OuputFormatKey)
	if format == "" {
		format = OutputFormatRaw
	} else if !(format == OutputFormatRaw || format == OutputFormatJSON) {
		registerFatal(fmt.Sprintf("Invalid format: %s", format), OutputFormatJSON)
	}

	// Config validation
	bitriseConfig, err := CreateBitriseConfigFromCLIParams(c)
	if err != nil {
		registerFatal(fmt.Sprintf("Failed to create config, err: %s", err), format)
	}

	// Trigger filter validation
	triggerPattern := ""
	if len(c.Args()) < 1 {
		registerFatal("No trigger pattern specified", format)
	} else {
		triggerPattern = c.Args()[0]
	}

	if triggerPattern == "" {
		registerFatal("No trigger pattern specified", format)
	}

	workflowToRunID, err := GetWorkflowIDByPattern(bitriseConfig, triggerPattern)
	if err != nil {
		registerFatal(fmt.Sprintf("Faild to select workflow by pattern (%s), err: %s", triggerPattern, err), format)
	}

	switch format {
	case OutputFormatRaw:
		fmt.Printf("%s -> %s\n", triggerPattern, colorstring.Blue(workflowToRunID))
		break
	case OutputFormatJSON:
		triggerModel := map[string]string{
			"pattern":  triggerPattern,
			"workflow": workflowToRunID,
		}
		bytes, err := json.Marshal(triggerModel)
		if err != nil {
			registerFatal(fmt.Sprintf("Faild to parse trigger model, err: %s", err), format)
		}

		fmt.Println(string(bytes))
		break
	default:
		registerFatal(fmt.Sprintf("Invalid format: %s", format), OutputFormatJSON)
	}

}
<commit_msg>start<commit_after>package cli

import (
	"encoding/json"
	"fmt"
	"os"

	log "github.com/Sirupsen/logrus"
	"github.com/bitrise-io/bitrise/models"
	"github.com/bitrise-io/go-utils/colorstring"
	"github.com/codegangsta/cli"
	"github.com/ryanuber/go-glob"
)

func registerFatal(errorMsg, format string) {
	msg := map[string]string{
		"error": errorMsg,
	}

	if format == OutputFormatRaw {
		log.Fatal(msg["error"])
	} else {
		bytes, err := json.Marshal(msg)
		if err != nil {
			log.Fatalf("Faild to parse error model, err: %s", err)
		}

		fmt.Println(string(bytes))
		os.Exit(1)
	}
}

// GetWorkflowIDByPattern ...
func GetWorkflowIDByPattern(config models.BitriseDataModel, pattern string) (string, error) {
	// Check for workflow ID in trigger map
	for _, item := range config.TriggerMap {
		if glob.Glob(item.Pattern, pattern) {
			if !item.IsPullRequestAllowed && IsPullRequestMode {
				return "", fmt.Errorf("Trigger pattern (%s) match found, but pull request is not enabled", pattern)
			}
			return item.WorkflowID, nil
		}
	}
	return "", fmt.Errorf("Run triggered by pull request (pattern: %s), but no matching pattern found", pattern)
}

func triggerCheck(c *cli.Context) {
	format := c.String(OuputFormatKey)
	if format == "" {
		format = OutputFormatRaw
	} else if !(format == OutputFormatRaw || format == OutputFormatJSON) {
		registerFatal(fmt.Sprintf("Invalid format: %s", format), OutputFormatJSON)
	}

	// Config validation
	bitriseConfig, err := CreateBitriseConfigFromCLIParams(c)
	if err != nil {
		registerFatal(fmt.Sprintf("Failed to create config, err: %s", err), format)
	}

	// Trigger filter validation
	triggerPattern := ""
	if len(c.Args()) < 1 {
		registerFatal("No trigger pattern specified", format)
	} else {
		triggerPattern = c.Args()[0]
	}

	if triggerPattern == "" {
		registerFatal("No trigger pattern specified", format)
	}

	workflowToRunID, err := GetWorkflowIDByPattern(bitriseConfig, triggerPattern)
	if err != nil {
		registerFatal(fmt.Sprintf("Faild to select workflow by pattern (%s), err: %s", triggerPattern, err), format)
	}

	switch format {
	case OutputFormatRaw:
		fmt.Printf("%s -> %s\n", triggerPattern, colorstring.Blue(workflowToRunID))
		break
	case OutputFormatJSON:
		triggerModel := map[string]string{
			"pattern":  triggerPattern,
			"workflow": workflowToRunID,
		}
		bytes, err := json.Marshal(triggerModel)
		if err != nil {
			registerFatal(fmt.Sprintf("Faild to parse trigger model, err: %s", err), format)
		}

		fmt.Println(string(bytes))
		break
	default:
		registerFatal(fmt.Sprintf("Invalid format: %s", format), OutputFormatJSON)
	}

}
<|endoftext|>


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

// Package https provides a helper for starting an HTTPS server.
package https // import "upspin.io/cloud/https"

import (
	"crypto/tls"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"time"

	"golang.org/x/crypto/acme/autocert"
	gContext "golang.org/x/net/context"
	"google.golang.org/api/option"

	"cloud.google.com/go/compute/metadata"
	"cloud.google.com/go/storage"

	"upspin.io/access"
	"upspin.io/errors"
	"upspin.io/flags"
	"upspin.io/log"
	"upspin.io/serverutil"
)

// Options permits the configuration of TLS certificates for servers running
// outside GCE. The default is the self-signed certificate in
// upspin.io/rpc/testdata.
type Options struct {
	// LetsEncryptCache specifies the cache file for Let's Encrypt.
	// If non-empty, enables Let's Encrypt certificates for this server.
	LetsEncryptCache string

	// LetsEncryptHosts specifies the list of hosts for which we should
	// obtain TLS certificates through Let's Encrypt. If LetsEncryptCache
	// is specified this should be specified also.
	LetsEncryptHosts []string

	// CertFile and KeyFile specifies the TLS certificates to use.
	// It has no effect if LetsEncryptCache is non-empty.
	CertFile string
	KeyFile  string
}

var defaultOptions = &Options{
	CertFile: filepath.Join(os.Getenv("GOPATH"), "/src/upspin.io/rpc/testdata/cert.pem"),
	KeyFile:  filepath.Join(os.Getenv("GOPATH"), "/src/upspin.io/rpc/testdata/key.pem"),
}

func (opt *Options) applyDefaults() {
	if opt.CertFile == "" {
		opt.CertFile = defaultOptions.CertFile
	}
	if opt.KeyFile == "" {
		opt.KeyFile = defaultOptions.KeyFile
	}
}

// ListenAndServe serves the http.DefaultServeMux by HTTPS (and HTTP,
// redirecting to HTTPS), storing SSL credentials in the Google Cloud Storage
// buckets letsencrypt*.
//
// If the server is running outside GCE, instead an HTTPS server is started on
// the address specified by addr using the certificate details specified by opt.
//
// The given channel, if any, is closed when the TCP listener has succeeded.
// It may be used to signal that the server is ready to start serving requests.
//
// ListenAndServe does not return. It exits the program when the server is
// shut down (via SIGTERM or due to an error) and calls serverutil.Shutdown.
func ListenAndServe(ready chan<- struct{}, serverName, addr string, opt *Options) {
	if opt == nil {
		opt = defaultOptions
	} else {
		opt.applyDefaults()
	}

	var m autocert.Manager
	m.Prompt = autocert.AcceptTOS
	if h := opt.LetsEncryptHosts; len(h) > 0 {
		m.HostPolicy = autocert.HostWhitelist(h...)
	}

	var config *tls.Config
	if file := opt.LetsEncryptCache; file != "" {
		log.Info.Printf("https: serving HTTPS on %q using Let's Encrypt certificates", addr)
		m.Cache = autocert.DirCache(file)
		config = &tls.Config{GetCertificate: m.GetCertificate}
	} else if metadata.OnGCE() {
		addr = ":443"
		log.Info.Printf("https: serving HTTPS on GCE %q using Let's Encrypt certificates", addr)
		const key = "letsencrypt-bucket"
		bucket, err := metadata.InstanceAttributeValue(key)
		if err != nil {
			log.Fatalf("https: couldn't read %q metadata value: %v", key, err)
		}
		cache, err := newAutocertCache(bucket, serverName)
		if err != nil {
			log.Fatalf("https: couldn't set up letsencrypt cache: %v", err)
		}
		m.Cache = cache
		config = &tls.Config{GetCertificate: m.GetCertificate}
	} else {
		log.Info.Printf("https: not on GCE; serving HTTPS on %q using provided certificates", addr)
		if opt.CertFile == defaultOptions.CertFile || opt.KeyFile == defaultOptions.KeyFile {
			log.Error.Print("https: WARNING: using self-signed test certificates.")
		}
		var err error
		config, err = newDefaultTLSConfig(opt.CertFile, opt.KeyFile)
		if err != nil {
			log.Fatalf("https: setting up TLS config: %v", err)
		}
	}
	server := &http.Server{
		ReadHeaderTimeout: 5 * time.Second,
		ReadTimeout:       15 * time.Second,
		WriteTimeout:      15 * time.Second,
		IdleTimeout:       60 * time.Second,
		TLSConfig:         config,
	}
	// TODO(adg): enable HTTP/2 once it's fast enough
	//err := http2.ConfigureServer(server, nil)
	//if err != nil {
	//	log.Fatalf("https: %v", err)
	//}

	ln, err := net.Listen("tcp", addr)
	if err != nil {
		log.Fatalf("https: %v", err)
	}
	if ready != nil {
		close(ready)
	}
	serverutil.RegisterShutdown(func() {
		// Stop accepting connections and forces the server to stop
		// its serving loop.
		ln.Close()
	})
	err = server.Serve(tls.NewListener(ln, config))
	if err != nil {
		log.Printf("https: %v", err)
	}
	// Ensure we terminate cleanly.
	serverutil.Shutdown()
}

// ListenAndServeFromFlags is the same as ListenAndServe, but it determines the
// listen address and Options from command-line flags in the flags package.
func ListenAndServeFromFlags(ready chan<- struct{}, serverName string) {
	ListenAndServe(ready, serverName, flags.HTTPSAddr, &Options{
		LetsEncryptCache: flags.LetsEncryptCache,
		CertFile:         flags.TLSCertFile,
		KeyFile:          flags.TLSKeyFile,
	})
}

// newDefaultTLSConfig creates a new TLS config based on the certificate files given.
func newDefaultTLSConfig(certFile string, certKeyFile string) (*tls.Config, error) {
	const op = "cloud/https.newDefaultTLSConfig"
	certReadable, err := isReadableFile(certFile)
	if err != nil {
		return nil, errors.E(op, errors.Invalid, errors.Errorf("SSL certificate in %q: %q", certFile, err))
	}
	if !certReadable {
		return nil, errors.E(op, errors.Invalid, errors.Errorf("certificate file %q not readable", certFile))
	}
	keyReadable, err := isReadableFile(certKeyFile)
	if err != nil {
		return nil, errors.E(op, errors.Invalid, errors.Errorf("SSL key in %q: %v", certKeyFile, err))
	}
	if !keyReadable {
		return nil, errors.E(op, errors.Invalid, errors.Errorf("certificate key file %q not readable", certKeyFile))
	}

	cert, err := tls.LoadX509KeyPair(certFile, certKeyFile)
	if err != nil {
		return nil, errors.E(op, err)
	}

	tlsConfig := &tls.Config{
		CipherSuites: []uint16{
			tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
			tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
			tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
			tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
			tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
			tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
		},
		MinVersion:               tls.VersionTLS12,
		PreferServerCipherSuites: true, // Use our choice, not the client's choice
		CurvePreferences:         []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256, tls.X25519},
		Certificates:             []tls.Certificate{cert},
	}
	tlsConfig.BuildNameToCertificate()
	return tlsConfig, nil
}

// isReadableFile reports whether the file exists and is readable.
// If the error is non-nil, it means there might be a file or directory
// with that name but we cannot read it.
func isReadableFile(path string) (bool, error) {
	// Is it stattable and is it a plain file?
	info, err := os.Stat(path)
	if err != nil {
		if os.IsNotExist(err) {
			return false, nil // Item does not exist.
		}
		return false, err // Item is problematic.
	}
	if info.IsDir() {
		return false, errors.Str("is directory")
	}
	// Is it readable?
	fd, err := os.Open(path)
	if err != nil {
		return false, access.ErrPermissionDenied
	}
	fd.Close()
	return true, nil // Item exists and is readable.
}

// autocertCache implements autocert.Cache.
type autocertCache struct {
	b      *storage.BucketHandle
	server string
}

func newAutocertCache(bucket, prefix string) (cache autocertCache, err error) {
	ctx := gContext.Background()
	client, err := storage.NewClient(ctx, option.WithScopes(storage.ScopeFullControl))
	if err != nil {
		return
	}
	cache.b = client.Bucket(bucket)
	cache.server = prefix + "-"
	return
}

func (cache autocertCache) Get(ctx gContext.Context, name string) ([]byte, error) {
	r, err := cache.b.Object(cache.server + name).NewReader(ctx)
	if err == storage.ErrObjectNotExist {
		return nil, autocert.ErrCacheMiss
	}
	if err != nil {
		return nil, err
	}
	defer r.Close()
	return ioutil.ReadAll(r)
}

func (cache autocertCache) Put(ctx gContext.Context, name string, data []byte) error {
	// TODO(ehg) Do we need to add contentType="text/plain; charset=utf-8"?
	w := cache.b.Object(cache.server + name).NewWriter(ctx)
	_, err := w.Write(data)
	if err != nil {
		log.Printf("https: writing letsencrypt cache: %s %v", name, err)
	}
	if err := w.Close(); err != nil {
		log.Printf("https: writing letsencrypt cache: %s %v", name, err)
	}
	return err
}

func (cache autocertCache) Delete(ctx gContext.Context, name string) error {
	return cache.b.Object(cache.server + name).Delete(ctx)
}
<commit_msg>https: turn off the http write timeout<commit_after>// Copyright 2016 The Upspin Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package https provides a helper for starting an HTTPS server.
package https // import "upspin.io/cloud/https"

import (
	"crypto/tls"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"time"

	"golang.org/x/crypto/acme/autocert"
	gContext "golang.org/x/net/context"
	"google.golang.org/api/option"

	"cloud.google.com/go/compute/metadata"
	"cloud.google.com/go/storage"

	"upspin.io/access"
	"upspin.io/errors"
	"upspin.io/flags"
	"upspin.io/log"
	"upspin.io/serverutil"
)

// Options permits the configuration of TLS certificates for servers running
// outside GCE. The default is the self-signed certificate in
// upspin.io/rpc/testdata.
type Options struct {
	// LetsEncryptCache specifies the cache file for Let's Encrypt.
	// If non-empty, enables Let's Encrypt certificates for this server.
	LetsEncryptCache string

	// LetsEncryptHosts specifies the list of hosts for which we should
	// obtain TLS certificates through Let's Encrypt. If LetsEncryptCache
	// is specified this should be specified also.
	LetsEncryptHosts []string

	// CertFile and KeyFile specifies the TLS certificates to use.
	// It has no effect if LetsEncryptCache is non-empty.
	CertFile string
	KeyFile  string
}

var defaultOptions = &Options{
	CertFile: filepath.Join(os.Getenv("GOPATH"), "/src/upspin.io/rpc/testdata/cert.pem"),
	KeyFile:  filepath.Join(os.Getenv("GOPATH"), "/src/upspin.io/rpc/testdata/key.pem"),
}

func (opt *Options) applyDefaults() {
	if opt.CertFile == "" {
		opt.CertFile = defaultOptions.CertFile
	}
	if opt.KeyFile == "" {
		opt.KeyFile = defaultOptions.KeyFile
	}
}

// ListenAndServe serves the http.DefaultServeMux by HTTPS (and HTTP,
// redirecting to HTTPS), storing SSL credentials in the Google Cloud Storage
// buckets letsencrypt*.
//
// If the server is running outside GCE, instead an HTTPS server is started on
// the address specified by addr using the certificate details specified by opt.
//
// The given channel, if any, is closed when the TCP listener has succeeded.
// It may be used to signal that the server is ready to start serving requests.
//
// ListenAndServe does not return. It exits the program when the server is
// shut down (via SIGTERM or due to an error) and calls serverutil.Shutdown.
func ListenAndServe(ready chan<- struct{}, serverName, addr string, opt *Options) {
	if opt == nil {
		opt = defaultOptions
	} else {
		opt.applyDefaults()
	}

	var m autocert.Manager
	m.Prompt = autocert.AcceptTOS
	if h := opt.LetsEncryptHosts; len(h) > 0 {
		m.HostPolicy = autocert.HostWhitelist(h...)
	}

	var config *tls.Config
	if file := opt.LetsEncryptCache; file != "" {
		log.Info.Printf("https: serving HTTPS on %q using Let's Encrypt certificates", addr)
		m.Cache = autocert.DirCache(file)
		config = &tls.Config{GetCertificate: m.GetCertificate}
	} else if metadata.OnGCE() {
		addr = ":443"
		log.Info.Printf("https: serving HTTPS on GCE %q using Let's Encrypt certificates", addr)
		const key = "letsencrypt-bucket"
		bucket, err := metadata.InstanceAttributeValue(key)
		if err != nil {
			log.Fatalf("https: couldn't read %q metadata value: %v", key, err)
		}
		cache, err := newAutocertCache(bucket, serverName)
		if err != nil {
			log.Fatalf("https: couldn't set up letsencrypt cache: %v", err)
		}
		m.Cache = cache
		config = &tls.Config{GetCertificate: m.GetCertificate}
	} else {
		log.Info.Printf("https: not on GCE; serving HTTPS on %q using provided certificates", addr)
		if opt.CertFile == defaultOptions.CertFile || opt.KeyFile == defaultOptions.KeyFile {
			log.Error.Print("https: WARNING: using self-signed test certificates.")
		}
		var err error
		config, err = newDefaultTLSConfig(opt.CertFile, opt.KeyFile)
		if err != nil {
			log.Fatalf("https: setting up TLS config: %v", err)
		}
	}
	// WriteTimeout is set to 0 because it also pertains to streaming
	// replies, e.g., the DirServer.Watch interface.
	server := &http.Server{
		ReadHeaderTimeout: 5 * time.Second,
		ReadTimeout:       15 * time.Second,
		WriteTimeout:      0,
		IdleTimeout:       60 * time.Second,
		TLSConfig:         config,
	}
	// TODO(adg): enable HTTP/2 once it's fast enough
	//err := http2.ConfigureServer(server, nil)
	//if err != nil {
	//	log.Fatalf("https: %v", err)
	//}

	ln, err := net.Listen("tcp", addr)
	if err != nil {
		log.Fatalf("https: %v", err)
	}
	if ready != nil {
		close(ready)
	}
	serverutil.RegisterShutdown(func() {
		// Stop accepting connections and forces the server to stop
		// its serving loop.
		ln.Close()
	})
	err = server.Serve(tls.NewListener(ln, config))
	if err != nil {
		log.Printf("https: %v", err)
	}
	// Ensure we terminate cleanly.
	serverutil.Shutdown()
}

// ListenAndServeFromFlags is the same as ListenAndServe, but it determines the
// listen address and Options from command-line flags in the flags package.
func ListenAndServeFromFlags(ready chan<- struct{}, serverName string) {
	ListenAndServe(ready, serverName, flags.HTTPSAddr, &Options{
		LetsEncryptCache: flags.LetsEncryptCache,
		CertFile:         flags.TLSCertFile,
		KeyFile:          flags.TLSKeyFile,
	})
}

// newDefaultTLSConfig creates a new TLS config based on the certificate files given.
func newDefaultTLSConfig(certFile string, certKeyFile string) (*tls.Config, error) {
	const op = "cloud/https.newDefaultTLSConfig"
	certReadable, err := isReadableFile(certFile)
	if err != nil {
		return nil, errors.E(op, errors.Invalid, errors.Errorf("SSL certificate in %q: %q", certFile, err))
	}
	if !certReadable {
		return nil, errors.E(op, errors.Invalid, errors.Errorf("certificate file %q not readable", certFile))
	}
	keyReadable, err := isReadableFile(certKeyFile)
	if err != nil {
		return nil, errors.E(op, errors.Invalid, errors.Errorf("SSL key in %q: %v", certKeyFile, err))
	}
	if !keyReadable {
		return nil, errors.E(op, errors.Invalid, errors.Errorf("certificate key file %q not readable", certKeyFile))
	}

	cert, err := tls.LoadX509KeyPair(certFile, certKeyFile)
	if err != nil {
		return nil, errors.E(op, err)
	}

	tlsConfig := &tls.Config{
		CipherSuites: []uint16{
			tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
			tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
			tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
			tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
			tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
			tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
		},
		MinVersion:               tls.VersionTLS12,
		PreferServerCipherSuites: true, // Use our choice, not the client's choice
		CurvePreferences:         []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256, tls.X25519},
		Certificates:             []tls.Certificate{cert},
	}
	tlsConfig.BuildNameToCertificate()
	return tlsConfig, nil
}

// isReadableFile reports whether the file exists and is readable.
// If the error is non-nil, it means there might be a file or directory
// with that name but we cannot read it.
func isReadableFile(path string) (bool, error) {
	// Is it stattable and is it a plain file?
	info, err := os.Stat(path)
	if err != nil {
		if os.IsNotExist(err) {
			return false, nil // Item does not exist.
		}
		return false, err // Item is problematic.
	}
	if info.IsDir() {
		return false, errors.Str("is directory")
	}
	// Is it readable?
	fd, err := os.Open(path)
	if err != nil {
		return false, access.ErrPermissionDenied
	}
	fd.Close()
	return true, nil // Item exists and is readable.
}

// autocertCache implements autocert.Cache.
type autocertCache struct {
	b      *storage.BucketHandle
	server string
}

func newAutocertCache(bucket, prefix string) (cache autocertCache, err error) {
	ctx := gContext.Background()
	client, err := storage.NewClient(ctx, option.WithScopes(storage.ScopeFullControl))
	if err != nil {
		return
	}
	cache.b = client.Bucket(bucket)
	cache.server = prefix + "-"
	return
}

func (cache autocertCache) Get(ctx gContext.Context, name string) ([]byte, error) {
	r, err := cache.b.Object(cache.server + name).NewReader(ctx)
	if err == storage.ErrObjectNotExist {
		return nil, autocert.ErrCacheMiss
	}
	if err != nil {
		return nil, err
	}
	defer r.Close()
	return ioutil.ReadAll(r)
}

func (cache autocertCache) Put(ctx gContext.Context, name string, data []byte) error {
	// TODO(ehg) Do we need to add contentType="text/plain; charset=utf-8"?
	w := cache.b.Object(cache.server + name).NewWriter(ctx)
	_, err := w.Write(data)
	if err != nil {
		log.Printf("https: writing letsencrypt cache: %s %v", name, err)
	}
	if err := w.Close(); err != nil {
		log.Printf("https: writing letsencrypt cache: %s %v", name, err)
	}
	return err
}

func (cache autocertCache) Delete(ctx gContext.Context, name string) error {
	return cache.b.Object(cache.server + name).Delete(ctx)
}
<|endoftext|>


<commit_before>/*
   PulseHA - HA Cluster Daemon
   Copyright (C) 2017  Andrew Zak <andrew@pulseha.com>

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published
   by the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package commands

import (
	"context"
	"flag"
	"github.com/Syleron/PulseHA/proto"
	"github.com/Syleron/PulseHA/src/utils"
	"github.com/mitchellh/cli"
	"google.golang.org/grpc"
	"strings"
)

type JoinCommand struct {
	Ui cli.Ui
}

/**
 *
 */
func (c *JoinCommand) Help() string {
	helpText := `
Usage: pulseha join [options] address ...
  Tells a running PulseHA agent to join the cluster
  by specifying at least one existing member.
Options:
  -bind-ip pulse daemon bind address
  -bind-port pulse daemon bind  port
`
	return strings.TrimSpace(helpText)
}

/**
 *
 */
func (c *JoinCommand) Run(args []string) int {
	cmdFlags := flag.NewFlagSet("join", flag.ContinueOnError)
	cmdFlags.Usage = func() { c.Ui.Output(c.Help()) }

	// Set the acceptable cmd flags
	bindIP := cmdFlags.String("bind-ip", "127.0.0.1", "Bind IP address for local Pulse daemon")
	bindPort := cmdFlags.String("bind-port", "1234", "Bind port for local Pulse daemon")

	// Parse and handle error
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	// Get the command params
	cmds := cmdFlags.Args()

	// Make sure that the join address and port is set
	if len(cmds) < 2 {
		c.Ui.Error("Please specify an address and port to join.")
		c.Ui.Error("")
		c.Ui.Error(c.Help())
		return 1
	}

	// If we have the default.. which we don't want.. error out.
	if *bindIP == "127.0.0.1" {
		c.Ui.Error("Please specify a bind IP address.\n")
		c.Ui.Output(c.Help())
		return 1
	}

	// If we have the default.. which we don't want.. error out.
	if *bindPort == "1234" {
		c.Ui.Error("Please specify a bind port.\n")
		c.Ui.Output(c.Help())
		return 1
	}

	// IP validation
	if utils.IsIPv6(*bindIP) {
		cleanIP := utils.SanitizeIPv6(*bindIP)
		bindIP = &cleanIP
	} else if !utils.IsIPv4(*bindIP) {
		c.Ui.Error("Please specify a valid join address.\n")
		c.Ui.Output(c.Help())
		return 1
	}

	// Port validation
	if !utils.IsPort(*bindPort) {
		c.Ui.Error("Please specify a valid port 0-65536.\n")
		c.Ui.Output(c.Help())
		return 1
	}

	// validate the join address
	joinIP := cmds[0]
	joinPort := cmds[1]

	if utils.IsIPv6(joinIP) {
		joinIP = utils.SanitizeIPv6(joinIP)
	} else if !utils.IsIPv4(joinIP) {
		c.Ui.Error("Please specify a valid join address.\n")
		c.Ui.Output(c.Help())
		return 1
	}

	// Validate join Port
	if !utils.IsPort(joinPort) {
		c.Ui.Error("Please specify a valid join port 0-65536.\n")
		c.Ui.Output(c.Help())
		return 1
	}

	// setup a connection
	connection, err := grpc.Dial("127.0.0.1:49152", grpc.WithInsecure())

	// handle the error
	if err != nil {
		c.Ui.Error("GRPC client connection error. Is the PulseHA service running?")
		c.Ui.Error(err.Error())
	}

	// defer the close
	defer connection.Close()

	// setup new RPC client
	client := proto.NewCLIClient(connection)

	r, err := client.Join(context.Background(), &proto.PulseJoin{
		Ip:       joinIP,
		Port:     joinPort,
		BindIp:   *bindIP,
		BindPort: *bindPort,
	})

	if err != nil {
		c.Ui.Output("PulseHA CLI connection error. Is the PulseHA service running?")
		c.Ui.Output(err.Error())
	} else {
		if r.Success {
			c.Ui.Output("\n[\u2713] " + r.Message + "\n")
		} else {
			c.Ui.Output("\n[x] " + r.Message + "\n")
		}
	}

	return 0
}

/**
 *
 */
func (c *JoinCommand) Synopsis() string {
	return "Tell PulseHA to join a cluster"
}
<commit_msg>join now includes hostname<commit_after>/*
   PulseHA - HA Cluster Daemon
   Copyright (C) 2017  Andrew Zak <andrew@pulseha.com>

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published
   by the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package commands

import (
	"context"
	"flag"
	"github.com/Syleron/PulseHA/proto"
	"github.com/Syleron/PulseHA/src/utils"
	"github.com/mitchellh/cli"
	"google.golang.org/grpc"
	"strings"
)

type JoinCommand struct {
	Ui cli.Ui
}

/**
 *
 */
func (c *JoinCommand) Help() string {
	helpText := `
Usage: pulseha join [options] address ...
  Tells a running PulseHA agent to join the cluster
  by specifying at least one existing member.
Options:
  -bind-ip pulse daemon bind address
  -bind-port pulse daemon bind  port
`
	return strings.TrimSpace(helpText)
}

/**
 *
 */
func (c *JoinCommand) Run(args []string) int {
	cmdFlags := flag.NewFlagSet("join", flag.ContinueOnError)
	cmdFlags.Usage = func() { c.Ui.Output(c.Help()) }

	// Set the acceptable cmd flags
	bindIP := cmdFlags.String("bind-ip", "127.0.0.1", "Bind IP address for local Pulse daemon")
	bindPort := cmdFlags.String("bind-port", "1234", "Bind port for local Pulse daemon")

	// Parse and handle error
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	// Get the command params
	cmds := cmdFlags.Args()

	// Make sure that the join address and port is set
	if len(cmds) < 2 {
		c.Ui.Error("Please specify an address and port to join.")
		c.Ui.Error("")
		c.Ui.Error(c.Help())
		return 1
	}

	// If we have the default.. which we don't want.. error out.
	if *bindIP == "127.0.0.1" {
		c.Ui.Error("Please specify a bind IP address.\n")
		c.Ui.Output(c.Help())
		return 1
	}

	// If we have the default.. which we don't want.. error out.
	if *bindPort == "1234" {
		c.Ui.Error("Please specify a bind port.\n")
		c.Ui.Output(c.Help())
		return 1
	}

	// IP validation
	if utils.IsIPv6(*bindIP) {
		cleanIP := utils.SanitizeIPv6(*bindIP)
		bindIP = &cleanIP
	} else if !utils.IsIPv4(*bindIP) {
		c.Ui.Error("Please specify a valid join address.\n")
		c.Ui.Output(c.Help())
		return 1
	}

	// Port validation
	if !utils.IsPort(*bindPort) {
		c.Ui.Error("Please specify a valid port 0-65536.\n")
		c.Ui.Output(c.Help())
		return 1
	}

	// validate the join address
	joinIP := cmds[0]
	joinPort := cmds[1]

	if utils.IsIPv6(joinIP) {
		joinIP = utils.SanitizeIPv6(joinIP)
	} else if !utils.IsIPv4(joinIP) {
		c.Ui.Error("Please specify a valid join address.\n")
		c.Ui.Output(c.Help())
		return 1
	}

	// Validate join Port
	if !utils.IsPort(joinPort) {
		c.Ui.Error("Please specify a valid join port 0-65536.\n")
		c.Ui.Output(c.Help())
		return 1
	}

	// setup a connection
	connection, err := grpc.Dial("127.0.0.1:49152", grpc.WithInsecure())

	// handle the error
	if err != nil {
		c.Ui.Error("GRPC client connection error. Is the PulseHA service running?")
		c.Ui.Error(err.Error())
	}

	// defer the close
	defer connection.Close()

	// setup new RPC client
	client := proto.NewCLIClient(connection)

	r, err := client.Join(context.Background(), &proto.PulseJoin{
		Ip:       joinIP,
		Port:     joinPort,
		BindIp:   *bindIP,
		BindPort: *bindPort,
		Hostname: utils.GetHostname(),
	})

	if err != nil {
		c.Ui.Output("PulseHA CLI connection error. Is the PulseHA service running?")
		c.Ui.Output(err.Error())
	} else {
		if r.Success {
			c.Ui.Output("\n[\u2713] " + r.Message + "\n")
		} else {
			c.Ui.Output("\n[x] " + r.Message + "\n")
		}
	}

	return 0
}

/**
 *
 */
func (c *JoinCommand) Synopsis() string {
	return "Tell PulseHA to join a cluster"
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"html"
	"io"
	"os"

	"src.elv.sh/pkg/diff"
	"src.elv.sh/pkg/md"
)

var (
	overwrite = flag.Bool("w", false, "write result to source file (requires -fmt)")
	showDiff  = flag.Bool("d", false, "show diff")
	width     = flag.Int("width", 0, "if > 0, reflow content to width")
)

func main() {
	md.UnescapeHTML = html.UnescapeString
	flag.Parse()

	files := flag.Args()
	if len(files) == 0 {
		text, err := io.ReadAll(os.Stdin)
		if err != nil {
			fmt.Fprintln(os.Stderr, "read stdin:", err)
			os.Exit(2)
		}
		fmt.Print(format(string(text)))
		return
	}
	for _, file := range files {
		text, err := os.ReadFile(file)
		if err != nil {
			fmt.Fprintf(os.Stderr, "read %s: %v\n", file, err)
			os.Exit(2)
		}
		result := format(string(text))
		if *overwrite {
			err := os.WriteFile(file, []byte(result), 0644)
			if err != nil {
				fmt.Fprintf(os.Stderr, "write %s: %v\n", file, err)
				os.Exit(2)
			}
		} else if !*showDiff {
			fmt.Print(result)
		}
		if *showDiff {
			os.Stdout.Write(diff.Diff(file+".orig", text, file, []byte(result)))
		}
	}
}

func format(original string) string {
	return md.RenderString(original, &md.FmtCodec{Width: *width})
}
<commit_msg>cmd/elvmdfmt: Fail when input uses unsupported features.<commit_after>package main

import (
	"flag"
	"fmt"
	"html"
	"io"
	"os"

	"src.elv.sh/pkg/diff"
	"src.elv.sh/pkg/md"
)

var (
	overwrite = flag.Bool("w", false, "write result to source file (requires -fmt)")
	showDiff  = flag.Bool("d", false, "show diff")
	width     = flag.Int("width", 0, "if > 0, reflow content to width")
)

func main() {
	md.UnescapeHTML = html.UnescapeString
	flag.Parse()

	files := flag.Args()
	if len(files) == 0 {
		text, err := io.ReadAll(os.Stdin)
		handleReadError("stdin", err)
		result, unsupported := format(string(text))
		fmt.Print(result)
		handleUnsupported("stdin", unsupported)
		return
	}
	for _, file := range files {
		text, err := os.ReadFile(file)
		handleReadError(file, err)
		result, unsupported := format(string(text))
		handleUnsupported(file, unsupported)
		if *overwrite {
			err := os.WriteFile(file, []byte(result), 0644)
			if err != nil {
				fmt.Fprintf(os.Stderr, "write %s: %v\n", file, err)
				os.Exit(2)
			}
		} else if !*showDiff {
			fmt.Print(result)
		}
		if *showDiff {
			os.Stdout.Write(diff.Diff(file+".orig", text, file, []byte(result)))
		}
	}
}

func format(original string) (string, *md.FmtUnsupported) {
	codec := &md.FmtCodec{Width: *width}
	formatted := md.RenderString(original, codec)
	return formatted, codec.Unsupported()
}

func handleReadError(name string, err error) {
	if err != nil {
		fmt.Fprintf(os.Stderr, "read %s: %v\n", name, err)
		os.Exit(2)
	}
}

func handleUnsupported(name string, u *md.FmtUnsupported) {
	if u == nil {
		return
	}
	if u.NestedEmphasisOrStrongEmphasis {
		fmt.Fprintln(os.Stderr, name, "contains nested emphasis or strong emphasis")
	}
	if u.ConsecutiveEmphasisOrStrongEmphasis {
		fmt.Fprintln(os.Stderr, name, "contains consecutive emphasis or strong emphasis")
	}
	os.Exit(2)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/immortal/immortal"
	"os"
)

var version string

func main() {
	parser := &immortal.Parse{
		UserFinder: &immortal.User{},
	}

	// flag set
	fs := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
	fs.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [-v -ctrl] [-d dir] [-e dir] [-f pidfile] [-l logfile] [-logger logger] [-p child_pidfile] [-P supervisor_pidfile] [-u user] command\n\n   command\n        The command with arguments if any, to supervise.\n\n", os.Args[0])
		fs.PrintDefaults()
	}

	flags, err := immortal.ParseArgs(parser, fs)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	//	 if -v print version
	if flags.Version {
		fmt.Printf("%s\n", version)
		os.Exit(0)
	}

	fmt.Printf("%#v", flags)
}
<commit_msg>	modified:   cmd/immortal/main.go<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/immortal/immortal"
	"os"
)

var version string

func main() {
	parser := &immortal.Parse{
		UserFinder: &immortal.User{},
	}

	// flag set
	fs := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
	fs.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [-v -ctrl] [-d dir] [-e dir] [-f pidfile] [-l logfile] [-logger logger] [-p child_pidfile] [-P supervisor_pidfile] [-u user] command\n\n   command\n        The command with arguments if any, to supervise\n\n", os.Args[0])
		fs.PrintDefaults()
	}

	flags, err := immortal.ParseArgs(parser, fs)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	//	 if -v print version
	if flags.Version {
		fmt.Printf("%s\n", version)
		os.Exit(0)
	}

	fmt.Printf("%#v", flags)
}
<|endoftext|>


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

package main

import (
	"bufio"
	"bytes"
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"os/user"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/dustin/go-humanize"
	"github.com/fatih/color"
	"github.com/spf13/viper"
	"github.com/ubccr/iquota"
	"github.com/ubccr/kerby/khttp"
)

const (
	RESOURCE_USER_QUOTA  = "/quota/user"
	RESOURCE_GROUP_QUOTA = "/quota/group"
	RESOURCE_OVER_QUOTA  = "/quota/exceeded"
	LONG_FORMAT          = "%-12s%-12s%15s%10s%10s%10s%10s%12s\n"
	SHORT_FORMAT         = "%-12s%-12s%15s%10s%10s%12s\n"
)

var (
	cyan   = color.New(color.FgCyan)
	green  = color.New(color.FgGreen)
	red    = color.New(color.FgRed)
	yellow = color.New(color.FgYellow)
	blue   = color.New(color.FgBlue)
)

type Filesystem struct {
	Host       string
	Path       string
	MountPoint string
	UserQuota  bool
	GroupQuota bool
}

type QuotaClient struct {
	Verbose     bool
	Group       bool
	User        bool
	Default     bool
	Long        bool
	FullPath    bool
	OverQuota   bool
	UserFilter  string
	GroupFilter string
	Filesystem  string
	certPool    *x509.CertPool
}

func (f *Filesystem) String() string {
	var buf bytes.Buffer
	if len(f.Host) > 0 {
		buf.WriteString(fmt.Sprintf("%s:", f.Host))
	}
	buf.WriteString(f.Path)
	return buf.String()
}

func (f *Filesystem) ShortString() string {
	if len(f.MountPoint) > 0 {
		return f.MountPoint
	}

	return f.Path
}

func (c *QuotaClient) format() string {
	if c.Long {
		return LONG_FORMAT
	}

	return SHORT_FORMAT
}

func (c *QuotaClient) printFilesystem(fs *Filesystem) {
	if c.FullPath {
		fmt.Printf("%s\n", fs)
		return
	}

	fmt.Printf("%s\n", fs.ShortString())
}

func (c *QuotaClient) fetchQuota(url string) (*iquota.QuotaRestResponse, error) {
	req, err := http.NewRequest("GET", url, nil)
	tr := &http.Transport{TLSClientConfig: &tls.Config{RootCAs: c.certPool}}

	// XXX should we default to this? seems a bit rash? Perhaps make this a config option
	if c.certPool == nil {
		tr.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
	}

	t := &khttp.Transport{Next: tr}
	client := &http.Client{Transport: t}

	res, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	if res.StatusCode == http.StatusInternalServerError {
		return nil, fmt.Errorf("Failed to fetch user quota with HTTP status code: %d", res.StatusCode)
	}

	rawJson, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}

	if res.StatusCode == http.StatusBadRequest {
		ierr := &iquota.IsiError{}
		err = json.Unmarshal(rawJson, ierr)
		if err != nil {
			return nil, err
		}
		return nil, ierr
	}

	if res.StatusCode != 200 {
		return nil, fmt.Errorf("Failed to fetch user quota with HTTP status code: %d", res.StatusCode)
	}

	qr := &iquota.QuotaRestResponse{}
	err = json.Unmarshal(rawJson, qr)
	if err != nil {
		return nil, err
	}

	return qr, nil
}

func (c *QuotaClient) parseMtab() ([]*Filesystem, error) {
	mounts := make([]*Filesystem, 0)

	defaultFs := viper.GetStringMapString("filesystems")

	mtab, err := os.Open("/etc/mtab")
	if err != nil {
		return nil, err
	}
	defer mtab.Close()

	scanner := bufio.NewScanner(mtab)
	for scanner.Scan() {
		fields := strings.Split(scanner.Text(), " ")
		if fields[2] == "nfs" {
			parts := strings.Split(fields[0], ":")
			fs := &Filesystem{
				Host:       parts[0],
				Path:       parts[1],
				MountPoint: fields[1],
				UserQuota:  true,
				GroupQuota: true,
			}

			defaults, ok := defaultFs[fs.Path]
			if ok {
				fs.UserQuota = strings.Contains(defaults, "user")
				fs.GroupQuota = strings.Contains(defaults, "group")
				mounts = append(mounts, fs)
			} else if len(defaultFs) == 0 && strings.HasPrefix(fs.Path, "/ifs") {
				// XXX only include isilon mounts. Will this always be /ifs?
				mounts = append(mounts, fs)
			}
		}
	}

	if err := scanner.Err(); err != nil {
		return nil, err
	}

	return mounts, nil
}

func (c *QuotaClient) printHeader(label string) {
	if c.Long {
		fmt.Printf(c.format(), "Filesystem ", label, "files", "logical", "physical", "soft", "hard", "grace ")
		return
	}

	fmt.Printf(c.format(), "Filesystem ", label, "files", "used", "limit", "grace ")
}

func (c *QuotaClient) printQuota(name string, quota *iquota.Quota) {
	now := time.Now()
	graceTime := now.Add(time.Duration(quota.Threshold.SoftGrace) * time.Second)
	var grace string

	printer := cyan
	if quota.Threshold.SoftExceeded {
		printer = red
		grace = humanize.RelTime(
			time.Unix(int64(quota.Threshold.SoftLastExceeded), 0).Add(time.Duration(quota.Threshold.SoftGrace)*time.Second),
			now,
			"ago",
			"")
	} else {
		grace = humanize.RelTime(graceTime, now, "", "")
	}

	if c.Long {
		printer.Printf(c.format(),
			"",
			name,
			humanize.Comma(int64(quota.Usage.Inodes)),
			humanize.Bytes(uint64(quota.Usage.Logical)),
			humanize.Bytes(uint64(quota.Usage.Physical)),
			humanize.Bytes(uint64(quota.Threshold.Soft)),
			humanize.Bytes(uint64(quota.Threshold.Hard)),
			grace)

		return
	}

	printer.Printf(c.format(),
		"",
		name,
		humanize.Comma(int64(quota.Usage.Inodes)),
		humanize.Bytes(uint64(quota.Usage.Logical)),
		humanize.Bytes(uint64(quota.Threshold.Soft)),
		grace)
}

func (c *QuotaClient) printDefaultQuota(quota *iquota.Quota) {
	now := time.Now()
	graceTime := now.Add(time.Duration(quota.Threshold.SoftGrace) * time.Second)
	grace := humanize.RelTime(graceTime, now, "", "")

	if c.Long {
		yellow.Printf(c.format(),
			"",
			"(default)",
			"",
			"",
			"",
			humanize.Bytes(uint64(quota.Threshold.Soft)),
			humanize.Bytes(uint64(quota.Threshold.Hard)),
			grace)

		return
	}

	yellow.Printf(c.format(),
		"",
		"(default)",
		"",
		"",
		humanize.Bytes(uint64(quota.Threshold.Soft)),
		grace)
}

func (c *QuotaClient) printUserQuota(username string, mounts []*Filesystem) {
	fmt.Printf("User quotas:\n")
	c.printHeader("user")
	for _, fs := range mounts {
		if !fs.UserQuota {
			logrus.Warn("User quota reporting disabled for filesystem: ", fs)
			continue
		}
		params := url.Values{}
		params.Add("user", username)
		params.Add("path", fs.Path)

		apiUrl := fmt.Sprintf("%s%s?%s", viper.GetString("iquota_url"), RESOURCE_USER_QUOTA, params.Encode())

		qr, err := c.fetchQuota(apiUrl)
		if err != nil {
			if ierr, ok := err.(*iquota.IsiError); ok {
				if ierr.Code == "AEC_NOT_FOUND" {
					logrus.Fatal("Invalid user: ", username)
				} else if ierr.Message == "Access denied" {
					logrus.Fatal("You must be an admin user to peform this operation.")
				}
			}

			if strings.Contains(err.Error(), "No Kerberos credentials available") {
				logrus.Fatal("No Kerberos credentials available. Please run kinit")
			}

			logrus.Fatal(err)
		}

		if len(qr.Quotas) == 0 && qr.Default == nil {
			if c.Verbose {
				c.printFilesystem(fs)
				fmt.Printf("   No quota defined.\n")

			} else {
				logrus.Warn("No quotas set for filesystem: ", fs)
			}

			continue
		}

		c.printFilesystem(fs)
		if qr.Default != nil && c.Default {
			c.printDefaultQuota(qr.Default)
		}
		for _, quota := range qr.Quotas {
			c.printQuota(username, quota)
		}
	}
}

func (c *QuotaClient) printGroupQuota(username string, mounts []*Filesystem) {
	fmt.Printf("Group quotas:\n")
	c.printHeader("group")
	group := c.GroupFilter

	for _, fs := range mounts {
		if !fs.GroupQuota {
			logrus.Warn("Group quota reporting disabled for filesystem: ", fs)
			continue
		}
		params := url.Values{}
		params.Add("user", username)
		params.Add("path", fs.Path)
		if len(group) > 0 {
			params.Add("group", group)
		}

		apiUrl := fmt.Sprintf("%s%s?%s", viper.GetString("iquota_url"), RESOURCE_GROUP_QUOTA, params.Encode())

		qr, err := c.fetchQuota(apiUrl)
		if err != nil {
			if ierr, ok := err.(*iquota.IsiError); ok {
				if ierr.Code == "AEC_NOT_FOUND" {
					logrus.Fatal("Invalid group: ", group)
				} else if ierr.Message == "Access denied" {
					logrus.Fatal("You must be an admin user to peform this operation.")
				}
			}

			if strings.Contains(err.Error(), "No Kerberos credentials available") {
				logrus.Fatal("No Kerberos credentials available. Please run kinit")
			}

			logrus.Fatal(err)
		}

		if len(qr.Quotas) == 0 && qr.Default == nil {
			if c.Verbose {
				c.printFilesystem(fs)
				fmt.Printf("   No quota defined.\n")

			} else {
				logrus.Warn("No quotas set for filesystem: ", fs)
			}

			continue
		}

		c.printFilesystem(fs)
		if qr.Default != nil && c.Default {
			c.printDefaultQuota(qr.Default)
		}
		for _, quota := range qr.Quotas {
			gname := group
			if len(gname) == 0 && quota.Persona != nil {
				gname = quota.Persona.Name
			}
			c.printQuota(gname, quota)
		}
	}
}

func (c *QuotaClient) exportOverQuota(mounts []*Filesystem) {
	params := url.Values{}
	if len(mounts) == 1 {
		params.Add("path", mounts[0].Path)
	}

	apiUrl := fmt.Sprintf("%s%s?%s", viper.GetString("iquota_url"), RESOURCE_OVER_QUOTA, params.Encode())

	qr, err := c.fetchQuota(apiUrl)
	if err != nil {
		if ierr, ok := err.(*iquota.IsiError); ok {
			if ierr.Message == "Access denied" {
				logrus.Fatal("You must be an admin user to peform this operation.")
			}
		}

		if strings.Contains(err.Error(), "No Kerberos credentials available") {
			logrus.Fatal("No Kerberos credentials available. Please run kinit")
		}

		logrus.Fatal(err)
	}

	enc := json.NewEncoder(os.Stdout)
	if err := enc.Encode(qr.Quotas); err != nil {
		logrus.Fatal(err)
	}
}

func (c *QuotaClient) Run() {
	uid, err := user.Current()
	if err != nil {
		logrus.Fatal("Failed to determine user information: ", err)
	}

	username := uid.Username
	if len(c.UserFilter) != 0 {
		username = c.UserFilter
	}

	// XXX ignore mtab parsing errors for now?
	mounts, err := c.parseMtab()
	if err != nil {
		logrus.Warn("Failed to parse /etc/mtab: ", err)
	}

	path := c.Filesystem

	if len(path) == 0 && len(mounts) == 0 {
		logrus.Fatal("No path given and no nfs mounts detected. Please provide a path")
	}

	if len(path) > 0 {
		fs := &Filesystem{Path: path}
		for _, f := range mounts {
			if fs.Path == f.Path || fs.MountPoint == f.MountPoint {
				fs = f
				break
			}
		}
		mounts = []*Filesystem{fs}
	}

	if c.OverQuota {
		c.exportOverQuota(mounts)
		return
	}

	if !c.Group && len(c.GroupFilter) == 0 && (c.User || len(c.UserFilter) > 0) {
		c.printUserQuota(username, mounts)
	} else if !c.User && len(c.UserFilter) == 0 && (c.Group || len(c.GroupFilter) > 0) {
		c.printGroupQuota(username, mounts)
	} else if (c.User || len(c.UserFilter) > 0) && (c.Group || len(c.GroupFilter) > 0) {
		c.printUserQuota(username, mounts)
		fmt.Println()
		c.printGroupQuota(username, mounts)
	} else {
		c.printUserQuota(username, mounts)
	}
}
<commit_msg>Add support for nfsv4 mounts<commit_after>// Copyright 2015 iquota Authors. All rights reserved.
// Use of this source code is governed by a BSD style
// license that can be found in the LICENSE file.

package main

import (
	"bufio"
	"bytes"
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"os/user"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/dustin/go-humanize"
	"github.com/fatih/color"
	"github.com/spf13/viper"
	"github.com/ubccr/iquota"
	"github.com/ubccr/kerby/khttp"
)

const (
	RESOURCE_USER_QUOTA  = "/quota/user"
	RESOURCE_GROUP_QUOTA = "/quota/group"
	RESOURCE_OVER_QUOTA  = "/quota/exceeded"
	LONG_FORMAT          = "%-12s%-12s%15s%10s%10s%10s%10s%12s\n"
	SHORT_FORMAT         = "%-12s%-12s%15s%10s%10s%12s\n"
)

var (
	cyan   = color.New(color.FgCyan)
	green  = color.New(color.FgGreen)
	red    = color.New(color.FgRed)
	yellow = color.New(color.FgYellow)
	blue   = color.New(color.FgBlue)
)

type Filesystem struct {
	Host       string
	Path       string
	MountPoint string
	UserQuota  bool
	GroupQuota bool
}

type QuotaClient struct {
	Verbose     bool
	Group       bool
	User        bool
	Default     bool
	Long        bool
	FullPath    bool
	OverQuota   bool
	UserFilter  string
	GroupFilter string
	Filesystem  string
	certPool    *x509.CertPool
}

func (f *Filesystem) String() string {
	var buf bytes.Buffer
	if len(f.Host) > 0 {
		buf.WriteString(fmt.Sprintf("%s:", f.Host))
	}
	buf.WriteString(f.Path)
	return buf.String()
}

func (f *Filesystem) ShortString() string {
	if len(f.MountPoint) > 0 {
		return f.MountPoint
	}

	return f.Path
}

func (c *QuotaClient) format() string {
	if c.Long {
		return LONG_FORMAT
	}

	return SHORT_FORMAT
}

func (c *QuotaClient) printFilesystem(fs *Filesystem) {
	if c.FullPath {
		fmt.Printf("%s\n", fs)
		return
	}

	fmt.Printf("%s\n", fs.ShortString())
}

func (c *QuotaClient) fetchQuota(url string) (*iquota.QuotaRestResponse, error) {
	req, err := http.NewRequest("GET", url, nil)
	tr := &http.Transport{TLSClientConfig: &tls.Config{RootCAs: c.certPool}}

	// XXX should we default to this? seems a bit rash? Perhaps make this a config option
	if c.certPool == nil {
		tr.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
	}

	t := &khttp.Transport{Next: tr}
	client := &http.Client{Transport: t}

	res, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	if res.StatusCode == http.StatusInternalServerError {
		return nil, fmt.Errorf("Failed to fetch user quota with HTTP status code: %d", res.StatusCode)
	}

	rawJson, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}

	if res.StatusCode == http.StatusBadRequest {
		ierr := &iquota.IsiError{}
		err = json.Unmarshal(rawJson, ierr)
		if err != nil {
			return nil, err
		}
		return nil, ierr
	}

	if res.StatusCode != 200 {
		return nil, fmt.Errorf("Failed to fetch user quota with HTTP status code: %d", res.StatusCode)
	}

	qr := &iquota.QuotaRestResponse{}
	err = json.Unmarshal(rawJson, qr)
	if err != nil {
		return nil, err
	}

	return qr, nil
}

func (c *QuotaClient) parseMtab() ([]*Filesystem, error) {
	mounts := make([]*Filesystem, 0)

	defaultFs := viper.GetStringMapString("filesystems")

	mtab, err := os.Open("/etc/mtab")
	if err != nil {
		return nil, err
	}
	defer mtab.Close()

	scanner := bufio.NewScanner(mtab)
	for scanner.Scan() {
		fields := strings.Split(scanner.Text(), " ")
		if fields[2] == "nfs" || fields[2] == "nfs4" {
			parts := strings.Split(fields[0], ":")
			fs := &Filesystem{
				Host:       parts[0],
				Path:       parts[1],
				MountPoint: fields[1],
				UserQuota:  true,
				GroupQuota: true,
			}

			defaults, ok := defaultFs[fs.Path]
			if ok {
				fs.UserQuota = strings.Contains(defaults, "user")
				fs.GroupQuota = strings.Contains(defaults, "group")
				mounts = append(mounts, fs)
			} else if len(defaultFs) == 0 && strings.HasPrefix(fs.Path, "/ifs") {
				// XXX only include isilon mounts. Will this always be /ifs?
				mounts = append(mounts, fs)
			}
		}
	}

	if err := scanner.Err(); err != nil {
		return nil, err
	}

	return mounts, nil
}

func (c *QuotaClient) printHeader(label string) {
	if c.Long {
		fmt.Printf(c.format(), "Filesystem ", label, "files", "logical", "physical", "soft", "hard", "grace ")
		return
	}

	fmt.Printf(c.format(), "Filesystem ", label, "files", "used", "limit", "grace ")
}

func (c *QuotaClient) printQuota(name string, quota *iquota.Quota) {
	now := time.Now()
	graceTime := now.Add(time.Duration(quota.Threshold.SoftGrace) * time.Second)
	var grace string

	printer := cyan
	if quota.Threshold.SoftExceeded {
		printer = red
		grace = humanize.RelTime(
			time.Unix(int64(quota.Threshold.SoftLastExceeded), 0).Add(time.Duration(quota.Threshold.SoftGrace)*time.Second),
			now,
			"ago",
			"")
	} else {
		grace = humanize.RelTime(graceTime, now, "", "")
	}

	if c.Long {
		printer.Printf(c.format(),
			"",
			name,
			humanize.Comma(int64(quota.Usage.Inodes)),
			humanize.Bytes(uint64(quota.Usage.Logical)),
			humanize.Bytes(uint64(quota.Usage.Physical)),
			humanize.Bytes(uint64(quota.Threshold.Soft)),
			humanize.Bytes(uint64(quota.Threshold.Hard)),
			grace)

		return
	}

	printer.Printf(c.format(),
		"",
		name,
		humanize.Comma(int64(quota.Usage.Inodes)),
		humanize.Bytes(uint64(quota.Usage.Logical)),
		humanize.Bytes(uint64(quota.Threshold.Soft)),
		grace)
}

func (c *QuotaClient) printDefaultQuota(quota *iquota.Quota) {
	now := time.Now()
	graceTime := now.Add(time.Duration(quota.Threshold.SoftGrace) * time.Second)
	grace := humanize.RelTime(graceTime, now, "", "")

	if c.Long {
		yellow.Printf(c.format(),
			"",
			"(default)",
			"",
			"",
			"",
			humanize.Bytes(uint64(quota.Threshold.Soft)),
			humanize.Bytes(uint64(quota.Threshold.Hard)),
			grace)

		return
	}

	yellow.Printf(c.format(),
		"",
		"(default)",
		"",
		"",
		humanize.Bytes(uint64(quota.Threshold.Soft)),
		grace)
}

func (c *QuotaClient) printUserQuota(username string, mounts []*Filesystem) {
	fmt.Printf("User quotas:\n")
	c.printHeader("user")
	for _, fs := range mounts {
		if !fs.UserQuota {
			logrus.Warn("User quota reporting disabled for filesystem: ", fs)
			continue
		}
		params := url.Values{}
		params.Add("user", username)
		params.Add("path", fs.Path)

		apiUrl := fmt.Sprintf("%s%s?%s", viper.GetString("iquota_url"), RESOURCE_USER_QUOTA, params.Encode())

		qr, err := c.fetchQuota(apiUrl)
		if err != nil {
			if ierr, ok := err.(*iquota.IsiError); ok {
				if ierr.Code == "AEC_NOT_FOUND" {
					logrus.Fatal("Invalid user: ", username)
				} else if ierr.Message == "Access denied" {
					logrus.Fatal("You must be an admin user to peform this operation.")
				}
			}

			if strings.Contains(err.Error(), "No Kerberos credentials available") {
				logrus.Fatal("No Kerberos credentials available. Please run kinit")
			}

			logrus.Fatal(err)
		}

		if len(qr.Quotas) == 0 && qr.Default == nil {
			if c.Verbose {
				c.printFilesystem(fs)
				fmt.Printf("   No quota defined.\n")

			} else {
				logrus.Warn("No quotas set for filesystem: ", fs)
			}

			continue
		}

		c.printFilesystem(fs)
		if qr.Default != nil && c.Default {
			c.printDefaultQuota(qr.Default)
		}
		for _, quota := range qr.Quotas {
			c.printQuota(username, quota)
		}
	}
}

func (c *QuotaClient) printGroupQuota(username string, mounts []*Filesystem) {
	fmt.Printf("Group quotas:\n")
	c.printHeader("group")
	group := c.GroupFilter

	for _, fs := range mounts {
		if !fs.GroupQuota {
			logrus.Warn("Group quota reporting disabled for filesystem: ", fs)
			continue
		}
		params := url.Values{}
		params.Add("user", username)
		params.Add("path", fs.Path)
		if len(group) > 0 {
			params.Add("group", group)
		}

		apiUrl := fmt.Sprintf("%s%s?%s", viper.GetString("iquota_url"), RESOURCE_GROUP_QUOTA, params.Encode())

		qr, err := c.fetchQuota(apiUrl)
		if err != nil {
			if ierr, ok := err.(*iquota.IsiError); ok {
				if ierr.Code == "AEC_NOT_FOUND" {
					logrus.Fatal("Invalid group: ", group)
				} else if ierr.Message == "Access denied" {
					logrus.Fatal("You must be an admin user to peform this operation.")
				}
			}

			if strings.Contains(err.Error(), "No Kerberos credentials available") {
				logrus.Fatal("No Kerberos credentials available. Please run kinit")
			}

			logrus.Fatal(err)
		}

		if len(qr.Quotas) == 0 && qr.Default == nil {
			if c.Verbose {
				c.printFilesystem(fs)
				fmt.Printf("   No quota defined.\n")

			} else {
				logrus.Warn("No quotas set for filesystem: ", fs)
			}

			continue
		}

		c.printFilesystem(fs)
		if qr.Default != nil && c.Default {
			c.printDefaultQuota(qr.Default)
		}
		for _, quota := range qr.Quotas {
			gname := group
			if len(gname) == 0 && quota.Persona != nil {
				gname = quota.Persona.Name
			}
			c.printQuota(gname, quota)
		}
	}
}

func (c *QuotaClient) exportOverQuota(mounts []*Filesystem) {
	params := url.Values{}
	if len(mounts) == 1 {
		params.Add("path", mounts[0].Path)
	}

	apiUrl := fmt.Sprintf("%s%s?%s", viper.GetString("iquota_url"), RESOURCE_OVER_QUOTA, params.Encode())

	qr, err := c.fetchQuota(apiUrl)
	if err != nil {
		if ierr, ok := err.(*iquota.IsiError); ok {
			if ierr.Message == "Access denied" {
				logrus.Fatal("You must be an admin user to peform this operation.")
			}
		}

		if strings.Contains(err.Error(), "No Kerberos credentials available") {
			logrus.Fatal("No Kerberos credentials available. Please run kinit")
		}

		logrus.Fatal(err)
	}

	enc := json.NewEncoder(os.Stdout)
	if err := enc.Encode(qr.Quotas); err != nil {
		logrus.Fatal(err)
	}
}

func (c *QuotaClient) Run() {
	uid, err := user.Current()
	if err != nil {
		logrus.Fatal("Failed to determine user information: ", err)
	}

	username := uid.Username
	if len(c.UserFilter) != 0 {
		username = c.UserFilter
	}

	// XXX ignore mtab parsing errors for now?
	mounts, err := c.parseMtab()
	if err != nil {
		logrus.Warn("Failed to parse /etc/mtab: ", err)
	}

	path := c.Filesystem

	if len(path) == 0 && len(mounts) == 0 {
		logrus.Fatal("No path given and no nfs mounts detected. Please provide a path")
	}

	if len(path) > 0 {
		fs := &Filesystem{Path: path}
		for _, f := range mounts {
			if fs.Path == f.Path || fs.MountPoint == f.MountPoint {
				fs = f
				break
			}
		}
		mounts = []*Filesystem{fs}
	}

	if c.OverQuota {
		c.exportOverQuota(mounts)
		return
	}

	if !c.Group && len(c.GroupFilter) == 0 && (c.User || len(c.UserFilter) > 0) {
		c.printUserQuota(username, mounts)
	} else if !c.User && len(c.UserFilter) == 0 && (c.Group || len(c.GroupFilter) > 0) {
		c.printGroupQuota(username, mounts)
	} else if (c.User || len(c.UserFilter) > 0) && (c.Group || len(c.GroupFilter) > 0) {
		c.printUserQuota(username, mounts)
		fmt.Println()
		c.printGroupQuota(username, mounts)
	} else {
		c.printUserQuota(username, mounts)
	}
}
<|endoftext|>