<commit_before>// Copyright 2020 The etcd 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.

// An example distributed locking with fencing in the case of etcd
// Based on https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html

// Important usage:
// If you are invoking this program as client 1, you need to configure GODEBUG env var like below:
// GODEBUG=gcstoptheworld=2 ./client 1

package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"go.etcd.io/etcd/v3/clientv3"
	"go.etcd.io/etcd/v3/clientv3/concurrency"
	"io/ioutil"
	"net/http"
	"os"
	"runtime"
	"strconv"
	"time"
)

type node struct {
	next *node
}

const (
	// These const values might be need adjustment.
	nrGarbageObjects = 100 * 1000 * 1000
	sessionTTL       = 1
)

func stopTheWorld() {
	n := new(node)
	root := n
	allocStart := time.Now()
	for i := 0; i < nrGarbageObjects; i++ {
		n.next = new(node)
		n = n.next
	}
	func(n *node) {}(root) // dummy usage of root for removing a compiler error
	root = nil
	allocDur := time.Since(allocStart)

	gcStart := time.Now()
	runtime.GC()
	gcDur := time.Since(gcStart)
	fmt.Printf("took %v for allocation, took %v for GC\n", allocDur, gcDur)
}

type request struct {
	Op      string `json:"op"`
	Key     string `json:"key"`
	Val     string `json:"val"`
	Version int64  `json:"version"`
}

type response struct {
	Val     string `json:"val"`
	Version int64  `json:"version"`
	Err     string `json:"err"`
}

func write(key string, value string, version int64) error {
	req := request{
		Op:      "write",
		Key:     key,
		Val:     value,
		Version: version,
	}

	reqBytes, err := json.Marshal(&req)
	if err != nil {
		fmt.Printf("failed to marshal request: %s\n", err)
		os.Exit(1)
	}

	httpResp, err := http.Post("http://localhost:8080", "application/json", bytes.NewReader(reqBytes))
	if err != nil {
		fmt.Printf("failed to send a request to storage: %s\n", err)
		os.Exit(1)
	}

	respBytes, err := ioutil.ReadAll(httpResp.Body)
	if err != nil {
		fmt.Printf("failed to read request body: %s\n", err)
		os.Exit(1)
	}

	resp := new(response)
	err = json.Unmarshal(respBytes, resp)
	if err != nil {
		fmt.Printf("failed to unmarshal response json: %s\n", err)
		os.Exit(1)
	}

	if resp.Err != "" {
		return fmt.Errorf("error: %s", resp.Err)
	}

	return nil
}

func read(key string) (string, int64) {
	req := request{
		Op:  "read",
		Key: key,
	}

	reqBytes, err := json.Marshal(&req)
	if err != nil {
		fmt.Printf("failed to marshal request: %s\n", err)
		os.Exit(1)
	}

	httpResp, err := http.Post("http://localhost:8080", "application/json", bytes.NewReader(reqBytes))
	if err != nil {
		fmt.Printf("failed to send a request to storage: %s\n", err)
		os.Exit(1)
	}

	respBytes, err := ioutil.ReadAll(httpResp.Body)
	if err != nil {
		fmt.Printf("failed to read request body: %s\n", err)
		os.Exit(1)
	}

	resp := new(response)
	err = json.Unmarshal(respBytes, resp)
	if err != nil {
		fmt.Printf("failed to unmarshal response json: %s\n", err)
		os.Exit(1)
	}

	return resp.Val, resp.Version
}

func main() {
	if len(os.Args) != 2 {
		fmt.Printf("usage: %s <1 or 2>\n", os.Args[0])
		return
	}

	mode, err := strconv.Atoi(os.Args[1])
	if err != nil || mode != 1 && mode != 2 {
		fmt.Printf("mode should be 1 or 2 (given value is %s)\n", os.Args[1])
		return
	}

	fmt.Printf("client %d starts\n", mode)

	client, err := clientv3.New(clientv3.Config{
		Endpoints: []string{"http://127.0.0.1:2379", "http://127.0.0.1:22379", "http://127.0.0.1:32379"},
	})
	if err != nil {
		fmt.Printf("failed to create an etcd client: %s\n", err)
		os.Exit(1)
	}

	fmt.Printf("creted etcd client\n")

	session, err := concurrency.NewSession(client, concurrency.WithTTL(sessionTTL))
	if err != nil {
		fmt.Printf("failed to create a session: %s\n", err)
		os.Exit(1)
	}

	locker := concurrency.NewLocker(session, "/lock")
	locker.Lock()
	version := session.Lease()
	fmt.Printf("acquired lock, version: %d\n", version)

	if mode == 1 {
		stopTheWorld()
		fmt.Printf("emulated stop the world GC, make sure the /lock/* key disappeared and hit any key after executing client 2: ")
		reader := bufio.NewReader(os.Stdin)
		reader.ReadByte()
		fmt.Printf("resuming client 1\n")
	} else {
		fmt.Printf("this is client 2, continuing\n")
	}

	err = write("key0", fmt.Sprintf("value from client %d", mode), int64(version))
	if err != nil {
		fmt.Printf("failed to write to storage: %s\n", err) // client 1 should show this message
	} else {
		fmt.Printf("successfully write a key to storage\n")
	}
}
<commit_msg>Documentation/learning/lock/client: Add defer Unlock (#11802)<commit_after>// Copyright 2020 The etcd 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.

// An example distributed locking with fencing in the case of etcd
// Based on https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html

// Important usage:
// If you are invoking this program as client 1, you need to configure GODEBUG env var like below:
// GODEBUG=gcstoptheworld=2 ./client 1

package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"go.etcd.io/etcd/v3/clientv3"
	"go.etcd.io/etcd/v3/clientv3/concurrency"
	"io/ioutil"
	"net/http"
	"os"
	"runtime"
	"strconv"
	"time"
)

type node struct {
	next *node
}

const (
	// These const values might be need adjustment.
	nrGarbageObjects = 100 * 1000 * 1000
	sessionTTL       = 1
)

func stopTheWorld() {
	n := new(node)
	root := n
	allocStart := time.Now()
	for i := 0; i < nrGarbageObjects; i++ {
		n.next = new(node)
		n = n.next
	}
	func(n *node) {}(root) // dummy usage of root for removing a compiler error
	root = nil
	allocDur := time.Since(allocStart)

	gcStart := time.Now()
	runtime.GC()
	gcDur := time.Since(gcStart)
	fmt.Printf("took %v for allocation, took %v for GC\n", allocDur, gcDur)
}

type request struct {
	Op      string `json:"op"`
	Key     string `json:"key"`
	Val     string `json:"val"`
	Version int64  `json:"version"`
}

type response struct {
	Val     string `json:"val"`
	Version int64  `json:"version"`
	Err     string `json:"err"`
}

func write(key string, value string, version int64) error {
	req := request{
		Op:      "write",
		Key:     key,
		Val:     value,
		Version: version,
	}

	reqBytes, err := json.Marshal(&req)
	if err != nil {
		fmt.Printf("failed to marshal request: %s\n", err)
		os.Exit(1)
	}

	httpResp, err := http.Post("http://localhost:8080", "application/json", bytes.NewReader(reqBytes))
	if err != nil {
		fmt.Printf("failed to send a request to storage: %s\n", err)
		os.Exit(1)
	}

	respBytes, err := ioutil.ReadAll(httpResp.Body)
	if err != nil {
		fmt.Printf("failed to read request body: %s\n", err)
		os.Exit(1)
	}

	resp := new(response)
	err = json.Unmarshal(respBytes, resp)
	if err != nil {
		fmt.Printf("failed to unmarshal response json: %s\n", err)
		os.Exit(1)
	}

	if resp.Err != "" {
		return fmt.Errorf("error: %s", resp.Err)
	}

	return nil
}

func read(key string) (string, int64) {
	req := request{
		Op:  "read",
		Key: key,
	}

	reqBytes, err := json.Marshal(&req)
	if err != nil {
		fmt.Printf("failed to marshal request: %s\n", err)
		os.Exit(1)
	}

	httpResp, err := http.Post("http://localhost:8080", "application/json", bytes.NewReader(reqBytes))
	if err != nil {
		fmt.Printf("failed to send a request to storage: %s\n", err)
		os.Exit(1)
	}

	respBytes, err := ioutil.ReadAll(httpResp.Body)
	if err != nil {
		fmt.Printf("failed to read request body: %s\n", err)
		os.Exit(1)
	}

	resp := new(response)
	err = json.Unmarshal(respBytes, resp)
	if err != nil {
		fmt.Printf("failed to unmarshal response json: %s\n", err)
		os.Exit(1)
	}

	return resp.Val, resp.Version
}

func main() {
	if len(os.Args) != 2 {
		fmt.Printf("usage: %s <1 or 2>\n", os.Args[0])
		return
	}

	mode, err := strconv.Atoi(os.Args[1])
	if err != nil || mode != 1 && mode != 2 {
		fmt.Printf("mode should be 1 or 2 (given value is %s)\n", os.Args[1])
		return
	}

	fmt.Printf("client %d starts\n", mode)

	client, err := clientv3.New(clientv3.Config{
		Endpoints: []string{"http://127.0.0.1:2379", "http://127.0.0.1:22379", "http://127.0.0.1:32379"},
	})
	if err != nil {
		fmt.Printf("failed to create an etcd client: %s\n", err)
		os.Exit(1)
	}

	fmt.Printf("creted etcd client\n")

	session, err := concurrency.NewSession(client, concurrency.WithTTL(sessionTTL))
	if err != nil {
		fmt.Printf("failed to create a session: %s\n", err)
		os.Exit(1)
	}

	locker := concurrency.NewLocker(session, "/lock")
	locker.Lock()
	defer locker.Unlock()
	version := session.Lease()
	fmt.Printf("acquired lock, version: %d\n", version)

	if mode == 1 {
		stopTheWorld()
		fmt.Printf("emulated stop the world GC, make sure the /lock/* key disappeared and hit any key after executing client 2: ")
		reader := bufio.NewReader(os.Stdin)
		reader.ReadByte()
		fmt.Printf("resuming client 1\n")
	} else {
		fmt.Printf("this is client 2, continuing\n")
	}

	err = write("key0", fmt.Sprintf("value from client %d", mode), int64(version))
	if err != nil {
		fmt.Printf("failed to write to storage: %s\n", err) // client 1 should show this message
	} else {
		fmt.Printf("successfully write a key to storage\n")
	}
}
<|endoftext|>


<commit_before>package MySQLProtocol

const HOSTNAME_LENGTH uint = 60
const SYSTEM_CHARSET_MBMAXLEN uint = 3
const NAME_CHAR_LEN uint = 64
const USERNAME_CHAR_LENGTH uint = 16
const NAME_LEN uint = NAME_CHAR_LEN * SYSTEM_CHARSET_MBMAXLEN
const USERNAME_LENGTH uint = USERNAME_CHAR_LENGTH * SYSTEM_CHARSET_MBMAXLEN

const MYSQL_AUTODETECT_CHARSET_NAME string = "auto"

const SERVER_VERSION_LENGTH uint = 60
const SQLSTATE_LENGTH uint = 5

const TABLE_COMMENT_INLINE_MAXLEN uint = 180
const TABLE_COMMENT_MAXLEN uint = 2048
const COLUMN_COMMENT_MAXLEN uint = 1024
const INDEX_COMMENT_MAXLEN uint = 1024
const TABLE_PARTITION_COMMENT_MAXLEN uint = 1024

const USER_HOST_BUFF_SIZE uint = HOSTNAME_LENGTH + USERNAME_LENGTH + 2

const MYSQL_ERRMSG_SIZE uint = 512
const NET_READ_TIMEOUT uint = 30
const NET_WRITE_TIMEOUT uint = 60
const NET_WAIT_TIMEOUT uint = 8 * 60 * 60

const ONLY_KILL_QUERY uint = 1

const LOCAL_HOST string = "localhost"
const LOCAL_HOST_NAMEDPIPE string = "."

const MODE_INIT uint = 0               // Connection opened
const MODE_READ_HANDSHAKE uint = 1     // Read the handshake from the server and process it
const MODE_SEND_HANDSHAKE uint = 2     // Forward the handshake from the server
const MODE_READ_AUTH uint = 3          // Read the reply from the client and process it
const MODE_SEND_AUTH uint = 4          // Forward the reply from the client
const MODE_READ_AUTH_RESULT uint = 5   // Read the reply from the server and process it
const MODE_SEND_AUTH_RESULT uint = 6   // Forward the reply from the server
const MODE_READ_QUERY uint = 7         // Read the query from the client and process it
const MODE_SEND_QUERY uint = 8         // Send the query to the server
const MODE_READ_QUERY_RESULT uint = 9  // Read the result set from the server and and process it
const MODE_SEND_QUERY_RESULT uint = 10 // Send a result set to the client
const MODE_CLEANUP uint = 11           // Connection closed

