<commit_before>package val

import (
	"io"
	"io/ioutil"
	"strings"
	"testing"
)

// Make string into io.ReadCloser
// this is just for convinience
func jsonFactory(s string) io.ReadCloser {
	return ioutil.NopCloser(strings.NewReader(s))
}

// Ensure all required fields are matching
func TestRequired(t *testing.T) {

	// // Test if STRING required is valid
	var testString struct {
		Test string `json:"something" validate:"required" `
	}

	testJSON := jsonFactory(`{"something": "hello"}`)

	if err := Guaranty(&testString, testJSON); err != nil {
		t.Error(err)
	}

	var testString2 struct {
		Test string `json:"something" validate:"required" `
	}

	testJSON = jsonFactory(`{}`)

	if err := Guaranty(&testString2, testJSON); err == nil {
		t.Error("Required string, empty JSON object should return error but did not.")
	}

	// Test if INT require is valid
	var testInt struct {
		Test int `json:"something" validate:"required" `
	}

	testJSON = jsonFactory(`{"something": 2}`)

	if err := Guaranty(&testInt, testJSON); err != nil {
		t.Error(err)
	}

	// Need to work on getting this to pass
	var testInt2 struct {
		Test int `json:"something" validate:"required" `
	}

	testJSON = jsonFactory(`{}`)

	if err := Guaranty(&testInt2, testJSON); err == nil {
		t.Error("Required int, empty JSON object should return error but did not.")
	}

	// Test if BOOL required is valid
	var testBool struct {
		Test bool `json:"something" validate:"required" `
	}

	testJSON = jsonFactory(`{"something": true}`)

	if err := Guaranty(&testBool, testJSON); err != nil {
		t.Error(err)
	}

	var testBool2 struct {
		Test string `json:"something" validate:"required" `
	}

	testJSON = jsonFactory(`{}`)

	if err := Guaranty(&testBool2, testJSON); err == nil {
		t.Error("Required bool, empty JSON object should return error but did not.")
	}

	// Test if ARRAY required is valid
	var testArray struct {
		Test []string `json:"something" validate:"required" `
	}

	testJSON = jsonFactory(`{"something": ["test", "data"]}`)

	if err := Guaranty(&testArray, testJSON); err != nil {
		t.Error(err)
	}

	var testArray2 struct {
		Test []string `json:"something" validate:"required" `
	}

	testJSON = jsonFactory(`{}`)

	if err := Guaranty(&testArray2, testJSON); err == nil {
		t.Error("Required array, empty JSON object should return error but did not.")
	}

	// Test is OBJECT required is valid
	type testObjectTP struct {
		Name string `json:"name" validate:"required" `
	}

	var testObject struct {
		Test testObjectTP `json:"something" validate:"required" `
	}

	testJSON = jsonFactory(`{"something": {"name": "test"}}`)

	if err := Guaranty(&testObject, testJSON); err != nil {
		t.Error(err)
	}

	type testObjectTP2 struct {
		Name string `json:"name" validate:"required" `
	}

	var testObject2 struct {
		Test testObjectTP2 `json:"something" validate:"required" `
	}

	testJSON = jsonFactory(`{}`)

	if err := Guaranty(&testObject2, testJSON); err == nil {
		t.Error("Required object, empty JSON object should return error but did not.")
	}
}

func TestEmail(t *testing.T) {

	var testValEmail struct {
		Test string `json:"email" validate:"email" `
	}

	testJSON := jsonFactory(`{"email": "michaeljs@gmail.com"}`)

	if err := Guaranty(&testValEmail, testJSON); err != nil {
		t.Error(err)
	}

	var testValEmail2 struct {
		Test string `json:"email" validate:"email" `
	}

	testJSON = jsonFactory(`{"email": "michaeljs@gail.edu"}`)

	if err := Guaranty(&testValEmail2, testJSON); err != nil {
		t.Error(err)
	}

	var testValEmail3 struct {
		Test string `json:"email" validate:"email" `
	}

	testJSON = jsonFactory(`{"email": "michaeljs.edu"}`)

	if err := Guaranty(&testValEmail3, testJSON); err == nil {
		t.Error("Email test failed, michaeljs.edu passed as a valid email.")
	}

	// This should not return an error since email is not required.
	var testValEmail4 struct {
		Test string `json:"email" validate:"email" `
	}

	testJSON = jsonFactory(`{"jeff": "really"}`)

	if err := Guaranty(&testValEmail4, testJSON); err != nil {
		t.Error(err)
	}

}

// Ensure In is matching properly
// Supporting string and int currently
func TestIn(t *testing.T) {

	var testValIn struct {
		Test string `json:"special" validate:"in:admin,user,other" `
	}

	testJSON := jsonFactory(`{"special": "admin"}`)

	if err := Guaranty(&testValIn, testJSON); err != nil {
		t.Error(err)
	}

	var testValIn2 struct {
		Test int `json:"special" validate:"in:1,3,2" `
	}

	testJSON = jsonFactory(`{"special": 3}`)

	if err := Guaranty(&testValIn2, testJSON); err != nil {
		t.Error(err)
	}

	var testValIn3 struct {
		Test int `json:"special" validate:"in:1,3,2" `
	}

	testJSON = jsonFactory(`{"special": 6}`)

	if err := Guaranty(&testValIn3, testJSON); err == nil {
		t.Error("6 is not in validate in call, err should not have been nil.")
	}

	var testValIn4 struct {
		Test2 string `json:"what" validate:in:this,that`
		Test  int    `json:"special" validate:"in:1,3,2" `
	}

	testJSON = jsonFactory(`{"special": 3,"what": "this"}`)

	if err := Guaranty(&testValIn4, testJSON); err != nil {
		t.Error(err)
	}

	var testValIn5 struct {
		Test2 string `json:"what" validate:in:this,that`
		Test  int    `json:"special" validate:"in:1,3,2" `
	}

	testJSON = jsonFactory(`{"special": 3}`)

	if err := Guaranty(&testValIn5, testJSON); err != nil {
		t.Error(err)
	}

	var testValIn6 struct {
		Test2 string `json:"what" validate:"in:this,that"`
		Test3 string `json:"what1" validate:"in:this,then"`
		Test4 string `json:"what2" validate:"in:this,that"`
		Test5 string `json:"what3" validate:"in:this,that"`
		Test  int    `json:"special" validate:"in:1,3,2"`
	}

	testJSON = jsonFactory(`{"sa": 34, "what":"this", "what1":"then", "what2":"this"}`)

	if err := Guaranty(&testValIn6, testJSON); err != nil {
		t.Error(err)
	}
}

// Check if the entered JSON is a data matching the one in a string.
func TestDigit(t *testing.T) {

	var testValDigit struct {
		Test int `json:"digit" validate:"digit:5" `
	}

	testJSON := jsonFactory(`{"digit": 12345}`)

	if err := Guaranty(&testValDigit, testJSON); err != nil {
		t.Error(err)
	}

	var testValDigit2 struct {
		Test int `json:"digit" validate:"digit:5" `
	}

	testJSON = jsonFactory(`{"digit": 123456}`)

	if err := Guaranty(&testValDigit2, testJSON); err == nil {
		t.Error("Error should have been thrown, digits should be 5 but was 6.")
	}

	var testValDigit3 struct {
		Test int `json:"digit" validate:"digit:12" `
	}

	testJSON = jsonFactory(`{"digit": 111111111111}`)

	if err := Guaranty(&testValDigit3, testJSON); err != nil {
		t.Error(err)
	}

}

// Check if the entered JSON is a data matching the one in a string.
func TestDigitBetween(t *testing.T) {

	var testValDigit struct {
		Test int `json:"digit" validate:"digits_between:5,7" `
	}

	testJSON := jsonFactory(`{"digit": 123456}`)

	if err := Guaranty(&testValDigit, testJSON); err != nil {
		t.Error(err)
	}

	var testValDigit2 struct {
		Test int `json:"digit" validate:"digits_between:0,10" `
	}

	testJSON = jsonFactory(`{"digit": 1234564}`)

	if err := Guaranty(&testValDigit2, testJSON); err != nil {
		t.Error("Error should have been thrown, digit was not between 0 and 10.")
	}

	var testValDigit3 struct {
		Test int `json:"digit" validate:"digits_between:0,1" `
	}

	testJSON := jsonFactory(`{"digit": 1234564}`)

	if err := Guaranty(&testValDigit3, testJSON); err == nil {
		t.Error(err)
	}

}
<commit_msg>Accidentally :=<commit_after>package val

import (
	"io"
	"io/ioutil"
	"strings"
	"testing"
)

// Make string into io.ReadCloser
// this is just for convinience
func jsonFactory(s string) io.ReadCloser {
	return ioutil.NopCloser(strings.NewReader(s))
}

// Ensure all required fields are matching
func TestRequired(t *testing.T) {

	// // Test if STRING required is valid
	var testString struct {
		Test string `json:"something" validate:"required" `
	}

	testJSON := jsonFactory(`{"something": "hello"}`)

	if err := Guaranty(&testString, testJSON); err != nil {
		t.Error(err)
	}

	var testString2 struct {
		Test string `json:"something" validate:"required" `
	}

	testJSON = jsonFactory(`{}`)

	if err := Guaranty(&testString2, testJSON); err == nil {
		t.Error("Required string, empty JSON object should return error but did not.")
	}

	// Test if INT require is valid
	var testInt struct {
		Test int `json:"something" validate:"required" `
	}

	testJSON = jsonFactory(`{"something": 2}`)

	if err := Guaranty(&testInt, testJSON); err != nil {
		t.Error(err)
	}

	// Need to work on getting this to pass
	var testInt2 struct {
		Test int `json:"something" validate:"required" `
	}

	testJSON = jsonFactory(`{}`)

	if err := Guaranty(&testInt2, testJSON); err == nil {
		t.Error("Required int, empty JSON object should return error but did not.")
	}

	// Test if BOOL required is valid
	var testBool struct {
		Test bool `json:"something" validate:"required" `
	}

	testJSON = jsonFactory(`{"something": true}`)

	if err := Guaranty(&testBool, testJSON); err != nil {
		t.Error(err)
	}

	var testBool2 struct {
		Test string `json:"something" validate:"required" `
	}

	testJSON = jsonFactory(`{}`)

	if err := Guaranty(&testBool2, testJSON); err == nil {
		t.Error("Required bool, empty JSON object should return error but did not.")
	}

	// Test if ARRAY required is valid
	var testArray struct {
		Test []string `json:"something" validate:"required" `
	}

	testJSON = jsonFactory(`{"something": ["test", "data"]}`)

	if err := Guaranty(&testArray, testJSON); err != nil {
		t.Error(err)
	}

	var testArray2 struct {
		Test []string `json:"something" validate:"required" `
	}

	testJSON = jsonFactory(`{}`)

	if err := Guaranty(&testArray2, testJSON); err == nil {
		t.Error("Required array, empty JSON object should return error but did not.")
	}

	// Test is OBJECT required is valid
	type testObjectTP struct {
		Name string `json:"name" validate:"required" `
	}

	var testObject struct {
		Test testObjectTP `json:"something" validate:"required" `
	}

	testJSON = jsonFactory(`{"something": {"name": "test"}}`)

	if err := Guaranty(&testObject, testJSON); err != nil {
		t.Error(err)
	}

	type testObjectTP2 struct {
		Name string `json:"name" validate:"required" `
	}

	var testObject2 struct {
		Test testObjectTP2 `json:"something" validate:"required" `
	}

	testJSON = jsonFactory(`{}`)

	if err := Guaranty(&testObject2, testJSON); err == nil {
		t.Error("Required object, empty JSON object should return error but did not.")
	}
}

func TestEmail(t *testing.T) {

	var testValEmail struct {
		Test string `json:"email" validate:"email" `
	}

	testJSON := jsonFactory(`{"email": "michaeljs@gmail.com"}`)

	if err := Guaranty(&testValEmail, testJSON); err != nil {
		t.Error(err)
	}

	var testValEmail2 struct {
		Test string `json:"email" validate:"email" `
	}

	testJSON = jsonFactory(`{"email": "michaeljs@gail.edu"}`)

	if err := Guaranty(&testValEmail2, testJSON); err != nil {
		t.Error(err)
	}

	var testValEmail3 struct {
		Test string `json:"email" validate:"email" `
	}

	testJSON = jsonFactory(`{"email": "michaeljs.edu"}`)

	if err := Guaranty(&testValEmail3, testJSON); err == nil {
		t.Error("Email test failed, michaeljs.edu passed as a valid email.")
	}

	// This should not return an error since email is not required.
	var testValEmail4 struct {
		Test string `json:"email" validate:"email" `
	}

	testJSON = jsonFactory(`{"jeff": "really"}`)

	if err := Guaranty(&testValEmail4, testJSON); err != nil {
		t.Error(err)
	}

}

