<commit_before>// +build linux

// Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
//	http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.

package networkutils

import (
	"context"
	"errors"
	"net"
	"testing"
	"time"

	"github.com/golang/mock/gomock"
	"github.com/stretchr/testify/assert"
	"github.com/vishvananda/netlink"

	"github.com/aws/amazon-ecs-agent/agent/eni/netlinkwrapper/mocks"
)

const (
	randomDevice     = "eth1"
	validMAC         = "00:0a:95:9d:68:16"
	pciDevPath       = " ../../devices/pci0000:00/0000:00:03.0/net/eth1"
	virtualDevPath   = "../../devices/virtual/net/lo"
	invalidDevPath   = "../../virtual/net/lo"
	incorrectDevPath = "../../devices/totally/wrong/net/path"
)

// TestGetMACAddress checks obtaining MACAddress by using netlinkClient
func TestGetMACAddress(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()
	mockNetlink := mock_netlinkwrapper.NewMockNetLink(mockCtrl)
	pm, _ := net.ParseMAC(validMAC)
	mockNetlink.EXPECT().LinkByName(randomDevice).Return(
		&netlink.Device{
			LinkAttrs: netlink.LinkAttrs{
				HardwareAddr: pm,
				Name:         randomDevice,
			},
		}, nil)
	ctx := context.TODO()
	mac, err := GetMACAddress(ctx, time.Millisecond, randomDevice, mockNetlink)
	assert.Nil(t, err)
	assert.Equal(t, mac, validMAC)
}

// TestGetMACAddressWithNetlinkError attempts to test the netlinkClient
// error code path
func TestGetMACAddressWithNetlinkError(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()
	mockNetlink := mock_netlinkwrapper.NewMockNetLink(mockCtrl)
	// LinkByName returns an error. This will ensure that a non-retriable
	// error is generated and halts the backoff-rety loop
	mockNetlink.EXPECT().LinkByName(randomDevice).Return(
		&netlink.Device{},
		errors.New("Dummy Netlink Error"))
	ctx := context.TODO()
	mac, err := GetMACAddress(ctx, 2*macAddressBackoffMax, randomDevice, mockNetlink)
	assert.Error(t, err)
	assert.Empty(t, mac)
}

// TestGetMACAddressNotFoundRetry tests if netlink.LinkByName gets invoked
// multiple times if the mac address for a device is returned as empty string
func TestGetMACAddressNotFoundRetry(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()
	mockNetlink := mock_netlinkwrapper.NewMockNetLink(mockCtrl)
	pm, _ := net.ParseMAC(validMAC)
	gomock.InOrder(
		// Return empty mac address on first invocation
		mockNetlink.EXPECT().LinkByName(randomDevice).Return(&netlink.Device{}, nil),
		// Return a valid mac address on first invocation. Even though the first
		// invocation did not result in an error, it did return an empty mac address.
		// Hence, we expect it to be retried
		mockNetlink.EXPECT().LinkByName(randomDevice).Return(&netlink.Device{
			LinkAttrs: netlink.LinkAttrs{
				HardwareAddr: pm,
				Name:         randomDevice,
			},
		}, nil),
	)
	ctx := context.TODO()
	// Set max retry duration to twice that of the min backoff to ensure that there's
	// enough time to retry
	mac, err := GetMACAddress(ctx, 2*macAddressBackoffMin, randomDevice, mockNetlink)
	assert.NoError(t, err)
	assert.Equal(t, mac, validMAC)
}

// TestGetMACAddressNotFoundRetryExpires tests if the backoff-retry logic for
// retrieving mac address returns error on timeout
func TestGetMACAddressNotFoundRetryExpires(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()
	mockNetlink := mock_netlinkwrapper.NewMockNetLink(mockCtrl)
	// Return empty mac address everytime
	mockNetlink.EXPECT().LinkByName(randomDevice).Return(&netlink.Device{}, nil).MinTimes(1)
	ctx := context.TODO()
	// Set max retry duration to twice that of the min backoff to ensure that there's
	// enough time to retry
	mac, err := GetMACAddress(ctx, 2*macAddressBackoffMin, randomDevice, mockNetlink)
	assert.Error(t, err)
	assert.Empty(t, mac)
}

// TestIsValidDevicePathTableTest does a table test for device path validity
func TestIsValidDevicePathTableTest(t *testing.T) {
	var table = []struct {
		input  string
		output bool
	}{
		{pciDevPath, true},
		{virtualDevPath, false},
		{invalidDevPath, false},
		{incorrectDevPath, false},
	}

	for _, entry := range table {
		status := IsValidNetworkDevice(entry.input)
		assert.Equal(t, status, entry.output)
	}
}
<commit_msg>eni: increase test retry duration, swap assert() params<commit_after>// +build linux

// Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
//	http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.

package networkutils

import (
	"context"
	"errors"
	"net"
	"testing"
	"time"

	"github.com/golang/mock/gomock"
	"github.com/stretchr/testify/assert"
	"github.com/vishvananda/netlink"

	"github.com/aws/amazon-ecs-agent/agent/eni/netlinkwrapper/mocks"
)

const (
	randomDevice     = "eth1"
	validMAC         = "00:0a:95:9d:68:16"
	pciDevPath       = " ../../devices/pci0000:00/0000:00:03.0/net/eth1"
	virtualDevPath   = "../../devices/virtual/net/lo"
	invalidDevPath   = "../../virtual/net/lo"
	incorrectDevPath = "../../devices/totally/wrong/net/path"
)

// TestGetMACAddress checks obtaining MACAddress by using netlinkClient
func TestGetMACAddress(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()
	mockNetlink := mock_netlinkwrapper.NewMockNetLink(mockCtrl)
	pm, _ := net.ParseMAC(validMAC)
	mockNetlink.EXPECT().LinkByName(randomDevice).Return(
		&netlink.Device{
			LinkAttrs: netlink.LinkAttrs{
				HardwareAddr: pm,
				Name:         randomDevice,
			},
		}, nil)
	ctx := context.TODO()
	mac, err := GetMACAddress(ctx, time.Millisecond, randomDevice, mockNetlink)
	assert.Nil(t, err)
	assert.Equal(t, validMAC, mac)
}

// TestGetMACAddressWithNetlinkError attempts to test the netlinkClient
// error code path
func TestGetMACAddressWithNetlinkError(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()
	mockNetlink := mock_netlinkwrapper.NewMockNetLink(mockCtrl)
	// LinkByName returns an error. This will ensure that a non-retriable
	// error is generated and halts the backoff-rety loop
	mockNetlink.EXPECT().LinkByName(randomDevice).Return(
		&netlink.Device{},
		errors.New("Dummy Netlink Error"))
	ctx := context.TODO()
	mac, err := GetMACAddress(ctx, 2*macAddressBackoffMax, randomDevice, mockNetlink)
	assert.Error(t, err)
	assert.Empty(t, mac)
}

// TestGetMACAddressNotFoundRetry tests if netlink.LinkByName gets invoked
// multiple times if the mac address for a device is returned as empty string
func TestGetMACAddressNotFoundRetry(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()
	mockNetlink := mock_netlinkwrapper.NewMockNetLink(mockCtrl)
	pm, _ := net.ParseMAC(validMAC)
	gomock.InOrder(
		// Return empty mac address on first invocation
		mockNetlink.EXPECT().LinkByName(randomDevice).Return(&netlink.Device{}, nil),
		// Return a valid mac address on first invocation. Even though the first
		// invocation did not result in an error, it did return an empty mac address.
		// Hence, we expect it to be retried
		mockNetlink.EXPECT().LinkByName(randomDevice).Return(&netlink.Device{
			LinkAttrs: netlink.LinkAttrs{
				HardwareAddr: pm,
				Name:         randomDevice,
			},
		}, nil),
	)
	ctx := context.TODO()
	// Set max retry duration to twice that of the max backoff to ensure that there's
	// enough time to retry
	mac, err := GetMACAddress(ctx, 2*macAddressBackoffMax, randomDevice, mockNetlink)
	assert.NoError(t, err)
	assert.Equal(t, validMAC, mac)
}

// TestGetMACAddressNotFoundRetryExpires tests if the backoff-retry logic for
// retrieving mac address returns error on timeout
func TestGetMACAddressNotFoundRetryExpires(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()
	mockNetlink := mock_netlinkwrapper.NewMockNetLink(mockCtrl)
	// Return empty mac address everytime
	mockNetlink.EXPECT().LinkByName(randomDevice).Return(&netlink.Device{}, nil).MinTimes(1)
	ctx := context.TODO()
	// Set max retry duration to twice that of the min backoff to ensure that there's
	// enough time to retry
	mac, err := GetMACAddress(ctx, 2*macAddressBackoffMin, randomDevice, mockNetlink)
	assert.Error(t, err)
	assert.Empty(t, mac)
}

// TestIsValidDevicePathTableTest does a table test for device path validity
func TestIsValidDevicePathTableTest(t *testing.T) {
	var table = []struct {
		input  string
		output bool
	}{
		{pciDevPath, true},
		{virtualDevPath, false},
		{invalidDevPath, false},
		{incorrectDevPath, false},
	}

	for _, entry := range table {
		status := IsValidNetworkDevice(entry.input)
		assert.Equal(t, entry.output, status)
	}
}
<|endoftext|>


<commit_before>/*
	Android Compile Server for Skia Bots.
*/

package main

import (
	"context"
	"encoding/json"
	"flag"
	"fmt"
	"html/template"
	"net/http"
	"path/filepath"
	"runtime"
	"strconv"
	"time"

	"cloud.google.com/go/datastore"
	"github.com/gorilla/mux"

	"go.skia.org/infra/go/common"
	"go.skia.org/infra/go/httputils"
	"go.skia.org/infra/go/login"
	"go.skia.org/infra/go/skiaversion"
	"go.skia.org/infra/go/sklog"
)

const (
	// OAUTH2_CALLBACK_PATH is callback endpoint used for the Oauth2 flow.
	OAUTH2_CALLBACK_PATH = "/oauth2callback/"

	REGISTER_RUN_POST_URI = "/_/register"
	GET_TASK_STATUS_URI   = "/get_task_status"

	PROD_URI = "https://android-compile.skia.org"
)

var (
	// Flags
	host         = flag.String("host", "localhost", "HTTP service host")
	promPort     = flag.String("prom_port", ":20000", "Metrics service address (e.g., ':20000')")
	httpPort     = flag.String("http_port", ":8002", "HTTP service port (e.g., ':8002')")
	tasksPort    = flag.String("tasks_port", ":8008", "Port used to register and query status of tasks (e.g., ':8008')")
	local        = flag.Bool("local", false, "Running locally if true. As opposed to in production.")
	workdir      = flag.String("workdir", ".", "Directory to use for scratch work.")
	resourcesDir = flag.String("resources_dir", "", "The directory to find compile.sh, templates, JS, and CSS files.  If blank then the directory two directories up from this source file will be used.")
	numCheckouts = flag.Int("num_checkouts", 10, "The number of checkouts the Android compile server should maintain.")

	// Datastore params
	namespace   = flag.String("namespace", "android-compile", "The Cloud Datastore namespace, such as 'android-compile'.")
	projectName = flag.String("project_name", "google.com:skia-buildbots", "The Google Cloud project name.")

	// OAUTH params
	authWhiteList = flag.String("auth_whitelist", "google.com", "White space separated list of domains and email addresses that are allowed to login.")
	redirectURL   = flag.String("redirect_url", "https://leasing.skia.org/oauth2callback/", "OAuth2 redirect url. Only used when local=false.")

	// indexTemplate is the main index.html page we serve.
	indexTemplate *template.Template = nil

	serverURL string
)

func reloadTemplates() {
	if *resourcesDir == "" {
		// If resourcesDir is not specified then consider the directory two directories up from this
		// source file as the resourcesDir.
		_, filename, _, _ := runtime.Caller(0)
		*resourcesDir = filepath.Join(filepath.Dir(filename), "../..")
	}
	indexTemplate = template.Must(template.ParseFiles(
		filepath.Join(*resourcesDir, "templates/index.html"),
		filepath.Join(*resourcesDir, "templates/header.html"),
	))
}

func loginHandler(w http.ResponseWriter, r *http.Request) {
	http.Redirect(w, r, login.LoginURL(w, r), http.StatusFound)
	return
}

func indexHandler(w http.ResponseWriter, r *http.Request) {
	if *local {
		reloadTemplates()
	}
	w.Header().Set("Content-Type", "text/html")

	waitingTasks, runningTasks, err := GetCompileTasks()
	if err != nil {
		httputils.ReportError(w, r, err, "Failed to get compile tasks")
		return
	}

	var templateTasks = struct {
		WaitingTasks []*CompileTask
		RunningTasks []*CompileTask
	}{
		WaitingTasks: waitingTasks,
		RunningTasks: runningTasks,
	}

	if err := indexTemplate.Execute(w, templateTasks); err != nil {
		httputils.ReportError(w, r, err, "Failed to expand template")
		return
	}
	return
}

func statusHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")

	taskParam := r.FormValue("task")
	if taskParam == "" {
		httputils.ReportError(w, r, nil, "Missing task parameter")
		return
	}
	taskID, err := strconv.ParseInt(taskParam, 10, 64)
	if err != nil {
		httputils.ReportError(w, r, err, "Invalid task parameter")
		return
	}

	_, t, err := GetDSTask(taskID)
	if err != nil {
		httputils.ReportError(w, r, err, "Could not find task")
		return
	}

	if err := json.NewEncoder(w).Encode(t); err != nil {
		httputils.ReportError(w, r, err, "Failed to encode JSON")
		return

	}

	return
}

func registerRunHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")

	// Either hash or (issue & patchset) must be specified.
	hashParam := r.FormValue("hash")
	issueParam := r.FormValue("issue")
	patchsetParam := r.FormValue("patchset")
	if hashParam == "" && (issueParam == "" || patchsetParam == "") {
		httputils.ReportError(w, r, nil, "Either hash or (issue & patchset) must be specified")
		return
	}

	key := GetNewDSKey()
	task := CompileTask{}
	if hashParam != "" {
		task.Hash = hashParam
	} else {
		issue, err := strconv.Atoi(issueParam)
		if err != nil {
			httputils.ReportError(w, r, nil, "Issue must be a number")
			return
		}
		patchset, err := strconv.Atoi(patchsetParam)
		if err != nil {
			httputils.ReportError(w, r, nil, "Patchset must be a number")
			return
		}
		task.Issue = issue
		task.PatchSet = patchset
	}

	task.Created = time.Now()
	ctx := context.Background()
	datastoreKey, err := PutDSTask(ctx, key, &task)
	if err != nil {
		httputils.ReportError(w, r, err, fmt.Sprintf("Error putting task in datastore: %v", err))
		return
	}

	// Kick off the task.
	triggerCompileTask(ctx, &task, datastoreKey)

	jsonResponse := map[string]interface{}{
		"taskID": datastoreKey.ID,
	}
	if err := json.NewEncoder(w).Encode(jsonResponse); err != nil {
		httputils.ReportError(w, r, err, "Failed to encode JSON")
		return
	}
}

// triggerCompileTask runs the specified CompileTask in a goroutine. After
// completion the task is marked as Done and updated in the Datastore.
func triggerCompileTask(ctx context.Context, task *CompileTask, datastoreKey *datastore.Key) {
	go func() {
		pathToCompileScript := filepath.Join(*resourcesDir, "compile.sh")
		if err := RunCompileTask(ctx, task, datastoreKey, pathToCompileScript); err != nil {
			task.InfraFailure = true
			sklog.Errorf("Error when compiling task with ID %d: %s", datastoreKey.ID, err)
		}
		task.Done = true
		task.Completed = time.Now()
		if _, err := UpdateDSTask(ctx, datastoreKey, task); err != nil {
			sklog.Errorf("Could not update compile task with ID %d: %s", datastoreKey.ID, err)
		}
	}()
}

func runServer() {
	httpRouter := mux.NewRouter()
	httpRouter.PathPrefix("/res/").HandlerFunc(httputils.MakeResourceHandler(*resourcesDir))

	httpRouter.HandleFunc("/", indexHandler)
	httpRouter.HandleFunc("/json/version", skiaversion.JsonHandler)
	httpRouter.HandleFunc(OAUTH2_CALLBACK_PATH, login.OAuth2CallbackHandler)
	httpRouter.HandleFunc("/login/", loginHandler)
	httpRouter.HandleFunc("/logout/", login.LogoutHandler)
	httpRouter.HandleFunc("/loginstatus/", login.StatusHandler)
	http.Handle("/", httputils.LoggingGzipRequestResponse(httpRouter))
	sklog.AddLogsRedirect(httpRouter)
	sklog.Infof("Ready to serve on %s", serverURL)
	go func() {
		sklog.Fatal(http.ListenAndServe(*httpPort, nil))
	}()

	tasksRouter := mux.NewRouter()
	// TODO(rmistry): Change to POST after testing is done.
	tasksRouter.HandleFunc(REGISTER_RUN_POST_URI, registerRunHandler).Methods("GET")
	tasksRouter.HandleFunc(GET_TASK_STATUS_URI, statusHandler)
	sklog.Infof("Handling registering and querying tasks on %s", *tasksPort)
	sklog.Fatal(http.ListenAndServe(*tasksPort, tasksRouter))

}

func main() {
	flag.Parse()

	if *local {
		// Dont log to cloud in local mode.
		common.InitWithMust(
			"android_compile",
			common.PrometheusOpt(promPort),
		)
		reloadTemplates()
	} else {
		common.InitWithMust(
			"android_compile",
			common.PrometheusOpt(promPort),
			common.CloudLoggingOpt(),
		)
	}
	defer common.Defer()
	skiaversion.MustLogVersion()

	reloadTemplates()
	serverURL = "https://" + *host
	if *local {
		serverURL = "http://" + *host + *httpPort
	}

	useRedirectURL := fmt.Sprintf("http://localhost%s/oauth2callback/", *httpPort)
	if !*local {
		useRedirectURL = *redirectURL
	}
	if err := login.Init(useRedirectURL, *authWhiteList); err != nil {
		sklog.Fatal(fmt.Errorf("Problem setting up server OAuth: %s", err))
	}

	// Initialize cloud datastore.
	if err := DatastoreInit(*projectName, *namespace); err != nil {
		sklog.Fatalf("Failed to init cloud datastore: %s", err)
	}

	// Initialize checkouts.
	if err := CheckoutsInit(*numCheckouts, *workdir); err != nil {
		sklog.Fatalf("Failed to init checkouts: %s", err)
	}

	// Find and reschedule all CompileTasks that are in "running" state. Any
	// "running" CompileTasks means that the server was restarted in the middle
	// of run(s).
	ctx := context.Background()
	_, runningTasksAndKeys, err := GetCompileTasksAndKeys()
	if err != nil {
		sklog.Fatalf("Failed to retrieve compile tasks and keys: %s", err)
	}
	for _, taskAndKey := range runningTasksAndKeys {
		sklog.Infof("Found orphaned task %d. Retriggering it...", taskAndKey.key.ID)
		triggerCompileTask(ctx, taskAndKey.task, taskAndKey.key)
	}

	runServer()
}
<commit_msg>[Android Compile Server] Use webhooks for register / get_task_status endpoints<commit_after>/*
	Android Compile Server for Skia Bots.
*/

package main

import (
	"context"
	"encoding/json"
	"flag"
	"fmt"
	"html/template"
	"net/http"
	"path/filepath"
	"runtime"
	"strconv"
	"time"

	"cloud.google.com/go/datastore"
	"github.com/gorilla/mux"

	"go.skia.org/infra/go/common"
	"go.skia.org/infra/go/httputils"
	"go.skia.org/infra/go/login"
	"go.skia.org/infra/go/skiaversion"
	"go.skia.org/infra/go/sklog"
	"go.skia.org/infra/go/webhook"
)

const (
	// OAUTH2_CALLBACK_PATH is callback endpoint used for the Oauth2 flow.
	OAUTH2_CALLBACK_PATH = "/oauth2callback/"

	REGISTER_RUN_POST_URI = "/_/register"
	GET_TASK_STATUS_URI   = "/get_task_status"

	PROD_URI = "https://android-compile.skia.org"
)

var (
	// Flags
	host         = flag.String("host", "localhost", "HTTP service host")
	promPort     = flag.String("prom_port", ":20000", "Metrics service address (e.g., ':20000')")
	port         = flag.String("port", ":8002", "HTTP service port (e.g., ':8002')")
	local        = flag.Bool("local", false, "Running locally if true. As opposed to in production.")
	workdir      = flag.String("workdir", ".", "Directory to use for scratch work.")
	resourcesDir = flag.String("resources_dir", "", "The directory to find compile.sh, templates, JS, and CSS files.  If blank then the directory two directories up from this source file will be used.")
	numCheckouts = flag.Int("num_checkouts", 10, "The number of checkouts the Android compile server should maintain.")

	// Datastore params
	namespace   = flag.String("namespace", "android-compile", "The Cloud Datastore namespace, such as 'android-compile'.")
	projectName = flag.String("project_name", "google.com:skia-buildbots", "The Google Cloud project name.")

	// OAUTH params
	authWhiteList = flag.String("auth_whitelist", "google.com", "White space separated list of domains and email addresses that are allowed to login.")
	redirectURL   = flag.String("redirect_url", "https://leasing.skia.org/oauth2callback/", "OAuth2 redirect url. Only used when local=false.")

	// indexTemplate is the main index.html page we serve.
	indexTemplate *template.Template = nil

	serverURL string
)

func reloadTemplates() {
	if *resourcesDir == "" {
		// If resourcesDir is not specified then consider the directory two directories up from this
		// source file as the resourcesDir.
		_, filename, _, _ := runtime.Caller(0)
		*resourcesDir = filepath.Join(filepath.Dir(filename), "../..")
	}
	indexTemplate = template.Must(template.ParseFiles(
		filepath.Join(*resourcesDir, "templates/index.html"),
		filepath.Join(*resourcesDir, "templates/header.html"),
	))
}

func loginHandler(w http.ResponseWriter, r *http.Request) {
	http.Redirect(w, r, login.LoginURL(w, r), http.StatusFound)
	return
}

func indexHandler(w http.ResponseWriter, r *http.Request) {
	if *local {
		reloadTemplates()
	}
	w.Header().Set("Content-Type", "text/html")

	waitingTasks, runningTasks, err := GetCompileTasks()
	if err != nil {
		httputils.ReportError(w, r, err, "Failed to get compile tasks")
		return
	}

	var templateTasks = struct {
		WaitingTasks []*CompileTask
		RunningTasks []*CompileTask
	}{
		WaitingTasks: waitingTasks,
		RunningTasks: runningTasks,
	}

	if err := indexTemplate.Execute(w, templateTasks); err != nil {
		httputils.ReportError(w, r, err, "Failed to expand template")
		return
	}
	return
}

func statusHandler(w http.ResponseWriter, r *http.Request) {
	_, err := webhook.AuthenticateRequest(r)
	if err != nil {
		httputils.ReportError(w, r, err, "Authentication failure")
		return
	}
	w.Header().Set("Content-Type", "application/json")

	taskParam := r.FormValue("task")
	if taskParam == "" {
		httputils.ReportError(w, r, nil, "Missing task parameter")
		return
	}
	taskID, err := strconv.ParseInt(taskParam, 10, 64)
	if err != nil {
		httputils.ReportError(w, r, err, "Invalid task parameter")
		return
	}

	_, t, err := GetDSTask(taskID)
	if err != nil {
		httputils.ReportError(w, r, err, "Could not find task")
		return
	}

	if err := json.NewEncoder(w).Encode(t); err != nil {
		httputils.ReportError(w, r, err, "Failed to encode JSON")
		return

	}

	return
}

func registerRunHandler(w http.ResponseWriter, r *http.Request) {
	data, err := webhook.AuthenticateRequest(r)
	if err != nil {
		httputils.ReportError(w, r, err, "Authentication failure")
		return
	}
	w.Header().Set("Content-Type", "application/json")

	task := CompileTask{}
	if err := json.Unmarshal(data, &task); err != nil {
		httputils.ReportError(w, r, err, "Failed to parse request.")
		return
	}

	// Either hash or (issue & patchset) must be specified.
	if task.Hash == "" && (task.Issue == 0 || task.PatchSet == 0) {
		httputils.ReportError(w, r, nil, "Either hash or (issue & patchset) must be specified")
		return
	}

	key := GetNewDSKey()
	task.Created = time.Now()
	ctx := context.Background()
	datastoreKey, err := PutDSTask(ctx, key, &task)
	if err != nil {
		httputils.ReportError(w, r, err, fmt.Sprintf("Error putting task in datastore: %v", err))
		return
	}

	// Kick off the task.
	triggerCompileTask(ctx, &task, datastoreKey)

	jsonResponse := map[string]interface{}{
		"taskID": datastoreKey.ID,
	}
	if err := json.NewEncoder(w).Encode(jsonResponse); err != nil {
		httputils.ReportError(w, r, err, "Failed to encode JSON")
		return
	}
}

// triggerCompileTask runs the specified CompileTask in a goroutine. After
// completion the task is marked as Done and updated in the Datastore.
func triggerCompileTask(ctx context.Context, task *CompileTask, datastoreKey *datastore.Key) {
	go func() {
		pathToCompileScript := filepath.Join(*resourcesDir, "compile.sh")
		if err := RunCompileTask(ctx, task, datastoreKey, pathToCompileScript); err != nil {
			task.InfraFailure = true
			sklog.Errorf("Error when compiling task with ID %d: %s", datastoreKey.ID, err)
		}
		task.Done = true
		task.Completed = time.Now()
		if _, err := UpdateDSTask(ctx, datastoreKey, task); err != nil {
			sklog.Errorf("Could not update compile task with ID %d: %s", datastoreKey.ID, err)
		}
	}()
}

func runServer() {
	r := mux.NewRouter()
	r.PathPrefix("/res/").HandlerFunc(httputils.MakeResourceHandler(*resourcesDir))
	r.HandleFunc("/", indexHandler)
	r.HandleFunc(REGISTER_RUN_POST_URI, registerRunHandler).Methods("POST")
	r.HandleFunc(GET_TASK_STATUS_URI, statusHandler)

	r.HandleFunc("/json/version", skiaversion.JsonHandler)
	r.HandleFunc(OAUTH2_CALLBACK_PATH, login.OAuth2CallbackHandler)
	r.HandleFunc("/login/", loginHandler)
	r.HandleFunc("/logout/", login.LogoutHandler)
	r.HandleFunc("/loginstatus/", login.StatusHandler)
	http.Handle("/", httputils.LoggingGzipRequestResponse(r))
	sklog.AddLogsRedirect(r)
	sklog.Infof("Ready to serve on %s", serverURL)
	sklog.Fatal(http.ListenAndServe(*port, nil))
}

