<commit_before>package route

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"os"
	"testing"
)

var endpoints = []string{
	"/test/handler/1",
	"/test/handler/2",
}

var rootTestingPath = "/temp_TestServeStaticResources"

func TestRouter(t *testing.T) {
	r := new(Router)
	r.HandleFunc(endpoints[0], handler1)
	r.HandleFunc(endpoints[1], handler2)

	server := httptest.NewServer(r)
	defer server.Close()

	var urls = []string{
		"/test/handler/1",
		"/test/handler/2",
		"/test/handler/1/",
		"/test/handler/2/",
	}

	for _, u := range urls {
		res, err := http.Get(server.URL + u)
		if err != nil {
			t.Fatal(err)
		}

		if res.StatusCode != http.StatusOK {
			t.Fatal(res)
		}
	}
}

func TestNotFoundRoute(t *testing.T) {
	r := new(Router)
	r.HandleFunc(endpoints[0], handler1)
	r.HandleFunc(endpoints[1], handler2)

	server := httptest.NewServer(r)
	defer server.Close()

	res, err := http.Get(server.URL + "/test/handler/3")
	if err != nil {
		t.Fatal(err)
	}

	if res.StatusCode != http.StatusNotFound {
		t.Fatal(res)
	}
}

func TestFoundRouteWithVariables(t *testing.T) {
	r := new(Router)
	r.HandleFunc("/test/handler/:id/hello/", handlerHello)
	r.HandleFunc("/test/handler/:id/hello/:f-o-o", handlerHello2)

	urls := []string{
		"/test/handler/1/hello",
		"/test/handler/2/hello/",
		"/test/handler/2123/hello/johndoe",
		"/test/handler/2123/hello/johndoe/",
	}

	server := httptest.NewServer(r)
	defer server.Close()

	for _, u := range urls {
		res, err := http.Get(server.URL + u)
		if err != nil {
			t.Fatal(err)
		}
		if res.StatusCode != http.StatusOK {
			t.Fatal(res)
		}
	}

	res, _ := http.Get(server.URL + urls[1])
	defer res.Body.Close()
	body, _ := ioutil.ReadAll(res.Body)
	expectedBody := "2"
	gotBody := string(body)
	if gotBody != expectedBody {
		t.Fatal("Expected", expectedBody, "and got", gotBody)
	}

	res, _ = http.Get(server.URL + urls[2])
	defer res.Body.Close()
	body, _ = ioutil.ReadAll(res.Body)
	expectedBody = "2123,johndoe"
	gotBody = string(body)
	if gotBody != expectedBody {
		t.Fatal("Expected", expectedBody, "and got", gotBody)
	}
}

func TestNotFoundRouteWithVariables(t *testing.T) {
	r := new(Router)
	r.HandleFunc("/test/handler/:id/hello", handlerHello)

	server := httptest.NewServer(r)
	defer server.Close()

	res, err := http.Get(server.URL + "/test/handler///hello")
	if err != nil {
		t.Fatal(err)
	}
	if res.StatusCode != http.StatusNotFound {
		t.Fatal(res)
	}
}

func TestServeStaticResources(t *testing.T) {

	createTestingData(rootTestingPath)

	r := new(Router)
	r.AddStaticResource(&rootTestingPath)

	server := httptest.NewServer(r)
	defer server.Close()

	res, err := http.Get(server.URL + "/")
	if err != nil {
		t.Fatal(err)
	}

	if res.StatusCode != http.StatusOK {
		t.Fatal(res)
	}

	cleanTestingData(rootTestingPath)
}

func TestServeTwoLevelStaticResources(t *testing.T) {
	cssTestingPath := rootTestingPath + "/css"

	createTestingData(rootTestingPath)

	r := new(Router)
	r.AddStaticResource(&cssTestingPath)

	server := httptest.NewServer(r)
	defer server.Close()

	res, err := http.Get(server.URL + "/")
	if err != nil {
		t.Fatal(err)
	}

	if res.StatusCode != http.StatusOK {
		t.Fatal(res)
	}

	cleanTestingData(rootTestingPath)
}

func TestServeNonExistingStaticResources(t *testing.T) {
	jsTestingPath := rootTestingPath + "/js"

	createTestingData(rootTestingPath)

	r := new(Router)
	r.AddStaticResource(&jsTestingPath)

	server := httptest.NewServer(r)
	defer server.Close()

	res, err := http.Get(server.URL + "/")
	if err != nil {
		t.Fatal(err)
	}

	if res.StatusCode != http.StatusNotFound {
		t.Fatal(res)
	}

	cleanTestingData(rootTestingPath)
}

func TestMatch(t *testing.T) {
	params := make(map[string]string)
	routeWithOneVariable := route{"/test/handler/:user_id/hello", params, nil}
	routeWithMultipleVariables := route{"/test/handler/:user_id/hello/:username", params, nil}

	matchingPattern := "/test/handler/50/hello"
	if !routeWithOneVariable.match(matchingPattern) {
		t.Fatal("route should match the pattern: pattern = " + routeWithOneVariable.pattern + ", path = " + matchingPattern)
	}

	if routeWithOneVariable.params["user_id"] != "50" {
		t.Fatal("Value for 'user_id' is not the expected one: expected = 50, stored = " + routeWithOneVariable.params["user_id"])
	}

	nonMatchingPattern := "/test/handler//hello"
	if !routeWithOneVariable.match(nonMatchingPattern) {
		t.Fatal("route should not match the pattern: pattern = " + routeWithOneVariable.pattern + ", path = " + nonMatchingPattern)
	}

	matchingPattern = "/test/handler/johndoe/hello"
	if !routeWithOneVariable.match(matchingPattern) {
		t.Fatal("route should match the pattern: pattern = " + routeWithOneVariable.pattern + ", path = " + matchingPattern)
	}

	if routeWithOneVariable.params["user_id"] != "johndoe" {
		t.Fatal("Value for 'user_id' is not the expected one: expected = johndoe, stored = " + routeWithOneVariable.params["user_id"])
	}

	matchingPattern = "/test/handler/50/hello/johndoe"
	if !routeWithMultipleVariables.match(matchingPattern) {
		t.Fatal("route should match the pattern: pattern = " + routeWithMultipleVariables.pattern + ", path = " + matchingPattern)
	}

	if routeWithMultipleVariables.params["user_id"] != "50" && routeWithMultipleVariables.params["username"] != "johndoe" {
		t.Fatal("Values stored in map are not the expected ones: expected = [50, john doe], stored = [" + routeWithMultipleVariables.params["user_id"] + ", " + routeWithMultipleVariables.params["username"] + "]")
	}

	nonMatchingPattern = "/test/handler/:user_id/hello//"
	if !routeWithMultipleVariables.match(nonMatchingPattern) {
		t.Fatal("route should not match the pattern: pattern = " + routeWithMultipleVariables.pattern + ", path = " + nonMatchingPattern)
	}
}

func handler1(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "testHandler1 has been reached!")
}

func handler2(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "testHandler2 has been reached!")
}

func handlerHello(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, Context.Get(r, "id"))
}

func handlerHello2(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, Context.Get(r, "id")+","+Context.Get(r, "f-o-o"))
}

func createTestingData(rootTestPath string) {
	src, err := os.Stat(rootTestPath)
	if err != nil || !src.IsDir() {
		os.Mkdir(rootTestPath, 0777)
	}

	src, err = os.Stat(rootTestPath + "/index.html")
	if err != nil || src.IsDir() {
		os.Create(rootTestPath + "/index.html")
	}

	cssTestPath := rootTestPath + "/css"

	src, err = os.Stat(cssTestPath)
	if err != nil || !src.IsDir() {
		os.Mkdir(cssTestPath, 0777)
	}

	src, err = os.Stat(cssTestPath + "/main.css")
	if err != nil || src.IsDir() {
		os.Create(cssTestPath + "/main.css")
	}
}

func cleanTestingData(nameTest string) {
	src, err := os.Stat(nameTest)
	if err == nil && src.IsDir() {
		os.RemoveAll(nameTest)
	}
}
<commit_msg>add new case to test named parameters containing '_ and ''-'<commit_after>package route

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"os"
	"testing"
)

var endpoints = []string{
	"/test/handler/1",
	"/test/handler/2",
}

var rootTestingPath = "/temp_TestServeStaticResources"

func TestRouter(t *testing.T) {
	r := new(Router)
	r.HandleFunc(endpoints[0], handler1)
	r.HandleFunc(endpoints[1], handler2)

	server := httptest.NewServer(r)
	defer server.Close()

	var urls = []string{
		"/test/handler/1",
		"/test/handler/2",
		"/test/handler/1/",
		"/test/handler/2/",
	}

	for _, u := range urls {
		res, err := http.Get(server.URL + u)
		if err != nil {
			t.Fatal(err)
		}

		if res.StatusCode != http.StatusOK {
			t.Fatal(res)
		}
	}
}

func TestNotFoundRoute(t *testing.T) {
	r := new(Router)
	r.HandleFunc(endpoints[0], handler1)
	r.HandleFunc(endpoints[1], handler2)

	server := httptest.NewServer(r)
	defer server.Close()

	res, err := http.Get(server.URL + "/test/handler/3")
	if err != nil {
		t.Fatal(err)
	}

	if res.StatusCode != http.StatusNotFound {
		t.Fatal(res)
	}
}

func TestFoundRouteWithVariables(t *testing.T) {
	r := new(Router)
	r.HandleFunc("/test/handler/:id/hello/", handlerHello)
	r.HandleFunc("/test/handler/:id/hello/:foo", handlerHello2)

	urls := []string{
		"/test/handler/1/hello",
		"/test/handler/2/hello/",
		"/test/handler/2123/hello/johndoe",
		"/test/handler/2123/hello/johndoe/",
	}

	server := httptest.NewServer(r)
	defer server.Close()

	for _, u := range urls {
		res, err := http.Get(server.URL + u)
		if err != nil {
			t.Fatal(err)
		}
		if res.StatusCode != http.StatusOK {
			t.Fatal(res)
		}
	}

	res, _ := http.Get(server.URL + urls[1])
	defer res.Body.Close()
	body, _ := ioutil.ReadAll(res.Body)
	expectedBody := "2"
	gotBody := string(body)
	if gotBody != expectedBody {
		t.Fatal("Expected", expectedBody, "and got", gotBody)
	}

	res, _ = http.Get(server.URL + urls[2])
	defer res.Body.Close()
	body, _ = ioutil.ReadAll(res.Body)
	expectedBody = "2123,johndoe"
	gotBody = string(body)
	if gotBody != expectedBody {
		t.Fatal("Expected", expectedBody, "and got", gotBody)
	}
}

func TestNotFoundRouteWithVariables(t *testing.T) {
	r := new(Router)
	r.HandleFunc("/test/handler/:id/hello", handlerHello)

	server := httptest.NewServer(r)
	defer server.Close()

	res, err := http.Get(server.URL + "/test/handler///hello")
	if err != nil {
		t.Fatal(err)
	}
	if res.StatusCode != http.StatusNotFound {
		t.Fatal(res)
	}
}

func TestFoundRouteWithSpecialCharacter(t *testing.T) {
	r := new(Router)
	r.HandleFunc("/test/handler/:user_id/hello/:f-o-o", handlerHelloWithSpecialCharacter)

	server := httptest.NewServer(r)
	defer server.Close()

	res, err := http.Get(server.URL + "/test/handler/42/hello/johndoe")
	if err != nil {
		t.Fatal(err)
	}
	if res.StatusCode != http.StatusOK {
		t.Fatal(res)
	}

	defer res.Body.Close()
	body, _ := ioutil.ReadAll(res.Body)
	expectedBody := "42,johndoe"
	gotBody := string(body)
	if gotBody != expectedBody {
		t.Fatal("Expected", expectedBody, "and got", gotBody)
	}
}

func TestServeStaticResources(t *testing.T) {

	createTestingData(rootTestingPath)

	r := new(Router)
	r.AddStaticResource(&rootTestingPath)

	server := httptest.NewServer(r)
	defer server.Close()

	res, err := http.Get(server.URL + "/")
	if err != nil {
		t.Fatal(err)
	}

	if res.StatusCode != http.StatusOK {
		t.Fatal(res)
	}

	cleanTestingData(rootTestingPath)
}

