<commit_before>package gqlrelay

import (
	"bytes"
	"fmt"
	"github.com/chris-ramon/graphql-go/testutil"
	"github.com/kr/pretty"
	"net/http"
	"net/url"
	"reflect"
	"testing"
)

func TestRequestOptions_GET_BasicQueryString(t *testing.T) {
	queryString := "query=query RebelsShipsQuery { rebels { name } }"
	expected := &requestOptions{
		Query: "query RebelsShipsQuery { rebels { name } }",
	}

	req, _ := http.NewRequest("GET", fmt.Sprintf("/graphql?%v", queryString), nil)
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}
func TestRequestOptions_GET_ContentTypeApplicationGraphQL(t *testing.T) {
	body := []byte(`query RebelsShipsQuery { rebels { name } }`)
	expected := &requestOptions{}

	req, _ := http.NewRequest("GET", "/graphql", bytes.NewBuffer(body))
	req.Header.Add("Content-Type", "application/graphql")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}
func TestRequestOptions_GET_ContentTypeApplicationJSON(t *testing.T) {
	body := `
	{
		"query": "query RebelsShipsQuery { rebels { name } }"
	}`
	expected := &requestOptions{}

	req, _ := http.NewRequest("GET", "/graphql", bytes.NewBufferString(body))
	req.Header.Add("Content-Type", "application/json")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}
func TestRequestOptions_GET_ContentTypeApplicationUrlEncoded(t *testing.T) {
	data := url.Values{}
	data.Add("query", "query RebelsShipsQuery { rebels { name } }")

	expected := &requestOptions{}

	req, _ := http.NewRequest("GET", "/graphql", bytes.NewBufferString(data.Encode()))
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}

func TestRequestOptions_POST_BasicQueryString_WithNoBody(t *testing.T) {
	queryString := "query=query RebelsShipsQuery { rebels { name } }"
	expected := &requestOptions{
		Query: "query RebelsShipsQuery { rebels { name } }",
	}

	req, _ := http.NewRequest("POST", fmt.Sprintf("/graphql?%v", queryString), nil)
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}
func TestRequestOptions_POST_ContentTypeApplicationGraphQL(t *testing.T) {
	body := []byte(`query RebelsShipsQuery { rebels { name } }`)
	expected := &requestOptions{
		Query: "query RebelsShipsQuery { rebels { name } }",
	}

	req, _ := http.NewRequest("POST", "/graphql", bytes.NewBuffer(body))
	req.Header.Add("Content-Type", "application/graphql")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}
func TestRequestOptions_POST_ContentTypeApplicationGraphQL_WithNonGraphQLQueryContent(t *testing.T) {
	body := []byte(`not a graphql query`)
	expected := &requestOptions{
		Query: "not a graphql query",
	}

	req, _ := http.NewRequest("POST", "/graphql", bytes.NewBuffer(body))
	req.Header.Add("Content-Type", "application/graphql")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}
func TestRequestOptions_POST_ContentTypeApplicationGraphQL_EmptyBody(t *testing.T) {
	body := []byte(``)
	expected := &requestOptions{
		Query: "",
	}

	req, _ := http.NewRequest("POST", "/graphql", bytes.NewBuffer(body))
	req.Header.Add("Content-Type", "application/graphql")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}
func TestRequestOptions_POST_ContentTypeApplicationGraphQL_NilBody(t *testing.T) {
	expected := &requestOptions{}

	req, err := http.NewRequest("POST", "/graphql", nil)
	pretty.Println(err)
	req.Header.Add("Content-Type", "application/graphql")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}

func TestRequestOptions_POST_ContentTypeApplicationJSON(t *testing.T) {
	body := `
	{
		"query": "query RebelsShipsQuery { rebels { name } }"
	}`
	expected := &requestOptions{
		Query: "query RebelsShipsQuery { rebels { name } }",
	}

	req, _ := http.NewRequest("POST", "/graphql", bytes.NewBufferString(body))
	req.Header.Add("Content-Type", "application/json")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}
func TestRequestOptions_POST_ContentTypeApplicationJSON_WithInvalidJSON(t *testing.T) {
	body := `INVALIDJSON{}`
	expected := &requestOptions{}

	req, _ := http.NewRequest("POST", "/graphql", bytes.NewBufferString(body))
	req.Header.Add("Content-Type", "application/json")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}
func TestRequestOptions_POST_ContentTypeApplicationJSON_WithNilBody(t *testing.T) {
	expected := &requestOptions{}

	req, _ := http.NewRequest("POST", "/graphql", nil)
	req.Header.Add("Content-Type", "application/json")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}

func TestRequestOptions_POST_ContentTypeApplicationUrlEncoded(t *testing.T) {
	data := url.Values{}
	data.Add("query", "query RebelsShipsQuery { rebels { name } }")

	expected := &requestOptions{
		Query: "query RebelsShipsQuery { rebels { name } }",
	}

	req, _ := http.NewRequest("POST", "/graphql", bytes.NewBufferString(data.Encode()))
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}
func TestRequestOptions_POST_ContentTypeApplicationUrlEncoded_WithInvalidData(t *testing.T) {
	data := "Invalid Data"

	expected := &requestOptions{}

	req, _ := http.NewRequest("POST", "/graphql", bytes.NewBufferString(data))
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}
func TestRequestOptions_POST_ContentTypeApplicationUrlEncoded_WithNilBody(t *testing.T) {

	expected := &requestOptions{}

	req, _ := http.NewRequest("POST", "/graphql", nil)
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}

func TestRequestOptions_PUT_BasicQueryString(t *testing.T) {
	queryString := "query=query RebelsShipsQuery { rebels { name } }"
	expected := &requestOptions{
		Query: "query RebelsShipsQuery { rebels { name } }",
	}

	req, _ := http.NewRequest("PUT", fmt.Sprintf("/graphql?%v", queryString), nil)
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}
func TestRequestOptions_PUT_ContentTypeApplicationGraphQL(t *testing.T) {
	body := []byte(`query RebelsShipsQuery { rebels { name } }`)
	expected := &requestOptions{}

	req, _ := http.NewRequest("PUT", "/graphql", bytes.NewBuffer(body))
	req.Header.Add("Content-Type", "application/graphql")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}
func TestRequestOptions_PUT_ContentTypeApplicationJSON(t *testing.T) {
	body := `
	{
		"query": "query RebelsShipsQuery { rebels { name } }"
	}`
	expected := &requestOptions{}

	req, _ := http.NewRequest("PUT", "/graphql", bytes.NewBufferString(body))
	req.Header.Add("Content-Type", "application/json")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}
func TestRequestOptions_PUT_ContentTypeApplicationUrlEncoded(t *testing.T) {
	data := url.Values{}
	data.Add("query", "query RebelsShipsQuery { rebels { name } }")

	expected := &requestOptions{}

	req, _ := http.NewRequest("PUT", "/graphql", bytes.NewBufferString(data.Encode()))
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}

func TestRequestOptions_DELETE_BasicQueryString(t *testing.T) {
	queryString := "query=query RebelsShipsQuery { rebels { name } }"
	expected := &requestOptions{
		Query: "query RebelsShipsQuery { rebels { name } }",
	}

	req, _ := http.NewRequest("DELETE", fmt.Sprintf("/graphql?%v", queryString), nil)
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}
func TestRequestOptions_DELETE_ContentTypeApplicationGraphQL(t *testing.T) {
	body := []byte(`query RebelsShipsQuery { rebels { name } }`)
	expected := &requestOptions{}

	req, _ := http.NewRequest("DELETE", "/graphql", bytes.NewBuffer(body))
	req.Header.Add("Content-Type", "application/graphql")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}
func TestRequestOptions_DELETE_ContentTypeApplicationJSON(t *testing.T) {
	body := `
	{
		"query": "query RebelsShipsQuery { rebels { name } }"
	}`
	expected := &requestOptions{}

	req, _ := http.NewRequest("DELETE", "/graphql", bytes.NewBufferString(body))
	req.Header.Add("Content-Type", "application/json")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}
func TestRequestOptions_DELETE_ContentTypeApplicationUrlEncoded(t *testing.T) {
	data := url.Values{}
	data.Add("query", "query RebelsShipsQuery { rebels { name } }")

	expected := &requestOptions{}

	req, _ := http.NewRequest("DELETE", "/graphql", bytes.NewBufferString(data.Encode()))
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}

func TestRequestOptions_POST_UnsupportedContentType(t *testing.T) {
	body := `<xml>query{}</xml>`
	expected := &requestOptions{}

	req, _ := http.NewRequest("POST", "/graphql", bytes.NewBufferString(body))
	req.Header.Add("Content-Type", "application/xml")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}
<commit_msg>Minor code clean up<commit_after>package gqlrelay

import (
	"bytes"
	"fmt"
	"github.com/chris-ramon/graphql-go/testutil"
	"net/http"
	"net/url"
	"reflect"
	"testing"
)

func TestRequestOptions_GET_BasicQueryString(t *testing.T) {
	queryString := "query=query RebelsShipsQuery { rebels { name } }"
	expected := &requestOptions{
		Query: "query RebelsShipsQuery { rebels { name } }",
	}

	req, _ := http.NewRequest("GET", fmt.Sprintf("/graphql?%v", queryString), nil)
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}
func TestRequestOptions_GET_ContentTypeApplicationGraphQL(t *testing.T) {
	body := []byte(`query RebelsShipsQuery { rebels { name } }`)
	expected := &requestOptions{}

	req, _ := http.NewRequest("GET", "/graphql", bytes.NewBuffer(body))
	req.Header.Add("Content-Type", "application/graphql")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}
func TestRequestOptions_GET_ContentTypeApplicationJSON(t *testing.T) {
	body := `
	{
		"query": "query RebelsShipsQuery { rebels { name } }"
	}`
	expected := &requestOptions{}

	req, _ := http.NewRequest("GET", "/graphql", bytes.NewBufferString(body))
	req.Header.Add("Content-Type", "application/json")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}
func TestRequestOptions_GET_ContentTypeApplicationUrlEncoded(t *testing.T) {
	data := url.Values{}
	data.Add("query", "query RebelsShipsQuery { rebels { name } }")

	expected := &requestOptions{}

	req, _ := http.NewRequest("GET", "/graphql", bytes.NewBufferString(data.Encode()))
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}

func TestRequestOptions_POST_BasicQueryString_WithNoBody(t *testing.T) {
	queryString := "query=query RebelsShipsQuery { rebels { name } }"
	expected := &requestOptions{
		Query: "query RebelsShipsQuery { rebels { name } }",
	}

	req, _ := http.NewRequest("POST", fmt.Sprintf("/graphql?%v", queryString), nil)
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}
func TestRequestOptions_POST_ContentTypeApplicationGraphQL(t *testing.T) {
	body := []byte(`query RebelsShipsQuery { rebels { name } }`)
	expected := &requestOptions{
		Query: "query RebelsShipsQuery { rebels { name } }",
	}

	req, _ := http.NewRequest("POST", "/graphql", bytes.NewBuffer(body))
	req.Header.Add("Content-Type", "application/graphql")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}
func TestRequestOptions_POST_ContentTypeApplicationGraphQL_WithNonGraphQLQueryContent(t *testing.T) {
	body := []byte(`not a graphql query`)
	expected := &requestOptions{
		Query: "not a graphql query",
	}

	req, _ := http.NewRequest("POST", "/graphql", bytes.NewBuffer(body))
	req.Header.Add("Content-Type", "application/graphql")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}
func TestRequestOptions_POST_ContentTypeApplicationGraphQL_EmptyBody(t *testing.T) {
	body := []byte(``)
	expected := &requestOptions{
		Query: "",
	}

	req, _ := http.NewRequest("POST", "/graphql", bytes.NewBuffer(body))
	req.Header.Add("Content-Type", "application/graphql")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}
func TestRequestOptions_POST_ContentTypeApplicationGraphQL_NilBody(t *testing.T) {
	expected := &requestOptions{}

	req, _ := http.NewRequest("POST", "/graphql", nil)
	req.Header.Add("Content-Type", "application/graphql")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}