// Ensure In is matching properly
// Supporting string and int currently
func TestIn(t *testing.T) {

	var testValIn struct {
		Test string `json:"special" validate:"in:admin,user,other" `
	}

	testJSON := jsonFactory(`{"special": "admin"}`)

	if err := Guaranty(&testValIn, testJSON); err != nil {
		t.Error(err)
	}

	var testValIn2 struct {
		Test int `json:"special" validate:"in:1,3,2" `
	}

	testJSON = jsonFactory(`{"special": 3}`)

	if err := Guaranty(&testValIn2, testJSON); err != nil {
		t.Error(err)
	}

	var testValIn3 struct {
		Test int `json:"special" validate:"in:1,3,2" `
	}

	testJSON = jsonFactory(`{"special": 6}`)

	if err := Guaranty(&testValIn3, testJSON); err == nil {
		t.Error("6 is not in validate in call, err should not have been nil.")
	}

	var testValIn4 struct {
		Test2 string `json:"what" validate:in:this,that`
		Test  int    `json:"special" validate:"in:1,3,2" `
	}

	testJSON = jsonFactory(`{"special": 3,"what": "this"}`)

	if err := Guaranty(&testValIn4, testJSON); err != nil {
		t.Error(err)
	}

	var testValIn5 struct {
		Test2 string `json:"what" validate:in:this,that`
		Test  int    `json:"special" validate:"in:1,3,2" `
	}

	testJSON = jsonFactory(`{"special": 3}`)

	if err := Guaranty(&testValIn5, testJSON); err != nil {
		t.Error(err)
	}

	var testValIn6 struct {
		Test2 string `json:"what" validate:"in:this,that"`
		Test3 string `json:"what1" validate:"in:this,then"`
		Test4 string `json:"what2" validate:"in:this,that"`
		Test5 string `json:"what3" validate:"in:this,that"`
		Test  int    `json:"special" validate:"in:1,3,2"`
	}

	testJSON = jsonFactory(`{"sa": 34, "what":"this", "what1":"then", "what2":"this"}`)

	if err := Guaranty(&testValIn6, testJSON); err != nil {
		t.Error(err)
	}
}

// Check if the entered JSON is a data matching the one in a string.
func TestDigit(t *testing.T) {

	var testValDigit struct {
		Test int `json:"digit" validate:"digit:5" `
	}

	testJSON := jsonFactory(`{"digit": 12345}`)

	if err := Guaranty(&testValDigit, testJSON); err != nil {
		t.Error(err)
	}

	var testValDigit2 struct {
		Test int `json:"digit" validate:"digit:5" `
	}

	testJSON = jsonFactory(`{"digit": 123456}`)

	if err := Guaranty(&testValDigit2, testJSON); err == nil {
		t.Error("Error should have been thrown, digits should be 5 but was 6.")
	}

	var testValDigit3 struct {
		Test int `json:"digit" validate:"digit:12" `
	}

	testJSON = jsonFactory(`{"digit": 111111111111}`)

	if err := Guaranty(&testValDigit3, testJSON); err != nil {
		t.Error(err)
	}

}

// Check if the entered JSON is a data matching the one in a string.
func TestDigitBetween(t *testing.T) {

	var testValDigit struct {
		Test int `json:"digit" validate:"digits_between:5,7" `
	}

	testJSON := jsonFactory(`{"digit": 123456}`)

	if err := Guaranty(&testValDigit, testJSON); err != nil {
		t.Error(err)
	}

	var testValDigit2 struct {
		Test int `json:"digit" validate:"digits_between:0,10" `
	}

	testJSON = jsonFactory(`{"digit": 1234564}`)

	if err := Guaranty(&testValDigit2, testJSON); err != nil {
		t.Error("Error should have been thrown, digit was not between 0 and 10.")
	}

	var testValDigit3 struct {
		Test int `json:"digit" validate:"digits_between:0,1" `
	}

	testJSON = jsonFactory(`{"digit": 1234564}`)

	if err := Guaranty(&testValDigit3, testJSON); err == nil {
		t.Error(err)
	}

}
<|endoftext|>


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

package main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"runtime/debug"

	"github.com/coreos/coreos-cloudinit-validate/third_party/github.com/coreos/coreos-cloudinit/config/validate"
	"github.com/coreos/coreos-cloudinit-validate/third_party/github.com/crawford/nap"
	"github.com/coreos/coreos-cloudinit-validate/third_party/github.com/gorilla/mux"
)

var (
	flags = struct {
		port    int
		address string
	}{}
)

type payloadWrapper struct{}

func (w payloadWrapper) Wrap(payload interface{}, status nap.Status) (interface{}, int) {
	return map[string]interface{}{
		"result": payload,
	}, status.Code()
}

type panicHandler struct{}

func (h panicHandler) Handle(e interface{}) {
	log.Printf("PANIC: %#v\n", e)
	debug.PrintStack()
}

func init() {
	flag.StringVar(&flags.address, "address", "0.0.0.0", "address to listen on")
	flag.IntVar(&flags.port, "port", 80, "port to bind on")

	nap.PayloadWrapper = payloadWrapper{}
	nap.PanicHandler = panicHandler{}
	nap.ResponseHeaders = []nap.Header{
		nap.Header{"Access-Control-Allow-Origin", []string{"*"}},
		nap.Header{"Access-Control-Allow-Methods", []string{"OPTIONS, PUT"}},
	}
}

func main() {
	flag.Parse()

	router := mux.NewRouter()
	server := &http.Server{
		Addr:    fmt.Sprintf("%s:%d", flags.address, flags.port),
		Handler: router,
	}

	router.Handle("/validate", nap.HandlerFunc(optionsValidate)).Methods("OPTIONS")
	router.Handle("/validate", nap.HandlerFunc(putValidate)).Methods("PUT")
	router.Handle("/health", nap.HandlerFunc(getHealth)).Methods("GET")

	log.Fatalln(server.ListenAndServe())
}

func optionsValidate(r *http.Request) (interface{}, nap.Status) {
	return nil, nap.OK{}
}

func putValidate(r *http.Request) (interface{}, nap.Status) {
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return nil, nap.InternalError{err.Error()}
	}

	config := bytes.Replace(body, []byte("\r"), []byte{}, -1)
	if report, err := validate.Validate(config); err == nil {
		return report.Entries(), nap.OK{}
	} else {
		return nil, nap.InternalError{err.Error()}
	}
}

func getHealth(r *http.Request) (interface{}, nap.Status) {
	return nil, nap.OK{}
}
<commit_msg>headers: add Content-Type and Cache-Control headers<commit_after>//
// Copyright 2014 The CoreOS Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"runtime/debug"

	"github.com/coreos/coreos-cloudinit-validate/third_party/github.com/coreos/coreos-cloudinit/config/validate"
	"github.com/coreos/coreos-cloudinit-validate/third_party/github.com/crawford/nap"
	"github.com/coreos/coreos-cloudinit-validate/third_party/github.com/gorilla/mux"
)

var (
	flags = struct {
		port    int
		address string
	}{}
)

type payloadWrapper struct{}

func (w payloadWrapper) Wrap(payload interface{}, status nap.Status) (interface{}, int) {
	return map[string]interface{}{
		"result": payload,
	}, status.Code()
}

type panicHandler struct{}

func (h panicHandler) Handle(e interface{}) {
	log.Printf("PANIC: %#v\n", e)
	debug.PrintStack()
}

func init() {
	flag.StringVar(&flags.address, "address", "0.0.0.0", "address to listen on")
	flag.IntVar(&flags.port, "port", 80, "port to bind on")

	nap.PayloadWrapper = payloadWrapper{}
	nap.PanicHandler = panicHandler{}
	nap.ResponseHeaders = []nap.Header{
		nap.Header{"Access-Control-Allow-Origin", []string{"*"}},
		nap.Header{"Access-Control-Allow-Methods", []string{"OPTIONS, PUT"}},
		nap.Header{"Content-Type", []string{"application/json"}},
		nap.Header{"Cache-Control", []string{"no-cache,must-revalidate"}},
	}
}

func main() {
	flag.Parse()

	router := mux.NewRouter()
	server := &http.Server{
		Addr:    fmt.Sprintf("%s:%d", flags.address, flags.port),
		Handler: router,
	}

	router.Handle("/validate", nap.HandlerFunc(optionsValidate)).Methods("OPTIONS")
	router.Handle("/validate", nap.HandlerFunc(putValidate)).Methods("PUT")
	router.Handle("/health", nap.HandlerFunc(getHealth)).Methods("GET")

	log.Fatalln(server.ListenAndServe())
}

func optionsValidate(r *http.Request) (interface{}, nap.Status) {
	return nil, nap.OK{}
}

func putValidate(r *http.Request) (interface{}, nap.Status) {
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return nil, nap.InternalError{err.Error()}
	}

	config := bytes.Replace(body, []byte("\r"), []byte{}, -1)
	if report, err := validate.Validate(config); err == nil {
		return report.Entries(), nap.OK{}
	} else {
		return nil, nap.InternalError{err.Error()}
	}
}

func getHealth(r *http.Request) (interface{}, nap.Status) {
	return nil, nap.OK{}
}
<|endoftext|>


<commit_before>package somaproto

type ValidityRequest struct {
	Validity *Validity `json:"validity,omitempty"`
}

type ValidityResult struct {
	Code     uint16     `json:"code,omitempty"`
	Status   string     `json:"status,omitempty"`
	Text     []string   `json:"text,omitempty"`
	Validity []Validity `json:"validity,omitempty"`
	JobId    string     `json:"jobid,omitempty"`
}

type Validity struct {
	SystemProperty string                `json:"system_property,omitempty"`
	ObjectType     string                `json:"object_type,omitempty"`
	Direct         bool                  `json:"direct,string"`
	Inherited      bool                  `json:"inherited,string"`
	Details        *ProtoValidityDetails `json:"details,omitempty"`
}

type ValidityDetails struct {
	CreatedAt string `json:"createdat,omitempty"`
	CreatedBy string `json:"createdby,omitempty"`
}

//
func (p *ValidityResult) ErrorMark(err error, imp bool, found bool,
	length int, jobid string) bool {
	if p.markError(err) {
		return true
	}
	if p.markImplemented(imp) {
		return true
	}
	if p.markFound(found, length) {
		return true
	}
	if p.hasJobId(jobid) {
		return p.markAccepted()
	}
	return p.markOk()
}

func (p *ValidityResult) markError(err error) bool {
	if err != nil {
		p.Code = 500
		p.Status = "ERROR"
		p.Text = []string{err.Error()}
		return true
	}
	return false
}

func (p *ValidityResult) markImplemented(f bool) bool {
	if f {
		p.Code = 501
		p.Status = "NOT IMPLEMENTED"
		return true
	}
	return false
}

func (p *ValidityResult) markFound(f bool, i int) bool {
	if f || i == 0 {
		p.Code = 404
		p.Status = "NOT FOUND"
		return true
	}
	return false
}

func (p *ValidityResult) markOk() bool {
	p.Code = 200
	p.Status = "OK"
	return false
}

func (p *ValidityResult) hasJobId(s string) bool {
	if s != "" {
		p.JobId = s
		return true
	}
	return false
}

