<commit_before>package dns

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"strings"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/unversioned"
	"k8s.io/kubernetes/pkg/apimachinery/registered"
	"k8s.io/kubernetes/pkg/util"
	"k8s.io/kubernetes/pkg/util/sets"
	"k8s.io/kubernetes/pkg/util/wait"
	e2e "k8s.io/kubernetes/test/e2e/framework"
)

func createDNSPod(namespace, probeCmd string) *api.Pod {
	pod := &api.Pod{
		TypeMeta: unversioned.TypeMeta{
			Kind:       "Pod",
			APIVersion: registered.GroupOrDie(api.GroupName).GroupVersion.String(),
		},
		ObjectMeta: api.ObjectMeta{
			Name:      "dns-test-" + string(util.NewUUID()),
			Namespace: namespace,
		},
		Spec: api.PodSpec{
			RestartPolicy: api.RestartPolicyNever,
			Containers: []api.Container{
				{
					Name:    "querier",
					Image:   "gcr.io/google_containers/dnsutils:e2e",
					Command: []string{"sh", "-c", probeCmd},
				},
			},
		},
	}
	return pod
}

func digForNames(namesToResolve []string, expect sets.String) string {
	fileNamePrefix := "test"
	var probeCmd string
	for _, name := range namesToResolve {
		// Resolve by TCP and UDP DNS.  Use $$(...) because $(...) is
		// expanded by kubernetes (though this won't expand so should
		// remain a literal, safe > sorry).
		lookup := "A"
		if strings.HasPrefix(name, "_") {
			lookup = "SRV"
		}
		fileName := fmt.Sprintf("%s_udp@%s", fileNamePrefix, name)
		expect.Insert(fileName)
		probeCmd += fmt.Sprintf(`test -n "$$(dig +notcp +noall +answer +search %s %s)" && echo %q;`, name, lookup, fileName)
		fileName = fmt.Sprintf("%s_tcp@%s", fileNamePrefix, name)
		expect.Insert(fileName)
		probeCmd += fmt.Sprintf(`test -n "$$(dig +tcp +noall +answer +search %s %s)" && echo %q;`, name, lookup, fileName)
	}
	return probeCmd
}

func digForARecords(records map[string][]string, expect sets.String) string {
	var probeCmd string
	fileNamePrefix := "test"
	for name, ips := range records {
		fileName := fmt.Sprintf("%s_endpoints@%s", fileNamePrefix, name)
		probeCmd += fmt.Sprintf(`[ "$$(dig +short +notcp +noall +answer +search %s A | sort | xargs echo)" = "%s" ] && echo %q;`, name, strings.Join(ips, " "), fileName)
		expect.Insert(fileName)
	}
	return probeCmd
}

func digForPod(namespace string, expect sets.String) string {
	var probeCmd string
	fileNamePrefix := "test"
	podARecByUDPFileName := fmt.Sprintf("%s_udp@PodARecord", fileNamePrefix)
	podARecByTCPFileName := fmt.Sprintf("%s_tcp@PodARecord", fileNamePrefix)
	probeCmd += fmt.Sprintf(`podARec=$$(hostname -i| awk -F. '{print $$1"-"$$2"-"$$3"-"$$4".%s.pod.cluster.local"}');`, namespace)
	probeCmd += fmt.Sprintf(`test -n "$$(dig +notcp +noall +answer +search $${podARec} A)" && echo %q;`, podARecByUDPFileName)
	probeCmd += fmt.Sprintf(`test -n "$$(dig +tcp +noall +answer +search $${podARec} A)" && echo %q;`, podARecByTCPFileName)
	expect.Insert(podARecByUDPFileName, podARecByTCPFileName)
	return probeCmd
}

func repeatCommand(times int, cmd ...string) string {
	probeCmd := fmt.Sprintf("for i in `seq 1 %d`; do ", times)
	probeCmd += strings.Join(cmd, " ")
	probeCmd += "sleep 1; done"
	return probeCmd
}

func assertLinesExist(lines sets.String, expect int, r io.Reader) error {
	count := make(map[string]int)
	unrecognized := sets.NewString()
	scan := bufio.NewScanner(r)
	for scan.Scan() {
		line := scan.Text()
		if lines.Has(line) {
			count[line]++
		} else {
			unrecognized.Insert(line)
		}
	}
	for k := range lines {
		if count[k] != expect {
			return fmt.Errorf("unexpected count %d/%d for %q: %v", count[k], expect, k, unrecognized)
		}
	}
	if unrecognized.Len() > 0 {
		return fmt.Errorf("unexpected matches from output: %v", unrecognized)
	}
	return nil
}

func validateDNSResults(f *e2e.Framework, pod *api.Pod, fileNames sets.String, expect int) {
	By("submitting the pod to kubernetes")
	podClient := f.Client.Pods(f.Namespace.Name)
	defer func() {
		By("deleting the pod")
		defer GinkgoRecover()
		podClient.Delete(pod.Name, api.NewDeleteOptions(0))
	}()
	if _, err := podClient.Create(pod); err != nil {
		e2e.Failf("Failed to create %s pod: %v", pod.Name, err)
	}

	Expect(f.WaitForPodRunning(pod.Name)).To(BeNil())
	Expect(wait.Poll(2*time.Second, 5*time.Minute, func() (bool, error) {
		pod, err := podClient.Get(pod.Name)
		if err != nil {
			return false, err
		}
		switch pod.Status.Phase {
		case api.PodSucceeded:
			return true, nil
		case api.PodFailed:
			return false, fmt.Errorf("pod failed")
		default:
			return false, nil
		}
	})).To(BeNil())

	By("retrieving the pod logs")
	r, err := podClient.GetLogs(pod.Name, &api.PodLogOptions{Container: "querier"}).Stream()
	if err != nil {
		e2e.Failf("Failed to get pod logs %s: %v", pod.Name, err)
	}
	out, err := ioutil.ReadAll(r)
	if err != nil {
		e2e.Failf("Failed to read pod logs %s: %v", pod.Name, err)
	}

	// Try to find results for each expected name.
	By("looking for the results for each expected name from probiers")

	if err := assertLinesExist(fileNames, expect, bytes.NewBuffer(out)); err != nil {
		e2e.Logf("Got results from pod:\n%s", out)
		e2e.Failf("Unexpected results: %v", err)
	}

	e2e.Logf("DNS probes using %s succeeded\n", pod.Name)
}

func createServiceSpec(serviceName string, isHeadless bool, selector map[string]string) *api.Service {
	headlessService := &api.Service{
		ObjectMeta: api.ObjectMeta{
			Name: serviceName,
		},
		Spec: api.ServiceSpec{
			Ports: []api.ServicePort{
				{Port: 80, Name: "http", Protocol: "TCP"},
			},
			Selector: selector,
		},
	}
	if isHeadless {
		headlessService.Spec.ClusterIP = "None"
	}
	return headlessService
}

func createEndpointSpec(name string) *api.Endpoints {
	return &api.Endpoints{
		ObjectMeta: api.ObjectMeta{
			Name: name,
		},
		Subsets: []api.EndpointSubset{
			{
				Addresses: []api.EndpointAddress{
					{IP: "1.1.1.1"},
					{IP: "1.1.1.2"},
				},
				NotReadyAddresses: []api.EndpointAddress{
					{IP: "2.1.1.1"},
					{IP: "2.1.1.2"},
				},
				Ports: []api.EndpointPort{
					{Port: 80},
				},
			},
		},
	}
}

func ipsForEndpoints(ep *api.Endpoints) []string {
	ips := sets.NewString()
	for _, sub := range ep.Subsets {
		for _, addr := range sub.Addresses {
			ips.Insert(addr.IP)
		}
	}
	return ips.List()
}

var _ = Describe("DNS", func() {
	f := e2e.NewDefaultFramework("dns")

	It("should answer endpoint and wildcard queries for the cluster [Conformance]", func() {
		if _, err := f.Client.Services(f.Namespace.Name).Create(createServiceSpec("headless", true, nil)); err != nil {
			e2e.Failf("unable to create headless service: %v", err)
		}
		if _, err := f.Client.Endpoints(f.Namespace.Name).Create(createEndpointSpec("headless")); err != nil {
			e2e.Failf("unable to create clusterip endpoints: %v", err)
		}
		if _, err := f.Client.Services(f.Namespace.Name).Create(createServiceSpec("clusterip", false, nil)); err != nil {
			e2e.Failf("unable to create clusterip service: %v", err)
		}
		if _, err := f.Client.Endpoints(f.Namespace.Name).Create(createEndpointSpec("clusterip")); err != nil {
			e2e.Failf("unable to create clusterip endpoints: %v", err)
		}

		ep, err := f.Client.Endpoints("default").Get("kubernetes")
		if err != nil {
			e2e.Failf("unable to find endpoints for kubernetes.default: %v", err)
		}
		kubeEndpoints := ipsForEndpoints(ep)

		readyEndpoints := ipsForEndpoints(createEndpointSpec(""))

		// All the names we need to be able to resolve.
		expect := sets.NewString()
		times := 10
		cmd := repeatCommand(
			times,
			// the DNS pod should be able to resolve these names
			digForNames([]string{
				// answer wildcards on default service
				"prefix.kubernetes.default",
				"prefix.kubernetes.default.svc",
				"prefix.kubernetes.default.svc.cluster.local",

				// answer wildcards on cluster service
				fmt.Sprintf("prefix.headless.%s", f.Namespace.Name),
				fmt.Sprintf("prefix.clusterip.%s", f.Namespace.Name),
			}, expect),

			// the DNS pod should be able to look up endpoints for names and wildcards
			digForARecords(map[string][]string{
				"kubernetes.default.endpoints":                      kubeEndpoints,
				"prefix.kubernetes.default.endpoints.cluster.local": kubeEndpoints,

				fmt.Sprintf("headless.%s.svc", f.Namespace.Name):        readyEndpoints,
				fmt.Sprintf("headless.%s.endpoints", f.Namespace.Name):  readyEndpoints,
				fmt.Sprintf("clusterip.%s.endpoints", f.Namespace.Name): readyEndpoints,
			}, expect),

			// the DNS pod should respond to its own request
			digForPod(f.Namespace.Name, expect),
		)

		By("Running these commands:" + cmd + "\n")

		// Run a pod which probes DNS and exposes the results by HTTP.
		By("creating a pod to probe DNS")
		pod := createDNSPod(f.Namespace.Name, cmd)
		validateDNSResults(f, pod, expect, times)
	})
})
<commit_msg>Fix a DNS flake by watching for pod succeeded<commit_after>package dns

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"strings"

	. "github.com/onsi/ginkgo"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/errors"
	"k8s.io/kubernetes/pkg/api/unversioned"
	"k8s.io/kubernetes/pkg/apimachinery/registered"
	"k8s.io/kubernetes/pkg/util"
	"k8s.io/kubernetes/pkg/util/sets"
	"k8s.io/kubernetes/pkg/watch"
	e2e "k8s.io/kubernetes/test/e2e/framework"
)

func createDNSPod(namespace, probeCmd string) *api.Pod {
	pod := &api.Pod{
		TypeMeta: unversioned.TypeMeta{
			Kind:       "Pod",
			APIVersion: registered.GroupOrDie(api.GroupName).GroupVersion.String(),
		},
		ObjectMeta: api.ObjectMeta{
			Name:      "dns-test-" + string(util.NewUUID()),
			Namespace: namespace,
		},
		Spec: api.PodSpec{
			RestartPolicy: api.RestartPolicyNever,
			Containers: []api.Container{
				{
					Name:    "querier",
					Image:   "gcr.io/google_containers/dnsutils:e2e",
					Command: []string{"sh", "-c", probeCmd},
				},
			},
		},
	}
	return pod
}

func digForNames(namesToResolve []string, expect sets.String) string {
	fileNamePrefix := "test"
	var probeCmd string
	for _, name := range namesToResolve {
		// Resolve by TCP and UDP DNS.  Use $$(...) because $(...) is
		// expanded by kubernetes (though this won't expand so should
		// remain a literal, safe > sorry).
		lookup := "A"
		if strings.HasPrefix(name, "_") {
			lookup = "SRV"
		}
		fileName := fmt.Sprintf("%s_udp@%s", fileNamePrefix, name)
		expect.Insert(fileName)
		probeCmd += fmt.Sprintf(`test -n "$$(dig +notcp +noall +answer +search %s %s)" && echo %q;`, name, lookup, fileName)
		fileName = fmt.Sprintf("%s_tcp@%s", fileNamePrefix, name)
		expect.Insert(fileName)
		probeCmd += fmt.Sprintf(`test -n "$$(dig +tcp +noall +answer +search %s %s)" && echo %q;`, name, lookup, fileName)
	}
	return probeCmd
}

