<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"os"
	"path"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
)

var TIMEOUT = 30
var apiUrl = "https://api.opsgenie.com"

func main() {
	app := cli.NewApp()
	app.Name = path.Base(os.Args[0])
	app.Version = "1.0"
	app.Usage = "Send hartbeats to OpsGenie"
	app.Flags = SharedFlags
	app.Commands = Commands
	app.Run(os.Args)
}

var logAndExit = func(msg string) {
	log.Fatal(msg)
}

func startHeartbeat(args OpsArgs) {
	heartbeat := getHeartbeat(args)
	if heartbeat == nil {
		addHeartbeat(args)
	} else {
		updateHeartbeatWithEnabledTrue(args, *heartbeat)
	}
	sendHeartbeat(args)
}

func getHeartbeat(args OpsArgs) *Heartbeat {
	var requestParams = make(map[string]string)
	requestParams["apiKey"] = args.apiKey
	requestParams["name"] = args.name
	responseBody := doHttpRequest("GET", "/v1/json/heartbeat/", requestParams, nil)
	heartbeat := &Heartbeat{}
	err := json.Unmarshal(responseBody, &heartbeat)
	handleError(err)
	log.Info("Successfully retrieved heartbeat [" + args.name + "]")
	return heartbeat
}

func addHeartbeat(args OpsArgs) {
	var contentParams = make(map[string]interface{})
	contentParams["apiKey"] = args.apiKey
	contentParams["name"] = args.name
	if args.description != "" {
		contentParams["description"] = args.description
	}
	if args.interval != 0 {
		contentParams["interval"] = args.interval
	}
	if args.intervalUnit != "" {
		contentParams["intervalUnit"] = args.intervalUnit
	}
	doHttpRequest("POST", "/v1/json/heartbeat/", nil, contentParams)
	log.Info("Successfully added heartbeat [" + args.name + "]")
}

func updateHeartbeatWithEnabledTrue(args OpsArgs, heartbeat Heartbeat) {
	var contentParams = make(map[string]interface{})
	contentParams["apiKey"] = args.apiKey
	contentParams["id"] = heartbeat.Id
	contentParams["enabled"] = true
	if args.description != "" {
		contentParams["description"] = args.description
	}
	if args.interval != 0 {
		contentParams["interval"] = args.interval
	}
	if args.intervalUnit != "" {
		contentParams["intervalUnit"] = args.intervalUnit
	}
	doHttpRequest("POST", "/v1/json/heartbeat", nil, contentParams)
	log.Info("Successfully enabled and updated heartbeat [" + args.name + "]")
}

func sendHeartbeat(args OpsArgs) {
	var contentParams = make(map[string]interface{})
	contentParams["apiKey"] = args.apiKey
	contentParams["name"] = args.name
	doHttpRequest("POST", "/v1/json/heartbeat/send", nil, contentParams)
	log.Info("Successfully sent heartbeat [" + args.name + "]")
}

func stopHeartbeat(args OpsArgs) {
	if args.delete == true {
		deleteHeartbeat(args)
	} else {
		disableHeartbeat(args)
	}
}

func deleteHeartbeat(args OpsArgs) {
	var requestParams = make(map[string]string)
	requestParams["apiKey"] = args.apiKey
	requestParams["name"] = args.name
	doHttpRequest("DELETE", "/v1/json/heartbeat", requestParams, nil)
	log.Info("Successfully deleted heartbeat [" + args.name + "]")
}

func disableHeartbeat(args OpsArgs) {
	var contentParams = make(map[string]interface{})
	contentParams["apiKey"] = args.apiKey
	contentParams["name"] = args.name
	doHttpRequest("POST", "/v1/json/heartbeat/disable", nil, contentParams)
	log.Info("Successfully disabled heartbeat [" + args.name + "]")
}

func createErrorResponse(responseBody []byte) ErrorResponse {
	errResponse := &ErrorResponse{}
	err := json.Unmarshal(responseBody, &errResponse)
	handleError(err)
	return *errResponse
}

func doHttpRequest(method string, urlSuffix string, requestParameters map[string]string, contentParameters map[string]interface{}) []byte {
	resp, err := getHttpClient().Do(createRequest(method, urlSuffix, requestParameters, contentParameters))
	handleError(err)
	body, err := ioutil.ReadAll(resp.Body)
	handleError(err)
	if resp.StatusCode != 200 {
		logAndExit(fmt.Sprintf("%#v", createErrorResponse(body)))
	}
	defer resp.Body.Close()
	return body
}

func createRequest(method string, urlSuffix string, requestParameters map[string]string, contentParameters map[string]interface{}) *http.Request {
	var body, err = json.Marshal(contentParameters)
	handleError(err)
	request, err := http.NewRequest(method, createUrl(urlSuffix, requestParameters).String(), bytes.NewReader(body))
	handleError(err)
	return request
}

func createUrl(urlSuffix string, requestParameters map[string]string) *url.URL {
	var Url *url.URL
	Url, err := url.Parse(apiUrl + urlSuffix)
	handleError(err)
	parameters := url.Values{}
	for k, v := range requestParameters {
		parameters.Add(k, v)
	}
	Url.RawQuery = parameters.Encode()
	return Url
}

func handleError(err error) {
	if err != nil {
		logAndExit(err.Error())
	}
}

func getHttpClient() *http.Client {
	client := &http.Client{
		Transport: &http.Transport{
			Proxy: http.ProxyFromEnvironment,
			Dial: func(netw, addr string) (net.Conn, error) {
				conn, err := net.DialTimeout(netw, addr, time.Second*time.Duration(TIMEOUT))
				if err != nil {
					return nil, err
				}
				conn.SetDeadline(time.Now().Add(time.Second * time.Duration(TIMEOUT)))
				return conn, nil
			},
		},
	}
	return client
}

type Heartbeat struct {
	Id string `json:"id"`
}

type ErrorResponse struct {
	Code    int    `json:"code"`
	Message string `json:"error"`
}
<commit_msg>consolidate param creation<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"os"
	"path"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
)

var TIMEOUT = 30
var apiUrl = "https://api.opsgenie.com"

func main() {
	app := cli.NewApp()
	app.Name = path.Base(os.Args[0])
	app.Version = "1.0"
	app.Usage = "Send hartbeats to OpsGenie"
	app.Flags = SharedFlags
	app.Commands = Commands
	app.Run(os.Args)
}

var logAndExit = func(msg string) {
	log.Fatal(msg)
}

func startHeartbeat(args OpsArgs) {
	heartbeat := getHeartbeat(args)
	if heartbeat == nil {
		addHeartbeat(args)
	} else {
		updateHeartbeatWithEnabledTrue(args, *heartbeat)
	}
	sendHeartbeat(args)
}

func getHeartbeat(args OpsArgs) *Heartbeat {
	responseBody := doHttpRequest("GET", "/v1/json/heartbeat/", mandatoryRequestParams(args), nil)
	heartbeat := &Heartbeat{}
	err := json.Unmarshal(responseBody, &heartbeat)
	handleError(err)
	log.Info("Successfully retrieved heartbeat [" + args.name + "]")
	return heartbeat
}

func addHeartbeat(args OpsArgs) {
	doHttpRequest("POST", "/v1/json/heartbeat/", nil, allContentParams(args))
	log.Info("Successfully added heartbeat [" + args.name + "]")
}

func updateHeartbeatWithEnabledTrue(args OpsArgs, heartbeat Heartbeat) {
	var contentParams = allContentParams(args)
	contentParams["id"] = heartbeat.Id
	contentParams["enabled"] = true
	doHttpRequest("POST", "/v1/json/heartbeat", nil, contentParams)
	log.Info("Successfully enabled and updated heartbeat [" + args.name + "]")
}

func sendHeartbeat(args OpsArgs) {
	doHttpRequest("POST", "/v1/json/heartbeat/send", nil, mandatoryContentParams(args))
	log.Info("Successfully sent heartbeat [" + args.name + "]")
}

func stopHeartbeat(args OpsArgs) {
	if args.delete {
		deleteHeartbeat(args)
	} else {
		disableHeartbeat(args)
	}
}

func deleteHeartbeat(args OpsArgs) {
	doHttpRequest("DELETE", "/v1/json/heartbeat", mandatoryRequestParams(args), nil)
	log.Info("Successfully deleted heartbeat [" + args.name + "]")
}

func disableHeartbeat(args OpsArgs) {
	doHttpRequest("POST", "/v1/json/heartbeat/disable", nil, mandatoryContentParams(args))
	log.Info("Successfully disabled heartbeat [" + args.name + "]")
}

func mandatoryContentParams(args OpsArgs) map[string]interface{} {
	var contentParams = make(map[string]interface{})
	contentParams["apiKey"] = args.apiKey
	contentParams["name"] = args.name
	return contentParams
}

func allContentParams(args OpsArgs) map[string]interface{} {
	var contentParams = mandatoryContentParams(args)
	if args.description != "" {
		contentParams["description"] = args.description
	}
	if args.interval != 0 {
		contentParams["interval"] = args.interval
	}
	if args.intervalUnit != "" {
		contentParams["intervalUnit"] = args.intervalUnit
	}
	return contentParams
}

func mandatoryRequestParams(args OpsArgs) map[string]string {
	var requestParams = make(map[string]string)
	requestParams["apiKey"] = args.apiKey
	requestParams["name"] = args.name
	return requestParams
}

func createErrorResponse(responseBody []byte) ErrorResponse {
	errResponse := &ErrorResponse{}
	err := json.Unmarshal(responseBody, &errResponse)
	handleError(err)
	return *errResponse
}

func doHttpRequest(method string, urlSuffix string, requestParameters map[string]string, contentParameters map[string]interface{}) []byte {
	resp, err := getHttpClient().Do(createRequest(method, urlSuffix, requestParameters, contentParameters))
	handleError(err)
	body, err := ioutil.ReadAll(resp.Body)
	handleError(err)
	if resp.StatusCode != 200 {
		logAndExit(fmt.Sprintf("%#v", createErrorResponse(body)))
	}
	defer resp.Body.Close()
	return body
}

func createRequest(method string, urlSuffix string, requestParameters map[string]string, contentParameters map[string]interface{}) *http.Request {
	var body, err = json.Marshal(contentParameters)
	handleError(err)
	request, err := http.NewRequest(method, createUrl(urlSuffix, requestParameters).String(), bytes.NewReader(body))
	handleError(err)
	return request
}

func createUrl(urlSuffix string, requestParameters map[string]string) *url.URL {
	var Url *url.URL
	Url, err := url.Parse(apiUrl + urlSuffix)
	handleError(err)
	parameters := url.Values{}
	for k, v := range requestParameters {
		parameters.Add(k, v)
	}
	Url.RawQuery = parameters.Encode()
	return Url
}

func handleError(err error) {
	if err != nil {
		logAndExit(err.Error())
	}
}

func getHttpClient() *http.Client {
	client := &http.Client{
		Transport: &http.Transport{
			Proxy: http.ProxyFromEnvironment,
			Dial: func(netw, addr string) (net.Conn, error) {
				conn, err := net.DialTimeout(netw, addr, time.Second*time.Duration(TIMEOUT))
				if err != nil {
					return nil, err
				}
				conn.SetDeadline(time.Now().Add(time.Second * time.Duration(TIMEOUT)))
				return conn, nil
			},
		},
	}
	return client
}

type Heartbeat struct {
	Id string `json:"id"`
}

type ErrorResponse struct {
	Code    int    `json:"code"`
	Message string `json:"error"`
}
<|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes 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 abac

import (
	"io/ioutil"
	"os"
	"testing"

	"k8s.io/kubernetes/pkg/auth/authorizer"
	"k8s.io/kubernetes/pkg/auth/user"
)

func TestEmptyFile(t *testing.T) {
	_, err := newWithContents(t, "")
	if err != nil {
		t.Errorf("unable to read policy file: %v", err)
	}
}

func TestOneLineFileNoNewLine(t *testing.T) {
	_, err := newWithContents(t, `{"user":"scheduler",  "readonly": true, "kind": "pods", "namespace":"ns1"}`)
	if err != nil {
		t.Errorf("unable to read policy file: %v", err)
	}
}