func TestRequestOptions_POST_ContentTypeApplicationJSON(t *testing.T) {
	body := `
	{
		"query": "query RebelsShipsQuery { rebels { name } }"
	}`
	expected := &requestOptions{
		Query: "query RebelsShipsQuery { rebels { name } }",
	}

	req, _ := http.NewRequest("POST", "/graphql", bytes.NewBufferString(body))
	req.Header.Add("Content-Type", "application/json")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}
func TestRequestOptions_POST_ContentTypeApplicationJSON_WithInvalidJSON(t *testing.T) {
	body := `INVALIDJSON{}`
	expected := &requestOptions{}

	req, _ := http.NewRequest("POST", "/graphql", bytes.NewBufferString(body))
	req.Header.Add("Content-Type", "application/json")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}
func TestRequestOptions_POST_ContentTypeApplicationJSON_WithNilBody(t *testing.T) {
	expected := &requestOptions{}

	req, _ := http.NewRequest("POST", "/graphql", nil)
	req.Header.Add("Content-Type", "application/json")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}

func TestRequestOptions_POST_ContentTypeApplicationUrlEncoded(t *testing.T) {
	data := url.Values{}
	data.Add("query", "query RebelsShipsQuery { rebels { name } }")

	expected := &requestOptions{
		Query: "query RebelsShipsQuery { rebels { name } }",
	}

	req, _ := http.NewRequest("POST", "/graphql", bytes.NewBufferString(data.Encode()))
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}
func TestRequestOptions_POST_ContentTypeApplicationUrlEncoded_WithInvalidData(t *testing.T) {
	data := "Invalid Data"

	expected := &requestOptions{}

	req, _ := http.NewRequest("POST", "/graphql", bytes.NewBufferString(data))
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}
func TestRequestOptions_POST_ContentTypeApplicationUrlEncoded_WithNilBody(t *testing.T) {

	expected := &requestOptions{}

	req, _ := http.NewRequest("POST", "/graphql", nil)
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}

func TestRequestOptions_PUT_BasicQueryString(t *testing.T) {
	queryString := "query=query RebelsShipsQuery { rebels { name } }"
	expected := &requestOptions{
		Query: "query RebelsShipsQuery { rebels { name } }",
	}

	req, _ := http.NewRequest("PUT", fmt.Sprintf("/graphql?%v", queryString), nil)
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}
func TestRequestOptions_PUT_ContentTypeApplicationGraphQL(t *testing.T) {
	body := []byte(`query RebelsShipsQuery { rebels { name } }`)
	expected := &requestOptions{}

	req, _ := http.NewRequest("PUT", "/graphql", bytes.NewBuffer(body))
	req.Header.Add("Content-Type", "application/graphql")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}
func TestRequestOptions_PUT_ContentTypeApplicationJSON(t *testing.T) {
	body := `
	{
		"query": "query RebelsShipsQuery { rebels { name } }"
	}`
	expected := &requestOptions{}

	req, _ := http.NewRequest("PUT", "/graphql", bytes.NewBufferString(body))
	req.Header.Add("Content-Type", "application/json")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}
func TestRequestOptions_PUT_ContentTypeApplicationUrlEncoded(t *testing.T) {
	data := url.Values{}
	data.Add("query", "query RebelsShipsQuery { rebels { name } }")

	expected := &requestOptions{}

	req, _ := http.NewRequest("PUT", "/graphql", bytes.NewBufferString(data.Encode()))
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}

func TestRequestOptions_DELETE_BasicQueryString(t *testing.T) {
	queryString := "query=query RebelsShipsQuery { rebels { name } }"
	expected := &requestOptions{
		Query: "query RebelsShipsQuery { rebels { name } }",
	}

	req, _ := http.NewRequest("DELETE", fmt.Sprintf("/graphql?%v", queryString), nil)
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}
func TestRequestOptions_DELETE_ContentTypeApplicationGraphQL(t *testing.T) {
	body := []byte(`query RebelsShipsQuery { rebels { name } }`)
	expected := &requestOptions{}

	req, _ := http.NewRequest("DELETE", "/graphql", bytes.NewBuffer(body))
	req.Header.Add("Content-Type", "application/graphql")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}
func TestRequestOptions_DELETE_ContentTypeApplicationJSON(t *testing.T) {
	body := `
	{
		"query": "query RebelsShipsQuery { rebels { name } }"
	}`
	expected := &requestOptions{}

	req, _ := http.NewRequest("DELETE", "/graphql", bytes.NewBufferString(body))
	req.Header.Add("Content-Type", "application/json")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}
func TestRequestOptions_DELETE_ContentTypeApplicationUrlEncoded(t *testing.T) {
	data := url.Values{}
	data.Add("query", "query RebelsShipsQuery { rebels { name } }")

	expected := &requestOptions{}

	req, _ := http.NewRequest("DELETE", "/graphql", bytes.NewBufferString(data.Encode()))
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}

func TestRequestOptions_POST_UnsupportedContentType(t *testing.T) {
	body := `<xml>query{}</xml>`
	expected := &requestOptions{}

	req, _ := http.NewRequest("POST", "/graphql", bytes.NewBufferString(body))
	req.Header.Add("Content-Type", "application/xml")
	result := getRequestOptions(req)

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("wrong result, graphql result diff: %v", testutil.Diff(expected, result))
	}
}
<|endoftext|>


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

package fusetesting

import (
	"fmt"
	"os"
	"reflect"
	"time"

	"github.com/jacobsa/oglematchers"
)

// Match os.FileInfo values that specify an mtime equal to the given time. On
// platforms where the Sys() method returns a struct containing an mtime, check
// also that it matches.
func MtimeIs(expected time.Time) oglematchers.Matcher {
	return oglematchers.NewMatcher(
		func(c interface{}) error { return mtimeIs(c, expected) },
		fmt.Sprintf("mtime is %v", expected))
}

func mtimeIs(c interface{}, expected time.Time) error {
	fi, ok := c.(os.FileInfo)
	if !ok {
		return fmt.Errorf("which is of type %v", reflect.TypeOf(c))
	}

	// Check ModTime().
	if fi.ModTime() != expected {
		d := fi.ModTime().Sub(expected)
		return fmt.Errorf("which has mtime %v, off by %v", fi.ModTime(), d)
	}

	// Check Sys().
	if sysMtime, ok := extractMtime(fi.Sys()); ok {
		if sysMtime != expected {
			d := sysMtime.Sub(expected)
			return fmt.Errorf("which has Sys() mtime %v, off by %v", sysMtime, d)
		}
	}

	return nil
}

// Match os.FileInfo values that specify a file birth time equal to the given
// time. On platforms where there is no birth time available, match all
// os.FileInfo values.
func BirthtimeIs(expected time.Time) oglematchers.Matcher
<commit_msg>Fixed a build error.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package fusetesting

import (
	"fmt"
	"os"
	"reflect"
	"time"

	"github.com/jacobsa/oglematchers"
)

// Match os.FileInfo values that specify an mtime equal to the given time. On
// platforms where the Sys() method returns a struct containing an mtime, check
// also that it matches.
func MtimeIs(expected time.Time) oglematchers.Matcher {
	return oglematchers.NewMatcher(
		func(c interface{}) error { return mtimeIs(c, expected) },
		fmt.Sprintf("mtime is %v", expected))
}

func mtimeIs(c interface{}, expected time.Time) error {
	fi, ok := c.(os.FileInfo)
	if !ok {
		return fmt.Errorf("which is of type %v", reflect.TypeOf(c))
	}

	// Check ModTime().
	if fi.ModTime() != expected {
		d := fi.ModTime().Sub(expected)
		return fmt.Errorf("which has mtime %v, off by %v", fi.ModTime(), d)
	}

	// Check Sys().
	if sysMtime, ok := extractMtime(fi.Sys()); ok {
		if sysMtime != expected {
			d := sysMtime.Sub(expected)
			return fmt.Errorf("which has Sys() mtime %v, off by %v", sysMtime, d)
		}
	}

	return nil
}

// Extract the mtime from the result of os.FileInfo.Sys(), in a
// platform-specific way. If not supported on this platform, return !ok.
//
// Defined in stat_darwin.go, etc.
func extractMtime(sys interface{}) (mtime time.Time, ok bool)

// Match os.FileInfo values that specify a file birth time equal to the given
// time. On platforms where there is no birth time available, match all
// os.FileInfo values.
func BirthtimeIs(expected time.Time) oglematchers.Matcher
<|endoftext|>


<commit_before>package morningStar

import (
	"github.com/ViBiOh/funds-ob/jsonHttp"
	"log"
	"net/http"
	"regexp"
	"sync"
	"time"
)

const refreshDelayInHours = 6
const maxConcurrentFetcher = 16

var requestList = regexp.MustCompile(`^/list$`)
var requestPerf = regexp.MustCompile(`^/(.+?)$`)

var ids [][]byte

type performance struct {
	ID            string    `json:"id"`
	Isin          string    `json:"isin"`
	Label         string    `json:"label"`
	Category      string    `json:"category"`
	Rating        string    `json:"rating"`
	OneMonth      float64   `json:"1m"`
	ThreeMonths   float64   `json:"3m"`
	SixMonths     float64   `json:"6m"`
	OneYear       float64   `json:"1y"`
	VolThreeYears float64   `json:"v3y"`
	Score         float64   `json:"score"`
	Update        time.Time `json:"ts"`
}

func (perf *performance) computeScore() {
	score := (0.25 * perf.OneMonth) + (0.3 * perf.ThreeMonths) + (0.25 * perf.SixMonths) + (0.2 * perf.OneYear) - (0.1 * perf.VolThreeYears)
	perf.Score = float64(int(score*100)) / 100
}

type results struct {
	Results interface{} `json:"results"`
}

var cacheRequests = make(chan *cacheRequest, maxConcurrentFetcher)

func init() {
	go cacheServer(cacheRequests)
	go func() {
		ids = fetchIds()
		refreshCache()
		c := time.Tick(refreshDelayInHours * time.Hour)
		for range c {
			refreshCache()
		}
	}()
}

func refreshCache() {
	log.Print(`Cache refresh - start`)
	defer log.Print(`Cache refresh - end`)

	loadCache(cacheRequests, retrievePerformances(ids))
}

func retrievePerformance(morningStarID []byte) (*performance, error) {
	perf := getCache(cacheRequests, cleanID(morningStarID))
	if perf != nil {
		return perf, nil
	}

	perf, err := fetchPerformance(morningStarID)
	if err != nil {
		return nil, err
	}

	pushCache(cacheRequests, perf)
	ids = append(ids, morningStarID)

	return perf, nil
}

func concurrentRetrievePerformances(ids [][]byte, wg *sync.WaitGroup, performances chan<- *performance) {
	tokens := make(chan int, maxConcurrentFetcher)

	clearSemaphores := func() {
		wg.Done()
		<-tokens
	}

	for _, id := range ids {
		tokens <- 1

		go func(morningStarID []byte) {
			defer clearSemaphores()
			if perf, err := fetchPerformance(morningStarID); err == nil {
				performances <- perf
			}
		}(id)
	}
}

func retrievePerformances(ids [][]byte) []*performance {
	var wg sync.WaitGroup
	wg.Add(len(ids))

	performances := make(chan *performance, maxConcurrentFetcher)
	go concurrentRetrievePerformances(ids, &wg, performances)

	go func() {
		wg.Wait()
		close(performances)
	}()

	results := make([]*performance, 0, len(ids))
	for perf := range performances {
		results = append(results, perf)
	}

	return results
}

func performanceHandler(w http.ResponseWriter, morningStarID []byte) {
	perf, err := retrievePerformance(morningStarID)

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	} else {
		jsonHttp.ResponseJSON(w, *perf)
	}
}

func listHandler(w http.ResponseWriter, r *http.Request) {
	perfs := make([]*performance, 0, len(ids))
	for perf := range listCache(cacheRequests) {
		perfs = append(perfs, perf)
	}

	jsonHttp.ResponseJSON(w, results{perfs})
}

// Handler for MorningStar request. Should be use with net/http
type Handler struct {
}

