<commit_before>// Copyright 2014-2017 Amazon.com, Inc. or its affiliates. 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. A copy of the
// License is located at
//
//	http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file 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 eventhandler

import (
	"errors"
	"strconv"
	"sync"
	"testing"
	"time"

	"github.com/aws/amazon-ecs-agent/agent/api"
	"github.com/aws/amazon-ecs-agent/agent/api/mocks"
	"github.com/aws/amazon-ecs-agent/agent/utils"
	"github.com/golang/mock/gomock"
	"github.com/stretchr/testify/assert"
)

func contEvent(arn string) api.ContainerStateChange {
	return api.ContainerStateChange{TaskArn: arn, ContainerName: "containerName", Status: api.ContainerRunning, Container: &api.Container{}}
}
func taskEvent(arn string) api.TaskStateChange {
	return api.TaskStateChange{TaskArn: arn, Status: api.TaskRunning, Task: &api.Task{}}
}

func TestSendsEventsOneContainer(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	client := mock_api.NewMockECSClient(ctrl)

	handler := NewTaskHandler()

	// Trivial: one container, no errors
	contCalled := make(chan struct{})
	taskCalled := make(chan struct{})
	contEvent1 := contEvent("1")
	contEvent2 := contEvent("2")
	taskEvent2 := taskEvent("2")

	client.EXPECT().SubmitContainerStateChange(contEvent1).Do(func(interface{}) { contCalled <- struct{}{} })
	client.EXPECT().SubmitContainerStateChange(contEvent2).Do(func(interface{}) { contCalled <- struct{}{} })
	client.EXPECT().SubmitTaskStateChange(taskEvent2).Do(func(interface{}) { taskCalled <- struct{}{} })

	handler.AddContainerEvent(contEvent1, client)
	handler.AddContainerEvent(contEvent2, client)
	handler.AddTaskEvent(taskEvent2, client)

	<-contCalled
	<-contCalled
	<-taskCalled
}

func TestSendsEventsOneEventRetries(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	client := mock_api.NewMockECSClient(ctrl)

	handler := NewTaskHandler()

	retriable := utils.NewRetriableError(utils.NewRetriable(true), errors.New("test"))
	contCalled := make(chan struct{})
	contEvent1 := contEvent("1")

	gomock.InOrder(
		client.EXPECT().SubmitContainerStateChange(contEvent1).Return(retriable).Do(func(interface{}) { contCalled <- struct{}{} }),
		client.EXPECT().SubmitContainerStateChange(contEvent1).Return(nil).Do(func(interface{}) { contCalled <- struct{}{} }),
	)

	handler.AddContainerEvent(contEvent1, client)

	<-contCalled
	<-contCalled
}

func TestSendsEventsConcurrentLimit(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	client := mock_api.NewMockECSClient(ctrl)

	handler := NewTaskHandler()

	contCalled := make(chan struct{}, concurrentEventCalls+1)
	completeStateChange := make(chan bool, concurrentEventCalls+1)
	count := 0
	countLock := &sync.Mutex{}
	client.EXPECT().SubmitContainerStateChange(gomock.Any()).Times(concurrentEventCalls + 1).Do(func(interface{}) {
		countLock.Lock()
		count++
		countLock.Unlock()
		<-completeStateChange
		contCalled <- struct{}{}
	})
	// Test concurrency; ensure it doesn't attempt to send more than
	// concurrentEventCalls at once
	// Put on N+1 events
	for i := 0; i < concurrentEventCalls+1; i++ {
		handler.AddContainerEvent(contEvent("concurrent_"+strconv.Itoa(i)), client)
	}
	time.Sleep(10 * time.Millisecond)

	// N events should be waiting for potential errors since we havent started completing state changes
	assert.Equal(t, concurrentEventCalls, count, "Too many event calls got through concurrently")
	// Let one state change finish
	completeStateChange <- true
	<-contCalled
	time.Sleep(10 * time.Millisecond)

	assert.Equal(t, concurrentEventCalls+1, count, "Another concurrent call didn't start when expected")

	// ensure the remaining requests are completed
	for i := 0; i < concurrentEventCalls; i++ {
		completeStateChange <- true
		<-contCalled
	}
	time.Sleep(5 * time.Millisecond)
	assert.Equal(t, concurrentEventCalls+1, count, "Extra concurrent calls appeared from nowhere")
}

func TestSendsEventsContainerDifferences(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	client := mock_api.NewMockECSClient(ctrl)

	handler := NewTaskHandler()

	// Test container event replacement doesn't happen
	notReplaced := contEvent("notreplaced1")
	sortaRedundant := contEvent("notreplaced1")
	sortaRedundant.Status = api.ContainerStopped
	contCalled := make(chan struct{})
	client.EXPECT().SubmitContainerStateChange(notReplaced).Do(func(interface{}) { contCalled <- struct{}{} })
	client.EXPECT().SubmitContainerStateChange(sortaRedundant).Do(func(interface{}) { contCalled <- struct{}{} })

	handler.AddContainerEvent(notReplaced, client)
	handler.AddContainerEvent(sortaRedundant, client)
	<-contCalled
	<-contCalled
}

func TestSendsEventsTaskDifferences(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	client := mock_api.NewMockECSClient(ctrl)

	handler := NewTaskHandler()

	// Test task event replacement doesn't happen
	notReplacedCont := contEvent("notreplaced2")
	sortaRedundantCont := contEvent("notreplaced2")
	sortaRedundantCont.Status = api.ContainerStopped
	notReplacedTask := taskEvent("notreplaced")
	sortaRedundantTask := taskEvent("notreplaced2")
	sortaRedundantTask.Status = api.TaskStopped

	wait := &sync.WaitGroup{}
	wait.Add(4)
	client.EXPECT().SubmitContainerStateChange(notReplacedCont).Do(func(interface{}) { wait.Done() })
	client.EXPECT().SubmitContainerStateChange(sortaRedundantCont).Do(func(interface{}) { wait.Done() })
	client.EXPECT().SubmitTaskStateChange(notReplacedTask).Do(func(interface{}) { wait.Done() })
	client.EXPECT().SubmitTaskStateChange(sortaRedundantTask).Do(func(interface{}) { wait.Done() })

	handler.AddContainerEvent(notReplacedCont, client)
	handler.AddTaskEvent(notReplacedTask, client)
	handler.AddContainerEvent(sortaRedundantCont, client)
	handler.AddTaskEvent(sortaRedundantTask, client)

	wait.Wait()
}

func TestSendsEventsDedupe(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	client := mock_api.NewMockECSClient(ctrl)

	handler := NewTaskHandler()

	// Verify that a task doesn't get sent if we already have 'sent' it
	task1 := taskEvent("alreadySent")
	task1.Task.SetSentStatus(api.TaskRunning)
	cont1 := contEvent("alreadySent")
	cont1.Container.SetSentStatus(api.ContainerRunning)

	handler.AddContainerEvent(cont1, client)
	handler.AddTaskEvent(task1, client)

	task2 := taskEvent("containerSent")
	task2.Task.SetSentStatus(api.TaskStatusNone)
	cont2 := contEvent("containerSent")
	cont2.Container.SetSentStatus(api.ContainerRunning)

	// Expect to send a task status but not a container status
	called := make(chan struct{})
	client.EXPECT().SubmitTaskStateChange(task2).Do(func(interface{}) { called <- struct{}{} })

	handler.AddContainerEvent(cont2, client)
	handler.AddTaskEvent(task2, client)

	<-called
	time.Sleep(5 * time.Millisecond)
}

func TestShouldBeSent(t *testing.T) {
	sendableEvent := newSendableContainerEvent(api.ContainerStateChange{
		Status: api.ContainerStopped,
	})

	if sendableEvent.taskShouldBeSent() {
		t.Error("Container event should not be sent as a task")
	}

	if !sendableEvent.containerShouldBeSent() {
		t.Error("Container should be sent if it's the first try")
	}
}
<commit_msg>eventhandler: fix racy test<commit_after>// Copyright 2014-2017 Amazon.com, Inc. or its affiliates. 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. A copy of the
// License is located at
//
//	http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file 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 eventhandler

import (
	"errors"
	"strconv"
	"sync"
	"testing"
	"time"

	"github.com/aws/amazon-ecs-agent/agent/api"
	"github.com/aws/amazon-ecs-agent/agent/api/mocks"
	"github.com/aws/amazon-ecs-agent/agent/utils"
	"github.com/golang/mock/gomock"
	"github.com/stretchr/testify/assert"
)

func contEvent(arn string) api.ContainerStateChange {
	return api.ContainerStateChange{TaskArn: arn, ContainerName: "containerName", Status: api.ContainerRunning, Container: &api.Container{}}
}
func taskEvent(arn string) api.TaskStateChange {
	return api.TaskStateChange{TaskArn: arn, Status: api.TaskRunning, Task: &api.Task{}}
}

func TestSendsEventsOneContainer(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	client := mock_api.NewMockECSClient(ctrl)

	handler := NewTaskHandler()

	var wg sync.WaitGroup
	wg.Add(3)

	// Trivial: one container, no errors
	contEvent1 := contEvent("1")
	contEvent2 := contEvent("2")
	taskEvent2 := taskEvent("2")

	client.EXPECT().SubmitContainerStateChange(contEvent1).Do(func(interface{}) { wg.Done() })
	client.EXPECT().SubmitContainerStateChange(contEvent2).Do(func(interface{}) { wg.Done() })
	client.EXPECT().SubmitTaskStateChange(taskEvent2).Do(func(interface{}) { wg.Done() })

	handler.AddContainerEvent(contEvent1, client)
	handler.AddContainerEvent(contEvent2, client)
	handler.AddTaskEvent(taskEvent2, client)

	wg.Wait()

}

func TestSendsEventsOneEventRetries(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	client := mock_api.NewMockECSClient(ctrl)

	handler := NewTaskHandler()

	retriable := utils.NewRetriableError(utils.NewRetriable(true), errors.New("test"))
	contCalled := make(chan struct{})
	contEvent1 := contEvent("1")

	gomock.InOrder(
		client.EXPECT().SubmitContainerStateChange(contEvent1).Return(retriable).Do(func(interface{}) { contCalled <- struct{}{} }),
		client.EXPECT().SubmitContainerStateChange(contEvent1).Return(nil).Do(func(interface{}) { contCalled <- struct{}{} }),
	)

	handler.AddContainerEvent(contEvent1, client)

	<-contCalled
	<-contCalled
}

func TestSendsEventsConcurrentLimit(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	client := mock_api.NewMockECSClient(ctrl)

	handler := NewTaskHandler()

	contCalled := make(chan struct{}, concurrentEventCalls+1)
	completeStateChange := make(chan bool, concurrentEventCalls+1)
	count := 0
	countLock := &sync.Mutex{}
	client.EXPECT().SubmitContainerStateChange(gomock.Any()).Times(concurrentEventCalls + 1).Do(func(interface{}) {
		countLock.Lock()
		count++
		countLock.Unlock()
		<-completeStateChange
		contCalled <- struct{}{}
	})
	// Test concurrency; ensure it doesn't attempt to send more than
	// concurrentEventCalls at once
	// Put on N+1 events
	for i := 0; i < concurrentEventCalls+1; i++ {
		handler.AddContainerEvent(contEvent("concurrent_"+strconv.Itoa(i)), client)
	}
	time.Sleep(10 * time.Millisecond)

	// N events should be waiting for potential errors since we havent started completing state changes
	assert.Equal(t, concurrentEventCalls, count, "Too many event calls got through concurrently")
	// Let one state change finish
	completeStateChange <- true
	<-contCalled
	time.Sleep(10 * time.Millisecond)

	assert.Equal(t, concurrentEventCalls+1, count, "Another concurrent call didn't start when expected")

	// ensure the remaining requests are completed
	for i := 0; i < concurrentEventCalls; i++ {
		completeStateChange <- true
		<-contCalled
	}
	time.Sleep(5 * time.Millisecond)
	assert.Equal(t, concurrentEventCalls+1, count, "Extra concurrent calls appeared from nowhere")
}

func TestSendsEventsContainerDifferences(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	client := mock_api.NewMockECSClient(ctrl)

	handler := NewTaskHandler()

	// Test container event replacement doesn't happen
	notReplaced := contEvent("notreplaced1")
	sortaRedundant := contEvent("notreplaced1")
	sortaRedundant.Status = api.ContainerStopped
	contCalled := make(chan struct{})
	client.EXPECT().SubmitContainerStateChange(notReplaced).Do(func(interface{}) { contCalled <- struct{}{} })
	client.EXPECT().SubmitContainerStateChange(sortaRedundant).Do(func(interface{}) { contCalled <- struct{}{} })

	handler.AddContainerEvent(notReplaced, client)
	handler.AddContainerEvent(sortaRedundant, client)
	<-contCalled
	<-contCalled
}