func (p *ValidityResult) markAccepted() bool {
	p.Code = 202
	p.Status = "ACCEPTED"
	return false
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<commit_msg>Fix validitydetails struct reference<commit_after>package somaproto

type ValidityRequest struct {
	Validity *Validity `json:"validity,omitempty"`
}

type ValidityResult struct {
	Code     uint16     `json:"code,omitempty"`
	Status   string     `json:"status,omitempty"`
	Text     []string   `json:"text,omitempty"`
	Validity []Validity `json:"validity,omitempty"`
	JobId    string     `json:"jobid,omitempty"`
}

type Validity struct {
	SystemProperty string           `json:"system_property,omitempty"`
	ObjectType     string           `json:"object_type,omitempty"`
	Direct         bool             `json:"direct,string"`
	Inherited      bool             `json:"inherited,string"`
	Details        *ValidityDetails `json:"details,omitempty"`
}

type ValidityDetails struct {
	CreatedAt string `json:"createdat,omitempty"`
	CreatedBy string `json:"createdby,omitempty"`
}

//
func (p *ValidityResult) ErrorMark(err error, imp bool, found bool,
	length int, jobid string) bool {
	if p.markError(err) {
		return true
	}
	if p.markImplemented(imp) {
		return true
	}
	if p.markFound(found, length) {
		return true
	}
	if p.hasJobId(jobid) {
		return p.markAccepted()
	}
	return p.markOk()
}

func (p *ValidityResult) markError(err error) bool {
	if err != nil {
		p.Code = 500
		p.Status = "ERROR"
		p.Text = []string{err.Error()}
		return true
	}
	return false
}

func (p *ValidityResult) markImplemented(f bool) bool {
	if f {
		p.Code = 501
		p.Status = "NOT IMPLEMENTED"
		return true
	}
	return false
}

func (p *ValidityResult) markFound(f bool, i int) bool {
	if f || i == 0 {
		p.Code = 404
		p.Status = "NOT FOUND"
		return true
	}
	return false
}

func (p *ValidityResult) markOk() bool {
	p.Code = 200
	p.Status = "OK"
	return false
}

func (p *ValidityResult) hasJobId(s string) bool {
	if s != "" {
		p.JobId = s
		return true
	}
	return false
}

func (p *ValidityResult) markAccepted() bool {
	p.Code = 202
	p.Status = "ACCEPTED"
	return false
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<|endoftext|>


<commit_before>package main

import (
    "fmt"
)

func main() {
    fmt.Println("abc")
}<commit_msg>修改abc<commit_after>package main

import (
    "fmt"
)

func main() {
    fmt.Println("abc")
    fmt.Println("abc")
}<|endoftext|>


<commit_before>package executehelpers

import (
	"github.com/concourse/atc"
	"github.com/concourse/fly/commands/internal/deprecated"
	"github.com/concourse/fly/rc"
	"github.com/concourse/go-concourse/concourse"
	"github.com/tedsuo/rata"
)

func CreateBuild(
	atcRequester *deprecated.AtcRequester,
	client concourse.Client,
	privileged bool,
	inputs []Input,
	outputs []Output,
	config atc.TaskConfig,
	tags []string,
	target string,
) (atc.Build, error) {
	if err := config.Validate(); err != nil {
		return atc.Build{}, err
	}

	targetProps, err := rc.SelectTarget(target)
	if err != nil {
		return atc.Build{}, err
	}

	buildInputs := atc.AggregatePlan{}
	for i, input := range inputs {
		var getPlan atc.GetPlan
		if input.Path != "" {
			readPipe, err := atcRequester.CreateRequest(
				atc.ReadPipe,
				rata.Params{"pipe_id": input.Pipe.ID},
				nil,
			)
			if err != nil {
				return atc.Build{}, err
			}

			source := atc.Source{
				"uri": readPipe.URL.String(),
			}

			if targetProps.Token != nil {
				source["authorization"] = targetProps.Token.Type + " " + targetProps.Token.Value
			}
			getPlan = atc.GetPlan{
				Name:   input.Name,
				Type:   "archive",
				Source: source,
			}
		} else {
			getPlan = atc.GetPlan{
				Name:    input.Name,
				Type:    input.BuildInput.Type,
				Source:  input.BuildInput.Source,
				Version: input.BuildInput.Version,
				Params:  input.BuildInput.Params,
				Tags:    input.BuildInput.Tags,
			}
		}

		buildInputs = append(buildInputs, atc.Plan{
			Location: &atc.Location{
				// offset by 2 because aggregate gets parallelgroup ID 1
				ID:            uint(i) + 2,
				ParentID:      0,
				ParallelGroup: 1,
			},
			Get: &getPlan,
		})
	}

	taskPlan := atc.Plan{
		Location: &atc.Location{
			// offset by 1 because aggregate gets parallelgroup ID 1
			ID:       uint(len(inputs)) + 2,
			ParentID: 0,
		},
		Task: &atc.TaskPlan{
			Name:       "one-off",
			Privileged: privileged,
			Config:     &config,
		},
	}

	if len(tags) != 0 {
		taskPlan.Task.Tags = tags
	}

	buildOutputs := atc.AggregatePlan{}
	for i, output := range outputs {
		writePipe, err := atcRequester.CreateRequest(
			atc.WritePipe,
			rata.Params{"pipe_id": output.Pipe.ID},
			nil,
		)
		if err != nil {
			return atc.Build{}, err
		}
		source := atc.Source{
			"uri": writePipe.URL.String(),
		}

		params := atc.Params{
			"directory": output.Name,
		}

		if targetProps.Token != nil {
			source["authorization"] = targetProps.Token.Type + " " + targetProps.Token.Value
		}

		buildOutputs = append(buildOutputs, atc.Plan{
			Location: &atc.Location{
				ID:            taskPlan.Location.ID + 2 + uint(i),
				ParentID:      0,
				ParallelGroup: taskPlan.Location.ID + 1,
			},
			Put: &atc.PutPlan{
				Name:   output.Name,
				Type:   "archive",
				Source: source,
				Params: params,
			},
		})
	}

	var plan atc.Plan
	if len(buildOutputs) == 0 {
		plan = atc.Plan{
			OnSuccess: &atc.OnSuccessPlan{
				Step: atc.Plan{
					Aggregate: &buildInputs,
				},
				Next: taskPlan,
			},
		}
	} else {
		plan = atc.Plan{
			OnSuccess: &atc.OnSuccessPlan{
				Step: atc.Plan{
					Aggregate: &buildInputs,
				},
				Next: atc.Plan{
					Ensure: &atc.EnsurePlan{
						Step: taskPlan,
						Next: atc.Plan{
							Aggregate: &buildOutputs,
						},
					},
				},
			},
		}
	}

	return client.CreateBuild(plan)
}
<commit_msg>WIP - update to handle new location stuff<commit_after>package executehelpers

import (
	"time"

	"github.com/concourse/atc"
	"github.com/concourse/fly/commands/internal/deprecated"
	"github.com/concourse/fly/rc"
	"github.com/concourse/go-concourse/concourse"
	"github.com/tedsuo/rata"
)

func CreateBuild(
	atcRequester *deprecated.AtcRequester,
	client concourse.Client,
	privileged bool,
	inputs []Input,
	outputs []Output,
	config atc.TaskConfig,
	tags []string,
	target string,
) (atc.Build, error) {
	pf := atc.NewPlanFactory(time.Now().Unix())

	if err := config.Validate(); err != nil {
		return atc.Build{}, err
	}

	targetProps, err := rc.SelectTarget(target)
	if err != nil {
		return atc.Build{}, err
	}

	buildInputs := atc.AggregatePlan{}
	for _, input := range inputs {
		var getPlan atc.GetPlan
		if input.Path != "" {
			readPipe, err := atcRequester.CreateRequest(
				atc.ReadPipe,
				rata.Params{"pipe_id": input.Pipe.ID},
				nil,
			)
			if err != nil {
				return atc.Build{}, err
			}

			source := atc.Source{
				"uri": readPipe.URL.String(),
			}

			if targetProps.Token != nil {
				source["authorization"] = targetProps.Token.Type + " " + targetProps.Token.Value
			}

			getPlan = atc.GetPlan{
				Name:   input.Name,
				Type:   "archive",
				Source: source,
			}
		} else {
			getPlan = atc.GetPlan{
				Name:    input.Name,
				Type:    input.BuildInput.Type,
				Source:  input.BuildInput.Source,
				Version: input.BuildInput.Version,
				Params:  input.BuildInput.Params,
				Tags:    input.BuildInput.Tags,
			}
		}

		buildInputs = append(buildInputs, pf.NewPlan(getPlan))
	}

	taskPlan := pf.NewPlan(atc.TaskPlan{
		Name:       "one-off",
		Privileged: privileged,
		Config:     &config,
	})

	if len(tags) != 0 {
		taskPlan.Task.Tags = tags
	}

	buildOutputs := atc.AggregatePlan{}
	for _, output := range outputs {
		writePipe, err := atcRequester.CreateRequest(
			atc.WritePipe,
			rata.Params{"pipe_id": output.Pipe.ID},
			nil,
		)
		if err != nil {
			return atc.Build{}, err
		}
		source := atc.Source{
			"uri": writePipe.URL.String(),
		}

		params := atc.Params{
			"directory": output.Name,
		}

		if targetProps.Token != nil {
			source["authorization"] = targetProps.Token.Type + " " + targetProps.Token.Value
		}

		buildOutputs = append(buildOutputs, pf.NewPlan(atc.PutPlan{
			Name:   output.Name,
			Type:   "archive",
			Source: source,
			Params: params,
		}))
	}

	var plan atc.Plan
	if len(buildOutputs) == 0 {
		plan = pf.NewPlan(atc.OnSuccessPlan{
			Step: pf.NewPlan(buildInputs),
			Next: taskPlan,
		})
	} else {
		plan = pf.NewPlan(atc.OnSuccessPlan{
			Step: pf.NewPlan(buildInputs),
			Next: pf.NewPlan(atc.EnsurePlan{
				Step: taskPlan,
				Next: pf.NewPlan(buildOutputs),
			}),
		})
	}

	return client.CreateBuild(plan)
}
<|endoftext|>


<commit_before>package pq

import (
	"context"
	"database/sql/driver"
	"errors"
	"fmt"
	"os"
	"strings"
)

// Connector represents a fixed configuration for the pq driver with a given
// name. Connector satisfies the database/sql/driver Connector interface and
// can be used to create any number of DB Conn's via the database/sql OpenDB
// function.
//
// See https://golang.org/pkg/database/sql/driver/#Connector.
// See https://golang.org/pkg/database/sql/#OpenDB.
type Connector struct {
	opts   values
	dialer Dialer
}

// Connect returns a connection to the database using the fixed configuration
// of this Connector. Context is not used.
func (c *Connector) Connect(ctx context.Context) (driver.Conn, error) {
	return c.open(ctx)
}

// Driver returns the underlying driver of this Connector.
func (c *Connector) Driver() driver.Driver {
	return &Driver{}
}

// NewConnector returns a connector for the pq driver in a fixed configuration
// with the given dsn. The returned connector can be used to create any number
// of equivalent Conn's. The returned connector is intended to be used with
// database/sql.OpenDB.
//
// See https://golang.org/pkg/database/sql/driver/#Connector.
// See https://golang.org/pkg/database/sql/#OpenDB.
func NewConnector(dsn string) (*Connector, error) {
	var err error
	o := make(values)

	// A number of defaults are applied here, in this order:
	//
	// * Very low precedence defaults applied in every situation
	// * Environment variables
	// * Explicitly passed connection information
	o["host"] = "localhost"
	o["port"] = "5432"
	// N.B.: Extra float digits should be set to 3, but that breaks
	// Postgres 8.4 and older, where the max is 2.
	o["extra_float_digits"] = "2"
	for k, v := range parseEnviron(os.Environ()) {
		o[k] = v
	}

	if strings.HasPrefix(dsn, "postgres://") || strings.HasPrefix(dsn, "postgresql://") {
		dsn, err = ParseURL(dsn)
		if err != nil {
			return nil, err
		}
	}

	if err := parseOpts(dsn, o); err != nil {
		return nil, err
	}

	// Use the "fallback" application name if necessary
	if fallback, ok := o["fallback_application_name"]; ok {
		if _, ok := o["application_name"]; !ok {
			o["application_name"] = fallback
		}
	}

	// We can't work with any client_encoding other than UTF-8 currently.
	// However, we have historically allowed the user to set it to UTF-8
	// explicitly, and there's no reason to break such programs, so allow that.
	// Note that the "options" setting could also set client_encoding, but
	// parsing its value is not worth it.  Instead, we always explicitly send
	// client_encoding as a separate run-time parameter, which should override
	// anything set in options.
	if enc, ok := o["client_encoding"]; ok && !isUTF8(enc) {
		return nil, errors.New("client_encoding must be absent or 'UTF8'")
	}
	o["client_encoding"] = "UTF8"
	// DateStyle needs a similar treatment.
	if datestyle, ok := o["datestyle"]; ok {
		if datestyle != "ISO, MDY" {
			return nil, fmt.Errorf("setting datestyle must be absent or %v; got %v", "ISO, MDY", datestyle)
		}
	} else {
		o["datestyle"] = "ISO, MDY"
	}

	// If a user is not provided by any other means, the last
	// resort is to use the current operating system provided user
	// name.
	if _, ok := o["user"]; !ok {
		u, err := userCurrent()
		if err != nil {
			return nil, err
		}
		o["user"] = u
	}

	// SSL is not necessary or supported over UNIX domain sockets
	if network, _ := network(o); network == "unix" {
		o["sslmode"] = "disable"
	}

	return &Connector{opts: o, dialer: defaultDialer{}}, nil
}
<commit_msg>Global PgBouncerCompatibility avoids extra_float_digits conn param<commit_after>package pq

import (
	"context"
	"database/sql/driver"
	"errors"
	"fmt"
	"os"
	"strings"
)

var PgBouncerCompatibility = false

// Connector represents a fixed configuration for the pq driver with a given
// name. Connector satisfies the database/sql/driver Connector interface and
// can be used to create any number of DB Conn's via the database/sql OpenDB
// function.
//
// See https://golang.org/pkg/database/sql/driver/#Connector.
// See https://golang.org/pkg/database/sql/#OpenDB.
type Connector struct {
	opts   values
	dialer Dialer
}

// Connect returns a connection to the database using the fixed configuration
// of this Connector. Context is not used.
func (c *Connector) Connect(ctx context.Context) (driver.Conn, error) {
	return c.open(ctx)
}

// Driver returns the underlying driver of this Connector.
func (c *Connector) Driver() driver.Driver {
	return &Driver{}
}

// NewConnector returns a connector for the pq driver in a fixed configuration
// with the given dsn. The returned connector can be used to create any number
// of equivalent Conn's. The returned connector is intended to be used with
// database/sql.OpenDB.
//
// See https://golang.org/pkg/database/sql/driver/#Connector.
// See https://golang.org/pkg/database/sql/#OpenDB.
func NewConnector(dsn string) (*Connector, error) {
	var err error
	o := make(values)

	// A number of defaults are applied here, in this order:
	//
	// * Very low precedence defaults applied in every situation
	// * Environment variables
	// * Explicitly passed connection information
	o["host"] = "localhost"
	o["port"] = "5432"
	if !PgBouncerCompatibility {
		// N.B.: Extra float digits should be set to 3, but that breaks
		// Postgres 8.4 and older, where the max is 2.
		o["extra_float_digits"] = "2"
	}
	for k, v := range parseEnviron(os.Environ()) {
		o[k] = v
	}

	if strings.HasPrefix(dsn, "postgres://") || strings.HasPrefix(dsn, "postgresql://") {
		dsn, err = ParseURL(dsn)
		if err != nil {
			return nil, err
		}
	}

	if err := parseOpts(dsn, o); err != nil {
		return nil, err
	}

	// Use the "fallback" application name if necessary
	if fallback, ok := o["fallback_application_name"]; ok {
		if _, ok := o["application_name"]; !ok {
			o["application_name"] = fallback
		}
	}

	// We can't work with any client_encoding other than UTF-8 currently.
	// However, we have historically allowed the user to set it to UTF-8
	// explicitly, and there's no reason to break such programs, so allow that.
	// Note that the "options" setting could also set client_encoding, but
	// parsing its value is not worth it.  Instead, we always explicitly send
	// client_encoding as a separate run-time parameter, which should override
	// anything set in options.
	if enc, ok := o["client_encoding"]; ok && !isUTF8(enc) {
		return nil, errors.New("client_encoding must be absent or 'UTF8'")
	}
	o["client_encoding"] = "UTF8"
	// DateStyle needs a similar treatment.
	if datestyle, ok := o["datestyle"]; ok {
		if datestyle != "ISO, MDY" {
			return nil, fmt.Errorf("setting datestyle must be absent or %v; got %v", "ISO, MDY", datestyle)
		}
	} else {
		o["datestyle"] = "ISO, MDY"
	}

	// If a user is not provided by any other means, the last
	// resort is to use the current operating system provided user
	// name.
	if _, ok := o["user"]; !ok {
		u, err := userCurrent()
		if err != nil {
			return nil, err
		}
		o["user"] = u
	}

	// SSL is not necessary or supported over UNIX domain sockets
	if network, _ := network(o); network == "unix" {
		o["sslmode"] = "disable"
	}

	return &Connector{opts: o, dialer: defaultDialer{}}, nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 Uber Technologies, Inc.
//
// 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 mocks

import "github.com/stretchr/testify/mock"

import "time"

type Statter struct {
	mock.Mock
}

func (_m *Statter) Inc(_a0 string, _a1 int64, _a2 float32) error {
	ret := _m.Called(_a0, _a1, _a2)

	var r0 error
	if rf, ok := ret.Get(0).(func(string, int64, float32) error); ok {
		r0 = rf(_a0, _a1, _a2)
	} else {
		r0 = ret.Error(0)
	}

	return r0
}
func (_m *Statter) Dec(_a0 string, _a1 int64, _a2 float32) error {
	ret := _m.Called(_a0, _a1, _a2)

	var r0 error
	if rf, ok := ret.Get(0).(func(string, int64, float32) error); ok {
		r0 = rf(_a0, _a1, _a2)
	} else {
		r0 = ret.Error(0)
	}

	return r0
}
func (_m *Statter) Gauge(_a0 string, _a1 int64, _a2 float32) error {
	ret := _m.Called(_a0, _a1, _a2)

	var r0 error
	if rf, ok := ret.Get(0).(func(string, int64, float32) error); ok {
		r0 = rf(_a0, _a1, _a2)
	} else {
		r0 = ret.Error(0)
	}

	return r0
}
func (_m *Statter) GaugeDelta(_a0 string, _a1 int64, _a2 float32) error {
	ret := _m.Called(_a0, _a1, _a2)

	var r0 error
	if rf, ok := ret.Get(0).(func(string, int64, float32) error); ok {
		r0 = rf(_a0, _a1, _a2)
	} else {
		r0 = ret.Error(0)
	}

	return r0
}
func (_m *Statter) Timing(_a0 string, _a1 int64, _a2 float32) error {
	ret := _m.Called(_a0, _a1, _a2)

	var r0 error
	if rf, ok := ret.Get(0).(func(string, int64, float32) error); ok {
		r0 = rf(_a0, _a1, _a2)
	} else {
		r0 = ret.Error(0)
	}

	return r0
}
func (_m *Statter) TimingDuration(_a0 string, _a1 time.Duration, _a2 float32) error {
	ret := _m.Called(_a0, _a1, _a2)

	var r0 error
	if rf, ok := ret.Get(0).(func(string, time.Duration, float32) error); ok {
		r0 = rf(_a0, _a1, _a2)
	} else {
		r0 = ret.Error(0)
	}

	return r0
}
func (_m *Statter) Set(_a0 string, _a1 string, _a2 float32) error {
	ret := _m.Called(_a0, _a1, _a2)

	var r0 error
	if rf, ok := ret.Get(0).(func(string, string, float32) error); ok {
		r0 = rf(_a0, _a1, _a2)
	} else {
		r0 = ret.Error(0)
	}

	return r0
}
func (_m *Statter) SetInt(_a0 string, _a1 int64, _a2 float32) error {
	ret := _m.Called(_a0, _a1, _a2)

	var r0 error
	if rf, ok := ret.Get(0).(func(string, int64, float32) error); ok {
		r0 = rf(_a0, _a1, _a2)
	} else {
		r0 = ret.Error(0)
	}

	return r0
}
func (_m *Statter) Raw(_a0 string, _a1 string, _a2 float32) error {
	ret := _m.Called(_a0, _a1, _a2)

	var r0 error
	if rf, ok := ret.Get(0).(func(string, string, float32) error); ok {
		r0 = rf(_a0, _a1, _a2)
	} else {
		r0 = ret.Error(0)
	}

	return r0
}
func (_m *Statter) SetPrefix(_a0 string) {
	_m.Called(_a0)
}
func (_m *Statter) Close() error {
	ret := _m.Called()

	var r0 error
	if rf, ok := ret.Get(0).(func() error); ok {
		r0 = rf()
	} else {
		r0 = ret.Error(0)
	}

	return r0
}
<commit_msg>Update mocks to match the latest go-statsd-client/statsd<commit_after>// Copyright (c) 2015 Uber Technologies, Inc.
//
// 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 mocks

import mock "github.com/stretchr/testify/mock"
import statsd "github.com/cactus/go-statsd-client/statsd"
import time "time"

// Statter is an autogenerated mock type for the Statter type
type Statter struct {
	mock.Mock
}

// Close provides a mock function with given fields:
func (_m *Statter) Close() error {
	ret := _m.Called()

	var r0 error
	if rf, ok := ret.Get(0).(func() error); ok {
		r0 = rf()
	} else {
		r0 = ret.Error(0)
	}

	return r0
}

// Dec provides a mock function with given fields: _a0, _a1, _a2
func (_m *Statter) Dec(_a0 string, _a1 int64, _a2 float32) error {
	ret := _m.Called(_a0, _a1, _a2)

	var r0 error
	if rf, ok := ret.Get(0).(func(string, int64, float32) error); ok {
		r0 = rf(_a0, _a1, _a2)
	} else {
		r0 = ret.Error(0)
	}

	return r0
}

// Gauge provides a mock function with given fields: _a0, _a1, _a2
func (_m *Statter) Gauge(_a0 string, _a1 int64, _a2 float32) error {
	ret := _m.Called(_a0, _a1, _a2)

	var r0 error
	if rf, ok := ret.Get(0).(func(string, int64, float32) error); ok {
		r0 = rf(_a0, _a1, _a2)
	} else {
		r0 = ret.Error(0)
	}

	return r0
}

// GaugeDelta provides a mock function with given fields: _a0, _a1, _a2
func (_m *Statter) GaugeDelta(_a0 string, _a1 int64, _a2 float32) error {
	ret := _m.Called(_a0, _a1, _a2)

	var r0 error
	if rf, ok := ret.Get(0).(func(string, int64, float32) error); ok {
		r0 = rf(_a0, _a1, _a2)
	} else {
		r0 = ret.Error(0)
	}

	return r0
}

// Inc provides a mock function with given fields: _a0, _a1, _a2
func (_m *Statter) Inc(_a0 string, _a1 int64, _a2 float32) error {
	ret := _m.Called(_a0, _a1, _a2)

	var r0 error
	if rf, ok := ret.Get(0).(func(string, int64, float32) error); ok {
		r0 = rf(_a0, _a1, _a2)
	} else {
		r0 = ret.Error(0)
	}

	return r0
}

// NewSubStatter provides a mock function with given fields: _a0
func (_m *Statter) NewSubStatter(_a0 string) statsd.SubStatter {
	ret := _m.Called(_a0)

	var r0 statsd.SubStatter
	if rf, ok := ret.Get(0).(func(string) statsd.SubStatter); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(statsd.SubStatter)
		}
	}

	return r0
}

// Raw provides a mock function with given fields: _a0, _a1, _a2
func (_m *Statter) Raw(_a0 string, _a1 string, _a2 float32) error {
	ret := _m.Called(_a0, _a1, _a2)

	var r0 error
	if rf, ok := ret.Get(0).(func(string, string, float32) error); ok {
		r0 = rf(_a0, _a1, _a2)
	} else {
		r0 = ret.Error(0)
	}

	return r0
}

// Set provides a mock function with given fields: _a0, _a1, _a2
func (_m *Statter) Set(_a0 string, _a1 string, _a2 float32) error {
	ret := _m.Called(_a0, _a1, _a2)

	var r0 error
	if rf, ok := ret.Get(0).(func(string, string, float32) error); ok {
		r0 = rf(_a0, _a1, _a2)
	} else {
		r0 = ret.Error(0)
	}

	return r0
}

// SetInt provides a mock function with given fields: _a0, _a1, _a2
func (_m *Statter) SetInt(_a0 string, _a1 int64, _a2 float32) error {
	ret := _m.Called(_a0, _a1, _a2)

	var r0 error
	if rf, ok := ret.Get(0).(func(string, int64, float32) error); ok {
		r0 = rf(_a0, _a1, _a2)
	} else {
		r0 = ret.Error(0)
	}

	return r0
}

// SetPrefix provides a mock function with given fields: _a0
func (_m *Statter) SetPrefix(_a0 string) {
	_m.Called(_a0)
}

// Timing provides a mock function with given fields: _a0, _a1, _a2
func (_m *Statter) Timing(_a0 string, _a1 int64, _a2 float32) error {
	ret := _m.Called(_a0, _a1, _a2)

	var r0 error
	if rf, ok := ret.Get(0).(func(string, int64, float32) error); ok {
		r0 = rf(_a0, _a1, _a2)
	} else {
		r0 = ret.Error(0)
	}

	return r0
}

// TimingDuration provides a mock function with given fields: _a0, _a1, _a2
func (_m *Statter) TimingDuration(_a0 string, _a1 time.Duration, _a2 float32) error {
	ret := _m.Called(_a0, _a1, _a2)

	var r0 error
	if rf, ok := ret.Get(0).(func(string, time.Duration, float32) error); ok {
		r0 = rf(_a0, _a1, _a2)
	} else {
		r0 = ret.Error(0)
	}

	return r0
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"sort"
	"strconv"
	"strings"
	"sync"

	"github.com/UniversityRadioYork/baps3-go"
)

// GetResponse is the outer structure of all GET responses.
type GetResponse struct {
	Status string
	Value  interface{}
}

// GetOk creates a GetResponse wrapping a successful GET result.
func GetOk(value interface{}) *GetResponse {
	r := new(GetResponse)
	r.Status = "ok"
	r.Value = value
	return r
}

type bfConnector struct {
	conn   *baps3.Connector
	name   string
	wg     *sync.WaitGroup
	logger *log.Logger
	state  *serviceState

	reqCh chan httpRequest
	resCh <-chan baps3.Message

	// TODO(CaptainHayashi): move this away from baps3.Message to
	// something generic.
	updateCh chan<- baps3.Message
}

func initBfConnector(name string, updateCh chan baps3.Message, waitGroup *sync.WaitGroup, logger *log.Logger) (c *bfConnector) {
	resCh := make(chan baps3.Message)

	c = new(bfConnector)
	c.resCh = resCh
	c.conn = baps3.InitConnector(name, resCh, waitGroup, logger)
	c.name = name
	c.wg = waitGroup
	c.logger = logger
	c.reqCh = make(chan httpRequest)
	c.updateCh = updateCh
	c.state = initServiceState()
	return
}

func (c *bfConnector) Run() {
	defer c.wg.Done()
	defer close(c.conn.ReqCh)

	go c.conn.Run()

	fmt.Printf("connector %s now listening for requests\n", c.name)

	for {
		select {
		case rq, ok := <-c.reqCh:
			if !ok {
				return
			}

			// TODO(CaptainHayashi): probably make this more robust
			resource := strings.Replace(rq.resource, "/"+c.name, "", 1)
			fmt.Printf("connector %s response %s\n", c.name, resource)

			// TODO(CaptainHayashi): other methods
			rq.resCh <- c.get(resource)
		case res := <-c.resCh:
			if err := c.state.update(res); err != nil {
				fmt.Println(err)
			}
			c.updateCh <- res
		}
	}

	return
}

func splitResource(resource string) []string {
	res := strings.Split(strings.Trim(resource, "/"), "/")

	// The empty resource is returned as {""}: let's fix that
	if len(res) == 1 && res[0] == "" {
		res = []string{}
	}

	return res
}

func (c *bfConnector) get(resource string) interface{} {
	// TODO(CaptainHayashi): HTTP status codes

	resourcePath := splitResource(resource)

	r := c.rootGet(resourcePath)

	if r == nil {
		// TODO(CaptainHayashi): more errors
		return GetResponse{
			Status: "what",
			Value:  "resource not found: " + resource,
		}
	}

	return GetOk(r)
}

/* These resMaps describe simple composite resources, mapping each child
 * resource to the functions handling them.
 *
 * TODO(CaptainHayashi): add support for things that aren't GET
 *   (have each resource be a jump table of possible methods, or send the method
 *    to the resMap func?)
 * TODO(CaptainHayashi): decouple traversal from GET
 * TODO(CaptainHayashi): maybe make traversal iterative instead of recursive?
 */

type resMap map[string]func(*bfConnector, []string) interface{}

var (
	rootRes = resMap{
		"control": (*bfConnector).controlGet,
		"player":  (*bfConnector).playerGet,
		// "playlist": (*bfConnector).playlistGet
	}
	controlRes = resMap{
		"features": (*bfConnector).featuresGet,
		"state":    (*bfConnector).stateGet,
	}
	playerRes = resMap{
		"time": (*bfConnector).timeGet,
		"file": (*bfConnector).fileGet,
	}
)

func (c *bfConnector) getResource(rm resMap, resourcePath []string) interface{} {
	if len(resourcePath) == 0 {
		// Pull down all of the available child resources in this
		// resource.
		object := make(map[string]interface{})

		for k := range rm {
			child := rm[k](c, []string{})

			// Only add a key if the child definitely exists.
			if child != nil {
				object[k] = child
			}
		}

		return object
	}

	// Does the next step on the resource path exist?
	rfunc, ok := rm[resourcePath[0]]
	if ok {
		// Make it that resource's responsibility to
		// find the resource, then.
		return rfunc(c, resourcePath[1:])
	}
	return nil
}

// controlGet is the GET handler for the /control resource.
func (c *bfConnector) controlGet(resourcePath []string) interface{} {
	return c.getResource(controlRes, resourcePath)
}

// playerGet is the GET handler for the /player resource.
func (c *bfConnector) playerGet(resourcePath []string) interface{} {
	// TODO(CaptainHayashi): Probably a spec change, but the fact that this
	// resource is guarded by more than one feature is iffy.  Do we need a
	// Player feature?
	if !(c.state.hasFeature(baps3.FtFileLoad) || c.state.hasFeature(baps3.FtTimeReport)) {
		return nil
	}

	return c.getResource(playerRes, resourcePath)
}

// rootGet is the GET handler for the / resource.
func (c *bfConnector) rootGet(resourcePath []string) interface{} {
	return c.getResource(rootRes, resourcePath)
}

// GET value for /control/features
func (c *bfConnector) featuresGet(resourcePath []string) interface{} {
	// We only want a resource length of 0 (all features), or 1
	// (some index into the list of resources).
	if 1 < len(resourcePath) {
		return nil
	}

	fstrings := []string{}

	for k := range c.state.features {
		fstrings = append(fstrings, k.String())
	}

	// There's no need to sort these, but it doesn't hurt to make the list a
	// bit easier for humans to eyeball.
	sort.Strings(fstrings)

	// Did we want the whole resource (the features list)?
	if len(resourcePath) == 0 {
		return fstrings
	}

	// If not, we assume we were indexing into the features list.
	// TODO(CaptainHayashi): Factor this list-resource pattern out?
	//   It might be useful for playlists later.
	i, err := strconv.Atoi(resourcePath[0])
	// TODO(CaptainHayashi): handle err properly
	if err == nil && 0 <= i && i <= len(fstrings) {
		return fstrings[i]
	}

	return nil
}

// GET value for /control/state
func (c *bfConnector) stateGet(resourcePath []string) interface{} {
	if 0 < len(resourcePath) {
		return nil
	}

	return c.state.state
}

// GET value for /player/time
func (c *bfConnector) timeGet(resourcePath []string) interface{} {
	if 0 < len(resourcePath) {
		return nil
	}
	if !c.state.hasFeature(baps3.FtTimeReport) {
		return nil
	}

	// Time is reported in _micro_seconds
	return c.state.time.Nanoseconds() / 1000
}

// GET value for /player/file
func (c *bfConnector) fileGet(resourcePath []string) interface{} {
	if 0 < len(resourcePath) {
		return nil
	}
	if !c.state.hasFeature(baps3.FtFileLoad) {
		return nil
	}

	return c.state.file
}
<commit_msg>Shorten an argument name a bit.<commit_after>package main

import (
	"fmt"
	"log"
	"sort"
	"strconv"
	"strings"
	"sync"

	"github.com/UniversityRadioYork/baps3-go"
)

// GetResponse is the outer structure of all GET responses.
type GetResponse struct {
	Status string
	Value  interface{}
}

// GetOk creates a GetResponse wrapping a successful GET result.
func GetOk(value interface{}) *GetResponse {
	r := new(GetResponse)
	r.Status = "ok"
	r.Value = value
	return r
}

type bfConnector struct {
	conn   *baps3.Connector
	name   string
	wg     *sync.WaitGroup
	logger *log.Logger
	state  *serviceState

	reqCh chan httpRequest
	resCh <-chan baps3.Message

	// TODO(CaptainHayashi): move this away from baps3.Message to
	// something generic.
	updateCh chan<- baps3.Message
}

func initBfConnector(name string, updateCh chan baps3.Message, wg *sync.WaitGroup, logger *log.Logger) (c *bfConnector) {
	resCh := make(chan baps3.Message)

	c = new(bfConnector)
	c.resCh = resCh
	c.conn = baps3.InitConnector(name, resCh, wg, logger)
	c.name = name
	c.wg = wg
	c.logger = logger
	c.reqCh = make(chan httpRequest)
	c.updateCh = updateCh
	c.state = initServiceState()
	return
}

func (c *bfConnector) Run() {
	defer c.wg.Done()
	defer close(c.conn.ReqCh)

	go c.conn.Run()

	fmt.Printf("connector %s now listening for requests\n", c.name)

	for {
		select {
		case rq, ok := <-c.reqCh:
			if !ok {
				return
			}

			// TODO(CaptainHayashi): probably make this more robust
			resource := strings.Replace(rq.resource, "/"+c.name, "", 1)
			fmt.Printf("connector %s response %s\n", c.name, resource)

			// TODO(CaptainHayashi): other methods
			rq.resCh <- c.get(resource)
		case res := <-c.resCh:
			if err := c.state.update(res); err != nil {
				fmt.Println(err)
			}
			c.updateCh <- res
		}
	}

	return
}

func splitResource(resource string) []string {
	res := strings.Split(strings.Trim(resource, "/"), "/")

	// The empty resource is returned as {""}: let's fix that
	if len(res) == 1 && res[0] == "" {
		res = []string{}
	}

	return res
}

func (c *bfConnector) get(resource string) interface{} {
	// TODO(CaptainHayashi): HTTP status codes

	resourcePath := splitResource(resource)

	r := c.rootGet(resourcePath)

	if r == nil {
		// TODO(CaptainHayashi): more errors
		return GetResponse{
			Status: "what",
			Value:  "resource not found: " + resource,
		}
	}

	return GetOk(r)
}

/* These resMaps describe simple composite resources, mapping each child
 * resource to the functions handling them.
 *
 * TODO(CaptainHayashi): add support for things that aren't GET
 *   (have each resource be a jump table of possible methods, or send the method
 *    to the resMap func?)
 * TODO(CaptainHayashi): decouple traversal from GET
 * TODO(CaptainHayashi): maybe make traversal iterative instead of recursive?
 */

type resMap map[string]func(*bfConnector, []string) interface{}

var (
	rootRes = resMap{
		"control": (*bfConnector).controlGet,
		"player":  (*bfConnector).playerGet,
		// "playlist": (*bfConnector).playlistGet
	}
	controlRes = resMap{
		"features": (*bfConnector).featuresGet,
		"state":    (*bfConnector).stateGet,
	}
	playerRes = resMap{
		"time": (*bfConnector).timeGet,
		"file": (*bfConnector).fileGet,
	}
)

func (c *bfConnector) getResource(rm resMap, resourcePath []string) interface{} {
	if len(resourcePath) == 0 {
		// Pull down all of the available child resources in this
		// resource.
		object := make(map[string]interface{})

		for k := range rm {
			child := rm[k](c, []string{})

			// Only add a key if the child definitely exists.
			if child != nil {
				object[k] = child
			}
		}

		return object
	}

	// Does the next step on the resource path exist?
	rfunc, ok := rm[resourcePath[0]]
	if ok {
		// Make it that resource's responsibility to
		// find the resource, then.
		return rfunc(c, resourcePath[1:])
	}
	return nil
}

// controlGet is the GET handler for the /control resource.
func (c *bfConnector) controlGet(resourcePath []string) interface{} {
	return c.getResource(controlRes, resourcePath)
}

// playerGet is the GET handler for the /player resource.
func (c *bfConnector) playerGet(resourcePath []string) interface{} {
	// TODO(CaptainHayashi): Probably a spec change, but the fact that this
	// resource is guarded by more than one feature is iffy.  Do we need a
	// Player feature?
	if !(c.state.hasFeature(baps3.FtFileLoad) || c.state.hasFeature(baps3.FtTimeReport)) {
		return nil
	}

	return c.getResource(playerRes, resourcePath)
}

// rootGet is the GET handler for the / resource.
func (c *bfConnector) rootGet(resourcePath []string) interface{} {
	return c.getResource(rootRes, resourcePath)
}

// GET value for /control/features
func (c *bfConnector) featuresGet(resourcePath []string) interface{} {
	// We only want a resource length of 0 (all features), or 1
	// (some index into the list of resources).
	if 1 < len(resourcePath) {
		return nil
	}

	fstrings := []string{}

	for k := range c.state.features {
		fstrings = append(fstrings, k.String())
	}

	// There's no need to sort these, but it doesn't hurt to make the list a
	// bit easier for humans to eyeball.
	sort.Strings(fstrings)

	// Did we want the whole resource (the features list)?
	if len(resourcePath) == 0 {
		return fstrings
	}

	// If not, we assume we were indexing into the features list.
	// TODO(CaptainHayashi): Factor this list-resource pattern out?
	//   It might be useful for playlists later.
	i, err := strconv.Atoi(resourcePath[0])
	// TODO(CaptainHayashi): handle err properly
	if err == nil && 0 <= i && i <= len(fstrings) {
		return fstrings[i]
	}

	return nil
}

// GET value for /control/state
func (c *bfConnector) stateGet(resourcePath []string) interface{} {
	if 0 < len(resourcePath) {
		return nil
	}

	return c.state.state
}

// GET value for /player/time
func (c *bfConnector) timeGet(resourcePath []string) interface{} {
	if 0 < len(resourcePath) {
		return nil
	}
	if !c.state.hasFeature(baps3.FtTimeReport) {
		return nil
	}

	// Time is reported in _micro_seconds
	return c.state.time.Nanoseconds() / 1000
}

// GET value for /player/file
func (c *bfConnector) fileGet(resourcePath []string) interface{} {
	if 0 < len(resourcePath) {
		return nil
	}
	if !c.state.hasFeature(baps3.FtFileLoad) {
		return nil
	}

	return c.state.file
}
<|endoftext|>


<commit_before>package invdapi

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"net/textproto"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/Invoiced/invoiced-go/invdendpoint"
)

