<commit_before>package cmd

import (
	"bufio"
	"encoding/json"
	"fmt"
	"os"
	"text/tabwriter"

	"github.com/mobingi/mobingi-cli/client"
	"github.com/mobingi/mobingi-cli/pkg/cli"
	d "github.com/mobingi/mobingi-cli/pkg/debug"
	"github.com/mobingi/mobingi-cli/pkg/iohelper"
	"github.com/mobingi/mobingi-cli/pkg/pretty"
	"github.com/mobingi/mobingi-cli/pkg/stack"
	"github.com/spf13/cobra"
)

func StackDescribeCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "describe",
		Short: "display stack details",
		Long: `Display stack details. If you specify the '--out=[filename]' option,
make sure you provide the full path of the file. If the path has
space(s) in it, make sure to surround it with double quotes.

Valid format values: text (default), json, raw, min

Examples:

  $ ` + cli.BinName() + ` stack describe --id=58c2297d25645-Y6NSE4VjP-tk
  $ ` + cli.BinName() + ` stack describe --id=58c2297d25645-Y6NSE4VjP-tk --fmt=min`,
		Run: describe,
	}

	cmd.Flags().StringP("id", "i", "", "stack id")
	return cmd
}

func describe(cmd *cobra.Command, args []string) {
	var err error
	id := cli.GetCliStringFlag(cmd, "id")
	if id == "" {
		d.ErrorExit("stack id cannot be empty", 1)
	}

	c := client.NewClient(client.NewApiConfig(cmd))
	body, err := c.AuthGet("/alm/stack/" + fmt.Sprintf("%s", id))
	d.ErrorExit(err, 1)

	// we process `--fmt=raw` option first
	out := cli.GetCliStringFlag(cmd, "out")
	pfmt := cli.GetCliStringFlag(cmd, "fmt")
	if pfmt == "raw" {
		fmt.Println(string(body))
		if out != "" {
			err = iohelper.WriteToFile(out, body)
			d.ErrorExit(err, 1)
		}

		return
	}

	// workaround: see description in struct definition
	var ptr interface{}  // pointer to 1st element of slice
	var sptr interface{} // pointer to the whole slice
	var stacks1 []stack.DescribeStack1
	var stacks2 []stack.DescribeStack2
	valid := 0
	err = json.Unmarshal(body, &stacks1)
	if err != nil {
		err = json.Unmarshal(body, &stacks2)
		d.ErrorExit(err, 1)

		ptr = &stacks2[0]
		sptr = stacks2
		valid = 2
	} else {
		ptr = &stacks1[0]
		sptr = stacks1
		valid = 1
	}

	switch pfmt {
	case "min":
		w := tabwriter.NewWriter(os.Stdout, 0, 10, 5, ' ', 0)
		fmt.Fprintf(w, "INSTANCE ID\tINSTANCE TYPE\tINSTANCE MODEL\tPUBLIC IP\tPRIVATE IP\tSTATUS\n")
		if valid == 1 {
			for _, inst := range stacks1[0].Instances {
				instype := "on-demand"
				if inst.InstanceLifecycle == "spot" {
					instype = inst.InstanceLifecycle
				}

				fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n",
					inst.InstanceId,
					instype,
					inst.InstanceType,
					inst.PublicIpAddress,
					inst.PrivateIpAddress,
					inst.State.Name)
			}
		}

		if valid == 2 {
			for _, inst := range stacks2[0].Instances {
				instype := "on-demand"
				if inst.InstanceLifecycle == "spot" {
					instype = inst.InstanceLifecycle
				}

				fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n",
					inst.InstanceId,
					instype,
					inst.InstanceType,
					inst.PublicIpAddress,
					inst.PrivateIpAddress,
					inst.State.Name)
			}
		}

		w.Flush()
	case "json":
		indent := cli.GetCliIntFlag(cmd, "indent")
		mi, err := json.MarshalIndent(sptr, "", pretty.Indent(indent))
		d.ErrorExit(err, 1)

		fmt.Println(string(mi))

		// write to file option
		if out != "" {
			err = iohelper.WriteToFile(out, mi)
			d.ErrorExit(err, 1)
		}
	default:
		if pfmt == "text" || pfmt == "" {
			indent := cli.GetCliIntFlag(cmd, "indent")
			stack.PrintR(os.Stdout, ptr, 0, indent)

			// write to file option
			if out != "" {
				fp, err := os.Create(out)
				d.ErrorExit(err, 1)

				defer fp.Close()
				w := bufio.NewWriter(fp)
				defer w.Flush()
				stack.PrintR(w, ptr, 0, indent)
				d.Info(fmt.Sprintf("output written to %s", out))
			}
		}
	}
}
<commit_msg>Make min the default. (#29)<commit_after>package cmd

import (
	"bufio"
	"encoding/json"
	"fmt"
	"os"
	"text/tabwriter"

	"github.com/mobingi/mobingi-cli/client"
	"github.com/mobingi/mobingi-cli/pkg/cli"
	d "github.com/mobingi/mobingi-cli/pkg/debug"
	"github.com/mobingi/mobingi-cli/pkg/iohelper"
	"github.com/mobingi/mobingi-cli/pkg/pretty"
	"github.com/mobingi/mobingi-cli/pkg/stack"
	"github.com/spf13/cobra"
)

func StackDescribeCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "describe",
		Short: "display stack details",
		Long: `Display stack details. If you specify the '--out=[filename]' option,
make sure you provide the full path of the file. If the path has
space(s) in it, make sure to surround it with double quotes.

Valid format values: min (default), json, raw, text

Examples:

  $ ` + cli.BinName() + ` stack describe --id=58c2297d25645-Y6NSE4VjP-tk
  $ ` + cli.BinName() + ` stack describe --id=58c2297d25645-Y6NSE4VjP-tk --fmt=json`,
		Run: describe,
	}

	cmd.Flags().StringP("id", "i", "", "stack id")
	return cmd
}