func digForARecords(records map[string][]string, expect sets.String) string {
	var probeCmd string
	fileNamePrefix := "test"
	for name, ips := range records {
		fileName := fmt.Sprintf("%s_endpoints@%s", fileNamePrefix, name)
		probeCmd += fmt.Sprintf(`[ "$$(dig +short +notcp +noall +answer +search %s A | sort | xargs echo)" = "%s" ] && echo %q;`, name, strings.Join(ips, " "), fileName)
		expect.Insert(fileName)
	}
	return probeCmd
}

func digForPod(namespace string, expect sets.String) string {
	var probeCmd string
	fileNamePrefix := "test"
	podARecByUDPFileName := fmt.Sprintf("%s_udp@PodARecord", fileNamePrefix)
	podARecByTCPFileName := fmt.Sprintf("%s_tcp@PodARecord", fileNamePrefix)
	probeCmd += fmt.Sprintf(`podARec=$$(hostname -i| awk -F. '{print $$1"-"$$2"-"$$3"-"$$4".%s.pod.cluster.local"}');`, namespace)
	probeCmd += fmt.Sprintf(`test -n "$$(dig +notcp +noall +answer +search $${podARec} A)" && echo %q;`, podARecByUDPFileName)
	probeCmd += fmt.Sprintf(`test -n "$$(dig +tcp +noall +answer +search $${podARec} A)" && echo %q;`, podARecByTCPFileName)
	expect.Insert(podARecByUDPFileName, podARecByTCPFileName)
	return probeCmd
}

func repeatCommand(times int, cmd ...string) string {
	probeCmd := fmt.Sprintf("for i in `seq 1 %d`; do ", times)
	probeCmd += strings.Join(cmd, " ")
	probeCmd += "sleep 1; done"
	return probeCmd
}

func assertLinesExist(lines sets.String, expect int, r io.Reader) error {
	count := make(map[string]int)
	unrecognized := sets.NewString()
	scan := bufio.NewScanner(r)
	for scan.Scan() {
		line := scan.Text()
		if lines.Has(line) {
			count[line]++
		} else {
			unrecognized.Insert(line)
		}
	}
	for k := range lines {
		if count[k] != expect {
			return fmt.Errorf("unexpected count %d/%d for %q: %v", count[k], expect, k, unrecognized)
		}
	}
	if unrecognized.Len() > 0 {
		return fmt.Errorf("unexpected matches from output: %v", unrecognized)
	}
	return nil
}

// PodSucceeded returns true if the pod has succeeded, false if the pod has not yet
// reached running state, or an error in any other case.
func PodSucceeded(event watch.Event) (bool, error) {
	switch event.Type {
	case watch.Deleted:
		return false, errors.NewNotFound(unversioned.GroupResource{Resource: "pods"}, "")
	}
	switch t := event.Object.(type) {
	case *api.Pod:
		switch t.Status.Phase {
		case api.PodSucceeded:
			return true, nil
		case api.PodFailed:
			return false, fmt.Errorf("pod failed: %#v", t)
		}
	}
	return false, nil
}

func validateDNSResults(f *e2e.Framework, pod *api.Pod, fileNames sets.String, expect int) {
	By("submitting the pod to kubernetes")
	podClient := f.Client.Pods(f.Namespace.Name)
	defer func() {
		By("deleting the pod")
		defer GinkgoRecover()
		podClient.Delete(pod.Name, api.NewDeleteOptions(0))
	}()
	updated, err := podClient.Create(pod)
	if err != nil {
		e2e.Failf("Failed to create %s pod: %v", pod.Name, err)
	}

	w, err := f.Client.Pods(f.Namespace.Name).Watch(api.SingleObject(api.ObjectMeta{Name: pod.Name, ResourceVersion: updated.ResourceVersion}))
	if err != nil {
		e2e.Failf("Failed: %v", err)
	}
	if _, err = watch.Until(e2e.PodStartTimeout, w, PodSucceeded); err != nil {
		e2e.Failf("Failed: %v", err)
	}

	By("retrieving the pod logs")
	r, err := podClient.GetLogs(pod.Name, &api.PodLogOptions{Container: "querier"}).Stream()
	if err != nil {
		e2e.Failf("Failed to get pod logs %s: %v", pod.Name, err)
	}
	out, err := ioutil.ReadAll(r)
	if err != nil {
		e2e.Failf("Failed to read pod logs %s: %v", pod.Name, err)
	}

	// Try to find results for each expected name.
	By("looking for the results for each expected name from probiers")

	if err := assertLinesExist(fileNames, expect, bytes.NewBuffer(out)); err != nil {
		e2e.Logf("Got results from pod:\n%s", out)
		e2e.Failf("Unexpected results: %v", err)
	}

	e2e.Logf("DNS probes using %s succeeded\n", pod.Name)
}

func createServiceSpec(serviceName string, isHeadless bool, selector map[string]string) *api.Service {
	headlessService := &api.Service{
		ObjectMeta: api.ObjectMeta{
			Name: serviceName,
		},
		Spec: api.ServiceSpec{
			Ports: []api.ServicePort{
				{Port: 80, Name: "http", Protocol: "TCP"},
			},
			Selector: selector,
		},
	}
	if isHeadless {
		headlessService.Spec.ClusterIP = "None"
	}
	return headlessService
}

func createEndpointSpec(name string) *api.Endpoints {
	return &api.Endpoints{
		ObjectMeta: api.ObjectMeta{
			Name: name,
		},
		Subsets: []api.EndpointSubset{
			{
				Addresses: []api.EndpointAddress{
					{IP: "1.1.1.1"},
					{IP: "1.1.1.2"},
				},
				NotReadyAddresses: []api.EndpointAddress{
					{IP: "2.1.1.1"},
					{IP: "2.1.1.2"},
				},
				Ports: []api.EndpointPort{
					{Port: 80},
				},
			},
		},
	}
}

func ipsForEndpoints(ep *api.Endpoints) []string {
	ips := sets.NewString()
	for _, sub := range ep.Subsets {
		for _, addr := range sub.Addresses {
			ips.Insert(addr.IP)
		}
	}
	return ips.List()
}

var _ = Describe("DNS", func() {
	f := e2e.NewDefaultFramework("dns")

	It("should answer endpoint and wildcard queries for the cluster [Conformance]", func() {
		if _, err := f.Client.Services(f.Namespace.Name).Create(createServiceSpec("headless", true, nil)); err != nil {
			e2e.Failf("unable to create headless service: %v", err)
		}
		if _, err := f.Client.Endpoints(f.Namespace.Name).Create(createEndpointSpec("headless")); err != nil {
			e2e.Failf("unable to create clusterip endpoints: %v", err)
		}
		if _, err := f.Client.Services(f.Namespace.Name).Create(createServiceSpec("clusterip", false, nil)); err != nil {
			e2e.Failf("unable to create clusterip service: %v", err)
		}
		if _, err := f.Client.Endpoints(f.Namespace.Name).Create(createEndpointSpec("clusterip")); err != nil {
			e2e.Failf("unable to create clusterip endpoints: %v", err)
		}

		ep, err := f.Client.Endpoints("default").Get("kubernetes")
		if err != nil {
			e2e.Failf("unable to find endpoints for kubernetes.default: %v", err)
		}
		kubeEndpoints := ipsForEndpoints(ep)

		readyEndpoints := ipsForEndpoints(createEndpointSpec(""))

		// All the names we need to be able to resolve.
		expect := sets.NewString()
		times := 10
		cmd := repeatCommand(
			times,
			// the DNS pod should be able to resolve these names
			digForNames([]string{
				// answer wildcards on default service
				"prefix.kubernetes.default",
				"prefix.kubernetes.default.svc",
				"prefix.kubernetes.default.svc.cluster.local",

				// answer wildcards on cluster service
				fmt.Sprintf("prefix.headless.%s", f.Namespace.Name),
				fmt.Sprintf("prefix.clusterip.%s", f.Namespace.Name),
			}, expect),

			// the DNS pod should be able to look up endpoints for names and wildcards
			digForARecords(map[string][]string{
				"kubernetes.default.endpoints":                      kubeEndpoints,
				"prefix.kubernetes.default.endpoints.cluster.local": kubeEndpoints,

				fmt.Sprintf("headless.%s.svc", f.Namespace.Name):        readyEndpoints,
				fmt.Sprintf("headless.%s.endpoints", f.Namespace.Name):  readyEndpoints,
				fmt.Sprintf("clusterip.%s.endpoints", f.Namespace.Name): readyEndpoints,
			}, expect),

			// the DNS pod should respond to its own request
			digForPod(f.Namespace.Name, expect),
		)

		By("Running these commands:" + cmd + "\n")

		// Run a pod which probes DNS and exposes the results by HTTP.
		By("creating a pod to probe DNS")
		pod := createDNSPod(f.Namespace.Name, cmd)
		validateDNSResults(f, pod, expect, times)
	})
})
<|endoftext|>


<commit_before>// BaruwaAPI Golang bindings for Baruwa REST API
// Copyright (C) 2019 Andrew Colin Kissa <andrew@topdog.za.net>

// 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/.

package api

import (
	"fmt"
	"strconv"
	"strings"
	"time"
)

var nilTime = (time.Time{}).UnixNano()

// SettingsAS hold the authentication server id
type SettingsAS struct {
	ID int `json:"id"`
}

// Meta holds meta
type Meta struct {
	Total int `json:"total"`
}

// Links holds links
type Links struct {
	Pages Pages `json:"pages"`
}

// Pages holds pages
type Pages struct {
	First    string `json:"first"`
	Last     string `json:"last"`
	Previous string `json:"prev"`
	Next     string `json:"next"`
}

// ListOptions holds list options
type ListOptions struct {
	Page string
}

// MyTime custom date formater
type MyTime struct {
	time.Time
}

// UnmarshalJSON unmarshals the custom date
func (mt *MyTime) UnmarshalJSON(b []byte) (err error) {
	s := strings.Trim(string(b), "\"")
	t, err := time.Parse(time.RFC3339Nano, s)

	if err != nil {
		t, err = time.Parse(timeFmt, s)
	}

	mt.Time = t

	return
}

// MarshalJSON marshals the custom date
func (mt *MyTime) MarshalJSON() ([]byte, error) {
	if mt.Time.UnixNano() == nilTime {
		return []byte("null"), nil
	}

	return []byte(fmt.Sprintf("\"%s\"", mt.Time.Format(timeFmt))), nil
}

// LocalFloat64 allow for overriding in cli
type LocalFloat64 float64

// Set is required by cli
func (f *LocalFloat64) Set(v string) (err error) {
	_, err = strconv.ParseFloat(v, 64)
	return
}

func (f *LocalFloat64) String() string {
	return fmt.Sprintf("%.1f", *f)
}
<commit_msg>FIX: Improve local float64 type<commit_after>// BaruwaAPI Golang bindings for Baruwa REST API
// Copyright (C) 2019 Andrew Colin Kissa <andrew@topdog.za.net>

// 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/.

package api

import (
	"fmt"
	"strconv"
	"strings"
	"time"
)

var nilTime = (time.Time{}).UnixNano()

// SettingsAS hold the authentication server id
type SettingsAS struct {
	ID int `json:"id"`
}

// Meta holds meta
type Meta struct {
	Total int `json:"total"`
}

// Links holds links
type Links struct {
	Pages Pages `json:"pages"`
}

// Pages holds pages
type Pages struct {
	First    string `json:"first"`
	Last     string `json:"last"`
	Previous string `json:"prev"`
	Next     string `json:"next"`
}

// ListOptions holds list options
type ListOptions struct {
	Page string
}

// MyTime custom date formater
type MyTime struct {
	time.Time
}

// UnmarshalJSON unmarshals the custom date
func (mt *MyTime) UnmarshalJSON(b []byte) (err error) {
	s := strings.Trim(string(b), "\"")
	t, err := time.Parse(time.RFC3339Nano, s)

	if err != nil {
		t, err = time.Parse(timeFmt, s)
	}

	mt.Time = t

	return
}

// MarshalJSON marshals the custom date
func (mt *MyTime) MarshalJSON() ([]byte, error) {
	if mt.Time.UnixNano() == nilTime {
		return []byte("null"), nil
	}

	return []byte(fmt.Sprintf("\"%s\"", mt.Time.Format(timeFmt))), nil
}

