<commit_before>package srslog

import (
	"crypto/tls"
	"net"
)

// dialerFunctionWrapper is a simple object that consists of a dialer function
// and its name. This is primarily for testing, so we can make sure that the
// getDialer method returns the correct dialer function. However, if you ever
// find that you need to check which dialer function you have, this would also
// be useful for you without having to use reflection.
type dialerFunctionWrapper struct {
	Name   string
	Dialer func() (serverConn, string, error)
}

// Call the wrapped dialer function and return its return values.
func (df dialerFunctionWrapper) Call() (serverConn, string, error) {
	return df.Dialer()
}

// getDialer returns a "dialer" function that can be called to connect to a
// syslog server.
//
// Each dialer function is responsible for dialing the remote host and returns
// a serverConn, the hostname (or a default if the Writer has not specified a
// hostname), and an error in case dialing fails.
//
// The reason for separate dialers is that different network types may need
// to dial their connection differently, yet still provide a net.Conn interface
// that you can use once they have dialed. Rather than an increasingly long
// conditional, we have a map of network -> dialer function (with a sane default
// value), and adding a new network type is as easy as writing the dialer
// function and adding it to the map.
func (w Writer) getDialer() dialerFunctionWrapper {
	dialers := map[string]dialerFunctionWrapper{
		"":        dialerFunctionWrapper{"unixDialer", w.unixDialer},
		"tcp+tls": dialerFunctionWrapper{"tlsDialer", w.tlsDialer},
	}
	dialer, ok := dialers[w.network]
	if !ok {
		dialer = dialerFunctionWrapper{"basicDialer", w.basicDialer}
	}
	return dialer
}

// unixDialer uses the unixSyslog method to open a connection to the syslog
// daemon running on the local machine.
func (w Writer) unixDialer() (serverConn, string, error) {
	sc, err := unixSyslog()
	hostname := w.hostname
	if hostname == "" {
		hostname = "localhost"
	}
	return sc, hostname, err
}

// tlsDialer connects to TLS over TCP, and is used for the "tcp+tls" network
// type.
func (w Writer) tlsDialer() (serverConn, string, error) {
	c, err := tls.Dial("tcp", w.raddr, w.tlsConfig)
	var sc serverConn
	hostname := w.hostname
	if err == nil {
		sc = &netConn{conn: c}
		if hostname == "" {
			hostname = c.LocalAddr().String()
		}
	}
	return sc, hostname, err
}

// basicDialer is the most common dialer for syslog, and supports both TCP and
// UDP connections.
func (w Writer) basicDialer() (serverConn, string, error) {
	c, err := net.Dial(w.network, w.raddr)
	var sc serverConn
	hostname := w.hostname
	if err == nil {
		sc = &netConn{conn: c}
		if hostname == "" {
			hostname = c.LocalAddr().String()
		}
	}
	return sc, hostname, err
}
<commit_msg>Suppress vet warnings about mutex copy<commit_after>package srslog

import (
	"crypto/tls"
	"net"
)

// dialerFunctionWrapper is a simple object that consists of a dialer function
// and its name. This is primarily for testing, so we can make sure that the
// getDialer method returns the correct dialer function. However, if you ever
// find that you need to check which dialer function you have, this would also
// be useful for you without having to use reflection.
type dialerFunctionWrapper struct {
	Name   string
	Dialer func() (serverConn, string, error)
}

// Call the wrapped dialer function and return its return values.
func (df dialerFunctionWrapper) Call() (serverConn, string, error) {
	return df.Dialer()
}

// getDialer returns a "dialer" function that can be called to connect to a
// syslog server.
//
// Each dialer function is responsible for dialing the remote host and returns
// a serverConn, the hostname (or a default if the Writer has not specified a
// hostname), and an error in case dialing fails.
//
// The reason for separate dialers is that different network types may need
// to dial their connection differently, yet still provide a net.Conn interface
// that you can use once they have dialed. Rather than an increasingly long
// conditional, we have a map of network -> dialer function (with a sane default
// value), and adding a new network type is as easy as writing the dialer
// function and adding it to the map.
func (w *Writer) getDialer() dialerFunctionWrapper {
	dialers := map[string]dialerFunctionWrapper{
		"":        dialerFunctionWrapper{"unixDialer", w.unixDialer},
		"tcp+tls": dialerFunctionWrapper{"tlsDialer", w.tlsDialer},
	}
	dialer, ok := dialers[w.network]
	if !ok {
		dialer = dialerFunctionWrapper{"basicDialer", w.basicDialer}
	}
	return dialer
}

// unixDialer uses the unixSyslog method to open a connection to the syslog
// daemon running on the local machine.
func (w *Writer) unixDialer() (serverConn, string, error) {
	sc, err := unixSyslog()
	hostname := w.hostname
	if hostname == "" {
		hostname = "localhost"
	}
	return sc, hostname, err
}

// tlsDialer connects to TLS over TCP, and is used for the "tcp+tls" network
// type.
func (w *Writer) tlsDialer() (serverConn, string, error) {
	c, err := tls.Dial("tcp", w.raddr, w.tlsConfig)
	var sc serverConn
	hostname := w.hostname
	if err == nil {
		sc = &netConn{conn: c}
		if hostname == "" {
			hostname = c.LocalAddr().String()
		}
	}
	return sc, hostname, err
}

// basicDialer is the most common dialer for syslog, and supports both TCP and
// UDP connections.
func (w *Writer) basicDialer() (serverConn, string, error) {
	c, err := net.Dial(w.network, w.raddr)
	var sc serverConn
	hostname := w.hostname
	if err == nil {
		sc = &netConn{conn: c}
		if hostname == "" {
			hostname = c.LocalAddr().String()
		}
	}
	return sc, hostname, err
}
<|endoftext|>


<commit_before>package slack

import (
	"context"
	"encoding/json"
)

// InputType is the type of the dialog input type
type InputType string

const (
	// InputTypeText textfield input
	InputTypeText InputType = "text"
	// InputTypeTextArea textarea input
	InputTypeTextArea InputType = "textarea"
	// InputTypeSelect select menus input
	InputTypeSelect InputType = "select"
)

// DialogInput for dialogs input type text or menu
type DialogInput struct {
	Type        InputType `json:"type"`
	Label       string    `json:"label"`
	Name        string    `json:"name"`
	Placeholder string    `json:"placeholder"`
	Optional    bool      `json:"optional"`
}

// DialogTrigger ...
type DialogTrigger struct {
	TriggerID string `json:"trigger_id"` //Required. Must respond within 3 seconds.
	Dialog    Dialog `json:"dialog"`     //Required.
}

// Dialog as in Slack dialogs
// https://api.slack.com/dialogs#option_element_attributes#top-level_dialog_attributes
type Dialog struct {
	TriggerID      string          `json:"trigger_id"`      // Required
	CallbackID     string          `json:"callback_id"`     // Required
	State          string          `json:"state,omitempty"` // Optional
	Title          string          `json:"title"`
	SubmitLabel    string          `json:"submit_label,omitempty"`
	NotifyOnCancel bool            `json:"notify_on_cancel"`
	Elements       []DialogElement `json:"elements"`
}

// DialogElement abstract type for dialogs.
type DialogElement interface{}

// DialogCallback DEPRECATED use InteractionCallback
type DialogCallback InteractionCallback

// DialogSubmissionCallback is sent from Slack when a user submits a form from within a dialog
type DialogSubmissionCallback struct {
	State      string            `json:"state,omitempty"`
	Submission map[string]string `json:"submission"`
}

// DialogOpenResponse response from `dialog.open`
type DialogOpenResponse struct {
	SlackResponse
	DialogResponseMetadata DialogResponseMetadata `json:"response_metadata"`
}

// DialogResponseMetadata lists the error messages
type DialogResponseMetadata struct {
	Messages []string `json:"messages"`
}

// DialogInputValidationError is an error when user inputs incorrect value to form from within a dialog
type DialogInputValidationError struct {
	Name  string `json:"name"`
	Error string `json:"error"`
}

// DialogInputValidationErrors lists the name of field and that error messages
type DialogInputValidationErrors struct {
	Errors []DialogInputValidationError `json:"errors"`
}

// OpenDialog opens a dialog window where the triggerID originated from.
// EXPERIMENTAL: dialog functionality is currently experimental, api is not considered stable.
func (api *Client) OpenDialog(triggerID string, dialog Dialog) (err error) {
	return api.OpenDialogContext(context.Background(), triggerID, dialog)
}

// OpenDialogContext opens a dialog window where the triggerId originated from with a custom context
// EXPERIMENTAL: dialog functionality is currently experimental, api is not considered stable.
func (api *Client) OpenDialogContext(ctx context.Context, triggerID string, dialog Dialog) (err error) {
	if triggerID == "" {
		return ErrParametersMissing
	}

	req := DialogTrigger{
		TriggerID: triggerID,
		Dialog:    dialog,
	}

	encoded, err := json.Marshal(req)
	if err != nil {
		return err
	}

	response := &DialogOpenResponse{}
	endpoint := api.endpoint + "dialog.open"
	if err := postJSON(ctx, api.httpclient, endpoint, api.token, encoded, response, api); err != nil {
		return err
	}

	return response.Err()
}
<commit_msg>returns validation error msgs on dialog.open<commit_after>package slack

import (
	"context"
	"encoding/json"
	"strings"
)

// InputType is the type of the dialog input type
type InputType string

const (
	// InputTypeText textfield input
	InputTypeText InputType = "text"
	// InputTypeTextArea textarea input
	InputTypeTextArea InputType = "textarea"
	// InputTypeSelect select menus input
	InputTypeSelect InputType = "select"
)

// DialogInput for dialogs input type text or menu
type DialogInput struct {
	Type        InputType `json:"type"`
	Label       string    `json:"label"`
	Name        string    `json:"name"`
	Placeholder string    `json:"placeholder"`
	Optional    bool      `json:"optional"`
}

// DialogTrigger ...
type DialogTrigger struct {
	TriggerID string `json:"trigger_id"` //Required. Must respond within 3 seconds.
	Dialog    Dialog `json:"dialog"`     //Required.
}

// Dialog as in Slack dialogs
// https://api.slack.com/dialogs#option_element_attributes#top-level_dialog_attributes
type Dialog struct {
	TriggerID      string          `json:"trigger_id"`      // Required
	CallbackID     string          `json:"callback_id"`     // Required
	State          string          `json:"state,omitempty"` // Optional
	Title          string          `json:"title"`
	SubmitLabel    string          `json:"submit_label,omitempty"`
	NotifyOnCancel bool            `json:"notify_on_cancel"`
	Elements       []DialogElement `json:"elements"`
}

// DialogElement abstract type for dialogs.
type DialogElement interface{}

// DialogCallback DEPRECATED use InteractionCallback
type DialogCallback InteractionCallback

// DialogSubmissionCallback is sent from Slack when a user submits a form from within a dialog
type DialogSubmissionCallback struct {
	State      string            `json:"state,omitempty"`
	Submission map[string]string `json:"submission"`
}

// DialogOpenResponse response from `dialog.open`
type DialogOpenResponse struct {
	SlackResponse
	DialogResponseMetadata DialogResponseMetadata `json:"response_metadata"`
}

// DialogResponseMetadata lists the error messages
type DialogResponseMetadata struct {
	Messages []string `json:"messages"`
}

// DialogInputValidationError is an error when user inputs incorrect value to form from within a dialog
type DialogInputValidationError struct {
	Name  string `json:"name"`
	Error string `json:"error"`
}

// DialogInputValidationErrors lists the name of field and that error messages
type DialogInputValidationErrors struct {
	Errors []DialogInputValidationError `json:"errors"`
}

// OpenDialog opens a dialog window where the triggerID originated from.
// EXPERIMENTAL: dialog functionality is currently experimental, api is not considered stable.
func (api *Client) OpenDialog(triggerID string, dialog Dialog) (err error) {
	return api.OpenDialogContext(context.Background(), triggerID, dialog)
}

// OpenDialogContext opens a dialog window where the triggerId originated from with a custom context
// EXPERIMENTAL: dialog functionality is currently experimental, api is not considered stable.
func (api *Client) OpenDialogContext(ctx context.Context, triggerID string, dialog Dialog) (err error) {
	if triggerID == "" {
		return ErrParametersMissing
	}

	req := DialogTrigger{
		TriggerID: triggerID,
		Dialog:    dialog,
	}

	encoded, err := json.Marshal(req)
	if err != nil {
		return err
	}

	response := &DialogOpenResponse{}
	endpoint := api.endpoint + "dialog.open"
	if err := postJSON(ctx, api.httpclient, endpoint, api.token, encoded, response, api); err != nil {
		return err
	}

	if len(response.DialogResponseMetadata.Messages) > 0 {
		response.Ok = false
		response.Error += "\n" + strings.Join(response.DialogResponseMetadata.Messages, "\n")
	}

	return response.Err()
}
<|endoftext|>


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

import (
	"strconv"
	"testing"
)

func empty() {
}

func TestGetGoId(t *testing.T) {
	for i := 0; i < 1000; i++ {
		goid := int(GetGoId())
		go empty()
		goid2 := int(GetGoId())
		if goid != goid2 {
			t.Errorf("%v, %v\n", goid, goid2)
		}
	}
}