const Character_Set_latin1_swedish_ci byte = 0x08
const Character_Set_utf8_general_ci byte = 0x21
const Character_Set_binary byte = 0x3f

const COM_SLEEP byte = 0x00
const COM_QUIT byte = 0x01
const COM_INIT_DB byte = 0x02
const COM_QUERY byte = 0x03
const COM_FIELD_LIST byte = 0x04
const COM_CREATE_DB byte = 0x05
const COM_DROP_DB byte = 0x06
const COM_REFRESH byte = 0x07
const COM_SHUTDOWN byte = 0x08
const COM_STATISTICS byte = 0x09
const COM_PROCESS_INFO byte = 0x0A // deprecated
const COM_CONNECT byte = 0x0B      // deprecated
const COM_PROCESS_KILL byte = 0x0C
const COM_DEBUG byte = 0x0D
const COM_PING byte = 0x0E
const COM_TIME byte = 0x0F           // deprecated
const COM_DELAYED_INSERT byte = 0x10 // deprecated
const COM_CHANGE_USER byte = 0x11
const COM_BINLOG_DUMP byte = 0x12
const COM_TABLE_DUMP byte = 0x13
const COM_CONNECT_OUT byte = 0x14
const COM_REGISTER_SLAVE byte = 0x15
const COM_STMT_PREPARE byte = 0x16
const COM_STMT_EXECUTE byte = 0x17
const COM_STMT_SEND_LONG_DATA byte = 0x18
const COM_STMT_CLOSE byte = 0x19
const COM_STMT_RESET byte = 0x1A
const COM_SET_OPTION byte = 0x1B
const COM_STMT_FETCH byte = 0x1C
const COM_DAEMON byte = 0x1D // deprecated
const COM_BINLOG_DUMP_GTID byte = 0x1E
const COM_RESET_CONNECTION byte = 0x1F // deprecated

const OK byte = 0x00
const ERR byte = 0xFF
const EOF byte = 0xFE
const LOCAL_INFILE byte = 0xFB

const SERVER_STATUS_IN_TRANS uint16 = 0x0001
const SERVER_STATUS_AUTOCOMMIT uint16 = 0x0002
const SERVER_MORE_RESULTS_EXISTS uint16 = 0x0008
const SERVER_QUERY_NO_GOOD_INDEX_USED uint16 = 0x0010
const SERVER_STATUS_NO_GOOD_INDEX_USED uint16 = SERVER_QUERY_NO_GOOD_INDEX_USED
const SERVER_QUERY_NO_INDEX_USED uint16 = 0x0020
const SERVER_STATUS_NO_INDEX_USED uint16 = SERVER_QUERY_NO_INDEX_USED
const SERVER_STATUS_CURSOR_EXISTS uint16 = 0x0040
const SERVER_STATUS_LAST_ROW_SENT uint16 = 0x0080
const SERVER_STATUS_DB_DROPPED uint16 = 0x0100
const SERVER_STATUS_NO_BACKSLASH_ESCAPES uint16 = 0x0200
const SERVER_STATUS_METADATA_CHANGED uint16 = 0x0400
const SERVER_QUERY_WAS_SLOW uint16 = 0x0800
const SERVER_PS_OUT_PARAMS uint16 = 0x1000
const SERVER_STATUS_IN_TRANS_READONLY uint16 = 0x2000

const SERVER_STATUS_CLEAR_SET uint16 = (SERVER_QUERY_NO_GOOD_INDEX_USED |
	SERVER_QUERY_NO_INDEX_USED |
	SERVER_MORE_RESULTS_EXISTS |
	SERVER_STATUS_METADATA_CHANGED |
	SERVER_QUERY_WAS_SLOW |
	SERVER_STATUS_DB_DROPPED |
	SERVER_STATUS_CURSOR_EXISTS |
	SERVER_STATUS_LAST_ROW_SENT)

const CLIENT_LONG_PASSWORD uint = 1
const CLIENT_FOUND_ROWS uint = 2
const CLIENT_LONG_FLAG uint = 4
const CLIENT_CONNECT_WITH_DB uint = 8
const CLIENT_NO_SCHEMA uint = 16
const CLIENT_COMPRESS uint = 32
const CLIENT_ODBC uint = 64
const CLIENT_LOCAL_FILES uint = 128
const CLIENT_IGNORE_SPACE uint = 256
const CLIENT_PROTOCOL_41 uint = 512
const CLIENT_INTERACTIVE uint = 1024
const CLIENT_SSL uint = 2048
const CLIENT_IGNORE_SIGPIPE uint = 4096
const CLIENT_TRANSACTIONS uint = 8192
const CLIENT_RESERVED uint = 16384
const CLIENT_SECURE_CONNECTION uint = 32768
const CLIENT_MULTI_STATEMENTS uint = 1 << 16
const CLIENT_MULTI_RESULTS uint = 1 << 17
const CLIENT_PS_MULTI_RESULTS uint = 1 << 18
const CLIENT_PLUGIN_AUTH uint = 1 << 19
const CLIENT_CONNECT_ATTRS uint = 1 << 20
const CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA uint = 1 << 21
const CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS uint = 1 << 22
const CLIENT_SSL_VERIFY_SERVER_CERT uint = 1 << 30
const CLIENT_REMEMBER_OPTIONS uint = 1 << 31

const CAN_CLIENT_COMPRESS uint = 0

const CLIENT_ALL_FLAGS uint = (CLIENT_LONG_PASSWORD |
	CLIENT_FOUND_ROWS |
	CLIENT_LONG_FLAG |
	CLIENT_CONNECT_WITH_DB |
	CLIENT_NO_SCHEMA |
	CLIENT_COMPRESS |
	CLIENT_ODBC |
	CLIENT_LOCAL_FILES |
	CLIENT_IGNORE_SPACE |
	CLIENT_PROTOCOL_41 |
	CLIENT_INTERACTIVE |
	CLIENT_SSL |
	CLIENT_IGNORE_SIGPIPE |
	CLIENT_TRANSACTIONS |
	CLIENT_RESERVED |
	CLIENT_SECURE_CONNECTION |
	CLIENT_MULTI_STATEMENTS |
	CLIENT_MULTI_RESULTS |
	CLIENT_PS_MULTI_RESULTS |
	CLIENT_SSL_VERIFY_SERVER_CERT |
	CLIENT_REMEMBER_OPTIONS |
	CLIENT_PLUGIN_AUTH |
	CLIENT_CONNECT_ATTRS |
	CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA |
	CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS)

const CLIENT_BASIC_FLAGS uint = (((CLIENT_ALL_FLAGS &
	^CLIENT_SSL) &
	^CLIENT_COMPRESS) &
	^CLIENT_SSL_VERIFY_SERVER_CERT)

const MYSQL_TYPE_DECIMAL uint = 0
const MYSQL_TYPE_TINY uint = 1
const MYSQL_TYPE_SHORT uint = 2
const MYSQL_TYPE_LONG uint = 3
const MYSQL_TYPE_FLOAT uint = 4
const MYSQL_TYPE_DOUBLE uint = 5
const MYSQL_TYPE_NULL uint = 6
const MYSQL_TYPE_TIMESTAMP uint = 7
const MYSQL_TYPE_LONGLONG uint = 8
const MYSQL_TYPE_INT24 uint = 9
const MYSQL_TYPE_DATE uint = 10
const MYSQL_TYPE_TIME uint = 11
const MYSQL_TYPE_DATETIME uint = 12
const MYSQL_TYPE_YEAR uint = 13
const MYSQL_TYPE_NEWDATE uint = 14
const MYSQL_TYPE_VARCHAR uint = 15
const MYSQL_TYPE_BIT uint = 16
const MYSQL_TYPE_TIMESTAMP2 uint = 17
const MYSQL_TYPE_DATETIME2 uint = 18
const MYSQL_TYPE_TIME2 uint = 19
const MYSQL_TYPE_NEWDECIMAL uint = 246
const MYSQL_TYPE_ENUM uint = 247
const MYSQL_TYPE_SET uint = 248
const MYSQL_TYPE_TINY_BLOB uint = 249
const MYSQL_TYPE_MEDIUM_BLOB uint = 250
const MYSQL_TYPE_LONG_BLOB uint = 251
const MYSQL_TYPE_BLOB uint = 252
const MYSQL_TYPE_VAR_STRING uint = 253
const MYSQL_TYPE_STRING uint = 254
const MYSQL_TYPE_GEOMETRY uint = 255

const REFRESH_GRANT uint = 1
const REFRESH_LOG uint = 2
const REFRESH_TABLES uint = 4
const REFRESH_HOSTS uint = 8
const REFRESH_STATUS uint = 16
const REFRESH_THREADS uint = 32
const REFRESH_SLAVE uint = 64
const REFRESH_MASTER uint = 128
const REFRESH_ERROR_LOG uint = 256
const REFRESH_ENGINE_LOG uint = 512
const REFRESH_BINARY_LOG uint = 1024
const REFRESH_RELAY_LOG uint = 2048
const REFRESH_GENERAL_LOG uint = 4096
const REFRESH_SLOW_LOG uint = 8192
const REFRESH_READ_LOCK uint = 16384
const REFRESH_FAST uint = 32768
const REFRESH_QUERY_CACHE uint = 65536
const REFRESH_QUERY_CACHE_FREE uint = 0x20000
const REFRESH_DES_KEY_FILE uint = 0x40000
const REFRESH_USER_RESOURCES uint = 0x80000
const REFRESH_FOR_EXPORT uint = 0x100000

const CURSOR_TYPE_NO_CURSOR uint = 0
const CURSOR_TYPE_READ_ONLY uint = 1
const CURSOR_TYPE_FOR_UPDATE uint = 2
const CURSOR_TYPE_SCROLLABLE uint = 4

const MYSQL_OPTION_MULTI_STATEMENTS_ON uint = 0
const MYSQL_OPTION_MULTI_STATEMENTS_OFF uint = 1

const ROW_TYPE_TEXT uint = 0
const ROW_TYPE_BINARY uint = 1

const RS_OK uint = 0
const RS_FULL uint = 1
const RS_COL_DEF uint = 2
const RS_DATA_FILE uint = 3

const SCRAMBLE_LENGTH uint = 20
const SCRAMBLE_LENGTH_323 uint = 8
const SCRAMBLED_PASSWORD_CHAR_LENGTH uint = SCRAMBLE_LENGTH*2 + 1
const SCRAMBLED_PASSWORD_CHAR_LENGTH_323 uint = SCRAMBLE_LENGTH_323 * 2

const NOT_NULL_FLAG uint = 1
const PRI_KEY_FLAG uint = 2
const UNIQUE_KEY_FLAG uint = 4
const MULTIPLE_KEY_FLAG uint = 8
const BLOB_FLAG uint = 16
const UNSIGNED_FLAG uint = 32
const ZEROFILL_FLAG uint = 64
const BINARY_FLAG uint = 128

const ENUM_FLAG uint = 256
const AUTO_INCREMENT_FLAG uint = 512
const TIMESTAMP_FLAG uint = 1024
const SET_FLAG uint = 2048
const NO_DEFAULT_VALUE_FLAG uint = 4096
const ON_UPDATE_NOW_FLAG uint = 8192
const NUM_FLAG uint = 32768
const PART_KEY_FLAG uint = 16384
const GROUP_FLAG uint = 32768
const UNIQUE_FLAG uint = 65536
const BINCMP_FLAG uint = 131072
const GET_FIXED_FIELDS_FLAG uint = 1 << 18
const FIELD_IN_PART_FUNC_FLAG uint = 1 << 19

const FIELD_IN_ADD_INDEX uint = 1 << 20
const FIELD_IS_RENAMED uint = 1 << 21
const FIELD_FLAGS_STORAGE_MEDIA uint = 22
const FIELD_FLAGS_STORAGE_MEDIA_MASK uint = 3 << FIELD_FLAGS_STORAGE_MEDIA
const FIELD_FLAGS_COLUMN_FORMAT uint = 24
const FIELD_FLAGS_COLUMN_FORMAT_MASK uint = 3 << FIELD_FLAGS_COLUMN_FORMAT
const FIELD_IS_DROPPED uint = 1 << 26

const MAX_TINYINT_WIDTH uint = 3
const MAX_SMALLINT_WIDTH uint = 5
const MAX_MEDIUMINT_WIDTH uint = 8
const MAX_INT_WIDTH uint = 10
const MAX_BIGINT_WIDTH uint = 20
const MAX_CHAR_WIDTH uint = 255
const MAX_BLOB_WIDTH uint = 16777216

const packet_error uint = 0

const MYSQL_SHUTDOWN_KILLABLE_CONNECT uint = (char)(1 << 0)
const MYSQL_SHUTDOWN_KILLABLE_TRANS uint = (char)(1 << 1)
const MYSQL_SHUTDOWN_KILLABLE_LOCK_TABLE uint = (char)(1 << 2)
const MYSQL_SHUTDOWN_KILLABLE_UPDATE uint = (char)(1 << 3)