// LocalFloat64 allow for overriding in cli
type LocalFloat64 float64

// Set is required by cli
func (f *LocalFloat64) Set(v string) (err error) {
	val, err := strconv.ParseFloat(v, 64)
	*f = LocalFloat64(val)
	return
}

func (f *LocalFloat64) String() string {
	if f == nil {
		return "0.0"
	}
	return fmt.Sprintf("%.1f", *f)
}
<|endoftext|>


<commit_before>package service_test

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

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/sclevine/agouti/api/internal/service"
)

var _ = Describe("Service", func() {
	var service *Service

	BeforeEach(func() {
		service = &Service{
			URLTemplate: "some-url",
			CmdTemplate: []string{"true"},
		}
	})

	Describe("#URL", func() {
		Context("when the server is not running", func() {
			It("should return an empty string", func() {
				Expect(service.URL()).To(BeEmpty())
				Expect(service.Start(false)).To(Succeed())
				Expect(service.Stop()).To(Succeed())
				Expect(service.URL()).To(BeEmpty())
			})
		})

		Context("when the server is running", func() {
			It("should successfully return the URL", func() {
				defer service.Stop()
				Expect(service.Start(false)).To(Succeed())
				Expect(service.URL()).To(MatchRegexp("some-url"))
			})
		})
	})

	Describe("#Start", func() {
		Context("when the service is started multiple times", func() {
			It("should return an error indicating that service is already running", func() {
				defer service.Stop()
				Expect(service.Start(false)).To(Succeed())
				Expect(service.Start(false)).To(MatchError("already running"))
			})
		})

		Context("when the binary is not available in PATH", func() {
			It("should return an error indicating the binary needs to be installed", func() {
				service.CmdTemplate = []string{"not-in-path"}
				Expect(service.Start(false)).To(MatchError("failed to run command: exec: \"not-in-path\": executable file not found in $PATH"))
			})
		})

		Context("when the service is started in debug mode", func() {
			It("should successfully start", func() {
				defer service.Stop()
				Expect(service.Start(true)).To(Succeed())
			})
		})

		Describe("the provided templated URL", func() {
			Context("when the template is invalid", func() {
				It("should return an error", func() {
					defer service.Stop()
					service.URLTemplate = "{{}}"
					Expect(service.Start(false)).To(MatchError("failed to parse URL: template: URL:1: missing value for command"))
				})
			})

			Context("when the template does not match the provided parameters", func() {
				It("should return an error", func() {
					defer service.Stop()
					service.URLTemplate = "{{.Bad}}"
					Expect(service.Start(false)).To(MatchError("failed to parse URL: template: URL:1:2: executing \"URL\" at <.Bad>: Bad is not a field of struct type service.addressInfo"))
				})
			})

			Context("when the template is valid", func() {
				It("should store a templated URL", func() {
					defer service.Stop()
					service.URLTemplate += "/status?test&{{.Address}}&{{.Host}}:{{.Port}}"
					service.Start(false)
					Expect(service.URL()).To(MatchRegexp(`test&127\.0\.0\.1:\d+&127\.0\.0\.1:\d+`))
				})
			})
		})

		Describe("the provided templated command", func() {
			Context("when the template is invalid", func() {
				It("should return an error", func() {
					defer service.Stop()
					service.CmdTemplate = []string{"correct", "{{}}"}
					Expect(service.Start(false)).To(MatchError("failed to parse command: template: command:1: missing value for command"))
				})
			})

			Context("when the template does not match the provided parameters", func() {
				It("should return an error", func() {
					defer service.Stop()
					service.CmdTemplate = []string{"correct", "{{.Bad}}"}
					Expect(service.Start(false)).To(MatchError("failed to parse command: template: command:1:2: executing \"command\" at <.Bad>: Bad is not a field of struct type service.addressInfo"))
				})
			})

			Context("when the template is empty", func() {
				It("should return an error", func() {
					defer service.Stop()
					service.CmdTemplate = []string{}
					Expect(service.Start(false)).To(MatchError("failed to parse command: empty command"))
				})
			})

			Context("when the template is valid", func() {
				It("should not return an error", func() {
					defer service.Stop()
					service.CmdTemplate = []string{"true", "{{.Address}}{{.Host}}{{.Port}}"}
					Expect(service.Start(false)).To(Succeed())
				})
			})
		})
	})

	Describe("#Stop", func() {
		It("should stop a running server", func() {
			defer service.Stop()
			Expect(service.Start(false)).To(Succeed())
			Expect(service.Stop()).To(Succeed())
			Expect(service.Start(false)).To(Succeed())
		})

		Context("when the command is not started", func() {
			It("should return an error", func() {
				err := service.Stop()
				Expect(err).To(MatchError("already stopped"))
			})
		})
	})

	Describe("#WaitForBoot", func() {
		var (
			started bool
			server  *httptest.Server
		)

		BeforeEach(func() {
			started = false

			server = httptest.NewServer(http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
				if started && request.URL.Path == "/status" {
					response.WriteHeader(200)
				} else {
					response.WriteHeader(400)
				}
			}))

			service.URLTemplate = server.URL
		})

		AfterEach(func() {
			server.Close()
		})

		Context("when the service does not start before the provided timeout", func() {
			It("should return an error", func() {
				defer service.Stop()
				go func() {
					time.Sleep(3000 * time.Millisecond)
					started = true
				}()
				Expect(service.Start(false)).To(Succeed())
				Expect(service.WaitForBoot(1500 * time.Millisecond)).To(MatchError("failed to start before timeout"))
			})
		})

		Context("when the service starts before the provided timeout", func() {
			It("should not return an error", func() {
				defer service.Stop()
				go func() {
					time.Sleep(200 * time.Millisecond)
					started = true
				}()
				Expect(service.Start(false)).To(Succeed())
				Expect(service.WaitForBoot(1500 * time.Millisecond)).To(Succeed())
			})
		})
	})
})
<commit_msg>make tests pass<commit_after>package service_test

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

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/sclevine/agouti/api/internal/service"
)

var _ = Describe("Service", func() {
	var service *Service

	BeforeEach(func() {
		service = &Service{
			URLTemplate: "some-url",
			CmdTemplate: []string{"true"},
		}
	})

	Describe("#URL", func() {
		Context("when the server is not running", func() {
			It("should return an empty string", func() {
				Expect(service.URL()).To(BeEmpty())
				Expect(service.Start(false)).To(Succeed())
				Expect(service.Stop()).To(Succeed())
				Expect(service.URL()).To(BeEmpty())
			})
		})

		Context("when the server is running", func() {
			It("should successfully return the URL", func() {
				defer service.Stop()
				Expect(service.Start(false)).To(Succeed())
				Expect(service.URL()).To(MatchRegexp("some-url"))
			})
		})
	})

	Describe("#Start", func() {
		Context("when the service is started multiple times", func() {
			It("should return an error indicating that service is already running", func() {
				defer service.Stop()
				Expect(service.Start(false)).To(Succeed())
				Expect(service.Start(false)).To(MatchError("already running"))
			})
		})

		Context("when the binary is not available in PATH", func() {
			It("should return an error indicating the binary needs to be installed", func() {
				service.CmdTemplate = []string{"not-in-path"}
				Expect(service.Start(false)).To(MatchError("failed to run command: exec: \"not-in-path\": executable file not found in $PATH"))
			})
		})

		Context("when the service is started in debug mode", func() {
			It("should successfully start", func() {
				defer service.Stop()
				Expect(service.Start(true)).To(Succeed())
			})
		})

		Describe("the provided templated URL", func() {
			Context("when the template is invalid", func() {
				It("should return an error", func() {
					defer service.Stop()
					service.URLTemplate = "{{}}"
					Expect(service.Start(false)).To(MatchError("failed to parse URL: template: URL:1: missing value for command"))
				})
			})

			Context("when the template does not match the provided parameters", func() {
				It("should return an error", func() {
					defer service.Stop()
					service.URLTemplate = "{{.Bad}}"
					Expect(service.Start(false)).To(Equal(fmt.Errorf("failed to parse URL: template: URL:1:2: executing \"URL\" at <.Bad>: can't evaluate field Bad in type service.addressInfo")))
				})
			})

			Context("when the template is valid", func() {
				It("should store a templated URL", func() {
					defer service.Stop()
					service.URLTemplate += "/status?test&{{.Address}}&{{.Host}}:{{.Port}}"
					service.Start(false)
					Expect(service.URL()).To(MatchRegexp(`test&127\.0\.0\.1:\d+&127\.0\.0\.1:\d+`))
				})
			})
		})

		Describe("the provided templated command", func() {
			Context("when the template is invalid", func() {
				It("should return an error", func() {
					defer service.Stop()
					service.CmdTemplate = []string{"correct", "{{}}"}
					Expect(service.Start(false)).To(MatchError("failed to parse command: template: command:1: missing value for command"))
				})
			})

			Context("when the template does not match the provided parameters", func() {
				It("should return an error", func() {
					defer service.Stop()
					service.CmdTemplate = []string{"correct", "{{.Bad}}"}
					Expect(service.Start(false)).To(Equal(fmt.Errorf("failed to parse command: template: command:1:2: executing \"command\" at <.Bad>: can't evaluate field Bad in type service.addressInfo")))
				})
			})

			Context("when the template is empty", func() {
				It("should return an error", func() {
					defer service.Stop()
					service.CmdTemplate = []string{}
					Expect(service.Start(false)).To(MatchError("failed to parse command: empty command"))
				})
			})

			Context("when the template is valid", func() {
				It("should not return an error", func() {
					defer service.Stop()
					service.CmdTemplate = []string{"true", "{{.Address}}{{.Host}}{{.Port}}"}
					Expect(service.Start(false)).To(Succeed())
				})
			})
		})
	})

	Describe("#Stop", func() {
		It("should stop a running server", func() {
			defer service.Stop()
			Expect(service.Start(false)).To(Succeed())
			Expect(service.Stop()).To(Succeed())
			Expect(service.Start(false)).To(Succeed())
		})

		Context("when the command is not started", func() {
			It("should return an error", func() {
				err := service.Stop()
				Expect(err).To(MatchError("already stopped"))
			})
		})
	})

	Describe("#WaitForBoot", func() {
		var (
			started bool
			server  *httptest.Server
		)

		BeforeEach(func() {
			started = false

			server = httptest.NewServer(http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
				if started && request.URL.Path == "/status" {
					response.WriteHeader(200)
				} else {
					response.WriteHeader(400)
				}
			}))

			service.URLTemplate = server.URL
		})

		AfterEach(func() {
			server.Close()
		})

		Context("when the service does not start before the provided timeout", func() {
			It("should return an error", func() {
				defer service.Stop()
				go func() {
					time.Sleep(3000 * time.Millisecond)
					started = true
				}()
				Expect(service.Start(false)).To(Succeed())
				Expect(service.WaitForBoot(1500 * time.Millisecond)).To(MatchError("failed to start before timeout"))
			})
		})

		Context("when the service starts before the provided timeout", func() {
			It("should not return an error", func() {
				defer service.Stop()
				go func() {
					time.Sleep(200 * time.Millisecond)
					started = true
				}()
				Expect(service.Start(false)).To(Succeed())
				Expect(service.WaitForBoot(1500 * time.Millisecond)).To(Succeed())
			})
		})
	})
})
<|endoftext|>


<commit_before>package mocks

import (
	"fmt"
	"reflect"

	"github.com/konjoot/reeky/errors"
)

type ResourceMock struct {
	F       interface{}
	Invalid bool

	created bool
}

func (r *ResourceMock) Created() bool {
	return r.created
}

func (r *ResourceMock) BindedWith(f interface{}) (binded bool) {
	var (
		name string
		val  interface{}
	)

	if _, ok := f.(map[string]string); !ok {
		return
	}

	rForm := reflect.ValueOf(r.F).Elem()

	for name, val = range f.(map[string]string) {
		if field := rForm.FieldByName(name); field.IsValid() && field.Interface() == val {
			continue
		}
		return
	}

	return true
}

func (r *ResourceMock) String() string {
	return fmt.Sprintf("ResourceMock{Invalid: %t, created: %t, Form: %#v}", r.Invalid, r.created, r.F)
}

func (r *ResourceMock) Url() string {
	return "some/url"
}

func (r *ResourceMock) Form() interface{} {
	return r.F
}

func (r *ResourceMock) Save() (e error) {
	if r.Invalid {
		e = errors.NewConflictError()
		return
	}

	r.created = true

	return
}
<commit_msg>ResourseMock updated for Get-handler tests<commit_after>package mocks

