<commit_before>package jsonclient

import (
	"encoding/json"
	"fmt"
	"math"
	"net/http"
	"net/http/httptest"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"testing"
	"time"

	"golang.org/x/net/context"

	"github.com/google/certificate-transparency/go/testdata"
)

func TestNewJSONClient(t *testing.T) {
	tests := []struct {
		pubKey string
		errstr string
	}{
		{"", "no PEM block"},
		{"bogus", "no PEM block"},
		{testdata.RsaPublicKeyPEM, ""},
		{testdata.EcdsaPublicKeyPEM, ""},
		{testdata.DsaPublicKeyPEM, "Unsupported public key type"},
		{testdata.RsaPublicKeyPEM + "bogus", "extra data found"},
	}
	for _, test := range tests {
		client, err := New("http://127.0.0.1", nil, test.pubKey)
		if test.errstr != "" {
			if err == nil {
				t.Errorf("New()=%p,nil; want error %q", client, test.errstr)
			} else if !strings.Contains(err.Error(), test.errstr) {
				t.Errorf("New()=nil,%q; want error %q", err.Error(), test.errstr)
			}
			continue
		}
		if err != nil {
			t.Errorf("New()=nil,%q; want no error", err.Error())
		} else if client == nil {
			t.Errorf("New()=nil,nil; want client")
		}
	}
}

type TestStruct struct {
	TreeSize  int    `json:"tree_size"`
	Timestamp int    `json:"timestamp"`
	Data      string `json:"data"`
}

type TestParams struct {
	RespCode int `json:"rc"`
}

func MockServer(t *testing.T, failCount int, retryAfter int) *httptest.Server {
	return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		switch r.URL.Path {
		case "/struct/path":
			fmt.Fprintf(w, `{"tree_size": 11, "timestamp": 99}`)
		case "/struct/params":
			var s TestStruct
			if r.Method == http.MethodGet {
				s.TreeSize, _ = strconv.Atoi(r.FormValue("tree_size"))
				s.Timestamp, _ = strconv.Atoi(r.FormValue("timestamp"))
				s.Data = r.FormValue("data")
			} else {
				decoder := json.NewDecoder(r.Body)
				err := decoder.Decode(&s)
				if err != nil {
					panic("Failed to decode: " + err.Error())
				}
				defer r.Body.Close()
			}
			fmt.Fprintf(w, `{"tree_size": %d, "timestamp": %d, "data": "%s"}`, s.TreeSize, s.Timestamp, s.Data)
		case "/error":
			var params TestParams
			if r.Method == http.MethodGet {
				params.RespCode, _ = strconv.Atoi(r.FormValue("rc"))
			} else {
				decoder := json.NewDecoder(r.Body)
				err := decoder.Decode(&params)
				if err != nil {
					panic("Failed to decode: " + err.Error())
				}
				defer r.Body.Close()
			}
			http.Error(w, "error page", params.RespCode)
		case "/malformed":
			fmt.Fprintf(w, `{"tree_size": 11, "timestamp": 99`) // no closing }
		case "/retry":
			if failCount > 0 {
				failCount--
				if retryAfter != 0 {
					if retryAfter > 0 {
						w.Header().Add("Retry-After", strconv.Itoa(retryAfter))
					}
					w.WriteHeader(http.StatusServiceUnavailable)
				} else {
					w.WriteHeader(http.StatusRequestTimeout)
				}
			} else {
				fmt.Fprintf(w, `{"tree_size": 11, "timestamp": 99}`)
			}
		case "/retry-rfc1123":
			if failCount > 0 {
				failCount--
				w.Header().Add("Retry-After", time.Now().Add(time.Duration(retryAfter)*time.Second).Format(time.RFC1123))
				w.WriteHeader(http.StatusServiceUnavailable)
			} else {
				fmt.Fprintf(w, `{"tree_size": 11, "timestamp": 99}`)
			}
		default:
			t.Fatalf("Unhandled URL path: %s", r.URL.Path)
		}
	}))
}

func TestGetAndParse(t *testing.T) {
	rc := regexp.MustCompile
	tests := []struct {
		uri    string
		params map[string]string
		status int
		result TestStruct
		errstr *regexp.Regexp
	}{
		{uri: "[invalid-uri]", errstr: rc("too many colons|unexpected .* in address")},
		{uri: "/short%", errstr: rc("invalid URL escape")},
		{uri: "/malformed", status: http.StatusOK, errstr: rc("unexpected EOF")},
		{uri: "/error", params: map[string]string{"rc": "404"}, status: http.StatusNotFound},
		{uri: "/error", params: map[string]string{"rc": "403"}, status: http.StatusForbidden},
		{uri: "/struct/path", status: http.StatusOK, result: TestStruct{11, 99, ""}},
		{
			uri:    "/struct/params",
			status: http.StatusOK,
			params: map[string]string{"tree_size": "42", "timestamp": "88", "data": "abcd"},
			result: TestStruct{42, 88, "abcd"},
		},
	}

	ts := MockServer(t, -1, 0)
	defer ts.Close()

	logClient, err := NewWithoutVerification(ts.URL, &http.Client{})
	if err != nil {
		t.Fatal(err)
	}
	ctx := context.Background()

	for _, test := range tests {
		var result TestStruct
		httpRsp, err := logClient.GetAndParse(ctx, test.uri, test.params, &result)
		if test.errstr != nil {
			if err == nil {
				t.Errorf("GetAndParse(%q)=%+v,nil; want error matching %q", test.uri, result, test.errstr)
			} else if !test.errstr.MatchString(err.Error()) {
				t.Errorf("GetAndParse(%q)=nil,%q; want error matching %q", test.uri, err.Error(), test.errstr)
			}
			continue
		}
		if httpRsp.StatusCode != test.status {
			t.Errorf("GetAndParse('%s') got status %d; want %d", test.uri, httpRsp.StatusCode, test.status)
		}
		if test.status == http.StatusOK {
			if err != nil {
				t.Errorf("GetAndParse(%q)=nil,%q; want %+v", test.uri, err.Error(), result)
			}
			if !reflect.DeepEqual(result, test.result) {
				t.Errorf("GetAndParse(%q)=%+v,nil; want %+v", test.uri, result, test.result)
			}
		}
	}
}

func TestPostAndParse(t *testing.T) {
	rc := regexp.MustCompile
	tests := []struct {
		uri     string
		request interface{}
		status  int
		result  TestStruct
		errstr  *regexp.Regexp
	}{
		{uri: "[invalid-uri]", errstr: rc("too many colons|unexpected .* in address")},
		{uri: "/short%", errstr: rc("invalid URL escape")},
		{uri: "/struct/params", request: json.Number(`invalid`), errstr: rc("invalid number literal")},
		{uri: "/malformed", status: http.StatusOK, errstr: rc("unexpected end of JSON")},
		{uri: "/error", request: TestParams{RespCode: 404}, status: http.StatusNotFound},
		{uri: "/error", request: TestParams{RespCode: 403}, status: http.StatusForbidden},
		{uri: "/struct/path", status: http.StatusOK, result: TestStruct{11, 99, ""}},
		{
			uri:     "/struct/params",
			status:  http.StatusOK,
			request: TestStruct{42, 88, "abcd"},
			result:  TestStruct{42, 88, "abcd"},
		},
	}

	ts := MockServer(t, -1, 0)
	defer ts.Close()

	logClient, err := NewWithoutVerification(ts.URL, &http.Client{})
	if err != nil {
		t.Fatal(err)
	}
	ctx := context.Background()

	for _, test := range tests {
		var result TestStruct
		httpRsp, err := logClient.PostAndParse(ctx, test.uri, test.request, &result)
		if test.errstr != nil {
			if err == nil {
				t.Errorf("PostAndParse(%q)=%+v,nil; want error matching %q", test.uri, result, test.errstr)
			} else if !test.errstr.MatchString(err.Error()) {
				t.Errorf("PostAndParse(%q)=nil,%q; want error matching %q", test.uri, err.Error(), test.errstr)
			}
			continue
		}
		if httpRsp.StatusCode != test.status {
			t.Errorf("PostAndParse(%q) got status %d; want %d", test.uri, httpRsp.StatusCode, test.status)
		}
		if test.status == http.StatusOK {
			if err != nil {
				t.Errorf("PostAndParse(%q)=nil,%q; want %+v", test.uri, err.Error(), test.result)
			}
			if !reflect.DeepEqual(result, test.result) {
				t.Errorf("PostAndParse(%q)=%+v,nil; want %+v", test.uri, result, test.result)
			}
		}
	}
}

func TestPostAndParseWithRetry(t *testing.T) {
	shortLeeway := time.Millisecond * 100
	jiffy := time.Millisecond

	tests := []struct {
		uri          string
		request      interface{}
		deadlineSecs int // -1 indicates no deadline
		expected     time.Duration
		leeway       time.Duration
		retryAfter   int // -1 indicates generate 503 with no Retry-After
		failCount    int
		errstr       string
	}{
		{"/retry", nil, -1, jiffy, shortLeeway, 0, 0, ""},
		{"/error", TestParams{RespCode: 418}, 2, jiffy, shortLeeway, 0, 0, "teapot"},
		{"/short%", nil, 2, 2 * time.Second, shortLeeway, 0, 0, "deadline exceeded"},
		{"/retry", nil, -1, 7 * time.Second, shortLeeway, -1, 3, ""},
		{"/retry", nil, 6, 5 * time.Second, shortLeeway, 5, 1, ""},
		{"/retry", nil, 5, 5 * time.Second, shortLeeway, 10, 1, "deadline exceeded"},
		{"/retry", nil, 10, 5 * time.Second, shortLeeway, 1, 5, ""},
		{"/retry", nil, 1, 10 * jiffy, shortLeeway, 0, 10, ""},
		{"/retry-rfc1123", nil, -1, 2 * time.Second, time.Millisecond * 750, 2, 1, ""},
	}
	for _, test := range tests {
		ts := MockServer(t, test.failCount, test.retryAfter)
		defer ts.Close()

		logClient, err := NewWithoutVerification(ts.URL, &http.Client{})
		if err != nil {
			t.Fatal(err)
		}
		ctx := context.Background()
		if test.deadlineSecs >= 0 {
			ctx, _ = context.WithDeadline(context.Background(), time.Now().Add(time.Duration(test.deadlineSecs)*time.Second))
		}

		var result TestStruct
		started := time.Now()
		httpRsp, err := logClient.PostAndParseWithRetry(ctx, test.uri, test.request, &result)
		took := time.Since(started)

		if math.Abs(float64(took-test.expected)) > float64(test.leeway) {
			t.Errorf("PostAndParseWithRetry() took %s; want ~%s", took, test.expected)
		}
		if test.errstr != "" {
			if err == nil {
				t.Errorf("PostAndParseWithRetry()=%+v,nil; want error %q", result, test.errstr)
			} else if !strings.Contains(err.Error(), test.errstr) {
				t.Errorf("PostAndParseWithRetry()=nil,%q; want error %q", err.Error(), test.errstr)
			}
			continue
		}
		if err != nil {
			t.Errorf("PostAndParseWithRetry()=nil,%q; want no error", err.Error())
		} else if httpRsp.StatusCode != http.StatusOK {
			t.Errorf("PostAndParseWithRetry() got status %d; want OK(404)", httpRsp.StatusCode)
		}
	}
}

func TestContextRequired(t *testing.T) {
	ts := MockServer(t, -1, 0)
	defer ts.Close()

	logClient, err := NewWithoutVerification(ts.URL, &http.Client{})
	if err != nil {
		t.Fatal(err)
	}
	var result TestStruct
	_, err = logClient.GetAndParse(nil, "/struct/path", nil, &result)
	if err == nil {
		t.Errorf("GetAndParse() succeeded with empty Context")
	}
	_, err = logClient.PostAndParse(nil, "/struct/path", nil, &result)
	if err == nil {
		t.Errorf("PostAndParse() succeeded with empty Context")
	}
	_, err = logClient.PostAndParseWithRetry(nil, "/struct/path", nil, &result)
	if err == nil {
		t.Errorf("PostAndParseWithRetry() succeeded with empty Context")
	}
}
<commit_msg>go/jsonclient: increase test leeway<commit_after>package jsonclient

import (
	"encoding/json"
	"fmt"
	"math"
	"net/http"
	"net/http/httptest"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"testing"
	"time"

	"golang.org/x/net/context"

	"github.com/google/certificate-transparency/go/testdata"
)