func TestServeTwoLevelStaticResources(t *testing.T) {
	cssTestingPath := rootTestingPath + "/css"

	createTestingData(rootTestingPath)

	r := new(Router)
	r.AddStaticResource(&cssTestingPath)

	server := httptest.NewServer(r)
	defer server.Close()

	res, err := http.Get(server.URL + "/")
	if err != nil {
		t.Fatal(err)
	}

	if res.StatusCode != http.StatusOK {
		t.Fatal(res)
	}

	cleanTestingData(rootTestingPath)
}

func TestServeNonExistingStaticResources(t *testing.T) {
	jsTestingPath := rootTestingPath + "/js"

	createTestingData(rootTestingPath)

	r := new(Router)
	r.AddStaticResource(&jsTestingPath)

	server := httptest.NewServer(r)
	defer server.Close()

	res, err := http.Get(server.URL + "/")
	if err != nil {
		t.Fatal(err)
	}

	if res.StatusCode != http.StatusNotFound {
		t.Fatal(res)
	}

	cleanTestingData(rootTestingPath)
}

func TestMatch(t *testing.T) {
	params := make(map[string]string)
	routeWithOneVariable := route{"/test/handler/:id/hello", params, nil}
	routeWithMultipleVariables := route{"/test/handler/:id/hello/:username", params, nil}

	matchingPattern := "/test/handler/50/hello"
	if !routeWithOneVariable.match(matchingPattern) {
		t.Fatal("route should match the pattern: pattern = " + routeWithOneVariable.pattern + ", path = " + matchingPattern)
	}

	if routeWithOneVariable.params["id"] != "50" {
		t.Fatal("Value for 'user_id' is not the expected one: expected = 50, stored = " + routeWithOneVariable.params["id"])
	}

	nonMatchingPattern := "/test/handler//hello"
	if !routeWithOneVariable.match(nonMatchingPattern) {
		t.Fatal("route should not match the pattern: pattern = " + routeWithOneVariable.pattern + ", path = " + nonMatchingPattern)
	}

	matchingPattern = "/test/handler/johndoe/hello"
	if !routeWithOneVariable.match(matchingPattern) {
		t.Fatal("route should match the pattern: pattern = " + routeWithOneVariable.pattern + ", path = " + matchingPattern)
	}

	if routeWithOneVariable.params["id"] != "johndoe" {
		t.Fatal("Value for 'user_id' is not the expected one: expected = johndoe, stored = " + routeWithOneVariable.params["id"])
	}

	matchingPattern = "/test/handler/50/hello/johndoe"
	if !routeWithMultipleVariables.match(matchingPattern) {
		t.Fatal("route should match the pattern: pattern = " + routeWithMultipleVariables.pattern + ", path = " + matchingPattern)
	}

	if routeWithMultipleVariables.params["id"] != "50" && routeWithMultipleVariables.params["username"] != "johndoe" {
		t.Fatal("Values stored in map are not the expected ones: expected = [50, john doe], stored = [" + routeWithMultipleVariables.params["id"] + ", " + routeWithMultipleVariables.params["username"] + "]")
	}

	nonMatchingPattern = "/test/handler/:id/hello//"
	if !routeWithMultipleVariables.match(nonMatchingPattern) {
		t.Fatal("route should not match the pattern: pattern = " + routeWithMultipleVariables.pattern + ", path = " + nonMatchingPattern)
	}
}

func handler1(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "testHandler1 has been reached!")
}

func handler2(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "testHandler2 has been reached!")
}

func handlerHello(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, Context.Get(r, "id"))
}

func handlerHello2(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, Context.Get(r, "id")+","+Context.Get(r, "foo"))
}

func handlerHelloWithSpecialCharacter(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, Context.Get(r, "user_id")+","+Context.Get(r, "f-o-o"))
}

func createTestingData(rootTestPath string) {
	src, err := os.Stat(rootTestPath)
	if err != nil || !src.IsDir() {
		os.Mkdir(rootTestPath, 0777)
	}

	src, err = os.Stat(rootTestPath + "/index.html")
	if err != nil || src.IsDir() {
		os.Create(rootTestPath + "/index.html")
	}

	cssTestPath := rootTestPath + "/css"

	src, err = os.Stat(cssTestPath)
	if err != nil || !src.IsDir() {
		os.Mkdir(cssTestPath, 0777)
	}

	src, err = os.Stat(cssTestPath + "/main.css")
	if err != nil || src.IsDir() {
		os.Create(cssTestPath + "/main.css")
	}
}

func cleanTestingData(nameTest string) {
	src, err := os.Stat(nameTest)
	if err == nil && src.IsDir() {
		os.RemoveAll(nameTest)
	}
}
<|endoftext|>


<commit_before>package project7_8

import (
	"encoding/json"
	"github.com/HRODEV/project7_8/dbActions"
	"github.com/HRODEV/project7_8/models"
	"github.com/gorilla/mux"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/sqlite"
	"net/http"
	"strconv"
)

func UserAuthGet(w http.ResponseWriter, r *http.Request, db *gorm.DB) {
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("Not implemented yet"))
}

func UserGet(w http.ResponseWriter, r *http.Request, db *gorm.DB) {
	vars := mux.Vars(r)
	id, _ := strconv.ParseUint(vars["id"], 10, 32)

	var user models.User
	dbActions.GetUserByID(uint(id), &user, db)

	js, err := json.Marshal(user)

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Write(js)
}

func UserPost(w http.ResponseWriter, r *http.Request, db *gorm.DB) {
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("Not implemented yet"))
}

func UserProjectsGet(w http.ResponseWriter, r *http.Request, db *gorm.DB) {
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("Not implemented yet"))
}
<commit_msg>Convert User routes to make use of db layer<commit_after>package project7_8

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

	"github.com/HRODEV/project7_8/dbActions"
	"github.com/HRODEV/project7_8/models"
	"github.com/gorilla/mux"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/sqlite"
)

func UserAuthGet(w http.ResponseWriter, r *http.Request, db *gorm.DB) {
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("Not implemented yet"))
}

func UserGet(w http.ResponseWriter, r *http.Request, db *gorm.DB) {
	vars := mux.Vars(r)
	id, _ := strconv.ParseUint(vars["id"], 10, 32)

	var user models.User
	dbActions.GetUserByID(uint(id), &user, db)

	js, err := json.Marshal(user)

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Write(js)
}

//
func UserPost(w http.ResponseWriter, r *http.Request, db *gorm.DB) {
	// Convert request body to interface
	user := &models.User{}
	reqBody, _ := ioutil.ReadAll(r.Body)
	err := json.Unmarshal(reqBody, user)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	dbActions.CreateUser(user, db)
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("Not implemented yet"))
}

func UserProjectsGet(w http.ResponseWriter, r *http.Request, db *gorm.DB) {
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("Not implemented yet"))
}
<|endoftext|>


<commit_before>package api

import (
	"path/filepath"
	"strings"
)

// customizationPasses Executes customization logic for the API by package name.
func (a *API) customizationPasses() {
	var svcCustomizations = map[string]func(*API){
		"s3":              s3Customizations,
		"cloudfront":      cloudfrontCustomizations,
		"dynamodbstreams": dynamodbstreamsCustomizations,
	}

	if fn := svcCustomizations[a.PackageName()]; fn != nil {
		fn(a)
	}
}

// s3Customizations customizes the API generation to replace values specific to S3.
func s3Customizations(a *API) {
	var strExpires *Shape

	for name, s := range a.Shapes {
		// Remove ContentMD5 members
		if _, ok := s.MemberRefs["ContentMD5"]; ok {
			delete(s.MemberRefs, "ContentMD5")
		}

		// Expires should be a string not time.Time since the format is not
		// enforced by S3, and any value can be set to this field outside of the SDK.
		if strings.HasSuffix(name, "Output") {
			if ref, ok := s.MemberRefs["Expires"]; ok {
				if strExpires == nil {
					newShape := *ref.Shape
					strExpires = &newShape
					strExpires.Type = "string"
					strExpires.refs = []*ShapeRef{}
				}
				ref.Shape.removeRef(ref)
				ref.Shape = strExpires
				ref.Shape.refs = append(ref.Shape.refs, &s.MemberRef)
			}
		}
	}
}

// cloudfrontCustomizations customized the API generation to replace values
// specific to CloudFront.
func cloudfrontCustomizations(a *API) {
	// MaxItems members should always be integers
	for _, s := range a.Shapes {
		if ref, ok := s.MemberRefs["MaxItems"]; ok {
			ref.ShapeName = "Integer"
			ref.Shape = a.Shapes["Integer"]
		}
	}
}

// dynamodbstreamsCustomizations references any duplicate shapes from DynamoDB
func dynamodbstreamsCustomizations(a *API) {
	p := strings.Replace(a.path, "streams.dynamodb", "dynamodb", -1)
	file := filepath.Join(p, "api-2.json")

	dbAPI := API{}
	dbAPI.Attach(file)
	dbAPI.Setup()

	for n := range a.Shapes {
		if _, ok := dbAPI.Shapes[n]; ok {
			a.Shapes[n].resolvePkg = "github.com/aws/aws-sdk-go/service/dynamodb"
		}
	}
}
<commit_msg>private/model/api: Add custom doc blob doc string<commit_after>package api

import (
	"fmt"
	"path/filepath"
	"strings"
)

// customizationPasses Executes customization logic for the API by package name.
func (a *API) customizationPasses() {
	var svcCustomizations = map[string]func(*API){
		"s3":              s3Customizations,
		"cloudfront":      cloudfrontCustomizations,
		"dynamodbstreams": dynamodbstreamsCustomizations,
	}

	if fn := svcCustomizations[a.PackageName()]; fn != nil {
		fn(a)
	}

	blobDocStringCustomizations(a)
}

const base64MarshalDocStr = "// %s is automatically base64 encoded/decoded by the SDK.\n"

func blobDocStringCustomizations(a *API) {
	for _, s := range a.Shapes {
		payloadMemberName := s.Payload

		for refName, ref := range s.MemberRefs {
			if refName == payloadMemberName {
				// Payload members have their own encoding and may
				// be raw bytes or io.Reader
				continue
			}
			if ref.Shape.Type == "blob" {
				docStr := fmt.Sprintf(base64MarshalDocStr, refName)
				if len(strings.TrimSpace(ref.Shape.Documentation)) != 0 {
					ref.Shape.Documentation += "//\n" + docStr
				} else if len(strings.TrimSpace(ref.Documentation)) != 0 {
					ref.Documentation += "//\n" + docStr
				} else {
					ref.Documentation = docStr
				}
			}
		}
	}
}

// s3Customizations customizes the API generation to replace values specific to S3.
func s3Customizations(a *API) {
	var strExpires *Shape

	for name, s := range a.Shapes {
		// Remove ContentMD5 members
		if _, ok := s.MemberRefs["ContentMD5"]; ok {
			delete(s.MemberRefs, "ContentMD5")
		}

		// Expires should be a string not time.Time since the format is not
		// enforced by S3, and any value can be set to this field outside of the SDK.
		if strings.HasSuffix(name, "Output") {
			if ref, ok := s.MemberRefs["Expires"]; ok {
				if strExpires == nil {
					newShape := *ref.Shape
					strExpires = &newShape
					strExpires.Type = "string"
					strExpires.refs = []*ShapeRef{}
				}
				ref.Shape.removeRef(ref)
				ref.Shape = strExpires
				ref.Shape.refs = append(ref.Shape.refs, &s.MemberRef)
			}
		}
	}
}

// cloudfrontCustomizations customized the API generation to replace values
// specific to CloudFront.
func cloudfrontCustomizations(a *API) {
	// MaxItems members should always be integers
	for _, s := range a.Shapes {
		if ref, ok := s.MemberRefs["MaxItems"]; ok {
			ref.ShapeName = "Integer"
			ref.Shape = a.Shapes["Integer"]
		}
	}
}

// dynamodbstreamsCustomizations references any duplicate shapes from DynamoDB
func dynamodbstreamsCustomizations(a *API) {
	p := strings.Replace(a.path, "streams.dynamodb", "dynamodb", -1)
	file := filepath.Join(p, "api-2.json")

	dbAPI := API{}
	dbAPI.Attach(file)
	dbAPI.Setup()

	for n := range a.Shapes {
		if _, ok := dbAPI.Shapes[n]; ok {
			a.Shapes[n].resolvePkg = "github.com/aws/aws-sdk-go/service/dynamodb"
		}
	}
}
<|endoftext|>