import (
	"fmt"
	"reflect"

	"github.com/konjoot/reeky/errors"
)

type ResourceMock struct {
	F        interface{}
	V        interface{}
	Invalid  bool
	NotFound bool

	created  bool
	findedBy string
}

func (r *ResourceMock) Created() bool {
	return r.created
}

func (r *ResourceMock) BindedWith(f interface{}) (binded bool) {
	var (
		name string
		val  interface{}
	)

	if _, ok := f.(map[string]string); !ok {
		return
	}

	rForm := reflect.ValueOf(r.F).Elem()

	for name, val = range f.(map[string]string) {
		if field := rForm.FieldByName(name); field.IsValid() && field.Interface() == val {
			continue
		}
		return
	}

	return true
}

func (r *ResourceMock) String() string {
	return fmt.Sprintf("ResourceMock{Invalid: %t, created: %t, findedBy: %#v, Form: %#v, View: %#v}", r.Invalid, r.created, r.findedBy, r.F, r.V)
}

func (r *ResourceMock) Url() string {
	return "some/url"
}

func (r *ResourceMock) Form() interface{} {
	return r.F
}

func (r *ResourceMock) Save() (e error) {
	if r.Invalid {
		e = errors.NewConflictError()
		return
	}

	r.created = true

	return
}

func (r *ResourceMock) FindedBy() string {
	return r.findedBy
}

func (r *ResourceMock) Finded() bool {
	return r.findedBy != ""
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/spf13/cobra"
	"io/ioutil"
	"log"
)

var listCmd = &cobra.Command{
	Use:   "list",
	Short: "list collection of resources from Pivotal metadata",
	Long:  "list a collection of resources from Pivotal metadata files downloaded from pivnet",
	Run:   nil,
}

var listStemcellsCmd = &cobra.Command{
	Use:   "stemcells",
	Short: "list all stemcells",
	Long:  "list all stemcells referenced in Pivotal metadata",
	Run:   listStemcells,
}

var displayUrl bool

func init() {
	listCmd.AddCommand(listStemcellsCmd)

	listStemcellsCmd.Flags().BoolVarP(&displayUrl, "url", "u", false, "Display URL used to download stemcell")
}

func listStemcells(cmd *cobra.Command, args []string) {
	filename := fileName
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		log.Fatal(err)
	}
	var meta PivotalMetadata
	if err := meta.Parse(data); err != nil {
		log.Fatal(err)
	}
	v := meta.Stemcell_Criteria.Version

	if displayUrl {
		// fmt.Printf("https://bosh.io/d/stemcells/bosh-aws-xen-ubuntu-trusty-go_agent?v=%v\n", v) // bosh.io has no param to request full stemcells
		fmt.Printf("https://d26ekeud912fhb.cloudfront.net/bosh-stemcell/aws/bosh-stemcell-%v-aws-xen-ubuntu-trusty-go_agent.tgz", v)
	} else {
		fmt.Printf("bosh-stemcell-%v-aws-xen-ubuntu-trusty-go_agent.tgz\n", v)
	}
}
<commit_msg>now adding newline to stemcell url listings<commit_after>package main

import (
	"fmt"
	"github.com/spf13/cobra"
	"io/ioutil"
	"log"
)

var listCmd = &cobra.Command{
	Use:   "list",
	Short: "list collection of resources from Pivotal metadata",
	Long:  "list a collection of resources from Pivotal metadata files downloaded from pivnet",
	Run:   nil,
}

var listStemcellsCmd = &cobra.Command{
	Use:   "stemcells",
	Short: "list all stemcells",
	Long:  "list all stemcells referenced in Pivotal metadata",
	Run:   listStemcells,
}

var displayUrl bool

func init() {
	listCmd.AddCommand(listStemcellsCmd)

	listStemcellsCmd.Flags().BoolVarP(&displayUrl, "url", "u", false, "Display URL used to download stemcell")
}

func listStemcells(cmd *cobra.Command, args []string) {
	filename := fileName
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		log.Fatal(err)
	}
	var meta PivotalMetadata
	if err := meta.Parse(data); err != nil {
		log.Fatal(err)
	}
	v := meta.Stemcell_Criteria.Version

	if displayUrl {
		// fmt.Printf("https://bosh.io/d/stemcells/bosh-aws-xen-ubuntu-trusty-go_agent?v=%v\n", v) // bosh.io has no param to request full stemcells
		fmt.Printf("https://d26ekeud912fhb.cloudfront.net/bosh-stemcell/aws/bosh-stemcell-%v-aws-xen-ubuntu-trusty-go_agent.tgz\n", v)
	} else {
		fmt.Printf("bosh-stemcell-%v-aws-xen-ubuntu-trusty-go_agent.tgz\n", v)
	}
}
<|endoftext|>


<commit_before>// +build appengine

package runtimeutil

// This is a repr implementation without using unsafe. It
// relies on the source code being present, otherwise it
// doesn't work.

import (
	"debug/gosym"
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"strconv"
	"strings"
)

func pointerRepr(val uint64, _ *gosym.Sym, _ bool) string {
	if val == 0 {
		return "= nil"
	}
	return "@ 0x" + strconv.FormatUint(val, 16)
}

func stringRepr(val1 uint64, val2 uint64) string {
	r := pointerRepr(val1, nil, false)
	if val1 != 0 {
		r += fmt.Sprintf(" (length = %d)", val2)
	}
	return r
}

func emptyInterfaceRepr(val1 uint64, val2 uint64) string {
	return pointerRepr(val2, nil, false)
}

func astTypeName(typ ast.Expr, pkg string) string {
	switch x := typ.(type) {
	case *ast.StarExpr:
		return "*" + astTypeName(x.X, pkg)
	case *ast.Ident:
		if basicType(x.Name) || pkg == "" {
			return x.Name
		}
		return pkg + "." + x.Name
	case *ast.SelectorExpr:
		return x.X.(*ast.Ident).Name + "." + x.Sel.Name
	case *ast.InterfaceType:
		return "interface {}"
	case *ast.MapType:
		return fmt.Sprintf("map[%s]%s", astTypeName(x.Key, pkg), astTypeName(x.Value, pkg))
	default:
		panic(fmt.Errorf("unhandled ast.Expr %T", typ))
	}
	return ""
}

func typeName(table *gosym.Table, fn *gosym.Func, s *gosym.Sym) string {
	// Get the argument position
	pos := -1
	for ii, v := range fn.Params {
		if v == s {
			pos = len(fn.Params) - ii - 1
			break
		}
	}
	if pos < 0 {
		return ""
	}
	file, _, _ := table.PCToLine(fn.Entry)
	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, file, nil, 0)
	if err == nil {
		var receiver string
		dot := strings.IndexByte(fn.Name, '.')
		name := fn.Name[dot+1:]
		pkg := fn.Name[:dot]
		if s := strings.LastIndex(pkg, "/"); s >= 0 {
			pkg = pkg[s+1:]
		}
		if ndot := strings.IndexByte(name, '.'); ndot >= 0 {
			receiver = strings.Trim(name[:ndot], "()")
			name = name[ndot+1:]
		}
		var fun *ast.FuncDecl
		ast.Inspect(f, func(n ast.Node) bool {
			if af, ok := n.(*ast.FuncDecl); ok && af.Name.Name == name {
				if receiver == "" || (af.Recv != nil && astTypeName(af.Recv.List[0].Type, "") == receiver) {
					fun = af
					return false
				}
			}
			return true
		})
		if fun != nil {
			ii := 0
			if receiver != "" {
				if pos == 0 {
					return astTypeName(fun.Recv.List[0].Type, pkg)
				}
				ii++
			}
			for _, v := range fun.Type.Params.List {
				c := len(v.Names)
				if ii+c > pos {
					return astTypeName(v.Type, pkg)
				}
				ii += c
			}
		}
	}
	return ""
}

func isInterface(table *gosym.Table, fn *gosym.Func, s *gosym.Sym, tn string) bool {
	// TODO: Use go/build to parse the package and look for the type definution
	return false
}
<commit_msg>Handle *ast.ArrayType when generating stack reprs in safe mode<commit_after>// +build appengine

package runtimeutil

// This is a repr implementation without using unsafe. It
// relies on the source code being present, otherwise it
// doesn't work.

import (
	"debug/gosym"
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"strconv"
	"strings"
)

func pointerRepr(val uint64, _ *gosym.Sym, _ bool) string {
	if val == 0 {
		return "= nil"
	}
	return "@ 0x" + strconv.FormatUint(val, 16)
}

func stringRepr(val1 uint64, val2 uint64) string {
	r := pointerRepr(val1, nil, false)
	if val1 != 0 {
		r += fmt.Sprintf(" (length = %d)", val2)
	}
	return r
}

func emptyInterfaceRepr(val1 uint64, val2 uint64) string {
	return pointerRepr(val2, nil, false)
}

func astTypeName(typ ast.Expr, pkg string) string {
	switch x := typ.(type) {
	case *ast.StarExpr:
		return "*" + astTypeName(x.X, pkg)
	case *ast.Ident:
		if basicType(x.Name) || pkg == "" {
			return x.Name
		}
		return pkg + "." + x.Name
	case *ast.SelectorExpr:
		return x.X.(*ast.Ident).Name + "." + x.Sel.Name
	case *ast.InterfaceType:
		return "interface {}"
	case *ast.MapType:
		return fmt.Sprintf("map[%s]%s", astTypeName(x.Key, pkg), astTypeName(x.Value, pkg))
	case *ast.ArrayType:
		return fmt.Sprintf("[]%s", astTypeName(x.Elt, pkg))
	default:
		panic(fmt.Errorf("unhandled ast.Expr %T", typ))
	}
	return ""
}

func typeName(table *gosym.Table, fn *gosym.Func, s *gosym.Sym) string {
	// Get the argument position
	pos := -1
	for ii, v := range fn.Params {
		if v == s {
			pos = len(fn.Params) - ii - 1
			break
		}
	}
	if pos < 0 {
		return ""
	}
	file, _, _ := table.PCToLine(fn.Entry)
	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, file, nil, 0)
	if err == nil {
		var receiver string
		dot := strings.IndexByte(fn.Name, '.')
		name := fn.Name[dot+1:]
		pkg := fn.Name[:dot]
		if s := strings.LastIndex(pkg, "/"); s >= 0 {
			pkg = pkg[s+1:]
		}
		if ndot := strings.IndexByte(name, '.'); ndot >= 0 {
			receiver = strings.Trim(name[:ndot], "()")
			name = name[ndot+1:]
		}
		var fun *ast.FuncDecl
		ast.Inspect(f, func(n ast.Node) bool {
			if af, ok := n.(*ast.FuncDecl); ok && af.Name.Name == name {
				if receiver == "" || (af.Recv != nil && astTypeName(af.Recv.List[0].Type, "") == receiver) {
					fun = af
					return false
				}
			}
			return true
		})
		if fun != nil {
			ii := 0
			if receiver != "" {
				if pos == 0 {
					return astTypeName(fun.Recv.List[0].Type, pkg)
				}
				ii++
			}
			for _, v := range fun.Type.Params.List {
				c := len(v.Names)
				if ii+c > pos {
					return astTypeName(v.Type, pkg)
				}
				ii += c
			}
		}
	}
	return ""
}

func isInterface(table *gosym.Table, fn *gosym.Func, s *gosym.Sym, tn string) bool {
	// TODO: Use go/build to parse the package and look for the type definution
	return false
}
<|endoftext|>


<commit_before>// mongodb_exporter
// Copyright (C) 2017 Percona LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

package exporter

import (
	"context"
	"fmt"
	"sort"
	"strings"
	"testing"
	"time"

	"github.com/percona/exporter_shared/helpers"
	"github.com/pkg/errors"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/testutil"
	"github.com/sirupsen/logrus"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"

	"github.com/percona/mongodb_exporter/internal/tu"
)