const (
	productionUrl = "https://api.invoiced.com"
	sandboxUrl    = "https://api.sandbox.invoiced.com"
	requestType   = "application/json"
)

const version = "6.1.0"

func Version() string {
	return version
}

type Connection struct {
	key     string
	client  *http.Client
	baseUrl string
}

func NewConnection(key string, sandbox bool) *Connection {
	url := productionUrl
	if sandbox {
		url = sandboxUrl
	}

	return &Connection{
		key:     key,
		client:  new(http.Client),
		baseUrl: url,
	}
}

func checkStatusForError(status int, r io.Reader) error {
	if status < 400 {
		return nil
	}

	body, err := ioutil.ReadAll(r)
	if err != nil {
		return err
	}

	apiError := new(APIError)

	err = json.Unmarshal(body, apiError)

	if err != nil {
		apiError.Type = string(body)
	}

	return errors.New(apiError.Error())
}

func pushDataIntoStruct(endpointData interface{}, respBody io.Reader) error {
	body, err := ioutil.ReadAll(respBody)
	if err != nil {
		return err
	}
	err = json.Unmarshal(body, endpointData)

	if err != nil {
		return err
	}

	return nil
}

func parseLinkHeader(s string) map[string]string {
	urlAndLinkMap := make(map[string]string)

	rawURLLinksAndRelations := strings.Split(s, ",")

	for _, rawURLLinkRelation := range rawURLLinksAndRelations {
		parsedRawURLAndRelation := strings.Split(rawURLLinkRelation, ";")
		url := parseLinkUrl(parsedRawURLAndRelation[0])
		relation := parseRelValue(parsedRawURLAndRelation[1])

		urlAndLinkMap[relation] = url
	}

	return urlAndLinkMap
}