func TestSeqid(t *testing.T) {
	logger := BasicLogger("test")
	for i := 0; i < 1000; i++ {
		name := strconv.Itoa(i + 1)
		seq := logger.nextSeqid()
		if seq != name {
			t.Errorf("%v, %v\n", seq, name)
		}
	}
}

func TestName(t *testing.T) {
	for i := 0; i < 1000; i++ {
		name := strconv.Itoa(i)
		logger := BasicLogger(name)
		if logger.Name() != name {
			t.Errorf("%v, %v\n", logger.Name(), name)
		}
	}
}
<commit_msg>revise fields_test based on the API changing in fields.go<commit_after>// Copyright 2013, Cong Ding. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// author: Cong Ding <dinggnu@gmail.com>
//
package logging

import (
	"fmt"
	"strconv"
	"testing"
)

func empty() {
}

func TestGetGoId(t *testing.T) {
	for i := 0; i < 1000; i++ {
		goid := int(GetGoId())
		go empty()
		goid2 := int(GetGoId())
		if goid != goid2 {
			t.Errorf("%v, %v\n", goid, goid2)
		}
	}
}

func TestSeqid(t *testing.T) {
	logger := BasicLogger("test")
	for i := 0; i < 1000; i++ {
		l := new(log)
		name := strconv.Itoa(i + 1)
		seq := logger.nextSeqid(l)
		if fmt.Sprintf("%d", seq) != name {
			t.Errorf("%v, %v\n", seq, name)
		}
	}
}

func TestName(t *testing.T) {
	for i := 0; i < 1000; i++ {
		name := strconv.Itoa(i)
		logger := BasicLogger(name)
		l := new(log)
		if logger.lname(l) != name {
			t.Errorf("%v, %v\n", logger.lname(l), name)
		}
	}
}
<|endoftext|>


<commit_before>// Package goarmorlogrusoutputers aim to use logrus
// as mgo (http://gopkg.in/mgo.v2)
// logger (http://godoc.org/gopkg.in/mgo.v2#SetLogger).
package goarmorlogrusoutputers