func main() {
	flag.Parse()

	if *local {
		// Dont log to cloud in local mode.
		common.InitWithMust(
			"android_compile",
			common.PrometheusOpt(promPort),
		)
		reloadTemplates()
	} else {
		common.InitWithMust(
			"android_compile",
			common.PrometheusOpt(promPort),
			common.CloudLoggingOpt(),
		)
	}
	defer common.Defer()
	skiaversion.MustLogVersion()

	reloadTemplates()
	serverURL = "https://" + *host
	if *local {
		serverURL = "http://" + *host + *port
	}

	useRedirectURL := fmt.Sprintf("http://localhost%s/oauth2callback/", *port)
	if !*local {
		useRedirectURL = *redirectURL
	}
	if err := login.Init(useRedirectURL, *authWhiteList); err != nil {
		sklog.Fatal(fmt.Errorf("Problem setting up server OAuth: %s", err))
	}

	// Initialize cloud datastore.
	if err := DatastoreInit(*projectName, *namespace); err != nil {
		sklog.Fatalf("Failed to init cloud datastore: %s", err)
	}

	// Initialize checkouts.
	if err := CheckoutsInit(*numCheckouts, *workdir); err != nil {
		sklog.Fatalf("Failed to init checkouts: %s", err)
	}

	// Initialize webhooks.
	if *local {
		webhook.InitRequestSaltForTesting()
	} else {
		webhook.MustInitRequestSaltFromMetadata("ac_webhook_request_salt")
	}

	// Find and reschedule all CompileTasks that are in "running" state. Any
	// "running" CompileTasks means that the server was restarted in the middle
	// of run(s).
	ctx := context.Background()
	_, runningTasksAndKeys, err := GetCompileTasksAndKeys()
	if err != nil {
		sklog.Fatalf("Failed to retrieve compile tasks and keys: %s", err)
	}
	for _, taskAndKey := range runningTasksAndKeys {
		sklog.Infof("Found orphaned task %d. Retriggering it...", taskAndKey.key.ID)
		triggerCompileTask(ctx, taskAndKey.task, taskAndKey.key)
	}

	runServer()
}
<|endoftext|>


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

package tfgcv

import (
	"os"
	"strings"

	"github.com/GoogleCloudPlatform/terraform-validator/converters/google"
	"github.com/GoogleCloudPlatform/terraform-validator/tfplan"
	"github.com/golang/glog"
	"github.com/hashicorp/terraform/terraform"
	"github.com/pkg/errors"
)

// ReadPlannedAssets extracts CAI assets from a terraform plan file.
// It ignores non-supported resources.
func ReadPlannedAssets(path, project string) ([]google.Asset, error) {
	f, err := os.Open(path)
	if err != nil {
		return nil, errors.Wrap(err, "opening plan file")
	}
	defer f.Close()

	plan, err := terraform.ReadPlan(f)
	if err != nil {
		return nil, errors.Wrap(err, "reading terraform plan")
	}

	// Attempt to pull the project from the provider.
	if project == "" {
		project, err = parseProviderProject(plan)
		if err != nil {
			return nil, err
		}
	}

	converter, err := google.NewConverter(project, "")
	if err != nil {
		return nil, errors.Wrap(err, "building google converter")
	}

	for _, r := range tfplan.ComposeResources(plan, converter.Schemas()) {
		if err := converter.AddResource(&r); err != nil {
			if errors.Cause(err) == google.ErrDuplicateAsset {
				glog.Warningf("converting resource: %v", err)
			} else {
				return nil, errors.Wrapf(err, "converting resource %v", r.Kind())
			}
		}
	}

	return converter.Assets(), nil
}

var ErrParsingProviderProject = errors.New("unable to parse provider project")

// parseProviderProject attempts to parse hardcoded "project" configuration
// from the "google" provider block. It is lazy and fails if the job involves
// interpolation.
// TODO: Replicate/incorporate terraform interpolation (or is that a good idea?).
func parseProviderProject(plan *terraform.Plan) (string, error) {
	for _, cfg := range plan.Module.Config().ProviderConfigs {
		if cfg.Name == "google" {
			inf, ok := cfg.RawConfig.Raw["project"]
			if !ok {
				continue
			}
			prj := inf.(string)

			// If the provider has a hardcoded project string, return it.
			if !strings.Contains(prj, "${") {
				return prj, nil
			}

			return "", ErrParsingProviderProject
		}
	}

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

package tfgcv

import (
	"os"
	"strings"

	"github.com/GoogleCloudPlatform/terraform-validator/converters/google"
	"github.com/GoogleCloudPlatform/terraform-validator/tfplan"
	"github.com/golang/glog"
	"github.com/hashicorp/terraform/terraform"
	"github.com/pkg/errors"
)

// ReadPlannedAssets extracts CAI assets from a terraform plan file.
// It ignores non-supported resources.
func ReadPlannedAssets(path, project string) ([]google.Asset, error) {
	f, err := os.Open(path)
	if err != nil {
		return nil, errors.Wrap(err, "opening plan file")
	}
	defer f.Close()

	plan, err := terraform.ReadPlan(f)
	if err != nil {
		return nil, errors.Wrap(err, "reading terraform plan")
	}

	// Attempt to pull the project from the provider.
	if project == "" {
		project, err = parseProviderProject(plan)
		if err != nil {
			return nil, err
		}
	}

	converter, err := google.NewConverter(project, "")
	if err != nil {
		return nil, errors.Wrap(err, "building google converter")
	}

	for _, r := range tfplan.ComposeResources(plan, converter.Schemas()) {
		if err := converter.AddResource(&r); err != nil {
			if errors.Cause(err) == google.ErrDuplicateAsset {
				glog.Warningf("converting resource: %v", err)
			} else {
				return nil, errors.Wrapf(err, "converting resource %v", r.Kind())
			}
		}
	}

	return converter.Assets(), nil
}

var ErrParsingProviderProject = errors.New("unable to parse provider project")

// parseProviderProject attempts to parse hardcoded "project" configuration
// from the "google" provider block. It is lazy and fails if the job involves
// interpolation.
// TODO: Replicate/incorporate terraform interpolation (or is that a good idea?).
func parseProviderProject(plan *terraform.Plan) (string, error) {
	for _, cfg := range plan.Module.Config().ProviderConfigs {
		if cfg.Name == "google" {
			inf, ok := cfg.RawConfig.Raw["project"]
			if !ok {
				continue
			}
			prj := inf.(string)

			// If the provider has a hardcoded project string, return it.
			if !strings.Contains(prj, "${") {
				return prj, nil
			}

			return "", ErrParsingProviderProject
		}
	}

	// If we have reached this point, there was no provider-level project that
	// was specified in this plan. This means the plan should be viable based
	// on resource-level project fields being set.
	return "", nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/base64"
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"os"
	"os/signal"
	"sync"
	"time"
)

const defaultPort = 9901

var ptInfo ptServerInfo

// When a connection handler starts, +1 is written to this channel; when it
// ends, -1 is written.
var handlerChan = make(chan int)

func logDebug(format string, v ...interface{}) {
	fmt.Fprintf(os.Stderr, format+"\n", v...)
}

type websocketConn struct {
	Ws *websocket
	Base64 bool
	messageBuf []byte
}

func (conn *websocketConn) Read(b []byte) (n int, err error) {
	for len(conn.messageBuf) == 0 {
		var m websocketMessage
		m, err = conn.Ws.ReadMessage()
		if err != nil {
			return
		}
		if conn.Base64 {
			if m.Opcode != 1 {
				err = errors.New(fmt.Sprintf("got non-text opcode %d with the base64 subprotocol", m.Opcode))
				return
			}
			conn.messageBuf = make([]byte, base64.StdEncoding.DecodedLen(len(m.Payload)))
			var num int
			num, err = base64.StdEncoding.Decode(conn.messageBuf, m.Payload)
			if err != nil {
				return
			}
			conn.messageBuf = conn.messageBuf[:num]
		} else {
			if m.Opcode != 2 {
				err = errors.New(fmt.Sprintf("got non-binary opcode %d with no subprotocol", m.Opcode))
				return
			}
			conn.messageBuf = m.Payload
		}
	}

	n = copy(b, conn.messageBuf)
	conn.messageBuf = conn.messageBuf[n:]

	return
}

func (conn *websocketConn) Write(b []byte) (n int, err error) {
	if conn.Base64 {
		buf := make([]byte, base64.StdEncoding.EncodedLen(len(b)))
		base64.StdEncoding.Encode(buf, b)
		err = conn.Ws.WriteMessage(1, buf)
		if err != nil {
			return
		}
		n = len(b)
	} else {
		err = conn.Ws.WriteMessage(2, b)
		n = len(b)
	}
	return
}

func (conn *websocketConn) Close() (err error) {
	err = conn.Ws.WriteFrame(8, nil)
	if err != nil {
		conn.Ws.Conn.Close()
		return
	}
	err = conn.Ws.Conn.Close()
	return
}

func (conn *websocketConn) LocalAddr() net.Addr {
	return conn.Ws.Conn.LocalAddr()
}

func (conn *websocketConn) RemoteAddr() net.Addr {
	return conn.Ws.Conn.RemoteAddr()
}

func (conn *websocketConn) SetDeadline(t time.Time) error {
	return conn.Ws.Conn.SetDeadline(t)
}

func (conn *websocketConn) SetReadDeadline(t time.Time) error {
	return conn.Ws.Conn.SetReadDeadline(t)
}

func (conn *websocketConn) SetWriteDeadline(t time.Time) error {
	return conn.Ws.Conn.SetWriteDeadline(t)
}

func NewWebsocketConn(ws *websocket) websocketConn {
	var conn websocketConn
	conn.Ws = ws
	conn.Base64 = (ws.Subprotocol == "base64")
	return conn
}

func proxy(local *net.TCPConn, conn *websocketConn) {
	var wg sync.WaitGroup

	wg.Add(2)

	go func() {
		_, err := io.Copy(conn, local)
		if err != nil {
			logDebug("error copying ORPort to WebSocket: " + err.Error())
		}
		local.CloseRead()
		conn.Close()
		wg.Done()
	}()

	go func() {
		_, err := io.Copy(local, conn)
		if err != nil {
			logDebug("error copying WebSocket to ORPort: " + err.Error())
		}
		local.CloseWrite()
		conn.Close()
		wg.Done()
	}()

	wg.Wait()
}

func websocketHandler(ws *websocket) {
	conn := NewWebsocketConn(ws)

	handlerChan <- 1
	defer func() {
		handlerChan <- -1
	}()

	s, err := net.DialTCP("tcp", nil, ptInfo.OrAddr)
	if err != nil {
		logDebug("Failed to connect to ORPort: " + err.Error())
		return
	}

	proxy(s, &conn)
}

func startListener(addr *net.TCPAddr) (*net.TCPListener, error) {
	ln, err := net.ListenTCP("tcp", addr)
	if err != nil {
		return nil, err
	}
	go func() {
		var config websocketConfig
		config.Subprotocols = []string{"base64"}
		config.MaxMessageSize = 1500
		http.Handle("/", config.Handler(websocketHandler))
		err = http.Serve(ln, nil)
		if err != nil {
			panic("http.Serve: " + err.Error())
		}
	}()
	return ln, nil
}

func main() {
	const ptMethodName = "websocket"

	ptInfo = ptServerSetup([]string{ptMethodName})

	listeners := make([]*net.TCPListener, 0)
	for _, bindAddr := range ptInfo.BindAddrs {
		// When tor tells us a port of 0, we are supposed to pick a
		// random port. But we actually want to use the configured port.
		if bindAddr.Addr.Port == 0 {
			bindAddr.Addr.Port = defaultPort
		}

		ln, err := startListener(bindAddr.Addr)
		if err != nil {
			ptSmethodError(bindAddr.MethodName, err.Error())
		}
		ptSmethod(bindAddr.MethodName, ln.Addr())
		listeners = append(listeners, ln)
	}
	ptSmethodsDone()

	var numHandlers int = 0

	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, os.Interrupt)
	var sigint bool = false
	for !sigint {
		select {
		case n := <-handlerChan:
			numHandlers += n
		case <-signalChan:
			logDebug("SIGINT")
			sigint = true
		}
	}

	for _, ln := range listeners {
		ln.Close()
	}

	sigint = false
	for numHandlers != 0 && !sigint {
		select {
		case n := <-handlerChan:
			numHandlers += n
		case <-signalChan:
			logDebug("SIGINT")
			sigint = true
		}
	}
}
<commit_msg>Use a logDebug instead of a panic.<commit_after>package main

import (
	"encoding/base64"
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"os"
	"os/signal"
	"sync"
	"time"
)

const defaultPort = 9901

var ptInfo ptServerInfo

// When a connection handler starts, +1 is written to this channel; when it
// ends, -1 is written.
var handlerChan = make(chan int)

func logDebug(format string, v ...interface{}) {
	fmt.Fprintf(os.Stderr, format+"\n", v...)
}

type websocketConn struct {
	Ws *websocket
	Base64 bool
	messageBuf []byte
}