func describe(cmd *cobra.Command, args []string) {
	var err error
	id := cli.GetCliStringFlag(cmd, "id")
	if id == "" {
		d.ErrorExit("stack id cannot be empty", 1)
	}

	c := client.NewClient(client.NewApiConfig(cmd))
	body, err := c.AuthGet("/alm/stack/" + fmt.Sprintf("%s", id))
	d.ErrorExit(err, 1)

	// we process `--fmt=raw` option first
	out := cli.GetCliStringFlag(cmd, "out")
	pfmt := cli.GetCliStringFlag(cmd, "fmt")
	if pfmt == "raw" {
		fmt.Println(string(body))
		if out != "" {
			err = iohelper.WriteToFile(out, body)
			d.ErrorExit(err, 1)
		}

		return
	}

	// workaround: see description in struct definition
	var ptr interface{}  // pointer to 1st element of slice
	var sptr interface{} // pointer to the whole slice
	var stacks1 []stack.DescribeStack1
	var stacks2 []stack.DescribeStack2
	valid := 0
	err = json.Unmarshal(body, &stacks1)
	if err != nil {
		err = json.Unmarshal(body, &stacks2)
		d.ErrorExit(err, 1)

		ptr = &stacks2[0]
		sptr = stacks2
		valid = 2
	} else {
		ptr = &stacks1[0]
		sptr = stacks1
		valid = 1
	}

	switch pfmt {
	case "text":
		indent := cli.GetCliIntFlag(cmd, "indent")
		stack.PrintR(os.Stdout, ptr, 0, indent)

		// write to file option
		if out != "" {
			fp, err := os.Create(out)
			d.ErrorExit(err, 1)

			defer fp.Close()
			w := bufio.NewWriter(fp)
			defer w.Flush()
			stack.PrintR(w, ptr, 0, indent)
			d.Info(fmt.Sprintf("output written to %s", out))
		}
	case "json":
		indent := cli.GetCliIntFlag(cmd, "indent")
		mi, err := json.MarshalIndent(sptr, "", pretty.Indent(indent))
		d.ErrorExit(err, 1)

		fmt.Println(string(mi))

		// write to file option
		if out != "" {
			err = iohelper.WriteToFile(out, mi)
			d.ErrorExit(err, 1)
		}
	default:
		if pfmt == "min" || pfmt == "" {
			w := tabwriter.NewWriter(os.Stdout, 0, 10, 5, ' ', 0)
			fmt.Fprintf(w, "INSTANCE ID\tINSTANCE TYPE\tINSTANCE MODEL\tPUBLIC IP\tPRIVATE IP\tSTATUS\n")
			if valid == 1 {
				for _, inst := range stacks1[0].Instances {
					instype := "on-demand"
					if inst.InstanceLifecycle == "spot" {
						instype = inst.InstanceLifecycle
					}

					fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n",
						inst.InstanceId,
						instype,
						inst.InstanceType,
						inst.PublicIpAddress,
						inst.PrivateIpAddress,
						inst.State.Name)
				}
			}

			if valid == 2 {
				for _, inst := range stacks2[0].Instances {
					instype := "on-demand"
					if inst.InstanceLifecycle == "spot" {
						instype = inst.InstanceLifecycle
					}

					fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n",
						inst.InstanceId,
						instype,
						inst.InstanceType,
						inst.PublicIpAddress,
						inst.PrivateIpAddress,
						inst.State.Name)
				}
			}

			w.Flush()
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2020 The Knative 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 gitlab

import (
	"context"
	"fmt"
	"net/url"
	"strings"

	//k8s.io imports

	corev1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/client-go/kubernetes"
	"knative.dev/eventing/pkg/reconciler/source"
	"knative.dev/pkg/kmeta"

	//knative.dev/serving imports

	servingv1 "knative.dev/serving/pkg/apis/serving/v1"
	servingclientset "knative.dev/serving/pkg/client/clientset/versioned"
	servinglisters "knative.dev/serving/pkg/client/listers/serving/v1"

	//knative.dev/eventing-contrib imports
	sourcesv1alpha1 "knative.dev/eventing-contrib/gitlab/pkg/apis/sources/v1alpha1"
	clientset "knative.dev/eventing-contrib/gitlab/pkg/client/clientset/versioned"
	listers "knative.dev/eventing-contrib/gitlab/pkg/client/listers/sources/v1alpha1"

	//knative.dev/pkg imports

	"knative.dev/pkg/reconciler"
	"knative.dev/pkg/resolver"
)

// Reconciler reconciles a GitLabSource object
type Reconciler struct {
	kubeClientSet kubernetes.Interface

	gitlabClientSet clientset.Interface
	gitlabLister    listers.GitLabSourceLister

	servingClientSet servingclientset.Interface
	servingLister    servinglisters.ServiceLister

	receiveAdapterImage string

	sinkResolver *resolver.URIResolver

	loggingContext context.Context

	configs source.ConfigAccessor
}

func (r *Reconciler) ReconcileKind(ctx context.Context, source *sourcesv1alpha1.GitLabSource) reconciler.Event {
	source.Status.InitializeConditions()
	source.Status.ObservedGeneration = source.Generation

	projectName, err := getProjectName(source.Spec.ProjectUrl)
	if err != nil {
		return fmt.Errorf("Failed to process project url to get the project name: " + err.Error())
	}

	hookOptions := projectHookOptions{}
	hookOptions.project = projectName
	hookOptions.id = source.Status.Id

	for _, event := range source.Spec.EventTypes {
		switch event {
		case "push_events":
			hookOptions.PushEvents = true
		case "issues_events":
			hookOptions.IssuesEvents = true
		case "confidential_issues_events":
			hookOptions.ConfidentialIssuesEvents = true
		case "merge_requests_events":
			hookOptions.MergeRequestsEvents = true
		case "tag_push_events":
			hookOptions.TagPushEvents = true
		case "pipeline_events":
			hookOptions.PipelineEvents = true
		case "wiki_page_events":
			hookOptions.WikiPageEvents = true
		case "job_events":
			hookOptions.JobEvents = true
		case "note_events":
			hookOptions.NoteEvents = true
		}
	}
	hookOptions.accessToken, err = r.secretFrom(source.Namespace, source.Spec.AccessToken.SecretKeyRef)
	if err != nil {
		source.Status.MarkNoSecret("NotFound", "%s", err)
		return err
	}
	hookOptions.secretToken, err = r.secretFrom(source.Namespace, source.Spec.SecretToken.SecretKeyRef)
	if err != nil {
		source.Status.MarkNoSecret("NotFound", "%s", err)
		return err
	}
	source.Status.MarkSecret()

	sink := source.Spec.Sink.DeepCopy()

	if sink.Ref != nil {
		// To call URIFromDestination(), dest.Ref must have a Namespace. If there is
		// no Namespace defined in dest.Ref, we will use the Namespace of the source
		// as the Namespace of dest.Ref.
		if sink.Ref.Namespace == "" {
			//TODO how does this work with deprecated fields
			sink.Ref.Namespace = source.GetNamespace()
		}
	}

	uri, err := r.sinkResolver.URIFromDestinationV1(*sink, source)
	if err != nil {
		source.Status.MarkNoSink("NotFound", "%s", err)
		return err
	}
	source.Status.MarkSink(uri)

	ksvc, err := r.getOwnedKnativeService(source)
	if err != nil {
		if apierrors.IsNotFound(err) {
			ksvc = r.generateKnativeServiceObject(source, r.receiveAdapterImage)
			ksvc, err = r.servingClientSet.ServingV1().Services(ksvc.GetNamespace()).Create(ksvc)
			if err != nil {
				return err
			}
		} else {
			return fmt.Errorf("Failed to verify if knative service is created for the gitlabsource: %v", err)
		}
	}
	if ksvc.Status.URL == nil {
		return nil
	}
	hookOptions.url = ksvc.Status.URL.String()
	if source.Spec.SslVerify {
		hookOptions.EnableSSLVerification = true
	}
	baseURL, err := getGitlabBaseURL(source.Spec.ProjectUrl)
	if err != nil {
		return fmt.Errorf("Failed to process project url to get the base url: %v", err)
	}
	gitlabClient := gitlabHookClient{}
	hookID, err := gitlabClient.Create(baseURL, &hookOptions)
	if err != nil {
		return fmt.Errorf("Failed to create project hook: %v", err)
	}
	source.Status.Id = hookID
	return nil
}

func (r *Reconciler) FinalizeKind(ctx context.Context, source *sourcesv1alpha1.GitLabSource) reconciler.Event {
	if source.Status.Id != "" {
		hookOptions := projectHookOptions{}
		projectName, err := getProjectName(source.Spec.ProjectUrl)
		if err != nil {
			return fmt.Errorf("failed to process project url to get the project name: %s", err.Error())
		}
		hookOptions.project = projectName
		hookOptions.id = source.Status.Id
		hookOptions.accessToken, err = r.secretFrom(source.Namespace, source.Spec.AccessToken.SecretKeyRef)
		if err != nil {
			return err
		}
		baseURL, err := getGitlabBaseURL(source.Spec.ProjectUrl)
		if err != nil {
			return fmt.Errorf("failed to process project url to get the base url: %s", err.Error())
		}
		gitlabClient := gitlabHookClient{}
		err = gitlabClient.Delete(baseURL, &hookOptions)
		if err != nil {
			return fmt.Errorf("failed to delete project hook: %s", err.Error())
		}
		source.Status.Id = ""
	}
	return nil
}

func getGitlabBaseURL(projectUrl string) (string, error) {
	u, err := url.Parse(projectUrl)
	if err != nil {
		return "", err
	}
	projectName := u.Path[1:]
	baseURL := strings.TrimSuffix(projectUrl, projectName)
	return baseURL, nil
}

func getProjectName(projectUrl string) (string, error) {
	u, err := url.Parse(projectUrl)
	if err != nil {
		return "", err
	}
	projectName := u.Path[1:]
	return projectName, nil
}

func (r *Reconciler) secretFrom(namespace string, secretKeySelector *corev1.SecretKeySelector) (string, error) {
	secret, err := r.kubeClientSet.CoreV1().Secrets(namespace).Get(secretKeySelector.Name, metav1.GetOptions{})
	if err != nil {
		return "", err
	}

	secretVal, ok := secret.Data[secretKeySelector.Key]
	if !ok {
		return "", fmt.Errorf(`key "%s" not found in secret "%s"`, secretKeySelector.Key, secretKeySelector.Name)
	}
	return string(secretVal), nil
}

func (r *Reconciler) generateKnativeServiceObject(source *sourcesv1alpha1.GitLabSource, receiveAdapterImage string) *servingv1.Service {
	labels := map[string]string{
		"receive-adapter": "gitlab",
	}

	env := append([]corev1.EnvVar{
		{
			Name: "GITLAB_SECRET_TOKEN",
			ValueFrom: &corev1.EnvVarSource{
				SecretKeyRef: source.Spec.SecretToken.SecretKeyRef,
			},
		}, {
			Name:  "K_SINK",
			Value: source.Status.SinkURI.String(),
		}, {
			Name:  "NAMESPACE",
			Value: source.GetNamespace(),
		}, {
			Name:  "METRICS_DOMAIN",
			Value: "knative.dev/eventing",
		}},
		r.configs.ToEnvVars()...)
	return &servingv1.Service{
		ObjectMeta: metav1.ObjectMeta{
			GenerateName: fmt.Sprintf("%s-", source.Name),
			Namespace:    source.Namespace,
			Labels:       labels,
			OwnerReferences: []metav1.OwnerReference{
				*kmeta.NewControllerRef(source),
			},
		},
		Spec: servingv1.ServiceSpec{
			ConfigurationSpec: servingv1.ConfigurationSpec{
				Template: servingv1.RevisionTemplateSpec{
					Spec: servingv1.RevisionSpec{
						PodSpec: corev1.PodSpec{
							ServiceAccountName: source.Spec.ServiceAccountName,
							Containers: []corev1.Container{
								{
									Image: receiveAdapterImage,
									Env:   env,
								},
							},
						},
					},
				},
			},
		},
	}
}

func (r *Reconciler) getOwnedKnativeService(source *sourcesv1alpha1.GitLabSource) (*servingv1.Service, error) {
	list, err := r.servingClientSet.ServingV1().Services(source.GetNamespace()).List(metav1.ListOptions{
		LabelSelector: labels.Everything().String(),
	})

	if err != nil {
		return nil, err
	}
	for _, ksvc := range list.Items {
		if metav1.IsControlledBy(&ksvc, source) {
			return &ksvc, nil
		}
	}

	return nil, apierrors.NewNotFound(servingv1.Resource("services"), "")
}
<commit_msg>Set Prometheus port to valid default value (#1480)<commit_after>/*
Copyright 2020 The Knative 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 gitlab

import (
	"context"
	"fmt"
	"net/url"
	"strings"

	//k8s.io imports

	corev1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/client-go/kubernetes"
	"knative.dev/eventing/pkg/reconciler/source"
	"knative.dev/pkg/kmeta"

	//knative.dev/serving imports

	servingv1 "knative.dev/serving/pkg/apis/serving/v1"
	servingclientset "knative.dev/serving/pkg/client/clientset/versioned"
	servinglisters "knative.dev/serving/pkg/client/listers/serving/v1"

	//knative.dev/eventing-contrib imports
	sourcesv1alpha1 "knative.dev/eventing-contrib/gitlab/pkg/apis/sources/v1alpha1"
	clientset "knative.dev/eventing-contrib/gitlab/pkg/client/clientset/versioned"
	listers "knative.dev/eventing-contrib/gitlab/pkg/client/listers/sources/v1alpha1"

	//knative.dev/pkg imports

	"knative.dev/pkg/reconciler"
	"knative.dev/pkg/resolver"
)

// Reconciler reconciles a GitLabSource object
type Reconciler struct {
	kubeClientSet kubernetes.Interface

	gitlabClientSet clientset.Interface
	gitlabLister    listers.GitLabSourceLister

	servingClientSet servingclientset.Interface
	servingLister    servinglisters.ServiceLister

	receiveAdapterImage string

	sinkResolver *resolver.URIResolver

	loggingContext context.Context

	configs source.ConfigAccessor
}

func (r *Reconciler) ReconcileKind(ctx context.Context, source *sourcesv1alpha1.GitLabSource) reconciler.Event {
	source.Status.InitializeConditions()
	source.Status.ObservedGeneration = source.Generation

	projectName, err := getProjectName(source.Spec.ProjectUrl)
	if err != nil {
		return fmt.Errorf("Failed to process project url to get the project name: " + err.Error())
	}

	hookOptions := projectHookOptions{}
	hookOptions.project = projectName
	hookOptions.id = source.Status.Id

	for _, event := range source.Spec.EventTypes {
		switch event {
		case "push_events":
			hookOptions.PushEvents = true
		case "issues_events":
			hookOptions.IssuesEvents = true
		case "confidential_issues_events":
			hookOptions.ConfidentialIssuesEvents = true
		case "merge_requests_events":
			hookOptions.MergeRequestsEvents = true
		case "tag_push_events":
			hookOptions.TagPushEvents = true
		case "pipeline_events":
			hookOptions.PipelineEvents = true
		case "wiki_page_events":
			hookOptions.WikiPageEvents = true
		case "job_events":
			hookOptions.JobEvents = true
		case "note_events":
			hookOptions.NoteEvents = true
		}
	}
	hookOptions.accessToken, err = r.secretFrom(source.Namespace, source.Spec.AccessToken.SecretKeyRef)
	if err != nil {
		source.Status.MarkNoSecret("NotFound", "%s", err)
		return err
	}
	hookOptions.secretToken, err = r.secretFrom(source.Namespace, source.Spec.SecretToken.SecretKeyRef)
	if err != nil {
		source.Status.MarkNoSecret("NotFound", "%s", err)
		return err
	}
	source.Status.MarkSecret()

	sink := source.Spec.Sink.DeepCopy()

	if sink.Ref != nil {
		// To call URIFromDestination(), dest.Ref must have a Namespace. If there is
		// no Namespace defined in dest.Ref, we will use the Namespace of the source
		// as the Namespace of dest.Ref.
		if sink.Ref.Namespace == "" {
			//TODO how does this work with deprecated fields
			sink.Ref.Namespace = source.GetNamespace()
		}
	}

	uri, err := r.sinkResolver.URIFromDestinationV1(*sink, source)
	if err != nil {
		source.Status.MarkNoSink("NotFound", "%s", err)
		return err
	}
	source.Status.MarkSink(uri)

	ksvc, err := r.getOwnedKnativeService(source)
	if err != nil {
		if apierrors.IsNotFound(err) {
			ksvc = r.generateKnativeServiceObject(source, r.receiveAdapterImage)
			ksvc, err = r.servingClientSet.ServingV1().Services(ksvc.GetNamespace()).Create(ksvc)
			if err != nil {
				return err
			}
		} else {
			return fmt.Errorf("Failed to verify if knative service is created for the gitlabsource: %v", err)
		}
	}
	if ksvc.Status.URL == nil {
		return nil
	}
	hookOptions.url = ksvc.Status.URL.String()
	if source.Spec.SslVerify {
		hookOptions.EnableSSLVerification = true
	}
	baseURL, err := getGitlabBaseURL(source.Spec.ProjectUrl)
	if err != nil {
		return fmt.Errorf("Failed to process project url to get the base url: %v", err)
	}
	gitlabClient := gitlabHookClient{}
	hookID, err := gitlabClient.Create(baseURL, &hookOptions)
	if err != nil {
		return fmt.Errorf("Failed to create project hook: %v", err)
	}
	source.Status.Id = hookID
	return nil
}

func (r *Reconciler) FinalizeKind(ctx context.Context, source *sourcesv1alpha1.GitLabSource) reconciler.Event {
	if source.Status.Id != "" {
		hookOptions := projectHookOptions{}
		projectName, err := getProjectName(source.Spec.ProjectUrl)
		if err != nil {
			return fmt.Errorf("failed to process project url to get the project name: %s", err.Error())
		}
		hookOptions.project = projectName
		hookOptions.id = source.Status.Id
		hookOptions.accessToken, err = r.secretFrom(source.Namespace, source.Spec.AccessToken.SecretKeyRef)
		if err != nil {
			return err
		}
		baseURL, err := getGitlabBaseURL(source.Spec.ProjectUrl)
		if err != nil {
			return fmt.Errorf("failed to process project url to get the base url: %s", err.Error())
		}
		gitlabClient := gitlabHookClient{}
		err = gitlabClient.Delete(baseURL, &hookOptions)
		if err != nil {
			return fmt.Errorf("failed to delete project hook: %s", err.Error())
		}
		source.Status.Id = ""
	}
	return nil
}

func getGitlabBaseURL(projectUrl string) (string, error) {
	u, err := url.Parse(projectUrl)
	if err != nil {
		return "", err
	}
	projectName := u.Path[1:]
	baseURL := strings.TrimSuffix(projectUrl, projectName)
	return baseURL, nil
}

func getProjectName(projectUrl string) (string, error) {
	u, err := url.Parse(projectUrl)
	if err != nil {
		return "", err
	}
	projectName := u.Path[1:]
	return projectName, nil
}

func (r *Reconciler) secretFrom(namespace string, secretKeySelector *corev1.SecretKeySelector) (string, error) {
	secret, err := r.kubeClientSet.CoreV1().Secrets(namespace).Get(secretKeySelector.Name, metav1.GetOptions{})
	if err != nil {
		return "", err
	}

	secretVal, ok := secret.Data[secretKeySelector.Key]
	if !ok {
		return "", fmt.Errorf(`key "%s" not found in secret "%s"`, secretKeySelector.Key, secretKeySelector.Name)
	}
	return string(secretVal), nil
}

func (r *Reconciler) generateKnativeServiceObject(source *sourcesv1alpha1.GitLabSource, receiveAdapterImage string) *servingv1.Service {
	labels := map[string]string{
		"receive-adapter": "gitlab",
	}

	env := append([]corev1.EnvVar{
		{
			Name: "GITLAB_SECRET_TOKEN",
			ValueFrom: &corev1.EnvVarSource{
				SecretKeyRef: source.Spec.SecretToken.SecretKeyRef,
			},
		}, {
			Name:  "K_SINK",
			Value: source.Status.SinkURI.String(),
		}, {
			Name:  "NAMESPACE",
			Value: source.GetNamespace(),
		}, {
			Name:  "METRICS_DOMAIN",
			Value: "knative.dev/eventing",
		}, {
			Name:  "METRICS_PROMETHEUS_PORT",
			Value: "9092",
		}},
		r.configs.ToEnvVars()...)
	return &servingv1.Service{
		ObjectMeta: metav1.ObjectMeta{
			GenerateName: fmt.Sprintf("%s-", source.Name),
			Namespace:    source.Namespace,
			Labels:       labels,
			OwnerReferences: []metav1.OwnerReference{
				*kmeta.NewControllerRef(source),
			},
		},
		Spec: servingv1.ServiceSpec{
			ConfigurationSpec: servingv1.ConfigurationSpec{
				Template: servingv1.RevisionTemplateSpec{
					Spec: servingv1.RevisionSpec{
						PodSpec: corev1.PodSpec{
							ServiceAccountName: source.Spec.ServiceAccountName,
							Containers: []corev1.Container{
								{
									Image: receiveAdapterImage,
									Env:   env,
								},
							},
						},
					},
				},
			},
		},
	}
}

func (r *Reconciler) getOwnedKnativeService(source *sourcesv1alpha1.GitLabSource) (*servingv1.Service, error) {
	list, err := r.servingClientSet.ServingV1().Services(source.GetNamespace()).List(metav1.ListOptions{
		LabelSelector: labels.Everything().String(),
	})

	if err != nil {
		return nil, err
	}
	for _, ksvc := range list.Items {
		if metav1.IsControlledBy(&ksvc, source) {
			return &ksvc, nil
		}
	}

	return nil, apierrors.NewNotFound(servingv1.Resource("services"), "")
}
<|endoftext|>


<commit_before>package index

import (
	"bytes"
	"reflect"
	"testing"
	"time"
)

var tr = track{
	ID:          "ID",
	Name:        "Name",
	Album:       "Album",
	AlbumArtist: "AlbumArtist",
	Artist:      "Artist",
	Composer:    "Composer",
	Genre:       "Genre",
	Location:    "Location",
	Kind:        "Kind",

	TotalTime:   1,
	Year:        2,
	DiscNumber:  3,
	TrackNumber: 4,
	TrackCount:  5,
	DiscCount:   6,
	BitRate:     7,

	DateAdded:    time.Now(),
	DateModified: time.Now(),
}

func TestTrack(t *testing.T) {
	stringFields := []string{"ID", "Name", "Album", "AlbumArtist", "Artist", "Composer", "Genre", "Location", "Kind"}
	for _, f := range stringFields {
		got := tr.GetString(f)
		if got != f {
			t.Errorf("tr.GetString(%#v) = %#v, expected %#v", f, got, f)
		}
	}

	func() {
		defer func() {
			if r := recover(); r == nil {
				t.Errorf("expected panic from invalid field")
			}
		}()

		y := tr.GetString("Year")
		t.Errorf("expected panic from GetString, got: %v", y)
	}()

	stringsFields := []string{"AlbumArtist", "Artist", "Composer"}
	for _, f := range stringsFields {
		got := tr.GetStrings(f)
		expected := []string{f}
		if !reflect.DeepEqual(got, expected) {
			t.Errorf("tr.GetStrings(%#v) = %#v, expected %#v", f, got, expected)
		}
	}

	func() {
		defer func() {
			if r := recover(); r == nil {
				t.Errorf("expected panic from invalid field")
			}
		}()

		y := tr.GetStrings("Name")
		t.Errorf("expected panic from GetStrings, got: %v", y)
	}()

	intFields := []string{"TotalTime", "Year", "DiscNumber", "TrackNumber", "TrackCount", "DiscCount", "BitRate"}
	for i, f := range intFields {
		got := tr.GetInt(f)
		expected := i + 1
		if got != expected {
			t.Errorf("tr.GetInt(%#v) = %d, expected %d", f, got, expected)
		}
	}

	func() {
		defer func() {
			if r := recover(); r == nil {
				t.Errorf("expected panic from invalid field")
			}
		}()

		y := tr.GetInt("Name")
		t.Errorf("expected panic from GetInt, got: %v", y)
	}()
}

type testLibrary struct {
	tr *track
}

func (t testLibrary) Tracks() []Track {
	return []Track{t.tr}
}

func (t testLibrary) Track(identifier string) (Track, bool) {
	return t.tr, true
}

func TestConvert(t *testing.T) {
	tl := testLibrary{
		tr: &tr,
	}

	l := Convert(tl, "ID")

	got := l.Tracks()
	expected := tl.Tracks()

	if !reflect.DeepEqual(got, expected) {
		t.Errorf("l.Tracks() = %v, expected: %v", got, expected)
	}

	id := "ID"
	gotTrack, _ := l.Track(id)
	expectedTrack, _ := tl.Track(id)
	if !reflect.DeepEqual(gotTrack, expectedTrack) {
		t.Errorf("l.Track(%#v) = %#v, expected: %#v", id, gotTrack, expectedTrack)
	}
}

func TestLibraryEncodeDecode(t *testing.T) {
	tl := testLibrary{
		tr: &tr,
	}

	l := Convert(tl, "ID")
	buf := &bytes.Buffer{}
	err := WriteTo(l, buf)
	if err != nil {
		t.Errorf("unexpected error in WriteTo: %v", err)
	}

	got, err := ReadFrom(buf)
	if err != nil {
		t.Errorf("unexpected error in ReadFrom: %v", err)
	}

	gotTracks := got.Tracks()
	expectedTracks := l.Tracks()

	if len(gotTracks) != len(expectedTracks) {
		t.Errorf("expected %d tracks, got: %d", len(expectedTracks), len(gotTracks))
	}

	// TODO(dhowden): Remove this mess!
	gotTrack := gotTracks[0].(*track)
	expectedTrack := expectedTracks[0].(*track)

	gotTrack.DateAdded = gotTrack.DateAdded.Local()
	gotTrack.DateModified = gotTrack.DateModified.Local()

	if !reflect.DeepEqual(expectedTrack, gotTrack) {
		t.Errorf("Encode -> Decode inconsistent, got: %#v, expected: %#v", gotTrack, expectedTrack)
	}
}
<commit_msg>Added test for invalid GetTime field<commit_after>package index

import (
	"bytes"
	"reflect"
	"testing"
	"time"
)

var tr = track{
	ID:          "ID",
	Name:        "Name",
	Album:       "Album",
	AlbumArtist: "AlbumArtist",
	Artist:      "Artist",
	Composer:    "Composer",
	Genre:       "Genre",
	Location:    "Location",
	Kind:        "Kind",

	TotalTime:   1,
	Year:        2,
	DiscNumber:  3,
	TrackNumber: 4,
	TrackCount:  5,
	DiscCount:   6,
	BitRate:     7,

	DateAdded:    time.Now(),
	DateModified: time.Now(),
}

func TestTrack(t *testing.T) {
	stringFields := []string{"ID", "Name", "Album", "AlbumArtist", "Artist", "Composer", "Genre", "Location", "Kind"}
	for _, f := range stringFields {
		got := tr.GetString(f)
		if got != f {
			t.Errorf("tr.GetString(%#v) = %#v, expected %#v", f, got, f)
		}
	}

	func() {
		defer func() {
			if r := recover(); r == nil {
				t.Errorf("expected panic from invalid field")
			}
		}()

		y := tr.GetString("Year")
		t.Errorf("expected panic from GetString, got: %v", y)
	}()

	stringsFields := []string{"AlbumArtist", "Artist", "Composer"}
	for _, f := range stringsFields {
		got := tr.GetStrings(f)
		expected := []string{f}
		if !reflect.DeepEqual(got, expected) {
			t.Errorf("tr.GetStrings(%#v) = %#v, expected %#v", f, got, expected)
		}
	}

	func() {
		defer func() {
			if r := recover(); r == nil {
				t.Errorf("expected panic from invalid field")
			}
		}()

		y := tr.GetStrings("Name")
		t.Errorf("expected panic from GetStrings, got: %v", y)
	}()

	intFields := []string{"TotalTime", "Year", "DiscNumber", "TrackNumber", "TrackCount", "DiscCount", "BitRate"}
	for i, f := range intFields {
		got := tr.GetInt(f)
		expected := i + 1
		if got != expected {
			t.Errorf("tr.GetInt(%#v) = %d, expected %d", f, got, expected)
		}
	}

	func() {
		defer func() {
			if r := recover(); r == nil {
				t.Errorf("expected panic from invalid field")
			}
		}()

		y := tr.GetInt("Name")
		t.Errorf("expected panic from GetInt, got: %v", y)
	}()

	func() {
		defer func() {
			if r := recover(); r == nil {
				t.Errorf("expected panic from invalid field")
			}
		}()

		y := tr.GetTime("Name")
		t.Errorf("expected panic from GetTime, got: %v", y)
	}()
}

type testLibrary struct {
	tr *track
}

func (t testLibrary) Tracks() []Track {
	return []Track{t.tr}
}

func (t testLibrary) Track(identifier string) (Track, bool) {
	return t.tr, true
}

func TestConvert(t *testing.T) {
	tl := testLibrary{
		tr: &tr,
	}

	l := Convert(tl, "ID")

	got := l.Tracks()
	expected := tl.Tracks()

	if !reflect.DeepEqual(got, expected) {
		t.Errorf("l.Tracks() = %v, expected: %v", got, expected)
	}

	id := "ID"
	gotTrack, _ := l.Track(id)
	expectedTrack, _ := tl.Track(id)
	if !reflect.DeepEqual(gotTrack, expectedTrack) {
		t.Errorf("l.Track(%#v) = %#v, expected: %#v", id, gotTrack, expectedTrack)
	}
}

func TestLibraryEncodeDecode(t *testing.T) {
	tl := testLibrary{
		tr: &tr,
	}

	l := Convert(tl, "ID")
	buf := &bytes.Buffer{}
	err := WriteTo(l, buf)
	if err != nil {
		t.Errorf("unexpected error in WriteTo: %v", err)
	}

	got, err := ReadFrom(buf)
	if err != nil {
		t.Errorf("unexpected error in ReadFrom: %v", err)
	}

	gotTracks := got.Tracks()
	expectedTracks := l.Tracks()

	if len(gotTracks) != len(expectedTracks) {
		t.Errorf("expected %d tracks, got: %d", len(expectedTracks), len(gotTracks))
	}

	// TODO(dhowden): Remove this mess!
	gotTrack := gotTracks[0].(*track)
	expectedTrack := expectedTracks[0].(*track)

	gotTrack.DateAdded = gotTrack.DateAdded.Local()
	gotTrack.DateModified = gotTrack.DateModified.Local()

	if !reflect.DeepEqual(expectedTrack, gotTrack) {
		t.Errorf("Encode -> Decode inconsistent, got: %#v, expected: %#v", gotTrack, expectedTrack)
	}
}
<|endoftext|>


<commit_before>package config

import (
	"github.com/stretchr/testify/assert"
	"io/ioutil"
	"log"
	"os"
	"testing"
)

func TestMissingFile(t *testing.T) {
	filename := "test"
	_, err := ReadConfig(filename)

	assert.NotNil(t, err)
}

func TestWrongJSONFormat(t *testing.T) {
	content := []byte(`{"DB_HOST": "127.0.0.1""DB_USERNAME": "root","DB_PASSWORD": "","DB_PORT": 3306,"DB_NAME": "test"}`)
	filename := "tempfile"

	if err := ioutil.WriteFile(filename, content, 0644); err != nil {
		log.Fatalf("WriteFile %s: %v", filename, err)
	}

	// clean up
	defer os.Remove(filename)

	_, err := ReadConfig(filename)

	assert.NotNil(t, err)
}

func TestReadConfig(t *testing.T) {
	content := []byte(`{"DB_HOST": "127.0.0.1","DB_USERNAME": "root","DB_PASSWORD": "","DB_PORT": 3306,"DB_NAME": "test"}`)
	filename := "tempfile"

	if err := ioutil.WriteFile(filename, content, 0644); err != nil {
		log.Fatalf("WriteFile %s: %v", filename, err)
	}

	// clean up
	defer os.Remove(filename)

	configs, err := ReadConfig(filename)

	assert.Nil(t, err)
	assert.Equal(t, configs.DB_HOST, "127.0.0.1")
	assert.Equal(t, configs.DB_USERNAME, "root")
	assert.Empty(t, configs.DB_PASSWORD)
	assert.Equal(t, configs.DB_PORT, 3306)
	assert.Equal(t, configs.DB_NAME, "test")
}
<commit_msg>Add comment.<commit_after>package config

import (
	"github.com/stretchr/testify/assert"
	"io/ioutil"
	"log"
	"os"
	"testing"
)

// Test file is missing
func TestMissingFile(t *testing.T) {
	filename := "test"
	_, err := ReadConfig(filename)

	assert.NotNil(t, err)
}

// Test wrong json format
func TestWrongJSONFormat(t *testing.T) {
	content := []byte(`{"DB_HOST": "127.0.0.1""DB_USERNAME": "root","DB_PASSWORD": "","DB_PORT": 3306,"DB_NAME": "test"}`)
	filename := "tempfile"

	if err := ioutil.WriteFile(filename, content, 0644); err != nil {
		log.Fatalf("WriteFile %s: %v", filename, err)
	}

	// clean up
	defer os.Remove(filename)

	// parse JSON format error
	_, err := ReadConfig(filename)

	assert.NotNil(t, err)
}

// Test config file.
func TestReadConfig(t *testing.T) {
	content := []byte(`{"DB_HOST": "127.0.0.1","DB_USERNAME": "root","DB_PASSWORD": "","DB_PORT": 3306,"DB_NAME": "test"}`)
	filename := "tempfile"

	if err := ioutil.WriteFile(filename, content, 0644); err != nil {
		log.Fatalf("WriteFile %s: %v", filename, err)
	}

	// clean up
	defer os.Remove(filename)

	configs, err := ReadConfig(filename)

	assert.Nil(t, err)
	assert.Equal(t, configs.DB_HOST, "127.0.0.1")
	assert.Equal(t, configs.DB_USERNAME, "root")
	assert.Empty(t, configs.DB_PASSWORD)
	assert.Equal(t, configs.DB_PORT, 3306)
	assert.Equal(t, configs.DB_NAME, "test")
}
<|endoftext|>


<commit_before>package config

import (
	"os"
	"strings"
	"testing"
	"time"

	"github.com/asobti/kube-monkey/config/param"
	"github.com/bouk/monkey"
	"github.com/golang/glog"
	"github.com/spf13/viper"
	"github.com/stretchr/testify/suite"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

type ConfigTestSuite struct {
	suite.Suite
}

func (s *ConfigTestSuite) SetupTest() {
	viper.Reset()
	SetDefaults()
}

func (s *ConfigTestSuite) TestSetDefaults() {

	s.True(viper.GetBool(param.DryRun))
	s.Equal("America/Los_Angeles", viper.GetString(param.Timezone))
	s.Equal(8, viper.GetInt(param.RunHour))
	s.Equal(10, viper.GetInt(param.StartHour))
	s.Equal(16, viper.GetInt(param.EndHour))
	s.Equal(int64(5), viper.GetInt64(param.GracePeriodSec))
	s.Equal([]string{metav1.NamespaceSystem}, viper.GetStringSlice(param.BlacklistedNamespaces))
	s.Equal([]string{metav1.NamespaceAll}, viper.GetStringSlice(param.WhitelistedNamespaces))
	s.False(viper.GetBool(param.DebugEnabled))
	s.Equal(viper.GetInt(param.DebugScheduleDelay), 30)
	s.False(viper.GetBool(param.DebugForceShouldKill))
	s.False(viper.GetBool(param.DebugScheduleImmediateKill))

}

func (s *ConfigTestSuite) TestDryRun() {
	viper.Set(param.DryRun, false)
	s.False(DryRun())
	viper.Set(param.DryRun, true)
	s.True(DryRun())
}

func (s *ConfigTestSuite) TestTimezone() {
	viper.Set(param.Timezone, "nolnexistent")

	// avoid Exit(255) on glog.Fatal
	monkey.Patch(glog.Fatal, func(a ...interface{}) {
		s.Contains(a[0], "cannot find nolnexistent in zip file")
	})
	defer func() { monkey.Unpatch(glog.Fatal) }()
	s.Equal((*time.Location)(nil), Timezone())
	viper.Set(param.Timezone, "UTC")
	s.Equal(Timezone().String(), "UTC")
}

func (s *ConfigTestSuite) TestStartHourEnv() {
	envname := "KUBEMONKEY_START_HOUR"
	defer os.Setenv(envname, os.Getenv(envname))
	os.Setenv(envname, "11")
	s.Equal(11, StartHour())
}

func (s *ConfigTestSuite) TestRunHour() {
	viper.Set(param.RunHour, 11)
	s.Equal(11, RunHour())
}

func (s *ConfigTestSuite) TestStartHour() {
	viper.Set(param.StartHour, 10)
	s.Equal(10, StartHour())
}

func (s *ConfigTestSuite) TestEndHour() {
	viper.Set(param.EndHour, 9)
	s.Equal(9, EndHour())
}

func (s *ConfigTestSuite) TestGracePeriodSeconds() {
	g := int64(100)
	viper.Set(param.GracePeriodSec, 100)
	s.Equal(&g, GracePeriodSeconds())
}

func (s *ConfigTestSuite) TestBlacklistedNamespacesEnv() {
	blns := []string{"namespace3", "namespace4"}
	envname := "KUBEMONKEY_BLACKLISTED_NAMESPACES"
	defer os.Setenv(envname, os.Getenv(envname))
	os.Setenv(envname, strings.Join(blns, " "))
	ns := BlacklistedNamespaces()
	s.Len(ns, len(blns))
	for _, v := range blns {
		s.Contains(ns, v)
	}
}

func (s *ConfigTestSuite) TestBlacklistedNamespaces() {
	blns := []string{"namespace1", "namespace2"}
	viper.Set(param.BlacklistedNamespaces, blns)
	ns := BlacklistedNamespaces()
	s.Len(ns, len(blns))
	for _, v := range blns {
		s.Contains(ns, v)
	}
}

func (s *ConfigTestSuite) TestWhitelistedNamespaces() {
	wlns := []string{"namespace1", "namespace2"}
	viper.Set(param.WhitelistedNamespaces, wlns)
	ns := WhitelistedNamespaces()
	s.Len(ns, len(wlns))
	for _, v := range wlns {
		s.Contains(ns, v)
	}
}

func (s *ConfigTestSuite) TestBlacklistEnabled() {
	s.True(BlacklistEnabled())
	viper.Set(param.BlacklistedNamespaces, []string{metav1.NamespaceNone})
	s.False(BlacklistEnabled())
}

func (s *ConfigTestSuite) TestWhitelistEnabled() {
	s.False(WhitelistEnabled())
	viper.Set(param.WhitelistedNamespaces, []string{metav1.NamespaceDefault})
	s.True(WhitelistEnabled())
}

func (s *ConfigTestSuite) TestClusterrAPIServerHost() {
	host, enabled := ClusterAPIServerHost()
	s.False(enabled)
	s.Empty(host)
	viper.Set(param.ClusterAPIServerHost, "Host")
	host, enabled = ClusterAPIServerHost()
	s.True(enabled)
	s.Equal("Host", host)
}

func (s *ConfigTestSuite) TestDebugEnabled() {
	viper.Set(param.DebugEnabled, true)
	s.True(DebugEnabled())
}

func (s *ConfigTestSuite) TestDebugScheduleDelay() {
	viper.Set(param.DebugScheduleDelay, 10)
	s.Equal(10*time.Second, DebugScheduleDelay())
}
func (s *ConfigTestSuite) TestDebugForceShouldKill() {
	viper.Set(param.DebugForceShouldKill, true)
	s.True(DebugForceShouldKill())
}

func (s *ConfigTestSuite) TestDebugInmediateKill() {
	viper.Set(param.DebugScheduleImmediateKill, true)
	s.True(DebugScheduleImmediateKill())
}

func TestSuite(t *testing.T) {
	suite.Run(t, new(ConfigTestSuite))
}
<commit_msg>new error for timezone<commit_after>package config

import (
	"os"
	"strings"
	"testing"
	"time"

	"github.com/asobti/kube-monkey/config/param"
	"github.com/bouk/monkey"
	"github.com/golang/glog"
	"github.com/spf13/viper"
	"github.com/stretchr/testify/suite"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

type ConfigTestSuite struct {
	suite.Suite
}

func (s *ConfigTestSuite) SetupTest() {
	viper.Reset()
	SetDefaults()
}

func (s *ConfigTestSuite) TestSetDefaults() {

	s.True(viper.GetBool(param.DryRun))
	s.Equal("America/Los_Angeles", viper.GetString(param.Timezone))
	s.Equal(8, viper.GetInt(param.RunHour))
	s.Equal(10, viper.GetInt(param.StartHour))
	s.Equal(16, viper.GetInt(param.EndHour))
	s.Equal(int64(5), viper.GetInt64(param.GracePeriodSec))
	s.Equal([]string{metav1.NamespaceSystem}, viper.GetStringSlice(param.BlacklistedNamespaces))
	s.Equal([]string{metav1.NamespaceAll}, viper.GetStringSlice(param.WhitelistedNamespaces))
	s.False(viper.GetBool(param.DebugEnabled))
	s.Equal(viper.GetInt(param.DebugScheduleDelay), 30)
	s.False(viper.GetBool(param.DebugForceShouldKill))
	s.False(viper.GetBool(param.DebugScheduleImmediateKill))

}

func (s *ConfigTestSuite) TestDryRun() {
	viper.Set(param.DryRun, false)
	s.False(DryRun())
	viper.Set(param.DryRun, true)
	s.True(DryRun())
}

func (s *ConfigTestSuite) TestTimezone() {
	viper.Set(param.Timezone, "nolnexistent")

	// avoid Exit(255) on glog.Fatal
	monkey.Patch(glog.Fatal, func(a ...interface{}) {
		s.Contains(a[0], "unknown time zone nolnexistent")
	})
	defer func() { monkey.Unpatch(glog.Fatal) }()
	s.Equal((*time.Location)(nil), Timezone())
	viper.Set(param.Timezone, "UTC")
	s.Equal(Timezone().String(), "UTC")
}

func (s *ConfigTestSuite) TestStartHourEnv() {
	envname := "KUBEMONKEY_START_HOUR"
	defer os.Setenv(envname, os.Getenv(envname))
	os.Setenv(envname, "11")
	s.Equal(11, StartHour())
}

func (s *ConfigTestSuite) TestRunHour() {
	viper.Set(param.RunHour, 11)
	s.Equal(11, RunHour())
}

func (s *ConfigTestSuite) TestStartHour() {
	viper.Set(param.StartHour, 10)
	s.Equal(10, StartHour())
}

func (s *ConfigTestSuite) TestEndHour() {
	viper.Set(param.EndHour, 9)
	s.Equal(9, EndHour())
}

func (s *ConfigTestSuite) TestGracePeriodSeconds() {
	g := int64(100)
	viper.Set(param.GracePeriodSec, 100)
	s.Equal(&g, GracePeriodSeconds())
}

func (s *ConfigTestSuite) TestBlacklistedNamespacesEnv() {
	blns := []string{"namespace3", "namespace4"}
	envname := "KUBEMONKEY_BLACKLISTED_NAMESPACES"
	defer os.Setenv(envname, os.Getenv(envname))
	os.Setenv(envname, strings.Join(blns, " "))
	ns := BlacklistedNamespaces()
	s.Len(ns, len(blns))
	for _, v := range blns {
		s.Contains(ns, v)
	}
}

func (s *ConfigTestSuite) TestBlacklistedNamespaces() {
	blns := []string{"namespace1", "namespace2"}
	viper.Set(param.BlacklistedNamespaces, blns)
	ns := BlacklistedNamespaces()
	s.Len(ns, len(blns))
	for _, v := range blns {
		s.Contains(ns, v)
	}
}

func (s *ConfigTestSuite) TestWhitelistedNamespaces() {
	wlns := []string{"namespace1", "namespace2"}
	viper.Set(param.WhitelistedNamespaces, wlns)
	ns := WhitelistedNamespaces()
	s.Len(ns, len(wlns))
	for _, v := range wlns {
		s.Contains(ns, v)
	}
}

func (s *ConfigTestSuite) TestBlacklistEnabled() {
	s.True(BlacklistEnabled())
	viper.Set(param.BlacklistedNamespaces, []string{metav1.NamespaceNone})
	s.False(BlacklistEnabled())
}

func (s *ConfigTestSuite) TestWhitelistEnabled() {
	s.False(WhitelistEnabled())
	viper.Set(param.WhitelistedNamespaces, []string{metav1.NamespaceDefault})
	s.True(WhitelistEnabled())
}

func (s *ConfigTestSuite) TestClusterrAPIServerHost() {
	host, enabled := ClusterAPIServerHost()
	s.False(enabled)
	s.Empty(host)
	viper.Set(param.ClusterAPIServerHost, "Host")
	host, enabled = ClusterAPIServerHost()
	s.True(enabled)
	s.Equal("Host", host)
}

func (s *ConfigTestSuite) TestDebugEnabled() {
	viper.Set(param.DebugEnabled, true)
	s.True(DebugEnabled())
}

func (s *ConfigTestSuite) TestDebugScheduleDelay() {
	viper.Set(param.DebugScheduleDelay, 10)
	s.Equal(10*time.Second, DebugScheduleDelay())
}
func (s *ConfigTestSuite) TestDebugForceShouldKill() {
	viper.Set(param.DebugForceShouldKill, true)
	s.True(DebugForceShouldKill())
}

func (s *ConfigTestSuite) TestDebugInmediateKill() {
	viper.Set(param.DebugScheduleImmediateKill, true)
	s.True(DebugScheduleImmediateKill())
}

func TestSuite(t *testing.T) {
	suite.Run(t, new(ConfigTestSuite))
}
<|endoftext|>


<commit_before>// Copyright 2016 The etcd 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 concurrency

import (
	v3 "github.com/coreos/etcd/clientv3"
	"golang.org/x/net/context"
)

// STM is an interface for software transactional memory.
type STM interface {
	// Get returns the value for a key and inserts the key in the txn's read set.
	// If Get fails, it aborts the transaction with an error, never returning.
	Get(key string) string
	// Put adds a value for a key to the write set.
	Put(key, val string, opts ...v3.OpOption)
	// Rev returns the revision of a key in the read set.
	Rev(key string) int64
	// Del deletes a key.
	Del(key string)

	// commit attempts to apply the txn's changes to the server.
	commit() *v3.TxnResponse
	reset()
}

// Isolation is an enumeration of transactional isolation levels which
// describes how transactions should interfere and conflict.
type Isolation int

const (
	// Snapshot is serializable but also checks writes for conflicts.
	Snapshot Isolation = iota
	// Serializable reads within the same transactiona attempt return data
	// from the at the revision of the first read.
	Serializable
	// RepeatableReads reads within the same transaction attempt always
	// return the same data.
	RepeatableReads
	// ReadCommitted reads keys from any committed revision.
	ReadCommitted
)

// stmError safely passes STM errors through panic to the STM error channel.
type stmError struct{ err error }

type stmOptions struct {
	iso Isolation
	ctx       context.Context
}

type stmOption func(*stmOptions)

// WithIsolation specifies the transaction isolation level.
func WithIsolation(lvl Isolation) stmOption {
	return func(so *stmOptions) { so.iso = lvl }
}

// WithAbortContext specifies the context for permanently aborting the transaction.
func WithAbortContext(ctx context.Context) stmOption {
	return func(so *stmOptions) { so.ctx = ctx }
}

// NewSTM initiates a new STM instance.
func NewSTM(c *v3.Client, apply func(STM) error, so ...stmOption) (*v3.TxnResponse, error) {
	opts := &stmOptions{ctx: c.Ctx()}
	for _, f := range so {
		f(opts)
	}
	var s STM
	switch opts.iso {
	case Serializable:
		s = &stmSerializable{
			stm:      stm{client: c, ctx: opts.ctx},
			prefetch: make(map[string]*v3.GetResponse),
		}
	case RepeatableReads:
		s = &stm{client: c, ctx: opts.ctx, getOpts: []v3.OpOption{v3.WithSerializable()}}
	case ReadCommitted:
		ss := stm{client: c, ctx: opts.ctx, getOpts: []v3.OpOption{v3.WithSerializable()}}
		s = &stmReadCommitted{ss}
	default:
		panic("unsupported")
	}
	return runSTM(s, apply)
}

type stmResponse struct {
	resp *v3.TxnResponse
	err  error
}

func runSTM(s STM, apply func(STM) error) (*v3.TxnResponse, error) {
	outc := make(chan stmResponse, 1)
	go func() {
		defer func() {
			if r := recover(); r != nil {
				e, ok := r.(stmError)
				if !ok {
					// client apply panicked
					panic(r)
				}
				outc <- stmResponse{nil, e.err}
			}
		}()
		var out stmResponse
		for {
			s.reset()
			if out.err = apply(s); out.err != nil {
				break
			}
			if out.resp = s.commit(); out.resp != nil {
				break
			}
		}
		outc <- out
	}()
	r := <-outc
	return r.resp, r.err
}

// stm implements repeatable-read software transactional memory over etcd
type stm struct {
	client *v3.Client
	ctx    context.Context
	// rset holds read key values and revisions
	rset map[string]*v3.GetResponse
	// wset holds overwritten keys and their values
	wset map[string]stmPut
	// getOpts are the opts used for gets
	getOpts []v3.OpOption
}

type stmPut struct {
	val string
	op  v3.Op
}

func (s *stm) Get(key string) string {
	if wv, ok := s.wset[key]; ok {
		return wv.val
	}
	return respToValue(s.fetch(key))
}

func (s *stm) Put(key, val string, opts ...v3.OpOption) {
	s.wset[key] = stmPut{val, v3.OpPut(key, val, opts...)}
}

func (s *stm) Del(key string) { s.wset[key] = stmPut{"", v3.OpDelete(key)} }

func (s *stm) Rev(key string) int64 {
	if resp := s.fetch(key); resp != nil && len(resp.Kvs) != 0 {
		return resp.Kvs[0].ModRevision
	}
	return 0
}

func (s *stm) commit() *v3.TxnResponse {
	txnresp, err := s.client.Txn(s.ctx).If(s.cmps()...).Then(s.puts()...).Commit()
	if err != nil {
		panic(stmError{err})
	}
	if txnresp.Succeeded {
		return txnresp
	}
	return nil
}

// cmps guards the txn from updates to read set
func (s *stm) cmps() []v3.Cmp {
	cmps := make([]v3.Cmp, 0, len(s.rset))
	for k, rk := range s.rset {
		cmps = append(cmps, isKeyCurrent(k, rk))
	}
	return cmps
}

func (s *stm) fetch(key string) *v3.GetResponse {
	if resp, ok := s.rset[key]; ok {
		return resp
	}
	resp, err := s.client.Get(s.ctx, key, s.getOpts...)
	if err != nil {
		panic(stmError{err})
	}
	s.rset[key] = resp
	return resp
}

// puts is the list of ops for all pending writes
func (s *stm) puts() []v3.Op {
	puts := make([]v3.Op, 0, len(s.wset))
	for _, v := range s.wset {
		puts = append(puts, v.op)
	}
	return puts
}

func (s *stm) reset() {
	s.rset = make(map[string]*v3.GetResponse)
	s.wset = make(map[string]stmPut)
}

type stmSerializable struct {
	stm
	prefetch map[string]*v3.GetResponse
}

func (s *stmSerializable) Get(key string) string {
	if wv, ok := s.wset[key]; ok {
		return wv.val
	}
	firstRead := len(s.rset) == 0
	if resp, ok := s.prefetch[key]; ok {
		delete(s.prefetch, key)
		s.rset[key] = resp
	}
	resp := s.stm.fetch(key)
	if firstRead {
		// txn's base revision is defined by the first read
		s.getOpts = []v3.OpOption{
			v3.WithRev(resp.Header.Revision),
			v3.WithSerializable(),
		}
	}
	return respToValue(resp)
}

func (s *stmSerializable) Rev(key string) int64 {
	s.Get(key)
	return s.stm.Rev(key)
}

func (s *stmSerializable) gets() ([]string, []v3.Op) {
	keys := make([]string, 0, len(s.rset))
	ops := make([]v3.Op, 0, len(s.rset))
	for k := range s.rset {
		keys = append(keys, k)
		ops = append(ops, v3.OpGet(k))
	}
	return keys, ops
}

func (s *stmSerializable) commit() *v3.TxnResponse {
	keys, getops := s.gets()
	txn := s.client.Txn(s.ctx).If(s.cmps()...).Then(s.puts()...)
	// use Else to prefetch keys in case of conflict to save a round trip
	txnresp, err := txn.Else(getops...).Commit()
	if err != nil {
		panic(stmError{err})
	}
	if txnresp.Succeeded {
		return txnresp
	}
	// load prefetch with Else data
	for i := range keys {
		resp := txnresp.Responses[i].GetResponseRange()
		s.rset[keys[i]] = (*v3.GetResponse)(resp)
	}
	s.prefetch = s.rset
	s.getOpts = nil
	return nil
}

type stmReadCommitted struct{ stm }

// commit always goes through when read committed
func (s *stmReadCommitted) commit() *v3.TxnResponse {
	s.rset = nil
	return s.stm.commit()
}

func isKeyCurrent(k string, r *v3.GetResponse) v3.Cmp {
	if len(r.Kvs) != 0 {
		return v3.Compare(v3.ModRevision(k), "=", r.Kvs[0].ModRevision)
	}
	return v3.Compare(v3.ModRevision(k), "=", 0)
}

func respToValue(resp *v3.GetResponse) string {
	if len(resp.Kvs) == 0 {
		return ""
	}
	return string(resp.Kvs[0].Value)
}
<commit_msg>concurrency: extend STM interface to Get from any of a list of keys<commit_after>// Copyright 2016 The etcd 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 concurrency

import (
	v3 "github.com/coreos/etcd/clientv3"
	"golang.org/x/net/context"
)

// STM is an interface for software transactional memory.
type STM interface {
	// Get returns the value for a key and inserts the key in the txn's read set.
	// If Get fails, it aborts the transaction with an error, never returning.
	Get(key ...string) string
	// Put adds a value for a key to the write set.
	Put(key, val string, opts ...v3.OpOption)
	// Rev returns the revision of a key in the read set.
	Rev(key string) int64
	// Del deletes a key.
	Del(key string)

	// commit attempts to apply the txn's changes to the server.
	commit() *v3.TxnResponse
	reset()
}

// Isolation is an enumeration of transactional isolation levels which
// describes how transactions should interfere and conflict.
type Isolation int

const (
	// Snapshot is serializable but also checks writes for conflicts.
	Snapshot Isolation = iota
	// Serializable reads within the same transactiona attempt return data
	// from the at the revision of the first read.
	Serializable
	// RepeatableReads reads within the same transaction attempt always
	// return the same data.
	RepeatableReads
	// ReadCommitted reads keys from any committed revision.
	ReadCommitted
)

// stmError safely passes STM errors through panic to the STM error channel.
type stmError struct{ err error }

type stmOptions struct {
	iso Isolation
	ctx context.Context
}

type stmOption func(*stmOptions)

// WithIsolation specifies the transaction isolation level.
func WithIsolation(lvl Isolation) stmOption {
	return func(so *stmOptions) { so.iso = lvl }
}

// WithAbortContext specifies the context for permanently aborting the transaction.
func WithAbortContext(ctx context.Context) stmOption {
	return func(so *stmOptions) { so.ctx = ctx }
}

// NewSTM initiates a new STM instance.
func NewSTM(c *v3.Client, apply func(STM) error, so ...stmOption) (*v3.TxnResponse, error) {
	opts := &stmOptions{ctx: c.Ctx()}
	for _, f := range so {
		f(opts)
	}
	var s STM
	switch opts.iso {
	case Serializable:
		s = &stmSerializable{
			stm:      stm{client: c, ctx: opts.ctx},
			prefetch: make(map[string]*v3.GetResponse),
		}
	case RepeatableReads:
		s = &stm{client: c, ctx: opts.ctx, getOpts: []v3.OpOption{v3.WithSerializable()}}
	case ReadCommitted:
		ss := stm{client: c, ctx: opts.ctx, getOpts: []v3.OpOption{v3.WithSerializable()}}
		s = &stmReadCommitted{ss}
	default:
		panic("unsupported")
	}
	return runSTM(s, apply)
}

type stmResponse struct {
	resp *v3.TxnResponse
	err  error
}

func runSTM(s STM, apply func(STM) error) (*v3.TxnResponse, error) {
	outc := make(chan stmResponse, 1)
	go func() {
		defer func() {
			if r := recover(); r != nil {
				e, ok := r.(stmError)
				if !ok {
					// client apply panicked
					panic(r)
				}
				outc <- stmResponse{nil, e.err}
			}
		}()
		var out stmResponse
		for {
			s.reset()
			if out.err = apply(s); out.err != nil {
				break
			}
			if out.resp = s.commit(); out.resp != nil {
				break
			}
		}
		outc <- out
	}()
	r := <-outc
	return r.resp, r.err
}

// stm implements repeatable-read software transactional memory over etcd
type stm struct {
	client *v3.Client
	ctx    context.Context
	// rset holds read key values and revisions
	rset map[string]*v3.GetResponse
	// wset holds overwritten keys and their values
	wset writeSet
	// getOpts are the opts used for gets
	getOpts []v3.OpOption
}

type stmPut struct {
	val string
	op  v3.Op
}

type writeSet map[string]stmPut

func (ws writeSet) get(keys ...string) *stmPut {
	for _, key := range keys {
		if wv, ok := ws[key]; ok {
			return &wv
		}
	}
	return nil
}

// puts is the list of ops for all pending writes
func (ws writeSet) puts() []v3.Op {
	puts := make([]v3.Op, 0, len(ws))
	for _, v := range ws {
		puts = append(puts, v.op)
	}
	return puts
}

func (s *stm) Get(keys ...string) string {
	if wv := s.wset.get(keys...); wv != nil {
		return wv.val
	}
	return respToValue(s.fetch(keys...))
}

func (s *stm) Put(key, val string, opts ...v3.OpOption) {
	s.wset[key] = stmPut{val, v3.OpPut(key, val, opts...)}
}

func (s *stm) Del(key string) { s.wset[key] = stmPut{"", v3.OpDelete(key)} }

func (s *stm) Rev(key string) int64 {
	if resp := s.fetch(key); resp != nil && len(resp.Kvs) != 0 {
		return resp.Kvs[0].ModRevision
	}
	return 0
}

func (s *stm) commit() *v3.TxnResponse {
	txnresp, err := s.client.Txn(s.ctx).If(s.cmps()...).Then(s.wset.puts()...).Commit()
	if err != nil {
		panic(stmError{err})
	}
	if txnresp.Succeeded {
		return txnresp
	}
	return nil
}

// cmps guards the txn from updates to read set
func (s *stm) cmps() []v3.Cmp {
	cmps := make([]v3.Cmp, 0, len(s.rset))
	for k, rk := range s.rset {
		cmps = append(cmps, isKeyCurrent(k, rk))
	}
	return cmps
}

func (s *stm) fetch(keys ...string) *v3.GetResponse {
	if len(keys) == 0 {
		return nil
	}
	ops := make([]v3.Op, len(keys))
	for i, key := range keys {
		if resp, ok := s.rset[key]; ok {
			return resp
		}
		ops[i] = v3.OpGet(key, s.getOpts...)
	}
	txnresp, err := s.client.Txn(s.ctx).Then(ops...).Commit()
	if err != nil {
		panic(stmError{err})
	}
	addTxnResp(s.rset, keys, txnresp)
	return (*v3.GetResponse)(txnresp.Responses[0].GetResponseRange())
}

func (s *stm) reset() {
	s.rset = make(map[string]*v3.GetResponse)
	s.wset = make(map[string]stmPut)
}

type stmSerializable struct {
	stm
	prefetch map[string]*v3.GetResponse
}

func (s *stmSerializable) Get(keys ...string) string {
	if wv := s.wset.get(keys...); wv != nil {
		return wv.val
	}
	firstRead := len(s.rset) == 0
	for _, key := range keys {
		if resp, ok := s.prefetch[key]; ok {
			delete(s.prefetch, key)
			s.rset[key] = resp
		}
	}
	resp := s.stm.fetch(keys...)
	if firstRead {
		// txn's base revision is defined by the first read
		s.getOpts = []v3.OpOption{
			v3.WithRev(resp.Header.Revision),
			v3.WithSerializable(),
		}
	}
	return respToValue(resp)
}

func (s *stmSerializable) Rev(key string) int64 {
	s.Get(key)
	return s.stm.Rev(key)
}

func (s *stmSerializable) gets() ([]string, []v3.Op) {
	keys := make([]string, 0, len(s.rset))
	ops := make([]v3.Op, 0, len(s.rset))
	for k := range s.rset {
		keys = append(keys, k)
		ops = append(ops, v3.OpGet(k))
	}
	return keys, ops
}

func (s *stmSerializable) commit() *v3.TxnResponse {
	keys, getops := s.gets()
	txn := s.client.Txn(s.ctx).If(s.cmps()...).Then(s.wset.puts()...)
	// use Else to prefetch keys in case of conflict to save a round trip
	txnresp, err := txn.Else(getops...).Commit()
	if err != nil {
		panic(stmError{err})
	}
	if txnresp.Succeeded {
		return txnresp
	}
	// load prefetch with Else data
	addTxnResp(s.rset, keys, txnresp)
	s.prefetch = s.rset
	s.getOpts = nil
	return nil
}

func addTxnResp(rset map[string]*v3.GetResponse, keys []string, txnresp *v3.TxnResponse) {
	for i, resp := range txnresp.Responses {
		rset[keys[i]] = (*v3.GetResponse)(resp.GetResponseRange())
	}
}

type stmReadCommitted struct{ stm }

// commit always goes through when read committed
func (s *stmReadCommitted) commit() *v3.TxnResponse {
	s.rset = nil
	return s.stm.commit()
}

func isKeyCurrent(k string, r *v3.GetResponse) v3.Cmp {
	if len(r.Kvs) != 0 {
		return v3.Compare(v3.ModRevision(k), "=", r.Kvs[0].ModRevision)
	}
	return v3.Compare(v3.ModRevision(k), "=", 0)
}

func respToValue(resp *v3.GetResponse) string {
	if resp == nil || len(resp.Kvs) == 0 {
		return ""
	}
	return string(resp.Kvs[0].Value)
}
<|endoftext|>


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

package config

import (
	"crypto/md5"
	"encoding/binary"
	"sync"
	"time"

	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/log/level"
	"github.com/prometheus/client_golang/prometheus"
)

// Coordinator coordinates Alertmanager configurations beyond the lifetime of a
// single configuration.
type Coordinator struct {
	configFilePath string
	logger         log.Logger

	// Protects config and subscribers
	mutex       sync.Mutex
	config      *Config
	subscribers []func(*Config) error

	configHashMetric        prometheus.Gauge
	configSuccessMetric     prometheus.Gauge
	configSuccessTimeMetric prometheus.Gauge
}

// NewCoordinator returns a new coordinator with the given configuration file
// path. It does not yet load the configuration from file. This is done in
// `Reload()`.
func NewCoordinator(configFilePath string, r prometheus.Registerer, l log.Logger) *Coordinator {
	c := &Coordinator{
		configFilePath: configFilePath,
		logger:         l,
	}

	c.registerMetrics(r)

	return c
}

func (c *Coordinator) registerMetrics(r prometheus.Registerer) {
	configHash := prometheus.NewGauge(prometheus.GaugeOpts{
		Name: "alertmanager_config_hash",
		Help: "Hash of the currently loaded alertmanager configuration.",
	})
	configSuccess := prometheus.NewGauge(prometheus.GaugeOpts{
		Name: "alertmanager_config_last_reload_successful",
		Help: "Whether the last configuration reload attempt was successful.",
	})
	configSuccessTime := prometheus.NewGauge(prometheus.GaugeOpts{
		Name: "alertmanager_config_last_reload_success_timestamp_seconds",
		Help: "Timestamp of the last successful configuration reload.",
	})

	r.MustRegister(configHash, configSuccess, configSuccessTime)

	c.configHashMetric = configHash
	c.configSuccessMetric = configSuccess
	c.configSuccessTimeMetric = configSuccessTime
}

// Subscribe subscribes the given Subscribers to configuration changes.
func (c *Coordinator) Subscribe(ss ...func(*Config) error) {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	c.subscribers = append(c.subscribers, ss...)
}

func (c *Coordinator) notifySubscribers() error {
	for _, s := range c.subscribers {
		if err := s(c.config); err != nil {
			return err
		}
	}

	return nil
}

// loadFromFile triggers a configuration load, discarding the old configuration.
func (c *Coordinator) loadFromFile() error {
	conf, err := LoadFile(c.configFilePath)
	if err != nil {
		c.configSuccessMetric.Set(0)
		return err
	}

	c.config = conf
	c.configSuccessMetric.Set(1)
	c.configSuccessTimeMetric.Set(float64(time.Now().Unix()))
	hash := md5HashAsMetricValue([]byte(c.config.original))
	c.configHashMetric.Set(hash)

	return nil
}

// Reload triggers a configuration reload from file and notifies all
// configuration change subscribers.
func (c *Coordinator) Reload() error {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	level.Info(c.logger).Log(
		"msg", "Loading configuration file",
		"file", c.configFilePath,
	)
	if err := c.loadFromFile(); err != nil {
		level.Error(c.logger).Log(
			"msg", "Loading configuration file failed",
			"file", c.configFilePath,
			"err", err,
		)
		return err
	}
	level.Info(c.logger).Log(
		"msg", "Completed loading of configuration file",
		"file", c.configFilePath,
	)

	if err := c.notifySubscribers(); err != nil {
		c.logger.Log(
			"msg", "one or more config change subscribers failed to apply new config",
			"file", c.configFilePath,
			"err", err,
		)
		return err
	}

	return nil
}

func md5HashAsMetricValue(data []byte) float64 {
	sum := md5.Sum(data)
	// We only want 48 bits as a float64 only has a 53 bit mantissa.
	smallSum := sum[0:6]
	var bytes = make([]byte, 8)
	copy(bytes, smallSum)
	return float64(binary.LittleEndian.Uint64(bytes))
}
<commit_msg>[#2372] Move config reload metrics to Coordinator.Reload() (#2373)<commit_after>// Copyright 2019 Prometheus Team
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package config

import (
	"crypto/md5"
	"encoding/binary"
	"sync"

	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/log/level"
	"github.com/prometheus/client_golang/prometheus"
)

// Coordinator coordinates Alertmanager configurations beyond the lifetime of a
// single configuration.
type Coordinator struct {
	configFilePath string
	logger         log.Logger

	// Protects config and subscribers
	mutex       sync.Mutex
	config      *Config
	subscribers []func(*Config) error

	configHashMetric        prometheus.Gauge
	configSuccessMetric     prometheus.Gauge
	configSuccessTimeMetric prometheus.Gauge
}

// NewCoordinator returns a new coordinator with the given configuration file
// path. It does not yet load the configuration from file. This is done in
// `Reload()`.
func NewCoordinator(configFilePath string, r prometheus.Registerer, l log.Logger) *Coordinator {
	c := &Coordinator{
		configFilePath: configFilePath,
		logger:         l,
	}

	c.registerMetrics(r)

	return c
}

func (c *Coordinator) registerMetrics(r prometheus.Registerer) {
	configHash := prometheus.NewGauge(prometheus.GaugeOpts{
		Name: "alertmanager_config_hash",
		Help: "Hash of the currently loaded alertmanager configuration.",
	})
	configSuccess := prometheus.NewGauge(prometheus.GaugeOpts{
		Name: "alertmanager_config_last_reload_successful",
		Help: "Whether the last configuration reload attempt was successful.",
	})
	configSuccessTime := prometheus.NewGauge(prometheus.GaugeOpts{
		Name: "alertmanager_config_last_reload_success_timestamp_seconds",
		Help: "Timestamp of the last successful configuration reload.",
	})

	r.MustRegister(configHash, configSuccess, configSuccessTime)

	c.configHashMetric = configHash
	c.configSuccessMetric = configSuccess
	c.configSuccessTimeMetric = configSuccessTime
}

// Subscribe subscribes the given Subscribers to configuration changes.
func (c *Coordinator) Subscribe(ss ...func(*Config) error) {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	c.subscribers = append(c.subscribers, ss...)
}

func (c *Coordinator) notifySubscribers() error {
	for _, s := range c.subscribers {
		if err := s(c.config); err != nil {
			return err
		}
	}

	return nil
}

// loadFromFile triggers a configuration load, discarding the old configuration.
func (c *Coordinator) loadFromFile() error {
	conf, err := LoadFile(c.configFilePath)
	if err != nil {
		return err
	}

	c.config = conf

	return nil
}

// Reload triggers a configuration reload from file and notifies all
// configuration change subscribers.
func (c *Coordinator) Reload() error {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	level.Info(c.logger).Log(
		"msg", "Loading configuration file",
		"file", c.configFilePath,
	)
	if err := c.loadFromFile(); err != nil {
		level.Error(c.logger).Log(
			"msg", "Loading configuration file failed",
			"file", c.configFilePath,
			"err", err,
		)
		c.configSuccessMetric.Set(0)
		return err
	}
	level.Info(c.logger).Log(
		"msg", "Completed loading of configuration file",
		"file", c.configFilePath,
	)

	if err := c.notifySubscribers(); err != nil {
		c.logger.Log(
			"msg", "one or more config change subscribers failed to apply new config",
			"file", c.configFilePath,
			"err", err,
		)
		c.configSuccessMetric.Set(0)
		return err
	}

	c.configSuccessMetric.Set(1)
	c.configSuccessTimeMetric.SetToCurrentTime()
	hash := md5HashAsMetricValue([]byte(c.config.original))
	c.configHashMetric.Set(hash)

	return nil
}

func md5HashAsMetricValue(data []byte) float64 {
	sum := md5.Sum(data)
	// We only want 48 bits as a float64 only has a 53 bit mantissa.
	smallSum := sum[0:6]
	var bytes = make([]byte, 8)
	copy(bytes, smallSum)
	return float64(binary.LittleEndian.Uint64(bytes))
}
<|endoftext|>


<commit_before>package modelhelper

import (
	"crypto/sha1"
	"encoding/hex"
	"fmt"
	"koding/db/models"

	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
)

// CheckAndGetUser validates the user with the given password. If not
// successfull it returns nil
func CheckAndGetUser(username string, password string) (*models.User, error) {
	user, err := GetUser(username)
	if err != nil {
		return nil, fmt.Errorf("Username does not match")
	}

	hash := sha1.New()
	hash.Write([]byte(user.Salt))
	hash.Write([]byte(password))

	if user.Password != hex.EncodeToString(hash.Sum(nil)) {
		return nil, fmt.Errorf("Password does not match")
	}

	return user, nil
}

func GetUser(username string) (*models.User, error) {
	user := new(models.User)

	query := func(c *mgo.Collection) error {
		return c.Find(bson.M{"username": username}).One(&user)
	}

	err := Mongo.Run("jUsers", query)
	if err != nil {
		return nil, err
	}

	return user, nil
}

func GetUserById(id string) (*models.User, error) {
	user := new(models.User)
	err := Mongo.One("jUsers", id, user)
	if err != nil {
		return nil, err
	}

	return user, nil
}

func GetSomeUsersBySelector(s Selector) ([]models.User, error) {
	users := make([]models.User, 0)
	query := func(c *mgo.Collection) error {
		return c.Find(s).All(&users)
	}

	return users, Mongo.Run("jUsers", query)
}
<commit_msg>mongodb: add helper functions for creating user, update freq<commit_after>package modelhelper

import (
	"crypto/sha1"
	"encoding/hex"
	"fmt"
	"koding/db/models"

	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
)

// CheckAndGetUser validates the user with the given password. If not
// successfull it returns nil
func CheckAndGetUser(username string, password string) (*models.User, error) {
	user, err := GetUser(username)
	if err != nil {
		return nil, fmt.Errorf("Username does not match")
	}

	hash := sha1.New()
	hash.Write([]byte(user.Salt))
	hash.Write([]byte(password))

	if user.Password != hex.EncodeToString(hash.Sum(nil)) {
		return nil, fmt.Errorf("Password does not match")
	}

	return user, nil
}

func GetUser(username string) (*models.User, error) {
	user := new(models.User)

	query := func(c *mgo.Collection) error {
		return c.Find(bson.M{"username": username}).One(&user)
	}

	err := Mongo.Run("jUsers", query)
	if err != nil {
		return nil, err
	}

	return user, nil
}

func GetUserById(id string) (*models.User, error) {
	user := new(models.User)
	err := Mongo.One("jUsers", id, user)
	if err != nil {
		return nil, err
	}

	return user, nil
}

func GetSomeUsersBySelector(s Selector) ([]models.User, error) {
	users := make([]models.User, 0)
	query := func(c *mgo.Collection) error {
		return c.Find(s).All(&users)
	}

	return users, Mongo.Run("jUsers", query)
}

func CreateUser(a *models.User) error {
	query := insertQuery(a)
	return Mongo.Run("jUsers", query)
}

func UpdateEmailFrequency(username string, e models.EmailFrequency) error {
	selector := bson.M{"username": username}
	updateQuery := bson.M{"$set": bson.M{"emailFrequency": e}}

	query := func(c *mgo.Collection) error {
		_, err := c.UpdateAll(selector, updateQuery)
		return err
	}

	return Mongo.Run("jUsers", query)
}
<|endoftext|>


<commit_before>package main

import (
	"math/rand"
	"socialapi/models"
	"socialapi/rest"
	"strconv"
	"testing"
	"time"

	. "github.com/smartystreets/goconvey/convey"
)

func TestGroupChannel(t *testing.T) {
	Convey("while  testing pinned activity channel", t, func() {
		rand.Seed(time.Now().UnixNano())
		groupName := "testgroup" + strconv.FormatInt(rand.Int63(), 10)
		Convey("channel should be there", func() {

			account := models.NewAccount()
			account.OldId = AccountOldId.Hex()
			account, err := rest.CreateAccount(account)
			So(err, ShouldBeNil)
			So(account, ShouldNotBeNil)

			channel1, err := rest.CreateChannelByGroupNameAndType(account.Id, groupName, models.Channel_TYPE_GROUP)
			So(err, ShouldBeNil)
			So(channel1, ShouldNotBeNil)

			channel2, err := rest.CreateChannelByGroupNameAndType(account.Id, groupName, models.Channel_TYPE_GROUP)
			So(err, ShouldNotBeNil)
			So(channel2, ShouldBeNil)
		})

		Convey("owner should be able to update it", func() {
			account := models.NewAccount()
			account.OldId = AccountOldId.Hex()
			account, err := rest.CreateAccount(account)
			So(err, ShouldBeNil)
			So(account, ShouldNotBeNil)

			channel1, err := rest.CreateChannelByGroupNameAndType(account.Id, groupName, models.Channel_TYPE_GROUP)
			So(err, ShouldBeNil)
			So(channel1, ShouldNotBeNil)
			// fetching channel returns creator id
			_, err = rest.UpdateChannel(channel1)
			So(err, ShouldBeNil)
		})

		Convey("owner should only be able to update name and purpose of the channel", nil)

		Convey("normal user should not be able to update it", func() {
			account := models.NewAccount()
			account.OldId = AccountOldId.Hex()
			account, err := rest.CreateAccount(account)
			So(err, ShouldBeNil)
			So(account, ShouldNotBeNil)

			channel1, err := rest.CreateChannelByGroupNameAndType(account.Id, groupName, models.Channel_TYPE_GROUP)
			So(err, ShouldBeNil)
			So(channel1, ShouldNotBeNil)

			channel1.CreatorId = rand.Int63()
			_, err = rest.UpdateChannel(channel1)
			So(err, ShouldNotBeNil)
		})

		Convey("owner cant delete it", func() {
			account := models.NewAccount()
			account.OldId = AccountOldId.Hex()
			account, err := rest.CreateAccount(account)
			So(err, ShouldBeNil)
			So(account, ShouldNotBeNil)

			channel1, err := rest.CreateChannelByGroupNameAndType(account.Id, groupName, models.Channel_TYPE_GROUP)
			So(err, ShouldBeNil)
			So(channel1, ShouldNotBeNil)

			err = rest.DeleteChannel(account.Id, channel1.Id)
			So(err, ShouldNotBeNil)
		})

		Convey("normal user cant delete it", func() {
			account := models.NewAccount()
			account.OldId = AccountOldId.Hex()
			account, err := rest.CreateAccount(account)
			So(err, ShouldBeNil)
			So(account, ShouldNotBeNil)

			channel1, err := rest.CreateChannelByGroupNameAndType(account.Id, groupName, models.Channel_TYPE_GROUP)
			So(err, ShouldBeNil)
			So(channel1, ShouldNotBeNil)

			err = rest.DeleteChannel(rand.Int63(), channel1.Id)
			So(err, ShouldNotBeNil)
		})

		Convey("member can post status update", nil)

		Convey("non-member can not post status update", nil)
	})
}
<commit_msg>social: add default channel fetch order test<commit_after>package main

import (
	"koding/db/mongodb/modelhelper"
	"math/rand"
	"socialapi/models"
	"socialapi/rest"
	"socialapi/workers/common/runner"
	"strconv"
	"testing"
	"time"

	. "github.com/smartystreets/goconvey/convey"
)

func TestGroupChannel(t *testing.T) {

	r := runner.New("test")
	if err := r.Init(); err != nil {
		t.Fatalf("couldnt start bongo %s", err.Error())
	}
	defer r.Close()

	modelhelper.Initialize(r.Conf.Mongo)
	defer modelhelper.Close()

	Convey("while  testing pinned activity channel", t, func() {
		rand.Seed(time.Now().UnixNano())
		groupName := "testgroup" + strconv.FormatInt(rand.Int63(), 10)
		Convey("channel should be there", func() {

			account := models.NewAccount()
			account.OldId = AccountOldId.Hex()
			account, err := rest.CreateAccount(account)
			So(err, ShouldBeNil)
			So(account, ShouldNotBeNil)

			channel1, err := rest.CreateChannelByGroupNameAndType(account.Id, groupName, models.Channel_TYPE_GROUP)
			So(err, ShouldBeNil)
			So(channel1, ShouldNotBeNil)

			channel2, err := rest.CreateChannelByGroupNameAndType(account.Id, groupName, models.Channel_TYPE_GROUP)
			So(err, ShouldNotBeNil)
			So(channel2, ShouldBeNil)
		})

		Convey("group channel should be shown before announcement", func() {
			account, err := models.CreateAccountInBothDbs()
			So(err, ShouldBeNil)

			channels, err := rest.FetchChannels(account.Id)
			So(err, ShouldBeNil)
			So(len(channels), ShouldEqual, 2)
			So(channels[0].TypeConstant, ShouldEqual, models.Channel_TYPE_GROUP)
			So(channels[1].TypeConstant, ShouldEqual, models.Channel_TYPE_ANNOUNCEMENT)

		})

		Convey("owner should be able to update it", func() {
			account := models.NewAccount()
			account.OldId = AccountOldId.Hex()
			account, err := rest.CreateAccount(account)
			So(err, ShouldBeNil)
			So(account, ShouldNotBeNil)

			channel1, err := rest.CreateChannelByGroupNameAndType(account.Id, groupName, models.Channel_TYPE_GROUP)
			So(err, ShouldBeNil)
			So(channel1, ShouldNotBeNil)
			// fetching channel returns creator id
			_, err = rest.UpdateChannel(channel1)
			So(err, ShouldBeNil)
		})

		Convey("owner should only be able to update name and purpose of the channel", nil)

		Convey("normal user should not be able to update it", func() {
			account := models.NewAccount()
			account.OldId = AccountOldId.Hex()
			account, err := rest.CreateAccount(account)
			So(err, ShouldBeNil)
			So(account, ShouldNotBeNil)

			channel1, err := rest.CreateChannelByGroupNameAndType(account.Id, groupName, models.Channel_TYPE_GROUP)
			So(err, ShouldBeNil)
			So(channel1, ShouldNotBeNil)

			channel1.CreatorId = rand.Int63()
			_, err = rest.UpdateChannel(channel1)
			So(err, ShouldNotBeNil)
		})

		Convey("owner cant delete it", func() {
			account := models.NewAccount()
			account.OldId = AccountOldId.Hex()
			account, err := rest.CreateAccount(account)
			So(err, ShouldBeNil)
			So(account, ShouldNotBeNil)

			channel1, err := rest.CreateChannelByGroupNameAndType(account.Id, groupName, models.Channel_TYPE_GROUP)
			So(err, ShouldBeNil)
			So(channel1, ShouldNotBeNil)

			err = rest.DeleteChannel(account.Id, channel1.Id)
			So(err, ShouldNotBeNil)
		})

		Convey("normal user cant delete it", func() {
			account := models.NewAccount()
			account.OldId = AccountOldId.Hex()
			account, err := rest.CreateAccount(account)
			So(err, ShouldBeNil)
			So(account, ShouldNotBeNil)

			channel1, err := rest.CreateChannelByGroupNameAndType(account.Id, groupName, models.Channel_TYPE_GROUP)
			So(err, ShouldBeNil)
			So(channel1, ShouldNotBeNil)

			err = rest.DeleteChannel(rand.Int63(), channel1.Id)
			So(err, ShouldNotBeNil)
		})

		Convey("member can post status update", nil)

		Convey("non-member can not post status update", nil)
	})
}
<|endoftext|>


<commit_before>// (c) 2021, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package avm

import (
	"encoding/binary"

	"github.com/ava-labs/avalanchego/database/versiondb"
	"github.com/ava-labs/avalanchego/utils/logging"
	"github.com/ava-labs/avalanchego/vms/components/avax"

	"github.com/ava-labs/avalanchego/database"
	"github.com/ava-labs/avalanchego/database/prefixdb"
	"github.com/ava-labs/avalanchego/ids"
	"github.com/ava-labs/avalanchego/utils/wrappers"
	"github.com/ava-labs/avalanchego/vms/secp256k1fx"
)

// Maintains an index of an address --> IDs of transactions that changed that address's balance.
// This includes both transactions that increased the address's balance and those that decreased it.
// A transaction is said to change an address's balance if either hold:
// 1) An input UTXO to the transaction was at least partially owned by the address
// 2) An output of the transaction is at least partially owned by the address
type AddressTxsIndexer interface {
	AddUTXOs(outputUTXOs []*avax.UTXO)
	AddUTXOIDs(vm *VM, inputUTXOs []*avax.UTXOID) error
	Write(txID ids.ID) error
}

// indexer implements AddressTxsIndexer
type indexer struct {
	// Address -> AssetID --> Present if the address's balance
	// of the asset has changed since last Commit
	// TODO is this description right?
	addressAssetIDTxMap map[ids.ShortID]map[ids.ID]struct{}
	db                  *versiondb.Database
	log                 logging.Logger
	metrics             metrics
}

// AddTransferOutput indexes given assetID and any number of addresses linked to the transferOutput
// to the provided vm.addressAssetIDIndex
func (i *indexer) addTransferOutput(assetID ids.ID, addrs []ids.ShortID) {
	for _, address := range addrs {
		if _, exists := i.addressAssetIDTxMap[address]; !exists {
			i.addressAssetIDTxMap[address] = make(map[ids.ID]struct{})
		}
		i.addressAssetIDTxMap[address][assetID] = struct{}{}
	}
}

func (i *indexer) AddUTXOIDs(vm *VM, inputUTXOs []*avax.UTXOID) error {
	for _, utxoID := range inputUTXOs {
		utxo, err := vm.getUTXO(utxoID)
		if err != nil {
			return err // should never happen
		}

		out, ok := utxo.Out.(*secp256k1fx.TransferOutput)
		if !ok {
			i.log.Verbo("Skipping input utxo %s for export indexing because it is not of secp256k1fx.TransferOutput", utxo.InputID().String())
			continue
		}

		i.addTransferOutput(utxo.AssetID(), out.Addrs)
	}
	return nil
}

func (i *indexer) AddUTXOs(outputUTXOs []*avax.UTXO) {
	for _, utxo := range outputUTXOs {
		out, ok := utxo.Out.(*secp256k1fx.TransferOutput)
		if !ok {
			i.log.Verbo("Skipping output utxo %s for export indexing because it is not of secp256k1fx.TransferOutput", utxo.InputID().String())
			continue
		}

		i.addTransferOutput(utxo.AssetID(), out.Addrs)
	}
}

// Commit commits given txID and already indexed data to the database.
// The database structure is thus:
// [address]
// |  [assetID]
// |  |
// |  | "idx" => 2 		Running transaction index key, represents the next index
// |  | "0"   => txID1
// |  | "1"   => txID1
func (i *indexer) Write(txID ids.ID) error {
	for address, assetIDs := range i.addressAssetIDTxMap {
		addressPrefixDB := prefixdb.New(address[:], i.db)
		for assetID := range assetIDs {
			assetPrefixDB := prefixdb.New(assetID[:], addressPrefixDB)

			var idx uint64
			idxBytes, err := assetPrefixDB.Get(idxKey)
			switch {
			case err != nil && err != database.ErrNotFound:
				// Unexpected error
				i.log.Fatal("Error checking idx value exists: %s", err)
				return err
			case err == database.ErrNotFound:
				// idx not found; this must be the first entry.
				idx = 0
				idxBytes = make([]byte, wrappers.LongLen)
				binary.BigEndian.PutUint64(idxBytes, idx)
			default:
				// Parse [idxBytes]
				idx = binary.BigEndian.Uint64(idxBytes)
				i.log.Verbo("fetched index %d", idx)
			}

			i.log.Debug("Writing at index %d txID %s", idx, txID)
			if err := assetPrefixDB.Put(idxBytes, txID[:]); err != nil {
				i.log.Fatal("Failed to save transaction to the address, assetID prefix DB %s", err)
				return err
			}

			// increment and store the index for next use
			idx++
			binary.BigEndian.PutUint64(idxBytes, idx)

			if err := assetPrefixDB.Put(idxKey, idxBytes); err != nil {
				i.log.Fatal("Failed to save transaction index to the address, assetID prefix DB: %s", err)
				return err
			}
		}
		delete(i.addressAssetIDTxMap, address)
	}
	i.metrics.numTxsIndexed.Observe(1)
	return nil
}

func (i *indexer) Reset() {
	i.addressAssetIDTxMap = make(map[ids.ShortID]map[ids.ID]struct{})
}

func NewAddressTxsIndexer(db *versiondb.Database, log logging.Logger, metrics metrics) AddressTxsIndexer {
	return &indexer{
		addressAssetIDTxMap: make(map[ids.ShortID]map[ids.ID]struct{}),
		db:                  db,
		log:                 log,
		metrics:             metrics,
	}
}

type noIndexer struct{}

func NewNoIndexer() AddressTxsIndexer {
	return &noIndexer{}
}

func (i *noIndexer) AddUTXOIDs(*VM, []*avax.UTXOID) error {
	return nil
}

func (i *noIndexer) AddTransferOutput(ids.ID, *secp256k1fx.TransferOutput) {}

func (i *noIndexer) AddUTXOs([]*avax.UTXO) {}

func (i *noIndexer) Write(ids.ID) error {
	return nil
}
<commit_msg>comment updates<commit_after>// (c) 2021, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package avm

import (
	"encoding/binary"

	"github.com/ava-labs/avalanchego/database/versiondb"
	"github.com/ava-labs/avalanchego/utils/logging"
	"github.com/ava-labs/avalanchego/vms/components/avax"

	"github.com/ava-labs/avalanchego/database"
	"github.com/ava-labs/avalanchego/database/prefixdb"
	"github.com/ava-labs/avalanchego/ids"
	"github.com/ava-labs/avalanchego/utils/wrappers"
	"github.com/ava-labs/avalanchego/vms/secp256k1fx"
)

// AddressTxsIndexer Maintains an index of an address --> IDs of transactions that changed that address's balance.
// This includes both transactions that increased the address's balance and those that decreased it.
// A transaction is said to change an address's balance if either hold:
// 1) An input UTXO to the transaction was at least partially owned by the address
// 2) An output of the transaction is at least partially owned by the address
type AddressTxsIndexer interface {
	AddUTXOs(outputUTXOs []*avax.UTXO)
	AddUTXOIDs(vm *VM, inputUTXOs []*avax.UTXOID) error
	Write(txID ids.ID) error
}

// indexer implements AddressTxsIndexer
type indexer struct {
	// Address -> AssetID --> Present if the address's balance
	// of the asset has changed since last Write
	// TODO is this description right?
	addressAssetIDTxMap map[ids.ShortID]map[ids.ID]struct{}
	db                  *versiondb.Database
	log                 logging.Logger
	metrics             metrics
}

// AddTransferOutput indexes given assetID and any number of addresses linked to the transferOutput
// to the provided vm.addressAssetIDIndex
func (i *indexer) addTransferOutput(assetID ids.ID, addrs []ids.ShortID) {
	for _, address := range addrs {
		if _, exists := i.addressAssetIDTxMap[address]; !exists {
			i.addressAssetIDTxMap[address] = make(map[ids.ID]struct{})
		}
		i.addressAssetIDTxMap[address][assetID] = struct{}{}
	}
}

func (i *indexer) AddUTXOIDs(vm *VM, inputUTXOs []*avax.UTXOID) error {
	for _, utxoID := range inputUTXOs {
		utxo, err := vm.getUTXO(utxoID)
		if err != nil {
			return err // should never happen
		}

		out, ok := utxo.Out.(*secp256k1fx.TransferOutput)
		if !ok {
			i.log.Verbo("Skipping input utxo %s for export indexing because it is not of secp256k1fx.TransferOutput", utxo.InputID().String())
			continue
		}

		i.addTransferOutput(utxo.AssetID(), out.Addrs)
	}
	return nil
}

func (i *indexer) AddUTXOs(outputUTXOs []*avax.UTXO) {
	for _, utxo := range outputUTXOs {
		out, ok := utxo.Out.(*secp256k1fx.TransferOutput)
		if !ok {
			i.log.Verbo("Skipping output utxo %s for export indexing because it is not of secp256k1fx.TransferOutput", utxo.InputID().String())
			continue
		}

		i.addTransferOutput(utxo.AssetID(), out.Addrs)
	}
}

// Commit commits given txID and already indexed data to the database.
// The database structure is thus:
// [address]
// |  [assetID]
// |  |
// |  | "idx" => 2 		Running transaction index key, represents the next index
// |  | "0"   => txID1
// |  | "1"   => txID1
func (i *indexer) Write(txID ids.ID) error {
	for address, assetIDs := range i.addressAssetIDTxMap {
		addressPrefixDB := prefixdb.New(address[:], i.db)
		for assetID := range assetIDs {
			assetPrefixDB := prefixdb.New(assetID[:], addressPrefixDB)

			var idx uint64
			idxBytes, err := assetPrefixDB.Get(idxKey)
			switch {
			case err != nil && err != database.ErrNotFound:
				// Unexpected error
				i.log.Fatal("Error checking idx value exists: %s", err)
				return err
			case err == database.ErrNotFound:
				// idx not found; this must be the first entry.
				idx = 0
				idxBytes = make([]byte, wrappers.LongLen)
				binary.BigEndian.PutUint64(idxBytes, idx)
			default:
				// Parse [idxBytes]
				idx = binary.BigEndian.Uint64(idxBytes)
				i.log.Verbo("fetched index %d", idx)
			}

			i.log.Debug("Writing at index %d txID %s", idx, txID)
			if err := assetPrefixDB.Put(idxBytes, txID[:]); err != nil {
				i.log.Fatal("Failed to save transaction to the address, assetID prefix DB %s", err)
				return err
			}

			// increment and store the index for next use
			idx++
			binary.BigEndian.PutUint64(idxBytes, idx)

			if err := assetPrefixDB.Put(idxKey, idxBytes); err != nil {
				i.log.Fatal("Failed to save transaction index to the address, assetID prefix DB: %s", err)
				return err
			}
		}
		delete(i.addressAssetIDTxMap, address)
	}
	i.metrics.numTxsIndexed.Observe(1)
	return nil
}

func (i *indexer) Reset() {
	i.addressAssetIDTxMap = make(map[ids.ShortID]map[ids.ID]struct{})
}

func NewAddressTxsIndexer(db *versiondb.Database, log logging.Logger, metrics metrics) AddressTxsIndexer {
	return &indexer{
		addressAssetIDTxMap: make(map[ids.ShortID]map[ids.ID]struct{}),
		db:                  db,
		log:                 log,
		metrics:             metrics,
	}
}

type noIndexer struct{}

func NewNoIndexer() AddressTxsIndexer {
	return &noIndexer{}
}

func (i *noIndexer) AddUTXOIDs(*VM, []*avax.UTXOID) error {
	return nil
}

func (i *noIndexer) AddTransferOutput(ids.ID, *secp256k1fx.TransferOutput) {}

func (i *noIndexer) AddUTXOs([]*avax.UTXO) {}

func (i *noIndexer) Write(ids.ID) error {
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
)

// SCALD_YOUTUBE_API Search request
// https://www.googleapis.com/youtube/v3
// + /search?key=' . $api_key . '&q=' . $q . '&part=snippet&order=rating&type=video,playlist
func searchRequest(c *gin.Context) {
	key := c.Query("key")
	q := url.QueryEscape(c.Query("q"))
	suffix := "&part=snippet&order=rating&type=video,playlist"
	log.Printf("search query = %s", q)
	resp, err := http.Get(fmt.Sprintf("https://www.googleapis.com/youtube/v3/search?key=%s&q=%s%s", key, q, suffix))
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	// fmt.Printf("%s", body)
	if err != nil {
		log.Fatal(err)
	}

	c.Header("Content-Type", "application/json; charset=utf-8")
	var out = string(body[:])
	c.String(http.StatusOK, out)
}

// SCALD_YOUTUBE_API RSS Feed request
// https://www.googleapis.com/youtube/v3
// + /videos?id=' . $id . '&key=' . $api_key . '&part=snippet
func rssFeedRequest(c *gin.Context) {
	id := c.Query("id")
	key := c.Query("key")
	suffix := "&part=snippet"
	log.Printf("video id = %s", id)
	resp, err := http.Get(fmt.Sprintf("https://www.googleapis.com/youtube/v3/videos?id=%s&key=%s%s", id, key, suffix))
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	// fmt.Printf("%s", body)
	if err != nil {
		log.Fatal(err)
	}

	c.Header("Content-Type", "application/json; charset=utf-8")
	var out = string(body[:])
	c.String(http.StatusOK, out)
}

// SCALD_YOUTUBE_WEB request
// https://www.youtube.com/watch
// + /watch?v=' . $id
func watchRequest(c *gin.Context) {
	id := c.Query("v")
	log.Printf("video id = %s", id)
	resp, err := http.Get("https://www.youtube.com/watch?v=" + id)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}

	c.Header("Content-Type", "text/html; charset=utf-8")
	var out = string(body[:])
	c.String(http.StatusOK, out)
}

// SCALD_YOUTUBE_THUMBNAIL request
// https://i.ytimg.com
func thumbnailRequest(c *gin.Context) {
	q := c.Query("q")
	log.Printf("query url = %s", q)
	resp, err := http.Get(q)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}

	// TODO: add in content type checking
	if strings.HasSuffix(q, "jpg") {
		c.Data(http.StatusOK, "image/jpeg", body)
	} else if strings.HasSuffix(q, "png") {
		c.Data(http.StatusOK, "image/png", body)
	} else {
		c.String(http.StatusForbidden, "403 Forbidden: Image requests only.")
	}
}

// Return ping requests with a nice timestamp.
func ping(c *gin.Context) {
	var resp struct {
		Response  string    `json:"response"`
		Timestamp time.Time `json:"timestamp"`
	}
	resp.Response = "pong"
	resp.Timestamp = time.Now().Local()
	c.JSON(http.StatusOK, resp)
}

// Simple front page, using a template for fun.
func home(c *gin.Context) {
	c.HTML(http.StatusOK, "index.tmpl", gin.H{
		"title":   "Hi there!",
		"heading": "Welcome",
		"content": "... to the API.",
	})
}

// Set up required variables
var (
	port = 5000
)

// Start the main function
func main() {

	var port string
	if os.Getenv("PORT") == "" {
		port = "5000"
	} else {
		port = os.Getenv("PORT")
	}

	if port == "" {
		log.Fatal("$PORT must be set")
	}

	// Call in the gin router
	router := gin.Default()

	// Serve the damn favicon.ico
	router.StaticFile("/favicon.ico", "./public/favicon.ico")

	// Simple front page, using a template for fun.
	router.LoadHTMLGlob("templates/*")
	router.GET("/", home)

	// Return ping requests with a nice timestamp.
	router.GET("/ping", ping)

	// ----- ACTUAL REAL THINGS

	// SCALD_YOUTUBE_API Search request
	// https://www.googleapis.com/youtube/v3
	// + /search?key=' . $api_key . '&q=' . $q . '&part=snippet&order=rating&type=video,playlist
	router.GET("/v1/search", searchRequest)

	// SCALD_YOUTUBE_API RSS Feed request
	// https://www.googleapis.com/youtube/v3
	// + /videos?id=' . $id . '&key=' . $api_key . '&part=snippet
	router.GET("/v1/videos", rssFeedRequest)

	// SCALD_YOUTUBE_WEB request
	// https://www.youtube.com/watch
	// + /watch?v=' . $id
	router.GET("/v1/watch", watchRequest)

	// SCALD_YOUTUBE_THUMBNAIL request
	// https://i.ytimg.com
	router.GET("/v1/thumbnail", thumbnailRequest)

	// ----- SOME TEST THINGS
	router.GET("/form-submissions", func(c *gin.Context) {
		formsAPIToken := os.Getenv("FORMS_API_TOKEN")
		if formsAPIToken == "" {
			log.Fatal("$FORMS_API_TOKEN must be set")
		}
		resp, err := http.Get("http://forms.commerce.wa.gov.au/api/forms/results?token=" + formsAPIToken)
		if err != nil {
			log.Fatal(err)
		}
		defer resp.Body.Close()
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			log.Fatal(err)
		}

		c.Header("Content-Type", "application/json; charset=utf-8")
		var out = string(body[:])
		c.String(http.StatusOK, out)
	})

	router.GET("/fuel/:suburb", func(c *gin.Context) {
		suburb := c.Param("suburb")
		resp, err := http.Get("http://nfwws.herokuapp.com/v1/s/" + suburb)
		if err != nil {
			log.Fatal(err)
		}
		defer resp.Body.Close()
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			log.Fatal(err)
		}

		// fmt.Printf("%s", body)

		c.Header("Content-Type", "application/json; charset=utf-8")
		var out = string(body[:])
		c.String(http.StatusOK, out)
	})

	// Run, collaborate and listen.
	router.Run(":" + port)
}
<commit_msg>📚  Added some godoc documentation, doesn't seem to be picked up much though.<commit_after>/*
Package main implements an API for proxying YouTube API requests.
Endpoints mirror their YouTube counterparts as much as possible,
so as little change as possible is required in the client (Drupal) codebase.
*/
package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
)

