<commit_before>package resolve

import (
	"fmt"
	"github.com/Aptomi/aptomi/pkg/slinga/errors"
	"github.com/Aptomi/aptomi/pkg/slinga/lang"
	"github.com/Aptomi/aptomi/pkg/slinga/object"
	"github.com/Aptomi/aptomi/pkg/slinga/util"
	"strconv"
	"time"
)

// ComponentInstanceObject is an informational data structure with Kind and Constructor for component instance object
var ComponentInstanceObject = &object.Info{
	Kind:        "component-instance",
	Constructor: func() object.Base { return &ComponentInstance{} },
}

// ComponentInstanceMetadata is object metadata for ComponentInstance
type ComponentInstanceMetadata struct {
	Key  *ComponentInstanceKey
	Kind string
}

// AllowIngres is an special key, which is used in DataForPlugins to indicate whether ingress traffic should be allowed for a given component instance
const AllowIngres = "allow_ingress"

// ComponentInstance is a struct that holds data for a given component instance, containing list of user IDs and calculated labels
// When adding new fields to this object, it's crucial to modify appendData() method as well (!)
type ComponentInstance struct {
	/*
		These fields get populated during policy resolution
	*/

	// Metadata is an object metadata for component instance
	Metadata *ComponentInstanceMetadata

	// DependencyKeys is a list of dependency keys which are keeping this component instantiated
	DependencyKeys map[string]bool

	// CalculatedLabels is a set of calculated labels for the component, aggregated over all uses
	CalculatedLabels *lang.LabelSet

	// CalculatedDiscovery is a set of calculated discovery parameters for the component (non-conflicting over all uses of this component)
	CalculatedDiscovery util.NestedParameterMap

	// CalculatedCodeParams is a set of calculated code parameters for the component (non-conflicting over all uses of this component)
	CalculatedCodeParams util.NestedParameterMap

	// EdgesIn is a set of incoming graph edges ('key' -> true) into this component instance. Storing for observability and reporting, so we can reconstruct the graph
	EdgesIn map[string]bool

	// EdgesOut is a set of outgoing graph edges ('key' -> true) into this component instance. Storing for observability and reporting, so we can reconstruct the graph
	EdgesOut map[string]bool

	// DataForPlugins is an additional data recorded for use in plugins
	DataForPlugins map[string]string

	/*
		These fields get populated during apply and desired -> actual state reconciliation
	*/

	// CreatedOn is when this component instance was created
	CreatedOn time.Time

	// UpdatedOn is the last time when this component instance was updated
	UpdatedOn time.Time
}

// Creates a new component instance
func newComponentInstance(cik *ComponentInstanceKey) *ComponentInstance {
	return &ComponentInstance{
		Metadata:             &ComponentInstanceMetadata{cik, ComponentInstanceObject.Kind},
		DependencyKeys:       make(map[string]bool),
		CalculatedLabels:     lang.NewLabelSet(make(map[string]string)),
		CalculatedDiscovery:  util.NestedParameterMap{},
		CalculatedCodeParams: util.NestedParameterMap{},
		EdgesIn:              make(map[string]bool),
		EdgesOut:             make(map[string]bool),
		DataForPlugins:       make(map[string]string),
	}
}

// GetKey returns an object key. It's a component instance key, as generated by the engine
func (instance *ComponentInstance) GetKey() string {
	return instance.Metadata.Key.GetKey()
}

// GetDeployName returns a string that could be used as name for deployment inside the cluster
func (instance *ComponentInstance) GetDeployName() string {
	return instance.Metadata.Key.GetDeployName()
}

// GetNamespace returns an object namespace. It's a system namespace for all component instances
func (instance *ComponentInstance) GetNamespace() string {
	return object.SystemNS
}

// GetKind returns object kind
func (instance *ComponentInstance) GetKind() string {
	return ComponentInstanceObject.Kind
}

// GetName returns object name
func (instance *ComponentInstance) GetName() string {
	return instance.GetKey()
}

// GetGeneration returns object generation. Not needed for component instances as they are not versioned
func (instance *ComponentInstance) GetGeneration() object.Generation {
	// we aren't storing multiple versions of ComponentInstance
	return 0
}

// SetGeneration sets object generation. Not needed for component instances as they are not versioned
func (instance *ComponentInstance) SetGeneration(generation object.Generation) {
	panic("ComponentInstance isn't a versioned object")
}

// GetRunningTime returns the total lifetime of a component instance (since it was launched till now)
func (instance *ComponentInstance) GetRunningTime() time.Duration {
	return time.Since(instance.CreatedOn)
}

func (instance *ComponentInstance) addDependency(dependencyKey string) {
	instance.DependencyKeys[dependencyKey] = true
}

func (instance *ComponentInstance) addRuleInformation(result *lang.RuleActionResult) {
	instance.DataForPlugins[AllowIngres] = strconv.FormatBool(result.AllowIngress)
}

func (instance *ComponentInstance) addCodeParams(codeParams util.NestedParameterMap) error {
	if len(instance.CalculatedCodeParams) == 0 {
		// Record code parameters
		instance.CalculatedCodeParams = codeParams
	} else if !instance.CalculatedCodeParams.DeepEqual(codeParams) {
		// Same component instance, different code parameters
		return errors.NewErrorWithDetails(
			fmt.Sprintf("Invalid policy. Conflicting code parameters for component instance: %s", instance.GetKey()),
			errors.Details{
				"instance":             instance.Metadata.Key,
				"code_params_existing": instance.CalculatedCodeParams,
				"code_params_new":      codeParams,
				"diff":                 instance.CalculatedCodeParams.Diff(codeParams),
			},
		)
	}
	return nil
}

func (instance *ComponentInstance) addDiscoveryParams(discoveryParams util.NestedParameterMap) error {
	if len(instance.CalculatedDiscovery) == 0 {
		// Record discovery parameters
		instance.CalculatedDiscovery = discoveryParams
	} else if !instance.CalculatedDiscovery.DeepEqual(discoveryParams) {
		// Same component instance, different discovery parameters
		return errors.NewErrorWithDetails(
			fmt.Sprintf("Invalid policy. Conflicting discovery parameters for component instance: %s", instance.GetKey()),
			errors.Details{
				"instance":                  instance.Metadata.Key,
				"discovery_params_existing": instance.CalculatedDiscovery,
				"discovery_params_new":      discoveryParams,
				"diff":                      instance.CalculatedDiscovery.Diff(discoveryParams),
			},
		)
	}
	return nil
}

func (instance *ComponentInstance) addLabels(labels *lang.LabelSet) {
	// it's pretty typical for us to come with different labels to a component instance, let's combine them all
	instance.CalculatedLabels.AddLabels(labels.Labels)
}

func (instance *ComponentInstance) addEdgeIn(srcKey string) {
	instance.EdgesIn[srcKey] = true
}

func (instance *ComponentInstance) addEdgeOut(dstKey string) {
	instance.EdgesOut[dstKey] = true
}

// UpdateTimes updates component creation and update times
func (instance *ComponentInstance) UpdateTimes(createdOn time.Time, updatedOn time.Time) {
	if time.Time.IsZero(instance.CreatedOn) || (!time.Time.IsZero(createdOn) && createdOn.Before(instance.CreatedOn)) {
		instance.CreatedOn = createdOn
	}
	if !time.Time.IsZero(updatedOn) && updatedOn.After(instance.UpdatedOn) {
		instance.UpdatedOn = updatedOn
	}
}

func (instance *ComponentInstance) appendData(ops *ComponentInstance) error {
	// List of dependencies which are keeping this component instantiated
	for dependencyKey := range ops.DependencyKeys {
		instance.addDependency(dependencyKey)
	}

	// Combine labels
	instance.addLabels(ops.CalculatedLabels)

	// Combine code params and discovery params for
	var err = instance.addDiscoveryParams(ops.CalculatedDiscovery)
	if err != nil {
		return err
	}

	err = instance.addCodeParams(ops.CalculatedCodeParams)
	if err != nil {
		return err
	}

	// Incoming and outgoing graph edges (instance: key -> true) as we are traversing the graph
	for key := range ops.EdgesIn {
		instance.addEdgeIn(key)
	}
	for keyDst := range ops.EdgesOut {
		instance.addEdgeOut(keyDst)
	}

	// Data for plugins
	for k, v := range ops.DataForPlugins {
		instance.DataForPlugins[k] = v
	}

	return nil
}
<commit_msg>Add Endpoints map[string]string to ComponentInstance<commit_after>package resolve

import (
	"fmt"
	"github.com/Aptomi/aptomi/pkg/slinga/errors"
	"github.com/Aptomi/aptomi/pkg/slinga/lang"
	"github.com/Aptomi/aptomi/pkg/slinga/object"
	"github.com/Aptomi/aptomi/pkg/slinga/util"
	"strconv"
	"time"
)

// ComponentInstanceObject is an informational data structure with Kind and Constructor for component instance object
var ComponentInstanceObject = &object.Info{
	Kind:        "component-instance",
	Constructor: func() object.Base { return &ComponentInstance{} },
}

// ComponentInstanceMetadata is object metadata for ComponentInstance
type ComponentInstanceMetadata struct {
	Key  *ComponentInstanceKey
	Kind string
}

// AllowIngres is an special key, which is used in DataForPlugins to indicate whether ingress traffic should be allowed for a given component instance
const AllowIngres = "allow_ingress"

// ComponentInstance is a struct that holds data for a given component instance, containing list of user IDs and calculated labels
// When adding new fields to this object, it's crucial to modify appendData() method as well (!)
type ComponentInstance struct {
	/*
		These fields get populated during policy resolution
	*/

	// Metadata is an object metadata for component instance
	Metadata *ComponentInstanceMetadata

	// DependencyKeys is a list of dependency keys which are keeping this component instantiated
	DependencyKeys map[string]bool

	// CalculatedLabels is a set of calculated labels for the component, aggregated over all uses
	CalculatedLabels *lang.LabelSet

	// CalculatedDiscovery is a set of calculated discovery parameters for the component (non-conflicting over all uses of this component)
	CalculatedDiscovery util.NestedParameterMap

	// CalculatedCodeParams is a set of calculated code parameters for the component (non-conflicting over all uses of this component)
	CalculatedCodeParams util.NestedParameterMap

	// EdgesIn is a set of incoming graph edges ('key' -> true) into this component instance. Storing for observability and reporting, so we can reconstruct the graph
	EdgesIn map[string]bool

	// EdgesOut is a set of outgoing graph edges ('key' -> true) into this component instance. Storing for observability and reporting, so we can reconstruct the graph
	EdgesOut map[string]bool

	// DataForPlugins is an additional data recorded for use in plugins
	DataForPlugins map[string]string

	/*
		These fields get populated during apply and desired -> actual state reconciliation
	*/

	// CreatedOn is when this component instance was created
	CreatedOn time.Time

	// UpdatedOn is the last time when this component instance was updated
	UpdatedOn time.Time

	// Endpoints represents all URLs that could be used to access deployed service
	Endpoints map[string]string
}

// Creates a new component instance
func newComponentInstance(cik *ComponentInstanceKey) *ComponentInstance {
	return &ComponentInstance{
		Metadata:             &ComponentInstanceMetadata{cik, ComponentInstanceObject.Kind},
		DependencyKeys:       make(map[string]bool),
		CalculatedLabels:     lang.NewLabelSet(make(map[string]string)),
		CalculatedDiscovery:  util.NestedParameterMap{},
		CalculatedCodeParams: util.NestedParameterMap{},
		EdgesIn:              make(map[string]bool),
		EdgesOut:             make(map[string]bool),
		DataForPlugins:       make(map[string]string),
	}
}

// GetKey returns an object key. It's a component instance key, as generated by the engine
func (instance *ComponentInstance) GetKey() string {
	return instance.Metadata.Key.GetKey()
}

// GetDeployName returns a string that could be used as name for deployment inside the cluster
func (instance *ComponentInstance) GetDeployName() string {
	return instance.Metadata.Key.GetDeployName()
}