func parseRelValue(s string) string {
	// parse rel="last" => last

	first := strings.Index(s, "\"")
	last := strings.LastIndex(s, "\"")

	trimmed := s[first+1 : last]

	trimmed = strings.TrimSpace(trimmed)

	return trimmed
}

func parseLinkUrl(s string) string {
	//<https://api.invoiced.com/invoices?page=1>
	trimmed := strings.TrimSpace(s)

	trimmed = strings.Trim(trimmed, "<")

	trimmed = strings.Trim(trimmed, ">")

	trimmed = strings.TrimSpace(trimmed)

	return trimmed
}

func addFilterAndSort(url string, filter *invdendpoint.Filter, sort *invdendpoint.Sort) string {
	emptyFilter := true
	emptySort := true

	if filter != nil && filter.String() != "" {
		emptyFilter = false
	}

	if sort != nil && sort.String() != "" {
		emptySort = false
	}

	if !emptyFilter && !emptySort {
		return url + "?" + filter.String() + "&" + sort.String()
	} else if !emptyFilter && emptySort {
		return url + "?" + filter.String()
	} else if emptyFilter && !emptySort {
		return url + "?" + sort.String()
	}

	return url
}

func addQueryParameter(url string, name string, value string) string {
	if strings.Contains(url, "?") {
		url += "&"
	} else {
		url += "?"
	}

	return url + name + "=" + value
}

