<commit_before>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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 util

import (
	"fmt"
	"time"

	"github.com/golang/glog"
	"github.com/pkg/errors"
	appsv1 "k8s.io/api/apps/v1"
	"k8s.io/api/core/v1"
	apierrs "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/kubernetes/cmd/kubeadm/app/constants"
)

var (
	ReasonableMutateTime = time.Minute * 1
	ReasonableStartTime  = time.Minute * 5
)

type PodStore struct {
	cache.Store
	stopCh    chan struct{}
	Reflector *cache.Reflector
}

func (s *PodStore) List() []*v1.Pod {
	objects := s.Store.List()
	pods := make([]*v1.Pod, 0)
	for _, o := range objects {
		pods = append(pods, o.(*v1.Pod))
	}
	return pods
}

func (s *PodStore) Stop() {
	close(s.stopCh)
}

func GetClient() (kubernetes.Interface, error) {
	loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
	configOverrides := &clientcmd.ConfigOverrides{}
	kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides)
	config, err := kubeConfig.ClientConfig()
	if err != nil {
		return nil, fmt.Errorf("Error creating kubeConfig: %v", err)
	}
	client, err := kubernetes.NewForConfig(config)
	if err != nil {
		return nil, errors.Wrap(err, "Error creating new client from kubeConfig.ClientConfig()")
	}
	return client, nil
}

func NewPodStore(c kubernetes.Interface, namespace string, label labels.Selector, field fields.Selector) *PodStore {
	lw := &cache.ListWatch{
		ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
			options.LabelSelector = label.String()
			options.FieldSelector = field.String()
			obj, err := c.Core().Pods(namespace).List(options)
			return runtime.Object(obj), err
		},
		WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
			options.LabelSelector = label.String()
			options.FieldSelector = field.String()
			return c.Core().Pods(namespace).Watch(options)
		},
	}
	store := cache.NewStore(cache.MetaNamespaceKeyFunc)
	stopCh := make(chan struct{})
	reflector := cache.NewReflector(lw, &v1.Pod{}, store, 0)
	go reflector.Run(stopCh)
	return &PodStore{Store: store, stopCh: stopCh, Reflector: reflector}
}

func StartPods(c kubernetes.Interface, namespace string, pod v1.Pod, waitForRunning bool) error {
	pod.ObjectMeta.Labels["name"] = pod.Name
	if waitForRunning {
		label := labels.SelectorFromSet(labels.Set(map[string]string{"name": pod.Name}))
		err := WaitForPodsWithLabelRunning(c, namespace, label)
		if err != nil {
			return fmt.Errorf("Error waiting for pod %s to be running: %v", pod.Name, err)
		}
	}
	return nil
}

// WaitForPodsWithLabelRunning waits for all matching pods to become Running and at least one matching pod exists.
func WaitForPodsWithLabelRunning(c kubernetes.Interface, ns string, label labels.Selector) error {
	glog.Infof("Waiting for pod with label %q in ns %q ...", ns, label)
	lastKnownPodNumber := -1
	return wait.PollImmediate(constants.APICallRetryInterval, ReasonableStartTime, func() (bool, error) {
		listOpts := metav1.ListOptions{LabelSelector: label.String()}
		pods, err := c.CoreV1().Pods(ns).List(listOpts)
		if err != nil {
			glog.Infof("error getting Pods with label selector %q [%v]\n", label.String(), err)
			return false, nil
		}

		if lastKnownPodNumber != len(pods.Items) {
			glog.Infof("Found %d Pods for label selector %s\n", len(pods.Items), label.String())
			lastKnownPodNumber = len(pods.Items)
		}

		if len(pods.Items) == 0 {
			return false, nil
		}

		for _, pod := range pods.Items {
			if pod.Status.Phase != v1.PodRunning {
				return false, nil
			}
		}

		return true, nil
	})
}

// WaitForPodDelete waits for a pod to be deleted
func WaitForPodDelete(c kubernetes.Interface, ns string, label labels.Selector) error {
	return wait.PollImmediate(constants.APICallRetryInterval, ReasonableMutateTime, func() (bool, error) {
		listOpts := metav1.ListOptions{LabelSelector: label.String()}
		pods, err := c.CoreV1().Pods(ns).List(listOpts)
		if err != nil {
			glog.Infof("error getting Pods with label selector %q [%v]\n", label.String(), err)
			return false, nil
		}
		return len(pods.Items) == 0, nil
	})
}

// WaitForEvent waits for the given event to appear
func WaitForEvent(c kubernetes.Interface, ns string, reason string) error {
	return wait.PollImmediate(constants.APICallRetryInterval, ReasonableMutateTime, func() (bool, error) {
		events, err := c.Events().Events("default").List(metav1.ListOptions{})
		if err != nil {
			glog.Infof("error getting events: %v", err)
			return false, nil
		}
		for _, e := range events.Items {
			if e.Reason == reason {
				return true, nil
			}
		}
		return false, nil
	})
}

// WaitForRCToStabilize waits till the RC has a matching generation/replica count between spec and status.
func WaitForRCToStabilize(c kubernetes.Interface, ns, name string, timeout time.Duration) error {
	options := metav1.ListOptions{FieldSelector: fields.Set{
		"metadata.name":      name,
		"metadata.namespace": ns,
	}.AsSelector().String()}
	w, err := c.Core().ReplicationControllers(ns).Watch(options)
	if err != nil {
		return err
	}
	_, err = watch.Until(timeout, w, func(event watch.Event) (bool, error) {
		switch event.Type {
		case watch.Deleted:
			return false, apierrs.NewNotFound(schema.GroupResource{Resource: "replicationcontrollers"}, "")
		}
		switch rc := event.Object.(type) {
		case *v1.ReplicationController:
			if rc.Name == name && rc.Namespace == ns &&
				rc.Generation <= rc.Status.ObservedGeneration &&
				*(rc.Spec.Replicas) == rc.Status.Replicas {
				return true, nil
			}
			glog.Infof("Waiting for rc %s to stabilize, generation %v observed generation %v spec.replicas %d status.replicas %d",
				name, rc.Generation, rc.Status.ObservedGeneration, *(rc.Spec.Replicas), rc.Status.Replicas)
		}
		return false, nil
	})
	return err
}

// WaitForDeploymentToStabilize waits till the Deployment has a matching generation/replica count between spec and status.
func WaitForDeploymentToStabilize(c kubernetes.Interface, ns, name string, timeout time.Duration) error {
	options := metav1.ListOptions{FieldSelector: fields.Set{
		"metadata.name":      name,
		"metadata.namespace": ns,
	}.AsSelector().String()}
	w, err := c.AppsV1().Deployments(ns).Watch(options)
	if err != nil {
		return err
	}
	_, err = watch.Until(timeout, w, func(event watch.Event) (bool, error) {
		switch event.Type {
		case watch.Deleted:
			return false, apierrs.NewNotFound(schema.GroupResource{Resource: "deployments"}, "")
		}
		switch dp := event.Object.(type) {
		case *appsv1.Deployment:
			if dp.Name == name && dp.Namespace == ns &&
				dp.Generation <= dp.Status.ObservedGeneration &&
				*(dp.Spec.Replicas) == dp.Status.Replicas {
				return true, nil
			}
			glog.Infof("Waiting for deployment %s to stabilize, generation %v observed generation %v spec.replicas %d status.replicas %d",
				name, dp.Generation, dp.Status.ObservedGeneration, *(dp.Spec.Replicas), dp.Status.Replicas)
		}
		return false, nil
	})
	return err
}

// WaitForService waits until the service appears (exist == true), or disappears (exist == false)
func WaitForService(c kubernetes.Interface, namespace, name string, exist bool, interval, timeout time.Duration) error {
	err := wait.PollImmediate(interval, timeout, func() (bool, error) {
		_, err := c.Core().Services(namespace).Get(name, metav1.GetOptions{})
		switch {
		case err == nil:
			glog.Infof("Service %s in namespace %s found.", name, namespace)
			return exist, nil
		case apierrs.IsNotFound(err):
			glog.Infof("Service %s in namespace %s disappeared.", name, namespace)
			return !exist, nil
		case !IsRetryableAPIError(err):
			glog.Info("Non-retryable failure while getting service.")
			return false, err
		default:
			glog.Infof("Get service %s in namespace %s failed: %v", name, namespace, err)
			return false, nil
		}
	})
	if err != nil {
		stateMsg := map[bool]string{true: "to appear", false: "to disappear"}
		return fmt.Errorf("error waiting for service %s/%s %s: %v", namespace, name, stateMsg[exist], err)
	}
	return nil
}

//WaitForServiceEndpointsNum waits until the amount of endpoints that implement service to expectNum.
func WaitForServiceEndpointsNum(c kubernetes.Interface, namespace, serviceName string, expectNum int, interval, timeout time.Duration) error {
	return wait.Poll(interval, timeout, func() (bool, error) {
		glog.Infof("Waiting for amount of service:%s endpoints to be %d", serviceName, expectNum)
		list, err := c.Core().Endpoints(namespace).List(metav1.ListOptions{})
		if err != nil {
			return false, err
		}

		for _, e := range list.Items {
			if e.Name == serviceName && countEndpointsNum(&e) == expectNum {
				return true, nil
			}
		}
		return false, nil
	})
}

func countEndpointsNum(e *v1.Endpoints) int {
	num := 0
	for _, sub := range e.Subsets {
		num += len(sub.Addresses)
	}
	return num
}

func IsRetryableAPIError(err error) bool {
	return apierrs.IsTimeout(err) || apierrs.IsServerTimeout(err) || apierrs.IsTooManyRequests(err) || apierrs.IsInternalError(err)
}
<commit_msg>Increase ReasonableStartTime from 5 to 9 minutes<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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 util

import (
	"fmt"
	"time"

	"github.com/golang/glog"
	"github.com/pkg/errors"
	appsv1 "k8s.io/api/apps/v1"
	v1 "k8s.io/api/core/v1"
	apierrs "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/kubernetes/cmd/kubeadm/app/constants"
)

var (
	// ReasonableMutateTime is how long to wait for basic object mutations, such as deletions, to show up
	ReasonableMutateTime = time.Minute * 1
	// ReasonableStartTime is how long to wait for pods to start, considering dependency chains & slow networks.
	ReasonableStartTime = time.Minute * 9
)

type PodStore struct {
	cache.Store
	stopCh    chan struct{}
	Reflector *cache.Reflector
}

func (s *PodStore) List() []*v1.Pod {
	objects := s.Store.List()
	pods := make([]*v1.Pod, 0)
	for _, o := range objects {
		pods = append(pods, o.(*v1.Pod))
	}
	return pods
}

func (s *PodStore) Stop() {
	close(s.stopCh)
}

func GetClient() (kubernetes.Interface, error) {
	loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
	configOverrides := &clientcmd.ConfigOverrides{}
	kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides)
	config, err := kubeConfig.ClientConfig()
	if err != nil {
		return nil, fmt.Errorf("Error creating kubeConfig: %v", err)
	}
	client, err := kubernetes.NewForConfig(config)
	if err != nil {
		return nil, errors.Wrap(err, "Error creating new client from kubeConfig.ClientConfig()")
	}
	return client, nil
}

func NewPodStore(c kubernetes.Interface, namespace string, label labels.Selector, field fields.Selector) *PodStore {
	lw := &cache.ListWatch{
		ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
			options.LabelSelector = label.String()
			options.FieldSelector = field.String()
			obj, err := c.Core().Pods(namespace).List(options)
			return runtime.Object(obj), err
		},
		WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
			options.LabelSelector = label.String()
			options.FieldSelector = field.String()
			return c.Core().Pods(namespace).Watch(options)
		},
	}
	store := cache.NewStore(cache.MetaNamespaceKeyFunc)
	stopCh := make(chan struct{})
	reflector := cache.NewReflector(lw, &v1.Pod{}, store, 0)
	go reflector.Run(stopCh)
	return &PodStore{Store: store, stopCh: stopCh, Reflector: reflector}
}

func StartPods(c kubernetes.Interface, namespace string, pod v1.Pod, waitForRunning bool) error {
	pod.ObjectMeta.Labels["name"] = pod.Name
	if waitForRunning {
		label := labels.SelectorFromSet(labels.Set(map[string]string{"name": pod.Name}))
		err := WaitForPodsWithLabelRunning(c, namespace, label)
		if err != nil {
			return fmt.Errorf("Error waiting for pod %s to be running: %v", pod.Name, err)
		}
	}
	return nil
}