func TestDiagnosticDataCollector(t *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
	defer cancel()

	client := tu.DefaultTestClient(ctx, t)
	logger := logrus.New()
	ti := labelsGetterMock{}

	c := &diagnosticDataCollector{
		client:       client,
		logger:       logger,
		topologyInfo: ti,
	}

	// The last \n at the end of this string is important
	expected := strings.NewReader(`
# HELP mongodb_oplog_stats_ok local.oplog.rs.stats.
# TYPE mongodb_oplog_stats_ok untyped
mongodb_oplog_stats_ok 1
# HELP mongodb_oplog_stats_wt_btree_fixed_record_size local.oplog.rs.stats.wiredTiger.btree.
# TYPE mongodb_oplog_stats_wt_btree_fixed_record_size untyped
mongodb_oplog_stats_wt_btree_fixed_record_size 0
# HELP mongodb_oplog_stats_wt_transaction_update_conflicts local.oplog.rs.stats.wiredTiger.transaction.
# TYPE mongodb_oplog_stats_wt_transaction_update_conflicts untyped
mongodb_oplog_stats_wt_transaction_update_conflicts 0` + "\n")
	// Filter metrics for 2 reasons:
	// 1. The result is huge
	// 2. We need to check against know values. Don't use metrics that return counters like uptime
	//    or counters like the number of transactions because they won't return a known value to compare
	filter := []string{
		"mongodb_oplog_stats_ok",
		"mongodb_oplog_stats_wt_btree_fixed_record_size",
		"mongodb_oplog_stats_wt_transaction_update_conflicts",
	}
	// TODO: use NewPedanticRegistry when mongodb_exporter code fulfils its requirements (https://jira.percona.com/browse/PMM-6630).
	reg := prometheus.NewRegistry()
	err := reg.Register(c)
	require.NoError(t, err)
	err = testutil.GatherAndCompare(reg, expected, filter...)
	assert.NoError(t, err)
}

func TestAllDiagnosticDataCollectorMetrics(t *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
	defer cancel()

	client := tu.DefaultTestClient(ctx, t)

	ti, err := newTopologyInfo(ctx, client)
	require.NoError(t, err)

	c := &diagnosticDataCollector{
		client:         client,
		logger:         logrus.New(),
		compatibleMode: true,
		topologyInfo:   ti,
	}

	metrics := helpers.CollectMetrics(c)
	actualMetrics := helpers.ReadMetrics(metrics)
	filters := []string{
		"mongodb_mongod_metrics_cursor_open",
		"mongodb_mongod_metrics_get_last_error_wtimeouts_total",
		"mongodb_mongod_wiredtiger_cache_bytes",
		"mongodb_mongod_wiredtiger_transactions_total",
		"mongodb_mongod_wiredtiger_cache_bytes_total",
		"mongodb_op_counters_total",
		"mongodb_ss_mem_resident",
		"mongodb_ss_mem_virtual",
		"mongodb_ss_metrics_cursor_open",
		"mongodb_ss_metrics_getLastError_wtime_totalMillis",
		"mongodb_ss_opcounters",
		"mongodb_ss_opcountersRepl",
		"mongodb_ss_wt_cache_maximum_bytes_configured",
		"mongodb_ss_wt_cache_modified_pages_evicted",
	}
	actualMetrics = filterMetrics(actualMetrics, filters)
	actualLines := helpers.Format(helpers.WriteMetrics(actualMetrics))
	metricNames := getMetricNames(actualLines)

	sort.Strings(filters)
	for _, want := range filters {
		assert.True(t, metricNames[want], fmt.Sprintf("missing %q metric", want))
	}
}

func TestContextTimeout(t *testing.T) {
	ctx := context.Background()

	client := tu.DefaultTestClient(ctx, t)

	ti, err := newTopologyInfo(ctx, client)
	require.NoError(t, err)

	dbCount := 1000

	err = addTestData(ctx, client, dbCount)
	assert.NoError(t, err)

	defer cleanTestData(ctx, client, dbCount) //nolint:errcheck

	cctx, ccancel := context.WithTimeout(ctx, 3*time.Millisecond)
	defer ccancel()

	c := &diagnosticDataCollector{
		client:         client,
		logger:         logrus.New(),
		compatibleMode: true,
		topologyInfo:   ti,
		ctx:            cctx,
	}
	metrics := helpers.CollectMetrics(c)
	assert.True(t, len(metrics) > 0)
}

func addTestData(ctx context.Context, client *mongo.Client, count int) error {
	session, err := client.StartSession()
	if err != nil {
		return errors.Wrap(err, "cannot create session to add test data")
	}

	if err := session.StartTransaction(); err != nil {
		return errors.Wrap(err, "cannot start session to add test data")
	}

	if err = mongo.WithSession(ctx, session, func(sc mongo.SessionContext) error {
		for i := 0; i < count; i++ {
			dbName := fmt.Sprintf("testdb_%06d", i)
			doc := bson.D{{Key: "field1", Value: "value 1"}}
			_, err := client.Database(dbName).Collection("test_col").InsertOne(ctx, doc)
			if err != nil {
				return errors.Wrap(err, "cannot add test data")
			}
		}

		if err = session.CommitTransaction(sc); err != nil {
			return errors.Wrap(err, "cannot commit add test data transaction")
		}

		return nil
	}); err != nil {
		return errors.Wrap(err, "cannot add data inside a session")
	}

	session.EndSession(ctx)

	return nil
}

func cleanTestData(ctx context.Context, client *mongo.Client, count int) error {
	session, err := client.StartSession()
	if err != nil {
		return errors.Wrap(err, "cannot create session to add test data")
	}

	if err := session.StartTransaction(); err != nil {
		return errors.Wrap(err, "cannot start session to add test data")
	}

	if err = mongo.WithSession(ctx, session, func(sc mongo.SessionContext) error {
		for i := 0; i < count; i++ {
			dbName := fmt.Sprintf("testdb_%06d", i)
			client.Database(dbName).Drop(ctx) //nolint:errcheck
		}

		if err = session.CommitTransaction(sc); err != nil {
			return errors.Wrap(err, "cannot commit add test data transaction")
		}

		return nil
	}); err != nil {
		return errors.Wrap(err, "cannot add data inside a session")
	}

	session.EndSession(ctx)

	return nil
}
<commit_msg>PMM-8849 Updated test for GH Actions<commit_after>// mongodb_exporter
// Copyright (C) 2017 Percona LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

package exporter

import (
	"context"
	"fmt"
	"sort"
	"strings"
	"testing"
	"time"

	"github.com/percona/exporter_shared/helpers"
	"github.com/pkg/errors"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/testutil"
	"github.com/sirupsen/logrus"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"

	"github.com/percona/mongodb_exporter/internal/tu"
)

func TestDiagnosticDataCollector(t *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
	defer cancel()

	client := tu.DefaultTestClient(ctx, t)
	logger := logrus.New()
	ti := labelsGetterMock{}

	c := &diagnosticDataCollector{
		client:       client,
		logger:       logger,
		topologyInfo: ti,
	}

	// The last \n at the end of this string is important
	expected := strings.NewReader(`
# HELP mongodb_oplog_stats_ok local.oplog.rs.stats.
# TYPE mongodb_oplog_stats_ok untyped
mongodb_oplog_stats_ok 1
# HELP mongodb_oplog_stats_wt_btree_fixed_record_size local.oplog.rs.stats.wiredTiger.btree.
# TYPE mongodb_oplog_stats_wt_btree_fixed_record_size untyped
mongodb_oplog_stats_wt_btree_fixed_record_size 0
# HELP mongodb_oplog_stats_wt_transaction_update_conflicts local.oplog.rs.stats.wiredTiger.transaction.
# TYPE mongodb_oplog_stats_wt_transaction_update_conflicts untyped
mongodb_oplog_stats_wt_transaction_update_conflicts 0` + "\n")
	// Filter metrics for 2 reasons:
	// 1. The result is huge
	// 2. We need to check against know values. Don't use metrics that return counters like uptime
	//    or counters like the number of transactions because they won't return a known value to compare
	filter := []string{
		"mongodb_oplog_stats_ok",
		"mongodb_oplog_stats_wt_btree_fixed_record_size",
		"mongodb_oplog_stats_wt_transaction_update_conflicts",
	}
	// TODO: use NewPedanticRegistry when mongodb_exporter code fulfils its requirements (https://jira.percona.com/browse/PMM-6630).
	reg := prometheus.NewRegistry()
	err := reg.Register(c)
	require.NoError(t, err)
	err = testutil.GatherAndCompare(reg, expected, filter...)
	assert.NoError(t, err)
}

func TestAllDiagnosticDataCollectorMetrics(t *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
	defer cancel()

	client := tu.DefaultTestClient(ctx, t)

	ti, err := newTopologyInfo(ctx, client)
	require.NoError(t, err)

	c := &diagnosticDataCollector{
		client:         client,
		logger:         logrus.New(),
		compatibleMode: true,
		topologyInfo:   ti,
	}

	metrics := helpers.CollectMetrics(c)
	actualMetrics := helpers.ReadMetrics(metrics)
	filters := []string{
		"mongodb_mongod_metrics_cursor_open",
		"mongodb_mongod_metrics_get_last_error_wtimeouts_total",
		"mongodb_mongod_wiredtiger_cache_bytes",
		"mongodb_mongod_wiredtiger_transactions_total",
		"mongodb_mongod_wiredtiger_cache_bytes_total",
		"mongodb_op_counters_total",
		"mongodb_ss_mem_resident",
		"mongodb_ss_mem_virtual",
		"mongodb_ss_metrics_cursor_open",
		"mongodb_ss_metrics_getLastError_wtime_totalMillis",
		"mongodb_ss_opcounters",
		"mongodb_ss_opcountersRepl",
		"mongodb_ss_wt_cache_maximum_bytes_configured",
		"mongodb_ss_wt_cache_modified_pages_evicted",
	}
	actualMetrics = filterMetrics(actualMetrics, filters)
	actualLines := helpers.Format(helpers.WriteMetrics(actualMetrics))
	metricNames := getMetricNames(actualLines)

	sort.Strings(filters)
	for _, want := range filters {
		assert.True(t, metricNames[want], fmt.Sprintf("missing %q metric", want))
	}
}

func TestContextTimeout(t *testing.T) {
	ctx := context.Background()

	client := tu.DefaultTestClient(ctx, t)

	ti, err := newTopologyInfo(ctx, client)
	require.NoError(t, err)

	dbCount := 1000

	err = addTestData(ctx, client, dbCount)
	assert.NoError(t, err)

	defer cleanTestData(ctx, client, dbCount) //nolint:errcheck

	cctx, ccancel := context.WithTimeout(ctx, 3*time.Minute)
	defer ccancel()

	c := &diagnosticDataCollector{
		client:         client,
		logger:         logrus.New(),
		compatibleMode: true,
		topologyInfo:   ti,
		ctx:            cctx,
	}
	metrics := helpers.CollectMetrics(c)
	assert.True(t, len(metrics) > 0)
}

func addTestData(ctx context.Context, client *mongo.Client, count int) error {
	session, err := client.StartSession()
	if err != nil {
		return errors.Wrap(err, "cannot create session to add test data")
	}

	if err := session.StartTransaction(); err != nil {
		return errors.Wrap(err, "cannot start session to add test data")
	}

	if err = mongo.WithSession(ctx, session, func(sc mongo.SessionContext) error {
		for i := 0; i < count; i++ {
			dbName := fmt.Sprintf("testdb_%06d", i)
			doc := bson.D{{Key: "field1", Value: "value 1"}}
			_, err := client.Database(dbName).Collection("test_col").InsertOne(ctx, doc)
			if err != nil {
				return errors.Wrap(err, "cannot add test data")
			}
		}

		if err = session.CommitTransaction(sc); err != nil {
			return errors.Wrap(err, "cannot commit add test data transaction")
		}

		return nil
	}); err != nil {
		return errors.Wrap(err, "cannot add data inside a session")
	}

	session.EndSession(ctx)

	return nil
}

func cleanTestData(ctx context.Context, client *mongo.Client, count int) error {
	session, err := client.StartSession()
	if err != nil {
		return errors.Wrap(err, "cannot create session to add test data")
	}

	if err := session.StartTransaction(); err != nil {
		return errors.Wrap(err, "cannot start session to add test data")
	}

	if err = mongo.WithSession(ctx, session, func(sc mongo.SessionContext) error {
		for i := 0; i < count; i++ {
			dbName := fmt.Sprintf("testdb_%06d", i)
			client.Database(dbName).Drop(ctx) //nolint:errcheck
		}

		if err = session.CommitTransaction(sc); err != nil {
			return errors.Wrap(err, "cannot commit add test data transaction")
		}

		return nil
	}); err != nil {
		return errors.Wrap(err, "cannot add data inside a session")
	}

	session.EndSession(ctx)

	return nil
}
<|endoftext|>


<commit_before>package terraform

import (
	"sync"

	"github.com/hashicorp/terraform/addrs"
	"github.com/hashicorp/terraform/lang"

	"github.com/hashicorp/hcl2/hcl"
	"github.com/hashicorp/terraform/config/configschema"
	"github.com/hashicorp/terraform/tfdiags"
	"github.com/zclconf/go-cty/cty"

	"github.com/hashicorp/terraform/config"
)