import (
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

type Outputer interface {
	Output(int, string) error
}

type DummyOutputer struct {
	Logger *logrus.Logger
}

func New(l *logrus.Logger) (*DummyOutputer, error) {
	if l == nil {
		return nil, errors.New("nil pointer")
	}

	return &DummyOutputer{Logger: l}, nil
}

func (l *DummyOutputer) Output(callDepth int, s string) error {
	if l == nil {
		return errors.New("nil pointer dereference")
	}

	l.Logger.WithFields(map[string]interface{}{"callDepth": callDepth}).Debug(s)

	return nil
}
<commit_msg>minor<commit_after>// Package goarmorlogrusoutputers aim to use logrus
// as mgo (http://gopkg.in/mgo.v2)
// logger (http://godoc.org/gopkg.in/mgo.v2#SetLogger).
package goarmorlogrusoutputers

import (
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

type Outputer interface {
	Output(int, string) error
}

type DummyOutputer struct {
	Logger *logrus.Logger
}

func New(l *logrus.Logger) (*DummyOutputer, error) {
	if l == nil {
		return nil, errors.New("nil pointer")
	}

	return &DummyOutputer{Logger: l}, nil
}

func (l *DummyOutputer) Output(callDepth int, s string) error {
	if l == nil {
		return errors.New("nil pointer")
	}

	l.Logger.WithFields(map[string]interface{}{"callDepth": callDepth}).Debug(s)

	return nil
}
<|endoftext|>


<commit_before>package review

import (
	"fmt"

	"github.com/jmoiron/sqlx"
	_ "github.com/lib/pq"
)

var postgres *sqlx.DB

func GetConn(connection *sqlx.DB) {
	postgres = connection
}

//checking if the data exist in the table ws_product_feedback
func IsDataExist(date string, shopID int64) bool {
	// get slave db connection

	query := fmt.Sprintf(
		`
		SELECT
		'x'
		FROM
		ws_active_seller
		WHERE
		shop_id = %d
		`,
		shopID,
	)

	result := postgres.QueryRow(query)
	fmt.Printf("Postgre Query Row = %+v\n", result)

	var data []uint8
	result.Scan(&data)

	fmt.Printf("Postgre Scan = %+v\n", data)

	if data[0] != 'x' {
		return false
	}

	return true
}
<commit_msg>add struct for request and response<commit_after>package review

import (
	"fmt"

	"github.com/jmoiron/sqlx"
	_ "github.com/lib/pq"
)

type (
	reviewResponse struct {
		Status  string
		Message string
		Data    interface{}
	}

	reviewRequest struct {
		UserID string `json:"user_id"`
		ShopID string `json:"shop_id"`
	}
)

var postgres *sqlx.DB

func GetConn(connection *sqlx.DB) {
	postgres = connection
}

//checking if the data exist in the table ws_product_feedback
func IsDataExist(date string, shopID int64) bool {
	// get slave db connection

	query := fmt.Sprintf(
		`
		SELECT
		'x'
		FROM
		ws_active_seller
		WHERE
		shop_id = %d
		`,
		shopID,
	)

	result := postgres.QueryRow(query)
	fmt.Printf("Postgre Query Row = %+v\n", result)

	var data []uint8
	result.Scan(&data)

	fmt.Printf("Postgre Scan = %+v\n", data)

	if data[0] != 'x' {
		return false
	}

	return true
}
<|endoftext|>


<commit_before>// Copyright 2015, Klaus Post, see LICENSE for details.

// Provides a standard test library for drivers.
//
// You can use the functions of this package to test
// your own driver implementations.
//
// If your driver provides both read and write functionality
// and no inbetween synchronization you can use the TestDriver
// function, otherwise read and write is split into TestImport
// and TestData
package drivers

import (
	"github.com/klauspost/password"
	"github.com/klauspost/password/testdata"
	"github.com/klauspost/password/tokenizer"

	"bytes"
	"fmt"
)

type TestDB interface {
	password.DbWriter
	password.DB
}

// TestDriver will test a driver by
// running TestImport followed by TestData
func TestDriver(db TestDB) error {
	err := TestImport(db)
	if err != nil {
		return err
	}
	err = TestData(db)
	if err != nil {
		return err
	}
	return nil
}

var single_val = "j984lop!#\"{}"

// Runs the import part of the tests.
func TestImport(db password.DbWriter) error {
	buf, err := testdata.Asset("testdata.txt.gz")
	if err != nil {
		return err
	}
	in, err := tokenizer.NewGzLine(bytes.NewBuffer(buf))
	if err != nil {
		return err
	}
	writer, ok := db.(password.DbWriter)
	if !ok {
		return fmt.Errorf("%T is not a DbWriter", db)
	}
	err = password.Import(in, writer, nil)
	if err != nil {
		return err
	}
	// Test Add once separately
	err = writer.Add(single_val)
	if err != nil {
		return err
	}
	// .. and test that it is ok to write it again.
	err = writer.Add(single_val)
	if err != nil {
		return err
	}
	return nil
}

func TestData(db password.DB) error {
	for p := range testdata.TestSet {
		if password.SanitizeOK(p, nil) != nil {
			continue
		}
		err := password.Check(p, db, nil)
		if err != password.ErrPasswordInDB {
			return fmt.Errorf("%s not found in database: %v", p, err)
		}
		if err != password.ErrPasswordInDB && err != nil {
			return fmt.Errorf("check %s returned unexpected error: %v", p, err)
		}
	}
	for p := range testdata.NotInSet {
		if password.SanitizeOK(p, nil) != nil {
			continue
		}
		err := password.Check(p, db, nil)
		if err == password.ErrPasswordInDB {
			return fmt.Errorf("%s should NOT be not found in database: %v", p, err)
		} else if err != nil {
			return err
		}
	}

	has, err := db.Has(single_val)
	if !has {
		return fmt.Errorf("%s not found in database. (single insert)", single_val)
	}
	if err != nil {
		return err
	}
	has, err = db.Has(single_val + "*")
	if has {
		return fmt.Errorf("%s* WAS found in database, it shouldn't. (single insert)", single_val)
	}
	if err != nil {
		return err
	}
	return nil
}
<commit_msg>Driver documentation.<commit_after>// Copyright 2015, Klaus Post, see LICENSE for details.

// Provides a standard test library for drivers.
//
// You can use the functions of this package to test
// your own driver implementations.
//
// If your driver provides both read and write functionality
// and no inbetween synchronization you can use the TestDriver
// function, otherwise read and write is split into TestImport
// and TestData
package drivers

import (
	"github.com/klauspost/password"
	"github.com/klauspost/password/testdata"
	"github.com/klauspost/password/tokenizer"

	"bytes"
	"fmt"
)

type TestDB interface {
	password.DbWriter
	password.DB
}

// TestDriver will test a driver by
// running TestImport followed by TestData
func TestDriver(db TestDB) error {
	err := TestImport(db)
	if err != nil {
		return err
	}
	err = TestData(db)
	if err != nil {
		return err
	}
	return nil
}

var single_val = "j984lop!#\"{}"

// TestImport will import about 1500 entries into your
// database. It will test "Add" and "AddMultiple" (if available).
// If any error is returned the test failed.
func TestImport(db password.DbWriter) error {
	buf, err := testdata.Asset("testdata.txt.gz")
	if err != nil {
		return err
	}
	in, err := tokenizer.NewGzLine(bytes.NewBuffer(buf))
	if err != nil {
		return err
	}
	writer, ok := db.(password.DbWriter)
	if !ok {
		return fmt.Errorf("%T is not a DbWriter", db)
	}
	err = password.Import(in, writer, nil)
	if err != nil {
		return err
	}
	// Test Add once separately
	err = writer.Add(single_val)
	if err != nil {
		return err
	}
	// .. and test that it is ok to write it again.
	err = writer.Add(single_val)
	if err != nil {
		return err
	}
	return nil
}

// TestData will test that the data imported with TestImport
// is correctly returned.
// It will test "Has" function of the driver.
// If any error is returned the test failed.
func TestData(db password.DB) error {
	for p := range testdata.TestSet {
		if password.SanitizeOK(p, nil) != nil {
			continue
		}
		err := password.Check(p, db, nil)
		if err != password.ErrPasswordInDB {
			return fmt.Errorf("%s not found in database: %v", p, err)
		}
		if err != password.ErrPasswordInDB && err != nil {
			return fmt.Errorf("check %s returned unexpected error: %v", p, err)
		}
	}
	for p := range testdata.NotInSet {
		if password.SanitizeOK(p, nil) != nil {
			continue
		}
		err := password.Check(p, db, nil)
		if err == password.ErrPasswordInDB {
			return fmt.Errorf("%s should NOT be not found in database: %v", p, err)
		} else if err != nil {
			return err
		}
	}

	has, err := db.Has(single_val)
	if !has {
		return fmt.Errorf("%s not found in database. (single insert)", single_val)
	}
	if err != nil {
		return err
	}
	has, err = db.Has(single_val + "*")
	if has {
		return fmt.Errorf("%s* WAS found in database, it shouldn't. (single insert)", single_val)
	}
	if err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package routes

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

func HomeGet(c *gin.Context) {
    c.HTML(200, "index.html", nil)
}
<commit_msg>update routes structure<commit_after>package routes

import (
    "app/server"

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

var HomeRoute = map[string] *server.Route {

    "GET[/]": server.NewRoute("GET", "/", func(c *gin.Context) {
        c.HTML(200, "index.html", nil)
    }),

}

<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"os/exec"

	"github.com/urfave/cli"
)

type action string

// Possible actions for kubectl command
const (
	actionCreate action = "create"
	actionDelete action = "delete"
	actionApply  action = "apply"
)

// Resources path map
var resourcePath = [3]string{
	"galera/k8s/secrets.yaml",
	"galera/k8s/galera-daemonset.yaml",
	"galera/k8s/galera-service.yaml",
}

func kubeCommand(args ...string) *exec.Cmd {
	return exec.Command("/usr/local/bin/kubectl", args...)
}

func fromFile(act action, path string) []byte {
	cmd := kubeCommand(string(act), "-f", path)
	out, _ := cmd.CombinedOutput()
	return out
}

func main() {

	app := cli.NewApp()
	app.Name = "management-galera"
	app.Usage = "Utility for management Galera Cluster inside Kubernetes"
	app.Version = "0.0.1"

	app.Commands = []cli.Command{
		{
			Name:  "create",
			Usage: "Create Kubernetes resources for Galera Cluster",
			Action: func(c *cli.Context) error {
				for _, path := range resourcePath {
					out := fromFile(actionCreate, path)
					fmt.Print(string(out))
				}
				return nil
			},
		},
		{
			Name:  "delete",
			Usage: "Delete Kubernetes resources for Galera Cluster",
			Action: func(c *cli.Context) error {
				for _, path := range resourcePath {
					out := fromFile(actionDelete, path)
					fmt.Print(string(out))
				}
				return nil
			},
		},
	}

	_ = app.Run(os.Args)
}
<commit_msg>Added DaemonSet validation<commit_after>package main

import (
	"errors"
	"fmt"
	"os"
	"os/exec"

	"github.com/Jeffail/gabs"
	"github.com/urfave/cli"
)

type action string

// Possible actions for kubectl command
const (
	actionCreate action = "create"
	actionDelete action = "delete"
	actionApply  action = "apply"
	actionGet    action = "get"
)

type resourcetype string

// Possible resource types for kubectl get command
const (
	resourcePod       resourcetype = "pod"
	resourceDaemonset resourcetype = "daemonset"
	resourceService   resourcetype = "service"
)

// Resources path map
var resourcePath = [3]string{
	"galera/k8s/secrets.yaml",
	"galera/k8s/galera-daemonset.yaml",
	"galera/k8s/galera-service.yaml",
}

func kubeCommand(args ...string) *exec.Cmd {
	return exec.Command("/usr/local/bin/kubectl", args...)
}

func fromFile(act action, path string, jsonOutput bool) []byte {
	var cmd *exec.Cmd
	if jsonOutput {
		cmd = kubeCommand(string(act), "-f", path, "-o", "json")
	} else {
		cmd = kubeCommand(string(act), "-f", path)
	}
	out, _ := cmd.CombinedOutput()
	return out
}

func getInfoJSON(resourcePath string, jsonPath string) interface{} {
	jsonParsed, err := gabs.ParseJSON(fromFile(actionGet, resourcePath, true))
	if err != nil {
		return nil
	}
	value := jsonParsed.Path(jsonPath).Data()

	return value
}

func checkDaemonSet() (err error) {
	currentNumberScheduled, ok := getInfoJSON("galera/k8s/galera-daemonset.yaml", "status.currentNumberScheduled").(float64)
	if !ok {
		return errors.New("DaemonSet has't a valid state. Check the JSON output.")
	}
	desiredNumberScheduled, ok := getInfoJSON("galera/k8s/galera-daemonset.yaml", "status.desiredNumberScheduled").(float64)
	if !ok {
		return errors.New("DaemonSet has't a valid state. Check the JSON output.")
	}

	if currentNumberScheduled != desiredNumberScheduled {
		return errors.New("DaemonSet has't a valid state. Number of current nodes not equal desired.")
	}

	return err
}

func main() {

	app := cli.NewApp()
	app.Name = "management-galera"
	app.Usage = "Utility for management Galera Cluster inside Kubernetes"
	app.Version = "0.0.1"

	app.Commands = []cli.Command{
		{
			Name:  "create",
			Usage: "Create Kubernetes resources for Galera Cluster",
			Action: func(c *cli.Context) error {
				for _, path := range resourcePath {
					out := fromFile(actionCreate, path, false)
					fmt.Print(string(out))
				}
				return nil
			},
		},
		{
			Name:  "delete",
			Usage: "Delete Kubernetes resources for Galera Cluster",
			Action: func(c *cli.Context) error {
				for _, path := range resourcePath {
					out := fromFile(actionDelete, path, false)
					fmt.Print(string(out))
				}
				return nil
			},
		},
		{
			Name:  "validate",
			Usage: "Validate readiness of pods for all labeled nodes and readiness of service",
			Action: func(c *cli.Context) error {
				err := checkDaemonSet()
				if err != nil {
					fmt.Println(err)
				} else {
					fmt.Println("DaemonSet has a valid state.")
				}
				return nil
			},
		},
	}

	_ = app.Run(os.Args)
}
<|endoftext|>


<commit_before>/*

*/
package mandrake

import (
	"log"
	//"golang.org/x/exp/inotify"
	"github.com/hosom/gomandrake/config"
	"github.com/hosom/gomandrake/inputs"
	//"github.com/hosom/gomandrake/plugin"
)

type Mandrake struct {
	AnalysisPipeline	chan string
	Input				string
}

func NewMandrake(c config.Config) (*Mandrake, error) {
	return &Mandrake{make(chan string), c.Input}, nil
}

func (m *Mandrake) ListenAndServe() {

	i := inputs.INotify{"/tmp/", m.AnalysisPipeline}
	//plugin.CreateListenerAndServe(m.AnalysisPipeline, m.Input)
}

func (m *Mandrake) DispatchAnalysis() {
	for fpath := range m.AnalysisPipeline {
		log.Printf("%s", fpath)
	}
}<commit_msg>updates<commit_after>/*

*/
package mandrake

import (
	"log"
	//"golang.org/x/exp/inotify"
	"github.com/hosom/gomandrake/config"
	"github.com/hosom/gomandrake/inputs"
	//"github.com/hosom/gomandrake/plugin"
)

type Mandrake struct {
	AnalysisPipeline	chan string
	Input				string
}

func NewMandrake(c config.Config) (*Mandrake, error) {
	return &Mandrake{make(chan string), c.Input}, nil
}

func (m *Mandrake) ListenAndServe() {

	i := inputs.INotify{"/tmp/", m.AnalysisPipeline}
	i.Monitor()
	//plugin.CreateListenerAndServe(m.AnalysisPipeline, m.Input)
}

func (m *Mandrake) DispatchAnalysis() {
	for fpath := range m.AnalysisPipeline {
		log.Printf("%s", fpath)
	}
}<|endoftext|>


<commit_before>// Package common provides common operation helpers to the generators
package common

import (
	"strings"
	"text/template"

	"github.com/cihangir/schema"
	"github.com/cihangir/stringext"
)

// TemplateFuncs provides utility functions for template operations
var TemplateFuncs = template.FuncMap{
	"Pointerize":              stringext.Pointerize,
	"ToLower":                 strings.ToLower,
	"ToLowerFirst":            stringext.ToLowerFirst,
	"ToUpperFirst":            stringext.ToUpperFirst,
	"DepunctWithInitialUpper": stringext.DepunctWithInitialUpper,
	"DepunctWithInitialLower": stringext.DepunctWithInitialLower,
	"Equal":                   stringext.Equal,
	"ToFieldName":             stringext.ToFieldName,
	"Argumentize":             schema.Argumentize,
}
<commit_msg>Common: added as comment function into template functions<commit_after>// Package common provides common operation helpers to the generators
package common

import (
	"strings"
	"text/template"

	"github.com/cihangir/schema"
	"github.com/cihangir/stringext"
)

// TemplateFuncs provides utility functions for template operations
var TemplateFuncs = template.FuncMap{
	"Pointerize":              stringext.Pointerize,
	"ToLower":                 strings.ToLower,
	"ToLowerFirst":            stringext.ToLowerFirst,
	"ToUpperFirst":            stringext.ToUpperFirst,
	"AsComment":               stringext.AsComment,
	"DepunctWithInitialUpper": stringext.DepunctWithInitialUpper,
	"DepunctWithInitialLower": stringext.DepunctWithInitialLower,
	"Equal":                   stringext.Equal,
	"ToFieldName":             stringext.ToFieldName,
	"Argumentize":             schema.Argumentize,
}
<|endoftext|>


<commit_before>package client

import (
	"encoding/json"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/pivotal-golang/bytefmt"
	"github.com/snagles/docker-registry-manager/utilities"
)

// GetImage returns the image information for a given tag
// HEAD /v2/<name>/manifests/<reference>
func GetImage(uri string, repository string, tag string) (Image, error) {
	var err error
	// Create and execute Get request
	body, _ := get(uri + "/" + repository + "/manifests/" + tag)

	img := Image{}
	if err := json.Unmarshal(body, &img); err != nil {
		utils.Log.WithFields(logrus.Fields{
			"Error": err,
		}).Error("Unable to unmarshal JSON!")
		return img, err
	}

	// V1 compatibility is an escaped string, so convert it to JSON and then update the key
	for index, v1 := range img.History {
		v1JSON := V1Compatibility{}
		err = json.Unmarshal([]byte(v1.V1CompatibilityStr), &v1JSON)
		if err != nil {
			utils.Log.Error(err)
		}
		v1JSON.SizeStr = bytefmt.ByteSize(uint64(v1JSON.Size))

		// Update the image if we have any size information from the v1 compatibility
		if v1JSON.Size != 0 {
			img.ContainsV1Size = true
		}

		// Get first 8 characters for the short ID
		v1JSON.IDShort = v1JSON.ID[0:7]

		// Remove shell command
		if len(v1JSON.ContainerConfig.Cmd) > 0 {
			v1JSON.ContainerConfig.CmdClean = strings.Replace(v1JSON.ContainerConfig.Cmd[0], "/bin/sh -c #(nop)", "", -1)
		}
		img.History[index].V1Compatibility = v1JSON
	}

	// Update each FsLayer size
	for index, layer := range img.FsLayers {
		// Create and execute Get request
		response, err := head(uri + "/" + repository + "/blobs/" + layer.BlobSum)
		if err != nil {
			utils.Log.Error(err)
		} else {
			img.FsLayers[index].Size = response.ContentLength
			img.FsLayers[index].SizeStr = bytefmt.ByteSize(uint64(response.ContentLength))
		}
	}

	return img, nil
}
<commit_msg>Fixed bytefmt import<commit_after>package client

import (
	"encoding/json"
	"strings"

	"code.cloudfoundry.org/bytefmt"

	"github.com/Sirupsen/logrus"
	"github.com/snagles/docker-registry-manager/utilities"
)

// GetImage returns the image information for a given tag
// HEAD /v2/<name>/manifests/<reference>
func GetImage(uri string, repository string, tag string) (Image, error) {
	var err error
	// Create and execute Get request
	body, _ := get(uri + "/" + repository + "/manifests/" + tag)

	img := Image{}
	if err := json.Unmarshal(body, &img); err != nil {
		utils.Log.WithFields(logrus.Fields{
			"Error": err,
		}).Error("Unable to unmarshal JSON!")
		return img, err
	}

	// V1 compatibility is an escaped string, so convert it to JSON and then update the key
	for index, v1 := range img.History {
		v1JSON := V1Compatibility{}
		err = json.Unmarshal([]byte(v1.V1CompatibilityStr), &v1JSON)
		if err != nil {
			utils.Log.Error(err)
		}
		v1JSON.SizeStr = bytefmt.ByteSize(uint64(v1JSON.Size))

		// Update the image if we have any size information from the v1 compatibility
		if v1JSON.Size != 0 {
			img.ContainsV1Size = true
		}

		// Get first 8 characters for the short ID
		v1JSON.IDShort = v1JSON.ID[0:7]

		// Remove shell command
		if len(v1JSON.ContainerConfig.Cmd) > 0 {
			v1JSON.ContainerConfig.CmdClean = strings.Replace(v1JSON.ContainerConfig.Cmd[0], "/bin/sh -c #(nop)", "", -1)
		}
		img.History[index].V1Compatibility = v1JSON
	}

	// Update each FsLayer size
	for index, layer := range img.FsLayers {
		// Create and execute Get request
		response, err := head(uri + "/" + repository + "/blobs/" + layer.BlobSum)
		if err != nil {
			utils.Log.Error(err)
		} else {
			img.FsLayers[index].Size = response.ContentLength
			img.FsLayers[index].SizeStr = bytefmt.ByteSize(uint64(response.ContentLength))
		}
	}

	return img, nil
}
<|endoftext|>


<commit_before>/*

*/
package main

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

	"github.com/gorilla/mux"
)

func WriteJSON(w http.ResponseWriter, data interface{}) {
	js, err := json.Marshal(data)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.Header().Set("Content-Type", "application/json")
	w.Write(js)
}

type VkvItem struct {
	Key     string `json:"key,omitempty"`
	Value   string `json:"value"`
	Version int    `json:"version"`
}

func vkvHandler() func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		switch r.Method {
		case "GET":
			vars := mux.Vars(r)
			res := &VkvItem{
				Key: vars["key"],
			}
			WriteJSON(w, res)
		case "HEAD":
			exists := true
			if exists {
				return
			}
			http.Error(w, http.StatusText(404), 404)
			return
		case "PUT":
			vars := mux.Vars(r)
			k := vars["key"]
			v := r.FormValue("value")
			res := &VkvItem{
				Key:   k,
				Value: v,
			}
			WriteJSON(w, res)
		default:
			w.WriteHeader(http.StatusMethodNotAllowed)
		}
	}
}

type VersionsReponse struct {
	Key      string     `json:"key"`
	Versions []*VkvItem `json:"versions"`
}

func vkvVersionsHandler() func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		switch r.Method {
		case "GET":
			vars := mux.Vars(r)
			res := &VersionsReponse{
				Key:      vars["key"],
				Versions: []*VkvItem{},
			}
			WriteJSON(w, res)
			return
		default:
			w.WriteHeader(http.StatusMethodNotAllowed)
		}
	}
}

func main() {
	r := mux.NewRouter()
	r.HandleFunc("/api/v1/vkv/{key}", vkvHandler())
	r.HandleFunc("/api/v1/vkv/{key}/versions", vkvVersionsHandler())
	http.Handle("/", r)
	http.ListenAndServe(":8050", nil)
}
<commit_msg>api: working vkv endpoint<commit_after>/*

*/
package main

import (
	"encoding/json"
	"net/http"
	"strconv"
	"time"

	"github.com/gorilla/mux"
	"github.com/tsileo/blobstash/vkv"
)