<commit_before>package testutil

import (
	"bytes"
	"os"
	"path/filepath"
	"time"

	"veyron.io/tools/lib/collect"
	"veyron.io/tools/lib/runutil"
	"veyron.io/tools/lib/util"
)

const (
	defaultJSTestTimeout = 10 * time.Minute
)

// runJSTest is a harness for executing javascript tests.
func runJSTest(ctx *util.Context, testName, testDir, target string, cleanFn func() error, env map[string]string) (_ *TestResult, e error) {
	// Initialize the test.
	cleanup, err := initTest(ctx, testName, []string{"web"})
	if err != nil {
		return nil, err
	}
	defer collect.Error(func() error { return cleanup() }, &e)

	// Navigate to the target directory.
	if err := ctx.Run().Chdir(testDir); err != nil {
		return nil, err
	}

	// Clean up after previous instances of the test.
	opts := ctx.Run().Opts()
	for key, value := range env {
		opts.Env[key] = value
	}
	if err := ctx.Run().CommandWithOpts(opts, "make", "clean"); err != nil {
		return nil, err
	}
	if cleanFn != nil {
		if err := cleanFn(); err != nil {
			return nil, err
		}
	}

	// Run the test target.
	var stderr bytes.Buffer
	opts.Stderr = &stderr
	if err := ctx.Run().TimedCommandWithOpts(defaultJSTestTimeout, opts, "make", target); err != nil {
		if err == runutil.CommandTimedOutErr {
			return &TestResult{
				Status:       TestTimedOut,
				TimeoutValue: defaultJSTestTimeout,
			}, nil
		}
		// Generate an xunit report if none exists.
		// This can happen when errors are not in javascript tests themselves.
		xunitFilePath := XUnitReportPath(testName)
		if _, err := os.Stat(xunitFilePath); err != nil {
			if !os.IsNotExist(err) {
				return nil, err
			}
			s := createTestSuiteWithFailure(testName, "Test", "failure", stderr.String(), 0)
			if err := createXUnitReport(ctx, testName, []testSuite{*s}); err != nil {
				return nil, err
			}
		}
		return &TestResult{Status: TestFailed}, nil
	}

	return &TestResult{Status: TestPassed}, nil
}

// veyronJSBuildExtension tests the veyron javascript build extension.
func veyronJSBuildExtension(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VeyronRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "veyron.js")
	target := "extension/veyron.crx"
	return runJSTest(ctx, testName, testDir, target, nil, nil)
}

// veyronJSDoc (re)generates the content of the veyron javascript
// documentation server.
func veyronJSDoc(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VeyronRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "veyron.js")
	target := "docs"
	webDir, jsDocDir := "/usr/share/nginx/www/jsdoc", filepath.Join(testDir, "docs")
	cleanFn := func() error {
		if err := ctx.Run().RemoveAll(webDir); err != nil {
			return err
		}
		return nil
	}
	result, err := runJSTest(ctx, testName, testDir, target, cleanFn, nil)
	if err != nil {
		return nil, err
	}
	// Move generated js documentation to the web server directory.
	if err := ctx.Run().Rename(jsDocDir, webDir); err != nil {
		return nil, err
	}
	return result, nil
}

// veyronJSBrowserIntegration runs the veyron javascript integration test in a browser environment using nacl plugin.
func veyronJSBrowserIntegration(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VeyronRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "veyron.js")
	target := "test-integration-browser"
	env := map[string]string{}
	setCommonJSEnv(env)
	env["BROWSER_OUTPUT"] = XUnitReportPath(testName)
	return runJSTest(ctx, testName, testDir, target, nil, env)
}

// veyronJSNodeIntegration runs the veyron javascript integration test in NodeJS environment using wspr.
func veyronJSNodeIntegration(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VeyronRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "veyron.js")
	target := "test-integration-node"
	env := map[string]string{}
	setCommonJSEnv(env)
	env["NODE_OUTPUT"] = XUnitReportPath(testName)
	return runJSTest(ctx, testName, testDir, target, nil, env)
}

// veyronJSUnit runs the veyron javascript unit test.
func veyronJSUnit(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VeyronRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "veyron.js")
	target := "test-unit"
	env := map[string]string{}
	setCommonJSEnv(env)
	env["NODE_OUTPUT"] = XUnitReportPath(testName)
	return runJSTest(ctx, testName, testDir, target, nil, env)
}

// veyronJSVdl runs the veyron javascript vdl test.
func veyronJSVdl(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VeyronRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "veyron.js")
	target := "test-vdl"
	env := map[string]string{}
	setCommonJSEnv(env)
	env["NODE_OUTPUT"] = XUnitReportPath(testName)
	return runJSTest(ctx, testName, testDir, target, nil, env)
}

// veyronJSVom runs the veyron javascript vom test.
func veyronJSVom(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VeyronRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "veyron", "javascript", "vom")
	target := "test"
	env := map[string]string{}
	setCommonJSEnv(env)
	env["NODE_OUTPUT"] = XUnitReportPath(testName)
	return runJSTest(ctx, testName, testDir, target, nil, env)
}

func setCommonJSEnv(env map[string]string) {
	env["XUNIT"] = "true"
	env["NOVDLGEN"] = "true"
}
<commit_msg>TBR: tools/lib/testutil: fix error output.<commit_after>package testutil

import (
	"bytes"
	"fmt"
	"os"
	"path/filepath"
	"time"

	"veyron.io/tools/lib/collect"
	"veyron.io/tools/lib/runutil"
	"veyron.io/tools/lib/util"
)

const (
	defaultJSTestTimeout = 10 * time.Minute
)

// runJSTest is a harness for executing javascript tests.
func runJSTest(ctx *util.Context, testName, testDir, target string, cleanFn func() error, env map[string]string) (_ *TestResult, e error) {
	// Initialize the test.
	cleanup, err := initTest(ctx, testName, []string{"web"})
	if err != nil {
		return nil, err
	}
	defer collect.Error(func() error { return cleanup() }, &e)

	// Navigate to the target directory.
	if err := ctx.Run().Chdir(testDir); err != nil {
		return nil, err
	}

	// Clean up after previous instances of the test.
	opts := ctx.Run().Opts()
	for key, value := range env {
		opts.Env[key] = value
	}
	if err := ctx.Run().CommandWithOpts(opts, "make", "clean"); err != nil {
		return nil, err
	}
	if cleanFn != nil {
		if err := cleanFn(); err != nil {
			return nil, err
		}
	}

	// Run the test target.
	var stderr bytes.Buffer
	opts = ctx.Run().Opts()
	opts.Stderr = &stderr
	if err := ctx.Run().TimedCommandWithOpts(defaultJSTestTimeout, opts, "make", target); err != nil {
		fmt.Fprintf(ctx.Stderr(), "Stderr:\n%s\n", stderr.String())
		if err == runutil.CommandTimedOutErr {
			return &TestResult{
				Status:       TestTimedOut,
				TimeoutValue: defaultJSTestTimeout,
			}, nil
		}
		// Generate an xunit report if none exists.
		// This can happen when errors are not in javascript tests themselves.
		xunitFilePath := XUnitReportPath(testName)
		if _, err := os.Stat(xunitFilePath); err != nil {
			if !os.IsNotExist(err) {
				return nil, err
			}
			s := createTestSuiteWithFailure(testName, "Test", "failure", stderr.String(), 0)
			if err := createXUnitReport(ctx, testName, []testSuite{*s}); err != nil {
				return nil, err
			}
		}
		return &TestResult{Status: TestFailed}, nil
	}

	return &TestResult{Status: TestPassed}, nil
}

// veyronJSBuildExtension tests the veyron javascript build extension.
func veyronJSBuildExtension(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VeyronRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "veyron.js")
	target := "extension/veyron.crx"
	return runJSTest(ctx, testName, testDir, target, nil, nil)
}

// veyronJSDoc (re)generates the content of the veyron javascript
// documentation server.
func veyronJSDoc(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VeyronRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "veyron.js")
	target := "docs"
	webDir, jsDocDir := "/usr/share/nginx/www/jsdoc", filepath.Join(testDir, "docs")
	cleanFn := func() error {
		if err := ctx.Run().RemoveAll(webDir); err != nil {
			return err
		}
		return nil
	}
	result, err := runJSTest(ctx, testName, testDir, target, cleanFn, nil)
	if err != nil {
		return nil, err
	}
	// Move generated js documentation to the web server directory.
	if err := ctx.Run().Rename(jsDocDir, webDir); err != nil {
		return nil, err
	}
	return result, nil
}

// veyronJSBrowserIntegration runs the veyron javascript integration test in a browser environment using nacl plugin.
func veyronJSBrowserIntegration(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VeyronRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "veyron.js")
	target := "test-integration-browser"
	env := map[string]string{}
	setCommonJSEnv(env)
	env["BROWSER_OUTPUT"] = XUnitReportPath(testName)
	return runJSTest(ctx, testName, testDir, target, nil, env)
}

// veyronJSNodeIntegration runs the veyron javascript integration test in NodeJS environment using wspr.
func veyronJSNodeIntegration(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VeyronRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "veyron.js")
	target := "test-integration-node"
	env := map[string]string{}
	setCommonJSEnv(env)
	env["NODE_OUTPUT"] = XUnitReportPath(testName)
	return runJSTest(ctx, testName, testDir, target, nil, env)
}

// veyronJSUnit runs the veyron javascript unit test.
func veyronJSUnit(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VeyronRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "veyron.js")
	target := "test-unit"
	env := map[string]string{}
	setCommonJSEnv(env)
	env["NODE_OUTPUT"] = XUnitReportPath(testName)
	return runJSTest(ctx, testName, testDir, target, nil, env)
}

// veyronJSVdl runs the veyron javascript vdl test.
func veyronJSVdl(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VeyronRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "veyron.js")
	target := "test-vdl"
	env := map[string]string{}
	setCommonJSEnv(env)
	env["NODE_OUTPUT"] = XUnitReportPath(testName)
	return runJSTest(ctx, testName, testDir, target, nil, env)
}

// veyronJSVom runs the veyron javascript vom test.
func veyronJSVom(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VeyronRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "veyron", "javascript", "vom")
	target := "test"
	env := map[string]string{}
	setCommonJSEnv(env)
	env["NODE_OUTPUT"] = XUnitReportPath(testName)
	return runJSTest(ctx, testName, testDir, target, nil, env)
}

func setCommonJSEnv(env map[string]string) {
	env["XUNIT"] = "true"
	env["NOVDLGEN"] = "true"
}
<|endoftext|>


<commit_before>package testutil

import (
	"path/filepath"

	"veyron.io/tools/lib/envutil"
	"veyron.io/tools/lib/runutil"
	"veyron.io/tools/lib/util"
)

// runJSTest is a harness for executing javascript tests.
func runJSTest(ctx *util.Context, testName, testDir, target string, cleanFn func() error, env map[string]string) (*TestResult, error) {
	// Initialize the test.
	cleanup, err := initTest(ctx, testName, []string{"web"})
	if err != nil {
		return nil, err
	}
	defer cleanup()

	// Navigate to the target directory.
	if err := ctx.Run().Function(runutil.Chdir(testDir)); err != nil {
		return nil, err
	}

	// Clean up after previous instances of the test.
	if err := ctx.Run().Command("make", "clean"); err != nil {
		return nil, err
	}
	if cleanFn != nil {
		if err := cleanFn(); err != nil {
			return nil, err
		}
	}

	// Run the test target.
	opts := ctx.Run().Opts()
	osEnv := envutil.NewSnapshotFromOS()
	for key, value := range env {
		osEnv.Set(key, value)
	}
	opts.Env = osEnv.Map()
	if err := ctx.Run().CommandWithOpts(opts, "make", target); err != nil {
		return &TestResult{Status: TestFailed}, nil
	}

	return &TestResult{Status: TestPassed}, nil
}