// MockEvalContext is a mock version of EvalContext that can be used
// for tests.
type MockEvalContext struct {
	StoppedCalled bool
	StoppedValue  <-chan struct{}

	HookCalled bool
	HookHook   Hook
	HookError  error

	InputCalled bool
	InputInput  UIInput

	InitProviderCalled   bool
	InitProviderType     string
	InitProviderAddr     addrs.ProviderConfig
	InitProviderProvider ResourceProvider
	InitProviderError    error

	ProviderCalled   bool
	ProviderAddr     addrs.AbsProviderConfig
	ProviderProvider ResourceProvider

	ProviderSchemaCalled bool
	ProviderSchemaAddr   addrs.AbsProviderConfig
	ProviderSchemaSchema *ProviderSchema

	CloseProviderCalled   bool
	CloseProviderAddr     addrs.ProviderConfig
	CloseProviderProvider ResourceProvider

	ProviderInputCalled bool
	ProviderInputAddr   addrs.ProviderConfig
	ProviderInputValues map[string]cty.Value

	SetProviderInputCalled bool
	SetProviderInputAddr   addrs.ProviderConfig
	SetProviderInputValues map[string]cty.Value

	ConfigureProviderCalled bool
	ConfigureProviderAddr   addrs.ProviderConfig
	ConfigureProviderConfig cty.Value
	ConfigureProviderDiags  tfdiags.Diagnostics

	InitProvisionerCalled      bool
	InitProvisionerName        string
	InitProvisionerProvisioner ResourceProvisioner
	InitProvisionerError       error

	ProvisionerCalled      bool
	ProvisionerName        string
	ProvisionerProvisioner ResourceProvisioner

	ProvisionerSchemaCalled bool
	ProvisionerSchemaName   string
	ProvisionerSchemaSchema *configschema.Block

	CloseProvisionerCalled      bool
	CloseProvisionerName        string
	CloseProvisionerProvisioner ResourceProvisioner

	EvaluateBlockCalled       bool
	EvaluateBlockBody         hcl.Body
	EvaluateBlockSchema       *configschema.Block
	EvaluateBlockSelf         addrs.Referenceable
	EvaluateBlockKey          addrs.InstanceKey
	EvaluateBlockResult       cty.Value
	EvaluateBlockExpandedBody hcl.Body
	EvaluateBlockDiags        tfdiags.Diagnostics

	EvaluateExprCalled   bool
	EvaluateExprExpr     hcl.Expression
	EvaluateExprWantType cty.Type
	EvaluateExprSelf     addrs.Referenceable
	EvaluateExprResult   cty.Value
	EvaluateExprDiags    tfdiags.Diagnostics

	EvaluationScopeCalled bool
	EvaluationScopeSelf   addrs.Referenceable
	EvaluationScopeKey    addrs.InstanceKey
	EvaluationScopeScope  *lang.Scope

	InterpolateCalled       bool
	InterpolateConfig       *config.RawConfig
	InterpolateResource     *Resource
	InterpolateConfigResult *ResourceConfig
	InterpolateError        error

	InterpolateProviderCalled       bool
	InterpolateProviderConfig       *config.ProviderConfig
	InterpolateProviderResource     *Resource
	InterpolateProviderConfigResult *ResourceConfig
	InterpolateProviderError        error

	PathCalled bool
	PathPath   addrs.ModuleInstance

	SetModuleCallArgumentsCalled bool
	SetModuleCallArgumentsModule addrs.ModuleCallInstance
	SetModuleCallArgumentsValues map[string]cty.Value

	DiffCalled bool
	DiffDiff   *Diff
	DiffLock   *sync.RWMutex

	StateCalled bool
	StateState  *State
	StateLock   *sync.RWMutex
}

// MockEvalContext implements EvalContext
var _ EvalContext = (*MockEvalContext)(nil)

func (c *MockEvalContext) Stopped() <-chan struct{} {
	c.StoppedCalled = true
	return c.StoppedValue
}

func (c *MockEvalContext) Hook(fn func(Hook) (HookAction, error)) error {
	c.HookCalled = true
	if c.HookHook != nil {
		if _, err := fn(c.HookHook); err != nil {
			return err
		}
	}

	return c.HookError
}

func (c *MockEvalContext) Input() UIInput {
	c.InputCalled = true
	return c.InputInput
}

func (c *MockEvalContext) InitProvider(t string, addr addrs.ProviderConfig) (ResourceProvider, error) {
	c.InitProviderCalled = true
	c.InitProviderType = t
	c.InitProviderAddr = addr
	return c.InitProviderProvider, c.InitProviderError
}

func (c *MockEvalContext) Provider(addr addrs.AbsProviderConfig) ResourceProvider {
	c.ProviderCalled = true
	c.ProviderAddr = addr
	return c.ProviderProvider
}

func (c *MockEvalContext) ProviderSchema(addr addrs.AbsProviderConfig) *ProviderSchema {
	c.ProviderSchemaCalled = true
	c.ProviderSchemaAddr = addr
	return c.ProviderSchemaSchema
}

func (c *MockEvalContext) CloseProvider(addr addrs.ProviderConfig) error {
	c.CloseProviderCalled = true
	c.CloseProviderAddr = addr
	return nil
}

func (c *MockEvalContext) ConfigureProvider(addr addrs.ProviderConfig, cfg cty.Value) tfdiags.Diagnostics {
	c.ConfigureProviderCalled = true
	c.ConfigureProviderAddr = addr
	c.ConfigureProviderConfig = cfg
	return c.ConfigureProviderDiags
}

func (c *MockEvalContext) ProviderInput(addr addrs.ProviderConfig) map[string]cty.Value {
	c.ProviderInputCalled = true
	c.ProviderInputAddr = addr
	return c.ProviderInputValues
}

func (c *MockEvalContext) SetProviderInput(addr addrs.ProviderConfig, vals map[string]cty.Value) {
	c.SetProviderInputCalled = true
	c.SetProviderInputAddr = addr
	c.SetProviderInputValues = vals
}

func (c *MockEvalContext) InitProvisioner(n string) (ResourceProvisioner, error) {
	c.InitProvisionerCalled = true
	c.InitProvisionerName = n
	return c.InitProvisionerProvisioner, c.InitProvisionerError
}

func (c *MockEvalContext) Provisioner(n string) ResourceProvisioner {
	c.ProvisionerCalled = true
	c.ProvisionerName = n
	return c.ProvisionerProvisioner
}

func (c *MockEvalContext) ProvisionerSchema(n string) *configschema.Block {
	c.ProvisionerSchemaCalled = true
	c.ProvisionerSchemaName = n
	return c.ProvisionerSchemaSchema
}

func (c *MockEvalContext) CloseProvisioner(n string) error {
	c.CloseProvisionerCalled = true
	c.CloseProvisionerName = n
	return nil
}

func (c *MockEvalContext) EvaluateBlock(body hcl.Body, schema *configschema.Block, self addrs.Referenceable, key addrs.InstanceKey) (cty.Value, hcl.Body, tfdiags.Diagnostics) {
	c.EvaluateBlockCalled = true
	c.EvaluateBlockBody = body
	c.EvaluateBlockSchema = schema
	c.EvaluateBlockSelf = self
	c.EvaluateBlockKey = key
	return c.EvaluateBlockResult, c.EvaluateBlockExpandedBody, c.EvaluateBlockDiags
}

func (c *MockEvalContext) EvaluateExpr(expr hcl.Expression, wantType cty.Type, self addrs.Referenceable) (cty.Value, tfdiags.Diagnostics) {
	c.EvaluateExprCalled = true
	c.EvaluateExprExpr = expr
	c.EvaluateExprWantType = wantType
	c.EvaluateExprSelf = self
	return c.EvaluateExprResult, c.EvaluateExprDiags
}

func (c *MockEvalContext) EvaluationScope(self addrs.Referenceable, key addrs.InstanceKey) *lang.Scope {
	c.EvaluationScopeCalled = true
	c.EvaluationScopeSelf = self
	c.EvaluationScopeKey = key
	return c.EvaluationScopeScope
}

func (c *MockEvalContext) Interpolate(
	config *config.RawConfig, resource *Resource) (*ResourceConfig, error) {
	c.InterpolateCalled = true
	c.InterpolateConfig = config
	c.InterpolateResource = resource
	return c.InterpolateConfigResult, c.InterpolateError
}

func (c *MockEvalContext) InterpolateProvider(
	config *config.ProviderConfig, resource *Resource) (*ResourceConfig, error) {
	c.InterpolateProviderCalled = true
	c.InterpolateProviderConfig = config
	c.InterpolateProviderResource = resource
	return c.InterpolateProviderConfigResult, c.InterpolateError
}

func (c *MockEvalContext) Path() addrs.ModuleInstance {
	c.PathCalled = true
	return c.PathPath
}

func (c *MockEvalContext) SetModuleCallArguments(n addrs.ModuleCallInstance, values map[string]cty.Value) {
	c.SetModuleCallArgumentsCalled = true
	c.SetModuleCallArgumentsModule = n
	c.SetModuleCallArgumentsValues = values
}

func (c *MockEvalContext) Diff() (*Diff, *sync.RWMutex) {
	c.DiffCalled = true
	return c.DiffDiff, c.DiffLock
}

func (c *MockEvalContext) State() (*State, *sync.RWMutex) {
	c.StateCalled = true
	return c.StateState, c.StateLock
}
<commit_msg>core: EvalContextMock code-based mocking of evaluation<commit_after>package terraform

import (
	"sync"

	"github.com/hashicorp/hcl2/hcl"
	"github.com/hashicorp/hcl2/hcldec"
	"github.com/zclconf/go-cty/cty"
	"github.com/zclconf/go-cty/cty/convert"

	"github.com/hashicorp/terraform/addrs"
	"github.com/hashicorp/terraform/config"
	"github.com/hashicorp/terraform/config/configschema"
	"github.com/hashicorp/terraform/lang"
	"github.com/hashicorp/terraform/tfdiags"
)

// MockEvalContext is a mock version of EvalContext that can be used
// for tests.
type MockEvalContext struct {
	StoppedCalled bool
	StoppedValue  <-chan struct{}

	HookCalled bool
	HookHook   Hook
	HookError  error

	InputCalled bool
	InputInput  UIInput

	InitProviderCalled   bool
	InitProviderType     string
	InitProviderAddr     addrs.ProviderConfig
	InitProviderProvider ResourceProvider
	InitProviderError    error

	ProviderCalled   bool
	ProviderAddr     addrs.AbsProviderConfig
	ProviderProvider ResourceProvider

	ProviderSchemaCalled bool
	ProviderSchemaAddr   addrs.AbsProviderConfig
	ProviderSchemaSchema *ProviderSchema

	CloseProviderCalled   bool
	CloseProviderAddr     addrs.ProviderConfig
	CloseProviderProvider ResourceProvider

	ProviderInputCalled bool
	ProviderInputAddr   addrs.ProviderConfig
	ProviderInputValues map[string]cty.Value

	SetProviderInputCalled bool
	SetProviderInputAddr   addrs.ProviderConfig
	SetProviderInputValues map[string]cty.Value

	ConfigureProviderCalled bool
	ConfigureProviderAddr   addrs.ProviderConfig
	ConfigureProviderConfig cty.Value
	ConfigureProviderDiags  tfdiags.Diagnostics

	InitProvisionerCalled      bool
	InitProvisionerName        string
	InitProvisionerProvisioner ResourceProvisioner
	InitProvisionerError       error

	ProvisionerCalled      bool
	ProvisionerName        string
	ProvisionerProvisioner ResourceProvisioner

	ProvisionerSchemaCalled bool
	ProvisionerSchemaName   string
	ProvisionerSchemaSchema *configschema.Block

	CloseProvisionerCalled      bool
	CloseProvisionerName        string
	CloseProvisionerProvisioner ResourceProvisioner

	EvaluateBlockCalled     bool
	EvaluateBlockBody       hcl.Body
	EvaluateBlockSchema     *configschema.Block
	EvaluateBlockSelf       addrs.Referenceable
	EvaluateBlockKey        addrs.InstanceKey
	EvaluateBlockResultFunc func(
		body hcl.Body,
		schema *configschema.Block,
		self addrs.Referenceable,
		key addrs.InstanceKey,
	) (cty.Value, hcl.Body, tfdiags.Diagnostics) // overrides the other values below, if set
	EvaluateBlockResult       cty.Value
	EvaluateBlockExpandedBody hcl.Body
	EvaluateBlockDiags        tfdiags.Diagnostics

	EvaluateExprCalled     bool
	EvaluateExprExpr       hcl.Expression
	EvaluateExprWantType   cty.Type
	EvaluateExprSelf       addrs.Referenceable
	EvaluateExprResultFunc func(
		expr hcl.Expression,
		wantType cty.Type,
		self addrs.Referenceable,
	) (cty.Value, tfdiags.Diagnostics) // overrides the other values below, if set
	EvaluateExprResult cty.Value
	EvaluateExprDiags  tfdiags.Diagnostics

	EvaluationScopeCalled bool
	EvaluationScopeSelf   addrs.Referenceable
	EvaluationScopeKey    addrs.InstanceKey
	EvaluationScopeScope  *lang.Scope

	InterpolateCalled       bool
	InterpolateConfig       *config.RawConfig
	InterpolateResource     *Resource
	InterpolateConfigResult *ResourceConfig
	InterpolateError        error

	InterpolateProviderCalled       bool
	InterpolateProviderConfig       *config.ProviderConfig
	InterpolateProviderResource     *Resource
	InterpolateProviderConfigResult *ResourceConfig
	InterpolateProviderError        error

	PathCalled bool
	PathPath   addrs.ModuleInstance

	SetModuleCallArgumentsCalled bool
	SetModuleCallArgumentsModule addrs.ModuleCallInstance
	SetModuleCallArgumentsValues map[string]cty.Value

	DiffCalled bool
	DiffDiff   *Diff
	DiffLock   *sync.RWMutex

	StateCalled bool
	StateState  *State
	StateLock   *sync.RWMutex
}