func TestSendsEventsTaskDifferences(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	client := mock_api.NewMockECSClient(ctrl)

	handler := NewTaskHandler()

	// Test task event replacement doesn't happen
	notReplacedCont := contEvent("notreplaced2")
	sortaRedundantCont := contEvent("notreplaced2")
	sortaRedundantCont.Status = api.ContainerStopped
	notReplacedTask := taskEvent("notreplaced")
	sortaRedundantTask := taskEvent("notreplaced2")
	sortaRedundantTask.Status = api.TaskStopped

	wait := &sync.WaitGroup{}
	wait.Add(4)
	client.EXPECT().SubmitContainerStateChange(notReplacedCont).Do(func(interface{}) { wait.Done() })
	client.EXPECT().SubmitContainerStateChange(sortaRedundantCont).Do(func(interface{}) { wait.Done() })
	client.EXPECT().SubmitTaskStateChange(notReplacedTask).Do(func(interface{}) { wait.Done() })
	client.EXPECT().SubmitTaskStateChange(sortaRedundantTask).Do(func(interface{}) { wait.Done() })

	handler.AddContainerEvent(notReplacedCont, client)
	handler.AddTaskEvent(notReplacedTask, client)
	handler.AddContainerEvent(sortaRedundantCont, client)
	handler.AddTaskEvent(sortaRedundantTask, client)

	wait.Wait()
}

func TestSendsEventsDedupe(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	client := mock_api.NewMockECSClient(ctrl)

	handler := NewTaskHandler()

	// Verify that a task doesn't get sent if we already have 'sent' it
	task1 := taskEvent("alreadySent")
	task1.Task.SetSentStatus(api.TaskRunning)
	cont1 := contEvent("alreadySent")
	cont1.Container.SetSentStatus(api.ContainerRunning)

	handler.AddContainerEvent(cont1, client)
	handler.AddTaskEvent(task1, client)

	task2 := taskEvent("containerSent")
	task2.Task.SetSentStatus(api.TaskStatusNone)
	cont2 := contEvent("containerSent")
	cont2.Container.SetSentStatus(api.ContainerRunning)

	// Expect to send a task status but not a container status
	called := make(chan struct{})
	client.EXPECT().SubmitTaskStateChange(task2).Do(func(interface{}) { called <- struct{}{} })

	handler.AddContainerEvent(cont2, client)
	handler.AddTaskEvent(task2, client)

	<-called
	time.Sleep(5 * time.Millisecond)
}

func TestShouldBeSent(t *testing.T) {
	sendableEvent := newSendableContainerEvent(api.ContainerStateChange{
		Status: api.ContainerStopped,
	})

	if sendableEvent.taskShouldBeSent() {
		t.Error("Container event should not be sent as a task")
	}

	if !sendableEvent.containerShouldBeSent() {
		t.Error("Container should be sent if it's the first try")
	}
}
<|endoftext|>


<commit_before>/*
Package airbrakehandler provides Airbrake/Errbit integration.

Create a new handler as you would create a gobrake.Notifier:

	projectID  := int64(1)
	projectKey := "key"

	handler := airbrakehandler.New(projectID, projectKey)

If you need access to the underlying Notifier instance (or need more advanced construction),
you can create the handler from a notifier directly:

	projectID  := int64(1)
	projectKey := "key"

	notifier := gobrake.NewNotifier(projectID, projectKey)
	handler := airbrakehandler.NewFromNotifier(notifier)

By default Gobrake sends errors asynchronously and expects to be closed before the program finishes:

	func main() {
		defer handler.Close()
	}

If you want to Flush notices you can do it as you would with Gobrake's notifier
or you can configure the handler to send notices synchronously:

	handler := airbrakehandler.NewFromNotifier(notifier, airbrakehandler.SendSynchronously(true))
*/
package airbrakehandler

import (
	"github.com/airbrake/gobrake"
	"github.com/goph/emperror"
	"github.com/goph/emperror/httperr"
	"github.com/goph/emperror/internal/keyvals"
)

// Option configures a logger instance.
type Option interface {
	apply(*Handler)
}

// SendSynchronously configures the handler to send notices synchronously.
type SendSynchronously bool

func (o SendSynchronously) apply(l *Handler) {
	l.sendSynchronously = bool(o)
}

// Handler is responsible for sending errors to Airbrake/Errbit.
type Handler struct {
	notifier *gobrake.Notifier

	sendSynchronously bool
}

// New creates a new Airbrake handler.
func New(projectID int64, projectKey string, opts ...Option) *Handler {
	h := &Handler{
		notifier: gobrake.NewNotifier(projectID, projectKey),
	}

	for _, o := range opts {
		o.apply(h)
	}

	return h
}

// NewFromNotifier creates a new Airbrake handler from a notifier instance.
func NewFromNotifier(notifier *gobrake.Notifier, opts ...Option) *Handler {
	h := &Handler{
		notifier: notifier,
	}

	for _, o := range opts {
		o.apply(h)
	}

	return h
}

// Handle calls the underlying Airbrake notifier.
func (h *Handler) Handle(err error) {
	// Get HTTP request (if any)
	req, _ := httperr.HTTPRequest(err)

	// Expose the stackTracer interface on the outer error (if there is stack trace in the error)
	err = emperror.ExposeStackTrace(err)

	notice := h.notifier.Notice(err, req, 1)

	// Extract context from the error and attach it to the notice
	if kvs := emperror.Context(err); len(kvs) > 0 {
		notice.Params = keyvals.ToMap(kvs)
	}

	if h.sendSynchronously {
		_, _ = h.notifier.SendNotice(notice)
	} else {
		h.notifier.SendNoticeAsync(notice)
	}
}

// Close closes the underlying Airbrake instance.
func (h *Handler) Close() error {
	return h.notifier.Close()
}
<commit_msg>Add constructor for async error handling<commit_after>/*
Package airbrakehandler provides Airbrake/Errbit integration.

Create a new handler as you would create a gobrake.Notifier:

	projectID  := int64(1)
	projectKey := "key"

	handler := airbrakehandler.New(projectID, projectKey)

If you need access to the underlying Notifier instance (or need more advanced construction),
you can create the handler from a notifier directly:

	projectID  := int64(1)
	projectKey := "key"

	notifier := gobrake.NewNotifier(projectID, projectKey)
	handler := airbrakehandler.NewFromNotifier(notifier)

By default Gobrake sends errors asynchronously and expects to be closed before the program finishes:

	func main() {
		defer handler.Close()
	}

If you want to Flush notices you can do it as you would with Gobrake's notifier
or you can configure the handler to send notices synchronously:

	handler := airbrakehandler.NewFromNotifier(notifier, airbrakehandler.SendSynchronously(true))
*/
package airbrakehandler

import (
	"github.com/airbrake/gobrake"
	"github.com/goph/emperror"
	"github.com/goph/emperror/httperr"
	"github.com/goph/emperror/internal/keyvals"
)

// Option configures a logger instance.
type Option interface {
	apply(*Handler)
}

// SendSynchronously configures the handler to send notices synchronously.
type SendSynchronously bool

func (o SendSynchronously) apply(l *Handler) {
	l.sendAsynchronously = bool(o)
}

// Handler is responsible for sending errors to Airbrake/Errbit.
type Handler struct {
	notifier *gobrake.Notifier

	sendAsynchronously bool
}

// New creates a new Airbrake handler.
func New(projectID int64, projectKey string, opts ...Option) *Handler {
	return NewFromNotifier(gobrake.NewNotifier(projectID, projectKey), opts...)
}

// NewAsync creates a new Airbrake handler that sends errors asynchronously.
func NewAsync(projectID int64, projectKey string, opts ...Option) *Handler {
	h := New(projectID, projectKey, opts...)

	h.sendAsynchronously = true

	return h
}

// NewFromNotifier creates a new Airbrake handler from a notifier instance.
func NewFromNotifier(notifier *gobrake.Notifier, opts ...Option) *Handler {
	h := &Handler{
		notifier: notifier,
	}

	for _, o := range opts {
		o.apply(h)
	}

	return h
}

// NewAsyncFromNotifier creates a new Airbrake handler from a notifier instance that sends errors asynchronously.
func NewAsyncFromNotifier(notifier *gobrake.Notifier, opts ...Option) *Handler {
	h := NewFromNotifier(notifier, opts...)

	h.sendAsynchronously = true

	return h
}

// Handle calls the underlying Airbrake notifier.
func (h *Handler) Handle(err error) {
	// Get HTTP request (if any)
	req, _ := httperr.HTTPRequest(err)

	// Expose the stackTracer interface on the outer error (if there is stack trace in the error)
	err = emperror.ExposeStackTrace(err)

	notice := h.notifier.Notice(err, req, 1)

	// Extract context from the error and attach it to the notice
	if kvs := emperror.Context(err); len(kvs) > 0 {
		notice.Params = keyvals.ToMap(kvs)
	}

	if h.sendAsynchronously {
		h.notifier.SendNoticeAsync(notice)
	} else {
		_, _ = h.notifier.SendNotice(notice)
	}
}

// Close closes the underlying Airbrake instance.
func (h *Handler) Close() error {
	return h.notifier.Close()
}
<|endoftext|>


<commit_before>// Package launcher configures Lantern to run on system start
package launcher

import (
	"github.com/kardianos/osext"
	"github.com/luisiturrios/gowin"

	"github.com/getlantern/golog"
)

const (
	RunDir = `Software\Microsoft\Windows\CurrentVersion\Run`
)

var (
	log = golog.LoggerFor("launcher")
)

func CreateLaunchFile(autoLaunch bool) {
	var err error

	if autoLaunch {
		lanternPath, err := osext.Executable()
		if err != nil {
			log.Errorf("Could not get Lantern directory path: %q", err)
			return
		}
		err = gowin.WriteStringReg("HKCU", RunDir, "value", lanternPath)
		if err != nil {
			log.Errorf("Error inserting Lantern auto-start registry key: %q", err)
		}
	} else {
		err = gowin.DeleteKey("HKCU", RunDir, "value")
		if err != nil {
			log.Errorf("Error removing Lantern auto-start registry key: %q", err)
		}
	}
}
<commit_msg>runDir should not be a public constant but a local one.<commit_after>// Package launcher configures Lantern to run on system start
package launcher

import (
	"github.com/kardianos/osext"
	"github.com/luisiturrios/gowin"

	"github.com/getlantern/golog"
)

const (
	runDir = `Software\Microsoft\Windows\CurrentVersion\Run`
)

var (
	log = golog.LoggerFor("launcher")
)

func CreateLaunchFile(autoLaunch bool) {
	var err error

	if autoLaunch {
		lanternPath, err := osext.Executable()
		if err != nil {
			log.Errorf("Could not get Lantern directory path: %q", err)
			return
		}
		err = gowin.WriteStringReg("HKCU", runDir, "value", lanternPath)
		if err != nil {
			log.Errorf("Error inserting Lantern auto-start registry key: %q", err)
		}
	} else {
		err = gowin.DeleteKey("HKCU", runDir, "value")
		if err != nil {
			log.Errorf("Error removing Lantern auto-start registry key: %q", err)
		}
	}
}
<|endoftext|>


<commit_before>package gpio

import (
	"unsafe"
)

// Pin represents one phisical pin (specific pin in specific port).
type Pin struct {
	h uintptr
}

// SelPin returns pin for its compact representation.
func SelPin(psel int8) Pin {
	ptr := uintptr(unsafe.Pointer(PortN(int(psel >> 5))))
	return Pin{ptr | uintptr(psel)&0x7F}
}

// IsValid reports whether p represents a valid pin.
func (p Pin) IsValid() bool {
	return p.h != 0
}

// Port returns the port where the pin is located.
func (p Pin) Port() *Port {
	return (*Port)(unsafe.Pointer(p.h &^ 0x7F))
}

// Sel returns compact representation of Pin.
func (p Pin) Sel() int8 {
	return int8(p.h & 0x7F)
}

func (p Pin) index() uintptr {
	return p.h & 0x1F
}

// Index returns pin index in the port.
func (p Pin) Index() int {
	return int(p.index())
}

// Setup configures pin.
func (p Pin) Setup(cfg Config) {
	p.Port().SetupPin(p.Index(), cfg)
}

// Config returns current configuration of pin.
func (p Pin) Config() Config {
	return p.Port().PinConfig(p.Index())
}

// Mask returns bitmask that represents the pin.
func (p Pin) Mask() Pins {
	return Pin0 << p.index()
}

// Load returns input value of the pin.
func (p Pin) Load() int {
	return int(p.Port().in.Load()) >> p.index() & 1
}

// LoadOut returns output value of the pin.
func (p Pin) LoadOut() int {
	return int(p.Port().out.Load()) >> p.index() & 1
}

// Set sets output value of the pin to 1 in one atomic operation.
func (p Pin) Set() {
	p.Port().outset.Store(uint32(Pin0) << p.index())
}

// Clear sets output value of the pin to 0 in one atomic operation.
func (p Pin) Clear() {
	p.Port().outclr.Store(uint32(Pin0) << p.index())
}

// Store sets output value of the pin to the least significant bit of val.
func (p Pin) Store(val int) {
	port := p.Port()
	n := p.index()
	if val&1 != 0 {
		port.outset.Store(uint32(Pin0) << n)
	} else {
		port.outclr.Store(uint32(Pin0) << n)
	}
}
<commit_msg>nrf5/hal/gpio: Fix SelPin and Pin.Sel functions.<commit_after>package gpio