// searchRequest
//
// Simple search request, used for the SCALD_YOUTUBE_API Search request endpoint.
// https://www.googleapis.com/youtube/v3
// + /search?key=' . $api_key . '&q=' . $q . '&part=snippet&order=rating&type=video,playlist
func searchRequest(c *gin.Context) {
	key := c.Query("key")
	q := url.QueryEscape(c.Query("q"))
	suffix := "&part=snippet&order=rating&type=video,playlist"
	log.Printf("search query = %s", q)
	resp, err := http.Get(fmt.Sprintf("https://www.googleapis.com/youtube/v3/search?key=%s&q=%s%s", key, q, suffix))
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	// fmt.Printf("%s", body)
	if err != nil {
		log.Fatal(err)
	}

	c.Header("Content-Type", "application/json; charset=utf-8")
	var out = string(body[:])
	c.String(http.StatusOK, out)
}

// rssFeedRequest
//
// RSS feed request, used for the SCALD_YOUTUBE_API RSS Feed request endpoint.
// https://www.googleapis.com/youtube/v3
// + /videos?id=' . $id . '&key=' . $api_key . '&part=snippet
func rssFeedRequest(c *gin.Context) {
	id := c.Query("id")
	key := c.Query("key")
	suffix := "&part=snippet"
	log.Printf("video id = %s", id)
	resp, err := http.Get(fmt.Sprintf("https://www.googleapis.com/youtube/v3/videos?id=%s&key=%s%s", id, key, suffix))
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	// fmt.Printf("%s", body)
	if err != nil {
		log.Fatal(err)
	}

	c.Header("Content-Type", "application/json; charset=utf-8")
	var out = string(body[:])
	c.String(http.StatusOK, out)
}