func (handler Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Header().Add(`Access-Control-Allow-Origin`, `*`)
	w.Header().Add(`Access-Control-Allow-Headers`, `Content-Type`)
	w.Header().Add(`Access-Control-Allow-Methods`, `GET`)
	w.Header().Add(`X-Content-Type-Options`, `nosniff`)

	urlPath := []byte(r.URL.Path)

	if requestList.Match(urlPath) {
		listHandler(w, r)
	} else if requestPerf.Match(urlPath) {
		performanceHandler(w, requestPerf.FindSubmatch(urlPath)[1])
	}
}
<commit_msg>Update morningStar.go<commit_after>package morningStar

import (
	"github.com/ViBiOh/funds-ob/jsonHttp"
	"log"
	"net/http"
	"regexp"
	"sync"
	"time"
)

const refreshDelayInHours = 6
const maxConcurrentFetcher = 16

var requestList = regexp.MustCompile(`^/list$`)
var requestPerf = regexp.MustCompile(`^/(.+?)$`)

var ids [][]byte

type performance struct {
	ID            string    `json:"id"`
	Isin          string    `json:"isin"`
	Label         string    `json:"label"`
	Category      string    `json:"category"`
	Rating        string    `json:"rating"`
	OneMonth      float64   `json:"1m"`
	ThreeMonths   float64   `json:"3m"`
	SixMonths     float64   `json:"6m"`
	OneYear       float64   `json:"1y"`
	VolThreeYears float64   `json:"v3y"`
	Score         float64   `json:"score"`
	Update        time.Time `json:"ts"`
}

func (perf *performance) computeScore() {
	score := (0.25 * perf.OneMonth) + (0.3 * perf.ThreeMonths) + (0.25 * perf.SixMonths) + (0.2 * perf.OneYear) - (0.1 * perf.VolThreeYears)
	perf.Score = float64(int(score*100)) / 100
}

type results struct {
	Results interface{} `json:"results"`
}

var cacheRequests = make(chan *cacheRequest, maxConcurrentFetcher)

func init() {
	go cacheServer(cacheRequests)
	go func() {
		ids = fetchIds()
		refreshCache()
		c := time.Tick(refreshDelayInHours * time.Hour)
		for range c {
			refreshCache()
		}
	}()
}

func refreshCache() {
	log.Print(`Cache refresh - start`)
	defer log.Print(`Cache refresh - end`)

	loadCache(cacheRequests, retrievePerformances(ids))
}

func retrievePerformance(morningStarID []byte) (*performance, error) {
	perf := getCache(cacheRequests, cleanID(morningStarID))
	if perf != nil {
		return perf, nil
	}

	perf, err := fetchPerformance(morningStarID)
	if err != nil {
		return nil, err
	}

	pushCache(cacheRequests, perf)
	ids = append(ids, morningStarID)

	return perf, nil
}

func concurrentRetrievePerformances(ids [][]byte, wg *sync.WaitGroup, performances chan<- *performance) {
	tokens := make(chan int, maxConcurrentFetcher)

	clearSemaphores := func() {
		wg.Done()
		<-tokens
	}

	for _, id := range ids {
		tokens <- 1

		go func(morningStarID []byte) {
			defer clearSemaphores()
			if perf, err := fetchPerformance(morningStarID); err == nil {
				performances <- perf
			}
		}(id)
	}
}

func retrievePerformances(ids [][]byte) []*performance {
	var wg sync.WaitGroup
	wg.Add(len(ids))

	performances := make(chan *performance, maxConcurrentFetcher)
	go concurrentRetrievePerformances(ids, &wg, performances)

	go func() {
		wg.Wait()
		close(performances)
	}()

	results := make([]*performance, 0, len(ids))
	for perf := range performances {
		results = append(results, perf)
	}

	return results
}

func performanceHandler(w http.ResponseWriter, morningStarID []byte) {
	perf, err := retrievePerformance(morningStarID)

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	} else {
		jsonHttp.ResponseJSON(w, *perf)
	}
}

func listHandler(w http.ResponseWriter, r *http.Request) {
	perfs := make([]*performance, 0, len(ids))
	for perf := range listCache(cacheRequests) {
		perfs = append(perfs, perf)
	}

	jsonHttp.ResponseJSON(w, results{perfs})
}

// Handler for MorningStar request. Should be use with net/http
type Handler struct {
}

func (handler Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Header().Add(`Access-Control-Allow-Origin`, `*`)
	w.Header().Add(`Access-Control-Allow-Headers`, `Content-Type`)
	w.Header().Add(`Access-Control-Allow-Methods`, `GET`)
	w.Header().Add(`X-Content-Type-Options`, `nosniff`)

	if r.Method == http.MethodOptions {
		w.Write(nil)
		return
	}

	urlPath := []byte(r.URL.Path)

	if requestList.Match(urlPath) {
		listHandler(w, r)
	} else if requestPerf.Match(urlPath) {
		performanceHandler(w, requestPerf.FindSubmatch(urlPath)[1])
	}
}
<|endoftext|>


<commit_before>// Copyright 2022 The LUCI 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 ui

import (
	"fmt"
	"strconv"
	"strings"

	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"

	"go.chromium.org/luci/server/router"
	"go.chromium.org/luci/server/templates"

	"go.chromium.org/luci/deploy/api/modelpb"
	"go.chromium.org/luci/deploy/api/rpcpb"
	"go.chromium.org/luci/deploy/service/model"
)

// actuationOutcome is an outcome of an actuation of a single asset.
type actuationOutcome string

const (
	outcomeUnknown   actuationOutcome = "UNKNOWN"   // should be unreachable
	outcomeUnchanged actuationOutcome = "UNCHANGED" // matches intent
	outcomeDisabled  actuationOutcome = "DISABLED"  // disabled in the config
	outcomeLocked    actuationOutcome = "LOCKED"    // has outstanding locks
	outcomeBroken    actuationOutcome = "BROKEN"    // broken configuration
	outcomeUpdating  actuationOutcome = "UPDATING"  // running right now
	outcomeUpdated   actuationOutcome = "UPDATED"   // applied changes
	outcomeFailed    actuationOutcome = "FAILED"    // failed to apply changes
)

func deriveOutcome(r *modelpb.AssetHistory) actuationOutcome {
	switch r.Decision.GetDecision() {
	case modelpb.ActuationDecision_SKIP_UPTODATE:
		return outcomeUnchanged

	case modelpb.ActuationDecision_SKIP_DISABLED:
		return outcomeDisabled

	case modelpb.ActuationDecision_SKIP_LOCKED:
		return outcomeLocked

	case modelpb.ActuationDecision_SKIP_BROKEN:
		return outcomeBroken

	case modelpb.ActuationDecision_ACTUATE_FORCE, modelpb.ActuationDecision_ACTUATE_STALE:
		switch r.Actuation.GetState() {
		case modelpb.Actuation_EXECUTING:
			return outcomeUpdating
		case modelpb.Actuation_SUCCEEDED:
			return outcomeUpdated
		case modelpb.Actuation_FAILED, modelpb.Actuation_EXPIRED:
			return outcomeFailed
		default:
			return outcomeUnknown // this should not be possible
		}

	default:
		return outcomeUnknown // this should not be possible
	}
}

// tableClass is the corresponding Bootstrap CSS table class.
func (o actuationOutcome) tableClass() string {
	switch o {
	case outcomeUnchanged, outcomeUpdated:
		return "" // default transparent background
	case outcomeDisabled:
		return "table-secondary"
	case outcomeLocked:
		return "table-warning"
	case outcomeUnknown, outcomeBroken, outcomeFailed:
		return "table-danger"
	case outcomeUpdating:
		return "table-info"
	default:
		panic("impossible")
	}
}

// badgeClass is the corresponding Bootstrap CSS badge class.
func (o actuationOutcome) badgeClass() string {
	switch o {
	case outcomeUnchanged, outcomeDisabled:
		return "bg-secondary"
	case outcomeLocked:
		return "bg-warning text-dark"
	case outcomeUnknown, outcomeBroken, outcomeFailed:
		return "bg-danger"
	case outcomeUpdating:
		return "bg-info text-dark"
	case outcomeUpdated:
		return "bg-success"
	default:
		panic("impossible")
	}
}

type commitDetails struct {
	Subject       linkHref // commit subject linking to gitiles
	Rev           string   // full commit revision
	AuthorEmail   string   // author email address
	CommitMessage string   // full commit message
}

func getCommitDetails(dep *modelpb.Deployment) commitDetails {
	message := strings.TrimSpace(dep.GetLatestCommit().GetCommitMessage())
	lines := strings.SplitN(message, "\n", 2)

	subject := commitHref(dep)
	if len(lines) == 0 {
		subject.Text = "-"
	} else {
		subject.Text = lines[0]
	}

	return commitDetails{
		Subject:       subject,
		Rev:           dep.GetConfigRev(),
		AuthorEmail:   dep.GetLatestCommit().GetAuthorEmail(),
		CommitMessage: message,
	}
}

type historyOverview struct {
	ID         linkHref         // a link to the dedicate history entry page
	Age        linkHref         // when it started
	Commit     commitDetails    // commit subject, commit message, etc.
	Outcome    actuationOutcome // summary of what happened
	TableClass string           // CSS class for the table row
	BadgeClass string           // CSS class for the state cell
	Actuation  linkHref         // link to the actuator invocation (e.g. build)
	Log        linkHref         // link to the concrete actuation log
}

func deriveHistoryOverview(asset *modelpb.Asset, rec *modelpb.AssetHistory) *historyOverview {
	out := &historyOverview{
		ID: linkHref{
			Text: fmt.Sprintf("#%d", rec.HistoryId),
			Href: fmt.Sprintf("/a/%s/history/%d", asset.Id, rec.HistoryId),
		},
		Age:     timestampHref(rec.Actuation.Created, "", ""),
		Commit:  getCommitDetails(rec.Actuation.Deployment),
		Outcome: deriveOutcome(rec),
		Actuation: linkHref{
			Text:   "link",
			Href:   buildbucketHref(rec.Actuation.Actuator),
			Target: "_blank",
		},
	}
	out.TableClass = out.Outcome.tableClass()
	out.BadgeClass = out.Outcome.badgeClass()

	if model.IsActuateDecision(rec.Decision.GetDecision()) {
		out.Log = linkHref{
			Text:   "link",
			Href:   rec.Actuation.LogUrl,
			Target: "_blank",
		}
	}

	return out
}

func parseHistoryID(historyID string) (int64, error) {
	id, err := strconv.ParseInt(historyID, 10, 64)
	if err != nil {
		return 0, status.Errorf(codes.InvalidArgument, "Bad history ID %q: %s", historyID, err)
	}
	if id <= 0 {
		return 0, status.Errorf(codes.InvalidArgument, "Bad history ID %q: must be non-negative", historyID)
	}
	return id, nil
}

// historyListingPage renders the history listing page.
func (ui *UI) historyListingPage(ctx *router.Context, assetID string) error {
	const pageSize = 50

	latest := int64(0)
	if latestVal := ctx.Request.FormValue("latest"); latestVal != "" {
		var err error
		if latest, err = parseHistoryID(latestVal); err != nil {
			return err
		}
	}

	assetHistory, err := ui.assets.ListAssetHistory(ctx.Context, &rpcpb.ListAssetHistoryRequest{
		AssetId:         assetID,
		LatestHistoryId: latest,
		Limit:           pageSize,
	})
	if err != nil {
		return err
	}

	history := make([]*historyOverview, len(assetHistory.History))
	for i, rec := range assetHistory.History {
		history[i] = deriveHistoryOverview(assetHistory.Asset, rec)
	}

	ref := assetRefFromID(assetHistory.Asset.Id)

	newerHref := ""
	if latest != 0 && assetHistory.LastRecordedHistoryId > latest {
		newer := latest + pageSize
		if newer >= assetHistory.LastRecordedHistoryId {
			newerHref = fmt.Sprintf("/a/%s/history", assetID)
		} else {
			newerHref = fmt.Sprintf("/a/%s/history?latest=%d", assetID, newer)
		}
	}

	olderHref := ""
	if len(assetHistory.History) == pageSize {
		older := assetHistory.History[len(assetHistory.History)-1].HistoryId - 1
		if older != 0 {
			olderHref = fmt.Sprintf("/a/%s/history?latest=%d", assetID, older)
		}
	}

	templates.MustRender(ctx.Context, ctx.Writer, "pages/history-listing.html", map[string]interface{}{
		"Breadcrumbs": historyListingBreadcrumbs(ref),
		"Ref":         ref,
		"Overview":    deriveAssetOverview(assetHistory.Asset),
		"History":     history,
		"NewerHref":   newerHref,
		"OlderHref":   olderHref,
	})
	return nil
}