func TestTwoLineFile(t *testing.T) {
	_, err := newWithContents(t, `{"user":"scheduler",  "readonly": true, "kind": "pods"}
{"user":"scheduler",  "readonly": true, "kind": "services"}
`)
	if err != nil {
		t.Errorf("unable to read policy file: %v", err)
	}
}

// Test the file that we will point users at as an example.
func TestExampleFile(t *testing.T) {
	_, err := NewFromFile("./example_policy_file.jsonl")
	if err != nil {
		t.Errorf("unable to read policy file: %v", err)
	}
}

func NotTestAuthorize(t *testing.T) {
	a, err := newWithContents(t, `{                     "readonly": true, "kind": "events"}
{"user":"scheduler",  "readonly": true, "kind": "pods"}
{"user":"scheduler",              "kind": "bindings"}
{"user":"kubelet",    "readonly": true, "kind": "bindings"}
{"user":"kubelet",                "kind": "events"}
{"user":"alice",                                     "ns": "projectCaribou"}
{"user":"bob",        "readonly": true,                    "ns": "projectCaribou"}
`)
	if err != nil {
		t.Fatalf("unable to read policy file: %v", err)
	}

	uScheduler := user.DefaultInfo{Name: "scheduler", UID: "uid1"}
	uAlice := user.DefaultInfo{Name: "alice", UID: "uid3"}
	uChuck := user.DefaultInfo{Name: "chuck", UID: "uid5"}

	testCases := []struct {
		User        user.DefaultInfo
		RO          bool
		Resource    string
		NS          string
		ExpectAllow bool
	}{
		// Scheduler can read pods
		{User: uScheduler, RO: true, Resource: "pods", NS: "ns1", ExpectAllow: true},
		{User: uScheduler, RO: true, Resource: "pods", NS: "", ExpectAllow: true},
		// Scheduler cannot write pods
		{User: uScheduler, RO: false, Resource: "pods", NS: "ns1", ExpectAllow: false},
		{User: uScheduler, RO: false, Resource: "pods", NS: "", ExpectAllow: false},
		// Scheduler can write bindings
		{User: uScheduler, RO: true, Resource: "bindings", NS: "ns1", ExpectAllow: true},
		{User: uScheduler, RO: true, Resource: "bindings", NS: "", ExpectAllow: true},

		// Alice can read and write anything in the right namespace.
		{User: uAlice, RO: true, Resource: "pods", NS: "projectCaribou", ExpectAllow: true},
		{User: uAlice, RO: true, Resource: "widgets", NS: "projectCaribou", ExpectAllow: true},
		{User: uAlice, RO: true, Resource: "", NS: "projectCaribou", ExpectAllow: true},
		{User: uAlice, RO: false, Resource: "pods", NS: "projectCaribou", ExpectAllow: true},
		{User: uAlice, RO: false, Resource: "widgets", NS: "projectCaribou", ExpectAllow: true},
		{User: uAlice, RO: false, Resource: "", NS: "projectCaribou", ExpectAllow: true},
		// .. but not the wrong namespace.
		{User: uAlice, RO: true, Resource: "pods", NS: "ns1", ExpectAllow: false},
		{User: uAlice, RO: true, Resource: "widgets", NS: "ns1", ExpectAllow: false},
		{User: uAlice, RO: true, Resource: "", NS: "ns1", ExpectAllow: false},

		// Chuck can read events, since anyone can.
		{User: uChuck, RO: true, Resource: "events", NS: "ns1", ExpectAllow: true},
		{User: uChuck, RO: true, Resource: "events", NS: "", ExpectAllow: true},
		// Chuck can't do other things.
		{User: uChuck, RO: false, Resource: "events", NS: "ns1", ExpectAllow: false},
		{User: uChuck, RO: true, Resource: "pods", NS: "ns1", ExpectAllow: false},
		{User: uChuck, RO: true, Resource: "floop", NS: "ns1", ExpectAllow: false},
		// Chunk can't access things with no kind or namespace
		// TODO: find a way to give someone access to miscellaneous endpoints, such as
		// /healthz, /version, etc.
		{User: uChuck, RO: true, Resource: "", NS: "", ExpectAllow: false},
	}
	for _, tc := range testCases {
		attr := authorizer.AttributesRecord{
			User:      &tc.User,
			ReadOnly:  tc.RO,
			Resource:  tc.Resource,
			Namespace: tc.NS,
		}
		t.Logf("tc: %v -> attr %v", tc, attr)
		err := a.Authorize(attr)
		actualAllow := bool(err == nil)
		if tc.ExpectAllow != actualAllow {
			t.Errorf("Expected allowed=%v but actually allowed=%v, for case %v",
				tc.ExpectAllow, actualAllow, tc)
		}
	}
}

func TestSubjectMatches(t *testing.T) {
	testCases := map[string]struct {
		User        user.DefaultInfo
		PolicyUser  string
		PolicyGroup string
		ExpectMatch bool
	}{
		"empty policy matches unauthed user": {
			User:        user.DefaultInfo{},
			PolicyUser:  "",
			PolicyGroup: "",
			ExpectMatch: true,
		},
		"empty policy matches authed user": {
			User:        user.DefaultInfo{Name: "Foo"},
			PolicyUser:  "",
			PolicyGroup: "",
			ExpectMatch: true,
		},
		"empty policy matches authed user with groups": {
			User:        user.DefaultInfo{Name: "Foo", Groups: []string{"a", "b"}},
			PolicyUser:  "",
			PolicyGroup: "",
			ExpectMatch: true,
		},

		"user policy does not match unauthed user": {
			User:        user.DefaultInfo{},
			PolicyUser:  "Foo",
			PolicyGroup: "",
			ExpectMatch: false,
		},
		"user policy does not match different user": {
			User:        user.DefaultInfo{Name: "Bar"},
			PolicyUser:  "Foo",
			PolicyGroup: "",
			ExpectMatch: false,
		},
		"user policy is case-sensitive": {
			User:        user.DefaultInfo{Name: "foo"},
			PolicyUser:  "Foo",
			PolicyGroup: "",
			ExpectMatch: false,
		},
		"user policy does not match substring": {
			User:        user.DefaultInfo{Name: "FooBar"},
			PolicyUser:  "Foo",
			PolicyGroup: "",
			ExpectMatch: false,
		},
		"user policy matches username": {
			User:        user.DefaultInfo{Name: "Foo"},
			PolicyUser:  "Foo",
			PolicyGroup: "",
			ExpectMatch: true,
		},

		"group policy does not match unauthed user": {
			User:        user.DefaultInfo{},
			PolicyUser:  "",
			PolicyGroup: "Foo",
			ExpectMatch: false,
		},
		"group policy does not match user in different group": {
			User:        user.DefaultInfo{Name: "FooBar", Groups: []string{"B"}},
			PolicyUser:  "",
			PolicyGroup: "A",
			ExpectMatch: false,
		},
		"group policy is case-sensitive": {
			User:        user.DefaultInfo{Name: "Foo", Groups: []string{"A", "B", "C"}},
			PolicyUser:  "",
			PolicyGroup: "b",
			ExpectMatch: false,
		},
		"group policy does not match substring": {
			User:        user.DefaultInfo{Name: "Foo", Groups: []string{"A", "BBB", "C"}},
			PolicyUser:  "",
			PolicyGroup: "B",
			ExpectMatch: false,
		},
		"group policy matches user in group": {
			User:        user.DefaultInfo{Name: "Foo", Groups: []string{"A", "B", "C"}},
			PolicyUser:  "",
			PolicyGroup: "B",
			ExpectMatch: true,
		},

		"user and group policy requires user match": {
			User:        user.DefaultInfo{Name: "Bar", Groups: []string{"A", "B", "C"}},
			PolicyUser:  "Foo",
			PolicyGroup: "B",
			ExpectMatch: false,
		},
		"user and group policy requires group match": {
			User:        user.DefaultInfo{Name: "Foo", Groups: []string{"A", "B", "C"}},
			PolicyUser:  "Foo",
			PolicyGroup: "D",
			ExpectMatch: false,
		},
		"user and group policy matches": {
			User:        user.DefaultInfo{Name: "Foo", Groups: []string{"A", "B", "C"}},
			PolicyUser:  "Foo",
			PolicyGroup: "B",
			ExpectMatch: true,
		},
	}

	for k, tc := range testCases {
		attr := authorizer.AttributesRecord{
			User: &tc.User,
		}
		actualMatch := policy{User: tc.PolicyUser, Group: tc.PolicyGroup}.subjectMatches(attr)
		if tc.ExpectMatch != actualMatch {
			t.Errorf("%v: Expected actorMatches=%v but actually got=%v",
				k, tc.ExpectMatch, actualMatch)
		}
	}
}

func newWithContents(t *testing.T, contents string) (authorizer.Authorizer, error) {
	f, err := ioutil.TempFile("", "abac_test")
	if err != nil {
		t.Fatalf("unexpected error creating policyfile: %v", err)
	}
	f.Close()
	defer os.Remove(f.Name())

	if err := ioutil.WriteFile(f.Name(), []byte(contents), 0700); err != nil {
		t.Fatalf("unexpected error writing policyfile: %v", err)
	}

	pl, err := NewFromFile(f.Name())
	return pl, err
}