const SHUTDOWN_DEFAULT uint = 0
const SHUTDOWN_WAIT_CONNECTIONS uint = MYSQL_SHUTDOWN_KILLABLE_CONNECT
const SHUTDOWN_WAIT_TRANSACTIONS uint = MYSQL_SHUTDOWN_KILLABLE_TRANS
const SHUTDOWN_WAIT_UPDATES uint = MYSQL_SHUTDOWN_KILLABLE_UPDATE
const SHUTDOWN_WAIT_ALL_BUFFERS uint = MYSQL_SHUTDOWN_KILLABLE_UPDATE << 1
const SHUTDOWN_WAIT_CRITICAL_BUFFERS uint = (MYSQL_SHUTDOWN_KILLABLE_UPDATE << 1) + 1
const KILL_QUERY uint = 254
const KILL_CONNECTION uint = 255

const STRING_RESULT uint = 0
const REAL_RESULT uint = 1
const INT_RESULT uint = 2
const ROW_RESULT uint = 3
const DECIMAL_RESULT uint = 4

const NET_HEADER_SIZE uint = 4
const COMP_HEADER_SIZE uint = 3

const NULL_LENGTH uint = ^0
const MYSQL_STMT_HEADER uint = 4
const MYSQL_LONG_DATA_HEADER uint = 6

const NOT_FIXED_DEC uint = 31
<commit_msg>Flags<commit_after>package MySQLProtocol

const HOSTNAME_LENGTH uint = 60
const SYSTEM_CHARSET_MBMAXLEN uint = 3
const NAME_CHAR_LEN uint = 64
const USERNAME_CHAR_LENGTH uint = 16
const NAME_LEN uint = NAME_CHAR_LEN * SYSTEM_CHARSET_MBMAXLEN
const USERNAME_LENGTH uint = USERNAME_CHAR_LENGTH * SYSTEM_CHARSET_MBMAXLEN

const MYSQL_AUTODETECT_CHARSET_NAME string = "auto"

const SERVER_VERSION_LENGTH uint = 60
const SQLSTATE_LENGTH uint = 5

const TABLE_COMMENT_INLINE_MAXLEN uint = 180
const TABLE_COMMENT_MAXLEN uint = 2048
const COLUMN_COMMENT_MAXLEN uint = 1024
const INDEX_COMMENT_MAXLEN uint = 1024
const TABLE_PARTITION_COMMENT_MAXLEN uint = 1024

const USER_HOST_BUFF_SIZE uint = HOSTNAME_LENGTH + USERNAME_LENGTH + 2

const MYSQL_ERRMSG_SIZE uint = 512
const NET_READ_TIMEOUT uint = 30
const NET_WRITE_TIMEOUT uint = 60
const NET_WAIT_TIMEOUT uint = 8 * 60 * 60

const ONLY_KILL_QUERY uint = 1

const LOCAL_HOST string = "localhost"
const LOCAL_HOST_NAMEDPIPE string = "."

const MODE_INIT uint = 0               // Connection opened
const MODE_READ_HANDSHAKE uint = 1     // Read the handshake from the server and process it
const MODE_SEND_HANDSHAKE uint = 2     // Forward the handshake from the server
const MODE_READ_AUTH uint = 3          // Read the reply from the client and process it
const MODE_SEND_AUTH uint = 4          // Forward the reply from the client
const MODE_READ_AUTH_RESULT uint = 5   // Read the reply from the server and process it
const MODE_SEND_AUTH_RESULT uint = 6   // Forward the reply from the server
const MODE_READ_QUERY uint = 7         // Read the query from the client and process it
const MODE_SEND_QUERY uint = 8         // Send the query to the server
const MODE_READ_QUERY_RESULT uint = 9  // Read the result set from the server and and process it
const MODE_SEND_QUERY_RESULT uint = 10 // Send a result set to the client
const MODE_CLEANUP uint = 11           // Connection closed

const Character_Set_latin1_swedish_ci byte = 0x08
const Character_Set_utf8_general_ci byte = 0x21
const Character_Set_binary byte = 0x3f

const COM_SLEEP byte = 0x00
const COM_QUIT byte = 0x01
const COM_INIT_DB byte = 0x02
const COM_QUERY byte = 0x03
const COM_FIELD_LIST byte = 0x04
const COM_CREATE_DB byte = 0x05
const COM_DROP_DB byte = 0x06
const COM_REFRESH byte = 0x07
const COM_SHUTDOWN byte = 0x08
const COM_STATISTICS byte = 0x09
const COM_PROCESS_INFO byte = 0x0A // deprecated
const COM_CONNECT byte = 0x0B      // deprecated
const COM_PROCESS_KILL byte = 0x0C
const COM_DEBUG byte = 0x0D
const COM_PING byte = 0x0E
const COM_TIME byte = 0x0F           // deprecated
const COM_DELAYED_INSERT byte = 0x10 // deprecated
const COM_CHANGE_USER byte = 0x11
const COM_BINLOG_DUMP byte = 0x12
const COM_TABLE_DUMP byte = 0x13
const COM_CONNECT_OUT byte = 0x14
const COM_REGISTER_SLAVE byte = 0x15
const COM_STMT_PREPARE byte = 0x16
const COM_STMT_EXECUTE byte = 0x17
const COM_STMT_SEND_LONG_DATA byte = 0x18
const COM_STMT_CLOSE byte = 0x19
const COM_STMT_RESET byte = 0x1A
const COM_SET_OPTION byte = 0x1B
const COM_STMT_FETCH byte = 0x1C
const COM_DAEMON byte = 0x1D // deprecated
const COM_BINLOG_DUMP_GTID byte = 0x1E
const COM_RESET_CONNECTION byte = 0x1F // deprecated

const OK byte = 0x00
const ERR byte = 0xFF
const EOF byte = 0xFE
const LOCAL_INFILE byte = 0xFB

const SERVER_STATUS_IN_TRANS uint16 = 0x0001
const SERVER_STATUS_AUTOCOMMIT uint16 = 0x0002
const SERVER_MORE_RESULTS_EXISTS uint16 = 0x0008
const SERVER_QUERY_NO_GOOD_INDEX_USED uint16 = 0x0010
const SERVER_STATUS_NO_GOOD_INDEX_USED uint16 = SERVER_QUERY_NO_GOOD_INDEX_USED
const SERVER_QUERY_NO_INDEX_USED uint16 = 0x0020
const SERVER_STATUS_NO_INDEX_USED uint16 = SERVER_QUERY_NO_INDEX_USED
const SERVER_STATUS_CURSOR_EXISTS uint16 = 0x0040
const SERVER_STATUS_LAST_ROW_SENT uint16 = 0x0080
const SERVER_STATUS_DB_DROPPED uint16 = 0x0100
const SERVER_STATUS_NO_BACKSLASH_ESCAPES uint16 = 0x0200
const SERVER_STATUS_METADATA_CHANGED uint16 = 0x0400
const SERVER_QUERY_WAS_SLOW uint16 = 0x0800
const SERVER_PS_OUT_PARAMS uint16 = 0x1000
const SERVER_STATUS_IN_TRANS_READONLY uint16 = 0x2000

const SERVER_STATUS_CLEAR_SET uint16 = (SERVER_QUERY_NO_GOOD_INDEX_USED |
	SERVER_QUERY_NO_INDEX_USED |
	SERVER_MORE_RESULTS_EXISTS |
	SERVER_STATUS_METADATA_CHANGED |
	SERVER_QUERY_WAS_SLOW |
	SERVER_STATUS_DB_DROPPED |
	SERVER_STATUS_CURSOR_EXISTS |
	SERVER_STATUS_LAST_ROW_SENT)

const CLIENT_LONG_PASSWORD uint = 1
const CLIENT_FOUND_ROWS uint = 2
const CLIENT_LONG_FLAG uint = 4
const CLIENT_CONNECT_WITH_DB uint = 8
const CLIENT_NO_SCHEMA uint = 16
const CLIENT_COMPRESS uint = 32
const CLIENT_ODBC uint = 64
const CLIENT_LOCAL_FILES uint = 128
const CLIENT_IGNORE_SPACE uint = 256
const CLIENT_PROTOCOL_41 uint = 512
const CLIENT_INTERACTIVE uint = 1024
const CLIENT_SSL uint = 2048
const CLIENT_IGNORE_SIGPIPE uint = 4096
const CLIENT_TRANSACTIONS uint = 8192
const CLIENT_RESERVED uint = 16384
const CLIENT_SECURE_CONNECTION uint = 32768
const CLIENT_MULTI_STATEMENTS uint = 1 << 16
const CLIENT_MULTI_RESULTS uint = 1 << 17
const CLIENT_PS_MULTI_RESULTS uint = 1 << 18
const CLIENT_PLUGIN_AUTH uint = 1 << 19
const CLIENT_CONNECT_ATTRS uint = 1 << 20
const CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA uint = 1 << 21
const CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS uint = 1 << 22
const CLIENT_SSL_VERIFY_SERVER_CERT uint = 1 << 30
const CLIENT_REMEMBER_OPTIONS uint = 1 << 31

const CAN_CLIENT_COMPRESS uint = 0

const CLIENT_ALL_FLAGS uint = (CLIENT_LONG_PASSWORD |
	CLIENT_FOUND_ROWS |
	CLIENT_LONG_FLAG |
	CLIENT_CONNECT_WITH_DB |
	CLIENT_NO_SCHEMA |
	CLIENT_COMPRESS |
	CLIENT_ODBC |
	CLIENT_LOCAL_FILES |
	CLIENT_IGNORE_SPACE |
	CLIENT_PROTOCOL_41 |
	CLIENT_INTERACTIVE |
	CLIENT_SSL |
	CLIENT_IGNORE_SIGPIPE |
	CLIENT_TRANSACTIONS |
	CLIENT_RESERVED |
	CLIENT_SECURE_CONNECTION |
	CLIENT_MULTI_STATEMENTS |
	CLIENT_MULTI_RESULTS |
	CLIENT_PS_MULTI_RESULTS |
	CLIENT_SSL_VERIFY_SERVER_CERT |
	CLIENT_REMEMBER_OPTIONS |
	CLIENT_PLUGIN_AUTH |
	CLIENT_CONNECT_ATTRS |
	CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA |
	CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS)

const CLIENT_BASIC_FLAGS uint = (((CLIENT_ALL_FLAGS &
	^CLIENT_SSL) &
	^CLIENT_COMPRESS) &
	^CLIENT_SSL_VERIFY_SERVER_CERT)

const MYSQL_TYPE_DECIMAL uint = 0
const MYSQL_TYPE_TINY uint = 1
const MYSQL_TYPE_SHORT uint = 2
const MYSQL_TYPE_LONG uint = 3
const MYSQL_TYPE_FLOAT uint = 4
const MYSQL_TYPE_DOUBLE uint = 5
const MYSQL_TYPE_NULL uint = 6
const MYSQL_TYPE_TIMESTAMP uint = 7
const MYSQL_TYPE_LONGLONG uint = 8
const MYSQL_TYPE_INT24 uint = 9
const MYSQL_TYPE_DATE uint = 10
const MYSQL_TYPE_TIME uint = 11
const MYSQL_TYPE_DATETIME uint = 12
const MYSQL_TYPE_YEAR uint = 13
const MYSQL_TYPE_NEWDATE uint = 14
const MYSQL_TYPE_VARCHAR uint = 15
const MYSQL_TYPE_BIT uint = 16
const MYSQL_TYPE_TIMESTAMP2 uint = 17
const MYSQL_TYPE_DATETIME2 uint = 18
const MYSQL_TYPE_TIME2 uint = 19
const MYSQL_TYPE_NEWDECIMAL uint = 246
const MYSQL_TYPE_ENUM uint = 247
const MYSQL_TYPE_SET uint = 248
const MYSQL_TYPE_TINY_BLOB uint = 249
const MYSQL_TYPE_MEDIUM_BLOB uint = 250
const MYSQL_TYPE_LONG_BLOB uint = 251
const MYSQL_TYPE_BLOB uint = 252
const MYSQL_TYPE_VAR_STRING uint = 253
const MYSQL_TYPE_STRING uint = 254
const MYSQL_TYPE_GEOMETRY uint = 255

const REFRESH_GRANT uint = 1
const REFRESH_LOG uint = 2
const REFRESH_TABLES uint = 4
const REFRESH_HOSTS uint = 8
const REFRESH_STATUS uint = 16
const REFRESH_THREADS uint = 32
const REFRESH_SLAVE uint = 64
const REFRESH_MASTER uint = 128
const REFRESH_ERROR_LOG uint = 256
const REFRESH_ENGINE_LOG uint = 512
const REFRESH_BINARY_LOG uint = 1024
const REFRESH_RELAY_LOG uint = 2048
const REFRESH_GENERAL_LOG uint = 4096
const REFRESH_SLOW_LOG uint = 8192
const REFRESH_READ_LOCK uint = 16384
const REFRESH_FAST uint = 32768
const REFRESH_QUERY_CACHE uint = 65536
const REFRESH_QUERY_CACHE_FREE uint = 0x20000
const REFRESH_DES_KEY_FILE uint = 0x40000
const REFRESH_USER_RESOURCES uint = 0x80000
const REFRESH_FOR_EXPORT uint = 0x100000