// WaitForPodsWithLabelRunning waits for all matching pods to become Running and at least one matching pod exists.
func WaitForPodsWithLabelRunning(c kubernetes.Interface, ns string, label labels.Selector) error {
	glog.Infof("Waiting for pod with label %q in ns %q ...", ns, label)
	lastKnownPodNumber := -1
	return wait.PollImmediate(constants.APICallRetryInterval, ReasonableStartTime, func() (bool, error) {
		listOpts := metav1.ListOptions{LabelSelector: label.String()}
		pods, err := c.CoreV1().Pods(ns).List(listOpts)
		if err != nil {
			glog.Infof("error getting Pods with label selector %q [%v]\n", label.String(), err)
			return false, nil
		}

		if lastKnownPodNumber != len(pods.Items) {
			glog.Infof("Found %d Pods for label selector %s\n", len(pods.Items), label.String())
			lastKnownPodNumber = len(pods.Items)
		}

		if len(pods.Items) == 0 {
			return false, nil
		}

		for _, pod := range pods.Items {
			if pod.Status.Phase != v1.PodRunning {
				return false, nil
			}
		}

		return true, nil
	})
}

// WaitForPodDelete waits for a pod to be deleted
func WaitForPodDelete(c kubernetes.Interface, ns string, label labels.Selector) error {
	return wait.PollImmediate(constants.APICallRetryInterval, ReasonableMutateTime, func() (bool, error) {
		listOpts := metav1.ListOptions{LabelSelector: label.String()}
		pods, err := c.CoreV1().Pods(ns).List(listOpts)
		if err != nil {
			glog.Infof("error getting Pods with label selector %q [%v]\n", label.String(), err)
			return false, nil
		}
		return len(pods.Items) == 0, nil
	})
}

// WaitForEvent waits for the given event to appear
func WaitForEvent(c kubernetes.Interface, ns string, reason string) error {
	return wait.PollImmediate(constants.APICallRetryInterval, ReasonableMutateTime, func() (bool, error) {
		events, err := c.Events().Events("default").List(metav1.ListOptions{})
		if err != nil {
			glog.Infof("error getting events: %v", err)
			return false, nil
		}
		for _, e := range events.Items {
			if e.Reason == reason {
				return true, nil
			}
		}
		return false, nil
	})
}

// WaitForRCToStabilize waits till the RC has a matching generation/replica count between spec and status.
func WaitForRCToStabilize(c kubernetes.Interface, ns, name string, timeout time.Duration) error {
	options := metav1.ListOptions{FieldSelector: fields.Set{
		"metadata.name":      name,
		"metadata.namespace": ns,
	}.AsSelector().String()}
	w, err := c.Core().ReplicationControllers(ns).Watch(options)
	if err != nil {
		return err
	}
	_, err = watch.Until(timeout, w, func(event watch.Event) (bool, error) {
		switch event.Type {
		case watch.Deleted:
			return false, apierrs.NewNotFound(schema.GroupResource{Resource: "replicationcontrollers"}, "")
		}
		switch rc := event.Object.(type) {
		case *v1.ReplicationController:
			if rc.Name == name && rc.Namespace == ns &&
				rc.Generation <= rc.Status.ObservedGeneration &&
				*(rc.Spec.Replicas) == rc.Status.Replicas {
				return true, nil
			}
			glog.Infof("Waiting for rc %s to stabilize, generation %v observed generation %v spec.replicas %d status.replicas %d",
				name, rc.Generation, rc.Status.ObservedGeneration, *(rc.Spec.Replicas), rc.Status.Replicas)
		}
		return false, nil
	})
	return err
}

// WaitForDeploymentToStabilize waits till the Deployment has a matching generation/replica count between spec and status.
func WaitForDeploymentToStabilize(c kubernetes.Interface, ns, name string, timeout time.Duration) error {
	options := metav1.ListOptions{FieldSelector: fields.Set{
		"metadata.name":      name,
		"metadata.namespace": ns,
	}.AsSelector().String()}
	w, err := c.AppsV1().Deployments(ns).Watch(options)
	if err != nil {
		return err
	}
	_, err = watch.Until(timeout, w, func(event watch.Event) (bool, error) {
		switch event.Type {
		case watch.Deleted:
			return false, apierrs.NewNotFound(schema.GroupResource{Resource: "deployments"}, "")
		}
		switch dp := event.Object.(type) {
		case *appsv1.Deployment:
			if dp.Name == name && dp.Namespace == ns &&
				dp.Generation <= dp.Status.ObservedGeneration &&
				*(dp.Spec.Replicas) == dp.Status.Replicas {
				return true, nil
			}
			glog.Infof("Waiting for deployment %s to stabilize, generation %v observed generation %v spec.replicas %d status.replicas %d",
				name, dp.Generation, dp.Status.ObservedGeneration, *(dp.Spec.Replicas), dp.Status.Replicas)
		}
		return false, nil
	})
	return err
}

// WaitForService waits until the service appears (exist == true), or disappears (exist == false)
func WaitForService(c kubernetes.Interface, namespace, name string, exist bool, interval, timeout time.Duration) error {
	err := wait.PollImmediate(interval, timeout, func() (bool, error) {
		_, err := c.Core().Services(namespace).Get(name, metav1.GetOptions{})
		switch {
		case err == nil:
			glog.Infof("Service %s in namespace %s found.", name, namespace)
			return exist, nil
		case apierrs.IsNotFound(err):
			glog.Infof("Service %s in namespace %s disappeared.", name, namespace)
			return !exist, nil
		case !IsRetryableAPIError(err):
			glog.Info("Non-retryable failure while getting service.")
			return false, err
		default:
			glog.Infof("Get service %s in namespace %s failed: %v", name, namespace, err)
			return false, nil
		}
	})
	if err != nil {
		stateMsg := map[bool]string{true: "to appear", false: "to disappear"}
		return fmt.Errorf("error waiting for service %s/%s %s: %v", namespace, name, stateMsg[exist], err)
	}
	return nil
}

//WaitForServiceEndpointsNum waits until the amount of endpoints that implement service to expectNum.
func WaitForServiceEndpointsNum(c kubernetes.Interface, namespace, serviceName string, expectNum int, interval, timeout time.Duration) error {
	return wait.Poll(interval, timeout, func() (bool, error) {
		glog.Infof("Waiting for amount of service:%s endpoints to be %d", serviceName, expectNum)
		list, err := c.Core().Endpoints(namespace).List(metav1.ListOptions{})
		if err != nil {
			return false, err
		}

		for _, e := range list.Items {
			if e.Name == serviceName && countEndpointsNum(&e) == expectNum {
				return true, nil
			}
		}
		return false, nil
	})
}

func countEndpointsNum(e *v1.Endpoints) int {
	num := 0
	for _, sub := range e.Subsets {
		num += len(sub.Addresses)
	}
	return num
}

func IsRetryableAPIError(err error) bool {
	return apierrs.IsTimeout(err) || apierrs.IsServerTimeout(err) || apierrs.IsTooManyRequests(err) || apierrs.IsInternalError(err)
}
<|endoftext|>


<commit_before>package backend

import (
	//"github.com/russross/blackfriday"
	"gopkg.in/russross/blackfriday.v1"
	// TODO change to v2
)

func (b *backend) Render(input []byte) ([]byte, error) {
	flag := 0 |
		blackfriday.HTML_USE_XHTML |
		blackfriday.HTML_USE_SMARTYPANTS |
		blackfriday.HTML_SMARTYPANTS_FRACTIONS |
		blackfriday.HTML_SMARTYPANTS_DASHES |
		blackfriday.HTML_SMARTYPANTS_LATEX_DASHES |
		blackfriday.HTML_HREF_TARGET_BLANK
	ext := 0 |
		blackfriday.EXTENSION_NO_INTRA_EMPHASIS |
		blackfriday.EXTENSION_TABLES |
		blackfriday.EXTENSION_FENCED_CODE |
		blackfriday.EXTENSION_AUTOLINK |
		blackfriday.EXTENSION_STRIKETHROUGH |
		blackfriday.EXTENSION_SPACE_HEADERS |
		blackfriday.EXTENSION_HEADER_IDS |
		blackfriday.EXTENSION_BACKSLASH_LINE_BREAK |
		blackfriday.EXTENSION_DEFINITION_LISTS |
		blackfriday.EXTENSION_FOOTNOTES |
		blackfriday.EXTENSION_AUTO_HEADER_IDS
	renderer := blackfriday.HtmlRenderer(flag, "", "")
	return blackfriday.MarkdownOptions(input, renderer, blackfriday.Options{
		Extensions: ext,
	}), nil
}
<commit_msg>Change renderer<commit_after>package backend

import (
	//"gopkg.in/russross/blackfriday.v2"
	//"github.com/russross/blackfriday"
	"github.com/russross/blackfriday/v2"
)

