<commit_before>package main

import (
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"lib/models"
	"lib/mutualtls"
	"lib/policy_client"
	"math/rand"
	"net/http"
	_ "net/http/pprof"
	"os"
	"os/exec"
	"time"

	"github.com/cloudfoundry-incubator/cf-test-helpers/helpers"

	"code.cloudfoundry.org/lager"
)

var (
	config               Config
	testDuration         time.Duration
	pollInterval         time.Duration
	externalPolicyClient *policy_client.ExternalClient
	internalPolicyClient *policy_client.InternalClient
)

type Config struct {
	Api                 string `json:"api"`
	Apps                int    `json:"apps"`
	CreateNewPolicies   bool   `json:"create_new_policies"`
	TestDurationMinutes int    `json:"test_duration_minutes"`
	Logs                string `json:"logs"`
	NumCells            int    `json:"num_cells"`
	PoliciesPerApp      int    `json:"policies_per_app"`
	PollIntervalSeconds int    `json:"poll_interval"`

	ServerCACertFile            string `json:"ca_cert_file" validate:"nonzero"`
	ClientCertFile              string `json:"client_cert_file" validate:"nonzero"`
	ClientKeyFile               string `json:"client_key_file" validate:"nonzero"`
	PolicyServerInternalBaseURL string `json:"policy_server_internal_base_url"`
}

func loadTestConfig(logger lager.Logger) {
	configPath := helpers.ConfigPath()
	configBytes, err := ioutil.ReadFile(configPath)
	if err != nil {
		logger.Fatal("reading-config", err)
	}

	err = json.Unmarshal(configBytes, &config)
	if err != nil {
		logger.Fatal("unmarshalling-config", err)
	}

	testDuration = time.Duration(config.TestDurationMinutes) * time.Minute
	pollInterval = time.Duration(config.PollIntervalSeconds) * time.Second
}

func getInternalPolicyClient(logger lager.Logger) *policy_client.InternalClient {
	clientTLSConfig, err := mutualtls.NewClientTLSConfig(config.ClientCertFile, config.ClientKeyFile, config.ServerCACertFile)
	if err != nil {
		logger.Fatal("mutual-tls", err)
	}
	clientTLSConfig.InsecureSkipVerify = true

	httpClient := &http.Client{
		Transport: &http.Transport{
			TLSClientConfig: clientTLSConfig,
		},
	}

	return policy_client.NewInternal(logger.Session("internal-policy-client"), httpClient, config.PolicyServerInternalBaseURL)
}

func getExternalPolicyClient(logger lager.Logger) *policy_client.ExternalClient {
	httpClient := &http.Client{
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{
				InsecureSkipVerify: true,
			},
		},
	}

	policyServerAPI := fmt.Sprintf("https://%s", config.Api)
	return policy_client.NewExternal(logger.Session("external-policy-client"), httpClient, policyServerAPI)
}

func main() {
	logger := lager.NewLogger("cf-networking.policy-server-test")

	loadTestConfig(logger)

	file, err := os.OpenFile(config.Logs, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		logger.Fatal("writing-to-log-file", err)
	}
	logger.RegisterSink(lager.NewWriterSink(file, lager.INFO))
	logger.Info("started")
	defer logger.Info("exited")

	if config.Api == "" {
		logger.Fatal("reading-api-from-config", errors.New("API not specified in config"))
	}

	internalPolicyClient = getInternalPolicyClient(logger)
	externalPolicyClient = getExternalPolicyClient(logger)

	token := getCurrentToken(logger)

	logger.Info("creating-application-guids")
	var guids []string
	for i := 0; i < config.Apps; i++ {
		guid := fmt.Sprintf("9cb281b-e272-4df7-b398-b6663ca-%04d", i) // TODO: improve guid creation
		guids = append(guids, guid)
	}
	logger.Info(fmt.Sprintf("finished-creating-%d-application-guids", config.Apps))

	if config.CreateNewPolicies {
		deleteOldPolicies(logger, token)
		addNewPolicies(logger, guids, token)
	} else {
		logger.Info("skipped-creating-policies")
	}

	appsPerCell := config.Apps / config.NumCells
	var cells [][]string
	for i := 0; i < config.NumCells; i++ {
		cells = append(cells, guids[i*appsPerCell:(i+1)*appsPerCell])
	}

	for i := 0; i < len(cells); i++ {
		go func(i int) {
			logger.Info(fmt.Sprintf("cell-%d-polling-policy-server", i))
			pollPolicyServer(logger, cells[i], i)
		}(i)
	}

	fmt.Println("Press CTRL-C to exit")
	select {
	case <-time.After(testDuration):
		logger.Info(fmt.Sprintf("exiting"))
		os.Exit(0)
	}
}