const CURSOR_TYPE_NO_CURSOR uint = 0
const CURSOR_TYPE_READ_ONLY uint = 1
const CURSOR_TYPE_FOR_UPDATE uint = 2
const CURSOR_TYPE_SCROLLABLE uint = 4

const MYSQL_OPTION_MULTI_STATEMENTS_ON uint = 0
const MYSQL_OPTION_MULTI_STATEMENTS_OFF uint = 1

const ROW_TYPE_TEXT uint = 0
const ROW_TYPE_BINARY uint = 1

const RS_OK uint = 0
const RS_FULL uint = 1
const RS_COL_DEF uint = 2
const RS_DATA_FILE uint = 3

const SCRAMBLE_LENGTH uint = 20
const SCRAMBLE_LENGTH_323 uint = 8
const SCRAMBLED_PASSWORD_CHAR_LENGTH uint = SCRAMBLE_LENGTH*2 + 1
const SCRAMBLED_PASSWORD_CHAR_LENGTH_323 uint = SCRAMBLE_LENGTH_323 * 2

const NOT_NULL_FLAG uint = 1
const PRI_KEY_FLAG uint = 2
const UNIQUE_KEY_FLAG uint = 4
const MULTIPLE_KEY_FLAG uint = 8
const BLOB_FLAG uint = 16
const UNSIGNED_FLAG uint = 32
const ZEROFILL_FLAG uint = 64
const BINARY_FLAG uint = 128

const ENUM_FLAG uint = 256
const AUTO_INCREMENT_FLAG uint = 512
const TIMESTAMP_FLAG uint = 1024
const SET_FLAG uint = 2048
const NO_DEFAULT_VALUE_FLAG uint = 4096
const ON_UPDATE_NOW_FLAG uint = 8192
const NUM_FLAG uint = 32768
const PART_KEY_FLAG uint = 16384
const GROUP_FLAG uint = 32768
const UNIQUE_FLAG uint = 65536
const BINCMP_FLAG uint = 131072
const GET_FIXED_FIELDS_FLAG uint = 1 << 18
const FIELD_IN_PART_FUNC_FLAG uint = 1 << 19

const FIELD_IN_ADD_INDEX uint = 1 << 20
const FIELD_IS_RENAMED uint = 1 << 21
const FIELD_FLAGS_STORAGE_MEDIA uint = 22
const FIELD_FLAGS_STORAGE_MEDIA_MASK uint = 3 << FIELD_FLAGS_STORAGE_MEDIA
const FIELD_FLAGS_COLUMN_FORMAT uint = 24
const FIELD_FLAGS_COLUMN_FORMAT_MASK uint = 3 << FIELD_FLAGS_COLUMN_FORMAT
const FIELD_IS_DROPPED uint = 1 << 26

const MAX_TINYINT_WIDTH uint = 3
const MAX_SMALLINT_WIDTH uint = 5
const MAX_MEDIUMINT_WIDTH uint = 8
const MAX_INT_WIDTH uint = 10
const MAX_BIGINT_WIDTH uint = 20
const MAX_CHAR_WIDTH uint = 255
const MAX_BLOB_WIDTH uint = 16777216

const packet_error uint = 0

const MYSQL_SHUTDOWN_KILLABLE_CONNECT uint = 1 << 0
const MYSQL_SHUTDOWN_KILLABLE_TRANS uint = 1 << 1
const MYSQL_SHUTDOWN_KILLABLE_LOCK_TABLE uint = 1 << 2
const MYSQL_SHUTDOWN_KILLABLE_UPDATE uint = 1 << 3

const SHUTDOWN_DEFAULT uint = 0
const SHUTDOWN_WAIT_CONNECTIONS uint = MYSQL_SHUTDOWN_KILLABLE_CONNECT
const SHUTDOWN_WAIT_TRANSACTIONS uint = MYSQL_SHUTDOWN_KILLABLE_TRANS
const SHUTDOWN_WAIT_UPDATES uint = MYSQL_SHUTDOWN_KILLABLE_UPDATE
const SHUTDOWN_WAIT_ALL_BUFFERS uint = MYSQL_SHUTDOWN_KILLABLE_UPDATE << 1
const SHUTDOWN_WAIT_CRITICAL_BUFFERS uint = (MYSQL_SHUTDOWN_KILLABLE_UPDATE << 1) + 1
const KILL_QUERY uint = 254
const KILL_CONNECTION uint = 255

const STRING_RESULT uint = 0
const REAL_RESULT uint = 1
const INT_RESULT uint = 2
const ROW_RESULT uint = 3
const DECIMAL_RESULT uint = 4

const NET_HEADER_SIZE uint = 4
const COMP_HEADER_SIZE uint = 3

const NULL_LENGTH int = ^0
const MYSQL_STMT_HEADER uint = 4
const MYSQL_LONG_DATA_HEADER uint = 6

const NOT_FIXED_DEC uint = 31
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package clientbuilder

import (
	"k8s.io/client-go/discovery"
	clientset "k8s.io/client-go/kubernetes"
	restclient "k8s.io/client-go/rest"
	"k8s.io/klog/v2"
)

// ControllerClientBuilder allows you to get clients and configs for controllers
// Please note a copy also exists in staging/src/k8s.io/cloud-provider/cloud.go
// TODO: Extract this into a separate controller utilities repo (issues/68947)
type ControllerClientBuilder interface {
	Config(name string) (*restclient.Config, error)
	ConfigOrDie(name string) *restclient.Config
	Client(name string) (clientset.Interface, error)
	ClientOrDie(name string) clientset.Interface
	DiscoveryClient(name string) (discovery.DiscoveryInterface, error)
	DiscoveryClientOrDie(name string) discovery.DiscoveryInterface
}

// SimpleControllerClientBuilder returns a fixed client with different user agents
type SimpleControllerClientBuilder struct {
	// ClientConfig is a skeleton config to clone and use as the basis for each controller client
	ClientConfig *restclient.Config
}

// Config returns a client config for a fixed client
func (b SimpleControllerClientBuilder) Config(name string) (*restclient.Config, error) {
	clientConfig := *b.ClientConfig
	return restclient.AddUserAgent(&clientConfig, name), nil
}

// ConfigOrDie returns a client config if no error from previous config func.
// If it gets an error getting the client, it will log the error and kill the process it's running in.
func (b SimpleControllerClientBuilder) ConfigOrDie(name string) *restclient.Config {
	clientConfig, err := b.Config(name)
	if err != nil {
		klog.Fatal(err)
	}
	return clientConfig
}

// Client returns a clientset.Interface built from the ClientBuilder
func (b SimpleControllerClientBuilder) Client(name string) (clientset.Interface, error) {
	clientConfig, err := b.Config(name)
	if err != nil {
		return nil, err
	}
	return clientset.NewForConfig(clientConfig)
}

// ClientOrDie returns a clientset.interface built from the ClientBuilder with no error.
// If it gets an error getting the client, it will log the error and kill the process it's running in.
func (b SimpleControllerClientBuilder) ClientOrDie(name string) clientset.Interface {
	client, err := b.Client(name)
	if err != nil {
		klog.Fatal(err)
	}
	return client
}

// DiscoveryClientOrDie returns a discovery.DiscoveryInterface built from the ClientBuilder
// Discovery is special because it will artificially pump the burst quite high to handle the many discovery requests.
func (b SimpleControllerClientBuilder) DiscoveryClient(name string) (discovery.DiscoveryInterface, error) {
	clientConfig, err := b.Config(name)
	if err != nil {
		return nil, err
	}
	// Discovery makes a lot of requests infrequently.  This allows the burst to succeed and refill to happen
	// in just a few seconds.
	clientConfig.Burst = 200
	clientConfig.QPS = 20
	return clientset.NewForConfig(clientConfig)
}

// DiscoveryClientOrDie returns a discovery.DiscoveryInterface built from the ClientBuilder with no error.
// Discovery is special because it will artificially pump the burst quite high to handle the many discovery requests.
// If it gets an error getting the client, it will log the error and kill the process it's running in.
func (b SimpleControllerClientBuilder) DiscoveryClientOrDie(name string) discovery.DiscoveryInterface {
	client, err := b.DiscoveryClient(name)
	if err != nil {
		klog.Fatal(err)
	}
	return client
}
<commit_msg>Fix a typo in comment<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package clientbuilder

import (
	"k8s.io/client-go/discovery"
	clientset "k8s.io/client-go/kubernetes"
	restclient "k8s.io/client-go/rest"
	"k8s.io/klog/v2"
)

// ControllerClientBuilder allows you to get clients and configs for controllers
// Please note a copy also exists in staging/src/k8s.io/cloud-provider/cloud.go
// TODO: Extract this into a separate controller utilities repo (issues/68947)
type ControllerClientBuilder interface {
	Config(name string) (*restclient.Config, error)
	ConfigOrDie(name string) *restclient.Config
	Client(name string) (clientset.Interface, error)
	ClientOrDie(name string) clientset.Interface
	DiscoveryClient(name string) (discovery.DiscoveryInterface, error)
	DiscoveryClientOrDie(name string) discovery.DiscoveryInterface
}

// SimpleControllerClientBuilder returns a fixed client with different user agents
type SimpleControllerClientBuilder struct {
	// ClientConfig is a skeleton config to clone and use as the basis for each controller client
	ClientConfig *restclient.Config
}

// Config returns a client config for a fixed client
func (b SimpleControllerClientBuilder) Config(name string) (*restclient.Config, error) {
	clientConfig := *b.ClientConfig
	return restclient.AddUserAgent(&clientConfig, name), nil
}

// ConfigOrDie returns a client config if no error from previous config func.
// If it gets an error getting the client, it will log the error and kill the process it's running in.
func (b SimpleControllerClientBuilder) ConfigOrDie(name string) *restclient.Config {
	clientConfig, err := b.Config(name)
	if err != nil {
		klog.Fatal(err)
	}
	return clientConfig
}

// Client returns a clientset.Interface built from the ClientBuilder
func (b SimpleControllerClientBuilder) Client(name string) (clientset.Interface, error) {
	clientConfig, err := b.Config(name)
	if err != nil {
		return nil, err
	}
	return clientset.NewForConfig(clientConfig)
}

// ClientOrDie returns a clientset.interface built from the ClientBuilder with no error.
// If it gets an error getting the client, it will log the error and kill the process it's running in.
func (b SimpleControllerClientBuilder) ClientOrDie(name string) clientset.Interface {
	client, err := b.Client(name)
	if err != nil {
		klog.Fatal(err)
	}
	return client
}

// DiscoveryClient returns a discovery.DiscoveryInterface built from the ClientBuilder
// Discovery is special because it will artificially pump the burst quite high to handle the many discovery requests.
func (b SimpleControllerClientBuilder) DiscoveryClient(name string) (discovery.DiscoveryInterface, error) {
	clientConfig, err := b.Config(name)
	if err != nil {
		return nil, err
	}
	// Discovery makes a lot of requests infrequently.  This allows the burst to succeed and refill to happen
	// in just a few seconds.
	clientConfig.Burst = 200
	clientConfig.QPS = 20
	return clientset.NewForConfig(clientConfig)
}

// DiscoveryClientOrDie returns a discovery.DiscoveryInterface built from the ClientBuilder with no error.
// Discovery is special because it will artificially pump the burst quite high to handle the many discovery requests.
// If it gets an error getting the client, it will log the error and kill the process it's running in.
func (b SimpleControllerClientBuilder) DiscoveryClientOrDie(name string) discovery.DiscoveryInterface {
	client, err := b.DiscoveryClient(name)
	if err != nil {
		klog.Fatal(err)
	}
	return client
}
<|endoftext|>


<commit_before>package signal

import (
	"context"
	"time"
)

type ActivityUpdater interface {
	Update()
}

type ActivityTimer struct {
	updated chan bool
	timeout chan time.Duration
	ctx     context.Context
	cancel  context.CancelFunc
}

func (t *ActivityTimer) Update() {
	select {
	case t.updated <- true:
	default:
	}
}

func (t *ActivityTimer) SetTimeout(timeout time.Duration) {
	t.timeout <- timeout
}

func (t *ActivityTimer) run() {
	ticker := time.NewTicker(<-t.timeout)
	defer func() {
		ticker.Stop()
	}()

	for {
		select {
		case <-ticker.C:
		case <-t.ctx.Done():
			return
		case timeout := <-t.timeout:
			ticker.Stop()
			ticker = time.NewTicker(timeout)
		}

		select {
		case <-t.updated:
		// Updated keep waiting.
		default:
			t.cancel()
			return
		}
	}
}