func (c *Connection) get(endpoint string) (*http.Response, error) {
	url := c.baseUrl + endpoint
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}

	req.SetBasicAuth(c.key, "")

	resp, err := c.client.Do(req)

	return resp, err
}

func (c *Connection) post(endpoint string, body io.Reader) (*http.Response, error) {
	url := c.baseUrl + endpoint
	req, err := http.NewRequest("POST", url, body)
	if err != nil {
		return nil, err
	}

	req.SetBasicAuth(c.key, "")
	req.Header.Set("Content-Type", requestType)

	resp, err := c.client.Do(req)

	return resp, err
}

func (c *Connection) postWithFormData(endpoint string, body io.Reader,formContentType string) (*http.Response, error) {
	url := c.baseUrl + endpoint
	req, err := http.NewRequest("POST", url, body)
	if err != nil {
		return nil, err
	}

	req.SetBasicAuth(c.key, "")
	req.Header.Set("Content-Type", formContentType)

	resp, err := c.client.Do(req)

	return resp, err
}

func (c *Connection) patch(endpoint string, body io.Reader) (*http.Response, error) {
	url := c.baseUrl + endpoint
	req, err := http.NewRequest("PATCH", url, body)
	if err != nil {
		return nil, err
	}

	req.SetBasicAuth(c.key, "")
	req.Header.Set("Content-Type", requestType)

	resp, err := c.client.Do(req)

	return resp, err
}

func (c *Connection) deleteRequest(endpoint string) (*http.Response, error) {
	url := c.baseUrl + endpoint
	req, err := http.NewRequest("DELETE", url, nil)
	if err != nil {
		return nil, err
	}

	req.SetBasicAuth(c.key, "")
	req.Header.Set("Content-Type", requestType)

	resp, err := c.client.Do(req)

	return resp, err
}

func (c *Connection) create(endpoint string, requestData interface{}, responseData interface{}) error {
	b, err := json.Marshal(requestData)
	if err != nil {
		return err
	}

	body := bytes.NewBuffer(b)

	resp, err := c.post(endpoint, body)
	if err != nil {
		return err
	}

	apiError := checkStatusForError(resp.StatusCode, resp.Body)

	if apiError != nil {
		return apiError
	}

	if responseData == nil {
		return nil
	}

	err = pushDataIntoStruct(responseData, resp.Body)

	if err != nil {
		return err
	}

	return nil
}

// CreateFormFile is a convenience wrapper around CreatePart. It creates
// a new form-data header with the provided field name and file name.
func (c *Connection) CreateFormFile(w *multipart.Writer,fieldname, filename string,fileType string) (io.Writer, error) {
	h := make(textproto.MIMEHeader)
	h.Set("Content-Disposition",
		fmt.Sprintf(`form-data; name="%s"; filename="%s"`,
			escapeQuotes(fieldname), escapeQuotes(filename)))
	h.Set("Content-Type", fileType)
	return w.CreatePart(h)
}

var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"")

func escapeQuotes(s string) string {
	return quoteEscaper.Replace(s)
}

func (c *Connection) upload(endpoint string,filePath string, fileParamName string, fileParams map[string]string,fileType string, responseData interface{}) error {
	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)
	file, err := os.Open(filePath)
	if err != nil {
		return err
	}
	defer file.Close()

	part, err := c.CreateFormFile(writer,fileParamName, filepath.Base(filePath),fileType)

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

	for key, val := range fileParams {
		_ = writer.WriteField(key, val)
	}
	err = writer.Close()

	if err != nil {
		return err
	}

	resp, err := c.postWithFormData(endpoint, body,writer.FormDataContentType())

	if err != nil {
		return err
	}

	apiError := checkStatusForError(resp.StatusCode, resp.Body)

	if apiError != nil {
		return apiError
	}

	if responseData == nil {
		return nil
	}

	err = pushDataIntoStruct(responseData, resp.Body)

	if err != nil {
		return err
	}

	return nil
}

func (c *Connection) delete(endpoint string) error {
	resp, err := c.deleteRequest(endpoint)
	if err != nil {
		return err
	}

	apiError := checkStatusForError(resp.StatusCode, resp.Body)

	if apiError != nil {
		return apiError
	}

	return nil
}

func (c *Connection) update(endpoint string, requestData interface{}, responseData interface{}) error {
	b, err := json.Marshal(requestData)
	if err != nil {
		return err
	}

	body := bytes.NewBuffer(b)

	resp, err := c.patch(endpoint, body)
	if err != nil {
		return err
	}

	apiError := checkStatusForError(resp.StatusCode, resp.Body)

	if apiError != nil {
		return apiError
	}

	err = pushDataIntoStruct(responseData, resp.Body)

	if err != nil {
		return err
	}

	return nil
}

func (c *Connection) postWithoutData(endpoint string, responseData interface{}) error {
	resp, err := c.post(endpoint, nil)
	if err != nil {
		return err
	}

	apiError := checkStatusForError(resp.StatusCode, resp.Body)

	if apiError != nil {
		return apiError
	}

	err = pushDataIntoStruct(responseData, resp.Body)

	if err != nil {
		return err
	}

	return nil
}

func (c *Connection) count(endpoint string) (int64, error) {
	resp, err := c.get(endpoint)
	if err != nil {
		return -1, err
	}

	defer resp.Body.Close()

	apiErr := checkStatusForError(resp.StatusCode, resp.Body)

	if apiErr != nil {
		return 0, apiErr
	}

	s := resp.Header.Get("X-Total-Count")

	i, err := strconv.ParseInt(s, 10, 64)
	if err != nil {
		return -1, err
	}

	return i, nil
}

func (c *Connection) retrieveDataFromAPI(endpoint string, endpointData interface{}) (string, error) {
	nextURL := ""

	resp, err := c.get(endpoint)
	if err != nil {
		return "", err
	}

	defer resp.Body.Close()

	link := resp.Header.Get("Link")

	if link != "" {
		nextMap := parseLinkHeader(link)

		if nextMap["self"] != nextMap["next"] {
			nextURL = nextMap["next"]
		}
	}

	apiError := checkStatusForError(resp.StatusCode, resp.Body)

	if apiError != nil {
		return "", apiError
	}

	err = pushDataIntoStruct(endpointData, resp.Body)

	if err != nil {
		return "", err
	}

	return strings.Replace(nextURL, c.baseUrl, "", -1), nil
}
<commit_msg>take care of golint issues<commit_after>package invdapi

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"net/textproto"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/Invoiced/invoiced-go/invdendpoint"
)

const (
	productionUrl = "https://api.invoiced.com"
	sandboxUrl    = "https://api.sandbox.invoiced.com"
	requestType   = "application/json"
)

const version = "6.1.0"

func Version() string {
	return version
}

type Connection struct {
	key     string
	client  *http.Client
	baseUrl string
}

func NewConnection(key string, sandbox bool) *Connection {
	url := productionUrl
	if sandbox {
		url = sandboxUrl
	}

	return &Connection{
		key:     key,
		client:  new(http.Client),
		baseUrl: url,
	}
}

func checkStatusForError(status int, r io.Reader) error {
	if status < 400 {
		return nil
	}

	body, err := ioutil.ReadAll(r)
	if err != nil {
		return err
	}

	apiError := new(APIError)

	err = json.Unmarshal(body, apiError)

	if err != nil {
		apiError.Type = string(body)
	}

	return errors.New(apiError.Error())
}

func pushDataIntoStruct(endpointData interface{}, respBody io.Reader) error {
	body, err := ioutil.ReadAll(respBody)
	if err != nil {
		return err
	}
	err = json.Unmarshal(body, endpointData)

	if err != nil {
		return err
	}

	return nil
}

func parseLinkHeader(s string) map[string]string {
	urlAndLinkMap := make(map[string]string)

	rawURLLinksAndRelations := strings.Split(s, ",")

	for _, rawURLLinkRelation := range rawURLLinksAndRelations {
		parsedRawURLAndRelation := strings.Split(rawURLLinkRelation, ";")
		url := parseLinkUrl(parsedRawURLAndRelation[0])
		relation := parseRelValue(parsedRawURLAndRelation[1])

		urlAndLinkMap[relation] = url
	}

	return urlAndLinkMap
}

func parseRelValue(s string) string {
	// parse rel="last" => last

	first := strings.Index(s, "\"")
	last := strings.LastIndex(s, "\"")

	trimmed := s[first+1 : last]

	trimmed = strings.TrimSpace(trimmed)

	return trimmed
}

func parseLinkUrl(s string) string {
	//<https://api.invoiced.com/invoices?page=1>
	trimmed := strings.TrimSpace(s)

	trimmed = strings.Trim(trimmed, "<")

	trimmed = strings.Trim(trimmed, ">")

	trimmed = strings.TrimSpace(trimmed)

	return trimmed
}

func addFilterAndSort(url string, filter *invdendpoint.Filter, sort *invdendpoint.Sort) string {
	emptyFilter := true
	emptySort := true

	if filter != nil && filter.String() != "" {
		emptyFilter = false
	}

	if sort != nil && sort.String() != "" {
		emptySort = false
	}

	if !emptyFilter && !emptySort {
		return url + "?" + filter.String() + "&" + sort.String()
	} else if !emptyFilter && emptySort {
		return url + "?" + filter.String()
	} else if emptyFilter && !emptySort {
		return url + "?" + sort.String()
	}

	return url
}

func addQueryParameter(url string, name string, value string) string {
	if strings.Contains(url, "?") {
		url += "&"
	} else {
		url += "?"
	}

	return url + name + "=" + value
}

func (c *Connection) get(endpoint string) (*http.Response, error) {
	url := c.baseUrl + endpoint
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}

	req.SetBasicAuth(c.key, "")

	resp, err := c.client.Do(req)

	return resp, err
}

func (c *Connection) post(endpoint string, body io.Reader) (*http.Response, error) {
	url := c.baseUrl + endpoint
	req, err := http.NewRequest("POST", url, body)
	if err != nil {
		return nil, err
	}

	req.SetBasicAuth(c.key, "")
	req.Header.Set("Content-Type", requestType)

	resp, err := c.client.Do(req)

	return resp, err
}

func (c *Connection) postWithFormData(endpoint string, body io.Reader,formContentType string) (*http.Response, error) {
	url := c.baseUrl + endpoint
	req, err := http.NewRequest("POST", url, body)
	if err != nil {
		return nil, err
	}

	req.SetBasicAuth(c.key, "")
	req.Header.Set("Content-Type", formContentType)

	resp, err := c.client.Do(req)

	return resp, err
}

func (c *Connection) patch(endpoint string, body io.Reader) (*http.Response, error) {
	url := c.baseUrl + endpoint
	req, err := http.NewRequest("PATCH", url, body)
	if err != nil {
		return nil, err
	}

	req.SetBasicAuth(c.key, "")
	req.Header.Set("Content-Type", requestType)

	resp, err := c.client.Do(req)

	return resp, err
}

func (c *Connection) deleteRequest(endpoint string) (*http.Response, error) {
	url := c.baseUrl + endpoint
	req, err := http.NewRequest("DELETE", url, nil)
	if err != nil {
		return nil, err
	}

	req.SetBasicAuth(c.key, "")
	req.Header.Set("Content-Type", requestType)

	resp, err := c.client.Do(req)

	return resp, err
}

func (c *Connection) create(endpoint string, requestData interface{}, responseData interface{}) error {
	b, err := json.Marshal(requestData)
	if err != nil {
		return err
	}

	body := bytes.NewBuffer(b)

	resp, err := c.post(endpoint, body)
	if err != nil {
		return err
	}

	apiError := checkStatusForError(resp.StatusCode, resp.Body)

	if apiError != nil {
		return apiError
	}

	if responseData == nil {
		return nil
	}

	err = pushDataIntoStruct(responseData, resp.Body)

	if err != nil {
		return err
	}

	return nil
}

// CreateFormFile is a convenience wrapper around CreatePart. It creates
// a new form-data header with the provided field name and file name.
func (c *Connection) CreateFormFile(w *multipart.Writer,fieldname, filename string,fileType string) (io.Writer, error) {
	h := make(textproto.MIMEHeader)
	h.Set("Content-Disposition",
		fmt.Sprintf(`form-data; name="%s"; filename="%s"`,
			escapeQuotes(fieldname), escapeQuotes(filename)))
	h.Set("Content-Type", fileType)
	return w.CreatePart(h)
}

var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"")

func escapeQuotes(s string) string {
	return quoteEscaper.Replace(s)
}

func (c *Connection) upload(endpoint string,filePath string, fileParamName string, fileParams map[string]string,fileType string, responseData interface{}) error {
	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)
	file, err := os.Open(filePath)
	if err != nil {
		return err
	}
	defer file.Close()

	part, err := c.CreateFormFile(writer,fileParamName, filepath.Base(filePath),fileType)

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

	if err != nil {
		return err
	}

	for key, val := range fileParams {
		_ = writer.WriteField(key, val)
	}
	err = writer.Close()

	if err != nil {
		return err
	}

	resp, err := c.postWithFormData(endpoint, body,writer.FormDataContentType())

	if err != nil {
		return err
	}

	apiError := checkStatusForError(resp.StatusCode, resp.Body)

	if apiError != nil {
		return apiError
	}

	if responseData == nil {
		return nil
	}

	err = pushDataIntoStruct(responseData, resp.Body)

	if err != nil {
		return err
	}

	return nil
}

func (c *Connection) delete(endpoint string) error {
	resp, err := c.deleteRequest(endpoint)
	if err != nil {
		return err
	}

	apiError := checkStatusForError(resp.StatusCode, resp.Body)

	if apiError != nil {
		return apiError
	}

	return nil
}