func (b *backend) Render(input []byte) ([]byte, error) {
	return blackfriday.Run(
		input,
		blackfriday.WithExtensions(blackfriday.CommonExtensions|blackfriday.Footnotes|blackfriday.AutoHeadingIDs),
		blackfriday.WithRenderer(
			blackfriday.NewHTMLRenderer(blackfriday.HTMLRendererParameters{
				Flags: blackfriday.CommonHTMLFlags | blackfriday.HrefTargetBlank, //| TOC
			}),
		),
	), nil
	/*
		flag := 0 |
			blackfriday.HTML_USE_XHTML |
			blackfriday.HTML_USE_SMARTYPANTS |
			blackfriday.HTML_SMARTYPANTS_FRACTIONS |
			blackfriday.HTML_SMARTYPANTS_DASHES |
			blackfriday.HTML_SMARTYPANTS_LATEX_DASHES |
			blackfriday.HTML_HREF_TARGET_BLANK
		ext := 0 |
			blackfriday.EXTENSION_NO_INTRA_EMPHASIS |
			blackfriday.EXTENSION_TABLES |
			blackfriday.EXTENSION_FENCED_CODE |
			blackfriday.EXTENSION_AUTOLINK |
			blackfriday.EXTENSION_STRIKETHROUGH |
			blackfriday.EXTENSION_SPACE_HEADERS |
			blackfriday.EXTENSION_HEADER_IDS |
			blackfriday.EXTENSION_BACKSLASH_LINE_BREAK |
			blackfriday.EXTENSION_DEFINITION_LISTS |
			blackfriday.EXTENSION_FOOTNOTES |
			blackfriday.EXTENSION_AUTO_HEADER_IDS
		renderer := blackfriday.HtmlRenderer(flag, "", "")
		return blackfriday.MarkdownOptions(input, renderer, blackfriday.Options{
			Extensions: ext,
		}), nil
	*/
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package gce

import (
	"fmt"
	"net/http"
	"path"

	"k8s.io/apimachinery/pkg/types"
	"k8s.io/kubernetes/pkg/cloudprovider"

	"github.com/golang/glog"
	compute "google.golang.org/api/compute/v1"
)

func newRoutesMetricContext(request string) *metricContext {
	return newGenericMetricContext("routes", request, unusedMetricLabel, unusedMetricLabel, computeV1Version)
}

func (gce *GCECloud) ListRoutes(clusterName string) ([]*cloudprovider.Route, error) {
	var routes []*cloudprovider.Route
	pageToken := ""
	page := 0
	for ; page == 0 || (pageToken != "" && page < maxPages); page++ {
		mc := newRoutesMetricContext("list_page")
		listCall := gce.service.Routes.List(gce.NetworkProjectID())

		prefix := truncateClusterName(clusterName)
		// Filter for routes starting with clustername AND belonging to the
		// relevant gcp network AND having description = "k8s-node-route".
		filter := "(name eq " + prefix + "-.*) "
		filter = filter + "(network eq " + gce.NetworkURL() + ") "
		filter = filter + "(description eq " + k8sNodeRouteTag + ")"
		listCall = listCall.Filter(filter)
		if pageToken != "" {
			listCall = listCall.PageToken(pageToken)
		}
		res, err := listCall.Do()
		mc.Observe(err)
		if err != nil {
			glog.Errorf("Error getting routes from GCE: %v", err)
			return nil, err
		}
		pageToken = res.NextPageToken
		for _, r := range res.Items {
			target := path.Base(r.NextHopInstance)
			// TODO: Should we lastComponent(target) this?
			targetNodeName := types.NodeName(target) // NodeName == Instance Name on GCE
			routes = append(routes, &cloudprovider.Route{Name: r.Name, TargetNode: targetNodeName, DestinationCIDR: r.DestRange})
		}
	}
	if page >= maxPages {
		glog.Errorf("ListRoutes exceeded maxPages=%d for Routes.List; truncating.", maxPages)
	}
	return routes, nil
}

func (gce *GCECloud) CreateRoute(clusterName string, nameHint string, route *cloudprovider.Route) error {
	routeName := truncateClusterName(clusterName) + "-" + nameHint

	instanceName := mapNodeNameToInstanceName(route.TargetNode)
	targetInstance, err := gce.getInstanceByName(instanceName)
	if err != nil {
		return err
	}

	mc := newRoutesMetricContext("create")
	insertOp, err := gce.service.Routes.Insert(gce.NetworkProjectID(), &compute.Route{
		Name:            routeName,
		DestRange:       route.DestinationCIDR,
		NextHopInstance: fmt.Sprintf("zones/%s/instances/%s", targetInstance.Zone, targetInstance.Name),
		Network:         gce.NetworkURL(),
		Priority:        1000,
		Description:     k8sNodeRouteTag,
	}).Do()
	if err != nil {
		if isHTTPErrorCode(err, http.StatusConflict) {
			glog.Infof("Route %v already exists.", routeName)
			return nil
		} else {
			return mc.Observe(err)
		}
	}
	return gce.waitForGlobalOpInProject(insertOp, gce.NetworkProjectID(), mc)
}

func (gce *GCECloud) DeleteRoute(clusterName string, route *cloudprovider.Route) error {
	mc := newRoutesMetricContext("delete")
	deleteOp, err := gce.service.Routes.Delete(gce.NetworkProjectID(), route.Name).Do()
	if err != nil {
		return mc.Observe(err)
	}
	return gce.waitForGlobalOpInProject(deleteOp, gce.NetworkProjectID(), mc)
}

func truncateClusterName(clusterName string) string {
	if len(clusterName) > 26 {
		return clusterName[:26]
	}
	return clusterName
}
<commit_msg>Update Routes to use generated code<commit_after>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package gce

import (
	"context"
	"fmt"
	"net/http"
	"path"

	"github.com/golang/glog"
	compute "google.golang.org/api/compute/v1"
	"k8s.io/apimachinery/pkg/types"

	"k8s.io/kubernetes/pkg/cloudprovider"
	"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/filter"
	"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/meta"
)

func newRoutesMetricContext(request string) *metricContext {
	return newGenericMetricContext("routes", request, unusedMetricLabel, unusedMetricLabel, computeV1Version)
}

// ListRoutes in the cloud environment.
func (gce *GCECloud) ListRoutes(clusterName string) ([]*cloudprovider.Route, error) {
	mc := newRoutesMetricContext("list")
	prefix := truncateClusterName(clusterName)
	f := filter.Regexp("name", prefix+"-.*").AndRegexp("network", gce.NetworkURL()).AndRegexp("description", k8sNodeRouteTag)
	routes, err := gce.c.Routes().List(context.Background(), f)
	if err != nil {
		return nil, mc.Observe(err)
	}
	var croutes []*cloudprovider.Route
	for _, r := range routes {
		target := path.Base(r.NextHopInstance)
		// TODO: Should we lastComponent(target) this?
		targetNodeName := types.NodeName(target) // NodeName == Instance Name on GCE
		croutes = append(croutes, &cloudprovider.Route{
			Name:            r.Name,
			TargetNode:      targetNodeName,
			DestinationCIDR: r.DestRange,
		})
	}
	return croutes, mc.Observe(nil)
}

// CreateRoute in the cloud environment.
func (gce *GCECloud) CreateRoute(clusterName string, nameHint string, route *cloudprovider.Route) error {
	mc := newRoutesMetricContext("create")

	targetInstance, err := gce.getInstanceByName(mapNodeNameToInstanceName(route.TargetNode))
	if err != nil {
		return mc.Observe(err)
	}
	cr := &compute.Route{
		Name:            truncateClusterName(clusterName) + "-" + nameHint,
		DestRange:       route.DestinationCIDR,
		NextHopInstance: fmt.Sprintf("zones/%s/instances/%s", targetInstance.Zone, targetInstance.Name),
		Network:         gce.NetworkURL(),
		Priority:        1000,
		Description:     k8sNodeRouteTag,
	}
	err = gce.c.Routes().Insert(context.Background(), meta.GlobalKey(cr.Name), cr)
	if isHTTPErrorCode(err, http.StatusConflict) {
		glog.Infof("Route %q already exists.", cr.Name)
		err = nil
	}
	return mc.Observe(err)
}

// DeleteRoute from the cloud environment.
func (gce *GCECloud) DeleteRoute(clusterName string, route *cloudprovider.Route) error {
	mc := newRoutesMetricContext("delete")
	return mc.Observe(gce.c.Routes().Delete(context.Background(), meta.GlobalKey(route.Name)))
}

func truncateClusterName(clusterName string) string {
	if len(clusterName) > 26 {
		return clusterName[:26]
	}
	return clusterName
}
<|endoftext|>


<commit_before>// +build !luminous,!mimic

package admin

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"testing"
	"time"

	"github.com/ceph/go-ceph/cephfs"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

var snapDir = ".snapshots"

func fsConnect(t *testing.T) *cephfs.MountInfo {
	mount, err := cephfs.CreateMount()
	require.NoError(t, err)
	require.NotNil(t, mount)

	err = mount.ReadDefaultConfigFile()
	require.NoError(t, err)
	err = mount.SetConfigOption("client_snapdir", snapDir)
	require.NoError(t, err)

	timeout := time.After(time.Second * 5)
	ch := make(chan error)
	go func(mount *cephfs.MountInfo) {
		ch <- mount.Mount()
	}(mount)
	select {
	case err = <-ch:
	case <-timeout:
		err = fmt.Errorf("timed out waiting for connect")
	}
	require.NoError(t, err)
	return mount
}

func writeFile(t *testing.T, mount *cephfs.MountInfo, path string, content []byte) {
	f1, err := mount.Open(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0700)
	require.NoError(t, err)
	defer f1.Close()
	f1.WriteAt(content, 0)
}

func readFile(t *testing.T, mount *cephfs.MountInfo, path string) []byte {
	f1, err := mount.Open(path, os.O_RDONLY, 0)
	require.NoError(t, err)
	defer f1.Close()
	b, err := ioutil.ReadAll(f1)
	require.NoError(t, err)
	return b
}

func getSnapPath(t *testing.T, mount *cephfs.MountInfo, subvol, snapname string) string {
	// I wish there was a nicer way to do this
	snapPath := path.Join(subvol, snapDir, snapname)
	_, err := mount.Statx(snapPath, cephfs.StatxBasicStats, 0)
	if err == nil {
		return snapPath
	}
	snapPath = path.Join(path.Dir(subvol), snapDir, snapname)
	_, err = mount.Statx(snapPath, cephfs.StatxBasicStats, 0)
	if err == nil {
		return snapPath
	}
	t.Fatalf("did not find a snap path for %s", snapname)
	return ""
}

// TestWorkflow aims to do more than just exercise the API calls, but rather to
// also check that they do what they say on the tin.  This means importing the
// cephfs lib in addition to the admin lib and reading and writing to the
// subvolume, snapshot, and clone as appropriate.
func TestWorkflow(t *testing.T) {
	fsa := getFSAdmin(t)
	volume := "cephfs"
	group := "workflow1"

	// verify the volume we want to use
	l, err := fsa.ListVolumes()
	require.NoError(t, err)
	require.Contains(t, l, volume)

	err = fsa.CreateSubVolumeGroup(volume, group, nil)
	require.NoError(t, err)
	defer func() {
		err := fsa.RemoveSubVolumeGroup(volume, group)
		assert.NoError(t, err)
	}()

	subname := "files1"
	svopts := &SubVolumeOptions{
		Mode: 0777,
		Size: 2 * gibiByte,
	}
	err = fsa.CreateSubVolume(volume, group, subname, svopts)
	require.NoError(t, err)
	defer func() {
		err := fsa.RemoveSubVolume(volume, group, subname)
		assert.NoError(t, err)
	}()

	// getpath
	subPath, err := fsa.SubVolumePath(volume, group, subname)
	require.NoError(t, err)
	require.NotEqual(t, "", subPath)

	// connect to volume, cd to path (?)
	mount := fsConnect(t)
	defer func(mount *cephfs.MountInfo) {
		assert.NoError(t, mount.Unmount())
		assert.NoError(t, mount.Release())
	}(mount)

	err = mount.ChangeDir(subPath)
	require.NoError(t, err)

	// write some dirs & files
	err = mount.MakeDir("content1", 0770)
	require.NoError(t, err)

	writeFile(t, mount, "content1/robots.txt",
		[]byte("robbie\nr2\nbender\nclaptrap\n"))
	writeFile(t, mount, "content1/songs.txt",
		[]byte("none of them knew they were robots\n"))
	assert.NoError(t, mount.MakeDir("content1/emptyDir1", 0770))

	err = mount.MakeDir("content2", 0770)
	require.NoError(t, err)

	writeFile(t, mount, "content2/androids.txt",
		[]byte("data\nmarvin\n"))
	assert.NoError(t, mount.MakeDir("content2/docs", 0770))
	writeFile(t, mount, "content2/docs/lore.txt",
		[]byte("Compendium\nLegend\nLore\nDeadweight\nSpirit at Aphelion\n"))

	assert.NoError(t, mount.SyncFs())

	// take a snapshot

	snapname1 := "hotSpans1"
	err = fsa.CreateSubVolumeSnapshot(volume, group, subname, snapname1)
	require.NoError(t, err)
	defer func() {
		err := fsa.RemoveSubVolumeSnapshot(volume, group, subname, snapname1)
		assert.NoError(t, err)
	}()

	sinfo, err := fsa.SubVolumeSnapshotInfo(volume, group, subname, snapname1)
	require.NoError(t, err)
	require.NotNil(t, sinfo)

	// examine the snapshot
	snapPath := getSnapPath(t, mount, subPath, snapname1)
	require.NotEqual(t, "", snapPath)

	tempPath := path.Join(snapPath, "content1/robots.txt")
	txt := readFile(t, mount, tempPath)
	assert.Contains(t, string(txt), "robbie")

	// original subvol can be manipulated
	err = mount.Rename("content2/docs/lore.txt", "content1/lore.txt")
	assert.NoError(t, err)
	writeFile(t, mount, "content1/songs.txt",
		[]byte("none of them knew they were robots\nars moriendi\n"))

	// snapshot may not be modified
	err = mount.Rename(
		path.Join(snapPath, "content2/docs/lore.txt"),
		path.Join(snapPath, "content1/lore.txt"))
	assert.Error(t, err)
	txt = readFile(t, mount, path.Join(snapPath, "content2/docs/lore.txt"))
	assert.Contains(t, string(txt), "Spirit")

	// make a clone

	clonename := "files2"
	err = fsa.CloneSubVolumeSnapshot(
		volume, group, subname, snapname1, clonename,
		&CloneOptions{TargetGroup: group})
	var x NotProtectedError
	if errors.As(err, &x) {
		err = fsa.ProtectSubVolumeSnapshot(volume, group, subname, snapname1)
		assert.NoError(t, err)
		defer func() {
			err := fsa.UnprotectSubVolumeSnapshot(volume, group, subname, snapname1)
			assert.NoError(t, err)
		}()

		err = fsa.CloneSubVolumeSnapshot(
			volume, group, subname, snapname1, clonename,
			&CloneOptions{TargetGroup: group})
	}
	require.NoError(t, err)
	defer func() {
		err := fsa.ForceRemoveSubVolume(volume, group, clonename)
		assert.NoError(t, err)
	}()

	// wait for cloning to complete
	for done := false; !done; {
		status, err := fsa.CloneStatus(volume, group, clonename)
		assert.NoError(t, err)
		assert.NotNil(t, status)
		switch status.State {
		case ClonePending, CloneInProgress:
			time.Sleep(5 * time.Millisecond)
		case CloneComplete:
			done = true
		case CloneFailed:
			t.Fatal("clone failed")
		default:
			t.Fatalf("invalid status.State: %q", status.State)
		}
	}

	// examine the clone
	clonePath, err := fsa.SubVolumePath(volume, group, clonename)
	require.NoError(t, err)
	require.NotEqual(t, "", clonePath)

	txt = readFile(t, mount, path.Join(clonePath, "content1/robots.txt"))
	assert.Contains(t, string(txt), "robbie")

	// clones are r/w
	err = mount.Rename(
		path.Join(clonePath, "content2/docs/lore.txt"),
		path.Join(clonePath, "content1/lore.txt"))
	assert.NoError(t, err)
	txt = readFile(t, mount, path.Join(clonePath, "content1/lore.txt"))
	assert.Contains(t, string(txt), "Spirit")

	// it reflects what was in the snapshot
	txt = readFile(t, mount, path.Join(clonePath, "content1/songs.txt"))
	assert.Contains(t, string(txt), "robots")
	assert.NotContains(t, string(txt), "moriendi")

	// ... with it's own independent data
	writeFile(t, mount, path.Join(clonePath, "content1/songs.txt"),
		[]byte("none of them knew they were robots\nsweet charity\n"))

	// (orig)
	txt = readFile(t, mount, "content1/songs.txt")
	assert.Contains(t, string(txt), "robots")
	assert.Contains(t, string(txt), "moriendi")
	assert.NotContains(t, string(txt), "charity")

	// (clone)
	txt = readFile(t, mount, path.Join(clonePath, "content1/songs.txt"))
	assert.Contains(t, string(txt), "robots")
	assert.NotContains(t, string(txt), "moriendi")
	assert.Contains(t, string(txt), "charity")
}
<commit_msg>cephfs admin: fix issues with TestWorkflow<commit_after>// +build !luminous,!mimic

package admin

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"testing"
	"time"

	"github.com/ceph/go-ceph/cephfs"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

var snapDir = ".snapshots"

func fsConnect(t *testing.T) *cephfs.MountInfo {
	mount, err := cephfs.CreateMount()
	require.NoError(t, err)
	require.NotNil(t, mount)

	err = mount.ReadDefaultConfigFile()
	require.NoError(t, err)
	err = mount.SetConfigOption("client_snapdir", snapDir)
	require.NoError(t, err)

	timeout := time.After(time.Second * 5)
	ch := make(chan error)
	go func(mount *cephfs.MountInfo) {
		ch <- mount.Mount()
	}(mount)
	select {
	case err = <-ch:
	case <-timeout:
		err = fmt.Errorf("timed out waiting for connect")
	}
	require.NoError(t, err)
	return mount
}

func writeFile(t *testing.T, mount *cephfs.MountInfo, path string, content []byte) {
	f1, err := mount.Open(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0700)
	require.NoError(t, err)
	defer f1.Close()
	f1.WriteAt(content, 0)
}

func readFile(t *testing.T, mount *cephfs.MountInfo, path string) []byte {
	f1, err := mount.Open(path, os.O_RDONLY, 0)
	require.NoError(t, err)
	defer f1.Close()
	b, err := ioutil.ReadAll(f1)
	require.NoError(t, err)
	return b
}

func getSnapPath(t *testing.T, mount *cephfs.MountInfo, subvol, snapname string) string {
	// I wish there was a nicer way to do this
	snapPath := path.Join(subvol, snapDir, snapname)
	_, err := mount.Statx(snapPath, cephfs.StatxBasicStats, 0)
	if err == nil {
		return snapPath
	}
	snapPath = path.Join(path.Dir(subvol), snapDir, snapname, path.Base(subvol))
	_, err = mount.Statx(snapPath, cephfs.StatxBasicStats, 0)
	if err == nil {
		return snapPath
	}
	t.Fatalf("did not find a snap path for %s", snapname)
	return ""
}

// TestWorkflow aims to do more than just exercise the API calls, but rather to
// also check that they do what they say on the tin.  This means importing the
// cephfs lib in addition to the admin lib and reading and writing to the
// subvolume, snapshot, and clone as appropriate.
func TestWorkflow(t *testing.T) {
	fsa := getFSAdmin(t)
	volume := "cephfs"
	group := "workflow1"

	// verify the volume we want to use
	l, err := fsa.ListVolumes()
	require.NoError(t, err)
	require.Contains(t, l, volume)

	err = fsa.CreateSubVolumeGroup(volume, group, nil)
	require.NoError(t, err)
	defer func() {
		err := fsa.RemoveSubVolumeGroup(volume, group)
		assert.NoError(t, err)
	}()

	subname := "files1"
	svopts := &SubVolumeOptions{
		Mode: 0777,
		Size: 2 * gibiByte,
	}
	err = fsa.CreateSubVolume(volume, group, subname, svopts)
	require.NoError(t, err)
	defer func() {
		err := fsa.RemoveSubVolume(volume, group, subname)
		assert.NoError(t, err)
	}()

	// getpath
	subPath, err := fsa.SubVolumePath(volume, group, subname)
	require.NoError(t, err)
	require.NotEqual(t, "", subPath)

	// connect to volume, cd to path (?)
	mount := fsConnect(t)
	defer func(mount *cephfs.MountInfo) {
		assert.NoError(t, mount.Unmount())
		assert.NoError(t, mount.Release())
	}(mount)

	err = mount.ChangeDir(subPath)
	require.NoError(t, err)

	// write some dirs & files
	err = mount.MakeDir("content1", 0770)
	require.NoError(t, err)

	writeFile(t, mount, "content1/robots.txt",
		[]byte("robbie\nr2\nbender\nclaptrap\n"))
	writeFile(t, mount, "content1/songs.txt",
		[]byte("none of them knew they were robots\n"))
	assert.NoError(t, mount.MakeDir("content1/emptyDir1", 0770))

	err = mount.MakeDir("content2", 0770)
	require.NoError(t, err)

	writeFile(t, mount, "content2/androids.txt",
		[]byte("data\nmarvin\n"))
	assert.NoError(t, mount.MakeDir("content2/docs", 0770))
	writeFile(t, mount, "content2/docs/lore.txt",
		[]byte("Compendium\nLegend\nLore\nDeadweight\nSpirit at Aphelion\n"))

	assert.NoError(t, mount.SyncFs())

	// take a snapshot

	snapname1 := "hotSpans1"
	err = fsa.CreateSubVolumeSnapshot(volume, group, subname, snapname1)
	require.NoError(t, err)
	defer func() {
		err := fsa.RemoveSubVolumeSnapshot(volume, group, subname, snapname1)
		assert.NoError(t, err)
	}()

	sinfo, err := fsa.SubVolumeSnapshotInfo(volume, group, subname, snapname1)
	require.NoError(t, err)
	require.NotNil(t, sinfo)

	time.Sleep(500 * time.Millisecond) // is there a race?

	// examine the snapshot
	snapPath := getSnapPath(t, mount, subPath, snapname1)
	require.NotEqual(t, "", snapPath)

	tempPath := path.Join(snapPath, "content1/robots.txt")
	txt := readFile(t, mount, tempPath)
	assert.Contains(t, string(txt), "robbie")

	// original subvol can be manipulated
	err = mount.Rename("content2/docs/lore.txt", "content1/lore.txt")
	assert.NoError(t, err)
	writeFile(t, mount, "content1/songs.txt",
		[]byte("none of them knew they were robots\nars moriendi\n"))

	// snapshot may not be modified
	err = mount.Rename(
		path.Join(snapPath, "content2/docs/lore.txt"),
		path.Join(snapPath, "content1/lore.txt"))
	assert.Error(t, err)
	txt = readFile(t, mount, path.Join(snapPath, "content2/docs/lore.txt"))
	assert.Contains(t, string(txt), "Spirit")

	// make a clone

	clonename := "files2"
	err = fsa.CloneSubVolumeSnapshot(
		volume, group, subname, snapname1, clonename,
		&CloneOptions{TargetGroup: group})
	var x NotProtectedError
	if errors.As(err, &x) {
		err = fsa.ProtectSubVolumeSnapshot(volume, group, subname, snapname1)
		assert.NoError(t, err)
		defer func() {
			err := fsa.UnprotectSubVolumeSnapshot(volume, group, subname, snapname1)
			assert.NoError(t, err)
		}()

		err = fsa.CloneSubVolumeSnapshot(
			volume, group, subname, snapname1, clonename,
			&CloneOptions{TargetGroup: group})
	}
	require.NoError(t, err)
	defer func() {
		err := fsa.ForceRemoveSubVolume(volume, group, clonename)
		assert.NoError(t, err)
	}()

	// wait for cloning to complete
	for done := false; !done; {
		status, err := fsa.CloneStatus(volume, group, clonename)
		assert.NoError(t, err)
		assert.NotNil(t, status)
		switch status.State {
		case ClonePending, CloneInProgress:
			time.Sleep(5 * time.Millisecond)
		case CloneComplete:
			done = true
		case CloneFailed:
			t.Fatal("clone failed")
		default:
			t.Fatalf("invalid status.State: %q", status.State)
		}
	}

	// examine the clone
	clonePath, err := fsa.SubVolumePath(volume, group, clonename)
	require.NoError(t, err)
	require.NotEqual(t, "", clonePath)

	txt = readFile(t, mount, path.Join(clonePath, "content1/robots.txt"))
	assert.Contains(t, string(txt), "robbie")

	// clones are r/w
	err = mount.Rename(
		path.Join(clonePath, "content2/docs/lore.txt"),
		path.Join(clonePath, "content1/lore.txt"))
	assert.NoError(t, err)
	txt = readFile(t, mount, path.Join(clonePath, "content1/lore.txt"))
	assert.Contains(t, string(txt), "Spirit")

	// it reflects what was in the snapshot
	txt = readFile(t, mount, path.Join(clonePath, "content1/songs.txt"))
	assert.Contains(t, string(txt), "robots")
	assert.NotContains(t, string(txt), "moriendi")

	// ... with it's own independent data
	writeFile(t, mount, path.Join(clonePath, "content1/songs.txt"),
		[]byte("none of them knew they were robots\nsweet charity\n"))

	// (orig)
	txt = readFile(t, mount, "content1/songs.txt")
	assert.Contains(t, string(txt), "robots")
	assert.Contains(t, string(txt), "moriendi")
	assert.NotContains(t, string(txt), "charity")

	// (clone)
	txt = readFile(t, mount, path.Join(clonePath, "content1/songs.txt"))
	assert.Contains(t, string(txt), "robots")
	assert.NotContains(t, string(txt), "moriendi")
	assert.Contains(t, string(txt), "charity")
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"os"

	"github.com/projectOpenRAP/OpenRAP/apiserver/config"
)

var (
	httpAddr, dataDir              string
	bleveDbDir, indexName          string
	telemetryEnable, devmgmtEnable bool
	indexDbPath                    string
	logger                         *config.Tracelog
)

func createActiveProfileDirectories(activeProfile config.DeviceInfo) {
	var pathErr error
	// Create /var/www/ekstep/config_json dir
	dirpath := []string{
		activeProfile.ConfigJsonDir,
		activeProfile.JsonDir,
		activeProfile.ContentRoot,
		activeProfile.UnzipContent,
		activeProfile.Telemetry}

	for _, dp := range dirpath {
		logger.Trace.Printf("Creating dir: %s", dp)
		pathErr = os.MkdirAll(dp, 0666)
		if pathErr != nil {
			logger.Error.Println(pathErr)
		}
	}
}

func main() {
	flag.StringVar(&httpAddr, "http", "0.0.0.0:9090", "HTTP service ip:port")
	flag.StringVar(&dataDir, "dataDir", "", "HTTP server root dir")
	flag.BoolVar(&telemetryEnable, "telemetry", true, "Enable search API")
	flag.BoolVar(&devmgmtEnable, "devmgmt", true, "Enable search API")
	//	flag.StringVar(&jsonDir, "jsonDir", "/var/www/ekstep/jsonDir", "HTTP server root dir")
	flag.StringVar(&bleveDbDir, "bleveDbDir", "/var/www/ekstep/bleveDbDir", "HTTP server root dir")
	flag.StringVar(&indexName, "index", "ekstep.bleve", "HTTP server root dir")

	flag.Parse()

	//Load Device configuration
	config.DeviceConfigInit()
	dconfig := config.DeviceConfigGet()
	if dconfig == nil {
		log.Fatal("Couldn't parse config json")
	}
	profile := dconfig.ActiveProfile.ProfileName

	if dataDir == "" {
		dataDir = dconfig.ActiveProfile.MediaRoot
	}
	logger = dconfig.Logger

	createActiveProfileDirectories(dconfig.ActiveProfile)
	//logfile := dconfig.ActiveProfile.MediaRoot + "apiserver.log"
	//LoggerInit(logfile)
	//logger.Trace.Printf("apiserver log stores into: %s\n", logfile)

	/*
		jd := config.DeviceDataJsonDirPath()
		if jd == nil {
			log.Fatal("Couldn't found json directory in config json")
		}

		indexDbPath := bleveDbDir + string(os.PathSeparator) + indexName
		log.Printf("index path: %s\n", indexDbPath)
		SearchInit(bleveDbDir, indexName, *jd)
	*/
	serve(profile)
}
<commit_msg>Fix for issue #12 ... Review comment addressed<commit_after>package main

import (
	"flag"
	"log"
	"os"

	"github.com/projectOpenRAP/OpenRAP/apiserver/config"
)

var (
	httpAddr, dataDir              string
	bleveDbDir, indexName          string
	telemetryEnable, devmgmtEnable bool
	indexDbPath                    string
	logger                         *config.Tracelog
)

func createActiveProfileDirectories(activeProfile config.DeviceInfo) {
	var pathErr error
	// Create directories based on profile.json
	dirpath := []string{
		activeProfile.ConfigJsonDir,
		activeProfile.JsonDir,
		activeProfile.ContentRoot,
		activeProfile.UnzipContent,
		activeProfile.Telemetry}

	for _, dp := range dirpath {
		logger.Trace.Printf("Creating dir: %s", dp)
		pathErr = os.MkdirAll(dp, 0744)
		if pathErr != nil {
			logger.Error.Println(pathErr)
		}
	}
}

func main() {
	flag.StringVar(&httpAddr, "http", "0.0.0.0:9090", "HTTP service ip:port")
	flag.StringVar(&dataDir, "dataDir", "", "HTTP server root dir")
	flag.BoolVar(&telemetryEnable, "telemetry", true, "Enable search API")
	flag.BoolVar(&devmgmtEnable, "devmgmt", true, "Enable search API")
	//	flag.StringVar(&jsonDir, "jsonDir", "/var/www/ekstep/jsonDir", "HTTP server root dir")
	flag.StringVar(&bleveDbDir, "bleveDbDir", "/var/www/ekstep/bleveDbDir", "HTTP server root dir")
	flag.StringVar(&indexName, "index", "ekstep.bleve", "HTTP server root dir")

	flag.Parse()

	//Load Device configuration
	config.DeviceConfigInit()
	dconfig := config.DeviceConfigGet()
	if dconfig == nil {
		log.Fatal("Couldn't parse config json")
	}
	profile := dconfig.ActiveProfile.ProfileName

	if dataDir == "" {
		dataDir = dconfig.ActiveProfile.MediaRoot
	}
	logger = dconfig.Logger

	createActiveProfileDirectories(dconfig.ActiveProfile)
	//logfile := dconfig.ActiveProfile.MediaRoot + "apiserver.log"
	//LoggerInit(logfile)
	//logger.Trace.Printf("apiserver log stores into: %s\n", logfile)

	/*
		jd := config.DeviceDataJsonDirPath()
		if jd == nil {
			log.Fatal("Couldn't found json directory in config json")
		}

		indexDbPath := bleveDbDir + string(os.PathSeparator) + indexName
		log.Printf("index path: %s\n", indexDbPath)
		SearchInit(bleveDbDir, indexName, *jd)
	*/
	serve(profile)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net"
	"os"
	"path/filepath"
	//"runtime"
	"strconv"

	"./jsonrpc2"
	"./lsp"
)

var (
	addr       = flag.String("addr", "localhost:2088", "language server address (tcp)")
	rootPath   = flag.String("root-path", ".", "language server root path")
	file       = flag.String("file", "", "File")
	line 	   = flag.Int("line", 1, "Symbol line (1-based")
	column 	   = flag.Int("column", 1, "Symbol column (1-based")
	command    = flag.String("command", "initialize", "LSP command")
)

var reqCounter = 0

func main() {
	flag.Parse()
	log.SetFlags(0)

	if err := run(); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

func run() error {
	client, err := client()
	if err != nil {
		return err
	}
	defer client.Close()

    switch *command {
		case "initialize": initialize(client)
		case "definition": getDefinition(client)
		case "hover": getHover(client)
		case "references": getReferences(client)
		case "external-refs": getWorkspaceSymbols(client, "externals")
		case "exported-symbols": getWorkspaceSymbols(client, "exported")
		case "shutdown": shutdown(client);
	}
    // initialize(client);
    // getDefinition(client);
	// getHover(client);
	return nil;
}

func initialize(client *jsonrpc2.Client) error {
// initialize server
	// note that RootPath should point to file path, not URI (at least Java binding expects it to be path)
	_, err := request(client, "initialize", lsp.InitializeParams{
		RootPath: *rootPath,
	})
	if err != nil {
		return err
	}
	return nil;
}

func shutdown(client *jsonrpc2.Client) error {
	_, err := request(client, "shutdown", nil);
	if err != nil {
		return err
	}
	return nil;
}

func getDefinition(client *jsonrpc2.Client) error {
	initialize(client);
	response, err := request(client, "textDocument/definition", lsp.TextDocumentPositionParams{
   		TextDocument: lsp.TextDocumentIdentifier{
   			URI: toUri(filepath.Join(*rootPath, *file)),
   		},
   		Position: lsp.Position{
   			Line:      *line,
   			Character: *column ,
   		},
   	})
   	if err != nil {
   		return err
   	}
   	var location []lsp.Location
   	if err := json.Unmarshal(*response.Result, &location); err != nil {
   		return err
   	}
   	for _, l := range location {
   		println(l.URI, l.Range.Start.Line, l.Range.Start.Character, l.Range.End.Line, l.Range.End.Character)
   	}
	return nil;

}

func getHover(client *jsonrpc2.Client) error {
	    initialize(client);
		response, err := request(client, "textDocument/hover", lsp.TextDocumentPositionParams{
   		TextDocument: lsp.TextDocumentIdentifier{
   			URI: toUri(filepath.Join(*rootPath, *file)),
   		},
   		Position: lsp.Position{
   			Line:      *line,
   			Character: *column ,
   		},
   	});

	if err != nil {
   		return err
   }
	println(*response.Result);
   	// var location []lsp.Location
   	// if err := json.Unmarshal(*response.Result, &location); err != nil {
   	// 	return err
   	// }
	return nil;
}

func getReferences(client *jsonrpc2.Client) error {
	  initialize(client);
		response, err := request(client, "textDocument/references", lsp.ReferenceParams{
		TextDocumentPositionParams: lsp.TextDocumentPositionParams{
   		TextDocument: lsp.TextDocumentIdentifier{
   			URI: toUri(filepath.Join(*rootPath, *file)),
   		},
   		Position: lsp.Position{
   			Line:      *line,
   			Character: *column ,
   		},
		}});
  if err != nil {
   		return err
   	}
  var location []lsp.Location
   	if err := json.Unmarshal(*response.Result, &location); err != nil {
   		return err
   	}
   	for _, l := range location {
   		println(l.URI, l.Range.Start.Line, l.Range.Start.Character, l.Range.End.Line, l.Range.End.Character)
   	}
  return nil;
}

func getWorkspaceSymbols(client *jsonrpc2.Client, query string) error {
	    initialize(client);
		response, err := request(client, "workspace/symbol", lsp.WorkspaceSymbolParams{
   		Query: query,
   	});

	if err != nil {
   		return err
   }
	println(*response.Result);
   	// var location []lsp.Location
   	// if err := json.Unmarshal(*response.Result, &location); err != nil {
   	// 	return err
   	// }
	return nil;
}

// creates new JSON-RPC client and connects it to remote language server
func client() (*jsonrpc2.Client, error) {
	conn, err := net.Dial("tcp", *addr)
	if err != nil {
		return nil, err
	}
	return jsonrpc2.NewClient(conn), nil
}

// request performs JSON-RPC request using initialized client connection and waits for response.
// This function assembles JSON-RPC requests from method name and parameters, also it assigns unique identifiers to each request
func request(client *jsonrpc2.Client, method string, params interface{}) (*jsonrpc2.Response, error) {
	reqCounter++

	request := &jsonrpc2.Request{
		Method: method,
	}
	if (params != nil) {
      request.SetParams(params);
	}

	request.ID = strconv.Itoa(reqCounter)
	return client.RequestAndWaitForResponse(request)
}

// converts file path to URI file://(/)POSIX-style-path
func toUri(path string) string {
	prefix := "file:///"
	// if runtime.GOOS == "windows" {
	// 	prefix += "/"
	// }
	r, _ := filepath.Rel(*rootPath, path)
	return prefix + filepath.ToSlash(r)
}
<commit_msg>Add workspace symbols method<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net"
	"os"
	"path/filepath"
	//"runtime"
	"strconv"

	"./jsonrpc2"
	"./lsp"
)

var (
	addr       = flag.String("addr", "localhost:2088", "language server address (tcp)")
	rootPath   = flag.String("root-path", ".", "language server root path")
	file       = flag.String("file", "", "File")
	line 	   = flag.Int("line", 1, "Symbol line (1-based")
	column 	   = flag.Int("column", 1, "Symbol column (1-based")
	command    = flag.String("command", "initialize", "LSP command")
)

var reqCounter = 0

func main() {
	flag.Parse()
	log.SetFlags(0)

	if err := run(); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

func run() error {
	client, err := client()
	if err != nil {
		return err
	}
	defer client.Close()

    switch *command {
		case "initialize": initialize(client)
		case "definition": getDefinition(client)
		case "hover": getHover(client)
		case "references": getReferences(client)
		case "external-refs": getWorkspaceSymbols(client, "externals")
		case "exported-symbols": getWorkspaceSymbols(client, "exported")
		case "workspace-symbols": getWorkspaceSymbols(client, "")
		case "shutdown": shutdown(client);
	}
    // initialize(client);
    // getDefinition(client);
	// getHover(client);
	return nil;
}

func initialize(client *jsonrpc2.Client) error {
// initialize server
	// note that RootPath should point to file path, not URI (at least Java binding expects it to be path)
	_, err := request(client, "initialize", lsp.InitializeParams{
		RootPath: *rootPath,
	})
	if err != nil {
		return err
	}
	return nil;
}

func shutdown(client *jsonrpc2.Client) error {
	_, err := request(client, "shutdown", nil);
	if err != nil {
		return err
	}
	return nil;
}

func getDefinition(client *jsonrpc2.Client) error {
	initialize(client);
	response, err := request(client, "textDocument/definition", lsp.TextDocumentPositionParams{
   		TextDocument: lsp.TextDocumentIdentifier{
   			URI: toUri(filepath.Join(*rootPath, *file)),
   		},
   		Position: lsp.Position{
   			Line:      *line,
   			Character: *column ,
   		},
   	})
   	if err != nil {
   		return err
   	}
   	var location []lsp.Location
   	if err := json.Unmarshal(*response.Result, &location); err != nil {
   		return err
   	}
   	for _, l := range location {
   		println(l.URI, l.Range.Start.Line, l.Range.Start.Character, l.Range.End.Line, l.Range.End.Character)
   	}
	return nil;

}

func getHover(client *jsonrpc2.Client) error {
	    initialize(client);
		response, err := request(client, "textDocument/hover", lsp.TextDocumentPositionParams{
   		TextDocument: lsp.TextDocumentIdentifier{
   			URI: toUri(filepath.Join(*rootPath, *file)),
   		},
   		Position: lsp.Position{
   			Line:      *line,
   			Character: *column ,
   		},
   	});

	if err != nil {
   		return err
   }
	println(*response.Result);
   	// var location []lsp.Location
   	// if err := json.Unmarshal(*response.Result, &location); err != nil {
   	// 	return err
   	// }
	return nil;
}

func getReferences(client *jsonrpc2.Client) error {
	  initialize(client);
		response, err := request(client, "textDocument/references", lsp.ReferenceParams{
		TextDocumentPositionParams: lsp.TextDocumentPositionParams{
   		TextDocument: lsp.TextDocumentIdentifier{
   			URI: toUri(filepath.Join(*rootPath, *file)),
   		},
   		Position: lsp.Position{
   			Line:      *line,
   			Character: *column ,
   		},
		}});
  if err != nil {
   		return err
   	}
  var location []lsp.Location
   	if err := json.Unmarshal(*response.Result, &location); err != nil {
   		return err
   	}
   	for _, l := range location {
   		println(l.URI, l.Range.Start.Line, l.Range.Start.Character, l.Range.End.Line, l.Range.End.Character)
   	}
  return nil;
}

func getWorkspaceSymbols(client *jsonrpc2.Client, query string) error {
	    initialize(client);
		response, err := request(client, "workspace/symbol", lsp.WorkspaceSymbolParams{
   		Query: query,
   	});

	if err != nil {
   		return err
   }
	println(*response.Result);
   	// var location []lsp.Location
   	// if err := json.Unmarshal(*response.Result, &location); err != nil {
   	// 	return err
   	// }
	return nil;
}

// creates new JSON-RPC client and connects it to remote language server
func client() (*jsonrpc2.Client, error) {
	conn, err := net.Dial("tcp", *addr)
	if err != nil {
		return nil, err
	}
	return jsonrpc2.NewClient(conn), nil
}

// request performs JSON-RPC request using initialized client connection and waits for response.
// This function assembles JSON-RPC requests from method name and parameters, also it assigns unique identifiers to each request
func request(client *jsonrpc2.Client, method string, params interface{}) (*jsonrpc2.Response, error) {
	reqCounter++

	request := &jsonrpc2.Request{
		Method: method,
	}
	if (params != nil) {
      request.SetParams(params);
	}

	request.ID = strconv.Itoa(reqCounter)
	return client.RequestAndWaitForResponse(request)
}

// converts file path to URI file://(/)POSIX-style-path
func toUri(path string) string {
	prefix := "file:///"
	// if runtime.GOOS == "windows" {
	// 	prefix += "/"
	// }
	r, _ := filepath.Rel(*rootPath, path)
	return prefix + filepath.ToSlash(r)
}
<|endoftext|>


<commit_before>package apps

import (
	"regexp"
	"time"

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

	"github.com/cloudfoundry-incubator/cf-test-helpers/cf"
	"github.com/cloudfoundry-incubator/cf-test-helpers/helpers"
	"github.com/cloudfoundry/cf-acceptance-tests/helpers/app_helpers"
	"github.com/cloudfoundry/cf-acceptance-tests/helpers/assets"
	"github.com/cloudfoundry/cf-acceptance-tests/helpers/random_name"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
)

var _ = AppsDescribe("Logging", func() {
	var logWriterAppName string
	var listenerAppName string
	var logs *Session
	var interrupt chan struct{}
	var serviceName string

	Describe("Syslog drains", func() {
		BeforeEach(func() {
			interrupt = make(chan struct{}, 1)
			serviceName = random_name.CATSRandomName("SVIN")
			listenerAppName = random_name.CATSRandomName("APP")
			logWriterAppName = random_name.CATSRandomName("APP")

			Eventually(cf.Cf("push", listenerAppName, "--no-start", "--health-check-type", "port", "-b", Config.GetGoBuildpackName(), "-m", DEFAULT_MEMORY_LIMIT, "-p", assets.NewAssets().SyslogDrainListener, "-d", Config.GetAppsDomain(), "-f", assets.NewAssets().SyslogDrainListener+"/manifest.yml"), Config.DefaultTimeoutDuration()).Should(Exit(0), "Failed to push app")
			Eventually(cf.Cf("push", logWriterAppName, "--no-start", "-b", Config.GetRubyBuildpackName(), "-m", DEFAULT_MEMORY_LIMIT, "-p", assets.NewAssets().RubySimple, "-d", Config.GetAppsDomain()), Config.DefaultTimeoutDuration()).Should(Exit(0), "Failed to push app")

			app_helpers.SetBackend(listenerAppName)
			app_helpers.SetBackend(logWriterAppName)

			Expect(cf.Cf("start", listenerAppName).Wait(Config.CfPushTimeoutDuration())).To(Exit(0))
			Expect(cf.Cf("start", logWriterAppName).Wait(Config.CfPushTimeoutDuration())).To(Exit(0))
		})

		AfterEach(func() {
			logs.Kill()
			close(interrupt)

			app_helpers.AppReport(logWriterAppName, Config.DefaultTimeoutDuration())
			app_helpers.AppReport(listenerAppName, Config.DefaultTimeoutDuration())

			Eventually(cf.Cf("delete", logWriterAppName, "-f", "-r"), Config.DefaultTimeoutDuration()).Should(Exit(0), "Failed to delete app")
			Eventually(cf.Cf("delete", listenerAppName, "-f", "-r"), Config.DefaultTimeoutDuration()).Should(Exit(0), "Failed to delete app")
			if serviceName != "" {
				Eventually(cf.Cf("delete-service", serviceName, "-f"), Config.DefaultTimeoutDuration()).Should(Exit(0), "Failed to delete service")
			}

			Eventually(cf.Cf("delete-orphaned-routes", "-f"), Config.CfPushTimeoutDuration()).Should(Exit(0), "Failed to delete orphaned routes")
		})

		It("forwards app messages to registered syslog drains", func() {
			syslogDrainURL := "syslog://" + getSyslogDrainAddress(listenerAppName)

			Eventually(cf.Cf("cups", serviceName, "-l", syslogDrainURL), Config.DefaultTimeoutDuration()).Should(Exit(0), "Failed to create syslog drain service")
			Eventually(cf.Cf("bind-service", logWriterAppName, serviceName), Config.DefaultTimeoutDuration()).Should(Exit(0), "Failed to bind service")
			// We don't need to restage, because syslog service bindings don't change the app's environment variables

			logs = cf.Cf("logs", listenerAppName)
			randomMessage := random_name.CATSRandomName("RANDOM-MESSAGE")
			go writeLogsUntilInterrupted(interrupt, randomMessage, logWriterAppName)

			Eventually(logs, Config.DefaultTimeoutDuration()+1*time.Minute).Should(Say(randomMessage))
		})
	})
})

func getSyslogDrainAddress(appName string) string {
	var address []byte

	Eventually(func() []byte {
		re, err := regexp.Compile("ADDRESS: \\|(.*)\\|")
		Expect(err).NotTo(HaveOccurred())

		logs := cf.Cf("logs", appName, "--recent").Wait(Config.DefaultTimeoutDuration())
		address = re.FindSubmatch(logs.Out.Contents())[1]
		return address
	}, Config.DefaultTimeoutDuration()).Should(Not(BeNil()))

	return string(address)
}

func writeLogsUntilInterrupted(interrupt chan struct{}, randomMessage string, logWriterAppName string) {
	defer GinkgoRecover()
	for {
		select {
		case <-interrupt:
			return
		default:
			helpers.CurlAppWithTimeout(Config, logWriterAppName, "/log/"+randomMessage, Config.DefaultTimeoutDuration())
			time.Sleep(3 * time.Second)
		}
	}
}
<commit_msg>Fixes panic in apps/syslog_drain.go when address not yet in logs<commit_after>package apps

import (
	"regexp"
	"time"

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

	"github.com/cloudfoundry-incubator/cf-test-helpers/cf"
	"github.com/cloudfoundry-incubator/cf-test-helpers/helpers"
	"github.com/cloudfoundry/cf-acceptance-tests/helpers/app_helpers"
	"github.com/cloudfoundry/cf-acceptance-tests/helpers/assets"
	"github.com/cloudfoundry/cf-acceptance-tests/helpers/random_name"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
)

var _ = AppsDescribe("Logging", func() {
	var logWriterAppName string
	var listenerAppName string
	var logs *Session
	var interrupt chan struct{}
	var serviceName string

	Describe("Syslog drains", func() {
		BeforeEach(func() {
			interrupt = make(chan struct{}, 1)
			serviceName = random_name.CATSRandomName("SVIN")
			listenerAppName = random_name.CATSRandomName("APP")
			logWriterAppName = random_name.CATSRandomName("APP")

			Eventually(cf.Cf("push", listenerAppName, "--no-start", "--health-check-type", "port", "-b", Config.GetGoBuildpackName(), "-m", DEFAULT_MEMORY_LIMIT, "-p", assets.NewAssets().SyslogDrainListener, "-d", Config.GetAppsDomain(), "-f", assets.NewAssets().SyslogDrainListener+"/manifest.yml"), Config.DefaultTimeoutDuration()).Should(Exit(0), "Failed to push app")
			Eventually(cf.Cf("push", logWriterAppName, "--no-start", "-b", Config.GetRubyBuildpackName(), "-m", DEFAULT_MEMORY_LIMIT, "-p", assets.NewAssets().RubySimple, "-d", Config.GetAppsDomain()), Config.DefaultTimeoutDuration()).Should(Exit(0), "Failed to push app")

			app_helpers.SetBackend(listenerAppName)
			app_helpers.SetBackend(logWriterAppName)

			Expect(cf.Cf("start", listenerAppName).Wait(Config.CfPushTimeoutDuration())).To(Exit(0))
			Expect(cf.Cf("start", logWriterAppName).Wait(Config.CfPushTimeoutDuration())).To(Exit(0))
		})

		AfterEach(func() {
			logs.Kill()
			close(interrupt)

			app_helpers.AppReport(logWriterAppName, Config.DefaultTimeoutDuration())
			app_helpers.AppReport(listenerAppName, Config.DefaultTimeoutDuration())

			Eventually(cf.Cf("delete", logWriterAppName, "-f", "-r"), Config.DefaultTimeoutDuration()).Should(Exit(0), "Failed to delete app")
			Eventually(cf.Cf("delete", listenerAppName, "-f", "-r"), Config.DefaultTimeoutDuration()).Should(Exit(0), "Failed to delete app")
			if serviceName != "" {
				Eventually(cf.Cf("delete-service", serviceName, "-f"), Config.DefaultTimeoutDuration()).Should(Exit(0), "Failed to delete service")
			}

			Eventually(cf.Cf("delete-orphaned-routes", "-f"), Config.CfPushTimeoutDuration()).Should(Exit(0), "Failed to delete orphaned routes")
		})

		It("forwards app messages to registered syslog drains", func() {
			syslogDrainURL := "syslog://" + getSyslogDrainAddress(listenerAppName)

			Eventually(cf.Cf("cups", serviceName, "-l", syslogDrainURL), Config.DefaultTimeoutDuration()).Should(Exit(0), "Failed to create syslog drain service")
			Eventually(cf.Cf("bind-service", logWriterAppName, serviceName), Config.DefaultTimeoutDuration()).Should(Exit(0), "Failed to bind service")
			// We don't need to restage, because syslog service bindings don't change the app's environment variables

			logs = cf.Cf("logs", listenerAppName)
			randomMessage := random_name.CATSRandomName("RANDOM-MESSAGE")
			go writeLogsUntilInterrupted(interrupt, randomMessage, logWriterAppName)

			Eventually(logs, Config.DefaultTimeoutDuration()+1*time.Minute).Should(Say(randomMessage))
		})
	})
})

func getSyslogDrainAddress(appName string) string {
	var address []byte

	Eventually(func() []byte {
		re, err := regexp.Compile("ADDRESS: \\|(.*)\\|")
		Expect(err).NotTo(HaveOccurred())

		logs := cf.Cf("logs", appName, "--recent").Wait(Config.DefaultTimeoutDuration())
		matched := re.FindSubmatch(logs.Out.Contents())
		if len(matched) < 2 {
			return nil
		}
		address = matched[1]
		return address
	}, Config.DefaultTimeoutDuration()).Should(Not(BeNil()))

	return string(address)
}

func writeLogsUntilInterrupted(interrupt chan struct{}, randomMessage string, logWriterAppName string) {
	defer GinkgoRecover()
	for {
		select {
		case <-interrupt:
			return
		default:
			helpers.CurlAppWithTimeout(Config, logWriterAppName, "/log/"+randomMessage, Config.DefaultTimeoutDuration())
			time.Sleep(3 * time.Second)
		}
	}
}
<|endoftext|>


<commit_before>package controllers

import (
	"log"
	"net/http"

	"github.com/nuveo/prest/adapters/postgres"
	"github.com/nuveo/prest/statements"
)

// GetDatabases list all (or filter) databases
func GetTables(w http.ResponseWriter, r *http.Request) {
	object, err := postgres.Query(statements.Tables)
	if err != nil {
		log.Println(err)
	}

	w.Write(object)
}
<commit_msg>fixed lint on GetTables controller<commit_after>package controllers

import (
	"log"
	"net/http"

	"github.com/nuveo/prest/adapters/postgres"
	"github.com/nuveo/prest/statements"
)

// GetTables list all (or filter) tables
func GetTables(w http.ResponseWriter, r *http.Request) {
	object, err := postgres.Query(statements.Tables)
	if err != nil {
		log.Println(err)
	}

	w.Write(object)
}
<|endoftext|>


<commit_before>// Copyright 2013, Cong Ding. All rights reserved.
//
// 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.
//
// author: Cong Ding <dinggnu@gmail.com>
//
package logging

import (
	"fmt"
	"os"
	"path"
	"runtime"
	"sync/atomic"
	"time"
)

type log struct {
	level           Level
	name            string
	seqid           uint64
	levelno         int
	levelname       string
	pathname        string
	filename        string
	module          string
	lineno          int
	funcName        string
	created         int64
	asctime         string
	msecs           int64
	relativeCreated int64
	thread          int
	threadName      string
	process         int
	message         string
	timestamp       int64
}

type field func(*logging, *log) interface{}

var fields = map[string]field{
	"name":            (*logging).lname,
	"seqid":           (*logging).nextSeqid,
	"levelno":         (*logging).levelno,
	"levelname":       (*logging).levelname,
	"pathname":        (*logging).pathname,
	"filename":        (*logging).filename,
	"module":          (*logging).module,
	"lineno":          (*logging).lineno,
	"funcName":        (*logging).funcName,
	"created":         (*logging).created,
	"asctime":         (*logging).asctime,
	"msecs":           (*logging).msecs,
	"relativeCreated": (*logging).relativeCreated,
	"thread":          (*logging).thread,
	"threadName":      (*logging).threadName,
	"process":         (*logging).process,
	"message":         (*logging).message,
	"timestamp":       (*logging).timestamp,
}

const errString = "???"

// GetGoId returns the id of goroutine, which is defined in ./get_go_id.c
func GetGoId() int32

// generate the runtime information, including pathname, function name,
// filename, line number.
func genRuntime(l *log) {
	calldepth := 5
	pc, file, line, ok := runtime.Caller(calldepth)
	if ok {
		// generate short function name
		fname := runtime.FuncForPC(pc).Name()
		fshort := fname
		for i := len(fname) - 1; i > 0; i-- {
			if fname[i] == '.' {
				fshort = fname[i+1:]
				break
			}
		}

		l.pathname = file
		l.funcName = fshort
		l.filename = path.Base(file)
		l.lineno = line
	} else {
		l.pathname = errString
		l.funcName = errString
		l.filename = errString
		l.lineno = 0
	}
}

func (logger *logging) lname(l *log) interface{} {
	return logger.name
}

func (logger *logging) nextSeqid(l *log) interface{} {
	if l.seqid == 0 {
		l.seqid = atomic.AddUint64(&(logger.seqid), 1)
	}
	return l.seqid
}

func (logger *logging) levelno(l *log) interface{} {
	return int(l.level)
}

func (logger *logging) levelname(l *log) interface{} {
	return levelNames[l.level]
}

func (logger *logging) pathname(l *log) interface{} {
	if l.pathname == "" {
		genRuntime(l)
	}
	return l.pathname
}

func (logger *logging) filename(l *log) interface{} {
	if l.filename == "" {
		genRuntime(l)
	}
	return l.filename
}

func (logger *logging) module(l *log) interface{} {
	return ""
}

func (logger *logging) lineno(l *log) interface{} {
	if l.lineno == 0 {
		genRuntime(l)
	}
	return l.lineno
}

func (logger *logging) funcName(l *log) interface{} {
	if l.funcName == "" {
		genRuntime(l)
	}
	return l.funcName
}

func (logger *logging) created(l *log) interface{} {
	return logger.startTime
}

func (logger *logging) asctime(l *log) interface{} {
	if l.asctime == "" {
		l.asctime = time.Now().String()
	}
	return l.asctime
}

func (logger *logging) msecs(l *log) interface{} {
	if l.msecs == 0 {
		l.msecs = logger.startTime % 1000
	}
	return l.msecs
}

func (logger *logging) timestamp(l *log) interface{} {
	if l.timestamp == 0 {
		l.timestamp = time.Now().UnixNano()
	}
	return l.timestamp
}

func (logger *logging) relativeCreated(l *log) interface{} {
	if l.relativeCreated == 0 {
		l.relativeCreated = time.Now().UnixNano() - logger.startTime
	}
	return l.relativeCreated
}

func (logger *logging) thread(l *log) interface{} {
	if l.thread == 0 {
		l.thread = int(GetGoId())
	}
	return l.thread
}

func (logger *logging) threadName(l *log) interface{} {
	if l.threadName == "" {
		l.threadName = fmt.Sprintf("Thread-%d", GetGoId())
	}
	return l.threadName
}

// Process ID
func (logger *logging) process(l *log) interface{} {
	if l.process == 0 {
		l.process = os.Getpid()
	}
	return l.process
}

func (logger *logging) message(l *log) interface{} {
	return l.message
}
<commit_msg>update lname in fields<commit_after>// Copyright 2013, Cong Ding. All rights reserved.
//
// 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.
//
// author: Cong Ding <dinggnu@gmail.com>
//
package logging

import (
	"fmt"
	"os"
	"path"
	"runtime"
	"sync/atomic"
	"time"
)

type log struct {
	level           Level
	name            string
	seqid           uint64
	levelno         int
	levelname       string
	pathname        string
	filename        string
	module          string
	lineno          int
	funcName        string
	created         int64
	asctime         string
	msecs           int64
	relativeCreated int64
	thread          int
	threadName      string
	process         int
	message         string
	timestamp       int64
}

type field func(*logging, *log) interface{}

var fields = map[string]field{
	"name":            (*logging).lname,
	"seqid":           (*logging).nextSeqid,
	"levelno":         (*logging).levelno,
	"levelname":       (*logging).levelname,
	"pathname":        (*logging).pathname,
	"filename":        (*logging).filename,
	"module":          (*logging).module,
	"lineno":          (*logging).lineno,
	"funcName":        (*logging).funcName,
	"created":         (*logging).created,
	"asctime":         (*logging).asctime,
	"msecs":           (*logging).msecs,
	"relativeCreated": (*logging).relativeCreated,
	"thread":          (*logging).thread,
	"threadName":      (*logging).threadName,
	"process":         (*logging).process,
	"message":         (*logging).message,
	"timestamp":       (*logging).timestamp,
}

const errString = "???"

// GetGoId returns the id of goroutine, which is defined in ./get_go_id.c
func GetGoId() int32

// generate the runtime information, including pathname, function name,
// filename, line number.
func genRuntime(l *log) {
	calldepth := 5
	pc, file, line, ok := runtime.Caller(calldepth)
	if ok {
		// generate short function name
		fname := runtime.FuncForPC(pc).Name()
		fshort := fname
		for i := len(fname) - 1; i > 0; i-- {
			if fname[i] == '.' {
				fshort = fname[i+1:]
				break
			}
		}

		l.pathname = file
		l.funcName = fshort
		l.filename = path.Base(file)
		l.lineno = line
	} else {
		l.pathname = errString
		l.funcName = errString
		l.filename = errString
		l.lineno = 0
	}
}

func (logger *logging) lname(l *log) interface{} {
	if l.name == "" {
		l.name = logger.name
	}
	return l.name
}

func (logger *logging) nextSeqid(l *log) interface{} {
	if l.seqid == 0 {
		l.seqid = atomic.AddUint64(&(logger.seqid), 1)
	}
	return l.seqid
}

func (logger *logging) levelno(l *log) interface{} {
	return int(l.level)
}

func (logger *logging) levelname(l *log) interface{} {
	return levelNames[l.level]
}

func (logger *logging) pathname(l *log) interface{} {
	if l.pathname == "" {
		genRuntime(l)
	}
	return l.pathname
}

func (logger *logging) filename(l *log) interface{} {
	if l.filename == "" {
		genRuntime(l)
	}
	return l.filename
}

func (logger *logging) module(l *log) interface{} {
	return ""
}

func (logger *logging) lineno(l *log) interface{} {
	if l.lineno == 0 {
		genRuntime(l)
	}
	return l.lineno
}

func (logger *logging) funcName(l *log) interface{} {
	if l.funcName == "" {
		genRuntime(l)
	}
	return l.funcName
}

func (logger *logging) created(l *log) interface{} {
	return logger.startTime
}

func (logger *logging) asctime(l *log) interface{} {
	if l.asctime == "" {
		l.asctime = time.Now().String()
	}
	return l.asctime
}

func (logger *logging) msecs(l *log) interface{} {
	if l.msecs == 0 {
		l.msecs = logger.startTime % 1000
	}
	return l.msecs
}

func (logger *logging) timestamp(l *log) interface{} {
	if l.timestamp == 0 {
		l.timestamp = time.Now().UnixNano()
	}
	return l.timestamp
}

func (logger *logging) relativeCreated(l *log) interface{} {
	if l.relativeCreated == 0 {
		l.relativeCreated = time.Now().UnixNano() - logger.startTime
	}
	return l.relativeCreated
}

func (logger *logging) thread(l *log) interface{} {
	if l.thread == 0 {
		l.thread = int(GetGoId())
	}
	return l.thread
}

func (logger *logging) threadName(l *log) interface{} {
	if l.threadName == "" {
		l.threadName = fmt.Sprintf("Thread-%d", GetGoId())
	}
	return l.threadName
}

// Process ID
func (logger *logging) process(l *log) interface{} {
	if l.process == 0 {
		l.process = os.Getpid()
	}
	return l.process
}

func (logger *logging) message(l *log) interface{} {
	return l.message
}
<|endoftext|>


<commit_before>// Copyright 2022 The Authors (see AUTHORS file)
//
// 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 logging sets up and configures standard logging.
package logging

import (
	"context"
	"fmt"
	"os"
	"strings"
	"sync"
	"time"

	"cloud.google.com/go/compute/metadata"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"go.uber.org/zap/zaptest"
	"google.golang.org/grpc"
	grpcmetadata "google.golang.org/grpc/metadata"
)

// contextKey is a private string type to prevent collisions in the context map.
type contextKey string

const (
	// loggerKey points to the value in the context where the logger is stored.
	loggerKey = contextKey("logger")

	// googleCloudTraceHeader is the header with trace data.
	googleCloudTraceHeader = "X-Cloud-Trace-Context"

	// googleCloudTraceKey is the key in the structured log where trace information
	// is expected to be present.
	googleCloudTraceKey = "logging.googleapis.com/trace"
)

var (
	// defaultLogger is the default logger. It is initialized once per package
	// include upon calling DefaultLogger.
	defaultLogger     *zap.SugaredLogger
	defaultLoggerOnce sync.Once
)

// NewFromEnv creates a new logger from env vars.
// Set envPrefix+"LOG_LEVEL" to overwrite log level. Default log level is warning.
// Set envPrefix+"LOG_MODE" to overwrite log mode. Default log mode is production.
func NewFromEnv(envPrefix string) *zap.SugaredLogger {
	level := os.Getenv(envPrefix + "LOG_LEVEL")
	devMode := strings.ToLower(strings.TrimSpace(os.Getenv(envPrefix+"LOG_MODE"))) == "development"
	var cfg zap.Config
	if devMode {
		cfg = zap.NewDevelopmentConfig()
		cfg.EncoderConfig = developmentEncoderConfig
	} else {
		cfg = zap.NewProductionConfig()
		cfg.EncoderConfig = productionEncoderConfig
	}

	var l zapcore.Level
	if err := l.Set(level); err != nil {
		// Invalid level? Default to warn.
		l = zapcore.WarnLevel
	}
	cfg.Level = zap.NewAtomicLevelAt(l)

	logger, err := cfg.Build()
	if err != nil {
		logger = zap.NewNop()
	}

	return logger.Sugar()
}

// Default creates a default logger.
// To overwrite log level and mode, set AUDIT_CLIENT_LOG_LEVEL and AUDIT_CLIENT_LOG_MODE.
// This is mostly used for audit client. The caller doesn't have to explicitly set up a logger.
func Default() *zap.SugaredLogger {
	defaultLoggerOnce.Do(func() {
		defaultLogger = NewFromEnv("AUDIT_CLIENT_")
	})
	return defaultLogger
}

// WithLogger creates a new context with the provided logger attached.
func WithLogger(ctx context.Context, logger *zap.SugaredLogger) context.Context {
	return context.WithValue(ctx, loggerKey, logger)
}

// FromContext returns the logger stored in the context. If no such logger
// exists, a default logger is returned.
func FromContext(ctx context.Context) *zap.SugaredLogger {
	if logger, ok := ctx.Value(loggerKey).(*zap.SugaredLogger); ok {
		return logger
	}
	return Default()
}

// TestLogger returns a logger configured for tests. It will only output log
// information if specific test fails or is run in verbose mode. See [zaptest]
// for more information.
//
//	func TestMyThing(t *testing.T) {
//		logger := logging.TestLogger(t)
//		thing := &MyThing{logger: logger}
//	}
//
// [zaptest]: https://pkg.go.dev/go.uber.org/zap/zaptest
func TestLogger(tb zaptest.TestingT) *zap.SugaredLogger {
	return zaptest.NewLogger(tb, zaptest.Level(zap.WarnLevel)).Sugar()
}

// GRPCInterceptor returns a gRPC interceptor that populates a logger in the context.
func GRPCInterceptor(logger *zap.SugaredLogger) grpc.UnaryServerInterceptor {
	return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
		var th string
		md, ok := grpcmetadata.FromIncomingContext(ctx)
		if ok {
			if len(md.Get(googleCloudTraceHeader)) > 0 {
				th = md.Get(googleCloudTraceHeader)[0]
			}
		}

		// Best effort to get the current project ID.
		projectID, _ := metadata.ProjectID()

		// This allows us to associate logs to traces.
		// See "logging.googleapis.com/trace" at:
		// https://cloud.google.com/logging/docs/agent/logging/configuration#special-fields
		if th != "" && projectID != "" {
			// The trace header is in the format:
			// "X-Cloud-Trace-Context: TRACE_ID/SPAN_ID;o=TRACE_TRUE"
			// See: https://cloud.google.com/trace/docs/setup#force-trace
			parts := strings.Split(th, "/")
			if len(parts) > 0 && len(parts[0]) > 0 {
				val := fmt.Sprintf("projects/%s/traces/%s", projectID, parts[0])
				logger = logger.With(googleCloudTraceKey, val)
			}
		}

		ctx = WithLogger(ctx, logger)
		return handler(ctx, req)
	}
}

const (
	timestamp  = "timestamp"
	severity   = "severity"
	logger     = "logger"
	caller     = "caller"
	message    = "message"
	stacktrace = "stacktrace"

	levelDebug     = "DEBUG"
	levelInfo      = "INFO"
	levelWarning   = "WARNING"
	levelError     = "ERROR"
	levelCritical  = "CRITICAL"
	levelAlert     = "ALERT"
	levelEmergency = "EMERGENCY"
)

var productionEncoderConfig = zapcore.EncoderConfig{
	TimeKey:        timestamp,
	LevelKey:       severity,
	NameKey:        logger,
	CallerKey:      caller,
	MessageKey:     message,
	StacktraceKey:  stacktrace,
	LineEnding:     zapcore.DefaultLineEnding,
	EncodeLevel:    levelEncoder(),
	EncodeTime:     timeEncoder(),
	EncodeDuration: zapcore.SecondsDurationEncoder,
	EncodeCaller:   zapcore.ShortCallerEncoder,
}

var developmentEncoderConfig = zapcore.EncoderConfig{
	TimeKey:        "",
	LevelKey:       "L",
	NameKey:        "N",
	CallerKey:      "C",
	FunctionKey:    zapcore.OmitKey,
	MessageKey:     "M",
	StacktraceKey:  "S",
	LineEnding:     zapcore.DefaultLineEnding,
	EncodeLevel:    zapcore.CapitalLevelEncoder,
	EncodeTime:     zapcore.ISO8601TimeEncoder,
	EncodeDuration: zapcore.StringDurationEncoder,
	EncodeCaller:   zapcore.ShortCallerEncoder,
}

// levelEncoder transforms a zap level to the associated stackdriver level.
func levelEncoder() zapcore.LevelEncoder {
	return func(l zapcore.Level, enc zapcore.PrimitiveArrayEncoder) {
		switch l {
		case zapcore.InvalidLevel:
			enc.AppendString(levelAlert)
		case zapcore.DebugLevel:
			enc.AppendString(levelDebug)
		case zapcore.InfoLevel:
			enc.AppendString(levelInfo)
		case zapcore.WarnLevel:
			enc.AppendString(levelWarning)
		case zapcore.ErrorLevel:
			enc.AppendString(levelError)
		case zapcore.DPanicLevel:
			enc.AppendString(levelCritical)
		case zapcore.PanicLevel:
			enc.AppendString(levelAlert)
		case zapcore.FatalLevel:
			enc.AppendString(levelEmergency)
		}
	}
}

// timeEncoder encodes the time as RFC3339 nano.
func timeEncoder() zapcore.TimeEncoder {
	return func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
		enc.AppendString(t.Format(time.RFC3339Nano))
	}
}
<commit_msg>Accept optional options in TestLogger (#36)<commit_after>// Copyright 2022 The Authors (see AUTHORS file)
//
// 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 logging sets up and configures standard logging.
package logging

import (
	"context"
	"fmt"
	"os"
	"strings"
	"sync"
	"time"

	"cloud.google.com/go/compute/metadata"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"go.uber.org/zap/zaptest"
	"google.golang.org/grpc"
	grpcmetadata "google.golang.org/grpc/metadata"
)

// contextKey is a private string type to prevent collisions in the context map.
type contextKey string

const (
	// loggerKey points to the value in the context where the logger is stored.
	loggerKey = contextKey("logger")

	// googleCloudTraceHeader is the header with trace data.
	googleCloudTraceHeader = "X-Cloud-Trace-Context"

	// googleCloudTraceKey is the key in the structured log where trace information
	// is expected to be present.
	googleCloudTraceKey = "logging.googleapis.com/trace"
)

var (
	// defaultLogger is the default logger. It is initialized once per package
	// include upon calling DefaultLogger.
	defaultLogger     *zap.SugaredLogger
	defaultLoggerOnce sync.Once
)

// NewFromEnv creates a new logger from env vars.
// Set envPrefix+"LOG_LEVEL" to overwrite log level. Default log level is warning.
// Set envPrefix+"LOG_MODE" to overwrite log mode. Default log mode is production.
func NewFromEnv(envPrefix string) *zap.SugaredLogger {
	level := os.Getenv(envPrefix + "LOG_LEVEL")
	devMode := strings.ToLower(strings.TrimSpace(os.Getenv(envPrefix+"LOG_MODE"))) == "development"
	var cfg zap.Config
	if devMode {
		cfg = zap.NewDevelopmentConfig()
		cfg.EncoderConfig = developmentEncoderConfig
	} else {
		cfg = zap.NewProductionConfig()
		cfg.EncoderConfig = productionEncoderConfig
	}

	var l zapcore.Level
	if err := l.Set(level); err != nil {
		// Invalid level? Default to warn.
		l = zapcore.WarnLevel
	}
	cfg.Level = zap.NewAtomicLevelAt(l)

	logger, err := cfg.Build()
	if err != nil {
		logger = zap.NewNop()
	}

	return logger.Sugar()
}

// Default creates a default logger.
// To overwrite log level and mode, set AUDIT_CLIENT_LOG_LEVEL and AUDIT_CLIENT_LOG_MODE.
// This is mostly used for audit client. The caller doesn't have to explicitly set up a logger.
func Default() *zap.SugaredLogger {
	defaultLoggerOnce.Do(func() {
		defaultLogger = NewFromEnv("AUDIT_CLIENT_")
	})
	return defaultLogger
}

// WithLogger creates a new context with the provided logger attached.
func WithLogger(ctx context.Context, logger *zap.SugaredLogger) context.Context {
	return context.WithValue(ctx, loggerKey, logger)
}

// FromContext returns the logger stored in the context. If no such logger
// exists, a default logger is returned.
func FromContext(ctx context.Context) *zap.SugaredLogger {
	if logger, ok := ctx.Value(loggerKey).(*zap.SugaredLogger); ok {
		return logger
	}
	return Default()
}

// TestLogger returns a logger configured for tests. It will only output log
// information if specific test fails or is run in verbose mode. See [zaptest]
// for more information.
//
//	func TestMyThing(t *testing.T) {
//		logger := logging.TestLogger(t)
//		thing := &MyThing{logger: logger}
//	}
//
// [zaptest]: https://pkg.go.dev/go.uber.org/zap/zaptest
func TestLogger(tb zaptest.TestingT, opts ...zaptest.LoggerOption) *zap.SugaredLogger {
	warnLevelOpt := zaptest.Level(zap.WarnLevel)
	opts = append([]zaptest.LoggerOption{warnLevelOpt}, opts...)
	return zaptest.NewLogger(tb, opts...).Sugar()
}

// GRPCInterceptor returns a gRPC interceptor that populates a logger in the context.
func GRPCInterceptor(logger *zap.SugaredLogger) grpc.UnaryServerInterceptor {
	return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
		var th string
		md, ok := grpcmetadata.FromIncomingContext(ctx)
		if ok {
			if len(md.Get(googleCloudTraceHeader)) > 0 {
				th = md.Get(googleCloudTraceHeader)[0]
			}
		}

		// Best effort to get the current project ID.
		projectID, _ := metadata.ProjectID()

		// This allows us to associate logs to traces.
		// See "logging.googleapis.com/trace" at:
		// https://cloud.google.com/logging/docs/agent/logging/configuration#special-fields
		if th != "" && projectID != "" {
			// The trace header is in the format:
			// "X-Cloud-Trace-Context: TRACE_ID/SPAN_ID;o=TRACE_TRUE"
			// See: https://cloud.google.com/trace/docs/setup#force-trace
			parts := strings.Split(th, "/")
			if len(parts) > 0 && len(parts[0]) > 0 {
				val := fmt.Sprintf("projects/%s/traces/%s", projectID, parts[0])
				logger = logger.With(googleCloudTraceKey, val)
			}
		}

		ctx = WithLogger(ctx, logger)
		return handler(ctx, req)
	}
}