func CancelAfterInactivity(ctx context.Context, cancel context.CancelFunc, timeout time.Duration) *ActivityTimer {
	timer := &ActivityTimer{
		ctx:     ctx,
		cancel:  cancel,
		timeout: make(chan time.Duration, 1),
		updated: make(chan bool, 1),
	}
	timer.timeout <- timeout
	go timer.run()
	return timer
}
<commit_msg>cancel immediately when timeout = 0<commit_after>package signal

import (
	"context"
	"time"
)

type ActivityUpdater interface {
	Update()
}

type ActivityTimer struct {
	updated chan bool
	timeout chan time.Duration
	ctx     context.Context
	cancel  context.CancelFunc
}

func (t *ActivityTimer) Update() {
	select {
	case t.updated <- true:
	default:
	}
}

func (t *ActivityTimer) SetTimeout(timeout time.Duration) {
	t.timeout <- timeout
}

func (t *ActivityTimer) run() {
	ticker := time.NewTicker(<-t.timeout)
	defer func() {
		ticker.Stop()
	}()

	for {
		select {
		case <-ticker.C:
		case <-t.ctx.Done():
			return
		case timeout := <-t.timeout:
			if timeout == 0 {
				t.cancel()
				return
			}

			ticker.Stop()
			ticker = time.NewTicker(timeout)
		}

		select {
		case <-t.updated:
		// Updated keep waiting.
		default:
			t.cancel()
			return
		}
	}
}

func CancelAfterInactivity(ctx context.Context, cancel context.CancelFunc, timeout time.Duration) *ActivityTimer {
	timer := &ActivityTimer{
		ctx:     ctx,
		cancel:  cancel,
		timeout: make(chan time.Duration, 1),
		updated: make(chan bool, 1),
	}
	timer.timeout <- timeout
	go timer.run()
	return timer
}
<|endoftext|>


<commit_before>package community

import (
	"time"

	"github.com/ion-channel/ionic/risk"
)

const (
	// GetRepoEndpoint is a string representation of the current endpoint for getting repo
	GetRepoEndpoint = `v1/repo/getRepo`
	// GetReposInCommonEndpoint is a string representation of the current endpoint for getting repos
	GetReposInCommonEndpoint = `/v1/repo/getReposInCommon`
	// GetReposForActorEndpoint is a string representation of the current endpoint for getting repos
	GetReposForActorEndpoint = `v1/repo/getReposForActor`
	// SearchRepoEndpoint is a string representation of the current endpoint for searching repo
	SearchRepoEndpoint = `v1/repo/search`
)

// Repo is a representation of a github repo and corresponding metrics about
// that repo pulled from github
type Repo struct {
	ID                              string               `json:"id" xml:"id"`
	Name                            string               `json:"name" xml:"name"`
	URL                             string               `json:"url" xml:"url"`
	Committers                      int                  `json:"committers" xml:"committers"`
	TotalActors                     int                  `json:"total_actors,omitempty" xml:"total_actors,omitempty"`
	Confidence                      float64              `json:"confidence" xml:"confidence"`
	OldNames                        []string             `json:"old_names" xml:"old_names"`
	DefaultBranch                   string               `json:"default_branch,omitempty" xml:"default_branch,omitempty"`
	MasterBranch                    string               `json:"master_branch,omitempty" xml:"master_branch,omitempty"`
	Stars                           int                  `json:"stars" xml:"stars"`
	CommittedAt                     time.Time            `json:"committed_at" xml:"committed_at"`
	UpdatedAt                       time.Time            `json:"updated_at" xml:"updated_at"`
	CommittersMonthlyCount          *[]MonthlyCount      `json:"committers_monthly_count" xml:"committers_monthly_count"`
	ReleasesTotalCount              *int                 `json:"releases_total_count" xml:"releases_total_count"`
	ReleasesMonthlyCount            *[]MonthlyCount      `json:"releases_monthly_count" xml:"releases_monthly_count"`
	ReleasesLastAt                  *time.Time           `json:"releases_last_at" xml:"releases_last_at"`
	PullRequestsTotalCount          *int                 `json:"pull_requests_total_count" xml:"pull_requests_total_count"`
	PullRequestsLastAt              *time.Time           `json:"pull_requests_last_at" xml:"pull_requests_last_at"`
	PullRequestsMonthlyCount        *[]MonthlyCount      `json:"pull_requests_monthly_count" xml:"pull_requests_monthly_count"`
	IssuesLastAt                    *time.Time           `json:"issues_last_at" xml:"issues_last_at"`
	IssuesOpenMonthlyCount          *[]MonthlyCount      `json:"issues_open_monthly_count" xml:"issues_open_monthly_count"`
	IssuesClosedMonthlyCount        *[]MonthlyCount      `json:"issues_closed_monthly_count" xml:"issues_closed_monthly_count"`
	IssuesClosedMttrMonthly         *[]MonthlyMttr       `json:"issues_closed_mttr_monthly" xml:"issues_closed_mttr_monthly"`
	IssuesClosedMttr                *int                 `json:"issues_closed_mttr" xml:"issues_closed_mttr"`
	CommitsTotalCount               *int                 `json:"commits_total_count" xml:"commits_total_count"`
	CommitsMonthlyCount             *[]MonthlyCount      `json:"commits_monthly_count" xml:"commits_monthly_count"`
	ActorsMonthlyCount              *[]MonthlyCount      `json:"actors_monthly_count" xml:"actors_monthly_count"`
	ActionsTotalCount               *int                 `json:"actions_total_count" xml:"actions_total_count"`
	ActionsLastAt                   *time.Time           `json:"actions_last_at" xml:"actions_last_at"`
	ActionsFirstAt                  *time.Time           `json:"actions_first_at" xml:"actions_first_at"`
	ActionsMonthlyCount             *[]MonthlyCount      `json:"actions_monthly_count" xml:"actions_monthly_count"`
	ContributingActorsTotalCount    *int                 `json:"contributing_actors_total_count" xml:"contributing_actors_total_count"`
	ContributingActorsMonthlyCount  *[]MonthlyCount      `json:"contributing_actors_monthly_count" xml:"contributing_actors_monthly_count"`
	ContributingActionsTotalCount   *int                 `json:"contributing_actions_total_count" xml:"contributing_actions_total_count"`
	ContributingActionsLastAt       *time.Time           `json:"contributing_actions_last_at" xml:"contributing_actions_last_at"`
	ContributingActionsMonthlyCount *[]MonthlyCount      `json:"contributing_actions_monthly_count" xml:"contributing_actions_monthly_count"`
	NewActorsMonthlyCount           *[]MonthlyCount      `json:"new_actors_monthly_count" xml:"new_actors_monthly_count"`
	MedianWorkingHour               *int                 `json:"median_working_hour" xml:"median_working_hour"`
	EOLRearFailingDaysCount         *int                 `json:"eol_rear_failing_months_count" xml:"eol_rear_failing_months_count"`
	MetricPoints                    *[]risk.MetricPoints `json:"metric_points" xml:"metric_points"`
	CreatedAt                       *time.Time           `json:"created_at" xml:"created_at"`
}

// MonthlyCount defines the data needed for month and count
type MonthlyCount struct {
	Month string `json:"month" xml:"month"`
	Count int    `json:"count" xml:"count"`
}

// MonthlyMttr defines the data needed for month and mttr
type MonthlyMttr struct {
	Month string  `json:"month" xml:"month"`
	Mttr  float32 `json:"mttr" xml:"mttr"`
}
<commit_msg>Remove points from repos struct<commit_after>package community

import (
	"time"
)

const (
	// GetRepoEndpoint is a string representation of the current endpoint for getting repo
	GetRepoEndpoint = `v1/repo/getRepo`
	// GetReposInCommonEndpoint is a string representation of the current endpoint for getting repos
	GetReposInCommonEndpoint = `/v1/repo/getReposInCommon`
	// GetReposForActorEndpoint is a string representation of the current endpoint for getting repos
	GetReposForActorEndpoint = `v1/repo/getReposForActor`
	// SearchRepoEndpoint is a string representation of the current endpoint for searching repo
	SearchRepoEndpoint = `v1/repo/search`
)

// Repo is a representation of a github repo and corresponding metrics about
// that repo pulled from github
type Repo struct {
	ID                              string          `json:"id" xml:"id"`
	Name                            string          `json:"name" xml:"name"`
	URL                             string          `json:"url" xml:"url"`
	Committers                      int             `json:"committers" xml:"committers"`
	TotalActors                     int             `json:"total_actors,omitempty" xml:"total_actors,omitempty"`
	Confidence                      float64         `json:"confidence" xml:"confidence"`
	OldNames                        []string        `json:"old_names" xml:"old_names"`
	DefaultBranch                   string          `json:"default_branch,omitempty" xml:"default_branch,omitempty"`
	MasterBranch                    string          `json:"master_branch,omitempty" xml:"master_branch,omitempty"`
	Stars                           int             `json:"stars" xml:"stars"`
	CommittedAt                     time.Time       `json:"committed_at" xml:"committed_at"`
	UpdatedAt                       time.Time       `json:"updated_at" xml:"updated_at"`
	CommittersMonthlyCount          *[]MonthlyCount `json:"committers_monthly_count" xml:"committers_monthly_count"`
	ReleasesTotalCount              *int            `json:"releases_total_count" xml:"releases_total_count"`
	ReleasesMonthlyCount            *[]MonthlyCount `json:"releases_monthly_count" xml:"releases_monthly_count"`
	ReleasesLastAt                  *time.Time      `json:"releases_last_at" xml:"releases_last_at"`
	PullRequestsTotalCount          *int            `json:"pull_requests_total_count" xml:"pull_requests_total_count"`
	PullRequestsLastAt              *time.Time      `json:"pull_requests_last_at" xml:"pull_requests_last_at"`
	PullRequestsMonthlyCount        *[]MonthlyCount `json:"pull_requests_monthly_count" xml:"pull_requests_monthly_count"`
	IssuesLastAt                    *time.Time      `json:"issues_last_at" xml:"issues_last_at"`
	IssuesOpenMonthlyCount          *[]MonthlyCount `json:"issues_open_monthly_count" xml:"issues_open_monthly_count"`
	IssuesClosedMonthlyCount        *[]MonthlyCount `json:"issues_closed_monthly_count" xml:"issues_closed_monthly_count"`
	IssuesClosedMttrMonthly         *[]MonthlyMttr  `json:"issues_closed_mttr_monthly" xml:"issues_closed_mttr_monthly"`
	IssuesClosedMttr                *int            `json:"issues_closed_mttr" xml:"issues_closed_mttr"`
	CommitsTotalCount               *int            `json:"commits_total_count" xml:"commits_total_count"`
	CommitsMonthlyCount             *[]MonthlyCount `json:"commits_monthly_count" xml:"commits_monthly_count"`
	ActorsMonthlyCount              *[]MonthlyCount `json:"actors_monthly_count" xml:"actors_monthly_count"`
	ActionsTotalCount               *int            `json:"actions_total_count" xml:"actions_total_count"`
	ActionsLastAt                   *time.Time      `json:"actions_last_at" xml:"actions_last_at"`
	ActionsFirstAt                  *time.Time      `json:"actions_first_at" xml:"actions_first_at"`
	ActionsMonthlyCount             *[]MonthlyCount `json:"actions_monthly_count" xml:"actions_monthly_count"`
	ContributingActorsTotalCount    *int            `json:"contributing_actors_total_count" xml:"contributing_actors_total_count"`
	ContributingActorsMonthlyCount  *[]MonthlyCount `json:"contributing_actors_monthly_count" xml:"contributing_actors_monthly_count"`
	ContributingActionsTotalCount   *int            `json:"contributing_actions_total_count" xml:"contributing_actions_total_count"`
	ContributingActionsLastAt       *time.Time      `json:"contributing_actions_last_at" xml:"contributing_actions_last_at"`
	ContributingActionsMonthlyCount *[]MonthlyCount `json:"contributing_actions_monthly_count" xml:"contributing_actions_monthly_count"`
	NewActorsMonthlyCount           *[]MonthlyCount `json:"new_actors_monthly_count" xml:"new_actors_monthly_count"`
	MedianWorkingHour               *int            `json:"median_working_hour" xml:"median_working_hour"`
	EOLRearFailingDaysCount         *int            `json:"eol_rear_failing_months_count" xml:"eol_rear_failing_months_count"`
	CreatedAt                       *time.Time      `json:"created_at" xml:"created_at"`
}

// MonthlyCount defines the data needed for month and count
type MonthlyCount struct {
	Month string `json:"month" xml:"month"`
	Count int    `json:"count" xml:"count"`
}

// MonthlyMttr defines the data needed for month and mttr
type MonthlyMttr struct {
	Month string  `json:"month" xml:"month"`
	Mttr  float32 `json:"mttr" xml:"mttr"`
}
<|endoftext|>


<commit_before>package common

import (
	"testing"
)

func testAccessConfig() *AccessConfig {
	return &AccessConfig{}
}