func (c *Connection) update(endpoint string, requestData interface{}, responseData interface{}) error {
	b, err := json.Marshal(requestData)
	if err != nil {
		return err
	}

	body := bytes.NewBuffer(b)

	resp, err := c.patch(endpoint, body)
	if err != nil {
		return err
	}

	apiError := checkStatusForError(resp.StatusCode, resp.Body)

	if apiError != nil {
		return apiError
	}

	err = pushDataIntoStruct(responseData, resp.Body)

	if err != nil {
		return err
	}

	return nil
}

func (c *Connection) postWithoutData(endpoint string, responseData interface{}) error {
	resp, err := c.post(endpoint, nil)
	if err != nil {
		return err
	}

	apiError := checkStatusForError(resp.StatusCode, resp.Body)

	if apiError != nil {
		return apiError
	}

	err = pushDataIntoStruct(responseData, resp.Body)

	if err != nil {
		return err
	}

	return nil
}

func (c *Connection) count(endpoint string) (int64, error) {
	resp, err := c.get(endpoint)
	if err != nil {
		return -1, err
	}

	defer resp.Body.Close()

	apiErr := checkStatusForError(resp.StatusCode, resp.Body)

	if apiErr != nil {
		return 0, apiErr
	}

	s := resp.Header.Get("X-Total-Count")

	i, err := strconv.ParseInt(s, 10, 64)
	if err != nil {
		return -1, err
	}

	return i, nil
}

func (c *Connection) retrieveDataFromAPI(endpoint string, endpointData interface{}) (string, error) {
	nextURL := ""

	resp, err := c.get(endpoint)
	if err != nil {
		return "", err
	}

	defer resp.Body.Close()

	link := resp.Header.Get("Link")

	if link != "" {
		nextMap := parseLinkHeader(link)

		if nextMap["self"] != nextMap["next"] {
			nextURL = nextMap["next"]
		}
	}

	apiError := checkStatusForError(resp.StatusCode, resp.Body)

	if apiError != nil {
		return "", apiError
	}

	err = pushDataIntoStruct(endpointData, resp.Body)

	if err != nil {
		return "", err
	}

	return strings.Replace(nextURL, c.baseUrl, "", -1), nil
}
<|endoftext|>


<commit_before>// Copyright © 2013, S.Çağlar Onur
// Use of this source code is governed by a LGPLv2.1
// license that can be found in the LICENSE file.
//
// Authors:
// S.Çağlar Onur <caglar@10ur.org>

// +build linux

package lxc

// #include <lxc/lxc.h>
// #include <lxc/lxccontainer.h>
// #include "lxc.h"
import "C"

import (
	"strconv"
	"strings"
	"sync"
	"time"
	"unsafe"
)

type Container struct {
	container *C.struct_lxc_container
	sync.RWMutex
}

// Returns container's name
func (lxc *Container) Name() string {
	lxc.RLock()
	defer lxc.RUnlock()
	return C.GoString(lxc.container.name)
}

// Returns whether the container is already defined or not
func (lxc *Container) Defined() bool {
	lxc.RLock()
	defer lxc.RUnlock()
	return bool(C.lxc_container_defined(lxc.container))
}

// Returns whether the container is already running or not
func (lxc *Container) Running() bool {
	lxc.RLock()
	defer lxc.RUnlock()
	return bool(C.lxc_container_running(lxc.container))
}

// Returns the container's state
func (lxc *Container) State() State {
	lxc.RLock()
	defer lxc.RUnlock()
	return stateMap[C.GoString(C.lxc_container_state(lxc.container))]
}

// Returns the container's PID
func (lxc *Container) InitPID() int {
	lxc.RLock()
	defer lxc.RUnlock()
	return int(C.lxc_container_init_pid(lxc.container))
}

// Returns whether the daemonize flag is set
func (lxc *Container) Daemonize() bool {
	lxc.RLock()
	defer lxc.RUnlock()
	return bool(lxc.container.daemonize != 0)
}

// Sets the daemonize flag
func (lxc *Container) SetDaemonize() {
	lxc.Lock()
	defer lxc.Unlock()
	C.lxc_container_want_daemonize(lxc.container)
}

// Freezes the running container
func (lxc *Container) Freeze() bool {
	lxc.Lock()
	defer lxc.Unlock()
	return bool(C.lxc_container_freeze(lxc.container))
}

// Unfreezes the frozen container
func (lxc *Container) Unfreeze() bool {
	lxc.Lock()
	defer lxc.Unlock()
	return bool(C.lxc_container_unfreeze(lxc.container))
}

// Creates the container using given template and arguments
func (lxc *Container) Create(template string, args []string) bool {
	lxc.Lock()
	defer lxc.Unlock()

	ctemplate := C.CString(template)
	defer C.free(unsafe.Pointer(ctemplate))
	if args != nil {
		cargs := makeArgs(args)
		defer freeArgs(cargs)
		return bool(C.lxc_container_create(lxc.container, ctemplate, &cargs[0]))
	}
	return bool(C.lxc_container_create(lxc.container, ctemplate, nil))
}

// Starts the container
func (lxc *Container) Start(useinit bool, args []string) bool {
	lxc.Lock()
	defer lxc.Unlock()

	cuseinit := 0
	if useinit {
		cuseinit = 1
	}
	if args != nil {
		cargs := makeArgs(args)
		defer freeArgs(cargs)
		return bool(C.lxc_container_start(lxc.container, C.int(cuseinit), &cargs[0]))
	}
	return bool(C.lxc_container_start(lxc.container, C.int(cuseinit), nil))
}

// Stops the container
func (lxc *Container) Stop() bool {
	lxc.Lock()
	defer lxc.Unlock()
	return bool(C.lxc_container_stop(lxc.container))
}

// Shutdowns the container
func (lxc *Container) Shutdown(timeout int) bool {
	lxc.Lock()
	defer lxc.Unlock()
	return bool(C.lxc_container_shutdown(lxc.container, C.int(timeout)))
}

// Destroys the container
func (lxc *Container) Destroy() bool {
	lxc.Lock()
	defer lxc.Unlock()
	return bool(C.lxc_container_destroy(lxc.container))
}

// Waits till the container changes its state or timeouts
func (lxc *Container) Wait(state State, timeout int) bool {
	lxc.Lock()
	defer lxc.Unlock()
	cstate := C.CString(state.String())
	defer C.free(unsafe.Pointer(cstate))
	return bool(C.lxc_container_wait(lxc.container, cstate, C.int(timeout)))
}

// Returns the container's configuration file's name
func (lxc *Container) ConfigFileName() string {
	lxc.RLock()
	defer lxc.RUnlock()

	// allocated in lxc.c
	configFileName := C.lxc_container_config_file_name(lxc.container)
	defer C.free(unsafe.Pointer(configFileName))

	return C.GoString(configFileName)
}

// Returns the value of the given key
func (lxc *Container) ConfigItem(key string) []string {
	lxc.RLock()
	defer lxc.RUnlock()

	ckey := C.CString(key)
	defer C.free(unsafe.Pointer(ckey))

	// allocated in lxc.c
	configItem := C.lxc_container_get_config_item(lxc.container, ckey)
	defer C.free(unsafe.Pointer(configItem))

	ret := strings.TrimSpace(C.GoString(configItem))
	return strings.Split(ret, "\n")
}

// Sets the value of given key
func (lxc *Container) SetConfigItem(key string, value string) bool {
	lxc.Lock()
	defer lxc.Unlock()
	ckey := C.CString(key)
	defer C.free(unsafe.Pointer(ckey))
	cvalue := C.CString(value)
	defer C.free(unsafe.Pointer(cvalue))
	return bool(C.lxc_container_set_config_item(lxc.container, ckey, cvalue))
}

// Returns the value of the given key
func (lxc *Container) CgroupItem(key string) []string {
	lxc.RLock()
	defer lxc.RUnlock()

	ckey := C.CString(key)
	defer C.free(unsafe.Pointer(ckey))

	// allocated in lxc.c
	cgroupItem := C.lxc_container_get_cgroup_item(lxc.container, ckey)
	defer C.free(unsafe.Pointer(cgroupItem))

	ret := strings.TrimSpace(C.GoString(cgroupItem))
	return strings.Split(ret, "\n")
}

// Sets the value of given key
func (lxc *Container) SetCgroupItem(key string, value string) bool {
	lxc.Lock()
	defer lxc.Unlock()
	ckey := C.CString(key)
	defer C.free(unsafe.Pointer(ckey))
	cvalue := C.CString(value)
	defer C.free(unsafe.Pointer(cvalue))
	return bool(C.lxc_container_set_cgroup_item(lxc.container, ckey, cvalue))
}

// Clears the value of given key
func (lxc *Container) ClearConfigItem(key string) bool {
	lxc.Lock()
	defer lxc.Unlock()
	ckey := C.CString(key)
	defer C.free(unsafe.Pointer(ckey))
	return bool(C.lxc_container_clear_config_item(lxc.container, ckey))
}

// Returns the keys
func (lxc *Container) Keys(key string) []string {
	lxc.RLock()
	defer lxc.RUnlock()

	ckey := C.CString(key)
	defer C.free(unsafe.Pointer(ckey))

	// allocated in lxc.c
	keys := C.lxc_container_get_keys(lxc.container, ckey)
	defer C.free(unsafe.Pointer(keys))

	ret := strings.TrimSpace(C.GoString(keys))
	return strings.Split(ret, "\n")
}

// Loads the configuration file from given path
func (lxc *Container) LoadConfigFile(path string) bool {
	lxc.Lock()
	defer lxc.Unlock()
	cpath := C.CString(path)
	defer C.free(unsafe.Pointer(cpath))
	return bool(C.lxc_container_load_config(lxc.container, cpath))
}

// Saves the configuration file to given path
func (lxc *Container) SaveConfigFile(path string) bool {
	lxc.Lock()
	defer lxc.Unlock()
	cpath := C.CString(path)
	defer C.free(unsafe.Pointer(cpath))
	return bool(C.lxc_container_save_config(lxc.container, cpath))
}

// Returns the configuration file's path
func (lxc *Container) ConfigPath() string {
	lxc.RLock()
	defer lxc.RUnlock()
	return C.GoString(C.lxc_container_get_config_path(lxc.container))
}

// Sets the configuration file's path
func (lxc *Container) SetConfigPath(path string) bool {
	lxc.Lock()
	defer lxc.Unlock()
	cpath := C.CString(path)
	defer C.free(unsafe.Pointer(cpath))
	return bool(C.lxc_container_set_config_path(lxc.container, cpath))
}

func (lxc *Container) NumberOfNetworkInterfaces() int {
	lxc.RLock()
	defer lxc.RUnlock()
	if lxc.Running() {
		return len(lxc.ConfigItem("lxc.network"))
	}
	return -1
}

func (lxc *Container) MemoryUsageInBytes() (ByteSize, error) {
	lxc.RLock()
	defer lxc.RUnlock()
	if lxc.Running() {
		memUsed, err := strconv.ParseFloat(lxc.CgroupItem("memory.usage_in_bytes")[0], 64)
		if err != nil {
			return -1, err
		}
		return ByteSize(memUsed), err
	}
	return -1, nil
}

func (lxc *Container) SwapUsageInBytes() (ByteSize, error) {
	lxc.RLock()
	defer lxc.RUnlock()
	if lxc.Running() {
		swapUsed, err := strconv.ParseFloat(lxc.CgroupItem("memory.memsw.usage_in_bytes")[0], 64)
		if err != nil {
			return -1, err
		}
		return ByteSize(swapUsed), err
	}
	return -1, nil
}

func (lxc *Container) MemoryLimitInBytes() (ByteSize, error) {
	lxc.RLock()
	defer lxc.RUnlock()
	if lxc.Running() {
		memLimit, err := strconv.ParseFloat(lxc.CgroupItem("memory.limit_in_bytes")[0], 64)
		if err != nil {
			return -1, err
		}
		return ByteSize(memLimit), err
	}
	return -1, nil
}

func (lxc *Container) SwapLimitInBytes() (ByteSize, error) {
	lxc.RLock()
	defer lxc.RUnlock()
	if lxc.Running() {
		swapLimit, err := strconv.ParseFloat(lxc.CgroupItem("memory.memsw.limit_in_bytes")[0], 64)
		if err != nil {
			return -1, err
		}
		return ByteSize(swapLimit), err
	}
	return -1, nil
}

// Returns the total CPU time (in nanoseconds) consumed by all tasks in this cgroup (including tasks lower in the hierarchy).
func (lxc *Container) CPUTime() (time.Duration, error) {
	lxc.RLock()
	defer lxc.RUnlock()
	if lxc.Running() {
		cpuUsage, err := strconv.ParseInt(lxc.CgroupItem("cpuacct.usage")[0], 10, 64)
		if err != nil {
			return -1, err
		}
		return time.Duration(cpuUsage), err
	}
	return -1, nil
}

// Returns the CPU time (in nanoseconds) consumed on each CPU by all tasks in this cgroup (including tasks lower in the hierarchy).
func (lxc *Container) CPUTimePerCPU() ([]time.Duration, error) {
	lxc.RLock()
	defer lxc.RUnlock()
	var cpuTimes []time.Duration

	if lxc.Running() {
		for _, v := range strings.Split(lxc.CgroupItem("cpuacct.usage_percpu")[0], " ") {
			cpuUsage, err := strconv.ParseInt(v, 10, 64)
			if err != nil {
				return nil, err
			}
			cpuTimes = append(cpuTimes, time.Duration(cpuUsage))
		}
		return cpuTimes, nil
	}
	return nil, nil
}