// GetNamespace returns an object namespace. It's a system namespace for all component instances
func (instance *ComponentInstance) GetNamespace() string {
	return object.SystemNS
}

// GetKind returns object kind
func (instance *ComponentInstance) GetKind() string {
	return ComponentInstanceObject.Kind
}

// GetName returns object name
func (instance *ComponentInstance) GetName() string {
	return instance.GetKey()
}

// GetGeneration returns object generation. Not needed for component instances as they are not versioned
func (instance *ComponentInstance) GetGeneration() object.Generation {
	// we aren't storing multiple versions of ComponentInstance
	return 0
}

// SetGeneration sets object generation. Not needed for component instances as they are not versioned
func (instance *ComponentInstance) SetGeneration(generation object.Generation) {
	panic("ComponentInstance isn't a versioned object")
}

// GetRunningTime returns the total lifetime of a component instance (since it was launched till now)
func (instance *ComponentInstance) GetRunningTime() time.Duration {
	return time.Since(instance.CreatedOn)
}

func (instance *ComponentInstance) addDependency(dependencyKey string) {
	instance.DependencyKeys[dependencyKey] = true
}

func (instance *ComponentInstance) addRuleInformation(result *lang.RuleActionResult) {
	instance.DataForPlugins[AllowIngres] = strconv.FormatBool(result.AllowIngress)
}

func (instance *ComponentInstance) addCodeParams(codeParams util.NestedParameterMap) error {
	if len(instance.CalculatedCodeParams) == 0 {
		// Record code parameters
		instance.CalculatedCodeParams = codeParams
	} else if !instance.CalculatedCodeParams.DeepEqual(codeParams) {
		// Same component instance, different code parameters
		return errors.NewErrorWithDetails(
			fmt.Sprintf("Invalid policy. Conflicting code parameters for component instance: %s", instance.GetKey()),
			errors.Details{
				"instance":             instance.Metadata.Key,
				"code_params_existing": instance.CalculatedCodeParams,
				"code_params_new":      codeParams,
				"diff":                 instance.CalculatedCodeParams.Diff(codeParams),
			},
		)
	}
	return nil
}

func (instance *ComponentInstance) addDiscoveryParams(discoveryParams util.NestedParameterMap) error {
	if len(instance.CalculatedDiscovery) == 0 {
		// Record discovery parameters
		instance.CalculatedDiscovery = discoveryParams
	} else if !instance.CalculatedDiscovery.DeepEqual(discoveryParams) {
		// Same component instance, different discovery parameters
		return errors.NewErrorWithDetails(
			fmt.Sprintf("Invalid policy. Conflicting discovery parameters for component instance: %s", instance.GetKey()),
			errors.Details{
				"instance":                  instance.Metadata.Key,
				"discovery_params_existing": instance.CalculatedDiscovery,
				"discovery_params_new":      discoveryParams,
				"diff":                      instance.CalculatedDiscovery.Diff(discoveryParams),
			},
		)
	}
	return nil
}

func (instance *ComponentInstance) addLabels(labels *lang.LabelSet) {
	// it's pretty typical for us to come with different labels to a component instance, let's combine them all
	instance.CalculatedLabels.AddLabels(labels.Labels)
}

func (instance *ComponentInstance) addEdgeIn(srcKey string) {
	instance.EdgesIn[srcKey] = true
}

func (instance *ComponentInstance) addEdgeOut(dstKey string) {
	instance.EdgesOut[dstKey] = true
}

// UpdateTimes updates component creation and update times
func (instance *ComponentInstance) UpdateTimes(createdOn time.Time, updatedOn time.Time) {
	if time.Time.IsZero(instance.CreatedOn) || (!time.Time.IsZero(createdOn) && createdOn.Before(instance.CreatedOn)) {
		instance.CreatedOn = createdOn
	}
	if !time.Time.IsZero(updatedOn) && updatedOn.After(instance.UpdatedOn) {
		instance.UpdatedOn = updatedOn
	}
}

func (instance *ComponentInstance) appendData(ops *ComponentInstance) error {
	// List of dependencies which are keeping this component instantiated
	for dependencyKey := range ops.DependencyKeys {
		instance.addDependency(dependencyKey)
	}

	// Combine labels
	instance.addLabels(ops.CalculatedLabels)

	// Combine code params and discovery params for
	var err = instance.addDiscoveryParams(ops.CalculatedDiscovery)
	if err != nil {
		return err
	}

	err = instance.addCodeParams(ops.CalculatedCodeParams)
	if err != nil {
		return err
	}

	// Incoming and outgoing graph edges (instance: key -> true) as we are traversing the graph
	for key := range ops.EdgesIn {
		instance.addEdgeIn(key)
	}
	for keyDst := range ops.EdgesOut {
		instance.addEdgeOut(keyDst)
	}

	// Data for plugins
	for k, v := range ops.DataForPlugins {
		instance.DataForPlugins[k] = v
	}

	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package scheduling

import (
	"fmt"
	"time"

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/apimachinery/pkg/util/uuid"
	clientset "k8s.io/client-go/kubernetes"
	api "k8s.io/kubernetes/pkg/apis/core"
	"k8s.io/kubernetes/test/e2e/framework"
	e2elog "k8s.io/kubernetes/test/e2e/framework/log"
	e2enode "k8s.io/kubernetes/test/e2e/framework/node"
	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
	testutils "k8s.io/kubernetes/test/utils"
	imageutils "k8s.io/kubernetes/test/utils/image"

	"github.com/onsi/ginkgo"
	"github.com/onsi/gomega"
	// ensure libs have a chance to initialize
	_ "github.com/stretchr/testify/assert"
)

const (
	defaultTimeout = 3 * time.Minute
)

var _ = framework.KubeDescribe("EquivalenceCache [Serial]", func() {
	var cs clientset.Interface
	var nodeList *v1.NodeList
	var masterNodes sets.String
	var systemPodsNo int
	var ns string
	f := framework.NewDefaultFramework("equivalence-cache")

	ginkgo.BeforeEach(func() {
		cs = f.ClientSet
		ns = f.Namespace.Name

		e2enode.WaitForTotalHealthy(cs, time.Minute)
		masterNodes, nodeList = framework.GetMasterAndWorkerNodesOrDie(cs)

		framework.ExpectNoError(framework.CheckTestingNSDeletedExcept(cs, ns))

		// Every test case in this suite assumes that cluster add-on pods stay stable and
		// cannot be run in parallel with any other test that touches Nodes or Pods.
		// It is so because we need to have precise control on what's running in the cluster.
		systemPods, err := e2epod.GetPodsInNamespace(cs, ns, map[string]string{})
		framework.ExpectNoError(err)
		systemPodsNo = 0
		for _, pod := range systemPods {
			if !masterNodes.Has(pod.Spec.NodeName) && pod.DeletionTimestamp == nil {
				systemPodsNo++
			}
		}

		err = e2epod.WaitForPodsRunningReady(cs, api.NamespaceSystem, int32(systemPodsNo), int32(systemPodsNo), framework.PodReadyBeforeTimeout, map[string]string{})
		framework.ExpectNoError(err)

		for _, node := range nodeList.Items {
			e2elog.Logf("\nLogging pods the kubelet thinks is on node %v before test", node.Name)
			framework.PrintAllKubeletPods(cs, node.Name)
		}

	})

	// This test verifies that GeneralPredicates works as expected:
	// When a replica pod (with HostPorts) is scheduled to a node, it will invalidate GeneralPredicates cache on this node,
	// so that subsequent replica pods with same host port claim will be rejected.
	// We enforce all replica pods bind to the same node so there will always be conflicts.
	ginkgo.It("validates GeneralPredicates is properly invalidated when a pod is scheduled [Slow]", func() {
		ginkgo.By("Launching a RC with two replica pods with HostPorts")
		nodeName := getNodeThatCanRunPodWithoutToleration(f)
		rcName := "host-port"

		// bind all replicas to same node
		nodeSelector := map[string]string{"kubernetes.io/hostname": nodeName}

		ginkgo.By("One pod should be scheduled, the other should be rejected")
		// CreateNodeSelectorPods creates RC with host port 4321
		WaitForSchedulerAfterAction(f, func() error {
			err := CreateNodeSelectorPods(f, rcName, 2, nodeSelector, false)
			return err
		}, ns, rcName, false)
		defer framework.DeleteRCAndWaitForGC(f.ClientSet, ns, rcName)
		// the first replica pod is scheduled, and the second pod will be rejected.
		verifyResult(cs, 1, 1, ns)
	})

	// This test verifies that MatchInterPodAffinity works as expected.
	// In equivalence cache, it does not handle inter pod affinity (anti-affinity) specially (unless node label changed),
	// because current predicates algorithm will ensure newly scheduled pod does not break existing affinity in cluster.
	ginkgo.It("validates pod affinity works properly when new replica pod is scheduled", func() {
		// create a pod running with label {security: S1}, and choose this node
		nodeName, _ := runAndKeepPodWithLabelAndGetNodeName(f)

		ginkgo.By("Trying to apply a random label on the found node.")
		// we need to use real failure domains, since scheduler only know them
		k := "failure-domain.beta.kubernetes.io/zone"
		v := "equivalence-e2e-test"
		oldValue := framework.AddOrUpdateLabelOnNodeAndReturnOldValue(cs, nodeName, k, v)
		framework.ExpectNodeHasLabel(cs, nodeName, k, v)
		// restore the node label
		defer framework.AddOrUpdateLabelOnNode(cs, nodeName, k, oldValue)

		ginkgo.By("Trying to schedule RC with Pod Affinity should success.")
		framework.WaitForStableCluster(cs, masterNodes)
		affinityRCName := "with-pod-affinity-" + string(uuid.NewUUID())
		replica := 2
		labelsMap := map[string]string{
			"name": affinityRCName,
		}
		affinity := &v1.Affinity{
			PodAffinity: &v1.PodAffinity{
				RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
					{
						LabelSelector: &metav1.LabelSelector{
							MatchExpressions: []metav1.LabelSelectorRequirement{
								{
									Key:      "security",
									Operator: metav1.LabelSelectorOpIn,
									Values:   []string{"S1"},
								},
							},
						},
						TopologyKey: k,
						Namespaces:  []string{ns},
					},
				},
			},
		}
		rc := getRCWithInterPodAffinity(affinityRCName, labelsMap, replica, affinity, imageutils.GetPauseImageName())
		defer framework.DeleteRCAndWaitForGC(f.ClientSet, ns, affinityRCName)

		// RC should be running successfully
		// TODO: WaitForSchedulerAfterAction() can on be used to wait for failure event,
		// not for successful RC, since no specific pod name can be provided.
		_, err := cs.CoreV1().ReplicationControllers(ns).Create(rc)
		framework.ExpectNoError(err)
		framework.ExpectNoError(e2epod.WaitForControlledPodsRunning(cs, ns, affinityRCName, api.Kind("ReplicationController")))

		ginkgo.By("Remove node failure domain label")
		framework.RemoveLabelOffNode(cs, nodeName, k)

		ginkgo.By("Trying to schedule another equivalent Pod should fail due to node label has been removed.")
		// use scale to create another equivalent pod and wait for failure event
		WaitForSchedulerAfterAction(f, func() error {
			err := framework.ScaleRC(f.ClientSet, f.ScalesGetter, ns, affinityRCName, uint(replica+1), false)
			return err
		}, ns, affinityRCName, false)
		// and this new pod should be rejected since node label has been updated
		verifyReplicasResult(cs, replica, 1, ns, affinityRCName)
	})

	// This test verifies that MatchInterPodAffinity (anti-affinity) is respected as expected.
	ginkgo.It("validates pod anti-affinity works properly when new replica pod is scheduled", func() {
		ginkgo.By("Launching two pods on two distinct nodes to get two node names")
		CreateHostPortPods(f, "host-port", 2, true)
		defer framework.DeleteRCAndWaitForGC(f.ClientSet, ns, "host-port")
		podList, err := cs.CoreV1().Pods(ns).List(metav1.ListOptions{})
		framework.ExpectNoError(err)
		gomega.Expect(len(podList.Items)).To(gomega.Equal(2))
		nodeNames := []string{podList.Items[0].Spec.NodeName, podList.Items[1].Spec.NodeName}
		gomega.Expect(nodeNames[0]).ToNot(gomega.Equal(nodeNames[1]))

		ginkgo.By("Applying a random label to both nodes.")
		k := "e2e.inter-pod-affinity.kubernetes.io/zone"
		v := "equivalence-e2etest"
		for _, nodeName := range nodeNames {
			framework.AddOrUpdateLabelOnNode(cs, nodeName, k, v)
			framework.ExpectNodeHasLabel(cs, nodeName, k, v)
			defer framework.RemoveLabelOffNode(cs, nodeName, k)
		}

		ginkgo.By("Trying to launch a pod with the service label on the selected nodes.")
		// run a pod with label {"service": "S1"} and expect it to be running
		runPausePod(f, pausePodConfig{
			Name:         "with-label-" + string(uuid.NewUUID()),
			Labels:       map[string]string{"service": "S1"},
			NodeSelector: map[string]string{k: v}, // only launch on our two nodes
		})

		ginkgo.By("Trying to launch RC with podAntiAffinity on these two nodes should be rejected.")
		labelRCName := "with-podantiaffinity-" + string(uuid.NewUUID())
		replica := 2
		labelsMap := map[string]string{
			"name": labelRCName,
		}
		affinity := &v1.Affinity{
			PodAntiAffinity: &v1.PodAntiAffinity{
				RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
					{
						LabelSelector: &metav1.LabelSelector{
							MatchExpressions: []metav1.LabelSelectorRequirement{
								{
									Key:      "service",
									Operator: metav1.LabelSelectorOpIn,
									Values:   []string{"S1"},
								},
							},
						},
						TopologyKey: k,
						Namespaces:  []string{ns},
					},
				},
			},
		}
		rc := getRCWithInterPodAffinityNodeSelector(labelRCName, labelsMap, replica, affinity,
			imageutils.GetPauseImageName(), map[string]string{k: v})
		defer framework.DeleteRCAndWaitForGC(f.ClientSet, ns, labelRCName)

		WaitForSchedulerAfterAction(f, func() error {
			_, err := cs.CoreV1().ReplicationControllers(ns).Create(rc)
			return err
		}, ns, labelRCName, false)

		// these two replicas should all be rejected since podAntiAffinity says it they anti-affinity with pod {"service": "S1"}
		verifyReplicasResult(cs, 0, replica, ns, labelRCName)
	})
})