const (
	timestamp  = "timestamp"
	severity   = "severity"
	logger     = "logger"
	caller     = "caller"
	message    = "message"
	stacktrace = "stacktrace"

	levelDebug     = "DEBUG"
	levelInfo      = "INFO"
	levelWarning   = "WARNING"
	levelError     = "ERROR"
	levelCritical  = "CRITICAL"
	levelAlert     = "ALERT"
	levelEmergency = "EMERGENCY"
)

var productionEncoderConfig = zapcore.EncoderConfig{
	TimeKey:        timestamp,
	LevelKey:       severity,
	NameKey:        logger,
	CallerKey:      caller,
	MessageKey:     message,
	StacktraceKey:  stacktrace,
	LineEnding:     zapcore.DefaultLineEnding,
	EncodeLevel:    levelEncoder(),
	EncodeTime:     timeEncoder(),
	EncodeDuration: zapcore.SecondsDurationEncoder,
	EncodeCaller:   zapcore.ShortCallerEncoder,
}

var developmentEncoderConfig = zapcore.EncoderConfig{
	TimeKey:        "",
	LevelKey:       "L",
	NameKey:        "N",
	CallerKey:      "C",
	FunctionKey:    zapcore.OmitKey,
	MessageKey:     "M",
	StacktraceKey:  "S",
	LineEnding:     zapcore.DefaultLineEnding,
	EncodeLevel:    zapcore.CapitalLevelEncoder,
	EncodeTime:     zapcore.ISO8601TimeEncoder,
	EncodeDuration: zapcore.StringDurationEncoder,
	EncodeCaller:   zapcore.ShortCallerEncoder,
}

// levelEncoder transforms a zap level to the associated stackdriver level.
func levelEncoder() zapcore.LevelEncoder {
	return func(l zapcore.Level, enc zapcore.PrimitiveArrayEncoder) {
		switch l {
		case zapcore.InvalidLevel:
			enc.AppendString(levelAlert)
		case zapcore.DebugLevel:
			enc.AppendString(levelDebug)
		case zapcore.InfoLevel:
			enc.AppendString(levelInfo)
		case zapcore.WarnLevel:
			enc.AppendString(levelWarning)
		case zapcore.ErrorLevel:
			enc.AppendString(levelError)
		case zapcore.DPanicLevel:
			enc.AppendString(levelCritical)
		case zapcore.PanicLevel:
			enc.AppendString(levelAlert)
		case zapcore.FatalLevel:
			enc.AppendString(levelEmergency)
		}
	}
}

// timeEncoder encodes the time as RFC3339 nano.
func timeEncoder() zapcore.TimeEncoder {
	return func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
		enc.AppendString(t.Format(time.RFC3339Nano))
	}
}
<|endoftext|>