func addNewPolicies(logger lager.Logger, guids []string, token string) {
	logger.Info("creating-policies-for-each-application-guid")
	policies := []models.Policy{}
	for _, guid := range guids {
		for i := 0; i < config.PoliciesPerApp; i++ {
			policy := models.Policy{
				Source: models.Source{
					ID: guid,
				},
				Destination: models.Destination{
					ID:       guid, // TODO: randomness in policy creation and distributions (eg hotspot)
					Protocol: "tcp",
					Port:     9000 + i,
				},
			}
			policies = append(policies, policy)
		}
	}

	logger.Info("adding-policies")
	err := externalPolicyClient.AddPolicies(token, policies)
	if err != nil {
		logger.Fatal("adding-policies", err)
	}
	logger.Info("finished-adding-policies-to-policy-server")
}

func getPoliciesForCell(logger lager.Logger, ids []string, index, numCalls int) {
	logger.Info("getting-policies-by-id", lager.Data{
		"index":    index,
		"numCalls": numCalls,
	})

	_, err := internalPolicyClient.GetPoliciesByID(ids...)
	if err != nil {
		logger.Fatal("getting-policies-by-id", err)
	} else {
		logger.Info(fmt.Sprintf("finished-request-from-cell-#%d-on-call-#%d", index, numCalls))
	}
}

func deleteOldPolicies(logger lager.Logger, token string) {
	logger.Info("getting-existing-policies")
	policies, err := externalPolicyClient.GetPolicies(token)
	if err != nil {
		logger.Fatal("get-policies", err)
	}
	logger.Info("number-of-existing-policies", lager.Data{"num-existing-policies": len(policies)})

	logger.Info("deleting-existing-policies")
	err = externalPolicyClient.DeletePolicies(token, policies)
	if err != nil {
		logger.Fatal("deleting-policies", err)
	}

	logger.Info("deleted-existing-policies")
}

const refreshTokenDuration = 5 * time.Minute

func jitter(baseTime time.Duration, jitterAmount time.Duration) time.Duration {
	x := rand.Int63n(int64(jitterAmount)*2) - int64(jitterAmount)
	return baseTime + time.Duration(x)
}

func pollPolicyServer(logger lager.Logger, ids []string, index int) {
	numCalls := 0
	for {
		select {
		case <-time.After(jitter(pollInterval, 1*time.Second)):
			go getPoliciesForCell(logger, ids, index, numCalls)
			numCalls = numCalls + 1
			continue
		}
	}
}

func getCurrentToken(logger lager.Logger) string {
	cmd := exec.Command("cf", "oauth-token")

	tokenBytes, err := cmd.Output()
	if err != nil {
		logger.Fatal("running-command-cf-oauth-token`", err)
	}

	token := string(tokenBytes[0 : len(tokenBytes)-1]) // remove trailing \n
	logger.Info("parsed-cf-oauth-token", lager.Data{"token": token})

	return token
}
<commit_msg>Policy Server perf test randomize app guid and policies<commit_after>package main

import (
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"lib/models"
	"lib/mutualtls"
	"lib/policy_client"
	"math/rand"
	"net/http"
	_ "net/http/pprof"
	"os"
	"os/exec"
	"time"

	"github.com/cloudfoundry-incubator/cf-test-helpers/helpers"

	"code.cloudfoundry.org/lager"
)