// VeyronJSBuildExtension tests the veyron javascript build extension.
func VeyronJSBuildExtension(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VeyronRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "veyron.js")
	target := "nacl/out"
	cleanFn := func() error {
		naclDir := filepath.Join(root, "go.nacl", "bin", "go")
		if err := ctx.Run().Function(runutil.RemoveAll(naclDir)); err != nil {
			return err
		}
		return nil
	}
	env := map[string]string{}
	env["NACLGOROOT"] = filepath.Join(root, "go.nacl")
	return runJSTest(ctx, testName, testDir, target, cleanFn, env)
}

// VeyronJSBuildNaClCompiler runs the veyron NaCl compiler build.
func VeyronJSBuildNaClCompiler(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VeyronRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "veyron.js")
	target := filepath.Join(root, "go.nacl", "bin", "go")
	cleanFn := func() error {
		if err := ctx.Run().Function(runutil.RemoveAll(target)); err != nil {
			return err
		}
		return nil
	}
	env := map[string]string{}
	env["NACLGOROOT"] = filepath.Join(root, "go.nacl")
	return runJSTest(ctx, testName, testDir, target, cleanFn, env)
}

// VeyronJSDoc (re)generates the content of the veyron javascript
// documentation server.
func VeyronJSDoc(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VeyronRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "veyron.js")
	target := "docs"
	webDir, jsDocDir := "/var/www/jsdoc", filepath.Join(testDir, "docs")
	cleanFn := func() error {
		if err := ctx.Run().Function(runutil.RemoveAll(webDir)); err != nil {
			return err
		}
		return nil
	}
	result, err := runJSTest(ctx, testName, testDir, target, cleanFn, nil)
	if err != nil {
		return nil, err
	}
	if err := ctx.Run().Function(runutil.Rename(jsDocDir, webDir)); err != nil {
		return nil, err
	}
	return result, nil
}

// VeyronJSIntegrationTest runs the veyron javascript integration test.
func VeyronJSIntegrationTest(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VeyronRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "veyron.js")
	target := "test-integration"
	env := map[string]string{}
	env["TAP"] = "true"
	env["NODE_OUTPUT"] = "integration_test.tap"
	return runJSTest(ctx, testName, testDir, target, nil, env)
}

// VeyronJSUnitTest runs the veyron javascript unit test.
func VeyronJSUnitTest(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VeyronRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "veyron.js")
	target := "test-unit"
	env := map[string]string{}
	env["TAP"] = "true"
	env["NODE_OUTPUT"] = "unit_test.tap"
	return runJSTest(ctx, testName, testDir, target, nil, env)
}

// VeyronJSVomTest runs the veyron javascript vom test.
func VeyronJSVomTest(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VeyronRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "veyron", "javascript", "vom")
	target := "test"
	return runJSTest(ctx, testName, testDir, target, nil, nil)
}
<commit_msg>tools: replacing "os.Rename" with "mv" to account for cross-partition moves in "veyron test run veyron-javascript-doc"<commit_after>package testutil

import (
	"path/filepath"

	"veyron.io/tools/lib/envutil"
	"veyron.io/tools/lib/runutil"
	"veyron.io/tools/lib/util"
)

// runJSTest is a harness for executing javascript tests.
func runJSTest(ctx *util.Context, testName, testDir, target string, cleanFn func() error, env map[string]string) (*TestResult, error) {
	// Initialize the test.
	cleanup, err := initTest(ctx, testName, []string{"web"})
	if err != nil {
		return nil, err
	}
	defer cleanup()

	// Navigate to the target directory.
	if err := ctx.Run().Function(runutil.Chdir(testDir)); err != nil {
		return nil, err
	}

	// Clean up after previous instances of the test.
	if err := ctx.Run().Command("make", "clean"); err != nil {
		return nil, err
	}
	if cleanFn != nil {
		if err := cleanFn(); err != nil {
			return nil, err
		}
	}

	// Run the test target.
	opts := ctx.Run().Opts()
	osEnv := envutil.NewSnapshotFromOS()
	for key, value := range env {
		osEnv.Set(key, value)
	}
	opts.Env = osEnv.Map()
	if err := ctx.Run().CommandWithOpts(opts, "make", target); err != nil {
		return &TestResult{Status: TestFailed}, nil
	}

	return &TestResult{Status: TestPassed}, nil
}

// VeyronJSBuildExtension tests the veyron javascript build extension.
func VeyronJSBuildExtension(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VeyronRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "veyron.js")
	target := "nacl/out"
	cleanFn := func() error {
		naclDir := filepath.Join(root, "go.nacl", "bin", "go")
		if err := ctx.Run().Function(runutil.RemoveAll(naclDir)); err != nil {
			return err
		}
		return nil
	}
	env := map[string]string{}
	env["NACLGOROOT"] = filepath.Join(root, "go.nacl")
	return runJSTest(ctx, testName, testDir, target, cleanFn, env)
}

// VeyronJSBuildNaClCompiler runs the veyron NaCl compiler build.
func VeyronJSBuildNaClCompiler(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VeyronRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "veyron.js")
	target := filepath.Join(root, "go.nacl", "bin", "go")
	cleanFn := func() error {
		if err := ctx.Run().Function(runutil.RemoveAll(target)); err != nil {
			return err
		}
		return nil
	}
	env := map[string]string{}
	env["NACLGOROOT"] = filepath.Join(root, "go.nacl")
	return runJSTest(ctx, testName, testDir, target, cleanFn, env)
}

// VeyronJSDoc (re)generates the content of the veyron javascript
// documentation server.
func VeyronJSDoc(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VeyronRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "veyron.js")
	target := "docs"
	webDir, jsDocDir := "/var/www/jsdoc", filepath.Join(testDir, "docs")
	cleanFn := func() error {
		if err := ctx.Run().Function(runutil.RemoveAll(webDir)); err != nil {
			return err
		}
		return nil
	}
	result, err := runJSTest(ctx, testName, testDir, target, cleanFn, nil)
	if err != nil {
		return nil, err
	}
	// Move generated js documentation to the web server directory
	// using "mv" instead of "os.Rename()" to account for the fact
	// the the source and the destination may be on different
	// partitions.
	if err := ctx.Run().Command("mv", jsDocDir, webDir); err != nil {
		return nil, err
	}
	return result, nil
}

// VeyronJSIntegrationTest runs the veyron javascript integration test.
func VeyronJSIntegrationTest(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VeyronRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "veyron.js")
	target := "test-integration"
	env := map[string]string{}
	env["TAP"] = "true"
	env["NODE_OUTPUT"] = "integration_test.tap"
	return runJSTest(ctx, testName, testDir, target, nil, env)
}

// VeyronJSUnitTest runs the veyron javascript unit test.
func VeyronJSUnitTest(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VeyronRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "veyron.js")
	target := "test-unit"
	env := map[string]string{}
	env["TAP"] = "true"
	env["NODE_OUTPUT"] = "unit_test.tap"
	return runJSTest(ctx, testName, testDir, target, nil, env)
}

// VeyronJSVomTest runs the veyron javascript vom test.
func VeyronJSVomTest(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VeyronRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "veyron", "javascript", "vom")
	target := "test"
	return runJSTest(ctx, testName, testDir, target, nil, nil)
}
<|endoftext|>


<commit_before>package checkcloudwatchlogs

import (
	"crypto/md5"
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
	"github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface"
	"github.com/jessevdk/go-flags"

	"github.com/mackerelio/checkers"
	"github.com/mackerelio/golib/pluginutil"
)

type logOpts struct {
	Region          string `long:"region" value-name:"REGION" description:"AWS Region"`
	AccessKeyID     string `long:"access-key-id" value-name:"ACCESS-KEY-ID" description:"AWS Access Key ID"`
	SecretAccessKey string `long:"secret-access-key" value-name:"SECRET-ACCESS-KEY" description:"AWS Secret Access Key"`
	LogGroupName    string `long:"log-group-name" required:"true" value-name:"LOG-GROUP-NAME" description:"Log group name"`

	Pattern       string `short:"p" long:"pattern" required:"true" value-name:"PATTERN" description:"Pattern to search for. The value is recognized as the pattern syntax of CloudWatch Logs."`
	WarningOver   int    `short:"w" long:"warning-over" value-name:"WARNING" description:"Trigger a warning if matched lines is over a number"`
	CriticalOver  int    `short:"c" long:"critical-over" value-name:"CRITICAL" description:"Trigger a critical if matched lines is over a number"`
	StateDir      string `short:"s" long:"state-dir" value-name:"DIR" description:"Dir to keep state files under"`
	ReturnContent bool   `short:"r" long:"return" description:"Output matched lines"`
}

// Do the plugin
func Do() {
	ckr := run(os.Args[1:])
	ckr.Name = "CloudWatch Logs"
	ckr.Exit()
}

type cloudwatchLogsPlugin struct {
	Service       cloudwatchlogsiface.CloudWatchLogsAPI
	LogGroupName  string
	Pattern       string
	WarningOver   int
	CriticalOver  int
	StateFile     string
	ReturnContent bool
}

func newCloudwatchLogsPlugin(args []string) (*cloudwatchLogsPlugin, error) {
	opts := &logOpts{}
	_, err := flags.ParseArgs(opts, args)
	if err != nil {
		os.Exit(1)
	}
	service, err := createService(opts)
	if err != nil {
		return nil, err
	}
	if opts.StateDir == "" {
		workdir := pluginutil.PluginWorkDir()
		opts.StateDir = filepath.Join(workdir, "check-cloudwatch-logs")
	}
	return &cloudwatchLogsPlugin{
		Service:       service,
		LogGroupName:  opts.LogGroupName,
		Pattern:       opts.Pattern,
		WarningOver:   opts.WarningOver,
		CriticalOver:  opts.CriticalOver,
		StateFile:     getStateFile(opts.StateDir, opts.LogGroupName, args),
		ReturnContent: opts.ReturnContent,
	}, nil
}

var stateRe = regexp.MustCompile(`[^-a-zA-Z0-9_.]`)

func getStateFile(stateDir, logGroupName string, args []string) string {
	return filepath.Join(
		stateDir,
		fmt.Sprintf(
			"%s-%x",
			strings.TrimLeft(stateRe.ReplaceAllString(logGroupName, "_"), "_"),
			md5.Sum([]byte(os.Getenv("AWS_PROFILE")+" "+strings.Join(args, " "))),
		),
	)
}

func createService(opts *logOpts) (*cloudwatchlogs.CloudWatchLogs, error) {
	sess, err := session.NewSession()
	if err != nil {
		return nil, err
	}
	config := aws.NewConfig()
	if opts.AccessKeyID != "" && opts.SecretAccessKey != "" {
		config = config.WithCredentials(
			credentials.NewStaticCredentials(opts.AccessKeyID, opts.SecretAccessKey, ""),
		)
	}
	if opts.Region != "" {
		config = config.WithRegion(opts.Region)
	}
	return cloudwatchlogs.New(sess, config), nil
}

type logState struct {
	NextToken *string
	StartTime *int64
}

func (p *cloudwatchLogsPlugin) run() ([]string, error) {
	var nextToken *string
	var startTime *int64
	s, err := p.loadState()
	if err != nil {
		if !os.IsNotExist(err) {
			return nil, err
		}
	} else {
		if s.StartTime != nil && *s.StartTime > time.Now().Add(-time.Hour).Unix()*1000 {
			nextToken = s.NextToken
			startTime = s.StartTime
		}
	}
	if startTime == nil {
		startTime = aws.Int64(time.Now().Add(-1*time.Minute).Unix() * 1000)
	}
	var messages []string
	for {
		output, err := p.Service.FilterLogEvents(&cloudwatchlogs.FilterLogEventsInput{
			StartTime:     startTime,
			LogGroupName:  aws.String(p.LogGroupName),
			NextToken:     nextToken,
			FilterPattern: aws.String(p.Pattern),
		})
		if err != nil {
			return nil, err
		}
		for _, event := range output.Events {
			messages = append(messages, *event.Message)
			startTime = aws.Int64(*event.Timestamp + 1)
		}
		if output.NextToken == nil {
			break
		}
		nextToken = output.NextToken
		time.Sleep(250 * time.Millisecond)
	}
	if nextToken != nil {
		err := p.saveState(&logState{nextToken, startTime})
		if err != nil {
			return nil, err
		}
	}
	return messages, nil
}