func TestNewJSONClient(t *testing.T) {
	tests := []struct {
		pubKey string
		errstr string
	}{
		{"", "no PEM block"},
		{"bogus", "no PEM block"},
		{testdata.RsaPublicKeyPEM, ""},
		{testdata.EcdsaPublicKeyPEM, ""},
		{testdata.DsaPublicKeyPEM, "Unsupported public key type"},
		{testdata.RsaPublicKeyPEM + "bogus", "extra data found"},
	}
	for _, test := range tests {
		client, err := New("http://127.0.0.1", nil, test.pubKey)
		if test.errstr != "" {
			if err == nil {
				t.Errorf("New()=%p,nil; want error %q", client, test.errstr)
			} else if !strings.Contains(err.Error(), test.errstr) {
				t.Errorf("New()=nil,%q; want error %q", err.Error(), test.errstr)
			}
			continue
		}
		if err != nil {
			t.Errorf("New()=nil,%q; want no error", err.Error())
		} else if client == nil {
			t.Errorf("New()=nil,nil; want client")
		}
	}
}

type TestStruct struct {
	TreeSize  int    `json:"tree_size"`
	Timestamp int    `json:"timestamp"`
	Data      string `json:"data"`
}

type TestParams struct {
	RespCode int `json:"rc"`
}

func MockServer(t *testing.T, failCount int, retryAfter int) *httptest.Server {
	return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		switch r.URL.Path {
		case "/struct/path":
			fmt.Fprintf(w, `{"tree_size": 11, "timestamp": 99}`)
		case "/struct/params":
			var s TestStruct
			if r.Method == http.MethodGet {
				s.TreeSize, _ = strconv.Atoi(r.FormValue("tree_size"))
				s.Timestamp, _ = strconv.Atoi(r.FormValue("timestamp"))
				s.Data = r.FormValue("data")
			} else {
				decoder := json.NewDecoder(r.Body)
				err := decoder.Decode(&s)
				if err != nil {
					panic("Failed to decode: " + err.Error())
				}
				defer r.Body.Close()
			}
			fmt.Fprintf(w, `{"tree_size": %d, "timestamp": %d, "data": "%s"}`, s.TreeSize, s.Timestamp, s.Data)
		case "/error":
			var params TestParams
			if r.Method == http.MethodGet {
				params.RespCode, _ = strconv.Atoi(r.FormValue("rc"))
			} else {
				decoder := json.NewDecoder(r.Body)
				err := decoder.Decode(&params)
				if err != nil {
					panic("Failed to decode: " + err.Error())
				}
				defer r.Body.Close()
			}
			http.Error(w, "error page", params.RespCode)
		case "/malformed":
			fmt.Fprintf(w, `{"tree_size": 11, "timestamp": 99`) // no closing }
		case "/retry":
			if failCount > 0 {
				failCount--
				if retryAfter != 0 {
					if retryAfter > 0 {
						w.Header().Add("Retry-After", strconv.Itoa(retryAfter))
					}
					w.WriteHeader(http.StatusServiceUnavailable)
				} else {
					w.WriteHeader(http.StatusRequestTimeout)
				}
			} else {
				fmt.Fprintf(w, `{"tree_size": 11, "timestamp": 99}`)
			}
		case "/retry-rfc1123":
			if failCount > 0 {
				failCount--
				w.Header().Add("Retry-After", time.Now().Add(time.Duration(retryAfter)*time.Second).Format(time.RFC1123))
				w.WriteHeader(http.StatusServiceUnavailable)
			} else {
				fmt.Fprintf(w, `{"tree_size": 11, "timestamp": 99}`)
			}
		default:
			t.Fatalf("Unhandled URL path: %s", r.URL.Path)
		}
	}))
}

func TestGetAndParse(t *testing.T) {
	rc := regexp.MustCompile
	tests := []struct {
		uri    string
		params map[string]string
		status int
		result TestStruct
		errstr *regexp.Regexp
	}{
		{uri: "[invalid-uri]", errstr: rc("too many colons|unexpected .* in address")},
		{uri: "/short%", errstr: rc("invalid URL escape")},
		{uri: "/malformed", status: http.StatusOK, errstr: rc("unexpected EOF")},
		{uri: "/error", params: map[string]string{"rc": "404"}, status: http.StatusNotFound},
		{uri: "/error", params: map[string]string{"rc": "403"}, status: http.StatusForbidden},
		{uri: "/struct/path", status: http.StatusOK, result: TestStruct{11, 99, ""}},
		{
			uri:    "/struct/params",
			status: http.StatusOK,
			params: map[string]string{"tree_size": "42", "timestamp": "88", "data": "abcd"},
			result: TestStruct{42, 88, "abcd"},
		},
	}

	ts := MockServer(t, -1, 0)
	defer ts.Close()

	logClient, err := NewWithoutVerification(ts.URL, &http.Client{})
	if err != nil {
		t.Fatal(err)
	}
	ctx := context.Background()

	for _, test := range tests {
		var result TestStruct
		httpRsp, err := logClient.GetAndParse(ctx, test.uri, test.params, &result)
		if test.errstr != nil {
			if err == nil {
				t.Errorf("GetAndParse(%q)=%+v,nil; want error matching %q", test.uri, result, test.errstr)
			} else if !test.errstr.MatchString(err.Error()) {
				t.Errorf("GetAndParse(%q)=nil,%q; want error matching %q", test.uri, err.Error(), test.errstr)
			}
			continue
		}
		if httpRsp.StatusCode != test.status {
			t.Errorf("GetAndParse('%s') got status %d; want %d", test.uri, httpRsp.StatusCode, test.status)
		}
		if test.status == http.StatusOK {
			if err != nil {
				t.Errorf("GetAndParse(%q)=nil,%q; want %+v", test.uri, err.Error(), result)
			}
			if !reflect.DeepEqual(result, test.result) {
				t.Errorf("GetAndParse(%q)=%+v,nil; want %+v", test.uri, result, test.result)
			}
		}
	}
}

func TestPostAndParse(t *testing.T) {
	rc := regexp.MustCompile
	tests := []struct {
		uri     string
		request interface{}
		status  int
		result  TestStruct
		errstr  *regexp.Regexp
	}{
		{uri: "[invalid-uri]", errstr: rc("too many colons|unexpected .* in address")},
		{uri: "/short%", errstr: rc("invalid URL escape")},
		{uri: "/struct/params", request: json.Number(`invalid`), errstr: rc("invalid number literal")},
		{uri: "/malformed", status: http.StatusOK, errstr: rc("unexpected end of JSON")},
		{uri: "/error", request: TestParams{RespCode: 404}, status: http.StatusNotFound},
		{uri: "/error", request: TestParams{RespCode: 403}, status: http.StatusForbidden},
		{uri: "/struct/path", status: http.StatusOK, result: TestStruct{11, 99, ""}},
		{
			uri:     "/struct/params",
			status:  http.StatusOK,
			request: TestStruct{42, 88, "abcd"},
			result:  TestStruct{42, 88, "abcd"},
		},
	}

	ts := MockServer(t, -1, 0)
	defer ts.Close()

	logClient, err := NewWithoutVerification(ts.URL, &http.Client{})
	if err != nil {
		t.Fatal(err)
	}
	ctx := context.Background()

	for _, test := range tests {
		var result TestStruct
		httpRsp, err := logClient.PostAndParse(ctx, test.uri, test.request, &result)
		if test.errstr != nil {
			if err == nil {
				t.Errorf("PostAndParse(%q)=%+v,nil; want error matching %q", test.uri, result, test.errstr)
			} else if !test.errstr.MatchString(err.Error()) {
				t.Errorf("PostAndParse(%q)=nil,%q; want error matching %q", test.uri, err.Error(), test.errstr)
			}
			continue
		}
		if httpRsp.StatusCode != test.status {
			t.Errorf("PostAndParse(%q) got status %d; want %d", test.uri, httpRsp.StatusCode, test.status)
		}
		if test.status == http.StatusOK {
			if err != nil {
				t.Errorf("PostAndParse(%q)=nil,%q; want %+v", test.uri, err.Error(), test.result)
			}
			if !reflect.DeepEqual(result, test.result) {
				t.Errorf("PostAndParse(%q)=%+v,nil; want %+v", test.uri, result, test.result)
			}
		}
	}
}

func TestPostAndParseWithRetry(t *testing.T) {
	shortLeeway := time.Millisecond * 100
	jiffy := time.Millisecond

	tests := []struct {
		uri          string
		request      interface{}
		deadlineSecs int // -1 indicates no deadline
		expected     time.Duration
		leeway       time.Duration
		retryAfter   int // -1 indicates generate 503 with no Retry-After
		failCount    int
		errstr       string
	}{
		{"/retry", nil, -1, jiffy, shortLeeway, 0, 0, ""},
		{"/error", TestParams{RespCode: 418}, 2, jiffy, shortLeeway, 0, 0, "teapot"},
		{"/short%", nil, 2, 2 * time.Second, shortLeeway, 0, 0, "deadline exceeded"},
		{"/retry", nil, -1, 7 * time.Second, shortLeeway, -1, 3, ""},
		{"/retry", nil, 6, 5 * time.Second, shortLeeway, 5, 1, ""},
		{"/retry", nil, 5, 5 * time.Second, shortLeeway, 10, 1, "deadline exceeded"},
		{"/retry", nil, 10, 5 * time.Second, shortLeeway, 1, 5, ""},
		{"/retry", nil, 1, 10 * jiffy, shortLeeway, 0, 10, ""},
		{"/retry-rfc1123", nil, -1, 2 * time.Second, 1 * time.Second, 2, 1, ""},
	}
	for _, test := range tests {
		ts := MockServer(t, test.failCount, test.retryAfter)
		defer ts.Close()

		logClient, err := NewWithoutVerification(ts.URL, &http.Client{})
		if err != nil {
			t.Fatal(err)
		}
		ctx := context.Background()
		if test.deadlineSecs >= 0 {
			ctx, _ = context.WithDeadline(context.Background(), time.Now().Add(time.Duration(test.deadlineSecs)*time.Second))
		}

		var result TestStruct
		started := time.Now()
		httpRsp, err := logClient.PostAndParseWithRetry(ctx, test.uri, test.request, &result)
		took := time.Since(started)

		if math.Abs(float64(took-test.expected)) > float64(test.leeway) {
			t.Errorf("PostAndParseWithRetry() took %s; want ~%s", took, test.expected)
		}
		if test.errstr != "" {
			if err == nil {
				t.Errorf("PostAndParseWithRetry()=%+v,nil; want error %q", result, test.errstr)
			} else if !strings.Contains(err.Error(), test.errstr) {
				t.Errorf("PostAndParseWithRetry()=nil,%q; want error %q", err.Error(), test.errstr)
			}
			continue
		}
		if err != nil {
			t.Errorf("PostAndParseWithRetry()=nil,%q; want no error", err.Error())
		} else if httpRsp.StatusCode != http.StatusOK {
			t.Errorf("PostAndParseWithRetry() got status %d; want OK(404)", httpRsp.StatusCode)
		}
	}
}

func TestContextRequired(t *testing.T) {
	ts := MockServer(t, -1, 0)
	defer ts.Close()

	logClient, err := NewWithoutVerification(ts.URL, &http.Client{})
	if err != nil {
		t.Fatal(err)
	}
	var result TestStruct
	_, err = logClient.GetAndParse(nil, "/struct/path", nil, &result)
	if err == nil {
		t.Errorf("GetAndParse() succeeded with empty Context")
	}
	_, err = logClient.PostAndParse(nil, "/struct/path", nil, &result)
	if err == nil {
		t.Errorf("PostAndParse() succeeded with empty Context")
	}
	_, err = logClient.PostAndParseWithRetry(nil, "/struct/path", nil, &result)
	if err == nil {
		t.Errorf("PostAndParseWithRetry() succeeded with empty Context")
	}
}
<|endoftext|>


<commit_before>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build !windows,!plan9

package interp_test

import (
	"bytes"
	"fmt"
	"go/build"
	"os"
	"path/filepath"
	"strings"
	"testing"
	"time"

	"golang.org/x/tools/go/loader"
	"golang.org/x/tools/go/ssa"
	"golang.org/x/tools/go/ssa/interp"
	"golang.org/x/tools/go/ssa/ssautil"
	"golang.org/x/tools/go/types"
)