// historyEntryPage renders a page with a single actuation history entry.
func (ui *UI) historyEntryPage(ctx *router.Context, assetID, historyID string) error {
	entryID, err := parseHistoryID(historyID)
	if err != nil {
		return err
	}

	assetHistory, err := ui.assets.ListAssetHistory(ctx.Context, &rpcpb.ListAssetHistoryRequest{
		AssetId:         assetID,
		LatestHistoryId: entryID,
		Limit:           2, // to see if we have the previous one for the pager
	})
	if err != nil {
		return err
	}

	// We allow to ask for the current unfinished actuation. That way it is
	// possible to send a permanent HTTP link to the currently executing actuation
	// in notifications. It will "transform" into the historical link once the
	// actuation finishes.
	var entry *modelpb.AssetHistory
	switch {
	case assetHistory.Current != nil && assetHistory.Current.HistoryId == entryID:
		entry = assetHistory.Current
	case len(assetHistory.History) > 0 && assetHistory.History[0].HistoryId == entryID:
		entry = assetHistory.History[0]
	default:
		return status.Errorf(codes.NotFound, "Actuation #%d doesn't exist: either it hasn't started yet or it was already deleted.", entryID)
	}

	ref := assetRefFromID(assetID)
	overview := deriveHistoryOverview(assetHistory.Asset, entry)

	newerHref := ""
	if assetHistory.LastRecordedHistoryId > entryID {
		newerHref = fmt.Sprintf("/a/%s/history/%d", assetID, entryID+1)
	}

	olderHref := ""
	if len(assetHistory.History) == 2 {
		olderHref = fmt.Sprintf("/a/%s/history/%d", assetID, entryID-1)
	}

	// TODO: Add GAE-specific details.

	templates.MustRender(ctx.Context, ctx.Writer, "pages/history-entry.html", map[string]interface{}{
		"Breadcrumbs": historyEntryBreadcrumbs(ref, entryID),
		"Ref":         ref,
		"Overview":    overview,
		"NewerHref":   newerHref,
		"OlderHref":   olderHref,
	})
	return nil
}
<commit_msg>[deploy] List more history on the history page.<commit_after>// Copyright 2022 The LUCI 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 ui

import (
	"fmt"
	"strconv"
	"strings"

	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"

	"go.chromium.org/luci/server/router"
	"go.chromium.org/luci/server/templates"

	"go.chromium.org/luci/deploy/api/modelpb"
	"go.chromium.org/luci/deploy/api/rpcpb"
	"go.chromium.org/luci/deploy/service/model"
)

// actuationOutcome is an outcome of an actuation of a single asset.
type actuationOutcome string

const (
	outcomeUnknown   actuationOutcome = "UNKNOWN"   // should be unreachable
	outcomeUnchanged actuationOutcome = "UNCHANGED" // matches intent
	outcomeDisabled  actuationOutcome = "DISABLED"  // disabled in the config
	outcomeLocked    actuationOutcome = "LOCKED"    // has outstanding locks
	outcomeBroken    actuationOutcome = "BROKEN"    // broken configuration
	outcomeUpdating  actuationOutcome = "UPDATING"  // running right now
	outcomeUpdated   actuationOutcome = "UPDATED"   // applied changes
	outcomeFailed    actuationOutcome = "FAILED"    // failed to apply changes
)

func deriveOutcome(r *modelpb.AssetHistory) actuationOutcome {
	switch r.Decision.GetDecision() {
	case modelpb.ActuationDecision_SKIP_UPTODATE:
		return outcomeUnchanged

	case modelpb.ActuationDecision_SKIP_DISABLED:
		return outcomeDisabled

	case modelpb.ActuationDecision_SKIP_LOCKED:
		return outcomeLocked

	case modelpb.ActuationDecision_SKIP_BROKEN:
		return outcomeBroken

	case modelpb.ActuationDecision_ACTUATE_FORCE, modelpb.ActuationDecision_ACTUATE_STALE:
		switch r.Actuation.GetState() {
		case modelpb.Actuation_EXECUTING:
			return outcomeUpdating
		case modelpb.Actuation_SUCCEEDED:
			return outcomeUpdated
		case modelpb.Actuation_FAILED, modelpb.Actuation_EXPIRED:
			return outcomeFailed
		default:
			return outcomeUnknown // this should not be possible
		}

	default:
		return outcomeUnknown // this should not be possible
	}
}

// tableClass is the corresponding Bootstrap CSS table class.
func (o actuationOutcome) tableClass() string {
	switch o {
	case outcomeUnchanged, outcomeUpdated:
		return "" // default transparent background
	case outcomeDisabled:
		return "table-secondary"
	case outcomeLocked:
		return "table-warning"
	case outcomeUnknown, outcomeBroken, outcomeFailed:
		return "table-danger"
	case outcomeUpdating:
		return "table-info"
	default:
		panic("impossible")
	}
}

// badgeClass is the corresponding Bootstrap CSS badge class.
func (o actuationOutcome) badgeClass() string {
	switch o {
	case outcomeUnchanged, outcomeDisabled:
		return "bg-secondary"
	case outcomeLocked:
		return "bg-warning text-dark"
	case outcomeUnknown, outcomeBroken, outcomeFailed:
		return "bg-danger"
	case outcomeUpdating:
		return "bg-info text-dark"
	case outcomeUpdated:
		return "bg-success"
	default:
		panic("impossible")
	}
}

type commitDetails struct {
	Subject       linkHref // commit subject linking to gitiles
	Rev           string   // full commit revision
	AuthorEmail   string   // author email address
	CommitMessage string   // full commit message
}

func getCommitDetails(dep *modelpb.Deployment) commitDetails {
	message := strings.TrimSpace(dep.GetLatestCommit().GetCommitMessage())
	lines := strings.SplitN(message, "\n", 2)

	subject := commitHref(dep)
	if len(lines) == 0 {
		subject.Text = "-"
	} else {
		subject.Text = lines[0]
	}

	return commitDetails{
		Subject:       subject,
		Rev:           dep.GetConfigRev(),
		AuthorEmail:   dep.GetLatestCommit().GetAuthorEmail(),
		CommitMessage: message,
	}
}

type historyOverview struct {
	ID         linkHref         // a link to the dedicate history entry page
	Age        linkHref         // when it started
	Commit     commitDetails    // commit subject, commit message, etc.
	Outcome    actuationOutcome // summary of what happened
	TableClass string           // CSS class for the table row
	BadgeClass string           // CSS class for the state cell
	Actuation  linkHref         // link to the actuator invocation (e.g. build)
	Log        linkHref         // link to the concrete actuation log
}

func deriveHistoryOverview(asset *modelpb.Asset, rec *modelpb.AssetHistory) *historyOverview {
	out := &historyOverview{
		ID: linkHref{
			Text: fmt.Sprintf("#%d", rec.HistoryId),
			Href: fmt.Sprintf("/a/%s/history/%d", asset.Id, rec.HistoryId),
		},
		Age:     timestampHref(rec.Actuation.Created, "", ""),
		Commit:  getCommitDetails(rec.Actuation.Deployment),
		Outcome: deriveOutcome(rec),
		Actuation: linkHref{
			Text:   "link",
			Href:   buildbucketHref(rec.Actuation.Actuator),
			Target: "_blank",
		},
	}
	out.TableClass = out.Outcome.tableClass()
	out.BadgeClass = out.Outcome.badgeClass()

	if model.IsActuateDecision(rec.Decision.GetDecision()) {
		out.Log = linkHref{
			Text:   "link",
			Href:   rec.Actuation.LogUrl,
			Target: "_blank",
		}
	}

	return out
}

func parseHistoryID(historyID string) (int64, error) {
	id, err := strconv.ParseInt(historyID, 10, 64)
	if err != nil {
		return 0, status.Errorf(codes.InvalidArgument, "Bad history ID %q: %s", historyID, err)
	}
	if id <= 0 {
		return 0, status.Errorf(codes.InvalidArgument, "Bad history ID %q: must be non-negative", historyID)
	}
	return id, nil
}

// historyListingPage renders the history listing page.
func (ui *UI) historyListingPage(ctx *router.Context, assetID string) error {
	const pageSize = 200

	latest := int64(0)
	if latestVal := ctx.Request.FormValue("latest"); latestVal != "" {
		var err error
		if latest, err = parseHistoryID(latestVal); err != nil {
			return err
		}
	}

	assetHistory, err := ui.assets.ListAssetHistory(ctx.Context, &rpcpb.ListAssetHistoryRequest{
		AssetId:         assetID,
		LatestHistoryId: latest,
		Limit:           pageSize,
	})
	if err != nil {
		return err
	}

	history := make([]*historyOverview, len(assetHistory.History))
	for i, rec := range assetHistory.History {
		history[i] = deriveHistoryOverview(assetHistory.Asset, rec)
	}

	ref := assetRefFromID(assetHistory.Asset.Id)

	newerHref := ""
	if latest != 0 && assetHistory.LastRecordedHistoryId > latest {
		newer := latest + pageSize
		if newer >= assetHistory.LastRecordedHistoryId {
			newerHref = fmt.Sprintf("/a/%s/history", assetID)
		} else {
			newerHref = fmt.Sprintf("/a/%s/history?latest=%d", assetID, newer)
		}
	}

	olderHref := ""
	if len(assetHistory.History) == pageSize {
		older := assetHistory.History[len(assetHistory.History)-1].HistoryId - 1
		if older != 0 {
			olderHref = fmt.Sprintf("/a/%s/history?latest=%d", assetID, older)
		}
	}

	templates.MustRender(ctx.Context, ctx.Writer, "pages/history-listing.html", map[string]interface{}{
		"Breadcrumbs": historyListingBreadcrumbs(ref),
		"Ref":         ref,
		"Overview":    deriveAssetOverview(assetHistory.Asset),
		"History":     history,
		"NewerHref":   newerHref,
		"OlderHref":   olderHref,
	})
	return nil
}

// historyEntryPage renders a page with a single actuation history entry.
func (ui *UI) historyEntryPage(ctx *router.Context, assetID, historyID string) error {
	entryID, err := parseHistoryID(historyID)
	if err != nil {
		return err
	}

	assetHistory, err := ui.assets.ListAssetHistory(ctx.Context, &rpcpb.ListAssetHistoryRequest{
		AssetId:         assetID,
		LatestHistoryId: entryID,
		Limit:           2, // to see if we have the previous one for the pager
	})
	if err != nil {
		return err
	}

	// We allow to ask for the current unfinished actuation. That way it is
	// possible to send a permanent HTTP link to the currently executing actuation
	// in notifications. It will "transform" into the historical link once the
	// actuation finishes.
	var entry *modelpb.AssetHistory
	switch {
	case assetHistory.Current != nil && assetHistory.Current.HistoryId == entryID:
		entry = assetHistory.Current
	case len(assetHistory.History) > 0 && assetHistory.History[0].HistoryId == entryID:
		entry = assetHistory.History[0]
	default:
		return status.Errorf(codes.NotFound, "Actuation #%d doesn't exist: either it hasn't started yet or it was already deleted.", entryID)
	}

	ref := assetRefFromID(assetID)
	overview := deriveHistoryOverview(assetHistory.Asset, entry)

	newerHref := ""
	if assetHistory.LastRecordedHistoryId > entryID {
		newerHref = fmt.Sprintf("/a/%s/history/%d", assetID, entryID+1)
	}

	olderHref := ""
	if len(assetHistory.History) == 2 {
		olderHref = fmt.Sprintf("/a/%s/history/%d", assetID, entryID-1)
	}

	// TODO: Add GAE-specific details.

	templates.MustRender(ctx.Context, ctx.Writer, "pages/history-entry.html", map[string]interface{}{
		"Breadcrumbs": historyEntryBreadcrumbs(ref, entryID),
		"Ref":         ref,
		"Overview":    overview,
		"NewerHref":   newerHref,
		"OlderHref":   olderHref,
	})
	return nil
}
<|endoftext|>