// Returns the number of CPU cycles (in the units defined by USER_HZ on the system) consumed by tasks in this cgroup and its children in both user mode and system (kernel) mode.
func (lxc *Container) CPUStats() ([]int64, error) {
	lxc.RLock()
	defer lxc.RUnlock()
	if lxc.Running() {
		cpuStat := lxc.CgroupItem("cpuacct.stat")
		user, _ := strconv.ParseInt(strings.Split(cpuStat[0], " ")[1], 10, 64)
		system, _ := strconv.ParseInt(strings.Split(cpuStat[1], " ")[1], 10, 64)
		return []int64{user, system}, nil
	}
	return nil, nil
}
<commit_msg>do not export our internal lock<commit_after>// Copyright © 2013, S.Çağlar Onur
// Use of this source code is governed by a LGPLv2.1
// license that can be found in the LICENSE file.
//
// Authors:
// S.Çağlar Onur <caglar@10ur.org>

// +build linux

package lxc

// #include <lxc/lxc.h>
// #include <lxc/lxccontainer.h>
// #include "lxc.h"
import "C"

import (
	"strconv"
	"strings"
	"sync"
	"time"
	"unsafe"
)

type Container struct {
	container *C.struct_lxc_container
	mu        sync.RWMutex
}

// Returns container's name
func (lxc *Container) Name() string {
	lxc.mu.RLock()
	defer lxc.mu.RUnlock()
	return C.GoString(lxc.container.name)
}

// Returns whether the container is already defined or not
func (lxc *Container) Defined() bool {
	lxc.mu.RLock()
	defer lxc.mu.RUnlock()
	return bool(C.lxc_container_defined(lxc.container))
}

// Returns whether the container is already running or not
func (lxc *Container) Running() bool {
	lxc.mu.RLock()
	defer lxc.mu.RUnlock()
	return bool(C.lxc_container_running(lxc.container))
}

// Returns the container's state
func (lxc *Container) State() State {
	lxc.mu.RLock()
	defer lxc.mu.RUnlock()
	return stateMap[C.GoString(C.lxc_container_state(lxc.container))]
}

// Returns the container's PID
func (lxc *Container) InitPID() int {
	lxc.mu.RLock()
	defer lxc.mu.RUnlock()
	return int(C.lxc_container_init_pid(lxc.container))
}

// Returns whether the daemonize flag is set
func (lxc *Container) Daemonize() bool {
	lxc.mu.RLock()
	defer lxc.mu.RUnlock()
	return bool(lxc.container.daemonize != 0)
}

// Sets the daemonize flag
func (lxc *Container) SetDaemonize() {
	lxc.mu.Lock()
	defer lxc.mu.Unlock()
	C.lxc_container_want_daemonize(lxc.container)
}

// Freezes the running container
func (lxc *Container) Freeze() bool {
	lxc.mu.Lock()
	defer lxc.mu.Unlock()
	return bool(C.lxc_container_freeze(lxc.container))
}

// Unfreezes the frozen container
func (lxc *Container) Unfreeze() bool {
	lxc.mu.Lock()
	defer lxc.mu.Unlock()
	return bool(C.lxc_container_unfreeze(lxc.container))
}

// Creates the container using given template and arguments
func (lxc *Container) Create(template string, args []string) bool {
	lxc.mu.Lock()
	defer lxc.mu.Unlock()

	ctemplate := C.CString(template)
	defer C.free(unsafe.Pointer(ctemplate))
	if args != nil {
		cargs := makeArgs(args)
		defer freeArgs(cargs)
		return bool(C.lxc_container_create(lxc.container, ctemplate, &cargs[0]))
	}
	return bool(C.lxc_container_create(lxc.container, ctemplate, nil))
}

// Starts the container
func (lxc *Container) Start(useinit bool, args []string) bool {
	lxc.mu.Lock()
	defer lxc.mu.Unlock()

	cuseinit := 0
	if useinit {
		cuseinit = 1
	}
	if args != nil {
		cargs := makeArgs(args)
		defer freeArgs(cargs)
		return bool(C.lxc_container_start(lxc.container, C.int(cuseinit), &cargs[0]))
	}
	return bool(C.lxc_container_start(lxc.container, C.int(cuseinit), nil))
}

// Stops the container
func (lxc *Container) Stop() bool {
	lxc.mu.Lock()
	defer lxc.mu.Unlock()
	return bool(C.lxc_container_stop(lxc.container))
}

// Shutdowns the container
func (lxc *Container) Shutdown(timeout int) bool {
	lxc.mu.Lock()
	defer lxc.mu.Unlock()
	return bool(C.lxc_container_shutdown(lxc.container, C.int(timeout)))
}

// Destroys the container
func (lxc *Container) Destroy() bool {
	lxc.mu.Lock()
	defer lxc.mu.Unlock()
	return bool(C.lxc_container_destroy(lxc.container))
}

// Waits till the container changes its state or timeouts
func (lxc *Container) Wait(state State, timeout int) bool {
	lxc.mu.Lock()
	defer lxc.mu.Unlock()
	cstate := C.CString(state.String())
	defer C.free(unsafe.Pointer(cstate))
	return bool(C.lxc_container_wait(lxc.container, cstate, C.int(timeout)))
}

// Returns the container's configuration file's name
func (lxc *Container) ConfigFileName() string {
	lxc.mu.RLock()
	defer lxc.mu.RUnlock()

	// allocated in lxc.c
	configFileName := C.lxc_container_config_file_name(lxc.container)
	defer C.free(unsafe.Pointer(configFileName))

	return C.GoString(configFileName)
}

// Returns the value of the given key
func (lxc *Container) ConfigItem(key string) []string {
	lxc.mu.RLock()
	defer lxc.mu.RUnlock()

	ckey := C.CString(key)
	defer C.free(unsafe.Pointer(ckey))

	// allocated in lxc.c
	configItem := C.lxc_container_get_config_item(lxc.container, ckey)
	defer C.free(unsafe.Pointer(configItem))

	ret := strings.TrimSpace(C.GoString(configItem))
	return strings.Split(ret, "\n")
}

// Sets the value of given key
func (lxc *Container) SetConfigItem(key string, value string) bool {
	lxc.mu.Lock()
	defer lxc.mu.Unlock()
	ckey := C.CString(key)
	defer C.free(unsafe.Pointer(ckey))
	cvalue := C.CString(value)
	defer C.free(unsafe.Pointer(cvalue))
	return bool(C.lxc_container_set_config_item(lxc.container, ckey, cvalue))
}

// Returns the value of the given key
func (lxc *Container) CgroupItem(key string) []string {
	lxc.mu.RLock()
	defer lxc.mu.RUnlock()

	ckey := C.CString(key)
	defer C.free(unsafe.Pointer(ckey))

	// allocated in lxc.c
	cgroupItem := C.lxc_container_get_cgroup_item(lxc.container, ckey)
	defer C.free(unsafe.Pointer(cgroupItem))

	ret := strings.TrimSpace(C.GoString(cgroupItem))
	return strings.Split(ret, "\n")
}

// Sets the value of given key
func (lxc *Container) SetCgroupItem(key string, value string) bool {
	lxc.mu.Lock()
	defer lxc.mu.Unlock()
	ckey := C.CString(key)
	defer C.free(unsafe.Pointer(ckey))
	cvalue := C.CString(value)
	defer C.free(unsafe.Pointer(cvalue))
	return bool(C.lxc_container_set_cgroup_item(lxc.container, ckey, cvalue))
}

// Clears the value of given key
func (lxc *Container) ClearConfigItem(key string) bool {
	lxc.mu.Lock()
	defer lxc.mu.Unlock()
	ckey := C.CString(key)
	defer C.free(unsafe.Pointer(ckey))
	return bool(C.lxc_container_clear_config_item(lxc.container, ckey))
}

// Returns the keys
func (lxc *Container) Keys(key string) []string {
	lxc.mu.RLock()
	defer lxc.mu.RUnlock()

	ckey := C.CString(key)
	defer C.free(unsafe.Pointer(ckey))

	// allocated in lxc.c
	keys := C.lxc_container_get_keys(lxc.container, ckey)
	defer C.free(unsafe.Pointer(keys))

	ret := strings.TrimSpace(C.GoString(keys))
	return strings.Split(ret, "\n")
}

// Loads the configuration file from given path
func (lxc *Container) LoadConfigFile(path string) bool {
	lxc.mu.Lock()
	defer lxc.mu.Unlock()
	cpath := C.CString(path)
	defer C.free(unsafe.Pointer(cpath))
	return bool(C.lxc_container_load_config(lxc.container, cpath))
}

// Saves the configuration file to given path
func (lxc *Container) SaveConfigFile(path string) bool {
	lxc.mu.Lock()
	defer lxc.mu.Unlock()
	cpath := C.CString(path)
	defer C.free(unsafe.Pointer(cpath))
	return bool(C.lxc_container_save_config(lxc.container, cpath))
}

// Returns the configuration file's path
func (lxc *Container) ConfigPath() string {
	lxc.mu.RLock()
	defer lxc.mu.RUnlock()
	return C.GoString(C.lxc_container_get_config_path(lxc.container))
}

// Sets the configuration file's path
func (lxc *Container) SetConfigPath(path string) bool {
	lxc.mu.Lock()
	defer lxc.mu.Unlock()
	cpath := C.CString(path)
	defer C.free(unsafe.Pointer(cpath))
	return bool(C.lxc_container_set_config_path(lxc.container, cpath))
}

func (lxc *Container) NumberOfNetworkInterfaces() int {
	lxc.mu.RLock()
	defer lxc.mu.RUnlock()
	if lxc.Running() {
		return len(lxc.ConfigItem("lxc.network"))
	}
	return -1
}

func (lxc *Container) MemoryUsageInBytes() (ByteSize, error) {
	lxc.mu.RLock()
	defer lxc.mu.RUnlock()
	if lxc.Running() {
		memUsed, err := strconv.ParseFloat(lxc.CgroupItem("memory.usage_in_bytes")[0], 64)
		if err != nil {
			return -1, err
		}
		return ByteSize(memUsed), err
	}
	return -1, nil
}

func (lxc *Container) SwapUsageInBytes() (ByteSize, error) {
	lxc.mu.RLock()
	defer lxc.mu.RUnlock()
	if lxc.Running() {
		swapUsed, err := strconv.ParseFloat(lxc.CgroupItem("memory.memsw.usage_in_bytes")[0], 64)
		if err != nil {
			return -1, err
		}
		return ByteSize(swapUsed), err
	}
	return -1, nil
}

func (lxc *Container) MemoryLimitInBytes() (ByteSize, error) {
	lxc.mu.RLock()
	defer lxc.mu.RUnlock()
	if lxc.Running() {
		memLimit, err := strconv.ParseFloat(lxc.CgroupItem("memory.limit_in_bytes")[0], 64)
		if err != nil {
			return -1, err
		}
		return ByteSize(memLimit), err
	}
	return -1, nil
}

func (lxc *Container) SwapLimitInBytes() (ByteSize, error) {
	lxc.mu.RLock()
	defer lxc.mu.RUnlock()
	if lxc.Running() {
		swapLimit, err := strconv.ParseFloat(lxc.CgroupItem("memory.memsw.limit_in_bytes")[0], 64)
		if err != nil {
			return -1, err
		}
		return ByteSize(swapLimit), err
	}
	return -1, nil
}

// Returns the total CPU time (in nanoseconds) consumed by all tasks in this cgroup (including tasks lower in the hierarchy).
func (lxc *Container) CPUTime() (time.Duration, error) {
	lxc.mu.RLock()
	defer lxc.mu.RUnlock()
	if lxc.Running() {
		cpuUsage, err := strconv.ParseInt(lxc.CgroupItem("cpuacct.usage")[0], 10, 64)
		if err != nil {
			return -1, err
		}
		return time.Duration(cpuUsage), err
	}
	return -1, nil
}

// Returns the CPU time (in nanoseconds) consumed on each CPU by all tasks in this cgroup (including tasks lower in the hierarchy).
func (lxc *Container) CPUTimePerCPU() ([]time.Duration, error) {
	lxc.mu.RLock()
	defer lxc.mu.RUnlock()
	var cpuTimes []time.Duration

	if lxc.Running() {
		for _, v := range strings.Split(lxc.CgroupItem("cpuacct.usage_percpu")[0], " ") {
			cpuUsage, err := strconv.ParseInt(v, 10, 64)
			if err != nil {
				return nil, err
			}
			cpuTimes = append(cpuTimes, time.Duration(cpuUsage))
		}
		return cpuTimes, nil
	}
	return nil, nil
}

// Returns the number of CPU cycles (in the units defined by USER_HZ on the system) consumed by tasks in this cgroup and its children in both user mode and system (kernel) mode.
func (lxc *Container) CPUStats() ([]int64, error) {
	lxc.mu.RLock()
	defer lxc.mu.RUnlock()
	if lxc.Running() {
		cpuStat := lxc.CgroupItem("cpuacct.stat")
		user, _ := strconv.ParseInt(strings.Split(cpuStat[0], " ")[1], 10, 64)
		system, _ := strconv.ParseInt(strings.Split(cpuStat[1], " ")[1], 10, 64)
		return []int64{user, system}, nil
	}
	return nil, nil
}
<|endoftext|>