func WriteJSON(w http.ResponseWriter, data interface{}) {
	js, err := json.Marshal(data)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.Header().Set("Content-Type", "application/json")
	w.Write(js)
}
func vkvHandler(db *vkv.DB) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		switch r.Method {
		case "GET":
			vars := mux.Vars(r)
			iversion := -1
			version := r.URL.Query().Get("version")
			if version != "" {
				iver, err := strconv.Atoi(version)
				if err != nil {
					panic(err)
				}
				iversion = iver
			}
			res, err := db.Get(vars["key"], iversion)
			if err != nil {
				panic(err)
			}
			WriteJSON(w, res)
		case "HEAD":
			exists := true
			if exists {
				return
			}
			http.Error(w, http.StatusText(404), 404)
			return
		case "PUT":
			vars := mux.Vars(r)
			k := vars["key"]
			v := r.FormValue("value")
			res, err := db.Put(k, v, -1)
			if err != nil {
				panic(err)
			}
			WriteJSON(w, res)
		default:
			w.WriteHeader(http.StatusMethodNotAllowed)
		}
	}
}

func vkvVersionsHandler(db *vkv.DB) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		switch r.Method {
		case "GET":
			vars := mux.Vars(r)
			res, err := db.Versions(vars["key"], 0, int(time.Now().UTC().UnixNano()), 0)
			if err != nil {
				panic(err)
			}
			WriteJSON(w, res)
			return
		default:
			w.WriteHeader(http.StatusMethodNotAllowed)
		}
	}
}

func main() {
	db, err := vkv.New("devdb")
	if err != nil {
		panic(err)
	}
	defer db.Close()
	r := mux.NewRouter()
	r.HandleFunc("/api/v1/vkv/{key}", vkvHandler(db))
	r.HandleFunc("/api/v1/vkv/{key}/versions", vkvVersionsHandler(db))
	http.Handle("/", r)
	http.ListenAndServe(":8050", nil)
}
<|endoftext|>


<commit_before>package api

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

	"github.com/ajvb/kala/job"
	"github.com/ajvb/kala/utils/logging"

	"github.com/gorilla/mux"
)

const (
	// Base API v1 Path
	ApiUrlPrefix = "/api/v1/"

	JobPath    = "job/"
	ApiJobPath = ApiUrlPrefix + JobPath

	contentType     = "Content-Type"
	jsonContentType = "application/json;charset=UTF-8"
)

var (
	log = logging.GetLogger("api")
)

type KalaStatsResponse struct {
	Stats *job.KalaStats
}

// HandleKalaStatsRequest is the hanlder for getting system-level metrics
// /api/v1/stats
func HandleKalaStatsRequest(w http.ResponseWriter, r *http.Request) {
	resp := &KalaStatsResponse{
		Stats: job.NewKalaStats(),
	}

	w.Header().Set(contentType, jsonContentType)
	w.WriteHeader(http.StatusOK)
	if err := json.NewEncoder(w).Encode(resp); err != nil {
		log.Error("Error occured when marshalling response: %s", err)
		return
	}

}

type ListJobStatsResponse struct {
	JobStats []*job.JobStat `json:"job_stats"`
}

// HandleListJobStatsRequest is the handler for getting job-specific stats
// /api/v1/job/stats/{id}
func HandleListJobStatsRequest(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id := vars["id"]
	j := job.AllJobs.Get(id)

	resp := &ListJobStatsResponse{
		JobStats: j.Stats,
	}

	w.Header().Set(contentType, jsonContentType)
	w.WriteHeader(http.StatusOK)
	if err := json.NewEncoder(w).Encode(resp); err != nil {
		log.Error("Error occured when marshalling response: %s", err)
		return
	}

}

type ListJobsResponse struct {
	Jobs map[string]*job.Job `json:"jobs"`
}

// HandleListJobs responds with an array of all Jobs within the server,
// active or disabled.
func HandleListJobsRequest(w http.ResponseWriter, r *http.Request) {
	resp := &ListJobsResponse{
		Jobs: job.AllJobs.GetAll(),
	}

	w.Header().Set(contentType, jsonContentType)
	w.WriteHeader(http.StatusOK)
	if err := json.NewEncoder(w).Encode(resp); err != nil {
		log.Error("Error occured when marshalling response: %s", err)
		return
	}

}

type AddJobResponse struct {
	Id string `json:"id"`
}

func unmarshalNewJob(r *http.Request) (*job.Job, error) {
	newJob := &job.Job{}

	body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1048576))
	if err != nil {
		log.Error("Error occured when reading r.Body: %s", err)
		return nil, err
	}
	defer r.Body.Close()

	if err := json.Unmarshal(body, newJob); err != nil {
		log.Error("Error occured when unmarshalling data: %s", err)
		return nil, err
	}

	return newJob, nil
}

// HandleAddJob takes a job object and unmarshals it to a Job type,
// and then throws the job in the schedulers.
func HandleAddJob(w http.ResponseWriter, r *http.Request) {
	newJob, err := unmarshalNewJob(r)
	if err != nil {
		http.Error(w, err.Error(), 400)
		return
	}

	err = newJob.Init()
	if err != nil {
		errStr := "Error occured when initializing the job"
		log.Error(errStr+": %s", err)
		http.Error(w, errStr, 400)
		return
	}
	job.AllJobs.Set(newJob)

	resp := &AddJobResponse{
		Id: newJob.Id,
	}

	w.Header().Set(contentType, jsonContentType)
	w.WriteHeader(http.StatusCreated)
	if err := json.NewEncoder(w).Encode(resp); err != nil {
		log.Error("Error occured when marshalling response: %s", err)
		return
	}
}

// HandleJobRequest routes requests to /api/v1/job/{id} to either
// handleDeleteJob if its a DELETE or handleGetJob if its a GET request.
func HandleJobRequest(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id := vars["id"]

	j := job.AllJobs.Get(id)
	if j == nil {
		w.WriteHeader(http.StatusNotFound)
		return
	}

	if r.Method == "DELETE" {
		handleDeleteJob(w, r, j)
	} else if r.Method == "GET" {
		handleGetJob(w, r, j)
	}
}

func handleDeleteJob(w http.ResponseWriter, r *http.Request, j *job.Job) {
	j.Delete()

	w.WriteHeader(http.StatusNoContent)
}

type JobResponse struct {
	Job *job.Job `json:"job"`
}

func handleGetJob(w http.ResponseWriter, r *http.Request, j *job.Job) {
	resp := &JobResponse{
		Job: j,
	}

	w.Header().Set(contentType, jsonContentType)
	w.WriteHeader(http.StatusOK)
	if err := json.NewEncoder(w).Encode(resp); err != nil {
		log.Error("Error occured when marshalling response: %s", err)
		return
	}
}

// HandleStartJobRequest is the handler for manually starting jobs
// /api/v1/job/start/{id}
func HandleStartJobRequest(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id := vars["id"]
	j := job.AllJobs.Get(id)

	j.Run()

	w.WriteHeader(http.StatusNoContent)
}

// SetupApiRoutes is used within main to initialize all of the routes
func SetupApiRoutes(r *mux.Router) {
	// Route for creating a job
	r.HandleFunc(ApiJobPath, HandleAddJob).Methods("POST")
	r.HandleFunc(ApiUrlPrefix+"job", HandleAddJob).Methods("POST")
	// Route for deleting and getting a job
	r.HandleFunc(ApiJobPath+"{id}", HandleJobRequest).Methods("DELETE", "GET")
	r.HandleFunc(ApiJobPath+"{id}/", HandleJobRequest).Methods("DELETE", "GET")
	// Route for getting job stats
	r.HandleFunc(ApiJobPath+"stats/{id}", HandleListJobStatsRequest).Methods("GET")
	r.HandleFunc(ApiJobPath+"stats/{id}/", HandleListJobStatsRequest).Methods("GET")
	// Route for listing all jops
	r.HandleFunc(ApiJobPath, HandleListJobsRequest).Methods("GET")
	r.HandleFunc(ApiUrlPrefix+"job", HandleListJobsRequest).Methods("GET")
	// Route for manually start a job
	r.HandleFunc(ApiJobPath+"start/{id}", HandleStartJobRequest).Methods("POST")
	r.HandleFunc(ApiJobPath+"start/{id}/", HandleStartJobRequest).Methods("POST")
	// Route for getting app-level metrics
	r.HandleFunc(ApiUrlPrefix+"stats", HandleKalaStatsRequest).Methods("GET")
	r.HandleFunc(ApiUrlPrefix+"stats/", HandleKalaStatsRequest).Methods("GET")
}
<commit_msg>Made id retrieval a one-liner in api.go<commit_after>package api

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

	"github.com/ajvb/kala/job"
	"github.com/ajvb/kala/utils/logging"

	"github.com/gorilla/mux"
)

const (
	// Base API v1 Path
	ApiUrlPrefix = "/api/v1/"

	JobPath    = "job/"
	ApiJobPath = ApiUrlPrefix + JobPath

	contentType     = "Content-Type"
	jsonContentType = "application/json;charset=UTF-8"
)

var (
	log = logging.GetLogger("api")
)

type KalaStatsResponse struct {
	Stats *job.KalaStats
}

// HandleKalaStatsRequest is the hanlder for getting system-level metrics
// /api/v1/stats
func HandleKalaStatsRequest(w http.ResponseWriter, r *http.Request) {
	resp := &KalaStatsResponse{
		Stats: job.NewKalaStats(),
	}

	w.Header().Set(contentType, jsonContentType)
	w.WriteHeader(http.StatusOK)
	if err := json.NewEncoder(w).Encode(resp); err != nil {
		log.Error("Error occured when marshalling response: %s", err)
		return
	}

}

type ListJobStatsResponse struct {
	JobStats []*job.JobStat `json:"job_stats"`
}

// HandleListJobStatsRequest is the handler for getting job-specific stats
// /api/v1/job/stats/{id}
func HandleListJobStatsRequest(w http.ResponseWriter, r *http.Request) {
	id := mux.Vars(r)["id"]
	j := job.AllJobs.Get(id)

	resp := &ListJobStatsResponse{
		JobStats: j.Stats,
	}

	w.Header().Set(contentType, jsonContentType)
	w.WriteHeader(http.StatusOK)
	if err := json.NewEncoder(w).Encode(resp); err != nil {
		log.Error("Error occured when marshalling response: %s", err)
		return
	}

}

type ListJobsResponse struct {
	Jobs map[string]*job.Job `json:"jobs"`
}

// HandleListJobs responds with an array of all Jobs within the server,
// active or disabled.
func HandleListJobsRequest(w http.ResponseWriter, r *http.Request) {
	resp := &ListJobsResponse{
		Jobs: job.AllJobs.GetAll(),
	}

	w.Header().Set(contentType, jsonContentType)
	w.WriteHeader(http.StatusOK)
	if err := json.NewEncoder(w).Encode(resp); err != nil {
		log.Error("Error occured when marshalling response: %s", err)
		return
	}

}

type AddJobResponse struct {
	Id string `json:"id"`
}

func unmarshalNewJob(r *http.Request) (*job.Job, error) {
	newJob := &job.Job{}

	body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1048576))
	if err != nil {
		log.Error("Error occured when reading r.Body: %s", err)
		return nil, err
	}
	defer r.Body.Close()

	if err := json.Unmarshal(body, newJob); err != nil {
		log.Error("Error occured when unmarshalling data: %s", err)
		return nil, err
	}

	return newJob, nil
}

// HandleAddJob takes a job object and unmarshals it to a Job type,
// and then throws the job in the schedulers.
func HandleAddJob(w http.ResponseWriter, r *http.Request) {
	newJob, err := unmarshalNewJob(r)
	if err != nil {
		http.Error(w, err.Error(), 400)
		return
	}

	err = newJob.Init()
	if err != nil {
		errStr := "Error occured when initializing the job"
		log.Error(errStr+": %s", err)
		http.Error(w, errStr, 400)
		return
	}
	job.AllJobs.Set(newJob)

	resp := &AddJobResponse{
		Id: newJob.Id,
	}

	w.Header().Set(contentType, jsonContentType)
	w.WriteHeader(http.StatusCreated)
	if err := json.NewEncoder(w).Encode(resp); err != nil {
		log.Error("Error occured when marshalling response: %s", err)
		return
	}
}

// HandleJobRequest routes requests to /api/v1/job/{id} to either
// handleDeleteJob if its a DELETE or handleGetJob if its a GET request.
func HandleJobRequest(w http.ResponseWriter, r *http.Request) {
	id := mux.Vars(r)["id"]

	j := job.AllJobs.Get(id)
	if j == nil {
		w.WriteHeader(http.StatusNotFound)
		return
	}

	if r.Method == "DELETE" {
		handleDeleteJob(w, r, j)
	} else if r.Method == "GET" {
		handleGetJob(w, r, j)
	}
}

func handleDeleteJob(w http.ResponseWriter, r *http.Request, j *job.Job) {
	j.Delete()

	w.WriteHeader(http.StatusNoContent)
}

type JobResponse struct {
	Job *job.Job `json:"job"`
}

func handleGetJob(w http.ResponseWriter, r *http.Request, j *job.Job) {
	resp := &JobResponse{
		Job: j,
	}

	w.Header().Set(contentType, jsonContentType)
	w.WriteHeader(http.StatusOK)
	if err := json.NewEncoder(w).Encode(resp); err != nil {
		log.Error("Error occured when marshalling response: %s", err)
		return
	}
}

// HandleStartJobRequest is the handler for manually starting jobs
// /api/v1/job/start/{id}
func HandleStartJobRequest(w http.ResponseWriter, r *http.Request) {
	id := mux.Vars(r)["id"]
	j := job.AllJobs.Get(id)

	j.Run()

	w.WriteHeader(http.StatusNoContent)
}