import (
	"unsafe"
)

// Pin represents one phisical pin (specific pin in specific port).
type Pin struct {
	h uintptr
}

// SelPin returns pin for its compact representation.
func SelPin(psel int8) Pin {
	if psel < 0 {
		return Pin{}
	}
	ptr := uintptr(unsafe.Pointer(PortN(int(psel >> 5))))
	return Pin{ptr | uintptr(psel)&0x7F}
}

// IsValid reports whether p represents a valid pin.
func (p Pin) IsValid() bool {
	return p.h != 0
}

// Port returns the port where the pin is located.
func (p Pin) Port() *Port {
	return (*Port)(unsafe.Pointer(p.h &^ 0x7F))
}

// Sel returns compact representation of Pin.
func (p Pin) Sel() int8 {
	if p.h == 0 {
		return -1
	}
	return int8(p.h & 0x7F)
}

func (p Pin) index() uintptr {
	return p.h & 0x1F
}

// Index returns pin index in the port.
func (p Pin) Index() int {
	return int(p.index())
}

// Setup configures pin.
func (p Pin) Setup(cfg Config) {
	p.Port().SetupPin(p.Index(), cfg)
}

// Config returns current configuration of pin.
func (p Pin) Config() Config {
	return p.Port().PinConfig(p.Index())
}

// Mask returns bitmask that represents the pin.
func (p Pin) Mask() Pins {
	return Pin0 << p.index()
}

// Load returns input value of the pin.
func (p Pin) Load() int {
	return int(p.Port().in.Load()) >> p.index() & 1
}

// LoadOut returns output value of the pin.
func (p Pin) LoadOut() int {
	return int(p.Port().out.Load()) >> p.index() & 1
}

// Set sets output value of the pin to 1 in one atomic operation.
func (p Pin) Set() {
	p.Port().outset.Store(uint32(Pin0) << p.index())
}

// Clear sets output value of the pin to 0 in one atomic operation.
func (p Pin) Clear() {
	p.Port().outclr.Store(uint32(Pin0) << p.index())
}

// Store sets output value of the pin to the least significant bit of val.
func (p Pin) Store(val int) {
	port := p.Port()
	n := p.index()
	if val&1 != 0 {
		port.outset.Store(uint32(Pin0) << n)
	} else {
		port.outclr.Store(uint32(Pin0) << n)
	}
}
<|endoftext|>


<commit_before>package endtoend_test

import (
	"flag"
	"launchpad.net/goamz/aws"
	"launchpad.net/goamz/ec2"
	. "launchpad.net/gocheck"
	"testing"
	"time"
)

func Test(t *testing.T) { TestingT(t) }

type S struct{}

var _ = Suite(&S{})

var flagDesc = "enable end-to-end tests that hits gandalf's server to try it's api, it's needed to configure the GANDALF_SERVER environment variable"
var enableSuite = flag.Bool("endtoend", false, flagDesc)

func (s *S) SetUpSuite(c *C) {
	if !*enableSuite {
		c.Skip("skipping end-to-end suite, use -endtoend to enable")
	}
}
func (s *S) TestTrueIsTrue(c *C) {
	c.Assert(true, Equals, true)
}
<commit_msg>end-to-end: removed unused imports<commit_after>package endtoend_test

import (
	"flag"
	. "launchpad.net/gocheck"
	"testing"
)

func Test(t *testing.T) { TestingT(t) }

type S struct{}

var _ = Suite(&S{})

var flagDesc = "enable end-to-end tests that hits gandalf's server to try it's api, it's needed to configure the GANDALF_SERVER environment variable"
var enableSuite = flag.Bool("endtoend", false, flagDesc)

func (s *S) SetUpSuite(c *C) {
	if !*enableSuite {
		c.Skip("skipping end-to-end suite, use -endtoend to enable")
	}
}

func (s *S) TestCreatesUser(c *C) {
	c.Assert(true, Equals, true)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"net/http"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
	"sync"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/prometheus/log"
	"github.com/prometheus/procfs"
)

var (
	// command line parameters
	listenAddress     = flag.String("web.listen-address", "9256", "Address on which to expose metrics and web interface.")
	metricsPath       = flag.String("web.telemetry-path", "/metrics", "Path under which to expose metrics.")
	processNamePrefix = flag.String("fluentd.process_name_prefix", "", "fluentd's process_name prefix.")

	processNameRegex       = regexp.MustCompile(`\s/usr/sbin/td-agent\s*`)
	tdAgentPathRegex       = regexp.MustCompile("\\s" + strings.Replace(tdAgentLaunchCommand, " ", "\\s", -1) + "(.+)?\\s*")
	configFileNameRegex    = regexp.MustCompile(`\s(-c|--config)\s.*/(.+)\.conf\s*`)
	processNamePrefixRegex = regexp.MustCompile(`\sworker:(.+)?\s*`)
)

const (
	// Can't use '-' for the metric name
	namespace            = "td_agent"
	tdAgentLaunchCommand = "/opt/td-agent/embedded/bin/ruby /usr/sbin/td-agent "
)

type Exporter struct {
	mutex sync.RWMutex

	scrapeFailures prometheus.Counter
	cpuTime        *prometheus.CounterVec
	virtualMemory  *prometheus.GaugeVec
	residentMemory *prometheus.GaugeVec
	tdAgentUp      prometheus.Gauge
}

func NewExporter() *Exporter {
	return &Exporter{
		scrapeFailures: prometheus.NewCounter(prometheus.CounterOpts{
			Namespace: namespace,
			Name:      "exporter_scrape_failures_total",
			Help:      "Number of errors while scraping td-agent.",
		}),
		cpuTime: prometheus.NewCounterVec(
			prometheus.CounterOpts{
				Namespace: namespace,
				Name:      "cpu_time",
				Help:      "td-agent cpu time",
			},
			[]string{"id"},
		),
		virtualMemory: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: namespace,
				Name:      "virtual_memory_usage",
				Help:      "td-agent virtual memory usage",
			},
			[]string{"id"},
		),
		residentMemory: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: namespace,
				Name:      "resident_memory_usage",
				Help:      "td-agent resident memory usage",
			},
			[]string{"id"},
		),
		tdAgentUp: prometheus.NewGauge(prometheus.GaugeOpts{
			Namespace: namespace,
			Name:      "up",
			Help:      "the td-agent processes",
		}),
	}
}

func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {
	e.scrapeFailures.Describe(ch)
	e.cpuTime.Describe(ch)
	e.virtualMemory.Describe(ch)
	e.residentMemory.Describe(ch)
	e.tdAgentUp.Describe(ch)
}

func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
	// To protect metrics from concurrent collects.
	e.mutex.Lock()
	defer e.mutex.Unlock()
	e.collect(ch)
}

func (e *Exporter) collect(ch chan<- prometheus.Metric) {
	e.tdAgentUp.Set(0)

	ids, err := e.resolveTdAgentId()
	if err != nil {
		e.scrapeFailures.Inc()
		e.scrapeFailures.Collect(ch)
		return
	}

	log.Debugf("td-agent ids = %v", ids)

	processes := 0
	for id, command := range ids {
		log.Debugf("td-agent id = %s", id)

		procStat, err := e.getProcStat(id, command)
		if err != nil {
			e.scrapeFailures.Inc()
			e.scrapeFailures.Collect(ch)
			continue
		}

		e.cpuTime.WithLabelValues(id).Add(procStat.CPUTime())
		e.virtualMemory.WithLabelValues(id).Set(float64(procStat.VirtualMemory()))
		e.residentMemory.WithLabelValues(id).Set(float64(procStat.ResidentMemory()))

		processes++
	}

	e.tdAgentUp.Set(float64(processes))

	e.cpuTime.Collect(ch)
	e.virtualMemory.Collect(ch)
	e.residentMemory.Collect(ch)
	e.tdAgentUp.Collect(ch)
}

func (e *Exporter) resolveTdAgentId() (map[string]string, error) {
	lines, err := e.execPsCommand()
	if err != nil {
		return nil, err
	}
	filtered := e.filter(strings.Split(lines, "\n"))

	log.Debugf("filtered = %s", filtered)

	var ids map[string]string
	if *processNamePrefix == "" {
		ids = e.resolveTdAgentIdWithConfigFileName(filtered)
	} else {
		ids, err = e.resolveTdAgentIdWithProcessNamePrefix(filtered)
		if err != nil {
			return nil, err
		}
	}

	return ids, nil
}

func (e *Exporter) execPsCommand() (string, error) {
	ps, err := exec.Command("ps", "-C", "ruby", "-f").Output()
	if err != nil {
		log.Error(err)
		return "", err
	}

	return string(ps), nil
}

func (e *Exporter) filter(lines []string) []string {
	var filtered []string
	for _, line := range lines {
		log.Debugf("line = %s", line)

		if *processNamePrefix == "" {
			if processNameRegex.MatchString(line) {
				filtered = append(filtered, line)
			}
		} else {
			groups := processNamePrefixRegex.FindStringSubmatch(line)
			if len(groups) > 0 {
				filtered = append(filtered, line)
			}
		}
	}

	return filtered
}

func (e *Exporter) resolveTdAgentIdWithConfigFileName(lines []string) map[string]string {
	ids := make(map[string]string)
	for _, line := range lines {
		log.Debugf("resolveTdAgentIdWithConfigFileName line = %s", line)

		groupsKey := configFileNameRegex.FindStringSubmatch(line)
		var key string
		if len(groupsKey) == 0 {
			// doesn't use config file
			key = "default"
		} else {
			key = strings.Trim(groupsKey[2], " ")
		}

		var value string
		groupsValue := tdAgentPathRegex.FindStringSubmatch(line)
		if len(groupsValue) > 0 {
			value = tdAgentLaunchCommand + groupsValue[1]
		} else {
			value = ""
		}

		if _, exist := ids[key]; !exist {
			ids[key] = value
		}
	}

	return ids
}

func (e *Exporter) resolveTdAgentIdWithProcessNamePrefix(lines []string) (map[string]string, error) {
	ids := make(map[string]string)
	for _, line := range lines {
		log.Debugf("resolveTdAgentIdWithProcessNamePrefix line = %s", line)

		groupsKey := processNamePrefixRegex.FindStringSubmatch(line)
		if len(groupsKey) == 0 {
			err := fmt.Errorf("Process not found. fluentd.process_name_prefix = `%s`", processNamePrefixRegex)
			log.Error(err)
			return nil, err
		}

		key := strings.Trim(groupsKey[1], " ")

		log.Debugf("key = %s", key)

		var value string
		groupsValue := tdAgentPathRegex.FindStringSubmatch(line)
		if len(groupsValue) > 0 {
			value = tdAgentLaunchCommand + groupsValue[1]
		} else {
			value = ""
		}

		if _, exist := ids[key]; !exist {
			ids[key] = value
		}
	}

	return ids, nil
}

func (e *Exporter) getProcStat(tdAgentId string, tdAgentCommand string) (procfs.ProcStat, error) {
	procfsPath := procfs.DefaultMountPoint
	fs, err := procfs.NewFS(procfsPath)
	if err != nil {
		log.Error(err)
		return procfs.ProcStat{}, err
	}

	targetPid, err := e.resolveTargetPid(tdAgentId, tdAgentCommand)
	if err != nil {
		return procfs.ProcStat{}, err
	}

	log.Debugf("targetPid = %v", targetPid)

	proc, err := fs.NewProc(targetPid)
	if err != nil {
		log.Error(err)
		return procfs.ProcStat{}, err
	}

	procStat, err := proc.NewStat()
	if err != nil {
		log.Error(err)
		return procfs.ProcStat{}, err
	}

	return procStat, nil
}

func (e *Exporter) resolveTargetPid(tdAgentId string, tdAgentCommand string) (int, error) {
	var pgrepArg string

	if *processNamePrefix == "" {
		pgrepArg = tdAgentCommand
	} else {
		pgrepArg = ":" + tdAgentId
	}

	log.Debugf("pgrep arg = [%s]", pgrepArg)

	out, err := exec.Command("pgrep", "-n", "-f", strings.Replace(pgrepArg, " ", "\\ ", -1)).Output()
	if err != nil {
		log.Error(err)
		return 0, err
	}

	targetPid, err := strconv.Atoi(strings.TrimSpace(string(out)))
	if err != nil {
		log.Error(err)
		return 0, err
	}

	return targetPid, nil
}

func main() {
	flag.Parse()

	exporter := NewExporter()
	prometheus.MustRegister(exporter)

	log.Infof("Starting Server: %s", *listenAddress)
	http.Handle(*metricsPath, promhttp.Handler())
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`
		<title>td-agent Exporter</title>

td-agent Exporter v` + version + `

<a href="` + *metricsPath + `">Metrics

		`))
	})

	log.Fatal(http.ListenAndServe(":"+*listenAddress, nil))
}
<commit_msg>fix cpu_time<commit_after>package main

import (
	"flag"
	"fmt"
	"net/http"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
	"sync"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/prometheus/log"
	"github.com/prometheus/procfs"
)

