<commit_before>package k8s

import (
	"fmt"
	"log"
	"strings"
	"sync"
	"time"

	"k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	pwatch "k8s.io/apimachinery/pkg/watch"

	"k8s.io/client-go/dynamic"
	_ "k8s.io/client-go/plugin/pkg/client/auth"
	"k8s.io/client-go/tools/cache"
)

type empty struct{}

type listWatchAdapter struct {
	resource dynamic.ResourceInterface
}

func (lw listWatchAdapter) List(options v1.ListOptions) (runtime.Object, error) {
	return lw.resource.List(options)
}

func (lw listWatchAdapter) Watch(options v1.ListOptions) (pwatch.Interface, error) {
	return lw.resource.Watch(options)
}

type Watcher struct {
	client       *Client
	watches      map[string]watch
	mutex        sync.Mutex
	started      bool
	stop         chan empty
	stopChans    []chan struct{}
	stoppedChans []chan empty
}

type watch struct {
	namespace string
	resource  dynamic.NamespaceableResourceInterface
	store     cache.Store
	invoke    func()
	runner    func()
}

// NewWatcher returns a Kubernetes Watcher for the specified cluster
func (c *Client) Watcher() *Watcher {
	w := &Watcher{
		client:  c,
		watches: make(map[string]watch),
		stop:    make(chan empty),
	}

	go func() {
		<-w.stop
		for _, c := range w.stopChans {
			close(c)
		}
		for {
			<-w.stop
		}
	}()

	return w
}

// Canonical returns the canonical form of either a resource name or a
// resource type name:
//
//   ResourceName: TYPE/NAME[.NAMESPACE]
//   ResourceType: TYPE
//
// BUG(lukeshu): Canonical's TYPE is just the resource type
// name/kind/shortname; it does NOT include the version or API group.
// This is because of limitations in Client.ResolveResourceType.
func (w *Watcher) Canonical(name string) string {
	parts := strings.Split(name, "/")

	var kind string
	switch len(parts) {
	case 1:
		kind = parts[0]
		name = ""
	case 2:
		kind = parts[0]
		name = parts[1]
	default:
		return ""
	}

	ri := w.client.ResolveResourceType(kind)
	//kind = ri.Name + "." + ri.Version + "." + ri.Group
	kind = ri.Name

	if name == "" {
		return kind
	}

	if ri.Namespaced {
		var namespace string

		parts = strings.Split(name, ".")
		switch len(parts) {
		case 1:
			namespace = "default"
		case 2:
			name = parts[0]
			namespace = parts[1]
		default:
			return ""
		}

		return fmt.Sprintf("%s/%s.%s", kind, name, namespace)
	} else {
		return fmt.Sprintf("%s/%s", kind, name)
	}
}

func (w *Watcher) Watch(resources string, listener func(*Watcher)) error {
	return w.WatchNamespace("", resources, listener)
}

func (w *Watcher) WatchNamespace(namespace, resources string, listener func(*Watcher)) error {
	ri := w.client.ResolveResourceType(resources)
	dyn, err := dynamic.NewForConfig(w.client.config)
	if err != nil {
		log.Fatal(err)
	}

	resource := dyn.Resource(schema.GroupVersionResource{
		Group:    ri.Group,
		Version:  ri.Version,
		Resource: ri.Name,
	})
	var watched dynamic.ResourceInterface
	if namespace != "" {
		watched = resource.Namespace(namespace)
	} else {
		watched = resource
	}

	invoke := func() {
		w.mutex.Lock()
		defer w.mutex.Unlock()
		listener(w)
	}

	store, controller := cache.NewInformer(
		listWatchAdapter{watched},
		nil,
		5*time.Minute,
		cache.ResourceEventHandlerFuncs{
			AddFunc: func(obj interface{}) {
				invoke()
			},
			UpdateFunc: func(oldObj, newObj interface{}) {
				oldUn := oldObj.(*unstructured.Unstructured)
				newUn := newObj.(*unstructured.Unstructured)
				// we ignore updates for objects
				// already in our store because we
				// assume this means we made the
				// change to them
				if oldUn.GetResourceVersion() != newUn.GetResourceVersion() {
					invoke()
				}
			},
			DeleteFunc: func(obj interface{}) {
				invoke()
			},
		},
	)

	stopChan := make(chan struct{})
	stoppedChan := make(chan empty)
	w.stoppedChans = append(w.stoppedChans, stoppedChan)
	w.stopChans = append(w.stopChans, stopChan)

	runner := func() {
		controller.Run(stopChan)
		close(stoppedChan)
	}

	kind := w.Canonical(ri.Kind)
	w.watches[kind] = watch{
		namespace: namespace,
		resource:  resource,
		store:     store,
		invoke:    invoke,
		runner:    runner,
	}

	return nil
}

func (w *Watcher) Start() {
	w.mutex.Lock()
	if w.started {
		w.mutex.Unlock()
		return
	} else {
		w.started = true
		w.mutex.Unlock()
	}
	for kind := range w.watches {
		w.sync(kind)
	}

	for _, watch := range w.watches {
		watch.invoke()
	}

	for _, watch := range w.watches {
		go watch.runner()
	}
}

func (w *Watcher) sync(kind string) {
	watch := w.watches[kind]
	resources, err := w.client.ListNamespace(watch.namespace, kind)
	if err != nil {
		log.Fatal(err)
	}
	for _, rsrc := range resources {
		var uns unstructured.Unstructured
		uns.SetUnstructuredContent(rsrc)
		err = watch.store.Update(&uns)
		if err != nil {
			log.Fatal(err)
		}
	}
}

func (w *Watcher) List(kind string) []Resource {
	kind = w.Canonical(kind)
	watch, ok := w.watches[kind]
	if ok {
		objs := watch.store.List()
		result := make([]Resource, len(objs))
		for idx, obj := range objs {
			result[idx] = obj.(*unstructured.Unstructured).UnstructuredContent()
		}
		return result
	} else {
		return nil
	}
}

func (w *Watcher) UpdateStatus(resource Resource) (Resource, error) {
	kind := w.Canonical(resource.Kind())
	if kind == "" {
		return nil, fmt.Errorf("unknow resource: %v", resource.Kind())
	}
	watch, ok := w.watches[kind]
	if !ok {
		return nil, fmt.Errorf("no watch: %s", kind)
	}

	var uns unstructured.Unstructured
	uns.SetUnstructuredContent(resource)

	// XXX: should we have an if Namespaced here?
	result, err := watch.resource.Namespace(uns.GetNamespace()).UpdateStatus(&uns, v1.UpdateOptions{})
	if err != nil {
		return nil, err
	} else {
		watch.store.Update(result)
		return result.UnstructuredContent(), nil
	}
}

func (w *Watcher) Get(kind, qname string) Resource {
	resources := w.List(kind)
	for _, res := range resources {
		if strings.ToLower(res.QName()) == strings.ToLower(qname) {
			return res
		}
	}
	return Resource{}
}

func (w *Watcher) Exists(kind, qname string) bool {
	return w.Get(kind, qname).Name() != ""
}

func (w *Watcher) Stop() {
	w.stop <- empty{}
}

func (w *Watcher) Wait() {
	w.Start()
	for _, c := range w.stoppedChans {
		<-c
	}
}
<commit_msg>pkg/k8s: Add a comment<commit_after>package k8s

import (
	"fmt"
	"log"
	"strings"
	"sync"
	"time"

	"k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	pwatch "k8s.io/apimachinery/pkg/watch"

	"k8s.io/client-go/dynamic"
	_ "k8s.io/client-go/plugin/pkg/client/auth"
	"k8s.io/client-go/tools/cache"
)

type empty struct{}

type listWatchAdapter struct {
	resource dynamic.ResourceInterface
}

func (lw listWatchAdapter) List(options v1.ListOptions) (runtime.Object, error) {
	// silently coerce the returned *unstructured.UnstructuredList
	// struct to a runtime.Object interface.
	return lw.resource.List(options)
}

func (lw listWatchAdapter) Watch(options v1.ListOptions) (pwatch.Interface, error) {
	return lw.resource.Watch(options)
}

type Watcher struct {
	client       *Client
	watches      map[string]watch
	mutex        sync.Mutex
	started      bool
	stop         chan empty
	stopChans    []chan struct{}
	stoppedChans []chan empty
}

type watch struct {
	namespace string
	resource  dynamic.NamespaceableResourceInterface
	store     cache.Store
	invoke    func()
	runner    func()
}

// NewWatcher returns a Kubernetes Watcher for the specified cluster
func (c *Client) Watcher() *Watcher {
	w := &Watcher{
		client:  c,
		watches: make(map[string]watch),
		stop:    make(chan empty),
	}

	go func() {
		<-w.stop
		for _, c := range w.stopChans {
			close(c)
		}
		for {
			<-w.stop
		}
	}()

	return w
}

// Canonical returns the canonical form of either a resource name or a
// resource type name:
//
//   ResourceName: TYPE/NAME[.NAMESPACE]
//   ResourceType: TYPE
//
// BUG(lukeshu): Canonical's TYPE is just the resource type
// name/kind/shortname; it does NOT include the version or API group.
// This is because of limitations in Client.ResolveResourceType.
func (w *Watcher) Canonical(name string) string {
	parts := strings.Split(name, "/")

	var kind string
	switch len(parts) {
	case 1:
		kind = parts[0]
		name = ""
	case 2:
		kind = parts[0]
		name = parts[1]
	default:
		return ""
	}

	ri := w.client.ResolveResourceType(kind)
	//kind = ri.Name + "." + ri.Version + "." + ri.Group
	kind = ri.Name

	if name == "" {
		return kind
	}

	if ri.Namespaced {
		var namespace string

		parts = strings.Split(name, ".")
		switch len(parts) {
		case 1:
			namespace = "default"
		case 2:
			name = parts[0]
			namespace = parts[1]
		default:
			return ""
		}

		return fmt.Sprintf("%s/%s.%s", kind, name, namespace)
	} else {
		return fmt.Sprintf("%s/%s", kind, name)
	}
}

func (w *Watcher) Watch(resources string, listener func(*Watcher)) error {
	return w.WatchNamespace("", resources, listener)
}

func (w *Watcher) WatchNamespace(namespace, resources string, listener func(*Watcher)) error {
	ri := w.client.ResolveResourceType(resources)
	dyn, err := dynamic.NewForConfig(w.client.config)
	if err != nil {
		log.Fatal(err)
	}

	resource := dyn.Resource(schema.GroupVersionResource{
		Group:    ri.Group,
		Version:  ri.Version,
		Resource: ri.Name,
	})
	var watched dynamic.ResourceInterface
	if namespace != "" {
		watched = resource.Namespace(namespace)
	} else {
		watched = resource
	}

	invoke := func() {
		w.mutex.Lock()
		defer w.mutex.Unlock()
		listener(w)
	}

	store, controller := cache.NewInformer(
		listWatchAdapter{watched},
		nil,
		5*time.Minute,
		cache.ResourceEventHandlerFuncs{
			AddFunc: func(obj interface{}) {
				invoke()
			},
			UpdateFunc: func(oldObj, newObj interface{}) {
				oldUn := oldObj.(*unstructured.Unstructured)
				newUn := newObj.(*unstructured.Unstructured)
				// we ignore updates for objects
				// already in our store because we
				// assume this means we made the
				// change to them
				if oldUn.GetResourceVersion() != newUn.GetResourceVersion() {
					invoke()
				}
			},
			DeleteFunc: func(obj interface{}) {
				invoke()
			},
		},
	)

	stopChan := make(chan struct{})
	stoppedChan := make(chan empty)
	w.stoppedChans = append(w.stoppedChans, stoppedChan)
	w.stopChans = append(w.stopChans, stopChan)

	runner := func() {
		controller.Run(stopChan)
		close(stoppedChan)
	}

	kind := w.Canonical(ri.Kind)
	w.watches[kind] = watch{
		namespace: namespace,
		resource:  resource,
		store:     store,
		invoke:    invoke,
		runner:    runner,
	}

	return nil
}