func TestAccessConfigPrepare_Region(t *testing.T) {
	c := testAccessConfig()

	c.RawRegion = "us-east-12"
	err := c.ValidateOSCRegion(c.RawRegion)
	if err == nil {
		t.Fatalf("should have region validation err: %s", c.RawRegion)
	}

	c.RawRegion = "us-east-1"
	err = c.ValidateOSCRegion(c.RawRegion)
	if err == nil {
		t.Fatalf("should have region validation err: %s", c.RawRegion)
	}

	c.RawRegion = "us-west-1"
	err = c.ValidateOSCRegion(c.RawRegion)
	if err != nil {
		t.Fatalf("shouldn't have region validation err: %s", c.RawRegion)
	}

	c.RawRegion = "custom"
	err = c.ValidateOSCRegion(c.RawRegion)
	if err == nil {
		t.Fatalf("should have region validation err: %s", c.RawRegion)
	}

	c.RawRegion = "custom"
	c.SkipValidation = true
	// testing whole prepare func here; this is checking that validation is
	// skipped, so we don't need a mock connection
	if err := c.Prepare(nil); err != nil {
		t.Fatalf("shouldn't have err: %s", err)
	}

	c.SkipValidation = false
	c.RawRegion = ""
	if err := c.Prepare(nil); err != nil {
		t.Fatalf("shouldn't have err: %s", err)
	}
}
<commit_msg>chore: fix test<commit_after>package common

import (
	"testing"
)

func testAccessConfig() *AccessConfig {
	return &AccessConfig{}
}

func TestAccessConfigPrepare_Region(t *testing.T) {
	c := testAccessConfig()

	c.RawRegion = "us-east-12"
	err := c.ValidateOSCRegion(c.RawRegion)
	if err == nil {
		t.Fatalf("should have region validation err: %s", c.RawRegion)
	}

	c.RawRegion = "us-east-1"
	err = c.ValidateOSCRegion(c.RawRegion)
	if err == nil {
		t.Fatalf("should have region validation err: %s", c.RawRegion)
	}

	c.RawRegion = "custom"
	err = c.ValidateOSCRegion(c.RawRegion)
	if err == nil {
		t.Fatalf("should have region validation err: %s", c.RawRegion)
	}

	c.RawRegion = "custom"
	c.SkipValidation = true
	// testing whole prepare func here; this is checking that validation is
	// skipped, so we don't need a mock connection
	if err := c.Prepare(nil); err != nil {
		t.Fatalf("shouldn't have err: %s", err)
	}

	c.SkipValidation = false
	c.RawRegion = ""
	if err := c.Prepare(nil); err != nil {
		t.Fatalf("shouldn't have err: %s", err)
	}
}
<|endoftext|>


<commit_before>package availability_test

import (
	"fmt"
	"time"

	"availability/helpers"

	vegeta "github.com/tsenart/vegeta/lib"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

const (
	availabilityTestRate             = 10
	availabilityTestMaxDuration      = 2 * time.Hour
	availabilityTestMaxLatency       = 300 * time.Millisecond
	availabilityTestMaxErrors        = 0
	availabilitySuccessRateThreshold = 99.9
	minimumTestDuration              = 15 * time.Second
	maximumErrorRate                 = 0.5

	vegetaRunForever = 0 * time.Second
	vegetaKeepAlive  = true
)

func loadTest(appUri string, endpoint string, rate uint64) (*vegeta.Attacker, <-chan *vegeta.Result) {
	targeter := vegeta.NewStaticTargeter(vegeta.Target{
		Method: "GET",
		URL:    appUri,
	})

	attacker := vegeta.NewAttacker(vegeta.KeepAlive(vegetaKeepAlive))
	res := attacker.Attack(targeter, rate, vegetaRunForever)

	return attacker, res
}

func errorRateThreshold(metrics vegeta.Metrics, minimumTestDuration time.Duration, maximumErrorRate float64) bool {
	// metrics.Close() does trigger the computation of metrics, but does not stop any process
	metrics.Close()

	fmt.Printf(
		" - Duration: %s, Requests: %d, Non 200 Requests: %d, Success Rate: %.2f%%\n",
		metrics.Duration,
		metrics.Requests,
		metrics.Requests-uint64(metrics.StatusCodes["200"]),
		metrics.Success*100,
	)

	if metrics.Duration > minimumTestDuration {
		if metrics.Success < (1 - maximumErrorRate) {
			return true
		}
	}
	return false
}

var _ = Describe("Availability test", func() {

	var metrics vegeta.Metrics
	var stopAttackCriteria func() bool

	Context("when runs (until the deployment is finished or error rate > 50%)", func() {
		var attacker *vegeta.Attacker
		var resultChannel <-chan *vegeta.Result

		BeforeEach(func() {
			stopAttackCriteria = func() bool {
				if helpers.DeploymentHasFinishedInConcourse() {
					return true
				}
				if errorRateThreshold(metrics, minimumTestDuration, maximumErrorRate) {
					return true
				}
				return false
			}
		})

		It(fmt.Sprintf("does not get request success rate less than %.2f%%", availabilitySuccessRateThreshold), func() {
			appUri := "https://healthcheck." + helpers.GetAppsDomainZoneName() + "/?availability-test=" + helpers.GetResourceVersion()

			attacker, resultChannel = loadTest(appUri, "/", availabilityTestRate)
			defer attacker.Stop()

			go func() {
				for res := range resultChannel {
					metrics.Add(res)
				}
			}()

			Eventually(
				stopAttackCriteria,
				availabilityTestMaxDuration,
				5*time.Second,
			).Should(BeTrue(), "Deployment did not finish in the expected time")

			attacker.Stop()
			metrics.Close()

			Expect(metrics.Success*100).To(
				BeNumerically(">=", availabilitySuccessRateThreshold),
				"Errors detected during the attack: %v", metrics.Errors,
			)
		})
	})

})
<commit_msg>Protect metrics variable with a lock.<commit_after>package availability_test

import (
	"fmt"
	"sync"
	"time"

	"availability/helpers"

	vegeta "github.com/tsenart/vegeta/lib"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

const (
	availabilityTestRate             = 10
	availabilityTestMaxDuration      = 2 * time.Hour
	availabilityTestMaxLatency       = 300 * time.Millisecond
	availabilityTestMaxErrors        = 0
	availabilitySuccessRateThreshold = 99.9
	minimumTestDuration              = 15 * time.Second
	maximumErrorRate                 = 0.5

	vegetaRunForever = 0 * time.Second
	vegetaKeepAlive  = true
)

func loadTest(appUri string, endpoint string, rate uint64) (*vegeta.Attacker, <-chan *vegeta.Result) {
	targeter := vegeta.NewStaticTargeter(vegeta.Target{
		Method: "GET",
		URL:    appUri,
	})

	attacker := vegeta.NewAttacker(vegeta.KeepAlive(vegetaKeepAlive))
	res := attacker.Attack(targeter, rate, vegetaRunForever)

	return attacker, res
}

func errorRateThreshold(metrics vegeta.Metrics, minimumTestDuration time.Duration, maximumErrorRate float64) bool {
	// metrics.Close() does trigger the computation of metrics, but does not stop any process
	metrics.Close()

	fmt.Printf(
		" - Duration: %s, Requests: %d, Non 200 Requests: %d, Success Rate: %.2f%%\n",
		metrics.Duration,
		metrics.Requests,
		metrics.Requests-uint64(metrics.StatusCodes["200"]),
		metrics.Success*100,
	)

	if metrics.Duration > minimumTestDuration {
		if metrics.Success < (1 - maximumErrorRate) {
			return true
		}
	}
	return false
}

var _ = Describe("Availability test", func() {

	var metrics vegeta.Metrics
	var metricsLock sync.Mutex
	var stopAttackCriteria func() bool

	Context("when runs (until the deployment is finished or error rate > 50%)", func() {
		var attacker *vegeta.Attacker
		var resultChannel <-chan *vegeta.Result

		BeforeEach(func() {
			stopAttackCriteria = func() bool {
				if helpers.DeploymentHasFinishedInConcourse() {
					return true
				}
				metricsLock.Lock()
				defer metricsLock.Unlock()
				if errorRateThreshold(metrics, minimumTestDuration, maximumErrorRate) {
					return true
				}
				return false
			}
		})

		It(fmt.Sprintf("does not get request success rate less than %.2f%%", availabilitySuccessRateThreshold), func() {
			appUri := "https://healthcheck." + helpers.GetAppsDomainZoneName() + "/?availability-test=" + helpers.GetResourceVersion()

			attacker, resultChannel = loadTest(appUri, "/", availabilityTestRate)
			defer attacker.Stop()

			go func() {
				for res := range resultChannel {
					metricsLock.Lock()
					metrics.Add(res)
					metricsLock.Unlock()
				}
			}()

			Eventually(
				stopAttackCriteria,
				availabilityTestMaxDuration,
				5*time.Second,
			).Should(BeTrue(), "Deployment did not finish in the expected time")

			attacker.Stop()
			metrics.Close()

			Expect(metrics.Success*100).To(
				BeNumerically(">=", availabilitySuccessRateThreshold),
				"Errors detected during the attack: %v", metrics.Errors,
			)
		})
	})

})
<|endoftext|>


<commit_before>package citrixadc

import (
	"github.com/chiradeep/go-nitro/config/responder"

	"github.com/chiradeep/go-nitro/netscaler"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"

	"fmt"
	"log"
)

func resourceCitrixAdcResponderaction() *schema.Resource {
	return &schema.Resource{
		SchemaVersion: 1,
		Create:        createResponderactionFunc,
		Read:          readResponderactionFunc,
		Update:        updateResponderactionFunc,
		Delete:        deleteResponderactionFunc,
		CustomizeDiff: customizeResponderactionDiff,
		Schema: map[string]*schema.Schema{
			"bypasssafetycheck": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"comment": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"htmlpage": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},
			"newname": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"reasonphrase": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"responsestatuscode": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				Computed: true,
			},
			"target": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"type": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},
		},
	}
}

func createResponderactionFunc(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG]  citrixadc-provider: In createResponderactionFunc")
	client := meta.(*NetScalerNitroClient).client
	var responderactionName string
	if v, ok := d.GetOk("name"); ok {
		responderactionName = v.(string)
	} else {
		responderactionName = resource.PrefixedUniqueId("tf-responderaction-")
		d.Set("name", responderactionName)
	}
	responderaction := responder.Responderaction{
		Bypasssafetycheck:  d.Get("bypasssafetycheck").(string),
		Comment:            d.Get("comment").(string),
		Htmlpage:           d.Get("htmlpage").(string),
		Name:               d.Get("name").(string),
		Newname:            d.Get("newname").(string),
		Reasonphrase:       d.Get("reasonphrase").(string),
		Responsestatuscode: d.Get("responsestatuscode").(int),
		Target:             d.Get("target").(string),
		Type:               d.Get("type").(string),
	}

	_, err := client.AddResource(netscaler.Responderaction.Type(), responderactionName, &responderaction)
	if err != nil {
		return err
	}

	d.SetId(responderactionName)

	err = readResponderactionFunc(d, meta)
	if err != nil {
		log.Printf("[ERROR] netscaler-provider: ?? we just created this responderaction but we can't read it ?? %s", responderactionName)
		return nil
	}
	return nil
}

func readResponderactionFunc(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG] citrixadc-provider:  In readResponderactionFunc")
	client := meta.(*NetScalerNitroClient).client
	responderactionName := d.Id()
	log.Printf("[DEBUG] citrixadc-provider: Reading responderaction state %s", responderactionName)
	data, err := client.FindResource(netscaler.Responderaction.Type(), responderactionName)
	if err != nil {
		log.Printf("[WARN] citrixadc-provider: Clearing responderaction state %s", responderactionName)
		d.SetId("")
		return nil
	}
	d.Set("name", data["name"])
	d.Set("bypasssafetycheck", data["bypasssafetycheck"])
	d.Set("comment", data["comment"])
	d.Set("htmlpage", data["htmlpage"])
	d.Set("name", data["name"])
	d.Set("newname", data["newname"])
	d.Set("reasonphrase", data["reasonphrase"])
	d.Set("responsestatuscode", data["responsestatuscode"])
	d.Set("target", data["target"])
	d.Set("type", data["type"])

	return nil

}