var (
	// command line parameters
	listenAddress     = flag.String("web.listen-address", "9256", "Address on which to expose metrics and web interface.")
	metricsPath       = flag.String("web.telemetry-path", "/metrics", "Path under which to expose metrics.")
	processNamePrefix = flag.String("fluentd.process_name_prefix", "", "fluentd's process_name prefix.")

	processNameRegex       = regexp.MustCompile(`\s/usr/sbin/td-agent\s*`)
	tdAgentPathRegex       = regexp.MustCompile("\\s" + strings.Replace(tdAgentLaunchCommand, " ", "\\s", -1) + "(.+)?\\s*")
	configFileNameRegex    = regexp.MustCompile(`\s(-c|--config)\s.*/(.+)\.conf\s*`)
	processNamePrefixRegex = regexp.MustCompile(`\sworker:(.+)?\s*`)
)

const (
	// Can't use '-' for the metric name
	namespace            = "td_agent"
	tdAgentLaunchCommand = "/opt/td-agent/embedded/bin/ruby /usr/sbin/td-agent "
)

type Exporter struct {
	mutex sync.RWMutex

	scrapeFailures prometheus.Counter
	cpuTime        *prometheus.GaugeVec
	virtualMemory  *prometheus.GaugeVec
	residentMemory *prometheus.GaugeVec
	tdAgentUp      prometheus.Gauge
}

func NewExporter() *Exporter {
	return &Exporter{
		scrapeFailures: prometheus.NewCounter(prometheus.CounterOpts{
			Namespace: namespace,
			Name:      "exporter_scrape_failures_total",
			Help:      "Number of errors while scraping td-agent.",
		}),
		cpuTime: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: namespace,
				Name:      "cpu_time",
				Help:      "td-agent cpu time",
			},
			[]string{"id"},
		),
		virtualMemory: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: namespace,
				Name:      "virtual_memory_usage",
				Help:      "td-agent virtual memory usage",
			},
			[]string{"id"},
		),
		residentMemory: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: namespace,
				Name:      "resident_memory_usage",
				Help:      "td-agent resident memory usage",
			},
			[]string{"id"},
		),
		tdAgentUp: prometheus.NewGauge(prometheus.GaugeOpts{
			Namespace: namespace,
			Name:      "up",
			Help:      "the td-agent processes",
		}),
	}
}

func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {
	e.scrapeFailures.Describe(ch)
	e.cpuTime.Describe(ch)
	e.virtualMemory.Describe(ch)
	e.residentMemory.Describe(ch)
	e.tdAgentUp.Describe(ch)
}

func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
	// To protect metrics from concurrent collects.
	e.mutex.Lock()
	defer e.mutex.Unlock()
	e.collect(ch)
}

func (e *Exporter) collect(ch chan<- prometheus.Metric) {
	e.tdAgentUp.Set(0)

	ids, err := e.resolveTdAgentId()
	if err != nil {
		e.scrapeFailures.Inc()
		e.scrapeFailures.Collect(ch)
		return
	}

	log.Debugf("td-agent ids = %v", ids)

	processes := 0
	for id, command := range ids {
		log.Debugf("td-agent id = %s", id)

		procStat, err := e.getProcStat(id, command)
		if err != nil {
			e.scrapeFailures.Inc()
			e.scrapeFailures.Collect(ch)
			continue
		}

		e.cpuTime.WithLabelValues(id).Set(procStat.CPUTime())
		e.virtualMemory.WithLabelValues(id).Set(float64(procStat.VirtualMemory()))
		e.residentMemory.WithLabelValues(id).Set(float64(procStat.ResidentMemory()))

		processes++
	}

	e.tdAgentUp.Set(float64(processes))

	e.cpuTime.Collect(ch)
	e.virtualMemory.Collect(ch)
	e.residentMemory.Collect(ch)
	e.tdAgentUp.Collect(ch)
}

func (e *Exporter) resolveTdAgentId() (map[string]string, error) {
	lines, err := e.execPsCommand()
	if err != nil {
		return nil, err
	}
	filtered := e.filter(strings.Split(lines, "\n"))

	log.Debugf("filtered = %s", filtered)

	var ids map[string]string
	if *processNamePrefix == "" {
		ids = e.resolveTdAgentIdWithConfigFileName(filtered)
	} else {
		ids, err = e.resolveTdAgentIdWithProcessNamePrefix(filtered)
		if err != nil {
			return nil, err
		}
	}

	return ids, nil
}

func (e *Exporter) execPsCommand() (string, error) {
	ps, err := exec.Command("ps", "-C", "ruby", "-f").Output()
	if err != nil {
		log.Error(err)
		return "", err
	}

	return string(ps), nil
}

func (e *Exporter) filter(lines []string) []string {
	var filtered []string
	for _, line := range lines {
		log.Debugf("line = %s", line)

		if *processNamePrefix == "" {
			if processNameRegex.MatchString(line) {
				filtered = append(filtered, line)
			}
		} else {
			groups := processNamePrefixRegex.FindStringSubmatch(line)
			if len(groups) > 0 {
				filtered = append(filtered, line)
			}
		}
	}

	return filtered
}

func (e *Exporter) resolveTdAgentIdWithConfigFileName(lines []string) map[string]string {
	ids := make(map[string]string)
	for _, line := range lines {
		log.Debugf("resolveTdAgentIdWithConfigFileName line = %s", line)

		groupsKey := configFileNameRegex.FindStringSubmatch(line)
		var key string
		if len(groupsKey) == 0 {
			// doesn't use config file
			key = "default"
		} else {
			key = strings.Trim(groupsKey[2], " ")
		}

		var value string
		groupsValue := tdAgentPathRegex.FindStringSubmatch(line)
		if len(groupsValue) > 0 {
			value = tdAgentLaunchCommand + groupsValue[1]
		} else {
			value = ""
		}

		if _, exist := ids[key]; !exist {
			ids[key] = value
		}
	}

	return ids
}

func (e *Exporter) resolveTdAgentIdWithProcessNamePrefix(lines []string) (map[string]string, error) {
	ids := make(map[string]string)
	for _, line := range lines {
		log.Debugf("resolveTdAgentIdWithProcessNamePrefix line = %s", line)

		groupsKey := processNamePrefixRegex.FindStringSubmatch(line)
		if len(groupsKey) == 0 {
			err := fmt.Errorf("Process not found. fluentd.process_name_prefix = `%s`", processNamePrefixRegex)
			log.Error(err)
			return nil, err
		}

		key := strings.Trim(groupsKey[1], " ")

		log.Debugf("key = %s", key)

		var value string
		groupsValue := tdAgentPathRegex.FindStringSubmatch(line)
		if len(groupsValue) > 0 {
			value = tdAgentLaunchCommand + groupsValue[1]
		} else {
			value = ""
		}

		if _, exist := ids[key]; !exist {
			ids[key] = value
		}
	}

	return ids, nil
}

func (e *Exporter) getProcStat(tdAgentId string, tdAgentCommand string) (procfs.ProcStat, error) {
	procfsPath := procfs.DefaultMountPoint
	fs, err := procfs.NewFS(procfsPath)
	if err != nil {
		log.Error(err)
		return procfs.ProcStat{}, err
	}

	targetPid, err := e.resolveTargetPid(tdAgentId, tdAgentCommand)
	if err != nil {
		return procfs.ProcStat{}, err
	}

	log.Debugf("targetPid = %v", targetPid)

	proc, err := fs.NewProc(targetPid)
	if err != nil {
		log.Error(err)
		return procfs.ProcStat{}, err
	}

	procStat, err := proc.NewStat()
	if err != nil {
		log.Error(err)
		return procfs.ProcStat{}, err
	}

	return procStat, nil
}

func (e *Exporter) resolveTargetPid(tdAgentId string, tdAgentCommand string) (int, error) {
	var pgrepArg string

	if *processNamePrefix == "" {
		pgrepArg = tdAgentCommand
	} else {
		pgrepArg = ":" + tdAgentId
	}

	log.Debugf("pgrep arg = [%s]", pgrepArg)

	out, err := exec.Command("pgrep", "-n", "-f", strings.Replace(pgrepArg, " ", "\\ ", -1)).Output()
	if err != nil {
		log.Error(err)
		return 0, err
	}

	targetPid, err := strconv.Atoi(strings.TrimSpace(string(out)))
	if err != nil {
		log.Error(err)
		return 0, err
	}

	return targetPid, nil
}

func main() {
	flag.Parse()

	exporter := NewExporter()
	prometheus.MustRegister(exporter)

	log.Infof("Starting Server: %s", *listenAddress)
	http.Handle(*metricsPath, promhttp.Handler())
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`
		<title>td-agent Exporter</title>

td-agent Exporter v` + version + `

<a href="` + *metricsPath + `">Metrics

		`))
	})

	log.Fatal(http.ListenAndServe(":"+*listenAddress, nil))
}
<|endoftext|>


<commit_before>package main

import (
  "./telegramapi"
  "./pierc"
  "github.com/othree/noemoji"
  "strings"
  "time"
  "fmt"
)

func telegram_to_pierc(updateChannel <-chan *telegramapi.Update, messageChannel chan<- *pierc.Message) {
  for update := range updateChannel {
    message := update.Message
    author := message.From
    name := strings.TrimSpace(strings.Join([]string{author.FirstName, author.LastName}, " "))
    if len(name) > 64 {
      name = name[0:64]
    }
    text := noemoji.Noemojitize(message.Text)
    if len(message.Photo) > 0 {
      text = strings.Join([]string{"[photo]", noemoji.Noemojitize(message.Caption)}, " ")
    }
    if message.Document.FileName != "" {
      document := message.Document
      text = strings.Join([]string{"[file:"+ document.MimeType +"]", noemoji.Noemojitize(document.FileName)}, " ")
    }
    if message.Sticker.FileID != "" {
      sticker := message.Sticker
      val, ok := foxmosaStickerMap[sticker.FileID]
      if ok {
        text = "[foxmosa] " + val
      } else {
        text = "[sticker]"
      }
    }
    tm := time.Unix(update.Message.Date, 0).Format("2006-01-02 15:04:05")
    fmt.Printf("[%d] %s %s: %s\n", update.UpdateID, tm, name, text)
    msg := pierc.Message{name, tm, text}
    messageChannel<-&msg
  }
}

<commit_msg>Not log unrecognize messages<commit_after>package main

import (
  "./telegramapi"
  "./pierc"
  "github.com/othree/noemoji"
  "strings"
  "time"
  "fmt"
)

func telegram_to_pierc(updateChannel <-chan *telegramapi.Update, messageChannel chan<- *pierc.Message) {
  for update := range updateChannel {
    message := update.Message
    author := message.From
    name := strings.TrimSpace(strings.Join([]string{author.FirstName, author.LastName}, " "))
    if len(name) > 64 {
      name = name[0:64]
    }
    text := noemoji.Noemojitize(message.Text)
    if len(message.Photo) > 0 {
      text = strings.Join([]string{"[photo]", noemoji.Noemojitize(message.Caption)}, " ")
    }
    if message.Document.FileName != "" {
      document := message.Document
      text = strings.Join([]string{"[file:"+ document.MimeType +"]", noemoji.Noemojitize(document.FileName)}, " ")
    }
    if message.Sticker.FileID != "" {
      sticker := message.Sticker
      val, ok := foxmosaStickerMap[sticker.FileID]
      if ok {
        text = "[foxmosa] " + val
      } else {
        text = "[sticker]"
      }
    }
    if text != "" {
      tm := time.Unix(update.Message.Date, 0).Format("2006-01-02 15:04:05")
      fmt.Printf("[%d] %s %s: %s\n", update.UpdateID, tm, name, text)
      msg := pierc.Message{name, tm, text}
      messageChannel<-&msg
    }
  }
}

<|endoftext|>


<commit_before>package telemetry

import (
	"fmt"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/joyent/containerpilot/utils"
	"github.com/prometheus/client_golang/prometheus"
)

// A Sensor is a single measurement of the application.
type Sensor struct {
	Namespace string      `mapstructure:"namespace"`
	Subsystem string      `mapstructure:"subsystem"`
	Name      string      `mapstructure:"name"`
	Help      string      `mapstructure:"help"` // help string returned by API
	Type      string      `mapstructure:"type"`
	Poll      int         `mapstructure:"poll"` // time in seconds
	CheckExec interface{} `mapstructure:"check"`
	checkCmd  *exec.Cmd
	collector prometheus.Collector
}

// PollTime implements Pollable for Sensor
// It returns the sensor's poll interval.
func (s Sensor) PollTime() time.Duration {
	return time.Duration(s.Poll) * time.Second
}

// PollAction implements Pollable for Sensor.
func (s *Sensor) PollAction() {
	if metricValue, err := s.observe(); err == nil {
		s.record(metricValue)
	} else {
		log.Errorln(err)
	}
}

// PollStop does nothing in a Sensor
func (s *Sensor) PollStop() {
	// Nothing to do
}

func (s *Sensor) observe() (string, error) {
	defer func() {
		// reset command object because it can't be reused
		s.checkCmd = utils.ArgsToCmd(s.checkCmd.Args)
	}()

	// we'll pass stderr to the container's stderr, but stdout must
	// be "clean" and not have anything other than what we intend
	// to write to our collector.
	s.checkCmd.Stderr = os.Stderr
	if out, err := s.checkCmd.Output(); err != nil {
		return "", err
	} else {
		return string(out[:]), nil
	}
}