func TestPolicy(t *testing.T) {
	tests := []struct {
		policy  policy
		attr    authorizer.Attributes
		matches bool
		name    string
	}{
		{
			policy:  policy{},
			attr:    authorizer.AttributesRecord{},
			matches: true,
			name:    "null",
		},
		{
			policy: policy{
				Readonly: true,
			},
			attr:    authorizer.AttributesRecord{},
			matches: false,
			name:    "read-only mismatch",
		},
		{
			policy: policy{
				User: "foo",
			},
			attr: authorizer.AttributesRecord{
				User: &user.DefaultInfo{
					Name: "bar",
				},
			},
			matches: false,
			name:    "user name mis-match",
		},
		{
			policy: policy{
				Resource: "foo",
			},
			attr: authorizer.AttributesRecord{
				Resource: "bar",
			},
			matches: false,
			name:    "resource mis-match",
		},
		{
			policy: policy{
				User:      "foo",
				Resource:  "foo",
				Namespace: "foo",
			},
			attr: authorizer.AttributesRecord{
				User: &user.DefaultInfo{
					Name: "foo",
				},
				Resource:  "foo",
				Namespace: "foo",
			},
			matches: true,
			name:    "namespace mis-match",
		},
		{
			policy: policy{
				Namespace: "foo",
			},
			attr: authorizer.AttributesRecord{
				Namespace: "bar",
			},
			matches: false,
			name:    "resource mis-match",
		},
	}
	for _, test := range tests {
		matches := test.policy.matches(test.attr)
		if test.matches != matches {
			t.Errorf("unexpected value for %s, expected: %t, saw: %t", test.name, test.matches, matches)
		}
	}
}
<commit_msg>fix ABAC tests<commit_after>/*
Copyright 2014 The Kubernetes 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 abac

import (
	"io/ioutil"
	"os"
	"testing"

	"k8s.io/kubernetes/pkg/auth/authorizer"
	"k8s.io/kubernetes/pkg/auth/user"
)

func TestEmptyFile(t *testing.T) {
	_, err := newWithContents(t, "")
	if err != nil {
		t.Errorf("unable to read policy file: %v", err)
	}
}

func TestOneLineFileNoNewLine(t *testing.T) {
	_, err := newWithContents(t, `{"user":"scheduler",  "readonly": true, "resource": "pods", "namespace":"ns1"}`)
	if err != nil {
		t.Errorf("unable to read policy file: %v", err)
	}
}

func TestTwoLineFile(t *testing.T) {
	_, err := newWithContents(t, `{"user":"scheduler",  "readonly": true, "resource": "pods"}
{"user":"scheduler",  "readonly": true, "resource": "services"}
`)
	if err != nil {
		t.Errorf("unable to read policy file: %v", err)
	}
}

// Test the file that we will point users at as an example.
func TestExampleFile(t *testing.T) {
	_, err := NewFromFile("./example_policy_file.jsonl")
	if err != nil {
		t.Errorf("unable to read policy file: %v", err)
	}
}

func TestNotAuthorized(t *testing.T) {
	a, err := newWithContents(t, `{                    "readonly": true, "resource": "events"   }
{"user":"scheduler", "readonly": true, "resource": "pods"     }
{"user":"scheduler",                   "resource": "bindings" }
{"user":"kubelet",   "readonly": true, "resource": "bindings" }
{"user":"kubelet",                     "resource": "events"   }
{"user":"alice",                                              "namespace": "projectCaribou"}
{"user":"bob",       "readonly": true,                        "namespace": "projectCaribou"}
`)
	if err != nil {
		t.Fatalf("unable to read policy file: %v", err)
	}

	uScheduler := user.DefaultInfo{Name: "scheduler", UID: "uid1"}
	uAlice := user.DefaultInfo{Name: "alice", UID: "uid3"}
	uChuck := user.DefaultInfo{Name: "chuck", UID: "uid5"}

	testCases := []struct {
		User        user.DefaultInfo
		RO          bool
		Resource    string
		NS          string
		ExpectAllow bool
	}{
		// Scheduler can read pods
		{User: uScheduler, RO: true, Resource: "pods", NS: "ns1", ExpectAllow: true},
		{User: uScheduler, RO: true, Resource: "pods", NS: "", ExpectAllow: true},
		// Scheduler cannot write pods
		{User: uScheduler, RO: false, Resource: "pods", NS: "ns1", ExpectAllow: false},
		{User: uScheduler, RO: false, Resource: "pods", NS: "", ExpectAllow: false},
		// Scheduler can write bindings
		{User: uScheduler, RO: true, Resource: "bindings", NS: "ns1", ExpectAllow: true},
		{User: uScheduler, RO: true, Resource: "bindings", NS: "", ExpectAllow: true},

		// Alice can read and write anything in the right namespace.
		{User: uAlice, RO: true, Resource: "pods", NS: "projectCaribou", ExpectAllow: true},
		{User: uAlice, RO: true, Resource: "widgets", NS: "projectCaribou", ExpectAllow: true},
		{User: uAlice, RO: true, Resource: "", NS: "projectCaribou", ExpectAllow: true},
		{User: uAlice, RO: false, Resource: "pods", NS: "projectCaribou", ExpectAllow: true},
		{User: uAlice, RO: false, Resource: "widgets", NS: "projectCaribou", ExpectAllow: true},
		{User: uAlice, RO: false, Resource: "", NS: "projectCaribou", ExpectAllow: true},
		// .. but not the wrong namespace.
		{User: uAlice, RO: true, Resource: "pods", NS: "ns1", ExpectAllow: false},
		{User: uAlice, RO: true, Resource: "widgets", NS: "ns1", ExpectAllow: false},
		{User: uAlice, RO: true, Resource: "", NS: "ns1", ExpectAllow: false},

		// Chuck can read events, since anyone can.
		{User: uChuck, RO: true, Resource: "events", NS: "ns1", ExpectAllow: true},
		{User: uChuck, RO: true, Resource: "events", NS: "", ExpectAllow: true},
		// Chuck can't do other things.
		{User: uChuck, RO: false, Resource: "events", NS: "ns1", ExpectAllow: false},
		{User: uChuck, RO: true, Resource: "pods", NS: "ns1", ExpectAllow: false},
		{User: uChuck, RO: true, Resource: "floop", NS: "ns1", ExpectAllow: false},
		// Chunk can't access things with no kind or namespace
		// TODO: find a way to give someone access to miscellaneous endpoints, such as
		// /healthz, /version, etc.
		{User: uChuck, RO: true, Resource: "", NS: "", ExpectAllow: false},
	}
	for i, tc := range testCases {
		attr := authorizer.AttributesRecord{
			User:      &tc.User,
			ReadOnly:  tc.RO,
			Resource:  tc.Resource,
			Namespace: tc.NS,
		}
		t.Logf("tc: %v -> attr %v", tc, attr)
		err := a.Authorize(attr)
		actualAllow := bool(err == nil)
		if tc.ExpectAllow != actualAllow {
			t.Errorf("%d: Expected allowed=%v but actually allowed=%v\n\t%v",
				i, tc.ExpectAllow, actualAllow, tc)
		}
	}
}

func TestSubjectMatches(t *testing.T) {
	testCases := map[string]struct {
		User        user.DefaultInfo
		PolicyUser  string
		PolicyGroup string
		ExpectMatch bool
	}{
		"empty policy matches unauthed user": {
			User:        user.DefaultInfo{},
			PolicyUser:  "",
			PolicyGroup: "",
			ExpectMatch: true,
		},
		"empty policy matches authed user": {
			User:        user.DefaultInfo{Name: "Foo"},
			PolicyUser:  "",
			PolicyGroup: "",
			ExpectMatch: true,
		},
		"empty policy matches authed user with groups": {
			User:        user.DefaultInfo{Name: "Foo", Groups: []string{"a", "b"}},
			PolicyUser:  "",
			PolicyGroup: "",
			ExpectMatch: true,
		},

		"user policy does not match unauthed user": {
			User:        user.DefaultInfo{},
			PolicyUser:  "Foo",
			PolicyGroup: "",
			ExpectMatch: false,
		},
		"user policy does not match different user": {
			User:        user.DefaultInfo{Name: "Bar"},
			PolicyUser:  "Foo",
			PolicyGroup: "",
			ExpectMatch: false,
		},
		"user policy is case-sensitive": {
			User:        user.DefaultInfo{Name: "foo"},
			PolicyUser:  "Foo",
			PolicyGroup: "",
			ExpectMatch: false,
		},
		"user policy does not match substring": {
			User:        user.DefaultInfo{Name: "FooBar"},
			PolicyUser:  "Foo",
			PolicyGroup: "",
			ExpectMatch: false,
		},
		"user policy matches username": {
			User:        user.DefaultInfo{Name: "Foo"},
			PolicyUser:  "Foo",
			PolicyGroup: "",
			ExpectMatch: true,
		},

		"group policy does not match unauthed user": {
			User:        user.DefaultInfo{},
			PolicyUser:  "",
			PolicyGroup: "Foo",
			ExpectMatch: false,
		},
		"group policy does not match user in different group": {
			User:        user.DefaultInfo{Name: "FooBar", Groups: []string{"B"}},
			PolicyUser:  "",
			PolicyGroup: "A",
			ExpectMatch: false,
		},
		"group policy is case-sensitive": {
			User:        user.DefaultInfo{Name: "Foo", Groups: []string{"A", "B", "C"}},
			PolicyUser:  "",
			PolicyGroup: "b",
			ExpectMatch: false,
		},
		"group policy does not match substring": {
			User:        user.DefaultInfo{Name: "Foo", Groups: []string{"A", "BBB", "C"}},
			PolicyUser:  "",
			PolicyGroup: "B",
			ExpectMatch: false,
		},
		"group policy matches user in group": {
			User:        user.DefaultInfo{Name: "Foo", Groups: []string{"A", "B", "C"}},
			PolicyUser:  "",
			PolicyGroup: "B",
			ExpectMatch: true,
		},

		"user and group policy requires user match": {
			User:        user.DefaultInfo{Name: "Bar", Groups: []string{"A", "B", "C"}},
			PolicyUser:  "Foo",
			PolicyGroup: "B",
			ExpectMatch: false,
		},
		"user and group policy requires group match": {
			User:        user.DefaultInfo{Name: "Foo", Groups: []string{"A", "B", "C"}},
			PolicyUser:  "Foo",
			PolicyGroup: "D",
			ExpectMatch: false,
		},
		"user and group policy matches": {
			User:        user.DefaultInfo{Name: "Foo", Groups: []string{"A", "B", "C"}},
			PolicyUser:  "Foo",
			PolicyGroup: "B",
			ExpectMatch: true,
		},
	}

	for k, tc := range testCases {
		attr := authorizer.AttributesRecord{
			User: &tc.User,
		}
		actualMatch := policy{User: tc.PolicyUser, Group: tc.PolicyGroup}.subjectMatches(attr)
		if tc.ExpectMatch != actualMatch {
			t.Errorf("%v: Expected actorMatches=%v but actually got=%v",
				k, tc.ExpectMatch, actualMatch)
		}
	}
}

func newWithContents(t *testing.T, contents string) (authorizer.Authorizer, error) {
	f, err := ioutil.TempFile("", "abac_test")
	if err != nil {
		t.Fatalf("unexpected error creating policyfile: %v", err)
	}
	f.Close()
	defer os.Remove(f.Name())

	if err := ioutil.WriteFile(f.Name(), []byte(contents), 0700); err != nil {
		t.Fatalf("unexpected error writing policyfile: %v", err)
	}

	pl, err := NewFromFile(f.Name())
	return pl, err
}

func TestPolicy(t *testing.T) {
	tests := []struct {
		policy  policy
		attr    authorizer.Attributes
		matches bool
		name    string
	}{
		{
			policy:  policy{},
			attr:    authorizer.AttributesRecord{},
			matches: true,
			name:    "null",
		},
		{
			policy: policy{
				Readonly: true,
			},
			attr:    authorizer.AttributesRecord{},
			matches: false,
			name:    "read-only mismatch",
		},
		{
			policy: policy{
				User: "foo",
			},
			attr: authorizer.AttributesRecord{
				User: &user.DefaultInfo{
					Name: "bar",
				},
			},
			matches: false,
			name:    "user name mis-match",
		},
		{
			policy: policy{
				Resource: "foo",
			},
			attr: authorizer.AttributesRecord{
				Resource: "bar",
			},
			matches: false,
			name:    "resource mis-match",
		},
		{
			policy: policy{
				User:      "foo",
				Resource:  "foo",
				Namespace: "foo",
			},
			attr: authorizer.AttributesRecord{
				User: &user.DefaultInfo{
					Name: "foo",
				},
				Resource:  "foo",
				Namespace: "foo",
			},
			matches: true,
			name:    "namespace mis-match",
		},
		{
			policy: policy{
				Namespace: "foo",
			},
			attr: authorizer.AttributesRecord{
				Namespace: "bar",
			},
			matches: false,
			name:    "resource mis-match",
		},
	}
	for _, test := range tests {
		matches := test.policy.matches(test.attr)
		if test.matches != matches {
			t.Errorf("unexpected value for %s, expected: %t, saw: %t", test.name, test.matches, matches)
		}
	}
}
<|endoftext|>


<commit_before>package networking

import (
	"context"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	awssdk "github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	ec2sdk "github.com/aws/aws-sdk-go/service/ec2"
	"github.com/go-logr/logr"
	"github.com/pkg/errors"
	networking "k8s.io/api/networking/v1beta1"
	"regexp"
	"sigs.k8s.io/aws-load-balancer-controller/pkg/aws/services"
	"sigs.k8s.io/aws-load-balancer-controller/pkg/runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"strings"
	"sync"
	"time"
)

const (
	resourceTypeSecurityGroup = "security-group"
	tagKeyK8sCluster          = "elbv2.k8s.aws/cluster"
	tagKeyResource            = "elbv2.k8s.aws/resource"
	tagValueBackend           = "backend-sg"

	explicitGroupFinalizerPrefix = "group.ingress.k8s.aws/"
	implicitGroupFinalizer       = "ingress.k8s.aws/resources"

	sgDescription = "[k8s] Shared Backend SecurityGroup for LoadBalancer"
)

// BackendSGProvider is responsible for providing backend security groups
type BackendSGProvider interface {
	// Get returns the backend security group to use
	Get(ctx context.Context) (string, error)
	// Release cleans up the auto-generated backend SG if necessary
	Release(ctx context.Context) error
}

// NewBackendSGProvider constructs a new  defaultBackendSGProvider
func NewBackendSGProvider(clusterName string, backendSG string, vpcID string,
	ec2Client services.EC2, k8sClient client.Client, logger logr.Logger) *defaultBackendSGProvider {
	return &defaultBackendSGProvider{
		vpcID:       vpcID,
		clusterName: clusterName,
		backendSG:   backendSG,
		ec2Client:   ec2Client,
		k8sClient:   k8sClient,
		logger:      logger,
		mutex:       sync.Mutex{},
	}
}

var _ BackendSGProvider = &defaultBackendSGProvider{}

type defaultBackendSGProvider struct {
	vpcID       string
	clusterName string
	mutex       sync.Mutex

	backendSG       string
	autoGeneratedSG string
	ec2Client       services.EC2
	k8sClient       client.Client
	logger          logr.Logger
}

func (p *defaultBackendSGProvider) Get(ctx context.Context) (string, error) {
	if len(p.backendSG) > 0 {
		return p.backendSG, nil
	}
	// Auto generate Backend Security group, and return the id
	if err := p.allocateBackendSG(ctx); err != nil {
		p.logger.Error(err, "Failed to auto-create backend SG")
		return "", err
	}
	return p.autoGeneratedSG, nil
}

func (p *defaultBackendSGProvider) Release(ctx context.Context) error {
	if len(p.backendSG) > 0 {
		return nil
	}
	if required, err := p.isBackendSGRequired(ctx); required || err != nil {
		return err
	}
	return p.releaseSG(ctx)
}

func (p *defaultBackendSGProvider) allocateBackendSG(ctx context.Context) error {
	p.mutex.Lock()
	defer p.mutex.Unlock()

	if len(p.autoGeneratedSG) > 0 {
		return nil
	}

	sgName := p.getBackendSGName()
	sgID, err := p.getBackendSGFromEC2(ctx, sgName, p.vpcID)
	if err != nil {
		return err
	}
	if len(sgID) > 1 {
		p.logger.V(1).Info("Existing SG found", "id", sgID)
		p.autoGeneratedSG = sgID
		return nil
	}

	createReq := &ec2sdk.CreateSecurityGroupInput{
		VpcId:             awssdk.String(p.vpcID),
		GroupName:         awssdk.String(sgName),
		Description:       awssdk.String(sgDescription),
		TagSpecifications: p.buildBackendSGTags(ctx),
	}
	p.logger.Info("creating securityGroup", "name", sgName)
	resp, err := p.ec2Client.CreateSecurityGroupWithContext(ctx, createReq)
	if err != nil {
		return err
	}
	p.logger.V(1).Info("created SecurityGroup", "name", sgName, "id", resp.GroupId)
	p.autoGeneratedSG = awssdk.StringValue(resp.GroupId)
	return nil
}

func (p *defaultBackendSGProvider) buildBackendSGTags(_ context.Context) []*ec2sdk.TagSpecification {
	return []*ec2sdk.TagSpecification{
		{
			ResourceType: awssdk.String(resourceTypeSecurityGroup),
			Tags: []*ec2sdk.Tag{
				{
					Key:   awssdk.String(tagKeyK8sCluster),
					Value: awssdk.String(p.clusterName),
				},
				{
					Key:   awssdk.String(tagKeyResource),
					Value: awssdk.String(tagValueBackend),
				},
			},
		},
	}
}

func (p *defaultBackendSGProvider) getBackendSGFromEC2(ctx context.Context, sgName string, vpcID string) (string, error) {
	req := &ec2sdk.DescribeSecurityGroupsInput{
		Filters: []*ec2sdk.Filter{
			{
				Name:   awssdk.String("vpc-id"),
				Values: awssdk.StringSlice([]string{vpcID}),
			},
			{
				Name:   awssdk.String(fmt.Sprintf("tag:%v", tagKeyK8sCluster)),
				Values: awssdk.StringSlice([]string{p.clusterName}),
			},
			{
				Name:   awssdk.String(fmt.Sprintf("tag:%v", tagKeyResource)),
				Values: awssdk.StringSlice([]string{tagValueBackend}),
			},
		},
	}
	p.logger.V(1).Info("Queriying existing SG", "vpc-id", vpcID, "name", sgName)
	sgs, err := p.ec2Client.DescribeSecurityGroupsAsList(ctx, req)
	if err != nil && !isEC2SecurityGroupNotFoundError(err) {
		return "", err
	}
	if len(sgs) > 0 {
		return awssdk.StringValue(sgs[0].GroupId), nil
	}
	return "", nil
}

func (p *defaultBackendSGProvider) isBackendSGRequired(ctx context.Context) (bool, error) {
	ingList := &networking.IngressList{}
	if err := p.k8sClient.List(ctx, ingList); err != nil {
		p.logger.Error(err, "Unable to list ingresses")
		return true, errors.Wrapf(err, "unable to list ingresses")
	}
	for _, ing := range ingList.Items {
		if !ing.DeletionTimestamp.IsZero() {
			continue
		}
		for _, fin := range ing.GetFinalizers() {
			if fin == implicitGroupFinalizer || strings.HasPrefix(fin, explicitGroupFinalizerPrefix) {
				return true, nil
			}
		}
	}
	p.logger.Info("No ingress found, backend SG can be deleted", "SG ID", p.autoGeneratedSG)
	return false, nil
}

func (p *defaultBackendSGProvider) releaseSG(ctx context.Context) error {
	p.mutex.Lock()
	defer p.mutex.Unlock()

	if len(p.autoGeneratedSG) == 0 {
		return nil
	}

	if required, err := p.isBackendSGRequired(ctx); required || err != nil {
		p.logger.V(1).Info("backend SG is required, releaseSG ignore delete")
		return err
	}
	req := &ec2sdk.DeleteSecurityGroupInput{
		GroupId: awssdk.String(p.autoGeneratedSG),
	}
	p.logger.V(1).Info("deleting default backend SG", "ID", p.autoGeneratedSG)
	if err := runtime.RetryImmediateOnError(2*time.Minute, 2*time.Second, isSecurityGroupDependencyViolationError, func() error {
		_, err := p.ec2Client.DeleteSecurityGroupWithContext(ctx, req)
		return err
	}); err != nil {
		return errors.Wrap(err, "failed to delete securityGroup")
	}
	p.logger.Info("deleted securityGroup", "ID", p.autoGeneratedSG)

	p.autoGeneratedSG = ""
	return nil
}

var invalidSGNamePattern = regexp.MustCompile("[[:^alnum:]]")

func (p *defaultBackendSGProvider) getBackendSGName() string {
	sgNameHash := sha256.New()
	_, _ = sgNameHash.Write([]byte(p.clusterName))
	sgHash := hex.EncodeToString(sgNameHash.Sum(nil))
	sanitizedClusterName := invalidSGNamePattern.ReplaceAllString(p.clusterName, "")
	return fmt.Sprintf("k8s-traffic-%.232s-%.10s", sanitizedClusterName, sgHash)
}

func isSecurityGroupDependencyViolationError(err error) bool {
	var awsErr awserr.Error
	if errors.As(err, &awsErr) {
		return awsErr.Code() == "DependencyViolation"
	}
	return false
}

func isEC2SecurityGroupNotFoundError(err error) bool {
	var awsErr awserr.Error
	if errors.As(err, &awsErr) {
		return awsErr.Code() == "InvalidGroup.NotFound"
	}
	return false
}
<commit_msg>fix backend sg provider deletion timeout<commit_after>package networking

import (
	"context"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	awssdk "github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	ec2sdk "github.com/aws/aws-sdk-go/service/ec2"
	"github.com/go-logr/logr"
	"github.com/pkg/errors"
	networking "k8s.io/api/networking/v1beta1"
	"regexp"
	"sigs.k8s.io/aws-load-balancer-controller/pkg/aws/services"
	"sigs.k8s.io/aws-load-balancer-controller/pkg/runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"strings"
	"sync"
	"time"
)

const (
	defaultSGDeletionPollInterval = 2 * time.Second
	defaultSGDeletionTimeout      = 2 * time.Minute

	resourceTypeSecurityGroup = "security-group"
	tagKeyK8sCluster          = "elbv2.k8s.aws/cluster"
	tagKeyResource            = "elbv2.k8s.aws/resource"
	tagValueBackend           = "backend-sg"

	explicitGroupFinalizerPrefix = "group.ingress.k8s.aws/"
	implicitGroupFinalizer       = "ingress.k8s.aws/resources"

	sgDescription = "[k8s] Shared Backend SecurityGroup for LoadBalancer"
)

// BackendSGProvider is responsible for providing backend security groups
type BackendSGProvider interface {
	// Get returns the backend security group to use
	Get(ctx context.Context) (string, error)
	// Release cleans up the auto-generated backend SG if necessary
	Release(ctx context.Context) error
}

// NewBackendSGProvider constructs a new  defaultBackendSGProvider
func NewBackendSGProvider(clusterName string, backendSG string, vpcID string,
	ec2Client services.EC2, k8sClient client.Client, logger logr.Logger) *defaultBackendSGProvider {
	return &defaultBackendSGProvider{
		vpcID:       vpcID,
		clusterName: clusterName,
		backendSG:   backendSG,
		ec2Client:   ec2Client,
		k8sClient:   k8sClient,
		logger:      logger,
		mutex:       sync.Mutex{},

		defaultDeletionPollInterval: defaultSGDeletionPollInterval,
		defaultDeletionTimeout:      defaultSGDeletionTimeout,
	}
}

var _ BackendSGProvider = &defaultBackendSGProvider{}

type defaultBackendSGProvider struct {
	vpcID       string
	clusterName string
	mutex       sync.Mutex

	backendSG       string
	autoGeneratedSG string
	ec2Client       services.EC2
	k8sClient       client.Client
	logger          logr.Logger

	defaultDeletionPollInterval time.Duration
	defaultDeletionTimeout      time.Duration
}

func (p *defaultBackendSGProvider) Get(ctx context.Context) (string, error) {
	if len(p.backendSG) > 0 {
		return p.backendSG, nil
	}
	// Auto generate Backend Security group, and return the id
	if err := p.allocateBackendSG(ctx); err != nil {
		p.logger.Error(err, "Failed to auto-create backend SG")
		return "", err
	}
	return p.autoGeneratedSG, nil
}

func (p *defaultBackendSGProvider) Release(ctx context.Context) error {
	if len(p.backendSG) > 0 {
		return nil
	}
	if required, err := p.isBackendSGRequired(ctx); required || err != nil {
		return err
	}
	return p.releaseSG(ctx)
}

func (p *defaultBackendSGProvider) allocateBackendSG(ctx context.Context) error {
	p.mutex.Lock()
	defer p.mutex.Unlock()

	if len(p.autoGeneratedSG) > 0 {
		return nil
	}

	sgName := p.getBackendSGName()
	sgID, err := p.getBackendSGFromEC2(ctx, sgName, p.vpcID)
	if err != nil {
		return err
	}
	if len(sgID) > 1 {
		p.logger.V(1).Info("Existing SG found", "id", sgID)
		p.autoGeneratedSG = sgID
		return nil
	}

	createReq := &ec2sdk.CreateSecurityGroupInput{
		VpcId:             awssdk.String(p.vpcID),
		GroupName:         awssdk.String(sgName),
		Description:       awssdk.String(sgDescription),
		TagSpecifications: p.buildBackendSGTags(ctx),
	}
	p.logger.Info("creating securityGroup", "name", sgName)
	resp, err := p.ec2Client.CreateSecurityGroupWithContext(ctx, createReq)
	if err != nil {
		return err
	}
	p.logger.V(1).Info("created SecurityGroup", "name", sgName, "id", resp.GroupId)
	p.autoGeneratedSG = awssdk.StringValue(resp.GroupId)
	return nil
}

func (p *defaultBackendSGProvider) buildBackendSGTags(_ context.Context) []*ec2sdk.TagSpecification {
	return []*ec2sdk.TagSpecification{
		{
			ResourceType: awssdk.String(resourceTypeSecurityGroup),
			Tags: []*ec2sdk.Tag{
				{
					Key:   awssdk.String(tagKeyK8sCluster),
					Value: awssdk.String(p.clusterName),
				},
				{
					Key:   awssdk.String(tagKeyResource),
					Value: awssdk.String(tagValueBackend),
				},
			},
		},
	}
}

func (p *defaultBackendSGProvider) getBackendSGFromEC2(ctx context.Context, sgName string, vpcID string) (string, error) {
	req := &ec2sdk.DescribeSecurityGroupsInput{
		Filters: []*ec2sdk.Filter{
			{
				Name:   awssdk.String("vpc-id"),
				Values: awssdk.StringSlice([]string{vpcID}),
			},
			{
				Name:   awssdk.String(fmt.Sprintf("tag:%v", tagKeyK8sCluster)),
				Values: awssdk.StringSlice([]string{p.clusterName}),
			},
			{
				Name:   awssdk.String(fmt.Sprintf("tag:%v", tagKeyResource)),
				Values: awssdk.StringSlice([]string{tagValueBackend}),
			},
		},
	}
	p.logger.V(1).Info("Queriying existing SG", "vpc-id", vpcID, "name", sgName)
	sgs, err := p.ec2Client.DescribeSecurityGroupsAsList(ctx, req)
	if err != nil && !isEC2SecurityGroupNotFoundError(err) {
		return "", err
	}
	if len(sgs) > 0 {
		return awssdk.StringValue(sgs[0].GroupId), nil
	}
	return "", nil
}

func (p *defaultBackendSGProvider) isBackendSGRequired(ctx context.Context) (bool, error) {
	ingList := &networking.IngressList{}
	if err := p.k8sClient.List(ctx, ingList); err != nil {
		p.logger.Error(err, "Unable to list ingresses")
		return true, errors.Wrapf(err, "unable to list ingresses")
	}
	for _, ing := range ingList.Items {
		if !ing.DeletionTimestamp.IsZero() {
			continue
		}
		for _, fin := range ing.GetFinalizers() {
			if fin == implicitGroupFinalizer || strings.HasPrefix(fin, explicitGroupFinalizerPrefix) {
				return true, nil
			}
		}
	}
	p.logger.Info("No ingress found, backend SG can be deleted", "SG ID", p.autoGeneratedSG)
	return false, nil
}

func (p *defaultBackendSGProvider) releaseSG(ctx context.Context) error {
	p.mutex.Lock()
	defer p.mutex.Unlock()

	if len(p.autoGeneratedSG) == 0 {
		return nil
	}

	if required, err := p.isBackendSGRequired(ctx); required || err != nil {
		p.logger.V(1).Info("backend SG is required, releaseSG ignore delete")
		return err
	}
	req := &ec2sdk.DeleteSecurityGroupInput{
		GroupId: awssdk.String(p.autoGeneratedSG),
	}
	p.logger.V(1).Info("deleting default backend SG", "ID", p.autoGeneratedSG)
	if err := runtime.RetryImmediateOnError(p.defaultDeletionPollInterval, p.defaultDeletionTimeout, isSecurityGroupDependencyViolationError, func() error {
		_, err := p.ec2Client.DeleteSecurityGroupWithContext(ctx, req)
		return err
	}); err != nil {
		return errors.Wrap(err, "failed to delete securityGroup")
	}
	p.logger.Info("deleted securityGroup", "ID", p.autoGeneratedSG)

	p.autoGeneratedSG = ""
	return nil
}

var invalidSGNamePattern = regexp.MustCompile("[[:^alnum:]]")

func (p *defaultBackendSGProvider) getBackendSGName() string {
	sgNameHash := sha256.New()
	_, _ = sgNameHash.Write([]byte(p.clusterName))
	sgHash := hex.EncodeToString(sgNameHash.Sum(nil))
	sanitizedClusterName := invalidSGNamePattern.ReplaceAllString(p.clusterName, "")
	return fmt.Sprintf("k8s-traffic-%.232s-%.10s", sanitizedClusterName, sgHash)
}

func isSecurityGroupDependencyViolationError(err error) bool {
	var awsErr awserr.Error
	if errors.As(err, &awsErr) {
		return awsErr.Code() == "DependencyViolation"
	}
	return false
}

func isEC2SecurityGroupNotFoundError(err error) bool {
	var awsErr awserr.Error
	if errors.As(err, &awsErr) {
		return awsErr.Code() == "InvalidGroup.NotFound"
	}
	return false
}
<|endoftext|>


<commit_before>package notifications

import (
	"bytes"
	"context"
	"fmt"
	"io/ioutil"
	"net/http"
	"time"

	"golang.org/x/net/context/ctxhttp"

	"github.com/grafana/grafana/pkg/log"
	"github.com/grafana/grafana/pkg/util"
)

type Webhook struct {
	Url        string
	User       string
	Password   string
	Body       string
	HttpMethod string
}

var webhookQueue chan *Webhook
var webhookLog log.Logger

func initWebhookQueue() {
	webhookLog = log.New("notifications.webhook")
	webhookQueue = make(chan *Webhook, 10)
	go processWebhookQueue()
}

func processWebhookQueue() {
	for {
		select {
		case webhook := <-webhookQueue:
			err := sendWebRequestSync(context.TODO(), webhook)

			if err != nil {
				webhookLog.Error("Failed to send webrequest ", "error", err)
			}
		}
	}
}

func sendWebRequestSync(ctx context.Context, webhook *Webhook) error {
	webhookLog.Debug("Sending webhook", "url", webhook.Url, "http method", webhook.HttpMethod)

	client := &http.Client{
		Timeout: time.Duration(10 * time.Second),
	}

	if webhook.HttpMethod == "" {
		webhook.HttpMethod = http.MethodPost
	}

	request, err := http.NewRequest(webhook.HttpMethod, webhook.Url, bytes.NewReader([]byte(webhook.Body)))
	if webhook.User != "" && webhook.Password != "" {
		request.Header.Add("Authorization", util.GetBasicAuthHeader(webhook.User, webhook.Password))
	}

	if err != nil {
		return err
	}

	resp, err := ctxhttp.Do(ctx, client, request)
	if err != nil {
		return err
	}

	if resp.StatusCode/100 == 2 {
		return nil
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	webhookLog.Debug("Webhook failed", "statuscode", resp.Status, "body", string(body))
	return fmt.Errorf("Webhook response status %v", resp.Status)
}

var addToWebhookQueue = func(msg *Webhook) {
	webhookQueue <- msg
}
<commit_msg>webhook: adds json content-type<commit_after>package notifications

import (
	"bytes"
	"context"
	"fmt"
	"io/ioutil"
	"net/http"

	"golang.org/x/net/context/ctxhttp"

	"github.com/grafana/grafana/pkg/log"
	"github.com/grafana/grafana/pkg/util"
)

type Webhook struct {
	Url        string
	User       string
	Password   string
	Body       string
	HttpMethod string
}

var (
	webhookQueue chan *Webhook
	webhookLog   log.Logger
)

func initWebhookQueue() {
	webhookLog = log.New("notifications.webhook")
	webhookQueue = make(chan *Webhook, 10)
	go processWebhookQueue()
}

func processWebhookQueue() {
	for {
		select {
		case webhook := <-webhookQueue:
			err := sendWebRequestSync(context.TODO(), webhook)

			if err != nil {
				webhookLog.Error("Failed to send webrequest ", "error", err)
			}
		}
	}
}

func sendWebRequestSync(ctx context.Context, webhook *Webhook) error {
	webhookLog.Debug("Sending webhook", "url", webhook.Url, "http method", webhook.HttpMethod)

	if webhook.HttpMethod == "" {
		webhook.HttpMethod = http.MethodPost
	}

	request, err := http.NewRequest(webhook.HttpMethod, webhook.Url, bytes.NewReader([]byte(webhook.Body)))
	if err != nil {
		return err
	}

	request.Header.Add("Content-Type", "application/json")
	request.Header.Add("User-Agent", "Grafana")
	if webhook.User != "" && webhook.Password != "" {
		request.Header.Add("Authorization", util.GetBasicAuthHeader(webhook.User, webhook.Password))
	}

	resp, err := ctxhttp.Do(ctx, http.DefaultClient, request)
	if err != nil {
		return err
	}

	if resp.StatusCode/100 == 2 {
		return nil
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	webhookLog.Debug("Webhook failed", "statuscode", resp.Status, "body", string(body))
	return fmt.Errorf("Webhook response status %v", resp.Status)
}

var addToWebhookQueue = func(msg *Webhook) {
	webhookQueue <- msg
}
<|endoftext|>


<commit_before>package main

var APPCONTENT = `package main

import (
	"net/http"
	"log"
)

func main() {
	mux := http.NewServeMux()

	mux.HandleFunc("/", DefaultHandler)

	log.Panic(http.ListenAndServe(":8080", mux))
}

func DefaultHandler(rw http.ResponseWriter, req *http.Request) {
	rw.Write([]byte("Server Running"))
}

`
<commit_msg>Add Static FileServer by default<commit_after>package main

var APPCONTENT = `package main

import (
	"net/http"
	"log"
)

const (
	STATIC = "/public/"
)

func main() {
	mux := http.NewServeMux()

	mux.HandleFunc("/", DefaultHandler)

	mux.Handle(STATIC, http.StripPrefix(STATIC, http.FileServer(http.Dir("public"))))

	log.Panic(http.ListenAndServe(":8080", mux))
}

func DefaultHandler(rw http.ResponseWriter, req *http.Request) {
	rw.Write([]byte("Server Running"))
}

`
<|endoftext|>


<commit_before>package cluster

import (
	"fmt"
	"strconv"
	"strings"

	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/pkg/api/v1"

	"github.com/zalando-incubator/postgres-operator/pkg/spec"
	"github.com/zalando-incubator/postgres-operator/pkg/util"
	"github.com/zalando-incubator/postgres-operator/pkg/util/constants"
	"github.com/zalando-incubator/postgres-operator/pkg/util/filesystems"
	"github.com/zalando-incubator/postgres-operator/pkg/util/volumes"
)

func (c *Cluster) listPersistentVolumeClaims() ([]v1.PersistentVolumeClaim, error) {
	ns := c.Namespace
	listOptions := metav1.ListOptions{
		LabelSelector: c.labelsSet(false).String(),
	}

	pvcs, err := c.KubeClient.PersistentVolumeClaims(ns).List(listOptions)
	if err != nil {
		return nil, fmt.Errorf("could not list of PersistentVolumeClaims: %v", err)
	}
	return pvcs.Items, nil
}

func (c *Cluster) deletePersistenVolumeClaims() error {
	c.logger.Debugln("deleting PVCs")
	pvcs, err := c.listPersistentVolumeClaims()
	if err != nil {
		return err
	}
	for _, pvc := range pvcs {
		c.logger.Debugf("deleting PVC %q", util.NameFromMeta(pvc.ObjectMeta))
		if err := c.KubeClient.PersistentVolumeClaims(pvc.Namespace).Delete(pvc.Name, c.deleteOptions); err != nil {
			c.logger.Warningf("could not delete PersistentVolumeClaim: %v", err)
		}
	}
	if len(pvcs) > 0 {
		c.logger.Debugln("PVCs have been deleted")
	} else {
		c.logger.Debugln("no PVCs to delete")
	}

	return nil
}

func (c *Cluster) listPersistentVolumes() ([]*v1.PersistentVolume, error) {
	result := make([]*v1.PersistentVolume, 0)

	pvcs, err := c.listPersistentVolumeClaims()
	if err != nil {
		return nil, fmt.Errorf("could not list cluster's PersistentVolumeClaims: %v", err)
	}

	pods, err := c.listPods()
	if err != nil {
		return nil, fmt.Errorf("could not get list of running pods for resizing persistent volumes: %v", err)
	}

	lastPodIndex := len(pods) - 1

	for _, pvc := range pvcs {
		lastDash := strings.LastIndex(pvc.Name, "-")
		if lastDash > 0 && lastDash < len(pvc.Name)-1 {
			pvcNumber, err := strconv.Atoi(pvc.Name[lastDash+1:])
			if err != nil {
				return nil, fmt.Errorf("could not convert last part of the persistent volume claim name %q to a number", pvc.Name)
			}
			if pvcNumber > lastPodIndex {
				c.logger.Debugf("skipping persistent volume %q corresponding to a non-running pods", pvc.Name)
				continue
			}
		}
		pv, err := c.KubeClient.PersistentVolumes().Get(pvc.Spec.VolumeName, metav1.GetOptions{})
		if err != nil {
			return nil, fmt.Errorf("could not get PersistentVolume: %v", err)
		}
		result = append(result, pv)
	}

	return result, nil
}

// resizeVolumes resize persistent volumes compatible with the given resizer interface
func (c *Cluster) resizeVolumes(newVolume spec.Volume, resizers []volumes.VolumeResizer) error {
	c.setProcessName("resizing volumes")

	totalCompatible := 0
	newQuantity, err := resource.ParseQuantity(newVolume.Size)
	if err != nil {
		return fmt.Errorf("could not parse volume size: %v", err)
	}
	pvs, newSize, err := c.listVolumesWithManifestSize(newVolume)
	if err != nil {
		return fmt.Errorf("could not list persistent volumes: %v", err)
	}

	for _, pv := range pvs {
		volumeSize := quantityToGigabyte(pv.Spec.Capacity[v1.ResourceStorage])
		if volumeSize > newSize {
			return fmt.Errorf("cannot shrink persistent volume")
		}
		if volumeSize == newSize {
			continue
		}
		for _, resizer := range resizers {
			if !resizer.VolumeBelongsToProvider(pv) {
				continue
			}
			totalCompatible++
			if !resizer.IsConnectedToProvider() {
				err := resizer.ConnectToProvider()
				if err != nil {
					return fmt.Errorf("could not connect to the volume provider: %v", err)
				}
				defer func() {
					if err := resizer.DisconnectFromProvider(); err != nil {
						c.logger.Errorf("%v", err)
					}
				}()
			}
			awsVolumeID, err := resizer.GetProviderVolumeID(pv)
			if err != nil {
				return err
			}
			c.logger.Debugf("updating persistent volume %q to %d", pv.Name, newSize)
			if err := resizer.ResizeVolume(awsVolumeID, newSize); err != nil {
				return fmt.Errorf("could not resize EBS volume %q: %v", awsVolumeID, err)
			}
			c.logger.Debugf("resizing the filesystem on the volume %q", pv.Name)
			podName := getPodNameFromPersistentVolume(pv)
			if err := c.resizePostgresFilesystem(podName, []filesystems.FilesystemResizer{&filesystems.Ext234Resize{}}); err != nil {
				return fmt.Errorf("could not resize the filesystem on pod %q: %v", podName, err)
			}
			c.logger.Debugf("filesystem resize successful on volume %q", pv.Name)
			pv.Spec.Capacity[v1.ResourceStorage] = newQuantity
			c.logger.Debugf("updating persistent volume definition for volume %q", pv.Name)
			if _, err := c.KubeClient.PersistentVolumes().Update(pv); err != nil {
				return fmt.Errorf("could not update persistent volume: %q", err)
			}
			c.logger.Debugf("successfully updated persistent volume %q", pv.Name)
		}
	}
	if len(pvs) > 0 && totalCompatible == 0 {
		return fmt.Errorf("could not resize EBS volumes: persistent volumes are not compatible with existing resizing providers")
	}
	return nil
}

func (c *Cluster) volumesNeedResizing(newVolume spec.Volume) (bool, error) {
	vols, manifestSize, err := c.listVolumesWithManifestSize(newVolume)
	if err != nil {
		return false, err
	}
	for _, pv := range vols {
		currentSize := quantityToGigabyte(pv.Spec.Capacity[v1.ResourceStorage])
		if currentSize != manifestSize {
			return true, nil
		}
	}
	return false, nil
}

func (c *Cluster) listVolumesWithManifestSize(newVolume spec.Volume) ([]*v1.PersistentVolume, int64, error) {
	newSize, err := resource.ParseQuantity(newVolume.Size)
	if err != nil {
		return nil, 0, fmt.Errorf("could not parse volume size from the manifest: %v", err)
	}
	manifestSize := quantityToGigabyte(newSize)
	vols, err := c.listPersistentVolumes()
	if err != nil {
		return nil, 0, fmt.Errorf("could not list persistent volumes: %v", err)
	}
	return vols, manifestSize, nil
}

// getPodNameFromPersistentVolume returns a pod name that it extracts from the volume claim ref.
func getPodNameFromPersistentVolume(pv *v1.PersistentVolume) *spec.NamespacedName {
	namespace := pv.Spec.ClaimRef.Namespace
	name := pv.Spec.ClaimRef.Name[len(constants.DataVolumeName)+1:]
	return &spec.NamespacedName{Namespace: namespace, Name: name}
}

func quantityToGigabyte(q resource.Quantity) int64 {
	return q.ScaledValue(0) / (1 * constants.Gigabyte)
}
<commit_msg>Be lenient when asked to shrink a persisten volume.<commit_after>package cluster

import (
	"fmt"
	"strconv"
	"strings"

	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/pkg/api/v1"

	"github.com/zalando-incubator/postgres-operator/pkg/spec"
	"github.com/zalando-incubator/postgres-operator/pkg/util"
	"github.com/zalando-incubator/postgres-operator/pkg/util/constants"
	"github.com/zalando-incubator/postgres-operator/pkg/util/filesystems"
	"github.com/zalando-incubator/postgres-operator/pkg/util/volumes"
)

func (c *Cluster) listPersistentVolumeClaims() ([]v1.PersistentVolumeClaim, error) {
	ns := c.Namespace
	listOptions := metav1.ListOptions{
		LabelSelector: c.labelsSet(false).String(),
	}

	pvcs, err := c.KubeClient.PersistentVolumeClaims(ns).List(listOptions)
	if err != nil {
		return nil, fmt.Errorf("could not list of PersistentVolumeClaims: %v", err)
	}
	return pvcs.Items, nil
}

func (c *Cluster) deletePersistenVolumeClaims() error {
	c.logger.Debugln("deleting PVCs")
	pvcs, err := c.listPersistentVolumeClaims()
	if err != nil {
		return err
	}
	for _, pvc := range pvcs {
		c.logger.Debugf("deleting PVC %q", util.NameFromMeta(pvc.ObjectMeta))
		if err := c.KubeClient.PersistentVolumeClaims(pvc.Namespace).Delete(pvc.Name, c.deleteOptions); err != nil {
			c.logger.Warningf("could not delete PersistentVolumeClaim: %v", err)
		}
	}
	if len(pvcs) > 0 {
		c.logger.Debugln("PVCs have been deleted")
	} else {
		c.logger.Debugln("no PVCs to delete")
	}

	return nil
}

func (c *Cluster) listPersistentVolumes() ([]*v1.PersistentVolume, error) {
	result := make([]*v1.PersistentVolume, 0)

	pvcs, err := c.listPersistentVolumeClaims()
	if err != nil {
		return nil, fmt.Errorf("could not list cluster's PersistentVolumeClaims: %v", err)
	}

	pods, err := c.listPods()
	if err != nil {
		return nil, fmt.Errorf("could not get list of running pods for resizing persistent volumes: %v", err)
	}

	lastPodIndex := len(pods) - 1

	for _, pvc := range pvcs {
		lastDash := strings.LastIndex(pvc.Name, "-")
		if lastDash > 0 && lastDash < len(pvc.Name)-1 {
			pvcNumber, err := strconv.Atoi(pvc.Name[lastDash+1:])
			if err != nil {
				return nil, fmt.Errorf("could not convert last part of the persistent volume claim name %q to a number", pvc.Name)
			}
			if pvcNumber > lastPodIndex {
				c.logger.Debugf("skipping persistent volume %q corresponding to a non-running pods", pvc.Name)
				continue
			}
		}
		pv, err := c.KubeClient.PersistentVolumes().Get(pvc.Spec.VolumeName, metav1.GetOptions{})
		if err != nil {
			return nil, fmt.Errorf("could not get PersistentVolume: %v", err)
		}
		result = append(result, pv)
	}

	return result, nil
}

// resizeVolumes resize persistent volumes compatible with the given resizer interface
func (c *Cluster) resizeVolumes(newVolume spec.Volume, resizers []volumes.VolumeResizer) error {
	c.setProcessName("resizing volumes")

	totalCompatible := 0
	newQuantity, err := resource.ParseQuantity(newVolume.Size)
	if err != nil {
		return fmt.Errorf("could not parse volume size: %v", err)
	}
	pvs, newSize, err := c.listVolumesWithManifestSize(newVolume)
	if err != nil {
		return fmt.Errorf("could not list persistent volumes: %v", err)
	}

	for _, pv := range pvs {
		volumeSize := quantityToGigabyte(pv.Spec.Capacity[v1.ResourceStorage])
		if volumeSize >= newSize {
			if volumeSize > newSize {
				c.logger.Warningf("cannot shrink persistent volume")
			}
			continue
		}
		for _, resizer := range resizers {
			if !resizer.VolumeBelongsToProvider(pv) {
				continue
			}
			totalCompatible++
			if !resizer.IsConnectedToProvider() {
				err := resizer.ConnectToProvider()
				if err != nil {
					return fmt.Errorf("could not connect to the volume provider: %v", err)
				}
				defer func() {
					if err := resizer.DisconnectFromProvider(); err != nil {
						c.logger.Errorf("%v", err)
					}
				}()
			}
			awsVolumeID, err := resizer.GetProviderVolumeID(pv)
			if err != nil {
				return err
			}
			c.logger.Debugf("updating persistent volume %q to %d", pv.Name, newSize)
			if err := resizer.ResizeVolume(awsVolumeID, newSize); err != nil {
				return fmt.Errorf("could not resize EBS volume %q: %v", awsVolumeID, err)
			}
			c.logger.Debugf("resizing the filesystem on the volume %q", pv.Name)
			podName := getPodNameFromPersistentVolume(pv)
			if err := c.resizePostgresFilesystem(podName, []filesystems.FilesystemResizer{&filesystems.Ext234Resize{}}); err != nil {
				return fmt.Errorf("could not resize the filesystem on pod %q: %v", podName, err)
			}
			c.logger.Debugf("filesystem resize successful on volume %q", pv.Name)
			pv.Spec.Capacity[v1.ResourceStorage] = newQuantity
			c.logger.Debugf("updating persistent volume definition for volume %q", pv.Name)
			if _, err := c.KubeClient.PersistentVolumes().Update(pv); err != nil {
				return fmt.Errorf("could not update persistent volume: %q", err)
			}
			c.logger.Debugf("successfully updated persistent volume %q", pv.Name)
		}
	}
	if len(pvs) > 0 && totalCompatible == 0 {
		return fmt.Errorf("could not resize EBS volumes: persistent volumes are not compatible with existing resizing providers")
	}
	return nil
}

func (c *Cluster) volumesNeedResizing(newVolume spec.Volume) (bool, error) {
	vols, manifestSize, err := c.listVolumesWithManifestSize(newVolume)
	if err != nil {
		return false, err
	}
	for _, pv := range vols {
		currentSize := quantityToGigabyte(pv.Spec.Capacity[v1.ResourceStorage])
		if currentSize != manifestSize {
			return true, nil
		}
	}
	return false, nil
}

func (c *Cluster) listVolumesWithManifestSize(newVolume spec.Volume) ([]*v1.PersistentVolume, int64, error) {
	newSize, err := resource.ParseQuantity(newVolume.Size)
	if err != nil {
		return nil, 0, fmt.Errorf("could not parse volume size from the manifest: %v", err)
	}
	manifestSize := quantityToGigabyte(newSize)
	vols, err := c.listPersistentVolumes()
	if err != nil {
		return nil, 0, fmt.Errorf("could not list persistent volumes: %v", err)
	}
	return vols, manifestSize, nil
}

// getPodNameFromPersistentVolume returns a pod name that it extracts from the volume claim ref.
func getPodNameFromPersistentVolume(pv *v1.PersistentVolume) *spec.NamespacedName {
	namespace := pv.Spec.ClaimRef.Namespace
	name := pv.Spec.ClaimRef.Name[len(constants.DataVolumeName)+1:]
	return &spec.NamespacedName{Namespace: namespace, Name: name}
}

func quantityToGigabyte(q resource.Quantity) int64 {
	return q.ScaledValue(0) / (1 * constants.Gigabyte)
}
<|endoftext|>


<commit_before>package command

import (
	"errors"
	"os"
	"strings"

	"github.com/jessevdk/go-flags"
)

type Options struct {
	Version           bool   `short:"v" long:"version" description:"Print version"`
	Debug             bool   `short:"d" long:"debug" description:"Enable debugging mode" default:"false"`
	Url               string `long:"url" description:"Database connection string"`
	Host              string `long:"host" description:"Server hostname or IP"`
	Port              int    `long:"port" description:"Server port" default:"5432"`
	User              string `long:"user" description:"Database user"`
	Pass              string `long:"pass" description:"Password for user"`
	DbName            string `long:"db" description:"Database name"`
	Ssl               string `long:"ssl" description:"SSL option"`
	HttpHost          string `long:"bind" description:"HTTP server host" default:"localhost"`
	HttpPort          uint   `long:"listen" description:"HTTP server listen port" default:"8081"`
	AuthUser          string `long:"auth-user" description:"HTTP basic auth user"`
	AuthPass          string `long:"auth-pass" description:"HTTP basic auth password"`
	SkipOpen          bool   `short:"s" long:"skip-open" description:"Skip browser open on start"`
	Sessions          bool   `long:"sessions" description:"Enable multiple database sessions" default:"false"`
	Prefix            string `long:"prefix" description:"Add a url prefix"`
	ReadOnly          bool   `long:"readonly" description:"Run database connection in readonly mode"`
	LockSession       bool   `long:"lock-session" description:"Lock session to a single database connection" default:"false"`
	Bookmark          string `short:"b" long:"bookmark" description:"Bookmark to use for connection. Bookmark files are stored under $HOME/.pgweb/bookmarks/*.toml" default:""`
	BookmarksDir      string `long:"bookmarks-dir" description:"Overrides default directory for bookmark files to search" default:""`
	DisablePrettyJson bool   `long:"no-pretty-json" description:"Disable JSON formatting feature for result export" default:"false"`
	DisableSSH        bool   `long:"no-ssh" description:"Disable database connections via SSH" default:"false"`
	ConnectBackend    string `long:"connect-backend" description:"Enable database authentication through a third party backend"`
	ConnectToken      string `long:"connect-token" description:"Authentication token for the third-party connect backend"`
	ConnectHeaders    string `long:"connect-headers" description:"List of headers to pass to the connect backend"`
}

var Opts Options

func ParseOptions() error {
	_, err := flags.ParseArgs(&Opts, os.Args)
	if err != nil {
		return err
	}

	if Opts.Url == "" {
		Opts.Url = os.Getenv("DATABASE_URL")
	}

	if os.Getenv("SESSIONS") != "" {
		Opts.Sessions = true
	}

	if os.Getenv("LOCK_SESSION") != "" {
		Opts.LockSession = true
		Opts.Sessions = false
	}

	if Opts.Prefix != "" && !strings.Contains(Opts.Prefix, "/") {
		Opts.Prefix = Opts.Prefix + "/"
	}

	if Opts.AuthUser == "" && os.Getenv("AUTH_USER") != "" {
		Opts.AuthUser = os.Getenv("AUTH_USER")
	}

	if Opts.AuthPass == "" && os.Getenv("AUTH_PASS") != "" {
		Opts.AuthPass = os.Getenv("AUTH_PASS")
	}

	if Opts.ConnectBackend != "" {
		if !Opts.Sessions {
			return errors.New("--sessions flag must be set")
		}
		if Opts.ConnectToken == "" {
			return errors.New("--connect-token flag must be set")
		}
	} else {
		if Opts.ConnectToken != "" || Opts.ConnectHeaders != "" {
			return errors.New("--connect-backend flag must be set")
		}
	}

	return nil
}
<commit_msg>Dot now allow using startup bookmark in multi-session mode<commit_after>package command

import (
	"errors"
	"os"
	"strings"

	"github.com/jessevdk/go-flags"
)

type Options struct {
	Version           bool   `short:"v" long:"version" description:"Print version"`
	Debug             bool   `short:"d" long:"debug" description:"Enable debugging mode" default:"false"`
	Url               string `long:"url" description:"Database connection string"`
	Host              string `long:"host" description:"Server hostname or IP"`
	Port              int    `long:"port" description:"Server port" default:"5432"`
	User              string `long:"user" description:"Database user"`
	Pass              string `long:"pass" description:"Password for user"`
	DbName            string `long:"db" description:"Database name"`
	Ssl               string `long:"ssl" description:"SSL option"`
	HttpHost          string `long:"bind" description:"HTTP server host" default:"localhost"`
	HttpPort          uint   `long:"listen" description:"HTTP server listen port" default:"8081"`
	AuthUser          string `long:"auth-user" description:"HTTP basic auth user"`
	AuthPass          string `long:"auth-pass" description:"HTTP basic auth password"`
	SkipOpen          bool   `short:"s" long:"skip-open" description:"Skip browser open on start"`
	Sessions          bool   `long:"sessions" description:"Enable multiple database sessions" default:"false"`
	Prefix            string `long:"prefix" description:"Add a url prefix"`
	ReadOnly          bool   `long:"readonly" description:"Run database connection in readonly mode"`
	LockSession       bool   `long:"lock-session" description:"Lock session to a single database connection" default:"false"`
	Bookmark          string `short:"b" long:"bookmark" description:"Bookmark to use for connection. Bookmark files are stored under $HOME/.pgweb/bookmarks/*.toml" default:""`
	BookmarksDir      string `long:"bookmarks-dir" description:"Overrides default directory for bookmark files to search" default:""`
	DisablePrettyJson bool   `long:"no-pretty-json" description:"Disable JSON formatting feature for result export" default:"false"`
	DisableSSH        bool   `long:"no-ssh" description:"Disable database connections via SSH" default:"false"`
	ConnectBackend    string `long:"connect-backend" description:"Enable database authentication through a third party backend"`
	ConnectToken      string `long:"connect-token" description:"Authentication token for the third-party connect backend"`
	ConnectHeaders    string `long:"connect-headers" description:"List of headers to pass to the connect backend"`
}

var Opts Options

func ParseOptions() error {
	_, err := flags.ParseArgs(&Opts, os.Args)
	if err != nil {
		return err
	}

	if Opts.Url == "" {
		Opts.Url = os.Getenv("DATABASE_URL")
	}

	if os.Getenv("SESSIONS") != "" {
		Opts.Sessions = true
	}

	if os.Getenv("LOCK_SESSION") != "" {
		Opts.LockSession = true
		Opts.Sessions = false
	}

	if Opts.Prefix != "" && !strings.Contains(Opts.Prefix, "/") {
		Opts.Prefix = Opts.Prefix + "/"
	}

	if Opts.AuthUser == "" && os.Getenv("AUTH_USER") != "" {
		Opts.AuthUser = os.Getenv("AUTH_USER")
	}

	if Opts.AuthPass == "" && os.Getenv("AUTH_PASS") != "" {
		Opts.AuthPass = os.Getenv("AUTH_PASS")
	}

	if Opts.Bookmark != "" && Opts.Sessions {
		return errors.New("--bookmark is not allowed in multi-session")
	}

	if Opts.ConnectBackend != "" {
		if !Opts.Sessions {
			return errors.New("--sessions flag must be set")
		}
		if Opts.ConnectToken == "" {
			return errors.New("--connect-token flag must be set")
		}
	} else {
		if Opts.ConnectToken != "" || Opts.ConnectHeaders != "" {
			return errors.New("--connect-backend flag must be set")
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package command

const (
	// Version is the current Pgweb application version
	Version = "0.11.10"
)

var (
	// GitCommit contains the Git commit SHA for the binary
	GitCommit string

	// BuildTime contains the binary build time
	BuildTime string

	// GoVersion contains the build time Go version
	GoVersion string
)
<commit_msg>Version bump: 0.11.11<commit_after>package command

const (
	// Version is the current Pgweb application version
	Version = "0.11.11"
)

var (
	// GitCommit contains the Git commit SHA for the binary
	GitCommit string

	// BuildTime contains the binary build time
	BuildTime string

	// GoVersion contains the build time Go version
	GoVersion string
)
<|endoftext|>


<commit_before>// +build linux

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

package disk

import "strconv"

// fsType2StringMap - list of filesystems supported on linux
var fsType2StringMap = map[string]string{
	"1021994":  "TMPFS",
	"137d":     "EXT",
	"4244":     "HFS",
	"4d44":     "MSDOS",
	"52654973": "REISERFS",
	"5346544e": "NTFS",
	"58465342": "XFS",
	"61756673": "AUFS",
	"6969":     "NFS",
	"ef51":     "EXT2OLD",
	"ef53":     "EXT4",
	"f15f":     "ecryptfs",
	"794c7630": "overlayfs",
	"2fc12fc1": "zfs",
	"ff534d42": "cifs",
}

// getFSType returns the filesystem type of the underlying mounted filesystem
func getFSType(ftype int64) string {
	fsTypeHex := strconv.FormatInt(ftype, 16)
	fsTypeString, ok := fsType2StringMap[fsTypeHex]
	if !ok {
		return "UNKNOWN"
	}
	return fsTypeString
}
<commit_msg>fix: add integer code for Windows Subsystem for Linux filesystem (#8010)<commit_after>// +build linux

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

package disk

import "strconv"

// fsType2StringMap - list of filesystems supported on linux
var fsType2StringMap = map[string]string{
	"1021994":  "TMPFS",
	"137d":     "EXT",
	"4244":     "HFS",
	"4d44":     "MSDOS",
	"52654973": "REISERFS",
	"5346544e": "NTFS",
	"58465342": "XFS",
	"61756673": "AUFS",
	"6969":     "NFS",
	"ef51":     "EXT2OLD",
	"ef53":     "EXT4",
	"f15f":     "ecryptfs",
	"794c7630": "overlayfs",
	"2fc12fc1": "zfs",
	"ff534d42": "cifs",
	"53464846": "wslfs",
}

// getFSType returns the filesystem type of the underlying mounted filesystem
func getFSType(ftype int64) string {
	fsTypeHex := strconv.FormatInt(ftype, 16)
	fsTypeString, ok := fsType2StringMap[fsTypeHex]
	if !ok {
		return "UNKNOWN"
	}
	return fsTypeString
}
<|endoftext|>


<commit_before>// Copyright 2017 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

// Package iset ("instruction set") provides base and helper types for ifuzz arch implementations.
package iset

import (
	"math/rand"
)

const (
	ArchX86     = "x86"
	ArchPowerPC = "powerpc"
)

var Arches = make(map[string]InsnSet)

type (
	Mode int
	Type int
)

type Insn interface {
	Info() (name string, mode Mode, pseudo, priv bool)
	Encode(cfg *Config, r *rand.Rand) []byte
}

type InsnSet interface {
	GetInsns(mode Mode, typ Type) []Insn
	Decode(mode Mode, text []byte) (int, error)
	DecodeExt(mode Mode, text []byte) (int, error) // XED, to keep ifuzz_test happy
}

type Config struct {
	Arch       string
	Len        int         // number of instructions to generate
	Mode       Mode        // one of ModeXXX
	Priv       bool        // generate CPL=0 instructions (x86), HV/!PR mode (PPC)
	Exec       bool        // generate instructions sequences interesting for execution
	MemRegions []MemRegion // generated instructions will reference these regions
}

type MemRegion struct {
	Start uint64
	Size  uint64
}

const (
	ModeLong64 Mode = iota
	ModeProt32
	ModeProt16
	ModeReal16
	ModeLast
)

const (
	TypeExec Type = iota
	TypePriv
	TypeUser
	TypeAll
	TypeLast
)

var SpecialNumbers = [...]uint64{0, 1 << 15, 1 << 16, 1 << 31, 1 << 32, 1 << 47, 1 << 47, 1 << 63}

type ModeInsns [ModeLast][TypeLast][]Insn

func (modeInsns *ModeInsns) Add(insn Insn) {
	_, mode, pseudo, priv := insn.Info()
	for m := Mode(0); m < ModeLast; m++ {
		if mode&(1<<uint(m)) == 0 {
			continue
		}
		set := &modeInsns[m]
		if pseudo {
			set[TypeExec] = append(set[TypeExec], insn)
		} else if priv {
			set[TypePriv] = append(set[TypePriv], insn)
			set[TypeAll] = append(set[TypeAll], insn)
		} else {
			set[TypeUser] = append(set[TypeUser], insn)
			set[TypeAll] = append(set[TypeAll], insn)
		}
	}
}

func (cfg *Config) IsCompatible(insn Insn) bool {
	_, mode, pseudo, priv := insn.Info()
	if cfg.Mode < 0 || cfg.Mode >= ModeLast {
		panic("bad mode")
	}
	if priv && !cfg.Priv {
		return false
	}
	if pseudo && !cfg.Exec {
		return false
	}
	if mode&(1<<uint(cfg.Mode)) == 0 {
		return false
	}
	return true
}
<commit_msg>pkg/ifuzz/iset: make Mode and Type unsigned<commit_after>// Copyright 2017 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

// Package iset ("instruction set") provides base and helper types for ifuzz arch implementations.
package iset

import (
	"math/rand"
)

const (
	ArchX86     = "x86"
	ArchPowerPC = "powerpc"
)

var Arches = make(map[string]InsnSet)

type (
	Mode uint
	Type uint
)

type Insn interface {
	Info() (name string, mode Mode, pseudo, priv bool)
	Encode(cfg *Config, r *rand.Rand) []byte
}

type InsnSet interface {
	GetInsns(mode Mode, typ Type) []Insn
	Decode(mode Mode, text []byte) (int, error)
	DecodeExt(mode Mode, text []byte) (int, error) // XED, to keep ifuzz_test happy
}

type Config struct {
	Arch       string
	Len        int         // number of instructions to generate
	Mode       Mode        // one of ModeXXX
	Priv       bool        // generate CPL=0 instructions (x86), HV/!PR mode (PPC)
	Exec       bool        // generate instructions sequences interesting for execution
	MemRegions []MemRegion // generated instructions will reference these regions
}

type MemRegion struct {
	Start uint64
	Size  uint64
}

const (
	ModeLong64 Mode = iota
	ModeProt32
	ModeProt16
	ModeReal16
	ModeLast
)

const (
	TypeExec Type = iota
	TypePriv
	TypeUser
	TypeAll
	TypeLast
)

var SpecialNumbers = [...]uint64{0, 1 << 15, 1 << 16, 1 << 31, 1 << 32, 1 << 47, 1 << 47, 1 << 63}

type ModeInsns [ModeLast][TypeLast][]Insn

func (modeInsns *ModeInsns) Add(insn Insn) {
	_, mode, pseudo, priv := insn.Info()
	for m := Mode(0); m < ModeLast; m++ {
		if mode&(1<<uint(m)) == 0 {
			continue
		}
		set := &modeInsns[m]
		if pseudo {
			set[TypeExec] = append(set[TypeExec], insn)
		} else if priv {
			set[TypePriv] = append(set[TypePriv], insn)
			set[TypeAll] = append(set[TypeAll], insn)
		} else {
			set[TypeUser] = append(set[TypeUser], insn)
			set[TypeAll] = append(set[TypeAll], insn)
		}
	}
}

func (cfg *Config) IsCompatible(insn Insn) bool {
	_, mode, pseudo, priv := insn.Info()
	if cfg.Mode >= ModeLast {
		panic("bad mode")
	}
	if priv && !cfg.Priv {
		return false
	}
	if pseudo && !cfg.Exec {
		return false
	}
	if mode&(1<<uint(cfg.Mode)) == 0 {
		return false
	}
	return true
}
<|endoftext|>


<commit_before>package imexport

import (
	"archive/tar"
	"bytes"
	"compress/gzip"
	"encoding/json"
	"io"
	"time"

	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/vfs"
)

func writeFile(fs vfs.VFS, name string, tw *tar.Writer, doc *vfs.FileDoc) error {
	file, err := fs.OpenFile(doc)
	if err != nil {
		return err
	}

	hdr := &tar.Header{
		Name:       name,
		Mode:       0644,
		Size:       doc.Size(),
		ModTime:    doc.ModTime(),
		AccessTime: doc.CreatedAt,
		ChangeTime: doc.UpdatedAt,
		Typeflag:   tar.TypeReg,
	}
	if doc.Executable {
		hdr.Mode = 0755
	}

	if err := tw.WriteHeader(hdr); err != nil {
		return err
	}
	if _, err := io.Copy(tw, file); err != nil {
		return err
	}
	return nil
}

func createDir(name string, tw *tar.Writer, dir *vfs.DirDoc) error {

	hdr := &tar.Header{
		Name:     name,
		Mode:     0755,
		Size:     dir.Size(),
		ModTime:  dir.ModTime(),
		Typeflag: tar.TypeDir,
	}
	err := tw.WriteHeader(hdr)

	return err
}

func metadata(tw *tar.Writer, fs vfs.VFS, domain string) error {
	db := couchdb.SimpleDatabasePrefix(domain)
	doctype := "io.cozy.files"
	req := &couchdb.AllDocsRequest{}
	var results []map[string]interface{}
	if err := couchdb.GetAllDocs(db, doctype, req, &results); err != nil {
		return err
	}

	metaDir := "metadata/album/"
	hdrDir := &tar.Header{
		Name:     metaDir,
		Mode:     0755,
		Typeflag: tar.TypeDir,
	}
	if err := tw.WriteHeader(hdrDir); err != nil {
		return err
	}

	hdr := &tar.Header{
		Name:       metaDir + "album.json",
		Mode:       0644,
		AccessTime: time.Now(),
		ChangeTime: time.Now(),
		Typeflag:   tar.TypeReg,
	}

	var content bytes.Buffer
	var size int64

	for _, val := range results {
		if val["referenced_by"] != nil {

			id := val["_id"].(string)
			doc, err := fs.FileByID(id)
			if err != nil {
				return err
			}

			path, err := fs.FilePath(doc)
			if err != nil {
				return err
			}
			val["album_filepath"] = path

			ref := doc.ReferencedBy
			for _, v := range ref {
				out := &couchdb.JSONDoc{}
				if err = couchdb.GetDoc(db, v.Type, v.ID, out); err != nil {
					return err
				}
				m := out.ToMapWithType()

				albumName := m["name"]
				val["album_name"] = albumName
			}

			jsonString, err := json.Marshal(val)
			if err != nil {
				return err
			}

			size += int64(len(jsonString) + 1) // len([]byte("\n"))

			content.Write(jsonString)
			content.Write([]byte("\n"))

		}

	}

	hdr.Size = size
	if err := tw.WriteHeader(hdr); err != nil {
		return err
	}

	if _, err := content.WriteTo(tw); err != nil {
		return err
	}

	return nil
}

func export(tw *tar.Writer, fs vfs.VFS, domain string) error {
	root := "/"

	err := vfs.Walk(fs, root, func(name string, dir *vfs.DirDoc, file *vfs.FileDoc, err error) error {
		if err != nil {
			return err
		}

		if dir != nil {
			if err := createDir(name, tw, dir); err != nil {
				return err
			}
		}

		if file != nil {
			if err := writeFile(fs, name, tw, file); err != nil {
				return err
			}

		}

		return nil
	})
	if err != nil {
		return err
	}
	return metadata(tw, fs, domain)
}

// Tardir tar doc directory
func Tardir(w io.Writer, fs vfs.VFS, domain string) error {
	//gzip writer
	gw := gzip.NewWriter(w)
	defer gw.Close()

	//tar writer
	tw := tar.NewWriter(gw)
	defer tw.Close()

	err := export(tw, fs, domain)

	return err

}
<commit_msg>checkerror<commit_after>package imexport

import (
	"archive/tar"
	"bytes"
	"compress/gzip"
	"encoding/json"
	"io"
	"time"

	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/vfs"
)

func writeFile(fs vfs.VFS, name string, tw *tar.Writer, doc *vfs.FileDoc) error {
	file, err := fs.OpenFile(doc)
	if err != nil {
		return err
	}

	hdr := &tar.Header{
		Name:       name,
		Mode:       0644,
		Size:       doc.Size(),
		ModTime:    doc.ModTime(),
		AccessTime: doc.CreatedAt,
		ChangeTime: doc.UpdatedAt,
		Typeflag:   tar.TypeReg,
	}
	if doc.Executable {
		hdr.Mode = 0755
	}

	if err := tw.WriteHeader(hdr); err != nil {
		return err
	}
	if _, err := io.Copy(tw, file); err != nil {
		return err
	}
	return nil
}

func createDir(name string, tw *tar.Writer, dir *vfs.DirDoc) error {

	hdr := &tar.Header{
		Name:     name,
		Mode:     0755,
		Size:     dir.Size(),
		ModTime:  dir.ModTime(),
		Typeflag: tar.TypeDir,
	}
	err := tw.WriteHeader(hdr)

	return err
}

func metadata(tw *tar.Writer, fs vfs.VFS, domain string) error {
	db := couchdb.SimpleDatabasePrefix(domain)
	doctype := "io.cozy.files"
	req := &couchdb.AllDocsRequest{}
	var results []map[string]interface{}
	if err := couchdb.GetAllDocs(db, doctype, req, &results); err != nil {
		return err
	}

	metaDir := "metadata/album/"
	hdrDir := &tar.Header{
		Name:     metaDir,
		Mode:     0755,
		Typeflag: tar.TypeDir,
	}
	if err := tw.WriteHeader(hdrDir); err != nil {
		return err
	}

	hdr := &tar.Header{
		Name:       metaDir + "album.json",
		Mode:       0644,
		AccessTime: time.Now(),
		ChangeTime: time.Now(),
		Typeflag:   tar.TypeReg,
	}

	var content bytes.Buffer
	var size int64

	for _, val := range results {
		if val["referenced_by"] != nil {

			id := val["_id"].(string)
			doc, err := fs.FileByID(id)
			if err != nil {
				return err
			}

			path, err := fs.FilePath(doc)
			if err != nil {
				return err
			}
			val["album_filepath"] = path

			ref := doc.ReferencedBy
			for _, v := range ref {
				out := &couchdb.JSONDoc{}
				if err = couchdb.GetDoc(db, v.Type, v.ID, out); err != nil {
					return err
				}
				m := out.ToMapWithType()

				albumName := m["name"]
				val["album_name"] = albumName
			}

			jsonString, err := json.Marshal(val)
			if err != nil {
				return err
			}

			size += int64(len(jsonString) + 1) // len([]byte("\n"))

			if _, err = content.Write(jsonString); err != nil {
				return err
			}
			if _, err = content.Write([]byte("\n")); err != nil {
				return err
			}

		}

	}

	hdr.Size = size
	if err := tw.WriteHeader(hdr); err != nil {
		return err
	}

	if _, err := content.WriteTo(tw); err != nil {
		return err
	}

	return nil
}

func export(tw *tar.Writer, fs vfs.VFS, domain string) error {
	root := "/"

	err := vfs.Walk(fs, root, func(name string, dir *vfs.DirDoc, file *vfs.FileDoc, err error) error {
		if err != nil {
			return err
		}

		if dir != nil {
			if err := createDir(name, tw, dir); err != nil {
				return err
			}
		}

		if file != nil {
			if err := writeFile(fs, name, tw, file); err != nil {
				return err
			}

		}

		return nil
	})
	if err != nil {
		return err
	}
	return metadata(tw, fs, domain)
}

// Tardir tar doc directory
func Tardir(w io.Writer, fs vfs.VFS, domain string) error {
	//gzip writer
	gw := gzip.NewWriter(w)
	defer gw.Close()

	//tar writer
	tw := tar.NewWriter(gw)
	defer tw.Close()

	err := export(tw, fs, domain)

	return err

}
<|endoftext|>