func updateResponderactionFunc(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG]  citrixadc-provider: In updateResponderactionFunc")
	client := meta.(*NetScalerNitroClient).client
	responderactionName := d.Get("name").(string)

	responderaction := responder.Responderaction{
		Name: d.Get("name").(string),
	}
	hasChange := false
	if d.HasChange("bypasssafetycheck") {
		log.Printf("[DEBUG]  citrixadc-provider: Bypasssafetycheck has changed for responderaction %s, starting update", responderactionName)
		responderaction.Bypasssafetycheck = d.Get("bypasssafetycheck").(string)
		hasChange = true
	}
	if d.HasChange("comment") {
		log.Printf("[DEBUG]  citrixadc-provider: Comment has changed for responderaction %s, starting update", responderactionName)
		responderaction.Comment = d.Get("comment").(string)
		hasChange = true
	}
	if d.HasChange("htmlpage") {
		log.Printf("[DEBUG]  citrixadc-provider: Htmlpage has changed for responderaction %s, starting update", responderactionName)
		responderaction.Htmlpage = d.Get("htmlpage").(string)
		hasChange = true
	}
	if d.HasChange("name") {
		log.Printf("[DEBUG]  citrixadc-provider: Name has changed for responderaction %s, starting update", responderactionName)
		responderaction.Name = d.Get("name").(string)
		hasChange = true
	}
	if d.HasChange("newname") {
		log.Printf("[DEBUG]  citrixadc-provider: Newname has changed for responderaction %s, starting update", responderactionName)
		responderaction.Newname = d.Get("newname").(string)
		hasChange = true
	}
	if d.HasChange("reasonphrase") {
		log.Printf("[DEBUG]  citrixadc-provider: Reasonphrase has changed for responderaction %s, starting update", responderactionName)
		responderaction.Reasonphrase = d.Get("reasonphrase").(string)
		hasChange = true
	}
	if d.HasChange("responsestatuscode") {
		log.Printf("[DEBUG]  citrixadc-provider: Responsestatuscode has changed for responderaction %s, starting update", responderactionName)
		responderaction.Responsestatuscode = d.Get("responsestatuscode").(int)
		hasChange = true
	}
	if d.HasChange("target") {
		log.Printf("[DEBUG]  citrixadc-provider: Target has changed for responderaction %s, starting update", responderactionName)
		responderaction.Target = d.Get("target").(string)
		hasChange = true
	}
	if d.HasChange("type") {
		log.Printf("[DEBUG]  citrixadc-provider: Type has changed for responderaction %s, starting update", responderactionName)
		responderaction.Type = d.Get("type").(string)
		hasChange = true
	}

	if hasChange {
		_, err := client.UpdateResource(netscaler.Responderaction.Type(), responderactionName, &responderaction)
		if err != nil {
			return fmt.Errorf("Error updating responderaction %s", responderactionName)
		}
	}
	return readResponderactionFunc(d, meta)
}

func deleteResponderactionFunc(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG]  citrixadc-provider: In deleteResponderactionFunc")
	client := meta.(*NetScalerNitroClient).client
	responderactionName := d.Id()
	err := client.DeleteResource(netscaler.Responderaction.Type(), responderactionName)
	if err != nil {
		return err
	}

	d.SetId("")

	return nil
}

func customizeResponderactionDiff(diff *schema.ResourceDiff, meta interface{}) error {
	log.Printf("[DEBUG] citrixadc-provider:  In customizeDiff")
	o := diff.GetChangedKeysPrefix("")

	// Check if target and bypasssafetycheck is in changed keys
	targetDefined := false
	bypasssafetycheckDefined := false

	for _, element := range o {

		if element == "target" {
			targetDefined = true
		}

		if element == "bypasssafetycheck" {
			bypasssafetycheckDefined = true
		}
	}

	// Clear bypasssafetycheck when present without target
	if bypasssafetycheckDefined && !targetDefined {
		diff.Clear("bypasssafetycheck")
	}

	return nil
}
<commit_msg>Implement import operation for resource responderaction<commit_after>package citrixadc

import (
	"github.com/chiradeep/go-nitro/config/responder"

	"github.com/chiradeep/go-nitro/netscaler"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"

	"fmt"
	"log"
)

func resourceCitrixAdcResponderaction() *schema.Resource {
	return &schema.Resource{
		SchemaVersion: 1,
		Create:        createResponderactionFunc,
		Read:          readResponderactionFunc,
		Update:        updateResponderactionFunc,
		Delete:        deleteResponderactionFunc,
		CustomizeDiff: customizeResponderactionDiff,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},
		Schema: map[string]*schema.Schema{
			"bypasssafetycheck": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"comment": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"htmlpage": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},
			"newname": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"reasonphrase": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"responsestatuscode": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				Computed: true,
			},
			"target": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"type": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},
		},
	}
}

func createResponderactionFunc(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG]  citrixadc-provider: In createResponderactionFunc")
	client := meta.(*NetScalerNitroClient).client
	var responderactionName string
	if v, ok := d.GetOk("name"); ok {
		responderactionName = v.(string)
	} else {
		responderactionName = resource.PrefixedUniqueId("tf-responderaction-")
		d.Set("name", responderactionName)
	}
	responderaction := responder.Responderaction{
		Bypasssafetycheck:  d.Get("bypasssafetycheck").(string),
		Comment:            d.Get("comment").(string),
		Htmlpage:           d.Get("htmlpage").(string),
		Name:               d.Get("name").(string),
		Newname:            d.Get("newname").(string),
		Reasonphrase:       d.Get("reasonphrase").(string),
		Responsestatuscode: d.Get("responsestatuscode").(int),
		Target:             d.Get("target").(string),
		Type:               d.Get("type").(string),
	}

	_, err := client.AddResource(netscaler.Responderaction.Type(), responderactionName, &responderaction)
	if err != nil {
		return err
	}

	d.SetId(responderactionName)

	err = readResponderactionFunc(d, meta)
	if err != nil {
		log.Printf("[ERROR] netscaler-provider: ?? we just created this responderaction but we can't read it ?? %s", responderactionName)
		return nil
	}
	return nil
}

func readResponderactionFunc(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG] citrixadc-provider:  In readResponderactionFunc")
	client := meta.(*NetScalerNitroClient).client
	responderactionName := d.Id()
	log.Printf("[DEBUG] citrixadc-provider: Reading responderaction state %s", responderactionName)
	data, err := client.FindResource(netscaler.Responderaction.Type(), responderactionName)
	if err != nil {
		log.Printf("[WARN] citrixadc-provider: Clearing responderaction state %s", responderactionName)
		d.SetId("")
		return nil
	}
	d.Set("name", data["name"])
	d.Set("bypasssafetycheck", data["bypasssafetycheck"])
	d.Set("comment", data["comment"])
	d.Set("htmlpage", data["htmlpage"])
	d.Set("name", data["name"])
	d.Set("newname", data["newname"])
	d.Set("reasonphrase", data["reasonphrase"])
	d.Set("responsestatuscode", data["responsestatuscode"])
	d.Set("target", data["target"])
	d.Set("type", data["type"])

	return nil

}

func updateResponderactionFunc(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG]  citrixadc-provider: In updateResponderactionFunc")
	client := meta.(*NetScalerNitroClient).client
	responderactionName := d.Get("name").(string)

	responderaction := responder.Responderaction{
		Name: d.Get("name").(string),
	}
	hasChange := false
	if d.HasChange("bypasssafetycheck") {
		log.Printf("[DEBUG]  citrixadc-provider: Bypasssafetycheck has changed for responderaction %s, starting update", responderactionName)
		responderaction.Bypasssafetycheck = d.Get("bypasssafetycheck").(string)
		hasChange = true
	}
	if d.HasChange("comment") {
		log.Printf("[DEBUG]  citrixadc-provider: Comment has changed for responderaction %s, starting update", responderactionName)
		responderaction.Comment = d.Get("comment").(string)
		hasChange = true
	}
	if d.HasChange("htmlpage") {
		log.Printf("[DEBUG]  citrixadc-provider: Htmlpage has changed for responderaction %s, starting update", responderactionName)
		responderaction.Htmlpage = d.Get("htmlpage").(string)
		hasChange = true
	}
	if d.HasChange("name") {
		log.Printf("[DEBUG]  citrixadc-provider: Name has changed for responderaction %s, starting update", responderactionName)
		responderaction.Name = d.Get("name").(string)
		hasChange = true
	}
	if d.HasChange("newname") {
		log.Printf("[DEBUG]  citrixadc-provider: Newname has changed for responderaction %s, starting update", responderactionName)
		responderaction.Newname = d.Get("newname").(string)
		hasChange = true
	}
	if d.HasChange("reasonphrase") {
		log.Printf("[DEBUG]  citrixadc-provider: Reasonphrase has changed for responderaction %s, starting update", responderactionName)
		responderaction.Reasonphrase = d.Get("reasonphrase").(string)
		hasChange = true
	}
	if d.HasChange("responsestatuscode") {
		log.Printf("[DEBUG]  citrixadc-provider: Responsestatuscode has changed for responderaction %s, starting update", responderactionName)
		responderaction.Responsestatuscode = d.Get("responsestatuscode").(int)
		hasChange = true
	}
	if d.HasChange("target") {
		log.Printf("[DEBUG]  citrixadc-provider: Target has changed for responderaction %s, starting update", responderactionName)
		responderaction.Target = d.Get("target").(string)
		hasChange = true
	}
	if d.HasChange("type") {
		log.Printf("[DEBUG]  citrixadc-provider: Type has changed for responderaction %s, starting update", responderactionName)
		responderaction.Type = d.Get("type").(string)
		hasChange = true
	}

	if hasChange {
		_, err := client.UpdateResource(netscaler.Responderaction.Type(), responderactionName, &responderaction)
		if err != nil {
			return fmt.Errorf("Error updating responderaction %s", responderactionName)
		}
	}
	return readResponderactionFunc(d, meta)
}

func deleteResponderactionFunc(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG]  citrixadc-provider: In deleteResponderactionFunc")
	client := meta.(*NetScalerNitroClient).client
	responderactionName := d.Id()
	err := client.DeleteResource(netscaler.Responderaction.Type(), responderactionName)
	if err != nil {
		return err
	}

	d.SetId("")

	return nil
}

func customizeResponderactionDiff(diff *schema.ResourceDiff, meta interface{}) error {
	log.Printf("[DEBUG] citrixadc-provider:  In customizeDiff")
	o := diff.GetChangedKeysPrefix("")

	// Check if target and bypasssafetycheck is in changed keys
	targetDefined := false
	bypasssafetycheckDefined := false

	for _, element := range o {

		if element == "target" {
			targetDefined = true
		}

		if element == "bypasssafetycheck" {
			bypasssafetycheckDefined = true
		}
	}

	// Clear bypasssafetycheck when present without target
	if bypasssafetycheckDefined && !targetDefined {
		diff.Clear("bypasssafetycheck")
	}

	return nil
}
<|endoftext|>


<commit_before>// pgconfig.go provides methods for configuring a node corresponding to if it is
// running a 'master' or 'slave' instance of postgres.

package main

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"os"
	"regexp"
	"strings"
)

// configureHBAConf attempts to open the 'pg_hba.conf' file. Once open it will scan
// the file line by line looking for replication settings, and overwrite only those
// settings with the settings required for redundancy on Pagoda Box
func configureHBAConf() error {

	// get the role of the other node in the cluster that is running an instance
	// of postgresql
	self := Whoami()
	other, err := Whoisnot(self.CRole)
	if err != nil {
		log.Warn("[pg_config.configureHBAConf] Unable to find another!\n%s\n", err)
	}

	// open the pg_hba.conf
	file := conf.DataDir + "pg_hba.conf"
	f, err := os.Open(file)
	if err != nil {
		log.Error("[pg_config.configureHBAConf] Failed to open '%s'!\n%s\n", file, err)
		return err
	}

	defer f.Close()

	scanner := bufio.NewScanner(f)
	reFindConfigOption := regexp.MustCompile(`^\s*#?\s*(local|host)\s*(replication)`)
	entry := ""

	// scan the file line by line to build an 'entry' to be re-written back to the
	// file, skipping ('removing') any line that deals with redundancy.
	for scanner.Scan() {

		// stop scanning if a special prefix is encountered. This ensures there are
		// no duplicate Pagoda Box comment blocks
		if strings.HasPrefix(scanner.Text(), "#~") {
			break
		}

		// dont care about submatches, just if the string matches, 'skipping' any lines
		// that are custom configurations
		if reFindConfigOption.FindString(scanner.Text()) == "" {
			entry += fmt.Sprintf("%s\n", scanner.Text())
		}
	}

	// if the other node is present, write redundancy into the 'entry' (otherwise
	// just leave it out)
	if other != nil {
		entry += fmt.Sprintf(`#~-----------------------------------------------------------------------------
# PAGODA BOX
#------------------------------------------------------------------------------

# these configuration options have been removed from their standard location and
# placed here so that Pagoda Box could override them with the neccessary values
# to configure redundancy.

# IMPORTANT: these settings will always be overriden when the server boots. They
# are set dynamically and so should never change.

host    replication     postgres        %s/32            trust`, other.Ip)
	}

	// write the 'entry' to the file
	err = ioutil.WriteFile(file, []byte(entry), 0644)
	if err != nil {
		log.Error("[pg_config.configureHBAConf] Failed to write to '%s'!\n%s\n", file, err)
		return err
	}

	return nil
}