func (s Sensor) record(metricValue string) {
	if val, err := strconv.ParseFloat(
		strings.TrimSpace(metricValue), 64); err != nil {
		log.Errorln(err)
	} else {
		// we should use a type switch here but the prometheus collector
		// implementations are themselves interfaces and not structs,
		// so that doesn't work...
		switch {
		case s.Type == "counter":
			s.collector.(prometheus.Counter).Add(val)
		case s.Type == "gauge":
			s.collector.(prometheus.Gauge).Set(val)
		case s.Type == "histogram":
			s.collector.(prometheus.Histogram).Observe(val)
		case s.Type == "summary":
			s.collector.(prometheus.Summary).Observe(val)
		default:
			// ...which is why we end up logging the fall-thru
			log.Errorf("Invalid sensor type: %s\n", s.Type)
		}
	}
}

func NewSensors(raw []interface{}) ([]*Sensor, error) {
	sensors := make([]*Sensor, 0)
	if err := utils.DecodeRaw(raw, &sensors); err != nil {
		return nil, fmt.Errorf("Sensor configuration error: %v", err)
	}
	for _, s := range sensors {
		if check, err := utils.ParseCommandArgs(s.CheckExec); err != nil {
			return nil, err
		} else {
			s.checkCmd = check
		}
		// the prometheus client lib's API here is baffling... they don't expose
		// an interface or embed their Opts type in each of the Opts "subtypes",
		// so we can't share the initialization.
		switch {
		case s.Type == "counter":
			s.collector = prometheus.NewCounter(prometheus.CounterOpts{
				Namespace: s.Namespace,
				Subsystem: s.Subsystem,
				Name:      s.Name,
				Help:      s.Help,
			})
		case s.Type == "gauge":
			s.collector = prometheus.NewGauge(prometheus.GaugeOpts{
				Namespace: s.Namespace,
				Subsystem: s.Subsystem,
				Name:      s.Name,
				Help:      s.Help,
			})
		case s.Type == "histogram":
			s.collector = prometheus.NewHistogram(prometheus.HistogramOpts{
				Namespace: s.Namespace,
				Subsystem: s.Subsystem,
				Name:      s.Name,
				Help:      s.Help,
			})
		case s.Type == "summary":
			s.collector = prometheus.NewSummary(prometheus.SummaryOpts{
				Namespace: s.Namespace,
				Subsystem: s.Subsystem,
				Name:      s.Name,
				Help:      s.Help,
			})
		default:
			return nil, fmt.Errorf("Invalid sensor type: %s\n", s.Type)
		}
		// we're going to unregister before every attempt to register
		// so that we can reload config
		prometheus.Unregister(s.collector)
		if err := prometheus.Register(s.collector); err != nil {
			return nil, err
		}
	}
	return sensors, nil
}
<commit_msg>lint: Fix linting errors in sensors.go<commit_after>package telemetry

import (
	"fmt"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/joyent/containerpilot/utils"
	"github.com/prometheus/client_golang/prometheus"
)

// A Sensor is a single measurement of the application.
type Sensor struct {
	Namespace string      `mapstructure:"namespace"`
	Subsystem string      `mapstructure:"subsystem"`
	Name      string      `mapstructure:"name"`
	Help      string      `mapstructure:"help"` // help string returned by API
	Type      string      `mapstructure:"type"`
	Poll      int         `mapstructure:"poll"` // time in seconds
	CheckExec interface{} `mapstructure:"check"`
	checkCmd  *exec.Cmd
	collector prometheus.Collector
}

// PollTime implements Pollable for Sensor
// It returns the sensor's poll interval.
func (s Sensor) PollTime() time.Duration {
	return time.Duration(s.Poll) * time.Second
}

// PollAction implements Pollable for Sensor.
func (s *Sensor) PollAction() {
	if metricValue, err := s.observe(); err == nil {
		s.record(metricValue)
	} else {
		log.Errorln(err)
	}
}

// PollStop does nothing in a Sensor
func (s *Sensor) PollStop() {
	// Nothing to do
}

func (s *Sensor) observe() (string, error) {
	defer func() {
		// reset command object because it can't be reused
		s.checkCmd = utils.ArgsToCmd(s.checkCmd.Args)
	}()

	// we'll pass stderr to the container's stderr, but stdout must
	// be "clean" and not have anything other than what we intend
	// to write to our collector.
	s.checkCmd.Stderr = os.Stderr
	out, err := s.checkCmd.Output()
	if err != nil {
		return "", err
	}
	return string(out[:]), nil
}

func (s Sensor) record(metricValue string) {
	if val, err := strconv.ParseFloat(
		strings.TrimSpace(metricValue), 64); err != nil {
		log.Errorln(err)
	} else {
		// we should use a type switch here but the prometheus collector
		// implementations are themselves interfaces and not structs,
		// so that doesn't work...
		switch {
		case s.Type == "counter":
			s.collector.(prometheus.Counter).Add(val)
		case s.Type == "gauge":
			s.collector.(prometheus.Gauge).Set(val)
		case s.Type == "histogram":
			s.collector.(prometheus.Histogram).Observe(val)
		case s.Type == "summary":
			s.collector.(prometheus.Summary).Observe(val)
		default:
			// ...which is why we end up logging the fall-thru
			log.Errorf("Invalid sensor type: %s\n", s.Type)
		}
	}
}

// NewSensors creates new sensors from a raw config
func NewSensors(raw []interface{}) ([]*Sensor, error) {
	var sensors []*Sensor
	if err := utils.DecodeRaw(raw, &sensors); err != nil {
		return nil, fmt.Errorf("Sensor configuration error: %v", err)
	}
	for _, s := range sensors {
		check, err := utils.ParseCommandArgs(s.CheckExec)
		if err != nil {
			return nil, err
		}
		s.checkCmd = check

		// the prometheus client lib's API here is baffling... they don't expose
		// an interface or embed their Opts type in each of the Opts "subtypes",
		// so we can't share the initialization.
		switch {
		case s.Type == "counter":
			s.collector = prometheus.NewCounter(prometheus.CounterOpts{
				Namespace: s.Namespace,
				Subsystem: s.Subsystem,
				Name:      s.Name,
				Help:      s.Help,
			})
		case s.Type == "gauge":
			s.collector = prometheus.NewGauge(prometheus.GaugeOpts{
				Namespace: s.Namespace,
				Subsystem: s.Subsystem,
				Name:      s.Name,
				Help:      s.Help,
			})
		case s.Type == "histogram":
			s.collector = prometheus.NewHistogram(prometheus.HistogramOpts{
				Namespace: s.Namespace,
				Subsystem: s.Subsystem,
				Name:      s.Name,
				Help:      s.Help,
			})
		case s.Type == "summary":
			s.collector = prometheus.NewSummary(prometheus.SummaryOpts{
				Namespace: s.Namespace,
				Subsystem: s.Subsystem,
				Name:      s.Name,
				Help:      s.Help,
			})
		default:
			return nil, fmt.Errorf("Invalid sensor type: %s\n", s.Type)
		}
		// we're going to unregister before every attempt to register
		// so that we can reload config
		prometheus.Unregister(s.collector)
		if err := prometheus.Register(s.collector); err != nil {
			return nil, err
		}
	}
	return sensors, nil
}
<|endoftext|>


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

package delete

import (
	"context"
	"fmt"
	"time"

	"github.com/GoogleContainerTools/kpt/internal/cmdsync"
	"github.com/GoogleContainerTools/kpt/internal/docs/generated/syncdocs"
	"github.com/GoogleContainerTools/kpt/internal/errors"
	"github.com/GoogleContainerTools/kpt/internal/util/porch"
	"github.com/spf13/cobra"
	coreapi "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/cli-runtime/pkg/genericclioptions"
	"sigs.k8s.io/cli-utils/pkg/kstatus/status"
	"sigs.k8s.io/controller-runtime/pkg/client"
)

const (
	command = "cmdsync.delete"
	// TODO: Find a better location for this git repo.
	emptyRepo       = "https://github.com/mortent/empty"
	emptyRepoBranch = "main"
	defaultTimeout  = 2 * time.Minute
)

var (
	rootSyncGVK = schema.GroupVersionKind{
		Group:   "configsync.gke.io",
		Version: "v1beta1",
		Kind:    "RootSync",
	}
	resourceGroupGVK = schema.GroupVersionKind{
		Group:   "kpt.dev",
		Version: "v1alpha1",
		Kind:    "ResourceGroup",
	}
)

func NewCommand(ctx context.Context, rcg *genericclioptions.ConfigFlags) *cobra.Command {
	return newRunner(ctx, rcg).Command
}

func newRunner(ctx context.Context, rcg *genericclioptions.ConfigFlags) *runner {
	r := &runner{
		ctx: ctx,
		cfg: rcg,
	}
	c := &cobra.Command{
		Use:     "del REPOSITORY [flags]",
		Aliases: []string{"delete"},
		Short:   syncdocs.DeleteShort,
		Long:    syncdocs.DeleteShort + "\n" + syncdocs.DeleteLong,
		Example: syncdocs.DeleteExamples,
		PreRunE: r.preRunE,
		RunE:    r.runE,
		Hidden:  porch.HidePorchCommands,
	}
	r.Command = c

	c.Flags().BoolVar(&r.keepSecret, "keep-auth-secret", false, "Keep the auth secret associated with the RootSync resource, if any")
	c.Flags().DurationVar(&r.timeout, "timeout", defaultTimeout, "How long to wait for Config Sync to delete package RootSync")

	return r
}

type runner struct {
	ctx     context.Context
	cfg     *genericclioptions.ConfigFlags
	client  client.WithWatch
	Command *cobra.Command

	// Flags
	keepSecret bool
	timeout    time.Duration
}

func (r *runner) preRunE(cmd *cobra.Command, args []string) error {
	const op errors.Op = command + ".preRunE"
	client, err := porch.CreateDynamicClient(r.cfg)
	if err != nil {
		return errors.E(op, err)
	}
	r.client = client
	return nil
}

func (r *runner) runE(cmd *cobra.Command, args []string) error {
	const op errors.Op = command + ".runE"

	if len(args) == 0 {
		return errors.E(op, fmt.Errorf("NAME is a required positional argument"))
	}

	name := args[0]
	namespace := cmdsync.RootSyncNamespace
	if *r.cfg.Namespace != "" {
		namespace = *r.cfg.Namespace
	}
	key := client.ObjectKey{
		Namespace: namespace,
		Name:      name,
	}
	rs := unstructured.Unstructured{}
	rs.SetGroupVersionKind(rootSyncGVK)
	if err := r.client.Get(r.ctx, key, &rs); err != nil {
		return errors.E(op, fmt.Errorf("cannot get %s: %v", key, err))
	}

	git, found, err := unstructured.NestedMap(rs.Object, "spec", "git")
	if err != nil || !found {
		return errors.E(op, fmt.Errorf("couldn't find `spec.git`: %v", err))
	}

	git["repo"] = emptyRepo
	git["branch"] = emptyRepoBranch
	git["dir"] = ""
	git["revision"] = ""

	if err := unstructured.SetNestedMap(rs.Object, git, "spec", "git"); err != nil {
		return errors.E(op, err)
	}

	fmt.Println("Deleting synced resources..")
	if err := r.client.Update(r.ctx, &rs); err != nil {
		return errors.E(op, err)
	}

	if err := func() error {
		ctx, cancel := context.WithTimeout(r.ctx, r.timeout)
		defer cancel()

		if err := r.waitForRootSync(ctx, name, namespace); err != nil {
			return err
		}

		fmt.Println("Waiting for deleted resources to be removed..")
		if err := r.waitForResourceGroup(ctx, name, namespace); err != nil {
			return err
		}
		return nil
	}(); err != nil {
		// TODO: See if we can expose more information here about what might have prevented a package
		// from being deleted.
		e := fmt.Errorf("package %s failed to be deleted after %f seconds: %v", name, r.timeout.Seconds(), err)
		return errors.E(op, e)
	}

	if err := r.client.Delete(r.ctx, &rs); err != nil {
		return errors.E(op, fmt.Errorf("failed to clean up RootSync: %w", err))
	}

	rg := unstructured.Unstructured{}
	rg.SetGroupVersionKind(resourceGroupGVK)
	rg.SetName(rs.GetName())
	rg.SetNamespace(rs.GetNamespace())
	if err := r.client.Delete(r.ctx, &rg); err != nil {
		return errors.E(op, fmt.Errorf("failed to clean up ResourceGroup: %w", err))
	}

	if r.keepSecret {
		return nil
	}

	secret := getSecretName(&rs)
	if secret == "" {
		return nil
	}

	if err := r.client.Delete(r.ctx, &coreapi.Secret{
		TypeMeta: metav1.TypeMeta{
			Kind:       "Secret",
			APIVersion: coreapi.SchemeGroupVersion.Identifier(),
		},
		ObjectMeta: metav1.ObjectMeta{
			Name:      secret,
			Namespace: namespace,
		},
	}); err != nil {
		return errors.E(op, fmt.Errorf("failed to delete Secret %s: %w", secret, err))
	}

	fmt.Printf("Sync %s successfully deleted\n", name)
	return nil
}