// getRCWithInterPodAffinity returns RC with given affinity rules.
func getRCWithInterPodAffinity(name string, labelsMap map[string]string, replica int, affinity *v1.Affinity, image string) *v1.ReplicationController {
	return getRCWithInterPodAffinityNodeSelector(name, labelsMap, replica, affinity, image, map[string]string{})
}

// getRCWithInterPodAffinity returns RC with given affinity rules and node selector.
func getRCWithInterPodAffinityNodeSelector(name string, labelsMap map[string]string, replica int, affinity *v1.Affinity, image string, nodeSelector map[string]string) *v1.ReplicationController {
	replicaInt32 := int32(replica)
	return &v1.ReplicationController{
		ObjectMeta: metav1.ObjectMeta{
			Name: name,
		},
		Spec: v1.ReplicationControllerSpec{
			Replicas: &replicaInt32,
			Selector: labelsMap,
			Template: &v1.PodTemplateSpec{
				ObjectMeta: metav1.ObjectMeta{
					Labels: labelsMap,
				},
				Spec: v1.PodSpec{
					Affinity: affinity,
					Containers: []v1.Container{
						{
							Name:  name,
							Image: image,
						},
					},
					DNSPolicy:    v1.DNSDefault,
					NodeSelector: nodeSelector,
				},
			},
		},
	}
}

// CreateNodeSelectorPods creates RC with host port 4321 and defines node selector
func CreateNodeSelectorPods(f *framework.Framework, id string, replicas int, nodeSelector map[string]string, expectRunning bool) error {
	ginkgo.By(fmt.Sprintf("Running RC which reserves host port and defines node selector"))

	config := &testutils.RCConfig{
		Client:       f.ClientSet,
		Name:         id,
		Namespace:    f.Namespace.Name,
		Timeout:      defaultTimeout,
		Image:        imageutils.GetPauseImageName(),
		Replicas:     replicas,
		HostPorts:    map[string]int{"port1": 4321},
		NodeSelector: nodeSelector,
	}
	err := framework.RunRC(*config)
	if expectRunning {
		return err
	}
	return nil
}
<commit_msg>Adding an if statement to check the number of worker nodes available before running a test that needs 2 nodes.<commit_after>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package scheduling

import (
	"fmt"
	"time"

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/apimachinery/pkg/util/uuid"
	clientset "k8s.io/client-go/kubernetes"
	api "k8s.io/kubernetes/pkg/apis/core"
	"k8s.io/kubernetes/test/e2e/framework"
	e2elog "k8s.io/kubernetes/test/e2e/framework/log"
	e2enode "k8s.io/kubernetes/test/e2e/framework/node"
	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
	testutils "k8s.io/kubernetes/test/utils"
	imageutils "k8s.io/kubernetes/test/utils/image"

	"github.com/onsi/ginkgo"
	"github.com/onsi/gomega"
	// ensure libs have a chance to initialize
	_ "github.com/stretchr/testify/assert"
)

const (
	defaultTimeout = 3 * time.Minute
)

var _ = framework.KubeDescribe("EquivalenceCache [Serial]", func() {
	var cs clientset.Interface
	var nodeList *v1.NodeList
	var masterNodes sets.String
	var systemPodsNo int
	var ns string
	f := framework.NewDefaultFramework("equivalence-cache")

	ginkgo.BeforeEach(func() {
		cs = f.ClientSet
		ns = f.Namespace.Name

		e2enode.WaitForTotalHealthy(cs, time.Minute)
		masterNodes, nodeList = framework.GetMasterAndWorkerNodesOrDie(cs)

		framework.ExpectNoError(framework.CheckTestingNSDeletedExcept(cs, ns))

		// Every test case in this suite assumes that cluster add-on pods stay stable and
		// cannot be run in parallel with any other test that touches Nodes or Pods.
		// It is so because we need to have precise control on what's running in the cluster.
		systemPods, err := e2epod.GetPodsInNamespace(cs, ns, map[string]string{})
		framework.ExpectNoError(err)
		systemPodsNo = 0
		for _, pod := range systemPods {
			if !masterNodes.Has(pod.Spec.NodeName) && pod.DeletionTimestamp == nil {
				systemPodsNo++
			}
		}

		err = e2epod.WaitForPodsRunningReady(cs, api.NamespaceSystem, int32(systemPodsNo), int32(systemPodsNo), framework.PodReadyBeforeTimeout, map[string]string{})
		framework.ExpectNoError(err)

		for _, node := range nodeList.Items {
			e2elog.Logf("\nLogging pods the kubelet thinks is on node %v before test", node.Name)
			framework.PrintAllKubeletPods(cs, node.Name)
		}

	})

	// This test verifies that GeneralPredicates works as expected:
	// When a replica pod (with HostPorts) is scheduled to a node, it will invalidate GeneralPredicates cache on this node,
	// so that subsequent replica pods with same host port claim will be rejected.
	// We enforce all replica pods bind to the same node so there will always be conflicts.
	ginkgo.It("validates GeneralPredicates is properly invalidated when a pod is scheduled [Slow]", func() {
		ginkgo.By("Launching a RC with two replica pods with HostPorts")
		nodeName := getNodeThatCanRunPodWithoutToleration(f)
		rcName := "host-port"

		// bind all replicas to same node
		nodeSelector := map[string]string{"kubernetes.io/hostname": nodeName}

		ginkgo.By("One pod should be scheduled, the other should be rejected")
		// CreateNodeSelectorPods creates RC with host port 4321
		WaitForSchedulerAfterAction(f, func() error {
			err := CreateNodeSelectorPods(f, rcName, 2, nodeSelector, false)
			return err
		}, ns, rcName, false)
		defer framework.DeleteRCAndWaitForGC(f.ClientSet, ns, rcName)
		// the first replica pod is scheduled, and the second pod will be rejected.
		verifyResult(cs, 1, 1, ns)
	})

	// This test verifies that MatchInterPodAffinity works as expected.
	// In equivalence cache, it does not handle inter pod affinity (anti-affinity) specially (unless node label changed),
	// because current predicates algorithm will ensure newly scheduled pod does not break existing affinity in cluster.
	ginkgo.It("validates pod affinity works properly when new replica pod is scheduled", func() {
		// create a pod running with label {security: S1}, and choose this node
		nodeName, _ := runAndKeepPodWithLabelAndGetNodeName(f)

		ginkgo.By("Trying to apply a random label on the found node.")
		// we need to use real failure domains, since scheduler only know them
		k := "failure-domain.beta.kubernetes.io/zone"
		v := "equivalence-e2e-test"
		oldValue := framework.AddOrUpdateLabelOnNodeAndReturnOldValue(cs, nodeName, k, v)
		framework.ExpectNodeHasLabel(cs, nodeName, k, v)
		// restore the node label
		defer framework.AddOrUpdateLabelOnNode(cs, nodeName, k, oldValue)

		ginkgo.By("Trying to schedule RC with Pod Affinity should success.")
		framework.WaitForStableCluster(cs, masterNodes)
		affinityRCName := "with-pod-affinity-" + string(uuid.NewUUID())
		replica := 2
		labelsMap := map[string]string{
			"name": affinityRCName,
		}
		affinity := &v1.Affinity{
			PodAffinity: &v1.PodAffinity{
				RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
					{
						LabelSelector: &metav1.LabelSelector{
							MatchExpressions: []metav1.LabelSelectorRequirement{
								{
									Key:      "security",
									Operator: metav1.LabelSelectorOpIn,
									Values:   []string{"S1"},
								},
							},
						},
						TopologyKey: k,
						Namespaces:  []string{ns},
					},
				},
			},
		}
		rc := getRCWithInterPodAffinity(affinityRCName, labelsMap, replica, affinity, imageutils.GetPauseImageName())
		defer framework.DeleteRCAndWaitForGC(f.ClientSet, ns, affinityRCName)

		// RC should be running successfully
		// TODO: WaitForSchedulerAfterAction() can on be used to wait for failure event,
		// not for successful RC, since no specific pod name can be provided.
		_, err := cs.CoreV1().ReplicationControllers(ns).Create(rc)
		framework.ExpectNoError(err)
		framework.ExpectNoError(e2epod.WaitForControlledPodsRunning(cs, ns, affinityRCName, api.Kind("ReplicationController")))

		ginkgo.By("Remove node failure domain label")
		framework.RemoveLabelOffNode(cs, nodeName, k)

		ginkgo.By("Trying to schedule another equivalent Pod should fail due to node label has been removed.")
		// use scale to create another equivalent pod and wait for failure event
		WaitForSchedulerAfterAction(f, func() error {
			err := framework.ScaleRC(f.ClientSet, f.ScalesGetter, ns, affinityRCName, uint(replica+1), false)
			return err
		}, ns, affinityRCName, false)
		// and this new pod should be rejected since node label has been updated
		verifyReplicasResult(cs, replica, 1, ns, affinityRCName)
	})

	// This test verifies that MatchInterPodAffinity (anti-affinity) is respected as expected.
	ginkgo.It("validates pod anti-affinity works properly when new replica pod is scheduled", func() {
		// check if there are at least 2 worker nodes available, else skip this test.
		if len(nodeList.Items) < 2 {
			framework.Skipf("Skipping as the test requires at least two worker nodes, current number of nodes: %d", len(nodeList.Items))
		}
		ginkgo.By("Launching two pods on two distinct nodes to get two node names")
		CreateHostPortPods(f, "host-port", 2, true)
		defer framework.DeleteRCAndWaitForGC(f.ClientSet, ns, "host-port")
		podList, err := cs.CoreV1().Pods(ns).List(metav1.ListOptions{})
		framework.ExpectNoError(err)
		gomega.Expect(len(podList.Items)).To(gomega.Equal(2))
		nodeNames := []string{podList.Items[0].Spec.NodeName, podList.Items[1].Spec.NodeName}
		gomega.Expect(nodeNames[0]).ToNot(gomega.Equal(nodeNames[1]))

		ginkgo.By("Applying a random label to both nodes.")
		k := "e2e.inter-pod-affinity.kubernetes.io/zone"
		v := "equivalence-e2etest"
		for _, nodeName := range nodeNames {
			framework.AddOrUpdateLabelOnNode(cs, nodeName, k, v)
			framework.ExpectNodeHasLabel(cs, nodeName, k, v)
			defer framework.RemoveLabelOffNode(cs, nodeName, k)
		}

		ginkgo.By("Trying to launch a pod with the service label on the selected nodes.")
		// run a pod with label {"service": "S1"} and expect it to be running
		runPausePod(f, pausePodConfig{
			Name:         "with-label-" + string(uuid.NewUUID()),
			Labels:       map[string]string{"service": "S1"},
			NodeSelector: map[string]string{k: v}, // only launch on our two nodes
		})

		ginkgo.By("Trying to launch RC with podAntiAffinity on these two nodes should be rejected.")
		labelRCName := "with-podantiaffinity-" + string(uuid.NewUUID())
		replica := 2
		labelsMap := map[string]string{
			"name": labelRCName,
		}
		affinity := &v1.Affinity{
			PodAntiAffinity: &v1.PodAntiAffinity{
				RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
					{
						LabelSelector: &metav1.LabelSelector{
							MatchExpressions: []metav1.LabelSelectorRequirement{
								{
									Key:      "service",
									Operator: metav1.LabelSelectorOpIn,
									Values:   []string{"S1"},
								},
							},
						},
						TopologyKey: k,
						Namespaces:  []string{ns},
					},
				},
			},
		}
		rc := getRCWithInterPodAffinityNodeSelector(labelRCName, labelsMap, replica, affinity,
			imageutils.GetPauseImageName(), map[string]string{k: v})
		defer framework.DeleteRCAndWaitForGC(f.ClientSet, ns, labelRCName)

		WaitForSchedulerAfterAction(f, func() error {
			_, err := cs.CoreV1().ReplicationControllers(ns).Create(rc)
			return err
		}, ns, labelRCName, false)

		// these two replicas should all be rejected since podAntiAffinity says it they anti-affinity with pod {"service": "S1"}
		verifyReplicasResult(cs, 0, replica, ns, labelRCName)
	})
})