// configurePGConf attempts to open the 'postgresql.conf' file. Once open it will
// scan the file line by line looking for replication settings, and overwrite only
// those settings with the settings required for redundancy on Pagoda Box
func configurePGConf(master bool) error {

	// open the postgresql.conf
	file := conf.DataDir + "postgresql.conf"
	f, err := os.Open(file)
	if err != nil {
		return err
	}

	defer f.Close()

	reFindConfigOption := regexp.MustCompile(`^\s*#?\s*(listen_addresses|port|wal_level|archive_mode|archive_command|max_wal_senders|wal_keep_segments|hot_standby|synchronous_standby_names)\s*=\s*`)
	scanner := bufio.NewScanner(f)
	entry := ""

	// scan the file line by line to build an 'entry' to be re-written back to the
	// file, skipping ('removing') any lines that need to be manually configured
	for scanner.Scan() {

		submatch := reFindConfigOption.FindStringSubmatch(scanner.Text())

		// stop scanning if a special prefix is encountered. This ensures there are
		// no duplicate Pagoda Box comment blocks
		if strings.HasPrefix(scanner.Text(), "#~") {
			break
		}

		// build the 'entry' from all lines that don't match the custom configurations
		if submatch == nil {
			entry += fmt.Sprintf("%s\n", scanner.Text())
		}
	}

	// write manual configurations into an 'entry'
	entry += fmt.Sprintf(`#~-----------------------------------------------------------------------------
# PAGODA BOX
#------------------------------------------------------------------------------

# these configuration options have been removed from their standard location and
# placed here so that Pagoda Box could override them with the neccessary values
# to configure redundancy.

# IMPORTANT: these settings will always be overriden when the server boots. They
# are set dynamically and so should never change.

listen_addresses = '0.0.0.0'      # what IP address(es) to listen on;
                                  # comma-separated list of addresses;
                                  # defaults to 'localhost'; use '*' for all
                                  # (change requires restart)
port = %d                     # (change requires restart)
wal_level = hot_standby           # minimal, archive, or hot_standby
                                  # (change requires restart)
archive_mode = on                 # allows archiving to be done
                                  # (change requires restart)
archive_command = 'exit 0'        # command to use to archive a logfile segment
                                  # placeholders: \%p = path of file to archive
                                  #               \%f = file name only
                                  # e.g. 'test ! -f /mnt/server/archivedir/\%f && cp \%p /mnt/server/archivedir/\%f'
max_wal_senders = 10              # max number of walsender processes
                                  # (change requires restart)
wal_keep_segments = 5000          # in logfile segments, 16MB each; 0 disables
hot_standby = on                  # "on" allows queries during recovery
                                  # (change requires restart)
`, conf.PGPort)

	// if this node is currenty 'master' then write one additional configuration
	// into the 'entry'
	if master {
		entry += `
# added only the the node running postgres as 'master'
synchronous_standby_names = slave # standby servers that provide sync rep
                                  # comma-separated list of application_name
                                  # from standby(s); '*' = all
`
	}

	// write 'entry' to the file
	err = ioutil.WriteFile(file, []byte(entry), 0644)
	if err != nil {
		log.Error("[pg_config.configurePGConf] Failed to write to '%s'!\n%s\n", file, err)
	}

	return nil
}

// createRecovery creates a 'recovery.conf' file with the necessary settings
// required for redundancy on Pagoda Box. This method is called on the node that
// is being configured to run the 'slave' instance of postgres
func createRecovery() error {

	file := conf.DataDir + "recovery.conf"

	self := Whoami()
	other, err := Whoisnot(self.CRole)
	if err != nil {
		log.Fatal("[pg_config.createRecovery] Unable to find another... Exiting!\n%s\n", err)
		os.Exit(1)
	}

	// open/truncate the recover.conf
	f, err := os.Create(file)
	if err != nil {
		log.Error("[pg_config.createRecovery] Failed to create '%s'!\n%s\n", file, err)
		return err
	}

	// write manual configuration an 'entry'
	entry := fmt.Sprintf(`#~-----------------------------------------------------------------------------
# PAGODA BOX
#------------------------------------------------------------------------------

# IMPORTANT: this config file is dynamically generated by Pagoda Box for redundancy
# any changes made here will be overriden.

# When standby_mode is enabled, the PostgreSQL server will work as a standby. It
# tries to connect to the primary according to the connection settings
# primary_conninfo, and receives XLOG records continuously.
standby_mode = on
primary_conninfo = 'host=%s port=%d application_name=slave'

# restore_command specifies the shell command that is executed to copy log files
# back from archival storage. This parameter is *required* for an archive
# recovery, but optional for streaming replication. The given command satisfies
# the requirement without doing anything.
restore_command = 'exit 0'
`, other.Ip, other.PGPort)

	// write 'entry' to the file
	if _, err := f.WriteString(entry); err != nil {
		log.Error("[pg_config.createRecovery] Failed to write to '%s'!\n%s\n", file, err)
		return err
	}

	return nil
}

// destroyRecovery attempts to destroy the 'recovery.conf'. This method is called
// on a node that is being configured to run the 'master' instance of postgres
func destroyRecovery() {

	file := conf.DataDir + "recovery.conf"

	// remove 'recovery.conf'
	err := os.Remove(file)
	if err != nil {
		log.Warn("[pg_config.destroyRecovery] No recovery.conf found at '%s'", file)
	}
}
<commit_msg>replacing pagodabox stuff with yoke<commit_after>// pgconfig.go provides methods for configuring a node corresponding to if it is
// running a 'master' or 'slave' instance of postgres.

package main

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"os"
	"regexp"
	"strings"
)

// configureHBAConf attempts to open the 'pg_hba.conf' file. Once open it will scan
// the file line by line looking for replication settings, and overwrite only those
// settings with the settings required for redundancy on Pagoda Box
func configureHBAConf() error {

	// get the role of the other node in the cluster that is running an instance
	// of postgresql
	self := Whoami()
	other, err := Whoisnot(self.CRole)
	if err != nil {
		log.Warn("[pg_config.configureHBAConf] Unable to find another!\n%s\n", err)
	}

	// open the pg_hba.conf
	file := conf.DataDir + "pg_hba.conf"
	f, err := os.Open(file)
	if err != nil {
		log.Error("[pg_config.configureHBAConf] Failed to open '%s'!\n%s\n", file, err)
		return err
	}

	defer f.Close()

	scanner := bufio.NewScanner(f)
	reFindConfigOption := regexp.MustCompile(`^\s*#?\s*(local|host)\s*(replication)`)
	entry := ""

	// scan the file line by line to build an 'entry' to be re-written back to the
	// file, skipping ('removing') any line that deals with redundancy.
	for scanner.Scan() {

		// stop scanning if a special prefix is encountered. This ensures there are
		// no duplicate Pagoda Box comment blocks
		if strings.HasPrefix(scanner.Text(), "#~") {
			break
		}

		// dont care about submatches, just if the string matches, 'skipping' any lines
		// that are custom configurations
		if reFindConfigOption.FindString(scanner.Text()) == "" {
			entry += fmt.Sprintf("%s\n", scanner.Text())
		}
	}

	// if the other node is present, write redundancy into the 'entry' (otherwise
	// just leave it out)
	if other != nil {
		entry += fmt.Sprintf(`#~-----------------------------------------------------------------------------
# YOKE CONFIG
#------------------------------------------------------------------------------

# these configuration options have been removed from their standard location and
# placed here so that Pagoda Box could override them with the neccessary values
# to configure redundancy.

# IMPORTANT: these settings will always be overriden when the server boots. They
# are set dynamically and so should never change.

host    replication     postgres        %s/32            trust`, other.Ip)
	}

	// write the 'entry' to the file
	err = ioutil.WriteFile(file, []byte(entry), 0644)
	if err != nil {
		log.Error("[pg_config.configureHBAConf] Failed to write to '%s'!\n%s\n", file, err)
		return err
	}

	return nil
}

// configurePGConf attempts to open the 'postgresql.conf' file. Once open it will
// scan the file line by line looking for replication settings, and overwrite only
// those settings with the settings required for redundancy on Pagoda Box
func configurePGConf(master bool) error {

	// open the postgresql.conf
	file := conf.DataDir + "postgresql.conf"
	f, err := os.Open(file)
	if err != nil {
		return err
	}

	defer f.Close()

	reFindConfigOption := regexp.MustCompile(`^\s*#?\s*(listen_addresses|port|wal_level|archive_mode|archive_command|max_wal_senders|wal_keep_segments|hot_standby|synchronous_standby_names)\s*=\s*`)
	scanner := bufio.NewScanner(f)
	entry := ""

	// scan the file line by line to build an 'entry' to be re-written back to the
	// file, skipping ('removing') any lines that need to be manually configured
	for scanner.Scan() {

		submatch := reFindConfigOption.FindStringSubmatch(scanner.Text())

		// stop scanning if a special prefix is encountered. This ensures there are
		// no duplicate Pagoda Box comment blocks
		if strings.HasPrefix(scanner.Text(), "#~") {
			break
		}

		// build the 'entry' from all lines that don't match the custom configurations
		if submatch == nil {
			entry += fmt.Sprintf("%s\n", scanner.Text())
		}
	}

	// write manual configurations into an 'entry'
	entry += fmt.Sprintf(`#~-----------------------------------------------------------------------------
# YOKE CONFIG
#------------------------------------------------------------------------------

# these configuration options have been removed from their standard location and
# placed here so that Pagoda Box could override them with the neccessary values
# to configure redundancy.

# IMPORTANT: these settings will always be overriden when the server boots. They
# are set dynamically and so should never change.

listen_addresses = '0.0.0.0'      # what IP address(es) to listen on;
                                  # comma-separated list of addresses;
                                  # defaults to 'localhost'; use '*' for all
                                  # (change requires restart)
port = %d                     # (change requires restart)
wal_level = hot_standby           # minimal, archive, or hot_standby
                                  # (change requires restart)
archive_mode = on                 # allows archiving to be done
                                  # (change requires restart)
archive_command = 'exit 0'        # command to use to archive a logfile segment
                                  # placeholders: \%p = path of file to archive
                                  #               \%f = file name only
                                  # e.g. 'test ! -f /mnt/server/archivedir/\%f && cp \%p /mnt/server/archivedir/\%f'
max_wal_senders = 10              # max number of walsender processes
                                  # (change requires restart)
wal_keep_segments = 5000          # in logfile segments, 16MB each; 0 disables
hot_standby = on                  # "on" allows queries during recovery
                                  # (change requires restart)
`, conf.PGPort)

	// if this node is currenty 'master' then write one additional configuration
	// into the 'entry'
	if master {
		entry += `
# added only the the node running postgres as 'master'
synchronous_standby_names = slave # standby servers that provide sync rep
                                  # comma-separated list of application_name
                                  # from standby(s); '*' = all
`
	}

	// write 'entry' to the file
	err = ioutil.WriteFile(file, []byte(entry), 0644)
	if err != nil {
		log.Error("[pg_config.configurePGConf] Failed to write to '%s'!\n%s\n", file, err)
	}

	return nil
}

// createRecovery creates a 'recovery.conf' file with the necessary settings
// required for redundancy on Pagoda Box. This method is called on the node that
// is being configured to run the 'slave' instance of postgres
func createRecovery() error {

	file := conf.DataDir + "recovery.conf"

	self := Whoami()
	other, err := Whoisnot(self.CRole)
	if err != nil {
		log.Fatal("[pg_config.createRecovery] Unable to find another... Exiting!\n%s\n", err)
		os.Exit(1)
	}

	// open/truncate the recover.conf
	f, err := os.Create(file)
	if err != nil {
		log.Error("[pg_config.createRecovery] Failed to create '%s'!\n%s\n", file, err)
		return err
	}

	// write manual configuration an 'entry'
	entry := fmt.Sprintf(`#~-----------------------------------------------------------------------------
# YOKE CONFIG
#------------------------------------------------------------------------------

# IMPORTANT: this config file is dynamically generated by Pagoda Box for redundancy
# any changes made here will be overriden.

# When standby_mode is enabled, the PostgreSQL server will work as a standby. It
# tries to connect to the primary according to the connection settings
# primary_conninfo, and receives XLOG records continuously.
standby_mode = on
primary_conninfo = 'host=%s port=%d application_name=slave'

# restore_command specifies the shell command that is executed to copy log files
# back from archival storage. This parameter is *required* for an archive
# recovery, but optional for streaming replication. The given command satisfies
# the requirement without doing anything.
restore_command = 'exit 0'
`, other.Ip, other.PGPort)

	// write 'entry' to the file
	if _, err := f.WriteString(entry); err != nil {
		log.Error("[pg_config.createRecovery] Failed to write to '%s'!\n%s\n", file, err)
		return err
	}

	return nil
}

// destroyRecovery attempts to destroy the 'recovery.conf'. This method is called
// on a node that is being configured to run the 'master' instance of postgres
func destroyRecovery() {

	file := conf.DataDir + "recovery.conf"

	// remove 'recovery.conf'
	err := os.Remove(file)
	if err != nil {
		log.Warn("[pg_config.destroyRecovery] No recovery.conf found at '%s'", file)
	}
}
<|endoftext|>