func (conn *websocketConn) Read(b []byte) (n int, err error) {
	for len(conn.messageBuf) == 0 {
		var m websocketMessage
		m, err = conn.Ws.ReadMessage()
		if err != nil {
			return
		}
		if conn.Base64 {
			if m.Opcode != 1 {
				err = errors.New(fmt.Sprintf("got non-text opcode %d with the base64 subprotocol", m.Opcode))
				return
			}
			conn.messageBuf = make([]byte, base64.StdEncoding.DecodedLen(len(m.Payload)))
			var num int
			num, err = base64.StdEncoding.Decode(conn.messageBuf, m.Payload)
			if err != nil {
				return
			}
			conn.messageBuf = conn.messageBuf[:num]
		} else {
			if m.Opcode != 2 {
				err = errors.New(fmt.Sprintf("got non-binary opcode %d with no subprotocol", m.Opcode))
				return
			}
			conn.messageBuf = m.Payload
		}
	}

	n = copy(b, conn.messageBuf)
	conn.messageBuf = conn.messageBuf[n:]

	return
}

func (conn *websocketConn) Write(b []byte) (n int, err error) {
	if conn.Base64 {
		buf := make([]byte, base64.StdEncoding.EncodedLen(len(b)))
		base64.StdEncoding.Encode(buf, b)
		err = conn.Ws.WriteMessage(1, buf)
		if err != nil {
			return
		}
		n = len(b)
	} else {
		err = conn.Ws.WriteMessage(2, b)
		n = len(b)
	}
	return
}

func (conn *websocketConn) Close() (err error) {
	err = conn.Ws.WriteFrame(8, nil)
	if err != nil {
		conn.Ws.Conn.Close()
		return
	}
	err = conn.Ws.Conn.Close()
	return
}

func (conn *websocketConn) LocalAddr() net.Addr {
	return conn.Ws.Conn.LocalAddr()
}

func (conn *websocketConn) RemoteAddr() net.Addr {
	return conn.Ws.Conn.RemoteAddr()
}

func (conn *websocketConn) SetDeadline(t time.Time) error {
	return conn.Ws.Conn.SetDeadline(t)
}

func (conn *websocketConn) SetReadDeadline(t time.Time) error {
	return conn.Ws.Conn.SetReadDeadline(t)
}

func (conn *websocketConn) SetWriteDeadline(t time.Time) error {
	return conn.Ws.Conn.SetWriteDeadline(t)
}

func NewWebsocketConn(ws *websocket) websocketConn {
	var conn websocketConn
	conn.Ws = ws
	conn.Base64 = (ws.Subprotocol == "base64")
	return conn
}

func proxy(local *net.TCPConn, conn *websocketConn) {
	var wg sync.WaitGroup

	wg.Add(2)

	go func() {
		_, err := io.Copy(conn, local)
		if err != nil {
			logDebug("error copying ORPort to WebSocket: " + err.Error())
		}
		local.CloseRead()
		conn.Close()
		wg.Done()
	}()

	go func() {
		_, err := io.Copy(local, conn)
		if err != nil {
			logDebug("error copying WebSocket to ORPort: " + err.Error())
		}
		local.CloseWrite()
		conn.Close()
		wg.Done()
	}()

	wg.Wait()
}

func websocketHandler(ws *websocket) {
	conn := NewWebsocketConn(ws)

	handlerChan <- 1
	defer func() {
		handlerChan <- -1
	}()

	s, err := net.DialTCP("tcp", nil, ptInfo.OrAddr)
	if err != nil {
		logDebug("Failed to connect to ORPort: " + err.Error())
		return
	}

	proxy(s, &conn)
}

func startListener(addr *net.TCPAddr) (*net.TCPListener, error) {
	ln, err := net.ListenTCP("tcp", addr)
	if err != nil {
		return nil, err
	}
	go func() {
		var config websocketConfig
		config.Subprotocols = []string{"base64"}
		config.MaxMessageSize = 1500
		http.Handle("/", config.Handler(websocketHandler))
		err = http.Serve(ln, nil)
		if err != nil {
			logDebug("http.Serve: " + err.Error())
		}
	}()
	return ln, nil
}

func main() {
	const ptMethodName = "websocket"

	ptInfo = ptServerSetup([]string{ptMethodName})

	listeners := make([]*net.TCPListener, 0)
	for _, bindAddr := range ptInfo.BindAddrs {
		// When tor tells us a port of 0, we are supposed to pick a
		// random port. But we actually want to use the configured port.
		if bindAddr.Addr.Port == 0 {
			bindAddr.Addr.Port = defaultPort
		}

		ln, err := startListener(bindAddr.Addr)
		if err != nil {
			ptSmethodError(bindAddr.MethodName, err.Error())
		}
		ptSmethod(bindAddr.MethodName, ln.Addr())
		listeners = append(listeners, ln)
	}
	ptSmethodsDone()

	var numHandlers int = 0

	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, os.Interrupt)
	var sigint bool = false
	for !sigint {
		select {
		case n := <-handlerChan:
			numHandlers += n
		case <-signalChan:
			logDebug("SIGINT")
			sigint = true
		}
	}

	for _, ln := range listeners {
		ln.Close()
	}

	sigint = false
	for numHandlers != 0 && !sigint {
		select {
		case n := <-handlerChan:
			numHandlers += n
		case <-signalChan:
			logDebug("SIGINT")
			sigint = true
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/hex"
	"log"
	"sync"

	"github.com/stamp/go-lifx/client"
	"github.com/stampzilla/stampzilla-go/protocol/devices"
)

// State contains the lifx node state
type State struct {
	Lamps       map[string]*Lamp `json:"lamps"`
	LifxClound  StateLifxCloud   `json:"lifx_clound"`
	LanProtocol StateLanProtocol `json:"lan_protocol"`

	publishStateFunc func()

	sync.Mutex
}

// Lamp is the state for each found lamp. Can be sourced from both lan and cloud protocols
type Lamp struct {
	ID   string `json:"id"`
	Name string `json:"name"`
	IP   string `json:"ip"`

	Level float64    `json:"level"`
	Color StateColor `json:"color"`
	Power bool       `json:"power"`

	LanConnected   bool `json:"connected_lan"`
	CloudConnected bool `json:"connected_cloud"`

	Capabilities map[string]bool `json:"capabilities"`

	publishState func()
	sync.Mutex
}

// StateColor describes the current color at the lifx lamp
type StateColor struct {
	Hue        float64 `json:"hue"`
	Saturation float64 `json:"saturation"`
	Kelvin     int     `json:"kelvin"`
}

// StateLifxCloud is the current state of the Lifx cloud poller
type StateLifxCloud struct {
	State         string `json:"state"`
	FoundDevices  int    `json:"found_devices"`
	OnlineDevices int    `json:"online_devices"`
}

// StateLanProtocol is the current state of the Lifx lan protocol
type StateLanProtocol struct {
	State         string `json:"state"`
	FoundDevices  int    `json:"found_devices"`
	OnlineDevices int    `json:"online_devices"`
}

// NewState creates a new State struct and initializes all values
func NewState() *State {
	return &State{Lamps: make(map[string]*Lamp), publishStateFunc: func() {}}
}

// Lamp Fetches a lamp from the state list based on id [4]byte
func (s *State) Lamp(id [4]byte) *Lamp {
	senderID := hex.EncodeToString(id[0:4])
	s.Lock()
	defer s.Unlock()
	if _, ok := s.Lamps[senderID]; ok {
		return s.Lamps[senderID]
	}
	return nil
}

// GetByID fetches a lamp from the state list based on id string
func (s *State) GetByID(id string) *Lamp {
	s.Lock()
	defer s.Unlock()
	if _, ok := s.Lamps[id]; ok {
		return s.Lamps[id]
	}

	return nil
}

// AddLanDevice checks if the device already exists, else adds a new one based on the information provided
func (s *State) AddLanDevice(light *client.Light) *Lamp {
	d := s.GetByID(light.Id())
	if d == nil {
		d = NewLamp(light.Id(), light.Label(), light.Ip.String())
		d.LanConnected = true
		return s.Add(d)
	}

	d.LanConnected = true

	return d
}

// Add a Lamp struct to the lamps list
func (s *State) Add(d *Lamp) *Lamp {
	log.Println("Added new ", d.ID)
	d.publishState = s.publishState

	s.Lock()
	defer s.Unlock()
	defer s.publishState()

	s.Lamps[d.ID] = d
	return d
}

// RemoveDevice removes a lamp that are no longer available from the list
func (s *State) RemoveDevice(id [4]byte) {
	s.Lock()
	defer s.Unlock()
	defer s.publishState()

	senderID := hex.EncodeToString(id[0:4])
	delete(s.Lamps, senderID)
}

func (s *State) publishState() {
	for _, v := range s.Lamps {
		node.Devices().Add(&devices.Device{
			Type:   "dimmableLamp",
			Name:   v.Name,
			Id:     v.ID,
			Online: v.LanConnected || v.CloudConnected,
			StateMap: map[string]string{
				"on":    "lamps[" + v.ID + "]" + ".power",
				"level": "lamps[" + v.ID + "]" + ".level",
			},
		})
	}

	s.publishStateFunc()
}

// -----------------------------------------------------------

// NewLamp initiates a new Lamp type
func NewLamp(id, name, ip string) *Lamp {
	d := &Lamp{Name: name, IP: ip}
	d.SetID(id)
	return d
}

// SetID setter for the idf field
func (d *Lamp) SetID(id string) {
	d.Lock()
	defer d.Unlock()
	d.ID = id

}

// SyncFromCloud syncronizes data from a cloudGetAllReponse struct received from the lifx cloud
func (d *Lamp) SyncFromCloud(c *cloudGetAllResponse) {
	d.Level = c.Brightness * 100
	d.Color.Hue = c.Color.Hue
	d.Color.Saturation = c.Color.Saturation
	d.Color.Kelvin = c.Color.Kelvin
	d.Capabilities = c.Product.Capabilites
	d.CloudConnected = c.Connected
	d.Power = c.Power == "on"

	defer d.publishState()
}

// SyncFromLan syncronizes data from the lan protocol
func (d *Lamp) SyncFromLan(c *client.Light) {
	d.LanConnected = true
	d.IP = c.Ip.String()

	defer d.publishState()
}
<commit_msg>Hopefully fixes #85<commit_after>package main

import (
	"encoding/hex"
	"encoding/json"
	"log"
	"sync"

	"github.com/stamp/go-lifx/client"
	"github.com/stampzilla/stampzilla-go/protocol/devices"
)

// State contains the lifx node state
type State struct {
	Lamps       map[string]*Lamp `json:"lamps"`
	LifxClound  StateLifxCloud   `json:"lifx_clound"`
	LanProtocol StateLanProtocol `json:"lan_protocol"`

	publishStateFunc func()

	sync.RWMutex
}

// Lamp is the state for each found lamp. Can be sourced from both lan and cloud protocols
type Lamp struct {
	ID   string `json:"id"`
	Name string `json:"name"`
	IP   string `json:"ip"`

	Level float64    `json:"level"`
	Color StateColor `json:"color"`
	Power bool       `json:"power"`

	LanConnected   bool `json:"connected_lan"`
	CloudConnected bool `json:"connected_cloud"`

	Capabilities map[string]bool `json:"capabilities"`

	publishState func()
	sync.Mutex
}

// StateColor describes the current color at the lifx lamp
type StateColor struct {
	Hue        float64 `json:"hue"`
	Saturation float64 `json:"saturation"`
	Kelvin     int     `json:"kelvin"`
}

// StateLifxCloud is the current state of the Lifx cloud poller
type StateLifxCloud struct {
	State         string `json:"state"`
	FoundDevices  int    `json:"found_devices"`
	OnlineDevices int    `json:"online_devices"`
}

// StateLanProtocol is the current state of the Lifx lan protocol
type StateLanProtocol struct {
	State         string `json:"state"`
	FoundDevices  int    `json:"found_devices"`
	OnlineDevices int    `json:"online_devices"`
}

// NewState creates a new State struct and initializes all values
func NewState() *State {
	return &State{Lamps: make(map[string]*Lamp), publishStateFunc: func() {}}
}

// Lamp Fetches a lamp from the state list based on id [4]byte
func (s *State) Lamp(id [4]byte) *Lamp {
	senderID := hex.EncodeToString(id[0:4])
	s.Lock()
	defer s.Unlock()
	if _, ok := s.Lamps[senderID]; ok {
		return s.Lamps[senderID]
	}
	return nil
}

// GetByID fetches a lamp from the state list based on id string
func (s *State) GetByID(id string) *Lamp {
	s.Lock()
	defer s.Unlock()
	if _, ok := s.Lamps[id]; ok {
		return s.Lamps[id]
	}

	return nil
}

// AddLanDevice checks if the device already exists, else adds a new one based on the information provided
func (s *State) AddLanDevice(light *client.Light) *Lamp {
	d := s.GetByID(light.Id())
	if d == nil {
		d = NewLamp(light.Id(), light.Label(), light.Ip.String())
		d.LanConnected = true
		return s.Add(d)
	}

	d.LanConnected = true

	return d
}

// Add a Lamp struct to the lamps list
func (s *State) Add(d *Lamp) *Lamp {
	log.Println("Added new ", d.ID)
	d.publishState = s.publishState

	s.Lock()
	defer s.Unlock()
	defer s.publishState()

	s.Lamps[d.ID] = d
	return d
}

// RemoveDevice removes a lamp that are no longer available from the list
func (s *State) RemoveDevice(id [4]byte) {
	s.Lock()
	defer s.Unlock()
	defer s.publishState()

	senderID := hex.EncodeToString(id[0:4])
	delete(s.Lamps, senderID)
}

func (s *State) publishState() {
	for _, v := range s.Lamps {
		node.Devices().Add(&devices.Device{
			Type:   "dimmableLamp",
			Name:   v.Name,
			Id:     v.ID,
			Online: v.LanConnected || v.CloudConnected,
			StateMap: map[string]string{
				"on":    "lamps[" + v.ID + "]" + ".power",
				"level": "lamps[" + v.ID + "]" + ".level",
			},
		})
	}

	s.publishStateFunc()
}

func (s *State) MarshalJSON() ([]byte, error) {
	type alias State

	s.RLock()
	defer s.RUnlock()
	return json.Marshal(alias(*s))
}

// -----------------------------------------------------------

// NewLamp initiates a new Lamp type
func NewLamp(id, name, ip string) *Lamp {
	d := &Lamp{Name: name, IP: ip}
	d.SetID(id)
	return d
}

// SetID setter for the idf field
func (d *Lamp) SetID(id string) {
	d.Lock()
	defer d.Unlock()
	d.ID = id

}

// SyncFromCloud syncronizes data from a cloudGetAllReponse struct received from the lifx cloud
func (d *Lamp) SyncFromCloud(c *cloudGetAllResponse) {
	d.Level = c.Brightness * 100
	d.Color.Hue = c.Color.Hue
	d.Color.Saturation = c.Color.Saturation
	d.Color.Kelvin = c.Color.Kelvin
	d.Capabilities = c.Product.Capabilites
	d.CloudConnected = c.Connected
	d.Power = c.Power == "on"

	defer d.publishState()
}

// SyncFromLan syncronizes data from the lan protocol
func (d *Lamp) SyncFromLan(c *client.Light) {
	d.LanConnected = true
	d.IP = c.Ip.String()

	defer d.publishState()
}
<|endoftext|>


<commit_before>package aqua

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/carbocation/interpose"
	"github.com/gorilla/mux"
	"github.com/tolexo/aero/auth"
	"github.com/tolexo/aero/cache"
	monit "github.com/tolexo/aero/monit"
	"github.com/tolexo/aero/panik"
)