func (r *runner) waitForRootSync(ctx context.Context, name string, namespace string) error {
	const op errors.Op = command + ".waitForRootSync"

	return r.waitForResource(ctx, resourceGroupGVK, name, namespace, func(u *unstructured.Unstructured) (bool, error) {
		res, err := status.Compute(u)
		if err != nil {
			return false, errors.E(op, err)
		}
		if res.Status == status.CurrentStatus {
			return true, nil
		}
		return false, nil
	})
}

func (r *runner) waitForResourceGroup(ctx context.Context, name string, namespace string) error {
	const op errors.Op = command + ".waitForResourceGroup"

	return r.waitForResource(ctx, resourceGroupGVK, name, namespace, func(u *unstructured.Unstructured) (bool, error) {
		resources, found, err := unstructured.NestedSlice(u.Object, "spec", "resources")
		if err != nil {
			return false, errors.E(op, err)
		}
		if !found {
			return true, nil
		}
		if len(resources) == 0 {
			return true, nil
		}
		return false, nil
	})
}

type ReconcileFunc func(*unstructured.Unstructured) (bool, error)

func (r *runner) waitForResource(ctx context.Context, gvk schema.GroupVersionKind, name, namespace string, reconcileFunc ReconcileFunc) error {
	const op errors.Op = command + ".waitForResource"

	u := unstructured.UnstructuredList{}
	u.SetGroupVersionKind(gvk)
	watch, err := r.client.Watch(r.ctx, &u)
	if err != nil {
		return errors.E(op, err)
	}
	defer watch.Stop()

	for {
		select {
		case ev, ok := <-watch.ResultChan():
			if !ok {
				return errors.E(op, fmt.Errorf("watch closed unexpectedly"))
			}
			if ev.Object == nil {
				continue
			}

			u := ev.Object.(*unstructured.Unstructured)

			if u.GetName() != name || u.GetNamespace() != namespace {
				continue
			}

			reconciled, err := reconcileFunc(u)
			if err != nil {
				return err
			}
			if reconciled {
				return nil
			}
		case <-ctx.Done():
			return ctx.Err()
		}
	}
}

func getSecretName(repo *unstructured.Unstructured) string {
	name, _, _ := unstructured.NestedString(repo.Object, "spec", "git", "secretRef", "name")
	return name
}
<commit_msg>Update sync delete command to use repo under platkrm org (#3111)<commit_after>// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package delete

import (
	"context"
	"fmt"
	"time"

	"github.com/GoogleContainerTools/kpt/internal/cmdsync"
	"github.com/GoogleContainerTools/kpt/internal/docs/generated/syncdocs"
	"github.com/GoogleContainerTools/kpt/internal/errors"
	"github.com/GoogleContainerTools/kpt/internal/util/porch"
	"github.com/spf13/cobra"
	coreapi "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/cli-runtime/pkg/genericclioptions"
	"sigs.k8s.io/cli-utils/pkg/kstatus/status"
	"sigs.k8s.io/controller-runtime/pkg/client"
)

const (
	command         = "cmdsync.delete"
	emptyRepo       = "https://github.com/platkrm/empty"
	emptyRepoBranch = "main"
	defaultTimeout  = 2 * time.Minute
)

var (
	rootSyncGVK = schema.GroupVersionKind{
		Group:   "configsync.gke.io",
		Version: "v1beta1",
		Kind:    "RootSync",
	}
	resourceGroupGVK = schema.GroupVersionKind{
		Group:   "kpt.dev",
		Version: "v1alpha1",
		Kind:    "ResourceGroup",
	}
)

func NewCommand(ctx context.Context, rcg *genericclioptions.ConfigFlags) *cobra.Command {
	return newRunner(ctx, rcg).Command
}

func newRunner(ctx context.Context, rcg *genericclioptions.ConfigFlags) *runner {
	r := &runner{
		ctx: ctx,
		cfg: rcg,
	}
	c := &cobra.Command{
		Use:     "del REPOSITORY [flags]",
		Aliases: []string{"delete"},
		Short:   syncdocs.DeleteShort,
		Long:    syncdocs.DeleteShort + "\n" + syncdocs.DeleteLong,
		Example: syncdocs.DeleteExamples,
		PreRunE: r.preRunE,
		RunE:    r.runE,
		Hidden:  porch.HidePorchCommands,
	}
	r.Command = c

	c.Flags().BoolVar(&r.keepSecret, "keep-auth-secret", false, "Keep the auth secret associated with the RootSync resource, if any")
	c.Flags().DurationVar(&r.timeout, "timeout", defaultTimeout, "How long to wait for Config Sync to delete package RootSync")

	return r
}

type runner struct {
	ctx     context.Context
	cfg     *genericclioptions.ConfigFlags
	client  client.WithWatch
	Command *cobra.Command

	// Flags
	keepSecret bool
	timeout    time.Duration
}

func (r *runner) preRunE(cmd *cobra.Command, args []string) error {
	const op errors.Op = command + ".preRunE"
	client, err := porch.CreateDynamicClient(r.cfg)
	if err != nil {
		return errors.E(op, err)
	}
	r.client = client
	return nil
}

func (r *runner) runE(cmd *cobra.Command, args []string) error {
	const op errors.Op = command + ".runE"

	if len(args) == 0 {
		return errors.E(op, fmt.Errorf("NAME is a required positional argument"))
	}

	name := args[0]
	namespace := cmdsync.RootSyncNamespace
	if *r.cfg.Namespace != "" {
		namespace = *r.cfg.Namespace
	}
	key := client.ObjectKey{
		Namespace: namespace,
		Name:      name,
	}
	rs := unstructured.Unstructured{}
	rs.SetGroupVersionKind(rootSyncGVK)
	if err := r.client.Get(r.ctx, key, &rs); err != nil {
		return errors.E(op, fmt.Errorf("cannot get %s: %v", key, err))
	}

	git, found, err := unstructured.NestedMap(rs.Object, "spec", "git")
	if err != nil || !found {
		return errors.E(op, fmt.Errorf("couldn't find `spec.git`: %v", err))
	}

	git["repo"] = emptyRepo
	git["branch"] = emptyRepoBranch
	git["dir"] = ""
	git["revision"] = ""

	if err := unstructured.SetNestedMap(rs.Object, git, "spec", "git"); err != nil {
		return errors.E(op, err)
	}

	fmt.Println("Deleting synced resources..")
	if err := r.client.Update(r.ctx, &rs); err != nil {
		return errors.E(op, err)
	}

	if err := func() error {
		ctx, cancel := context.WithTimeout(r.ctx, r.timeout)
		defer cancel()

		if err := r.waitForRootSync(ctx, name, namespace); err != nil {
			return err
		}

		fmt.Println("Waiting for deleted resources to be removed..")
		if err := r.waitForResourceGroup(ctx, name, namespace); err != nil {
			return err
		}
		return nil
	}(); err != nil {
		// TODO: See if we can expose more information here about what might have prevented a package
		// from being deleted.
		e := fmt.Errorf("package %s failed to be deleted after %f seconds: %v", name, r.timeout.Seconds(), err)
		return errors.E(op, e)
	}

	if err := r.client.Delete(r.ctx, &rs); err != nil {
		return errors.E(op, fmt.Errorf("failed to clean up RootSync: %w", err))
	}

	rg := unstructured.Unstructured{}
	rg.SetGroupVersionKind(resourceGroupGVK)
	rg.SetName(rs.GetName())
	rg.SetNamespace(rs.GetNamespace())
	if err := r.client.Delete(r.ctx, &rg); err != nil {
		return errors.E(op, fmt.Errorf("failed to clean up ResourceGroup: %w", err))
	}

	if r.keepSecret {
		return nil
	}

	secret := getSecretName(&rs)
	if secret == "" {
		return nil
	}

	if err := r.client.Delete(r.ctx, &coreapi.Secret{
		TypeMeta: metav1.TypeMeta{
			Kind:       "Secret",
			APIVersion: coreapi.SchemeGroupVersion.Identifier(),
		},
		ObjectMeta: metav1.ObjectMeta{
			Name:      secret,
			Namespace: namespace,
		},
	}); err != nil {
		return errors.E(op, fmt.Errorf("failed to delete Secret %s: %w", secret, err))
	}

	fmt.Printf("Sync %s successfully deleted\n", name)
	return nil
}

func (r *runner) waitForRootSync(ctx context.Context, name string, namespace string) error {
	const op errors.Op = command + ".waitForRootSync"

	return r.waitForResource(ctx, resourceGroupGVK, name, namespace, func(u *unstructured.Unstructured) (bool, error) {
		res, err := status.Compute(u)
		if err != nil {
			return false, errors.E(op, err)
		}
		if res.Status == status.CurrentStatus {
			return true, nil
		}
		return false, nil
	})
}

func (r *runner) waitForResourceGroup(ctx context.Context, name string, namespace string) error {
	const op errors.Op = command + ".waitForResourceGroup"

	return r.waitForResource(ctx, resourceGroupGVK, name, namespace, func(u *unstructured.Unstructured) (bool, error) {
		resources, found, err := unstructured.NestedSlice(u.Object, "spec", "resources")
		if err != nil {
			return false, errors.E(op, err)
		}
		if !found {
			return true, nil
		}
		if len(resources) == 0 {
			return true, nil
		}
		return false, nil
	})
}

type ReconcileFunc func(*unstructured.Unstructured) (bool, error)

func (r *runner) waitForResource(ctx context.Context, gvk schema.GroupVersionKind, name, namespace string, reconcileFunc ReconcileFunc) error {
	const op errors.Op = command + ".waitForResource"

	u := unstructured.UnstructuredList{}
	u.SetGroupVersionKind(gvk)
	watch, err := r.client.Watch(r.ctx, &u)
	if err != nil {
		return errors.E(op, err)
	}
	defer watch.Stop()

	for {
		select {
		case ev, ok := <-watch.ResultChan():
			if !ok {
				return errors.E(op, fmt.Errorf("watch closed unexpectedly"))
			}
			if ev.Object == nil {
				continue
			}

			u := ev.Object.(*unstructured.Unstructured)

			if u.GetName() != name || u.GetNamespace() != namespace {
				continue
			}

			reconciled, err := reconcileFunc(u)
			if err != nil {
				return err
			}
			if reconciled {
				return nil
			}
		case <-ctx.Done():
			return ctx.Err()
		}
	}
}

func getSecretName(repo *unstructured.Unstructured) string {
	name, _, _ := unstructured.NestedString(repo.Object, "spec", "git", "secretRef", "name")
	return name
}
<|endoftext|>


<commit_before>package rpc

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"hash/crc32"
	"io"
	"math"

	hdfs "github.com/colinmarc/hdfs/v2/internal/protocol/hadoop_hdfs"
	"github.com/golang/protobuf/proto"
)

const (
	outboundPacketSize = 65536
	outboundChunkSize  = 512
	maxPacketsInQueue  = 5
)

// blockWriteStream writes data out to a datanode, and reads acks back.
type blockWriteStream struct {
	block *hdfs.LocatedBlockProto

	conn   io.ReadWriter
	buf    bytes.Buffer
	offset int64
	closed bool

	packets chan outboundPacket
	seqno   int

	ackError        error
	acksDone        chan struct{}
	lastPacketSeqno int
}

type outboundPacket struct {
	seqno     int
	offset    int64
	last      bool
	checksums []byte
	data      []byte
}

type ackError struct {
	pipelineIndex int
	seqno         int
	status        hdfs.Status
}

func (ae ackError) Error() string {
	return fmt.Sprintf("Ack error from datanode: %s", ae.status.String())
}

var ErrInvalidSeqno = errors.New("invalid ack sequence number")

func newBlockWriteStream(conn io.ReadWriter, offset int64) *blockWriteStream {
	s := &blockWriteStream{
		conn:     conn,
		offset:   offset,
		seqno:    1,
		packets:  make(chan outboundPacket, maxPacketsInQueue),
		acksDone: make(chan struct{}),
	}

	// Ack packets in the background.
	go func() {
		s.ackPackets()
		close(s.acksDone)
	}()

	return s
}

// func newBlockWriteStreamForRecovery(conn io.ReadWriter, oldWriteStream *blockWriteStream) {
// 	s := &blockWriteStream{
// 		conn: conn,
// 		buf: oldWriteStream.buf,
// 		packets: oldWriteStream.packets,
// 		offset: oldWriteStream.offset,
// 		seqno: oldWriteStream.seqno,
// 		packets
// 	}

// 	go s.ackPackets()
// 	return s
// }

func (s *blockWriteStream) Write(b []byte) (int, error) {
	if s.closed {
		return 0, io.ErrClosedPipe
	}

	if err := s.getAckError(); err != nil {
		return 0, err
	}

	n, _ := s.buf.Write(b)
	err := s.flush(false)
	return n, err
}