func (w *Watcher) Start() {
	w.mutex.Lock()
	if w.started {
		w.mutex.Unlock()
		return
	} else {
		w.started = true
		w.mutex.Unlock()
	}
	for kind := range w.watches {
		w.sync(kind)
	}

	for _, watch := range w.watches {
		watch.invoke()
	}

	for _, watch := range w.watches {
		go watch.runner()
	}
}

func (w *Watcher) sync(kind string) {
	watch := w.watches[kind]
	resources, err := w.client.ListNamespace(watch.namespace, kind)
	if err != nil {
		log.Fatal(err)
	}
	for _, rsrc := range resources {
		var uns unstructured.Unstructured
		uns.SetUnstructuredContent(rsrc)
		err = watch.store.Update(&uns)
		if err != nil {
			log.Fatal(err)
		}
	}
}

func (w *Watcher) List(kind string) []Resource {
	kind = w.Canonical(kind)
	watch, ok := w.watches[kind]
	if ok {
		objs := watch.store.List()
		result := make([]Resource, len(objs))
		for idx, obj := range objs {
			result[idx] = obj.(*unstructured.Unstructured).UnstructuredContent()
		}
		return result
	} else {
		return nil
	}
}

func (w *Watcher) UpdateStatus(resource Resource) (Resource, error) {
	kind := w.Canonical(resource.Kind())
	if kind == "" {
		return nil, fmt.Errorf("unknow resource: %v", resource.Kind())
	}
	watch, ok := w.watches[kind]
	if !ok {
		return nil, fmt.Errorf("no watch: %s", kind)
	}

	var uns unstructured.Unstructured
	uns.SetUnstructuredContent(resource)

	// XXX: should we have an if Namespaced here?
	result, err := watch.resource.Namespace(uns.GetNamespace()).UpdateStatus(&uns, v1.UpdateOptions{})
	if err != nil {
		return nil, err
	} else {
		watch.store.Update(result)
		return result.UnstructuredContent(), nil
	}
}

func (w *Watcher) Get(kind, qname string) Resource {
	resources := w.List(kind)
	for _, res := range resources {
		if strings.ToLower(res.QName()) == strings.ToLower(qname) {
			return res
		}
	}
	return Resource{}
}

func (w *Watcher) Exists(kind, qname string) bool {
	return w.Get(kind, qname).Name() != ""
}

func (w *Watcher) Stop() {
	w.stop <- empty{}
}

func (w *Watcher) Wait() {
	w.Start()
	for _, c := range w.stoppedChans {
		<-c
	}
}
<|endoftext|>


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

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

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

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

package kubectl

import (
	"fmt"
	"strconv"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
)

type BasicReplicationController struct{}

func (BasicReplicationController) ParamNames() []GeneratorParam {
	return []GeneratorParam{
		{"labels", false},
		{"default-name", true},
		{"name", false},
		{"replicas", true},
		{"image", true},
		{"port", false},
		{"hostport", false},
	}
}