type endPoint struct {
	caller     MethodInvoker
	info       Fixture
	httpMethod string

	isStdHttpHandler bool
	needsJarInput    bool

	muxUrl    string
	muxVars   []string
	modules   []func(http.Handler) http.Handler
	stash     cache.Cacher
	serviceId string
}

func NewEndPoint(inv MethodInvoker, f Fixture, matchUrl string, httpMethod string, mods map[string]func(http.Handler) http.Handler,
	caches map[string]cache.Cacher, serviceId string) endPoint {

	out := endPoint{
		caller:           inv,
		info:             f,
		isStdHttpHandler: false,
		needsJarInput:    false,
		muxUrl:           matchUrl,
		muxVars:          extractRouteVars(matchUrl),
		httpMethod:       httpMethod,
		modules:          make([]func(http.Handler) http.Handler, 0),
		stash:            nil,
		serviceId:        serviceId,
	}

	if f.Stub == "" {
		out.isStdHttpHandler = out.signatureMatchesDefaultHttpHandler()
		out.needsJarInput = out.needsVariableJar()

		out.validateMuxVarsMatchFuncInputs()
		out.validateFuncInputsAreOfRightType()
		out.validateFuncOutputsAreCorrect()
	}

	// Tag modules used by this endpoint
	if mods != nil && f.Modules != "" {
		names := strings.Split(f.Modules, ",")
		out.modules = make([]func(http.Handler) http.Handler, 0)
		for _, name := range names {
			name = strings.TrimSpace(name)
			fn, found := mods[name]
			if !found {
				panic(fmt.Sprintf("Module:%s not found", name))
			}
			out.modules = append(out.modules, fn)
		}
	}

	// Tag the cache
	if c, ok := caches[f.Cache]; ok {
		out.stash = c
	} else if f.Cache != "" {
		panic("Cache not found: " + f.Cache + " for " + matchUrl)
	}

	return out
}

func (me *endPoint) signatureMatchesDefaultHttpHandler() bool {
	return me.caller.outCount == 0 &&
		me.caller.inpCount == 2 &&
		me.caller.inpParams[0] == "i:net/http.ResponseWriter" &&
		me.caller.inpParams[1] == "*st:net/http.Request"
}

func (me *endPoint) needsVariableJar() bool {
	// needs jar input as the last parameter
	for i := 0; i < len(me.caller.inpParams)-1; i++ {
		if me.caller.inpParams[i] == "st:github.com/tolexo/aqua.Jar" {
			panic("Jar parameter should be the last one: " + me.caller.name)
		}
	}
	return me.caller.inpCount > 0 && me.caller.inpParams[me.caller.inpCount-1] == "st:github.com/tolexo/aqua.Jar"
}

func (me *endPoint) validateMuxVarsMatchFuncInputs() {
	// for non-standard http handlers, the mux vars count should match
	// the count of inputs to the user's method
	if !me.isStdHttpHandler {
		inputs := me.caller.inpCount
		if me.needsJarInput {
			inputs += -1
		}
		if len(me.muxVars) != inputs {
			panic(fmt.Sprintf("%s has %d inputs, but the func (%s) has %d",
				me.muxUrl, len(me.muxVars), me.caller.name, inputs))
		}
	}
}

func (me *endPoint) validateFuncInputsAreOfRightType() {
	if !me.isStdHttpHandler {
		for _, s := range me.caller.inpParams {
			switch s {
			case "st:github.com/tolexo/aqua.Jar":
			case "int":
			case "string":
			default:
				panic("Func input params should be 'int' or 'string'. Observed: " + s + " in: " + me.caller.name)
			}
		}
	}
}

func (me *endPoint) validateFuncOutputsAreCorrect() {

	var accepts = make(map[string]bool)
	accepts["string"] = true
	accepts["map"] = true
	accepts["st:github.com/tolexo/aqua.Sac"] = true
	accepts["*st:github.com/tolexo/aqua.Sac"] = true

	if !me.isStdHttpHandler {
		switch me.caller.outCount {
		case 1:
			_, found := accepts[me.caller.outParams[0]]
			if !found && !strings.HasPrefix(me.caller.outParams[0], "st:") {
				fmt.Println(me.caller.outParams[0])
				panic("Incorrect return type found in: " + me.caller.name)
			}
		case 2:
			if me.caller.outParams[0] != "int" {
				panic("When a func returns two params, the first must be an int (http status code) : " + me.caller.name)
			}
			_, found := accepts[me.caller.outParams[1]]
			if !found && !strings.HasPrefix(me.caller.outParams[1], "st:") {
				panic("Incorrect return type for second return param found in: " + me.caller.name)
			}
		default:
			panic("Incorrect number of returns for Func: " + me.caller.name)
		}
	}
}

// func middleman(next http.Handler) http.Handler {
// 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 		fmt.Println("In the middle >>>>")
// 		next.ServeHTTP(w, r)
// 		fmt.Println("And leaving middle <<<<")
// 	})
// }

func (me *endPoint) setupMuxHandlers(mux *mux.Router) {

	fn := handleIncoming(me)

	m := interpose.New()
	for i, _ := range me.modules {
		m.Use(me.modules[i])
		//fmt.Println("using module:", me.modules[i], reflect.TypeOf(me.modules[i]))
	}
	m.UseHandler(http.HandlerFunc(fn))

	if me.info.Version == "*" {
		mux.Handle(me.muxUrl, m).Methods(me.httpMethod)
	} else {
		urlWithVersion := cleanUrl(me.info.Prefix, "v"+me.info.Version, me.muxUrl)
		urlWithoutVersion := cleanUrl(me.info.Prefix, me.muxUrl)

		// versioned url
		mux.Handle(urlWithVersion, m).Methods(me.httpMethod)

		// content type (style1)
		header1 := fmt.Sprintf("application/%s-v%s+json", me.info.Vendor, me.info.Version)
		mux.Handle(urlWithoutVersion, m).Methods(me.httpMethod).Headers("Accept", header1)

		// content type (style2)
		header2 := fmt.Sprintf("application/%s+json;version=%s", me.info.Vendor, me.info.Version)
		mux.Handle(urlWithoutVersion, m).Methods(me.httpMethod).Headers("Accept", header2)
	}
}

func handleIncoming(e *endPoint) func(http.ResponseWriter, *http.Request) {

	// return stub
	if e.info.Stub != "" {
		return func(w http.ResponseWriter, r *http.Request) {
			d, err := getContent(e.info.Stub)
			if err == nil {
				fmt.Fprintf(w, "%s", d)
			} else {
				w.WriteHeader(400)
				fmt.Fprintf(w, "{ message: \"%s\"}", "Stub path not found")
			}
		}
	}

	return func(w http.ResponseWriter, r *http.Request) {

		cacheHit := false

		// TODO: create less local variables
		// TODO: move vars to closure level

		var out []reflect.Value
		//TODO: capture this using instrumentation handler
		defer func(reqStartTime time.Time) {
			go func() {
				if e.serviceId != "" {
					respTime := time.Since(reqStartTime).Seconds() * 1000
					var responseCode int64 = 200
					if out != nil && len(out) == 2 && e.caller.outParams[0] == "int" {
						responseCode = out[0].Int()
					}
					monitorParams := monit.MonitorParams{
						ServiceId:    e.serviceId,
						RespTime:     respTime,
						ResponseCode: responseCode,
						CacheHit:     cacheHit,
					}
					monit.MonitorMe(monitorParams)
				}
			}()
			if reqR := recover(); reqR != nil {
				monit.PanicLogger(reqR, e.serviceId, r.RequestURI, time.Now().UTC().Round(time.Second))
			}
		}(time.Now())

		//check authentication
		if e.info.Auth != "" {
			ok, errMsg := auth.AuthenticateRequest(r, e.info.Auth)
			if !ok { //print authentication error
				w.WriteHeader(401)
				w.Header().Set("Content-Type", "application/json")
				w.Header().Set("Content-Length", strconv.Itoa(len(errMsg)))
				fmt.Fprintf(w, "%s", errMsg)
				return
			}
		}

		var useCache bool = false
		var ttl time.Duration = 0 * time.Second
		var val []byte
		var err error

		if e.info.Ttl != "" {
			ttl, err = time.ParseDuration(e.info.Ttl)
			panik.On(err)
		}
		useCache = r.Method == "GET" && ttl > 0 && e.stash != nil

		muxVals := mux.Vars(r)
		params := make([]string, len(e.muxVars))
		for i, v := range e.muxVars {
			params[i] = muxVals[v]
		}

		if e.isStdHttpHandler {
			//TODO: caching of standard handler
			e.caller.Do([]reflect.Value{reflect.ValueOf(w), reflect.ValueOf(r)})
		} else {
			ref := convertToType(params, e.caller.inpParams)
			if e.needsJarInput {
				ref = append(ref, reflect.ValueOf(NewJar(r)))
			}

			if useCache {
				val, err = e.stash.Get(r.RequestURI)
				if err == nil {
					cacheHit = true
					// fmt.Print(".")
					out = decomposeCachedValues(val, e.caller.outParams)
				} else {
					out = e.caller.Do(ref)
					if len(out) == 2 && e.caller.outParams[0] == "int" {
						code := out[0].Int()
						if code < 200 || code > 299 {
							useCache = false
						}
					}
					if useCache {
						bytes := prepareForCaching(out, e.caller.outParams)
						e.stash.Set(r.RequestURI, bytes, ttl)
						// fmt.Print(":", len(bytes), r.RequestURI)
					}

				}
			} else {
				out = e.caller.Do(ref)
				// fmt.Print("!")
			}
			writeOutput(w, e.caller.outParams, out, e.info.Pretty)
		}
	}
}

func prepareForCaching(r []reflect.Value, outputParams []string) []byte {

	var err error
	buf := new(bytes.Buffer)
	encd := json.NewEncoder(buf)

	for i, _ := range r {
		switch outputParams[i] {
		case "int":
			err = encd.Encode(r[i].Int())
			panik.On(err)
		case "map":
			err = encd.Encode(r[i].Interface().(map[string]interface{}))
			panik.On(err)
		case "string":
			err = encd.Encode(r[i].String())
			panik.On(err)
		case "*st:github.com/tolexo/aqua.Sac":
			err = encd.Encode(r[i].Elem().Interface().(Sac).Data)
		default:
			panic("Unknown type of output to be sent to endpoint cache: " + outputParams[i])
		}
	}

	return buf.Bytes()
}

func decomposeCachedValues(data []byte, outputParams []string) []reflect.Value {

	var err error
	buf := bytes.NewBuffer(data)
	decd := json.NewDecoder(buf)
	out := make([]reflect.Value, len(outputParams))

	for i, o := range outputParams {
		switch o {
		case "int":
			var j int
			err = decd.Decode(&j)
			panik.On(err)
			out[i] = reflect.ValueOf(j)
		case "map":
			var m map[string]interface{}
			err = decd.Decode(&m)
			panik.On(err)
			out[i] = reflect.ValueOf(m)
		case "string":
			var s string
			err = decd.Decode(&s)
			panik.On(err)
			out[i] = reflect.ValueOf(s)
		case "*st:github.com/tolexo/aqua.Sac":
			var m map[string]interface{}
			err = decd.Decode(&m)
			panik.On(err)
			s := NewSac()
			s.Data = m
			out[i] = reflect.ValueOf(s)
		default:
			panic("Unknown type of output to be decoded from endpoint cache:" + o)
		}
	}

	return out

}
<commit_msg>TTA-226A changed time to ist<commit_after>package aqua

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/carbocation/interpose"
	"github.com/gorilla/mux"
	"github.com/tolexo/aero/auth"
	"github.com/tolexo/aero/cache"
	monit "github.com/tolexo/aero/monit"
	"github.com/tolexo/aero/panik"
)