// finish flushes the rest of the buffered bytes, and then sends a final empty
// packet signifying the end of the block.
func (s *blockWriteStream) finish() error {
	if s.closed {
		return nil
	}
	s.closed = true

	if err := s.getAckError(); err != nil {
		return err
	}

	err := s.flush(true)
	if err != nil {
		return err
	}

	// The last packet has no data; it's just a marker that the block is finished.
	lastPacket := outboundPacket{
		seqno:     s.seqno,
		offset:    s.offset,
		last:      true,
		checksums: []byte{},
		data:      []byte{},
	}
	s.packets <- lastPacket
	err = s.writePacket(lastPacket)
	if err != nil {
		return err
	}
	close(s.packets)

	// Wait for the ack loop to finish.
	<-s.acksDone

	// Check one more time for any ack errors.
	if err := s.getAckError(); err != nil {
		return err
	}

	return nil
}

// flush parcels out the buffered bytes into packets, which it then flushes to
// the datanode. We keep around a reference to the packet, in case the ack
// fails, and we need to send it again later.
func (s *blockWriteStream) flush(force bool) error {
	if err := s.getAckError(); err != nil {
		return err
	}

	for s.buf.Len() > 0 && (force || s.buf.Len() >= outboundPacketSize) {
		packet := s.makePacket()
		s.packets <- packet
		s.offset += int64(len(packet.data))
		s.seqno++

		err := s.writePacket(packet)
		if err != nil {
			return err
		}
	}

	return nil
}

func (s *blockWriteStream) makePacket() outboundPacket {
	packetLength := outboundPacketSize
	if s.buf.Len() < outboundPacketSize {
		packetLength = s.buf.Len()
	}

	// If we're starting from a weird offset (usually because of an Append), HDFS
	// gets unhappy unless we first align to a chunk boundary with a small packet.
	// Otherwise it yells at us with "a partial chunk must be sent in an
	// individual packet" or just complains about a corrupted block.
	alignment := int(s.offset) % outboundChunkSize
	if alignment > 0 && packetLength > (outboundChunkSize-alignment) {
		packetLength = outboundChunkSize - alignment
	}

	numChunks := int(math.Ceil(float64(packetLength) / float64(outboundChunkSize)))
	packet := outboundPacket{
		seqno:     s.seqno,
		offset:    s.offset,
		last:      false,
		checksums: make([]byte, numChunks*4),
		data:      make([]byte, packetLength),
	}

	// TODO: we shouldn't actually need this extra copy. We should also be able
	// to "reuse" packets.
	io.ReadFull(&s.buf, packet.data)

	// Fill in the checksum for each chunk of data.
	for i := 0; i < numChunks; i++ {
		chunkOff := i * outboundChunkSize
		chunkEnd := chunkOff + outboundChunkSize
		if chunkEnd >= len(packet.data) {
			chunkEnd = len(packet.data)
		}

		checksum := crc32.Checksum(packet.data[chunkOff:chunkEnd], crc32.IEEETable)
		binary.BigEndian.PutUint32(packet.checksums[i*4:], checksum)
	}

	return packet
}

// ackPackets is meant to run in the background, reading acks and setting
// ackError if one fails.
func (s *blockWriteStream) ackPackets() {
	reader := bufio.NewReader(s.conn)

	for {
		p, ok := <-s.packets
		if !ok {
			// All packets all acked.
			return
		}

		// If we fail to read the ack at all, that counts as a failure from the
		// first datanode (the one we're connected to).
		ack := &hdfs.PipelineAckProto{}
		err := readPrefixedMessage(reader, ack)
		if err != nil {
			s.ackError = err
			break
		}

		seqno := int(ack.GetSeqno())
		for i, status := range ack.GetReply() {
			if status != hdfs.Status_SUCCESS {
				s.ackError = ackError{status: status, seqno: seqno, pipelineIndex: i}
				break
			}
		}

		if seqno != p.seqno {
			s.ackError = ErrInvalidSeqno
			break
		}
	}

	// Once we've seen an error, just keep reading packets off the channel (but
	// not off the socket) until the writing thread figures it out. If we don't,
	// the upstream thread could deadlock waiting for the channel to have space.
	for _ = range s.packets {
	}
}

func (s *blockWriteStream) getAckError() error {
	select {
	case <-s.acksDone:
		if s.ackError != nil {
			return s.ackError
		}
	default:
	}

	return nil
}

// A packet for the datanode:
// +-----------------------------------------------------------+
// |  uint32 length of the packet                              |
// +-----------------------------------------------------------+
// |  size of the PacketHeaderProto, uint16                    |
// +-----------------------------------------------------------+
// |  PacketHeaderProto                                        |
// +-----------------------------------------------------------+
// |  N checksums, 4 bytes each                                |
// +-----------------------------------------------------------+
// |  N chunks of payload data                                 |
// +-----------------------------------------------------------+
func (s *blockWriteStream) writePacket(p outboundPacket) error {
	headerInfo := &hdfs.PacketHeaderProto{
		OffsetInBlock:     proto.Int64(p.offset),
		Seqno:             proto.Int64(int64(p.seqno)),
		LastPacketInBlock: proto.Bool(p.last),
		DataLen:           proto.Int32(int32(len(p.data))),
	}

	header := make([]byte, 6)
	infoBytes, err := proto.Marshal(headerInfo)
	if err != nil {
		return err
	}

	// Don't ask me why this doesn't include the header proto...
	totalLength := len(p.data) + len(p.checksums) + 4
	binary.BigEndian.PutUint32(header, uint32(totalLength))
	binary.BigEndian.PutUint16(header[4:], uint16(len(infoBytes)))
	header = append(header, infoBytes...)

	_, err = s.conn.Write(header)
	if err != nil {
		return err
	}

	_, err = s.conn.Write(p.checksums)
	if err != nil {
		return err
	}

	_, err = s.conn.Write(p.data)
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>Send heart beats to datanode<commit_after>package rpc

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"hash/crc32"
	"io"
	"math"
	"sync"
	"time"

	hdfs "github.com/colinmarc/hdfs/v2/internal/protocol/hadoop_hdfs"
	"github.com/golang/protobuf/proto"
)

const (
	outboundPacketSize = 65536
	outboundChunkSize  = 512
	maxPacketsInQueue  = 5
	heartbeatSeqno     = -1
	heartbeatInterval  = 30 * time.Second
)

// heartbeatPacket is sent every 30 seconds to keep the stream alive. It's
// always the same.
var heartbeatPacket []byte

func init() {
	b, err := proto.Marshal(&hdfs.PacketHeaderProto{
		OffsetInBlock:     proto.Int64(0),
		Seqno:             proto.Int64(heartbeatSeqno),
		LastPacketInBlock: proto.Bool(false),
		DataLen:           proto.Int32(0),
	})

	if err != nil {
		panic(err)
	}

	header := make([]byte, 6)
	binary.BigEndian.PutUint32(header, 4)
	binary.BigEndian.PutUint16(header[4:], uint16(len(b)))
	heartbeatPacket = append(header, b...)
}

// blockWriteStream writes data out to a datanode, and reads acks back.
type blockWriteStream struct {
	block *hdfs.LocatedBlockProto

	conn   io.ReadWriter
	buf    bytes.Buffer
	offset int64
	closed bool

	packets chan outboundPacket
	seqno   int

	ackError        error
	acksDone        chan struct{}
	lastPacketSeqno int

	heartbeats chan struct{}
	writeLock  sync.Mutex
}

type outboundPacket struct {
	seqno     int
	offset    int64
	last      bool
	checksums []byte
	data      []byte
}

type ackError struct {
	pipelineIndex int
	seqno         int
	status        hdfs.Status
}

func (ae ackError) Error() string {
	return fmt.Sprintf("Ack error from datanode: %s", ae.status.String())
}

var ErrInvalidSeqno = errors.New("invalid ack sequence number")

func newBlockWriteStream(conn io.ReadWriter, offset int64) *blockWriteStream {
	s := &blockWriteStream{
		conn:       conn,
		offset:     offset,
		seqno:      1,
		packets:    make(chan outboundPacket, maxPacketsInQueue),
		acksDone:   make(chan struct{}),
		heartbeats: make(chan struct{}),
	}

	// Send idle heartbeats every 30 seconds.
	go s.writeHeartbeats()

	// Ack packets in the background.
	go func() {
		s.ackPackets()
		close(s.acksDone)
	}()

	return s
}

// func newBlockWriteStreamForRecovery(conn io.ReadWriter, oldWriteStream *blockWriteStream) {
// 	s := &blockWriteStream{
// 		conn: conn,
// 		buf: oldWriteStream.buf,
// 		packets: oldWriteStream.packets,
// 		offset: oldWriteStream.offset,
// 		seqno: oldWriteStream.seqno,
// 		packets
// 	}

// 	go s.ackPackets()
// 	return s
// }

func (s *blockWriteStream) Write(b []byte) (int, error) {
	if s.closed {
		return 0, io.ErrClosedPipe
	}

	if err := s.getAckError(); err != nil {
		return 0, err
	}

	n, _ := s.buf.Write(b)
	err := s.flush(false)
	return n, err
}

// finish flushes the rest of the buffered bytes, and then sends a final empty
// packet signifying the end of the block.
func (s *blockWriteStream) finish() error {
	if s.closed {
		return nil
	}
	s.closed = true

	// Stop sending heartbeats.
	close(s.heartbeats)

	if err := s.getAckError(); err != nil {
		return err
	}

	if err := s.flush(true); err != nil {
		return err
	}

	// The last packet has no data; it's just a marker that the block is finished.
	lastPacket := outboundPacket{
		seqno:     s.seqno,
		offset:    s.offset,
		last:      true,
		checksums: []byte{},
		data:      []byte{},
	}
	s.packets <- lastPacket

	err := s.writePacket(lastPacket)
	if err != nil {
		return err
	}

	// Wait for the ack loop to finish.
	close(s.packets)
	<-s.acksDone

	// Check one more time for any ack errors.
	if err := s.getAckError(); err != nil {
		return err
	}

	return nil
}

// flush parcels out the buffered bytes into packets, which it then flushes to
// the datanode. We keep around a reference to the packet, in case the ack
// fails, and we need to send it again later.
func (s *blockWriteStream) flush(force bool) error {
	s.writeLock.Lock()
	defer s.writeLock.Unlock()

	for s.buf.Len() > 0 && (force || s.buf.Len() >= outboundPacketSize) {
		packet := s.makePacket()
		s.packets <- packet
		s.offset += int64(len(packet.data))
		s.seqno++

		err := s.writePacket(packet)
		if err != nil {
			return err
		}
	}

	return nil
}

func (s *blockWriteStream) makePacket() outboundPacket {
	packetLength := outboundPacketSize
	if s.buf.Len() < outboundPacketSize {
		packetLength = s.buf.Len()
	}

	// If we're starting from a weird offset (usually because of an Append), HDFS
	// gets unhappy unless we first align to a chunk boundary with a small packet.
	// Otherwise it yells at us with "a partial chunk must be sent in an
	// individual packet" or just complains about a corrupted block.
	alignment := int(s.offset) % outboundChunkSize
	if alignment > 0 && packetLength > (outboundChunkSize-alignment) {
		packetLength = outboundChunkSize - alignment
	}

	numChunks := int(math.Ceil(float64(packetLength) / float64(outboundChunkSize)))
	packet := outboundPacket{
		seqno:     s.seqno,
		offset:    s.offset,
		last:      false,
		checksums: make([]byte, numChunks*4),
		data:      make([]byte, packetLength),
	}

	// TODO: we shouldn't actually need this extra copy. We should also be able
	// to "reuse" packets.
	io.ReadFull(&s.buf, packet.data)

	// Fill in the checksum for each chunk of data.
	for i := 0; i < numChunks; i++ {
		chunkOff := i * outboundChunkSize
		chunkEnd := chunkOff + outboundChunkSize
		if chunkEnd >= len(packet.data) {
			chunkEnd = len(packet.data)
		}

		checksum := crc32.Checksum(packet.data[chunkOff:chunkEnd], crc32.IEEETable)
		binary.BigEndian.PutUint32(packet.checksums[i*4:], checksum)
	}

	return packet
}

// ackPackets is meant to run in the background, reading acks and setting
// ackError if one fails.
func (s *blockWriteStream) ackPackets() {
	reader := bufio.NewReader(s.conn)

Acks:
	for {
		p, ok := <-s.packets
		if !ok {
			// All packets all acked.
			return
		}

		var seqno int
		for {
			// If we fail to read the ack at all, that counts as a failure from the
			// first datanode (the one we're connected to).
			ack := &hdfs.PipelineAckProto{}
			err := readPrefixedMessage(reader, ack)
			if err != nil {
				s.ackError = err
				break Acks
			}

			seqno = int(ack.GetSeqno())

			for i, status := range ack.GetReply() {
				if status != hdfs.Status_SUCCESS {
					s.ackError = ackError{status: status, seqno: seqno, pipelineIndex: i}
					break Acks
				}
			}

			if seqno != heartbeatSeqno {
				break
			}
		}

		if seqno != p.seqno {
			s.ackError = ErrInvalidSeqno
			break Acks
		}
	}

	// Once we've seen an error, just keep reading packets off the channel (but
	// not off the socket) until the writing thread figures it out. If we don't,
	// the upstream thread could deadlock waiting for the channel to have space.
	for _ = range s.packets {
	}
}