<commit_before>package generate

import (
	"fmt"
	"io"
	"regexp"

	"github.com/aaasen/kapok/parse"
)

type CSVGenerator struct {
	ids *IDGenerator
}

func NewCSVGenerator() *CSVGenerator {
	return &CSVGenerator{
		NewIDGenerator(),
	}
}

type IDGenerator struct {
	currentId int64
	labels    map[string]int64
}

func NewIDGenerator() *IDGenerator {
	return &IDGenerator{
		0,
		make(map[string]int64),
	}
}

func (self *IDGenerator) GetID(title string, category bool) (int64, bool) {
	if category {
		title = "c4ef35:" + title
	}

	id, exists := self.labels[title]

	if exists {
		return id, false
	}

	self.labels[title] = self.currentId
	self.currentId++

	return self.currentId - 1, true
}

func (self *CSVGenerator) GeneratePage(page *parse.Page,
	nodes io.Writer, rels io.Writer) {

	originId, created := self.ids.GetID(page.Title, false)

	if created {
		writeNode(nodes, originId, page.Title, "Article")
	}

	for _, link := range page.Links {
		linkId, created := self.ids.GetID(link, false)

		if created {
			writeNode(nodes, linkId, link, "Article")
		}

		writeRel(rels, originId, linkId, "REFERS_TO")
	}

	for _, category := range page.Categories {
		catId, created := self.ids.GetID(category, true)

		if created {
			writeNode(nodes, catId, category, "Category")
		}

		writeRel(rels, originId, catId, "IN_CATEGORY")
	}
}

var invalidCharacterRegex = regexp.MustCompile(`[\t\n\"]`)

func writeNode(out io.Writer, id int64, title string, label string) {
	title = invalidCharacterRegex.ReplaceAllString(title, "")

	out.Write([]byte(fmt.Sprintf("%d\t%s\t%s\n", id, title, label)))
}

func writeRel(out io.Writer, origin int64, dest int64, rel string) {
	out.Write([]byte(fmt.Sprintf("%d\t%d\t%s\n", origin, dest, rel)))
}
<commit_msg>moves invalid character checking away from regexes. no more regexes used in parsing!<commit_after>package generate

import (
	"bytes"
	"fmt"
	"io"

	"github.com/aaasen/kapok/parse"
)

type CSVGenerator struct {
	ids *IDGenerator
}

func NewCSVGenerator() *CSVGenerator {
	return &CSVGenerator{
		NewIDGenerator(),
	}
}

type IDGenerator struct {
	currentId int64
	labels    map[string]int64
}

func NewIDGenerator() *IDGenerator {
	return &IDGenerator{
		0,
		make(map[string]int64),
	}
}

func (self *IDGenerator) GetID(title string, category bool) (int64, bool) {
	if category {
		title = "c4ef35:" + title
	}

	id, exists := self.labels[title]

	if exists {
		return id, false
	}

	self.labels[title] = self.currentId
	self.currentId++

	return self.currentId - 1, true
}

func (self *CSVGenerator) GeneratePage(page *parse.Page,
	nodes io.Writer, rels io.Writer) {

	originId, created := self.ids.GetID(page.Title, false)

	if created {
		writeNode(nodes, originId, page.Title, "Article")
	}

	for _, link := range page.Links {
		linkId, created := self.ids.GetID(link, false)

		if created {
			writeNode(nodes, linkId, link, "Article")
		}

		writeRel(rels, originId, linkId, "REFERS_TO")
	}

	for _, category := range page.Categories {
		catId, created := self.ids.GetID(category, true)

		if created {
			writeNode(nodes, catId, category, "Category")
		}

		writeRel(rels, originId, catId, "IN_CATEGORY")
	}
}

func writeNode(out io.Writer, id int64, title string, label string) {
	title = string(removeAllBytes([]byte(title), []byte(`[\t\n\"]`)))

	out.Write([]byte(fmt.Sprintf("%d\t%s\t%s\n", id, title, label)))
}

func writeRel(out io.Writer, origin int64, dest int64, rel string) {
	out.Write([]byte(fmt.Sprintf("%d\t%d\t%s\n", origin, dest, rel)))
}

func removeAllBytes(source []byte, targets []byte) []byte {
	clean := make([]byte, len(source))
	cleanBytes := 0

	for _, b := range source {
		if bytes.IndexByte(targets, b) == -1 {
			clean[cleanBytes] = b
		}
	}

	return clean[:cleanBytes+1]
}
<|endoftext|>


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

// Package attest abstracts TPM attestation operations.
package attest

import (
	"crypto"
	"errors"
	"fmt"

	"github.com/google/certificate-transparency-go/x509"
	"github.com/google/go-tpm/tpm2"
)

// TPMVersion is used to configure a preference in
// which TPM to use, if multiple are available.
type TPMVersion uint8

// TPM versions
const (
	TPMVersionAgnostic TPMVersion = iota
	TPMVersion12
	TPMVersion20
)

// TPMInterface indicates how the client communicates
// with the TPM.
type TPMInterface uint8

// TPM interfaces
const (
	TPMInterfaceDirect TPMInterface = iota
	TPMInterfaceKernelManaged
	TPMInterfaceDaemonManaged
)

// OpenConfig encapsulates settings passed to OpenTPM().
type OpenConfig struct {
	// TPMVersion indicates which TPM version the library should
	// attempt to use. If the specified version is not available,
	// ErrTPMNotAvailable is returned. Defaults to TPMVersionAgnostic.
	TPMVersion TPMVersion
}

// keyEncoding indicates how an exported TPM key is represented.
type keyEncoding uint8

func (e keyEncoding) String() string {
	switch e {
	case keyEncodingInvalid:
		return "invalid"
	case keyEncodingOSManaged:
		return "os-managed"
	case keyEncodingEncrypted:
		return "encrypted"
	case keyEncodingParameterized:
		return "parameterized"
	default:
		return fmt.Sprintf("keyEncoding<%d>", int(e))
	}
}

// Key encodings
const (
	keyEncodingInvalid keyEncoding = iota
	// Managed by the OS but loadable by name.
	keyEncodingOSManaged
	// Key fully represented but in encrypted form.
	keyEncodingEncrypted
	// Parameters stored, but key must be regenerated before use.
	keyEncodingParameterized
)

// KeyPurpose indicates the intended use of the key. It is implied that
// the key was created with usage restrictions to constrain its use
// to the given purpose.
type KeyPurpose uint8

// Key purposes.
const (
	AttestationKey KeyPurpose = iota
	StorageKey
)

type aik interface {
	Close(*TPM) error
	Marshal() ([]byte, error)
	ActivateCredential(tpm *TPM, in EncryptedCredential) ([]byte, error)
	Quote(t *TPM, nonce []byte, alg HashAlg) (*Quote, error)
	AttestationParameters() AttestationParameters
	Public() crypto.PublicKey
}

// AIK represents a key which can be used for attestation.
type AIK struct {
	aik aik
}

// Close unloads the AIK from the system.
func (k *AIK) Close(t *TPM) error {
	return k.aik.Close(t)
}

// Marshal encodes the AIK in a format that can be reloaded with tpm.LoadAIK().
// This method exists to allow consumers to store the key persistently and load
// it as a later time. Users SHOULD NOT attempt to interpret or extract values
// from this blob.
func (k *AIK) Marshal() ([]byte, error) {
	return k.aik.Marshal()
}

// ActivateCredential decrypts the specified credential using the key.
// This operation is synonymous with TPM2_ActivateCredential.
func (k *AIK) ActivateCredential(tpm *TPM, in EncryptedCredential) ([]byte, error) {
	return k.aik.ActivateCredential(tpm, in)
}

// Quote returns a quote over the platform state, signed by the AIK.
func (k *AIK) Quote(tpm *TPM, nonce []byte, alg HashAlg) (*Quote, error) {
	return k.aik.Quote(tpm, nonce, alg)
}

// Parameters returns information about the AIK, typically used to generate
// a credential activation challenge.
func (k *AIK) AttestationParameters() AttestationParameters {
	return k.aik.AttestationParameters()
}

// Public returns the public part of the AIK.
func (k *AIK) Public() crypto.PublicKey {
	return k.aik.Public()
}

// MintOptions encapsulates parameters for minting keys. This type is defined
// now (despite being empty) for future interface compatibility.
type MintOptions struct {
}

// EncryptedCredential represents encrypted parameters which must be activated
// against a key.
type EncryptedCredential struct {
	Credential []byte
	Secret     []byte
}

// Quote encapsulates the results of a Quote operation against the TPM,
// using an attestation key.
type Quote struct {
	Version   TPMVersion
	Quote     []byte
	Signature []byte
}

// PCR encapsulates the value of a PCR at a point in time.
type PCR struct {
	Index     int
	Digest    []byte
	DigestAlg crypto.Hash
}

// PlatformEK represents a burned-in Endorsement Key, and its
// corrresponding EKCert (where present).
type PlatformEK struct {
	Cert   *x509.Certificate
	Public crypto.PublicKey
}

// AttestationParameters describes information about a key which is necessary
// for verifying its properties remotely.
type AttestationParameters struct {
	// Public represents the public key in a TPM-version specific encoding.
	// For TPM 2.0 devices, this is encoded as a TPMT_PUBLIC structure.
	// For TPM 1.2 devices, this is a TPM_PUBKEY structure, as defined in
	// the TPM Part 2 Structures specification, available at
	// https://trustedcomputinggroup.org/wp-content/uploads/TPM-Main-Part-2-TPM-Structures_v1.2_rev116_01032011.pdf
	Public []byte

	// UseTCSDActivationFormat is set when tcsd (trousers daemon) is operating
	// as an intermediary between this library and the TPM. A value of true
	// indicates that activation challenges should use the TCSD-specific format.
	UseTCSDActivationFormat bool

	// Subsequent fields are only populated for AIKs generated on a TPM
	// implementing version 2.0 of the specification. The specific structures
	// referenced for each field are defined in the TPM Revision 2, Part 2 -
	// Structures specification, available here:
	// https://www.trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-2-Structures-01.38.pdf

	// CreateData represents the properties of a TPM 2.0 key. It is encoded
	// as a TPMS_CREATION_DATA structure.
	CreateData []byte
	// CreateAttestation represents an assertion as to the details of the key.
	// It is encoded as a TPMS_ATTEST structure.
	CreateAttestation []byte
	// CreateSignature represents a signature of the CreateAttestation structure.
	// It is encoded as a TPMT_SIGNATURE structure.
	CreateSignature []byte
}

// HashAlg identifies a hashing Algorithm.
type HashAlg uint8

// Valid hash algorithms.
var (
	HashSHA1   = HashAlg(tpm2.AlgSHA1)
	HashSHA256 = HashAlg(tpm2.AlgSHA256)
)

var (
	defaultOpenConfig = &OpenConfig{}

	// ErrTPMNotAvailable is returned in response to OpenTPM() when
	// either no TPM is available, or a TPM of the requested version
	// is not available (if TPMVersion was set in the provided config).
	ErrTPMNotAvailable = errors.New("TPM device not available")
	// ErrTPM12NotImplemented is returned in response to methods which
	// need to interact with the TPM1.2 device in ways that have not
	// yet been implemented.
	ErrTPM12NotImplemented = errors.New("TPM 1.2 support not yet implemented")
)

// TPMInfo contains information about the version & interface
// of an open TPM.
type TPMInfo struct {
	Version      TPMVersion
	Interface    TPMInterface
	VendorInfo   string
	Manufacturer TCGVendorID
}

// probedTPM identifies a TPM device on the system, which
// is a candidate for being used.
type probedTPM struct {
	Version TPMVersion
	Path    string
}

// MatchesConfig returns true if the TPM satisfies the constraints
// specified by the given config.
func (t *probedTPM) MatchesConfig(config OpenConfig) bool {
	return config.TPMVersion == TPMVersionAgnostic || t.Version == config.TPMVersion
}