type endPoint struct {
	caller     MethodInvoker
	info       Fixture
	httpMethod string

	isStdHttpHandler bool
	needsJarInput    bool

	muxUrl    string
	muxVars   []string
	modules   []func(http.Handler) http.Handler
	stash     cache.Cacher
	serviceId string
}

func NewEndPoint(inv MethodInvoker, f Fixture, matchUrl string, httpMethod string, mods map[string]func(http.Handler) http.Handler,
	caches map[string]cache.Cacher, serviceId string) endPoint {

	out := endPoint{
		caller:           inv,
		info:             f,
		isStdHttpHandler: false,
		needsJarInput:    false,
		muxUrl:           matchUrl,
		muxVars:          extractRouteVars(matchUrl),
		httpMethod:       httpMethod,
		modules:          make([]func(http.Handler) http.Handler, 0),
		stash:            nil,
		serviceId:        serviceId,
	}

	if f.Stub == "" {
		out.isStdHttpHandler = out.signatureMatchesDefaultHttpHandler()
		out.needsJarInput = out.needsVariableJar()

		out.validateMuxVarsMatchFuncInputs()
		out.validateFuncInputsAreOfRightType()
		out.validateFuncOutputsAreCorrect()
	}

	// Tag modules used by this endpoint
	if mods != nil && f.Modules != "" {
		names := strings.Split(f.Modules, ",")
		out.modules = make([]func(http.Handler) http.Handler, 0)
		for _, name := range names {
			name = strings.TrimSpace(name)
			fn, found := mods[name]
			if !found {
				panic(fmt.Sprintf("Module:%s not found", name))
			}
			out.modules = append(out.modules, fn)
		}
	}

	// Tag the cache
	if c, ok := caches[f.Cache]; ok {
		out.stash = c
	} else if f.Cache != "" {
		panic("Cache not found: " + f.Cache + " for " + matchUrl)
	}

	return out
}

func (me *endPoint) signatureMatchesDefaultHttpHandler() bool {
	return me.caller.outCount == 0 &&
		me.caller.inpCount == 2 &&
		me.caller.inpParams[0] == "i:net/http.ResponseWriter" &&
		me.caller.inpParams[1] == "*st:net/http.Request"
}

func (me *endPoint) needsVariableJar() bool {
	// needs jar input as the last parameter
	for i := 0; i < len(me.caller.inpParams)-1; i++ {
		if me.caller.inpParams[i] == "st:github.com/tolexo/aqua.Jar" {
			panic("Jar parameter should be the last one: " + me.caller.name)
		}
	}
	return me.caller.inpCount > 0 && me.caller.inpParams[me.caller.inpCount-1] == "st:github.com/tolexo/aqua.Jar"
}

func (me *endPoint) validateMuxVarsMatchFuncInputs() {
	// for non-standard http handlers, the mux vars count should match
	// the count of inputs to the user's method
	if !me.isStdHttpHandler {
		inputs := me.caller.inpCount
		if me.needsJarInput {
			inputs += -1
		}
		if len(me.muxVars) != inputs {
			panic(fmt.Sprintf("%s has %d inputs, but the func (%s) has %d",
				me.muxUrl, len(me.muxVars), me.caller.name, inputs))
		}
	}
}

func (me *endPoint) validateFuncInputsAreOfRightType() {
	if !me.isStdHttpHandler {
		for _, s := range me.caller.inpParams {
			switch s {
			case "st:github.com/tolexo/aqua.Jar":
			case "int":
			case "string":
			default:
				panic("Func input params should be 'int' or 'string'. Observed: " + s + " in: " + me.caller.name)
			}
		}
	}
}

func (me *endPoint) validateFuncOutputsAreCorrect() {

	var accepts = make(map[string]bool)
	accepts["string"] = true
	accepts["map"] = true
	accepts["st:github.com/tolexo/aqua.Sac"] = true
	accepts["*st:github.com/tolexo/aqua.Sac"] = true

	if !me.isStdHttpHandler {
		switch me.caller.outCount {
		case 1:
			_, found := accepts[me.caller.outParams[0]]
			if !found && !strings.HasPrefix(me.caller.outParams[0], "st:") {
				fmt.Println(me.caller.outParams[0])
				panic("Incorrect return type found in: " + me.caller.name)
			}
		case 2:
			if me.caller.outParams[0] != "int" {
				panic("When a func returns two params, the first must be an int (http status code) : " + me.caller.name)
			}
			_, found := accepts[me.caller.outParams[1]]
			if !found && !strings.HasPrefix(me.caller.outParams[1], "st:") {
				panic("Incorrect return type for second return param found in: " + me.caller.name)
			}
		default:
			panic("Incorrect number of returns for Func: " + me.caller.name)
		}
	}
}

// func middleman(next http.Handler) http.Handler {
// 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 		fmt.Println("In the middle >>>>")
// 		next.ServeHTTP(w, r)
// 		fmt.Println("And leaving middle <<<<")
// 	})
// }

func (me *endPoint) setupMuxHandlers(mux *mux.Router) {

	fn := handleIncoming(me)

	m := interpose.New()
	for i, _ := range me.modules {
		m.Use(me.modules[i])
		//fmt.Println("using module:", me.modules[i], reflect.TypeOf(me.modules[i]))
	}
	m.UseHandler(http.HandlerFunc(fn))

	if me.info.Version == "*" {
		mux.Handle(me.muxUrl, m).Methods(me.httpMethod)
	} else {
		urlWithVersion := cleanUrl(me.info.Prefix, "v"+me.info.Version, me.muxUrl)
		urlWithoutVersion := cleanUrl(me.info.Prefix, me.muxUrl)

		// versioned url
		mux.Handle(urlWithVersion, m).Methods(me.httpMethod)

		// content type (style1)
		header1 := fmt.Sprintf("application/%s-v%s+json", me.info.Vendor, me.info.Version)
		mux.Handle(urlWithoutVersion, m).Methods(me.httpMethod).Headers("Accept", header1)

		// content type (style2)
		header2 := fmt.Sprintf("application/%s+json;version=%s", me.info.Vendor, me.info.Version)
		mux.Handle(urlWithoutVersion, m).Methods(me.httpMethod).Headers("Accept", header2)
	}
}

func handleIncoming(e *endPoint) func(http.ResponseWriter, *http.Request) {

	// return stub
	if e.info.Stub != "" {
		return func(w http.ResponseWriter, r *http.Request) {
			d, err := getContent(e.info.Stub)
			if err == nil {
				fmt.Fprintf(w, "%s", d)
			} else {
				w.WriteHeader(400)
				fmt.Fprintf(w, "{ message: \"%s\"}", "Stub path not found")
			}
		}
	}

	return func(w http.ResponseWriter, r *http.Request) {

		cacheHit := false

		// TODO: create less local variables
		// TODO: move vars to closure level

		var out []reflect.Value
		//TODO: capture this using instrumentation handler
		defer func(reqStartTime time.Time) {
			go func() {
				if e.serviceId != "" {
					respTime := time.Since(reqStartTime).Seconds() * 1000
					var responseCode int64 = 200
					if out != nil && len(out) == 2 && e.caller.outParams[0] == "int" {
						responseCode = out[0].Int()
					}
					monitorParams := monit.MonitorParams{
						ServiceId:    e.serviceId,
						RespTime:     respTime,
						ResponseCode: responseCode,
						CacheHit:     cacheHit,
					}
					monit.MonitorMe(monitorParams)
				}
			}()
			if reqR := recover(); reqR != nil {
				monit.PanicLogger(reqR, e.serviceId, r.RequestURI, time.Now())
			}
		}(time.Now())

		//check authentication
		if e.info.Auth != "" {
			ok, errMsg := auth.AuthenticateRequest(r, e.info.Auth)
			if !ok { //print authentication error
				w.WriteHeader(401)
				w.Header().Set("Content-Type", "application/json")
				w.Header().Set("Content-Length", strconv.Itoa(len(errMsg)))
				fmt.Fprintf(w, "%s", errMsg)
				return
			}
		}

		var useCache bool = false
		var ttl time.Duration = 0 * time.Second
		var val []byte
		var err error

		if e.info.Ttl != "" {
			ttl, err = time.ParseDuration(e.info.Ttl)
			panik.On(err)
		}
		useCache = r.Method == "GET" && ttl > 0 && e.stash != nil

		muxVals := mux.Vars(r)
		params := make([]string, len(e.muxVars))
		for i, v := range e.muxVars {
			params[i] = muxVals[v]
		}

		if e.isStdHttpHandler {
			//TODO: caching of standard handler
			e.caller.Do([]reflect.Value{reflect.ValueOf(w), reflect.ValueOf(r)})
		} else {
			ref := convertToType(params, e.caller.inpParams)
			if e.needsJarInput {
				ref = append(ref, reflect.ValueOf(NewJar(r)))
			}

			if useCache {
				val, err = e.stash.Get(r.RequestURI)
				if err == nil {
					cacheHit = true
					// fmt.Print(".")
					out = decomposeCachedValues(val, e.caller.outParams)
				} else {
					out = e.caller.Do(ref)
					if len(out) == 2 && e.caller.outParams[0] == "int" {
						code := out[0].Int()
						if code < 200 || code > 299 {
							useCache = false
						}
					}
					if useCache {
						bytes := prepareForCaching(out, e.caller.outParams)
						e.stash.Set(r.RequestURI, bytes, ttl)
						// fmt.Print(":", len(bytes), r.RequestURI)
					}

				}
			} else {
				out = e.caller.Do(ref)
				// fmt.Print("!")
			}
			writeOutput(w, e.caller.outParams, out, e.info.Pretty)
		}
	}
}

func prepareForCaching(r []reflect.Value, outputParams []string) []byte {

	var err error
	buf := new(bytes.Buffer)
	encd := json.NewEncoder(buf)

	for i, _ := range r {
		switch outputParams[i] {
		case "int":
			err = encd.Encode(r[i].Int())
			panik.On(err)
		case "map":
			err = encd.Encode(r[i].Interface().(map[string]interface{}))
			panik.On(err)
		case "string":
			err = encd.Encode(r[i].String())
			panik.On(err)
		case "*st:github.com/tolexo/aqua.Sac":
			err = encd.Encode(r[i].Elem().Interface().(Sac).Data)
		default:
			panic("Unknown type of output to be sent to endpoint cache: " + outputParams[i])
		}
	}

	return buf.Bytes()
}

func decomposeCachedValues(data []byte, outputParams []string) []reflect.Value {

	var err error
	buf := bytes.NewBuffer(data)
	decd := json.NewDecoder(buf)
	out := make([]reflect.Value, len(outputParams))

	for i, o := range outputParams {
		switch o {
		case "int":
			var j int
			err = decd.Decode(&j)
			panik.On(err)
			out[i] = reflect.ValueOf(j)
		case "map":
			var m map[string]interface{}
			err = decd.Decode(&m)
			panik.On(err)
			out[i] = reflect.ValueOf(m)
		case "string":
			var s string
			err = decd.Decode(&s)
			panik.On(err)
			out[i] = reflect.ValueOf(s)
		case "*st:github.com/tolexo/aqua.Sac":
			var m map[string]interface{}
			err = decd.Decode(&m)
			panik.On(err)
			s := NewSac()
			s.Data = m
			out[i] = reflect.ValueOf(s)
		default:
			panic("Unknown type of output to be decoded from endpoint cache:" + o)
		}
	}

	return out

}
<|endoftext|>


<commit_before>package replica

import (
	"fmt"
	cmd "github.com/go-epaxos/epaxos/command"
)

var _ = fmt.Printf

func (r *Replica) recvPropose(propose *Propose, messageChan chan Message) {
	deps := r.findDependencies(propose.cmds)

	// increment instance number
	instNo := r.MaxInstanceNum[r.Id]
	instNo = instNo
	r.MaxInstanceNum[r.Id]++

	// set cmds
	r.InstanceMatrix[r.Id][instNo] = &Instance{
		cmds:   propose.cmds,
		deps:   deps,
		status: preaccepted,
		ballot: r.makeInitialBallot(),
		info:   NewInstanceInfo(),
	}

	// TODO: before we send the message, we need to record and sync it in disk/persistent.

	// send PreAccept
	preAccept := &PreAccept{
		cmds:  propose.cmds,
		deps:  deps,
		repId: r.Id,
		insId: instNo,
	}

	// fast quorum
	go func() {
		for i := 0; i < r.fastQuorumSize(); i++ {
			messageChan <- preAccept
		}
	}()

}

func (r *Replica) recvPreAccept(preAccept *PreAccept, messageChan chan Message) {
	// TODO: we need to check ballot for that coming from prepare phase
	// update
	deps, changed := r.updateDependencies(preAccept.cmds, preAccept.deps, preAccept.repId)
	// set cmd
	r.InstanceMatrix[preAccept.repId][preAccept.insId] = &Instance{
		cmds:   preAccept.cmds,
		deps:   deps,
		status: preaccepted,
		info:   NewInstanceInfo(),
	}
	if preAccept.insId >= r.MaxInstanceNum[preAccept.repId] {
		r.MaxInstanceNum[preAccept.repId] = preAccept.insId + 1
	}
	// reply
	go func() {
		if !changed {
			paOK := &PreAcceptOK{
				insId: preAccept.insId,
			}
			messageChan <- paOK
		} else {
			paReply := &PreAcceptReply{
				deps:  deps,
				repId: preAccept.repId,
				insId: preAccept.insId,
			}
			messageChan <- paReply
		}
	}()
}