func (p *cloudwatchLogsPlugin) loadState() (*logState, error) {
	f, err := os.Open(p.StateFile)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	var s logState
	err = json.NewDecoder(f).Decode(&s)
	if err != nil {
		return nil, err
	}
	return &s, nil
}

func (p *cloudwatchLogsPlugin) saveState(s *logState) error {
	err := os.MkdirAll(filepath.Dir(p.StateFile), 0755)
	if err != nil {
		return err
	}
	f, err := os.Create(p.StateFile)
	if err != nil {
		return err
	}
	defer f.Close()
	return json.NewEncoder(f).Encode(s)
}

func run(args []string) *checkers.Checker {
	p, err := newCloudwatchLogsPlugin(args)
	if err != nil {
		return checkers.NewChecker(checkers.UNKNOWN, fmt.Sprint(err))
	}
	messages, err := p.run()
	if err != nil {
		return checkers.NewChecker(checkers.UNKNOWN, fmt.Sprint(err))
	}
	status := checkers.OK
	msg := fmt.Sprint(len(messages))
	if len(messages) > p.CriticalOver {
		status = checkers.CRITICAL
		msg += " > " + fmt.Sprint(p.CriticalOver) + " messages"
	} else if len(messages) > p.WarningOver {
		status = checkers.WARNING
		msg += " > " + fmt.Sprint(p.WarningOver) + " messages"
	} else {
		msg += " messages"
	}
	msg += " for pattern /" + p.Pattern + "/"
	if messages != nil {
		if p.ReturnContent {
			msg += "\n" + strings.Join(messages, "")
		}
		return checkers.NewChecker(status, msg)
	}
	return checkers.NewChecker(checkers.OK, msg)
}
<commit_msg>refactoring<commit_after>package checkcloudwatchlogs

import (
	"crypto/md5"
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
	"github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface"
	"github.com/jessevdk/go-flags"

	"github.com/mackerelio/checkers"
	"github.com/mackerelio/golib/pluginutil"
)

type logOpts struct {
	Region          string `long:"region" value-name:"REGION" description:"AWS Region"`
	AccessKeyID     string `long:"access-key-id" value-name:"ACCESS-KEY-ID" description:"AWS Access Key ID"`
	SecretAccessKey string `long:"secret-access-key" value-name:"SECRET-ACCESS-KEY" description:"AWS Secret Access Key"`
	LogGroupName    string `long:"log-group-name" required:"true" value-name:"LOG-GROUP-NAME" description:"Log group name"`

	Pattern       string `short:"p" long:"pattern" required:"true" value-name:"PATTERN" description:"Pattern to search for. The value is recognized as the pattern syntax of CloudWatch Logs."`
	WarningOver   int    `short:"w" long:"warning-over" value-name:"WARNING" description:"Trigger a warning if matched lines is over a number"`
	CriticalOver  int    `short:"c" long:"critical-over" value-name:"CRITICAL" description:"Trigger a critical if matched lines is over a number"`
	StateDir      string `short:"s" long:"state-dir" value-name:"DIR" description:"Dir to keep state files under"`
	ReturnContent bool   `short:"r" long:"return" description:"Output matched lines"`
}

// Do the plugin
func Do() {
	ckr := run(os.Args[1:])
	ckr.Name = "CloudWatch Logs"
	ckr.Exit()
}

type cloudwatchLogsPlugin struct {
	Service       cloudwatchlogsiface.CloudWatchLogsAPI
	LogGroupName  string
	Pattern       string
	WarningOver   int
	CriticalOver  int
	StateFile     string
	ReturnContent bool
}

func newCloudwatchLogsPlugin(args []string) (*cloudwatchLogsPlugin, error) {
	opts := &logOpts{}
	_, err := flags.ParseArgs(opts, args)
	if err != nil {
		os.Exit(1)
	}
	service, err := createService(opts)
	if err != nil {
		return nil, err
	}
	if opts.StateDir == "" {
		workdir := pluginutil.PluginWorkDir()
		opts.StateDir = filepath.Join(workdir, "check-cloudwatch-logs")
	}
	return &cloudwatchLogsPlugin{
		Service:       service,
		LogGroupName:  opts.LogGroupName,
		Pattern:       opts.Pattern,
		WarningOver:   opts.WarningOver,
		CriticalOver:  opts.CriticalOver,
		StateFile:     getStateFile(opts.StateDir, opts.LogGroupName, args),
		ReturnContent: opts.ReturnContent,
	}, nil
}

var stateRe = regexp.MustCompile(`[^-a-zA-Z0-9_.]`)

func getStateFile(stateDir, logGroupName string, args []string) string {
	return filepath.Join(
		stateDir,
		fmt.Sprintf(
			"%s-%x",
			strings.TrimLeft(stateRe.ReplaceAllString(logGroupName, "_"), "_"),
			md5.Sum([]byte(os.Getenv("AWS_PROFILE")+" "+strings.Join(args, " "))),
		),
	)
}

func createService(opts *logOpts) (*cloudwatchlogs.CloudWatchLogs, error) {
	sess, err := session.NewSession()
	if err != nil {
		return nil, err
	}
	config := aws.NewConfig()
	if opts.AccessKeyID != "" && opts.SecretAccessKey != "" {
		config = config.WithCredentials(
			credentials.NewStaticCredentials(opts.AccessKeyID, opts.SecretAccessKey, ""),
		)
	}
	if opts.Region != "" {
		config = config.WithRegion(opts.Region)
	}
	return cloudwatchlogs.New(sess, config), nil
}

type logState struct {
	NextToken *string
	StartTime *int64
}

func (p *cloudwatchLogsPlugin) run() ([]string, error) {
	var nextToken *string
	var startTime *int64
	s, err := p.loadState()
	if err != nil {
		if !os.IsNotExist(err) {
			return nil, err
		}
	} else {
		if s.StartTime != nil && *s.StartTime > time.Now().Add(-time.Hour).Unix()*1000 {
			nextToken = s.NextToken
			startTime = s.StartTime
		}
	}
	if startTime == nil {
		startTime = aws.Int64(time.Now().Add(-1*time.Minute).Unix() * 1000)
	}
	var messages []string
	for {
		output, err := p.Service.FilterLogEvents(&cloudwatchlogs.FilterLogEventsInput{
			StartTime:     startTime,
			LogGroupName:  aws.String(p.LogGroupName),
			NextToken:     nextToken,
			FilterPattern: aws.String(p.Pattern),
		})
		if err != nil {
			return nil, err
		}
		for _, event := range output.Events {
			messages = append(messages, *event.Message)
			startTime = aws.Int64(*event.Timestamp + 1)
		}
		if output.NextToken == nil {
			break
		}
		nextToken = output.NextToken
		time.Sleep(250 * time.Millisecond)
	}
	if nextToken != nil {
		err := p.saveState(&logState{nextToken, startTime})
		if err != nil {
			return nil, err
		}
	}
	return messages, nil
}

func (p *cloudwatchLogsPlugin) loadState() (*logState, error) {
	f, err := os.Open(p.StateFile)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	var s logState
	err = json.NewDecoder(f).Decode(&s)
	if err != nil {
		return nil, err
	}
	return &s, nil
}

func (p *cloudwatchLogsPlugin) saveState(s *logState) error {
	err := os.MkdirAll(filepath.Dir(p.StateFile), 0755)
	if err != nil {
		return err
	}
	f, err := os.Create(p.StateFile)
	if err != nil {
		return err
	}
	defer f.Close()
	return json.NewEncoder(f).Encode(s)
}

func (p *cloudwatchLogsPlugin) check(messages []string) *checkers.Checker {
	status := checkers.OK
	msg := fmt.Sprint(len(messages))
	if len(messages) > p.CriticalOver {
		status = checkers.CRITICAL
		msg += " > " + fmt.Sprint(p.CriticalOver)
	} else if len(messages) > p.WarningOver {
		status = checkers.WARNING
		msg += " > " + fmt.Sprint(p.WarningOver)
	}
	msg += " messages for pattern /" + p.Pattern + "/"
	if messages != nil {
		if p.ReturnContent {
			msg += "\n" + strings.Join(messages, "")
		}
		return checkers.NewChecker(status, msg)
	}
	return checkers.NewChecker(checkers.OK, msg)
}

func run(args []string) *checkers.Checker {
	p, err := newCloudwatchLogsPlugin(args)
	if err != nil {
		return checkers.NewChecker(checkers.UNKNOWN, fmt.Sprint(err))
	}
	messages, err := p.run()
	if err != nil {
		return checkers.NewChecker(checkers.UNKNOWN, fmt.Sprint(err))
	}
	return p.check(messages)
}
<|endoftext|>


<commit_before>package http_opentracing

import (
	"log"
	"strings"

	http_ctxtags "github.com/improbable-eng/go-httpwares/tags"
	opentracing "github.com/opentracing/opentracing-go"
)

const (
	TagTraceId           = "trace.traceid"
	TagSpanId            = "trace.spanid"
	TagSampled           = "trace.sampled"
	JaegerNotSampledFlag = "0"
)

// injectOpentracingIdsToTags writes the given context to the ctxtags.
// This is done in an incredibly hacky way, because the public-facing interface of opentracing doesn't give access to
// the TraceId and SpanId of the SpanContext. Only the Tracer's Inject/Extract methods know what these are.
// Most tracers have them encoded as keys with 'traceid' and 'spanid':
// https://github.com/openzipkin/zipkin-go-opentracing/blob/594640b9ef7e5c994e8d9499359d693c032d738c/propagation_ot.go#L29
// https://github.com/opentracing/basictracer-go/blob/1b32af207119a14b1b231d451df3ed04a72efebf/propagation_ot.go#L26
// Jaeger from Uber use one-key schema with next format '{trace-id}:{span-id}:{parent-span-id}:{flags}'
// https://www.jaegertracing.io/docs/client-libraries/#trace-span-identity
func injectOpentracingIdsToTags(span opentracing.Span, tags *http_ctxtags.Tags) {
	if err := span.Tracer().Inject(span.Context(), opentracing.HTTPHeaders, &tagsCarrier{tags}); err != nil {
		log.Printf("http_opentracing: failed extracting trace info into ctx %v", err)
	}
}

// tagsCarrier is a really hacky way of
type tagsCarrier struct {
	*http_ctxtags.Tags
}