// getRCWithInterPodAffinity returns RC with given affinity rules.
func getRCWithInterPodAffinity(name string, labelsMap map[string]string, replica int, affinity *v1.Affinity, image string) *v1.ReplicationController {
	return getRCWithInterPodAffinityNodeSelector(name, labelsMap, replica, affinity, image, map[string]string{})
}

// getRCWithInterPodAffinity returns RC with given affinity rules and node selector.
func getRCWithInterPodAffinityNodeSelector(name string, labelsMap map[string]string, replica int, affinity *v1.Affinity, image string, nodeSelector map[string]string) *v1.ReplicationController {
	replicaInt32 := int32(replica)
	return &v1.ReplicationController{
		ObjectMeta: metav1.ObjectMeta{
			Name: name,
		},
		Spec: v1.ReplicationControllerSpec{
			Replicas: &replicaInt32,
			Selector: labelsMap,
			Template: &v1.PodTemplateSpec{
				ObjectMeta: metav1.ObjectMeta{
					Labels: labelsMap,
				},
				Spec: v1.PodSpec{
					Affinity: affinity,
					Containers: []v1.Container{
						{
							Name:  name,
							Image: image,
						},
					},
					DNSPolicy:    v1.DNSDefault,
					NodeSelector: nodeSelector,
				},
			},
		},
	}
}

// CreateNodeSelectorPods creates RC with host port 4321 and defines node selector
func CreateNodeSelectorPods(f *framework.Framework, id string, replicas int, nodeSelector map[string]string, expectRunning bool) error {
	ginkgo.By(fmt.Sprintf("Running RC which reserves host port and defines node selector"))

	config := &testutils.RCConfig{
		Client:       f.ClientSet,
		Name:         id,
		Namespace:    f.Namespace.Name,
		Timeout:      defaultTimeout,
		Image:        imageutils.GetPauseImageName(),
		Replicas:     replicas,
		HostPorts:    map[string]int{"port1": 4321},
		NodeSelector: nodeSelector,
	}
	err := framework.RunRC(*config)
	if expectRunning {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package wakatime

import (
	"net/http"
)

const ApiBase = "http://api"

type WakaTime struct {
	client *http.Client
}

func NewWakaTime(tr *http.Transport) *WakaTime {
	return &WakaTime{
		client: &http.Client{
			Transport: tr,
		},
	}
}

func (wt *WakaTime) Durations() {
	_, _ = wt.fetch("/durations")
}

func (wt *WakaTime) Stats() {}

func (wt *WakaTime) Summaries() {}

func (wt *WakaTime) Users() {}

func (wt *WakaTime) fetch(path string) (*http.Response, error) {
	return wt.client.Get(wt.getURL(path))
}

func (wt *WakaTime) getURL(path string) string {
	return ApiBase + path
}
<commit_msg>Started durations<commit_after>package wakatime

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
)

const ApiBase = "http://api"
const CurrentUser = "current"

type WakaTime struct {
	client *http.Client
}

type Durations struct{}

func NewWakaTime(tr *http.Transport) *WakaTime {
	return &WakaTime{
		client: &http.Client{
			Transport: tr,
		},
	}
}

func (wt *WakaTime) Durations(user string) (*Durations, error) {
	var err error
	var resp *http.Response
	if resp, err = wt.fetch("users/" + user + "/durations"); err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	var content []byte
	if content, err = ioutil.ReadAll(resp.Body); err != nil {
		return nil, err
	}
	var dr Durations
	if err = json.Unmarshal(content, dr); err != nil {
		return nil, err
	}
	return &dr, nil
}

func (wt *WakaTime) Stats() {}

func (wt *WakaTime) Summaries() {}

func (wt *WakaTime) Users() {}

func (wt *WakaTime) fetch(path string) (*http.Response, error) {
	return wt.client.Get(wt.getURL(path))
}

func (wt *WakaTime) getURL(path string) string {
	return ApiBase + path
}
<|endoftext|>


<commit_before>package plugin

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io"
	"net"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/health"
	"google.golang.org/grpc/health/grpc_health_v1"
)

// GRPCServiceName is the name of the service that the health check should
// return as passing.
const GRPCServiceName = "plugin"

// DefaultGRPCServer can be used with the "GRPCServer" field for Server
// as a default factory method to create a gRPC server with no extra options.
func DefaultGRPCServer(opts []grpc.ServerOption) *grpc.Server {
	return grpc.NewServer(opts...)
}

// GRPCServer is a ServerType implementation that serves plugins over
// gRPC. This allows plugins to easily be written for other languages.
//
// The GRPCServer outputs a custom configuration as a base64-encoded
// JSON structure represented by the GRPCServerConfig config structure.
type GRPCServer struct {
	// Plugins are the list of plugins to serve.
	Plugins map[string]Plugin

	// Server is the actual server that will accept connections. This
	// will be used for plugin registration as well.
	Server func([]grpc.ServerOption) *grpc.Server

	// TLS should be the TLS configuration if available. If this is nil,
	// the connection will not have transport security.
	TLS *tls.Config

	// DoneCh is the channel that is closed when this server has exited.
	DoneCh chan struct{}

	// Stdout/StderrLis are the readers for stdout/stderr that will be copied
	// to the stdout/stderr connection that is output.
	Stdout io.Reader
	Stderr io.Reader

	config GRPCServerConfig
	server *grpc.Server
	broker *GRPCBroker
}

// ServerProtocol impl.
func (s *GRPCServer) Init() error {
	// Create our server
	var opts []grpc.ServerOption
	if s.TLS != nil {
		opts = append(opts, grpc.Creds(credentials.NewTLS(s.TLS)))
	}
	s.server = s.Server(opts)

	// Register the health service
	healthCheck := health.NewServer()
	healthCheck.SetServingStatus(
		GRPCServiceName, grpc_health_v1.HealthCheckResponse_SERVING)
	grpc_health_v1.RegisterHealthServer(s.server, healthCheck)

	// Register the broker service
	brokerServer := newGRPCBrokerServer()
	RegisterGRPCBrokerServer(s.server, brokerServer)
	s.broker = newGRPCBroker(brokerServer, s.TLS)
	go s.broker.Run()

	// Register all our plugins onto the gRPC server.
	for k, raw := range s.Plugins {
		p, ok := raw.(GRPCPlugin)
		if !ok {
			return fmt.Errorf("%q is not a GRPC-compatibile plugin", k)
		}

		if err := p.GRPCServer(s.broker, s.server); err != nil {
			return fmt.Errorf("error registring %q: %s", k, err)
		}
	}

	return nil
}

// Config is the GRPCServerConfig encoded as JSON then base64.
func (s *GRPCServer) Config() string {
	// Create a buffer that will contain our final contents
	var buf bytes.Buffer

	// Wrap the base64 encoding with JSON encoding.
	if err := json.NewEncoder(&buf).Encode(s.config); err != nil {
		// We panic since ths shouldn't happen under any scenario. We
		// carefully control the structure being encoded here and it should
		// always be successful.
		panic(err)
	}

	return buf.String()
}

func (s *GRPCServer) Serve(lis net.Listener) {
	// Start serving in a goroutine
	go s.server.Serve(lis)

	// Wait until graceful completion
	<-s.DoneCh
}

// GRPCServerConfig is the extra configuration passed along for consumers
// to facilitate using GRPC plugins.
type GRPCServerConfig struct {
	StdoutAddr string `json:"stdout_addr"`
	StderrAddr string `json:"stderr_addr"`
}
<commit_msg>expose Stop and GracefulStop in GRPCServer<commit_after>package plugin

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io"
	"net"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/health"
	"google.golang.org/grpc/health/grpc_health_v1"
)

// GRPCServiceName is the name of the service that the health check should
// return as passing.
const GRPCServiceName = "plugin"

// DefaultGRPCServer can be used with the "GRPCServer" field for Server
// as a default factory method to create a gRPC server with no extra options.
func DefaultGRPCServer(opts []grpc.ServerOption) *grpc.Server {
	return grpc.NewServer(opts...)
}

// GRPCServer is a ServerType implementation that serves plugins over
// gRPC. This allows plugins to easily be written for other languages.
//
// The GRPCServer outputs a custom configuration as a base64-encoded
// JSON structure represented by the GRPCServerConfig config structure.
type GRPCServer struct {
	// Plugins are the list of plugins to serve.
	Plugins map[string]Plugin

	// Server is the actual server that will accept connections. This
	// will be used for plugin registration as well.
	Server func([]grpc.ServerOption) *grpc.Server

	// TLS should be the TLS configuration if available. If this is nil,
	// the connection will not have transport security.
	TLS *tls.Config

	// DoneCh is the channel that is closed when this server has exited.
	DoneCh chan struct{}

	// Stdout/StderrLis are the readers for stdout/stderr that will be copied
	// to the stdout/stderr connection that is output.
	Stdout io.Reader
	Stderr io.Reader

	config GRPCServerConfig
	server *grpc.Server
	broker *GRPCBroker
}