func (r *Replica) recvPreAcceptOK(paOK *PreAcceptOK) {
	// recvpreacceptok() is subset of recvpreacceptreply()
	// It doens't need to do union
	// We need some refactoring between these two functions.
}

func (r *Replica) recvPreAcceptReply(paReply *PreAcceptReply) {
	inst := r.InstanceMatrix[paReply.repId][paReply.insId]

	if inst == nil {
		// TODO: should not happen
		return
	}

	// when we receive a reply which is not what we expect (sometimes the status
	// is later than we thought), it's probably because the instance has been delayed/partitioned
	// for a period of time. And after it comes back, things have changed. It's been accepted, or
	// committed, or even executed. Since it's accepted by majority already, we ignore it here and hope
	// the instance would be fixed later (by asking dependencies when executing commands).
	if inst.status > preaccepted {
		// TODO: slow reply
		return
	}

	inst.info.preaccCnt++

	// recvpreacceptok doesn't need this {
	deps, same := r.union(inst.deps, paReply.deps)
	if !same {
		if inst.info.preaccCnt > 1 {
			inst.info.haveDiffReply = true
		}
		inst.deps = deps
	}
	// }

	if inst.info.preaccCnt >= r.Size/2 && !inst.allReplyTheSame() {
		// slow path

	} else if inst.info.preaccCnt == r.fastQuorumSize() && inst.allReplyTheSame() {
		// fast path
	}
}

// findDependencies finds the most recent interference instance from each instance space
// of this replica.
// It returns the ids of these instances as an array.
func (r *Replica) findDependencies(cmds []cmd.Command) []InstanceIdType {
	deps := make([]InstanceIdType, r.Size)

	for i := range r.InstanceMatrix {
		instances := r.InstanceMatrix[i]
		start := r.MaxInstanceNum[i] - 1

		if conflict, ok := r.scanConflicts(instances, cmds, start, 0); ok {
			deps[i] = conflict
		}
	}

	return deps
}

func (r *Replica) updateDependencies(cmds []cmd.Command, deps []InstanceIdType, from int) ([]InstanceIdType, bool) {
	changed := false

	for curr := range r.InstanceMatrix {
		// short cut here, the sender knows the latest dependencies for itself
		if curr == from {
			continue
		}

		instances := r.InstanceMatrix[curr]
		start, end := r.MaxInstanceNum[curr]-1, deps[curr]

		if conflict, ok := r.scanConflicts(instances, cmds, start, end); ok {
			changed = true
			deps[curr] = conflict
		}
	}

	return deps, changed
}

func (r *Replica) scanConflicts(instances []*Instance, cmds []cmd.Command, start InstanceIdType, end InstanceIdType) (InstanceIdType, bool) {
	for id := start; id > end; id-- {
		if instances[id] == nil {
			continue
		}
		// we only need to find the highest instance in conflict
		if r.StateMac.HaveConflicts(cmds, instances[id].cmds) {
			return InstanceIdType(id), true
		}
	}

	return 0, false
}

func (r *Replica) union(deps1, deps2 []InstanceIdType) ([]InstanceIdType, bool) {
	same := true
	for rep := 0; rep < r.Size; rep++ {
		if deps1[rep] != deps2[rep] {
			same = false
			if deps1[rep] < deps2[rep] {
				deps1[rep] = deps2[rep]
			}
		}
	}
	return deps1, same
}
<commit_msg>refactor<commit_after>package replica

import (
	"fmt"
	cmd "github.com/go-epaxos/epaxos/command"
)

var _ = fmt.Printf

func (r *Replica) recvPropose(propose *Propose, messageChan chan Message) {
	deps := r.findDependencies(propose.cmds)

	// increment instance number
	instNo := r.MaxInstanceNum[r.Id]
	instNo = instNo
	r.MaxInstanceNum[r.Id]++

	// set cmds
	r.InstanceMatrix[r.Id][instNo] = &Instance{
		cmds:   propose.cmds,
		deps:   deps,
		status: preaccepted,
		ballot: r.makeInitialBallot(),
		info:   NewInstanceInfo(),
	}

	// TODO: before we send the message, we need to record and sync it in disk/persistent.

	// send PreAccept
	preAccept := &PreAccept{
		cmds:  propose.cmds,
		deps:  deps,
		repId: r.Id,
		insId: instNo,
	}

	// fast quorum
	go func() {
		for i := 0; i < r.fastQuorumSize(); i++ {
			messageChan <- preAccept
		}
	}()

}

func (r *Replica) recvPreAccept(preAccept *PreAccept, messageChan chan Message) {
	// TODO: we need to check ballot for that coming from prepare phase
	// update
	deps, changed := r.updateDependencies(preAccept.cmds, preAccept.deps, preAccept.repId)
	// set cmd
	r.InstanceMatrix[preAccept.repId][preAccept.insId] = &Instance{
		cmds:   preAccept.cmds,
		deps:   deps,
		status: preaccepted,
		info:   NewInstanceInfo(),
	}
	if preAccept.insId >= r.MaxInstanceNum[preAccept.repId] {
		r.MaxInstanceNum[preAccept.repId] = preAccept.insId + 1
	}
	// reply
	go func() {
		if !changed {
			paOK := &PreAcceptOK{
				insId: preAccept.insId,
			}
			messageChan <- paOK
		} else {
			paReply := &PreAcceptReply{
				deps:  deps,
				repId: preAccept.repId,
				insId: preAccept.insId,
			}
			messageChan <- paReply
		}
	}()
}

func (r *Replica) recvPreAcceptOK(paOK *PreAcceptOK) {
	// recvpreacceptok() is subset of recvpreacceptreply()
	// It doens't need to do union
	// We need some refactoring between these two functions.
}

func (r *Replica) recvPreAcceptReply(paReply *PreAcceptReply) {
	inst := r.InstanceMatrix[paReply.repId][paReply.insId]

	if inst == nil {
		// TODO: should not happen
		return
	}

	// when we receive a reply which is not what we expect (sometimes the status
	// is later than we thought), it's probably because the instance has been delayed/partitioned
	// for a period of time. And after it comes back, things have changed. It's been accepted, or
	// committed, or even executed. Since it's accepted by majority already, we ignore it here and hope
	// the instance would be fixed later (by asking dependencies when executing commands).
	if inst.status > preaccepted {
		// TODO: slow reply
		return
	}

	inst.info.preaccCnt++

	// recvpreacceptok doesn't need this {
	deps, same := r.union(inst.deps, paReply.deps)
	if !same {
		if inst.info.preaccCnt > 1 {
			inst.info.haveDiffReply = true
		}
		inst.deps = deps
	}
	// }

	if inst.info.preaccCnt >= r.Size/2 && !inst.allReplyTheSame() {
		// slow path

	} else if inst.info.preaccCnt == r.fastQuorumSize() && inst.allReplyTheSame() {
		// fast path
	}
}

// findDependencies finds the most recent interference instance from each instance space
// of this replica.
// It returns the ids of these instances as an array.
func (r *Replica) findDependencies(cmds []cmd.Command) []InstanceIdType {
	deps := make([]InstanceIdType, r.Size)

	for i := range r.InstanceMatrix {
		instances := r.InstanceMatrix[i]
		start := r.MaxInstanceNum[i] - 1

		if conflict, ok := r.scanConflicts(instances, cmds, start, 0); ok {
			deps[i] = conflict
		}
	}

	return deps
}

func (r *Replica) updateDependencies(cmds []cmd.Command, deps []InstanceIdType, from int) ([]InstanceIdType, bool) {
	changed := false

	for curr := range r.InstanceMatrix {
		// short cut here, the sender knows the latest dependencies for itself
		if curr == from {
			continue
		}

		instances := r.InstanceMatrix[curr]
		start, end := r.MaxInstanceNum[curr]-1, deps[curr]

		if conflict, ok := r.scanConflicts(instances, cmds, start, end); ok {
			changed = true
			deps[curr] = conflict
		}
	}

	return deps, changed
}

func (r *Replica) scanConflicts(instances []*Instance, cmds []cmd.Command, start InstanceIdType, end InstanceIdType) (InstanceIdType, bool) {
	for i := start; i > end; i-- {
		if instances[i] == nil {
			continue
		}
		// we only need to find the highest instance in conflict
		if r.StateMac.HaveConflicts(cmds, instances[i].cmds) {
			return i, true
		}
	}

	return conflictNotFound, false
}

func (r *Replica) union(deps1, deps2 []InstanceIdType) ([]InstanceIdType, bool) {
	same := true
	for rep := 0; rep < r.Size; rep++ {
		if deps1[rep] != deps2[rep] {
			same = false
			if deps1[rep] < deps2[rep] {
				deps1[rep] = deps2[rep]
			}
		}
	}
	return deps1, same
}
<|endoftext|>


<commit_before>package request

import (
	"bufio"
	"bytes"
	"crypto/rand"
	"crypto/tls"
	"encoding/binary"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"strconv"
	"strings"

	"github.com/toqueteos/webbrowser"
)

func authCodeGrant(request *AurlExecution) (*string, error) {
	state := random()
	authZRequestUrl := authorizationRequestURL("code", request, state)
	webbrowser.Open(authZRequestUrl)
	fmt.Fprintf(os.Stderr, "Open browser and get code from %s\n", authZRequestUrl)

	reader := bufio.NewReader(os.Stdin)
	fmt.Fprint(os.Stderr, "Enter code: ")
	if code, _, err := reader.ReadLine(); err != nil {
		return nil, err
	} else {
		values := url.Values{
			"grant_type":   {"authorization_code"},
			"code":         {string(code)},
			"redirect_uri": {request.Profile.RedirectURI},
		}
		return tokenRequest(values, request)
	}
}

func implicitGrant(request *AurlExecution) (*string, error) {
	state := random()
	url := authorizationRequestURL("token", request, state)
	webbrowser.Open(url)
	fmt.Fprintf(os.Stderr, "Open browser and get token from %s\n", url)

	reader := bufio.NewReader(os.Stdin)
	fmt.Fprint(os.Stderr, "Enter token: ")
	if token, _, err := reader.ReadLine(); err != nil {
		return nil, err
	} else {
		s := "{\"token_type\": \"bearer\",\"access_token\": \"" + string(token) + "\"}" // TODO
		return &s, nil
	}
}

func resourceOwnerPasswordCredentialsGrant(request *AurlExecution) (*string, error) {
	values := url.Values{
		"grant_type": {"password"},
		"username":   {request.Profile.Username},
		"password":   {request.Profile.Password},
		"scope":      condVal(strings.Join(strings.Split(request.Profile.Scope, ","), " ")),
	}
	return tokenRequest(values, request)
}

func clientCredentialsGrant(request *AurlExecution) (*string, error) {
	values := url.Values{
		"grant_type": {"client_credentials"},
		"scope":      condVal(strings.Join(strings.Split(request.Profile.Scope, ","), " ")),
	}
	return tokenRequest(values, request)
}

func refreshGrant(request *AurlExecution, refreshToken string) (*string, error) {
	values := url.Values{
		"grant_type":    {"refresh_token"},
		"refresh_token": {refreshToken},
		"scope":         condVal(strings.Join(strings.Split(request.Profile.Scope, ","), " ")),
	}
	return tokenRequest(values, request)
}

func authorizationRequestURL(responseType string, request *AurlExecution, state string) string {
	var buf bytes.Buffer
	buf.WriteString(request.Profile.AuthorizationEndpoint)
	v := url.Values{
		"response_type": {responseType},
		"client_id":     {request.Profile.ClientId},
		"redirect_uri":  condVal(request.Profile.RedirectURI),
		"scope":         condVal(strings.Join(strings.Split(request.Profile.Scope, ","), " ")),
		"state":         condVal(state),
	}
	if strings.Contains(request.Profile.AuthorizationEndpoint, "?") {
		buf.WriteByte('&')
	} else {
		buf.WriteByte('?')
	}
	buf.WriteString(v.Encode())
	return buf.String()
}

func tokenRequest(v url.Values, request *AurlExecution) (*string, error) {
	req, err := http.NewRequest("POST", request.Profile.TokenEndpoint, strings.NewReader(v.Encode()))
	if err != nil {
		return nil, err
	}
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Add("Accept", "application/json")
	req.SetBasicAuth(request.Profile.ClientId, request.Profile.ClientSecret)

	if dumpReq, err := httputil.DumpRequestOut(req, true); err == nil {
		log.Printf("Token request >>>\n%s\n<<<", string(dumpReq))
	} else {
		log.Printf("Token request dump failed: %s", err)
	}

	client := &http.Client{
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{
				InsecureSkipVerify: *request.Insecure,
			},
		},
	}
	resp, err := client.Do(req)
	if err != nil {
		log.Printf("Token request failed: %s", err.Error())
		return nil, err
	}

	defer resp.Body.Close()

	if dumpResp, err := httputil.DumpResponse(resp, true); err == nil {
		log.Printf("Token response >>>\n%s\n<<<", string(dumpResp))
	} else {
		log.Printf("Token response dump failed: %s", err)
	}

	if resp.StatusCode == 200 {
		if b, err := ioutil.ReadAll(resp.Body); err == nil {
			s := string(b)
			return &s, nil
		} else {
			return nil, err
		}
	} else {
		log.Printf("Token request failed: %d", resp.StatusCode)
		return nil, err
	}
}

func condVal(v string) []string {
	if v == "" {
		return nil
	}
	return []string{v}
}