// SetupApiRoutes is used within main to initialize all of the routes
func SetupApiRoutes(r *mux.Router) {
	// Route for creating a job
	r.HandleFunc(ApiJobPath, HandleAddJob).Methods("POST")
	r.HandleFunc(ApiUrlPrefix+"job", HandleAddJob).Methods("POST")
	// Route for deleting and getting a job
	r.HandleFunc(ApiJobPath+"{id}", HandleJobRequest).Methods("DELETE", "GET")
	r.HandleFunc(ApiJobPath+"{id}/", HandleJobRequest).Methods("DELETE", "GET")
	// Route for getting job stats
	r.HandleFunc(ApiJobPath+"stats/{id}", HandleListJobStatsRequest).Methods("GET")
	r.HandleFunc(ApiJobPath+"stats/{id}/", HandleListJobStatsRequest).Methods("GET")
	// Route for listing all jops
	r.HandleFunc(ApiJobPath, HandleListJobsRequest).Methods("GET")
	r.HandleFunc(ApiUrlPrefix+"job", HandleListJobsRequest).Methods("GET")
	// Route for manually start a job
	r.HandleFunc(ApiJobPath+"start/{id}", HandleStartJobRequest).Methods("POST")
	r.HandleFunc(ApiJobPath+"start/{id}/", HandleStartJobRequest).Methods("POST")
	// Route for getting app-level metrics
	r.HandleFunc(ApiUrlPrefix+"stats", HandleKalaStatsRequest).Methods("GET")
	r.HandleFunc(ApiUrlPrefix+"stats/", HandleKalaStatsRequest).Methods("GET")
}
<|endoftext|>


<commit_before>package api

import (
	"fmt"
	log "github.com/Sirupsen/logrus"

	"github.com/kolo/xmlrpc"
)

/*
 * RPC Client and secret key
 */
type Rpc struct {
	RPCClient xmlrpc.Client
	Key       string
}

/**
 *
 * Creates an RPCClient with endpoint and returns it
 *
 **/
func NewRPCClient(endpoint string, username string, password string) (*Rpc, error) {
	RPCclient, err := xmlrpc.NewClient(endpoint, nil)

	if err != nil {
		return nil, fmt.Errorf("unable to connect with one %q: %s", endpoint, err)
	}
	log.Debugf("[one-go] -- client --- \ncommand: %s\nconnected: %s\n[one-go] --- client --- \n", endpoint)

	return &Rpc{
		RPCClient: *RPCclient,
		Key:       username + ":" + password}, nil
}

/**
 *
 * Do an RPC Call
 *
 **/
func (c *Rpc) Call(RPC xmlrpc.Client, command string, args []interface{}) ([]interface{}, error) {
	log.Debugf("[one-go] -- request --- \ncommand: %s\nargs: %s\n[one-go] --- request --- \n", command, args)
	result := []interface{}{}
	err := RPC.Call(command, args, &result)
	if err != nil {
		return nil, fmt.Errorf("one rpc execution %q failure: %s", command, err)
	}
	log.Debugf("[one-go] -- response --- \nresult: %s\n[one-go] --- request --- \n", result)
	return result, nil
}
<commit_msg>minor log msg change<commit_after>package api

import (
	"fmt"
	log "github.com/Sirupsen/logrus"

	"github.com/kolo/xmlrpc"
)

/*
 * RPC Client and secret key
 */
type Rpc struct {
	RPCClient xmlrpc.Client
	Key       string
}

/**
 *
 * Creates an RPCClient with endpoint and returns it
 *
 **/
func NewRPCClient(endpoint string, username string, password string) (*Rpc, error) {
	RPCclient, err := xmlrpc.NewClient(endpoint, nil)

	if err != nil {
		return nil, fmt.Errorf("unable to connect with one %q: %s", endpoint, err)
	}
	log.Debugf("[one-go] -- client --- \nconnected: %s\n[one-go] --- client --- \n", endpoint)

	return &Rpc{
		RPCClient: *RPCclient,
		Key:       username + ":" + password}, nil
}

/**
 *
 * Do an RPC Call
 *
 **/
func (c *Rpc) Call(RPC xmlrpc.Client, command string, args []interface{}) ([]interface{}, error) {
	log.Debugf("[one-go] -- request --- \ncommand: %s\nargs: %s\n[one-go] --- request --- \n", command, args)
	result := []interface{}{}
	err := RPC.Call(command, args, &result)
	if err != nil {
		return nil, fmt.Errorf("one rpc execution %q failure: %s", command, err)
	}
	log.Debugf("[one-go] -- response --- \nresult: %s\n[one-go] --- request --- \n", result)
	return result, nil
}
<|endoftext|>


<commit_before>package api

import (
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"log"
	"net/http"
	"os"

	statsd "gopkg.in/alexcesaro/statsd.v2"

	"github.com/mark-adams/inc/backends"
	"github.com/pressly/chi"
)

// DefaultRouter is a mux populated with the default set of handlers for the inc app
var DefaultRouter *chi.Mux
var metrics MetricCollector

func getRandomID() (string, error) {
	newID := make([]byte, 16)
	_, err := rand.Read(newID)
	if err != nil {
		return "", err
	}

	return hex.EncodeToString(newID), nil
}