// MockEvalContext implements EvalContext
var _ EvalContext = (*MockEvalContext)(nil)

func (c *MockEvalContext) Stopped() <-chan struct{} {
	c.StoppedCalled = true
	return c.StoppedValue
}

func (c *MockEvalContext) Hook(fn func(Hook) (HookAction, error)) error {
	c.HookCalled = true
	if c.HookHook != nil {
		if _, err := fn(c.HookHook); err != nil {
			return err
		}
	}

	return c.HookError
}

func (c *MockEvalContext) Input() UIInput {
	c.InputCalled = true
	return c.InputInput
}

func (c *MockEvalContext) InitProvider(t string, addr addrs.ProviderConfig) (ResourceProvider, error) {
	c.InitProviderCalled = true
	c.InitProviderType = t
	c.InitProviderAddr = addr
	return c.InitProviderProvider, c.InitProviderError
}

func (c *MockEvalContext) Provider(addr addrs.AbsProviderConfig) ResourceProvider {
	c.ProviderCalled = true
	c.ProviderAddr = addr
	return c.ProviderProvider
}

func (c *MockEvalContext) ProviderSchema(addr addrs.AbsProviderConfig) *ProviderSchema {
	c.ProviderSchemaCalled = true
	c.ProviderSchemaAddr = addr
	return c.ProviderSchemaSchema
}

func (c *MockEvalContext) CloseProvider(addr addrs.ProviderConfig) error {
	c.CloseProviderCalled = true
	c.CloseProviderAddr = addr
	return nil
}

func (c *MockEvalContext) ConfigureProvider(addr addrs.ProviderConfig, cfg cty.Value) tfdiags.Diagnostics {
	c.ConfigureProviderCalled = true
	c.ConfigureProviderAddr = addr
	c.ConfigureProviderConfig = cfg
	return c.ConfigureProviderDiags
}

func (c *MockEvalContext) ProviderInput(addr addrs.ProviderConfig) map[string]cty.Value {
	c.ProviderInputCalled = true
	c.ProviderInputAddr = addr
	return c.ProviderInputValues
}

func (c *MockEvalContext) SetProviderInput(addr addrs.ProviderConfig, vals map[string]cty.Value) {
	c.SetProviderInputCalled = true
	c.SetProviderInputAddr = addr
	c.SetProviderInputValues = vals
}

func (c *MockEvalContext) InitProvisioner(n string) (ResourceProvisioner, error) {
	c.InitProvisionerCalled = true
	c.InitProvisionerName = n
	return c.InitProvisionerProvisioner, c.InitProvisionerError
}

func (c *MockEvalContext) Provisioner(n string) ResourceProvisioner {
	c.ProvisionerCalled = true
	c.ProvisionerName = n
	return c.ProvisionerProvisioner
}

func (c *MockEvalContext) ProvisionerSchema(n string) *configschema.Block {
	c.ProvisionerSchemaCalled = true
	c.ProvisionerSchemaName = n
	return c.ProvisionerSchemaSchema
}

func (c *MockEvalContext) CloseProvisioner(n string) error {
	c.CloseProvisionerCalled = true
	c.CloseProvisionerName = n
	return nil
}

func (c *MockEvalContext) EvaluateBlock(body hcl.Body, schema *configschema.Block, self addrs.Referenceable, key addrs.InstanceKey) (cty.Value, hcl.Body, tfdiags.Diagnostics) {
	c.EvaluateBlockCalled = true
	c.EvaluateBlockBody = body
	c.EvaluateBlockSchema = schema
	c.EvaluateBlockSelf = self
	c.EvaluateBlockKey = key
	if c.EvaluateBlockResultFunc != nil {
		return c.EvaluateBlockResultFunc(body, schema, self, key)
	}
	return c.EvaluateBlockResult, c.EvaluateBlockExpandedBody, c.EvaluateBlockDiags
}

func (c *MockEvalContext) EvaluateExpr(expr hcl.Expression, wantType cty.Type, self addrs.Referenceable) (cty.Value, tfdiags.Diagnostics) {
	c.EvaluateExprCalled = true
	c.EvaluateExprExpr = expr
	c.EvaluateExprWantType = wantType
	c.EvaluateExprSelf = self
	if c.EvaluateExprResultFunc != nil {
		return c.EvaluateExprResultFunc(expr, wantType, self)
	}
	return c.EvaluateExprResult, c.EvaluateExprDiags
}

// installSimpleEval is a helper to install a simple mock implementation of
// both EvaluateBlock and EvaluateExpr into the receiver.
//
// These default implementations will either evaluate the given input against
// the scope in field EvaluationScopeScope or, if it is nil, with no eval
// context at all so that only constant values may be used.
//
// This function overwrites any existing functions installed in fields
// EvaluateBlockResultFunc and EvaluateExprResultFunc.
func (c *MockEvalContext) installSimpleEval() {
	c.EvaluateBlockResultFunc = func(body hcl.Body, schema *configschema.Block, self addrs.Referenceable, key addrs.InstanceKey) (cty.Value, hcl.Body, tfdiags.Diagnostics) {
		if scope := c.EvaluationScopeScope; scope != nil {
			// Fully-functional codepath.
			var diags tfdiags.Diagnostics
			body, diags = scope.ExpandBlock(body, schema)
			if diags.HasErrors() {
				return cty.DynamicVal, body, diags
			}
			val, evalDiags := c.EvaluationScopeScope.EvalBlock(body, schema)
			diags = diags.Append(evalDiags)
			if evalDiags.HasErrors() {
				return cty.DynamicVal, body, diags
			}
			return val, body, diags
		}

		// Fallback codepath supporting constant values only.
		val, hclDiags := hcldec.Decode(body, schema.DecoderSpec(), nil)
		return val, body, tfdiags.Diagnostics(nil).Append(hclDiags)
	}
	c.EvaluateExprResultFunc = func(expr hcl.Expression, wantType cty.Type, self addrs.Referenceable) (cty.Value, tfdiags.Diagnostics) {
		if scope := c.EvaluationScopeScope; scope != nil {
			// Fully-functional codepath.
			return scope.EvalExpr(expr, wantType)
		}

		// Fallback codepath supporting constant values only.
		var diags tfdiags.Diagnostics
		val, hclDiags := expr.Value(nil)
		diags = diags.Append(hclDiags)
		if hclDiags.HasErrors() {
			return cty.DynamicVal, diags
		}
		var err error
		val, err = convert.Convert(val, wantType)
		if err != nil {
			diags = diags.Append(err)
			return cty.DynamicVal, diags
		}
		return val, diags
	}
}

func (c *MockEvalContext) EvaluationScope(self addrs.Referenceable, key addrs.InstanceKey) *lang.Scope {
	c.EvaluationScopeCalled = true
	c.EvaluationScopeSelf = self
	c.EvaluationScopeKey = key
	return c.EvaluationScopeScope
}

func (c *MockEvalContext) Interpolate(
	config *config.RawConfig, resource *Resource) (*ResourceConfig, error) {
	c.InterpolateCalled = true
	c.InterpolateConfig = config
	c.InterpolateResource = resource
	return c.InterpolateConfigResult, c.InterpolateError
}

func (c *MockEvalContext) InterpolateProvider(
	config *config.ProviderConfig, resource *Resource) (*ResourceConfig, error) {
	c.InterpolateProviderCalled = true
	c.InterpolateProviderConfig = config
	c.InterpolateProviderResource = resource
	return c.InterpolateProviderConfigResult, c.InterpolateError
}

func (c *MockEvalContext) Path() addrs.ModuleInstance {
	c.PathCalled = true
	return c.PathPath
}

func (c *MockEvalContext) SetModuleCallArguments(n addrs.ModuleCallInstance, values map[string]cty.Value) {
	c.SetModuleCallArgumentsCalled = true
	c.SetModuleCallArgumentsModule = n
	c.SetModuleCallArgumentsValues = values
}

func (c *MockEvalContext) Diff() (*Diff, *sync.RWMutex) {
	c.DiffCalled = true
	return c.DiffDiff, c.DiffLock
}

func (c *MockEvalContext) State() (*State, *sync.RWMutex) {
	c.StateCalled = true
	return c.StateState, c.StateLock
}
<|endoftext|>


<commit_before>package rocserv

import (
	"context"
	"encoding/json"
	"fmt"
	etcd "github.com/coreos/etcd/client"
	"github.com/shawnfeng/consistent"
	"github.com/shawnfeng/sutil/slog"
	"github.com/shawnfeng/sutil/stime"
	"sync"
	"time"
)

type GrpcClientEtcd struct {
	confEtcd configEtcd
	servKey  string
	servPath string
	// 使用的注册器位置，不同版本会注册到不同版本的dist目录
	// 但是会保持多版本的兼容，客户端优先使用最新版本的
	distLoc string

	etcdClient etcd.KeysAPI

	// 缓存地址列表，按照service id 降序的顺序存储
	// 按照processor 进行分组

	muServlist sync.Mutex
	servCopy   servCopyCollect
	servHash   *consistent.Consistent

	breakerGlobalPath string
	breakerServPath   string

	breakerMutex      sync.RWMutex
	breakerGlobalConf string
	breakerServConf   string
}

func NewGrpcClientEtcd(confEtcd configEtcd, servlocation string) (*GrpcClientEtcd, error) {
	//fun := "NewClientEtcdV2 -->"

	cfg := etcd.Config{
		Endpoints: confEtcd.etcdAddrs,
		Transport: etcd.DefaultTransport,
	}

	c, err := etcd.New(cfg)
	if err != nil {
		return nil, fmt.Errorf("create etchd client cfg error")
	}

	client := etcd.NewKeysAPI(c)
	if client == nil {
		return nil, fmt.Errorf("create etchd api error")
	}

	distloc := checkDistVersion(client, confEtcd.useBaseloc, servlocation)

	cli := &GrpcClientEtcd{
		confEtcd: confEtcd,
		servKey:  servlocation,
		distLoc:  distloc,
		servPath: fmt.Sprintf("%s/%s/%s", confEtcd.useBaseloc, distloc, servlocation),

		etcdClient: client,

		breakerServPath:   fmt.Sprintf("%s/%s/%s", confEtcd.useBaseloc, BASE_LOC_BREAKER, servlocation),
		breakerGlobalPath: fmt.Sprintf("%s/%s", confEtcd.useBaseloc, BASE_LOC_BREAKER_GLOBAL),
	}

	cli.watch(cli.servPath, cli.parseResponse)
	cli.watch(cli.breakerServPath, cli.handleBreakerServResponse)
	cli.watch(cli.breakerGlobalPath, cli.handleBreakerGlobalResponse)

	return cli, nil

}