// watchRequest
//
// Used for the SCALD_YOUTUBE_WEB request endpoint.
// https://www.youtube.com/watch
// + /watch?v=' . $id
func watchRequest(c *gin.Context) {
	id := c.Query("v")
	log.Printf("video id = %s", id)
	resp, err := http.Get("https://www.youtube.com/watch?v=" + id)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}

	c.Header("Content-Type", "text/html; charset=utf-8")
	var out = string(body[:])
	c.String(http.StatusOK, out)
}

// thumbnailRequest
//
// Used for the SCALD_YOUTUBE_THUMBNAIL request endpoint.
// https://i.ytimg.com
func thumbnailRequest(c *gin.Context) {
	q := c.Query("q")
	log.Printf("query url = %s", q)
	resp, err := http.Get(q)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}

	// TODO: add in content type checking
	if strings.HasSuffix(q, "jpg") {
		c.Data(http.StatusOK, "image/jpeg", body)
	} else if strings.HasSuffix(q, "png") {
		c.Data(http.StatusOK, "image/png", body)
	} else {
		c.String(http.StatusForbidden, "403 Forbidden: Image requests only.")
	}
}

// ping
//
// Return ping requests with a nice timestamp.
func ping(c *gin.Context) {
	var resp struct {
		Response  string    `json:"response"`
		Timestamp time.Time `json:"timestamp"`
	}
	resp.Response = "pong"
	resp.Timestamp = time.Now().Local()
	c.JSON(http.StatusOK, resp)
}