// NewToken creates a new counting token
func NewToken(w http.ResponseWriter, r *http.Request) {
	metrics.Increment("inc.api.create_token")

	id, err := getRandomID()
	if err != nil {
		log.Printf("Error: %s", err)
		http.Error(w, "😞 Something bad happened... try again?", http.StatusInternalServerError)
		return
	}
	db, err := backends.GetBackend()
	if err != nil {
		log.Printf("Database error: %s", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer db.Close()

	err = db.CreateToken(id)
	if err != nil {
		log.Printf("Insert error: %s", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.WriteHeader(http.StatusCreated)
	fmt.Fprintf(w, id)
}

// IncrementToken increments an existing token
func IncrementToken(w http.ResponseWriter, r *http.Request) {
	metrics.Increment("inc.api.increment_token")
	db, err := backends.GetBackend()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer db.Close()

	count, err := db.IncrementAndGetToken(chi.URLParam(r, "token"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	fmt.Fprintf(w, "%d", count)
}

// IncrementTokenNamespace increments a specific namespace inside a specific token's context
func IncrementTokenNamespace(w http.ResponseWriter, r *http.Request) {
	metrics.Increment("inc.api.increment_namespace_token")
	db, err := backends.GetBackend()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer db.Close()

	count, err := db.IncrementAndGetNamespacedToken(chi.URLParam(r, "token"), chi.URLParam(r, "namespace"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	fmt.Fprintf(w, "%d", count)
}

func init() {
	var err error

	router := chi.NewRouter()

	metrics = &NullMetricsCollector{}
	metrics, err = statsd.New(statsd.Address(os.Getenv("STATSD_HOST")))
	if err != nil && os.Getenv("STATSD_HOST") != "" {
		log.Printf("error initializing metrics: %s", err)
		metrics = &NullMetricsCollector{}
	}

	router.Get("/_healthcheck", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "OK")
	})

	router.Post("/new", NewToken)
	router.Route("/:token", func(r chi.Router) {
		r.Put("/", IncrementToken)
		r.Put("/:namespace", IncrementTokenNamespace)
	})

	DefaultRouter = router
}
<commit_msg>Fixed a bug resulting from a chi upgrade<commit_after>package api

import (
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"log"
	"net/http"
	"os"

	statsd "gopkg.in/alexcesaro/statsd.v2"

	"github.com/mark-adams/inc/backends"
	"github.com/pressly/chi"
)

// DefaultRouter is a mux populated with the default set of handlers for the inc app
var DefaultRouter *chi.Mux
var metrics MetricCollector

func getRandomID() (string, error) {
	newID := make([]byte, 16)
	_, err := rand.Read(newID)
	if err != nil {
		return "", err
	}

	return hex.EncodeToString(newID), nil
}

// NewToken creates a new counting token
func NewToken(w http.ResponseWriter, r *http.Request) {
	metrics.Increment("inc.api.create_token")

	id, err := getRandomID()
	if err != nil {
		log.Printf("Error: %s", err)
		http.Error(w, "😞 Something bad happened... try again?", http.StatusInternalServerError)
		return
	}
	db, err := backends.GetBackend()
	if err != nil {
		log.Printf("Database error: %s", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer db.Close()

	err = db.CreateToken(id)
	if err != nil {
		log.Printf("Insert error: %s", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.WriteHeader(http.StatusCreated)
	fmt.Fprintf(w, id)
}

// IncrementToken increments an existing token
func IncrementToken(w http.ResponseWriter, r *http.Request) {
	metrics.Increment("inc.api.increment_token")
	db, err := backends.GetBackend()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer db.Close()

	count, err := db.IncrementAndGetToken(chi.URLParam(r, "token"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	fmt.Fprintf(w, "%d", count)
}

// IncrementTokenNamespace increments a specific namespace inside a specific token's context
func IncrementTokenNamespace(w http.ResponseWriter, r *http.Request) {
	metrics.Increment("inc.api.increment_namespace_token")
	db, err := backends.GetBackend()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer db.Close()

	count, err := db.IncrementAndGetNamespacedToken(chi.URLParam(r, "token"), chi.URLParam(r, "namespace"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	fmt.Fprintf(w, "%d", count)
}

func init() {
	var err error

	router := chi.NewRouter()

	metrics = &NullMetricsCollector{}
	metrics, err = statsd.New(statsd.Address(os.Getenv("STATSD_HOST")))
	if err != nil && os.Getenv("STATSD_HOST") != "" {
		log.Printf("error initializing metrics: %s", err)
		metrics = &NullMetricsCollector{}
	}

	router.Get("/_healthcheck", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "OK")
	})

	router.Post("/new", NewToken)
	router.Route("/{token}", func(r chi.Router) {
		r.Put("/", IncrementToken)
		r.Put("/{namespace}", IncrementTokenNamespace)
	})
	router.Post("/new", NewToken)

	DefaultRouter = router
}
<|endoftext|>


<commit_before>package digest

import (
	"fmt"
	"hash"
	"io"
	"regexp"
	"strings"
)

const (
	// DigestSha256EmptyTar is the canonical sha256 digest of empty data
	DigestSha256EmptyTar = "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
)

// Digest allows simple protection of hex formatted digest strings, prefixed
// by their algorithm. Strings of type Digest have some guarantee of being in
// the correct format and it provides quick access to the components of a
// digest string.
//
// The following is an example of the contents of Digest types:
//
// 	sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc
//
// This allows to abstract the digest behind this type and work only in those
// terms.
type Digest string

// NewDigest returns a Digest from alg and a hash.Hash object.
func NewDigest(alg Algorithm, h hash.Hash) Digest {
	return Digest(fmt.Sprintf("%s:%x", alg, h.Sum(nil)))
}

// NewDigestFromHex returns a Digest from alg and a the hex encoded digest.
func NewDigestFromHex(alg, hex string) Digest {
	return Digest(fmt.Sprintf("%s:%s", alg, hex))
}

// DigestRegexp matches valid digest types.
var DigestRegexp = regexp.MustCompile(`[a-zA-Z0-9-_+.]+:[a-fA-F0-9]+`)

// DigestRegexpAnchored matches valid digest types, anchored to the start and end of the match.
var DigestRegexpAnchored = regexp.MustCompile(`^` + DigestRegexp.String() + `$`)

var (
	// ErrDigestInvalidFormat returned when digest format invalid.
	ErrDigestInvalidFormat = fmt.Errorf("invalid checksum digest format")

	// ErrDigestInvalidLength returned when digest has invalid length.
	ErrDigestInvalidLength = fmt.Errorf("invalid checksum digest length")

	// ErrDigestUnsupported returned when the digest algorithm is unsupported.
	ErrDigestUnsupported = fmt.Errorf("unsupported digest algorithm")
)

// ParseDigest parses s and returns the validated digest object. An error will
// be returned if the format is invalid.
func ParseDigest(s string) (Digest, error) {
	d := Digest(s)

	return d, d.Validate()
}

// FromReader returns the most valid digest for the underlying content using
// the canonical digest algorithm.
func FromReader(rd io.Reader) (Digest, error) {
	return Canonical.FromReader(rd)
}

// FromBytes digests the input and returns a Digest.
func FromBytes(p []byte) Digest {
	return Canonical.FromBytes(p)
}

// Validate checks that the contents of d is a valid digest, returning an
// error if not.
func (d Digest) Validate() error {
	s := string(d)

	if !DigestRegexpAnchored.MatchString(s) {
		return ErrDigestInvalidFormat
	}

	i := strings.Index(s, ":")
	if i < 0 {
		return ErrDigestInvalidFormat
	}

	// case: "sha256:" with no hex.
	if i+1 == len(s) {
		return ErrDigestInvalidFormat
	}

	switch algorithm := Algorithm(s[:i]); algorithm {
	case SHA256, SHA384, SHA512:
		if algorithm.Size()*2 != len(s[i+1:]) {
			return ErrDigestInvalidLength
		}
		break
	default:
		return ErrDigestUnsupported
	}

	return nil
}

// Algorithm returns the algorithm portion of the digest. This will panic if
// the underlying digest is not in a valid format.
func (d Digest) Algorithm() Algorithm {
	return Algorithm(d[:d.sepIndex()])
}

// Hex returns the hex digest portion of the digest. This will panic if the
// underlying digest is not in a valid format.
func (d Digest) Hex() string {
	return string(d[d.sepIndex()+1:])
}

func (d Digest) String() string {
	return string(d)
}

func (d Digest) sepIndex() int {
	i := strings.Index(string(d), ":")

	if i < 0 {
		panic("could not find ':' in digest: " + d)
	}

	return i
}
<commit_msg>digest: add NewDigestFromBytes for completeness<commit_after>package digest

import (
	"fmt"
	"hash"
	"io"
	"regexp"
	"strings"
)

const (
	// DigestSha256EmptyTar is the canonical sha256 digest of empty data
	DigestSha256EmptyTar = "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
)

// Digest allows simple protection of hex formatted digest strings, prefixed
// by their algorithm. Strings of type Digest have some guarantee of being in
// the correct format and it provides quick access to the components of a
// digest string.
//
// The following is an example of the contents of Digest types:
//
// 	sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc
//
// This allows to abstract the digest behind this type and work only in those
// terms.
type Digest string

// NewDigest returns a Digest from alg and a hash.Hash object.
func NewDigest(alg Algorithm, h hash.Hash) Digest {
	return NewDigestFromBytes(alg, h.Sum(nil))
}

// NewDigestFromBytes returns a new digest from the byte contents of p.
// Typically, this can come from hash.Hash.Sum(...) or xxx.SumXXX(...)
// functions. This is also useful for rebuilding digests from binary
// serializations.
func NewDigestFromBytes(alg Algorithm, p []byte) Digest {
	return Digest(fmt.Sprintf("%s:%x", alg, p))
}

// NewDigestFromHex returns a Digest from alg and a the hex encoded digest.
func NewDigestFromHex(alg, hex string) Digest {
	return Digest(fmt.Sprintf("%s:%s", alg, hex))
}

// DigestRegexp matches valid digest types.
var DigestRegexp = regexp.MustCompile(`[a-zA-Z0-9-_+.]+:[a-fA-F0-9]+`)

// DigestRegexpAnchored matches valid digest types, anchored to the start and end of the match.
var DigestRegexpAnchored = regexp.MustCompile(`^` + DigestRegexp.String() + `$`)

var (
	// ErrDigestInvalidFormat returned when digest format invalid.
	ErrDigestInvalidFormat = fmt.Errorf("invalid checksum digest format")

	// ErrDigestInvalidLength returned when digest has invalid length.
	ErrDigestInvalidLength = fmt.Errorf("invalid checksum digest length")

	// ErrDigestUnsupported returned when the digest algorithm is unsupported.
	ErrDigestUnsupported = fmt.Errorf("unsupported digest algorithm")
)

// ParseDigest parses s and returns the validated digest object. An error will
// be returned if the format is invalid.
func ParseDigest(s string) (Digest, error) {
	d := Digest(s)

	return d, d.Validate()
}

// FromReader returns the most valid digest for the underlying content using
// the canonical digest algorithm.
func FromReader(rd io.Reader) (Digest, error) {
	return Canonical.FromReader(rd)
}

// FromBytes digests the input and returns a Digest.
func FromBytes(p []byte) Digest {
	return Canonical.FromBytes(p)
}

// Validate checks that the contents of d is a valid digest, returning an
// error if not.
func (d Digest) Validate() error {
	s := string(d)

	if !DigestRegexpAnchored.MatchString(s) {
		return ErrDigestInvalidFormat
	}

	i := strings.Index(s, ":")
	if i < 0 {
		return ErrDigestInvalidFormat
	}

	// case: "sha256:" with no hex.
	if i+1 == len(s) {
		return ErrDigestInvalidFormat
	}

	switch algorithm := Algorithm(s[:i]); algorithm {
	case SHA256, SHA384, SHA512:
		if algorithm.Size()*2 != len(s[i+1:]) {
			return ErrDigestInvalidLength
		}
		break
	default:
		return ErrDigestUnsupported
	}

	return nil
}

// Algorithm returns the algorithm portion of the digest. This will panic if
// the underlying digest is not in a valid format.
func (d Digest) Algorithm() Algorithm {
	return Algorithm(d[:d.sepIndex()])
}

// Hex returns the hex digest portion of the digest. This will panic if the
// underlying digest is not in a valid format.
func (d Digest) Hex() string {
	return string(d[d.sepIndex()+1:])
}

func (d Digest) String() string {
	return string(d)
}

func (d Digest) sepIndex() int {
	i := strings.Index(string(d), ":")

	if i < 0 {
		panic("could not find ':' in digest: " + d)
	}

	return i
}
<|endoftext|>


<commit_before>// The MIT License (MIT)

// Copyright (c) 2016 Maciej Borzecki

// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package models

import (
	"github.com/bboozzoo/q3stats/store"
	"github.com/jinzhu/gorm"
	"log"
)

type PlayerGlobalStats struct {
	Kills    uint
	Deaths   uint
	Suicides uint
	Matches  uint
	Wins     uint
	// global weapon stats
	Weapons []WeaponStat
	// global item stats
	Items []ItemStat
}

func getPlayerKillDeathSuicide(db *gorm.DB, player uint) (uint, uint, uint) {
	var result = struct {
		Kills    uint
		Deaths   uint
		Suicides uint
	}{}
	db.Model(&PlayerMatchStat{}).
		Select("sum(kills) as kills, sum(deaths) as deaths, sum(suicides)as suicides").
		Joins("join aliases on player_match_stats.alias_id = aliases.id").
		Where("aliases.player_id = ?", player).
		Scan(&result)
	log.Printf("stats: %+v", result)

	return result.Kills, result.Deaths, result.Suicides
}

func GetPlayerGlobaStats(store store.DB, player uint) *PlayerGlobalStats {
	db := store.Conn()

	pgs := PlayerGlobalStats{}
	pgs.Kills, pgs.Deaths, pgs.Suicides = getPlayerKillDeathSuicide(db,
		player)

	return &pgs
}
<commit_msg>models/playerglobal: count player matches<commit_after>// The MIT License (MIT)

// Copyright (c) 2016 Maciej Borzecki

// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package models

import (
	"github.com/bboozzoo/q3stats/store"
	"github.com/jinzhu/gorm"
	"log"
)

type PlayerGlobalStats struct {
	Kills    uint
	Deaths   uint
	Suicides uint
	Matches  uint
	Wins     uint
	// global weapon stats
	Weapons []WeaponStat
	// global item stats
	Items []ItemStat
}

func getPlayerKillDeathSuicide(db *gorm.DB, player uint) (uint, uint, uint) {
	var result = struct {
		Kills    uint
		Deaths   uint
		Suicides uint
	}{}
	db.Model(&PlayerMatchStat{}).
		Select("sum(kills) as kills, sum(deaths) as deaths, sum(suicides)as suicides").
		Joins("join aliases on player_match_stats.alias_id = aliases.id").
		Where("aliases.player_id = ?", player).
		Scan(&result)
	log.Printf("stats: %+v", result)

	return result.Kills, result.Deaths, result.Suicides
}

func getPlayerMatches(db *gorm.DB, player uint) uint {
	var result = struct {
		Matches uint
	}{}
	db.Model(&PlayerMatchStat{}).
		Select("count(distinct player_match_stats.match_id) as matches").
		Joins("join aliases on player_match_stats.alias_id = aliases.id").
		Where("aliases.player_id = ?", player).
		Scan(&result)
	log.Printf("stats: %+v", result)

	return result.Matches
}

func GetPlayerGlobaStats(store store.DB, player uint) *PlayerGlobalStats {
	db := store.Conn()

	pgs := PlayerGlobalStats{}
	pgs.Kills, pgs.Deaths, pgs.Suicides = getPlayerKillDeathSuicide(db,
		player)
	pgs.Matches = getPlayerMatches(db, player)
	return &pgs
}
<|endoftext|>


<commit_before>package api

import (
	"bufio"
	"bytes"
	"code.google.com/p/go-uuid/uuid"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"sort"
	"strings"
	"time"
)

type GitContentAPI struct {
	dir string
}

func (gci GitContentAPI) ByUUID(uuid string) (bool, Content) {
	var content Content
	bytes, err := ioutil.ReadFile(path.Join(gci.dir, fmt.Sprintf("%s.json", uuid)))
	if err != nil {
		panic(err)
		return false, Content{}
	}
	err = json.Unmarshal(bytes, &content)
	if err != nil {
		panic(err)
		return false, Content{}
	}

	content.PublishedDate = content.PublishedDate.Local()

	return true, content
}

func (gci GitContentAPI) ByUUIDAndDate(id string, dateTime time.Time) (bool, Content) {
	filename := fmt.Sprintf("%s.json", id)
	cmd := exec.Command("git", "log", "--date=iso", "--pretty=format:%ad%x08%x08%x08%x08%x08%x08 %H", filename) //git log --date=iso --pretty=format:'%ad%x08%x08%x08%x08%x08%x08'
	var out bytes.Buffer
	cmd.Stdout = &out
	cmd.Stderr = &out
	cmd.Dir = gci.dir
	err := cmd.Run()
	if err != nil {
		log.Fatalf("git log command failed with error %v:\n%s\n", err, out.String())
		return false, Content{}
	}

	scanner := bufio.NewScanner(&out)
	for scanner.Scan() {
		line := scanner.Text()
		if containsDateTime(line, dateTime) {
			gitData := strings.Split(line, " ")
			log.Printf("Git Data is %v\n", gitData)
			return gci.Version(id, gitData[3]) // Why gitData[3] and why not gitData[2]
		}
	}

	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}

	return gci.ByUUID(id)
}

func (gci GitContentAPI) Version(id string, hash string) (bool, Content) {
	log.Printf("Returning content:%s @ version: %s\n", id, hash)

	filename := fmt.Sprintf("%s.json", id)
	cmd := exec.Command("git", "show", hash+":"+filename) //git show 0718a08eea5480ce0eed731a5d3157086548e3d8:c5a98003-0a8c-4fd0-9707-df880e1627b5.json
	var out bytes.Buffer
	cmd.Stdout = &out
	cmd.Stderr = &out
	cmd.Dir = gci.dir
	err := cmd.Run()
	if err != nil {
		log.Printf("git show command failed with error %v:\n%s\n", err, out.String())
		return false, Content{}
	}

	var content Content
	err = json.Unmarshal(out.Bytes(), &content)
	if err != nil {
		log.Fatalf("git show command failed with error %v:\n%s\n", err, out.String())
	}
	return true, content

}

func containsDateTime(item string, dateTime time.Time) bool {
	items := strings.Split(strings.Trim(item, "'"), " ")
	commitDateTime, err := time.Parse(time.RFC3339, items[0]+"T"+items[1]+"Z")
	if err != nil {
		log.Printf("Error when parsing date time %v\n", err)
	}

	return dateTime.After(commitDateTime) || dateTime.Equal(commitDateTime)

}

func (gci GitContentAPI) Versions(id string) (versions []Version) {
	filename := fmt.Sprintf("%s.json", id)
	cmd := exec.Command("git", "log", "--pretty=format:%ai %H", filename)
	var out bytes.Buffer
	cmd.Stdout = &out
	cmd.Stderr = &out
	cmd.Dir = gci.dir
	err := cmd.Run()
	if err != nil {
		log.Fatalf("git log command failed with error %v:\n%s\n", err, out.String())
		return nil
	}

	scanner := bufio.NewScanner(&out)
	for scanner.Scan() {
		line := scanner.Text()
		gitData := strings.Split(line, " ")

		dateString := gitData[0] + " " + gitData[1] + " " + gitData[2]
		pubDate, err := time.Parse("2006-01-02 15:04:05 -0700", dateString)
		if err != nil {
			log.Fatal(err)
		}
		pubDate = pubDate.Local()
		version := Version{
			UUID:          id,
			Version:       gitData[3],
			PublishedDate: pubDate,
			PDString: pubDate.Format("2006-01-02 15:04:05"),
		}
		now := time.Now()
		if now.Year() == pubDate.Year() && now.Month() == pubDate.Month() && now.Day() == pubDate.Day() {
			version.PDString = pubDate.Format("15:04:05")
		}

		versions = append(versions, version)
	}

	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}

	sort.Sort(Versions(versions))

	return versions
}

func (gci GitContentAPI) Write(c Content) error {
	// validate uuid
	u := uuid.Parse(c.UUID)
	if u.String() != c.UUID {
		return fmt.Errorf("invalid uuid: %v\n", c.UUID)
	}

	// marshal
	json, err := json.Marshal(c)
	if err != nil {
		return err
	}

	// write it to disk
	filename := fmt.Sprintf("%s.json", u.String())
	err = ioutil.WriteFile(path.Join(gci.dir, filename), json, 0644)
	if err != nil {
		return err
	}

	// add to git index
	cmd := exec.Command("git", "add", filename)
	var out bytes.Buffer
	cmd.Stdout = &out
	cmd.Dir = gci.dir
	err = cmd.Run()
	if err != nil {
		return fmt.Errorf("git add command failed with error %v:\n%s\n", err, out.String())
	}

	// do commit
	cmd = exec.Command("git", "commit", "-m", fmt.Sprintf("adding or updating %s", filename),
		fmt.Sprintf("--date=%s", c.PublishedDate.UTC().Format(time.RFC3339)))
	out = bytes.Buffer{}
	cmd.Stdout = &out
	cmd.Stderr = &out
	cmd.Dir = gci.dir
	err = cmd.Run()
	if err != nil {
		if strings.Contains(string(out.Bytes()), "nothing to commit, working directory clean") {
			return nil
		}
		return fmt.Errorf("git commit command failed with error %v:\n%s\n", err, out.String())
	}
	return nil
}

func (gci GitContentAPI) Close() {
}

func (gci GitContentAPI) Count() int {
	count := 0
	files, _ := ioutil.ReadDir(gci.dir)
	for _, f := range files {
		if !f.IsDir() {
			count++
		}
	}

	return count
}

func (gci GitContentAPI) init() error {

	_, err := os.Stat(gci.dir)
	if err == nil {
		// already set up
		return nil
	}
	if os.IsNotExist(err) {
		return gci.doInit()
	}
	return err

}

func (gci GitContentAPI) doInit() error {
	if err := os.Mkdir(gci.dir, 0755); err != nil {
		return err
	}

	cmd := exec.Command("git", "init")
	var out bytes.Buffer
	cmd.Stdout = &out
	cmd.Dir = gci.dir
	return cmd.Run()
}

func (gci GitContentAPI) Drop() error {
	if err := os.RemoveAll(gci.dir); err != nil {
		return err
	}
	return gci.init()
}

func (gci GitContentAPI) Recent(stop chan struct{}, limit int) (chan Content, error) {
	panic("")
}

func (gci GitContentAPI) All() ([]Content, error) {
	cmd := exec.Command("ls")
	var out bytes.Buffer
	cmd.Stdout = &out
	cmd.Stderr = &out
	cmd.Dir = gci.dir
	err := cmd.Run()
	if err != nil {
		return nil, err
	}

	var cont []Content

	scanner := bufio.NewScanner(&out)
	for scanner.Scan() {
		line := scanner.Text()
		uuid := strings.Split(line, ".")[0]
		found, c := gci.ByUUID(uuid)
		if !found {
			return nil, fmt.Errorf("failed to find content %s", uuid)
		}
		cont = append(cont, c)
	}

	if err := scanner.Err(); err != nil {
		return nil, err
	}

	sort.Sort(sort.Reverse(Contents(cont)))
	return cont, nil
}

func NewGitContentAPI() (ContentAPI, error) {
	api := GitContentAPI{"/tmp/gitapi/"}
	err := api.init()
	if err != nil {
		return nil, err
	}
	return api, nil
}
<commit_msg>some debug<commit_after>package api

import (
	"bufio"
	"bytes"
	"code.google.com/p/go-uuid/uuid"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"sort"
	"strings"
	"time"
)

type GitContentAPI struct {
	dir string
}

func (gci GitContentAPI) ByUUID(uuid string) (bool, Content) {
	var content Content
	bytes, err := ioutil.ReadFile(path.Join(gci.dir, fmt.Sprintf("%s.json", uuid)))
	if err != nil {
		panic(err)
		return false, Content{}
	}
	err = json.Unmarshal(bytes, &content)
	if err != nil {
		panic(err)
		return false, Content{}
	}

	content.PublishedDate = content.PublishedDate.Local()

	return true, content
}

func (gci GitContentAPI) ByUUIDAndDate(id string, dateTime time.Time) (bool, Content) {
	filename := fmt.Sprintf("%s.json", id)
	cmd := exec.Command("git", "log", "--date=iso", "--pretty=format:%ad%x08%x08%x08%x08%x08%x08 %H", filename) //git log --date=iso --pretty=format:'%ad%x08%x08%x08%x08%x08%x08'
	var out bytes.Buffer
	cmd.Stdout = &out
	cmd.Stderr = &out
	cmd.Dir = gci.dir
	err := cmd.Run()
	if err != nil {
		log.Fatalf("git log command failed with error %v:\n%s\n", err, out.String())
		return false, Content{}
	}

	scanner := bufio.NewScanner(&out)
	for scanner.Scan() {
		line := scanner.Text()
		if containsDateTime(line, dateTime) {
			gitData := strings.Split(line, " ")
			log.Printf("Git Data is %v\n", gitData)
			return gci.Version(id, gitData[3]) // Why gitData[3] and why not gitData[2]
		}
	}

	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}

	return gci.ByUUID(id)
}

func (gci GitContentAPI) Version(id string, hash string) (bool, Content) {
	log.Printf("Returning content:%s @ version: %s\n", id, hash)

	filename := fmt.Sprintf("%s.json", id)
	cmd := exec.Command("git", "show", hash+":"+filename) //git show 0718a08eea5480ce0eed731a5d3157086548e3d8:c5a98003-0a8c-4fd0-9707-df880e1627b5.json
	var out bytes.Buffer
	cmd.Stdout = &out
	cmd.Stderr = &out
	cmd.Dir = gci.dir
	err := cmd.Run()
	if err != nil {
		log.Printf("git show command failed with error %v:\n%s\n", err, out.String())
		return false, Content{}
	}

	var content Content
	err = json.Unmarshal(out.Bytes(), &content)
	if err != nil {
		log.Fatalf("git show command failed with error %v:\n%s\n", err, out.String())
	}
	return true, content

}

func containsDateTime(item string, dateTime time.Time) bool {
	items := strings.Split(strings.Trim(item, "'"), " ")
	commitDateTime, err := time.Parse(time.RFC3339, items[0]+"T"+items[1]+"Z")
	if err != nil {
		log.Printf("Error when parsing date time %v\n", err)
	}

	return dateTime.After(commitDateTime) || dateTime.Equal(commitDateTime)

}

func (gci GitContentAPI) Versions(id string) (versions []Version) {
	filename := fmt.Sprintf("%s.json", id)
	cmd := exec.Command("git", "log", "--pretty=format:%ai %H", filename)
	var out bytes.Buffer
	cmd.Stdout = &out
	cmd.Stderr = &out
	cmd.Dir = gci.dir
	err := cmd.Run()
	if err != nil {
		log.Fatalf("git log command failed with error %v:\n%s\n", err, out.String())
		return nil
	}

	scanner := bufio.NewScanner(&out)
	for scanner.Scan() {
		line := scanner.Text()
		gitData := strings.Split(line, " ")

		dateString := gitData[0] + " " + gitData[1] + " " + gitData[2]
		pubDate, err := time.Parse("2006-01-02 15:04:05 -0700", dateString)
		if err != nil {
			log.Fatal(err)
		}
		pubDate = pubDate.Local()
		version := Version{
			UUID:          id,
			Version:       gitData[3],
			PublishedDate: pubDate,
			PDString: pubDate.Format("2006-01-02 15:04:05"),
		}
		now := time.Now()
		if now.Year() == pubDate.Year() && now.Month() == pubDate.Month() && now.Day() == pubDate.Day() {
			version.PDString = pubDate.Format("15:04:05")
		}

		versions = append(versions, version)
	}

	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}

	sort.Sort(Versions(versions))

	return versions
}

func (gci GitContentAPI) Write(c Content) error {
	// validate uuid
	u := uuid.Parse(c.UUID)
	if u.String() != c.UUID {
		return fmt.Errorf("invalid uuid: %v\n", c.UUID)
	}

	// marshal
	json, err := json.Marshal(c)
	if err != nil {
		return err
	}

	// write it to disk
	filename := fmt.Sprintf("%s.json", u.String())
	err = ioutil.WriteFile(path.Join(gci.dir, filename), json, 0644)
	if err != nil {
		return err
	}

	// add to git index
	cmd := exec.Command("git", "add", filename)
	var out bytes.Buffer
	cmd.Stdout = &out
	cmd.Dir = gci.dir
	err = cmd.Run()
	if err != nil {
		return fmt.Errorf("git add command failed with error %v:\n%s\n", err, out.String())
	}

	fmt.Printf("date is %s", c.PublishedDate)
	// do commit
	cmd = exec.Command("git", "commit", "-m", fmt.Sprintf("adding or updating %s", filename),
		fmt.Sprintf("--date=%s", c.PublishedDate.UTC().Format(time.RFC3339)))
	out = bytes.Buffer{}
	cmd.Stdout = &out
	cmd.Stderr = &out
	cmd.Dir = gci.dir
	err = cmd.Run()
	if err != nil {
		if strings.Contains(string(out.Bytes()), "nothing to commit, working directory clean") {
			return nil
		}
		return fmt.Errorf("git commit command failed with error %v:\n%s\n", err, out.String())
	}
	return nil
}

func (gci GitContentAPI) Close() {
}

func (gci GitContentAPI) Count() int {
	count := 0
	files, _ := ioutil.ReadDir(gci.dir)
	for _, f := range files {
		if !f.IsDir() {
			count++
		}
	}

	return count
}

func (gci GitContentAPI) init() error {

	_, err := os.Stat(gci.dir)
	if err == nil {
		// already set up
		return nil
	}
	if os.IsNotExist(err) {
		return gci.doInit()
	}
	return err

}

func (gci GitContentAPI) doInit() error {
	if err := os.Mkdir(gci.dir, 0755); err != nil {
		return err
	}

	cmd := exec.Command("git", "init")
	var out bytes.Buffer
	cmd.Stdout = &out
	cmd.Dir = gci.dir
	return cmd.Run()
}

func (gci GitContentAPI) Drop() error {
	if err := os.RemoveAll(gci.dir); err != nil {
		return err
	}
	return gci.init()
}

func (gci GitContentAPI) Recent(stop chan struct{}, limit int) (chan Content, error) {
	panic("")
}

func (gci GitContentAPI) All() ([]Content, error) {
	cmd := exec.Command("ls")
	var out bytes.Buffer
	cmd.Stdout = &out
	cmd.Stderr = &out
	cmd.Dir = gci.dir
	err := cmd.Run()
	if err != nil {
		return nil, err
	}

	var cont []Content

	scanner := bufio.NewScanner(&out)
	for scanner.Scan() {
		line := scanner.Text()
		uuid := strings.Split(line, ".")[0]
		found, c := gci.ByUUID(uuid)
		if !found {
			return nil, fmt.Errorf("failed to find content %s", uuid)
		}
		cont = append(cont, c)
	}

	if err := scanner.Err(); err != nil {
		return nil, err
	}

	sort.Sort(sort.Reverse(Contents(cont)))
	return cont, nil
}

func NewGitContentAPI() (ContentAPI, error) {
	api := GitContentAPI{"/tmp/gitapi/"}
	err := api.init()
	if err != nil {
		return nil, err
	}
	return api, nil
}
<|endoftext|>


<commit_before>package apnsapi

const (
	DevelopmentServer = "https://api.development.push.apple.com"
	ProductionServer  = "https://api.push.apple.com"
)

type Header struct {
	ApnsID         string
	ApnsExpiration string
	ApnsPriority   string
	ApnsTopic      string
}

type Response struct {
	ApnsID     string
	StatusCode int
}

type ErrorResponse struct {
	Reason    string `json:"reason"`
	Timestamp string `json:"timestamp"` //TODO is string ?
}

func (e *ErrorResponse) Error() string {
	return e.Reason
}
<commit_msg>timestamp in error response is int<commit_after>package apnsapi

const (
	DevelopmentServer = "https://api.development.push.apple.com"
	ProductionServer  = "https://api.push.apple.com"
)

type Header struct {
	ApnsID         string
	ApnsExpiration string
	ApnsPriority   string
	ApnsTopic      string
}

type Response struct {
	ApnsID     string
	StatusCode int
}

type ErrorResponse struct {
	Reason    string `json:"reason"`
	Timestamp int    `json:"timestamp"`
}

func (e *ErrorResponse) Error() string {
	return e.Reason
}
<|endoftext|>


<commit_before>// Copyright 2020 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package simplefs

import (
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"sync"

	"github.com/keybase/client/go/kbfs/libkbfs"
	"github.com/keybase/client/go/protocol/keybase1"
	"github.com/pkg/errors"
	"golang.org/x/net/context"
)

const (
	// ulCtxOpID is the display name for the unique operation SimpleFS ID tag.
	ulCtxOpID = "SFSULID"
)

// ulCtxTagKey is the type used for unique context tags
type ulCtxTagKey int

const (
	// ulCtxIDKey is the type of the tag for unique operation IDs.
	ulCtxIDKey ulCtxTagKey = iota
)

type upload struct {
	dirToDelete *string
	uploadID    string
	opid        keybase1.OpID

	state keybase1.UploadState
}

type uploadManager struct {
	k         *SimpleFS
	publisher libkbfs.SubscriptionManagerPublisher
	cacheDir  string

	lock      sync.RWMutex
	tempPaths map[string]bool
	uploads   map[string]upload
}

func newUploadManager(simpleFS *SimpleFS) *uploadManager {
	return &uploadManager{
		k:         simpleFS,
		publisher: simpleFS.config.SubscriptionManagerPublisher(),
		cacheDir:  simpleFS.config.KbEnv().GetCacheDir(),
		tempPaths: make(map[string]bool),
		uploads:   make(map[string]upload),
	}
}

func (m *uploadManager) makeContext() (ctx context.Context) {
	return libkbfs.CtxWithRandomIDReplayable(context.Background(), ulCtxIDKey, ulCtxOpID, m.k.log)
}

func (m *uploadManager) makeTempDir() (tempDirPath string, err error) {
	cacheDirAbs, err := filepath.Abs(m.cacheDir)
	if err != nil {
		return "", err
	}
	tempDirPath, err = ioutil.TempDir(cacheDirAbs, "uploads-")
	if err != nil {
		return "", err
	}

	m.lock.Lock()
	defer m.lock.Unlock()
	m.tempPaths[tempDirPath] = true

	return tempDirPath, nil
}

func (m *uploadManager) doWhileLocked(action func()) {
	m.lock.Lock()
	defer m.lock.Unlock()
	action()
}

func (m *uploadManager) getUpload(uploadID string) (upload, bool) {
	m.lock.RLock()
	defer m.lock.RUnlock()
	u, ok := m.uploads[uploadID]
	return u, ok
}

func (m *uploadManager) waitForCopy(uploadID string) {
	upload, ok := m.getUpload(uploadID)
	if !ok {
		return
	}
	defer func() {
		m.publisher.PublishChange(keybase1.SubscriptionTopic_UPLOAD_STATUS)
		if upload.dirToDelete == nil {
			return
		}
		if err := os.RemoveAll(*upload.dirToDelete); err != nil {
			m.k.log.CDebugf(m.makeContext(), "remove temp dir error %s", err)
		}

	}()

	err := m.k.SimpleFSWait(m.makeContext(), upload.opid)
	if err != nil {
		m.doWhileLocked(func() {
			upload, ok := m.uploads[uploadID]
			if !ok {
				return
			}
			if errors.Cause(err) == context.Canceled {
				upload.state.Canceled = true
			} else {
				errStr := err.Error()
				upload.state.Error = &errStr
			}
			m.uploads[uploadID] = upload
		})
		return
	}
	m.doWhileLocked(func() {
		delete(m.uploads, uploadID)
	})
}

const uploadSuffixMax = 1024

func (m *uploadManager) doStart(ctx context.Context,
	sourceLocalPath string, dstParentPath string) (opid keybase1.OpID, dstPath keybase1.KBFSPath, err error) {
	opid, err = m.k.SimpleFSMakeOpid(ctx)
	if err != nil {
		return keybase1.OpID{}, keybase1.KBFSPath{}, err
	}
	basename := filepath.Base(sourceLocalPath)

renameLoop:
	for i := 0; i < uploadSuffixMax; i++ {
		name := basename
		if i > 0 {
			name = fmt.Sprintf("%s (%d)", basename, i)
		}
		dstPath = keybase1.NewPathWithKbfsPath(
			path.Join(dstParentPath, name)).Kbfs()

		// First check with stat. This should cover most cases, and is
		// the last resort for avoiding merging directories where we
		// don't get something like O_CREATE for free.
		_, err = m.k.SimpleFSStat(ctx, keybase1.SimpleFSStatArg{
			Path: keybase1.NewPathWithKbfs(dstPath),
		})
		switch {
		case err == nil:
			continue renameLoop
		case os.IsNotExist(err):
		default:
			return keybase1.OpID{}, keybase1.KBFSPath{}, err
		}

		err = m.k.SimpleFSCopyRecursive(ctx, keybase1.SimpleFSCopyRecursiveArg{
			OpID:                   opid,
			Src:                    keybase1.NewPathWithLocal(sourceLocalPath),
			Dest:                   keybase1.NewPathWithKbfs(dstPath),
			OverwriteExistingFiles: false,
		})
		switch errors.Cause(err) {
		case os.ErrExist:
			continue renameLoop
		case nil:
			return opid, dstPath, nil
		default:
			return keybase1.OpID{}, keybase1.KBFSPath{}, err
		}
	}

	return keybase1.OpID{}, keybase1.KBFSPath{},
		errors.New("too many rename attempts")
}

func (m *uploadManager) start(ctx context.Context, sourceLocalPath string,
	targetParentPath keybase1.KBFSPath) (uploadID string, err error) {
	opid, dstPath, err := m.doStart(ctx, sourceLocalPath, targetParentPath.Path)
	if err != nil {
		return "", err
	}
	uploadID = base64.RawURLEncoding.EncodeToString(opid[:])

	defer m.publisher.PublishChange(keybase1.SubscriptionTopic_UPLOAD_STATUS)

	m.doWhileLocked(func() {
		var dirToDelete *string
		sourceParent := filepath.Dir(sourceLocalPath)
		if m.tempPaths[sourceParent] {
			dirToDelete = &sourceParent
		}
		m.uploads[uploadID] = upload{
			dirToDelete: dirToDelete,
			uploadID:    uploadID,
			opid:        opid,
			state: keybase1.UploadState{
				UploadID:   uploadID,
				TargetPath: dstPath,
			},
		}
	})

	// If we start having tests on this we'll need proper shutdown mechanism
	// for these goroutines.
	go m.waitForCopy(uploadID)

	return uploadID, nil
}

func (m *uploadManager) cancel(ctx context.Context, uploadID string) error {
	upload, ok := m.getUpload(uploadID)
	if !ok {
		return nil
	}
	return m.k.SimpleFSCancel(ctx, upload.opid)
}

func (m *uploadManager) dismiss(uploadID string) error {
	upload, ok := m.getUpload(uploadID)
	if !ok {
		return nil
	}
	if !upload.state.Canceled && upload.state.Error == nil {
		return errors.New("dismiss called on ongoing upload")
	}
	m.doWhileLocked(func() {
		delete(m.uploads, uploadID)
	})
	m.publisher.PublishChange(keybase1.SubscriptionTopic_UPLOAD_STATUS)
	return nil
}

func (m *uploadManager) getUploads() (uploads []keybase1.UploadState) {
	m.lock.RLock()
	defer m.lock.RUnlock()
	uploads = make([]keybase1.UploadState, 0, len(m.uploads))
	for _, upload := range m.uploads {
		uploads = append(uploads, upload.state)
	}
	return uploads
}
<commit_msg>fix fs upload (#23271)<commit_after>// Copyright 2020 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package simplefs

import (
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"sync"

	"github.com/keybase/client/go/kbfs/libkbfs"
	"github.com/keybase/client/go/protocol/keybase1"
	"github.com/pkg/errors"
	"golang.org/x/net/context"
)

const (
	// ulCtxOpID is the display name for the unique operation SimpleFS ID tag.
	ulCtxOpID = "SFSULID"
)

// ulCtxTagKey is the type used for unique context tags
type ulCtxTagKey int

const (
	// ulCtxIDKey is the type of the tag for unique operation IDs.
	ulCtxIDKey ulCtxTagKey = iota
)

type upload struct {
	dirToDelete *string
	uploadID    string
	opid        keybase1.OpID

	state keybase1.UploadState
}

type uploadManager struct {
	k         *SimpleFS
	publisher libkbfs.SubscriptionManagerPublisher
	cacheDir  string

	lock      sync.RWMutex
	tempPaths map[string]bool
	uploads   map[string]upload
}

func newUploadManager(simpleFS *SimpleFS) *uploadManager {
	return &uploadManager{
		k:         simpleFS,
		publisher: simpleFS.config.SubscriptionManagerPublisher(),
		cacheDir:  simpleFS.config.KbEnv().GetCacheDir(),
		tempPaths: make(map[string]bool),
		uploads:   make(map[string]upload),
	}
}

func (m *uploadManager) makeContext() (ctx context.Context) {
	return libkbfs.CtxWithRandomIDReplayable(context.Background(), ulCtxIDKey, ulCtxOpID, m.k.log)
}

func (m *uploadManager) makeTempDir() (tempDirPath string, err error) {
	cacheDirAbs, err := filepath.Abs(m.cacheDir)
	if err != nil {
		return "", err
	}
	tempDirPath, err = ioutil.TempDir(cacheDirAbs, "uploads-")
	if err != nil {
		return "", err
	}

	m.lock.Lock()
	defer m.lock.Unlock()
	m.tempPaths[tempDirPath] = true

	return tempDirPath, nil
}

func (m *uploadManager) doWhileLocked(action func()) {
	m.lock.Lock()
	defer m.lock.Unlock()
	action()
}

func (m *uploadManager) getUpload(uploadID string) (upload, bool) {
	m.lock.RLock()
	defer m.lock.RUnlock()
	u, ok := m.uploads[uploadID]
	return u, ok
}

func (m *uploadManager) waitForCopy(uploadID string) {
	upload, ok := m.getUpload(uploadID)
	if !ok {
		return
	}
	defer func() {
		m.publisher.PublishChange(keybase1.SubscriptionTopic_UPLOAD_STATUS)
		if upload.dirToDelete == nil {
			return
		}
		if err := os.RemoveAll(*upload.dirToDelete); err != nil {
			m.k.log.CDebugf(m.makeContext(), "remove temp dir error %s", err)
		}

	}()

	err := m.k.SimpleFSWait(m.makeContext(), upload.opid)
	if err != nil {
		m.doWhileLocked(func() {
			upload, ok := m.uploads[uploadID]
			if !ok {
				return
			}
			if errors.Cause(err) == context.Canceled {
				upload.state.Canceled = true
			} else {
				errStr := err.Error()
				upload.state.Error = &errStr
			}
			m.uploads[uploadID] = upload
		})
		return
	}
	m.doWhileLocked(func() {
		delete(m.uploads, uploadID)
	})
}

const uploadSuffixMax = 1024

func (m *uploadManager) doStart(ctx context.Context,
	sourceLocalPath string, dstParentPath string) (opid keybase1.OpID, dstPath keybase1.KBFSPath, err error) {
	opid, err = m.k.SimpleFSMakeOpid(ctx)
	if err != nil {
		return keybase1.OpID{}, keybase1.KBFSPath{}, err
	}
	basename := filepath.Base(sourceLocalPath)

renameLoop:
	for i := 0; i < uploadSuffixMax; i++ {
		name := basename
		if i > 0 {
			name = fmt.Sprintf("%s (%d)", basename, i)
		}
		dstPath = keybase1.NewPathWithKbfsPath(
			path.Join(dstParentPath, name)).Kbfs()

		// First check with stat. This should cover most cases, and is
		// the last resort for avoiding merging directories where we
		// don't get something like O_CREATE for free.
		_, err = m.k.SimpleFSStat(ctx, keybase1.SimpleFSStatArg{
			Path: keybase1.NewPathWithKbfs(dstPath),
		})
		switch {
		case err == nil:
			continue renameLoop
		case err == errNotExist:
		default:
			return keybase1.OpID{}, keybase1.KBFSPath{}, err
		}

		err = m.k.SimpleFSCopyRecursive(ctx, keybase1.SimpleFSCopyRecursiveArg{
			OpID:                   opid,
			Src:                    keybase1.NewPathWithLocal(sourceLocalPath),
			Dest:                   keybase1.NewPathWithKbfs(dstPath),
			OverwriteExistingFiles: false,
		})
		switch errors.Cause(err) {
		case errNameExists:
			continue renameLoop
		case nil:
			return opid, dstPath, nil
		default:
			return keybase1.OpID{}, keybase1.KBFSPath{}, err
		}
	}

	return keybase1.OpID{}, keybase1.KBFSPath{},
		errors.New("too many rename attempts")
}

func (m *uploadManager) start(ctx context.Context, sourceLocalPath string,
	targetParentPath keybase1.KBFSPath) (uploadID string, err error) {
	opid, dstPath, err := m.doStart(ctx, sourceLocalPath, targetParentPath.Path)
	if err != nil {
		return "", err
	}
	uploadID = base64.RawURLEncoding.EncodeToString(opid[:])

	defer m.publisher.PublishChange(keybase1.SubscriptionTopic_UPLOAD_STATUS)

	m.doWhileLocked(func() {
		var dirToDelete *string
		sourceParent := filepath.Dir(sourceLocalPath)
		if m.tempPaths[sourceParent] {
			dirToDelete = &sourceParent
		}
		m.uploads[uploadID] = upload{
			dirToDelete: dirToDelete,
			uploadID:    uploadID,
			opid:        opid,
			state: keybase1.UploadState{
				UploadID:   uploadID,
				TargetPath: dstPath,
			},
		}
	})

	// If we start having tests on this we'll need proper shutdown mechanism
	// for these goroutines.
	go m.waitForCopy(uploadID)

	return uploadID, nil
}

func (m *uploadManager) cancel(ctx context.Context, uploadID string) error {
	upload, ok := m.getUpload(uploadID)
	if !ok {
		return nil
	}
	return m.k.SimpleFSCancel(ctx, upload.opid)
}

func (m *uploadManager) dismiss(uploadID string) error {
	upload, ok := m.getUpload(uploadID)
	if !ok {
		return nil
	}
	if !upload.state.Canceled && upload.state.Error == nil {
		return errors.New("dismiss called on ongoing upload")
	}
	m.doWhileLocked(func() {
		delete(m.uploads, uploadID)
	})
	m.publisher.PublishChange(keybase1.SubscriptionTopic_UPLOAD_STATUS)
	return nil
}

func (m *uploadManager) getUploads() (uploads []keybase1.UploadState) {
	m.lock.RLock()
	defer m.lock.RUnlock()
	uploads = make([]keybase1.UploadState, 0, len(m.uploads))
	for _, upload := range m.uploads {
		uploads = append(uploads, upload.state)
	}
	return uploads
}
<|endoftext|>