func (s *blockWriteStream) getAckError() error {
	select {
	case <-s.acksDone:
		if s.ackError != nil {
			return s.ackError
		}
	default:
	}

	return nil
}

// A packet for the datanode:
// +-----------------------------------------------------------+
// |  uint32 length of the packet                              |
// +-----------------------------------------------------------+
// |  size of the PacketHeaderProto, uint16                    |
// +-----------------------------------------------------------+
// |  PacketHeaderProto                                        |
// +-----------------------------------------------------------+
// |  N checksums, 4 bytes each                                |
// +-----------------------------------------------------------+
// |  N chunks of payload data                                 |
// +-----------------------------------------------------------+
func (s *blockWriteStream) writePacket(p outboundPacket) error {
	headerInfo := &hdfs.PacketHeaderProto{
		OffsetInBlock:     proto.Int64(p.offset),
		Seqno:             proto.Int64(int64(p.seqno)),
		LastPacketInBlock: proto.Bool(p.last),
		DataLen:           proto.Int32(int32(len(p.data))),
	}

	header := make([]byte, 6)
	infoBytes, err := proto.Marshal(headerInfo)
	if err != nil {
		return err
	}

	// Don't ask me why this doesn't include the header proto...
	totalLength := len(p.data) + len(p.checksums) + 4
	binary.BigEndian.PutUint32(header, uint32(totalLength))
	binary.BigEndian.PutUint16(header[4:], uint16(len(infoBytes)))
	header = append(header, infoBytes...)

	_, err = s.conn.Write(header)
	if err != nil {
		return err
	}

	_, err = s.conn.Write(p.checksums)
	if err != nil {
		return err
	}

	_, err = s.conn.Write(p.data)
	if err != nil {
		return err
	}

	return nil
}

func (s *blockWriteStream) writeHeartbeats() {
	ticker := time.NewTicker(heartbeatInterval)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			s.writeLock.Lock()
			s.conn.Write(heartbeatPacket)
			s.writeLock.Unlock()
		case <-s.heartbeats:
			return
		}
	}
}
<|endoftext|>


<commit_before>package gitsync

import (
	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	"github.com/checkr/codeflow/server/agent"
	"github.com/checkr/codeflow/server/plugins"
	log "github.com/codeamp/logger"
	"github.com/spf13/viper"
)

type GitSync struct {
	events chan agent.Event
	idRsa  string
}

func init() {
	agent.RegisterPlugin("gitsync", func() agent.Plugin {
		return &GitSync{}
	})
}

func (x *GitSync) Description() string {
	return "Sync Git repositories and create new features"
}

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

func (x *GitSync) Start(e chan agent.Event) error {
	x.events = e
	log.SetLogLevel("warning")
	log.Info("Started GitSync")

	return nil
}

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

func (x *GitSync) Subscribe() []string {
	return []string{
		"plugins.GitPing",
		"plugins.GitSync:update",
	}
}

func (x *GitSync) git(args ...string) ([]byte, error) {
	cmd := exec.Command("git", args...)
	env := os.Environ()
	env = append(env, x.idRsa)
	cmd.Env = env
	out, err := cmd.CombinedOutput()

	if err != nil {
		if ee, ok := err.(*exec.Error); ok {
			if ee.Err == exec.ErrNotFound {
				return nil, errors.New("Git executable not found in $PATH")
			}
		}

		return nil, errors.New(string(bytes.TrimSpace(out)))
	}

	return out, nil
}

func (x *GitSync) toGitCommit(entry string) (plugins.GitCommit, error) {
	items := strings.Split(entry, "#@#")
	commiterDate, err := time.Parse("2006-01-02T15:04:05-07:00", items[4])

	if err != nil {
		return plugins.GitCommit{}, err
	}

	return plugins.GitCommit{
		Hash:       items[0],
		ParentHash: items[1],
		Message:    items[2],
		User:       items[3],
		Created:    commiterDate,
	}, nil
}

func (x *GitSync) commits(project plugins.Project, git plugins.Git) ([]plugins.GitCommit, error) {
	var err error
	var output []byte

	idRsaPath := fmt.Sprintf("%s/%s_id_rsa", viper.GetString("plugins.gitsync.workdir"), project.Repository)
	x.idRsa = fmt.Sprintf("GIT_SSH_COMMAND=ssh -o StrictHostKeyChecking=no -i %s -F /dev/null", idRsaPath)
	repoPath := fmt.Sprintf("%s/%s_%s", viper.GetString("plugins.gitsync.workdir"), project.Repository, git.Branch)

	output, err = exec.Command("mkdir", "-p", filepath.Dir(repoPath)).CombinedOutput()
	if err != nil {
		log.Error(err)
		return nil, err
	}
	log.Info(string(output))

	if _, err = os.Stat(idRsaPath); err != nil {
		if os.IsNotExist(err) {
			err = ioutil.WriteFile(idRsaPath, []byte(git.RsaPrivateKey), 0600)
			if err != nil {
				log.Error(err)
				return nil, err
			}
		}
	}

	if _, err = os.Stat(fmt.Sprintf("%s", repoPath)); err != nil {
		if os.IsNotExist(err) {
			output, err = x.git("clone", git.Url, repoPath)
			if err != nil {
				log.Error(err)
				return nil, err
			}
			log.Info(string(output))
		}
	} else {
		output, err = x.git("-C", repoPath, "pull", "origin", git.Branch)
		if err != nil {
			log.Error(err)
			return nil, err
		}
		log.Info(string(output))
	}

	output, err = x.git("-C", repoPath, "checkout", git.Branch)
	if err != nil {
		log.Error(err)
		return nil, err
	}
	log.Info(string(output))

	output, err = x.git("-C", repoPath, "log", "--date=iso-strict", "-n", "50", "--pretty=format:%H#@#%P#@#%s#@#%cN#@#%cd", git.Branch)

	if err != nil {
		log.Error(err)
		return nil, err
	}

	var commits []plugins.GitCommit

	for _, line := range strings.Split(strings.TrimSuffix(string(output), "\n"), "\n") {
		commit, err := x.toGitCommit(line)
		if err != nil {
			log.Error(err)
			return nil, err
		}

		commits = append(commits, commit)
	}

	return commits, nil
}

func (x *GitSync) Process(e agent.Event) error {
	log.InfoWithFields("Process GitSync event", log.Fields{
		"event": e.Name,
	})

	var err error

	gitSyncEvent := e.Payload.(plugins.GitSync)
	gitSyncEvent.Action = plugins.Status
	gitSyncEvent.State = plugins.Fetching
	gitSyncEvent.StateMessage = ""
	x.events <- e.NewEvent(gitSyncEvent, nil)

	commits, err := x.commits(gitSyncEvent.Project, gitSyncEvent.Git)
	if err != nil {
		log.Error(err)
		gitSyncEvent.State = plugins.Failed
		gitSyncEvent.StateMessage = fmt.Sprintf("%v (Action: %v)", err.Error(), gitSyncEvent.State)
		event := e.NewEvent(gitSyncEvent, err)
		x.events <- event
		return err
	}

	for i := range commits {
		c := commits[i]
		c.Repository = gitSyncEvent.Project.Repository
		c.Ref = fmt.Sprintf("refs/heads/%s", gitSyncEvent.Git.Branch)

		if c.Hash == gitSyncEvent.From {
			break
		}

		x.events <- e.NewEvent(c, nil)
	}

	gitSyncEvent.State = plugins.Complete
	gitSyncEvent.StateMessage = ""
	x.events <- e.NewEvent(gitSyncEvent, nil)

	return nil
}
<commit_msg>add first-parent filter (#169)<commit_after>package gitsync

import (
	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	"github.com/checkr/codeflow/server/agent"
	"github.com/checkr/codeflow/server/plugins"
	log "github.com/codeamp/logger"
	"github.com/spf13/viper"
)

type GitSync struct {
	events chan agent.Event
	idRsa  string
}

func init() {
	agent.RegisterPlugin("gitsync", func() agent.Plugin {
		return &GitSync{}
	})
}

func (x *GitSync) Description() string {
	return "Sync Git repositories and create new features"
}

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

func (x *GitSync) Start(e chan agent.Event) error {
	x.events = e
	log.SetLogLevel("warning")
	log.Info("Started GitSync")

	return nil
}

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

func (x *GitSync) Subscribe() []string {
	return []string{
		"plugins.GitPing",
		"plugins.GitSync:update",
	}
}

func (x *GitSync) git(args ...string) ([]byte, error) {
	cmd := exec.Command("git", args...)
	env := os.Environ()
	env = append(env, x.idRsa)
	cmd.Env = env
	out, err := cmd.CombinedOutput()

	if err != nil {
		if ee, ok := err.(*exec.Error); ok {
			if ee.Err == exec.ErrNotFound {
				return nil, errors.New("Git executable not found in $PATH")
			}
		}

		return nil, errors.New(string(bytes.TrimSpace(out)))
	}

	return out, nil
}

func (x *GitSync) toGitCommit(entry string) (plugins.GitCommit, error) {
	items := strings.Split(entry, "#@#")
	commiterDate, err := time.Parse("2006-01-02T15:04:05-07:00", items[4])

	if err != nil {
		return plugins.GitCommit{}, err
	}

	return plugins.GitCommit{
		Hash:       items[0],
		ParentHash: items[1],
		Message:    items[2],
		User:       items[3],
		Created:    commiterDate,
	}, nil
}

func (x *GitSync) commits(project plugins.Project, git plugins.Git) ([]plugins.GitCommit, error) {
	var err error
	var output []byte

	idRsaPath := fmt.Sprintf("%s/%s_id_rsa", viper.GetString("plugins.gitsync.workdir"), project.Repository)
	x.idRsa = fmt.Sprintf("GIT_SSH_COMMAND=ssh -o StrictHostKeyChecking=no -i %s -F /dev/null", idRsaPath)
	repoPath := fmt.Sprintf("%s/%s_%s", viper.GetString("plugins.gitsync.workdir"), project.Repository, git.Branch)

	output, err = exec.Command("mkdir", "-p", filepath.Dir(repoPath)).CombinedOutput()
	if err != nil {
		log.Error(err)
		return nil, err
	}
	log.Info(string(output))

	if _, err = os.Stat(idRsaPath); err != nil {
		if os.IsNotExist(err) {
			err = ioutil.WriteFile(idRsaPath, []byte(git.RsaPrivateKey), 0600)
			if err != nil {
				log.Error(err)
				return nil, err
			}
		}
	}

	if _, err = os.Stat(fmt.Sprintf("%s", repoPath)); err != nil {
		if os.IsNotExist(err) {
			output, err = x.git("clone", git.Url, repoPath)
			if err != nil {
				log.Error(err)
				return nil, err
			}
			log.Info(string(output))
		}
	} else {
		output, err = x.git("-C", repoPath, "pull", "origin", git.Branch)
		if err != nil {
			log.Error(err)
			return nil, err
		}
		log.Info(string(output))
	}

	output, err = x.git("-C", repoPath, "checkout", git.Branch)
	if err != nil {
		log.Error(err)
		return nil, err
	}
	log.Info(string(output))

	output, err = x.git("-C", repoPath, "log", "--first-parent", "--date=iso-strict", "-n", "50", "--pretty=format:%H#@#%P#@#%s#@#%cN#@#%cd", git.Branch)

	if err != nil {
		log.Error(err)
		return nil, err
	}

	var commits []plugins.GitCommit

	for _, line := range strings.Split(strings.TrimSuffix(string(output), "\n"), "\n") {
		commit, err := x.toGitCommit(line)
		if err != nil {
			log.Error(err)
			return nil, err
		}

		commits = append(commits, commit)
	}

	return commits, nil
}

func (x *GitSync) Process(e agent.Event) error {
	log.InfoWithFields("Process GitSync event", log.Fields{
		"event": e.Name,
	})

	var err error

	gitSyncEvent := e.Payload.(plugins.GitSync)
	gitSyncEvent.Action = plugins.Status
	gitSyncEvent.State = plugins.Fetching
	gitSyncEvent.StateMessage = ""
	x.events <- e.NewEvent(gitSyncEvent, nil)

	commits, err := x.commits(gitSyncEvent.Project, gitSyncEvent.Git)
	if err != nil {
		log.Error(err)
		gitSyncEvent.State = plugins.Failed
		gitSyncEvent.StateMessage = fmt.Sprintf("%v (Action: %v)", err.Error(), gitSyncEvent.State)
		event := e.NewEvent(gitSyncEvent, err)
		x.events <- event
		return err
	}

	for i := range commits {
		c := commits[i]
		c.Repository = gitSyncEvent.Project.Repository
		c.Ref = fmt.Sprintf("refs/heads/%s", gitSyncEvent.Git.Branch)

		if c.Hash == gitSyncEvent.From {
			break
		}

		x.events <- e.NewEvent(c, nil)
	}

	gitSyncEvent.State = plugins.Complete
	gitSyncEvent.StateMessage = ""
	x.events <- e.NewEvent(gitSyncEvent, nil)

	return nil
}
<|endoftext|>