// OpenTPM initializes access to the TPM based on the
// config provided.
func OpenTPM(config *OpenConfig) (*TPM, error) {
	if config == nil {
		config = defaultOpenConfig
	}
	candidateTPMs, err := probeSystemTPMs()
	if err != nil {
		return nil, err
	}

	for _, tpm := range candidateTPMs {
		if tpm.MatchesConfig(*config) {
			return openTPM(tpm)
		}
	}

	return nil, ErrTPMNotAvailable
}

// AvailableTPMs returns information about available TPMs matching
// the given config, without opening the devices.
func AvailableTPMs(config *OpenConfig) ([]TPMInfo, error) {
	if config == nil {
		config = defaultOpenConfig
	}

	candidateTPMs, err := probeSystemTPMs()
	if err != nil {
		return nil, err
	}

	var out []TPMInfo

	for _, tpm := range candidateTPMs {
		if tpm.MatchesConfig(*config) {
			t, err := openTPM(tpm)
			if err != nil {
				return nil, err
			}
			defer t.Close()
			i, err := t.Info()
			if err != nil {
				return nil, err
			}
			out = append(out, *i)
		}
	}

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

// Package attest abstracts TPM attestation operations.
package attest

import (
	"crypto"
	"errors"
	"fmt"

	"github.com/google/certificate-transparency-go/x509"
	"github.com/google/go-tpm/tpm2"
)

// TPMVersion is used to configure a preference in
// which TPM to use, if multiple are available.
type TPMVersion uint8

// TPM versions
const (
	TPMVersionAgnostic TPMVersion = iota
	TPMVersion12
	TPMVersion20
)

// TPMInterface indicates how the client communicates
// with the TPM.
type TPMInterface uint8

// TPM interfaces
const (
	TPMInterfaceDirect TPMInterface = iota
	TPMInterfaceKernelManaged
	TPMInterfaceDaemonManaged
)

// OpenConfig encapsulates settings passed to OpenTPM().
type OpenConfig struct {
	// TPMVersion indicates which TPM version the library should
	// attempt to use. If the specified version is not available,
	// ErrTPMNotAvailable is returned. Defaults to TPMVersionAgnostic.
	TPMVersion TPMVersion
}

// keyEncoding indicates how an exported TPM key is represented.
type keyEncoding uint8

func (e keyEncoding) String() string {
	switch e {
	case keyEncodingInvalid:
		return "invalid"
	case keyEncodingOSManaged:
		return "os-managed"
	case keyEncodingEncrypted:
		return "encrypted"
	case keyEncodingParameterized:
		return "parameterized"
	default:
		return fmt.Sprintf("keyEncoding<%d>", int(e))
	}
}

// Key encodings
const (
	keyEncodingInvalid keyEncoding = iota
	// Managed by the OS but loadable by name.
	keyEncodingOSManaged
	// Key fully represented but in encrypted form.
	keyEncodingEncrypted
	// Parameters stored, but key must be regenerated before use.
	keyEncodingParameterized
)

// KeyPurpose indicates the intended use of the key. It is implied that
// the key was created with usage restrictions to constrain its use
// to the given purpose.
type KeyPurpose uint8

// Key purposes.
const (
	AttestationKey KeyPurpose = iota
	StorageKey
)

type aik interface {
	Close(*TPM) error
	Marshal() ([]byte, error)
	ActivateCredential(tpm *TPM, in EncryptedCredential) ([]byte, error)
	Quote(t *TPM, nonce []byte, alg HashAlg) (*Quote, error)
	AttestationParameters() AttestationParameters
	Public() crypto.PublicKey
}

// AIK represents a key which can be used for attestation.
type AIK struct {
	aik aik
}

// Close unloads the AIK from the system.
func (k *AIK) Close(t *TPM) error {
	return k.aik.Close(t)
}

// Marshal encodes the AIK in a format that can be reloaded with tpm.LoadAIK().
// This method exists to allow consumers to store the key persistently and load
// it as a later time. Users SHOULD NOT attempt to interpret or extract values
// from this blob.
func (k *AIK) Marshal() ([]byte, error) {
	return k.aik.Marshal()
}

// ActivateCredential decrypts the secret using the key to prove that the AIK
// was generated on the same TPM as the EK.
//
// This operation is synonymous with TPM2_ActivateCredential.
func (k *AIK) ActivateCredential(tpm *TPM, in EncryptedCredential) (secret []byte, err error) {
	return k.aik.ActivateCredential(tpm, in)
}

// Quote returns a quote over the platform state, signed by the AIK.
func (k *AIK) Quote(tpm *TPM, nonce []byte, alg HashAlg) (*Quote, error) {
	return k.aik.Quote(tpm, nonce, alg)
}

// Parameters returns information about the AIK, typically used to generate
// a credential activation challenge.
func (k *AIK) AttestationParameters() AttestationParameters {
	return k.aik.AttestationParameters()
}

// Public returns the public part of the AIK.
func (k *AIK) Public() crypto.PublicKey {
	return k.aik.Public()
}

// MintOptions encapsulates parameters for minting keys. This type is defined
// now (despite being empty) for future interface compatibility.
type MintOptions struct {
}

// EncryptedCredential represents encrypted parameters which must be activated
// against a key.
type EncryptedCredential struct {
	Credential []byte
	Secret     []byte
}

// Quote encapsulates the results of a Quote operation against the TPM,
// using an attestation key.
type Quote struct {
	Version   TPMVersion
	Quote     []byte
	Signature []byte
}

// PCR encapsulates the value of a PCR at a point in time.
type PCR struct {
	Index     int
	Digest    []byte
	DigestAlg crypto.Hash
}

// PlatformEK represents a burned-in Endorsement Key, and its
// corrresponding EKCert (where present).
type PlatformEK struct {
	Cert   *x509.Certificate
	Public crypto.PublicKey
}

// AttestationParameters describes information about a key which is necessary
// for verifying its properties remotely.
type AttestationParameters struct {
	// Public represents the public key in a TPM-version specific encoding.
	// For TPM 2.0 devices, this is encoded as a TPMT_PUBLIC structure.
	// For TPM 1.2 devices, this is a TPM_PUBKEY structure, as defined in
	// the TPM Part 2 Structures specification, available at
	// https://trustedcomputinggroup.org/wp-content/uploads/TPM-Main-Part-2-TPM-Structures_v1.2_rev116_01032011.pdf
	Public []byte

	// UseTCSDActivationFormat is set when tcsd (trousers daemon) is operating
	// as an intermediary between this library and the TPM. A value of true
	// indicates that activation challenges should use the TCSD-specific format.
	UseTCSDActivationFormat bool

	// Subsequent fields are only populated for AIKs generated on a TPM
	// implementing version 2.0 of the specification. The specific structures
	// referenced for each field are defined in the TPM Revision 2, Part 2 -
	// Structures specification, available here:
	// https://www.trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-2-Structures-01.38.pdf

	// CreateData represents the properties of a TPM 2.0 key. It is encoded
	// as a TPMS_CREATION_DATA structure.
	CreateData []byte
	// CreateAttestation represents an assertion as to the details of the key.
	// It is encoded as a TPMS_ATTEST structure.
	CreateAttestation []byte
	// CreateSignature represents a signature of the CreateAttestation structure.
	// It is encoded as a TPMT_SIGNATURE structure.
	CreateSignature []byte
}

// HashAlg identifies a hashing Algorithm.
type HashAlg uint8

// Valid hash algorithms.
var (
	HashSHA1   = HashAlg(tpm2.AlgSHA1)
	HashSHA256 = HashAlg(tpm2.AlgSHA256)
)

var (
	defaultOpenConfig = &OpenConfig{}

	// ErrTPMNotAvailable is returned in response to OpenTPM() when
	// either no TPM is available, or a TPM of the requested version
	// is not available (if TPMVersion was set in the provided config).
	ErrTPMNotAvailable = errors.New("TPM device not available")
	// ErrTPM12NotImplemented is returned in response to methods which
	// need to interact with the TPM1.2 device in ways that have not
	// yet been implemented.
	ErrTPM12NotImplemented = errors.New("TPM 1.2 support not yet implemented")
)

// TPMInfo contains information about the version & interface
// of an open TPM.
type TPMInfo struct {
	Version      TPMVersion
	Interface    TPMInterface
	VendorInfo   string
	Manufacturer TCGVendorID
}

// probedTPM identifies a TPM device on the system, which
// is a candidate for being used.
type probedTPM struct {
	Version TPMVersion
	Path    string
}

// MatchesConfig returns true if the TPM satisfies the constraints
// specified by the given config.
func (t *probedTPM) MatchesConfig(config OpenConfig) bool {
	return config.TPMVersion == TPMVersionAgnostic || t.Version == config.TPMVersion
}

// OpenTPM initializes access to the TPM based on the
// config provided.
func OpenTPM(config *OpenConfig) (*TPM, error) {
	if config == nil {
		config = defaultOpenConfig
	}
	candidateTPMs, err := probeSystemTPMs()
	if err != nil {
		return nil, err
	}

	for _, tpm := range candidateTPMs {
		if tpm.MatchesConfig(*config) {
			return openTPM(tpm)
		}
	}

	return nil, ErrTPMNotAvailable
}

// AvailableTPMs returns information about available TPMs matching
// the given config, without opening the devices.
func AvailableTPMs(config *OpenConfig) ([]TPMInfo, error) {
	if config == nil {
		config = defaultOpenConfig
	}

	candidateTPMs, err := probeSystemTPMs()
	if err != nil {
		return nil, err
	}

	var out []TPMInfo

	for _, tpm := range candidateTPMs {
		if tpm.MatchesConfig(*config) {
			t, err := openTPM(tpm)
			if err != nil {
				return nil, err
			}
			defer t.Close()
			i, err := t.Info()
			if err != nil {
				return nil, err
			}
			out = append(out, *i)
		}
	}

	return out, nil
}
<|endoftext|>


<commit_before>package paxos

/**
 * Paxos Library
 *
 * @author: Anant Bhardwaj
 * @date: 03/23/2014
 */

//
// Paxos library, to be included in an application.
// Multiple applications will run, each including
// a Paxos peer.
//
// Manages a sequence of agreed-on values.
// The set of peers is fixed.
// Copes with network failures (partition, msg loss, &c).
// Does not store anything persistently, so cannot handle crash+restart.
//
// The application interface:
//
// px = paxos.Make(peers []string, me string)
// px.Start(seq int, v interface{}) -- start agreement on new instance
// px.Status(seq int) (decided bool, v interface{}) -- get info about an instance
// px.Done(seq int) -- ok to forget all instances <= seq
// px.Max() int -- highest instance seq known, or -1
// px.Min() int -- instances before this seq have been forgotten
//

import "net"
import "net/rpc"
import "log"
import "os"
import "syscall"
import "sync"
import "fmt"
import "math/rand"
import "math"
import "time"

type Paxo struct {
  n_p int64
  n_a int64
  v_a interface{}
  decided bool
}

type Paxos struct {
  mu sync.Mutex
  l net.Listener
  dead bool
  unreliable bool
  rpcCount int
  peers []string
  me int // index into peers[]

  // Your data here.
  majority int
  max_seq int
  done map[string]int
  store map[int]*Paxo
  paxos_lock sync.Mutex
}

const (
  OK = "OK"
  REJECT = "REJECT"
)

const PORT = ":9001"

type status string

type PrepareArgs struct {
  Me string
  N int64
  Seq int
  Done int
}

type PrepareReply struct {
  Status status
  N_A int64
  Value interface{}
  Done int
}

type AcceptArgs struct {
  Me string
  N_A int64
  Seq int
  Value interface{}
  Done int
}

type AcceptReply struct {
  Status status
  Done int
}

type DecidedArgs struct {
  Me string
  Seq int
  Value interface{}
  Done int
}

type DecidedReply struct {
  Status status
  Done int
}

func (px *Paxos) Prepare(args *PrepareArgs, reply *PrepareReply) error {
  px.mu.Lock()
  px.done[args.Me] = args.Done

  paxo, ok := px.store[args.Seq]
  if !ok {
    paxo = &Paxo{n_p: -1, n_a: -1, v_a: nil, decided: false}
    px.store[args.Seq] = paxo
  }
  px.mu.Unlock()

  if args.N > paxo.n_p {
    paxo.n_p = args.N
    reply.Status = OK
    reply.N_A = paxo.n_a
    reply.Value = paxo.v_a
  } else {
    reply.Status = REJECT
  }

  reply.Done = px.done[px.peers[px.me]]
  return nil
}

func (px *Paxos) Accept(args *AcceptArgs, reply *AcceptReply) error {
  px.mu.Lock()
  px.done[args.Me] = args.Done

  paxo, ok := px.store[args.Seq]
  if !ok {
    paxo = &Paxo{n_p: -1, n_a: -1, v_a: nil, decided: false}
    px.store[args.Seq] = paxo
  }
  px.mu.Unlock()

  if args.N_A >= paxo.n_p {
    paxo.n_p = args.N_A
    paxo.n_a = args.N_A
    paxo.v_a = args.Value
    reply.Status = OK
  } else {
    reply.Status = REJECT
  }

  reply.Done = px.done[px.peers[px.me]]
  return nil
}

func (px *Paxos) Decided(args *DecidedArgs, reply *DecidedReply) error {
  px.mu.Lock()
  px.done[args.Me] = args.Done

  paxo, ok := px.store[args.Seq]
  if !ok {
    paxo = &Paxo{n_p: -1, n_a: -1, v_a: nil, decided: false}
    px.store[args.Seq] = paxo
  }
  px.mu.Unlock()

  paxo.v_a = args.Value
  paxo.decided = true

  reply.Status = OK
  reply.Done = px.done[px.peers[px.me]]
  return nil
}

//
// call() sends an RPC to the rpcname handler on server srv
// with arguments args, waits for the reply, and leaves the
// reply in reply. the reply argument should be a pointer
// to a reply structure.
//
// the return value is true if the server responded, and false
// if call() was not able to contact the server. in particular,
// the replys contents are only valid if call() returned true.
//
// you should assume that call() will time out and return an
// error after a while if it does not get a reply from the server.
//
// please use call() to send all RPCs, in client.go and server.go.
// please do not change this function.
//
func call(srv string, name string, args interface{}, reply interface{}) bool {
  c, err := rpc.Dial("tcp", srv)
  if err != nil {
    err1 := err.(*net.OpError)
    if err1.Err != syscall.ENOENT && err1.Err != syscall.ECONNREFUSED {
      fmt.Printf("paxos Dial() failed: %v\n", err1)
    }
    return false
  }
  defer c.Close()

  err = c.Call(name, args, reply)
  if err == nil {
    return true
  }

  fmt.Println(err)
  return false
}

func (px *Paxos) generate_id() int64 {
  return time.Now().UnixNano()
}

func (px *Paxos) Propose(seq int, v interface{}) {
  // Your code here.
  px.mu.Lock()
  if seq > px.max_seq {
    px.max_seq = seq
  }

  paxo, ok := px.store[seq]
  if !ok {
    paxo = &Paxo{n_p: -1, n_a: -1, v_a: nil, decided: false}
    px.store[seq] = paxo
  }
  px.mu.Unlock()
  me := px.peers[px.me]
  t_wait := 50 * time.Millisecond
  for px.dead == false && paxo.decided == false {
    n := px.generate_id()
    highest_n_a := int64(-1)
    highest_v_a := v
    prepare_ok_count := 0
    accept_ok_count := 0

    for _, peer := range px.peers {
      //fmt.Printf ("Prepare: %v,%v -- %v\n", n, seq, v)
      prepare_args := PrepareArgs {Me:me, N: n, Seq: seq, Done: px.done[me]}
      var prepare_reply PrepareReply
      if peer != me {
        call(peer, "Paxos.Prepare", &prepare_args, &prepare_reply)
      } else {
        px.Prepare(&prepare_args, &prepare_reply)
      }
      if prepare_reply.Status == OK {
        prepare_ok_count += 1
        if prepare_reply.N_A > highest_n_a {
          highest_n_a = prepare_reply.N_A
          highest_v_a = prepare_reply.Value
        }
        px.done[peer] = prepare_reply.Done
      }
    }

    if prepare_ok_count < px.majority {
      time.Sleep(t_wait)
      continue
    }

    if highest_v_a == nil {
      highest_v_a = v
    }

    //fmt.Printf ("Accept: %v,%v -- %v\n", n, seq, highest_v_a)
    for _, peer := range px.peers {
      accept_args := AcceptArgs {Me:me, N_A: n, Seq: seq, Value: highest_v_a, Done: px.done[me]}
      var accept_reply AcceptReply
      if peer != me {
        call(peer, "Paxos.Accept", &accept_args, &accept_reply)
      } else {
        px.Accept(&accept_args, &accept_reply)
      }
      if accept_reply.Status == OK {
        accept_ok_count += 1
        px.done[peer] = accept_reply.Done
      }
    }

    if accept_ok_count < px.majority  {
      time.Sleep(t_wait)
      continue
    }

    //fmt.Printf ("Decide: %v,%v -- %v\n", n, seq, highest_v_a)
    for _, peer := range px.peers {
      decided_args := DecidedArgs {Me:me, Seq: seq, Value: highest_v_a, Done: px.done[me]}
      var decided_reply DecidedReply
      if peer != me {
        call(peer, "Paxos.Decided", &decided_args, &decided_reply)
      } else {
        px.Decided(&decided_args, &decided_reply)
      }
      px.done[peer] = decided_reply.Done
    }

  }
}

func (px *Paxos) ClearMemory(seq int) {
  for k, _ := range(px.store) {
    if k < seq {
      delete(px.store, k)
    }
  }
}

//
// the application wants paxos to start agreement on
// instance seq, with proposed value v.
// Start() returns right away; the application will
// call Status() to find out if/when agreement
// is reached.
//
func (px *Paxos) Start(seq int, v interface{}) {
  // Your code here.
  if seq >= px.Min() {
    go px.Propose(seq, v)
  }
}

//
// the application on this machine is done with
// all instances <= seq.
//
// see the comments for Min() for more explanation.
//
func (px *Paxos) Done(seq int) {
  // Your code here.
  px.mu.Lock()
  defer px.mu.Unlock()
  peer := px.peers[px.me]
  if px.done[peer] < seq {
    px.done[peer] = seq
  }
}

//
// the application wants to know the
// highest instance sequence known to
// this peer.
//
func (px *Paxos) Max() int {
  // Your code here.
  px.mu.Lock()
  defer px.mu.Unlock()
  return px.max_seq
}

//
// Min() should return one more than the minimum among z_i,
// where z_i is the highest number ever passed
// to Done() on peer i. A peers z_i is -1 if it has
// never called Done().
//
// Paxos is required to have forgotten all information
// about any instances it knows that are < Min().
// The point is to free up memory in long-running
// Paxos-based servers.
//
// Paxos peers need to exchange their highest Done()
// arguments in order to implement Min(). These
// exchanges can be piggybacked on ordinary Paxos
// agreement protocol messages, so it is OK if one
// peers Min does not reflect another Peers Done()
// until after the next instance is agreed to.
//
// The fact that Min() is defined as a minimum over
// *all* Paxos peers means that Min() cannot increase until
// all peers have been heard from. So if a peer is dead
// or unreachable, other peers Min()s will not increase
// even if all reachable peers call Done. The reason for
// this is that when the unreachable peer comes back to
// life, it will need to catch up on instances that it
// missed -- the other peers therefor cannot forget these
// instances.
//
func (px *Paxos) Min() int {
  // You code here.
  px.mu.Lock()
  defer px.mu.Unlock()
  min := math.MaxInt32
  for _, v := range(px.done) {
    if v < min {
      min = v
    }
  }

  min = min + 1
  px.ClearMemory(min)

  return min
}

//
// the application wants to know whether this
// peer thinks an instance has been decided,
// and if so what the agreed value is. Status()
// should just inspect the local peer state;
// it should not contact other Paxos peers.
//
func (px *Paxos) Status(seq int) (bool, interface{}) {
  // Your code here.
  min := px.Min()
  px.mu.Lock()
  defer px.mu.Unlock()
  if seq >= min && px.store[seq] != nil && px.store[seq].decided == true {
    return true, px.store[seq].v_a
  }
  return false, nil
}

//
// tell the peer to shut itself down.
// for testing.
// please do not change this function.
//
func (px *Paxos) Kill() {
  px.dead = true
  if px.l != nil {
    px.l.Close()
  }
}

//
// the application wants to create a paxos peer.
// the ports of all the paxos peers (including this one)
// are in peers[]. this servers port is peers[me].
// @mvartak: port is now included in peer addresses
func Make(peers []string, me int, rpcs *rpc.Server, unix bool) *Paxos {
  px := &Paxos{}
  px.peers = peers
  px.me = me

  // Your initialization code here.
  px.majority = (len(peers) / 2) + 1
  px.max_seq = -1
  px.store = make(map[int]*Paxo)
  px.done = make(map[string]int)
  px.done[peers[me]] = -1

  if rpcs != nil {
    // caller will create socket &c
    rpcs.Register(px)
  } else {
    rpcs = rpc.NewServer()
    rpcs.Register(px)

  var l net.Listener
  var e error
  if unix {
    os.Remove(peers[me])
    l, e = net.Listen("unix", peers[me])
  } else {
    l, e = net.Listen("tcp", peers[me])
  }

   if e != nil {
      log.Fatal("listen error: ", e);
    }
    px.l = l

    // please do not change any of the following code,
    // or do anything to subvert it.

    // create a thread to accept RPC connections
    go func() {
      for px.dead == false {
        conn, err := px.l.Accept()
        if err == nil && px.dead == false {
          if px.unreliable && (rand.Int63() % 1000) < 100 {
            // discard the request.
            conn.Close()
          } else if px.unreliable && (rand.Int63() % 1000) < 200 {
            // process the request but force discard of reply.

            if unix {
              c1 := conn.(*net.UnixConn)
              f, _ := c1.File()
              err := syscall.Shutdown(int(f.Fd()), syscall.SHUT_WR)
              if err != nil {
                fmt.Printf("shutdown: %v\n", err)
              }
              px.rpcCount++
              go rpcs.ServeConn(conn)
            } else {
              c1 := conn.(*net.TCPConn)
              f, _ := c1.File()
              err := syscall.Shutdown(int(f.Fd()), syscall.SHUT_WR)
              if err != nil {
                fmt.Printf("shutdown: %v\n", err)
              }
              px.rpcCount++
              go rpcs.ServeConn(conn)
            }
          } else {
            px.rpcCount++
            go rpcs.ServeConn(conn)
          }
        } else if err == nil {
          conn.Close()
        }
        if err != nil && px.dead == false {
          fmt.Printf("Paxos(%v) accept: %v\n", me, err.Error())
        }
      }
    }()
  }

  return px
}
<commit_msg>fix to paxos for unix conn<commit_after>package paxos

/**
 * Paxos Library
 *
 * @author: Anant Bhardwaj
 * @date: 03/23/2014
 */

//
// Paxos library, to be included in an application.
// Multiple applications will run, each including
// a Paxos peer.
//
// Manages a sequence of agreed-on values.
// The set of peers is fixed.
// Copes with network failures (partition, msg loss, &c).
// Does not store anything persistently, so cannot handle crash+restart.
//
// The application interface:
//
// px = paxos.Make(peers []string, me string)
// px.Start(seq int, v interface{}) -- start agreement on new instance
// px.Status(seq int) (decided bool, v interface{}) -- get info about an instance
// px.Done(seq int) -- ok to forget all instances <= seq
// px.Max() int -- highest instance seq known, or -1
// px.Min() int -- instances before this seq have been forgotten
//

import "net"
import "net/rpc"
import "log"
import "os"
import "syscall"
import "sync"
import "fmt"
import "math/rand"
import "math"
import "time"

type Paxo struct {
  n_p int64
  n_a int64
  v_a interface{}
  decided bool
}

type Paxos struct {
  mu sync.Mutex
  l net.Listener
  dead bool
  unreliable bool
  rpcCount int
  peers []string
  me int // index into peers[]

  // Your data here.
  majority int
  max_seq int
  done map[string]int
  store map[int]*Paxo
  paxos_lock sync.Mutex
}

const (
  OK = "OK"
  REJECT = "REJECT"
)

const PORT = ":9001"

type status string

type PrepareArgs struct {
  Me string
  N int64
  Seq int
  Done int
}

type PrepareReply struct {
  Status status
  N_A int64
  Value interface{}
  Done int
}

type AcceptArgs struct {
  Me string
  N_A int64
  Seq int
  Value interface{}
  Done int
}

type AcceptReply struct {
  Status status
  Done int
}

type DecidedArgs struct {
  Me string
  Seq int
  Value interface{}
  Done int
}

type DecidedReply struct {
  Status status
  Done int
}

func (px *Paxos) Prepare(args *PrepareArgs, reply *PrepareReply) error {
  px.mu.Lock()
  px.done[args.Me] = args.Done

  paxo, ok := px.store[args.Seq]
  if !ok {
    paxo = &Paxo{n_p: -1, n_a: -1, v_a: nil, decided: false}
    px.store[args.Seq] = paxo
  }
  px.mu.Unlock()

  if args.N > paxo.n_p {
    paxo.n_p = args.N
    reply.Status = OK
    reply.N_A = paxo.n_a
    reply.Value = paxo.v_a
  } else {
    reply.Status = REJECT
  }

  reply.Done = px.done[px.peers[px.me]]
  return nil
}

func (px *Paxos) Accept(args *AcceptArgs, reply *AcceptReply) error {
  px.mu.Lock()
  px.done[args.Me] = args.Done

  paxo, ok := px.store[args.Seq]
  if !ok {
    paxo = &Paxo{n_p: -1, n_a: -1, v_a: nil, decided: false}
    px.store[args.Seq] = paxo
  }
  px.mu.Unlock()

  if args.N_A >= paxo.n_p {
    paxo.n_p = args.N_A
    paxo.n_a = args.N_A
    paxo.v_a = args.Value
    reply.Status = OK
  } else {
    reply.Status = REJECT
  }

  reply.Done = px.done[px.peers[px.me]]
  return nil
}

func (px *Paxos) Decided(args *DecidedArgs, reply *DecidedReply) error {
  px.mu.Lock()
  px.done[args.Me] = args.Done

  paxo, ok := px.store[args.Seq]
  if !ok {
    paxo = &Paxo{n_p: -1, n_a: -1, v_a: nil, decided: false}
    px.store[args.Seq] = paxo
  }
  px.mu.Unlock()

  paxo.v_a = args.Value
  paxo.decided = true

  reply.Status = OK
  reply.Done = px.done[px.peers[px.me]]
  return nil
}

//
// call() sends an RPC to the rpcname handler on server srv
// with arguments args, waits for the reply, and leaves the
// reply in reply. the reply argument should be a pointer
// to a reply structure.
//
// the return value is true if the server responded, and false
// if call() was not able to contact the server. in particular,
// the replys contents are only valid if call() returned true.
//
// you should assume that call() will time out and return an
// error after a while if it does not get a reply from the server.
//
// please use call() to send all RPCs, in client.go and server.go.
// please do not change this function.
//
func call(srv string, name string, args interface{}, reply interface{}, unix bool) bool {
  var err error
  var c Conn
  if unix {
    c, err = rpc.Dial("unix", srv)
  } else {
    c, err = rpc.Dial("tcp", srv)
  }

  if err != nil {
    err1 := err.(*net.OpError)
    if err1.Err != syscall.ENOENT && err1.Err != syscall.ECONNREFUSED {
      fmt.Printf("paxos Dial() failed: %v\n", err1)
    }
    return false
  }
  defer c.Close()

  err = c.Call(name, args, reply)
  if err == nil {
    return true
  }

  fmt.Println(err)
  return false
}

func (px *Paxos) generate_id() int64 {
  return time.Now().UnixNano()
}

func (px *Paxos) Propose(seq int, v interface{}) {
  // Your code here.
  px.mu.Lock()
  if seq > px.max_seq {
    px.max_seq = seq
  }

  paxo, ok := px.store[seq]
  if !ok {
    paxo = &Paxo{n_p: -1, n_a: -1, v_a: nil, decided: false}
    px.store[seq] = paxo
  }
  px.mu.Unlock()
  me := px.peers[px.me]
  t_wait := 50 * time.Millisecond
  for px.dead == false && paxo.decided == false {
    n := px.generate_id()
    highest_n_a := int64(-1)
    highest_v_a := v
    prepare_ok_count := 0
    accept_ok_count := 0

    for _, peer := range px.peers {
      //fmt.Printf ("Prepare: %v,%v -- %v\n", n, seq, v)
      prepare_args := PrepareArgs {Me:me, N: n, Seq: seq, Done: px.done[me]}
      var prepare_reply PrepareReply
      if peer != me {
        call(peer, "Paxos.Prepare", &prepare_args, &prepare_reply)
      } else {
        px.Prepare(&prepare_args, &prepare_reply)
      }
      if prepare_reply.Status == OK {
        prepare_ok_count += 1
        if prepare_reply.N_A > highest_n_a {
          highest_n_a = prepare_reply.N_A
          highest_v_a = prepare_reply.Value
        }
        px.done[peer] = prepare_reply.Done
      }
    }

    if prepare_ok_count < px.majority {
      time.Sleep(t_wait)
      continue
    }

    if highest_v_a == nil {
      highest_v_a = v
    }

    //fmt.Printf ("Accept: %v,%v -- %v\n", n, seq, highest_v_a)
    for _, peer := range px.peers {
      accept_args := AcceptArgs {Me:me, N_A: n, Seq: seq, Value: highest_v_a, Done: px.done[me]}
      var accept_reply AcceptReply
      if peer != me {
        call(peer, "Paxos.Accept", &accept_args, &accept_reply)
      } else {
        px.Accept(&accept_args, &accept_reply)
      }
      if accept_reply.Status == OK {
        accept_ok_count += 1
        px.done[peer] = accept_reply.Done
      }
    }

    if accept_ok_count < px.majority  {
      time.Sleep(t_wait)
      continue
    }

    //fmt.Printf ("Decide: %v,%v -- %v\n", n, seq, highest_v_a)
    for _, peer := range px.peers {
      decided_args := DecidedArgs {Me:me, Seq: seq, Value: highest_v_a, Done: px.done[me]}
      var decided_reply DecidedReply
      if peer != me {
        call(peer, "Paxos.Decided", &decided_args, &decided_reply)
      } else {
        px.Decided(&decided_args, &decided_reply)
      }
      px.done[peer] = decided_reply.Done
    }

  }
}

func (px *Paxos) ClearMemory(seq int) {
  for k, _ := range(px.store) {
    if k < seq {
      delete(px.store, k)
    }
  }
}

//
// the application wants paxos to start agreement on
// instance seq, with proposed value v.
// Start() returns right away; the application will
// call Status() to find out if/when agreement
// is reached.
//
func (px *Paxos) Start(seq int, v interface{}) {
  // Your code here.
  if seq >= px.Min() {
    go px.Propose(seq, v)
  }
}

//
// the application on this machine is done with
// all instances <= seq.
//
// see the comments for Min() for more explanation.
//
func (px *Paxos) Done(seq int) {
  // Your code here.
  px.mu.Lock()
  defer px.mu.Unlock()
  peer := px.peers[px.me]
  if px.done[peer] < seq {
    px.done[peer] = seq
  }
}

//
// the application wants to know the
// highest instance sequence known to
// this peer.
//
func (px *Paxos) Max() int {
  // Your code here.
  px.mu.Lock()
  defer px.mu.Unlock()
  return px.max_seq
}

//
// Min() should return one more than the minimum among z_i,
// where z_i is the highest number ever passed
// to Done() on peer i. A peers z_i is -1 if it has
// never called Done().
//
// Paxos is required to have forgotten all information
// about any instances it knows that are < Min().
// The point is to free up memory in long-running
// Paxos-based servers.
//
// Paxos peers need to exchange their highest Done()
// arguments in order to implement Min(). These
// exchanges can be piggybacked on ordinary Paxos
// agreement protocol messages, so it is OK if one
// peers Min does not reflect another Peers Done()
// until after the next instance is agreed to.
//
// The fact that Min() is defined as a minimum over
// *all* Paxos peers means that Min() cannot increase until
// all peers have been heard from. So if a peer is dead
// or unreachable, other peers Min()s will not increase
// even if all reachable peers call Done. The reason for
// this is that when the unreachable peer comes back to
// life, it will need to catch up on instances that it
// missed -- the other peers therefor cannot forget these
// instances.
//
func (px *Paxos) Min() int {
  // You code here.
  px.mu.Lock()
  defer px.mu.Unlock()
  min := math.MaxInt32
  for _, v := range(px.done) {
    if v < min {
      min = v
    }
  }

  min = min + 1
  px.ClearMemory(min)

  return min
}

//
// the application wants to know whether this
// peer thinks an instance has been decided,
// and if so what the agreed value is. Status()
// should just inspect the local peer state;
// it should not contact other Paxos peers.
//
func (px *Paxos) Status(seq int) (bool, interface{}) {
  // Your code here.
  min := px.Min()
  px.mu.Lock()
  defer px.mu.Unlock()
  if seq >= min && px.store[seq] != nil && px.store[seq].decided == true {
    return true, px.store[seq].v_a
  }
  return false, nil
}

//
// tell the peer to shut itself down.
// for testing.
// please do not change this function.
//
func (px *Paxos) Kill() {
  px.dead = true
  if px.l != nil {
    px.l.Close()
  }
}

//
// the application wants to create a paxos peer.
// the ports of all the paxos peers (including this one)
// are in peers[]. this servers port is peers[me].
// @mvartak: port is now included in peer addresses
func Make(peers []string, me int, rpcs *rpc.Server, unix bool) *Paxos {
  px := &Paxos{}
  px.peers = peers
  px.me = me

  // Your initialization code here.
  px.majority = (len(peers) / 2) + 1
  px.max_seq = -1
  px.store = make(map[int]*Paxo)
  px.done = make(map[string]int)
  px.done[peers[me]] = -1

  if rpcs != nil {
    // caller will create socket &c
    rpcs.Register(px)
  } else {
    rpcs = rpc.NewServer()
    rpcs.Register(px)

  var l net.Listener
  var e error
  if unix {
    os.Remove(peers[me])
    l, e = net.Listen("unix", peers[me])
  } else {
    l, e = net.Listen("tcp", peers[me])
  }

   if e != nil {
      log.Fatal("listen error: ", e);
    }
    px.l = l

    // please do not change any of the following code,
    // or do anything to subvert it.

    // create a thread to accept RPC connections
    go func() {
      for px.dead == false {
        conn, err := px.l.Accept()
        if err == nil && px.dead == false {
          if px.unreliable && (rand.Int63() % 1000) < 100 {
            // discard the request.
            conn.Close()
          } else if px.unreliable && (rand.Int63() % 1000) < 200 {
            // process the request but force discard of reply.

            if unix {
              c1 := conn.(*net.UnixConn)
              f, _ := c1.File()
              err := syscall.Shutdown(int(f.Fd()), syscall.SHUT_WR)
              if err != nil {
                fmt.Printf("shutdown: %v\n", err)
              }
              px.rpcCount++
              go rpcs.ServeConn(conn)
            } else {
              c1 := conn.(*net.TCPConn)
              f, _ := c1.File()
              err := syscall.Shutdown(int(f.Fd()), syscall.SHUT_WR)
              if err != nil {
                fmt.Printf("shutdown: %v\n", err)
              }
              px.rpcCount++
              go rpcs.ServeConn(conn)
            }
          } else {
            px.rpcCount++
            go rpcs.ServeConn(conn)
          }
        } else if err == nil {
          conn.Close()
        }
        if err != nil && px.dead == false {
          fmt.Printf("Paxos(%v) accept: %v\n", me, err.Error())
        }
      }
    }()
  }

  return px
}
<|endoftext|>