// ServerProtocol impl.
func (s *GRPCServer) Init() error {
	// Create our server
	var opts []grpc.ServerOption
	if s.TLS != nil {
		opts = append(opts, grpc.Creds(credentials.NewTLS(s.TLS)))
	}
	s.server = s.Server(opts)

	// Register the health service
	healthCheck := health.NewServer()
	healthCheck.SetServingStatus(
		GRPCServiceName, grpc_health_v1.HealthCheckResponse_SERVING)
	grpc_health_v1.RegisterHealthServer(s.server, healthCheck)

	// Register the broker service
	brokerServer := newGRPCBrokerServer()
	RegisterGRPCBrokerServer(s.server, brokerServer)
	s.broker = newGRPCBroker(brokerServer, s.TLS)
	go s.broker.Run()

	// Register all our plugins onto the gRPC server.
	for k, raw := range s.Plugins {
		p, ok := raw.(GRPCPlugin)
		if !ok {
			return fmt.Errorf("%q is not a GRPC-compatibile plugin", k)
		}

		if err := p.GRPCServer(s.broker, s.server); err != nil {
			return fmt.Errorf("error registring %q: %s", k, err)
		}
	}

	return nil
}

// Stop calls Stop on the underlying grpc.Server
func (s *GRPCServer) Stop() {
	s.server.Stop()
}

// GracefulStop calls GracefulStop on the underlying grpc.Server
func (s *GRPCServer) GracefulStop() {
	s.server.GracefulStop()
}

// Config is the GRPCServerConfig encoded as JSON then base64.
func (s *GRPCServer) Config() string {
	// Create a buffer that will contain our final contents
	var buf bytes.Buffer

	// Wrap the base64 encoding with JSON encoding.
	if err := json.NewEncoder(&buf).Encode(s.config); err != nil {
		// We panic since ths shouldn't happen under any scenario. We
		// carefully control the structure being encoded here and it should
		// always be successful.
		panic(err)
	}

	return buf.String()
}

func (s *GRPCServer) Serve(lis net.Listener) {
	// Start serving in a goroutine
	go s.server.Serve(lis)

	// Wait until graceful completion
	<-s.DoneCh
}

// GRPCServerConfig is the extra configuration passed along for consumers
// to facilitate using GRPC plugins.
type GRPCServerConfig struct {
	StdoutAddr string `json:"stdout_addr"`
	StderrAddr string `json:"stderr_addr"`
}
<|endoftext|>


<commit_before>package grawler

import (
	"io"

	"golang.org/x/net/html"
	"golang.org/x/net/html/atom"
)

func tokenIsAnchor(t *html.Token) (string, bool) {
	return "href", t.DataAtom == atom.A
}

func tokenIsScript(t *html.Token) (string, bool) {
	return "src", t.DataAtom == atom.Script
}

func tokenIsLink(t *html.Token) (string, bool) {
	return "href", t.DataAtom == atom.Link
}

func tokenIsImg(t *html.Token) (string, bool) {
	return "src", t.DataAtom == atom.Img
}

func isLink(t *html.Token) (string, bool) {
	return tokenIsAnchor(t)
}

func isAsset(t *html.Token) (string, bool) {
	if tag, ok := tokenIsScript(t); ok {
		return tag, ok
	} else if tag, ok := tokenIsLink(t); ok {
		return tag, ok
	} else if tag, ok := tokenIsImg(t); ok {
		return tag, ok
	}
	return "", false
}

func findAttr(name string, token *html.Token) *html.Attribute {
	for _, attr := range token.Attr {
		if attr.Key == name {
			return &attr
		}
	}
	return nil
}

func findLinksAndAssets(rdr io.Reader) ([]string, []string) {
	links := []string{}
	assets := []string{}

	doc := html.NewTokenizer(rdr)

	for tokenType := doc.Next(); tokenType != html.ErrorToken; tokenType = doc.Next() {
		token := doc.Token()
		switch tokenType {
		case html.StartTagToken:
			fallthrough
		case html.SelfClosingTagToken:
			// TODO XXX
			// Which HTML elements can have href=? and/or src=?
			// https://www.w3.org/TR/REC-html40/index/attributes.html
			if tag, ok := isLink(&token); ok {
				if attr := findAttr(tag, &token); attr != nil {
					links = append(links, attr.Val)
				}
			}
			if tag, ok := isAsset(&token); ok {
				if attr := findAttr(tag, &token); attr != nil {
					assets = append(assets, attr.Val)
				}
			}
		}
	}

	return links, assets
}
<commit_msg>links: only call doc.Token() when necessary<commit_after>package grawler

import (
	"io"

	"golang.org/x/net/html"
	"golang.org/x/net/html/atom"
)

func tokenIsAnchor(t *html.Token) (string, bool) {
	return "href", t.DataAtom == atom.A
}

func tokenIsScript(t *html.Token) (string, bool) {
	return "src", t.DataAtom == atom.Script
}

func tokenIsLink(t *html.Token) (string, bool) {
	return "href", t.DataAtom == atom.Link
}

func tokenIsImg(t *html.Token) (string, bool) {
	return "src", t.DataAtom == atom.Img
}

func isLink(t *html.Token) (string, bool) {
	return tokenIsAnchor(t)
}

func isAsset(t *html.Token) (string, bool) {
	if tag, ok := tokenIsScript(t); ok {
		return tag, ok
	} else if tag, ok := tokenIsLink(t); ok {
		return tag, ok
	} else if tag, ok := tokenIsImg(t); ok {
		return tag, ok
	}
	return "", false
}

func findAttr(name string, token *html.Token) *html.Attribute {
	for _, attr := range token.Attr {
		if attr.Key == name {
			return &attr
		}
	}
	return nil
}

func findLinksAndAssets(rdr io.Reader) ([]string, []string) {
	links := []string{}
	assets := []string{}

	doc := html.NewTokenizer(rdr)

	for tokenType := doc.Next(); tokenType != html.ErrorToken; tokenType = doc.Next() {
		switch tokenType {
		case html.StartTagToken:
			fallthrough
		case html.SelfClosingTagToken:
			token := doc.Token()
			// TODO XXX
			// Which HTML elements can have href=? and/or src=?
			// https://www.w3.org/TR/REC-html40/index/attributes.html
			if tag, ok := isLink(&token); ok {
				if attr := findAttr(tag, &token); attr != nil {
					links = append(links, attr.Val)
				}
			}
			if tag, ok := isAsset(&token); ok {
				if attr := findAttr(tag, &token); attr != nil {
					assets = append(assets, attr.Val)
				}
			}
		}
	}

	return links, assets
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"

	"github.com/hyperledger/fabric/core/chaincode/shim"
)

type SimpleChaincode struct {
}

func main() {
	err := shim.Start(new(SimpleChaincode))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode: %s", err)
	}
}

func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	if len(args) != 1 {
		return nil, errors.New("incorrect number of args")
	}

	err := stub.PutState("hello_world", []byte(args[0]))
	if err != nil {
		return nil, err
	}

	return nil, nil
}

func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	fmt.Println("invoke is running" + function)

	if function == "init" {
		return t.Init(stub, "init", args)
	} else if function == "write" {
		return t.write(stub, args)
	}
	fmt.Println("invoke did not find func:" + function)

	return nil, errors.New("received unknow function" + function)
}

func (t *SimpleChaincode) Query(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	fmt.Println("query is running " + function)

	if function == "read" {
		return t.read(stub, args)
	}

	if function == "read2" {
		return t.read2(stub, args)
	}

	if function == "helloworld" {
		return t.helloworld()
	}

	fmt.Println("query did not find func: " + function)

	return nil, errors.New("received unknow function" + function)
}

func (t *SimpleChaincode) read2(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	key := args[0]
	result := []byte("helloworld" + key)
	var err error
	return result, err
}

func (t *SimpleChaincode) helloworld() ([]byte, error) {
	result := []byte("hello world")
	return result, nil
}

func (t *SimpleChaincode) write(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	var key, value string
	var err error
	fmt.Println("running write()")

	if len(args) != 2 {
		return nil, errors.New("Incorrect number of arguments. Expecting 2")
	}

	key = args[0]
	value = args[1]

	err = stub.PutState(key, []byte(value))

	if err != nil {
		return nil, err
	}

	return nil, nil
}

func (t *SimpleChaincode) read(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	var key, jsonResp string
	var err error

	if len(args) != 1 {
		return nil, errors.New("incorrect number of arguments. expecting 1")
	}

	key = args[0]
	valAsbytes, err := stub.GetState(key)

	if err != nil {
		jsonResp = "{\"Error\":\"Failed to get state for " + key + "\"}"
		return nil, errors.New(jsonResp)
	}

	return valAsbytes, nil
}
<commit_msg>demo04.go use switch instead of if+else<commit_after>package main

import (
	"errors"
	"fmt"

	"github.com/hyperledger/fabric/core/chaincode/shim"
)

type SimpleChaincode struct {
}

func main() {
	err := shim.Start(new(SimpleChaincode))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode: %s", err)
	}
}

func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	if len(args) != 1 {
		return nil, errors.New("incorrect number of args")
	}

	err := stub.PutState("hello_world", []byte(args[0]))
	if err != nil {
		return nil, err
	}

	return nil, nil
}

func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	fmt.Println("invoke is running" + function)

	if function == "init" {
		return t.Init(stub, "init", args)
	} else if function == "write" {
		return t.write(stub, args)
	}
	fmt.Println("invoke did not find func:" + function)

	return nil, errors.New("received unknow function" + function)
}

func (t *SimpleChaincode) Query(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	fmt.Println("query is running " + function)

	// old code @20170307
	// if function == "read" {
	// 	return t.read(stub, args)
	// }
	//
	// if function == "read2" {
	// 	return t.read2(stub, args)
	// }
	//
	// if function == "helloworld" {
	// 	return t.helloworld()
	// }

	switch function {
	case "read":
		return t.read(stub, args)
	case "read2":
		return t.read2(stub, args)
	case "helloworld":
		return t.helloworld()
	default:
		fmt.Println("query did not find func: " + function)
	}
	return nil, errors.New("received unknow function: " + function)
}

func (t *SimpleChaincode) read2(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	key := args[0]
	result := []byte("helloworld" + key)
	var err error
	return result, err
}

func (t *SimpleChaincode) helloworld() ([]byte, error) {
	result := []byte("hello world")
	return result, nil
}

func (t *SimpleChaincode) write(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	var key, value string
	var err error
	fmt.Println("running write()")

	if len(args) != 2 {
		return nil, errors.New("Incorrect number of arguments. Expecting 2")
	}

	key = args[0]
	value = args[1]

	err = stub.PutState(key, []byte(value))

	if err != nil {
		return nil, err
	}

	return nil, nil
}

func (t *SimpleChaincode) read(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	var key, jsonResp string
	var err error

	if len(args) != 1 {
		return nil, errors.New("incorrect number of arguments. expecting 1")
	}

	key = args[0]
	valAsbytes, err := stub.GetState(key)

	if err != nil {
		jsonResp = "{\"Error\":\"Failed to get state for " + key + "\"}"
		return nil, errors.New(jsonResp)
	}

	return valAsbytes, nil
}
<|endoftext|>


<commit_before>//
// Copyright 2018, Sander van Harmelen
//
// 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 gitlab

import (
	"fmt"
	"time"
)

// DeploymentsService handles communication with the deployment related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/deployments.html
type DeploymentsService struct {
	client *Client
}

// Deployment represents the Gitlab deployment
type Deployment struct {
	ID          int          `json:"id"`
	IID         int          `json:"iid"`
	Ref         string       `json:"ref"`
	SHA         string       `json:"sha"`
	CreatedAt   *time.Time   `json:"created_at"`
	User        *ProjectUser `json:"user"`
	Environment *Environment `json:"environment"`
	Deployable  struct {
		ID         int        `json:"id"`
		Status     string     `json:"status"`
		Stage      string     `json:"stage"`
		Name       string     `json:"name"`
		Ref        string     `json:"ref"`
		Tag        bool       `json:"tag"`
		Coverage   float64    `json:"coverage"`
		CreatedAt  *time.Time `json:"created_at"`
		StartedAt  *time.Time `json:"started_at"`
		FinishedAt *time.Time `json:"finished_at"`
		Duration   float64    `json:"duration"`
		User       *User      `json:"user"`
		Commit     *Commit    `json:"commit"`
		Pipeline   struct {
			ID     int    `json:"id"`
			SHA    string `json:"sha"`
			Ref    string `json:"ref"`
			Status string `json:"status"`
		} `json:"pipeline"`
		Runner *Runner `json:"runner"`
	} `json:"deployable"`
}