// Each line contains a space-separated list of $GOROOT/test/
// filenames comprising the main package of a program.
// They are ordered quickest-first, roughly.
//
// TODO(adonovan): integrate into the $GOROOT/test driver scripts,
// golden file checking, etc.
var gorootTestTests = []string{
	"235.go",
	"alias1.go",
	"chancap.go",
	"func5.go",
	"func6.go",
	"func7.go",
	"func8.go",
	"helloworld.go",
	"varinit.go",
	"escape3.go",
	"initcomma.go",
	"cmp.go",
	"compos.go",
	"turing.go",
	"indirect.go",
	"complit.go",
	"for.go",
	"struct0.go",
	"intcvt.go",
	"printbig.go",
	"deferprint.go",
	"escape.go",
	"range.go",
	"const4.go",
	"float_lit.go",
	"bigalg.go",
	"decl.go",
	"if.go",
	"named.go",
	"bigmap.go",
	"func.go",
	"reorder2.go",
	"closure.go",
	"gc.go",
	"simassign.go",
	"iota.go",
	"nilptr2.go",
	"goprint.go", // doesn't actually assert anything (cmpout)
	"utf.go",
	"method.go",
	"char_lit.go",
	"env.go",
	"int_lit.go",
	"string_lit.go",
	"defer.go",
	"typeswitch.go",
	"stringrange.go",
	"reorder.go",
	"method3.go",
	"literal.go",
	"nul1.go", // doesn't actually assert anything (errorcheckoutput)
	"zerodivide.go",
	"convert.go",
	"convT2X.go",
	"switch.go",
	"initialize.go",
	"ddd.go",
	"blank.go", // partly disabled
	"map.go",
	"closedchan.go",
	"divide.go",
	"rename.go",
	"const3.go",
	"nil.go",
	"recover.go", // reflection parts disabled
	"recover1.go",
	"recover2.go",
	"recover3.go",
	"typeswitch1.go",
	"floatcmp.go",
	"crlf.go", // doesn't actually assert anything (runoutput)
	// Slow tests follow.
	"bom.go", // ~1.7s
	"gc1.go", // ~1.7s
	"cmplxdivide.go cmplxdivide1.go", // ~2.4s

	// Working, but not worth enabling:
	// "append.go",    // works, but slow (15s).
	// "gc2.go",       // works, but slow, and cheats on the memory check.
	// "sigchld.go",   // works, but only on POSIX.
	// "peano.go",     // works only up to n=9, and slow even then.
	// "stack.go",     // works, but too slow (~30s) by default.
	// "solitaire.go", // works, but too slow (~30s).
	// "const.go",     // works but for but one bug: constant folder doesn't consider representations.
	// "init1.go",     // too slow (80s) and not that interesting. Cheats on ReadMemStats check too.
	// "rotate.go rotate0.go", // emits source for a test
	// "rotate.go rotate1.go", // emits source for a test
	// "rotate.go rotate2.go", // emits source for a test
	// "rotate.go rotate3.go", // emits source for a test
	// "64bit.go",             // emits source for a test
	// "run.go",               // test driver, not a test.

	// Broken.  TODO(adonovan): fix.
	// copy.go         // very slow; but with N=4 quickly crashes, slice index out of range.
	// nilptr.go       // interp: V > uintptr not implemented. Slow test, lots of mem
	// args.go         // works, but requires specific os.Args from the driver.
	// index.go        // a template, not a real test.
	// mallocfin.go    // SetFinalizer not implemented.

	// TODO(adonovan): add tests from $GOROOT/test/* subtrees:
	// bench chan bugs fixedbugs interface ken.
}

// These are files in go.tools/go/ssa/interp/testdata/.
var testdataTests = []string{
	"boundmeth.go",
	"complit.go",
	"coverage.go",
	"defer.go",
	"fieldprom.go",
	"ifaceconv.go",
	"ifaceprom.go",
	"initorder.go",
	"methprom.go",
	"mrvchain.go",
	"range.go",
	"recover.go",
	"reflect.go",
	"static.go",
	"callstack.go",
}

// These are files and packages in $GOROOT/src/.
var gorootSrcTests = []string{
	"encoding/ascii85",
	"encoding/hex",
	// "encoding/pem", // TODO(adonovan): implement (reflect.Value).SetString
	// "testing",      // TODO(adonovan): implement runtime.Goexit correctly
	"unicode",

	// Too slow:
	// "container/ring",
	// "hash/adler32",

	// TODO(adonovan): packages with Examples require os.Pipe (unimplemented):
	// "hash/crc32",
	// "unicode/utf8",
	// "log",
	// "path",
	// "flag",
	// "encoding/csv"
	// "text/scanner"
}

type successPredicate func(exitcode int, output string) error

func run(t *testing.T, dir, input string, success successPredicate) bool {
	fmt.Printf("Input: %s\n", input)

	start := time.Now()

	var inputs []string
	for _, i := range strings.Split(input, " ") {
		if strings.HasSuffix(i, ".go") {
			i = dir + i
		}
		inputs = append(inputs, i)
	}

	var conf loader.Config
	if _, err := conf.FromArgs(inputs, true); err != nil {
		t.Errorf("FromArgs(%s) failed: %s", inputs, err)
		return false
	}

	conf.Import("runtime")

	// Print a helpful hint if we don't make it to the end.
	var hint string
	defer func() {
		if hint != "" {
			fmt.Println("FAIL")
			fmt.Println(hint)
		} else {
			fmt.Println("PASS")
		}

		interp.CapturedOutput = nil
	}()

	hint = fmt.Sprintf("To dump SSA representation, run:\n%% go build golang.org/x/tools/cmd/ssadump && ./ssadump -build=CFP %s\n", input)

	iprog, err := conf.Load()
	if err != nil {
		t.Errorf("conf.Load(%s) failed: %s", inputs, err)
		return false
	}

	prog := ssautil.CreateProgram(iprog, ssa.SanityCheckFunctions)
	prog.BuildAll()

	var mainPkg *ssa.Package
	var initialPkgs []*ssa.Package
	for _, info := range iprog.InitialPackages() {
		if info.Pkg.Path() == "runtime" {
			continue // not an initial package
		}
		p := prog.Package(info.Pkg)
		initialPkgs = append(initialPkgs, p)
		if mainPkg == nil && p.Func("main") != nil {
			mainPkg = p
		}
	}
	if mainPkg == nil {
		testmainPkg := prog.CreateTestMainPackage(initialPkgs...)
		if testmainPkg == nil {
			t.Errorf("CreateTestMainPackage(%s) returned nil", mainPkg)
			return false
		}
		if testmainPkg.Func("main") == nil {
			t.Errorf("synthetic testmain package has no main")
			return false
		}
		mainPkg = testmainPkg
	}

	var out bytes.Buffer
	interp.CapturedOutput = &out

	hint = fmt.Sprintf("To trace execution, run:\n%% go build golang.org/x/tools/cmd/ssadump && ./ssadump -build=C -run --interp=T %s\n", input)
	exitCode := interp.Interpret(mainPkg, 0, &types.StdSizes{8, 8}, inputs[0], []string{})

	// The definition of success varies with each file.
	if err := success(exitCode, out.String()); err != nil {
		t.Errorf("interp.Interpret(%s) failed: %s", inputs, err)
		return false
	}

	hint = "" // call off the hounds

	if false {
		fmt.Println(input, time.Since(start)) // test profiling
	}

	return true
}

const slash = string(os.PathSeparator)

func printFailures(failures []string) {
	if failures != nil {
		fmt.Println("The following tests failed:")
		for _, f := range failures {
			fmt.Printf("\t%s\n", f)
		}
	}
}

func success(exitcode int, output string) error {
	if exitcode != 0 {
		return fmt.Errorf("exit code was %d", exitcode)
	}
	if strings.Contains(output, "BUG") {
		return fmt.Errorf("exited zero but output contained 'BUG'")
	}
	return nil
}

// TestTestdataFiles runs the interpreter on testdata/*.go.
func TestTestdataFiles(t *testing.T) {
	var failures []string
	start := time.Now()
	for _, input := range testdataTests {
		if testing.Short() && time.Since(start) > 30*time.Second {
			printFailures(failures)
			t.Skipf("timeout - aborting test")
		}
		if !run(t, "testdata"+slash, input, success) {
			failures = append(failures, input)
		}
	}
	printFailures(failures)
}

// TestGorootTest runs the interpreter on $GOROOT/test/*.go.
func TestGorootTest(t *testing.T) {
	if testing.Short() {
		return // too slow (~30s)
	}

	var failures []string

	for _, input := range gorootTestTests {
		if !run(t, filepath.Join(build.Default.GOROOT, "test")+slash, input, success) {
			failures = append(failures, input)
		}
	}
	for _, input := range gorootSrcTests {
		if !run(t, filepath.Join(build.Default.GOROOT, "src")+slash, input, success) {
			failures = append(failures, input)
		}
	}
	printFailures(failures)
}

// TestTestmainPackage runs the interpreter on a synthetic "testmain" package.
func TestTestmainPackage(t *testing.T) {
	success := func(exitcode int, output string) error {
		if exitcode == 0 {
			return fmt.Errorf("unexpected success")
		}
		if !strings.Contains(output, "FAIL: TestFoo") {
			return fmt.Errorf("missing failure log for TestFoo")
		}
		if !strings.Contains(output, "FAIL: TestBar") {
			return fmt.Errorf("missing failure log for TestBar")
		}
		// TODO(adonovan): test benchmarks too
		return nil
	}
	run(t, "testdata"+slash, "a_test.go", success)
}

// CreateTestMainPackage should return nil if there were no tests.
func TestNullTestmainPackage(t *testing.T) {
	var conf loader.Config
	conf.CreateFromFilenames("", "testdata/b_test.go")
	iprog, err := conf.Load()
	if err != nil {
		t.Fatalf("CreatePackages failed: %s", err)
	}
	prog := ssautil.CreateProgram(iprog, ssa.SanityCheckFunctions)
	mainPkg := prog.Package(iprog.Created[0].Pkg)
	if mainPkg.Func("main") != nil {
		t.Fatalf("unexpected main function")
	}
	if prog.CreateTestMainPackage(mainPkg) != nil {
		t.Fatalf("CreateTestMainPackage returned non-nil")
	}
}
<commit_msg>go/ssa/interp: exclude another long-running test if -short is set<commit_after>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build !windows,!plan9

package interp_test

import (
	"bytes"
	"fmt"
	"go/build"
	"os"
	"path/filepath"
	"strings"
	"testing"
	"time"

	"golang.org/x/tools/go/loader"
	"golang.org/x/tools/go/ssa"
	"golang.org/x/tools/go/ssa/interp"
	"golang.org/x/tools/go/ssa/ssautil"
	"golang.org/x/tools/go/types"
)

// Each line contains a space-separated list of $GOROOT/test/
// filenames comprising the main package of a program.
// They are ordered quickest-first, roughly.
//
// TODO(adonovan): integrate into the $GOROOT/test driver scripts,
// golden file checking, etc.
var gorootTestTests = []string{
	"235.go",
	"alias1.go",
	"chancap.go",
	"func5.go",
	"func6.go",
	"func7.go",
	"func8.go",
	"helloworld.go",
	"varinit.go",
	"escape3.go",
	"initcomma.go",
	"cmp.go",
	"compos.go",
	"turing.go",
	"indirect.go",
	"complit.go",
	"for.go",
	"struct0.go",
	"intcvt.go",
	"printbig.go",
	"deferprint.go",
	"escape.go",
	"range.go",
	"const4.go",
	"float_lit.go",
	"bigalg.go",
	"decl.go",
	"if.go",
	"named.go",
	"bigmap.go",
	"func.go",
	"reorder2.go",
	"closure.go",
	"gc.go",
	"simassign.go",
	"iota.go",
	"nilptr2.go",
	"goprint.go", // doesn't actually assert anything (cmpout)
	"utf.go",
	"method.go",
	"char_lit.go",
	"env.go",
	"int_lit.go",
	"string_lit.go",
	"defer.go",
	"typeswitch.go",
	"stringrange.go",
	"reorder.go",
	"method3.go",
	"literal.go",
	"nul1.go", // doesn't actually assert anything (errorcheckoutput)
	"zerodivide.go",
	"convert.go",
	"convT2X.go",
	"switch.go",
	"initialize.go",
	"ddd.go",
	"blank.go", // partly disabled
	"map.go",
	"closedchan.go",
	"divide.go",
	"rename.go",
	"const3.go",
	"nil.go",
	"recover.go", // reflection parts disabled
	"recover1.go",
	"recover2.go",
	"recover3.go",
	"typeswitch1.go",
	"floatcmp.go",
	"crlf.go", // doesn't actually assert anything (runoutput)
	// Slow tests follow.
	"bom.go", // ~1.7s
	"gc1.go", // ~1.7s
	"cmplxdivide.go cmplxdivide1.go", // ~2.4s

	// Working, but not worth enabling:
	// "append.go",    // works, but slow (15s).
	// "gc2.go",       // works, but slow, and cheats on the memory check.
	// "sigchld.go",   // works, but only on POSIX.
	// "peano.go",     // works only up to n=9, and slow even then.
	// "stack.go",     // works, but too slow (~30s) by default.
	// "solitaire.go", // works, but too slow (~30s).
	// "const.go",     // works but for but one bug: constant folder doesn't consider representations.
	// "init1.go",     // too slow (80s) and not that interesting. Cheats on ReadMemStats check too.
	// "rotate.go rotate0.go", // emits source for a test
	// "rotate.go rotate1.go", // emits source for a test
	// "rotate.go rotate2.go", // emits source for a test
	// "rotate.go rotate3.go", // emits source for a test
	// "64bit.go",             // emits source for a test
	// "run.go",               // test driver, not a test.

	// Broken.  TODO(adonovan): fix.
	// copy.go         // very slow; but with N=4 quickly crashes, slice index out of range.
	// nilptr.go       // interp: V > uintptr not implemented. Slow test, lots of mem
	// args.go         // works, but requires specific os.Args from the driver.
	// index.go        // a template, not a real test.
	// mallocfin.go    // SetFinalizer not implemented.

	// TODO(adonovan): add tests from $GOROOT/test/* subtrees:
	// bench chan bugs fixedbugs interface ken.
}