func (BasicReplicationController) Generate(params map[string]string) (runtime.Object, error) {
	name, found := params["name"]
	if !found || len(name) == 0 {
		name, found = params["default-name"]
		if !found || len(name) == 0 {
			return nil, fmt.Errorf("'name' is a required parameter.")
		}
	}
	// TODO: extract this flag to a central location.
	labelString, found := params["labels"]
	var labels map[string]string
	var err error
	if found && len(labelString) > 0 {
		labels, err = ParseLabels(labelString)
		if err != nil {
			return nil, err
		}
	} else {
		labels = map[string]string{
			"run": name,
		}
	}
	count, err := strconv.Atoi(params["replicas"])
	if err != nil {
		return nil, err
	}
	controller := api.ReplicationController{
		ObjectMeta: api.ObjectMeta{
			Name:   name,
			Labels: labels,
		},
		Spec: api.ReplicationControllerSpec{
			Replicas: count,
			Selector: labels,
			Template: &api.PodTemplateSpec{
				ObjectMeta: api.ObjectMeta{
					Labels: labels,
				},
				Spec: api.PodSpec{
					Containers: []api.Container{
						{
							Name:  name,
							Image: params["image"],
						},
					},
				},
			},
		},
	}

	port := -1
	hostPort := -1
	if len(params["port"]) > 0 {
		port, err = strconv.Atoi(params["port"])
		if err != nil {
			return nil, err
		}
	}

	if len(params["hostport"]) > 0 {
		hostPort, err = strconv.Atoi(params["hostport"])
		if err != nil {
			return nil, err
		}
		if hostPort > 0 && port < 0 {
			return nil, fmt.Errorf("--hostport requires --port to be specified")
		}
	}

	// Don't include the port if it was not specified.
	if port > 0 {
		controller.Spec.Template.Spec.Containers[0].Ports = []api.ContainerPort{
			{
				ContainerPort: port,
			},
		}
		if hostPort > 0 {
			controller.Spec.Template.Spec.Containers[0].Ports[0].HostPort = hostPort
		}
	}
	return &controller, nil
}
<commit_msg>Name is a required parameter for kubectl run, default-name is not<commit_after>/*
Copyright 2014 The Kubernetes Authors All rights reserved.

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

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

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

package kubectl

import (
	"fmt"
	"strconv"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
)

type BasicReplicationController struct{}

func (BasicReplicationController) ParamNames() []GeneratorParam {
	return []GeneratorParam{
		{"labels", false},
		{"default-name", false},
		{"name", true},
		{"replicas", true},
		{"image", true},
		{"port", false},
		{"hostport", false},
	}
}

func (BasicReplicationController) Generate(params map[string]string) (runtime.Object, error) {
	name, found := params["name"]
	if !found || len(name) == 0 {
		name, found = params["default-name"]
		if !found || len(name) == 0 {
			return nil, fmt.Errorf("'name' is a required parameter.")
		}
	}
	// TODO: extract this flag to a central location.
	labelString, found := params["labels"]
	var labels map[string]string
	var err error
	if found && len(labelString) > 0 {
		labels, err = ParseLabels(labelString)
		if err != nil {
			return nil, err
		}
	} else {
		labels = map[string]string{
			"run": name,
		}
	}
	count, err := strconv.Atoi(params["replicas"])
	if err != nil {
		return nil, err
	}
	controller := api.ReplicationController{
		ObjectMeta: api.ObjectMeta{
			Name:   name,
			Labels: labels,
		},
		Spec: api.ReplicationControllerSpec{
			Replicas: count,
			Selector: labels,
			Template: &api.PodTemplateSpec{
				ObjectMeta: api.ObjectMeta{
					Labels: labels,
				},
				Spec: api.PodSpec{
					Containers: []api.Container{
						{
							Name:  name,
							Image: params["image"],
						},
					},
				},
			},
		},
	}

	port := -1
	hostPort := -1
	if len(params["port"]) > 0 {
		port, err = strconv.Atoi(params["port"])
		if err != nil {
			return nil, err
		}
	}

	if len(params["hostport"]) > 0 {
		hostPort, err = strconv.Atoi(params["hostport"])
		if err != nil {
			return nil, err
		}
		if hostPort > 0 && port < 0 {
			return nil, fmt.Errorf("--hostport requires --port to be specified")
		}
	}

	// Don't include the port if it was not specified.
	if port > 0 {
		controller.Spec.Template.Spec.Containers[0].Ports = []api.ContainerPort{
			{
				ContainerPort: port,
			},
		}
		if hostPort > 0 {
			controller.Spec.Template.Spec.Containers[0].Ports[0].HostPort = hostPort
		}
	}
	return &controller, nil
}
<|endoftext|>


<commit_before>package slack

import (
	"fmt"
	"log"
	"strings"

	slack_webhook "github.com/ashwanthkumar/slack-go-webhook"
	"github.com/checkr/codeflow/server/agent"
	"github.com/checkr/codeflow/server/plugins"
	"github.com/spf13/viper"
)

type Slack struct {
}

func init() {
	agent.RegisterPlugin("slack", func() agent.Plugin {
		return &Slack{}
	})
}

func (x *Slack) Description() string {
	return "Send slack events to subscribed plugins"
}

func (x *Slack) SampleConfig() string {
	return ` `
}

func (x *Slack) slack() error {
	return nil
}

func (x *Slack) Start(e chan agent.Event) error {
	go x.slack()
	log.Println("Started Slack")

	return nil
}

func (x *Slack) Stop() {
	log.Println("Stopping Slack")
}

func (x *Slack) Subscribe() []string {
	return []string{
		"plugins.DockerBuild",
		"plugins.DockerDeploy",
		"plugins.Project",
		"plugins.Release",
	}
}

func (x *Slack) Process(e agent.Event) error {
	webhookUrl := viper.GetString("plugins.slack.webhook_url")

	switch e.Name {
	case "plugins.DockerDeploy:create":
		payload := e.Payload.(plugins.DockerDeploy)

		for _, channel := range payload.Project.NotifyChannels {
			project := payload.Project.Slug
			message := strings.Replace(
				payload.Release.HeadFeature.Message,
				"\n", " ", -1,
			)
			author  := payload.Release.HeadFeature.User

			repository := payload.Project.Repository
			tail := payload.Release.TailFeature.Hash
			head := payload.Release.HeadFeature.Hash

			msg := fmt.Sprintf(
				"%s: <https://github.com/%s|%s> is deploying %s. <https://github.com/%s/compare/%s...%s|diff>",
				project, author, author,
				message, repository, tail, head,
			)

			slackPayload := slack_webhook.Payload{
				Text:        msg,
				Username:    "codeflow-bot",
				Channel:     channel,
				IconEmoji:   ":rocket:",
			}
			err := slack_webhook.Send(webhookUrl, "", slackPayload)
			if len(err) > 0 {
				log.Println("error: %s\n", err)
			}
		}
	case "plugins.DockerDeploy:status":
		payload := e.Payload.(plugins.DockerDeploy)

		if payload.State != plugins.Complete && payload.State != plugins.Failed {
			return nil
		}

		for _, channel := range payload.Project.NotifyChannels {
			project := payload.Project.Slug
			release := payload.Release.HeadFeature.Hash

			var msg, color string

			if payload.State == plugins.Failed {
				color = "#FF0000"
				msg = fmt.Sprintf("Deploying %s:%s failed", project, release)
			} else {
				msg = fmt.Sprintf("%s:%s went live", project, release)
				color = "#008000"
			}

			attachment1 := slack_webhook.Attachment{Color: &color, Text: &msg}

			slackPayload := slack_webhook.Payload{
				Username:    "codeflow-bot",
				Channel:     channel,
				IconEmoji:   ":rocket:",
				Attachments: []slack_webhook.Attachment{attachment1},
			}
			err := slack_webhook.Send(webhookUrl, "", slackPayload)
			if len(err) > 0 {
				log.Println("error: %s\n", err)
			}
		}
	}

	return nil
}
<commit_msg>Make it short hash<commit_after>package slack

import (
	"fmt"
	"log"
	"strings"

	slack_webhook "github.com/ashwanthkumar/slack-go-webhook"
	"github.com/checkr/codeflow/server/agent"
	"github.com/checkr/codeflow/server/plugins"
	"github.com/spf13/viper"
)

type Slack struct {
}

func init() {
	agent.RegisterPlugin("slack", func() agent.Plugin {
		return &Slack{}
	})
}

func (x *Slack) Description() string {
	return "Send slack events to subscribed plugins"
}

func (x *Slack) SampleConfig() string {
	return ` `
}

func (x *Slack) slack() error {
	return nil
}

func (x *Slack) Start(e chan agent.Event) error {
	go x.slack()
	log.Println("Started Slack")

	return nil
}

func (x *Slack) Stop() {
	log.Println("Stopping Slack")
}

func (x *Slack) Subscribe() []string {
	return []string{
		"plugins.DockerBuild",
		"plugins.DockerDeploy",
		"plugins.Project",
		"plugins.Release",
	}
}

func (x *Slack) Process(e agent.Event) error {
	webhookUrl := viper.GetString("plugins.slack.webhook_url")

	switch e.Name {
	case "plugins.DockerDeploy:create":
		payload := e.Payload.(plugins.DockerDeploy)

		for _, channel := range payload.Project.NotifyChannels {
			project := payload.Project.Slug
			message := strings.Replace(
				payload.Release.HeadFeature.Message,
				"\n", " ", -1,
			)
			author  := payload.Release.HeadFeature.User

			repository := payload.Project.Repository
			tail := payload.Release.TailFeature.Hash
			head := payload.Release.HeadFeature.Hash

			msg := fmt.Sprintf(
				"%s: <https://github.com/%s|%s> is deploying %s. <https://github.com/%s/compare/%s...%s|diff>",
				project, author, author,
				message, repository, tail, head,
			)

			slackPayload := slack_webhook.Payload{
				Text:        msg,
				Username:    "codeflow-bot",
				Channel:     channel,
				IconEmoji:   ":rocket:",
			}
			err := slack_webhook.Send(webhookUrl, "", slackPayload)
			if len(err) > 0 {
				log.Println("error: %s\n", err)
			}
		}
	case "plugins.DockerDeploy:status":
		payload := e.Payload.(plugins.DockerDeploy)

		if payload.State != plugins.Complete && payload.State != plugins.Failed {
			return nil
		}

		for _, channel := range payload.Project.NotifyChannels {
			project := payload.Project.Slug
			release := payload.Release.HeadFeature.Hash[0:6]

			var msg, color string

			if payload.State == plugins.Failed {
				color = "#FF0000"
				msg = fmt.Sprintf("Deploying %s:%s failed", project, release)
			} else {
				msg = fmt.Sprintf("%s:%s went live", project, release)
				color = "#008000"
			}

			attachment1 := slack_webhook.Attachment{Color: &color, Text: &msg}

			slackPayload := slack_webhook.Payload{
				Username:    "codeflow-bot",
				Channel:     channel,
				IconEmoji:   ":rocket:",
				Attachments: []slack_webhook.Attachment{attachment1},
			}
			err := slack_webhook.Send(webhookUrl, "", slackPayload)
			if len(err) > 0 {
				log.Println("error: %s\n", err)
			}
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package azurerm

import (
	"fmt"
	"net/http"
	"regexp"
	"strings"

	"github.com/Azure/azure-sdk-for-go/arm/storage"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceArmStorageAccount() *schema.Resource {
	return &schema.Resource{
		Create: resourceArmStorageAccountCreate,
		Read:   resourceArmStorageAccountRead,
		Update: resourceArmStorageAccountUpdate,
		Delete: resourceArmStorageAccountDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"name": {
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validateArmStorageAccountName,
			},

			"resource_group_name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"location": {
				Type:      schema.TypeString,
				Required:  true,
				ForceNew:  true,
				StateFunc: azureRMNormalizeLocation,
			},

			"account_type": {
				Type:         schema.TypeString,
				Required:     true,
				ValidateFunc: validateArmStorageAccountType,
			},

			"primary_location": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"secondary_location": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"primary_blob_endpoint": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"secondary_blob_endpoint": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"primary_queue_endpoint": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"secondary_queue_endpoint": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"primary_table_endpoint": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"secondary_table_endpoint": {
				Type:     schema.TypeString,
				Computed: true,
			},

			// NOTE: The API does not appear to expose a secondary file endpoint
			"primary_file_endpoint": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"primary_access_key": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"secondary_access_key": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"tags": tagsSchema(),
		},
	}
}

func resourceArmStorageAccountCreate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*ArmClient).storageServiceClient

	resourceGroupName := d.Get("resource_group_name").(string)
	storageAccountName := d.Get("name").(string)
	accountType := d.Get("account_type").(string)
	location := d.Get("location").(string)
	tags := d.Get("tags").(map[string]interface{})

	sku := storage.Sku{
		Name: storage.SkuName(accountType),
	}

	opts := storage.AccountCreateParameters{
		Location: &location,
		Sku:      &sku,
		Tags:     expandTags(tags),
	}

	_, err := client.Create(resourceGroupName, storageAccountName, opts, make(chan struct{}))
	if err != nil {
		return fmt.Errorf("Error creating Azure Storage Account '%s': %s", storageAccountName, err)
	}

	// The only way to get the ID back apparently is to read the resource again
	read, err := client.GetProperties(resourceGroupName, storageAccountName)
	if err != nil {
		return err
	}
	if read.ID == nil {
		return fmt.Errorf("Cannot read Storage Account %s (resource group %s) ID",
			storageAccountName, resourceGroupName)
	}

	d.SetId(*read.ID)

	return resourceArmStorageAccountRead(d, meta)
}

// resourceArmStorageAccountUpdate is unusual in the ARM API where most resources have a combined
// and idempotent operation for CreateOrUpdate. In particular updating all of the parameters
// available requires a call to Update per parameter...
func resourceArmStorageAccountUpdate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*ArmClient).storageServiceClient
	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}
	storageAccountName := id.Path["storageAccounts"]
	resourceGroupName := id.ResourceGroup

	d.Partial(true)

	if d.HasChange("account_type") {
		accountType := d.Get("account_type").(string)

		sku := storage.Sku{
			Name: storage.SkuName(accountType),
		}

		opts := storage.AccountUpdateParameters{
			Sku: &sku,
		}
		_, err := client.Update(resourceGroupName, storageAccountName, opts)
		if err != nil {
			return fmt.Errorf("Error updating Azure Storage Account type %q: %s", storageAccountName, err)
		}

		d.SetPartial("account_type")
	}

	if d.HasChange("tags") {
		tags := d.Get("tags").(map[string]interface{})

		opts := storage.AccountUpdateParameters{
			Tags: expandTags(tags),
		}
		_, err := client.Update(resourceGroupName, storageAccountName, opts)
		if err != nil {
			return fmt.Errorf("Error updating Azure Storage Account tags %q: %s", storageAccountName, err)
		}

		d.SetPartial("tags")
	}

	d.Partial(false)
	return nil
}

func resourceArmStorageAccountRead(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*ArmClient).storageServiceClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}
	name := id.Path["storageAccounts"]
	resGroup := id.ResourceGroup

	resp, err := client.GetProperties(resGroup, name)
	if err != nil {
		if resp.StatusCode == http.StatusNotFound {
			d.SetId("")
			return nil
		}

		return fmt.Errorf("Error reading the state of AzureRM Storage Account %q: %s", name, err)
	}

	keys, err := client.ListKeys(resGroup, name)
	if err != nil {
		return err
	}

	accessKeys := *keys.Keys
	d.Set("primary_access_key", accessKeys[0].KeyName)
	d.Set("secondary_access_key", accessKeys[1].KeyName)
	d.Set("location", resp.Location)
	d.Set("account_type", resp.Sku.Name)
	d.Set("primary_location", resp.Properties.PrimaryLocation)
	d.Set("secondary_location", resp.Properties.SecondaryLocation)

	if resp.Properties.PrimaryEndpoints != nil {
		d.Set("primary_blob_endpoint", resp.Properties.PrimaryEndpoints.Blob)
		d.Set("primary_queue_endpoint", resp.Properties.PrimaryEndpoints.Queue)
		d.Set("primary_table_endpoint", resp.Properties.PrimaryEndpoints.Table)
		d.Set("primary_file_endpoint", resp.Properties.PrimaryEndpoints.File)
	}

	if resp.Properties.SecondaryEndpoints != nil {
		if resp.Properties.SecondaryEndpoints.Blob != nil {
			d.Set("secondary_blob_endpoint", resp.Properties.SecondaryEndpoints.Blob)
		} else {
			d.Set("secondary_blob_endpoint", "")
		}
		if resp.Properties.SecondaryEndpoints.Queue != nil {
			d.Set("secondary_queue_endpoint", resp.Properties.SecondaryEndpoints.Queue)
		} else {
			d.Set("secondary_queue_endpoint", "")
		}
		if resp.Properties.SecondaryEndpoints.Table != nil {
			d.Set("secondary_table_endpoint", resp.Properties.SecondaryEndpoints.Table)
		} else {
			d.Set("secondary_table_endpoint", "")
		}
	}

	d.Set("name", resp.Name)

	flattenAndSetTags(d, resp.Tags)

	return nil
}

func resourceArmStorageAccountDelete(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*ArmClient).storageServiceClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}
	name := id.Path["storageAccounts"]
	resGroup := id.ResourceGroup

	_, err = client.Delete(resGroup, name)
	if err != nil {
		return fmt.Errorf("Error issuing AzureRM delete request for storage account %q: %s", name, err)
	}

	return nil
}

func validateArmStorageAccountName(v interface{}, k string) (ws []string, es []error) {
	input := v.(string)

	if !regexp.MustCompile(`\A([a-z0-9]{3,24})\z`).MatchString(input) {
		es = append(es, fmt.Errorf("name can only consist of lowercase letters and numbers, and must be between 3 and 24 characters long"))
	}

	return
}

func validateArmStorageAccountType(v interface{}, k string) (ws []string, es []error) {
	validAccountTypes := []string{"standard_lrs", "standard_zrs",
		"standard_grs", "standard_ragrs", "premium_lrs"}

	input := strings.ToLower(v.(string))

	for _, valid := range validAccountTypes {
		if valid == input {
			return
		}
	}

	es = append(es, fmt.Errorf("Invalid storage account type %q", input))
	return
}
<commit_msg>azurerm_storage_account now returns storage keys value instead of their names (#7674)<commit_after>package azurerm

import (
	"fmt"
	"net/http"
	"regexp"
	"strings"

	"github.com/Azure/azure-sdk-for-go/arm/storage"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceArmStorageAccount() *schema.Resource {
	return &schema.Resource{
		Create: resourceArmStorageAccountCreate,
		Read:   resourceArmStorageAccountRead,
		Update: resourceArmStorageAccountUpdate,
		Delete: resourceArmStorageAccountDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"name": {
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validateArmStorageAccountName,
			},

			"resource_group_name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"location": {
				Type:      schema.TypeString,
				Required:  true,
				ForceNew:  true,
				StateFunc: azureRMNormalizeLocation,
			},

			"account_type": {
				Type:         schema.TypeString,
				Required:     true,
				ValidateFunc: validateArmStorageAccountType,
			},

			"primary_location": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"secondary_location": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"primary_blob_endpoint": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"secondary_blob_endpoint": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"primary_queue_endpoint": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"secondary_queue_endpoint": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"primary_table_endpoint": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"secondary_table_endpoint": {
				Type:     schema.TypeString,
				Computed: true,
			},

			// NOTE: The API does not appear to expose a secondary file endpoint
			"primary_file_endpoint": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"primary_access_key": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"secondary_access_key": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"tags": tagsSchema(),
		},
	}
}

func resourceArmStorageAccountCreate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*ArmClient).storageServiceClient

	resourceGroupName := d.Get("resource_group_name").(string)
	storageAccountName := d.Get("name").(string)
	accountType := d.Get("account_type").(string)
	location := d.Get("location").(string)
	tags := d.Get("tags").(map[string]interface{})

	sku := storage.Sku{
		Name: storage.SkuName(accountType),
	}

	opts := storage.AccountCreateParameters{
		Location: &location,
		Sku:      &sku,
		Tags:     expandTags(tags),
	}

	_, err := client.Create(resourceGroupName, storageAccountName, opts, make(chan struct{}))
	if err != nil {
		return fmt.Errorf("Error creating Azure Storage Account '%s': %s", storageAccountName, err)
	}

	// The only way to get the ID back apparently is to read the resource again
	read, err := client.GetProperties(resourceGroupName, storageAccountName)
	if err != nil {
		return err
	}
	if read.ID == nil {
		return fmt.Errorf("Cannot read Storage Account %s (resource group %s) ID",
			storageAccountName, resourceGroupName)
	}

	d.SetId(*read.ID)

	return resourceArmStorageAccountRead(d, meta)
}

// resourceArmStorageAccountUpdate is unusual in the ARM API where most resources have a combined
// and idempotent operation for CreateOrUpdate. In particular updating all of the parameters
// available requires a call to Update per parameter...
func resourceArmStorageAccountUpdate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*ArmClient).storageServiceClient
	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}
	storageAccountName := id.Path["storageAccounts"]
	resourceGroupName := id.ResourceGroup

	d.Partial(true)

	if d.HasChange("account_type") {
		accountType := d.Get("account_type").(string)

		sku := storage.Sku{
			Name: storage.SkuName(accountType),
		}

		opts := storage.AccountUpdateParameters{
			Sku: &sku,
		}
		_, err := client.Update(resourceGroupName, storageAccountName, opts)
		if err != nil {
			return fmt.Errorf("Error updating Azure Storage Account type %q: %s", storageAccountName, err)
		}

		d.SetPartial("account_type")
	}

	if d.HasChange("tags") {
		tags := d.Get("tags").(map[string]interface{})

		opts := storage.AccountUpdateParameters{
			Tags: expandTags(tags),
		}
		_, err := client.Update(resourceGroupName, storageAccountName, opts)
		if err != nil {
			return fmt.Errorf("Error updating Azure Storage Account tags %q: %s", storageAccountName, err)
		}

		d.SetPartial("tags")
	}

	d.Partial(false)
	return nil
}

func resourceArmStorageAccountRead(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*ArmClient).storageServiceClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}
	name := id.Path["storageAccounts"]
	resGroup := id.ResourceGroup

	resp, err := client.GetProperties(resGroup, name)
	if err != nil {
		if resp.StatusCode == http.StatusNotFound {
			d.SetId("")
			return nil
		}

		return fmt.Errorf("Error reading the state of AzureRM Storage Account %q: %s", name, err)
	}

	keys, err := client.ListKeys(resGroup, name)
	if err != nil {
		return err
	}

	accessKeys := *keys.Keys
	d.Set("primary_access_key", accessKeys[0].Value)
	d.Set("secondary_access_key", accessKeys[1].Value)
	d.Set("location", resp.Location)
	d.Set("account_type", resp.Sku.Name)
	d.Set("primary_location", resp.Properties.PrimaryLocation)
	d.Set("secondary_location", resp.Properties.SecondaryLocation)

	if resp.Properties.PrimaryEndpoints != nil {
		d.Set("primary_blob_endpoint", resp.Properties.PrimaryEndpoints.Blob)
		d.Set("primary_queue_endpoint", resp.Properties.PrimaryEndpoints.Queue)
		d.Set("primary_table_endpoint", resp.Properties.PrimaryEndpoints.Table)
		d.Set("primary_file_endpoint", resp.Properties.PrimaryEndpoints.File)
	}

	if resp.Properties.SecondaryEndpoints != nil {
		if resp.Properties.SecondaryEndpoints.Blob != nil {
			d.Set("secondary_blob_endpoint", resp.Properties.SecondaryEndpoints.Blob)
		} else {
			d.Set("secondary_blob_endpoint", "")
		}
		if resp.Properties.SecondaryEndpoints.Queue != nil {
			d.Set("secondary_queue_endpoint", resp.Properties.SecondaryEndpoints.Queue)
		} else {
			d.Set("secondary_queue_endpoint", "")
		}
		if resp.Properties.SecondaryEndpoints.Table != nil {
			d.Set("secondary_table_endpoint", resp.Properties.SecondaryEndpoints.Table)
		} else {
			d.Set("secondary_table_endpoint", "")
		}
	}

	d.Set("name", resp.Name)

	flattenAndSetTags(d, resp.Tags)

	return nil
}

func resourceArmStorageAccountDelete(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*ArmClient).storageServiceClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}
	name := id.Path["storageAccounts"]
	resGroup := id.ResourceGroup

	_, err = client.Delete(resGroup, name)
	if err != nil {
		return fmt.Errorf("Error issuing AzureRM delete request for storage account %q: %s", name, err)
	}

	return nil
}

func validateArmStorageAccountName(v interface{}, k string) (ws []string, es []error) {
	input := v.(string)

	if !regexp.MustCompile(`\A([a-z0-9]{3,24})\z`).MatchString(input) {
		es = append(es, fmt.Errorf("name can only consist of lowercase letters and numbers, and must be between 3 and 24 characters long"))
	}

	return
}

func validateArmStorageAccountType(v interface{}, k string) (ws []string, es []error) {
	validAccountTypes := []string{"standard_lrs", "standard_zrs",
		"standard_grs", "standard_ragrs", "premium_lrs"}

	input := strings.ToLower(v.(string))

	for _, valid := range validAccountTypes {
		if valid == input {
			return
		}
	}

	es = append(es, fmt.Errorf("Invalid storage account type %q", input))
	return
}
<|endoftext|>


<commit_before>package setup

import (
	"github.com/dcos/dcos-cli/pkg/login"
	"github.com/sirupsen/logrus"
	"github.com/spf13/afero"
	"github.com/spf13/pflag"
)

// Flags are command-line flags for a cluster setup.
type Flags struct {
	caBundle     []byte
	caBundlePath string
	name         string
	noCheck      bool
	noPlugin     bool
	insecure     bool
	loginFlags   *login.Flags
	fs           afero.Fs
}

// NewFlags creates flags for a cluster setup.
func NewFlags(fs afero.Fs, envLookup func(key string) (string, bool), logger *logrus.Logger) *Flags {
	return &Flags{
		fs:         fs,
		loginFlags: login.NewFlags(fs, envLookup, logger),
	}
}

// Register registers cluster setup flags.
func (f *Flags) Register(flags *pflag.FlagSet) {
	flags.BoolVar(
		&f.insecure,
		"insecure",
		false,
		"Allow requests to bypass TLS certificate verification (insecure).",
	)
	flags.BoolVar(
		&f.noCheck,
		"no-check",
		false,
		"Do not check CA certficate downloaded from cluster (insecure). Applies to Enterprise DC/OS only.",
	)
	flags.BoolVar(
		&f.noPlugin,
		"no-plugin",
		false,
		"Do not auto-install dcos-core-cli and dcos-enterprise-cli plugins.",
	)
	flags.StringVar(
		&f.caBundlePath,
		"ca-certs",
		"",
		"Specify the path to a file with trusted CAs to verify requests against.",
	)
	flags.StringVar(
		&f.name,
		"name",
		"",
		"Specify a custom name for the cluster.",
	)
	f.loginFlags.Register(flags)
}

// Resolve resolves setup and login flags.
func (f *Flags) Resolve() error {
	if f.caBundlePath != "" {
		caBundle, err := afero.ReadFile(f.fs, f.caBundlePath)
		if err != nil {
			return err
		}
		f.caBundle = caBundle
	}
	return f.loginFlags.Resolve()
}

// LoginFlags returns the login flags.
func (f *Flags) LoginFlags() *login.Flags {
	return f.loginFlags
}
<commit_msg>Don't prompt with fingerprint when using --ca-certs<commit_after>package setup

import (
	"github.com/dcos/dcos-cli/pkg/login"
	"github.com/sirupsen/logrus"
	"github.com/spf13/afero"
	"github.com/spf13/pflag"
)

// Flags are command-line flags for a cluster setup.
type Flags struct {
	caBundle     []byte
	caBundlePath string
	name         string
	noCheck      bool
	noPlugin     bool
	insecure     bool
	loginFlags   *login.Flags
	fs           afero.Fs
}

// NewFlags creates flags for a cluster setup.
func NewFlags(fs afero.Fs, envLookup func(key string) (string, bool), logger *logrus.Logger) *Flags {
	return &Flags{
		fs:         fs,
		loginFlags: login.NewFlags(fs, envLookup, logger),
	}
}

// Register registers cluster setup flags.
func (f *Flags) Register(flags *pflag.FlagSet) {
	flags.BoolVar(
		&f.insecure,
		"insecure",
		false,
		"Allow requests to bypass TLS certificate verification (insecure).",
	)
	flags.BoolVar(
		&f.noCheck,
		"no-check",
		false,
		"Do not check CA certficate downloaded from cluster (insecure). Applies to Enterprise DC/OS only.",
	)
	flags.BoolVar(
		&f.noPlugin,
		"no-plugin",
		false,
		"Do not auto-install dcos-core-cli and dcos-enterprise-cli plugins.",
	)
	flags.StringVar(
		&f.caBundlePath,
		"ca-certs",
		"",
		"Specify the path to a file with trusted CAs to verify requests against.",
	)
	flags.StringVar(
		&f.name,
		"name",
		"",
		"Specify a custom name for the cluster.",
	)
	f.loginFlags.Register(flags)
}

// Resolve resolves setup and login flags.
func (f *Flags) Resolve() error {
	if f.caBundlePath != "" {
		caBundle, err := afero.ReadFile(f.fs, f.caBundlePath)
		if err != nil {
			return err
		}
		f.caBundle = caBundle

		// Don't prompt for fingerprint confirmation when a CA is explicitly passed.
		f.noCheck = true
	}
	return f.loginFlags.Resolve()
}

// LoginFlags returns the login flags.
func (f *Flags) LoginFlags() *login.Flags {
	return f.loginFlags
}
<|endoftext|>


<commit_before>package trdsql

import (
	"reflect"
	"testing"
)

func TestNewSliceReader(t *testing.T) {
	type args struct {
		tableName string
		args      interface{}
	}
	tests := []struct {
		name string
		args args
		want *SliceReader
	}{
		{
			name: "string",
			args: args{
				tableName: "one",
				args:      "one",
			},
			want: &SliceReader{
				tableName: "one",
				names:     []string{"c1"},
				types:     []string{"text"},
				data:      [][]interface{}{{"one"}},
			},
		},
		{
			name: "single",
			args: args{
				tableName: "single",
				args:      []interface{}{"a", "b"},
			},
			want: &SliceReader{
				tableName: "single",
				names:     []string{"c1"},
				types:     []string{"text"},
				data:      [][]interface{}{{"a"}, {"b"}},
			},
		},
		{
			name: "slice",
			args: args{
				tableName: "slice",
				args: [][]interface{}{
					{1, "one"},
					{2, "two"},
					{3, "three"},
				},
			},
			want: &SliceReader{
				tableName: "slice",
				names:     []string{"c1", "c2"},
				types:     []string{"text", "text"},
				data: [][]interface{}{
					{1, "one"},
					{2, "two"},
					{3, "three"},
				},
			},
		},
		{
			name: "struct",
			args: args{
				tableName: "struct",
				args: []struct {
					id   int
					name string
				}{
					{id: 1, name: "one"},
					{id: 2, name: "two"},
					{id: 3, name: "three"},
				},
			},
			want: &SliceReader{
				tableName: "struct",
				names:     []string{"id", "name"},
				types:     []string{"int", "text"},
				data: [][]interface{}{
					{"1", "one"},
					{"2", "two"},
					{"3", "three"},
				},
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := NewSliceReader(tt.args.tableName, tt.args.args); len(got.data) != len(tt.want.data) {
				t.Errorf("NewSliceReader() = %#v, want %#v", got, tt.want)
			}
		})
	}
}
func TestNewMapSliceReader(t *testing.T) {
	type args struct {
		tableName string
		args      interface{}
	}
	tests := []struct {
		name string
		args args
		want *SliceReader
	}{
		{
			name: "map",
			args: args{
				tableName: "map",
				args: map[string]string{
					"1": "one",
					"2": "two",
					"3": "three",
				},
			},
			want: &SliceReader{
				tableName: "map",
				names:     []string{"c1", "c2"},
				types:     []string{"text", "text"},
				data: [][]interface{}{
					{"1", "one"},
					{"2", "two"},
					{"3", "three"},
				},
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := NewSliceReader(tt.args.tableName, tt.args.args); !reflect.DeepEqual(got, tt.want) {
				t.Errorf("NewSliceReader() = %#v, want %#v", got, tt.want)
			}
		})
	}
}

func TestSliceReader_PreReadRow(t *testing.T) {
	type fields struct {
		tableName string
		names     []string
		types     []string
		data      [][]interface{}
	}
	tests := []struct {
		name   string
		fields fields
		want   [][]interface{}
	}{
		{
			name: "struct",
			fields: fields{
				tableName: "struct",
				names:     []string{"id", "name"},
				types:     []string{"text", "text"},
				data: [][]interface{}{
					{"1", "one"},
					{"2", "two"},
					{"3", "three"},
				},
			},
			want: [][]interface{}{
				{"1", "one"},
				{"2", "two"},
				{"3", "three"},
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			r := &SliceReader{
				tableName: tt.fields.tableName,
				names:     tt.fields.names,
				types:     tt.fields.types,
				data:      tt.fields.data,
			}
			if got := r.PreReadRow(); !reflect.DeepEqual(got, tt.want) {
				t.Errorf("SliceReader.PreReadRow() = %v, want %v", got, tt.want)
			}
		})
	}
}
<commit_msg>Fix of comparison target<commit_after>package trdsql

import (
	"reflect"
	"testing"
)

func TestNewSliceReader(t *testing.T) {
	type args struct {
		tableName string
		args      interface{}
	}
	tests := []struct {
		name string
		args args
		want *SliceReader
	}{
		{
			name: "string",
			args: args{
				tableName: "one",
				args:      "one",
			},
			want: &SliceReader{
				tableName: "one",
				names:     []string{"c1"},
				types:     []string{"text"},
				data:      [][]interface{}{{"one"}},
			},
		},
		{
			name: "single",
			args: args{
				tableName: "single",
				args:      []interface{}{"a", "b"},
			},
			want: &SliceReader{
				tableName: "single",
				names:     []string{"c1"},
				types:     []string{"text"},
				data:      [][]interface{}{{"a"}, {"b"}},
			},
		},
		{
			name: "slice",
			args: args{
				tableName: "slice",
				args: [][]interface{}{
					{1, "one"},
					{2, "two"},
					{3, "three"},
				},
			},
			want: &SliceReader{
				tableName: "slice",
				names:     []string{"c1", "c2"},
				types:     []string{"text", "text"},
				data: [][]interface{}{
					{1, "one"},
					{2, "two"},
					{3, "three"},
				},
			},
		},
		{
			name: "struct",
			args: args{
				tableName: "struct",
				args: []struct {
					id   int
					name string
				}{
					{id: 1, name: "one"},
					{id: 2, name: "two"},
					{id: 3, name: "three"},
				},
			},
			want: &SliceReader{
				tableName: "struct",
				names:     []string{"id", "name"},
				types:     []string{"int", "text"},
				data: [][]interface{}{
					{"1", "one"},
					{"2", "two"},
					{"3", "three"},
				},
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := NewSliceReader(tt.args.tableName, tt.args.args);!reflect.DeepEqual(got, tt.want) {
				t.Errorf("NewSliceReader() = %#v, want %#v", got, tt.want)
			}
		})
	}
}
func TestNewMapSliceReader(t *testing.T) {
	type args struct {
		tableName string
		args      interface{}
	}
	tests := []struct {
		name string
		args args
		want *SliceReader
	}{
		{
			name: "map",
			args: args{
				tableName: "map",
				args: map[string]string{
					"1": "one",
					"2": "two",
					"3": "three",
				},
			},
			want: &SliceReader{
				tableName: "map",
				names:     []string{"c1", "c2"},
				types:     []string{"text", "text"},
				data: [][]interface{}{
					{"1", "one"},
					{"2", "two"},
					{"3", "three"},
				},
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := NewSliceReader(tt.args.tableName, tt.args.args); len(got.data) != len(tt.want.data) {
				t.Errorf("NewSliceReader() = %#v, want %#v", got, tt.want)
			}
		})
	}
}

func TestSliceReader_PreReadRow(t *testing.T) {
	type fields struct {
		tableName string
		names     []string
		types     []string
		data      [][]interface{}
	}
	tests := []struct {
		name   string
		fields fields
		want   [][]interface{}
	}{
		{
			name: "struct",
			fields: fields{
				tableName: "struct",
				names:     []string{"id", "name"},
				types:     []string{"text", "text"},
				data: [][]interface{}{
					{"1", "one"},
					{"2", "two"},
					{"3", "three"},
				},
			},
			want: [][]interface{}{
				{"1", "one"},
				{"2", "two"},
				{"3", "three"},
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			r := &SliceReader{
				tableName: tt.fields.tableName,
				names:     tt.fields.names,
				types:     tt.fields.types,
				data:      tt.fields.data,
			}
			if got := r.PreReadRow(); !reflect.DeepEqual(got, tt.want) {
				t.Errorf("SliceReader.PreReadRow() = %v, want %v", got, tt.want)
			}
		})
	}
}
<|endoftext|>


<commit_before>package plugin

import (
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/mackerelio/mkr/logger"
	"github.com/mholt/archiver"
	"github.com/pkg/errors"
	"gopkg.in/urfave/cli.v1"
)

// CommandPlugin is definition of mkr plugin
var CommandPlugin = cli.Command{
	Name:        "plugin",
	Usage:       "Manage mackerel plugin",
	Description: `[WIP] Manage mackerel plugin`,
	Subcommands: []cli.Command{
		{
			Name:        "install",
			Usage:       "install mackerel plugin",
			Description: `WIP`,
			Action:      doPluginInstall,
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "prefix",
					Usage: "plugin install location",
				},
			},
		},
	},
	Hidden: true,
}

// main function for mkr plugin install
func doPluginInstall(c *cli.Context) error {
	argInstallTarget := c.Args().First()
	if argInstallTarget == "" {
		return fmt.Errorf("Specify install target")
	}

	_, err := parseInstallTarget(argInstallTarget)
	if err != nil {
		return errors.Wrap(err, "Failed to install plugin while setup plugin directory")
	}

	prefix, err := setupPluginDir(c.String("prefix"))
	if err != nil {
		return errors.Wrap(err, "Failed to install plugin while parsing install target")
	}

	// Create a work directory for downloading and extracting an artifact
	workdir, err := ioutil.TempDir(prefix, "work-")
	if err != nil {
		return errors.Wrap(err, "failed to install plugin while creating a work directory")
	}
	defer os.RemoveAll(workdir)

	fmt.Println("do plugin install [wip]")
	return nil
}

// Parse install target string passed from args
// example is below
// - mackerelio/mackerel-plugin-sample
// - mackerel-plugin-sample
// - mackerelio/mackerel-plugin-sample@v0.0.1
func parseInstallTarget(target string) (*installTarget, error) {
	it := &installTarget{}

	ownerRepoAndReleaseTag := strings.Split(target, "@")
	var ownerRepo string
	switch len(ownerRepoAndReleaseTag) {
	case 1:
		ownerRepo = ownerRepoAndReleaseTag[0]
	case 2:
		ownerRepo = ownerRepoAndReleaseTag[0]
		it.releaseTag = ownerRepoAndReleaseTag[1]
	default:
		return nil, fmt.Errorf("Install target is invalid: %s", target)
	}

	ownerAndRepo := strings.Split(ownerRepo, "/")
	switch len(ownerAndRepo) {
	case 1:
		it.pluginName = ownerAndRepo[0]
	case 2:
		it.owner = ownerAndRepo[0]
		it.repo = ownerAndRepo[1]
	default:
		return nil, fmt.Errorf("Install target is invalid: %s", target)
	}

	return it, nil
}

// Create a directory for plugin install
func setupPluginDir(prefix string) (string, error) {
	if prefix == "" {
		prefix = "/opt/mackerel-agent/plugins"
	}
	err := os.MkdirAll(filepath.Join(prefix, "bin"), 0755)
	if err != nil {
		return "", err
	}
	return prefix, nil
}

// Download plugin artifact from `url` to `workdir`,
// and returns downloaded filepath
func downloadPluginArtifact(url, workdir string) (fpath string, err error) {
	// Create request to download
	req, err := http.NewRequest(http.MethodGet, url, nil)
	if err != nil {
		return "", err
	}
	req.Header.Set("User-Agent", "mkr-plugin-installer/0.0.0")
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		err = fmt.Errorf("http response not OK. code: %d, url: %s", resp.StatusCode, url)
		return "", err
	}

	// fpath is filepath where artifact will be saved
	fpath = filepath.Join(workdir, path.Base(url))

	// download artifact
	file, err := os.OpenFile(fpath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
	if err != nil {
		return "", err
	}
	defer file.Close()

	_, err = io.Copy(file, resp.Body)
	if err != nil {
		return "", err
	}

	return fpath, nil
}

// Extract artifact and install plugin
func installByArtifact(artifactFile, bindir, workdir string) error {
	// unzip artifact to work directory
	err := archiver.Zip.Open(artifactFile, workdir)
	if err != nil {
		return err
	}

	// Look for plugin files recursively, and place those to binPath
	return filepath.Walk(workdir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if info.IsDir() {
			return nil
		}

		// a plugin file should be executable, and have specified name.
		name := info.Name()
		isExecutable := (info.Mode() & 0111) != 0
		if isExecutable && looksLikePlugin(name) {
			return placePlugin(path, filepath.Join(bindir, name))
		}

		// `path` is a file but not plugin.
		return nil
	})
}

func looksLikePlugin(name string) bool {
	return strings.HasPrefix(name, "check-") || strings.HasPrefix(name, "mackerel-plugin-")
}

func placePlugin(src, dest string) error {
	_, err := os.Stat(dest)
	if err == nil {
		logger.Log("", fmt.Sprintf("%s already exists. Skip installing for now", dest))
		return nil
	}
	logger.Log("", fmt.Sprintf("Install %s to %s", filepath.Base(dest), filepath.Dir(dest)))
	return os.Rename(src, dest)
}

type installTarget struct {
	owner      string
	repo       string
	pluginName string
	releaseTag string
}

// Make artifact's download URL
func (it *installTarget) makeDownloadURL() (string, error) {
	if it.owner != "" && it.repo != "" {
		if it.releaseTag == "" {
			// TODO: Make latest release download URL by github API
			return "", fmt.Errorf("not implemented")
		}
		filename := fmt.Sprintf("%s_%s_%s.zip", it.repo, runtime.GOOS, runtime.GOARCH)
		return fmt.Sprintf("https://github.com/%s/%s/releases/download/%s/%s",
			it.owner, it.repo, it.releaseTag, filename), nil
	}
	// TODO: Make download URL by plugin registry
	return "", fmt.Errorf("not implemented")
}
<commit_msg>Implements doPluginInstall to run correctly<commit_after>package plugin

import (
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/mackerelio/mkr/logger"
	"github.com/mholt/archiver"
	"github.com/pkg/errors"
	"gopkg.in/urfave/cli.v1"
)

// CommandPlugin is definition of mkr plugin
var CommandPlugin = cli.Command{
	Name:        "plugin",
	Usage:       "Manage mackerel plugin",
	Description: `[WIP] Manage mackerel plugin`,
	Subcommands: []cli.Command{
		{
			Name:        "install",
			Usage:       "install mackerel plugin",
			Description: `WIP`,
			Action:      doPluginInstall,
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "prefix",
					Usage: "plugin install location",
				},
			},
		},
	},
	Hidden: true,
}

// main function for mkr plugin install
func doPluginInstall(c *cli.Context) error {
	argInstallTarget := c.Args().First()
	if argInstallTarget == "" {
		return fmt.Errorf("Specify install target")
	}

	it, err := parseInstallTarget(argInstallTarget)
	if err != nil {
		return errors.Wrap(err, "Failed to install plugin while setup plugin directory")
	}

	prefix, err := setupPluginDir(c.String("prefix"))
	if err != nil {
		return errors.Wrap(err, "Failed to install plugin while parsing install target")
	}

	// Create a work directory for downloading and extracting an artifact
	workdir, err := ioutil.TempDir(prefix, "work-")
	if err != nil {
		return errors.Wrap(err, "Failed to install plugin while creating a work directory")
	}
	defer os.RemoveAll(workdir)

	// Download an artifact and install by it
	downloadURL, err := it.makeDownloadURL()
	if err != nil {
		return errors.Wrap(err, "Failed to install plugin while making a download URL")
	}
	artifactFile, err := downloadPluginArtifact(downloadURL, workdir)
	if err != nil {
		return errors.Wrap(err, "Failed to install plugin while downloading an artifact")
	}
	err = installByArtifact(artifactFile, filepath.Join(prefix, "bin"), workdir)
	if err != nil {
		return errors.Wrap(err, "Failed to install plugin while extracting and placing")
	}

	return nil
}

// Parse install target string passed from args
// example is below
// - mackerelio/mackerel-plugin-sample
// - mackerel-plugin-sample
// - mackerelio/mackerel-plugin-sample@v0.0.1
func parseInstallTarget(target string) (*installTarget, error) {
	it := &installTarget{}

	ownerRepoAndReleaseTag := strings.Split(target, "@")
	var ownerRepo string
	switch len(ownerRepoAndReleaseTag) {
	case 1:
		ownerRepo = ownerRepoAndReleaseTag[0]
	case 2:
		ownerRepo = ownerRepoAndReleaseTag[0]
		it.releaseTag = ownerRepoAndReleaseTag[1]
	default:
		return nil, fmt.Errorf("Install target is invalid: %s", target)
	}

	ownerAndRepo := strings.Split(ownerRepo, "/")
	switch len(ownerAndRepo) {
	case 1:
		it.pluginName = ownerAndRepo[0]
	case 2:
		it.owner = ownerAndRepo[0]
		it.repo = ownerAndRepo[1]
	default:
		return nil, fmt.Errorf("Install target is invalid: %s", target)
	}

	return it, nil
}

// Create a directory for plugin install
func setupPluginDir(prefix string) (string, error) {
	if prefix == "" {
		prefix = "/opt/mackerel-agent/plugins"
	}
	err := os.MkdirAll(filepath.Join(prefix, "bin"), 0755)
	if err != nil {
		return "", err
	}
	return prefix, nil
}

// Download plugin artifact from `url` to `workdir`,
// and returns downloaded filepath
func downloadPluginArtifact(url, workdir string) (fpath string, err error) {
	// Create request to download
	req, err := http.NewRequest(http.MethodGet, url, nil)
	if err != nil {
		return "", err
	}
	req.Header.Set("User-Agent", "mkr-plugin-installer/0.0.0")
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		err = fmt.Errorf("http response not OK. code: %d, url: %s", resp.StatusCode, url)
		return "", err
	}

	// fpath is filepath where artifact will be saved
	fpath = filepath.Join(workdir, path.Base(url))

	// download artifact
	file, err := os.OpenFile(fpath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
	if err != nil {
		return "", err
	}
	defer file.Close()

	_, err = io.Copy(file, resp.Body)
	if err != nil {
		return "", err
	}

	return fpath, nil
}

// Extract artifact and install plugin
func installByArtifact(artifactFile, bindir, workdir string) error {
	// unzip artifact to work directory
	err := archiver.Zip.Open(artifactFile, workdir)
	if err != nil {
		return err
	}

	// Look for plugin files recursively, and place those to binPath
	return filepath.Walk(workdir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if info.IsDir() {
			return nil
		}

		// a plugin file should be executable, and have specified name.
		name := info.Name()
		isExecutable := (info.Mode() & 0111) != 0
		if isExecutable && looksLikePlugin(name) {
			return placePlugin(path, filepath.Join(bindir, name))
		}

		// `path` is a file but not plugin.
		return nil
	})
}

func looksLikePlugin(name string) bool {
	return strings.HasPrefix(name, "check-") || strings.HasPrefix(name, "mackerel-plugin-")
}

func placePlugin(src, dest string) error {
	_, err := os.Stat(dest)
	if err == nil {
		logger.Log("", fmt.Sprintf("%s already exists. Skip installing for now", dest))
		return nil
	}
	logger.Log("", fmt.Sprintf("Install %s to %s", filepath.Base(dest), filepath.Dir(dest)))
	return os.Rename(src, dest)
}

type installTarget struct {
	owner      string
	repo       string
	pluginName string
	releaseTag string
}

// Make artifact's download URL
func (it *installTarget) makeDownloadURL() (string, error) {
	if it.owner != "" && it.repo != "" {
		if it.releaseTag == "" {
			// TODO: Make latest release download URL by github API
			return "", fmt.Errorf("not implemented")
		}
		filename := fmt.Sprintf("%s_%s_%s.zip", it.repo, runtime.GOOS, runtime.GOARCH)
		return fmt.Sprintf("https://github.com/%s/%s/releases/download/%s/%s",
			it.owner, it.repo, it.releaseTag, filename), nil
	}
	// TODO: Make download URL by plugin registry
	return "", fmt.Errorf("not implemented")
}
<|endoftext|>


<commit_before>package main

import (

	"fmt"

)

func main() {

	//Variables

	source_string := "Burning 'em, if you ain't quick and nimble\nI go crazy when I hear a cymbal"

	store := make([]byte, len(source_string))

}<commit_msg>Lets start now<commit_after>package main

import (

	"fmt"

)

func main() {

	//Variables

	source_string := "Burning 'em, if you ain't quick and nimble\nI go crazy when I hear a cymbal"

	store := make([]byte, len(source_string))

	key_one := 0
	key_two := 1
	key_three := 3

	for a := 0; a < len(source_string); a++ {

	}

}<|endoftext|>


<commit_before>package logger

import (
	"github.com/realglobe-Inc/go-lib-rg/rglog/handler"
	"github.com/realglobe-Inc/go-lib-rg/rglog/level"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"testing"
	"time"
)

func testLoggerHandler(t *testing.T, mgr Manager) {
	log := mgr.Logger("a/b/c/d")

	if hndl := log.Handler("test"); hndl != nil {
		t.Error(hndl)
	}

	memHndl := handler.NewNopHandler()
	if hndl := log.AddHandler("test", memHndl); hndl != nil {
		t.Error(hndl)
	}

	oldHndl := log.Handler("test")
	if oldHndl != memHndl {
		t.Error(oldHndl, memHndl)
	}

	memHndl = handler.NewNopHandler()
	if hndl := log.AddHandler("test", memHndl); hndl != oldHndl {
		t.Error(hndl, oldHndl)
	}

	if hndl := log.RemoveHandler("test"); hndl != memHndl {
		t.Error(hndl, memHndl)
	}

	if hndl := log.Handler("test"); hndl != nil {
		t.Error(hndl)
	}
}

func testLoggerLevel(t *testing.T, mgr Manager) {
	log := mgr.Logger("a/b/c/d")

	for _, lv := range level.Values() {
		log.SetLevel(lv)
		if log.Level() != lv {
			t.Error(log.Level(), lv)
		}
	}
}

func testLoggerUseParent(t *testing.T, mgr Manager) {
	log := mgr.Logger("a/b/c/d")

	for _, b := range []bool{true, false} {
		log.SetUseParent(b)
		if log.UseParent() != b {
			t.Error(log.UseParent(), b)
		}
	}
}

func testLoggerIsLoggable(t *testing.T, mgr Manager) {
	//                    先祖にハンドラが無い 先祖の基準重要度より低い 先祖の基準重要度より高い
	// ハンドラが無い     false                false                    true
	// 基準重要度より低い false                false                    true
	// 基準重要度より高い true                 true                     true

	log := mgr.Logger("a/b/c/d")
	log.SetLevel(level.INFO)
	log.SetUseParent(true)
	parentLog := mgr.Logger("a/b")
	parentLog.SetLevel(level.INFO)
	parentLog.SetUseParent(false)

	// ハンドラが無い、先祖にハンドラが無い。
	if log.IsLoggable(level.INFO) {
		t.Error("true: no handler, no handler")
	}

	log.AddHandler("test", handler.NewNopHandler())

	// 基準重要度より低い、先祖にハンドラが無い。
	if log.IsLoggable(level.DEBUG) {
		t.Error("true: lower level, no handler")
	}

	// 基準重要度より高い、先祖にハンドラが無い。
	if !log.IsLoggable(level.INFO) {
		t.Error("false: upper or equal level, no handler")
	}

	log.RemoveHandler("test")
	parentLog.AddHandler("test", handler.NewNopHandler())
	parentLog.SetLevel(level.WARN)

	// ハンドラが無い、先祖の基準重要度より低い。
	if log.IsLoggable(level.INFO) {
		t.Error("true: no handler, lower level")
	}

	log.AddHandler("test", handler.NewNopHandler())

	// 基準重要度より低い、先祖の基準重要度より低い。
	if log.IsLoggable(level.DEBUG) {
		t.Error("true: lower level, lower level")
	}

	// 基準重要度より高い、先祖の基準重要度より低い。
	if !log.IsLoggable(level.INFO) {
		t.Error("false: upper or equal level, lower level")
	}

	log.RemoveHandler("test")
	parentLog.AddHandler("test", handler.NewNopHandler())
	parentLog.SetLevel(level.DEBUG)

	// ハンドラが無い、先祖の基準重要度より高い。
	if !log.IsLoggable(level.INFO) {
		t.Error("false: no handler, upper level")
	}

	log.AddHandler("test", handler.NewNopHandler())

	// 基準重要度より低い、先祖の基準重要度より高い。
	if !log.IsLoggable(level.DEBUG) {
		t.Error("true: lower level, upper level")
	}

	// 基準重要度より高い、先祖の基準重要度より高い。
	if !log.IsLoggable(level.INFO) {
		t.Error("false: upper or equal level, upper level")
	}
}

// ファイル名がちゃんと解決できてるかどうか。
func testLoggerFileName(t *testing.T, mgr Manager) {
	log := mgr.Logger("a/b/c")
	log.SetLevel(level.ALL)
	log.SetUseParent(false)

	hndl := handler.NewMemoryHandlerUsing(&fileOnlyFormatter{})
	hndl.SetLevel(level.ALL)
	log.AddHandler("test", hndl)

	log.Log(level.INFO, "")
	dum := hndl.Dump()
	if dum != filepath.Join("github.com", "realglobe-Inc", "go-lib-rg", "rglog", "logger", "logger_test.go") {
		t.Error(dum)
	}

	for _, logging := range []func(...interface{}){log.Err, log.Warn, log.Info, log.Debug} {
		hndl := handler.NewMemoryHandlerUsing(&fileOnlyFormatter{})
		hndl.SetLevel(level.ALL)
		log.AddHandler("test", hndl)

		logging("")
		dum := hndl.Dump()
		if dum != filepath.Join("github.com", "realglobe-Inc", "go-lib-rg", "rglog", "logger", "logger_test.go") {
			t.Error(dum)
		}
	}
}

type fileOnlyFormatter struct{}

func (fmter *fileOnlyFormatter) Format(rec handler.Record) []byte {
	return []byte(rec.File())
}

func testLoggerConcurrent(t *testing.T, mgr Manager) {
	rootLabel := "a/b/c"

	conc := 100
	loop := 1000

	rootLog := mgr.Logger(rootLabel)
	rootLog.SetLevel(level.ALL)
	rootLog.SetUseParent(false)

	path := filepath.Join(os.TempDir(), "locklog_test")
	if err := os.Remove(path); err != nil {
		if !os.IsNotExist(err) {
			t.Fatal(err)
		}
	}
	defer os.Remove(path)

	hndl := handler.NewMemoryHandler()
	hndl.SetLevel(level.DEBUG)
	rootLog.AddHandler("test", hndl)

	c := make(chan bool)
	timeout := time.After(time.Duration(int64(conc*loop*100) * int64(time.Microsecond)))

	for i := 0; i < conc; i++ {
		id := i
		go func() {
			for j := 0; j < loop; j++ {
				mgr.Logger(rootLabel+"/"+strconv.Itoa(id)).Info(id, j)
			}

			c <- true
		}()
	}

	for i := 0; i < conc; i++ {
		select {
		case <-c:
		case <-timeout:
			t.Fatal("Dead lock?")
		}
	}

	mgr.Flush()

	// ファイルに書き込めているかどうか検査。
	buff := hndl.Dump()

	if len(buff) > 0 && buff[len(buff)-1] == '\n' { // 最後の空行は抜かしとく。
		buff = buff[:len(buff)-1]
	}

	lines := strings.Split(buff, "\n")
	if len(lines) != conc*loop {
		t.Error(len(lines), conc*loop)
	}
}

func benchmarkLoggerConcurrent(b *testing.B, mgr Manager) {
	rootLabel := "a/b/c"
	conc := 100

	rootLog := mgr.Logger(rootLabel)
	rootLog.SetLevel(level.ALL)
	rootLog.SetUseParent(false)

	path := filepath.Join(os.TempDir(), "locklog_test")
	if err := os.Remove(path); err != nil {
		if !os.IsNotExist(err) {
			b.Fatal(err)
		}
	}
	defer os.Remove(path)

	hndl := handler.NewNopHandler()
	hndl.SetLevel(level.DEBUG)
	rootLog.AddHandler("test", hndl)

	c := make(chan bool)
	timeout := time.After(time.Duration(int64(conc*b.N*100) * int64(time.Microsecond)))

	for i := 0; i < conc; i++ {
		id := i
		go func() {
			for j := 0; j < b.N/conc; j++ {
				mgr.Logger(rootLabel+"/"+strconv.Itoa(id)).Info(id, j)
			}

			c <- true
		}()
	}

	for i := 0; i < conc; i++ {
		select {
		case <-c:
		case <-timeout:
			b.Fatal("Dead lock?")
		}
	}

	mgr.Flush()
}
<commit_msg>ロガーの初期重要度が level.OFF であることをテストするようにした<commit_after>package logger

import (
	"github.com/realglobe-Inc/go-lib-rg/rglog/handler"
	"github.com/realglobe-Inc/go-lib-rg/rglog/level"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"testing"
	"time"
)

func testLoggerHandler(t *testing.T, mgr Manager) {
	log := mgr.Logger("a/b/c/d")

	if hndl := log.Handler("test"); hndl != nil {
		t.Error(hndl)
	}

	memHndl := handler.NewNopHandler()
	if hndl := log.AddHandler("test", memHndl); hndl != nil {
		t.Error(hndl)
	}

	oldHndl := log.Handler("test")
	if oldHndl != memHndl {
		t.Error(oldHndl, memHndl)
	}

	memHndl = handler.NewNopHandler()
	if hndl := log.AddHandler("test", memHndl); hndl != oldHndl {
		t.Error(hndl, oldHndl)
	}

	if hndl := log.RemoveHandler("test"); hndl != memHndl {
		t.Error(hndl, memHndl)
	}

	if hndl := log.Handler("test"); hndl != nil {
		t.Error(hndl)
	}
}

func testLoggerLevel(t *testing.T, mgr Manager) {
	log := mgr.Logger("a/b/c/d")

	if log.Level() != level.OFF {
		t.Error(log.Level())
	}

	for _, lv := range level.Values() {
		log.SetLevel(lv)
		if log.Level() != lv {
			t.Error(log.Level(), lv)
		}
	}
}

func testLoggerUseParent(t *testing.T, mgr Manager) {
	log := mgr.Logger("a/b/c/d")

	for _, b := range []bool{true, false} {
		log.SetUseParent(b)
		if log.UseParent() != b {
			t.Error(log.UseParent(), b)
		}
	}
}

func testLoggerIsLoggable(t *testing.T, mgr Manager) {
	//                    先祖にハンドラが無い 先祖の基準重要度より低い 先祖の基準重要度より高い
	// ハンドラが無い     false                false                    true
	// 基準重要度より低い false                false                    true
	// 基準重要度より高い true                 true                     true

	log := mgr.Logger("a/b/c/d")
	log.SetLevel(level.INFO)
	log.SetUseParent(true)
	parentLog := mgr.Logger("a/b")
	parentLog.SetLevel(level.INFO)
	parentLog.SetUseParent(false)

	// ハンドラが無い、先祖にハンドラが無い。
	if log.IsLoggable(level.INFO) {
		t.Error("true: no handler, no handler")
	}

	log.AddHandler("test", handler.NewNopHandler())

	// 基準重要度より低い、先祖にハンドラが無い。
	if log.IsLoggable(level.DEBUG) {
		t.Error("true: lower level, no handler")
	}

	// 基準重要度より高い、先祖にハンドラが無い。
	if !log.IsLoggable(level.INFO) {
		t.Error("false: upper or equal level, no handler")
	}

	log.RemoveHandler("test")
	parentLog.AddHandler("test", handler.NewNopHandler())
	parentLog.SetLevel(level.WARN)

	// ハンドラが無い、先祖の基準重要度より低い。
	if log.IsLoggable(level.INFO) {
		t.Error("true: no handler, lower level")
	}

	log.AddHandler("test", handler.NewNopHandler())

	// 基準重要度より低い、先祖の基準重要度より低い。
	if log.IsLoggable(level.DEBUG) {
		t.Error("true: lower level, lower level")
	}

	// 基準重要度より高い、先祖の基準重要度より低い。
	if !log.IsLoggable(level.INFO) {
		t.Error("false: upper or equal level, lower level")
	}

	log.RemoveHandler("test")
	parentLog.AddHandler("test", handler.NewNopHandler())
	parentLog.SetLevel(level.DEBUG)

	// ハンドラが無い、先祖の基準重要度より高い。
	if !log.IsLoggable(level.INFO) {
		t.Error("false: no handler, upper level")
	}

	log.AddHandler("test", handler.NewNopHandler())

	// 基準重要度より低い、先祖の基準重要度より高い。
	if !log.IsLoggable(level.DEBUG) {
		t.Error("true: lower level, upper level")
	}

	// 基準重要度より高い、先祖の基準重要度より高い。
	if !log.IsLoggable(level.INFO) {
		t.Error("false: upper or equal level, upper level")
	}
}

// ファイル名がちゃんと解決できてるかどうか。
func testLoggerFileName(t *testing.T, mgr Manager) {
	log := mgr.Logger("a/b/c")
	log.SetLevel(level.ALL)
	log.SetUseParent(false)

	hndl := handler.NewMemoryHandlerUsing(&fileOnlyFormatter{})
	hndl.SetLevel(level.ALL)
	log.AddHandler("test", hndl)

	log.Log(level.INFO, "")
	dum := hndl.Dump()
	if dum != filepath.Join("github.com", "realglobe-Inc", "go-lib-rg", "rglog", "logger", "logger_test.go") {
		t.Error(dum)
	}

	for _, logging := range []func(...interface{}){log.Err, log.Warn, log.Info, log.Debug} {
		hndl := handler.NewMemoryHandlerUsing(&fileOnlyFormatter{})
		hndl.SetLevel(level.ALL)
		log.AddHandler("test", hndl)

		logging("")
		dum := hndl.Dump()
		if dum != filepath.Join("github.com", "realglobe-Inc", "go-lib-rg", "rglog", "logger", "logger_test.go") {
			t.Error(dum)
		}
	}
}

type fileOnlyFormatter struct{}

func (fmter *fileOnlyFormatter) Format(rec handler.Record) []byte {
	return []byte(rec.File())
}

func testLoggerConcurrent(t *testing.T, mgr Manager) {
	rootLabel := "a/b/c"

	conc := 100
	loop := 1000

	rootLog := mgr.Logger(rootLabel)
	rootLog.SetLevel(level.ALL)
	rootLog.SetUseParent(false)

	path := filepath.Join(os.TempDir(), "locklog_test")
	if err := os.Remove(path); err != nil {
		if !os.IsNotExist(err) {
			t.Fatal(err)
		}
	}
	defer os.Remove(path)

	hndl := handler.NewMemoryHandler()
	hndl.SetLevel(level.DEBUG)
	rootLog.AddHandler("test", hndl)

	c := make(chan bool)
	timeout := time.After(time.Duration(int64(conc*loop*100) * int64(time.Microsecond)))

	for i := 0; i < conc; i++ {
		id := i
		go func() {
			for j := 0; j < loop; j++ {
				mgr.Logger(rootLabel+"/"+strconv.Itoa(id)).Info(id, j)
			}

			c <- true
		}()
	}

	for i := 0; i < conc; i++ {
		select {
		case <-c:
		case <-timeout:
			t.Fatal("Dead lock?")
		}
	}

	mgr.Flush()

	// ファイルに書き込めているかどうか検査。
	buff := hndl.Dump()

	if len(buff) > 0 && buff[len(buff)-1] == '\n' { // 最後の空行は抜かしとく。
		buff = buff[:len(buff)-1]
	}

	lines := strings.Split(buff, "\n")
	if len(lines) != conc*loop {
		t.Error(len(lines), conc*loop)
	}
}

func benchmarkLoggerConcurrent(b *testing.B, mgr Manager) {
	rootLabel := "a/b/c"
	conc := 100

	rootLog := mgr.Logger(rootLabel)
	rootLog.SetLevel(level.ALL)
	rootLog.SetUseParent(false)

	path := filepath.Join(os.TempDir(), "locklog_test")
	if err := os.Remove(path); err != nil {
		if !os.IsNotExist(err) {
			b.Fatal(err)
		}
	}
	defer os.Remove(path)

	hndl := handler.NewNopHandler()
	hndl.SetLevel(level.DEBUG)
	rootLog.AddHandler("test", hndl)

	c := make(chan bool)
	timeout := time.After(time.Duration(int64(conc*b.N*100) * int64(time.Microsecond)))

	for i := 0; i < conc; i++ {
		id := i
		go func() {
			for j := 0; j < b.N/conc; j++ {
				mgr.Logger(rootLabel+"/"+strconv.Itoa(id)).Info(id, j)
			}

			c <- true
		}()
	}

	for i := 0; i < conc; i++ {
		select {
		case <-c:
		case <-timeout:
			b.Fatal("Dead lock?")
		}
	}

	mgr.Flush()
}
<|endoftext|>


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

package log

import (
	"bytes"
	"fmt"
	"log/syslog"
	"os/exec"
	"strings"

	"golang.org/x/sys/unix"
)

type LoggerOps interface {
	Emerg(string) error
	Alert(string) error
	Crit(string) error
	Err(string) error
	Warning(string) error
	Notice(string) error
	Info(string) error
	Debug(string) error
	Close() error
}

// Logger implements a variadic flavor of log/syslog.Writer
type Logger struct {
	ops           LoggerOps
	prefixStack   []string
	opSequenceNum int
}

// New creates a new logger.
// If logToStdout is true, syslog is tried first. If syslog fails or logToStdout
// is false Stdout is used.
func New(logToStdout bool) Logger {
	logger := Logger{}
	if !logToStdout {
		var err error
		logger.ops, err = syslog.New(syslog.LOG_DEBUG, "ignition")
		if err != nil {
			logger.ops = Stdout{}
			logger.Err("unable to open syslog: %v", err)
		}
		return logger
	}
	logger.ops = Stdout{}
	return logger
}

// Close closes the logger.
func (l Logger) Close() {
	l.ops.Close()
}

// Emerg logs a message at emergency priority.
func (l Logger) Emerg(format string, a ...interface{}) error {
	return l.log(l.ops.Emerg, format, a...)
}

// Alert logs a message at alert priority.
func (l Logger) Alert(format string, a ...interface{}) error {
	return l.log(l.ops.Alert, format, a...)
}

// Crit logs a message at critical priority.
func (l Logger) Crit(format string, a ...interface{}) error {
	return l.log(l.ops.Crit, format, a...)
}

// Err logs a message at error priority.
func (l Logger) Err(format string, a ...interface{}) error {
	return l.log(l.ops.Err, format, a...)
}

// Warning logs a message at warning priority.
func (l Logger) Warning(format string, a ...interface{}) error {
	return l.log(l.ops.Warning, format, a...)
}

// Notice logs a message at notice priority.
func (l Logger) Notice(format string, a ...interface{}) error {
	return l.log(l.ops.Notice, format, a...)
}

// Info logs a message at info priority.
func (l Logger) Info(format string, a ...interface{}) error {
	return l.log(l.ops.Info, format, a...)
}

// Debug logs a message at debug priority.
func (l Logger) Debug(format string, a ...interface{}) error {
	return l.log(l.ops.Debug, format, a...)
}

// PushPrefix pushes the supplied message onto the Logger's prefix stack.
// The prefix stack is concatenated in FIFO order and prefixed to the start of every message logged via Logger.
func (l *Logger) PushPrefix(format string, a ...interface{}) {
	l.prefixStack = append(l.prefixStack, fmt.Sprintf(format, a...))
}

// PopPrefix pops the top entry from the Logger's prefix stack.
// The prefix stack is concatenated in FIFO order and prefixed to the start of every message logged via Logger.
func (l *Logger) PopPrefix() {
	if len(l.prefixStack) == 0 {
		l.Debug("popped from empty stack")
		return
	}
	l.prefixStack = l.prefixStack[:len(l.prefixStack)-1]
}

// QuotedCmd returns a concatenated, quoted form of cmd's cmdline
func QuotedCmd(cmd *exec.Cmd) string {
	if len(cmd.Args) == 0 {
		return fmt.Sprintf("%q", cmd.Path)
	}

	var q []string
	for _, s := range cmd.Args {
		q = append(q, fmt.Sprintf("%q", s))
	}

	return strings.Join(q, ` `)
}

// LogCmd runs and logs the supplied cmd as an operation with distinct start/finish/fail log messages uniformly combined with the supplied format string.
// The exact command path and arguments being executed are also logged for debugging assistance.
func (l *Logger) LogCmd(cmd *exec.Cmd, format string, a ...interface{}) (int, error) {
	code := -1
	f := func() error {
		cmdLine := QuotedCmd(cmd)
		l.Debug("executing: %s", cmdLine)

		stdout := &bytes.Buffer{}
		stderr := &bytes.Buffer{}
		cmd.Stdout = stdout
		cmd.Stderr = stderr
		if err := cmd.Run(); err != nil {
			if exitErr, ok := err.(*exec.ExitError); ok {
				code = exitErr.Sys().(unix.WaitStatus).ExitStatus()
			}
			return fmt.Errorf("%v: Cmd: %s Stdout: %q Stderr: %q", err, cmdLine, stdout.Bytes(), stderr.Bytes())
		}
		return nil
	}
	err := l.LogOp(f, format, a...)
	return code, err
}

// LogOp calls and logs the supplied function as an operation with distinct start/finish/fail log messages uniformly combined with the supplied format string.
func (l *Logger) LogOp(op func() error, format string, a ...interface{}) error {
	l.opSequenceNum++
	l.PushPrefix("op(%x)", l.opSequenceNum)
	defer l.PopPrefix()

	l.logStart(format, a...)
	if err := op(); err != nil {
		l.logFail("%s: %v", fmt.Sprintf(format, a...), err)
		return err
	}
	l.logFinish(format, a...)
	return nil
}

// logStart logs the start of a multi-step/substantial/time-consuming operation.
func (l Logger) logStart(format string, a ...interface{}) {
	l.Info(fmt.Sprintf("[started]  %s", format), a...)
}

// logFail logs the failure of a multi-step/substantial/time-consuming operation.
func (l Logger) logFail(format string, a ...interface{}) {
	l.Crit(fmt.Sprintf("[failed]   %s", format), a...)
}

// logFinish logs the completion of a multi-step/substantial/time-consuming operation.
func (l Logger) logFinish(format string, a ...interface{}) {
	l.Info(fmt.Sprintf("[finished] %s", format), a...)
}

// log logs a formatted message using the supplied logFunc.
func (l Logger) log(logFunc func(string) error, format string, a ...interface{}) error {
	return logFunc(l.sprintf(format, a...))
}

// sprintf returns the current prefix stack, if any, concatenated with the supplied format string and args in expanded form.
func (l Logger) sprintf(format string, a ...interface{}) string {
	m := []string{}
	for _, pfx := range l.prefixStack {
		m = append(m, fmt.Sprintf("%s:", pfx))
	}
	m = append(m, fmt.Sprintf(format, a...))
	return strings.Join(m, " ")
}
<commit_msg>log: use os.ProcessState.ExitCode instead of unix<commit_after>// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package log

import (
	"bytes"
	"fmt"
	"log/syslog"
	"os/exec"
	"strings"
)

type LoggerOps interface {
	Emerg(string) error
	Alert(string) error
	Crit(string) error
	Err(string) error
	Warning(string) error
	Notice(string) error
	Info(string) error
	Debug(string) error
	Close() error
}

// Logger implements a variadic flavor of log/syslog.Writer
type Logger struct {
	ops           LoggerOps
	prefixStack   []string
	opSequenceNum int
}

// New creates a new logger.
// If logToStdout is true, syslog is tried first. If syslog fails or logToStdout
// is false Stdout is used.
func New(logToStdout bool) Logger {
	logger := Logger{}
	if !logToStdout {
		var err error
		logger.ops, err = syslog.New(syslog.LOG_DEBUG, "ignition")
		if err != nil {
			logger.ops = Stdout{}
			logger.Err("unable to open syslog: %v", err)
		}
		return logger
	}
	logger.ops = Stdout{}
	return logger
}

// Close closes the logger.
func (l Logger) Close() {
	l.ops.Close()
}

// Emerg logs a message at emergency priority.
func (l Logger) Emerg(format string, a ...interface{}) error {
	return l.log(l.ops.Emerg, format, a...)
}

// Alert logs a message at alert priority.
func (l Logger) Alert(format string, a ...interface{}) error {
	return l.log(l.ops.Alert, format, a...)
}

// Crit logs a message at critical priority.
func (l Logger) Crit(format string, a ...interface{}) error {
	return l.log(l.ops.Crit, format, a...)
}

// Err logs a message at error priority.
func (l Logger) Err(format string, a ...interface{}) error {
	return l.log(l.ops.Err, format, a...)
}

// Warning logs a message at warning priority.
func (l Logger) Warning(format string, a ...interface{}) error {
	return l.log(l.ops.Warning, format, a...)
}

// Notice logs a message at notice priority.
func (l Logger) Notice(format string, a ...interface{}) error {
	return l.log(l.ops.Notice, format, a...)
}

// Info logs a message at info priority.
func (l Logger) Info(format string, a ...interface{}) error {
	return l.log(l.ops.Info, format, a...)
}

// Debug logs a message at debug priority.
func (l Logger) Debug(format string, a ...interface{}) error {
	return l.log(l.ops.Debug, format, a...)
}

// PushPrefix pushes the supplied message onto the Logger's prefix stack.
// The prefix stack is concatenated in FIFO order and prefixed to the start of every message logged via Logger.
func (l *Logger) PushPrefix(format string, a ...interface{}) {
	l.prefixStack = append(l.prefixStack, fmt.Sprintf(format, a...))
}

// PopPrefix pops the top entry from the Logger's prefix stack.
// The prefix stack is concatenated in FIFO order and prefixed to the start of every message logged via Logger.
func (l *Logger) PopPrefix() {
	if len(l.prefixStack) == 0 {
		l.Debug("popped from empty stack")
		return
	}
	l.prefixStack = l.prefixStack[:len(l.prefixStack)-1]
}

// QuotedCmd returns a concatenated, quoted form of cmd's cmdline
func QuotedCmd(cmd *exec.Cmd) string {
	if len(cmd.Args) == 0 {
		return fmt.Sprintf("%q", cmd.Path)
	}

	var q []string
	for _, s := range cmd.Args {
		q = append(q, fmt.Sprintf("%q", s))
	}

	return strings.Join(q, ` `)
}

// LogCmd runs and logs the supplied cmd as an operation with distinct start/finish/fail log messages uniformly combined with the supplied format string.
// The exact command path and arguments being executed are also logged for debugging assistance.
func (l *Logger) LogCmd(cmd *exec.Cmd, format string, a ...interface{}) (int, error) {
	code := -1
	f := func() error {
		cmdLine := QuotedCmd(cmd)
		l.Debug("executing: %s", cmdLine)

		stdout := &bytes.Buffer{}
		stderr := &bytes.Buffer{}
		cmd.Stdout = stdout
		cmd.Stderr = stderr
		if err := cmd.Run(); err != nil {
			if exitErr, ok := err.(*exec.ExitError); ok {
				code = exitErr.ExitCode()
			}
			return fmt.Errorf("%v: Cmd: %s Stdout: %q Stderr: %q", err, cmdLine, stdout.Bytes(), stderr.Bytes())
		}
		return nil
	}
	err := l.LogOp(f, format, a...)
	return code, err
}

// LogOp calls and logs the supplied function as an operation with distinct start/finish/fail log messages uniformly combined with the supplied format string.
func (l *Logger) LogOp(op func() error, format string, a ...interface{}) error {
	l.opSequenceNum++
	l.PushPrefix("op(%x)", l.opSequenceNum)
	defer l.PopPrefix()

	l.logStart(format, a...)
	if err := op(); err != nil {
		l.logFail("%s: %v", fmt.Sprintf(format, a...), err)
		return err
	}
	l.logFinish(format, a...)
	return nil
}

// logStart logs the start of a multi-step/substantial/time-consuming operation.
func (l Logger) logStart(format string, a ...interface{}) {
	l.Info(fmt.Sprintf("[started]  %s", format), a...)
}

// logFail logs the failure of a multi-step/substantial/time-consuming operation.
func (l Logger) logFail(format string, a ...interface{}) {
	l.Crit(fmt.Sprintf("[failed]   %s", format), a...)
}

// logFinish logs the completion of a multi-step/substantial/time-consuming operation.
func (l Logger) logFinish(format string, a ...interface{}) {
	l.Info(fmt.Sprintf("[finished] %s", format), a...)
}

// log logs a formatted message using the supplied logFunc.
func (l Logger) log(logFunc func(string) error, format string, a ...interface{}) error {
	return logFunc(l.sprintf(format, a...))
}

// sprintf returns the current prefix stack, if any, concatenated with the supplied format string and args in expanded form.
func (l Logger) sprintf(format string, a ...interface{}) string {
	m := []string{}
	for _, pfx := range l.prefixStack {
		m = append(m, fmt.Sprintf("%s:", pfx))
	}
	m = append(m, fmt.Sprintf(format, a...))
	return strings.Join(m, " ")
}
<|endoftext|>


<commit_before>package mbs

import (
	"github.com/dvrkps/valida/internal/digits"
	"github.com/dvrkps/valida/internal/validate"
)

// OK validate MBS number.
func OK(in string) bool {
	const maxDigits = 12
	if len(in) == maxDigits {
		in = in[:8]
	}

	digs, ok := digits.ParseDigits([]byte(in))
	if !ok {
		return false
	}

	zzz := 0

	const (
		noDigits = 7
		coefMax  = 8
	)

	for i, d := range digs.First(noDigits) {
		coef := coefMax - i
		zzz += d * coef
	}

	return validate.IsValid(zzz, digs.Last())
}
<commit_msg>internal/mbs: use check.OK<commit_after>package mbs

import (
	"github.com/dvrkps/valida/internal/check"
	"github.com/dvrkps/valida/internal/digits"
)

// OK validate MBS number.
func OK(in string) bool {
	const maxDigits = 12
	if len(in) == maxDigits {
		in = in[:8]
	}

	digs, ok := digits.ParseDigits([]byte(in))
	if !ok {
		return false
	}

	zzz := 0

	const (
		noDigits = 7
		coefMax  = 8
	)

	for i, d := range digs.First(noDigits) {
		coef := coefMax - i
		zzz += d * coef
	}

	const wantRem = 1

	return check.OK(zzz, digs.Last(), wantRem)
}
<|endoftext|>