var (
	config               Config
	testDuration         time.Duration
	pollInterval         time.Duration
	externalPolicyClient *policy_client.ExternalClient
	internalPolicyClient *policy_client.InternalClient
)

type Config struct {
	Api                 string `json:"api"`
	Apps                int    `json:"apps"`
	CreateNewPolicies   bool   `json:"create_new_policies"`
	TestDurationMinutes int    `json:"test_duration_minutes"`
	Logs                string `json:"logs"`
	NumCells            int    `json:"num_cells"`
	PoliciesPerApp      int    `json:"policies_per_app"`
	PollIntervalSeconds int    `json:"poll_interval"`

	ServerCACertFile            string `json:"ca_cert_file" validate:"nonzero"`
	ClientCertFile              string `json:"client_cert_file" validate:"nonzero"`
	ClientKeyFile               string `json:"client_key_file" validate:"nonzero"`
	PolicyServerInternalBaseURL string `json:"policy_server_internal_base_url"`
}

func loadTestConfig(logger lager.Logger) {
	configPath := helpers.ConfigPath()
	configBytes, err := ioutil.ReadFile(configPath)
	if err != nil {
		logger.Fatal("reading-config", err)
	}

	err = json.Unmarshal(configBytes, &config)
	if err != nil {
		logger.Fatal("unmarshalling-config", err)
	}

	testDuration = time.Duration(config.TestDurationMinutes) * time.Minute
	pollInterval = time.Duration(config.PollIntervalSeconds) * time.Second
}

func getInternalPolicyClient(logger lager.Logger) *policy_client.InternalClient {
	clientTLSConfig, err := mutualtls.NewClientTLSConfig(config.ClientCertFile, config.ClientKeyFile, config.ServerCACertFile)
	if err != nil {
		logger.Fatal("mutual-tls", err)
	}
	clientTLSConfig.InsecureSkipVerify = true

	httpClient := &http.Client{
		Transport: &http.Transport{
			TLSClientConfig: clientTLSConfig,
		},
	}

	return policy_client.NewInternal(logger.Session("internal-policy-client"), httpClient, config.PolicyServerInternalBaseURL)
}

func getExternalPolicyClient(logger lager.Logger) *policy_client.ExternalClient {
	httpClient := &http.Client{
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{
				InsecureSkipVerify: true,
			},
		},
	}

	policyServerAPI := fmt.Sprintf("https://%s", config.Api)
	return policy_client.NewExternal(logger.Session("external-policy-client"), httpClient, policyServerAPI)
}

func randomAppGUID(index int) string {
	return fmt.Sprintf("%08x", rand.Int63())
}

func main() {
	logger := lager.NewLogger("cf-networking.policy-server-test")

	loadTestConfig(logger)

	file, err := os.OpenFile(config.Logs, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		logger.Fatal("writing-to-log-file", err)
	}
	logger.RegisterSink(lager.NewWriterSink(file, lager.INFO))
	logger.Info("started")
	defer logger.Info("exited")

	if config.Api == "" {
		logger.Fatal("reading-api-from-config", errors.New("API not specified in config"))
	}

	internalPolicyClient = getInternalPolicyClient(logger)
	externalPolicyClient = getExternalPolicyClient(logger)

	token := getCurrentToken(logger)

	logger.Info("creating-application-guids")
	rand.Seed(1) // always use the same random sequence
	var guids []string
	for i := 0; i < config.Apps; i++ {
		guids = append(guids, randomAppGUID(i))
	}
	logger.Info(fmt.Sprintf("finished-creating-%d-application-guids", config.Apps))

	if config.CreateNewPolicies {
		deleteOldPolicies(logger, token)
		token = getCurrentToken(logger)
		addNewPolicies(logger, guids, token)
	} else {
		logger.Info("skipped-creating-policies")
	}

	appsPerCell := config.Apps / config.NumCells
	var cells [][]string
	for i := 0; i < config.NumCells; i++ {
		cells = append(cells, guids[i*appsPerCell:(i+1)*appsPerCell])
	}

	for i := 0; i < len(cells); i++ {
		go func(i int) {
			logger.Info(fmt.Sprintf("cell-%d-polling-policy-server", i))
			pollPolicyServer(logger, cells[i], i)
		}(i)
	}

	fmt.Println("Press CTRL-C to exit")
	select {
	case <-time.After(testDuration):
		logger.Info(fmt.Sprintf("exiting"))
		os.Exit(0)
	}
}