// These are files in go.tools/go/ssa/interp/testdata/.
var testdataTests = []string{
	"boundmeth.go",
	"complit.go",
	"coverage.go",
	"defer.go",
	"fieldprom.go",
	"ifaceconv.go",
	"ifaceprom.go",
	"initorder.go",
	"methprom.go",
	"mrvchain.go",
	"range.go",
	"recover.go",
	"reflect.go",
	"static.go",
	"callstack.go",
}

// These are files and packages in $GOROOT/src/.
var gorootSrcTests = []string{
	"encoding/ascii85",
	"encoding/hex",
	// "encoding/pem", // TODO(adonovan): implement (reflect.Value).SetString
	// "testing",      // TODO(adonovan): implement runtime.Goexit correctly
	"unicode",

	// Too slow:
	// "container/ring",
	// "hash/adler32",

	// TODO(adonovan): packages with Examples require os.Pipe (unimplemented):
	// "hash/crc32",
	// "unicode/utf8",
	// "log",
	// "path",
	// "flag",
	// "encoding/csv"
	// "text/scanner"
}

type successPredicate func(exitcode int, output string) error

func run(t *testing.T, dir, input string, success successPredicate) bool {
	fmt.Printf("Input: %s\n", input)

	start := time.Now()

	var inputs []string
	for _, i := range strings.Split(input, " ") {
		if strings.HasSuffix(i, ".go") {
			i = dir + i
		}
		inputs = append(inputs, i)
	}

	var conf loader.Config
	if _, err := conf.FromArgs(inputs, true); err != nil {
		t.Errorf("FromArgs(%s) failed: %s", inputs, err)
		return false
	}

	conf.Import("runtime")

	// Print a helpful hint if we don't make it to the end.
	var hint string
	defer func() {
		if hint != "" {
			fmt.Println("FAIL")
			fmt.Println(hint)
		} else {
			fmt.Println("PASS")
		}

		interp.CapturedOutput = nil
	}()

	hint = fmt.Sprintf("To dump SSA representation, run:\n%% go build golang.org/x/tools/cmd/ssadump && ./ssadump -build=CFP %s\n", input)

	iprog, err := conf.Load()
	if err != nil {
		t.Errorf("conf.Load(%s) failed: %s", inputs, err)
		return false
	}

	prog := ssautil.CreateProgram(iprog, ssa.SanityCheckFunctions)
	prog.BuildAll()

	var mainPkg *ssa.Package
	var initialPkgs []*ssa.Package
	for _, info := range iprog.InitialPackages() {
		if info.Pkg.Path() == "runtime" {
			continue // not an initial package
		}
		p := prog.Package(info.Pkg)
		initialPkgs = append(initialPkgs, p)
		if mainPkg == nil && p.Func("main") != nil {
			mainPkg = p
		}
	}
	if mainPkg == nil {
		testmainPkg := prog.CreateTestMainPackage(initialPkgs...)
		if testmainPkg == nil {
			t.Errorf("CreateTestMainPackage(%s) returned nil", mainPkg)
			return false
		}
		if testmainPkg.Func("main") == nil {
			t.Errorf("synthetic testmain package has no main")
			return false
		}
		mainPkg = testmainPkg
	}

	var out bytes.Buffer
	interp.CapturedOutput = &out

	hint = fmt.Sprintf("To trace execution, run:\n%% go build golang.org/x/tools/cmd/ssadump && ./ssadump -build=C -run --interp=T %s\n", input)
	exitCode := interp.Interpret(mainPkg, 0, &types.StdSizes{8, 8}, inputs[0], []string{})

	// The definition of success varies with each file.
	if err := success(exitCode, out.String()); err != nil {
		t.Errorf("interp.Interpret(%s) failed: %s", inputs, err)
		return false
	}

	hint = "" // call off the hounds

	if false {
		fmt.Println(input, time.Since(start)) // test profiling
	}

	return true
}

const slash = string(os.PathSeparator)

func printFailures(failures []string) {
	if failures != nil {
		fmt.Println("The following tests failed:")
		for _, f := range failures {
			fmt.Printf("\t%s\n", f)
		}
	}
}

func success(exitcode int, output string) error {
	if exitcode != 0 {
		return fmt.Errorf("exit code was %d", exitcode)
	}
	if strings.Contains(output, "BUG") {
		return fmt.Errorf("exited zero but output contained 'BUG'")
	}
	return nil
}

// TestTestdataFiles runs the interpreter on testdata/*.go.
func TestTestdataFiles(t *testing.T) {
	var failures []string
	start := time.Now()
	for _, input := range testdataTests {
		if testing.Short() && time.Since(start) > 30*time.Second {
			printFailures(failures)
			t.Skipf("timeout - aborting test")
		}
		if !run(t, "testdata"+slash, input, success) {
			failures = append(failures, input)
		}
	}
	printFailures(failures)
}

// TestGorootTest runs the interpreter on $GOROOT/test/*.go.
func TestGorootTest(t *testing.T) {
	if testing.Short() {
		t.Skip() // too slow (~30s)
	}

	var failures []string

	for _, input := range gorootTestTests {
		if !run(t, filepath.Join(build.Default.GOROOT, "test")+slash, input, success) {
			failures = append(failures, input)
		}
	}
	for _, input := range gorootSrcTests {
		if !run(t, filepath.Join(build.Default.GOROOT, "src")+slash, input, success) {
			failures = append(failures, input)
		}
	}
	printFailures(failures)
}

// TestTestmainPackage runs the interpreter on a synthetic "testmain" package.
func TestTestmainPackage(t *testing.T) {
	if testing.Short() {
		t.Skip() // too slow on some platforms
	}

	success := func(exitcode int, output string) error {
		if exitcode == 0 {
			return fmt.Errorf("unexpected success")
		}
		if !strings.Contains(output, "FAIL: TestFoo") {
			return fmt.Errorf("missing failure log for TestFoo")
		}
		if !strings.Contains(output, "FAIL: TestBar") {
			return fmt.Errorf("missing failure log for TestBar")
		}
		// TODO(adonovan): test benchmarks too
		return nil
	}
	run(t, "testdata"+slash, "a_test.go", success)
}

// CreateTestMainPackage should return nil if there were no tests.
func TestNullTestmainPackage(t *testing.T) {
	var conf loader.Config
	conf.CreateFromFilenames("", "testdata/b_test.go")
	iprog, err := conf.Load()
	if err != nil {
		t.Fatalf("CreatePackages failed: %s", err)
	}
	prog := ssautil.CreateProgram(iprog, ssa.SanityCheckFunctions)
	mainPkg := prog.Package(iprog.Created[0].Pkg)
	if mainPkg.Func("main") != nil {
		t.Fatalf("unexpected main function")
	}
	if prog.CreateTestMainPackage(mainPkg) != nil {
		t.Fatalf("CreateTestMainPackage returned non-nil")
	}
}
<|endoftext|>


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

// +build go1.7

package azure

import (
	"encoding/xml"
	"fmt"
)

var (
	azureImageReplicateURL = "services/images/%s/replicate"
)

type ReplicationInput struct {
	XMLName         xml.Name `xml:"http://schemas.microsoft.com/windowsazure ReplicationInput"`
	TargetLocations []string `xml:"TargetLocations>Region"`
	Offer           string   `xml:"ComputeImageAttributes>Offer"`
	Sku             string   `xml:"ComputeImageAttributes>Sku"`
	Version         string   `xml:"ComputeImageAttributes>Version"`
}

