diff --git a/commands.go b/commands.go index 37d90c1a..7409dbac 100644 --- a/commands.go +++ b/commands.go @@ -29,11 +29,12 @@ const ( var ( RegisteredCommands = map[string]cli.CommandFactory{ - "apply": ApplyCmdFactory, - "destroy": DestroyCmdFactory, - "bastion": SshCmdFactory, - "node": NodeSshCmdFactory, - "geth": GethCmdFactory, + "apply": ApplyCmdFactory, + "destroy": DestroyCmdFactory, + "bastion": SshCmdFactory, + "node": NodeSshCmdFactory, + "nodeinfo": NodeInfoSshCmdFactory, + "geth": GethCmdFactory, } ) @@ -54,6 +55,10 @@ type NodeSshCmd struct { SshCmd } +type NodeInfoSshCmd struct { + SshCmd +} + type GethCmd struct { SshCmd } @@ -66,6 +71,10 @@ func NodeSshCmdFactory() (command cli.Command, err error) { return NodeSshCmd{}, nil } +func NodeInfoSshCmdFactory() (command cli.Command, err error) { + return NodeInfoSshCmd{}, nil +} + func SshCmdFactory() (command cli.Command, err error) { return SshCmd{}, nil } @@ -107,6 +116,12 @@ func (nodeSshCmd NodeSshCmd) Run(args []string) (exitCode int) { return exitCode } +func (nodeSshCmd NodeInfoSshCmd) Run(args []string) (exitCode int) { + exitCode = nodeSshCmd.getNodeInfo() + + return exitCode +} + func (sshCmd SshCmd) Run(args []string) (exitCode int) { if runtime.GOOS == "windows" { panic("Cannot run ssh command on Windows") @@ -295,6 +310,14 @@ func (nodeSshCmd NodeSshCmd) Help() (helpMessage string) { return helpMessage } +func (nodeSshCmd NodeInfoSshCmd) Help() (helpMessage string) { + helpMessage = "\n This command let you to get detailed info about runing nodes\n" + helpMessage = helpMessage + "You must not provide any arguments. There are not arguments in this command\n" + helpMessage = helpMessage + "Example: apollo nodeinfo" + + return helpMessage +} + func (gethCmd GethCmd) Help() (helpMessage string) { helpMessage = "\n This command let you attach via rpc (geth) to certain node\n" helpMessage = helpMessage + "You must provide node number as argument. If number is out of range, ssh will fail\n" @@ -323,6 +346,11 @@ func (nodeSshCmd NodeSshCmd) Synopsis() (synopsis string) { return synopsis } +func (nodeSshCmd NodeInfoSshCmd) Synopsis() (synopsis string) { + synopsis = "nodeinfo" + return synopsis +} + func (gethCmd GethCmd) Synopsis() (synopsis string) { synopsis = "geth [number]" return synopsis @@ -404,3 +432,18 @@ func (sshCmd *SshCmd) runWithScriptLocation(scriptName string, args []string) (e return exitCode } + +func (sshCmd *SshCmd) getNodeInfo() (exitCode int) { + bastionSshNodeInfoLocator := "cat /qdata/nodeinfo/ip_* | sort" + additionalSshCmdArgs := []string{bastionSshNodeInfoLocator} + coreCmd, err := SshCmdFactory() + + if nil != err { + fmt.Println(err) + return ExitCodeInvalidSetup + } + + exitCode = coreCmd.Run(additionalSshCmdArgs) + + return exitCode +} diff --git a/commands_test.go b/commands_test.go index 60653ddd..d1d60e7b 100644 --- a/commands_test.go +++ b/commands_test.go @@ -37,6 +37,13 @@ func TestNodeSshCmdFactory(t *testing.T) { testThatCommandHasWholeInterface(t, command) } +func TestNodeInfoSshCmdFactory(t *testing.T) { + command, err := NodeInfoSshCmdFactory() + assert.Nil(t, err) + assert.IsType(t, NodeInfoSshCmd{}, command) + testThatCommandHasWholeInterface(t, command) +} + func TestSshCmdFactory(t *testing.T) { command, err := SshCmdFactory() assert.Nil(t, err) @@ -129,6 +136,27 @@ func TestNodeSshCmd_RunInvalid(t *testing.T) { terra.TempDirPathLocation = terra.TempDirPath } +func TestNodeInfoSshCmd_RunInvalid(t *testing.T) { + terra.TempDirPathLocation = ".dummyApolloNodeInfo" + dummyFileName := "output.log" + deployName := "dummyDeployName" + dummyDeployName := terra.TempDirPathLocation + "/" + deployName + err := os.MkdirAll(dummyDeployName, 0777) + assert.Nil(t, err) + PrepareDummyFile(t, dummyDeployName+"/"+dummyFileName, terra.OutputAsAStringWithoutHeaderFixture) + + command, err := NodeInfoSshCmdFactory() + assert.Nil(t, err) + assert.IsType(t, NodeInfoSshCmd{}, command) + + exitCode := command.Run([]string{}) + assert.Equal(t, ExitCodeSshDialError, exitCode) + + err = os.RemoveAll(terra.TempDirPathLocation) + assert.Nil(t, err) + terra.TempDirPathLocation = terra.TempDirPath +} + func TestSshCmd_RunInvalid(t *testing.T) { terra.TempDirPathLocation = ".dummyApollo" dummyFileName := "output.log" diff --git a/terra/bastion/main-pantheon.tf b/terra/bastion/main-pantheon.tf index 14716179..2bfd6cf9 100644 --- a/terra/bastion/main-pantheon.tf +++ b/terra/bastion/main-pantheon.tf @@ -119,6 +119,12 @@ resource "local_file" "bootstrap" { set -e +# Kill all running containers +if [ $(docker ps -a -q | wc -l ) -gt 0 ]; then + sudo docker stop $(docker ps -a -q) + sudo docker rm $(docker ps -a -q) +fi + sudo curl -Ls "https://github.com/docker/compose/releases/download/1.24.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose echo "Pull docker images ..." @@ -351,7 +357,7 @@ do done echo ']' >> $target_file sudo mv $target_file /opt/ethstats/ -echo '["'${random_id.ethstat_secret.hex}'"]' | sudo tee -a /opt/ethstats/ws_secret.json +echo '["'${random_id.ethstat_secret.hex}'"]' | sudo tee /opt/ethstats/ws_secret.json # Chainhammer ================================================== WORKDIR=/home/admin/chainhammer diff --git a/terra/bastion/main-parity.tf b/terra/bastion/main-parity.tf index 9c191179..ea2bb481 100644 --- a/terra/bastion/main-parity.tf +++ b/terra/bastion/main-parity.tf @@ -121,6 +121,12 @@ resource "local_file" "bootstrap" { set -e +# Kill all running containers +if [ $(docker ps -a -q | wc -l ) -gt 0 ]; then + sudo docker stop $(docker ps -a -q) + sudo docker rm $(docker ps -a -q) +fi + sudo curl -Ls "https://github.com/docker/compose/releases/download/1.24.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose echo "Pull docker images ..." @@ -350,7 +356,7 @@ do done echo ']' >> $target_file sudo mv $target_file /opt/ethstats/ -echo '["'${random_id.ethstat_secret.hex}'"]' | sudo tee -a /opt/ethstats/ws_secret.json +echo '["'${random_id.ethstat_secret.hex}'"]' | sudo tee /opt/ethstats/ws_secret.json # Chainhammer ================================================== WORKDIR=/home/admin/chainhammer diff --git a/terra/bastion/main-quorum.tf b/terra/bastion/main-quorum.tf index aae3c1c4..9489b744 100644 --- a/terra/bastion/main-quorum.tf +++ b/terra/bastion/main-quorum.tf @@ -124,6 +124,12 @@ resource "local_file" "bootstrap" { set -e +# Kill all running containers +if [ $(docker ps -a -q | wc -l ) -gt 0 ]; then + sudo docker stop $(docker ps -a -q) + sudo docker rm $(docker ps -a -q) +fi + sudo curl -Ls "https://github.com/docker/compose/releases/download/1.24.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose echo "Pull docker images ..." diff --git a/terra/pantheon/container_definition_bootstrap.tf b/terra/pantheon/container_definition_bootstrap.tf index 5ffe08cf..f3bc52d6 100644 --- a/terra/pantheon/container_definition_bootstrap.tf +++ b/terra/pantheon/container_definition_bootstrap.tf @@ -6,6 +6,7 @@ locals { hosts_folder = "${local.shared_volume_container_path}/hosts" libfaketime_folder = "${local.shared_volume_container_path}/lib" libfaketime_file = "${local.shared_volume_container_path}/lib/libfaketime_value" + node_info_folder = "${local.shared_volume_container_path}/nodeinfo" metadata_bootstrap_container_status_file = "${local.shared_volume_container_path}/metadata_bootstrap_container_status" @@ -95,14 +96,19 @@ locals { "echo $HOST_IP > ${local.host_ip_file}", "export TASK_ARN=$(curl --connect-timeout 5 --retry 5 --max-time 10 -s $ECS_CONTAINER_METADATA_URI/task | jq -r '.TaskARN')", "export REGION=$(echo $TASK_ARN | awk -F: '{ print $4}')", - "aws ecs describe-tasks --region $REGION --cluster ${local.ecs_cluster_name} --tasks $TASK_ARN | jq -r '.tasks[0] | .group' > ${local.service_file}", + "export SERVICE_GROUP=$(aws ecs describe-tasks --region $REGION --cluster ${local.ecs_cluster_name} --tasks $TASK_ARN | jq -r '.tasks[0] | .group')", + "echo $SERVICE_GROUP > ${local.service_file}", "mkdir -p ${local.hosts_folder}", "mkdir -p ${local.node_ids_folder}", + "mkdir -p ${local.node_info_folder}", "mkdir -p ${local.accounts_folder}", "mkdir -p ${local.libfaketime_folder}", "count=0; while [ $count -lt 1 ]; do count=$(ls ${local.libfaketime_folder} | grep libfaketime.so | wc -l); aws s3 cp s3://${local.s3_libfaketime_file} ${local.libfaketime_folder}/libfaketime.so > /dev/null 2>&1 | echo \"Wait for libfaketime to appear on S3 ... \"; sleep 1; done", "touch ${local.libfaketime_file}", - "aws sqs --region $REGION receive-message --queue-url ${aws_sqs_queue.faketime_queue.id} --visibility-timeout=300 | jq .Messages[].Body | tr -d '\\\"' > ${local.libfaketime_file}", + "export CLOCK_SKEW=$(aws sqs --region $REGION receive-message --queue-url ${aws_sqs_queue.faketime_queue.id} --visibility-timeout=300 | jq .Messages[].Body | tr -d '\\\"')", + "echo $CLOCK_SKEW > ${local.libfaketime_file}", + "echo \"$(echo $SERVICE_GROUP | sed 's/.*://') ip=$HOST_IP clock_skew=$CLOCK_SKEW chaos_testing_command=${join(" ", var.chaos_testing_run_command)}\" > ${local.node_info_folder}/${local.normalized_host_ip}", + "aws s3 cp ${local.node_info_folder}/${local.normalized_host_ip} s3://${local.s3_revision_folder}/nodeinfo/${local.normalized_host_ip} --sse aws:kms --sse-kms-key-id ${aws_kms_key.bucket.arn}", "aws s3 cp ${local.node_id_file} s3://${local.s3_revision_folder}/nodeids/${local.normalized_host_ip} --sse aws:kms --sse-kms-key-id ${aws_kms_key.bucket.arn}", "aws s3 cp ${local.host_ip_file} s3://${local.s3_revision_folder}/hosts/${local.normalized_host_ip} --sse aws:kms --sse-kms-key-id ${aws_kms_key.bucket.arn}", "aws s3 cp ${local.account_address_file} s3://${local.s3_revision_folder}/accounts/${local.normalized_host_ip} --sse aws:kms --sse-kms-key-id ${aws_kms_key.bucket.arn}", diff --git a/terra/pantheon/ecs.tf b/terra/pantheon/ecs.tf index e0b4cffc..c02bb427 100644 --- a/terra/pantheon/ecs.tf +++ b/terra/pantheon/ecs.tf @@ -1,5 +1,5 @@ locals { - service_name_fmt = "node-%0${min(length(format("%d", var.number_of_nodes)), length(format("%s", var.number_of_nodes))) + 1}d-%s" + service_name_fmt = "node-%0${min(length(format("%d", var.number_of_nodes)), length(format("%s", var.number_of_nodes))) + 1}d-%s-%s" ecs_cluster_name = "pantheon-network-${var.network_name}" pantheon_bucket = "${var.region}-ecs-${lower(var.network_name)}-${random_id.bucket_postfix.hex}" } @@ -30,7 +30,7 @@ resource "aws_ecs_task_definition" "pantheon" { resource "aws_ecs_service" "pantheon" { count = "${var.number_of_nodes}" - name = "${format(local.service_name_fmt, count.index + 1, var.network_name)}" + name = "${format(local.service_name_fmt, count.index + 1, var.network_name, aws_ecs_task_definition.pantheon.revision)}" cluster = "${aws_ecs_cluster.pantheon.id}" task_definition = "${aws_ecs_task_definition.pantheon.arn}" launch_type = "EC2" diff --git a/terra/parity/container_definition_bootstrap.tf b/terra/parity/container_definition_bootstrap.tf index 4f1bad5f..48215f4e 100644 --- a/terra/parity/container_definition_bootstrap.tf +++ b/terra/parity/container_definition_bootstrap.tf @@ -6,6 +6,7 @@ locals { hosts_folder = "${local.shared_volume_container_path}/hosts" libfaketime_folder = "${local.shared_volume_container_path}/lib" libfaketime_file = "${local.shared_volume_container_path}/lib/libfaketime_value" + node_info_folder = "${local.shared_volume_container_path}/nodeinfo" metadata_bootstrap_container_status_file = "${local.shared_volume_container_path}/metadata_bootstrap_container_status" @@ -95,14 +96,19 @@ locals { "echo $HOST_IP > ${local.host_ip_file}", "export TASK_ARN=$(curl -s $ECS_CONTAINER_METADATA_URI/task | jq -r '.TaskARN')", "export REGION=$(echo $TASK_ARN | awk -F: '{ print $4}')", - "aws ecs describe-tasks --region $REGION --cluster ${local.ecs_cluster_name} --tasks $TASK_ARN | jq -r '.tasks[0] | .group' > ${local.service_file}", + "export SERVICE_GROUP=$(aws ecs describe-tasks --region $REGION --cluster ${local.ecs_cluster_name} --tasks $TASK_ARN | jq -r '.tasks[0] | .group')", + "echo $SERVICE_GROUP > ${local.service_file}", "mkdir -p ${local.hosts_folder}", "mkdir -p ${local.node_ids_folder}", + "mkdir -p ${local.node_info_folder}", "mkdir -p ${local.accounts_folder}", "mkdir -p ${local.libfaketime_folder}", "count=0; while [ $count -lt 1 ]; do count=$(ls ${local.libfaketime_folder} | grep libfaketime.so | wc -l); aws s3 cp s3://${local.s3_libfaketime_file} ${local.libfaketime_folder}/libfaketime.so > /dev/null 2>&1 | echo \"Wait for libfaketime to appear on S3 ... \"; sleep 1; done", "touch ${local.libfaketime_file}", - "aws sqs --region $REGION receive-message --queue-url ${aws_sqs_queue.faketime_queue.id} --visibility-timeout=300 | jq .Messages[].Body | tr -d '\\\"' > ${local.libfaketime_file}", + "export CLOCK_SKEW=$(aws sqs --region $REGION receive-message --queue-url ${aws_sqs_queue.faketime_queue.id} --visibility-timeout=300 | jq .Messages[].Body | tr -d '\\\"')", + "echo $CLOCK_SKEW > ${local.libfaketime_file}", + "echo \"$(echo $SERVICE_GROUP | sed 's/.*://') ip=$HOST_IP clock_skew=$CLOCK_SKEW chaos_testing_command=${join(" ", var.chaos_testing_run_command)}\" > ${local.node_info_folder}/${local.normalized_host_ip}", + "aws s3 cp ${local.node_info_folder}/${local.normalized_host_ip} s3://${local.s3_revision_folder}/nodeinfo/${local.normalized_host_ip} --sse aws:kms --sse-kms-key-id ${aws_kms_key.bucket.arn}", "aws s3 cp ${local.node_id_file} s3://${local.s3_revision_folder}/nodeids/${local.normalized_host_ip} --sse aws:kms --sse-kms-key-id ${aws_kms_key.bucket.arn}", "aws s3 cp ${local.host_ip_file} s3://${local.s3_revision_folder}/hosts/${local.normalized_host_ip} --sse aws:kms --sse-kms-key-id ${aws_kms_key.bucket.arn}", "aws s3 cp ${local.account_address_file} s3://${local.s3_revision_folder}/accounts/${local.normalized_host_ip} --sse aws:kms --sse-kms-key-id ${aws_kms_key.bucket.arn}", diff --git a/terra/parity/ecs.tf b/terra/parity/ecs.tf index 26f7e115..c810a1ee 100644 --- a/terra/parity/ecs.tf +++ b/terra/parity/ecs.tf @@ -1,5 +1,5 @@ locals { - service_name_fmt = "node-%0${min(length(format("%d", var.number_of_nodes)), length(format("%s", var.number_of_nodes))) + 1}d-%s" + service_name_fmt = "node-%0${min(length(format("%d", var.number_of_nodes)), length(format("%s", var.number_of_nodes))) + 1}d-%s-%s" ecs_cluster_name = "parity-network-${var.network_name}" parity_bucket = "${var.region}-ecs-${lower(var.network_name)}-${random_id.bucket_postfix.hex}" } @@ -30,7 +30,7 @@ resource "aws_ecs_task_definition" "parity" { resource "aws_ecs_service" "parity" { count = "${var.number_of_nodes}" - name = "${format(local.service_name_fmt, count.index + 1, var.network_name)}" + name = "${format(local.service_name_fmt, count.index + 1, var.network_name, aws_ecs_task_definition.parity.revision)}" cluster = "${aws_ecs_cluster.parity.id}" task_definition = "${aws_ecs_task_definition.parity.arn}" launch_type = "EC2" diff --git a/terra/provisioner.go b/terra/provisioner.go index a06ca304..c1a89bb1 100644 --- a/terra/provisioner.go +++ b/terra/provisioner.go @@ -1,7 +1,6 @@ package terra import ( - "github.com/hashicorp/terraform/builtin/provisioners/local-exec" "github.com/hashicorp/terraform/builtin/provisioners/remote-exec" "github.com/hashicorp/terraform/terraform" ) @@ -11,9 +10,3 @@ func RemoteProvisioner(key string) (returnKey string, provisioner terraform.Reso return key, provisioner } - -func LocalProvisioner(key string) (returnKey string, provisioner terraform.ResourceProvisioner) { - provisioner = localexec.Provisioner() - - return key, provisioner -} diff --git a/terra/provisioner_test.go b/terra/provisioner_test.go index e72d261a..5e6528ce 100644 --- a/terra/provisioner_test.go +++ b/terra/provisioner_test.go @@ -1,21 +1,14 @@ package terra import ( - "github.com/hashicorp/terraform/builtin/provisioners/local-exec" + "github.com/hashicorp/terraform/builtin/provisioners/remote-exec" "github.com/stretchr/testify/assert" "testing" ) -func TestLocalProvisioner(t *testing.T) { - expectedKey := "local-exec" - key, provisioner := LocalProvisioner(expectedKey) - assert.Equal(t, expectedKey, key) - assert.IsType(t, localexec.Provisioner(), provisioner) -} - func TestRemoteProvisioner(t *testing.T) { expectedKey := "remote-exec" key, provisioner := RemoteProvisioner(expectedKey) assert.Equal(t, expectedKey, key) - assert.IsType(t, localexec.Provisioner(), provisioner) + assert.IsType(t, remoteexec.Provisioner(), provisioner) } diff --git a/terra/quorum/container_definition_bootstrap.tf b/terra/quorum/container_definition_bootstrap.tf index 69a14fbe..e5ed107a 100644 --- a/terra/quorum/container_definition_bootstrap.tf +++ b/terra/quorum/container_definition_bootstrap.tf @@ -6,6 +6,7 @@ locals { hosts_folder = "${local.shared_volume_container_path}/hosts" libfaketime_folder = "${local.shared_volume_container_path}/lib" libfaketime_file = "${local.shared_volume_container_path}/lib/libfaketime_value" + node_info_folder = "${local.shared_volume_container_path}/nodeinfo" metadata_bootstrap_container_status_file = "${local.shared_volume_container_path}/metadata_bootstrap_container_status" @@ -142,14 +143,19 @@ EOP "echo $HOST_IP > ${local.host_ip_file}", "export TASK_ARN=$(curl -s $ECS_CONTAINER_METADATA_URI/task | jq -r '.TaskARN')", "export REGION=$(echo $TASK_ARN | awk -F: '{ print $4}')", - "aws ecs describe-tasks --region $REGION --cluster ${local.ecs_cluster_name} --tasks $TASK_ARN | jq -r '.tasks[0] | .group' > ${local.service_file}", + "export SERVICE_GROUP=$(aws ecs describe-tasks --region $REGION --cluster ${local.ecs_cluster_name} --tasks $TASK_ARN | jq -r '.tasks[0] | .group')", + "echo $SERVICE_GROUP > ${local.service_file}", "mkdir -p ${local.hosts_folder}", "mkdir -p ${local.node_ids_folder}", + "mkdir -p ${local.node_info_folder}", "mkdir -p ${local.accounts_folder}", "mkdir -p ${local.libfaketime_folder}", "count=0; while [ $count -lt 1 ]; do count=$(ls ${local.libfaketime_folder} | grep libfaketime.so | wc -l); aws s3 cp s3://${local.s3_libfaketime_file} ${local.libfaketime_folder}/libfaketime.so > /dev/null 2>&1 | echo \"Wait for libfaketime to appear on S3 ... \"; sleep 1; done", "touch ${local.libfaketime_file}", - "aws sqs --region $REGION receive-message --queue-url ${aws_sqs_queue.faketime_queue.id} --visibility-timeout=300 | jq .Messages[].Body | tr -d '\\\"' > ${local.libfaketime_file}", + "export CLOCK_SKEW=$(aws sqs --region $REGION receive-message --queue-url ${aws_sqs_queue.faketime_queue.id} --visibility-timeout=300 | jq .Messages[].Body | tr -d '\\\"')", + "echo $CLOCK_SKEW > ${local.libfaketime_file}", + "echo \"$(echo $SERVICE_GROUP | sed 's/.*://') ip=$HOST_IP clock_skew=$CLOCK_SKEW chaos_testing_command=${join(" ", var.chaos_testing_run_command)}\" > ${local.node_info_folder}/${local.normalized_host_ip}", + "aws s3 cp ${local.node_info_folder}/${local.normalized_host_ip} s3://${local.s3_revision_folder}/nodeinfo/${local.normalized_host_ip} --sse aws:kms --sse-kms-key-id ${aws_kms_key.bucket.arn}", "aws s3 cp ${local.node_id_file} s3://${local.s3_revision_folder}/nodeids/${local.normalized_host_ip} --sse aws:kms --sse-kms-key-id ${aws_kms_key.bucket.arn}", "aws s3 cp ${local.host_ip_file} s3://${local.s3_revision_folder}/hosts/${local.normalized_host_ip} --sse aws:kms --sse-kms-key-id ${aws_kms_key.bucket.arn}", "aws s3 cp ${local.account_address_file} s3://${local.s3_revision_folder}/accounts/${local.normalized_host_ip} --sse aws:kms --sse-kms-key-id ${aws_kms_key.bucket.arn}", diff --git a/terra/quorum/ecs.tf b/terra/quorum/ecs.tf index 4da960ad..7626dd1c 100644 --- a/terra/quorum/ecs.tf +++ b/terra/quorum/ecs.tf @@ -1,5 +1,5 @@ locals { - service_name_fmt = "node-%0${min(length(format("%d", var.number_of_nodes)), length(format("%s", var.number_of_nodes))) + 1}d-%s" + service_name_fmt = "node-%0${min(length(format("%d", var.number_of_nodes)), length(format("%s", var.number_of_nodes))) + 1}d-%s-%s" ecs_cluster_name = "quorum-network-${var.network_name}" quorum_bucket = "${var.region}-ecs-${lower(var.network_name)}-${random_id.bucket_postfix.hex}" } @@ -31,7 +31,7 @@ resource "aws_ecs_task_definition" "quorum" { resource "aws_ecs_service" "quorum" { count = "${var.number_of_nodes}" - name = "${format(local.service_name_fmt, count.index + 1, var.network_name)}" + name = "${format(local.service_name_fmt, count.index + 1, var.network_name, aws_ecs_task_definition.quorum.revision)}" cluster = "${aws_ecs_cluster.quorum.id}" task_definition = "${aws_ecs_task_definition.quorum.arn}" launch_type = "EC2" diff --git a/terra/schema.go b/terra/schema.go index 2f36781e..f9bcc4d9 100644 --- a/terra/schema.go +++ b/terra/schema.go @@ -3,10 +3,11 @@ package terra import ( "fmt" "gopkg.in/yaml.v2" - "math/rand" "path" + "regexp" "strconv" - "time" + "strings" + "unicode" ) var ( @@ -16,6 +17,99 @@ var ( "genesis_difficulty", "genesis_nonce", } + ValidRegions = []string{ + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "eu-north-1", + "ap-east-1", + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-southeast-1", + "ap-southeast-2", + "ap-south-1", + "me-south-1", + "sa-east-1", + } + ValidInstanceTypes = []string{ + "a1.medium", + "a1.large", + "a1.xlarge", + "a1.2xlarge", + "a1.4xlarge", + "t3.nano", + "t3.micro", + "t3.small", + "t3.medium", + "t3.large", + "t3.xlarge", + "t3.2xlarge", + "t3a.nano", + "t3a.micro", + "t3a.small", + "t3a.medium", + "t3a.large", + "t3a.xlarge", + "t3a.2xlarge", + "t2.nano", + "t2.micro", + "t2.small", + "t2.medium", + "t2.large", + "t2.xlarge", + "t2.2xlarge", + "m5.large", + "m5.xlarge", + "m5.2xlarge", + "m5.4xlarge", + "m5.8xlarge", + "m5.12xlarge", + "m5.16xlarge", + "m5.24xlarge", + "m5.metal", + "m5d.large", + "m5d.xlarge", + "m5d.2xlarge", + "m5d.4xlarge", + "m5d.8xlarge", + "m5d.12xlarge", + "m5d.16xlarge", + "m5d.24xlarge", + "m5d.metal", + "m5a.large", + "m5a.xlarge", + "m5a.2xlarge", + "m5a.4xlarge", + "m5a.8xlarge", + "m5a.12xlarge", + "m5a.16xlarge", + "m5a.24xlarge", + "m5ad.large", + "m5ad.xlarge", + "m5ad.2xlarge", + "m5ad.4xlarge", + "m5ad.12xlarge", + "m5ad.24xlarge", + "m4.large", + "m4.xlarge", + "m4.2xlarge", + "m4.4xlarge", + "m4.10xlarge", + "m4.16xlarge", + } + ValidConsensusMechanisms = []string{"raft", "instanbul"} + SupportedFileTypes = []string{".yml", ".yaml"} + SupportedResourceTypes = []string{"variables"} + SupportedClockSkewSigns = []string{"+", "-"} + SupportedClockSkewUnits = []string{"s", "m", "h", "d", "y"} + StringVariablesToValidate = []string{"region", "default_region", "profile", "aws_access_key_id", "aws_secret_access_key"} ) type VariablesSchema struct { @@ -31,17 +125,21 @@ type variablesModel struct { } const ( - CurrentVersion = float64(0.3) - NetworkNameKey = "network_name" - ClockSkewKey = "faketime" - SchemaV02 = `version: 0.2 + CurrentVersion = float64(0.3) + MaxNetworkNameVarLength = 20 + NetworkNameKey = "network_name" + ClockSkewKey = "faketime" + NodeNumbersKey = "number_of_nodes" + QuorumDockerImageTagKey = "quorum_docker_image_tag" + NetworkNameTerraRegExp = "[a-z]([-a-z0-9]*[a-z0-9])?" + SchemaV02 = `version: 0.2 resourceType: variables variables: simpleKey: variable region: 'us-east-2' ## You can set region for deployment here default_region: 'us-east-2' ## If key region is not present it is default region setter profile: 'default' ## It chooses profile from your ~/.aws config. If not present, profile is "default" - network_name: 'sidechain-example' + network_name: 'sidechain' number_of_nodes: '5' quorum_docker_image_tag: '2.2.5' aws_access_key_id: 'dummyValue' ## It overrides access key id env variable. If omitted system env is used @@ -73,13 +171,6 @@ variables: ` ) -var ( - SupportedFileTypes = []string{".yml", ".yaml"} - SupportedResourceTypes = []string{"variables"} - SupportedClockSkewSigns = []string{"+", "-"} - SupportedClockSkewUnits = []string{"s", "m", "h", "d", "y"} -) - func (variablesSchema *VariablesSchema) Read() (err error) { err = variablesSchema.guard() @@ -87,8 +178,12 @@ func (variablesSchema *VariablesSchema) Read() (err error) { return err } - variablesSchema.mapGenesisVariables() - variablesSchema.mapNetworkName() + err = variablesSchema.mapGenesisVariables() + + if nil != err { + return err + } + err = variablesSchema.ValidateSchemaVariables() return err @@ -97,35 +192,65 @@ func (variablesSchema *VariablesSchema) Read() (err error) { func (variablesSchema *VariablesSchema) ValidateSchemaVariables() (err error) { err = variablesSchema.validateClockSkewVariable() - return err -} + if nil != err { + return err + } -func (variablesSchema *VariablesSchema) mapNetworkName() { - variables := variablesSchema.Variables + err = variablesSchema.validateNodeNumbersVariable() - if nil == variables { - return + if nil != err { + return err } - networkName := variables[NetworkNameKey] + err = variablesSchema.validateQuorumDockerImageTagVariable() - if nil == networkName { - return + if nil != err { + return err } - fixedSalt := rand.New(rand.NewSource(time.Now().UnixNano())) - randomString := fmt.Sprintf("%v", fixedSalt.Int()) - variablesSchema.Variables[NetworkNameKey] = fmt.Sprintf("%s-%v", networkName, randomString[0:8]) + err = variablesSchema.validateNetworkNameVariable() + + if nil != err { + return err + } + + err = variablesSchema.validateNonSpacesStringVariable() + + if nil != err { + return err + } + + err = variablesSchema.validateAwsProperties() + + if nil != err { + return err + } + + err = variablesSchema.validateConsensus() + + if nil != err { + return err + } + + return nil } -func (variablesSchema *VariablesSchema) mapGenesisVariables() { +func (variablesSchema *VariablesSchema) mapGenesisVariables() (err error) { for key, variable := range variablesSchema.Variables { if false == contains(VariablesKeyToHex, key) { continue } - variablesSchema.Variables[key] = ConvertInterfaceToHex(variable) + hexValue, err := ConvertInterfaceToHex(variable) + + if nil != err { + return err + } + + variablesSchema.Variables[key] = hexValue } + + return nil } func (variablesSchema *VariablesSchema) guard() (err error) { @@ -166,15 +291,16 @@ func guardExtension(filePath string) (err error) { fileExtension := path.Ext(filePath) if false == contains(SupportedFileTypes, fileExtension) { - return ClientError{fmt.Sprintf( - "%s is not in supported file types. Valid are: %s", - fileExtension, - SupportedFileTypes, - ), + return ClientError{ + fmt.Sprintf( + "%s is not in supported file types. Valid are: %s", + fileExtension, + SupportedFileTypes, + ), } } - return + return nil } func (variablesModel *variablesModel) guard() (err error) { @@ -207,7 +333,7 @@ func (variablesModel *variablesModel) guardVersion() (err error) { } } - return + return nil } func (variablesModel *variablesModel) guardResourceType() (err error) { @@ -222,7 +348,7 @@ func (variablesModel *variablesModel) guardResourceType() (err error) { } } - return + return nil } func (variablesModel *variablesModel) guardVariables() (err error) { @@ -232,7 +358,7 @@ func (variablesModel *variablesModel) guardVariables() (err error) { return ClientError{"No variables found"} } - return + return nil } func (variablesSchema *VariablesSchema) validateClockSkewVariable() (err error) { @@ -253,6 +379,24 @@ func (variablesSchema *VariablesSchema) validateClockSkewVariable() (err error) return nil } +func (variablesSchema *VariablesSchema) validateConsensus() (err error) { + desiredKey := "consensus_mechanism" + + value, ok := variablesSchema.Variables[desiredKey] + + if false == ok { + return nil + } + + if contains(ValidConsensusMechanisms, value.(string)) { + return nil + } + + return ClientError{ + fmt.Sprintf("Invalid %s, valid are: %s", desiredKey, ValidConsensusMechanisms), + } +} + func validateClockSkewVariable(variable interface{}) (err error) { variableString := variable.(string) sign := variableString[:1] @@ -306,6 +450,172 @@ func validateClockSkewVariable(variable interface{}) (err error) { return nil } +func (variablesSchema *VariablesSchema) validateNodeNumbersVariable() (err error) { + if nil == variablesSchema.Variables[NodeNumbersKey] { + return nil + } + + nodeNumbersVariable := variablesSchema.Variables[NodeNumbersKey].(string) + _, err = strconv.ParseInt(nodeNumbersVariable, 10, 64) + + if nil != err { + return ClientError{ + fmt.Sprintf( + "%s is not in supported node of numbers variable value type.", + nodeNumbersVariable, + ), + } + } + + return nil +} + +func (variablesSchema *VariablesSchema) validateQuorumDockerImageTagVariable() (err error) { + if nil == variablesSchema.Variables[QuorumDockerImageTagKey] { + return nil + } + + nodeNumbersVariable := variablesSchema.Variables[QuorumDockerImageTagKey].(string) + + versionIntegers := strings.Split(nodeNumbersVariable, ".") + + for _, intVal := range versionIntegers { + _, err = strconv.ParseInt(intVal, 10, 64) + + if nil != err { + return ClientError{"Invalid value, should be integer docker image tag version between dots"} + } + } + + return nil +} + +func (variablesSchema *VariablesSchema) validateNetworkNameVariable() (err error) { + if nil == variablesSchema.Variables[NetworkNameKey] { + return nil + } + + networkNameVariable := variablesSchema.Variables[NetworkNameKey].(string) + + if MaxNetworkNameVarLength < len(networkNameVariable) { + return ClientError{ + fmt.Sprintf( + "Network name is too long. Maximum allowed characters are %d", + MaxNetworkNameVarLength, + ), + } + } + + variableDeclarationRegex := regexp.MustCompile(NetworkNameTerraRegExp) + variableDeclarations := variableDeclarationRegex.FindString(networkNameVariable) + + if networkNameVariable != variableDeclarations { + return ClientError{"Network name is invalid"} + } + + return nil +} + +func (variablesSchema *VariablesSchema) validateNonSpacesStringVariable() (err error) { + if nil == variablesSchema.Variables { + return nil + } + + for key, variable := range variablesSchema.Variables { + if false != contains(StringVariablesToValidate, key) { + err = validateNonSpacesStringVariable(variable) + + if nil != err { + return ClientError{ + fmt.Sprintf( + "Variable with key: %s contains white space which is not allowed", + key, + ), + } + } + } + } + + return nil +} + +func (variablesSchema *VariablesSchema) validateAwsProperties() (err error) { + if nil == variablesSchema.Variables { + return nil + } + + for key, variable := range variablesSchema.Variables { + err = validateAwsRegions(key, variable) + + if nil != err { + return err + } + + err = validateAwsInstanceType(key, variable) + + if nil != err { + return err + } + } + + return nil +} + +func validateAwsRegions(key string, variable interface{}) (err error) { + desiredKeys := []string{"region", "default_region"} + + if false == contains(desiredKeys, key) { + return nil + } + + stringVariable := fmt.Sprintf("%v", variable) + + if contains(ValidRegions, stringVariable) { + return nil + } + + return ClientError{ + fmt.Sprintf( + "%s is not valid AWS region. Valid are: %s", + stringVariable, + ValidRegions, + ), + } +} + +func validateAwsInstanceType(key string, variable interface{}) (err error) { + desiredKey := "asg_instance_type" + + if key != desiredKey { + return nil + } + + stringVariable := fmt.Sprintf("%v", variable) + + if contains(ValidInstanceTypes, stringVariable) { + return nil + } + + return ClientError{ + fmt.Sprintf( + "%s is not valid %s, valid are: %s", + stringVariable, + desiredKey, + ValidInstanceTypes, + ), + } +} + +func validateNonSpacesStringVariable(variable interface{}) (err error) { + for _, v := range variable.(string) { + if unicode.IsSpace(v) { + return ClientError{} + } + } + + return nil +} + func contains(haystack []string, needle string) bool { for _, element := range haystack { if needle == element { diff --git a/terra/schema_test.go b/terra/schema_test.go index 44e381de..5f0ea712 100644 --- a/terra/schema_test.go +++ b/terra/schema_test.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/stretchr/testify/assert" "path" + "strconv" "testing" ) @@ -140,7 +141,7 @@ func TestVariablesSchema_Read_v02(t *testing.T) { variables := variablesSchema.Variables // Network name should look like `variable-[8-length-random-integer]` newNetworkName := variables["network_name"].(string) - assert.Equal(t, 17, len(newNetworkName)) + assert.Equal(t, 8, len(newNetworkName)) RemoveDummyFile(t, dummyFilePath) } @@ -161,6 +162,17 @@ func TestVariablesSchema_Read_WithHexUtil(t *testing.T) { RemoveDummyFile(t, dummyFilePath) } +func TestVariablesSchema_Read_WithHexUtil_Failure(t *testing.T) { + variablesSchema := VariablesSchema{} + dummyFilePath := "dummy.yml" + PrepareDummyFile(t, dummyFilePath, YamlFixtureWithInvalidHexUtils) + variablesSchema.Location = dummyFilePath + err := variablesSchema.Read() + assert.Error(t, err) + assert.IsType(t, &strconv.NumError{}, err) + RemoveDummyFile(t, dummyFilePath) +} + func TestVariablesSchema_ValidateSchemaVariables(t *testing.T) { variablesSchema := VariablesSchema{} dummyFilePath := "dummy.yml" @@ -174,31 +186,98 @@ func TestVariablesSchema_ValidateSchemaVariables(t *testing.T) { RemoveDummyFile(t, dummyFilePath) } -func TestVariablesSchema_ValidateSchemaVariablesFailure(t *testing.T) { +func TestVariablesSchema_Readv03Failure(t *testing.T) { variablesSchema := VariablesSchema{} dummyFilePath := "dummy.yml" PrepareDummyFile(t, dummyFilePath, IncorrectSignYamlV03Fixture) variablesSchema.Location = dummyFilePath err := variablesSchema.Read() - err = variablesSchema.ValidateSchemaVariables() assert.Error(t, err) assert.Equal(t, ClientError{Message: "@2s is not in supported faketime variable signs. Valid are: [+ -]"}, err) PrepareDummyFile(t, dummyFilePath, IncorrectUnitYamlV03Fixture) variablesSchema.Location = dummyFilePath err = variablesSchema.Read() - err = variablesSchema.ValidateSchemaVariables() assert.Error(t, err) assert.Equal(t, ClientError{Message: "+2x is not in supported faketime variable units. Valid are: [s m h d y]"}, err) PrepareDummyFile(t, dummyFilePath, IncorrectValueYamlV03Fixture) variablesSchema.Location = dummyFilePath err = variablesSchema.Read() - err = variablesSchema.ValidateSchemaVariables() assert.Error(t, err) assert.Equal(t, ClientError{Message: "Invalid value, should be integer between sign and faketime unit"}, err) + PrepareDummyFile(t, dummyFilePath, IncorrectNodesNumberYamlV03Fixture) + variablesSchema.Location = dummyFilePath + err = variablesSchema.Read() + assert.Error(t, err) + assert.Equal(t, ClientError{Message: "Abc is not in supported node of numbers variable value type."}, err) + + PrepareDummyFile(t, dummyFilePath, IncorrectQuorumDockerImageTagYamlV03Fixture) + variablesSchema.Location = dummyFilePath + err = variablesSchema.Read() + assert.Error(t, err) + assert.Equal(t, ClientError{Message: "Invalid value, should be integer docker image tag version between dots"}, err) + + PrepareDummyFile(t, dummyFilePath, IncorrectNetworkNameLengthYamlV03Fixture) + variablesSchema.Location = dummyFilePath + err = variablesSchema.Read() + assert.Error(t, err) + assert.Equal(t, ClientError{Message: "Network name is too long. Maximum allowed characters are 20"}, err) + + PrepareDummyFile(t, dummyFilePath, IncorrectNetworkNameStringYamlV03Fixture) + variablesSchema.Location = dummyFilePath + err = variablesSchema.Read() + err = variablesSchema.ValidateSchemaVariables() + assert.Error(t, err) + assert.Equal(t, ClientError{Message: "Network name is invalid"}, err) + + PrepareDummyFile(t, dummyFilePath, IncorrectStringVariablesYamlV03Fixture) + variablesSchema.Location = dummyFilePath + err = variablesSchema.Read() + assert.Error(t, err) + assert.Equal(t, ClientError{Message: "Variable with key: region contains white space which is not allowed"}, err) + + PrepareDummyFile(t, dummyFilePath, IncorrectAwsRegionType) + variablesSchema.Location = dummyFilePath + err = variablesSchema.Read() + assert.Error(t, err) + expectedError := ClientError{ + Message: fmt.Sprintf( + "kaz-uk-2 is not valid AWS region. Valid are: %s", + ValidRegions, + ), + } + assert.Equal(t, expectedError, err) + + PrepareDummyFile(t, dummyFilePath, IncorrectAwsInstanceType) + variablesSchema.Location = dummyFilePath + err = variablesSchema.Read() + assert.Error(t, err) + expectedError = ClientError{ + Message: fmt.Sprintf( + "%s is not valid %s, valid are: %s", + "someInvalid-instance-type.large", + "asg_instance_type", + ValidInstanceTypes, + ), + } + assert.Equal(t, expectedError, err) + + PrepareDummyFile(t, dummyFilePath, IncorrectConsensusMechanismType) + variablesSchema.Location = dummyFilePath + err = variablesSchema.Read() + assert.Error(t, err) + expectedError = ClientError{ + Message: fmt.Sprintf( + "Invalid %s, valid are: %s", + "consensus_mechanism", + ValidConsensusMechanisms, + ), + } + assert.Equal(t, expectedError, err) + RemoveDummyFile(t, dummyFilePath) } diff --git a/terra/terra.go b/terra/terra.go index 5a7e11d4..17fab5ba 100644 --- a/terra/terra.go +++ b/terra/terra.go @@ -202,7 +202,6 @@ func (client *Client) addDependencies() { client.platform.AddProvider(TemplateProvider("template")) client.platform.AddProvider(TlsProvider("tls")) - client.platform.AddProvisioner(LocalProvisioner("local-exec")) client.platform.AddProvisioner(RemoteProvisioner("remote-exec")) } diff --git a/terra/terra_test.go b/terra/terra_test.go index 7188bb58..930e4db6 100644 --- a/terra/terra_test.go +++ b/terra/terra_test.go @@ -2,7 +2,6 @@ package terra import ( "fmt" - "github.com/hashicorp/terraform/builtin/provisioners/local-exec" "github.com/hashicorp/terraform/builtin/provisioners/remote-exec" "github.com/johandry/terranova" "github.com/stretchr/testify/assert" @@ -369,11 +368,7 @@ func TestClient_DefaultClient(t *testing.T) { assert.True(t, ok) assert.IsType(t, aws.Provider(), provider) - provisioner, ok := provisioners["local-exec"] - assert.True(t, ok) - assert.IsType(t, localexec.Provisioner(), provisioner) - - provisioner, ok = provisioners["remote-exec"] + provisioner, ok := provisioners["remote-exec"] assert.True(t, ok) assert.IsType(t, remoteexec.Provisioner(), provisioner) } diff --git a/terra/util.go b/terra/util.go index 8b9cad1b..1a51dda8 100644 --- a/terra/util.go +++ b/terra/util.go @@ -10,51 +10,46 @@ import ( "strings" ) -func ConvertIntToHex(value int64) (hexInt string) { - bigInt := big.NewInt(value) - hexInt = hexutil.EncodeBig(bigInt) - - return hexInt -} - -func ConvertInterfaceToHex(variable interface{}) (hexInt string) { +func ConvertInterfaceToHex(variable interface{}) (hexInt string, err error) { switch valueInterface := variable.(type) { case float64: - return ConvertIntToHex(int64(variable.(float64))) + return convertIntToHex(int64(variable.(float64))), err case float32: - return ConvertIntToHex(int64(variable.(float32))) + return convertIntToHex(int64(variable.(float32))), err case int64: - return ConvertIntToHex(variable.(int64)) + return convertIntToHex(variable.(int64)), err case int32: - return ConvertIntToHex(int64(variable.(int32))) + return convertIntToHex(int64(variable.(int32))), err case int16: - return ConvertIntToHex(int64(variable.(int16))) + return convertIntToHex(int64(variable.(int16))), err case int8: - return ConvertIntToHex(int64(variable.(int8))) + return convertIntToHex(int64(variable.(int8))), err case int: - return ConvertIntToHex(int64(variable.(int))) + return convertIntToHex(int64(variable.(int))), err case uint64: - return ConvertIntToHex(int64(variable.(uint64))) + return convertIntToHex(int64(variable.(uint64))), err case uint32: - return ConvertIntToHex(int64(variable.(uint32))) + return convertIntToHex(int64(variable.(uint32))), err case uint16: - return ConvertIntToHex(int64(variable.(uint16))) + return convertIntToHex(int64(variable.(uint16))), err case uint8: - return ConvertIntToHex(int64(variable.(uint8))) + return convertIntToHex(int64(variable.(uint8))), err case uint: - return ConvertIntToHex(int64(variable.(uint))) + return convertIntToHex(int64(variable.(uint))), err case string: if strings.HasPrefix(valueInterface, "0x") { - return string(valueInterface) + return string(valueInterface), err } - float64Value, err := strconv.ParseFloat(valueInterface, 64) + + float64Value, err := strconv.ParseInt(valueInterface, 10, 64) + if nil != err { - return ConvertIntToHex(int64(0)) + return convertIntToHex(0), err } - return ConvertIntToHex(int64(float64Value)) + return convertIntToHex(int64(float64Value)), err default: - return ConvertIntToHex(int64(0)) + return convertIntToHex(int64(0)), ClientError{Message: "Invalid interface to hex conversion"} } } @@ -127,3 +122,10 @@ func fetchDeployDir() (err error, deployNameLocator string) { return err, deployNameLocator } + +func convertIntToHex(value int64) (hexInt string) { + bigInt := big.NewInt(value) + hexInt = hexutil.EncodeBig(bigInt) + + return hexInt +} diff --git a/terra/util_test.go b/terra/util_test.go index 6599e01f..a7a6107b 100644 --- a/terra/util_test.go +++ b/terra/util_test.go @@ -7,65 +7,78 @@ import ( "testing" ) -func TestConvertIntToHex(t *testing.T) { - valueToTest := int64(2) - hex := ConvertIntToHex(valueToTest) - assert.Equal(t, "0x2", hex) +func TestConvertInterfaceToHexFailure(t *testing.T) { + hex, err := ConvertInterfaceToHex("two") + assert.Equal(t, "0x0", hex) + assert.Error(t, err) + + hex, err = ConvertInterfaceToHex("dummy") + assert.Equal(t, "0x0", hex) + assert.Error(t, err) + + dummyValue := map[string]int{"key": 4} + hex, err = ConvertInterfaceToHex(dummyValue) + assert.Equal(t, "0x0", hex) + assert.Error(t, err) + + hex, err = ConvertInterfaceToHex(string(4)) + assert.Equal(t, "0x0", hex) + assert.Error(t, err) } func TestConvertInterfaceToHex(t *testing.T) { intValue := 2 - hex := ConvertInterfaceToHex(float64(intValue)) + hex, err := ConvertInterfaceToHex(float64(intValue)) assert.Equal(t, "0x2", hex) + assert.Nil(t, err) - hex = ConvertInterfaceToHex(float32(intValue)) + hex, err = ConvertInterfaceToHex(float32(intValue)) assert.Equal(t, "0x2", hex) + assert.Nil(t, err) - hex = ConvertInterfaceToHex(int64(intValue)) + hex, err = ConvertInterfaceToHex(int64(intValue)) assert.Equal(t, "0x2", hex) + assert.Nil(t, err) - hex = ConvertInterfaceToHex(int32(intValue)) + hex, err = ConvertInterfaceToHex(int32(intValue)) assert.Equal(t, "0x2", hex) + assert.Nil(t, err) - hex = ConvertInterfaceToHex(int16(intValue)) + hex, err = ConvertInterfaceToHex(int16(intValue)) assert.Equal(t, "0x2", hex) + assert.Nil(t, err) - hex = ConvertInterfaceToHex(int8(intValue)) + hex, err = ConvertInterfaceToHex(int8(intValue)) assert.Equal(t, "0x2", hex) + assert.Nil(t, err) - hex = ConvertInterfaceToHex(intValue) + hex, err = ConvertInterfaceToHex(intValue) assert.Equal(t, "0x2", hex) + assert.Nil(t, err) - hex = ConvertInterfaceToHex(uint64(intValue)) + hex, err = ConvertInterfaceToHex(uint64(intValue)) assert.Equal(t, "0x2", hex) + assert.Nil(t, err) - hex = ConvertInterfaceToHex(uint32(intValue)) + hex, err = ConvertInterfaceToHex(uint32(intValue)) assert.Equal(t, "0x2", hex) + assert.Nil(t, err) - hex = ConvertInterfaceToHex(uint16(intValue)) + hex, err = ConvertInterfaceToHex(uint16(intValue)) assert.Equal(t, "0x2", hex) + assert.Nil(t, err) - hex = ConvertInterfaceToHex(uint8(intValue)) + hex, err = ConvertInterfaceToHex(uint8(intValue)) assert.Equal(t, "0x2", hex) + assert.Nil(t, err) - hex = ConvertInterfaceToHex(uint(intValue)) + hex, err = ConvertInterfaceToHex(uint(intValue)) assert.Equal(t, "0x2", hex) + assert.Nil(t, err) - hex = ConvertInterfaceToHex(string(intValue)) - assert.Equal(t, "0x0", hex) - - hex = ConvertInterfaceToHex("0xE0000000") + hex, err = ConvertInterfaceToHex("0xE0000000") assert.Equal(t, "0xE0000000", hex) - - hex = ConvertInterfaceToHex("two") - assert.Equal(t, "0x0", hex) - - hex = ConvertInterfaceToHex("dummy") - assert.Equal(t, "0x0", hex) - - dummyValue := map[string]int{"key": intValue} - hex = ConvertInterfaceToHex(dummyValue) - assert.Equal(t, "0x0", hex) + assert.Nil(t, err) } func TestReadOutputLogVarFailure(t *testing.T) {