// ListProjectDeploymentsOptions represents the available ListProjectDeployments() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/deployments.html#list-project-deployments
type ListProjectDeploymentsOptions struct {
	ListOptions
	OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
	Sort    *string `url:"sort,omitempty" json:"sort,omitempty"`
}

// ListProjectDeployments gets a list of deployments in a project.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/deployments.html#list-project-deployments
func (s *DeploymentsService) ListProjectDeployments(pid interface{}, opts *ListProjectDeploymentsOptions, options ...OptionFunc) ([]*Deployment, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/deployments", pathEscape(project))

	req, err := s.client.NewRequest("GET", u, opts, options)
	if err != nil {
		return nil, nil, err
	}

	var ds []*Deployment
	resp, err := s.client.Do(req, &ds)
	if err != nil {
		return nil, resp, err
	}

	return ds, resp, err
}

// GetProjectDeployment get a deployment for a project.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/deployments.html#get-a-specific-deployment
func (s *DeploymentsService) GetProjectDeployment(pid interface{}, deployment int, options ...OptionFunc) (*Deployment, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/deployments/%d", pathEscape(project), deployment)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	d := new(Deployment)
	resp, err := s.client.Do(req, d)
	if err != nil {
		return nil, resp, err
	}

	return d, resp, err
}

// CreateProjectDeploymentOptions represents the available
// CreateProjectDeployment() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/deployments.html#create-a-deployment
type CreateProjectDeploymentOptions struct {
	Environment *string                `url:"environment,omitempty" json:"environment,omitempty"`
	Ref         *string                `url:"ref,omitempty" json:"ref,omitempty"`
	SHA         *string                `url:"sha,omitempty" json:"sha,omitempty"`
	Tag         *bool                  `url:"tag,omitempty" json:"tag,omitempty"`
	Status      *DeploymentStatusValue `url:"status,omitempty" json:"status,omitempty"`
}