func (a *API) ReplicateImage(image, offer, sku, version string, regions ...string) error {
	ri := ReplicationInput{
		TargetLocations: regions,
		Offer:           offer,
		Sku:             sku,
		Version:         version,
	}

	data, err := xml.Marshal(&ri)
	if err != nil {
		return err
	}

	url := fmt.Sprintf(azureImageReplicateURL, image)

	op, err := a.client.SendAzurePutRequest(url, "", data)
	if err != nil {
		return err
	}

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

// +build go1.7

package azure

import (
	"encoding/xml"
	"fmt"
)

var (
	azureImageReplicateURL   = "services/images/%s/replicate"
	azureImageUnreplicateURL = "services/images/%s/unreplicate"
)

type ReplicationInput struct {
	XMLName         xml.Name `xml:"http://schemas.microsoft.com/windowsazure ReplicationInput"`
	TargetLocations []string `xml:"TargetLocations>Region"`
	Offer           string   `xml:"ComputeImageAttributes>Offer"`
	Sku             string   `xml:"ComputeImageAttributes>Sku"`
	Version         string   `xml:"ComputeImageAttributes>Version"`
}

func (a *API) ReplicateImage(image, offer, sku, version string, regions ...string) error {
	ri := ReplicationInput{
		TargetLocations: regions,
		Offer:           offer,
		Sku:             sku,
		Version:         version,
	}

	data, err := xml.Marshal(&ri)
	if err != nil {
		return err
	}

	url := fmt.Sprintf(azureImageReplicateURL, image)

	op, err := a.client.SendAzurePutRequest(url, "", data)
	if err != nil {
		return err
	}

	return a.client.WaitForOperation(op, nil)
}

func (a *API) UnreplicateImage(image string) error {
	url := fmt.Sprintf(azureImageUnreplicateURL, image)
	op, err := a.client.SendAzurePutRequest(url, "", []byte{})
	if err != nil {
		return err
	}

	return a.client.WaitForOperation(op, nil)
}
<|endoftext|>


<commit_before>/*
 * // Copyright (c) 2017 Cisco and/or its affiliates.
 * //
 * // 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 cache

import (
	"github.com/ligato/cn-infra/datasync"

	nsmodel "github.com/contiv/vpp/plugins/ksr/model/namespace"
	podmodel "github.com/contiv/vpp/plugins/ksr/model/pod"
	policymodel "github.com/contiv/vpp/plugins/ksr/model/policy"
)

// PolicyCacheAPI defines API of PolicyCache used for a non-persistent storage
// of K8s State data with fast lookups.
// The cache processes K8s State data updates and RESYNC events through Update()
// and Resync() APIs, respectively.
// The cache allows to get notified about changes via convenient callbacks.
// A watcher needs to implement the interface PolicyCacheWatcher and subscribe
// for watching using Watch() API.
// The cache provides various fast lookup methods (e.g. by the label selector).
type PolicyCacheAPI interface {
	// Update processes a datasync change event associated with K8s State data.
	// The change is applied into the cache and all subscribed watchers are
	// notified.
	// The function will forward any error returned by a watcher.
	Update(dataChngEv datasync.ChangeEvent) error

	// Resync processes a datasync resync event associated with K8s State data.
	// The cache content is full replaced with the received data and all
	// subscribed watchers are notified.
	// The function will forward any error returned by a watcher.
	Resync(resyncEv datasync.ResyncEvent) error

	// Watch subscribes a new watcher.
	Watch(watcher PolicyCacheWatcher) error

	// LookupPod returns data of a given Pod.
	LookupPod(pod podmodel.ID) (found bool, data *podmodel.Pod)

	// LookupPodsByLabelSelector evaluates label selector (expression and/or match
	// labels) and returns IDs of matching pods.
	LookupPodsByLabelSelector(podLabelSelector *policymodel.Policy_LabelSelector) (pods []podmodel.ID)

	// LookupPodsByNSLabelSelector evaluates label selector (expression and/or match
	// labels and returns IDs of matching pods in a namespace.
	// todo - namespace to nsmodel.ID
	LookupPodsByNSLabelSelector(namespace string, podLabelSelector *policymodel.Policy_LabelSelector) (pods []podmodel.ID)

	// LookupPodsByNamespace returns IDs of all pods inside a given namespace.
	LookupPodsByNamespace(namespace string) (pods []podmodel.ID)

	// ListAllPods returns IDs of all known pods.
	ListAllPods() (pods []podmodel.ID)

	// LookupPolicy returns data of a given Policy.
	LookupPolicy(policy policymodel.ID) (found bool, data *policymodel.Policy)

	// LookupPoliciesByPod returns IDs of all policies assigned to a given pod.
	LookupPoliciesByPod(pod podmodel.ID) (policies []policymodel.ID)

	// ListAllPolicies returns IDs of all policies.
	ListAllPolicies() (policies []policymodel.ID)

	// LookupNamespace returns data of a given namespace.
	LookupNamespace(namespace nsmodel.ID) (found bool, data *nsmodel.Namespace)

	// LookupNamespacesByLabelSelector evaluates label selector (expression
	// and/or match labels) and returns IDs of matching namespaces.
	LookupNamespacesByLabelSelector(nsLabelSelector *policymodel.Policy_LabelSelector) (namespaces []nsmodel.ID)

	// ListAllNamespaces returns IDs of all known namespaces.
	ListAllNamespaces() (namespaces []nsmodel.ID)
}

// PolicyCacheWatcher defines interface that a PolicyCache watcher must implement.
type PolicyCacheWatcher interface {
	// Resync is called by Policy Cache during a RESYNC event.
	Resync(data *DataResyncEvent) error

	// AddPod is called by Policy Cache when a new pod is created.
	AddPod(pod *podmodel.Pod) error

	// DelPod is called by Policy Cache after a pod was removed.
	DelPod(pod *podmodel.Pod) error

	// UpdatePod is called by Policy Cache when data of a pod were modified.
	UpdatePod(oldPod, newPod *podmodel.Pod) error

	// AddPolicy is called by Policy Cache when a new policy is created.
	AddPolicy(policy *policymodel.Policy) error

	// DelPolicy is called by Policy Cache after a policy was removed.
	DelPolicy(policy *policymodel.Policy) error

	// UpdatePolicy is called by Policy Cache when date of a policy were
	// modified.
	UpdatePolicy(oldPolicy, newPolicy *policymodel.Policy) error

	// AddNamespace is called by Policy Cache when a new namespace is created.
	AddNamespace(ns *nsmodel.Namespace) error

	// DelNamespace is called by Policy Cache after a namespace was removed.
	DelNamespace(ns *nsmodel.Namespace) error

	// UpdateNamespace is called by Policy Cache when data of a namespace were
	// modified.
	UpdateNamespace(oldNs, newNs *nsmodel.Namespace) error
}
<commit_msg>Fixed bugs for empty LabelSelectors and Ingress/Egress Policies<commit_after>/*
 * // Copyright (c) 2017 Cisco and/or its affiliates.
 * //
 * // 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 cache

import (
	"github.com/ligato/cn-infra/datasync"

	nsmodel "github.com/contiv/vpp/plugins/ksr/model/namespace"
	podmodel "github.com/contiv/vpp/plugins/ksr/model/pod"
	policymodel "github.com/contiv/vpp/plugins/ksr/model/policy"
)

// PolicyCacheAPI defines API of PolicyCache used for a non-persistent storage
// of K8s State data with fast lookups.
// The cache processes K8s State data updates and RESYNC events through Update()
// and Resync() APIs, respectively.
// The cache allows to get notified about changes via convenient callbacks.
// A watcher needs to implement the interface PolicyCacheWatcher and subscribe
// for watching using Watch() API.
// The cache provides various fast lookup methods (e.g. by the label selector).
type PolicyCacheAPI interface {
	// Update processes a datasync change event associated with K8s State data.
	// The change is applied into the cache and all subscribed watchers are
	// notified.
	// The function will forward any error returned by a watcher.
	Update(dataChngEv datasync.ChangeEvent) error

	// Resync processes a datasync resync event associated with K8s State data.
	// The cache content is full replaced with the received data and all
	// subscribed watchers are notified.
	// The function will forward any error returned by a watcher.
	Resync(resyncEv datasync.ResyncEvent) error

	// Watch subscribes a new watcher.
	Watch(watcher PolicyCacheWatcher) error

	// LookupPod returns data of a given Pod.
	LookupPod(pod podmodel.ID) (found bool, data *podmodel.Pod)

	// LookupPodsByLabelSelector evaluates label selector (expression and/or match
	// labels) and returns IDs of matching pods.
	LookupPodsByLabelSelector(podLabelSelector *policymodel.Policy_LabelSelector) (pods []podmodel.ID)

	// LookupPodsByNSLabelSelector evaluates label selector (expression and/or match
	// labels and returns IDs of matching pods in a namespace.
	LookupPodsByNSLabelSelector(policyNamespace string, podLabelSelector *policymodel.Policy_LabelSelector) (pods []podmodel.ID)

	// LookupPodsByNamespace returns IDs of all pods inside a given namespace.
	LookupPodsByNamespace(policyNamespace string) (pods []podmodel.ID)

	// ListAllPods returns IDs of all known pods.
	ListAllPods() (pods []podmodel.ID)

	// LookupPolicy returns data of a given Policy.
	LookupPolicy(policy policymodel.ID) (found bool, data *policymodel.Policy)

	// LookupPoliciesByPod returns IDs of all policies assigned to a given pod.
	LookupPoliciesByPod(pod podmodel.ID) (policies []policymodel.ID)

	// ListAllPolicies returns IDs of all policies.
	ListAllPolicies() (policies []policymodel.ID)

	// LookupNamespace returns data of a given namespace.
	LookupNamespace(namespace nsmodel.ID) (found bool, data *nsmodel.Namespace)

	// LookupNamespacesByLabelSelector evaluates label selector (expression
	// and/or match labels) and returns IDs of matching namespaces.
	LookupNamespacesByLabelSelector(nsLabelSelector *policymodel.Policy_LabelSelector) (namespaces []nsmodel.ID)

	// ListAllNamespaces returns IDs of all known namespaces.
	ListAllNamespaces() (namespaces []nsmodel.ID)
}

// PolicyCacheWatcher defines interface that a PolicyCache watcher must implement.
type PolicyCacheWatcher interface {
	// Resync is called by Policy Cache during a RESYNC event.
	Resync(data *DataResyncEvent) error

	// AddPod is called by Policy Cache when a new pod is created.
	AddPod(pod *podmodel.Pod) error

	// DelPod is called by Policy Cache after a pod was removed.
	DelPod(pod *podmodel.Pod) error

	// UpdatePod is called by Policy Cache when data of a pod were modified.
	UpdatePod(oldPod, newPod *podmodel.Pod) error

	// AddPolicy is called by Policy Cache when a new policy is created.
	AddPolicy(policy *policymodel.Policy) error

	// DelPolicy is called by Policy Cache after a policy was removed.
	DelPolicy(policy *policymodel.Policy) error

	// UpdatePolicy is called by Policy Cache when date of a policy were
	// modified.
	UpdatePolicy(oldPolicy, newPolicy *policymodel.Policy) error

	// AddNamespace is called by Policy Cache when a new namespace is created.
	AddNamespace(ns *nsmodel.Namespace) error

	// DelNamespace is called by Policy Cache after a namespace was removed.
	DelNamespace(ns *nsmodel.Namespace) error

	// UpdateNamespace is called by Policy Cache when data of a namespace were
	// modified.
	UpdateNamespace(oldNs, newNs *nsmodel.Namespace) error
}
<|endoftext|>


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

package parser

// TODO(bep) archetype remove unused from this package.

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"strings"

	"github.com/spf13/cast"

	"github.com/BurntSushi/toml"
	"github.com/chaseadamsio/goorgeous"

	"gopkg.in/yaml.v2"
)

// FrontmatterType represents a type of frontmatter.
type FrontmatterType struct {
	// Parse decodes content into a Go interface.
	Parse func([]byte) (map[string]interface{}, error)

	markstart, markend []byte // starting and ending delimiters
	includeMark        bool   // include start and end mark in output
}

// InterfaceToConfig encodes a given input based upon the mark and writes to w.
func InterfaceToConfig(in interface{}, mark rune, w io.Writer) error {
	if in == nil {
		return errors.New("input was nil")
	}

	switch mark {
	case rune(YAMLLead[0]):
		b, err := yaml.Marshal(in)
		if err != nil {
			return err
		}

		_, err = w.Write(b)
		return err

	case rune(TOMLLead[0]):
		return toml.NewEncoder(w).Encode(in)
	case rune(JSONLead[0]):
		b, err := json.MarshalIndent(in, "", "   ")
		if err != nil {
			return err
		}

		_, err = w.Write(b)
		if err != nil {
			return err
		}

		_, err = w.Write([]byte{'\n'})
		return err

	default:
		return errors.New("Unsupported Format provided")
	}
}

// InterfaceToFrontMatter encodes a given input into a frontmatter
// representation based upon the mark with the appropriate front matter delimiters
// surrounding the output, which is written to w.
func InterfaceToFrontMatter(in interface{}, mark rune, w io.Writer) error {
	if in == nil {
		return errors.New("input was nil")
	}

	switch mark {
	case rune(YAMLLead[0]):
		_, err := w.Write([]byte(YAMLDelimUnix))
		if err != nil {
			return err
		}

		err = InterfaceToConfig(in, mark, w)
		if err != nil {
			return err
		}

		_, err = w.Write([]byte(YAMLDelimUnix))
		return err

	case rune(TOMLLead[0]):
		_, err := w.Write([]byte(TOMLDelimUnix))
		if err != nil {
			return err
		}

		err = InterfaceToConfig(in, mark, w)

		if err != nil {
			return err
		}

		_, err = w.Write([]byte("\n" + TOMLDelimUnix))
		return err

	default:
		return InterfaceToConfig(in, mark, w)
	}
}

// FormatToLeadRune takes a given format kind and return the leading front
// matter delimiter.
func FormatToLeadRune(kind string) rune {
	switch FormatSanitize(kind) {
	case "yaml":
		return rune([]byte(YAMLLead)[0])
	case "json":
		return rune([]byte(JSONLead)[0])
	case "org":
		return '#'
	default:
		return rune([]byte(TOMLLead)[0])
	}
}

// FormatSanitize returns the canonical format name for a given kind.
//
// TODO(bep) move to helpers
func FormatSanitize(kind string) string {
	switch strings.ToLower(kind) {
	case "yaml", "yml":
		return "yaml"
	case "toml", "tml":
		return "toml"
	case "json", "js":
		return "json"
	case "org":
		return kind
	default:
		return "toml"
	}
}

// DetectFrontMatter detects the type of frontmatter analysing its first character.
func DetectFrontMatter(mark rune) (f *FrontmatterType) {
	switch mark {
	case '-':
		return &FrontmatterType{HandleYAMLMetaData, []byte(YAMLDelim), []byte(YAMLDelim), false}
	case '+':
		return &FrontmatterType{HandleTOMLMetaData, []byte(TOMLDelim), []byte(TOMLDelim), false}
	case '{':
		return &FrontmatterType{HandleJSONMetaData, []byte{'{'}, []byte{'}'}, true}
	case '#':
		return &FrontmatterType{HandleOrgMetaData, []byte("#+"), []byte("\n"), false}
	default:
		return nil
	}
}

// HandleTOMLMetaData unmarshals TOML-encoded datum and returns a Go interface
// representing the encoded data structure.
func HandleTOMLMetaData(datum []byte) (map[string]interface{}, error) {
	m := map[string]interface{}{}
	datum = removeTOMLIdentifier(datum)

	_, err := toml.Decode(string(datum), &m)

	return m, err

}

// removeTOMLIdentifier removes, if necessary, beginning and ending TOML
// frontmatter delimiters from a byte slice.
func removeTOMLIdentifier(datum []byte) []byte {
	ld := len(datum)
	if ld < 8 {
		return datum
	}

	b := bytes.TrimPrefix(datum, []byte(TOMLDelim))
	if ld-len(b) != 3 {
		// No TOML prefix trimmed, so bail out
		return datum
	}

	b = bytes.Trim(b, "\r\n")
	return bytes.TrimSuffix(b, []byte(TOMLDelim))
}

// HandleYAMLMetaData unmarshals YAML-encoded datum and returns a Go interface
// representing the encoded data structure.
func HandleYAMLMetaData(datum []byte) (map[string]interface{}, error) {
	m := map[string]interface{}{}
	err := yaml.Unmarshal(datum, &m)

	// To support boolean keys, the `yaml` package unmarshals maps to
	// map[interface{}]interface{}. Here we recurse through the result
	// and change all maps to map[string]interface{} like we would've
	// gotten from `json`.
	if err == nil {
		for k, v := range m {
			if vv, changed := stringifyMapKeys(v); changed {
				m[k] = vv
			}
		}
	}

	return m, err
}

// HandleYAMLData unmarshals YAML-encoded datum and returns a Go interface
// representing the encoded data structure.
func HandleYAMLData(datum []byte) (interface{}, error) {
	var m interface{}
	err := yaml.Unmarshal(datum, &m)
	if err != nil {
		return nil, err
	}

	// To support boolean keys, the `yaml` package unmarshals maps to
	// map[interface{}]interface{}. Here we recurse through the result
	// and change all maps to map[string]interface{} like we would've
	// gotten from `json`.
	if mm, changed := stringifyMapKeys(m); changed {
		return mm, nil
	}

	return m, nil
}

// stringifyMapKeys recurses into in and changes all instances of
// map[interface{}]interface{} to map[string]interface{}. This is useful to
// work around the impedence mismatch between JSON and YAML unmarshaling that's
// described here: https://github.com/go-yaml/yaml/issues/139
//
// Inspired by https://github.com/stripe/stripe-mock, MIT licensed
func stringifyMapKeys(in interface{}) (interface{}, bool) {
	switch in := in.(type) {
	case []interface{}:
		for i, v := range in {
			if vv, replaced := stringifyMapKeys(v); replaced {
				in[i] = vv
			}
		}
	case map[interface{}]interface{}:
		res := make(map[string]interface{})
		for k, v := range in {
			ks, err := cast.ToStringE(k)
			if err != nil {
				ks = fmt.Sprintf("%v", k)
			}
			if vv, replaced := stringifyMapKeys(v); replaced {
				res[ks] = vv
			} else {
				res[ks] = v
			}
		}
		return res, true
	}

	return nil, false
}

// HandleJSONMetaData unmarshals JSON-encoded datum and returns a Go interface
// representing the encoded data structure.
func HandleJSONMetaData(datum []byte) (map[string]interface{}, error) {
	m := make(map[string]interface{})

	if datum == nil {
		// Package json returns on error on nil input.
		// Return an empty map to be consistent with our other supported
		// formats.
		return m, nil
	}

	err := json.Unmarshal(datum, &m)
	return m, err
}

// HandleJSONData unmarshals JSON-encoded datum and returns a Go interface
// representing the encoded data structure.
func HandleJSONData(datum []byte) (interface{}, error) {
	if datum == nil {
		// Package json returns on error on nil input.
		// Return an empty map to be consistent with our other supported
		// formats.
		return make(map[string]interface{}), nil
	}

	var f interface{}
	err := json.Unmarshal(datum, &f)
	return f, err
}

// HandleOrgMetaData unmarshals org-mode encoded datum and returns a Go
// interface representing the encoded data structure.
func HandleOrgMetaData(datum []byte) (map[string]interface{}, error) {
	return goorgeous.OrgHeaders(datum)
}
<commit_msg>parser: Add WARNING for integer YAML keys<commit_after>// Copyright 2015 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package parser

// TODO(bep) archetype remove unused from this package.

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"strings"

	"github.com/gohugoio/hugo/helpers"

	"github.com/spf13/cast"

	"github.com/BurntSushi/toml"
	"github.com/chaseadamsio/goorgeous"

	"gopkg.in/yaml.v2"
)

// FrontmatterType represents a type of frontmatter.
type FrontmatterType struct {
	// Parse decodes content into a Go interface.
	Parse func([]byte) (map[string]interface{}, error)

	markstart, markend []byte // starting and ending delimiters
	includeMark        bool   // include start and end mark in output
}

// InterfaceToConfig encodes a given input based upon the mark and writes to w.
func InterfaceToConfig(in interface{}, mark rune, w io.Writer) error {
	if in == nil {
		return errors.New("input was nil")
	}

	switch mark {
	case rune(YAMLLead[0]):
		b, err := yaml.Marshal(in)
		if err != nil {
			return err
		}

		_, err = w.Write(b)
		return err

	case rune(TOMLLead[0]):
		return toml.NewEncoder(w).Encode(in)
	case rune(JSONLead[0]):
		b, err := json.MarshalIndent(in, "", "   ")
		if err != nil {
			return err
		}

		_, err = w.Write(b)
		if err != nil {
			return err
		}

		_, err = w.Write([]byte{'\n'})
		return err

	default:
		return errors.New("Unsupported Format provided")
	}
}

// InterfaceToFrontMatter encodes a given input into a frontmatter
// representation based upon the mark with the appropriate front matter delimiters
// surrounding the output, which is written to w.
func InterfaceToFrontMatter(in interface{}, mark rune, w io.Writer) error {
	if in == nil {
		return errors.New("input was nil")
	}

	switch mark {
	case rune(YAMLLead[0]):
		_, err := w.Write([]byte(YAMLDelimUnix))
		if err != nil {
			return err
		}

		err = InterfaceToConfig(in, mark, w)
		if err != nil {
			return err
		}

		_, err = w.Write([]byte(YAMLDelimUnix))
		return err

	case rune(TOMLLead[0]):
		_, err := w.Write([]byte(TOMLDelimUnix))
		if err != nil {
			return err
		}

		err = InterfaceToConfig(in, mark, w)

		if err != nil {
			return err
		}

		_, err = w.Write([]byte("\n" + TOMLDelimUnix))
		return err

	default:
		return InterfaceToConfig(in, mark, w)
	}
}

// FormatToLeadRune takes a given format kind and return the leading front
// matter delimiter.
func FormatToLeadRune(kind string) rune {
	switch FormatSanitize(kind) {
	case "yaml":
		return rune([]byte(YAMLLead)[0])
	case "json":
		return rune([]byte(JSONLead)[0])
	case "org":
		return '#'
	default:
		return rune([]byte(TOMLLead)[0])
	}
}

// FormatSanitize returns the canonical format name for a given kind.
//
// TODO(bep) move to helpers
func FormatSanitize(kind string) string {
	switch strings.ToLower(kind) {
	case "yaml", "yml":
		return "yaml"
	case "toml", "tml":
		return "toml"
	case "json", "js":
		return "json"
	case "org":
		return kind
	default:
		return "toml"
	}
}

// DetectFrontMatter detects the type of frontmatter analysing its first character.
func DetectFrontMatter(mark rune) (f *FrontmatterType) {
	switch mark {
	case '-':
		return &FrontmatterType{HandleYAMLMetaData, []byte(YAMLDelim), []byte(YAMLDelim), false}
	case '+':
		return &FrontmatterType{HandleTOMLMetaData, []byte(TOMLDelim), []byte(TOMLDelim), false}
	case '{':
		return &FrontmatterType{HandleJSONMetaData, []byte{'{'}, []byte{'}'}, true}
	case '#':
		return &FrontmatterType{HandleOrgMetaData, []byte("#+"), []byte("\n"), false}
	default:
		return nil
	}
}

// HandleTOMLMetaData unmarshals TOML-encoded datum and returns a Go interface
// representing the encoded data structure.
func HandleTOMLMetaData(datum []byte) (map[string]interface{}, error) {
	m := map[string]interface{}{}
	datum = removeTOMLIdentifier(datum)

	_, err := toml.Decode(string(datum), &m)

	return m, err

}

// removeTOMLIdentifier removes, if necessary, beginning and ending TOML
// frontmatter delimiters from a byte slice.
func removeTOMLIdentifier(datum []byte) []byte {
	ld := len(datum)
	if ld < 8 {
		return datum
	}

	b := bytes.TrimPrefix(datum, []byte(TOMLDelim))
	if ld-len(b) != 3 {
		// No TOML prefix trimmed, so bail out
		return datum
	}

	b = bytes.Trim(b, "\r\n")
	return bytes.TrimSuffix(b, []byte(TOMLDelim))
}

// HandleYAMLMetaData unmarshals YAML-encoded datum and returns a Go interface
// representing the encoded data structure.
func HandleYAMLMetaData(datum []byte) (map[string]interface{}, error) {
	m := map[string]interface{}{}
	err := yaml.Unmarshal(datum, &m)

	// To support boolean keys, the `yaml` package unmarshals maps to
	// map[interface{}]interface{}. Here we recurse through the result
	// and change all maps to map[string]interface{} like we would've
	// gotten from `json`.
	if err == nil {
		for k, v := range m {
			if vv, changed := stringifyMapKeys(v); changed {
				m[k] = vv
			}
		}
	}

	return m, err
}

// HandleYAMLData unmarshals YAML-encoded datum and returns a Go interface
// representing the encoded data structure.
func HandleYAMLData(datum []byte) (interface{}, error) {
	var m interface{}
	err := yaml.Unmarshal(datum, &m)
	if err != nil {
		return nil, err
	}

	// To support boolean keys, the `yaml` package unmarshals maps to
	// map[interface{}]interface{}. Here we recurse through the result
	// and change all maps to map[string]interface{} like we would've
	// gotten from `json`.
	if mm, changed := stringifyMapKeys(m); changed {
		return mm, nil
	}

	return m, nil
}

// stringifyMapKeys recurses into in and changes all instances of
// map[interface{}]interface{} to map[string]interface{}. This is useful to
// work around the impedence mismatch between JSON and YAML unmarshaling that's
// described here: https://github.com/go-yaml/yaml/issues/139
//
// Inspired by https://github.com/stripe/stripe-mock, MIT licensed
func stringifyMapKeys(in interface{}) (interface{}, bool) {
	switch in := in.(type) {
	case []interface{}:
		for i, v := range in {
			if vv, replaced := stringifyMapKeys(v); replaced {
				in[i] = vv
			}
		}
	case map[interface{}]interface{}:
		res := make(map[string]interface{})
		var (
			ok  bool
			err error
		)
		for k, v := range in {
			var ks string

			if ks, ok = k.(string); !ok {
				ks, err = cast.ToStringE(k)
				if err != nil {
					ks = fmt.Sprintf("%v", k)
				}
				// TODO(bep) added in Hugo 0.37, remove some time in the future.
				helpers.DistinctFeedbackLog.Printf("WARNING: YAML data/frontmatter with keys of type %T is since Hugo 0.37 converted to strings", k)
			}
			if vv, replaced := stringifyMapKeys(v); replaced {
				res[ks] = vv
			} else {
				res[ks] = v
			}
		}
		return res, true
	}

	return nil, false
}

// HandleJSONMetaData unmarshals JSON-encoded datum and returns a Go interface
// representing the encoded data structure.
func HandleJSONMetaData(datum []byte) (map[string]interface{}, error) {
	m := make(map[string]interface{})

	if datum == nil {
		// Package json returns on error on nil input.
		// Return an empty map to be consistent with our other supported
		// formats.
		return m, nil
	}

	err := json.Unmarshal(datum, &m)
	return m, err
}

// HandleJSONData unmarshals JSON-encoded datum and returns a Go interface
// representing the encoded data structure.
func HandleJSONData(datum []byte) (interface{}, error) {
	if datum == nil {
		// Package json returns on error on nil input.
		// Return an empty map to be consistent with our other supported
		// formats.
		return make(map[string]interface{}), nil
	}

	var f interface{}
	err := json.Unmarshal(datum, &f)
	return f, err
}

// HandleOrgMetaData unmarshals org-mode encoded datum and returns a Go
// interface representing the encoded data structure.
func HandleOrgMetaData(datum []byte) (map[string]interface{}, error) {
	return goorgeous.OrgHeaders(datum)
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package handlers

import (
	"fmt"
	"net/http"
	"net/url"
	"strings"

	"k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apiserver/pkg/endpoints/request"
)

// ScopeNamer handles accessing names from requests and objects
type ScopeNamer interface {
	// Namespace returns the appropriate namespace value from the request (may be empty) or an
	// error.
	Namespace(req *http.Request) (namespace string, err error)
	// Name returns the name from the request, and an optional namespace value if this is a namespace
	// scoped call. An error is returned if the name is not available.
	Name(req *http.Request) (namespace, name string, err error)
	// ObjectName returns the namespace and name from an object if they exist, or an error if the object
	// does not support names.
	ObjectName(obj runtime.Object) (namespace, name string, err error)
	// SetSelfLink sets the provided URL onto the object. The method should return nil if the object
	// does not support selfLinks.
	SetSelfLink(obj runtime.Object, url string) error
	// GenerateLink creates an encoded URI for a given runtime object that represents the canonical path
	// and query.
	GenerateLink(requestInfo *request.RequestInfo, obj runtime.Object) (uri string, err error)
	// GenerateListLink creates an encoded URI for a list that represents the canonical path and query.
	GenerateListLink(req *http.Request) (uri string, err error)
}

type ContextBasedNaming struct {
	SelfLinker    runtime.SelfLinker
	ClusterScoped bool

	SelfLinkPathPrefix string
	SelfLinkPathSuffix string
}

// ContextBasedNaming implements ScopeNamer
var _ ScopeNamer = ContextBasedNaming{}

func (n ContextBasedNaming) SetSelfLink(obj runtime.Object, url string) error {
	return n.SelfLinker.SetSelfLink(obj, url)
}

func (n ContextBasedNaming) Namespace(req *http.Request) (namespace string, err error) {
	requestInfo, ok := request.RequestInfoFrom(req.Context())
	if !ok {
		return "", fmt.Errorf("missing requestInfo")
	}
	return requestInfo.Namespace, nil
}

func (n ContextBasedNaming) Name(req *http.Request) (namespace, name string, err error) {
	requestInfo, ok := request.RequestInfoFrom(req.Context())
	if !ok {
		return "", "", fmt.Errorf("missing requestInfo")
	}
	ns, err := n.Namespace(req)
	if err != nil {
		return "", "", err
	}

	if len(requestInfo.Name) == 0 {
		return "", "", errEmptyName
	}
	return ns, requestInfo.Name, nil
}

// fastURLPathEncode encodes the provided path as a URL path
func fastURLPathEncode(path string) string {
	for _, r := range []byte(path) {
		switch {
		case r >= '-' && r <= '9', r >= 'A' && r <= 'Z', r >= 'a' && r <= 'z':
			// characters within this range do not require escaping
		default:
			var u url.URL
			u.Path = path
			return u.EscapedPath()
		}
	}
	return path
}

func (n ContextBasedNaming) GenerateLink(requestInfo *request.RequestInfo, obj runtime.Object) (uri string, err error) {
	namespace, name, err := n.ObjectName(obj)
	if err == errEmptyName && len(requestInfo.Name) > 0 {
		name = requestInfo.Name
	} else if err != nil {
		return "", err
	}
	if len(namespace) == 0 && len(requestInfo.Namespace) > 0 {
		namespace = requestInfo.Namespace
	}

	if n.ClusterScoped {
		return n.SelfLinkPathPrefix + url.QueryEscape(name) + n.SelfLinkPathSuffix, nil
	}

	builder := strings.Builder{}
	builder.Grow(len(n.SelfLinkPathPrefix) + len(namespace) + len(requestInfo.Resource) + len(name) + len(n.SelfLinkPathSuffix) + 8)
	builder.WriteString(n.SelfLinkPathPrefix)
	builder.WriteString(namespace)
	builder.WriteByte('/')
	builder.WriteString(requestInfo.Resource)
	builder.WriteByte('/')
	builder.WriteString(name)
	builder.WriteString(n.SelfLinkPathSuffix)
	return fastURLPathEncode(builder.String()), nil
}

func (n ContextBasedNaming) GenerateListLink(req *http.Request) (uri string, err error) {
	if len(req.URL.RawPath) > 0 {
		return req.URL.RawPath, nil
	}
	return fastURLPathEncode(req.URL.Path), nil
}

func (n ContextBasedNaming) ObjectName(obj runtime.Object) (namespace, name string, err error) {
	name, err = n.SelfLinker.Name(obj)
	if err != nil {
		return "", "", err
	}
	if len(name) == 0 {
		return "", "", errEmptyName
	}
	namespace, err = n.SelfLinker.Namespace(obj)
	if err != nil {
		return "", "", err
	}
	return namespace, name, err
}

// errEmptyName is returned when API requests do not fill the name section of the path.
var errEmptyName = errors.NewBadRequest("name must be provided")
<commit_msg>apiserver: avoid repeated loading context for requestInfo in Namer.Name<commit_after>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package handlers

import (
	"fmt"
	"net/http"
	"net/url"
	"strings"

	"k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apiserver/pkg/endpoints/request"
)

// ScopeNamer handles accessing names from requests and objects
type ScopeNamer interface {
	// Namespace returns the appropriate namespace value from the request (may be empty) or an
	// error.
	Namespace(req *http.Request) (namespace string, err error)
	// Name returns the name from the request, and an optional namespace value if this is a namespace
	// scoped call. An error is returned if the name is not available.
	Name(req *http.Request) (namespace, name string, err error)
	// ObjectName returns the namespace and name from an object if they exist, or an error if the object
	// does not support names.
	ObjectName(obj runtime.Object) (namespace, name string, err error)
	// SetSelfLink sets the provided URL onto the object. The method should return nil if the object
	// does not support selfLinks.
	SetSelfLink(obj runtime.Object, url string) error
	// GenerateLink creates an encoded URI for a given runtime object that represents the canonical path
	// and query.
	GenerateLink(requestInfo *request.RequestInfo, obj runtime.Object) (uri string, err error)
	// GenerateListLink creates an encoded URI for a list that represents the canonical path and query.
	GenerateListLink(req *http.Request) (uri string, err error)
}

type ContextBasedNaming struct {
	SelfLinker    runtime.SelfLinker
	ClusterScoped bool

	SelfLinkPathPrefix string
	SelfLinkPathSuffix string
}

// ContextBasedNaming implements ScopeNamer
var _ ScopeNamer = ContextBasedNaming{}

func (n ContextBasedNaming) SetSelfLink(obj runtime.Object, url string) error {
	return n.SelfLinker.SetSelfLink(obj, url)
}

func (n ContextBasedNaming) Namespace(req *http.Request) (namespace string, err error) {
	requestInfo, ok := request.RequestInfoFrom(req.Context())
	if !ok {
		return "", fmt.Errorf("missing requestInfo")
	}
	return requestInfo.Namespace, nil
}

func (n ContextBasedNaming) Name(req *http.Request) (namespace, name string, err error) {
	requestInfo, ok := request.RequestInfoFrom(req.Context())
	if !ok {
		return "", "", fmt.Errorf("missing requestInfo")
	}

	if len(requestInfo.Name) == 0 {
		return "", "", errEmptyName
	}
	return requestInfo.Namespace, requestInfo.Name, nil
}

// fastURLPathEncode encodes the provided path as a URL path
func fastURLPathEncode(path string) string {
	for _, r := range []byte(path) {
		switch {
		case r >= '-' && r <= '9', r >= 'A' && r <= 'Z', r >= 'a' && r <= 'z':
			// characters within this range do not require escaping
		default:
			var u url.URL
			u.Path = path
			return u.EscapedPath()
		}
	}
	return path
}

func (n ContextBasedNaming) GenerateLink(requestInfo *request.RequestInfo, obj runtime.Object) (uri string, err error) {
	namespace, name, err := n.ObjectName(obj)
	if err == errEmptyName && len(requestInfo.Name) > 0 {
		name = requestInfo.Name
	} else if err != nil {
		return "", err
	}
	if len(namespace) == 0 && len(requestInfo.Namespace) > 0 {
		namespace = requestInfo.Namespace
	}

	if n.ClusterScoped {
		return n.SelfLinkPathPrefix + url.QueryEscape(name) + n.SelfLinkPathSuffix, nil
	}

	builder := strings.Builder{}
	builder.Grow(len(n.SelfLinkPathPrefix) + len(namespace) + len(requestInfo.Resource) + len(name) + len(n.SelfLinkPathSuffix) + 8)
	builder.WriteString(n.SelfLinkPathPrefix)
	builder.WriteString(namespace)
	builder.WriteByte('/')
	builder.WriteString(requestInfo.Resource)
	builder.WriteByte('/')
	builder.WriteString(name)
	builder.WriteString(n.SelfLinkPathSuffix)
	return fastURLPathEncode(builder.String()), nil
}

func (n ContextBasedNaming) GenerateListLink(req *http.Request) (uri string, err error) {
	if len(req.URL.RawPath) > 0 {
		return req.URL.RawPath, nil
	}
	return fastURLPathEncode(req.URL.Path), nil
}

func (n ContextBasedNaming) ObjectName(obj runtime.Object) (namespace, name string, err error) {
	name, err = n.SelfLinker.Name(obj)
	if err != nil {
		return "", "", err
	}
	if len(name) == 0 {
		return "", "", errEmptyName
	}
	namespace, err = n.SelfLinker.Namespace(obj)
	if err != nil {
		return "", "", err
	}
	return namespace, name, err
}

// errEmptyName is returned when API requests do not fill the name section of the path.
var errEmptyName = errors.NewBadRequest("name must be provided")
<|endoftext|>


<commit_before>// docker_tls_args: find the docker daemon's tls args
package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"path/filepath"
	"strconv"
	"strings"
)

func dockerTLSArgs(args []string) error {
	if len(args) > 0 {
		cmdUsage("docker-tls-args", "")
	}
	procRoot := "/proc"
	dirEntries, err := ioutil.ReadDir(procRoot)
	if err != nil {
		return err
	}

	for _, dirEntry := range dirEntries {
		dirName := dirEntry.Name()
		if _, err := strconv.Atoi(dirName); err != nil {
			continue
		}

		if comm, err := ioutil.ReadFile(filepath.Join(procRoot, dirName, "comm")); err != nil || string(comm) != "docker\n" {
			continue
		}

		cmdline, err := ioutil.ReadFile(filepath.Join(procRoot, dirName, "cmdline"))
		if err != nil {
			continue
		}

		isDaemon := false
		tlsArgs := []string{}
		args := bytes.Split(cmdline, []byte{'\000'})
		for i := 0; i < len(args); i++ {
			arg := string(args[i])
			switch {
			case arg == "-d" || arg == "daemon":
				isDaemon = true
				break
			case arg == "--tls", arg == "--tlsverify":
				tlsArgs = append(tlsArgs, arg)
			case strings.HasPrefix(arg, "--tls"):
				tlsArgs = append(tlsArgs, arg)
				if len(args) > i+1 &&
					!strings.Contains(arg, "=") &&
					!strings.HasPrefix(string(args[i+1]), "-") {
					tlsArgs = append(tlsArgs, string(args[i+1]))
					i++
				}
			}
		}
		if !isDaemon {
			continue
		}

		fmt.Println(strings.Join(tlsArgs, " "))
		return nil
	}

	return fmt.Errorf("cannot locate running docker daemon")
}
<commit_msg>Handle Docker daemon named 'dockerd'<commit_after>// docker_tls_args: find the docker daemon's tls args
package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"path/filepath"
	"strconv"
	"strings"
)

func dockerTLSArgs(args []string) error {
	if len(args) > 0 {
		cmdUsage("docker-tls-args", "")
	}
	procRoot := "/proc"
	dirEntries, err := ioutil.ReadDir(procRoot)
	if err != nil {
		return err
	}

	for _, dirEntry := range dirEntries {
		dirName := dirEntry.Name()
		if _, err := strconv.Atoi(dirName); err != nil {
			continue
		}

		isDaemon := false
		if comm, err := ioutil.ReadFile(filepath.Join(procRoot, dirName, "comm")); err != nil {
			continue
		} else if string(comm) == "dockerd\n" {
			isDaemon = true
		} else if string(comm) != "docker\n" {
			continue
		}

		cmdline, err := ioutil.ReadFile(filepath.Join(procRoot, dirName, "cmdline"))
		if err != nil {
			continue
		}

		tlsArgs := []string{}
		args := bytes.Split(cmdline, []byte{'\000'})
		for i := 0; i < len(args); i++ {
			arg := string(args[i])
			switch {
			case arg == "-d" || arg == "daemon":
				isDaemon = true
				break
			case arg == "--tls", arg == "--tlsverify":
				tlsArgs = append(tlsArgs, arg)
			case strings.HasPrefix(arg, "--tls"):
				tlsArgs = append(tlsArgs, arg)
				if len(args) > i+1 &&
					!strings.Contains(arg, "=") &&
					!strings.HasPrefix(string(args[i+1]), "-") {
					tlsArgs = append(tlsArgs, string(args[i+1]))
					i++
				}
			}
		}
		if !isDaemon {
			continue
		}

		fmt.Println(strings.Join(tlsArgs, " "))
		return nil
	}

	return fmt.Errorf("cannot locate running docker daemon")
}
<|endoftext|>


<commit_before>// +build darwin

package memory

import (
	"reflect"
	"strings"
	"testing"
)

func TestGetMemory(t *testing.T) {
	memory, err := Get()
	if err != nil {
		t.Fatalf("error should be nil but got: %v", err)
	}
	if memory.Used <= 0 || memory.Total <= 0 || memory.SwapTotal <= 0 {
		t.Errorf("invalid memory value: %+v", memory)
	}
}

func TestCollectMemoryStats(t *testing.T) {
	got, err := collectMemoryStats(strings.NewReader(
		`Mach Virtual Memory Statistics: (page size of 4096 bytes)
Pages free:                               72827.
Pages active:                           2154445.
Pages inactive:                         1511468.
Pages speculative:                         8107.
Pages throttled:                              0.
Pages wired down:                        446975.
Pages purgeable:                         383371.
"Translation faults":                  97589077.
Pages copy-on-write:                    3305869.
Pages zero filled:                     50848672.
Pages reactivated:                         1999.
Pages purged:                           2496610.
File-backed pages:                       677870.
Anonymous pages:                        2996150.
Pages stored in compressor:                   0.
Pages occupied by compressor:                 0.
Decompressions:                               0.
Compressions:                                 0.
Pageins:                                6333901.
Pageouts:                                   353.
Swapins:                                      0.
Swapouts:                                     0.
`))
	if err != nil {
		t.Fatalf("error should be nil but got: %v", err)
	}
	pageSize := 4096
	expected := &Memory{
		Total:    uint64((446975 + 2154445 + 1511468 + 8107 + 72827) * pageSize),
		Used:     uint64((446975 + 2154445 + 1511468 + 8107 - (383371 + 677870)) * pageSize),
		Cached:   uint64((383371 + 677870) * pageSize),
		Free:     uint64(72827 * pageSize),
		Active:   uint64(2154445 * pageSize),
		Inactive: uint64(1511468 * pageSize),
	}
	if !reflect.DeepEqual(got, expected) {
		t.Errorf("invalid memory value: %+v (expected: %+v)", got, expected)
	}
}

func TestCollectSwapStats(t *testing.T) {
	got, err := collectSwapStats(strings.NewReader(
		"\x00\x00\x00\x40\x01\x00\x00\x00\x00\x00\x3c\x56\x00\x00\x00\x00\x00\x00\xc4\xe9\x00\x00\x00\x00\x00\x10\x00\x00\x01\x00\x00",
	))
	if err != nil {
		t.Fatalf("error should be nil but got: %v", err)
	}
	expected := &swapUsage{
		Total:     0x0000000140000000,
		Avail:     0x00000000563c0000,
		Used:      0x00000000e9c40000,
		Pagesize:  4096,
		Encrypted: true,
	}
	if !reflect.DeepEqual(got, expected) {
		t.Errorf("invalid memory value: %+v (expected: %+v)", got, expected)
	}
}
<commit_msg>swap total can be zero so skip checking the value in test<commit_after>// +build darwin

package memory

import (
	"reflect"
	"strings"
	"testing"
)

func TestGetMemory(t *testing.T) {
	memory, err := Get()
	if err != nil {
		t.Fatalf("error should be nil but got: %v", err)
	}
	if memory.Used <= 0 || memory.Total <= 0 {
		t.Errorf("invalid memory value: %+v", memory)
	}
}

func TestCollectMemoryStats(t *testing.T) {
	got, err := collectMemoryStats(strings.NewReader(
		`Mach Virtual Memory Statistics: (page size of 4096 bytes)
Pages free:                               72827.
Pages active:                           2154445.
Pages inactive:                         1511468.
Pages speculative:                         8107.
Pages throttled:                              0.
Pages wired down:                        446975.
Pages purgeable:                         383371.
"Translation faults":                  97589077.
Pages copy-on-write:                    3305869.
Pages zero filled:                     50848672.
Pages reactivated:                         1999.
Pages purged:                           2496610.
File-backed pages:                       677870.
Anonymous pages:                        2996150.
Pages stored in compressor:                   0.
Pages occupied by compressor:                 0.
Decompressions:                               0.
Compressions:                                 0.
Pageins:                                6333901.
Pageouts:                                   353.
Swapins:                                      0.
Swapouts:                                     0.
`))
	if err != nil {
		t.Fatalf("error should be nil but got: %v", err)
	}
	pageSize := 4096
	expected := &Memory{
		Total:    uint64((446975 + 2154445 + 1511468 + 8107 + 72827) * pageSize),
		Used:     uint64((446975 + 2154445 + 1511468 + 8107 - (383371 + 677870)) * pageSize),
		Cached:   uint64((383371 + 677870) * pageSize),
		Free:     uint64(72827 * pageSize),
		Active:   uint64(2154445 * pageSize),
		Inactive: uint64(1511468 * pageSize),
	}
	if !reflect.DeepEqual(got, expected) {
		t.Errorf("invalid memory value: %+v (expected: %+v)", got, expected)
	}
}

func TestCollectSwapStats(t *testing.T) {
	got, err := collectSwapStats(strings.NewReader(
		"\x00\x00\x00\x40\x01\x00\x00\x00\x00\x00\x3c\x56\x00\x00\x00\x00\x00\x00\xc4\xe9\x00\x00\x00\x00\x00\x10\x00\x00\x01\x00\x00",
	))
	if err != nil {
		t.Fatalf("error should be nil but got: %v", err)
	}
	expected := &swapUsage{
		Total:     0x0000000140000000,
		Avail:     0x00000000563c0000,
		Used:      0x00000000e9c40000,
		Pagesize:  4096,
		Encrypted: true,
	}
	if !reflect.DeepEqual(got, expected) {
		t.Errorf("invalid memory value: %+v (expected: %+v)", got, expected)
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2014 Google Inc. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package client

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"strconv"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/probe"
	httprobe "github.com/GoogleCloudPlatform/kubernetes/pkg/probe/http"
)

// ErrPodInfoNotAvailable may be returned when the requested pod info is not available.
var ErrPodInfoNotAvailable = errors.New("no pod info available")

// KubeletClient is an interface for all kubelet functionality
type KubeletClient interface {
	KubeletHealthChecker
	PodInfoGetter
}

// KubeletHealthchecker is an interface for healthchecking kubelets
type KubeletHealthChecker interface {
	HealthCheck(host string) (probe.Result, error)
}

// PodInfoGetter is an interface for things that can get information about a pod's containers.
// Injectable for easy testing.
type PodInfoGetter interface {
	// GetPodStatus returns information about all containers which are part
	// Returns an api.PodStatus, or an error if one occurs.
	GetPodStatus(host, podNamespace, podID string) (api.PodStatusResult, error)
}

// HTTPKubeletClient is the default implementation of PodInfoGetter and KubeletHealthchecker, accesses the kubelet over HTTP.
type HTTPKubeletClient struct {
	Client      *http.Client
	Port        uint
	EnableHttps bool
}

// TODO: this structure is questionable, it should be using client.Config and overriding defaults.
func NewKubeletClient(config *KubeletConfig) (KubeletClient, error) {
	transport := http.DefaultTransport

	tlsConfig, err := TLSConfigFor(&Config{
		TLSClientConfig: config.TLSClientConfig,
	})
	if err != nil {
		return nil, err
	}
	if tlsConfig != nil {
		transport = &http.Transport{
			TLSClientConfig: tlsConfig,
		}
	}

	c := &http.Client{
		Transport: transport,
	}
	return &HTTPKubeletClient{
		Client:      c,
		Port:        config.Port,
		EnableHttps: config.EnableHttps,
	}, nil
}

func (c *HTTPKubeletClient) url(host string) string {
	scheme := "http://"
	if c.EnableHttps {
		scheme = "https://"
	}

	return fmt.Sprintf(
		"%s%s",
		scheme,
		net.JoinHostPort(host, strconv.FormatUint(uint64(c.Port), 10)))
}

// GetPodInfo gets information about the specified pod.
func (c *HTTPKubeletClient) GetPodStatus(host, podNamespace, podID string) (api.PodStatusResult, error) {
	request, err := http.NewRequest(
		"GET",
		fmt.Sprintf(
			"%s/api/v1beta1/podInfo?podID=%s&podNamespace=%s",
			c.url(host),
			podID,
			podNamespace),
		nil)
	status := api.PodStatusResult{}
	if err != nil {
		return status, err
	}
	response, err := c.Client.Do(request)
	if err != nil {
		return status, err
	}
	defer response.Body.Close()
	if response.StatusCode == http.StatusNotFound {
		return status, ErrPodInfoNotAvailable
	}
	if response.StatusCode >= 300 || response.StatusCode < 200 {
		return status, fmt.Errorf("kubelet %q server responded with HTTP error code %d for pod %s/%s", host, response.StatusCode, podNamespace, podID)
	}
	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return status, err
	}
	// Check that this data can be unmarshalled
	err = latest.Codec.DecodeInto(body, &status)
	if err != nil {
		return status, err
	}
	return status, nil
}

func (c *HTTPKubeletClient) HealthCheck(host string) (probe.Result, error) {
	return httprobe.DoHTTPProbe(fmt.Sprintf("%s/healthz", c.url(host)), c.Client)
}

// FakeKubeletClient is a fake implementation of KubeletClient which returns an error
// when called.  It is useful to pass to the master in a test configuration with
// no kubelets.
type FakeKubeletClient struct{}

// GetPodInfo is a fake implementation of PodInfoGetter.GetPodInfo.
func (c FakeKubeletClient) GetPodStatus(host, podNamespace string, podID string) (api.PodStatusResult, error) {
	return api.PodStatusResult{}, errors.New("Not Implemented")
}

func (c FakeKubeletClient) HealthCheck(host string) (probe.Result, error) {
	return probe.Unknown, errors.New("Not Implemented")
}
<commit_msg>kubelet: improve client url composition<commit_after>/*
Copyright 2014 Google Inc. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package client

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"strconv"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/probe"
	httprobe "github.com/GoogleCloudPlatform/kubernetes/pkg/probe/http"
)

// ErrPodInfoNotAvailable may be returned when the requested pod info is not available.
var ErrPodInfoNotAvailable = errors.New("no pod info available")

// KubeletClient is an interface for all kubelet functionality
type KubeletClient interface {
	KubeletHealthChecker
	PodInfoGetter
}

// KubeletHealthchecker is an interface for healthchecking kubelets
type KubeletHealthChecker interface {
	HealthCheck(host string) (probe.Result, error)
}

// PodInfoGetter is an interface for things that can get information about a pod's containers.
// Injectable for easy testing.
type PodInfoGetter interface {
	// GetPodStatus returns information about all containers which are part
	// Returns an api.PodStatus, or an error if one occurs.
	GetPodStatus(host, podNamespace, podID string) (api.PodStatusResult, error)
}

// HTTPKubeletClient is the default implementation of PodInfoGetter and KubeletHealthchecker, accesses the kubelet over HTTP.
type HTTPKubeletClient struct {
	Client      *http.Client
	Port        uint
	EnableHttps bool
}

// TODO: this structure is questionable, it should be using client.Config and overriding defaults.
func NewKubeletClient(config *KubeletConfig) (KubeletClient, error) {
	transport := http.DefaultTransport

	tlsConfig, err := TLSConfigFor(&Config{
		TLSClientConfig: config.TLSClientConfig,
	})
	if err != nil {
		return nil, err
	}
	if tlsConfig != nil {
		transport = &http.Transport{
			TLSClientConfig: tlsConfig,
		}
	}

	c := &http.Client{
		Transport: transport,
	}
	return &HTTPKubeletClient{
		Client:      c,
		Port:        config.Port,
		EnableHttps: config.EnableHttps,
	}, nil
}

func (c *HTTPKubeletClient) url(host, path, query string) string {
	scheme := "http"
	if c.EnableHttps {
		scheme = "https"
	}

	return (&url.URL{
		Scheme:   scheme,
		Host:     net.JoinHostPort(host, strconv.FormatUint(uint64(c.Port), 10)),
		Path:     path,
		RawQuery: query,
	}).String()
}

// GetPodInfo gets information about the specified pod.
func (c *HTTPKubeletClient) GetPodStatus(host, podNamespace, podID string) (api.PodStatusResult, error) {
	query := url.Values{"podID": {podID}, "podNamespace": {podNamespace}}
	request, err := http.NewRequest("GET", c.url(host, "/api/v1beta1/podInfo", query.Encode()), nil)
	status := api.PodStatusResult{}
	if err != nil {
		return status, err
	}
	response, err := c.Client.Do(request)
	if err != nil {
		return status, err
	}
	defer response.Body.Close()
	if response.StatusCode == http.StatusNotFound {
		return status, ErrPodInfoNotAvailable
	}
	if response.StatusCode >= 300 || response.StatusCode < 200 {
		return status, fmt.Errorf("kubelet %q server responded with HTTP error code %d for pod %s/%s", host, response.StatusCode, podNamespace, podID)
	}
	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return status, err
	}
	// Check that this data can be unmarshalled
	err = latest.Codec.DecodeInto(body, &status)
	if err != nil {
		return status, err
	}
	return status, nil
}

func (c *HTTPKubeletClient) HealthCheck(host string) (probe.Result, error) {
	return httprobe.DoHTTPProbe(c.url(host, "/healthz", ""), c.Client)
}

// FakeKubeletClient is a fake implementation of KubeletClient which returns an error
// when called.  It is useful to pass to the master in a test configuration with
// no kubelets.
type FakeKubeletClient struct{}

// GetPodInfo is a fake implementation of PodInfoGetter.GetPodInfo.
func (c FakeKubeletClient) GetPodStatus(host, podNamespace string, podID string) (api.PodStatusResult, error) {
	return api.PodStatusResult{}, errors.New("Not Implemented")
}

func (c FakeKubeletClient) HealthCheck(host string) (probe.Result, error) {
	return probe.Unknown, errors.New("Not Implemented")
}
<|endoftext|>