// home
//
// Simple front page, using a template for fun.
func home(c *gin.Context) {
	c.HTML(http.StatusOK, "index.tmpl", gin.H{
		"title":   "Hi there!",
		"heading": "Welcome",
		"content": "... to the API.",
	})
}

// Set up required variables
var (
	port = 5000
)

// Start the main function
func main() {

	var port string
	if os.Getenv("PORT") == "" {
		port = "5000"
	} else {
		port = os.Getenv("PORT")
	}

	if port == "" {
		log.Fatal("$PORT must be set")
	}

	// Call in the gin router
	router := gin.Default()

	// Serve the damn favicon.ico
	router.StaticFile("/favicon.ico", "./public/favicon.ico")

	// Simple front page, using a template for fun.
	router.LoadHTMLGlob("templates/*")
	router.GET("/", home)

	// Return ping requests with a nice timestamp.
	router.GET("/ping", ping)

	// ----- ACTUAL REAL THINGS

	// SCALD_YOUTUBE_API Search request
	// https://www.googleapis.com/youtube/v3
	// + /search?key=' . $api_key . '&q=' . $q . '&part=snippet&order=rating&type=video,playlist
	router.GET("/v1/search", searchRequest)

	// SCALD_YOUTUBE_API RSS Feed request
	// https://www.googleapis.com/youtube/v3
	// + /videos?id=' . $id . '&key=' . $api_key . '&part=snippet
	router.GET("/v1/videos", rssFeedRequest)

	// SCALD_YOUTUBE_WEB request
	// https://www.youtube.com/watch
	// + /watch?v=' . $id
	router.GET("/v1/watch", watchRequest)

	// SCALD_YOUTUBE_THUMBNAIL request
	// https://i.ytimg.com
	router.GET("/v1/thumbnail", thumbnailRequest)

	// ----- SOME TEST THINGS
	router.GET("/form-submissions", func(c *gin.Context) {
		formsAPIToken := os.Getenv("FORMS_API_TOKEN")
		if formsAPIToken == "" {
			log.Fatal("$FORMS_API_TOKEN must be set")
		}
		resp, err := http.Get("http://forms.commerce.wa.gov.au/api/forms/results?token=" + formsAPIToken)
		if err != nil {
			log.Fatal(err)
		}
		defer resp.Body.Close()
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			log.Fatal(err)
		}

		c.Header("Content-Type", "application/json; charset=utf-8")
		var out = string(body[:])
		c.String(http.StatusOK, out)
	})

	router.GET("/fuel/:suburb", func(c *gin.Context) {
		suburb := c.Param("suburb")
		resp, err := http.Get("http://nfwws.herokuapp.com/v1/s/" + suburb)
		if err != nil {
			log.Fatal(err)
		}
		defer resp.Body.Close()
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			log.Fatal(err)
		}

		// fmt.Printf("%s", body)

		c.Header("Content-Type", "application/json; charset=utf-8")
		var out = string(body[:])
		c.String(http.StatusOK, out)
	})

	// Run, collaborate and listen.
	router.Run(":" + port)
}
<|endoftext|>