func (m *GrpcClientEtcd) startWatch(chg chan *etcd.Response, path string) {
	fun := "ClientEtcdV2.startWatch -->"

	for i := 0; ; i++ {
		r, err := m.etcdClient.Get(context.Background(), path, &etcd.GetOptions{Recursive: true, Sort: false})
		if err != nil {
			slog.Warnf("%s get path:%s err:%s", fun, path, err)
			//close(chg)
			//return

		} else {
			chg <- r
		}

		index := uint64(0)
		if r != nil {
			index = r.Index
			sresp, _ := json.Marshal(r)
			slog.Infof("%s init get action:%s nodes:%d index:%d servPath:%s resp:%s", fun, r.Action, len(r.Node.Nodes), r.Index, path, sresp)
		}

		// 每次循环都设置下，测试发现放外边不好使
		wop := &etcd.WatcherOptions{
			Recursive:  true,
			AfterIndex: index,
		}
		watcher := m.etcdClient.Watcher(path, wop)
		if watcher == nil {
			slog.Errorf("%s new watcher path:%s", fun, path)
			return
		}

		resp, err := watcher.Next(context.Background())
		// etcd 关闭时候会返回
		if err != nil {
			slog.Errorf("%s watch path:%s err:%s", fun, path, err)
			close(chg)
			return
		} else {
			slog.Infof("%s next get idx:%d action:%s nodes:%d index:%d after:%d servPath:%s", fun, i, resp.Action, len(resp.Node.Nodes), resp.Index, wop.AfterIndex, path)
			// 测试发现next获取到的返回，index，重新获取总有问题，触发两次，不确定，为什么？为什么？
			// 所以这里每次next前使用的afterindex都重新get了
		}
	}

}

func (m *GrpcClientEtcd) watch(path string, hander func(*etcd.Response)) {
	fun := "ClientEtcdV2.watch -->"

	backoff := stime.NewBackOffCtrl(time.Millisecond*10, time.Second*5)

	var chg chan *etcd.Response

	go func() {
		slog.Infof("%s start watch:%s", fun, path)
		for {
			if chg == nil {
				slog.Infof("%s loop watch new receiver:%s", fun, path)
				chg = make(chan *etcd.Response)
				go m.startWatch(chg, path)
			}

			r, ok := <-chg
			if !ok {
				slog.Errorf("%s chg info nil:%s", fun, path)
				chg = nil
				backoff.BackOff()
			} else {
				slog.Infof("%s update v:%s serv:%s", fun, r.Node.Key, path)
				hander(r)
				backoff.Reset()
			}

		}
	}()
}

func (m *GrpcClientEtcd) parseResponse(r *etcd.Response) {
	fun := "ClientEtcdV2.parseResponse -->"
	if !r.Node.Dir {
		slog.Errorf("%s not dir %s", fun, r.Node.Key)
		return
	}

}

func (m *GrpcClientEtcd) handleBreakerGlobalResponse(r *etcd.Response) {
	fun := "ClientEtcdV2.handleBreakerGlobalResponse -->"

	if r.Node.Dir {
		slog.Errorf("%s not file %s", fun, r.Node.Key)
		return
	}

	m.breakerMutex.Lock()
	defer m.breakerMutex.Unlock()
	m.breakerGlobalConf = r.Node.Value
}

func (m *GrpcClientEtcd) handleBreakerServResponse(r *etcd.Response) {
	fun := "GrpcClientEtcd.handleBreakerServResponse -->"

	if r.Node.Dir {
		slog.Errorf("%s not file %s", fun, r.Node.Key)
		return
	}

	m.breakerMutex.Lock()
	defer m.breakerMutex.Unlock()
	m.breakerServConf = r.Node.Value
}
<commit_msg>grpc 注册代码<commit_after><|endoftext|>


<commit_before>package monitor

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"

	"github.com/rancher/norman/api/access"
	"github.com/rancher/norman/parse"
	"github.com/rancher/norman/types"
	"github.com/rancher/norman/types/convert"
	"github.com/rancher/rancher/pkg/clustermanager"
	monitorutil "github.com/rancher/rancher/pkg/monitoring"
	"github.com/rancher/rancher/pkg/ref"
	"github.com/rancher/types/apis/management.cattle.io/v3"
	mgmtclientv3 "github.com/rancher/types/client/management/v3"
	"github.com/rancher/types/config/dialer"
)

func NewProjectGraphHandler(dialerFactory dialer.Factory, clustermanager *clustermanager.Manager) *ProjectGraphHandler {
	return &ProjectGraphHandler{
		dialerFactory:  dialerFactory,
		clustermanager: clustermanager,
	}
}

type ProjectGraphHandler struct {
	dialerFactory  dialer.Factory
	clustermanager *clustermanager.Manager
}

func (h *ProjectGraphHandler) QuerySeriesAction(actionName string, action *types.Action, apiContext *types.APIContext) error {
	var queryGraphInput v3.QueryGraphInput
	actionInput, err := parse.ReadBody(apiContext.Request)
	if err != nil {
		return err
	}

	if err = convert.ToObj(actionInput, &queryGraphInput); err != nil {
		return err
	}

	inputParser := newProjectGraphInputParser(queryGraphInput)
	if err = inputParser.parse(); err != nil {
		return err
	}

	clusterName := inputParser.ClusterName
	userContext, err := h.clustermanager.UserContext(clusterName)
	if err != nil {
		return fmt.Errorf("get usercontext failed, %v", err)
	}

	check := newAuthChecker(apiContext.Request.Context(), userContext, inputParser.Input, inputParser.ProjectID)
	if err = check.check(); err != nil {
		return err
	}

	prometheusName, prometheusNamespace := monitorutil.ClusterMonitoringInfo()
	token, err := getAuthToken(userContext, prometheusName, prometheusNamespace)
	if err != nil {
		return err
	}

	reqContext, cancel := context.WithTimeout(context.Background(), prometheusReqTimeout)
	defer cancel()

	svcName, svcNamespace, _ := monitorutil.ProjectPrometheusEndpoint(inputParser.ProjectName)
	prometheusQuery, err := NewPrometheusQuery(reqContext, userContext, clusterName, token, svcNamespace, svcName, h.clustermanager, h.dialerFactory)
	if err != nil {
		return err
	}

	var graphs []mgmtclientv3.ProjectMonitorGraph
	err = access.List(apiContext, apiContext.Version, mgmtclientv3.ProjectMonitorGraphType, &types.QueryOptions{Conditions: inputParser.Conditions}, &graphs)
	if err != nil {
		return err
	}

	mgmtClient := h.clustermanager.ScaledContext.Management
	var queries []*PrometheusQuery
	for _, graph := range graphs {
		g := graph
		_, projectName := ref.Parse(graph.ProjectID)
		refName := getRefferenceGraphName(projectName, graph.Name)
		monitorMetrics, err := graph2Metrics(userContext, mgmtClient, clusterName, g.ResourceType, refName, graph.MetricsSelector, graph.DetailsMetricsSelector, inputParser.Input.MetricParams, inputParser.Input.IsDetails)
		if err != nil {
			return err
		}

		queries = append(queries, metrics2PrometheusQuery(monitorMetrics, inputParser.Start, inputParser.End, inputParser.Step, isInstanceGraph(g.GraphType))...)
	}

	seriesSlice, err := prometheusQuery.Do(queries)
	if err != nil {
		return fmt.Errorf("query series failed, %v", err)
	}

	if seriesSlice == nil {
		apiContext.WriteResponse(http.StatusNoContent, nil)
		return nil
	}

	collection := v3.QueryProjectGraphOutput{Type: "collection"}
	for k, v := range seriesSlice {
		graphName, _, _ := parseID(k)
		queryGraph := v3.QueryProjectGraph{
			GraphName: graphName,
			Series:    parseResponse(v),
		}
		collection.Data = append(collection.Data, queryGraph)
	}

	res, err := json.Marshal(collection)
	if err != nil {
		return fmt.Errorf("marshal query series result failed, %v", err)
	}
	apiContext.Response.Write(res)
	return nil
}

func parseResponse(seriesSlice []*TimeSeries) []*v3.TimeSeries {
	var series []*v3.TimeSeries
	for _, v := range seriesSlice {
		series = append(series, &v3.TimeSeries{
			Name:   v.Name,
			Points: v.Points,
		})
	}
	return series
}
<commit_msg>Use cluster-level Prometheus for project metric<commit_after>package monitor

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"

	"github.com/rancher/norman/api/access"
	"github.com/rancher/norman/parse"
	"github.com/rancher/norman/types"
	"github.com/rancher/norman/types/convert"
	"github.com/rancher/rancher/pkg/clustermanager"
	monitorutil "github.com/rancher/rancher/pkg/monitoring"
	"github.com/rancher/rancher/pkg/ref"
	"github.com/rancher/types/apis/management.cattle.io/v3"
	mgmtclientv3 "github.com/rancher/types/client/management/v3"
	"github.com/rancher/types/config/dialer"
)

func NewProjectGraphHandler(dialerFactory dialer.Factory, clustermanager *clustermanager.Manager) *ProjectGraphHandler {
	return &ProjectGraphHandler{
		dialerFactory:  dialerFactory,
		clustermanager: clustermanager,
	}
}

type ProjectGraphHandler struct {
	dialerFactory  dialer.Factory
	clustermanager *clustermanager.Manager
}

func (h *ProjectGraphHandler) QuerySeriesAction(actionName string, action *types.Action, apiContext *types.APIContext) error {
	var queryGraphInput v3.QueryGraphInput
	actionInput, err := parse.ReadBody(apiContext.Request)
	if err != nil {
		return err
	}

	if err = convert.ToObj(actionInput, &queryGraphInput); err != nil {
		return err
	}

	inputParser := newProjectGraphInputParser(queryGraphInput)
	if err = inputParser.parse(); err != nil {
		return err
	}

	clusterName := inputParser.ClusterName
	userContext, err := h.clustermanager.UserContext(clusterName)
	if err != nil {
		return fmt.Errorf("get usercontext failed, %v", err)
	}

	check := newAuthChecker(apiContext.Request.Context(), userContext, inputParser.Input, inputParser.ProjectID)
	if err = check.check(); err != nil {
		return err
	}

	prometheusName, prometheusNamespace := monitorutil.ClusterMonitoringInfo()
	token, err := getAuthToken(userContext, prometheusName, prometheusNamespace)
	if err != nil {
		return err
	}

	reqContext, cancel := context.WithTimeout(context.Background(), prometheusReqTimeout)
	defer cancel()

	svcName, svcNamespace, _ := monitorutil.ClusterPrometheusEndpoint()
	prometheusQuery, err := NewPrometheusQuery(reqContext, userContext, clusterName, token, svcNamespace, svcName, h.clustermanager, h.dialerFactory)
	if err != nil {
		return err
	}

	var graphs []mgmtclientv3.ProjectMonitorGraph
	err = access.List(apiContext, apiContext.Version, mgmtclientv3.ProjectMonitorGraphType, &types.QueryOptions{Conditions: inputParser.Conditions}, &graphs)
	if err != nil {
		return err
	}

	mgmtClient := h.clustermanager.ScaledContext.Management
	var queries []*PrometheusQuery
	for _, graph := range graphs {
		g := graph
		_, projectName := ref.Parse(graph.ProjectID)
		refName := getRefferenceGraphName(projectName, graph.Name)
		monitorMetrics, err := graph2Metrics(userContext, mgmtClient, clusterName, g.ResourceType, refName, graph.MetricsSelector, graph.DetailsMetricsSelector, inputParser.Input.MetricParams, inputParser.Input.IsDetails)
		if err != nil {
			return err
		}

		queries = append(queries, metrics2PrometheusQuery(monitorMetrics, inputParser.Start, inputParser.End, inputParser.Step, isInstanceGraph(g.GraphType))...)
	}

	seriesSlice, err := prometheusQuery.Do(queries)
	if err != nil {
		return fmt.Errorf("query series failed, %v", err)
	}

	if seriesSlice == nil {
		apiContext.WriteResponse(http.StatusNoContent, nil)
		return nil
	}

	collection := v3.QueryProjectGraphOutput{Type: "collection"}
	for k, v := range seriesSlice {
		graphName, _, _ := parseID(k)
		queryGraph := v3.QueryProjectGraph{
			GraphName: graphName,
			Series:    parseResponse(v),
		}
		collection.Data = append(collection.Data, queryGraph)
	}

	res, err := json.Marshal(collection)
	if err != nil {
		return fmt.Errorf("marshal query series result failed, %v", err)
	}
	apiContext.Response.Write(res)
	return nil
}

func parseResponse(seriesSlice []*TimeSeries) []*v3.TimeSeries {
	var series []*v3.TimeSeries
	for _, v := range seriesSlice {
		series = append(series, &v3.TimeSeries{
			Name:   v.Name,
			Points: v.Points,
		})
	}
	return series
}
<|endoftext|>