func (t *tagsCarrier) Set(key, val string) {
	if strings.Contains(strings.ToLower(key), "traceid") {
		t.Tags.Set(TagTraceId, val) // this will most likely be base-16 (hex) encoded
	}

	if strings.Contains(strings.ToLower(key), "spanid") && !strings.Contains(strings.ToLower(key), "parent") {
		t.Tags.Set(TagSpanId, val) // this will most likely be base-16 (hex) encoded
	}

	if strings.Contains(strings.ToLower(key), "sampled") {
		t.Tags.Set(TagSampled, val)
	}

	if key == "uber-trace-id" {
		parts := strings.Split(val, ":")
		if len(parts) == 4 {
			t.Tags.Set(TagTraceId, parts[0])
			t.Tags.Set(TagSpanId, parts[1])

			if parts[3] != JaegerNotSampledFlag {
				t.Tags.Set(TagSampled, "true")
			} else {
				t.Tags.Set(TagSampled, "false")
			}
		}
	}
}
<commit_msg>update to fit with grpc (#52)<commit_after>package http_opentracing

import (
	"log"
	"strings"

	http_ctxtags "github.com/improbable-eng/go-httpwares/tags"
	opentracing "github.com/opentracing/opentracing-go"
)

const (
	TagTraceId           = "trace.traceid"
	TagSpanId            = "trace.spanid"
	TagSampled           = "trace.sampled"
	jaegerNotSampledFlag = "0"
)

// injectOpentracingIdsToTags writes the given context to the ctxtags.
// This is done in an incredibly hacky way, because the public-facing interface of opentracing doesn't give access to
// the TraceId and SpanId of the SpanContext. Only the Tracer's Inject/Extract methods know what these are.
// Most tracers have them encoded as keys with 'traceid' and 'spanid':
// https://github.com/openzipkin/zipkin-go-opentracing/blob/594640b9ef7e5c994e8d9499359d693c032d738c/propagation_ot.go#L29
// https://github.com/opentracing/basictracer-go/blob/1b32af207119a14b1b231d451df3ed04a72efebf/propagation_ot.go#L26
// Jaeger from Uber use one-key schema with next format '{trace-id}:{span-id}:{parent-span-id}:{flags}'
// https://www.jaegertracing.io/docs/client-libraries/#trace-span-identity
func injectOpentracingIdsToTags(span opentracing.Span, tags *http_ctxtags.Tags) {
	if err := span.Tracer().Inject(span.Context(), opentracing.HTTPHeaders, &tagsCarrier{tags}); err != nil {
		log.Printf("http_opentracing: failed extracting trace info into ctx %v", err)
	}
}

// tagsCarrier is a really hacky way of
type tagsCarrier struct {
	*http_ctxtags.Tags
}

func (t *tagsCarrier) Set(key, val string) {
	key = strings.ToLower(key)
	if strings.Contains(key, "traceid") {
		t.Tags.Set(TagTraceId, val) // this will most likely be base-16 (hex) encoded
	}

	if strings.Contains(key, "spanid") && !strings.Contains(strings.ToLower(key), "parent") {
		t.Tags.Set(TagSpanId, val) // this will most likely be base-16 (hex) encoded
	}

	if strings.Contains(key, "sampled") {
		t.Tags.Set(TagSampled, val)
	}

	if key == "uber-trace-id" {
		parts := strings.Split(val, ":")
		if len(parts) == 4 {
			t.Tags.Set(TagTraceId, parts[0])
			t.Tags.Set(TagSpanId, parts[1])

			if parts[3] != jaegerNotSampledFlag {
				t.Tags.Set(TagSampled, "true")
			} else {
				t.Tags.Set(TagSampled, "false")
			}
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes 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 trigger

import (
	"encoding/json"
	"testing"

	"github.com/sirupsen/logrus"
	"k8s.io/test-infra/prow/config"
	"k8s.io/test-infra/prow/github"
	"k8s.io/test-infra/prow/github/fakegithub"
	"k8s.io/test-infra/prow/plugins"
)

func TestTrusted(t *testing.T) {
	const rando = "random-person"
	const member = "org-member"
	const sister = "trusted-org-member"
	const friend = "repo-collaborator"

	const accept = "/ok-to-test"
	const chatter = "ignore random stuff"

	var testcases = []struct {
		name      string
		author    string
		comment   string
		commenter string
		onlyOrg   bool
		expected  bool
	}{
		{
			name:     "trust org member",
			author:   member,
			expected: true,
		},
		{
			name:     "trust member of other trusted org",
			author:   sister,
			expected: true,
		},
		{
			name: "reject random author",
		},
		{
			name:      "reject random author on random org member commentary",
			comment:   chatter,
			commenter: member,
		},
		{
			name:      "accept random PR after org member ok",
			comment:   accept,
			commenter: member,
			expected:  true,
		},
		{
			name:      "accept random PR after ok from trusted org member",
			comment:   accept,
			commenter: sister,
			expected:  true,
		},
		{
			name:      "ok may end with a \\r",
			comment:   accept + "\r",
			commenter: member,
			expected:  true,
		},
		{
			name:      "ok start on a middle line",
			comment:   "hello\n" + accept + "\r\nplease",
			commenter: member,
			expected:  true,
		},
		{
			name:      "require ok on start of line",
			comment:   "please, " + accept,
			commenter: member,
		},
		{
			name:      "reject acceptance from random person",
			comment:   accept,
			commenter: rando + " III",
		},
		{
			name:      "reject acceptance from this bot",
			comment:   accept,
			commenter: fakegithub.Bot,
		},
		{
			name:      "reject acceptance from random author",
			comment:   accept,
			commenter: rando,
		},
		{
			name:      "reject acceptance from repo collaborator in org-only mode",
			comment:   accept,
			commenter: friend,
			onlyOrg:   true,
		},
		{
			name:      "accept ok from repo collaborator",
			comment:   accept,
			commenter: friend,
			expected:  true,
		},
	}
	for _, tc := range testcases {
		t.Run(tc.name, func(t *testing.T) {
			if tc.author == "" {
				tc.author = rando
			}
			g := &fakegithub.FakeClient{
				OrgMembers:    map[string][]string{"kubernetes": {sister}, "kubernetes-incubator": {member, fakegithub.Bot}},
				Collaborators: []string{friend},
				IssueComments: map[int][]github.IssueComment{},
			}
			trigger := plugins.Trigger{
				TrustedOrg:     "kubernetes",
				OnlyOrgMembers: tc.onlyOrg,
			}
			var comments []github.IssueComment
			if tc.comment != "" {
				comments = append(comments, github.IssueComment{
					Body: tc.comment,
					User: github.User{Login: tc.commenter},
				})
			}
			actual, err := trustedPullRequest(g, &trigger, tc.author, "kubernetes-incubator", "random-repo", comments)
			if err != nil {
				t.Fatalf("Didn't expect error: %s", err)
			}
			if actual != tc.expected {
				t.Errorf("actual result %t != expected %t", actual, tc.expected)
			}
		})
	}
}
func TestHandlePullRequest(t *testing.T) {
	var testcases = []struct {
		name string

		Author        string
		ShouldBuild   bool
		ShouldComment bool
		HasOkToTest   bool
		prLabel       string
		prChanges     bool
		prAction      github.PullRequestEventAction
	}{
		{
			name: "Trusted user Open PR.",

			Author:      "t",
			ShouldBuild: true,
			prAction:    github.PullRequestActionOpened,
		},
		{
			name: "Untrusted user Open PR.",

			Author:        "u",
			ShouldBuild:   false,
			ShouldComment: true,
			prAction:      github.PullRequestActionOpened,
		},
		{
			name: "Trusted user Reopen PR.",

			Author:      "t",
			ShouldBuild: true,
			prAction:    github.PullRequestActionReopened,
		},
		{
			name: "Untrusted user Reopen PR with ok-to-test.",

			Author:      "u",
			ShouldBuild: true,
			HasOkToTest: true,
			prAction:    github.PullRequestActionReopened,
		},
		{
			name: "Untrusted user Reopen PR without ok-to-test.",

			Author:      "u",
			ShouldBuild: false,
			prAction:    github.PullRequestActionReopened,
		},
		{
			name: "Trusted user Edit PR with changes",

			Author:      "t",
			ShouldBuild: true,
			prChanges:   true,
			prAction:    github.PullRequestActionEdited,
		},
		{
			name: "Trusted user Edit PR without changes",

			Author:      "t",
			ShouldBuild: false,
			prAction:    github.PullRequestActionEdited,
		},
		{
			name: "Untrusted user Edit PR.",

			Author:      "u",
			ShouldBuild: false,
			prAction:    github.PullRequestActionEdited,
		},
		{
			name: "Trusted user Sync PR.",

			Author:      "t",
			ShouldBuild: true,
			prAction:    github.PullRequestActionSynchronize,
		},
		{
			name: "Untrusted user Sync PR.",

			Author:      "u",
			ShouldBuild: false,
			prAction:    github.PullRequestActionSynchronize,
		},
		{
			name: "Trusted user Labeled PR.",

			Author:      "t",
			ShouldBuild: false,
			prAction:    github.PullRequestActionLabeled,
			prLabel:     "lgtm",
		},
		{
			name: "Untrusted user Labeled PR with lgtm.",

			Author:      "u",
			ShouldBuild: true,
			prAction:    github.PullRequestActionLabeled,
			prLabel:     "lgtm",
		},
		{
			name: "Untrusted user Labeled PR without lgtm.",

			Author:      "u",
			ShouldBuild: false,
			prAction:    github.PullRequestActionLabeled,
			prLabel:     "test",
		},
		{
			name: "Closed PR.",

			Author:      "t",
			ShouldBuild: false,
			prAction:    github.PullRequestActionClosed,
		},
	}
	for _, tc := range testcases {
		t.Logf("running scenario %q", tc.name)

		g := &fakegithub.FakeClient{
			IssueComments: map[int][]github.IssueComment{},
			OrgMembers:    map[string][]string{"org": {"t"}},
			PullRequests: map[int]*github.PullRequest{
				0: {
					Number: 0,
					User:   github.User{Login: tc.Author},
					Base: github.PullRequestBranch{
						Ref: "master",
						Repo: github.Repo{
							Owner: github.User{Login: "org"},
							Name:  "repo",
						},
					},
				},
			},
		}
		kc := &fkc{}
		c := client{
			GitHubClient: g,
			KubeClient:   kc,
			Config:       &config.Config{},
			Logger:       logrus.WithField("plugin", pluginName),
		}

		presubmits := map[string][]config.Presubmit{
			"org/repo": {
				{
					Name:      "jib",
					AlwaysRun: true,
				},
			},
		}
		if err := c.Config.SetPresubmits(presubmits); err != nil {
			t.Fatalf("failed to set presubmits: %v", err)
		}

		if tc.HasOkToTest {
			g.IssueComments[0] = []github.IssueComment{{
				Body: "/ok-to-test",
				User: github.User{Login: "t"},
			}}
		}
		pr := github.PullRequestEvent{
			Action: tc.prAction,
			Label:  github.Label{Name: tc.prLabel},
			PullRequest: github.PullRequest{
				Number: 0,
				User:   github.User{Login: tc.Author},
				Base: github.PullRequestBranch{
					Ref: "master",
					Repo: github.Repo{
						Owner:    github.User{Login: "org"},
						Name:     "repo",
						FullName: "org/repo",
					},
				},
			},
		}
		if tc.prChanges {
			data := []byte(`{"base":{"ref":{"from":"REF"}, "sha":{"from":"SHA"}}}`)
			pr.Changes = (json.RawMessage)(data)
		}
		trigger := plugins.Trigger{
			TrustedOrg:     "org",
			OnlyOrgMembers: true,
		}
		if err := handlePR(c, &trigger, pr); err != nil {
			t.Fatalf("Didn't expect error: %s", err)
		}
		if len(kc.started) > 0 && !tc.ShouldBuild {
			t.Errorf("Built but should not have: %+v", tc)
		} else if len(kc.started) == 0 && tc.ShouldBuild {
			t.Errorf("Not built but should have: %+v", tc)
		}
		if tc.ShouldComment && len(g.IssueCommentsAdded) == 0 {
			t.Error("Expected comment to github")
		} else if !tc.ShouldComment && len(g.IssueCommentsAdded) > 0 {
			t.Errorf("Expected no comments to github, but got %d", len(g.CreatedStatuses))
		}
		if tc.HasOkToTest {
			if len(g.LabelsRemoved) != 1 {
				t.Errorf("expected a label to be removed")
				continue
			}
			expected := "org/repo#0:needs-ok-to-test"
			if g.LabelsRemoved[0] != expected {
				t.Errorf("expected %q to be removed, got %q", expected, g.LabelsRemoved[0])
			}
		}
	}
}
<commit_msg>Improve test description and add scenarios<commit_after>/*
Copyright 2016 The Kubernetes 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 trigger

import (
	"encoding/json"
	"testing"

	"github.com/sirupsen/logrus"
	"k8s.io/test-infra/prow/config"
	"k8s.io/test-infra/prow/github"
	"k8s.io/test-infra/prow/github/fakegithub"
	"k8s.io/test-infra/prow/plugins"
)

func TestTrusted(t *testing.T) {
	const rando = "random-person"
	const member = "org-member"
	const sister = "trusted-org-member"
	const friend = "repo-collaborator"

	const accept = "/ok-to-test"
	const chatter = "ignore random stuff"

	var testcases = []struct {
		name      string
		author    string
		comment   string
		commenter string
		onlyOrg   bool
		expected  bool
	}{
		{
			name:     "trust org member",
			author:   member,
			expected: true,
		},
		{
			name:     "trust member of other trusted org",
			author:   sister,
			expected: true,
		},
		{
			name: "reject random author",
		},
		{
			name:      "reject random author on random org member commentary",
			comment:   chatter,
			commenter: member,
		},
		{
			name:      "accept random PR after org member ok",
			comment:   accept,
			commenter: member,
			expected:  true,
		},
		{
			name:      "accept random PR after ok from trusted org member",
			comment:   accept,
			commenter: sister,
			expected:  true,
		},
		{
			name:      "ok may end with a \\r",
			comment:   accept + "\r",
			commenter: member,
			expected:  true,
		},
		{
			name:      "ok start on a middle line",
			comment:   "hello\n" + accept + "\r\nplease",
			commenter: member,
			expected:  true,
		},
		{
			name:      "require ok on start of line",
			comment:   "please, " + accept,
			commenter: member,
		},
		{
			name:      "reject acceptance from random person",
			comment:   accept,
			commenter: rando + " III",
		},
		{
			name:      "reject acceptance from this bot",
			comment:   accept,
			commenter: fakegithub.Bot,
		},
		{
			name:      "reject acceptance from random author",
			comment:   accept,
			commenter: rando,
		},
		{
			name:      "reject acceptance from repo collaborator in org-only mode",
			comment:   accept,
			commenter: friend,
			onlyOrg:   true,
		},
		{
			name:      "accept ok from repo collaborator",
			comment:   accept,
			commenter: friend,
			expected:  true,
		},
	}
	for _, tc := range testcases {
		t.Run(tc.name, func(t *testing.T) {
			if tc.author == "" {
				tc.author = rando
			}
			g := &fakegithub.FakeClient{
				OrgMembers:    map[string][]string{"kubernetes": {sister}, "kubernetes-incubator": {member, fakegithub.Bot}},
				Collaborators: []string{friend},
				IssueComments: map[int][]github.IssueComment{},
			}
			trigger := plugins.Trigger{
				TrustedOrg:     "kubernetes",
				OnlyOrgMembers: tc.onlyOrg,
			}
			var comments []github.IssueComment
			if tc.comment != "" {
				comments = append(comments, github.IssueComment{
					Body: tc.comment,
					User: github.User{Login: tc.commenter},
				})
			}
			actual, err := trustedPullRequest(g, &trigger, tc.author, "kubernetes-incubator", "random-repo", comments)
			if err != nil {
				t.Fatalf("Didn't expect error: %s", err)
			}
			if actual != tc.expected {
				t.Errorf("actual result %t != expected %t", actual, tc.expected)
			}
		})
	}
}
func TestHandlePullRequest(t *testing.T) {
	var testcases = []struct {
		name string

		Author        string
		ShouldBuild   bool
		ShouldComment bool
		HasOkToTest   bool
		prLabel       string
		prChanges     bool
		prAction      github.PullRequestEventAction
	}{
		{
			name: "Trusted user open PR should build",

			Author:      "t",
			ShouldBuild: true,
			prAction:    github.PullRequestActionOpened,
		},
		{
			name: "Untrusted user open PR should not build and should comment",

			Author:        "u",
			ShouldBuild:   false,
			ShouldComment: true,
			prAction:      github.PullRequestActionOpened,
		},
		{
			name: "Trusted user reopen PR should build",

			Author:      "t",
			ShouldBuild: true,
			prAction:    github.PullRequestActionReopened,
		},
		{
			name: "Untrusted user reopen PR with ok-to-test should build",

			Author:      "u",
			ShouldBuild: true,
			HasOkToTest: true,
			prAction:    github.PullRequestActionReopened,
		},
		{
			name: "Untrusted user reopen PR without ok-to-test should not build",

			Author:      "u",
			ShouldBuild: false,
			prAction:    github.PullRequestActionReopened,
		},
		{
			name: "Trusted user edit PR with changes should build",

			Author:      "t",
			ShouldBuild: true,
			prChanges:   true,
			prAction:    github.PullRequestActionEdited,
		},
		{
			name: "Trusted user edit PR without changes should not build",

			Author:      "t",
			ShouldBuild: false,
			prAction:    github.PullRequestActionEdited,
		},
		{
			name: "Untrusted user edit PR without changes and without ok-to-test should not build",

			Author:      "u",
			ShouldBuild: false,
			prAction:    github.PullRequestActionEdited,
		},
		{
			name: "Untrusted user edit PR with changes and without ok-to-test should not build",

			Author:      "u",
			ShouldBuild: false,
			prChanges:   true,
			prAction:    github.PullRequestActionEdited,
		},
		{
			name: "Untrusted user edit PR without changes and with ok-to-test should not build",

			Author:      "u",
			ShouldBuild: false,
			HasOkToTest: true,
			prAction:    github.PullRequestActionEdited,
		},
		{
			name: "Untrusted user edit PR with changes and with ok-to-test should build",

			Author:      "u",
			ShouldBuild: true,
			HasOkToTest: true,
			prChanges:   true,
			prAction:    github.PullRequestActionEdited,
		},
		{
			name: "Trusted user sync PR should build",

			Author:      "t",
			ShouldBuild: true,
			prAction:    github.PullRequestActionSynchronize,
		},
		{
			name: "Untrusted user sync PR without ok-to-test should not build",

			Author:      "u",
			ShouldBuild: false,
			prAction:    github.PullRequestActionSynchronize,
		},
		{
			name: "Untrusted user sync PR with ok-to-test should build",

			Author:      "u",
			ShouldBuild: true,
			HasOkToTest: true,
			prAction:    github.PullRequestActionSynchronize,
		},
		{
			name: "Trusted user labeled PR wiht lgtm should not build",

			Author:      "t",
			ShouldBuild: false,
			prAction:    github.PullRequestActionLabeled,
			prLabel:     "lgtm",
		},
		{
			name: "Untrusted user labeled PR with lgtm should build",

			Author:      "u",
			ShouldBuild: true,
			prAction:    github.PullRequestActionLabeled,
			prLabel:     "lgtm",
		},
		{
			name: "Untrusted user labeled PR without lgtm should not build",

			Author:      "u",
			ShouldBuild: false,
			prAction:    github.PullRequestActionLabeled,
			prLabel:     "test",
		},
		{
			name: "Trusted user closed PR should not build",

			Author:      "t",
			ShouldBuild: false,
			prAction:    github.PullRequestActionClosed,
		},
	}
	for _, tc := range testcases {
		t.Logf("running scenario %q", tc.name)

		g := &fakegithub.FakeClient{
			IssueComments: map[int][]github.IssueComment{},
			OrgMembers:    map[string][]string{"org": {"t"}},
			PullRequests: map[int]*github.PullRequest{
				0: {
					Number: 0,
					User:   github.User{Login: tc.Author},
					Base: github.PullRequestBranch{
						Ref: "master",
						Repo: github.Repo{
							Owner: github.User{Login: "org"},
							Name:  "repo",
						},
					},
				},
			},
		}
		kc := &fkc{}
		c := client{
			GitHubClient: g,
			KubeClient:   kc,
			Config:       &config.Config{},
			Logger:       logrus.WithField("plugin", pluginName),
		}

		presubmits := map[string][]config.Presubmit{
			"org/repo": {
				{
					Name:      "jib",
					AlwaysRun: true,
				},
			},
		}
		if err := c.Config.SetPresubmits(presubmits); err != nil {
			t.Fatalf("failed to set presubmits: %v", err)
		}

		if tc.HasOkToTest {
			g.IssueComments[0] = []github.IssueComment{{
				Body: "/ok-to-test",
				User: github.User{Login: "t"},
			}}
		}
		pr := github.PullRequestEvent{
			Action: tc.prAction,
			Label:  github.Label{Name: tc.prLabel},
			PullRequest: github.PullRequest{
				Number: 0,
				User:   github.User{Login: tc.Author},
				Base: github.PullRequestBranch{
					Ref: "master",
					Repo: github.Repo{
						Owner:    github.User{Login: "org"},
						Name:     "repo",
						FullName: "org/repo",
					},
				},
			},
		}
		if tc.prChanges {
			data := []byte(`{"base":{"ref":{"from":"REF"}, "sha":{"from":"SHA"}}}`)
			pr.Changes = (json.RawMessage)(data)
		}
		trigger := plugins.Trigger{
			TrustedOrg:     "org",
			OnlyOrgMembers: true,
		}
		if err := handlePR(c, &trigger, pr); err != nil {
			t.Fatalf("Didn't expect error: %s", err)
		}
		if len(kc.started) > 0 && !tc.ShouldBuild {
			t.Errorf("Built but should not have: %+v", tc)
		} else if len(kc.started) == 0 && tc.ShouldBuild {
			t.Errorf("Not built but should have: %+v", tc)
		}
		if tc.ShouldComment && len(g.IssueCommentsAdded) == 0 {
			t.Error("Expected comment to github")
		} else if !tc.ShouldComment && len(g.IssueCommentsAdded) > 0 {
			t.Errorf("Expected no comments to github, but got %d", len(g.CreatedStatuses))
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"github.com/phzfi/RIC/server/config"
	"github.com/phzfi/RIC/server/logging"
	"github.com/phzfi/RIC/server/operator"
	"github.com/phzfi/RIC/server/ops"
	"github.com/phzfi/RIC/server/testutils"
	"github.com/valyala/fasthttp"
	"log"
	"net"
	"time"
)

// Port will be incremented for each server created in the test
var port = 8022

// This is an utility function to launch a server.
func startServer() (server *fasthttp.Server, ln net.Listener, srverr chan error) {
	// Start the server
	conf, err := config.ReadConfig("config/testconfig.ini")

	if err != nil {
		log.Fatal("Error while reading config" + err.Error())
		return
	}

	port++
	server, _, ln = NewServer(port, 500000, conf)
	srverr = make(chan error)
	go func() {
		srverr <- server.Serve(ln)
	}()
	time.Sleep(100 * time.Millisecond)
	return
}

// Stop server and block until stopped
func stopServer(server *fasthttp.Server, ln net.Listener, srverr chan error) error {
	_ = ln.Close()
	select {
	case err := <-srverr:
		return err
	default:
	}
	return nil
}

// A setup function for simple Operator tests. Creates a cache, operator that
// uses the cache and ImageSource operation with current working dir as root.
// Returns the operator and the source operation.
func SetupOperatorSource() (o operator.Operator, src ops.ImageSource) {
	o = operator.MakeDefault(512*1024*1024, "/tmp/RIC_testimagecache")
	src = ops.MakeImageSource()
	src.AddRoot("./")
	return
}

// Gets blob from server. package variable port is used as port and localhost as address
func getBlobFromServer(getname string) (blob []byte, err error) {
	statuscode, blob, err := fasthttp.Get(nil, fmt.Sprintf("http://localhost:%d/", port)+getname)
	if statuscode != 200 {
		return nil, errors.New("Server returned " + string(statuscode))
	}
	if err != nil {
		return
	}

	return
}

// Tests getting images. c.Testfn is treated as GET string (filename?params).
// Executes the tests supported by TestCaseAll.
func testGetImages(cases []testutils.TestCaseAll) (err error) {

	s, ln, srverr := startServer()
	defer stopServer(s, ln, srverr)

	tolerance := 0.002

	// Todo: this threading is copied from common_test.go unify it to single implementation (DRY)
	sem := make(chan error, len(cases))
	for _, c := range cases {
		go func(tc testutils.TestCaseAll) {
			logging.Debug(fmt.Sprintf("Testing get: %v, %v, %v, %v, %v", tc.Testfn, tc.Reffn, tc.Resfn, tc.W, tc.H))
			blob, err := getBlobFromServer(tc.Testfn)
			if err != nil {
				sem <- err
				return
			}

			sem <- testutils.TestAll(tc, blob, tolerance)
		}(c)
	}

	for range cases {
		var verr = <-sem
		if verr != nil && err == nil {
			err = verr
		}
	}
	return
}
<commit_msg>fix previous<commit_after>package main

import (
	"errors"
	"fmt"
	"github.com/phzfi/RIC/server/config"
	"github.com/phzfi/RIC/server/logging"
	"github.com/phzfi/RIC/server/operator"
	"github.com/phzfi/RIC/server/ops"
	"github.com/phzfi/RIC/server/testutils"
	"github.com/valyala/fasthttp"
	"log"
	"net"
	"time"
)

// Port will be incremented for each server created in the test
var port = 8022

// This is an utility function to launch a server.
func startServer() (server *fasthttp.Server, ln net.Listener, srverr chan error) {
	// Start the server
	conf, err := config.ReadConfig("config/testconfig.ini")

	if err != nil {
		log.Fatal("Error while reading config" + err.Error())
		return
	}

	port++
	server, _, ln = NewServer(port, 500000, conf)
	srverr = make(chan error)
	go func() {
		srverr <- server.Serve(ln)
	}()
	time.Sleep(100 * time.Millisecond)
	return
}

// Stop server and block until stopped
func stopServer(server *fasthttp.Server, ln net.Listener, srverr chan error) error {
	_ = ln.Close()
	select {
	case err := <-srverr:
		return err
	default:
	}
	return nil
}

// A setup function for simple Operator tests. Creates a cache, operator that
// uses the cache and ImageSource operation with current working dir as root.
// Returns the operator and the source operation.
func SetupOperatorSource() (o operator.Operator, src ops.ImageSource) {
	o = operator.MakeDefault(512*1024*1024, "/tmp/RIC_testimagecache")
	src = ops.MakeImageSource()
	src.AddRoot("./")
	return
}

// Gets blob from server. package variable port is used as port and localhost as address
func getBlobFromServer(getname string) (blob []byte, err error) {
	statuscode, blob, err := fasthttp.Get(nil, fmt.Sprintf("http://localhost:%d/", port)+getname)
	if statuscode != 200 {
		return nil, errors.New(fmt.Sprintf("Server returned %d", statuscode))
	}
	if err != nil {
		return
	}

	return
}

// Tests getting images. c.Testfn is treated as GET string (filename?params).
// Executes the tests supported by TestCaseAll.
func testGetImages(cases []testutils.TestCaseAll) (err error) {

	s, ln, srverr := startServer()
	defer stopServer(s, ln, srverr)

	tolerance := 0.002

	// Todo: this threading is copied from common_test.go unify it to single implementation (DRY)
	sem := make(chan error, len(cases))
	for _, c := range cases {
		go func(tc testutils.TestCaseAll) {
			logging.Debug(fmt.Sprintf("Testing get: %v, %v, %v, %v, %v", tc.Testfn, tc.Reffn, tc.Resfn, tc.W, tc.H))
			blob, err := getBlobFromServer(tc.Testfn)
			if err != nil {
				sem <- err
				return
			}

			sem <- testutils.TestAll(tc, blob, tolerance)
		}(c)
	}

	for range cases {
		var verr = <-sem
		if verr != nil && err == nil {
			err = verr
		}
	}
	return
}
<|endoftext|>


<commit_before>// SILVER - Service Wrapper
//
// Copyright (c) 2014-2021 PaperCut Software http://www.papercut.com/
// Use of this source code is governed by an MIT or GPL Version 2 license.
// See the project's LICENSE file for more information.
//
// FUTURE: Parsing structs should be separated from returns structs.  The
//         return structs should have types like time.Duration, etc.
//

package config

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
)

type Config struct {
	ServiceDescription ServiceDescription
	ServiceConfig      ServiceConfig
	Include            []string
	EnvironmentVars    map[string]string
	Services           []Service
	StartupTasks       []StartupTask
	ScheduledTasks     []ScheduledTask
	Commands           []Command
}

type ServiceDescription struct {
	Name        string
	DisplayName string
	Description string
}

type ServiceConfig struct {
	StopFile         string
	ReloadFile       string
	LogFile          string
	LogFileMaxSizeMb int
	PidFile          string
	UserLevel        bool
	UserName         string
}

type command struct {
	Path string
	Args []string
}

type Service struct {
	command
	GracefulShutdownTimeoutSecs int
	MaxCrashCountPerHour        int
	RestartDelaySecs            int
	StartupDelaySecs            int
	MonitorPing                 *MonitorPing
}

type MonitorPing struct {
	URL                   string
	IntervalSecs          int
	TimeoutSecs           int
	StartupDelaySecs      int
	RestartOnFailureCount int
}

type Task struct {
	command
	TimeoutSecs            int
	StartupDelaySecs       int
	StartupRandomDelaySecs int
}

type StartupTask struct {
	Task
	Async bool
}

type ScheduledTask struct {
	Task
	Schedule string
}

type Command struct {
	command
	Name        string
	TimeoutSecs int
}

type ReplacementVars struct {
	ServiceName string
	ServiceRoot string
}

// LoadConfig parses config.
func LoadConfig(path string, vars ReplacementVars) (conf *Config, err error) {
	if _, err = os.Stat(path); os.IsNotExist(err) {
		return nil, fmt.Errorf("The configuration file does not exist. Please place the file here: %s", path)
	}
	conf, err = load(path, vars)
	if err != nil {
		return nil, err
	}
	err = validate(conf)
	if err != nil {
		return nil, err
	}
	return conf, nil
}

// Merge in an include file.  Include files can contain services, tasks and commands
func MergeInclude(conf Config, path string, vars ReplacementVars) (*Config, error) {
	include, err := load(path, vars)
	if err != nil {
		return &conf, err
	}

	conf.Services = append(conf.Services, include.Services...)
	conf.StartupTasks = append(conf.StartupTasks, include.StartupTasks...)
	conf.ScheduledTasks = append(conf.ScheduledTasks, include.ScheduledTasks...)
	conf.Commands = append(conf.Commands, include.Commands...)
	for k, v := range include.EnvironmentVars {
		conf.EnvironmentVars[k] = v
	}
	return &conf, nil
}

func load(path string, vars ReplacementVars) (conf *Config, err error) {
	s, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}

	// Special case for an empty file (empty file will raise error with JSON parser)
	if string(s) == "" {
		conf = &Config{}
		applyDefaults(conf)
		return conf, nil
	}

	err = json.Unmarshal(s, &conf)
	if err != nil {
		return nil, err
	}

	replacements := map[string]string{
		"${ServiceName}": jsonEscapeString(vars.ServiceName),
		"${ServiceRoot}": jsonEscapeString(vars.ServiceRoot),
	}
	s = []byte(replaceVars(string(s), replacements))

	err = json.Unmarshal(s, &conf)
	if err != nil {
		return nil, err
	}

	applyDefaults(conf)

	return conf, nil
}

func validate(conf *Config) error {
	if conf.ServiceDescription.DisplayName == "" {
		return fmt.Errorf("ServiceDescription.DisplayName is required configuration")
	}
	return nil
}

func applyDefaults(conf *Config) {
	if conf.ServiceConfig.StopFile == "" {
		conf.ServiceConfig.StopFile = ".stop"
	}
	if conf.ServiceConfig.ReloadFile == "" {
		conf.ServiceConfig.ReloadFile = ".reload"
	}

	if conf.ServiceConfig.LogFileMaxSizeMb == 0 {
		conf.ServiceConfig.LogFileMaxSizeMb = 50
	}

	if conf.EnvironmentVars == nil {
		conf.EnvironmentVars = make(map[string]string)
	}

	// Default graceful is 5 seconds
	for i := range conf.Services {
		if conf.Services[i].GracefulShutdownTimeoutSecs == 0 {
			conf.Services[i].GracefulShutdownTimeoutSecs = 5
		}
	}
}

func replaceVars(in string, replacements map[string]string) (out string) {
	out = in
	for key, value := range replacements {
		out = strings.Replace(out, key, value, -1)
	}
	return out
}

func jsonEscapeString(in string) (out string) {
	// FIXME: We should be a bit smarter
	r := strings.NewReplacer("\\", "\\\\", "\"", "\\\"")
	return r.Replace(in)
}
<commit_msg>refactor: extract constants, move arg to be receiver, use replaceAll<commit_after>// SILVER - Service Wrapper
//
// Copyright (c) 2014-2021 PaperCut Software http://www.papercut.com/
// Use of this source code is governed by an MIT or GPL Version 2 license.
// See the project's LICENSE file for more information.
//
// FUTURE: Parsing structs should be separated from returns structs.  The
//         return structs should have types like time.Duration, etc.
//

package config

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
)

const StopFileName = ".stop"
const ReloadFileName = ".reload"

type Config struct {
	ServiceDescription ServiceDescription
	ServiceConfig      ServiceConfig
	Include            []string
	EnvironmentVars    map[string]string
	Services           []Service
	StartupTasks       []StartupTask
	ScheduledTasks     []ScheduledTask
	Commands           []Command
}

type ServiceDescription struct {
	Name        string
	DisplayName string
	Description string
}

type ServiceConfig struct {
	StopFile         string
	ReloadFile       string
	LogFile          string
	LogFileMaxSizeMb int
	PidFile          string
	UserLevel        bool
	UserName         string
}

type command struct {
	Path string
	Args []string
}

type Service struct {
	command
	GracefulShutdownTimeoutSecs int
	MaxCrashCountPerHour        int
	RestartDelaySecs            int
	StartupDelaySecs            int
	MonitorPing                 *MonitorPing
}

type MonitorPing struct {
	URL                   string
	IntervalSecs          int
	TimeoutSecs           int
	StartupDelaySecs      int
	RestartOnFailureCount int
}

type Task struct {
	command
	TimeoutSecs            int
	StartupDelaySecs       int
	StartupRandomDelaySecs int
}

type StartupTask struct {
	Task
	Async bool
}

type ScheduledTask struct {
	Task
	Schedule string
}

type Command struct {
	command
	Name        string
	TimeoutSecs int
}

type ReplacementVars struct {
	ServiceName string
	ServiceRoot string
}

// LoadConfig parses config.
func LoadConfig(path string, vars ReplacementVars) (conf *Config, err error) {
	if _, err = os.Stat(path); os.IsNotExist(err) {
		return nil, fmt.Errorf("The configuration file does not exist. Please place the file here: %s", path)
	}
	conf, err = load(path, vars)
	if err != nil {
		return nil, err
	}
	err = conf.validate()
	if err != nil {
		return nil, err
	}
	return conf, nil
}

// MergeInclude merges in an include file.  Include files can contain services, tasks and commands
func MergeInclude(conf Config, path string, vars ReplacementVars) (*Config, error) {
	include, err := load(path, vars)
	if err != nil {
		return &conf, err
	}

	conf.Services = append(conf.Services, include.Services...)
	conf.StartupTasks = append(conf.StartupTasks, include.StartupTasks...)
	conf.ScheduledTasks = append(conf.ScheduledTasks, include.ScheduledTasks...)
	conf.Commands = append(conf.Commands, include.Commands...)
	for k, v := range include.EnvironmentVars {
		conf.EnvironmentVars[k] = v
	}
	return &conf, nil
}

func load(path string, vars ReplacementVars) (conf *Config, err error) {
	s, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}

	// Special case for an empty file (empty file will raise error with JSON parser)
	if string(s) == "" {
		conf = &Config{}
		conf.applyDefaults()
		return conf, nil
	}

	err = json.Unmarshal(s, &conf)
	if err != nil {
		return nil, err
	}

	replacements := map[string]string{
		"${ServiceName}": jsonEscapeString(vars.ServiceName),
		"${ServiceRoot}": jsonEscapeString(vars.ServiceRoot),
	}
	s = []byte(replaceVars(string(s), replacements))

	err = json.Unmarshal(s, &conf)
	if err != nil {
		return nil, err
	}

	conf.applyDefaults()

	return conf, nil
}

func validate(conf *Config) error {
	if conf.ServiceDescription.DisplayName == "" {
		return fmt.Errorf("ServiceDescription.DisplayName is required configuration")
	}
	return nil
}

func (conf *Config) applyDefaults() {
	if conf.ServiceConfig.StopFile == "" {
		conf.ServiceConfig.StopFile = StopFileName
	}
	if conf.ServiceConfig.ReloadFile == "" {
		conf.ServiceConfig.ReloadFile = ReloadFileName
	}

	if conf.ServiceConfig.LogFileMaxSizeMb == 0 {
		conf.ServiceConfig.LogFileMaxSizeMb = 50
	}

	if conf.EnvironmentVars == nil {
		conf.EnvironmentVars = make(map[string]string)
	}

	// Default graceful is 5 seconds
	for i := range conf.Services {
		if conf.Services[i].GracefulShutdownTimeoutSecs == 0 {
			conf.Services[i].GracefulShutdownTimeoutSecs = 5
		}
	}
}

func replaceVars(in string, replacements map[string]string) (out string) {
	out = in
	for key, value := range replacements {
		out = strings.ReplaceAll(out, key, value)
	}
	return out
}

func jsonEscapeString(in string) (out string) {
	// FIXME: We should be a bit smarter
	r := strings.NewReplacer("\\", "\\\\", "\"", "\\\"")
	return r.Replace(in)
}
<|endoftext|>