func random() string {
	var n uint64
	binary.Read(rand.Reader, binary.LittleEndian, &n)
	return strconv.FormatUint(n, 36)
}
<commit_msg>Use UserAgent header value which user specified in CLI opt<commit_after>package request

import (
	"bufio"
	"bytes"
	"crypto/rand"
	"crypto/tls"
	"encoding/binary"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"strconv"
	"strings"

	"github.com/toqueteos/webbrowser"
)

func authCodeGrant(request *AurlExecution) (*string, error) {
	state := random()
	authZRequestUrl := authorizationRequestURL("code", request, state)
	webbrowser.Open(authZRequestUrl)
	fmt.Fprintf(os.Stderr, "Open browser and get code from %s\n", authZRequestUrl)

	reader := bufio.NewReader(os.Stdin)
	fmt.Fprint(os.Stderr, "Enter code: ")
	if code, _, err := reader.ReadLine(); err != nil {
		return nil, err
	} else {
		values := url.Values{
			"grant_type":   {"authorization_code"},
			"code":         {string(code)},
			"redirect_uri": {request.Profile.RedirectURI},
		}
		return tokenRequest(values, request)
	}
}

func implicitGrant(request *AurlExecution) (*string, error) {
	state := random()
	url := authorizationRequestURL("token", request, state)
	webbrowser.Open(url)
	fmt.Fprintf(os.Stderr, "Open browser and get token from %s\n", url)

	reader := bufio.NewReader(os.Stdin)
	fmt.Fprint(os.Stderr, "Enter token: ")
	if token, _, err := reader.ReadLine(); err != nil {
		return nil, err
	} else {
		s := "{\"token_type\": \"bearer\",\"access_token\": \"" + string(token) + "\"}" // TODO
		return &s, nil
	}
}

func resourceOwnerPasswordCredentialsGrant(request *AurlExecution) (*string, error) {
	values := url.Values{
		"grant_type": {"password"},
		"username":   {request.Profile.Username},
		"password":   {request.Profile.Password},
		"scope":      condVal(strings.Join(strings.Split(request.Profile.Scope, ","), " ")),
	}
	return tokenRequest(values, request)
}

func clientCredentialsGrant(request *AurlExecution) (*string, error) {
	values := url.Values{
		"grant_type": {"client_credentials"},
		"scope":      condVal(strings.Join(strings.Split(request.Profile.Scope, ","), " ")),
	}
	return tokenRequest(values, request)
}

func refreshGrant(request *AurlExecution, refreshToken string) (*string, error) {
	values := url.Values{
		"grant_type":    {"refresh_token"},
		"refresh_token": {refreshToken},
		"scope":         condVal(strings.Join(strings.Split(request.Profile.Scope, ","), " ")),
	}
	return tokenRequest(values, request)
}

func authorizationRequestURL(responseType string, request *AurlExecution, state string) string {
	var buf bytes.Buffer
	buf.WriteString(request.Profile.AuthorizationEndpoint)
	v := url.Values{
		"response_type": {responseType},
		"client_id":     {request.Profile.ClientId},
		"redirect_uri":  condVal(request.Profile.RedirectURI),
		"scope":         condVal(strings.Join(strings.Split(request.Profile.Scope, ","), " ")),
		"state":         condVal(state),
	}
	if strings.Contains(request.Profile.AuthorizationEndpoint, "?") {
		buf.WriteByte('&')
	} else {
		buf.WriteByte('?')
	}
	buf.WriteString(v.Encode())
	return buf.String()
}

func tokenRequest(v url.Values, request *AurlExecution) (*string, error) {
	req, err := http.NewRequest("POST", request.Profile.TokenEndpoint, strings.NewReader(v.Encode()))
	if err != nil {
		return nil, err
	}
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Add("Accept", "application/json")
	if request.Headers.Get("User-Agent") == "" {
		req.Header.Add("User-Agent", fmt.Sprintf("%s-%s", request.Name, request.Version))
	} else {
		req.Header.Add("User-Agent", request.Headers.Get("User-Agent"))
	}
	req.SetBasicAuth(request.Profile.ClientId, request.Profile.ClientSecret)

	if dumpReq, err := httputil.DumpRequestOut(req, true); err == nil {
		log.Printf("Token request >>>\n%s\n<<<", string(dumpReq))
	} else {
		log.Printf("Token request dump failed: %s", err)
	}

	client := &http.Client{
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{
				InsecureSkipVerify: *request.Insecure,
			},
		},
	}
	resp, err := client.Do(req)
	if err != nil {
		log.Printf("Token request failed: %s", err.Error())
		return nil, err
	}

	defer resp.Body.Close()

	if dumpResp, err := httputil.DumpResponse(resp, true); err == nil {
		log.Printf("Token response >>>\n%s\n<<<", string(dumpResp))
	} else {
		log.Printf("Token response dump failed: %s", err)
	}

	if resp.StatusCode == 200 {
		if b, err := ioutil.ReadAll(resp.Body); err == nil {
			s := string(b)
			return &s, nil
		} else {
			return nil, err
		}
	} else {
		log.Printf("Token request failed: %d", resp.StatusCode)
		return nil, err
	}
}

func condVal(v string) []string {
	if v == "" {
		return nil
	}
	return []string{v}
}

func random() string {
	var n uint64
	binary.Read(rand.Reader, binary.LittleEndian, &n)
	return strconv.FormatUint(n, 36)
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/nyaxt/otaru/mgmt"
	"github.com/nyaxt/otaru/mgmt/mblobstore"
)

func main() {
	srv := mgmt.NewServer()
	mblobstore.Install(srv)
	srv.Run()
}
<commit_msg>remove mgmt_standalone<commit_after><|endoftext|>


<commit_before>package buffalo

import (
	"time"

	"github.com/Sirupsen/logrus"
	humanize "github.com/dustin/go-humanize"
	"github.com/markbates/going/randx"
)

// RequestLogger can be be overridden to a user specified
// function that can be used to log the request.
var RequestLogger = RequestLoggerFunc

// RequestLoggerFunc is the default implementation of the RequestLogger.
// By default it will log a uniq "request_id", the HTTP Method of the request,
// the path that was requested, the duration (time) it took to process the
// request, the size of the response (and the "human" size), and the status
// code of the response.
func RequestLoggerFunc(h Handler) Handler {
	return func(c Context) error {
		var irid interface{}
		if irid = c.Session().Get("requestor_id"); irid == nil {
			irid = randx.String(10)
			c.Session().Set("requestor_id", irid)
			c.Session().Save()
		}
		now := time.Now()
		c.LogFields(logrus.Fields{
			"request_id": irid.(string) + "-" + randx.String(10),
			"method":     c.Request().Method,
			"path":       c.Request().URL.String(),
		})
		ct := c.Request().Header.Get("Content-Type")
		if ct != "" {
			c.LogField("content_type", ct)
		}
		defer func() {
			ws := c.Response().(*buffaloResponse)
			c.LogFields(logrus.Fields{
				"duration":   time.Now().Sub(now),
				"size":       ws.size,
				"human_size": humanize.Bytes(uint64(ws.size)),
				"status":     ws.status,
			})
			c.Logger().Info()
		}()
		return h(c)
	}
}
<commit_msg>set request_id in context<commit_after>package buffalo

import (
	"time"

	"github.com/Sirupsen/logrus"
	humanize "github.com/dustin/go-humanize"
	"github.com/markbates/going/randx"
)

// RequestLogger can be be overridden to a user specified
// function that can be used to log the request.
var RequestLogger = RequestLoggerFunc

// RequestLoggerFunc is the default implementation of the RequestLogger.
// By default it will log a uniq "request_id", the HTTP Method of the request,
// the path that was requested, the duration (time) it took to process the
// request, the size of the response (and the "human" size), and the status
// code of the response.
func RequestLoggerFunc(h Handler) Handler {
	return func(c Context) error {
		var irid interface{}
		if irid = c.Session().Get("requestor_id"); irid == nil {
			irid = randx.String(10)
			c.Session().Set("requestor_id", irid)
			c.Session().Save()
		}
		rid := irid.(string) + "-" + randx.String(10)
		c.Set("request_id", rid)

		now := time.Now()
		c.LogFields(logrus.Fields{
			"request_id": rid,
			"method":     c.Request().Method,
			"path":       c.Request().URL.String(),
		})
		ct := c.Request().Header.Get("Content-Type")
		if ct != "" {
			c.LogField("content_type", ct)
		}
		defer func() {
			ws := c.Response().(*buffaloResponse)
			c.LogFields(logrus.Fields{
				"duration":   time.Now().Sub(now),
				"size":       ws.size,
				"human_size": humanize.Bytes(uint64(ws.size)),
				"status":     ws.status,
			})
			c.Logger().Info()
		}()
		return h(c)
	}
}
<|endoftext|>


<commit_before>package timing

import "time"

// A DynamicTicker is a ticker which can
// be sent signals in the form of durations to
// change how often it ticks.
type DynamicTicker struct {
	ticker    *time.Ticker
	C         chan time.Time
	resetCh   chan *time.Ticker
	forceTick chan bool
}

// NewDynamicTicker returns a null-initialized
// dynamic ticker
func NewDynamicTicker() *DynamicTicker {
	ch := make(chan time.Time)
	resetCh := make(chan *time.Ticker)
	forceTick := make(chan bool)
	dt := &DynamicTicker{
		// Please do not leave the application running
		// for a thousand hours without clicking on
		// the visualization knub, or else your next
		// visualization animation might skip a frame!
		// (We need -some- ticker defined or else
		// the program will crash in the following
		// routine on a nil pointer)
		ticker:    time.NewTicker(1000 * time.Hour),
		C:         ch,
		resetCh:   resetCh,
		forceTick: forceTick,
	}
	go func(dt *DynamicTicker) {
		for {
			select {
			case v := <-dt.ticker.C:
				select {
				case <-dt.forceTick:
					continue
				case dt.C <- v:
				case ticker := <-dt.resetCh:
					dt.ticker.Stop()
					dt.ticker = ticker
				}
			case ticker := <-dt.resetCh:
				dt.ticker.Stop()
				dt.ticker = ticker
			case r := <-dt.forceTick:
				if !r {
					close(dt.forceTick)
					close(dt.C)
					close(dt.resetCh)
					return
				}
				select {
				case <-dt.forceTick:
					continue
				case dt.C <- time.Time{}:
				}
			}
		}
	}(dt)
	return dt
}

// SetTick changes the rate at which a dynamic ticker
// ticks
func (dt *DynamicTicker) SetTick(d time.Duration) {
	dt.resetCh <- time.NewTicker(d)
}

// Step will force the dynamic ticker to tick, once.
// If the forced tick is not received, multiple calls
// to step will do nothing.
func (dt *DynamicTicker) Step() {
	select {
	case dt.forceTick <- true:
	default:
	}
}

// Stop closes all internal channels and stops dt's internal ticker
func (dt *DynamicTicker) Stop() {
	dt.ticker.Stop()
	select {
	case <-dt.C:
	default:
	}
	dt.forceTick <- false
}
<commit_msg>Added some dynamic ticker protection from closed sends, but this is a bug we should fix<commit_after>package timing

import (
	"time"

	"bitbucket.org/oakmoundstudio/oak/dlog"
)

// A DynamicTicker is a ticker which can
// be sent signals in the form of durations to
// change how often it ticks.
type DynamicTicker struct {
	ticker    *time.Ticker
	C         chan time.Time
	resetCh   chan *time.Ticker
	forceTick chan bool
}

// NewDynamicTicker returns a null-initialized
// dynamic ticker
func NewDynamicTicker() *DynamicTicker {
	ch := make(chan time.Time)
	resetCh := make(chan *time.Ticker)
	forceTick := make(chan bool)
	dt := &DynamicTicker{
		// Please do not leave the application running
		// for a thousand hours without clicking on
		// the visualization knub, or else your next
		// visualization animation might skip a frame!
		// (We need -some- ticker defined or else
		// the program will crash in the following
		// routine on a nil pointer)
		ticker:    time.NewTicker(1000 * time.Hour),
		C:         ch,
		resetCh:   resetCh,
		forceTick: forceTick,
	}
	go func(dt *DynamicTicker) {
		for {
			select {
			case v := <-dt.ticker.C:
				select {
				case <-dt.forceTick:
					continue
				case dt.C <- v:
				case ticker := <-dt.resetCh:
					dt.ticker.Stop()
					dt.ticker = ticker
				}
			case ticker := <-dt.resetCh:
				dt.ticker.Stop()
				dt.ticker = ticker
			case r := <-dt.forceTick:
				if !r {
					close(dt.forceTick)
					close(dt.C)
					close(dt.resetCh)
					return
				}
				select {
				case <-dt.forceTick:
					continue
				case dt.C <- time.Time{}:
				}
			}
		}
	}(dt)
	return dt
}

// SetTick changes the rate at which a dynamic ticker
// ticks
func (dt *DynamicTicker) SetTick(d time.Duration) {
	dt.resetCh <- time.NewTicker(d)
}

// Step will force the dynamic ticker to tick, once.
// If the forced tick is not received, multiple calls
// to step will do nothing.
func (dt *DynamicTicker) Step() {
	select {
	case dt.forceTick <- true:
	default:
	}
}

// Stop closes all internal channels and stops dt's internal ticker
func (dt *DynamicTicker) Stop() {
	defer func() {
		if x := recover(); x != nil {
			dlog.Error("Dynamic Ticker stopped twice")
		}
	}()
	dt.ticker.Stop()
	select {
	case <-dt.C:
	default:
	}
	dt.forceTick <- false
}
<|endoftext|>