<commit_before>/*
Copyright 2014 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 config

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"strings"

	"k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
	utilyaml "k8s.io/apimachinery/pkg/util/yaml"
	utilfeature "k8s.io/apiserver/pkg/util/feature"
	api "k8s.io/kubernetes/pkg/apis/core"
	"k8s.io/kubernetes/pkg/apis/core/helper"
	"k8s.io/kubernetes/pkg/features"

	// TODO: remove this import if
	// api.Registry.GroupOrDie(v1.GroupName).GroupVersion.String() is changed
	// to "v1"?
	"k8s.io/kubernetes/pkg/api/legacyscheme"
	// Ensure that core apis are installed
	_ "k8s.io/kubernetes/pkg/apis/core/install"
	k8s_api_v1 "k8s.io/kubernetes/pkg/apis/core/v1"
	"k8s.io/kubernetes/pkg/apis/core/validation"
	kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
	"k8s.io/kubernetes/pkg/util/hash"

	"k8s.io/klog/v2"
)

const (
	maxConfigLength = 10 * 1 << 20 // 10MB
)

// Generate a pod name that is unique among nodes by appending the nodeName.
func generatePodName(name string, nodeName types.NodeName) string {
	return fmt.Sprintf("%s-%s", name, strings.ToLower(string(nodeName)))
}

func applyDefaults(pod *api.Pod, source string, isFile bool, nodeName types.NodeName) error {
	if len(pod.UID) == 0 {
		hasher := md5.New()
		if isFile {
			fmt.Fprintf(hasher, "host:%s", nodeName)
			fmt.Fprintf(hasher, "file:%s", source)
		} else {
			fmt.Fprintf(hasher, "url:%s", source)
		}
		hash.DeepHashObject(hasher, pod)
		pod.UID = types.UID(hex.EncodeToString(hasher.Sum(nil)[0:]))
		klog.V(5).Infof("Generated UID %q pod %q from %s", pod.UID, pod.Name, source)
	}

	pod.Name = generatePodName(pod.Name, nodeName)
	klog.V(5).Infof("Generated Name %q for UID %q from URL %s", pod.Name, pod.UID, source)

	if pod.Namespace == "" {
		pod.Namespace = metav1.NamespaceDefault
	}
	klog.V(5).Infof("Using namespace %q for pod %q from %s", pod.Namespace, pod.Name, source)

	// Set the Host field to indicate this pod is scheduled on the current node.
	pod.Spec.NodeName = string(nodeName)

	pod.ObjectMeta.SelfLink = getSelfLink(pod.Name, pod.Namespace)

	if pod.Annotations == nil {
		pod.Annotations = make(map[string]string)
	}
	// The generated UID is the hash of the file.
	pod.Annotations[kubetypes.ConfigHashAnnotationKey] = string(pod.UID)

	if isFile {
		// Applying the default Taint tolerations to static pods,
		// so they are not evicted when there are node problems.
		helper.AddOrUpdateTolerationInPod(pod, &api.Toleration{
			Operator: "Exists",
			Effect:   api.TaintEffectNoExecute,
		})
	}

	// Set the default status to pending.
	pod.Status.Phase = api.PodPending
	return nil
}

func getSelfLink(name, namespace string) string {
	var selfLink string
	if len(namespace) == 0 {
		namespace = metav1.NamespaceDefault
	}
	selfLink = fmt.Sprintf("/api/v1/namespaces/%s/pods/%s", namespace, name)
	return selfLink
}

type defaultFunc func(pod *api.Pod) error

// tryDecodeSinglePod takes data and tries to extract valid Pod config information from it.
func tryDecodeSinglePod(data []byte, defaultFn defaultFunc) (parsed bool, pod *v1.Pod, err error) {
	// JSON is valid YAML, so this should work for everything.
	json, err := utilyaml.ToJSON(data)
	if err != nil {
		return false, nil, err
	}
	obj, err := runtime.Decode(legacyscheme.Codecs.UniversalDecoder(), json)
	if err != nil {
		return false, pod, err
	}

	newPod, ok := obj.(*api.Pod)
	// Check whether the object could be converted to single pod.
	if !ok {
		return false, pod, fmt.Errorf("invalid pod: %#v", obj)
	}

	// Apply default values and validate the pod.
	if err = defaultFn(newPod); err != nil {
		return true, pod, err
	}
	opts := validation.PodValidationOptions{
		AllowMultipleHugePageResources: utilfeature.DefaultFeatureGate.Enabled(features.HugePageStorageMediumSize),
	}
	if errs := validation.ValidatePodCreate(newPod, opts); len(errs) > 0 {
		return true, pod, fmt.Errorf("invalid pod: %v", errs)
	}
	v1Pod := &v1.Pod{}
	if err := k8s_api_v1.Convert_core_Pod_To_v1_Pod(newPod, v1Pod, nil); err != nil {
		klog.Errorf("Pod %q failed to convert to v1", newPod.Name)
		return true, nil, err
	}
	return true, v1Pod, nil
}

func tryDecodePodList(data []byte, defaultFn defaultFunc) (parsed bool, pods v1.PodList, err error) {
	obj, err := runtime.Decode(legacyscheme.Codecs.UniversalDecoder(), data)
	if err != nil {
		return false, pods, err
	}

	newPods, ok := obj.(*api.PodList)
	// Check whether the object could be converted to list of pods.
	if !ok {
		err = fmt.Errorf("invalid pods list: %#v", obj)
		return false, pods, err
	}

	opts := validation.PodValidationOptions{
		AllowMultipleHugePageResources: utilfeature.DefaultFeatureGate.Enabled(features.HugePageStorageMediumSize),
	}

	// Apply default values and validate pods.
	for i := range newPods.Items {
		newPod := &newPods.Items[i]
		if err = defaultFn(newPod); err != nil {
			return true, pods, err
		}
		if errs := validation.ValidatePodCreate(newPod, opts); len(errs) > 0 {
			err = fmt.Errorf("invalid pod: %v", errs)
			return true, pods, err
		}
	}
	v1Pods := &v1.PodList{}
	if err := k8s_api_v1.Convert_core_PodList_To_v1_PodList(newPods, v1Pods, nil); err != nil {
		return true, pods, err
	}
	return true, *v1Pods, err
}
<commit_msg>UPSTREAM: 87461: kubelet: ensure pod UIDs are unique<commit_after>/*
Copyright 2014 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 config

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"strings"

	"k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
	utilyaml "k8s.io/apimachinery/pkg/util/yaml"
	utilfeature "k8s.io/apiserver/pkg/util/feature"
	api "k8s.io/kubernetes/pkg/apis/core"
	"k8s.io/kubernetes/pkg/apis/core/helper"
	"k8s.io/kubernetes/pkg/features"

	// TODO: remove this import if
	// api.Registry.GroupOrDie(v1.GroupName).GroupVersion.String() is changed
	// to "v1"?
	"k8s.io/kubernetes/pkg/api/legacyscheme"
	// Ensure that core apis are installed
	_ "k8s.io/kubernetes/pkg/apis/core/install"
	k8s_api_v1 "k8s.io/kubernetes/pkg/apis/core/v1"
	"k8s.io/kubernetes/pkg/apis/core/validation"
	kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
	"k8s.io/kubernetes/pkg/util/hash"

	"k8s.io/klog/v2"
)

const (
	maxConfigLength = 10 * 1 << 20 // 10MB
)

// Generate a pod name that is unique among nodes by appending the nodeName.
func generatePodName(name string, nodeName types.NodeName) string {
	return fmt.Sprintf("%s-%s", name, strings.ToLower(string(nodeName)))
}

func applyDefaults(pod *api.Pod, source string, isFile bool, nodeName types.NodeName) error {
	if len(pod.UID) == 0 {
		hasher := md5.New()
		hash.DeepHashObject(hasher, pod)
		// DeepHashObject resets the hash, so we should write the pod source
		// information AFTER it.
		if isFile {
			fmt.Fprintf(hasher, "host:%s", nodeName)
			fmt.Fprintf(hasher, "file:%s", source)
		} else {
			fmt.Fprintf(hasher, "url:%s", source)
		}
		pod.UID = types.UID(hex.EncodeToString(hasher.Sum(nil)[0:]))
		klog.V(5).Infof("Generated UID %q pod %q from %s", pod.UID, pod.Name, source)
	}

	pod.Name = generatePodName(pod.Name, nodeName)
	klog.V(5).Infof("Generated Name %q for UID %q from URL %s", pod.Name, pod.UID, source)

	if pod.Namespace == "" {
		pod.Namespace = metav1.NamespaceDefault
	}
	klog.V(5).Infof("Using namespace %q for pod %q from %s", pod.Namespace, pod.Name, source)

	// Set the Host field to indicate this pod is scheduled on the current node.
	pod.Spec.NodeName = string(nodeName)

	pod.ObjectMeta.SelfLink = getSelfLink(pod.Name, pod.Namespace)

	if pod.Annotations == nil {
		pod.Annotations = make(map[string]string)
	}
	// The generated UID is the hash of the file.
	pod.Annotations[kubetypes.ConfigHashAnnotationKey] = string(pod.UID)

	if isFile {
		// Applying the default Taint tolerations to static pods,
		// so they are not evicted when there are node problems.
		helper.AddOrUpdateTolerationInPod(pod, &api.Toleration{
			Operator: "Exists",
			Effect:   api.TaintEffectNoExecute,
		})
	}

	// Set the default status to pending.
	pod.Status.Phase = api.PodPending
	return nil
}

func getSelfLink(name, namespace string) string {
	var selfLink string
	if len(namespace) == 0 {
		namespace = metav1.NamespaceDefault
	}
	selfLink = fmt.Sprintf("/api/v1/namespaces/%s/pods/%s", namespace, name)
	return selfLink
}

type defaultFunc func(pod *api.Pod) error

// tryDecodeSinglePod takes data and tries to extract valid Pod config information from it.
func tryDecodeSinglePod(data []byte, defaultFn defaultFunc) (parsed bool, pod *v1.Pod, err error) {
	// JSON is valid YAML, so this should work for everything.
	json, err := utilyaml.ToJSON(data)
	if err != nil {
		return false, nil, err
	}
	obj, err := runtime.Decode(legacyscheme.Codecs.UniversalDecoder(), json)
	if err != nil {
		return false, pod, err
	}

	newPod, ok := obj.(*api.Pod)
	// Check whether the object could be converted to single pod.
	if !ok {
		return false, pod, fmt.Errorf("invalid pod: %#v", obj)
	}

	// Apply default values and validate the pod.
	if err = defaultFn(newPod); err != nil {
		return true, pod, err
	}
	opts := validation.PodValidationOptions{
		AllowMultipleHugePageResources: utilfeature.DefaultFeatureGate.Enabled(features.HugePageStorageMediumSize),
	}
	if errs := validation.ValidatePodCreate(newPod, opts); len(errs) > 0 {
		return true, pod, fmt.Errorf("invalid pod: %v", errs)
	}
	v1Pod := &v1.Pod{}
	if err := k8s_api_v1.Convert_core_Pod_To_v1_Pod(newPod, v1Pod, nil); err != nil {
		klog.Errorf("Pod %q failed to convert to v1", newPod.Name)
		return true, nil, err
	}
	return true, v1Pod, nil
}

func tryDecodePodList(data []byte, defaultFn defaultFunc) (parsed bool, pods v1.PodList, err error) {
	obj, err := runtime.Decode(legacyscheme.Codecs.UniversalDecoder(), data)
	if err != nil {
		return false, pods, err
	}

	newPods, ok := obj.(*api.PodList)
	// Check whether the object could be converted to list of pods.
	if !ok {
		err = fmt.Errorf("invalid pods list: %#v", obj)
		return false, pods, err
	}

	opts := validation.PodValidationOptions{
		AllowMultipleHugePageResources: utilfeature.DefaultFeatureGate.Enabled(features.HugePageStorageMediumSize),
	}

	// Apply default values and validate pods.
	for i := range newPods.Items {
		newPod := &newPods.Items[i]
		if err = defaultFn(newPod); err != nil {
			return true, pods, err
		}
		if errs := validation.ValidatePodCreate(newPod, opts); len(errs) > 0 {
			err = fmt.Errorf("invalid pod: %v", errs)
			return true, pods, err
		}
	}
	v1Pods := &v1.PodList{}
	if err := k8s_api_v1.Convert_core_PodList_To_v1_PodList(newPods, v1Pods, nil); err != nil {
		return true, pods, err
	}
	return true, *v1Pods, err
}
<|endoftext|>


<commit_before>package service

import (
	"time"

	kubeModels "git.containerum.net/ch/kube-client/pkg/model"
	"github.com/containerum/chkit/pkg/model"
	"github.com/sirupsen/logrus"
)

type Service struct {
	Name      string
	CreatedAt *time.Time
	Deploy    string
	IPs       []string
	Domain    string
	Ports     []Port
	origin    *kubeModels.Service
}

func ServiceFromKube(kubeService kubeModels.Service) Service {
	ports := make([]Port, 0, len(kubeService.Ports))
	for _, kubePort := range kubeService.Ports {
		ports = append(ports, PortFromKube(kubePort))
	}
	var createdAt *time.Time
	if kubeService.CreatedAt != nil {
		t, err := time.Parse(model.TimestampFormat, *kubeService.CreatedAt)
		if err != nil {
			logrus.WithError(err).Debugf("invalid created_at timestamp")
		} else {
			createdAt = &t
		}
	}
	return Service{
		Name:      kubeService.Name,
		CreatedAt: createdAt,
		Deploy:    kubeService.Deploy,
		IPs:       kubeService.IPs,
		Domain:    kubeService.Domain,
		Ports:     ports,
		origin:    &kubeService,
	}
}

func (serv *Service) ToKube() kubeModels.Service {
	if serv.origin != nil {
		return *serv.origin
	}
	kubeServ := kubeModels.Service{
		Name:   serv.Name,
		Deploy: serv.Deploy,
		IPs:    serv.IPs,
		Domain: serv.Domain,
	}
	ports := make([]kubeModels.ServicePort, 0, len(serv.Ports))
	for _, port := range serv.Ports {
		ports = append(ports, kubeModels.ServicePort(kubeModels.ServicePort{
			Name:       port.Name,
			Port:       port.Port,
			TargetPort: port.TargetPort,
			Protocol:   kubeModels.Protocol(port.Protocol),
		}))
	}
	kubeServ.Ports = ports
	serv.origin = &kubeServ
	return *serv.origin
}
<commit_msg>fix to kube method<commit_after>package service

import (
	"time"

	kubeModels "git.containerum.net/ch/kube-client/pkg/model"
	"github.com/containerum/chkit/pkg/model"
	"github.com/sirupsen/logrus"
)

type Service struct {
	Name      string
	CreatedAt *time.Time
	Deploy    string
	IPs       []string
	Domain    string
	Ports     []Port
	origin    *kubeModels.Service
}

func ServiceFromKube(kubeService kubeModels.Service) Service {
	ports := make([]Port, 0, len(kubeService.Ports))
	for _, kubePort := range kubeService.Ports {
		ports = append(ports, PortFromKube(kubePort))
	}
	var createdAt *time.Time
	if kubeService.CreatedAt != nil {
		t, err := time.Parse(model.TimestampFormat, *kubeService.CreatedAt)
		if err != nil {
			logrus.WithError(err).Debugf("invalid created_at timestamp")
		} else {
			createdAt = &t
		}
	}
	return Service{
		Name:      kubeService.Name,
		CreatedAt: createdAt,
		Deploy:    kubeService.Deploy,
		IPs:       kubeService.IPs,
		Domain:    kubeService.Domain,
		Ports:     ports,
		origin:    &kubeService,
	}
}

func (serv *Service) ToKube() kubeModels.Service {
	kubeServ := kubeModels.Service{
		Name:   serv.Name,
		Deploy: serv.Deploy,
		IPs:    serv.IPs,
		Domain: serv.Domain,
	}
	ports := make([]kubeModels.ServicePort, 0, len(serv.Ports))
	for _, port := range serv.Ports {
		ports = append(ports, kubeModels.ServicePort(kubeModels.ServicePort{
			Name:       port.Name,
			Port:       port.Port,
			TargetPort: port.TargetPort,
			Protocol:   kubeModels.Protocol(port.Protocol),
		}))
	}
	kubeServ.Ports = ports
	serv.origin = &kubeServ
	return *serv.origin
}
<|endoftext|>


<commit_before>package proxy_test

import (
	"testing"

	"github.com/hellofresh/janus/pkg/proxy"
	"github.com/stretchr/testify/assert"
)

func TestSuccessfulValidation(t *testing.T) {
	definition := proxy.Definition{
		ListenPath: "/*",
	}

	assert.True(t, proxy.Validate(&definition))
}

func TestEmptyListenPathValidation(t *testing.T) {
	definition := proxy.Definition{}

	assert.False(t, proxy.Validate(&definition))
}

func TestSpaceInListenPathValidation(t *testing.T) {
	definition := proxy.Definition{
		ListenPath: " ",
	}

	assert.False(t, proxy.Validate(&definition))
}

func TestRouteToJSON(t *testing.T) {
	definition := proxy.Definition{
		Methods: make([]string, 0),
		Hosts:   make([]string, 0),
	}
	route := proxy.NewRoute(&definition)
	json, err := route.JSONMarshal()
	assert.NoError(t, err)
	assert.JSONEq(
		t,
		`{"proxy": {"append_path":false, "enable_load_balancing":false, "methods":[], "hosts":[], "preserve_host":false, "listen_path":"", "upstream_url":"", "strip_path":false}}`,
		string(json),
	)
}

func TestJSONToRoute(t *testing.T) {
	route, err := proxy.JSONUnmarshalRoute([]byte(`{"proxy": {"append_path":false, "enable_load_balancing":false, "methods":[], "hosts":[], "preserve_host":false, "listen_path":"", "upstream_url":"/*", "strip_path":false}}`))

	assert.NoError(t, err)
	assert.IsType(t, &proxy.Route{}, route)
}
<commit_msg>Added more tests for the proxy definition<commit_after>package proxy_test

import (
	"testing"

	"github.com/hellofresh/janus/pkg/proxy"
	"github.com/stretchr/testify/assert"
)

func TestSuccessfulValidation(t *testing.T) {
	definition := proxy.Definition{
		ListenPath: "/*",
	}

	assert.True(t, proxy.Validate(&definition))
}

func TestEmptyListenPathValidation(t *testing.T) {
	definition := proxy.Definition{}

	assert.False(t, proxy.Validate(&definition))
}

func TestNilProxy(t *testing.T) {
	assert.False(t, proxy.Validate(nil))
}

func TestSpaceInListenPathValidation(t *testing.T) {
	definition := proxy.Definition{
		ListenPath: " ",
	}

	assert.False(t, proxy.Validate(&definition))
}

func TestRouteToJSON(t *testing.T) {
	definition := proxy.Definition{
		Methods: make([]string, 0),
		Hosts:   make([]string, 0),
	}
	route := proxy.NewRoute(&definition)
	json, err := route.JSONMarshal()
	assert.NoError(t, err)
	assert.JSONEq(
		t,
		`{"proxy": {"append_path":false, "enable_load_balancing":false, "methods":[], "hosts":[], "preserve_host":false, "listen_path":"", "upstream_url":"", "strip_path":false}}`,
		string(json),
	)
}

func TestJSONToRoute(t *testing.T) {
	route, err := proxy.JSONUnmarshalRoute([]byte(`{"proxy": {"append_path":false, "enable_load_balancing":false, "methods":[], "hosts":[], "preserve_host":false, "listen_path":"", "upstream_url":"/*", "strip_path":false}}`))

	assert.NoError(t, err)
	assert.IsType(t, &proxy.Route{}, route)
}

func TestJSONToRouteError(t *testing.T) {
	_, err := proxy.JSONUnmarshalRoute([]byte{})

	assert.Error(t, err)
}
<|endoftext|>


<commit_before>package proxy

import (
	"testing"

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

func TestDefinition(t *testing.T) {
	t.Parallel()

	tests := []struct {
		scenario string
		function func(*testing.T)
	}{
		{
			scenario: "new definitions",
			function: testNewDefinitions,
		},
		{
			scenario: "successful validation",
			function: testSuccessfulValidation,
		},
		{
			scenario: "empty listen path validation",
			function: testEmptyListenPathValidation,
		},
		{
			scenario: "invalid target url validation",
			function: testInvalidTargetURLValidation,
		},
		{
			scenario: "is balancer defined",
			function: testIsBalancerDefined,
		},
	}

	for _, test := range tests {
		t.Run(test.scenario, func(t *testing.T) {
			test.function(t)
		})
	}
}

func testNewDefinitions(t *testing.T) {
	definition := NewDefinition()

	assert.Equal(t, []string{"GET"}, definition.Methods)
	assert.NotNil(t, definition)
}

func testSuccessfulValidation(t *testing.T) {
	definition := Definition{
		ListenPath: "/*",
		Upstreams: &Upstreams{
			Balancing: "roundrobin",
			Targets: Targets{
				{Target: "http://test.com"},
			},
		},
	}
	isValid, err := definition.Validate()

	assert.NoError(t, err)
	assert.True(t, isValid)
}

func testEmptyListenPathValidation(t *testing.T) {
	definition := Definition{}
	isValid, err := definition.Validate()

	assert.Error(t, err)
	assert.False(t, isValid)
}

func testInvalidTargetURLValidation(t *testing.T) {
	definition := Definition{
		ListenPath: " ",
		Upstreams: &Upstreams{
			Balancing: "roundrobin",
			Targets: Targets{
				{Target: "wrong"},
			},
		},
	}
	isValid, err := definition.Validate()

	assert.Error(t, err)
	assert.False(t, isValid)
}

func testIsBalancerDefined(t *testing.T) {
	definition := NewDefinition()
	assert.False(t, definition.IsBalancerDefined())

	target := &Target{Target: "http://localhost:8080/api-name"}
	definition.Upstreams.Targets = append(definition.Upstreams.Targets, target)
	assert.True(t, definition.IsBalancerDefined())
}
<commit_msg>Added more tests<commit_after>package proxy

import (
	"testing"

	"github.com/hellofresh/janus/pkg/middleware"
	"github.com/stretchr/testify/assert"
)

func TestDefinition(t *testing.T) {
	t.Parallel()

	tests := []struct {
		scenario string
		function func(*testing.T)
	}{
		{
			scenario: "new definitions",
			function: testNewDefinitions,
		},
		{
			scenario: "successful validation",
			function: testSuccessfulValidation,
		},
		{
			scenario: "empty listen path validation",
			function: testEmptyListenPathValidation,
		},
		{
			scenario: "invalid target url validation",
			function: testInvalidTargetURLValidation,
		},
		{
			scenario: "is balancer defined",
			function: testIsBalancerDefined,
		},
		{
			scenario: "add middleware",
			function: testAddMiddlewares,
		},
	}

	for _, test := range tests {
		t.Run(test.scenario, func(t *testing.T) {
			test.function(t)
		})
	}
}

func testNewDefinitions(t *testing.T) {
	definition := NewDefinition()

	assert.Equal(t, []string{"GET"}, definition.Methods)
	assert.NotNil(t, definition)
}

func testSuccessfulValidation(t *testing.T) {
	definition := Definition{
		ListenPath: "/*",
		Upstreams: &Upstreams{
			Balancing: "roundrobin",
			Targets: Targets{
				{Target: "http://test.com"},
			},
		},
	}
	isValid, err := definition.Validate()

	assert.NoError(t, err)
	assert.True(t, isValid)
}

func testEmptyListenPathValidation(t *testing.T) {
	definition := Definition{}
	isValid, err := definition.Validate()

	assert.Error(t, err)
	assert.False(t, isValid)
}

func testInvalidTargetURLValidation(t *testing.T) {
	definition := Definition{
		ListenPath: " ",
		Upstreams: &Upstreams{
			Balancing: "roundrobin",
			Targets: Targets{
				{Target: "wrong"},
			},
		},
	}
	isValid, err := definition.Validate()

	assert.Error(t, err)
	assert.False(t, isValid)
}

func testIsBalancerDefined(t *testing.T) {
	definition := NewDefinition()
	assert.False(t, definition.IsBalancerDefined())

	target := &Target{Target: "http://localhost:8080/api-name"}
	definition.Upstreams.Targets = append(definition.Upstreams.Targets, target)
	assert.True(t, definition.IsBalancerDefined())
	assert.Len(t, definition.Upstreams.Targets.ToBalancerTargets(), 1)
}

func testAddMiddlewares(t *testing.T) {
	definition := NewDefinition()
	definition.AddMiddleware(middleware.NewLogger().Handler)

	assert.Len(t, definition.Middleware(), 1)
}
<|endoftext|>


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

package testutils

import (
	"fmt"
	"sync"
	"testing"

	"github.com/stretchr/testify/assert"
	"go.uber.org/zap"
)

func TestNewLogger(t *testing.T) {
	logger, log := NewLogger()
	logger.Warn("hello", zap.String("x", "y"))

	assert.Equal(t, `{"level":"warn","msg":"hello","x":"y"}`, log.Lines()[0])
	assert.Equal(t, map[string]string{
		"level": "warn",
		"msg":   "hello",
		"x":     "y",
	}, log.JSONLine(0))
}

func TestJSONLineError(t *testing.T) {
	log := &Buffer{}
	log.WriteString("bad-json\n")
	_, ok := log.JSONLine(0)["error"]
	assert.True(t, ok, "must have 'error' key")
}

// NB. Run with -race to ensure no race condition
func TestRaceCondition(t *testing.T) {
	logger, buffer := NewLogger()

	start := make(chan struct{})
	finish := sync.WaitGroup{}
	finish.Add(2)

	go func() {
		<-start
		logger.Info("test")
		finish.Done()
	}()

	go func() {
		<-start
		buffer.Lines()
		buffer.Stripped()
		_ = buffer.String()
		finish.Done()
	}()

	close(start)
	finish.Wait()
}

func TestLogMatcher(t *testing.T) {
	tests := []struct {
		occurences int
		subStr     string
		logs       []string
		expected   bool
		errMsg     string
	}{
		{occurences: 1, expected: false, errMsg: "subStr '' does not occur 1 time(s) in []"},
		{occurences: 1, subStr: "hi", logs: []string{"hi"}, expected: true},
		{occurences: 3, subStr: "hi", logs: []string{"hi", "hi"}, expected: false, errMsg: "subStr 'hi' does not occur 3 time(s) in [hi hi]"},
		{occurences: 3, subStr: "hi", logs: []string{"hi", "hi", "hi"}, expected: true},
		{occurences: 1, subStr: "hi", logs: []string{"bye", "bye"}, expected: false, errMsg: "subStr 'hi' does not occur 1 time(s) in [bye bye]"},
	}
	for i, tt := range tests {
		test := tt
		t.Run(fmt.Sprintf("%v", i), func(t *testing.T) {
			match, errMsg := LogMatcher(test.occurences, test.subStr, test.logs)
			assert.Equal(t, test.expected, match)
			assert.Equal(t, test.errMsg, errMsg)
		})
	}
}
<commit_msg>Fix spelling in logger_test.go (#1390)<commit_after>// Copyright (c) 2017 Uber Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package testutils

import (
	"fmt"
	"sync"
	"testing"

	"github.com/stretchr/testify/assert"
	"go.uber.org/zap"
)

func TestNewLogger(t *testing.T) {
	logger, log := NewLogger()
	logger.Warn("hello", zap.String("x", "y"))

	assert.Equal(t, `{"level":"warn","msg":"hello","x":"y"}`, log.Lines()[0])
	assert.Equal(t, map[string]string{
		"level": "warn",
		"msg":   "hello",
		"x":     "y",
	}, log.JSONLine(0))
}

func TestJSONLineError(t *testing.T) {
	log := &Buffer{}
	log.WriteString("bad-json\n")
	_, ok := log.JSONLine(0)["error"]
	assert.True(t, ok, "must have 'error' key")
}

// NB. Run with -race to ensure no race condition
func TestRaceCondition(t *testing.T) {
	logger, buffer := NewLogger()

	start := make(chan struct{})
	finish := sync.WaitGroup{}
	finish.Add(2)

	go func() {
		<-start
		logger.Info("test")
		finish.Done()
	}()

	go func() {
		<-start
		buffer.Lines()
		buffer.Stripped()
		_ = buffer.String()
		finish.Done()
	}()

	close(start)
	finish.Wait()
}

func TestLogMatcher(t *testing.T) {
	tests := []struct {
		occurrences int
		subStr      string
		logs        []string
		expected    bool
		errMsg      string
	}{
		{occurrences: 1, expected: false, errMsg: "subStr '' does not occur 1 time(s) in []"},
		{occurrences: 1, subStr: "hi", logs: []string{"hi"}, expected: true},
		{occurrences: 3, subStr: "hi", logs: []string{"hi", "hi"}, expected: false, errMsg: "subStr 'hi' does not occur 3 time(s) in [hi hi]"},
		{occurrences: 3, subStr: "hi", logs: []string{"hi", "hi", "hi"}, expected: true},
		{occurrences: 1, subStr: "hi", logs: []string{"bye", "bye"}, expected: false, errMsg: "subStr 'hi' does not occur 1 time(s) in [bye bye]"},
	}
	for i, tt := range tests {
		test := tt
		t.Run(fmt.Sprintf("%v", i), func(t *testing.T) {
			match, errMsg := LogMatcher(test.occurrences, test.subStr, test.logs)
			assert.Equal(t, test.expected, match)
			assert.Equal(t, test.errMsg, errMsg)
		})
	}
}
<|endoftext|>


<commit_before>package timeseries

import (
	"math"
	"sort"
	"time"

	"github.com/grafana/grafana-plugin-sdk-go/data"
)

func NewTimeSeriesData() *TimeSeriesData {
	return &TimeSeriesData{
		TS:   NewTimeSeries(),
		Meta: TimeSeriesMeta{},
	}
}

func (tsd TimeSeriesData) Len() int {
	return len(tsd.TS)
}

func (tsd *TimeSeriesData) Add(point TimePoint) *TimeSeriesData {
	if tsd.TS == nil {
		tsd.TS = NewTimeSeries()
	}

	tsd.TS = append(tsd.TS, point)
	return tsd
}

// Gets point timestamp rounded according to provided interval.
func (p *TimePoint) GetTimeFrame(interval time.Duration) time.Time {
	return p.Time.Truncate(interval)
}

// GroupBy groups points in given interval by applying provided `aggFunc`. Source time series should be sorted by time.
func (ts TimeSeries) GroupBy(interval time.Duration, aggFunc AggFunc) TimeSeries {
	if ts.Len() == 0 {
		return ts
	}

	groupedSeries := NewTimeSeries()
	frame := make([]TimePoint, 0)
	frameTS := ts[0].GetTimeFrame(interval)
	var pointFrameTs time.Time

	for _, point := range ts {
		pointFrameTs = point.GetTimeFrame(interval)

		// Iterate over points and push it into the frame if point time stamp fit the frame
		if pointFrameTs == frameTS {
			frame = append(frame, point)
		} else if pointFrameTs.After(frameTS) {
			// If point outside frame, then we've done with current frame
			groupedSeries = append(groupedSeries, TimePoint{
				Time:  frameTS,
				Value: aggFunc(frame),
			})

			// Move frame window to next non-empty interval and fill empty by null
			frameTS = frameTS.Add(interval)
			for frameTS.Before(pointFrameTs) {
				groupedSeries = append(groupedSeries, TimePoint{
					Time:  frameTS,
					Value: nil,
				})
				frameTS = frameTS.Add(interval)
			}
			frame = []TimePoint{point}
		}
	}

	groupedSeries = append(groupedSeries, TimePoint{
		Time:  frameTS,
		Value: aggFunc(frame),
	})

	return groupedSeries
}

func (ts TimeSeries) GroupByRange(aggFunc AggFunc) TimeSeries {
	if ts.Len() == 0 {
		return ts
	}

	value := aggFunc(ts)
	return []TimePoint{
		{Time: ts[0].Time, Value: value},
		{Time: ts[ts.Len()-1].Time, Value: value},
	}
}

func (ts TimeSeries) Delta() TimeSeries {
	deltaSeries := NewTimeSeries()
	for i := 1; i < ts.Len(); i++ {
		currentPoint := ts[i]
		previousPoint := ts[i-1]
		if currentPoint.Value != nil && previousPoint.Value != nil {
			deltaValue := *currentPoint.Value - *previousPoint.Value
			deltaSeries = append(deltaSeries, TimePoint{Time: ts[i].Time, Value: &deltaValue})
		} else {
			deltaSeries = append(deltaSeries, TimePoint{Time: ts[i].Time, Value: nil})
		}
	}

	return deltaSeries
}

func (ts TimeSeries) Rate() TimeSeries {
	rateSeries := NewTimeSeries()
	var valueDelta float64 = 0
	for i := 1; i < ts.Len(); i++ {
		currentPoint := ts[i]
		previousPoint := ts[i-1]
		timeDelta := currentPoint.Time.Sub(previousPoint.Time)

		// Handle counter reset - use previous value
		if currentPoint.Value != nil && previousPoint.Value != nil && *currentPoint.Value >= *previousPoint.Value {
			valueDelta = (*currentPoint.Value - *previousPoint.Value) / timeDelta.Seconds()
		}

		value := valueDelta
		rateSeries = append(rateSeries, TimePoint{Time: ts[i].Time, Value: &value})
	}

	return rateSeries
}

func (ts TimeSeries) Transform(transformFunc TransformFunc) TimeSeries {
	for i, p := range ts {
		ts[i] = transformFunc(p)
	}
	return ts
}

func Filter(series []*TimeSeriesData, n int, order string, aggFunc AggFunc) []*TimeSeriesData {
	SortBy(series, "asc", aggFunc)

	maxN := int(math.Min(float64(n), float64(len(series))))
	filteredSeries := make([]*TimeSeriesData, maxN)
	for i := 0; i < maxN; i++ {
		if order == "top" {
			filteredSeries[i] = series[len(series)-1-i]
		} else if order == "bottom" {
			filteredSeries[i] = series[i]
		}
	}

	return filteredSeries
}

func AggregateBy(series []*TimeSeriesData, interval time.Duration, aggFunc AggFunc) *TimeSeriesData {
	aggregatedSeries := NewTimeSeries()

	// Combine all points into one time series
	for _, s := range series {
		aggregatedSeries = append(aggregatedSeries, s.TS...)
	}

	// GroupBy works correctly only with sorted time series
	aggregatedSeries.Sort()

	aggregatedSeries = aggregatedSeries.GroupBy(interval, aggFunc)
	aggregatedSeriesData := NewTimeSeriesData()
	aggregatedSeriesData.TS = aggregatedSeries
	return aggregatedSeriesData
}

func AggregateByRange(series []*TimeSeriesData, aggFunc AggFunc) *TimeSeriesData {
	aggregatedSeries := NewTimeSeries()

	// Combine all points into one time series
	for _, s := range series {
		aggregatedSeries = append(aggregatedSeries, s.TS...)
	}

	value := aggFunc(aggregatedSeries)
	aggregatedSeriesData := NewTimeSeriesData()
	aggregatedSeriesData.TS = []TimePoint{
		{Time: aggregatedSeries[0].Time, Value: value},
		{Time: aggregatedSeries[aggregatedSeries.Len()-1].Time, Value: value},
	}

	return aggregatedSeriesData
}

func (ts TimeSeries) Sort() {
	sorted := sort.SliceIsSorted(ts, ts.less())
	if !sorted {
		sort.Slice(ts, ts.less())
	}
}

// Implements less() function for sorting slice
func (ts TimeSeries) less() func(i, j int) bool {
	return func(i, j int) bool {
		return ts[i].Time.Before(ts[j].Time)
	}
}

func SumSeries(series []*TimeSeriesData) *TimeSeriesData {
	// Build unique set of time stamps from all series
	interpolatedTimeStampsMap := make(map[time.Time]time.Time)
	for _, s := range series {
		for _, p := range s.TS {
			interpolatedTimeStampsMap[p.Time] = p.Time
		}
	}

	// Convert to slice and sort
	interpolatedTimeStamps := make([]time.Time, 0)
	for _, ts := range interpolatedTimeStampsMap {
		interpolatedTimeStamps = append(interpolatedTimeStamps, ts)
	}
	sort.Slice(interpolatedTimeStamps, func(i, j int) bool {
		return interpolatedTimeStamps[i].Before(interpolatedTimeStamps[j])
	})

	interpolatedSeries := make([]TimeSeries, 0)

	for _, s := range series {
		if s.Len() == 0 {
			continue
		}

		pointsToInterpolate := make([]TimePoint, 0)

		currentPointIndex := 0
		for _, its := range interpolatedTimeStamps {
			currentPoint := s.TS[currentPointIndex]
			if its.Equal(currentPoint.Time) {
				if currentPointIndex < s.Len()-1 {
					currentPointIndex++
				}
			} else {
				pointsToInterpolate = append(pointsToInterpolate, TimePoint{Time: its, Value: nil})
			}
		}

		s.TS = append(s.TS, pointsToInterpolate...)
		s.TS.Sort()
		s.TS = interpolateSeries(s.TS)
		interpolatedSeries = append(interpolatedSeries, s.TS)
	}

	sumSeries := NewTimeSeriesData()
	for i := 0; i < len(interpolatedTimeStamps); i++ {
		var sum float64 = 0
		for _, s := range interpolatedSeries {
			if s[i].Value != nil {
				sum += *s[i].Value
			}
		}
		sumSeries.TS = append(sumSeries.TS, TimePoint{Time: interpolatedTimeStamps[i], Value: &sum})
	}

	return sumSeries
}

func interpolateSeries(series TimeSeries) TimeSeries {
	for i := series.Len() - 1; i >= 0; i-- {
		point := series[i]
		if point.Value == nil {
			left := findNearestLeft(series, i)
			right := findNearestRight(series, i)

			if left == nil && right == nil {
				continue
			}
			if left == nil {
				left = right
			}
			if right == nil {
				right = left
			}

			pointValue := linearInterpolation(point.Time, *left, *right)
			point.Value = &pointValue
			series[i] = point
		}
	}
	return series
}

func linearInterpolation(ts time.Time, left, right TimePoint) float64 {
	if left.Time.Equal(right.Time) {
		return (*left.Value + *right.Value) / 2
	} else {
		return *left.Value + (*right.Value-*left.Value)/float64((right.Time.UnixNano()-left.Time.UnixNano()))*float64((ts.UnixNano()-left.Time.UnixNano()))
	}
}

func findNearestRight(series TimeSeries, pointIndex int) *TimePoint {
	for i := pointIndex; i < series.Len(); i++ {
		if series[i].Value != nil {
			return &series[i]
		}
	}
	return nil
}

func findNearestLeft(series TimeSeries, pointIndex int) *TimePoint {
	for i := pointIndex; i > 0; i-- {
		if series[i].Value != nil {
			return &series[i]
		}
	}
	return nil
}

func getTimeFieldIndex(frame *data.Frame) int {
	for i := 0; i < len(frame.Fields); i++ {
		if frame.Fields[i].Type() == data.FieldTypeTime {
			return i
		}
	}

	return -1
}

func getTimestampAt(frame *data.Frame, index int) *time.Time {
	timeFieldIdx := getTimeFieldIndex(frame)
	if timeFieldIdx < 0 {
		return nil
	}

	tsValue := frame.Fields[timeFieldIdx].At(index)
	ts, ok := tsValue.(time.Time)
	if !ok {
		return nil
	}

	return &ts
}

func setTimeAt(frame *data.Frame, frameTs time.Time, index int) {
	for _, field := range frame.Fields {
		if field.Type() == data.FieldTypeTime {
			field.Insert(index, frameTs)
		}
	}
}
<commit_msg>fix AggregateByRange() on empty data<commit_after>package timeseries

import (
	"math"
	"sort"
	"time"

	"github.com/grafana/grafana-plugin-sdk-go/data"
)

func NewTimeSeriesData() *TimeSeriesData {
	return &TimeSeriesData{
		TS:   NewTimeSeries(),
		Meta: TimeSeriesMeta{},
	}
}

func (tsd TimeSeriesData) Len() int {
	return len(tsd.TS)
}

func (tsd *TimeSeriesData) Add(point TimePoint) *TimeSeriesData {
	if tsd.TS == nil {
		tsd.TS = NewTimeSeries()
	}

	tsd.TS = append(tsd.TS, point)
	return tsd
}

// Gets point timestamp rounded according to provided interval.
func (p *TimePoint) GetTimeFrame(interval time.Duration) time.Time {
	return p.Time.Truncate(interval)
}

// GroupBy groups points in given interval by applying provided `aggFunc`. Source time series should be sorted by time.
func (ts TimeSeries) GroupBy(interval time.Duration, aggFunc AggFunc) TimeSeries {
	if ts.Len() == 0 {
		return ts
	}

	groupedSeries := NewTimeSeries()
	frame := make([]TimePoint, 0)
	frameTS := ts[0].GetTimeFrame(interval)
	var pointFrameTs time.Time

	for _, point := range ts {
		pointFrameTs = point.GetTimeFrame(interval)

		// Iterate over points and push it into the frame if point time stamp fit the frame
		if pointFrameTs == frameTS {
			frame = append(frame, point)
		} else if pointFrameTs.After(frameTS) {
			// If point outside frame, then we've done with current frame
			groupedSeries = append(groupedSeries, TimePoint{
				Time:  frameTS,
				Value: aggFunc(frame),
			})

			// Move frame window to next non-empty interval and fill empty by null
			frameTS = frameTS.Add(interval)
			for frameTS.Before(pointFrameTs) {
				groupedSeries = append(groupedSeries, TimePoint{
					Time:  frameTS,
					Value: nil,
				})
				frameTS = frameTS.Add(interval)
			}
			frame = []TimePoint{point}
		}
	}

	groupedSeries = append(groupedSeries, TimePoint{
		Time:  frameTS,
		Value: aggFunc(frame),
	})

	return groupedSeries
}

func (ts TimeSeries) GroupByRange(aggFunc AggFunc) TimeSeries {
	if ts.Len() == 0 {
		return ts
	}

	value := aggFunc(ts)
	return []TimePoint{
		{Time: ts[0].Time, Value: value},
		{Time: ts[ts.Len()-1].Time, Value: value},
	}
}

func (ts TimeSeries) Delta() TimeSeries {
	deltaSeries := NewTimeSeries()
	for i := 1; i < ts.Len(); i++ {
		currentPoint := ts[i]
		previousPoint := ts[i-1]
		if currentPoint.Value != nil && previousPoint.Value != nil {
			deltaValue := *currentPoint.Value - *previousPoint.Value
			deltaSeries = append(deltaSeries, TimePoint{Time: ts[i].Time, Value: &deltaValue})
		} else {
			deltaSeries = append(deltaSeries, TimePoint{Time: ts[i].Time, Value: nil})
		}
	}

	return deltaSeries
}

func (ts TimeSeries) Rate() TimeSeries {
	rateSeries := NewTimeSeries()
	var valueDelta float64 = 0
	for i := 1; i < ts.Len(); i++ {
		currentPoint := ts[i]
		previousPoint := ts[i-1]
		timeDelta := currentPoint.Time.Sub(previousPoint.Time)

		// Handle counter reset - use previous value
		if currentPoint.Value != nil && previousPoint.Value != nil && *currentPoint.Value >= *previousPoint.Value {
			valueDelta = (*currentPoint.Value - *previousPoint.Value) / timeDelta.Seconds()
		}

		value := valueDelta
		rateSeries = append(rateSeries, TimePoint{Time: ts[i].Time, Value: &value})
	}

	return rateSeries
}

func (ts TimeSeries) Transform(transformFunc TransformFunc) TimeSeries {
	for i, p := range ts {
		ts[i] = transformFunc(p)
	}
	return ts
}

func Filter(series []*TimeSeriesData, n int, order string, aggFunc AggFunc) []*TimeSeriesData {
	SortBy(series, "asc", aggFunc)

	maxN := int(math.Min(float64(n), float64(len(series))))
	filteredSeries := make([]*TimeSeriesData, maxN)
	for i := 0; i < maxN; i++ {
		if order == "top" {
			filteredSeries[i] = series[len(series)-1-i]
		} else if order == "bottom" {
			filteredSeries[i] = series[i]
		}
	}

	return filteredSeries
}

func AggregateBy(series []*TimeSeriesData, interval time.Duration, aggFunc AggFunc) *TimeSeriesData {
	aggregatedSeries := NewTimeSeries()

	// Combine all points into one time series
	for _, s := range series {
		aggregatedSeries = append(aggregatedSeries, s.TS...)
	}

	// GroupBy works correctly only with sorted time series
	aggregatedSeries.Sort()

	aggregatedSeries = aggregatedSeries.GroupBy(interval, aggFunc)
	aggregatedSeriesData := NewTimeSeriesData()
	aggregatedSeriesData.TS = aggregatedSeries
	return aggregatedSeriesData
}

func AggregateByRange(series []*TimeSeriesData, aggFunc AggFunc) *TimeSeriesData {
	aggregatedSeries := NewTimeSeries()

	// Combine all points into one time series
	for _, s := range series {
		aggregatedSeries = append(aggregatedSeries, s.TS...)
	}

	value := aggFunc(aggregatedSeries)
	aggregatedSeriesData := NewTimeSeriesData()
	if len(aggregatedSeries) > 0 {
		aggregatedSeriesData.TS = []TimePoint{
			{Time: aggregatedSeries[0].Time, Value: value},
			{Time: aggregatedSeries[aggregatedSeries.Len()-1].Time, Value: value},
		}
	}

	return aggregatedSeriesData
}

func (ts TimeSeries) Sort() {
	sorted := sort.SliceIsSorted(ts, ts.less())
	if !sorted {
		sort.Slice(ts, ts.less())
	}
}

// Implements less() function for sorting slice
func (ts TimeSeries) less() func(i, j int) bool {
	return func(i, j int) bool {
		return ts[i].Time.Before(ts[j].Time)
	}
}

func SumSeries(series []*TimeSeriesData) *TimeSeriesData {
	// Build unique set of time stamps from all series
	interpolatedTimeStampsMap := make(map[time.Time]time.Time)
	for _, s := range series {
		for _, p := range s.TS {
			interpolatedTimeStampsMap[p.Time] = p.Time
		}
	}

	// Convert to slice and sort
	interpolatedTimeStamps := make([]time.Time, 0)
	for _, ts := range interpolatedTimeStampsMap {
		interpolatedTimeStamps = append(interpolatedTimeStamps, ts)
	}
	sort.Slice(interpolatedTimeStamps, func(i, j int) bool {
		return interpolatedTimeStamps[i].Before(interpolatedTimeStamps[j])
	})

	interpolatedSeries := make([]TimeSeries, 0)

	for _, s := range series {
		if s.Len() == 0 {
			continue
		}

		pointsToInterpolate := make([]TimePoint, 0)

		currentPointIndex := 0
		for _, its := range interpolatedTimeStamps {
			currentPoint := s.TS[currentPointIndex]
			if its.Equal(currentPoint.Time) {
				if currentPointIndex < s.Len()-1 {
					currentPointIndex++
				}
			} else {
				pointsToInterpolate = append(pointsToInterpolate, TimePoint{Time: its, Value: nil})
			}
		}

		s.TS = append(s.TS, pointsToInterpolate...)
		s.TS.Sort()
		s.TS = interpolateSeries(s.TS)
		interpolatedSeries = append(interpolatedSeries, s.TS)
	}

	sumSeries := NewTimeSeriesData()
	for i := 0; i < len(interpolatedTimeStamps); i++ {
		var sum float64 = 0
		for _, s := range interpolatedSeries {
			if s[i].Value != nil {
				sum += *s[i].Value
			}
		}
		sumSeries.TS = append(sumSeries.TS, TimePoint{Time: interpolatedTimeStamps[i], Value: &sum})
	}

	return sumSeries
}

func interpolateSeries(series TimeSeries) TimeSeries {
	for i := series.Len() - 1; i >= 0; i-- {
		point := series[i]
		if point.Value == nil {
			left := findNearestLeft(series, i)
			right := findNearestRight(series, i)

			if left == nil && right == nil {
				continue
			}
			if left == nil {
				left = right
			}
			if right == nil {
				right = left
			}

			pointValue := linearInterpolation(point.Time, *left, *right)
			point.Value = &pointValue
			series[i] = point
		}
	}
	return series
}

func linearInterpolation(ts time.Time, left, right TimePoint) float64 {
	if left.Time.Equal(right.Time) {
		return (*left.Value + *right.Value) / 2
	} else {
		return *left.Value + (*right.Value-*left.Value)/float64((right.Time.UnixNano()-left.Time.UnixNano()))*float64((ts.UnixNano()-left.Time.UnixNano()))
	}
}

func findNearestRight(series TimeSeries, pointIndex int) *TimePoint {
	for i := pointIndex; i < series.Len(); i++ {
		if series[i].Value != nil {
			return &series[i]
		}
	}
	return nil
}

func findNearestLeft(series TimeSeries, pointIndex int) *TimePoint {
	for i := pointIndex; i > 0; i-- {
		if series[i].Value != nil {
			return &series[i]
		}
	}
	return nil
}

func getTimeFieldIndex(frame *data.Frame) int {
	for i := 0; i < len(frame.Fields); i++ {
		if frame.Fields[i].Type() == data.FieldTypeTime {
			return i
		}
	}

	return -1
}

func getTimestampAt(frame *data.Frame, index int) *time.Time {
	timeFieldIdx := getTimeFieldIndex(frame)
	if timeFieldIdx < 0 {
		return nil
	}

	tsValue := frame.Fields[timeFieldIdx].At(index)
	ts, ok := tsValue.(time.Time)
	if !ok {
		return nil
	}

	return &ts
}

func setTimeAt(frame *data.Frame, frameTs time.Time, index int) {
	for _, field := range frame.Fields {
		if field.Type() == data.FieldTypeTime {
			field.Insert(index, frameTs)
		}
	}
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor:
// - Julien Vehent jvehent@mozilla.com [:ulfr]
package main

import (
	"fmt"
	"io/ioutil"
	"mig"
	"time"

	"code.google.com/p/gcfg"
)

type config struct {
	Agent struct {
		IsImmortal     bool
		InstallService bool
		Relay          string
		Socket         string
		HeartbeatFreq  string
		ModuleTimeout  string
	}
	Certs struct {
		Ca, Cert, Key string
	}
	Logging mig.Logging
}

// configLoad reads a local configuration file and overwrite the global conf
// variable with the parameters from the file
func configLoad(path string) (err error) {
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("configLoad() -> %v", e)
		}
	}()
	var config config
	err = gcfg.ReadFileInto(&config, path)
	if err != nil {
		panic(err)
	}
	ISIMMORTAL = config.Agent.IsImmortal
	MUSTINSTALLSERVICE = config.Agent.InstallService
	LOGGINGCONF = config.Logging
	AMQPBROKER = config.Agent.Relay
	HEARTBEATFREQ, err = time.ParseDuration(config.Agent.HeartbeatFreq)
	if err != nil {
		panic(err)
	}
	MODULETIMEOUT, err = time.ParseDuration(config.Agent.ModuleTimeout)
	if err != nil {
		panic(err)
	}
	CACERT, err = ioutil.ReadFile(config.Certs.Ca)
	if err != nil {
		panic(err)
	}
	AGENTCERT, err = ioutil.ReadFile(config.Certs.Cert)
	if err != nil {
		panic(err)
	}
	AGENTKEY, err = ioutil.ReadFile(config.Certs.Key)
	if err != nil {
		panic(err)
	}
	return
}
<commit_msg>[minor] add conf file option to discover public ip<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor:
// - Julien Vehent jvehent@mozilla.com [:ulfr]
package main

import (
	"fmt"
	"io/ioutil"
	"mig"
	"time"

	"code.google.com/p/gcfg"
)

type config struct {
	Agent struct {
		IsImmortal       bool
		InstallService   bool
		DiscoverPublicIP bool
		Relay            string
		Socket           string
		HeartbeatFreq    string
		ModuleTimeout    string
	}
	Certs struct {
		Ca, Cert, Key string
	}
	Logging mig.Logging
}

// configLoad reads a local configuration file and overwrite the global conf
// variable with the parameters from the file
func configLoad(path string) (err error) {
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("configLoad() -> %v", e)
		}
	}()
	var config config
	err = gcfg.ReadFileInto(&config, path)
	if err != nil {
		panic(err)
	}
	ISIMMORTAL = config.Agent.IsImmortal
	MUSTINSTALLSERVICE = config.Agent.InstallService
	DISCOVERPUBLICIP = config.Agent.DiscoverPublicIP
	LOGGINGCONF = config.Logging
	AMQPBROKER = config.Agent.Relay
	HEARTBEATFREQ, err = time.ParseDuration(config.Agent.HeartbeatFreq)
	if err != nil {
		panic(err)
	}
	MODULETIMEOUT, err = time.ParseDuration(config.Agent.ModuleTimeout)
	if err != nil {
		panic(err)
	}
	CACERT, err = ioutil.ReadFile(config.Certs.Ca)
	if err != nil {
		panic(err)
	}
	AGENTCERT, err = ioutil.ReadFile(config.Certs.Cert)
	if err != nil {
		panic(err)
	}
	AGENTKEY, err = ioutil.ReadFile(config.Certs.Key)
	if err != nil {
		panic(err)
	}
	return
}
<|endoftext|>


<commit_before>package services

import (
	"fmt"
	"io/ioutil"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
	"time"

	"jbrodriguez/unbalance/server/src/common"
	"jbrodriguez/unbalance/server/src/domain"
	"jbrodriguez/unbalance/server/src/dto"
	"jbrodriguez/unbalance/server/src/lib"

	"github.com/jbrodriguez/actor"
	"github.com/jbrodriguez/mlog"
	"github.com/jbrodriguez/pubsub"
	ini "github.com/vaughan0/go-ini"
)

const certDir = "/boot/config/ssl/certs"

// Array -
type Array struct {
	bus      *pubsub.PubSub
	settings *lib.Settings
	actor    *actor.Actor
}

// NewArray -
func NewArray(bus *pubsub.PubSub, settings *lib.Settings) *Array {
	array := &Array{
		bus:      bus,
		settings: settings,
		actor:    actor.NewActor(bus),
	}

	return array
}

// Start -
func (a *Array) Start() (err error) {
	mlog.Info("starting service Array ...")

	err = a.SanityCheck(a.settings.APIFolders)
	if err != nil {
		return err
	}

	a.actor.Register(common.IntGetArrayStatus, a.getStatus)
	a.actor.Register(common.APIGetTree, a.getTree)
	a.actor.Register(common.APIGetLog, a.getLog)

	go a.actor.React()

	return nil
}

// Stop -
func (a *Array) Stop() {
	mlog.Info("stopped service Array ...")
}

// SanityCheck -
func (a *Array) SanityCheck(locations []string) error {
	location := lib.SearchFile("var.ini", locations)
	if location == "" {
		return fmt.Errorf("Unable to find var.ini (%s)", strings.Join(locations, ", "))
	}

	location = lib.SearchFile("disks.ini", locations)
	if location == "" {
		return fmt.Errorf("Unable to find var.ini (%s)", strings.Join(locations, ", "))
	}

	return nil
}

// GetCertificate -
func (a *Array) GetCertificate() string {
	// get array status
	file, err := ini.LoadFile("/var/local/emhttp/var.ini")
	if err != nil {
		return ""
	}

	usessl, _ := file.Get("", "USE_SSL")
	usessl = strings.Replace(usessl, "\"", "", -1)

	name, _ := file.Get("", "NAME")
	name = strings.Replace(name, "\"", "", -1)

	cert := getCertificateName(certDir, name)

	secure := cert != "" && !(usessl == "" || usessl == "no")

	if secure {
		return cert
	}

	return ""
}

func (a *Array) getStatus(msg *pubsub.Message) {
	unraid, err := getArrayData()
	if err != nil {
		msg.Reply <- dto.Message{Data: nil, Error: err}
	}

	msg.Reply <- dto.Message{Data: unraid, Error: nil}
}

func getArrayData() (*domain.Unraid, error) {
	unraid := &domain.Unraid{}

	// get array status
	file, err := ini.LoadFile("/var/local/emhttp/var.ini")
	if err != nil {
		return nil, err
	}

	tmp, _ := file.Get("", "mdNumDisks")
	numDisks := strings.Replace(tmp, "\"", "", -1)
	unraid.NumDisks, _ = strconv.ParseInt(numDisks, 10, 64)

	tmp, _ = file.Get("", "mdNumProtected")
	numProtected := strings.Replace(tmp, "\"", "", -1)
	unraid.NumProtected, _ = strconv.ParseInt(numProtected, 10, 64)

	tmp, _ = file.Get("", "sbSynced")
	synced := strings.Replace(tmp, "\"", "", -1)
	ut, _ := strconv.ParseInt(synced, 10, 64)
	unraid.Synced = time.Unix(ut, 0)

	tmp, _ = file.Get("", "sbSyncErrs")
	syncErrs := strings.Replace(tmp, "\"", "", -1)
	unraid.SyncErrs, _ = strconv.ParseInt(syncErrs, 10, 64)

	tmp, _ = file.Get("", "mdResync")
	resync := strings.Replace(tmp, "\"", "", -1)
	unraid.Resync, _ = strconv.ParseInt(resync, 10, 64)

	tmp, _ = file.Get("", "mdResyncPos")
	resyncPos := strings.Replace(tmp, "\"", "", -1)
	unraid.ResyncPos, _ = strconv.ParseInt(resyncPos, 10, 64)

	tmp, _ = file.Get("", "mdState")
	unraid.State = strings.Replace(tmp, "\"", "", -1)

	// get disks
	file, err = ini.LoadFile("/var/local/emhttp/disks.ini")
	if err != nil {
		return nil, err
	}

	// get free/size data
	free := make(map[string]int64)
	size := make(map[string]int64)

	err = lib.Shell("df --block-size=1 /mnt/*", mlog.Warning, "Refresh error:", "", func(line string) {
		data := strings.Fields(line)
		size[data[5]], _ = strconv.ParseInt(data[1], 10, 64)
		free[data[5]], _ = strconv.ParseInt(data[3], 0, 64)
	})

	if err != nil {
		return nil, err
	}

	var totalSize, totalFree int64
	disks := make([]*domain.Disk, 0)

	for _, section := range file {
		diskType := strings.Replace(section["type"], "\"", "", -1)
		diskName := strings.Replace(section["name"], "\"", "", -1)
		diskStatus := strings.Replace(section["status"], "\"", "", -1)

		if diskType == "Parity" || diskType == "Flash" || (diskType == "Cache" && len(diskName) > 5 || diskStatus == "DISK_NP") {
			continue
		}

		disk := &domain.Disk{}

		disk.ID, _ = strconv.ParseInt(strings.Replace(section["idx"], "\"", "", -1), 10, 64) // 1
		disk.Name = diskName                                                                 // disk1, cache
		disk.Path = "/mnt/" + disk.Name                                                      // /mnt/disk1, /mnt/cache
		disk.Device = strings.Replace(section["device"], "\"", "", -1)                       // sdp
		disk.Type = diskType                                                                 // Flash, Parity, Data, Cache
		disk.FsType = strings.Replace(section["fsType"], "\"", "", -1)                       // xfs, reiserfs, btrfs
		disk.Free = free[disk.Path]
		disk.Size = size[disk.Path]
		disk.Serial = strings.Replace(section["id"], "\"", "", -1) // WDC_WD30EZRX-00DC0B0_WD-WMC9T204468
		disk.Status = diskStatus                                   // DISK_OK

		totalSize += disk.Size
		totalFree += disk.Free

		disks = append(disks, disk)
	}

	unraid.Size = totalSize
	unraid.Free = totalFree

	sort.Slice(disks, func(i, j int) bool { return disks[i].ID < disks[j].ID })

	unraid.Disks = disks

	return unraid, nil
}

// GetTree -
func (a *Array) getTree(msg *pubsub.Message) {
	path := msg.Payload.(string)

	entry := &dto.Entry{Path: path}

	items := make([]dto.Node, 0)

	elements, _ := ioutil.ReadDir(path)
	for _, element := range elements {
		var node dto.Node

		// default values
		node.Label = element.Name()
		node.Collapsed = true
		node.Checkbox = true
		node.Path = filepath.Join(path, element.Name())

		if element.IsDir() {
			// let's check if the folder is empty
			// we can still get an i/o error, if that's the case
			// we assume the folder's empty
			// otherwise we act accordingly
			folder := filepath.Join(path, element.Name())
			empty, err := lib.IsEmpty(folder)
			if err != nil {
				mlog.Warning("GetTree - Unable to determine if folder is empty: %s", folder)
				node.Children = nil
			} else {
				if empty {
					node.Children = nil
				} else {
					node.Children = []dto.Node{dto.Node{Label: "Loading ...", Collapsed: true, Checkbox: false, Children: nil}}
				}
			}
		} else {
			node.Children = nil
		}

		items = append(items, node)
	}

	entry.Nodes = items

	msg.Reply <- entry
}

func (a *Array) getLog(msg *pubsub.Message) {
	cmd := "tail -n 100 /boot/logs/unbalance.log"

	log := make([]string, 0)

	err := lib.Shell(cmd, mlog.Warning, "Get Log error:", "", func(line string) {
		log = append(log, line)
	})

	if err != nil {
		mlog.Warning("Unable to get log: %s", err)
	}

	outbound := &dto.Packet{Topic: "gotLog", Payload: log}
	a.bus.Pub(&pubsub.Message{Payload: outbound}, "socket:broadcast")
}

func getCertificateName(certDir, name string) string {
	cert := filepath.Join(certDir, "certificate_bundle.pem")

	exists, err := lib.Exists(cert)
	if err != nil {
		mlog.Warning("unable to check for %s presence:(%s)", cert, err)
		return ""
	}

	if exists {
		return cert
	}

	cert = filepath.Join(certDir, name+"_unraid_bundle.pem")

	exists, err = lib.Exists(filepath.Join(certDir, cert))
	if err != nil {
		mlog.Warning("unable to check for %s presence:(%s)", cert, err)
		return ""
	}

	if exists {
		return cert
	}

	return ""
}
<commit_msg>Closes #129<commit_after>package services

import (
	"fmt"
	"io/ioutil"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
	"time"

	"jbrodriguez/unbalance/server/src/common"
	"jbrodriguez/unbalance/server/src/domain"
	"jbrodriguez/unbalance/server/src/dto"
	"jbrodriguez/unbalance/server/src/lib"

	"github.com/jbrodriguez/actor"
	"github.com/jbrodriguez/mlog"
	"github.com/jbrodriguez/pubsub"
	ini "github.com/vaughan0/go-ini"
)

const certDir = "/boot/config/ssl/certs"

// Array -
type Array struct {
	bus      *pubsub.PubSub
	settings *lib.Settings
	actor    *actor.Actor
}

// NewArray -
func NewArray(bus *pubsub.PubSub, settings *lib.Settings) *Array {
	array := &Array{
		bus:      bus,
		settings: settings,
		actor:    actor.NewActor(bus),
	}

	return array
}

// Start -
func (a *Array) Start() (err error) {
	mlog.Info("starting service Array ...")

	err = a.SanityCheck(a.settings.APIFolders)
	if err != nil {
		return err
	}

	a.actor.Register(common.IntGetArrayStatus, a.getStatus)
	a.actor.Register(common.APIGetTree, a.getTree)
	a.actor.Register(common.APIGetLog, a.getLog)

	go a.actor.React()

	return nil
}

// Stop -
func (a *Array) Stop() {
	mlog.Info("stopped service Array ...")
}

// SanityCheck -
func (a *Array) SanityCheck(locations []string) error {
	location := lib.SearchFile("var.ini", locations)
	if location == "" {
		return fmt.Errorf("Unable to find var.ini (%s)", strings.Join(locations, ", "))
	}

	location = lib.SearchFile("disks.ini", locations)
	if location == "" {
		return fmt.Errorf("Unable to find var.ini (%s)", strings.Join(locations, ", "))
	}

	return nil
}

// GetCertificate -
func (a *Array) GetCertificate() string {
	// get ssl settings
	ident, err := ini.LoadFile("/boot/config/ident.cfg")
	if err != nil {
		return ""
	}

	usessl, _ := ident.Get("", "USE_SSL")
	usessl = strings.Replace(usessl, "\"", "", -1)

	// get array status
	file, err := ini.LoadFile("/var/local/emhttp/var.ini")
	if err != nil {
		return ""
	}

	name, _ := file.Get("", "NAME")
	name = strings.Replace(name, "\"", "", -1)

	cert := getCertificateName(certDir, name)

	secure := cert != "" && !(usessl == "" || usessl == "no")

	if secure {
		return cert
	}

	return ""
}

func (a *Array) getStatus(msg *pubsub.Message) {
	unraid, err := getArrayData()
	if err != nil {
		msg.Reply <- dto.Message{Data: nil, Error: err}
	}

	msg.Reply <- dto.Message{Data: unraid, Error: nil}
}

func getArrayData() (*domain.Unraid, error) {
	unraid := &domain.Unraid{}

	// get array status
	file, err := ini.LoadFile("/var/local/emhttp/var.ini")
	if err != nil {
		return nil, err
	}

	tmp, _ := file.Get("", "mdNumDisks")
	numDisks := strings.Replace(tmp, "\"", "", -1)
	unraid.NumDisks, _ = strconv.ParseInt(numDisks, 10, 64)

	tmp, _ = file.Get("", "mdNumProtected")
	numProtected := strings.Replace(tmp, "\"", "", -1)
	unraid.NumProtected, _ = strconv.ParseInt(numProtected, 10, 64)

	tmp, _ = file.Get("", "sbSynced")
	synced := strings.Replace(tmp, "\"", "", -1)
	ut, _ := strconv.ParseInt(synced, 10, 64)
	unraid.Synced = time.Unix(ut, 0)

	tmp, _ = file.Get("", "sbSyncErrs")
	syncErrs := strings.Replace(tmp, "\"", "", -1)
	unraid.SyncErrs, _ = strconv.ParseInt(syncErrs, 10, 64)

	tmp, _ = file.Get("", "mdResync")
	resync := strings.Replace(tmp, "\"", "", -1)
	unraid.Resync, _ = strconv.ParseInt(resync, 10, 64)

	tmp, _ = file.Get("", "mdResyncPos")
	resyncPos := strings.Replace(tmp, "\"", "", -1)
	unraid.ResyncPos, _ = strconv.ParseInt(resyncPos, 10, 64)

	tmp, _ = file.Get("", "mdState")
	unraid.State = strings.Replace(tmp, "\"", "", -1)

	// get disks
	file, err = ini.LoadFile("/var/local/emhttp/disks.ini")
	if err != nil {
		return nil, err
	}

	// get free/size data
	free := make(map[string]int64)
	size := make(map[string]int64)

	err = lib.Shell("df --block-size=1 /mnt/*", mlog.Warning, "Refresh error:", "", func(line string) {
		data := strings.Fields(line)
		size[data[5]], _ = strconv.ParseInt(data[1], 10, 64)
		free[data[5]], _ = strconv.ParseInt(data[3], 0, 64)
	})

	if err != nil {
		return nil, err
	}

	var totalSize, totalFree int64
	disks := make([]*domain.Disk, 0)

	for _, section := range file {
		diskType := strings.Replace(section["type"], "\"", "", -1)
		diskName := strings.Replace(section["name"], "\"", "", -1)
		diskStatus := strings.Replace(section["status"], "\"", "", -1)

		if diskType == "Parity" || diskType == "Flash" || (diskType == "Cache" && len(diskName) > 5 || diskStatus == "DISK_NP") {
			continue
		}

		disk := &domain.Disk{}

		disk.ID, _ = strconv.ParseInt(strings.Replace(section["idx"], "\"", "", -1), 10, 64) // 1
		disk.Name = diskName                                                                 // disk1, cache
		disk.Path = "/mnt/" + disk.Name                                                      // /mnt/disk1, /mnt/cache
		disk.Device = strings.Replace(section["device"], "\"", "", -1)                       // sdp
		disk.Type = diskType                                                                 // Flash, Parity, Data, Cache
		disk.FsType = strings.Replace(section["fsType"], "\"", "", -1)                       // xfs, reiserfs, btrfs
		disk.Free = free[disk.Path]
		disk.Size = size[disk.Path]
		disk.Serial = strings.Replace(section["id"], "\"", "", -1) // WDC_WD30EZRX-00DC0B0_WD-WMC9T204468
		disk.Status = diskStatus                                   // DISK_OK

		totalSize += disk.Size
		totalFree += disk.Free

		disks = append(disks, disk)
	}

	unraid.Size = totalSize
	unraid.Free = totalFree

	sort.Slice(disks, func(i, j int) bool { return disks[i].ID < disks[j].ID })

	unraid.Disks = disks

	return unraid, nil
}

// GetTree -
func (a *Array) getTree(msg *pubsub.Message) {
	path := msg.Payload.(string)

	entry := &dto.Entry{Path: path}

	items := make([]dto.Node, 0)

	elements, _ := ioutil.ReadDir(path)
	for _, element := range elements {
		var node dto.Node

		// default values
		node.Label = element.Name()
		node.Collapsed = true
		node.Checkbox = true
		node.Path = filepath.Join(path, element.Name())

		if element.IsDir() {
			// let's check if the folder is empty
			// we can still get an i/o error, if that's the case
			// we assume the folder's empty
			// otherwise we act accordingly
			folder := filepath.Join(path, element.Name())
			empty, err := lib.IsEmpty(folder)
			if err != nil {
				mlog.Warning("GetTree - Unable to determine if folder is empty: %s", folder)
				node.Children = nil
			} else {
				if empty {
					node.Children = nil
				} else {
					node.Children = []dto.Node{dto.Node{Label: "Loading ...", Collapsed: true, Checkbox: false, Children: nil}}
				}
			}
		} else {
			node.Children = nil
		}

		items = append(items, node)
	}

	entry.Nodes = items

	msg.Reply <- entry
}

func (a *Array) getLog(msg *pubsub.Message) {
	cmd := "tail -n 100 /boot/logs/unbalance.log"

	log := make([]string, 0)

	err := lib.Shell(cmd, mlog.Warning, "Get Log error:", "", func(line string) {
		log = append(log, line)
	})

	if err != nil {
		mlog.Warning("Unable to get log: %s", err)
	}

	outbound := &dto.Packet{Topic: "gotLog", Payload: log}
	a.bus.Pub(&pubsub.Message{Payload: outbound}, "socket:broadcast")
}

func getCertificateName(certDir, name string) string {
	cert := filepath.Join(certDir, "certificate_bundle.pem")

	exists, err := lib.Exists(cert)
	if err != nil {
		mlog.Warning("unable to check for %s presence:(%s)", cert, err)
		return ""
	}

	if exists {
		return cert
	}

	cert = filepath.Join(certDir, name+"_unraid_bundle.pem")

	exists, err = lib.Exists(filepath.Join(certDir, cert))
	if err != nil {
		mlog.Warning("unable to check for %s presence:(%s)", cert, err)
		return ""
	}

	if exists {
		return cert
	}

	return ""
}
<|endoftext|>


<commit_before>package sources

import (
	"time"
	"fmt"
	"encoding/json"

	"github.com/OWASP/Amass/services"
	"github.com/OWASP/Amass/config"
	eb "github.com/OWASP/Amass/eventbus"
	"github.com/OWASP/Amass/resolvers"
	"github.com/OWASP/Amass/requests"
	"github.com/OWASP/Amass/net/http"
)

type DataItem struct {
    Id   string          `json:"id"`
    Tags string          `json:"tags"`
    Time string 	     `json:"time"`
}

// Extract the response from the Web response given by pastebin
type Data struct {
	Search   	string       `json:"search"`
	Count   int          `json:"count"`
	Data   	[]DataItem   `json:"data"`
}

// Pastebin is the Service that handles access to the CertSpotter data source.
type Pastebin struct {
	services.BaseService

	SourceType string
	RateLimit  time.Duration
}

// NewPastebin returns he object initialized, but not yet started.
func NewPastebin(cfg *config.Config, bus *eb.EventBus, pool *resolvers.ResolverPool) *Pastebin {
	p := &Pastebin{
		SourceType: requests.API,
		RateLimit:  3 * time.Second,
	}

	p.BaseService = *services.NewBaseService(p, "Pastebin", cfg, bus, pool)
	return p
}

// OnStart implements the Service interface
func (p *Pastebin) OnStart() error {
	p.BaseService.OnStart()

	go p.processRequests()
	return nil
}

func (p *Pastebin) processRequests() {
	last := time.Now()

	for {
		select {
		case <-p.Quit():
			return
		case req := <-p.DNSRequestChan():
			if p.Config().IsDomainInScope(req.Domain) {
				if time.Now().Sub(last) < p.RateLimit {
					time.Sleep(p.RateLimit)
				}
				last = time.Now()
				p.executeQuery(req.Domain)
				last = time.Now()
			}
		case <-p.AddrRequestChan():
		case <-p.ASNRequestChan():
		case <-p.WhoisRequestChan():
		}
	}
}

func (p *Pastebin) executeQuery(domain string) {
	var url, page string
	var err error
	re := p.Config().DomainRegex(domain)

	ids, err := p.extractIDs(domain,url)
	if err != nil {
		p.Bus().Publish(requests.LogTopic, fmt.Sprintf("%s: %s: %v", p.String(), url, err))
		return
	}

	for _, id := range ids {
		url = p.webURLDumpData(id)
		page, err = http.RequestWebPage(url, nil, nil, "", "")
		if err != nil {
			p.Bus().Publish(requests.LogTopic, fmt.Sprintf("%s: %s: %v", p.String(), url, err))
			return
		}
		for _, name := range re.FindAllString(page, -1) {
			p.Bus().Publish(requests.NewNameTopic, &requests.DNSRequest{
				Name:   name,
				Domain: domain,
				Tag:    p.SourceType,
				Source: p.String(),
			})
		}
	}
}

// Extract the IDs from the pastebin Web response
func (p *Pastebin) extractIDs(domain string, url string) ([]string,error) {
	var page string
	var data Data
	var err error
	var ids []string

	url = p.webURLDumpIDs(domain)
	page, err = http.RequestWebPage(url, nil, nil, "", "")
	if err != nil {
		p.Bus().Publish(requests.LogTopic, fmt.Sprintf("%s: %s: %v", p.String(), url, err))
		return nil, err
	}

	in := []byte(page)

	err = json.Unmarshal(in, &data)
    if err != nil {
        panic(err)
	}

	for _, item := range data.Data {
		ids = append(ids,item.Id)
	}

	return ids, nil
}

// Returns the Web URL to fetch all dump ids for a given doamin
func (p *Pastebin) webURLDumpIDs(domain string) string {
	return fmt.Sprintf("https://psbdmp.ws/api/search/%s", domain)
}

// Returns the Web URL to get all dumps for a given doamin
func (p *Pastebin) webURLDumpData(id string) string {
	return fmt.Sprintf("https://psbdmp.ws/api/dump/get/%s",id)
}<commit_msg>Refactoring: Refactored extractIDs<commit_after>package sources

import (
	"time"
	"fmt"
	"encoding/json"

	"github.com/OWASP/Amass/services"
	"github.com/OWASP/Amass/config"
	eb "github.com/OWASP/Amass/eventbus"
	"github.com/OWASP/Amass/resolvers"
	"github.com/OWASP/Amass/requests"
	"github.com/OWASP/Amass/net/http"
)

type DataItem struct {
    Id   string          `json:"id"`
    Tags string          `json:"tags"`
    Time string 	     `json:"time"`
}

// Extract the response from the Web response given by pastebin
type Data struct {
	Search   	string       `json:"search"`
	Count   int          `json:"count"`
	Data   	[]DataItem   `json:"data"`
}

// Pastebin is the Service that handles access to the CertSpotter data source.
type Pastebin struct {
	services.BaseService

	SourceType string
	RateLimit  time.Duration
}

// NewPastebin returns he object initialized, but not yet started.
func NewPastebin(cfg *config.Config, bus *eb.EventBus, pool *resolvers.ResolverPool) *Pastebin {
	p := &Pastebin{
		SourceType: requests.API,
		RateLimit:  3 * time.Second,
	}

	p.BaseService = *services.NewBaseService(p, "Pastebin", cfg, bus, pool)
	return p
}

// OnStart implements the Service interface
func (p *Pastebin) OnStart() error {
	p.BaseService.OnStart()

	go p.processRequests()
	return nil
}

func (p *Pastebin) processRequests() {
	last := time.Now()

	for {
		select {
		case <-p.Quit():
			return
		case req := <-p.DNSRequestChan():
			if p.Config().IsDomainInScope(req.Domain) {
				if time.Now().Sub(last) < p.RateLimit {
					time.Sleep(p.RateLimit)
				}
				last = time.Now()
				p.executeQuery(req.Domain)
				last = time.Now()
			}
		case <-p.AddrRequestChan():
		case <-p.ASNRequestChan():
		case <-p.WhoisRequestChan():
		}
	}
}

func (p *Pastebin) executeQuery(domain string) {
	var url, page string
	var err error
	re := p.Config().DomainRegex(domain)

	ids, err := p.extractIDs(domain,url)
	if err != nil {
		p.Bus().Publish(requests.LogTopic, fmt.Sprintf("%s: %s: %v", p.String(), url, err))
		return
	}

	for _, id := range ids {
		url = p.webURLDumpData(id)
		page, err = http.RequestWebPage(url, nil, nil, "", "")
		if err != nil {
			p.Bus().Publish(requests.LogTopic, fmt.Sprintf("%s: %s: %v", p.String(), url, err))
			return
		}
		for _, name := range re.FindAllString(page, -1) {
			p.Bus().Publish(requests.NewNameTopic, &requests.DNSRequest{
				Name:   name,
				Domain: domain,
				Tag:    p.SourceType,
				Source: p.String(),
			})
		}
	}
}

// Extract the IDs from the pastebin Web response
func (p *Pastebin) extractIDs(domain string, url string) ([]string,error) {
	var page string
	var data Data
	var err error
	var ids []string

	url = p.webURLDumpIDs(domain)
	page, err = http.RequestWebPage(url, nil, nil, "", "")
	if err != nil {
		p.Bus().Publish(requests.LogTopic, fmt.Sprintf("%s: %s: %v", p.String(), url, err))
		return nil, err
	}

	err = json.Unmarshal([]byte(page), &data)
    if err != nil {
        panic(err)
	}

	for _, item := range data.Data {
		ids = append(ids,item.Id)
	}

	return ids, nil
}

// Returns the Web URL to fetch all dump ids for a given doamin
func (p *Pastebin) webURLDumpIDs(domain string) string {
	return fmt.Sprintf("https://psbdmp.ws/api/search/%s", domain)
}

// Returns the Web URL to get all dumps for a given doamin
func (p *Pastebin) webURLDumpData(id string) string {
	return fmt.Sprintf("https://psbdmp.ws/api/dump/get/%s",id)
}<|endoftext|>


<commit_before>package consumption

import (
	"github.com/timeredbull/tsuru/api/auth"
	"github.com/timeredbull/tsuru/api/service"
	"github.com/timeredbull/tsuru/db"
	"github.com/timeredbull/tsuru/errors"
	"labix.org/v2/mgo/bson"
	"net/http"
)

func GetServiceOr404(name string) (service.Service, error) {
	s := service.Service{Name: name}
	err := s.Get()
	if err != nil {
		return s, &errors.Http{Code: http.StatusNotFound, Message: "Service not found"}
	}
	return s, nil
}

func GetServiceOrError(name string, u *auth.User) (service.Service, error) {
	s, err := GetServiceOr404(name)
	if err != nil {
		return s, err
	}
	if !s.IsRestricted {
		return s, nil
	}
	if !auth.CheckUserAccess(s.Teams, u) {
		msg := "This user does not have access to this service"
		return s, &errors.Http{Code: http.StatusForbidden, Message: msg}
	}
	return s, err
}

func GetServiceInstanceOr404(name string) (service.ServiceInstance, error) {
	var si service.ServiceInstance
	err := db.Session.ServiceInstances().Find(bson.M{"_id": name}).One(&si)
	if err != nil {
		return si, &errors.Http{Code: http.StatusNotFound, Message: "Service instance not found"}
	}
	return si, nil
}

func GetServiceInstanceOrError(name string, u *auth.User) (service.ServiceInstance, error) {
	si, err := GetServiceInstanceOr404(name)
	if err != nil {
		return si, err
	}
	if !auth.CheckUserAccess(si.Teams, u) {
		msg := "This user does not have access to this service instance"
		return si, &errors.Http{Code: http.StatusForbidden, Message: msg}
	}
	return si, nil
}

func ServiceAndServiceInstancesByTeams(u *auth.User) []service.ServiceModel {
	services, _ := service.GetServicesByTeamKind("teams", u)
	sInstances, _ := service.GetServiceInstancesByServicesAndTeams(services, u)
	results := make([]service.ServiceModel, len(services))
	for i, s := range services {
		results[i].Service = s.Name
		for _, si := range sInstances {
			if si.ServiceName == s.Name {
				results[i].Instances = append(results[i].Instances, si.Name)
			}
		}
	}
	return results
}
<commit_msg>service.consumption: refactoring method name.<commit_after>package consumption

import (
	"github.com/timeredbull/tsuru/api/auth"
	"github.com/timeredbull/tsuru/api/service"
	"github.com/timeredbull/tsuru/db"
	"github.com/timeredbull/tsuru/errors"
	"labix.org/v2/mgo/bson"
	"net/http"
)

func GetServiceOr404(name string) (service.Service, error) {
	s := service.Service{Name: name}
	err := s.Get()
	if err != nil {
		return s, &errors.Http{Code: http.StatusNotFound, Message: "Service not found"}
	}
	return s, nil
}

func GetServiceOrError(name string, u *auth.User) (service.Service, error) {
	s, err := GetServiceOr404(name)
	if err != nil {
		return s, err
	}
	if !s.IsRestricted {
		return s, nil
	}
	if !auth.CheckUserAccess(s.Teams, u) {
		msg := "This user does not have access to this service"
		return s, &errors.Http{Code: http.StatusForbidden, Message: msg}
	}
	return s, err
}

func GetServiceInstanceOr404(name string) (service.ServiceInstance, error) {
	var si service.ServiceInstance
	err := db.Session.ServiceInstances().Find(bson.M{"_id": name}).One(&si)
	if err != nil {
		return si, &errors.Http{Code: http.StatusNotFound, Message: "Service instance not found"}
	}
	return si, nil
}

func GetServiceInstanceOrError(name string, u *auth.User) (service.ServiceInstance, error) {
	si, err := GetServiceInstanceOr404(name)
	if err != nil {
		return si, err
	}
	if !auth.CheckUserAccess(si.Teams, u) {
		msg := "This user does not have access to this service instance"
		return si, &errors.Http{Code: http.StatusForbidden, Message: msg}
	}
	return si, nil
}

func ServiceAndServiceInstancesByTeams(u *auth.User) []service.ServiceModel {
	services, _ := service.GetServicesByTeamKindAndNoRestriction("teams", u)
	sInstances, _ := service.GetServiceInstancesByServicesAndTeams(services, u)
	results := make([]service.ServiceModel, len(services))
	for i, s := range services {
		results[i].Service = s.Name
		for _, si := range sInstances {
			if si.ServiceName == s.Name {
				results[i].Instances = append(results[i].Instances, si.Name)
			}
		}
	}
	return results
}
<|endoftext|>


<commit_before>// Copyright (c) 2017, Janoš Guljaš <janos@resenje.org>
// All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package servers

import (
	"context"
	"fmt"
	"log"
	"net"
	"sync"
)

// Logger defines methods required for logging.
type Logger interface {
	Infof(format string, a ...interface{})
	Errorf(format string, a ...interface{})
}

// stdLogger is a simple implementation of Logger interface
// that uses log package for logging messages.
type stdLogger struct{}

func (l stdLogger) Infof(format string, a ...interface{}) {
	log.Printf("INFO "+format, a...)
}

func (l stdLogger) Errorf(format string, a ...interface{}) {
	log.Printf("ERROR "+format, a...)
}

// Option is a function that sets optional parameters for Servers.
type Option func(*Servers)

// WithLogger sets the Logger instance for logging messages.
func WithLogger(logger Logger) Option { return func(o *Servers) { o.logger = logger } }

// WithRecoverFunc sets a function that will be used to recover
// from panic inside a goroutune that servers are serving requests.
func WithRecoverFunc(recover func()) Option { return func(o *Servers) { o.recover = recover } }

// Servers holds a list of servers and their options.
// It provides a simple way to construct server group with Add method,
// to start them with Serve method, and stop them with Close or Shutdown methods.
type Servers struct {
	servers []*server
	mu      sync.Mutex
	logger  Logger
	recover func()
}

// New creates a new instance of Servers with applied options.
func New(opts ...Option) (s *Servers) {
	s = &Servers{
		logger:  stdLogger{},
		recover: func() {},
	}
	for _, opt := range opts {
		opt(s)
	}
	return
}

// Server defines required methods for a type that can be added to
// the Servers.
// In addition to this methods, a Server should implement TCPServer
// or UDPServer to be able to serve requests.
type Server interface {
	// Close should stop server from serving all existing requests
	// and stop accepting new ones.
	// The listener provided in Serve method must stop listening.
	Close() error
	// Shutdown should gracefully stop server. All existing requests
	// should be processed within a deadline provided by the context.
	// No new requests should be accepted.
	// The listener provided in Serve method must stop listening.
	Shutdown(ctx context.Context) error
}

// TCPServer defines methods for a server that accepts requests
// over TCP listener.
type TCPServer interface {
	// Serve should start server responding to requests.
	// The listener is initialized and already listening.
	ServeTCP(ln net.Listener) error
}

// UDPServer defines methods for a server that accepts requests
// over UDP listener.
type UDPServer interface {
	ServeUDP(conn *net.UDPConn) error
}

type server struct {
	Server
	name    string
	address string
	tcpAddr *net.TCPAddr
	udpAddr *net.UDPAddr
}

func (s *server) label() string {
	if s.name == "" {
		return "server"
	}
	return s.name + " server"
}

func (s *server) isTCP() (srv TCPServer, yes bool) {
	srv, yes = s.Server.(TCPServer)
	return
}

func (s *server) isUDP() (srv UDPServer, yes bool) {
	srv, yes = s.Server.(UDPServer)
	return
}

// Add adds a new server instance by a custom name and with
// address to listen to.
func (s *Servers) Add(name, address string, srv Server) {
	s.mu.Lock()
	s.servers = append(s.servers, &server{
		Server:  srv,
		name:    name,
		address: address,
	})
	s.mu.Unlock()
}

// Serve starts all added servers.
// New new servers must be added after this methid is called.
func (s *Servers) Serve() (err error) {
	lns := make([]net.Listener, len(s.servers))
	conns := make([]*net.UDPConn, len(s.servers))
	for i, srv := range s.servers {
		if _, yes := srv.isTCP(); yes {
			ln, err := net.Listen("tcp", srv.address)
			if err != nil {
				for _, l := range lns {
					if l == nil {
						continue
					}
					if err := l.Close(); err != nil {
						s.logger.Errorf("%s tcp listener %q close: %v", srv.label(), srv.address, err)
					}
				}
				return fmt.Errorf("%s tcp listener %q: %v", srv.label(), srv.address, err)
			}
			lns[i] = ln
		}
		if _, yes := srv.isUDP(); yes {
			addr, err := net.ResolveUDPAddr("udp", srv.address)
			if err != nil {
				return fmt.Errorf("%s resolve udp address %q: %v", srv.label(), srv.address, err)
			}
			conn, err := net.ListenUDP("udp", addr)
			if err != nil {
				return fmt.Errorf("%s udp listener %q: %v", srv.label(), srv.address, err)
			}
			conns[i] = conn
		}
	}
	for i, srv := range s.servers {
		if tcpSrv, yes := srv.isTCP(); yes {
			go func(srv *server, ln net.Listener) {
				defer s.recover()

				s.mu.Lock()
				srv.tcpAddr = ln.Addr().(*net.TCPAddr)
				s.mu.Unlock()

				s.logger.Infof("%s listening on %q", srv.label(), srv.tcpAddr.String())
				if err := tcpSrv.ServeTCP(ln); err != nil {
					s.logger.Errorf("%s serve %q: %v", srv.label(), srv.tcpAddr.String(), err)
				}
			}(srv, lns[i])
		}
		if udpSrv, yes := srv.isUDP(); yes {
			go func(srv *server, conn *net.UDPConn) {
				defer s.recover()

				s.mu.Lock()
				srv.udpAddr = conn.LocalAddr().(*net.UDPAddr)
				s.mu.Unlock()

				s.logger.Infof("%s listening on %q", srv.label(), srv.tcpAddr.String())
				if err := udpSrv.ServeUDP(conn); err != nil {
					s.logger.Errorf("%s serve %q: %v", srv.label(), srv.tcpAddr.String(), err)
				}
			}(srv, conns[i])
		}
	}
	return nil
}

// TCPAddr returns a TCP address of the listener that a server
// with a specific name is using. If there are more servers
// with the same name, the address of the first started server
// is returned.
func (s *Servers) TCPAddr(name string) (a *net.TCPAddr) {
	s.mu.Lock()
	defer s.mu.Unlock()

	for _, srv := range s.servers {
		if srv.name == name {
			return srv.tcpAddr
		}
	}
	return nil
}

// UDPAddr returns a UDP address of the listener that a server
// with a specific name is using. If there are more servers
// with the same name, the address of the first started server
// is returned.
func (s *Servers) UDPAddr(name string) (a *net.UDPAddr) {
	s.mu.Lock()
	defer s.mu.Unlock()

	for _, srv := range s.servers {
		if srv.name == name {
			return srv.udpAddr
		}
	}
	return nil
}

// Close stops all servers, by calling Close method on each of them.
func (s *Servers) Close() {
	wg := &sync.WaitGroup{}
	for _, srv := range s.servers {
		wg.Add(1)
		go func(srv *server) {
			defer s.recover()
			defer wg.Done()

			s.logger.Infof("%s closing", srv.label())
			if err := srv.Close(); err != nil {
				s.logger.Errorf("%s close: %v", srv.label(), err)
			}
		}(srv)
	}
	wg.Wait()
	return
}

// Shutdown gracefully stops all servers, by calling Shutdown method on each of them.
func (s *Servers) Shutdown(ctx context.Context) {
	wg := &sync.WaitGroup{}
	for _, srv := range s.servers {
		wg.Add(1)
		go func(srv *server) {
			defer s.recover()
			defer wg.Done()

			s.logger.Infof("%s shutting down", srv.label())
			if err := srv.Shutdown(ctx); err != nil {
				s.logger.Errorf("%s shutdown: %v", srv.label(), err)
			}
		}(srv)
	}
	wg.Wait()
	return
}
<commit_msg>servers: redundant return statements<commit_after>// Copyright (c) 2017, Janoš Guljaš <janos@resenje.org>
// All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package servers

import (
	"context"
	"fmt"
	"log"
	"net"
	"sync"
)

// Logger defines methods required for logging.
type Logger interface {
	Infof(format string, a ...interface{})
	Errorf(format string, a ...interface{})
}

// stdLogger is a simple implementation of Logger interface
// that uses log package for logging messages.
type stdLogger struct{}

func (l stdLogger) Infof(format string, a ...interface{}) {
	log.Printf("INFO "+format, a...)
}

func (l stdLogger) Errorf(format string, a ...interface{}) {
	log.Printf("ERROR "+format, a...)
}

// Option is a function that sets optional parameters for Servers.
type Option func(*Servers)

// WithLogger sets the Logger instance for logging messages.
func WithLogger(logger Logger) Option { return func(o *Servers) { o.logger = logger } }

// WithRecoverFunc sets a function that will be used to recover
// from panic inside a goroutune that servers are serving requests.
func WithRecoverFunc(recover func()) Option { return func(o *Servers) { o.recover = recover } }

// Servers holds a list of servers and their options.
// It provides a simple way to construct server group with Add method,
// to start them with Serve method, and stop them with Close or Shutdown methods.
type Servers struct {
	servers []*server
	mu      sync.Mutex
	logger  Logger
	recover func()
}

// New creates a new instance of Servers with applied options.
func New(opts ...Option) (s *Servers) {
	s = &Servers{
		logger:  stdLogger{},
		recover: func() {},
	}
	for _, opt := range opts {
		opt(s)
	}
	return
}

// Server defines required methods for a type that can be added to
// the Servers.
// In addition to this methods, a Server should implement TCPServer
// or UDPServer to be able to serve requests.
type Server interface {
	// Close should stop server from serving all existing requests
	// and stop accepting new ones.
	// The listener provided in Serve method must stop listening.
	Close() error
	// Shutdown should gracefully stop server. All existing requests
	// should be processed within a deadline provided by the context.
	// No new requests should be accepted.
	// The listener provided in Serve method must stop listening.
	Shutdown(ctx context.Context) error
}

// TCPServer defines methods for a server that accepts requests
// over TCP listener.
type TCPServer interface {
	// Serve should start server responding to requests.
	// The listener is initialized and already listening.
	ServeTCP(ln net.Listener) error
}

// UDPServer defines methods for a server that accepts requests
// over UDP listener.
type UDPServer interface {
	ServeUDP(conn *net.UDPConn) error
}

type server struct {
	Server
	name    string
	address string
	tcpAddr *net.TCPAddr
	udpAddr *net.UDPAddr
}

func (s *server) label() string {
	if s.name == "" {
		return "server"
	}
	return s.name + " server"
}

func (s *server) isTCP() (srv TCPServer, yes bool) {
	srv, yes = s.Server.(TCPServer)
	return
}

func (s *server) isUDP() (srv UDPServer, yes bool) {
	srv, yes = s.Server.(UDPServer)
	return
}

// Add adds a new server instance by a custom name and with
// address to listen to.
func (s *Servers) Add(name, address string, srv Server) {
	s.mu.Lock()
	s.servers = append(s.servers, &server{
		Server:  srv,
		name:    name,
		address: address,
	})
	s.mu.Unlock()
}

// Serve starts all added servers.
// New new servers must be added after this methid is called.
func (s *Servers) Serve() (err error) {
	lns := make([]net.Listener, len(s.servers))
	conns := make([]*net.UDPConn, len(s.servers))
	for i, srv := range s.servers {
		if _, yes := srv.isTCP(); yes {
			ln, err := net.Listen("tcp", srv.address)
			if err != nil {
				for _, l := range lns {
					if l == nil {
						continue
					}
					if err := l.Close(); err != nil {
						s.logger.Errorf("%s tcp listener %q close: %v", srv.label(), srv.address, err)
					}
				}
				return fmt.Errorf("%s tcp listener %q: %v", srv.label(), srv.address, err)
			}
			lns[i] = ln
		}
		if _, yes := srv.isUDP(); yes {
			addr, err := net.ResolveUDPAddr("udp", srv.address)
			if err != nil {
				return fmt.Errorf("%s resolve udp address %q: %v", srv.label(), srv.address, err)
			}
			conn, err := net.ListenUDP("udp", addr)
			if err != nil {
				return fmt.Errorf("%s udp listener %q: %v", srv.label(), srv.address, err)
			}
			conns[i] = conn
		}
	}
	for i, srv := range s.servers {
		if tcpSrv, yes := srv.isTCP(); yes {
			go func(srv *server, ln net.Listener) {
				defer s.recover()

				s.mu.Lock()
				srv.tcpAddr = ln.Addr().(*net.TCPAddr)
				s.mu.Unlock()

				s.logger.Infof("%s listening on %q", srv.label(), srv.tcpAddr.String())
				if err := tcpSrv.ServeTCP(ln); err != nil {
					s.logger.Errorf("%s serve %q: %v", srv.label(), srv.tcpAddr.String(), err)
				}
			}(srv, lns[i])
		}
		if udpSrv, yes := srv.isUDP(); yes {
			go func(srv *server, conn *net.UDPConn) {
				defer s.recover()

				s.mu.Lock()
				srv.udpAddr = conn.LocalAddr().(*net.UDPAddr)
				s.mu.Unlock()

				s.logger.Infof("%s listening on %q", srv.label(), srv.tcpAddr.String())
				if err := udpSrv.ServeUDP(conn); err != nil {
					s.logger.Errorf("%s serve %q: %v", srv.label(), srv.tcpAddr.String(), err)
				}
			}(srv, conns[i])
		}
	}
	return nil
}

// TCPAddr returns a TCP address of the listener that a server
// with a specific name is using. If there are more servers
// with the same name, the address of the first started server
// is returned.
func (s *Servers) TCPAddr(name string) (a *net.TCPAddr) {
	s.mu.Lock()
	defer s.mu.Unlock()

	for _, srv := range s.servers {
		if srv.name == name {
			return srv.tcpAddr
		}
	}
	return nil
}

// UDPAddr returns a UDP address of the listener that a server
// with a specific name is using. If there are more servers
// with the same name, the address of the first started server
// is returned.
func (s *Servers) UDPAddr(name string) (a *net.UDPAddr) {
	s.mu.Lock()
	defer s.mu.Unlock()

	for _, srv := range s.servers {
		if srv.name == name {
			return srv.udpAddr
		}
	}
	return nil
}

// Close stops all servers, by calling Close method on each of them.
func (s *Servers) Close() {
	wg := &sync.WaitGroup{}
	for _, srv := range s.servers {
		wg.Add(1)
		go func(srv *server) {
			defer s.recover()
			defer wg.Done()

			s.logger.Infof("%s closing", srv.label())
			if err := srv.Close(); err != nil {
				s.logger.Errorf("%s close: %v", srv.label(), err)
			}
		}(srv)
	}
	wg.Wait()
}

// Shutdown gracefully stops all servers, by calling Shutdown method on each of them.
func (s *Servers) Shutdown(ctx context.Context) {
	wg := &sync.WaitGroup{}
	for _, srv := range s.servers {
		wg.Add(1)
		go func(srv *server) {
			defer s.recover()
			defer wg.Done()

			s.logger.Infof("%s shutting down", srv.label())
			if err := srv.Shutdown(ctx); err != nil {
				s.logger.Errorf("%s shutdown: %v", srv.label(), err)
			}
		}(srv)
	}
	wg.Wait()
}
<|endoftext|>


<commit_before>package speccy

import (
	"github.com/jbert/zog"
	"github.com/veandco/go-sdl2/sdl"
)

// An entry in the map means the key is depressed. (poor key)
type keyboardState map[sdl.Keycode]struct{}

func NewKeyboardState() *keyboardState {
	ks := keyboardState(make(map[sdl.Keycode]struct{}))
	return &ks
}

func (ks *keyboardState) InstallKeyboardInputPorts(z *zog.Zog) {
	keymaps := []struct {
		port uint16
		keys []sdl.Keycode
	}{
		{0xfefe, []sdl.Keycode{sdl.K_LSHIFT, sdl.K_z, sdl.K_x, sdl.K_c, sdl.K_v}},
		{0xfdfe, []sdl.Keycode{sdl.K_a, sdl.K_s, sdl.K_d, sdl.K_f, sdl.K_g}},
		{0xfbfe, []sdl.Keycode{sdl.K_q, sdl.K_w, sdl.K_e, sdl.K_r, sdl.K_t}},
		{0xf7fe, []sdl.Keycode{sdl.K_1, sdl.K_2, sdl.K_3, sdl.K_4, sdl.K_5}},
		{0xeffe, []sdl.Keycode{sdl.K_0, sdl.K_9, sdl.K_8, sdl.K_7, sdl.K_6}},
		{0xdffe, []sdl.Keycode{sdl.K_p, sdl.K_o, sdl.K_i, sdl.K_u, sdl.K_y}},
		{0xbffe, []sdl.Keycode{sdl.K_RETURN, sdl.K_l, sdl.K_k, sdl.K_j, sdl.K_h}},
		{0x7ffe, []sdl.Keycode{sdl.K_SPACE, sdl.K_RSHIFT, sdl.K_m, sdl.K_n, sdl.K_b}},
	}

	for _, keymapReused := range keymaps {
		keymap := keymapReused // This bit of go really sucks
		err := z.RegisterInputHandler(keymap.port, func() byte {
			return ks.inputHandler(keymap.keys)
		})
		if err != nil {
			panic(err)
		}
	}
}

// 5 keys = bit0 1st, bit 4last
func (ks *keyboardState) inputHandler(keys []sdl.Keycode) byte {
	// We have 5 bits. Key pressed is 0, else 1
	//	for k, _ := range *ks {
	//		fmt.Printf("JB - [%d] down\n", int(k))
	//	}
	n := byte(0xff)
	mask := byte(0xfe)
	for _, key := range keys {
		//		fmt.Printf("JB - checking key [%d]\n", int(key))
		_, ok := (*ks)[key]
		if ok {
			// Key pressed, clear bit
			n &= mask
		}
		mask <<= 1
		mask |= 0x01
		//		} else {
		//			fmt.Printf("JB - flagging key [%d] pressed\n", int(key))
		//		}
	}
	//	fmt.Printf("JB - ret [%02X]\n", n)
	return n
}

func (ks *keyboardState) Update() {
	// Drain events to update map
	for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
		switch ev := event.(type) {
		case *sdl.KeyDownEvent:
			sc := ev.Keysym.Sym
			//					s := sdl.GetScancodeName(sc)
			(*ks)[sc] = struct{}{}
		case *sdl.KeyUpEvent:
			sc := ev.Keysym.Sym
			//					s := sdl.GetScancodeName(sc)
			delete(*ks, sc)
		}
	}
}
<commit_msg>Support mapped keys<commit_after>package speccy

import (
	"github.com/jbert/zog"
	"github.com/veandco/go-sdl2/sdl"
)

// An entry in the map means the key is depressed. (poor key)
type keyboardState map[sdl.Keycode]struct{}

func NewKeyboardState() *keyboardState {
	ks := keyboardState(make(map[sdl.Keycode]struct{}))
	return &ks
}

func (ks *keyboardState) InstallKeyboardInputPorts(z *zog.Zog) {
	keymaps := []struct {
		port uint16
		keys []sdl.Keycode
	}{
		{0xfefe, []sdl.Keycode{sdl.K_LSHIFT, sdl.K_z, sdl.K_x, sdl.K_c, sdl.K_v}},
		{0xfdfe, []sdl.Keycode{sdl.K_a, sdl.K_s, sdl.K_d, sdl.K_f, sdl.K_g}},
		{0xfbfe, []sdl.Keycode{sdl.K_q, sdl.K_w, sdl.K_e, sdl.K_r, sdl.K_t}},
		{0xf7fe, []sdl.Keycode{sdl.K_1, sdl.K_2, sdl.K_3, sdl.K_4, sdl.K_5}},
		{0xeffe, []sdl.Keycode{sdl.K_0, sdl.K_9, sdl.K_8, sdl.K_7, sdl.K_6}},
		{0xdffe, []sdl.Keycode{sdl.K_p, sdl.K_o, sdl.K_i, sdl.K_u, sdl.K_y}},
		{0xbffe, []sdl.Keycode{sdl.K_RETURN, sdl.K_l, sdl.K_k, sdl.K_j, sdl.K_h}},
		{0x7ffe, []sdl.Keycode{sdl.K_SPACE, sdl.K_RSHIFT, sdl.K_m, sdl.K_n, sdl.K_b}},
	}

	for _, keymapReused := range keymaps {
		keymap := keymapReused // This bit of go really sucks
		err := z.RegisterInputHandler(keymap.port, func() byte {
			return ks.inputHandler(keymap.keys)
		})
		if err != nil {
			panic(err)
		}
	}
}

// 5 keys = bit0 1st, bit 4last
func (ks *keyboardState) inputHandler(keys []sdl.Keycode) byte {
	// We have 5 bits. Key pressed is 0, else 1
	//	for k, _ := range *ks {
	//		fmt.Printf("JB - [%d] down\n", int(k))
	//	}
	n := byte(0xff)
	mask := byte(0xfe)
	for _, key := range keys {
		//		fmt.Printf("JB - checking key [%d]\n", int(key))
		_, ok := (*ks)[key]
		if ok {
			// Key pressed, clear bit
			n &= mask
		}
		mask <<= 1
		mask |= 0x01
		//		} else {
		//			fmt.Printf("JB - flagging key [%d] pressed\n", int(key))
		//		}
	}
	//	fmt.Printf("JB - ret [%02X]\n", n)
	return n
}

func (ks *keyboardState) Update() {
	// Drain events to update map
	for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
		switch ev := event.(type) {
		case *sdl.KeyDownEvent:
			kc := ev.Keysym.Sym
			//					s := sdl.GetScancodeName(sc)
			ks.keymove(kc, false)
			//(*ks)[sc] = struct{}{}
		case *sdl.KeyUpEvent:
			kc := ev.Keysym.Sym
			ks.keymove(kc, true)
			//					s := sdl.GetScancodeName(sc)
			//delete(*ks, kc)
		}
	}
}

func (ks *keyboardState) keymove(kc sdl.Keycode, up bool) {
	mappedKeys := []sdl.Keycode{}
	switch kc {
	case sdl.K_BACKSPACE:
		// Delete is shift-0
		mappedKeys = append(mappedKeys, sdl.K_LSHIFT)
		mappedKeys = append(mappedKeys, sdl.K_0)
	default:
		mappedKeys = append(mappedKeys, kc)
	}

	for _, mappedKey := range mappedKeys {
		if up {
			delete(*ks, mappedKey)
		} else {
			(*ks)[mappedKey] = struct{}{}
		}
	}
}
<|endoftext|>


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

package remoterelations

import (
	"github.com/juju/errors"
	"github.com/juju/juju/state"
)

type RemoteRelationsState interface {
	KeyRelation(string) (Relation, error)
	WatchRemoteServices() state.StringsWatcher
	WatchRemoteServiceRelations(serviceName string) (state.StringsWatcher, error)
}

type Relation interface {
	Id() int
	Life() state.Life
	Unit(unitId string) (RelationUnit, error)
	WatchCounterpartEndpointUnits(serviceName string) (state.RelationUnitsWatcher, error)
}

type RelationUnit interface {
	Settings() (map[string]interface{}, error)
}

type stateShim struct {
	*state.State
}

func (st stateShim) KeyRelation(key string) (Relation, error) {
	r, err := st.State.KeyRelation(key)
	if err != nil {
		return nil, errors.Trace(err)
	}
	return relationShim{r, st.State}, nil
}

func (st stateShim) WatchRemoteServiceRelations(serviceName string) (state.StringsWatcher, error) {
	s, err := st.RemoteService(serviceName)
	if err != nil {
		return nil, errors.Trace(err)
	}
	return s.WatchRelations(), nil
}

type relationShim struct {
	*state.Relation
	st *state.State
}

func (r relationShim) Unit(unitId string) (RelationUnit, error) {
	unit, err := r.st.Unit(unitId)
	if err != nil {
		return nil, errors.Trace(err)
	}
	ru, err := r.Relation.Unit(unit)
	if err != nil {
		return nil, errors.Trace(err)
	}
	return relationUnitShim{ru}, nil
}

type relationUnitShim struct {
	*state.RelationUnit
}

func (r relationUnitShim) Settings() (map[string]interface{}, error) {
	settings, err := r.RelationUnit.Settings()
	if err != nil {
		return nil, errors.Trace(err)
	}
	return settings.Map(), nil
}
<commit_msg>apiserver/remoterelations: add comments<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package remoterelations

import (
	"github.com/juju/errors"
	"github.com/juju/juju/state"
)

// RemoteRelationState provides the subset of global state required by the
// remote relations facade.
type RemoteRelationsState interface {
	// KeyRelation returns the existing relation with the given key (which can
	// be derived unambiguously from the relation's endpoints).
	KeyRelation(string) (Relation, error)

	// WatchRemoteServices returns a StringsWatcher that notifies of changes to
	// the lifecycles of the remote services in the environment.
	WatchRemoteServices() state.StringsWatcher

	// WatchRemoteServiceRelations returns a StringsWatcher that notifies of
	// changes to the lifecycles of relations involving the specified remote
	// service.
	WatchRemoteServiceRelations(serviceName string) (state.StringsWatcher, error)
}

// Relation provides access a relation in global state.
type Relation interface {
	// Id returns the integer internal relation key.
	Id() int

	// Life returns the relation's current life state.
	Life() state.Life

	// Unit returns a RelationUnit for the unit with the supplied ID.
	Unit(unitId string) (RelationUnit, error)

	// WatchCounterpartEndpointUnits returns a watcher that notifies of
	// changes to the units with the endpoint counterpart to the specified
	// service.
	WatchCounterpartEndpointUnits(serviceName string) (state.RelationUnitsWatcher, error)
}

// RelationUnit provides access to the settings of a single unit in a relation.
type RelationUnit interface {
	Settings() (map[string]interface{}, error)
}

type stateShim struct {
	*state.State
}

func (st stateShim) KeyRelation(key string) (Relation, error) {
	r, err := st.State.KeyRelation(key)
	if err != nil {
		return nil, errors.Trace(err)
	}
	return relationShim{r, st.State}, nil
}

func (st stateShim) WatchRemoteServiceRelations(serviceName string) (state.StringsWatcher, error) {
	s, err := st.RemoteService(serviceName)
	if err != nil {
		return nil, errors.Trace(err)
	}
	return s.WatchRelations(), nil
}

type relationShim struct {
	*state.Relation
	st *state.State
}

func (r relationShim) Unit(unitId string) (RelationUnit, error) {
	unit, err := r.st.Unit(unitId)
	if err != nil {
		return nil, errors.Trace(err)
	}
	ru, err := r.Relation.Unit(unit)
	if err != nil {
		return nil, errors.Trace(err)
	}
	return relationUnitShim{ru}, nil
}

type relationUnitShim struct {
	*state.RelationUnit
}

func (r relationUnitShim) Settings() (map[string]interface{}, error) {
	settings, err := r.RelationUnit.Settings()
	if err != nil {
		return nil, errors.Trace(err)
	}
	return settings.Map(), nil
}
<|endoftext|>


<commit_before>package system

import (
	"fmt"
	"syscall"

	"github.com/buildkite/agent/v3/logger"
)

func VersionDump(_ logger.Logger) (string, error) {
	dll := syscall.MustLoadDLL("kernel32.dll")
	p := dll.MustFindProc("GetVersion")
	v, _, _ := p.Call()

	return fmt.Sprintf("Windows version %d.%d (Build %d)\n", byte(v), uint8(v>>8), uint16(v>>16)), nil
}
<commit_msg>Update the way we detect windows versions<commit_after>package system

import (
	"fmt"
	"github.com/buildkite/agent/v3/logger"
	"golang.org/x/sys/windows"
)

func VersionDump(_ logger.Logger) (string, error) {
	info := windows.RtlGetVersion()

	return fmt.Sprintf("Windows version %d.%d (Build %d)\n", info.MajorVersion, info.MinorVersion, info.BuildNumber), nil
}
<|endoftext|>