// CreateProjectDeployment creates a project deployment.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/deployments.html#create-a-deployment
func (s *DeploymentsService) CreateProjectDeployment(pid interface{}, opt *CreateProjectDeploymentOptions, options ...OptionFunc) (*Deployment, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/deployments", pathEscape(project))

	req, err := s.client.NewRequest("POST", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	d := new(Deployment)
	resp, err := s.client.Do(req, &d)
	if err != nil {
		return nil, resp, err
	}

	return d, resp, err
}

// UpdateProjectDeploymentOptions represents the available
// UpdateProjectDeployment() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/deployments.html#updating-a-deployment
type UpdateProjectDeploymentOptions struct {
	Status *DeploymentStatusValue `url:"status,omitempty" json:"status,omitempty"`
}

// UpdateProjectDeployment updates a project deployment.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/deployments.html#updating-a-deployment
func (s *DeploymentsService) UpdateProjectDeployment(pid interface{}, deployment int, opt *UpdateProjectDeploymentOptions, options ...OptionFunc) (*Deployment, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/deployments/%d", pathEscape(project), deployment)

	req, err := s.client.NewRequest("PUT", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	d := new(Deployment)
	resp, err := s.client.Do(req, &d)
	if err != nil {
		return nil, resp, err
	}

	return d, resp, err
}
<commit_msg>Add four more options to ListProjectDeployments call<commit_after>//
// Copyright 2018, Sander van Harmelen
//
// 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 gitlab

import (
	"fmt"
	"time"
)

// DeploymentsService handles communication with the deployment related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/deployments.html
type DeploymentsService struct {
	client *Client
}

// Deployment represents the Gitlab deployment
type Deployment struct {
	ID          int          `json:"id"`
	IID         int          `json:"iid"`
	Ref         string       `json:"ref"`
	SHA         string       `json:"sha"`
	CreatedAt   *time.Time   `json:"created_at"`
	User        *ProjectUser `json:"user"`
	Environment *Environment `json:"environment"`
	Deployable  struct {
		ID         int        `json:"id"`
		Status     string     `json:"status"`
		Stage      string     `json:"stage"`
		Name       string     `json:"name"`
		Ref        string     `json:"ref"`
		Tag        bool       `json:"tag"`
		Coverage   float64    `json:"coverage"`
		CreatedAt  *time.Time `json:"created_at"`
		StartedAt  *time.Time `json:"started_at"`
		FinishedAt *time.Time `json:"finished_at"`
		Duration   float64    `json:"duration"`
		User       *User      `json:"user"`
		Commit     *Commit    `json:"commit"`
		Pipeline   struct {
			ID     int    `json:"id"`
			SHA    string `json:"sha"`
			Ref    string `json:"ref"`
			Status string `json:"status"`
		} `json:"pipeline"`
		Runner *Runner `json:"runner"`
	} `json:"deployable"`
}

// ListProjectDeploymentsOptions represents the available ListProjectDeployments() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/deployments.html#list-project-deployments
type ListProjectDeploymentsOptions struct {
	ListOptions
	OrderBy       *string    `url:"order_by,omitempty" json:"order_by,omitempty"`
	Sort          *string    `url:"sort,omitempty" json:"sort,omitempty"`
	UpdatedAfter  *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
	UpdatedBefore *time.Time `url:"update_before,omitempty" json:"updated_before,omitempty"`
	Environment   *string    `url:"environment,omitempty" json:"environment,omitempty"`
	Status        *string    `url:"status,omitempty" json:"status,omitempty"`
}

// ListProjectDeployments gets a list of deployments in a project.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/deployments.html#list-project-deployments
func (s *DeploymentsService) ListProjectDeployments(pid interface{}, opts *ListProjectDeploymentsOptions, options ...OptionFunc) ([]*Deployment, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/deployments", pathEscape(project))

	req, err := s.client.NewRequest("GET", u, opts, options)
	if err != nil {
		return nil, nil, err
	}

	var ds []*Deployment
	resp, err := s.client.Do(req, &ds)
	if err != nil {
		return nil, resp, err
	}

	return ds, resp, err
}

// GetProjectDeployment get a deployment for a project.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/deployments.html#get-a-specific-deployment
func (s *DeploymentsService) GetProjectDeployment(pid interface{}, deployment int, options ...OptionFunc) (*Deployment, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/deployments/%d", pathEscape(project), deployment)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	d := new(Deployment)
	resp, err := s.client.Do(req, d)
	if err != nil {
		return nil, resp, err
	}

	return d, resp, err
}

// CreateProjectDeploymentOptions represents the available
// CreateProjectDeployment() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/deployments.html#create-a-deployment
type CreateProjectDeploymentOptions struct {
	Environment *string                `url:"environment,omitempty" json:"environment,omitempty"`
	Ref         *string                `url:"ref,omitempty" json:"ref,omitempty"`
	SHA         *string                `url:"sha,omitempty" json:"sha,omitempty"`
	Tag         *bool                  `url:"tag,omitempty" json:"tag,omitempty"`
	Status      *DeploymentStatusValue `url:"status,omitempty" json:"status,omitempty"`
}

// CreateProjectDeployment creates a project deployment.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/deployments.html#create-a-deployment
func (s *DeploymentsService) CreateProjectDeployment(pid interface{}, opt *CreateProjectDeploymentOptions, options ...OptionFunc) (*Deployment, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/deployments", pathEscape(project))

	req, err := s.client.NewRequest("POST", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	d := new(Deployment)
	resp, err := s.client.Do(req, &d)
	if err != nil {
		return nil, resp, err
	}

	return d, resp, err
}

// UpdateProjectDeploymentOptions represents the available
// UpdateProjectDeployment() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/deployments.html#updating-a-deployment
type UpdateProjectDeploymentOptions struct {
	Status *DeploymentStatusValue `url:"status,omitempty" json:"status,omitempty"`
}

// UpdateProjectDeployment updates a project deployment.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/deployments.html#updating-a-deployment
func (s *DeploymentsService) UpdateProjectDeployment(pid interface{}, deployment int, opt *UpdateProjectDeploymentOptions, options ...OptionFunc) (*Deployment, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/deployments/%d", pathEscape(project), deployment)

	req, err := s.client.NewRequest("PUT", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	d := new(Deployment)
	resp, err := s.client.Do(req, &d)
	if err != nil {
		return nil, resp, err
	}

	return d, resp, err
}
<|endoftext|>


<commit_before>package mockroute53

import (
	"github.com/aws/aws-sdk-go/aws/request"
	"github.com/aws/aws-sdk-go/service/route53"
	"github.com/golang/glog"
)

type zoneInfo struct {
}

func (m *MockRoute53) ListHostedZonesRequest(*route53.ListHostedZonesInput) (*request.Request, *route53.ListHostedZonesOutput) {
	panic("MockRoute53 function not implemented")
	return nil, nil
}

func (m *MockRoute53) ListHostedZones(*route53.ListHostedZonesInput) (*route53.ListHostedZonesOutput, error) {
	panic("MockRoute53 function not implemented")
	return nil, nil
}

func (m *MockRoute53) ListHostedZonesPages(request *route53.ListHostedZonesInput, callback func(*route53.ListHostedZonesOutput, bool) bool) error {
	glog.Infof("ListHostedZonesPages %v", request)

	page := &route53.ListHostedZonesOutput{}
	for _, zone := range m.Zones {
		copy := *zone
		page.HostedZones = append(page.HostedZones, &copy)
	}
	lastPage := true
	callback(page, lastPage)

	return nil
}

func (m *MockRoute53) ListHostedZonesByNameRequest(*route53.ListHostedZonesByNameInput) (*request.Request, *route53.ListHostedZonesByNameOutput) {
	panic("MockRoute53 function not implemented")
	return nil, nil
}

func (m *MockRoute53) ListHostedZonesByName(*route53.ListHostedZonesByNameInput) (*route53.ListHostedZonesByNameOutput, error) {
	panic("MockRoute53 function not implemented")
	return nil, nil
}
<commit_msg>Fix boilerplate on zones.go<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package mockroute53

import (
	"github.com/aws/aws-sdk-go/aws/request"
	"github.com/aws/aws-sdk-go/service/route53"
	"github.com/golang/glog"
)

type zoneInfo struct {
}

func (m *MockRoute53) ListHostedZonesRequest(*route53.ListHostedZonesInput) (*request.Request, *route53.ListHostedZonesOutput) {
	panic("MockRoute53 function not implemented")
	return nil, nil
}

func (m *MockRoute53) ListHostedZones(*route53.ListHostedZonesInput) (*route53.ListHostedZonesOutput, error) {
	panic("MockRoute53 function not implemented")
	return nil, nil
}

func (m *MockRoute53) ListHostedZonesPages(request *route53.ListHostedZonesInput, callback func(*route53.ListHostedZonesOutput, bool) bool) error {
	glog.Infof("ListHostedZonesPages %v", request)

	page := &route53.ListHostedZonesOutput{}
	for _, zone := range m.Zones {
		copy := *zone
		page.HostedZones = append(page.HostedZones, &copy)
	}
	lastPage := true
	callback(page, lastPage)

	return nil
}

func (m *MockRoute53) ListHostedZonesByNameRequest(*route53.ListHostedZonesByNameInput) (*request.Request, *route53.ListHostedZonesByNameOutput) {
	panic("MockRoute53 function not implemented")
	return nil, nil
}

func (m *MockRoute53) ListHostedZonesByName(*route53.ListHostedZonesByNameInput) (*route53.ListHostedZonesByNameOutput, error) {
	panic("MockRoute53 function not implemented")
	return nil, nil
}
<|endoftext|>


<commit_before>package main

import (
	"time"

	log "github.com/Sirupsen/logrus"
	logx "github.com/cerana/cerana/pkg/logrusx"
	"github.com/cerana/cerana/provider"
	"github.com/cerana/cerana/providers/clusterconf"
	flag "github.com/spf13/pflag"
)

func main() {
	log.SetFormatter(&logx.JSONFormatter{})

	config := clusterconf.NewConfig(nil, nil)
	flag.DurationP("dataset-ttl", "d", time.Minute, "ttl for dataset usage heartbeats")
	flag.DurationP("bundle-ttl", "b", time.Minute, "ttl for bundle usage heartbeats")
	flag.DurationP("node-ttl", "o", time.Minute, "ttl for node heartbeats")
	flag.Parse()

	dieOnError(config.LoadConfig())
	dieOnError(config.SetupLogging())

	server, err := provider.NewServer(config.Config)
	dieOnError(err)
	c := clusterconf.New(config, server.Tracker())
	dieOnError(err)
	c.RegisterTasks(server)

	if len(server.RegisteredTasks()) != 0 {
		dieOnError(server.Start())
		server.StopOnSignal()
	} else {
		log.Warn("no registered tasks, exiting")
	}
}

func dieOnError(err error) {
	if err != nil {
		log.Fatal("encountered an error during startup")
	}
}
<commit_msg>Underscore for consistency<commit_after>package main

import (
	"time"

	log "github.com/Sirupsen/logrus"
	logx "github.com/cerana/cerana/pkg/logrusx"
	"github.com/cerana/cerana/provider"
	"github.com/cerana/cerana/providers/clusterconf"
	flag "github.com/spf13/pflag"
)

func main() {
	log.SetFormatter(&logx.JSONFormatter{})

	config := clusterconf.NewConfig(nil, nil)
	flag.DurationP("dataset_ttl", "d", time.Minute, "ttl for dataset usage heartbeats")
	flag.DurationP("bundle_ttl", "b", time.Minute, "ttl for bundle usage heartbeats")
	flag.DurationP("node_ttl", "o", time.Minute, "ttl for node heartbeats")
	flag.Parse()

	dieOnError(config.LoadConfig())
	dieOnError(config.SetupLogging())

	server, err := provider.NewServer(config.Config)
	dieOnError(err)
	c := clusterconf.New(config, server.Tracker())
	dieOnError(err)
	c.RegisterTasks(server)

	if len(server.RegisteredTasks()) != 0 {
		dieOnError(server.Start())
		server.StopOnSignal()
	} else {
		log.Warn("no registered tasks, exiting")
	}
}

func dieOnError(err error) {
	if err != nil {
		log.Fatal("encountered an error during startup")
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2019 Google LLC

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

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

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

package main

import (
	"cloud.google.com/go/compute/metadata"
	"flag"
	"fmt"
	"github.com/google/knative-gcp/pkg/pubsub/adapter"
	"github.com/google/knative-gcp/pkg/reconciler/pullsubscription/resources"
	"github.com/kelseyhightower/envconfig"
	"go.uber.org/zap"
	"knative.dev/pkg/logging"
	"knative.dev/pkg/metrics"
	"knative.dev/pkg/signals"
	"log"
)

const (
	component = "PullSubscription::ReceiveAdapter"
)

func main() {
	flag.Parse()

	startable := adapter.Adapter{}
	if err := envconfig.Process("", &startable); err != nil {
		panic(fmt.Sprintf("Failed to process env var: %s", err))
	}

	// Convert base64 encoded json logging.Config to logging.Config.
	loggingConfig, err := resources.Base64ToLoggingConfig(startable.LoggingConfigBase64)
	if err != nil {
		fmt.Printf("[ERROR] filed to process logging config: %s", err.Error())
		// Use default logging config.
		if loggingConfig, err = logging.NewConfigFromMap(map[string]string{}); err != nil {
			// If this fails, there is no recovering.
			panic(err)
		}
	}

	logger, _ := logging.NewLoggerFromConfig(loggingConfig, component)
	defer flush(logger)
	ctx := logging.WithLogger(signals.NewContext(), logger)

	// Convert base64 encoded json metrics.ExporterOptions to metrics.ExporterOptions.
	metricsConfig, err := resources.Base64ToMetricsOptions(startable.MetricsConfigBase64)
	if err != nil {
		logger.Fatalf("failed to process metrics options: %s", err.Error())
	}

	if metricsConfig == nil {
		logger.Fatal("invalid metric options: nil")
	}

	if err := metrics.UpdateExporter(*metricsConfig, logger); err != nil {
		log.Fatalf("Failed to create the metrics exporter: %s", err.Error())
	}

	if startable.Project == "" {
		project, err := metadata.ProjectID()
		if err != nil {
			logger.Fatal("failed to find project id. ", zap.Error(err))
		}
		startable.Project = project
	}

	logger.Info("using project.", zap.String("project", startable.Project))

	logger.Info("Starting Pub/Sub Receive Adapter.", zap.Any("adapter", startable))
	if err := startable.Start(ctx); err != nil {
		logger.Fatal("failed to start adapter: ", zap.Error(err))
	}
}

func flush(logger *zap.SugaredLogger) {
	_ = logger.Sync()
	metrics.FlushExporter()
}
<commit_msg>main metrics<commit_after>/*
Copyright 2019 Google LLC

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

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

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

package main

import (
	"cloud.google.com/go/compute/metadata"
	"flag"
	"fmt"
	"github.com/google/knative-gcp/pkg/pubsub/adapter"
	"github.com/google/knative-gcp/pkg/reconciler/pullsubscription/resources"
	"github.com/kelseyhightower/envconfig"
	"go.uber.org/zap"
	"knative.dev/pkg/logging"
	"knative.dev/pkg/metrics"
	"knative.dev/pkg/signals"
	"log"
)

const (
	component = "PullSubscription::ReceiveAdapter"
)

func main() {
	flag.Parse()

	startable := adapter.Adapter{}
	if err := envconfig.Process("", &startable); err != nil {
		panic(fmt.Sprintf("Failed to process env var: %s", err))
	}

	// Convert base64 encoded json logging.Config to logging.Config.
	loggingConfig, err := resources.Base64ToLoggingConfig(startable.LoggingConfigBase64)
	if err != nil {
		fmt.Printf("[ERROR] filed to process logging config: %s", err.Error())
		// Use default logging config.
		if loggingConfig, err = logging.NewConfigFromMap(map[string]string{}); err != nil {
			// If this fails, there is no recovering.
			panic(err)
		}
	}

	logger, _ := logging.NewLoggerFromConfig(loggingConfig, component)
	defer flush(logger)
	ctx := logging.WithLogger(signals.NewContext(), logger)

	// Convert base64 encoded json metrics.ExporterOptions to metrics.ExporterOptions.
	metricsConfig, err := resources.Base64ToMetricsOptions(startable.MetricsConfigBase64)
	if err != nil {
		logger.Errorf("failed to process metrics options: %s", err.Error())
	}

	mainMetrics(logger, metricsConfig)

	if startable.Project == "" {
		project, err := metadata.ProjectID()
		if err != nil {
			logger.Fatal("failed to find project id. ", zap.Error(err))
		}
		startable.Project = project
	}

	logger.Info("using project.", zap.String("project", startable.Project))

	logger.Info("Starting Pub/Sub Receive Adapter.", zap.Any("adapter", startable))
	if err := startable.Start(ctx); err != nil {
		logger.Fatal("failed to start adapter: ", zap.Error(err))
	}
}

func mainMetrics(logger *zap.SugaredLogger, opts *metrics.ExporterOptions) {
	if opts == nil {
		logger.Info("metrics disabled")
		return
	}

	if err := metrics.UpdateExporter(*opts, logger); err != nil {
		log.Fatalf("Failed to create the metrics exporter: %v", err)
	}

	// TODO metrics are API surface, so make sure we need to expose this before doing so.
	// Register the views
	//if err := view.Register(
	//	client.LatencyView,
	//	transporthttp.LatencyView,
	//	json.LatencyView,
	//	xml.LatencyView,
	//	datacodec.LatencyView,
	//	adapter.LatencyView,
	//); err != nil {
	//	log.Fatalf("Failed to register views: %v", err)
	//}
	//
	//view.SetReportingPeriod(2 * time.Second)
}

func flush(logger *zap.SugaredLogger) {
	_ = logger.Sync()
	metrics.FlushExporter()
}
<|endoftext|>


<commit_before>package mysql

import (
	"context"
	"fmt"
	"time"

	"github.com/percona/pmm/proto"
	pc "github.com/percona/pmm/proto/config"
	"github.com/percona/qan-agent/data"
	"github.com/percona/qan-agent/mrms"
	"github.com/percona/qan-agent/mysql"
	"github.com/percona/qan-agent/pct"
	"github.com/percona/qan-agent/qan/analyzer"
	"github.com/percona/qan-agent/qan/analyzer/mysql/config"
	"github.com/percona/qan-agent/qan/analyzer/mysql/factory"
	"github.com/percona/qan-agent/qan/analyzer/mysql/iter"
	"github.com/percona/qan-agent/qan/analyzer/mysql/worker"
	"github.com/percona/qan-agent/qan/analyzer/mysql/worker/perfschema"
	"github.com/percona/qan-agent/qan/analyzer/mysql/worker/slowlog"
	"github.com/percona/qan-agent/ticker"
)

func New(ctx context.Context, protoInstance proto.Instance) analyzer.Analyzer {
	// Get available services from ctx
	services, _ := ctx.Value("services").(map[string]interface{})

	// Get services we need
	logger, _ := services["logger"].(*pct.Logger)
	clock, _ := services["clock"].(ticker.Manager)
	spool, _ := services["spool"].(data.Spooler)
	mrms, _ := services["mrms"].(mrms.Monitor)

	// Create internal services we need
	logChan := logger.LogChan()
	iterFactory := factory.NewRealIntervalIterFactory(logChan)
	slowlogWorkerFactory := slowlog.NewRealWorkerFactory(logChan)
	perfschemaWorkerFactory := perfschema.NewRealWorkerFactory(logChan)
	mysqlConnFactory := &mysql.RealConnectionFactory{}

	// return initialized MySQLAnalyzer
	return &MySQLAnalyzer{
		// on initialization config and analyzer are uninitialized
		config:   pc.QAN{},
		analyzer: nil,
		// initialize
		protoInstance:           protoInstance,
		logger:                  logger,
		clock:                   clock,
		spool:                   spool,
		mrms:                    mrms,
		iterFactory:             iterFactory,
		slowlogWorkerFactory:    slowlogWorkerFactory,
		perfschemaWorkerFactory: perfschemaWorkerFactory,
		mysqlConnFactory:        mysqlConnFactory,
	}
}

// MySQLAnalyzer is a proxy Analyzer between QAN manager
// and MySQL implementations of Slowlog Analyzer and Perfschema Analyzer
type MySQLAnalyzer struct {
	// on initialization config and analyzer are uninitialized
	config   pc.QAN
	analyzer analyzer.Analyzer
	// services initialized in New
	protoInstance           proto.Instance
	logger                  *pct.Logger
	clock                   ticker.Manager
	spool                   data.Spooler
	mrms                    mrms.Monitor
	iterFactory             iter.IntervalIterFactory
	slowlogWorkerFactory    slowlog.WorkerFactory
	perfschemaWorkerFactory perfschema.WorkerFactory
	mysqlConnFactory        mysql.ConnectionFactory
	// real analyzer channels
	tickChan    chan time.Time
	restartChan chan proto.Instance
}

// SetConfig sets the config
func (m *MySQLAnalyzer) SetConfig(setConfig pc.QAN) {
	m.config = setConfig
	if m.analyzer != nil {
		m.analyzer.SetConfig(m.config)
	}
}

// Config returns analyzer running configuration
func (m *MySQLAnalyzer) Config() pc.QAN {
	if m.analyzer != nil {
		m.config = m.analyzer.Config()
	}

	return m.config
}

// Start starts analyzer but doesn't wait until it exits
func (m *MySQLAnalyzer) Start() error {
	setConfig := m.Config()

	// Create a MySQL connection.
	mysqlConn := m.mysqlConnFactory.Make(m.protoInstance.DSN)

	// Validate and transform the set config and into a running config.
	config, err := config.ValidateConfig(setConfig)
	if err != nil {
		return fmt.Errorf("invalid QAN config: %s", err)
	}

	// Add the MySQL DSN to the MySQL restart monitor. If MySQL restarts,
	// the analyzer will stop its worker and re-configure MySQL.
	restartChan := m.mrms.Add(m.protoInstance)

	// Make a chan on which the clock will tick at even intervals:
	// clock -> tickChan -> iter -> analyzer -> worker
	tickChan := make(chan time.Time, 1)
	m.clock.Add(tickChan, config.Interval, true)

	name := m.logger.Service()
	logChan := m.logger.LogChan()
	var worker worker.Worker
	analyzerType := config.CollectFrom
	switch analyzerType {
	case "slowlog":
		worker = m.slowlogWorkerFactory.Make(name+"-worker", config, mysqlConn)
	case "perfschema":
		worker = m.perfschemaWorkerFactory.Make(name+"-worker", mysqlConn)
	default:
		panic("Invalid analyzerType: " + analyzerType)
	}
	worker.SetConfig(config)

	// Create and start a new analyzer. This should return immediately.
	// The analyzer will configure MySQL, start its iter, then run it worker
	// for each interval.
	m.analyzer = NewRealAnalyzer(
		pct.NewLogger(logChan, name),
		config,
		m.iterFactory.Make(analyzerType, mysqlConn, tickChan),
		mysqlConn,
		restartChan,
		worker,
		m.clock,
		m.spool,
	)

	return m.analyzer.Start()
}

// Status returns list of statuses
func (m *MySQLAnalyzer) Status() map[string]string {
	if m.analyzer != nil {
		return m.analyzer.Status()
	}

	service := m.logger.Service()
	status := "Not running"

	return map[string]string{
		service: status,
	}
}

// Stop stops running analyzer, waits until it stops
func (m *MySQLAnalyzer) Stop() error {
	if m.analyzer == nil {
		return nil
	}

	a := m.analyzer
	tickChan := m.tickChan
	restartChan := m.restartChan
	m.analyzer = nil
	m.tickChan = nil
	m.restartChan = nil

	// Stop ticking on this tickChan. Other services receiving ticks at the same
	// interval are not affected.
	m.clock.Remove(tickChan)

	// Stop watching this MySQL instance. Other services watching this MySQL
	// instance are not affected.
	m.mrms.Remove(m.protoInstance.UUID, restartChan)

	return a.Stop()
}

func (m *MySQLAnalyzer) GetDefaults(uuid string) map[string]interface{} {
	// Configuration
	collectFrom := m.config.CollectFrom
	interval := m.config.Interval
	exampleQueries := m.config.ExampleQueries
	// For old format of qan config, get Interval and ExampleQueries from running config.
	if interval == 0 {
		interval = m.analyzer.Config().Interval
		exampleQueries = m.analyzer.Config().ExampleQueries
	}
	cfg := map[string]interface{}{
		"CollectFrom":       collectFrom,
		"Interval":          interval,
		"MaxSlowLogSize":    config.DEFAULT_MAX_SLOW_LOG_SIZE,
		"RemoveOldSlowLogs": config.DEFAULT_REMOVE_OLD_SLOW_LOGS,
		"ExampleQueries":    exampleQueries,
		"WorkerRunTime":     config.DEFAULT_WORKER_RUNTIME,
		"ReportLimit":       config.DEFAULT_REPORT_LIMIT,
	}

	// Info from SHOW GLOBAL STATUS
	mysqlInstance := m.protoInstance
	mysqlConn := m.mysqlConnFactory.Make(mysqlInstance.DSN)
	mysqlConn.Connect()
	defer mysqlConn.Close()
	info := config.ReadInfoFromShowGlobalStatus(mysqlConn) // Read current values
	for k, v := range info {
		cfg[k] = v
	}

	return cfg
}

// String returns human readable identification of Analyzer
func (m *MySQLAnalyzer) String() string {
	return m.analyzer.String()
}
<commit_msg>Strip trailing whitespace.<commit_after>package mysql

import (
	"context"
	"fmt"
	"time"

	"github.com/percona/pmm/proto"
	pc "github.com/percona/pmm/proto/config"
	"github.com/percona/qan-agent/data"
	"github.com/percona/qan-agent/mrms"
	"github.com/percona/qan-agent/mysql"
	"github.com/percona/qan-agent/pct"
	"github.com/percona/qan-agent/qan/analyzer"
	"github.com/percona/qan-agent/qan/analyzer/mysql/config"
	"github.com/percona/qan-agent/qan/analyzer/mysql/factory"
	"github.com/percona/qan-agent/qan/analyzer/mysql/iter"
	"github.com/percona/qan-agent/qan/analyzer/mysql/worker"
	"github.com/percona/qan-agent/qan/analyzer/mysql/worker/perfschema"
	"github.com/percona/qan-agent/qan/analyzer/mysql/worker/slowlog"
	"github.com/percona/qan-agent/ticker"
)

func New(ctx context.Context, protoInstance proto.Instance) analyzer.Analyzer {
	// Get available services from ctx
	services, _ := ctx.Value("services").(map[string]interface{})

	// Get services we need
	logger, _ := services["logger"].(*pct.Logger)
	clock, _ := services["clock"].(ticker.Manager)
	spool, _ := services["spool"].(data.Spooler)
	mrms, _ := services["mrms"].(mrms.Monitor)

	// Create internal services we need
	logChan := logger.LogChan()
	iterFactory := factory.NewRealIntervalIterFactory(logChan)
	slowlogWorkerFactory := slowlog.NewRealWorkerFactory(logChan)
	perfschemaWorkerFactory := perfschema.NewRealWorkerFactory(logChan)
	mysqlConnFactory := &mysql.RealConnectionFactory{}

	// return initialized MySQLAnalyzer
	return &MySQLAnalyzer{
		// on initialization config and analyzer are uninitialized
		config:   pc.QAN{},
		analyzer: nil,
		// initialize
		protoInstance:           protoInstance,
		logger:                  logger,
		clock:                   clock,
		spool:                   spool,
		mrms:                    mrms,
		iterFactory:             iterFactory,
		slowlogWorkerFactory:    slowlogWorkerFactory,
		perfschemaWorkerFactory: perfschemaWorkerFactory,
		mysqlConnFactory:        mysqlConnFactory,
	}
}

// MySQLAnalyzer is a proxy Analyzer between QAN manager
// and MySQL implementations of Slowlog Analyzer and Perfschema Analyzer
type MySQLAnalyzer struct {
	// on initialization config and analyzer are uninitialized
	config   pc.QAN
	analyzer analyzer.Analyzer
	// services initialized in New
	protoInstance           proto.Instance
	logger                  *pct.Logger
	clock                   ticker.Manager
	spool                   data.Spooler
	mrms                    mrms.Monitor
	iterFactory             iter.IntervalIterFactory
	slowlogWorkerFactory    slowlog.WorkerFactory
	perfschemaWorkerFactory perfschema.WorkerFactory
	mysqlConnFactory        mysql.ConnectionFactory
	// real analyzer channels
	tickChan    chan time.Time
	restartChan chan proto.Instance
}

// SetConfig sets the config
func (m *MySQLAnalyzer) SetConfig(setConfig pc.QAN) {
	m.config = setConfig
	if m.analyzer != nil {
		m.analyzer.SetConfig(m.config)
	}
}

// Config returns analyzer running configuration
func (m *MySQLAnalyzer) Config() pc.QAN {
	if m.analyzer != nil {
		m.config = m.analyzer.Config()
	}

	return m.config
}

// Start starts analyzer but doesn't wait until it exits
func (m *MySQLAnalyzer) Start() error {
	setConfig := m.Config()

	// Create a MySQL connection.
	mysqlConn := m.mysqlConnFactory.Make(m.protoInstance.DSN)

	// Validate and transform the set config and into a running config.
	config, err := config.ValidateConfig(setConfig)
	if err != nil {
		return fmt.Errorf("invalid QAN config: %s", err)
	}

	// Add the MySQL DSN to the MySQL restart monitor. If MySQL restarts,
	// the analyzer will stop its worker and re-configure MySQL.
	restartChan := m.mrms.Add(m.protoInstance)

	// Make a chan on which the clock will tick at even intervals:
	// clock -> tickChan -> iter -> analyzer -> worker
	tickChan := make(chan time.Time, 1)
	m.clock.Add(tickChan, config.Interval, true)

	name := m.logger.Service()
	logChan := m.logger.LogChan()
	var worker worker.Worker
	analyzerType := config.CollectFrom
	switch analyzerType {
	case "slowlog":
		worker = m.slowlogWorkerFactory.Make(name+"-worker", config, mysqlConn)
	case "perfschema":
		worker = m.perfschemaWorkerFactory.Make(name+"-worker", mysqlConn)
	default:
		panic("Invalid analyzerType: " + analyzerType)
	}
	worker.SetConfig(config)

	// Create and start a new analyzer. This should return immediately.
	// The analyzer will configure MySQL, start its iter, then run it worker
	// for each interval.
	m.analyzer = NewRealAnalyzer(
		pct.NewLogger(logChan, name),
		config,
		m.iterFactory.Make(analyzerType, mysqlConn, tickChan),
		mysqlConn,
		restartChan,
		worker,
		m.clock,
		m.spool,
	)

	return m.analyzer.Start()
}

// Status returns list of statuses
func (m *MySQLAnalyzer) Status() map[string]string {
	if m.analyzer != nil {
		return m.analyzer.Status()
	}

	service := m.logger.Service()
	status := "Not running"

	return map[string]string{
		service: status,
	}
}

// Stop stops running analyzer, waits until it stops
func (m *MySQLAnalyzer) Stop() error {
	if m.analyzer == nil {
		return nil
	}

	a := m.analyzer
	tickChan := m.tickChan
	restartChan := m.restartChan
	m.analyzer = nil
	m.tickChan = nil
	m.restartChan = nil

	// Stop ticking on this tickChan. Other services receiving ticks at the same
	// interval are not affected.
	m.clock.Remove(tickChan)

	// Stop watching this MySQL instance. Other services watching this MySQL
	// instance are not affected.
	m.mrms.Remove(m.protoInstance.UUID, restartChan)

	return a.Stop()
}

func (m *MySQLAnalyzer) GetDefaults(uuid string) map[string]interface{} {
	// Configuration
	collectFrom := m.config.CollectFrom
	interval := m.config.Interval
	exampleQueries := m.config.ExampleQueries
	// For old format of qan config, get Interval and ExampleQueries from running config.
	if interval == 0 {
		interval = m.analyzer.Config().Interval
		exampleQueries = m.analyzer.Config().ExampleQueries
	}
	cfg := map[string]interface{}{
		"CollectFrom":       collectFrom,
		"Interval":          interval,
		"MaxSlowLogSize":    config.DEFAULT_MAX_SLOW_LOG_SIZE,
		"RemoveOldSlowLogs": config.DEFAULT_REMOVE_OLD_SLOW_LOGS,
		"ExampleQueries":    exampleQueries,
		"WorkerRunTime":     config.DEFAULT_WORKER_RUNTIME,
		"ReportLimit":       config.DEFAULT_REPORT_LIMIT,
	}

	// Info from SHOW GLOBAL STATUS
	mysqlInstance := m.protoInstance
	mysqlConn := m.mysqlConnFactory.Make(mysqlInstance.DSN)
	mysqlConn.Connect()
	defer mysqlConn.Close()
	info := config.ReadInfoFromShowGlobalStatus(mysqlConn) // Read current values
	for k, v := range info {
		cfg[k] = v
	}

	return cfg
}

// String returns human readable identification of Analyzer
func (m *MySQLAnalyzer) String() string {
	return m.analyzer.String()
}
<|endoftext|>