func addNewPolicies(logger lager.Logger, appGuids []string, token string) {
	logger.Info("creating-policies-for-each-application-guid")
	policies := []models.Policy{}
	for _ = range appGuids {
		for i := 0; i < config.PoliciesPerApp; i++ {
			dstGuid := appGuids[rand.Intn(len(appGuids))]
			srcGuid := appGuids[rand.Intn(len(appGuids))]

			policy := models.Policy{
				Source: models.Source{
					ID: srcGuid,
				},
				Destination: models.Destination{
					ID:       dstGuid,
					Protocol: "tcp",
					Port:     10000 + rand.Intn(10000),
				},
			}
			policies = append(policies, policy)
		}
	}

	logger.Info("adding-policies")
	err := externalPolicyClient.AddPolicies(token, policies)
	if err != nil {
		logger.Fatal("adding-policies", err)
	}
	logger.Info("finished-adding-policies-to-policy-server")
}

func getPoliciesForCell(logger lager.Logger, ids []string, index, numCalls int) {
	logger.Info("getting-policies-by-id", lager.Data{
		"index":    index,
		"numCalls": numCalls,
	})

	_, err := internalPolicyClient.GetPoliciesByID(ids...)
	if err != nil {
		logger.Fatal("getting-policies-by-id", err)
	} else {
		logger.Info(fmt.Sprintf("finished-request-from-cell-#%d-on-call-#%d", index, numCalls))
	}
}

func deleteOldPolicies(logger lager.Logger, token string) {
	logger.Info("getting-existing-policies")
	policies, err := externalPolicyClient.GetPolicies(token)
	if err != nil {
		logger.Fatal("get-policies", err)
	}
	logger.Info("number-of-existing-policies", lager.Data{"num-existing-policies": len(policies)})

	logger.Info("deleting-existing-policies")
	err = externalPolicyClient.DeletePolicies(token, policies)
	if err != nil {
		logger.Fatal("deleting-policies", err)
	}

	logger.Info("deleted-existing-policies")
}

const refreshTokenDuration = 5 * time.Minute

func jitter(baseTime time.Duration, jitterAmount time.Duration) time.Duration {
	x := rand.Int63n(int64(jitterAmount)*2) - int64(jitterAmount)
	return baseTime + time.Duration(x)
}

func pollPolicyServer(logger lager.Logger, ids []string, index int) {
	numCalls := 0
	for {
		select {
		case <-time.After(jitter(pollInterval, 1*time.Second)):
			go getPoliciesForCell(logger, ids, index, numCalls)
			numCalls = numCalls + 1
			continue
		}
	}
}

func getCurrentToken(logger lager.Logger) string {
	cmd := exec.Command("cf", "oauth-token")

	tokenBytes, err := cmd.Output()
	if err != nil {
		logger.Fatal("running-command-cf-oauth-token`", err)
	}

	token := string(tokenBytes[0 : len(tokenBytes)-1]) // remove trailing \n
	logger.Info("parsed-cf-oauth-token", lager.Data{"token": token})

	return token
}

{'fraction_non_alphanumeric': 0.14046993578517222, 'fraction_numerical': 0.006056625802685347, 'mean_word_length': 4.389303971686984, 'pattern_counts': {'":': 8, '<': 15, '<?xml version=': 0, '>': 3, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2870157', 'n_tokens_mistral': 5004, 'n_tokens_neox': 4753, 'n_words': 855}