From 4372b67ddb036335cb40563e0c48fb9ff8b1c169 Mon Sep 17 00:00:00 2001 From: Alex Wood Date: Mon, 7 Oct 2024 12:13:43 +0100 Subject: [PATCH 1/8] support remote peering of networks, and add example tf for this --- .../terraform.tfvars.local_example | 4 + examples/platform_stack_gov/.gitignore | 4 + examples/platform_stack_gov/main.tf | 504 +++++++++++++++++ .../platform_stack_gov/main_discovered.tf | 531 ++++++++++++++++++ examples/platform_stack_gov/variables.tf | 20 + .../.gitignore | 4 + .../main.tf | 240 ++++++++ .../terraform.tfvars.example | 20 + .../variables.tf | 81 +++ kaleido/kaleidobase/providerdata.go | 5 +- kaleido/platform/authenticator.go | 218 +++++++ kaleido/platform/common.go | 147 +++++ kaleido/platform/mockserver_test.go | 5 + kaleido/platform/network.go | 234 +++++++- kaleido/platform/network_bootstrap.go | 224 ++++++++ kaleido/platform/network_bootstrap_test.go | 91 +++ kaleido/platform/network_test.go | 3 +- kaleido/platform/runtime.go | 10 + kaleido/platform/service.go | 135 ++++- kaleido/platform/service_test.go | 30 +- main.go | 1 + terraform.tfstate | 9 + 22 files changed, 2478 insertions(+), 42 deletions(-) create mode 100644 examples/platform_stack/terraform.tfvars.local_example create mode 100644 examples/platform_stack_gov/.gitignore create mode 100644 examples/platform_stack_gov/main.tf create mode 100644 examples/platform_stack_gov/main_discovered.tf create mode 100644 examples/platform_stack_gov/variables.tf create mode 100644 examples/platform_stack_two_env_besu_network/.gitignore create mode 100644 examples/platform_stack_two_env_besu_network/main.tf create mode 100644 examples/platform_stack_two_env_besu_network/terraform.tfvars.example create mode 100644 examples/platform_stack_two_env_besu_network/variables.tf create mode 100644 kaleido/platform/authenticator.go create mode 100644 kaleido/platform/network_bootstrap.go create mode 100644 kaleido/platform/network_bootstrap_test.go create mode 100644 terraform.tfstate diff --git a/examples/platform_stack/terraform.tfvars.local_example b/examples/platform_stack/terraform.tfvars.local_example new file mode 100644 index 0000000..db74263 --- /dev/null +++ b/examples/platform_stack/terraform.tfvars.local_example @@ -0,0 +1,4 @@ +kaleido_platform_api = "https://myaccount.kaleido.dev" +kaleido_platform_username = "key1" +kaleido_platform_password = "ac8255da-9ddf-4be4-92ad-daa957196e7a8e0328af-10b8-47f0-96ce-ebc042da7b89" +environment_name = "env_alex" \ No newline at end of file diff --git a/examples/platform_stack_gov/.gitignore b/examples/platform_stack_gov/.gitignore new file mode 100644 index 0000000..26eb080 --- /dev/null +++ b/examples/platform_stack_gov/.gitignore @@ -0,0 +1,4 @@ +.terraform* +terraform.d +terraform.tfstate* +terraform.tfvars \ No newline at end of file diff --git a/examples/platform_stack_gov/main.tf b/examples/platform_stack_gov/main.tf new file mode 100644 index 0000000..06fa06f --- /dev/null +++ b/examples/platform_stack_gov/main.tf @@ -0,0 +1,504 @@ +terraform { + required_providers { + kaleido = { + source = "kaleido-io/kaleido" + version = "1.1.0-rc.4" + } + } +} + +provider "kaleido" { + platform_api = var.kaleido_platform_api + platform_username = var.kaleido_platform_username + platform_password = var.kaleido_platform_password +} + +resource "kaleido_platform_environment" "env_0" { + name = var.environment_name +} + +resource "kaleido_platform_network" "net_0" { + type = "Besu" + name = "evmchain1" + environment = kaleido_platform_environment.env_0.id + config_json = jsonencode({ + bootstrapOptions = { + qbft = { + blockperiodseconds = 2 + } + } + }) +} + + +resource "kaleido_platform_runtime" "bnr" { + type = "BesuNode" + name = "evmchain1_node${count.index+1}" + environment = kaleido_platform_environment.env_0.id + config_json = jsonencode({}) + count = var.node_count +} + +resource "kaleido_platform_service" "bns" { + type = "BesuNode" + name = "evmchain1_node${count.index+1}" + environment = kaleido_platform_environment.env_0.id + runtime = kaleido_platform_runtime.bnr[count.index].id + config_json = jsonencode({ + network = { + id = kaleido_platform_network.net_0.id + } + }) + count = var.node_count +} + +resource "kaleido_platform_runtime" "gwr_0" { + type = "EVMGateway" + name = "evmchain1_gateway" + environment = kaleido_platform_environment.env_0.id + config_json = jsonencode({}) +} + +resource "kaleido_platform_service" "gws_0" { + type = "EVMGateway" + name = "evmchain1_gateway" + environment = kaleido_platform_environment.env_0.id + runtime = kaleido_platform_runtime.gwr_0.id + config_json = jsonencode({ + network = { + id = kaleido_platform_network.net_0.id + } + }) +} + +data "kaleido_platform_evm_netinfo" "gws_0" { + environment = kaleido_platform_environment.env_0.id + service = kaleido_platform_service.gws_0.id + depends_on = [ + kaleido_platform_service.bns, + kaleido_platform_service.gws_0 + ] +} + +resource "kaleido_platform_runtime" "kmr_0" { + type = "KeyManager" + name = "kms1" + environment = kaleido_platform_environment.env_0.id + config_json = jsonencode({}) +} + +resource "kaleido_platform_service" "kms_0" { + type = "KeyManager" + name = "kms1" + environment = kaleido_platform_environment.env_0.id + runtime = kaleido_platform_runtime.kmr_0.id + config_json = jsonencode({}) +} + +resource "kaleido_platform_kms_wallet" "wallet_0" { + type = "hdwallet" + name = "hdwallet1" + environment = kaleido_platform_environment.env_0.id + service = kaleido_platform_service.kms_0.id + config_json = jsonencode({}) +} + +resource "kaleido_platform_kms_key" "key_0" { + name = "key0" + environment = kaleido_platform_environment.env_0.id + service = kaleido_platform_service.kms_0.id + wallet = kaleido_platform_kms_wallet.wallet_0.id +} + +resource "kaleido_platform_runtime" "tmr_0" { + type = "TransactionManager" + name = "evmchain1_txmgr" + environment = kaleido_platform_environment.env_0.id + config_json = jsonencode({}) +} + +resource "kaleido_platform_service" "tms_0" { + type = "TransactionManager" + name = "evmchain1_txmgr" + environment = kaleido_platform_environment.env_0.id + runtime = kaleido_platform_runtime.tmr_0.id + config_json = jsonencode({ + keyManager = { + id: kaleido_platform_service.kms_0.id + } + type = "evm" + evm = { + confirmations = { + required = 0 + } + connector = { + evmGateway = { + id = kaleido_platform_service.gws_0.id + } + # url = var.json_rpc_url + # auth = { + # credSetRef = "rpc_auth" + # } + } + } + }) + # cred_sets = { + # "rpc_auth" = { + # type = "basic_auth" + # basic_auth = { + # username = var.json_rpc_username + # password = var.json_rpc_password + # } + # } + # } +} + +resource "kaleido_platform_runtime" "ffr_0" { + type = "FireFly" + name = "firefly1" + environment = kaleido_platform_environment.env_0.id + config_json = jsonencode({}) +} + +resource "kaleido_platform_service" "ffs_0" { + type = "FireFly" + name = "firefly1" + environment = kaleido_platform_environment.env_0.id + runtime = kaleido_platform_runtime.ffr_0.id + config_json = jsonencode({ + transactionManager = { + id = kaleido_platform_service.tms_0.id + } + }) +} + +resource "kaleido_platform_runtime" "cmr_0" { + type = "ContractManager" + name = "smart_contract_manager" + environment = kaleido_platform_environment.env_0.id + config_json = jsonencode({}) +} + +resource "kaleido_platform_service" "cms_0" { + type = "ContractManager" + name = "smart_contract_manager" + environment = kaleido_platform_environment.env_0.id + runtime = kaleido_platform_runtime.cmr_0.id + config_json = jsonencode({}) +} + +resource "kaleido_platform_runtime" "bir_0"{ + type = "BlockIndexer" + name = "block_indexer" + environment = kaleido_platform_environment.env_0.id + config_json = jsonencode({}) +} + +resource "kaleido_platform_service" "bis_0"{ + type = "BlockIndexer" + name = "block_indexer" + environment = kaleido_platform_environment.env_0.id + runtime = kaleido_platform_runtime.bir_0.id + config_json=jsonencode( + { + contractManager = { + id = kaleido_platform_service.cms_0.id + } + evmGateway = { + id = kaleido_platform_service.gws_0.id + } + } + ) + hostnames = {"${lower(replace(var.environment_name, "/[^\\w]/", ""))}_${kaleido_platform_network.net_0.name}" = ["ui", "rest"]} +} + +resource "kaleido_platform_runtime" "amr_0" { + type = "AssetManager" + name = "asset_manager1" + environment = kaleido_platform_environment.env_0.id + config_json = jsonencode({}) +} + +resource "kaleido_platform_service" "ams_0" { + type = "AssetManager" + name = "asset_manager1" + environment = kaleido_platform_environment.env_0.id + runtime = kaleido_platform_runtime.amr_0.id + config_json = jsonencode({ + keyManager = { + id: kaleido_platform_service.kms_0.id + } + }) +} + +resource "kaleido_platform_cms_build" "erc20" { + environment = kaleido_platform_environment.env_0.id + service = kaleido_platform_service.cms_0.id + type = "github" + name = "ERC20WithData" + path = "erc20_samples" + github = { + contract_url = "https://github.com/hyperledger/firefly-tokens-erc20-erc721/blob/main/samples/solidity/contracts/ERC20WithData.sol" + contract_name = "ERC20WithData" + } +} + +resource "kaleido_platform_cms_build" "erc721" { + environment = kaleido_platform_environment.env_0.id + service = kaleido_platform_service.cms_0.id + type = "github" + name = "ERC721WithData" + path = "erc721_samples" + github = { + contract_url = "https://github.com/hyperledger/firefly-tokens-erc20-erc721/blob/main/samples/solidity/contracts/ERC721WithData.sol" + contract_name = "ERC721WithData" + } + depends_on = [ kaleido_platform_cms_build.erc20 ] // don't compile in parallel (excessive disk usage for npm) +} + +resource "kaleido_platform_cms_action_deploy" "demotoken_erc20" { + environment = kaleido_platform_environment.env_0.id + service = kaleido_platform_service.cms_0.id + build = kaleido_platform_cms_build.erc20.id + name = "deploy_erc20" + firefly_namespace = kaleido_platform_service.ffs_0.name + signing_key = kaleido_platform_kms_key.key_0.address + params_json = jsonencode([ + "DemoToken", + "DTOK" + ]) + depends_on = [ data.kaleido_platform_evm_netinfo.gws_0 ] +} + +resource "kaleido_platform_cms_action_deploy" "demotoken_erc721" { + environment = kaleido_platform_environment.env_0.id + service = kaleido_platform_service.cms_0.id + build = kaleido_platform_cms_build.erc721.id + name = "deploy_erc721" + firefly_namespace = kaleido_platform_service.ffs_0.name + signing_key = kaleido_platform_kms_key.key_0.address + params_json = jsonencode([ + "DemoNFT", + "DNFT", + "demo://token/" + ]) + depends_on = [ data.kaleido_platform_evm_netinfo.gws_0 ] +} + +resource "kaleido_platform_cms_action_createapi" "erc20" { + environment = kaleido_platform_environment.env_0.id + service = kaleido_platform_service.cms_0.id + build = kaleido_platform_cms_build.erc20.id + name = "erc20withdata" + firefly_namespace = kaleido_platform_service.ffs_0.name + api_name = "erc20withdata" + depends_on = [ data.kaleido_platform_evm_netinfo.gws_0 ] +} + +resource "kaleido_platform_cms_action_createapi" "erc721" { + environment = kaleido_platform_environment.env_0.id + service = kaleido_platform_service.cms_0.id + build = kaleido_platform_cms_build.erc721.id + name = "erc721withdata" + firefly_namespace = kaleido_platform_service.ffs_0.name + api_name = "erc721withdata" + depends_on = [ data.kaleido_platform_evm_netinfo.gws_0 ] +} + +resource "kaleido_platform_ams_task" "erc20_indexer" { + environment = kaleido_platform_environment.env_0.id + service = kaleido_platform_service.ams_0.id + depends_on = [ kaleido_platform_ams_task.erc20_indexer ] + name = "erc20_indexer" + task_yaml = <- + [{ + "updateType": "create_or_ignore", + "name": "erc721_" & input.blockchainEvent.info.address + }] + assets: >- + [{ + "updateType": "create_or_update", + "collection": "erc721_" & input.blockchainEvent.info.address, + "name": "erc721_" & input.blockchainEvent.info.address & "_" & input.blockchainEvent.output.tokenId + }] + nfts: >- + [{ + "updateType": "create_or_ignore", + "asset": "erc721_" & input.blockchainEvent.info.address & "_" & input.blockchainEvent.output.tokenId, + "name": "erc721", + "standard": "ERC-721", + "address": input.blockchainEvent.info.address, + "tokenIndex": input.blockchainEvent.output.tokenId, + "uri": steps.query_uri.data.output + }] + transfers: >- + [{ + "protocolId": input.blockchainEvent.protocolId, + "asset": "erc721_" & input.blockchainEvent.info.address & "_" & input.blockchainEvent.output.tokenId, + "from": input.blockchainEvent.output.from, + "to": input.blockchainEvent.output.to, + "amount": "1", + "transactionHash": input.blockchainEvent.info.transactionHash, + "parent": { + "type": "nft", + "ref": "erc721" + } + }] + name: upsert_transfer + options: {} + skip: |- + input.blockchainEvent.info.signature != + "Transfer(address,address,uint256)" or + $boolean([input.blockchainEvent.output.tokenId]) = false + type: data_model_update + EOT +} + +resource "kaleido_platform_ams_fflistener" "erc721_indexer" { + environment = kaleido_platform_environment.env_0.id + service = kaleido_platform_service.ams_0.id + name = "erc721_indexer" + depends_on = [ kaleido_platform_ams_task.erc721_indexer ] + config_json = jsonencode({ + namespace = kaleido_platform_service.ffs_0.name, + taskName = "erc721_indexer", + blockchainEvents = { + createOptions = { + firstEvent = "0" + }, + abiEvents = [ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + } + ] + } + }) +} \ No newline at end of file diff --git a/examples/platform_stack_gov/main_discovered.tf b/examples/platform_stack_gov/main_discovered.tf new file mode 100644 index 0000000..9843b67 --- /dev/null +++ b/examples/platform_stack_gov/main_discovered.tf @@ -0,0 +1,531 @@ +terraform { + required_providers { + kaleido = { + source = "kaleido-io/kaleido" + version = "1.1.0-rc.4" + } + } +} + +provider "kaleido" { + platform_api = var.kaleido_platform_api + platform_username = var.kaleido_platform_username + platform_password = var.kaleido_platform_password +} + +resource "kaleido_platform_environment" "env_0" { + name = var.environment_name +} + +resource "kaleido_platform_network" "net_0" { + type = "Besu" + name = "evmchain1" + environment = kaleido_platform_environment.env_0.id + config_json = jsonencode({ + bootstrapOptions = { + qbft = { + blockperiodseconds = 2 + } + } + }) +} + +resource "kaleido_platform_network_connection" "conn_0" { + network = net_0.id + name = "conn0" + environment = kaleido_platform_environment.env_0.id + config_json = jsonencode({ + }) +} + +resource "kaleido_platform_network" "net_1" { + type = "Besu" + name = "evmchain1" + environment = kaleido_platform_environment.env_0.id + config_json = jsonencode({ + chainID = net_0.config.chainID + externalGenesis = true + }) +} + +resource "kaleido_platform_network_connection" "conn_1" { + network = net_1.id + name = "conn1" + environment = kaleido_platform_environment.env_0.id + config_json = jsonencode({ + + }) +} + + +resource "kaleido_platform_runtime" "bnr" { + type = "BesuNode" + name = "evmchain1_node${count.index+1}" + environment = kaleido_platform_environment.env_0.id + config_json = jsonencode({}) + count = var.node_count +} + +resource "kaleido_platform_service" "bns" { + type = "BesuNode" + name = "evmchain1_node${count.index+1}" + environment = kaleido_platform_environment.env_0.id + runtime = kaleido_platform_runtime.bnr[count.index].id + config_json = jsonencode({ + network = { + id = kaleido_platform_network.net_0.id + } + }) + count = var.node_count +} + +resource "kaleido_platform_runtime" "gwr_0" { + type = "EVMGateway" + name = "evmchain1_gateway" + environment = kaleido_platform_environment.env_0.id + config_json = jsonencode({}) +} + +resource "kaleido_platform_service" "gws_0" { + type = "EVMGateway" + name = "evmchain1_gateway" + environment = kaleido_platform_environment.env_0.id + runtime = kaleido_platform_runtime.gwr_0.id + config_json = jsonencode({ + network = { + id = kaleido_platform_network.net_0.id + } + }) +} + +data "kaleido_platform_evm_netinfo" "gws_0" { + environment = kaleido_platform_environment.env_0.id + service = kaleido_platform_service.gws_0.id + depends_on = [ + kaleido_platform_service.bns, + kaleido_platform_service.gws_0 + ] +} + +resource "kaleido_platform_runtime" "kmr_0" { + type = "KeyManager" + name = "kms1" + environment = kaleido_platform_environment.env_0.id + config_json = jsonencode({}) +} + +resource "kaleido_platform_service" "kms_0" { + type = "KeyManager" + name = "kms1" + environment = kaleido_platform_environment.env_0.id + runtime = kaleido_platform_runtime.kmr_0.id + config_json = jsonencode({}) +} + +resource "kaleido_platform_kms_wallet" "wallet_0" { + type = "hdwallet" + name = "hdwallet1" + environment = kaleido_platform_environment.env_0.id + service = kaleido_platform_service.kms_0.id + config_json = jsonencode({}) +} + +resource "kaleido_platform_kms_key" "key_0" { + name = "key0" + environment = kaleido_platform_environment.env_0.id + service = kaleido_platform_service.kms_0.id + wallet = kaleido_platform_kms_wallet.wallet_0.id +} + +resource "kaleido_platform_runtime" "tmr_0" { + type = "TransactionManager" + name = "evmchain1_txmgr" + environment = kaleido_platform_environment.env_0.id + config_json = jsonencode({}) +} + +resource "kaleido_platform_service" "tms_0" { + type = "TransactionManager" + name = "evmchain1_txmgr" + environment = kaleido_platform_environment.env_0.id + runtime = kaleido_platform_runtime.tmr_0.id + config_json = jsonencode({ + keyManager = { + id: kaleido_platform_service.kms_0.id + } + type = "evm" + evm = { + confirmations = { + required = 0 + } + connector = { + evmGateway = { + id = kaleido_platform_service.gws_0.id + } + # url = var.json_rpc_url + # auth = { + # credSetRef = "rpc_auth" + # } + } + } + }) + # cred_sets = { + # "rpc_auth" = { + # type = "basic_auth" + # basic_auth = { + # username = var.json_rpc_username + # password = var.json_rpc_password + # } + # } + # } +} + +resource "kaleido_platform_runtime" "ffr_0" { + type = "FireFly" + name = "firefly1" + environment = kaleido_platform_environment.env_0.id + config_json = jsonencode({}) +} + +resource "kaleido_platform_service" "ffs_0" { + type = "FireFly" + name = "firefly1" + environment = kaleido_platform_environment.env_0.id + runtime = kaleido_platform_runtime.ffr_0.id + config_json = jsonencode({ + transactionManager = { + id = kaleido_platform_service.tms_0.id + } + }) +} + +resource "kaleido_platform_runtime" "cmr_0" { + type = "ContractManager" + name = "smart_contract_manager" + environment = kaleido_platform_environment.env_0.id + config_json = jsonencode({}) +} + +resource "kaleido_platform_service" "cms_0" { + type = "ContractManager" + name = "smart_contract_manager" + environment = kaleido_platform_environment.env_0.id + runtime = kaleido_platform_runtime.cmr_0.id + config_json = jsonencode({}) +} + +resource "kaleido_platform_runtime" "bir_0"{ + type = "BlockIndexer" + name = "block_indexer" + environment = kaleido_platform_environment.env_0.id + config_json = jsonencode({}) +} + +resource "kaleido_platform_service" "bis_0"{ + type = "BlockIndexer" + name = "block_indexer" + environment = kaleido_platform_environment.env_0.id + runtime = kaleido_platform_runtime.bir_0.id + config_json=jsonencode( + { + contractManager = { + id = kaleido_platform_service.cms_0.id + } + evmGateway = { + id = kaleido_platform_service.gws_0.id + } + } + ) + hostnames = {"${lower(replace(var.environment_name, "/[^\\w]/", ""))}_${kaleido_platform_network.net_0.name}" = ["ui", "rest"]} +} + +resource "kaleido_platform_runtime" "amr_0" { + type = "AssetManager" + name = "asset_manager1" + environment = kaleido_platform_environment.env_0.id + config_json = jsonencode({}) +} + +resource "kaleido_platform_service" "ams_0" { + type = "AssetManager" + name = "asset_manager1" + environment = kaleido_platform_environment.env_0.id + runtime = kaleido_platform_runtime.amr_0.id + config_json = jsonencode({ + keyManager = { + id: kaleido_platform_service.kms_0.id + } + }) +} + +resource "kaleido_platform_cms_build" "erc20" { + environment = kaleido_platform_environment.env_0.id + service = kaleido_platform_service.cms_0.id + type = "github" + name = "ERC20WithData" + path = "erc20_samples" + github = { + contract_url = "https://github.com/hyperledger/firefly-tokens-erc20-erc721/blob/main/samples/solidity/contracts/ERC20WithData.sol" + contract_name = "ERC20WithData" + } +} + +resource "kaleido_platform_cms_build" "erc721" { + environment = kaleido_platform_environment.env_0.id + service = kaleido_platform_service.cms_0.id + type = "github" + name = "ERC721WithData" + path = "erc721_samples" + github = { + contract_url = "https://github.com/hyperledger/firefly-tokens-erc20-erc721/blob/main/samples/solidity/contracts/ERC721WithData.sol" + contract_name = "ERC721WithData" + } + depends_on = [ kaleido_platform_cms_build.erc20 ] // don't compile in parallel (excessive disk usage for npm) +} + +resource "kaleido_platform_cms_action_deploy" "demotoken_erc20" { + environment = kaleido_platform_environment.env_0.id + service = kaleido_platform_service.cms_0.id + build = kaleido_platform_cms_build.erc20.id + name = "deploy_erc20" + firefly_namespace = kaleido_platform_service.ffs_0.name + signing_key = kaleido_platform_kms_key.key_0.address + params_json = jsonencode([ + "DemoToken", + "DTOK" + ]) + depends_on = [ data.kaleido_platform_evm_netinfo.gws_0 ] +} + +resource "kaleido_platform_cms_action_deploy" "demotoken_erc721" { + environment = kaleido_platform_environment.env_0.id + service = kaleido_platform_service.cms_0.id + build = kaleido_platform_cms_build.erc721.id + name = "deploy_erc721" + firefly_namespace = kaleido_platform_service.ffs_0.name + signing_key = kaleido_platform_kms_key.key_0.address + params_json = jsonencode([ + "DemoNFT", + "DNFT", + "demo://token/" + ]) + depends_on = [ data.kaleido_platform_evm_netinfo.gws_0 ] +} + +resource "kaleido_platform_cms_action_createapi" "erc20" { + environment = kaleido_platform_environment.env_0.id + service = kaleido_platform_service.cms_0.id + build = kaleido_platform_cms_build.erc20.id + name = "erc20withdata" + firefly_namespace = kaleido_platform_service.ffs_0.name + api_name = "erc20withdata" + depends_on = [ data.kaleido_platform_evm_netinfo.gws_0 ] +} + +resource "kaleido_platform_cms_action_createapi" "erc721" { + environment = kaleido_platform_environment.env_0.id + service = kaleido_platform_service.cms_0.id + build = kaleido_platform_cms_build.erc721.id + name = "erc721withdata" + firefly_namespace = kaleido_platform_service.ffs_0.name + api_name = "erc721withdata" + depends_on = [ data.kaleido_platform_evm_netinfo.gws_0 ] +} + +resource "kaleido_platform_ams_task" "erc20_indexer" { + environment = kaleido_platform_environment.env_0.id + service = kaleido_platform_service.ams_0.id + depends_on = [ kaleido_platform_ams_task.erc20_indexer ] + name = "erc20_indexer" + task_yaml = <- + [{ + "updateType": "create_or_ignore", + "name": "erc721_" & input.blockchainEvent.info.address + }] + assets: >- + [{ + "updateType": "create_or_update", + "collection": "erc721_" & input.blockchainEvent.info.address, + "name": "erc721_" & input.blockchainEvent.info.address & "_" & input.blockchainEvent.output.tokenId + }] + nfts: >- + [{ + "updateType": "create_or_ignore", + "asset": "erc721_" & input.blockchainEvent.info.address & "_" & input.blockchainEvent.output.tokenId, + "name": "erc721", + "standard": "ERC-721", + "address": input.blockchainEvent.info.address, + "tokenIndex": input.blockchainEvent.output.tokenId, + "uri": steps.query_uri.data.output + }] + transfers: >- + [{ + "protocolId": input.blockchainEvent.protocolId, + "asset": "erc721_" & input.blockchainEvent.info.address & "_" & input.blockchainEvent.output.tokenId, + "from": input.blockchainEvent.output.from, + "to": input.blockchainEvent.output.to, + "amount": "1", + "transactionHash": input.blockchainEvent.info.transactionHash, + "parent": { + "type": "nft", + "ref": "erc721" + } + }] + name: upsert_transfer + options: {} + skip: |- + input.blockchainEvent.info.signature != + "Transfer(address,address,uint256)" or + $boolean([input.blockchainEvent.output.tokenId]) = false + type: data_model_update + EOT +} + +resource "kaleido_platform_ams_fflistener" "erc721_indexer" { + environment = kaleido_platform_environment.env_0.id + service = kaleido_platform_service.ams_0.id + name = "erc721_indexer" + depends_on = [ kaleido_platform_ams_task.erc721_indexer ] + config_json = jsonencode({ + namespace = kaleido_platform_service.ffs_0.name, + taskName = "erc721_indexer", + blockchainEvents = { + createOptions = { + firstEvent = "0" + }, + abiEvents = [ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + } + ] + } + }) +} \ No newline at end of file diff --git a/examples/platform_stack_gov/variables.tf b/examples/platform_stack_gov/variables.tf new file mode 100644 index 0000000..a066288 --- /dev/null +++ b/examples/platform_stack_gov/variables.tf @@ -0,0 +1,20 @@ +variable "kaleido_platform_api" { + type = string +} + +variable "kaleido_platform_username" { + type = string +} + +variable "kaleido_platform_password" { + type = string +} + +variable "environment_name" { + type = string +} + +variable "node_count" { + type = number + default = 1 +} diff --git a/examples/platform_stack_two_env_besu_network/.gitignore b/examples/platform_stack_two_env_besu_network/.gitignore new file mode 100644 index 0000000..26eb080 --- /dev/null +++ b/examples/platform_stack_two_env_besu_network/.gitignore @@ -0,0 +1,4 @@ +.terraform* +terraform.d +terraform.tfstate* +terraform.tfvars \ No newline at end of file diff --git a/examples/platform_stack_two_env_besu_network/main.tf b/examples/platform_stack_two_env_besu_network/main.tf new file mode 100644 index 0000000..5a901b9 --- /dev/null +++ b/examples/platform_stack_two_env_besu_network/main.tf @@ -0,0 +1,240 @@ +terraform { + required_providers { + kaleido = { + source = "kaleido-io/kaleido" + version = "1.1.0-rc.4" + configuration_aliases = [ kaleido.originator, kaleido.secondary ] + } + } +} + +provider "kaleido" { + alias = "originator" + platform_api = var.originator_api_url + platform_username = var.originator_api_key_name + platform_password = var.originator_api_key_value +} + +provider "kaleido" { + alias = "secondary" + platform_api = var.secondary_api_url + platform_username = var.secondary_api_key_name + platform_password = var.secondary_api_key_value +} + +// Environment 1 - the originator of the network + +resource "kaleido_platform_environment" "env_og" { + provider = kaleido.originator + name = var.originator_name +} + +resource "kaleido_platform_network" "net_og" { + provider = kaleido.originator + type = "Besu" + name = var.originator_name + environment = kaleido_platform_environment.env_og.id + init_mode = "automated" + + config_json = jsonencode({ + chainID = 12345 + bootstrapOptions = { + qbft = { + blockperiodseconds = 5 + } + eipBlockConfig = { + shanghaiTime = 0 + } + initialBalances = { + "0x12F62772C4652280d06E64CfBC9033d409559aD4" = "0x111111111111", + } + blockConfigFlags = { + zeroBaseFee = true + } + } + }) +} + + +resource "kaleido_platform_runtime" "bnr_signer_net_og" { + provider = kaleido.originator + type = "BesuNode" + name = "${var.originator_name}_signer${count.index+1}" + environment = kaleido_platform_environment.env_og.id + config_json = jsonencode({}) + count = var.originator_signer_count +} + +resource "kaleido_platform_service" "bns_signer_net_og" { + provider = kaleido.originator + type = "BesuNode" + name = "${var.originator_name}_signer${count.index+1}" + environment = kaleido_platform_environment.env_og.id + runtime = kaleido_platform_runtime.bnr_signer_net_og[count.index].id + config_json = jsonencode({ + network = { + id = kaleido_platform_network.net_og.id + } + signer = true + }) + count = var.originator_signer_count +} + +resource "kaleido_platform_runtime" "bnr_peer_net_og" { + provider = kaleido.originator + type = "BesuNode" + name = "${var.originator_name}_peer${count.index+1}" + environment = kaleido_platform_environment.env_og.id + zone = var.originator_peer_network_dz + config_json = jsonencode({}) + count = var.originator_peer_count +} + +resource "kaleido_platform_service" "bns_peer_net_og" { + provider = kaleido.originator + type = "BesuNode" + name = "${var.originator_name}_peer${count.index+1}" + environment = kaleido_platform_environment.env_og.id + runtime = kaleido_platform_runtime.bnr_peer_net_og[count.index].id + config_json = jsonencode({ + network = { + id = kaleido_platform_network.net_og.id + } + signer = false + }) + count = var.originator_peer_count +} + +resource "kaleido_platform_runtime" "gwr_net_og" { + provider = kaleido.originator + type = "EVMGateway" + name = "${var.originator_name}_gateway" + environment = kaleido_platform_environment.env_og.id + config_json = jsonencode({}) + count = var.originator_gateway_count +} + + +resource "kaleido_platform_service" "gws_net_og" { + provider = kaleido.originator + type = "EVMGateway" + name = "${var.originator_name}_gateway" + environment = kaleido_platform_environment.env_og.id + runtime = kaleido_platform_runtime.gwr_net_og[count.index].id + config_json = jsonencode({ + network = { + id = kaleido_platform_network.net_og.id + } + }) + count = var.originator_gateway_count +} + +data "kaleido_platform_network_bootstrap_data" "net_og_bootstrap" { + provider = kaleido.originator + environment = kaleido_platform_environment.env_og.id + network = kaleido_platform_network.net_og.id + depends_on = [ + kaleido_platform_service.bns_signer_net_og, + kaleido_platform_network.net_og + ] +} + + + +// Environment 2 - another member of the network + +resource "kaleido_platform_environment" "env_sec" { + provider = kaleido.secondary + name = var.secondary_name +} + +resource "kaleido_platform_network" "net_sec" { + provider = kaleido.secondary + type = "Besu" + name = var.secondary_name + environment = kaleido_platform_environment.env_sec.id + init_mode = "manual" + file_sets = data.kaleido_platform_network_bootstrap_data.net_og_bootstrap.bootstrap_files != null ? { + init = data.kaleido_platform_network_bootstrap_data.net_og_bootstrap.bootstrap_files + } : {} + init_files = data.kaleido_platform_network_bootstrap_data.net_og_bootstrap.bootstrap_files != null ? "init" : null + config_json = jsonencode({}) + depends_on = [kaleido_platform_network.net_og, kaleido_platform_service.bns_signer_net_og, data.kaleido_platform_network_bootstrap_data.net_og_bootstrap] +} + +resource "kaleido_platform_runtime" "bnr_peer_net_sec" { + provider = kaleido.secondary + type = "BesuNode" + name = "${var.secondary_name}_peer${count.index+1}" + environment = kaleido_platform_environment.env_sec.id + zone = var.secondary_peer_network_dz + config_json = jsonencode({}) + count = var.secondary_peer_count + depends_on = [kaleido_platform_network.net_sec] +} + +resource "kaleido_platform_service" "bns_peer_net_sec" { + provider = kaleido.secondary + type = "BesuNode" + name = "${var.secondary_name}_peer${count.index+1}" + environment = kaleido_platform_environment.env_sec.id + runtime = kaleido_platform_runtime.bnr_peer_net_sec[count.index].id + config_json = jsonencode({ + network = { + id = kaleido_platform_network.net_sec.id + } + signer = false + }) + count = var.secondary_peer_count +} + +resource "kaleido_platform_runtime" "gwr_net_sec" { + provider = kaleido.secondary + type = "EVMGateway" + name = "${var.originator_name}_gateway" + environment = kaleido_platform_environment.env_sec.id + config_json = jsonencode({}) + depends_on = [kaleido_platform_network.net_sec] + count = var.secondary_gateway_count +} + +resource "kaleido_platform_service" "gws_net_sec" { + provider = kaleido.secondary + type = "EVMGateway" + name = "${var.originator_name}_gateway" + environment = kaleido_platform_environment.env_sec.id + runtime = kaleido_platform_runtime.gwr_net_sec[count.index].id + config_json = jsonencode({ + network = { + id = kaleido_platform_network.net_sec.id + } + }) + count = var.secondary_gateway_count +} + + +// Authenticators +resource "kaleido_platform_authenticator" "net_sec_authenticator" { + provider = kaleido.secondary + type = "Permitted" + name = "${var.secondary_name}_auth" + environment = kaleido_platform_environment.env_sec.id + network = kaleido_platform_network.net_sec.id + zone = var.secondary_peer_network_dz + conn = var.secondary_peer_network_connection + permitted_json = jsonencode({ peers = [ for peer in resource.kaleido_platform_service.bns_peer_net_og : jsondecode(peer.connectivity_json) ] }) + depends_on = [kaleido_platform_network.net_sec, kaleido_platform_service.bns_peer_net_og] +} + + +resource "kaleido_platform_authenticator" "net_og_authenticator" { + provider = kaleido.originator + type = "Permitted" + name = "${var.originator_name}_auth" + environment = kaleido_platform_environment.env_og.id + network = kaleido_platform_network.net_og.id + zone = var.originator_peer_network_dz + conn = var.originator_peer_network_connection + permitted_json = jsonencode({ peers = [ for peer in resource.kaleido_platform_service.bns_peer_net_sec : jsondecode(peer.connectivity_json) ] }) + depends_on = [kaleido_platform_network.net_og, kaleido_platform_service.bns_peer_net_sec] +} diff --git a/examples/platform_stack_two_env_besu_network/terraform.tfvars.example b/examples/platform_stack_two_env_besu_network/terraform.tfvars.example new file mode 100644 index 0000000..b711537 --- /dev/null +++ b/examples/platform_stack_two_env_besu_network/terraform.tfvars.example @@ -0,0 +1,20 @@ +kaleido_platform_api_baseurl = "kaleido.dev" + +originator_account_name = "myaccount" +originator_api_key_name = "key1" +originator_api_key_value = "1dbf8a3a-51c0-4e5e-a36a-50c58535db1b60cb9dc4-691c-4243-b88e-221a890cc511" + +secondary_account_name = "other" +secondary_api_key_name = "key1" +secondary_api_key_value = "fc07c759-dace-445e-9a06-8c21cca4e7080cd9e28d-32f6-4c49-96b6-839d009fb1fb" + +originator_name = "besu_originator" +originator_signer_count = 2 +originator_peer_count = 1 +originator_peer_network_dz = "local-external-zone-1" +originator_peer_network_connection = "local" + +secondary_name = "besu_secondary" +secondary_peer_count = 2 +secondary_peer_network_dz = "local-external-zone-1" +secondary_peer_network_connection = "local" diff --git a/examples/platform_stack_two_env_besu_network/variables.tf b/examples/platform_stack_two_env_besu_network/variables.tf new file mode 100644 index 0000000..a2b2428 --- /dev/null +++ b/examples/platform_stack_two_env_besu_network/variables.tf @@ -0,0 +1,81 @@ + +variable "originator_api_url" { + type = string +} + +variable "originator_api_key_name" { + type = string +} + +variable "originator_api_key_value" { + type = string +} + +variable "secondary_api_url" { + type = string +} + +variable "secondary_api_key_name" { + type = string +} + +variable "secondary_api_key_value" { + type = string +} + +variable "originator_name" { + type = string +} + +variable "secondary_name" { + type = string +} + +variable "originator_signer_count" { + type = number + default = 1 +} + +variable "originator_peer_count" { + type = number + default = 1 +} + +variable "secondary_peer_count" { + type = number + default = 1 +} + +variable "originator_peer_network_dz" { + type = string +} + +variable "originator_peer_network_connection" { + type = string +} + +variable "secondary_peer_network_dz" { + type = string +} + +variable "secondary_peer_network_connection" { + type = string +} + +variable "originator_gateway_count" { + type = number + default = 1 + validation { + condition = contains([0, 1], var.originator_gateway_count) + error_message = "Valid values for originator_gateway_count are (0, 1)" + } +} + +variable "secondary_gateway_count" { + type = number + default = 1 + validation { + condition = contains([0, 1], var.secondary_gateway_count) + error_message = "Valid values for secondary_gateway_count are (0, 1)" + } +} diff --git a/kaleido/kaleidobase/providerdata.go b/kaleido/kaleidobase/providerdata.go index 4a37ab6..fea4982 100644 --- a/kaleido/kaleidobase/providerdata.go +++ b/kaleido/kaleidobase/providerdata.go @@ -15,6 +15,7 @@ package kaleidobase import ( "context" + "crypto/tls" "fmt" "net/http" "os" @@ -89,7 +90,9 @@ func NewProviderData(logCtx context.Context, conf *ProviderModel) *ProviderData } platform := resty.New(). SetTransport(http.DefaultTransport). - SetBaseURL(platformAPI) + SetBaseURL(platformAPI). + SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true}) //TODO remove this + if platformUsername != "" && platformPassword != "" { platform = platform.SetBasicAuth(platformUsername, platformPassword) } diff --git a/kaleido/platform/authenticator.go b/kaleido/platform/authenticator.go new file mode 100644 index 0000000..43b1ba6 --- /dev/null +++ b/kaleido/platform/authenticator.go @@ -0,0 +1,218 @@ +// Copyright © Kaleido, Inc. 2024 + +// 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 platform + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "time" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +type AuthenticatorResourceModel struct { + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + Type types.String `tfsdk:"type"` + Environment types.String `tfsdk:"environment"` + Network types.String `tfsdk:"network"` + Zone types.String `tfsdk:"zone"` + Conn types.String `tfsdk:"conn"` + PermittedJSON types.String `tfsdk:"permitted_json"` +} + +type AuthenticatorAPIModel struct { + ID string `json:"id,omitempty"` + Created *time.Time `json:"created,omitempty"` + Updated *time.Time `json:"updated,omitempty"` + Type string `json:"type"` + Name string `json:"name"` + NetworkID string `json:"networkId,omitempty"` + Zone string `json:"zone,omitempty"` + Connection string `json:"connection,omitempty"` + Permitted map[string]interface{} `json:"permitted,omitempty"` + Deleted bool `json:"deleted,omitempty"` + Status string `json:"status,omitempty"` +} + +func AuthenticatorResourceFactory() resource.Resource { + return &authenticatorResource{} +} + +type authenticatorResource struct { + commonResource +} + +func (r *authenticatorResource) Metadata(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "kaleido_platform_authenticator" +} + +func (r *authenticatorResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "id": &schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, + }, + "type": &schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + }, + "name": &schema.StringAttribute{ + Required: true, + }, + "network": &schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + }, + "environment": &schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + }, + "zone": &schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + }, + "conn": &schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + }, + "permitted_json": &schema.StringAttribute{ + Optional: true, + }, + }, + } +} + +func (data *AuthenticatorResourceModel) toAPI(ctx context.Context, api *AuthenticatorAPIModel, diagnostics *diag.Diagnostics) { + // required fields + api.Type = data.Type.ValueString() + api.Name = data.Name.ValueString() + api.NetworkID = data.Network.ValueString() + + api.Zone = data.Zone.ValueString() + api.Connection = data.Conn.ValueString() + + // optional fields + api.Permitted = map[string]interface{}{} + if !data.PermittedJSON.IsNull() && data.PermittedJSON.String() != "{}" { + _ = json.Unmarshal([]byte(data.PermittedJSON.ValueString()), &api.Permitted) + } + +} + +func (api *AuthenticatorAPIModel) toData(data *AuthenticatorResourceModel, diagnostics *diag.Diagnostics) { + data.ID = types.StringValue(api.ID) + data.Name = types.StringValue(api.Name) + + data.Network = types.StringValue(api.NetworkID) + data.Zone = types.StringValue(api.Zone) + data.Conn = types.StringValue(api.Connection) + + info := make(map[string]attr.Value) + for k, v := range api.Permitted { + v, isString := v.(string) + if isString && v != "" { + info[k] = types.StringValue(v) + } + } +} + +func (r *authenticatorResource) apiPath(data *AuthenticatorResourceModel) string { + path := fmt.Sprintf("/api/v1/environments/%s/networks/%s/authenticators", data.Environment.ValueString(), data.Network.ValueString()) + if data.ID.ValueString() != "" { + path = path + "/" + data.ID.ValueString() + } + return path +} + +func (r *authenticatorResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + + var data AuthenticatorResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + + var api AuthenticatorAPIModel + data.toAPI(ctx, &api, &resp.Diagnostics) + ok, _ := r.apiRequest(ctx, http.MethodPost, r.apiPath(&data), api, &api, &resp.Diagnostics) + if !ok { + return + } + + api.toData(&data, &resp.Diagnostics) // need the ID copied over + r.waitForReadyStatus(ctx, r.apiPath(&data), &resp.Diagnostics) + api.toData(&data, &resp.Diagnostics) // need the latest status after the readiness check completes, to extract generated values + resp.Diagnostics.Append(resp.State.Set(ctx, data)...) + +} + +func (r *authenticatorResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + + var data AuthenticatorResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("id"), &data.ID)...) + + // Read full current object + var api AuthenticatorAPIModel + if ok, _ := r.apiRequest(ctx, http.MethodGet, r.apiPath(&data), nil, &api, &resp.Diagnostics); !ok { + return + } + + // Update from plan + data.toAPI(ctx, &api, &resp.Diagnostics) + if ok, _ := r.apiRequest(ctx, http.MethodPut, r.apiPath(&data), api, &api, &resp.Diagnostics); !ok { + return + } + + api.toData(&data, &resp.Diagnostics) // need the ID copied over + r.waitForReadyStatus(ctx, r.apiPath(&data), &resp.Diagnostics) + api.toData(&data, &resp.Diagnostics) // need the latest status after the readiness check completes, to extract generated values + resp.Diagnostics.Append(resp.State.Set(ctx, data)...) +} + +func (r *authenticatorResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var data AuthenticatorResourceModel + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + var api AuthenticatorAPIModel + api.ID = data.ID.ValueString() + ok, status := r.apiRequest(ctx, http.MethodGet, r.apiPath(&data), nil, &api, &resp.Diagnostics, Allow404()) + if !ok { + return + } + if status == 404 { + resp.State.RemoveResource(ctx) + return + } + + api.toData(&data, &resp.Diagnostics) + resp.Diagnostics.Append(resp.State.Set(ctx, data)...) +} + +func (r *authenticatorResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var data AuthenticatorResourceModel + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + _, _ = r.apiRequest(ctx, http.MethodDelete, r.apiPath(&data), nil, nil, &resp.Diagnostics, Allow404()) + + r.waitForRemoval(ctx, r.apiPath(&data), &resp.Diagnostics) +} diff --git a/kaleido/platform/common.go b/kaleido/platform/common.go index c574907..abe0a61 100644 --- a/kaleido/platform/common.go +++ b/kaleido/platform/common.go @@ -106,6 +106,119 @@ func (r *commonResource) apiRequest(ctx context.Context, method, path string, bo req := r.Platform.R(). SetContext(ctx). SetDoNotParseResponse(true) + + if isYaml { + req = req.SetHeader("Content-type", "application/x-yaml") + } else { + req = req.SetHeader("Content-type", "application/json") + } + if bodyBytes != nil { + req = req.SetBody(bodyBytes) + } + res, err = req.Execute(method, path) + } + var statusString string + var rawBytes []byte + statusCode := -1 + if err != nil { + statusString = err.Error() + } else { + statusCode = res.StatusCode() + statusString = fmt.Sprintf("%d %s", statusCode, res.Status()) + } + tflog.Debug(ctx, fmt.Sprintf("<-- %s %s%s [%s]", method, r.Platform.BaseURL, path, statusString)) + if res != nil && res.RawResponse != nil { + defer res.RawResponse.Body.Close() + rawBytes, err = io.ReadAll(res.RawBody()) + } + if rawBytes != nil { + tflog.Debug(ctx, fmt.Sprintf("Response: %s", rawBytes)) + } + if err == nil && res.IsSuccess() && result != nil { + err = json.Unmarshal(rawBytes, &result) + } + ok := true + if err != nil { + ok = false + isCancelled := false + select { + case <-ctx.Done(): + isCancelled = true + default: + } + errorInfo := fmt.Sprintf("%s %s failed with error: %s", method, path, err) + if isCancelled { + for _, o := range options { + if o.CancelInfo != "" { + errorInfo = fmt.Sprintf("%s %s", errorInfo, o.CancelInfo) + } + } + diagnostics.AddError( + fmt.Sprintf("%s cancelled", method), + errorInfo, + ) + } else { + diagnostics.AddError( + fmt.Sprintf("%s failed", method), + errorInfo, + ) + } + } else if !res.IsSuccess() { + isOk404 := false + if statusCode == 404 { + for _, o := range options { + if o.allow404 { + isOk404 = true + } + } + } + if !isOk404 { + ok = false + errorInfo := fmt.Sprintf("%s %s returned status code %d: %s", method, path, statusCode, rawBytes) + diagnostics.AddError( + fmt.Sprintf("%s failed", method), + errorInfo, + ) + } + } + return ok, statusCode +} + +func (r *commonDataSource) apiRequest(ctx context.Context, method, path string, body, result interface{}, diagnostics *diag.Diagnostics, options ...*APIRequestOption) (bool, int) { + var bodyBytes []byte + var err error + bodyString := "" + isYaml := false + for _, o := range options { + isYaml = isYaml || o.yamlBody + } + if body != nil { + switch tBody := body.(type) { + case []byte: + bodyBytes = tBody + bodyString = string(tBody) + case string: + bodyBytes = []byte(tBody) + bodyString = tBody + default: + if isYaml { + bodyBytes, err = yaml.Marshal(body) + } else { + bodyBytes, err = json.Marshal(body) + } + } + if err == nil { + bodyString = string(bodyBytes) + } + } + tflog.Debug(ctx, fmt.Sprintf("--> %s %s%s %s", method, r.Platform.BaseURL, path, bodyString)) + + var res *resty.Response + if err == nil { + req := r.Platform.R(). + SetContext(ctx). + SetDoNotParseResponse(true) + if isYaml { req = req.SetHeader("Content-type", "application/x-yaml") } else { @@ -224,6 +337,7 @@ func (r *commonResource) waitForRemoval(ctx context.Context, path string, diagno func DataSources() []func() datasource.DataSource { return []func() datasource.DataSource{ EVMNetInfoDataSourceFactory, + NetworkBootstrapDatasourceModelFactory, } } @@ -247,5 +361,38 @@ func Resources() []func() resource.Resource { AMSDMUpsertResourceFactory, AMSVariableSetResourceFactory, FireFlyRegistrationResourceFactory, + AuthenticatorResourceFactory, } } + +type FileSetAPI struct { + Name string `json:"name"` + Files map[string]*FileAPI `json:"files"` +} + +type FileAPI struct { + Type string `json:"type,omitempty"` + Data FileDataAPI `json:"data,omitempty"` +} + +type FileDataAPI struct { + Base64 string `json:"base64,omitempty"` + Text string `json:"text,omitempty"` + Hex string `json:"hex,omitempty"` +} + +type CredSetAPI struct { + Name string `json:"name"` + Type string `json:"type,omitempty"` + BasicAuth *CredSetBasicAuthAPI `json:"basicAuth,omitempty"` + Key *CredSetKeyAPI `json:"key,omitempty"` +} + +type CredSetBasicAuthAPI struct { + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` +} + +type CredSetKeyAPI struct { + Value string `json:"value,omitempty"` +} diff --git a/kaleido/platform/mockserver_test.go b/kaleido/platform/mockserver_test.go index 1dcd126..f52500c 100644 --- a/kaleido/platform/mockserver_test.go +++ b/kaleido/platform/mockserver_test.go @@ -40,6 +40,7 @@ type mockPlatform struct { runtimes map[string]*RuntimeAPIModel services map[string]*ServiceAPIModel networks map[string]*NetworkAPIModel + networkinitdatas map[string]*NetworkInitData kmsWallets map[string]*KMSWalletAPIModel kmsKeys map[string]*KMSKeyAPIModel cmsBuilds map[string]*CMSBuildAPIModel @@ -65,6 +66,7 @@ func startMockPlatformServer(t *testing.T) *mockPlatform { runtimes: make(map[string]*RuntimeAPIModel), services: make(map[string]*ServiceAPIModel), networks: make(map[string]*NetworkAPIModel), + networkinitdatas: make(map[string]*NetworkInitData), kmsWallets: make(map[string]*KMSWalletAPIModel), kmsKeys: make(map[string]*KMSKeyAPIModel), cmsBuilds: make(map[string]*CMSBuildAPIModel), @@ -105,6 +107,9 @@ func startMockPlatformServer(t *testing.T) *mockPlatform { mp.register("/api/v1/environments/{env}/networks/{network}", http.MethodPut, mp.putNetwork) mp.register("/api/v1/environments/{env}/networks/{network}", http.MethodDelete, mp.deleteNetwork) + // See network_bootstrap_test.go + mp.register("/api/v1/environments/{env}/networks/{network}/initdata", http.MethodGet, mp.getNetworkInitData) + // See kms_wallet.go mp.register("/endpoint/{env}/{service}/rest/api/v1/wallets", http.MethodPost, mp.postKMSWallet) mp.register("/endpoint/{env}/{service}/rest/api/v1/wallets/{wallet}", http.MethodGet, mp.getKMSWallet) diff --git a/kaleido/platform/network.go b/kaleido/platform/network.go index cd69503..5715f33 100644 --- a/kaleido/platform/network.go +++ b/kaleido/platform/network.go @@ -21,6 +21,7 @@ import ( "time" "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" @@ -37,6 +38,12 @@ type NetworkResourceModel struct { ConfigJSON types.String `tfsdk:"config_json"` Info types.Map `tfsdk:"info"` EnvironmentMemberID types.String `tfsdk:"environment_member_id"` + InitFiles types.String `tfsdk:"init_files"` + InitMode types.String `tfsdk:"init_mode"` + Initialized types.Bool `tfsdk:"initialized"` + Filesets types.Map `tfsdk:"file_sets"` + Credsets types.Map `tfsdk:"cred_sets"` + StatusInitFiles types.Map `tfsdk:"status_init_files"` } type NetworkAPIModel struct { @@ -49,6 +56,16 @@ type NetworkAPIModel struct { EnvironmentMemberID string `json:"environmentMemberId,omitempty"` Status string `json:"status,omitempty"` Deleted bool `json:"deleted,omitempty"` + InitFiles string `json:"initFiles,omitempty"` + InitMode string `json:"initMode,omitempty"` + Initialized bool `json:"initialized,omitempty"` + Filesets map[string]*FileSetAPI `json:"fileSets,omitempty"` + Credsets map[string]*CredSetAPI `json:"credSets,omitempty"` + StatusDetails NetworkStatusDetails `json:"statusDetails,omitempty"` +} + +type NetworkStatusDetails struct { + InitFiles map[string]string `json:"initFiles,omitempty"` } func NetworkResourceFactory() resource.Resource { @@ -92,11 +109,87 @@ func (r *networkResource) Schema(_ context.Context, _ resource.SchemaRequest, re Computed: true, ElementType: types.StringType, }, + "init_files": &schema.StringAttribute{ + Optional: true, + }, + "init_mode": &schema.StringAttribute{ + Optional: true, + }, + "initialized": &schema.BoolAttribute{ + Computed: true, + }, + "file_sets": &schema.MapNestedAttribute{ + Optional: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "files": &schema.MapNestedAttribute{ + Required: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "type": &schema.StringAttribute{ + Required: true, + }, + "data": &schema.SingleNestedAttribute{ + Required: true, + Sensitive: true, + Attributes: map[string]schema.Attribute{ + "base64": &schema.StringAttribute{ + Optional: true, + }, + "text": &schema.StringAttribute{ + Optional: true, + }, + "hex": &schema.StringAttribute{ + Optional: true, + }, + }, + }, + }, + }, + }, + }, + }, + }, + "cred_sets": &schema.MapNestedAttribute{ + Optional: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "type": &schema.StringAttribute{ + Required: true, + }, + "basic_auth": &schema.SingleNestedAttribute{ + Optional: true, + Attributes: map[string]schema.Attribute{ + "username": &schema.StringAttribute{ + Required: true, + }, + "password": &schema.StringAttribute{ + Required: true, + Sensitive: true, + }, + }, + }, + "key": &schema.SingleNestedAttribute{ + Optional: true, + Attributes: map[string]schema.Attribute{ + "value": &schema.StringAttribute{ + Required: true, + Sensitive: true, + }, + }, + }, + }, + }, + }, + "status_init_files": &schema.MapAttribute{ + Computed: true, + ElementType: types.StringType, + }, }, } } -func (data *NetworkResourceModel) toAPI(api *NetworkAPIModel) { +func (data *NetworkResourceModel) toAPI(ctx context.Context, api *NetworkAPIModel, diagnostics *diag.Diagnostics) { // required fields api.Type = data.Type.ValueString() api.Name = data.Name.ValueString() @@ -105,10 +198,119 @@ func (data *NetworkResourceModel) toAPI(api *NetworkAPIModel) { if !data.ConfigJSON.IsNull() { _ = json.Unmarshal([]byte(data.ConfigJSON.ValueString()), &api.Config) } + + if !data.InitMode.IsNull() { + api.InitMode = data.InitMode.ValueString() + } + + if !data.InitFiles.IsNull() { + api.InitFiles = data.InitFiles.ValueString() + } + + // filesets (complex nested structure) + if !data.Filesets.IsNull() { + dataAttrs := map[string]attr.Type{ + "base64": types.StringType, + "text": types.StringType, + "hex": types.StringType, + } + fileAttrs := map[string]attr.Type{ + "type": types.StringType, + "data": types.ObjectType{ + AttrTypes: dataAttrs, + }, + } + fileSetAttrs := map[string]attr.Type{ + "files": types.MapType{ + ElemType: types.ObjectType{ + AttrTypes: fileAttrs, + }, + }, + } + api.Filesets = make(map[string]*FileSetAPI) + tfFilesets := data.Filesets.Elements() + for fileSetName, tfFileSet := range tfFilesets { + tfFileSet, d := types.ObjectValueFrom(ctx, fileSetAttrs, tfFileSet) + diagnostics.Append(d...) + tfFiles, d := types.MapValueFrom(ctx, types.ObjectType{AttrTypes: fileAttrs}, tfFileSet.Attributes()["files"]) + diagnostics.Append(d...) + fs := &FileSetAPI{ + Name: fileSetName, + Files: make(map[string]*FileAPI), + } + for filename, tfFileVal := range tfFiles.Elements() { + tfFile, d := types.ObjectValueFrom(ctx, fileAttrs, tfFileVal) + diagnostics.Append(d...) + tfFileAttrs := tfFile.Attributes() + tfFileData, d := types.ObjectValueFrom(ctx, dataAttrs, tfFileAttrs["data"]) + diagnostics.Append(d...) + f := &FileAPI{ + Type: tfFileAttrs["type"].(types.String).ValueString(), + } + tfData := tfFileData.Attributes() + if !tfData["base64"].IsNull() { + f.Data.Base64 = tfData["base64"].(types.String).ValueString() + } else if !tfData["text"].IsNull() { + f.Data.Text = tfData["text"].(types.String).ValueString() + } else if !tfData["hex"].IsNull() { + f.Data.Hex = tfData["hex"].(types.String).ValueString() + } else { + diagnostics.AddError("missing data", fmt.Sprintf("must specify base64, text, or hexx data for file '%s'", filename)) + return + } + fs.Files[filename] = f + } + api.Filesets[fileSetName] = fs + } + } + + // credsets (complex nested structure) + if !data.Credsets.IsNull() { + basicAuthAttrs := map[string]attr.Type{ + "username": types.StringType, + "password": types.StringType, + } + keyAttrs := map[string]attr.Type{ + "value": types.StringType, + } + api.Credsets = make(map[string]*CredSetAPI) + tfCredsets := data.Credsets.Elements() + for credSetName, tfCredSetAttr := range tfCredsets { + tfCredSet := tfCredSetAttr.(types.Object) + tfCredSetAttrs := tfCredSet.Attributes() + crType := tfCredSetAttrs["type"].(types.String).ValueString() + cr := &CredSetAPI{ + Name: credSetName, + Type: crType, + } + if crType == "basic_auth" && !tfCredSetAttrs["basic_auth"].IsNull() { + tfBasicAuth, d := types.ObjectValueFrom(ctx, basicAuthAttrs, tfCredSetAttrs["basic_auth"]) + diagnostics.Append(d...) + tfBasicAuthAttrs := tfBasicAuth.Attributes() + cr.BasicAuth = &CredSetBasicAuthAPI{ + Username: tfBasicAuthAttrs["username"].(types.String).ValueString(), + Password: tfBasicAuthAttrs["password"].(types.String).ValueString(), + } + } else if crType == "key" && !tfCredSetAttrs["key"].IsNull() { + tfKey, d := types.ObjectValueFrom(ctx, keyAttrs, tfCredSetAttrs["key"]) + diagnostics.Append(d...) + tfKeyAttrs := tfKey.Attributes() + cr.Key = &CredSetKeyAPI{ + Value: tfKeyAttrs["value"].(types.String).ValueString(), + } + } else { + diagnostics.AddError("missing credential", fmt.Sprintf("must specify key/basic_auth as appropriate for type '%s'", crType)) + return + } + api.Credsets[credSetName] = cr + } + } + } -func (api *NetworkAPIModel) toData(data *NetworkResourceModel) { +func (api *NetworkAPIModel) toData(data *NetworkResourceModel, diagnostics *diag.Diagnostics) { data.ID = types.StringValue(api.ID) + data.Initialized = types.BoolValue(api.Initialized) data.EnvironmentMemberID = types.StringValue(api.EnvironmentMemberID) info := make(map[string]attr.Value) for k, v := range api.Config { @@ -117,7 +319,19 @@ func (api *NetworkAPIModel) toData(data *NetworkResourceModel) { info[k] = types.StringValue(v) } } - data.Info, _ = types.MapValue(types.StringType, info) + + var d diag.Diagnostics + data.Info, d = types.MapValue(types.StringType, info) + diagnostics.Append(d...) + + //status init files + statusInitFiles := make(map[string]attr.Value) + for name, data := range api.StatusDetails.InitFiles { + initFileData := types.StringValue(data) + statusInitFiles[name] = initFileData + } + data.StatusInitFiles, d = types.MapValue(types.StringType, statusInitFiles) + diagnostics.Append(d...) } func (r *networkResource) apiPath(data *NetworkResourceModel) string { @@ -134,15 +348,15 @@ func (r *networkResource) Create(ctx context.Context, req resource.CreateRequest resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) var api NetworkAPIModel - data.toAPI(&api) + data.toAPI(ctx, &api, &resp.Diagnostics) ok, _ := r.apiRequest(ctx, http.MethodPost, r.apiPath(&data), api, &api, &resp.Diagnostics) if !ok { return } - api.toData(&data) // need the ID copied over + api.toData(&data, &resp.Diagnostics) // need the ID copied over r.waitForReadyStatus(ctx, r.apiPath(&data), &resp.Diagnostics) - api.toData(&data) // need the latest status after the readiness check completes, to extract generated values + api.toData(&data, &resp.Diagnostics) // need the latest status after the readiness check completes, to extract generated values resp.Diagnostics.Append(resp.State.Set(ctx, data)...) } @@ -160,14 +374,14 @@ func (r *networkResource) Update(ctx context.Context, req resource.UpdateRequest } // Update from plan - data.toAPI(&api) + data.toAPI(ctx, &api, &resp.Diagnostics) if ok, _ := r.apiRequest(ctx, http.MethodPut, r.apiPath(&data), api, &api, &resp.Diagnostics); !ok { return } - api.toData(&data) // need the ID copied over + api.toData(&data, &resp.Diagnostics) // need the ID copied over r.waitForReadyStatus(ctx, r.apiPath(&data), &resp.Diagnostics) - api.toData(&data) // need the latest status after the readiness check completes, to extract generated values + api.toData(&data, &resp.Diagnostics) // need the latest status after the readiness check completes, to extract generated values resp.Diagnostics.Append(resp.State.Set(ctx, data)...) } @@ -186,7 +400,7 @@ func (r *networkResource) Read(ctx context.Context, req resource.ReadRequest, re return } - api.toData(&data) + api.toData(&data, &resp.Diagnostics) resp.Diagnostics.Append(resp.State.Set(ctx, data)...) } diff --git a/kaleido/platform/network_bootstrap.go b/kaleido/platform/network_bootstrap.go new file mode 100644 index 0000000..1115c24 --- /dev/null +++ b/kaleido/platform/network_bootstrap.go @@ -0,0 +1,224 @@ +// Copyright © Kaleido, Inc. 2024 + +// 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 platform + +import ( + "context" + "fmt" + "net/http" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +type NetworkBootstrapDatasourceModel struct { + Network types.String `tfsdk:"network"` + Environment types.String `tfsdk:"environment"` + BootstrapFiles *BootstrapFiles `tfsdk:"bootstrap_files"` +} + +type BootstrapFiles struct { + Name types.String `tfsdk:"name"` + Files types.Map `tfsdk:"files"` +} + +type NetworkInitData struct { + Name string `json:"name"` + Files map[string]*FileAPI `json:"files"` +} + +func NetworkBootstrapDatasourceModelFactory() datasource.DataSource { + return &networkBootstrapDatasource{} +} + +type networkBootstrapDatasource struct { + commonDataSource +} + +func (r *networkBootstrapDatasource) Metadata(_ context.Context, _ datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = "kaleido_platform_network_bootstrap_data" +} + +func (r *networkBootstrapDatasource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "network": &schema.StringAttribute{ + Required: true, + }, + "environment": &schema.StringAttribute{ + Required: true, + }, + "bootstrap_files": &schema.SingleNestedAttribute{ + Computed: true, + Attributes: map[string]schema.Attribute{ + "files": &schema.MapNestedAttribute{ + Required: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "type": &schema.StringAttribute{ + Optional: true, + }, + "data": &schema.SingleNestedAttribute{ + Optional: true, + Attributes: map[string]schema.Attribute{ + /*"base64": &schema.StringAttribute{ + Optional: true, + },*/ + "text": &schema.StringAttribute{ + Optional: true, + }, + /*"hex": &schema.StringAttribute{ + Optional: true, + },*/ + }, + }, + }, + }, + }, + "name": &schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + } +} + +func (r *networkBootstrapDatasource) apiPath(data *NetworkBootstrapDatasourceModel) string { + path := fmt.Sprintf("/api/v1/environments/%s/networks/%s/initdata", data.Environment.ValueString(), data.Network.ValueString()) + return path +} + +func (r *networkBootstrapDatasource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + + var data NetworkBootstrapDatasourceModel + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + + var api NetworkInitData + + ok, status := r.apiRequest(ctx, http.MethodGet, r.apiPath(&data), nil, &api, &resp.Diagnostics, Allow404()) + if !ok { + return + } + if status == 404 { + resp.State.RemoveResource(ctx) + return + } + + api.toBootstrapData(ctx, &data, &resp.Diagnostics) + resp.Diagnostics.Append(resp.State.Set(ctx, data)...) +} + +func (api *NetworkInitData) toBootstrapData(ctx context.Context, data *NetworkBootstrapDatasourceModel, diagnostics *diag.Diagnostics) { + + /*initDataAttrs := map[string]attr.Type{ + "name": types.StringType, + "files": types.MapType{ + ElemType: types.ObjectType{ + AttrTypes: fileAttrs, + }, + }, + }*/ + if api != nil { + var d diag.Diagnostics + + data.BootstrapFiles = &BootstrapFiles{} + + //copy api.Name to data.BoostrapFiles.Name + nameValue := types.StringValue(api.Name) + data.BootstrapFiles.Name = nameValue + + //iterate api.Files + dataAttrs := map[string]attr.Type{ + //"base64": types.StringType, + "text": types.StringType, + //"hex": types.StringType, + } + fileAttrs := map[string]attr.Type{ + "type": types.StringType, + "data": types.ObjectType{ + AttrTypes: dataAttrs, + }, + } + files := map[string]attr.Value{} + + for k, f := range api.Files { + file := map[string]attr.Value{} + file["type"] = types.StringValue(f.Type) + + data := map[string]attr.Value{} + + if len(f.Data.Text) > 0 { + data["text"] = types.StringValue(f.Data.Text) + } + + /*if len(f.Data.Base64) > 0 { + data["base64"] = types.StringValue(f.Data.Base64) + } + + if len(f.Data.Hex) > 0 { + data["hex"] = types.StringValue(f.Data.Hex) + }*/ + + tfData, d := types.ObjectValue(dataAttrs, data) + diagnostics.Append(d...) + file["data"] = tfData + + tfFile, d := types.ObjectValue(fileAttrs, file) + diagnostics.Append(d...) + files[k] = tfFile + } + data.BootstrapFiles.Files, d = types.MapValue(types.ObjectType{ + AttrTypes: fileAttrs, + }, files) + diagnostics.Append(d...) + + /*for name, data := range api.Files { + fileData, diag := types.ObjectValueFrom(ctx, fileAttrs, data) + diagnostics.Append(diag...) + + filesValue[name] = fileData + }*/ + + //files + /*filesValue := make(map[string]attr.Value) + for name, data := range api.Files { + fileData, _ := types.ObjectValueFrom(ctx, fileAttrs, data) + filesValue[name] = fileData + }*/ + + //data.StatusInitFiles, _ = types.MapValue(types.StringType, statusInitFiles) + //diagnostics.Append(d...) + + //var bootstrapFiles *BootstrapFiles + //diag := api.Files.As(ctx, &bootstrapFiles, basetypes.ObjectAsOptions{}) + + //filesMapValue, diag := types.MapValueFrom(ctx, types.Map{}, filesValue) + //filesMapValue, diag := types.MapValueFrom(ctx, types.ObjectType{AttrTypes: fileAttrs}, filesValue) + //diagnostics.Append(diag...) + //nameValue := types.StringValue(api.Name) + + //nameStringValue := types.StringValue(api.Name) + + } else { + diagnostics.AddError( + "API failed to get network initdata", + "", + ) + } + +} diff --git a/kaleido/platform/network_bootstrap_test.go b/kaleido/platform/network_bootstrap_test.go new file mode 100644 index 0000000..d4c3739 --- /dev/null +++ b/kaleido/platform/network_bootstrap_test.go @@ -0,0 +1,91 @@ +// Copyright © Kaleido, Inc. 2024 + +// 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 platform + +import ( + "net/http" + "testing" + + "github.com/gorilla/mux" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + + _ "embed" +) + +var networkBoostrapStep1 = ` +data "kaleido_platform_network_bootstrap_data" "boot1" { + environment = "env1" + network = "net1" +} +` + +var networkBoostrapStep2 = ` +data "kaleido_platform_network_bootstrap_data" "boot1" { + environment = "env1" + network = "net1" + bootstrap_files = jsonencode({ + "files": { + "genesis.json": { + "data": { + "text": "{\"alloc\":{\"0x12F62772C4652280d06E64CfBC9033d409559aD4\":{\"balance\":\"0x111111111111\"}},\"coinbase\":\"0x0000000000000000000000000000000000000000\",\"config\":{\"berlinBlock\":0,\"chainId\":12345,\"contractSizeLimit\":98304,\"qbft\":{\"blockperiodseconds\":5,\"epochlength\":30000,\"requesttimeoutseconds\":10},\"shanghaiTime\":0,\"zeroBaseFee\":true},\"difficulty\":\"0x1\",\"extraData\":\"0xf83aa00000000000000000000000000000000000000000000000000000000000000000d594ca45971aa3e3eb66e8cd4ad6ed491520b601bf6cc080c0\",\"gasLimit\":\"0x2fefd800\",\"mixHash\":\"0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365\"}" + }, + "type": "json" + } + }, + "name": "init" + }) +} +` + +func TestBootstrap1(t *testing.T) { + + mp, providerConfig := testSetup(t) + defer func() { + mp.checkClearCalls([]string{ + "GET /api/v1/environments/{env}/networks/{network}/initdata", + "GET /api/v1/environments/{env}/networks/{network}/initdata", + "GET /api/v1/environments/{env}/networks/{network}/initdata", + }) + mp.server.Close() + }() + + boot1Data := "data.kaleido_platform_network_bootstrap_data.boot1" + resource.Test(t, resource.TestCase{ + IsUnitTest: true, + ProtoV6ProviderFactories: testAccProviders, + Steps: []resource.TestStep{ + { + Config: providerConfig + networkBoostrapStep1, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(boot1Data, "environment", `env1`), + ), + }, + { + Config: providerConfig + networkBoostrapStep2, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(boot1Data, "environment", `env1`), + ), + }, + }, + }) +} + +func (mp *mockPlatform) getNetworkInitData(res http.ResponseWriter, req *http.Request) { + bd := mp.networkinitdatas[mux.Vars(req)["env"]+"/"+mux.Vars(req)["network"]+"/"+"initdata"] + if bd == nil { + mp.respond(res, nil, 404) + } else { + mp.respond(res, bd, 200) + } +} diff --git a/kaleido/platform/network_test.go b/kaleido/platform/network_test.go index f867e21..ce4193a 100644 --- a/kaleido/platform/network_test.go +++ b/kaleido/platform/network_test.go @@ -111,7 +111,8 @@ func TestNetwork1(t *testing.T) { "setting2": "value2" }, "environmentMemberId": "%[4]s", - "status": "ready" + "status": "ready", + "statusDetails": {} } `, // generated fields that vary per test run diff --git a/kaleido/platform/runtime.go b/kaleido/platform/runtime.go index b00bf17..5fa5c9a 100644 --- a/kaleido/platform/runtime.go +++ b/kaleido/platform/runtime.go @@ -36,6 +36,7 @@ type RuntimeResourceModel struct { ConfigJSON types.String `tfsdk:"config_json"` LogLevel types.String `tfsdk:"log_level"` Size types.String `tfsdk:"size"` + Zone types.String `tfsdk:"zone"` EnvironmentMemberID types.String `tfsdk:"environment_member_id"` Stopped types.Bool `tfsdk:"stopped"` } @@ -49,6 +50,7 @@ type RuntimeAPIModel struct { Config map[string]interface{} `json:"config"` LogLevel string `json:"loglevel,omitempty"` Size string `json:"size,omitempty"` + Zone string `json:"zone,omitempty"` EnvironmentMemberID string `json:"environmentMemberId,omitempty"` Status string `json:"status,omitempty"` Deleted bool `json:"deleted,omitempty"` @@ -99,6 +101,10 @@ func (r *runtimeResource) Schema(_ context.Context, _ resource.SchemaRequest, re Optional: true, Computed: true, }, + "zone": &schema.StringAttribute{ + Optional: true, + Computed: true, + }, "stopped": &schema.BoolAttribute{ Optional: true, Computed: true, @@ -122,6 +128,9 @@ func (data *RuntimeResourceModel) toAPI(api *RuntimeAPIModel) { if !data.Size.IsNull() { api.Size = data.Size.ValueString() } + if !data.Zone.IsNull() { + api.Zone = data.Zone.ValueString() + } if !data.Stopped.IsNull() { api.Stopped = data.Stopped.ValueBool() } @@ -132,6 +141,7 @@ func (api *RuntimeAPIModel) toData(data *RuntimeResourceModel) { data.EnvironmentMemberID = types.StringValue(api.EnvironmentMemberID) data.LogLevel = types.StringValue(api.LogLevel) data.Size = types.StringValue(api.Size) + data.Zone = types.StringValue(api.Zone) data.Stopped = types.BoolValue(api.Stopped) } diff --git a/kaleido/platform/service.go b/kaleido/platform/service.go index 6e98cf8..d8b573e 100644 --- a/kaleido/platform/service.go +++ b/kaleido/platform/service.go @@ -42,6 +42,7 @@ type ServiceResourceModel struct { Hostnames types.Map `tfsdk:"hostnames"` Filesets types.Map `tfsdk:"file_sets"` Credsets types.Map `tfsdk:"cred_sets"` + ConnectivityJSON types.String `tfsdk:"connectivity_json"` } type ServiceAPIModel struct { @@ -53,13 +54,14 @@ type ServiceAPIModel struct { Runtime ServiceAPIRuntimeRef `json:"runtime,omitempty"` Account string `json:"account,omitempty"` EnvironmentMemberID string `json:"environmentMemberId,omitempty"` - Status string `json:"status,omitempty"` Deleted bool `json:"deleted,omitempty"` Config map[string]interface{} `json:"config"` Endpoints map[string]ServiceAPIEndpoint `json:"endpoints,omitempty"` Hostnames map[string][]string `json:"hostnames,omitempty"` Filesets map[string]*FileSetAPI `json:"fileSets,omitempty"` Credsets map[string]*CredSetAPI `json:"credSets,omitempty"` + Status string `json:"status,omitempty"` + StatusDetails ServiceStatusDetails `json:"statusDetails,omitempty"` } type ServiceAPIRuntimeRef struct { @@ -71,36 +73,20 @@ type ServiceAPIEndpoint struct { URLS []string `json:"urls,omitempty"` } -type FileSetAPI struct { - Name string `json:"name"` - Files map[string]*FileAPI `json:"files"` -} - -type FileAPI struct { - Type string `json:"type,omitempty"` - Data FileDataAPI `json:"data,omitempty"` -} - -type FileDataAPI struct { - Base64 string `json:"base64,omitempty"` - Text string `json:"text,omitempty"` - Hex string `json:"hex,omitempty"` +type ServiceStatusDetails struct { + Connectivity *Connectivity `json:"connectivity,omitempty"` } -type CredSetAPI struct { - Name string `json:"name"` - Type string `json:"type,omitempty"` - BasicAuth *CredSetBasicAuthAPI `json:"basicAuth,omitempty"` - Key *CredSetKeyAPI `json:"key,omitempty"` +type Connectivity struct { + Identity string `json:"identity,omitempty"` + Endpoints []Endpoint `json:"endpoints,omitempty"` } -type CredSetBasicAuthAPI struct { - Username string `json:"username,omitempty"` - Password string `json:"password,omitempty"` -} - -type CredSetKeyAPI struct { - Value string `json:"value,omitempty"` +type Endpoint struct { + Host string `json:"host,omitempty"` + NAT string `json:"nat,omitempty"` + Port int64 `json:"port,omitempty"` + Protocol string `json:"protocol,omitempty"` } func ServiceResourceFactory() resource.Resource { @@ -226,6 +212,37 @@ func (r *serviceResource) Schema(_ context.Context, _ resource.SchemaRequest, re }, }, }, + "connectivity_json": &schema.StringAttribute{ + Computed: true, + }, + /*"connectivity": &schema.SingleNestedAttribute{ + Computed: true, + Optional: true, + Attributes: map[string]schema.Attribute{ + "identity": &schema.StringAttribute{ + Computed: true, + }, + "endpoints": &schema.ListNestedAttribute{ + Computed: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "host": &schema.StringAttribute{ + Computed: true, + }, + "nat": &schema.StringAttribute{ + Computed: true, + }, + "port": &schema.Int64Attribute{ + Computed: true, + }, + "protocol": &schema.StringAttribute{ + Computed: true, + }, + }, + }, + }, + }, + },*/ }, } } @@ -341,6 +358,8 @@ func (data *ServiceResourceModel) toAPI(ctx context.Context, api *ServiceAPIMode api.Credsets[credSetName] = cr } } + + //connectivity is computed. So only goes from api to data not via versa } func (api *ServiceAPIModel) toData(data *ServiceResourceModel, diagnostics *diag.Diagnostics) { @@ -370,6 +389,55 @@ func (api *ServiceAPIModel) toData(data *ServiceResourceModel, diagnostics *diag AttrTypes: endpointAttrTypes, }, endpoints) diagnostics.Append(d...) + + //connectivity + if api.StatusDetails.Connectivity != nil { + d, err := json.Marshal(api.StatusDetails.Connectivity) + if err != nil { + diagnostics.AddError("failed to marshal connectivity", err.Error()) + return + } + connectivityJSON := string(d) + data.ConnectivityJSON = types.StringValue(connectivityJSON) + } else { + data.ConnectivityJSON = types.StringValue("") + } + + /*connectivity := map[string]attr.Value{} + connEndpointAttrTypes := map[string]attr.Type{ + "host": types.StringType, + "nat": types.StringType, + "port": types.Int64Type, + "protocol": types.StringType, + } + connectivityAttrTypes := map[string]attr.Type{ + "identity": types.StringType, + "endpoints": types.ListType{ElemType: types.ObjectType{ + AttrTypes: connEndpointAttrTypes, + }}, + } + connectivity["identity"] = types.StringValue(api.StatusDetails.Connectivity.Identity) + connEndpoints := make([]attr.Value, len(api.StatusDetails.Connectivity.Endpoints)) + + for i, ce := range api.StatusDetails.Connectivity.Endpoints { + connEndpoint := map[string]attr.Value{} + connEndpoint["host"] = types.StringValue(ce.Host) + connEndpoint["nat"] = types.StringValue(ce.NAT) + connEndpoint["port"] = types.Int64Value(ce.Port) + connEndpoint["protocol"] = types.StringValue(ce.Protocol) + tfConnEndpoint, d := types.ObjectValue(connEndpointAttrTypes, connEndpoint) + diagnostics.Append(d...) + connEndpoints[i] = tfConnEndpoint + } + tfEndpoints, d := types.ListValue(types.ObjectType{ + AttrTypes: connEndpointAttrTypes, + }, connEndpoints) + diagnostics.Append(d...) + connectivity["endpoints"] = tfEndpoints + + data.Connectivity, d = types.ObjectValue(connectivityAttrTypes, connectivity) + diagnostics.Append(d...)*/ + } func (r *serviceResource) apiPath(data *ServiceResourceModel) string { @@ -394,9 +462,16 @@ func (r *serviceResource) Create(ctx context.Context, req resource.CreateRequest api.toData(&data, &resp.Diagnostics) // need the ID copied over r.waitForReadyStatus(ctx, r.apiPath(&data), &resp.Diagnostics) + + //re-read from api + api.ID = data.ID.ValueString() + ok, _ = r.apiRequest(ctx, http.MethodGet, r.apiPath(&data), nil, &api, &resp.Diagnostics, &APIRequestOption{}) + if !ok { + return + } + api.toData(&data, &resp.Diagnostics) // need the latest status after the readiness check completes, to extract generated values resp.Diagnostics.Append(resp.State.Set(ctx, data)...) - } func (r *serviceResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { @@ -419,6 +494,10 @@ func (r *serviceResource) Update(ctx context.Context, req resource.UpdateRequest api.toData(&data, &resp.Diagnostics) // need the ID copied over r.waitForReadyStatus(ctx, r.apiPath(&data), &resp.Diagnostics) + //re-read from api + if ok, _ := r.apiRequest(ctx, http.MethodGet, r.apiPath(&data), nil, &api, &resp.Diagnostics); !ok { + return + } api.toData(&data, &resp.Diagnostics) // need the latest status after the readiness check completes, to extract generated values resp.Diagnostics.Append(resp.State.Set(ctx, data)...) } diff --git a/kaleido/platform/service_test.go b/kaleido/platform/service_test.go index 851928b..8aa697d 100644 --- a/kaleido/platform/service_test.go +++ b/kaleido/platform/service_test.go @@ -162,6 +162,12 @@ func TestService1(t *testing.T) { "urls": [ "https://example.com/api/v1/environments/env1/services/%[1]s" ] + }, + "ws": { + "type": "ws", + "urls": [ + "wss://example.com/api/v1/environments/env1/services/%[1]s" + ] } }, "hostnames": { @@ -205,7 +211,13 @@ func TestService1(t *testing.T) { "value": "abce12345" } } + }, + "statusDetails": { + "connectivity": { + "identity": "192ea525cecb7302efa31283a205142b989217afef2d555a0af8370417e233fe9fa47a11effed21f1dfcfd7887e7ba5d1b983b03980c88c0ef9543f1a2be80c7" + } } + } `, // generated fields that vary per test run @@ -228,8 +240,21 @@ func (mp *mockPlatform) getService(res http.ResponseWriter, req *http.Request) { mp.respond(res, nil, 404) } else { mp.respond(res, svc, 200) - // Next time will return ready + + // Next +1 time will return ready and have details svc.Status = "ready" + /*endpoints := []map[string]interface{}{ + { + "host": "10.244.3.35", + "port": 30303, + "protocol": "TCP", + }, + }*/ + svc.StatusDetails = ServiceStatusDetails{ + Connectivity: &Connectivity{ + Identity: "192ea525cecb7302efa31283a205142b989217afef2d555a0af8370417e233fe9fa47a11effed21f1dfcfd7887e7ba5d1b983b03980c88c0ef9543f1a2be80c7", + }, + } } } @@ -263,7 +288,7 @@ func (mp *mockPlatform) putService(res http.ResponseWriter, req *http.Request) { newSVC.Created = svc.Created newSVC.Updated = &now newSVC.Status = "pending" - svc.Endpoints = map[string]ServiceAPIEndpoint{ + newSVC.Endpoints = map[string]ServiceAPIEndpoint{ "api": { Type: "http", URLS: []string{fmt.Sprintf("https://example.com/api/v1/environments/%s/services/%s", mux.Vars(req)["env"], svc.ID)}, @@ -274,6 +299,7 @@ func (mp *mockPlatform) putService(res http.ResponseWriter, req *http.Request) { URLS: []string{fmt.Sprintf("wss://example.com/api/v1/environments/%s/services/%s", mux.Vars(req)["env"], svc.ID)}, }, } + newSVC.StatusDetails = svc.StatusDetails mp.services[mux.Vars(req)["env"]+"/"+mux.Vars(req)["service"]] = &newSVC mp.respond(res, &newSVC, 200) } diff --git a/main.go b/main.go index aa1f469..de3743c 100644 --- a/main.go +++ b/main.go @@ -31,6 +31,7 @@ func main() { opts := providerserver.ServeOpts{ Address: "registry.terraform.io/kaleido-io/terraform", ProtocolVersion: 6, + //Debug: true, } err := providerserver.Serve(context.Background(), kaleido.New(version), opts) diff --git a/terraform.tfstate b/terraform.tfstate new file mode 100644 index 0000000..1b66c42 --- /dev/null +++ b/terraform.tfstate @@ -0,0 +1,9 @@ +{ + "version": 4, + "terraform_version": "1.8.5", + "serial": 1, + "lineage": "338a0150-8df8-f675-dff5-3634a897913e", + "outputs": {}, + "resources": [], + "check_results": null +} From 983b9d49038bfa83888d138efb53fcc096a412f5 Mon Sep 17 00:00:00 2001 From: Alex Wood Date: Mon, 7 Oct 2024 14:24:27 +0100 Subject: [PATCH 2/8] update unit tests --- kaleido/platform/authentictor_test.go | 181 +++++++++++++++++++++ kaleido/platform/mockserver_test.go | 8 + kaleido/platform/network_bootstrap_test.go | 41 ++--- kaleido/platform/service_test.go | 2 + 4 files changed, 202 insertions(+), 30 deletions(-) create mode 100644 kaleido/platform/authentictor_test.go diff --git a/kaleido/platform/authentictor_test.go b/kaleido/platform/authentictor_test.go new file mode 100644 index 0000000..8d83375 --- /dev/null +++ b/kaleido/platform/authentictor_test.go @@ -0,0 +1,181 @@ +// Copyright © Kaleido, Inc. 2024 + +// 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 platform + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/aidarkhanov/nanoid" + "github.com/gorilla/mux" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/stretchr/testify/assert" + + _ "embed" +) + +var authenticatorStep1 = ` +resource "kaleido_platform_authenticator" "authenticator1" { + environment = "env1" + network = "net1" + type = "permitted" + name = "auth1" + zone = "zone1" + conn = "conn1" + permitted_json = jsonencode({"peers":[{"endpoints":[{"host":"10.244.3.64","nat":"None","port":30303,"protocol":"TCP"},{"host":"86.13.78.205","nat":"Source","port":30303,"protocol":"TCP"}],"identity":"496f2bfe5cac576cb33f98778eb5617e3d3fe2e9ffeda8e7d0bde22f5e15d2dd4750f59a268ece9197aa10f4e709012564b514782ea86529c11d02a3c604ee7b"}]}) +} +` + +var authenticatorStep2 = ` +resource "kaleido_platform_authenticator" "authenticator1" { + environment = "env1" + network = "net1" + type = "permitted" + name = "auth1" + zone = "zone1" + conn = "conn1" + permitted_json = jsonencode({"peers":[{"endpoints":[{"host":"10.244.3.64","nat":"None","port":30303,"protocol":"TCP"},{"host":"86.13.78.205","nat":"Source","port":30303,"protocol":"TCP"}],"identity":"496f2bfe5cac576cb33f98778eb5617e3d3fe2e9ffeda8e7d0bde22f5e15d2dd4750f59a268ece9197aa10f4e709012564b514782ea86529c11d02a3c604ee7b"}]}) +} +` + +func TestAuthenticator(t *testing.T) { + + mp, providerConfig := testSetup(t) + defer func() { + mp.checkClearCalls([]string{ + "POST /api/v1/environments/{env}/networks/{net}/authenticators", + "GET /api/v1/environments/{env}/networks/{net}/authenticators/{authenticator}", + "GET /api/v1/environments/{env}/networks/{net}/authenticators/{authenticator}", + "GET /api/v1/environments/{env}/networks/{net}/authenticators/{authenticator}", + "GET /api/v1/environments/{env}/networks/{net}/authenticators/{authenticator}", + "GET /api/v1/environments/{env}/networks/{net}/authenticators/{authenticator}", + "DELETE /api/v1/environments/{env}/networks/{net}/authenticators/{authenticator}", + "GET /api/v1/environments/{env}/networks/{net}/authenticators/{authenticator}", + }) + mp.server.Close() + }() + + authenticator1Resource := "kaleido_platform_authenticator.authenticator1" + resource.Test(t, resource.TestCase{ + IsUnitTest: true, + ProtoV6ProviderFactories: testAccProviders, + Steps: []resource.TestStep{ + { + Config: providerConfig + authenticatorStep1, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrSet(authenticator1Resource, "id"), + ), + }, + { + Config: providerConfig + authenticatorStep2, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrSet(authenticator1Resource, "id"), + func(s *terraform.State) error { + // Compare the final result on the mock-server side + id := s.RootModule().Resources[authenticator1Resource].Primary.Attributes["id"] + auth := mp.authenticators[fmt.Sprintf("env1/net1/%s", id)] + testJSONEqual(t, auth, fmt.Sprintf(` + { + "id": "%[1]s", + "created": "%[2]s", + "updated": "%[3]s", + "type": "permitted", + "name": "auth1", + "networkId": "net1", + "permitted": { + "peers": [ + { + "endpoints": [ + { + "host": "10.244.3.64", + "nat": "None", + "port": 30303, + "protocol": "TCP" + }, + { + "host": "86.13.78.205", + "nat": "Source", + "port": 30303, + "protocol": "TCP" + } + ], + "identity": "496f2bfe5cac576cb33f98778eb5617e3d3fe2e9ffeda8e7d0bde22f5e15d2dd4750f59a268ece9197aa10f4e709012564b514782ea86529c11d02a3c604ee7b" + } + ] + }, + "zone": "zone1", + "connection": "conn1", + "status": "ready" + } + `, + // generated fields that vary per test run + id, + auth.Created.UTC().Format(time.RFC3339Nano), + auth.Updated.UTC().Format(time.RFC3339Nano), + )) + return nil + }, + ), + }, + }, + }) +} + +func (mp *mockPlatform) getAuthenticator(res http.ResponseWriter, req *http.Request) { + auth := mp.authenticators[mux.Vars(req)["env"]+"/"+mux.Vars(req)["net"]+"/"+mux.Vars(req)["authenticator"]] + if auth == nil { + mp.respond(res, nil, 404) + } else { + mp.respond(res, auth, 200) + // Next time will return ready + auth.Status = "ready" + } +} + +func (mp *mockPlatform) postAuthenticator(res http.ResponseWriter, req *http.Request) { + var auth AuthenticatorAPIModel + mp.getBody(req, &auth) + auth.ID = nanoid.New() + now := time.Now().UTC() + auth.Created = &now + auth.Updated = &now + auth.Status = "pending" + mp.authenticators[mux.Vars(req)["env"]+"/"+mux.Vars(req)["net"]+"/"+auth.ID] = &auth + mp.respond(res, &auth, 201) +} + +func (mp *mockPlatform) putAuthenticator(res http.ResponseWriter, req *http.Request) { + auth := mp.authenticators[mux.Vars(req)["env"]+"/"+mux.Vars(req)["net"]+"/"+mux.Vars(req)["authenticator"]] + assert.NotNil(mp.t, auth) + var newAuth AuthenticatorAPIModel + mp.getBody(req, &newAuth) + assert.Equal(mp.t, auth.ID, newAuth.ID) // expected behavior of provider + assert.Equal(mp.t, auth.ID, mux.Vars(req)["authenticator"]) // expected behavior of provider + now := time.Now().UTC() + newAuth.Created = auth.Created + newAuth.Updated = &now + newAuth.Status = "pending" + mp.authenticators[mux.Vars(req)["env"]+"/"+mux.Vars(req)["net"]+"/"+mux.Vars(req)["authenticator"]] = &newAuth + mp.respond(res, &newAuth, 200) +} + +func (mp *mockPlatform) deleteAuthenticator(res http.ResponseWriter, req *http.Request) { + rt := mp.authenticators[mux.Vars(req)["env"]+"/"+mux.Vars(req)["net"]+"/"+mux.Vars(req)["authenticator"]] + assert.NotNil(mp.t, rt) + delete(mp.authenticators, mux.Vars(req)["env"]+"/"+mux.Vars(req)["net"]+"/"+mux.Vars(req)["authenticator"]) + mp.respond(res, nil, 204) +} diff --git a/kaleido/platform/mockserver_test.go b/kaleido/platform/mockserver_test.go index f52500c..59cbe73 100644 --- a/kaleido/platform/mockserver_test.go +++ b/kaleido/platform/mockserver_test.go @@ -40,6 +40,7 @@ type mockPlatform struct { runtimes map[string]*RuntimeAPIModel services map[string]*ServiceAPIModel networks map[string]*NetworkAPIModel + authenticators map[string]*AuthenticatorAPIModel networkinitdatas map[string]*NetworkInitData kmsWallets map[string]*KMSWalletAPIModel kmsKeys map[string]*KMSKeyAPIModel @@ -66,6 +67,7 @@ func startMockPlatformServer(t *testing.T) *mockPlatform { runtimes: make(map[string]*RuntimeAPIModel), services: make(map[string]*ServiceAPIModel), networks: make(map[string]*NetworkAPIModel), + authenticators: make(map[string]*AuthenticatorAPIModel), networkinitdatas: make(map[string]*NetworkInitData), kmsWallets: make(map[string]*KMSWalletAPIModel), kmsKeys: make(map[string]*KMSKeyAPIModel), @@ -107,6 +109,12 @@ func startMockPlatformServer(t *testing.T) *mockPlatform { mp.register("/api/v1/environments/{env}/networks/{network}", http.MethodPut, mp.putNetwork) mp.register("/api/v1/environments/{env}/networks/{network}", http.MethodDelete, mp.deleteNetwork) + // See authenticator_test.go + mp.register("/api/v1/environments/{env}/networks/{net}/authenticators", http.MethodPost, mp.postAuthenticator) + mp.register("/api/v1/environments/{env}/networks/{net}/authenticators/{authenticator}", http.MethodGet, mp.getAuthenticator) + mp.register("/api/v1/environments/{env}/networks/{net}/authenticators/{authenticator}", http.MethodPut, mp.putAuthenticator) + mp.register("/api/v1/environments/{env}/networks/{net}/authenticators/{authenticator}", http.MethodDelete, mp.deleteAuthenticator) + // See network_bootstrap_test.go mp.register("/api/v1/environments/{env}/networks/{network}/initdata", http.MethodGet, mp.getNetworkInitData) diff --git a/kaleido/platform/network_bootstrap_test.go b/kaleido/platform/network_bootstrap_test.go index d4c3739..0c7d952 100644 --- a/kaleido/platform/network_bootstrap_test.go +++ b/kaleido/platform/network_bootstrap_test.go @@ -17,7 +17,6 @@ import ( "net/http" "testing" - "github.com/gorilla/mux" "github.com/hashicorp/terraform-plugin-testing/helper/resource" _ "embed" @@ -30,24 +29,6 @@ data "kaleido_platform_network_bootstrap_data" "boot1" { } ` -var networkBoostrapStep2 = ` -data "kaleido_platform_network_bootstrap_data" "boot1" { - environment = "env1" - network = "net1" - bootstrap_files = jsonencode({ - "files": { - "genesis.json": { - "data": { - "text": "{\"alloc\":{\"0x12F62772C4652280d06E64CfBC9033d409559aD4\":{\"balance\":\"0x111111111111\"}},\"coinbase\":\"0x0000000000000000000000000000000000000000\",\"config\":{\"berlinBlock\":0,\"chainId\":12345,\"contractSizeLimit\":98304,\"qbft\":{\"blockperiodseconds\":5,\"epochlength\":30000,\"requesttimeoutseconds\":10},\"shanghaiTime\":0,\"zeroBaseFee\":true},\"difficulty\":\"0x1\",\"extraData\":\"0xf83aa00000000000000000000000000000000000000000000000000000000000000000d594ca45971aa3e3eb66e8cd4ad6ed491520b601bf6cc080c0\",\"gasLimit\":\"0x2fefd800\",\"mixHash\":\"0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365\"}" - }, - "type": "json" - } - }, - "name": "init" - }) -} -` - func TestBootstrap1(t *testing.T) { mp, providerConfig := testSetup(t) @@ -71,21 +52,21 @@ func TestBootstrap1(t *testing.T) { resource.TestCheckResourceAttr(boot1Data, "environment", `env1`), ), }, - { - Config: providerConfig + networkBoostrapStep2, - Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr(boot1Data, "environment", `env1`), - ), - }, }, }) } func (mp *mockPlatform) getNetworkInitData(res http.ResponseWriter, req *http.Request) { - bd := mp.networkinitdatas[mux.Vars(req)["env"]+"/"+mux.Vars(req)["network"]+"/"+"initdata"] - if bd == nil { - mp.respond(res, nil, 404) - } else { - mp.respond(res, bd, 200) + nid := &NetworkInitData{ + Name: "init", + Files: map[string]*FileAPI{ + "genesis.json": { + Type: "json", + Data: FileDataAPI{ + Text: "{\"alloc\":{\"0x12F62772C4652280d06E64CfBC9033d409559aD4\":{\"balance\":\"0x111111111111\"}},\"coinbase\":\"0x0000000000000000000000000000000000000000\",\"config\":{\"berlinBlock\":0,\"chainId\":12345,\"contractSizeLimit\":98304,\"qbft\":{\"blockperiodseconds\":5,\"epochlength\":30000,\"requesttimeoutseconds\":10},\"shanghaiTime\":0,\"zeroBaseFee\":true},\"difficulty\":\"0x1\",\"extraData\":\"0xf83aa00000000000000000000000000000000000000000000000000000000000000000d59478e9bce6be9b0afa377f29669e8fda460df6838cc080c0\",\"gasLimit\":\"0x2fefd800\",\"mixHash\":\"0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365\"}", + }, + }, + }, } + mp.respond(res, nid, 200) } diff --git a/kaleido/platform/service_test.go b/kaleido/platform/service_test.go index 8aa697d..113f860 100644 --- a/kaleido/platform/service_test.go +++ b/kaleido/platform/service_test.go @@ -101,10 +101,12 @@ func TestService1(t *testing.T) { "GET /api/v1/environments/{env}/services/{service}", "GET /api/v1/environments/{env}/services/{service}", "GET /api/v1/environments/{env}/services/{service}", + "GET /api/v1/environments/{env}/services/{service}", "PUT /api/v1/environments/{env}/services/{service}", "GET /api/v1/environments/{env}/services/{service}", "GET /api/v1/environments/{env}/services/{service}", "GET /api/v1/environments/{env}/services/{service}", + "GET /api/v1/environments/{env}/services/{service}", "DELETE /api/v1/environments/{env}/services/{service}", "GET /api/v1/environments/{env}/services/{service}", }) From ef555f4cc91cbbf3178876d70df24a6caff6e69c Mon Sep 17 00:00:00 2001 From: Alex Wood Date: Mon, 7 Oct 2024 14:30:45 +0100 Subject: [PATCH 3/8] remove defunct example --- examples/platform_stack_gov/.gitignore | 4 - examples/platform_stack_gov/main.tf | 504 ----------------- .../platform_stack_gov/main_discovered.tf | 531 ------------------ examples/platform_stack_gov/variables.tf | 20 - 4 files changed, 1059 deletions(-) delete mode 100644 examples/platform_stack_gov/.gitignore delete mode 100644 examples/platform_stack_gov/main.tf delete mode 100644 examples/platform_stack_gov/main_discovered.tf delete mode 100644 examples/platform_stack_gov/variables.tf diff --git a/examples/platform_stack_gov/.gitignore b/examples/platform_stack_gov/.gitignore deleted file mode 100644 index 26eb080..0000000 --- a/examples/platform_stack_gov/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -.terraform* -terraform.d -terraform.tfstate* -terraform.tfvars \ No newline at end of file diff --git a/examples/platform_stack_gov/main.tf b/examples/platform_stack_gov/main.tf deleted file mode 100644 index 06fa06f..0000000 --- a/examples/platform_stack_gov/main.tf +++ /dev/null @@ -1,504 +0,0 @@ -terraform { - required_providers { - kaleido = { - source = "kaleido-io/kaleido" - version = "1.1.0-rc.4" - } - } -} - -provider "kaleido" { - platform_api = var.kaleido_platform_api - platform_username = var.kaleido_platform_username - platform_password = var.kaleido_platform_password -} - -resource "kaleido_platform_environment" "env_0" { - name = var.environment_name -} - -resource "kaleido_platform_network" "net_0" { - type = "Besu" - name = "evmchain1" - environment = kaleido_platform_environment.env_0.id - config_json = jsonencode({ - bootstrapOptions = { - qbft = { - blockperiodseconds = 2 - } - } - }) -} - - -resource "kaleido_platform_runtime" "bnr" { - type = "BesuNode" - name = "evmchain1_node${count.index+1}" - environment = kaleido_platform_environment.env_0.id - config_json = jsonencode({}) - count = var.node_count -} - -resource "kaleido_platform_service" "bns" { - type = "BesuNode" - name = "evmchain1_node${count.index+1}" - environment = kaleido_platform_environment.env_0.id - runtime = kaleido_platform_runtime.bnr[count.index].id - config_json = jsonencode({ - network = { - id = kaleido_platform_network.net_0.id - } - }) - count = var.node_count -} - -resource "kaleido_platform_runtime" "gwr_0" { - type = "EVMGateway" - name = "evmchain1_gateway" - environment = kaleido_platform_environment.env_0.id - config_json = jsonencode({}) -} - -resource "kaleido_platform_service" "gws_0" { - type = "EVMGateway" - name = "evmchain1_gateway" - environment = kaleido_platform_environment.env_0.id - runtime = kaleido_platform_runtime.gwr_0.id - config_json = jsonencode({ - network = { - id = kaleido_platform_network.net_0.id - } - }) -} - -data "kaleido_platform_evm_netinfo" "gws_0" { - environment = kaleido_platform_environment.env_0.id - service = kaleido_platform_service.gws_0.id - depends_on = [ - kaleido_platform_service.bns, - kaleido_platform_service.gws_0 - ] -} - -resource "kaleido_platform_runtime" "kmr_0" { - type = "KeyManager" - name = "kms1" - environment = kaleido_platform_environment.env_0.id - config_json = jsonencode({}) -} - -resource "kaleido_platform_service" "kms_0" { - type = "KeyManager" - name = "kms1" - environment = kaleido_platform_environment.env_0.id - runtime = kaleido_platform_runtime.kmr_0.id - config_json = jsonencode({}) -} - -resource "kaleido_platform_kms_wallet" "wallet_0" { - type = "hdwallet" - name = "hdwallet1" - environment = kaleido_platform_environment.env_0.id - service = kaleido_platform_service.kms_0.id - config_json = jsonencode({}) -} - -resource "kaleido_platform_kms_key" "key_0" { - name = "key0" - environment = kaleido_platform_environment.env_0.id - service = kaleido_platform_service.kms_0.id - wallet = kaleido_platform_kms_wallet.wallet_0.id -} - -resource "kaleido_platform_runtime" "tmr_0" { - type = "TransactionManager" - name = "evmchain1_txmgr" - environment = kaleido_platform_environment.env_0.id - config_json = jsonencode({}) -} - -resource "kaleido_platform_service" "tms_0" { - type = "TransactionManager" - name = "evmchain1_txmgr" - environment = kaleido_platform_environment.env_0.id - runtime = kaleido_platform_runtime.tmr_0.id - config_json = jsonencode({ - keyManager = { - id: kaleido_platform_service.kms_0.id - } - type = "evm" - evm = { - confirmations = { - required = 0 - } - connector = { - evmGateway = { - id = kaleido_platform_service.gws_0.id - } - # url = var.json_rpc_url - # auth = { - # credSetRef = "rpc_auth" - # } - } - } - }) - # cred_sets = { - # "rpc_auth" = { - # type = "basic_auth" - # basic_auth = { - # username = var.json_rpc_username - # password = var.json_rpc_password - # } - # } - # } -} - -resource "kaleido_platform_runtime" "ffr_0" { - type = "FireFly" - name = "firefly1" - environment = kaleido_platform_environment.env_0.id - config_json = jsonencode({}) -} - -resource "kaleido_platform_service" "ffs_0" { - type = "FireFly" - name = "firefly1" - environment = kaleido_platform_environment.env_0.id - runtime = kaleido_platform_runtime.ffr_0.id - config_json = jsonencode({ - transactionManager = { - id = kaleido_platform_service.tms_0.id - } - }) -} - -resource "kaleido_platform_runtime" "cmr_0" { - type = "ContractManager" - name = "smart_contract_manager" - environment = kaleido_platform_environment.env_0.id - config_json = jsonencode({}) -} - -resource "kaleido_platform_service" "cms_0" { - type = "ContractManager" - name = "smart_contract_manager" - environment = kaleido_platform_environment.env_0.id - runtime = kaleido_platform_runtime.cmr_0.id - config_json = jsonencode({}) -} - -resource "kaleido_platform_runtime" "bir_0"{ - type = "BlockIndexer" - name = "block_indexer" - environment = kaleido_platform_environment.env_0.id - config_json = jsonencode({}) -} - -resource "kaleido_platform_service" "bis_0"{ - type = "BlockIndexer" - name = "block_indexer" - environment = kaleido_platform_environment.env_0.id - runtime = kaleido_platform_runtime.bir_0.id - config_json=jsonencode( - { - contractManager = { - id = kaleido_platform_service.cms_0.id - } - evmGateway = { - id = kaleido_platform_service.gws_0.id - } - } - ) - hostnames = {"${lower(replace(var.environment_name, "/[^\\w]/", ""))}_${kaleido_platform_network.net_0.name}" = ["ui", "rest"]} -} - -resource "kaleido_platform_runtime" "amr_0" { - type = "AssetManager" - name = "asset_manager1" - environment = kaleido_platform_environment.env_0.id - config_json = jsonencode({}) -} - -resource "kaleido_platform_service" "ams_0" { - type = "AssetManager" - name = "asset_manager1" - environment = kaleido_platform_environment.env_0.id - runtime = kaleido_platform_runtime.amr_0.id - config_json = jsonencode({ - keyManager = { - id: kaleido_platform_service.kms_0.id - } - }) -} - -resource "kaleido_platform_cms_build" "erc20" { - environment = kaleido_platform_environment.env_0.id - service = kaleido_platform_service.cms_0.id - type = "github" - name = "ERC20WithData" - path = "erc20_samples" - github = { - contract_url = "https://github.com/hyperledger/firefly-tokens-erc20-erc721/blob/main/samples/solidity/contracts/ERC20WithData.sol" - contract_name = "ERC20WithData" - } -} - -resource "kaleido_platform_cms_build" "erc721" { - environment = kaleido_platform_environment.env_0.id - service = kaleido_platform_service.cms_0.id - type = "github" - name = "ERC721WithData" - path = "erc721_samples" - github = { - contract_url = "https://github.com/hyperledger/firefly-tokens-erc20-erc721/blob/main/samples/solidity/contracts/ERC721WithData.sol" - contract_name = "ERC721WithData" - } - depends_on = [ kaleido_platform_cms_build.erc20 ] // don't compile in parallel (excessive disk usage for npm) -} - -resource "kaleido_platform_cms_action_deploy" "demotoken_erc20" { - environment = kaleido_platform_environment.env_0.id - service = kaleido_platform_service.cms_0.id - build = kaleido_platform_cms_build.erc20.id - name = "deploy_erc20" - firefly_namespace = kaleido_platform_service.ffs_0.name - signing_key = kaleido_platform_kms_key.key_0.address - params_json = jsonencode([ - "DemoToken", - "DTOK" - ]) - depends_on = [ data.kaleido_platform_evm_netinfo.gws_0 ] -} - -resource "kaleido_platform_cms_action_deploy" "demotoken_erc721" { - environment = kaleido_platform_environment.env_0.id - service = kaleido_platform_service.cms_0.id - build = kaleido_platform_cms_build.erc721.id - name = "deploy_erc721" - firefly_namespace = kaleido_platform_service.ffs_0.name - signing_key = kaleido_platform_kms_key.key_0.address - params_json = jsonencode([ - "DemoNFT", - "DNFT", - "demo://token/" - ]) - depends_on = [ data.kaleido_platform_evm_netinfo.gws_0 ] -} - -resource "kaleido_platform_cms_action_createapi" "erc20" { - environment = kaleido_platform_environment.env_0.id - service = kaleido_platform_service.cms_0.id - build = kaleido_platform_cms_build.erc20.id - name = "erc20withdata" - firefly_namespace = kaleido_platform_service.ffs_0.name - api_name = "erc20withdata" - depends_on = [ data.kaleido_platform_evm_netinfo.gws_0 ] -} - -resource "kaleido_platform_cms_action_createapi" "erc721" { - environment = kaleido_platform_environment.env_0.id - service = kaleido_platform_service.cms_0.id - build = kaleido_platform_cms_build.erc721.id - name = "erc721withdata" - firefly_namespace = kaleido_platform_service.ffs_0.name - api_name = "erc721withdata" - depends_on = [ data.kaleido_platform_evm_netinfo.gws_0 ] -} - -resource "kaleido_platform_ams_task" "erc20_indexer" { - environment = kaleido_platform_environment.env_0.id - service = kaleido_platform_service.ams_0.id - depends_on = [ kaleido_platform_ams_task.erc20_indexer ] - name = "erc20_indexer" - task_yaml = <- - [{ - "updateType": "create_or_ignore", - "name": "erc721_" & input.blockchainEvent.info.address - }] - assets: >- - [{ - "updateType": "create_or_update", - "collection": "erc721_" & input.blockchainEvent.info.address, - "name": "erc721_" & input.blockchainEvent.info.address & "_" & input.blockchainEvent.output.tokenId - }] - nfts: >- - [{ - "updateType": "create_or_ignore", - "asset": "erc721_" & input.blockchainEvent.info.address & "_" & input.blockchainEvent.output.tokenId, - "name": "erc721", - "standard": "ERC-721", - "address": input.blockchainEvent.info.address, - "tokenIndex": input.blockchainEvent.output.tokenId, - "uri": steps.query_uri.data.output - }] - transfers: >- - [{ - "protocolId": input.blockchainEvent.protocolId, - "asset": "erc721_" & input.blockchainEvent.info.address & "_" & input.blockchainEvent.output.tokenId, - "from": input.blockchainEvent.output.from, - "to": input.blockchainEvent.output.to, - "amount": "1", - "transactionHash": input.blockchainEvent.info.transactionHash, - "parent": { - "type": "nft", - "ref": "erc721" - } - }] - name: upsert_transfer - options: {} - skip: |- - input.blockchainEvent.info.signature != - "Transfer(address,address,uint256)" or - $boolean([input.blockchainEvent.output.tokenId]) = false - type: data_model_update - EOT -} - -resource "kaleido_platform_ams_fflistener" "erc721_indexer" { - environment = kaleido_platform_environment.env_0.id - service = kaleido_platform_service.ams_0.id - name = "erc721_indexer" - depends_on = [ kaleido_platform_ams_task.erc721_indexer ] - config_json = jsonencode({ - namespace = kaleido_platform_service.ffs_0.name, - taskName = "erc721_indexer", - blockchainEvents = { - createOptions = { - firstEvent = "0" - }, - abiEvents = [ - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": true, - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - } - ], - "name": "Transfer", - "type": "event" - } - ] - } - }) -} \ No newline at end of file diff --git a/examples/platform_stack_gov/main_discovered.tf b/examples/platform_stack_gov/main_discovered.tf deleted file mode 100644 index 9843b67..0000000 --- a/examples/platform_stack_gov/main_discovered.tf +++ /dev/null @@ -1,531 +0,0 @@ -terraform { - required_providers { - kaleido = { - source = "kaleido-io/kaleido" - version = "1.1.0-rc.4" - } - } -} - -provider "kaleido" { - platform_api = var.kaleido_platform_api - platform_username = var.kaleido_platform_username - platform_password = var.kaleido_platform_password -} - -resource "kaleido_platform_environment" "env_0" { - name = var.environment_name -} - -resource "kaleido_platform_network" "net_0" { - type = "Besu" - name = "evmchain1" - environment = kaleido_platform_environment.env_0.id - config_json = jsonencode({ - bootstrapOptions = { - qbft = { - blockperiodseconds = 2 - } - } - }) -} - -resource "kaleido_platform_network_connection" "conn_0" { - network = net_0.id - name = "conn0" - environment = kaleido_platform_environment.env_0.id - config_json = jsonencode({ - }) -} - -resource "kaleido_platform_network" "net_1" { - type = "Besu" - name = "evmchain1" - environment = kaleido_platform_environment.env_0.id - config_json = jsonencode({ - chainID = net_0.config.chainID - externalGenesis = true - }) -} - -resource "kaleido_platform_network_connection" "conn_1" { - network = net_1.id - name = "conn1" - environment = kaleido_platform_environment.env_0.id - config_json = jsonencode({ - - }) -} - - -resource "kaleido_platform_runtime" "bnr" { - type = "BesuNode" - name = "evmchain1_node${count.index+1}" - environment = kaleido_platform_environment.env_0.id - config_json = jsonencode({}) - count = var.node_count -} - -resource "kaleido_platform_service" "bns" { - type = "BesuNode" - name = "evmchain1_node${count.index+1}" - environment = kaleido_platform_environment.env_0.id - runtime = kaleido_platform_runtime.bnr[count.index].id - config_json = jsonencode({ - network = { - id = kaleido_platform_network.net_0.id - } - }) - count = var.node_count -} - -resource "kaleido_platform_runtime" "gwr_0" { - type = "EVMGateway" - name = "evmchain1_gateway" - environment = kaleido_platform_environment.env_0.id - config_json = jsonencode({}) -} - -resource "kaleido_platform_service" "gws_0" { - type = "EVMGateway" - name = "evmchain1_gateway" - environment = kaleido_platform_environment.env_0.id - runtime = kaleido_platform_runtime.gwr_0.id - config_json = jsonencode({ - network = { - id = kaleido_platform_network.net_0.id - } - }) -} - -data "kaleido_platform_evm_netinfo" "gws_0" { - environment = kaleido_platform_environment.env_0.id - service = kaleido_platform_service.gws_0.id - depends_on = [ - kaleido_platform_service.bns, - kaleido_platform_service.gws_0 - ] -} - -resource "kaleido_platform_runtime" "kmr_0" { - type = "KeyManager" - name = "kms1" - environment = kaleido_platform_environment.env_0.id - config_json = jsonencode({}) -} - -resource "kaleido_platform_service" "kms_0" { - type = "KeyManager" - name = "kms1" - environment = kaleido_platform_environment.env_0.id - runtime = kaleido_platform_runtime.kmr_0.id - config_json = jsonencode({}) -} - -resource "kaleido_platform_kms_wallet" "wallet_0" { - type = "hdwallet" - name = "hdwallet1" - environment = kaleido_platform_environment.env_0.id - service = kaleido_platform_service.kms_0.id - config_json = jsonencode({}) -} - -resource "kaleido_platform_kms_key" "key_0" { - name = "key0" - environment = kaleido_platform_environment.env_0.id - service = kaleido_platform_service.kms_0.id - wallet = kaleido_platform_kms_wallet.wallet_0.id -} - -resource "kaleido_platform_runtime" "tmr_0" { - type = "TransactionManager" - name = "evmchain1_txmgr" - environment = kaleido_platform_environment.env_0.id - config_json = jsonencode({}) -} - -resource "kaleido_platform_service" "tms_0" { - type = "TransactionManager" - name = "evmchain1_txmgr" - environment = kaleido_platform_environment.env_0.id - runtime = kaleido_platform_runtime.tmr_0.id - config_json = jsonencode({ - keyManager = { - id: kaleido_platform_service.kms_0.id - } - type = "evm" - evm = { - confirmations = { - required = 0 - } - connector = { - evmGateway = { - id = kaleido_platform_service.gws_0.id - } - # url = var.json_rpc_url - # auth = { - # credSetRef = "rpc_auth" - # } - } - } - }) - # cred_sets = { - # "rpc_auth" = { - # type = "basic_auth" - # basic_auth = { - # username = var.json_rpc_username - # password = var.json_rpc_password - # } - # } - # } -} - -resource "kaleido_platform_runtime" "ffr_0" { - type = "FireFly" - name = "firefly1" - environment = kaleido_platform_environment.env_0.id - config_json = jsonencode({}) -} - -resource "kaleido_platform_service" "ffs_0" { - type = "FireFly" - name = "firefly1" - environment = kaleido_platform_environment.env_0.id - runtime = kaleido_platform_runtime.ffr_0.id - config_json = jsonencode({ - transactionManager = { - id = kaleido_platform_service.tms_0.id - } - }) -} - -resource "kaleido_platform_runtime" "cmr_0" { - type = "ContractManager" - name = "smart_contract_manager" - environment = kaleido_platform_environment.env_0.id - config_json = jsonencode({}) -} - -resource "kaleido_platform_service" "cms_0" { - type = "ContractManager" - name = "smart_contract_manager" - environment = kaleido_platform_environment.env_0.id - runtime = kaleido_platform_runtime.cmr_0.id - config_json = jsonencode({}) -} - -resource "kaleido_platform_runtime" "bir_0"{ - type = "BlockIndexer" - name = "block_indexer" - environment = kaleido_platform_environment.env_0.id - config_json = jsonencode({}) -} - -resource "kaleido_platform_service" "bis_0"{ - type = "BlockIndexer" - name = "block_indexer" - environment = kaleido_platform_environment.env_0.id - runtime = kaleido_platform_runtime.bir_0.id - config_json=jsonencode( - { - contractManager = { - id = kaleido_platform_service.cms_0.id - } - evmGateway = { - id = kaleido_platform_service.gws_0.id - } - } - ) - hostnames = {"${lower(replace(var.environment_name, "/[^\\w]/", ""))}_${kaleido_platform_network.net_0.name}" = ["ui", "rest"]} -} - -resource "kaleido_platform_runtime" "amr_0" { - type = "AssetManager" - name = "asset_manager1" - environment = kaleido_platform_environment.env_0.id - config_json = jsonencode({}) -} - -resource "kaleido_platform_service" "ams_0" { - type = "AssetManager" - name = "asset_manager1" - environment = kaleido_platform_environment.env_0.id - runtime = kaleido_platform_runtime.amr_0.id - config_json = jsonencode({ - keyManager = { - id: kaleido_platform_service.kms_0.id - } - }) -} - -resource "kaleido_platform_cms_build" "erc20" { - environment = kaleido_platform_environment.env_0.id - service = kaleido_platform_service.cms_0.id - type = "github" - name = "ERC20WithData" - path = "erc20_samples" - github = { - contract_url = "https://github.com/hyperledger/firefly-tokens-erc20-erc721/blob/main/samples/solidity/contracts/ERC20WithData.sol" - contract_name = "ERC20WithData" - } -} - -resource "kaleido_platform_cms_build" "erc721" { - environment = kaleido_platform_environment.env_0.id - service = kaleido_platform_service.cms_0.id - type = "github" - name = "ERC721WithData" - path = "erc721_samples" - github = { - contract_url = "https://github.com/hyperledger/firefly-tokens-erc20-erc721/blob/main/samples/solidity/contracts/ERC721WithData.sol" - contract_name = "ERC721WithData" - } - depends_on = [ kaleido_platform_cms_build.erc20 ] // don't compile in parallel (excessive disk usage for npm) -} - -resource "kaleido_platform_cms_action_deploy" "demotoken_erc20" { - environment = kaleido_platform_environment.env_0.id - service = kaleido_platform_service.cms_0.id - build = kaleido_platform_cms_build.erc20.id - name = "deploy_erc20" - firefly_namespace = kaleido_platform_service.ffs_0.name - signing_key = kaleido_platform_kms_key.key_0.address - params_json = jsonencode([ - "DemoToken", - "DTOK" - ]) - depends_on = [ data.kaleido_platform_evm_netinfo.gws_0 ] -} - -resource "kaleido_platform_cms_action_deploy" "demotoken_erc721" { - environment = kaleido_platform_environment.env_0.id - service = kaleido_platform_service.cms_0.id - build = kaleido_platform_cms_build.erc721.id - name = "deploy_erc721" - firefly_namespace = kaleido_platform_service.ffs_0.name - signing_key = kaleido_platform_kms_key.key_0.address - params_json = jsonencode([ - "DemoNFT", - "DNFT", - "demo://token/" - ]) - depends_on = [ data.kaleido_platform_evm_netinfo.gws_0 ] -} - -resource "kaleido_platform_cms_action_createapi" "erc20" { - environment = kaleido_platform_environment.env_0.id - service = kaleido_platform_service.cms_0.id - build = kaleido_platform_cms_build.erc20.id - name = "erc20withdata" - firefly_namespace = kaleido_platform_service.ffs_0.name - api_name = "erc20withdata" - depends_on = [ data.kaleido_platform_evm_netinfo.gws_0 ] -} - -resource "kaleido_platform_cms_action_createapi" "erc721" { - environment = kaleido_platform_environment.env_0.id - service = kaleido_platform_service.cms_0.id - build = kaleido_platform_cms_build.erc721.id - name = "erc721withdata" - firefly_namespace = kaleido_platform_service.ffs_0.name - api_name = "erc721withdata" - depends_on = [ data.kaleido_platform_evm_netinfo.gws_0 ] -} - -resource "kaleido_platform_ams_task" "erc20_indexer" { - environment = kaleido_platform_environment.env_0.id - service = kaleido_platform_service.ams_0.id - depends_on = [ kaleido_platform_ams_task.erc20_indexer ] - name = "erc20_indexer" - task_yaml = <- - [{ - "updateType": "create_or_ignore", - "name": "erc721_" & input.blockchainEvent.info.address - }] - assets: >- - [{ - "updateType": "create_or_update", - "collection": "erc721_" & input.blockchainEvent.info.address, - "name": "erc721_" & input.blockchainEvent.info.address & "_" & input.blockchainEvent.output.tokenId - }] - nfts: >- - [{ - "updateType": "create_or_ignore", - "asset": "erc721_" & input.blockchainEvent.info.address & "_" & input.blockchainEvent.output.tokenId, - "name": "erc721", - "standard": "ERC-721", - "address": input.blockchainEvent.info.address, - "tokenIndex": input.blockchainEvent.output.tokenId, - "uri": steps.query_uri.data.output - }] - transfers: >- - [{ - "protocolId": input.blockchainEvent.protocolId, - "asset": "erc721_" & input.blockchainEvent.info.address & "_" & input.blockchainEvent.output.tokenId, - "from": input.blockchainEvent.output.from, - "to": input.blockchainEvent.output.to, - "amount": "1", - "transactionHash": input.blockchainEvent.info.transactionHash, - "parent": { - "type": "nft", - "ref": "erc721" - } - }] - name: upsert_transfer - options: {} - skip: |- - input.blockchainEvent.info.signature != - "Transfer(address,address,uint256)" or - $boolean([input.blockchainEvent.output.tokenId]) = false - type: data_model_update - EOT -} - -resource "kaleido_platform_ams_fflistener" "erc721_indexer" { - environment = kaleido_platform_environment.env_0.id - service = kaleido_platform_service.ams_0.id - name = "erc721_indexer" - depends_on = [ kaleido_platform_ams_task.erc721_indexer ] - config_json = jsonencode({ - namespace = kaleido_platform_service.ffs_0.name, - taskName = "erc721_indexer", - blockchainEvents = { - createOptions = { - firstEvent = "0" - }, - abiEvents = [ - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": true, - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - } - ], - "name": "Transfer", - "type": "event" - } - ] - } - }) -} \ No newline at end of file diff --git a/examples/platform_stack_gov/variables.tf b/examples/platform_stack_gov/variables.tf deleted file mode 100644 index a066288..0000000 --- a/examples/platform_stack_gov/variables.tf +++ /dev/null @@ -1,20 +0,0 @@ -variable "kaleido_platform_api" { - type = string -} - -variable "kaleido_platform_username" { - type = string -} - -variable "kaleido_platform_password" { - type = string -} - -variable "environment_name" { - type = string -} - -variable "node_count" { - type = number - default = 1 -} From bd512baf12256c7270b3fd0682b0d0d054aead17 Mon Sep 17 00:00:00 2001 From: Alex Wood Date: Mon, 7 Oct 2024 14:33:34 +0100 Subject: [PATCH 4/8] Update providerdata.go fix merge --- kaleido/kaleidobase/providerdata.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/kaleido/kaleidobase/providerdata.go b/kaleido/kaleidobase/providerdata.go index fea4982..7cd868b 100644 --- a/kaleido/kaleidobase/providerdata.go +++ b/kaleido/kaleidobase/providerdata.go @@ -15,7 +15,6 @@ package kaleidobase import ( "context" - "crypto/tls" "fmt" "net/http" "os" @@ -91,7 +90,6 @@ func NewProviderData(logCtx context.Context, conf *ProviderModel) *ProviderData platform := resty.New(). SetTransport(http.DefaultTransport). SetBaseURL(platformAPI). - SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true}) //TODO remove this if platformUsername != "" && platformPassword != "" { platform = platform.SetBasicAuth(platformUsername, platformPassword) From 63230143ffa6f15fc5b97296edd192576c9ebf7e Mon Sep 17 00:00:00 2001 From: Alex Wood Date: Mon, 7 Oct 2024 14:34:01 +0100 Subject: [PATCH 5/8] Update providerdata.go fix merge --- kaleido/kaleidobase/providerdata.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kaleido/kaleidobase/providerdata.go b/kaleido/kaleidobase/providerdata.go index 7cd868b..4a37ab6 100644 --- a/kaleido/kaleidobase/providerdata.go +++ b/kaleido/kaleidobase/providerdata.go @@ -89,8 +89,7 @@ func NewProviderData(logCtx context.Context, conf *ProviderModel) *ProviderData } platform := resty.New(). SetTransport(http.DefaultTransport). - SetBaseURL(platformAPI). - + SetBaseURL(platformAPI) if platformUsername != "" && platformPassword != "" { platform = platform.SetBasicAuth(platformUsername, platformPassword) } From 62b93b0914a728f8d258d89009566a854d6d4900 Mon Sep 17 00:00:00 2001 From: Alex Wood Date: Mon, 7 Oct 2024 14:36:05 +0100 Subject: [PATCH 6/8] remove erroneous file --- terraform.tfstate | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 terraform.tfstate diff --git a/terraform.tfstate b/terraform.tfstate deleted file mode 100644 index 1b66c42..0000000 --- a/terraform.tfstate +++ /dev/null @@ -1,9 +0,0 @@ -{ - "version": 4, - "terraform_version": "1.8.5", - "serial": 1, - "lineage": "338a0150-8df8-f675-dff5-3634a897913e", - "outputs": {}, - "resources": [], - "check_results": null -} From 2d5838f7d5d1c1aeff061f8f3cf9433b63071c91 Mon Sep 17 00:00:00 2001 From: Alex Wood Date: Mon, 7 Oct 2024 14:45:41 +0100 Subject: [PATCH 7/8] fix merge (zones added in v1.1 already) --- kaleido/platform/runtime.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/kaleido/platform/runtime.go b/kaleido/platform/runtime.go index 1ea6c72..c36d90c 100644 --- a/kaleido/platform/runtime.go +++ b/kaleido/platform/runtime.go @@ -36,7 +36,6 @@ type RuntimeResourceModel struct { ConfigJSON types.String `tfsdk:"config_json"` LogLevel types.String `tfsdk:"log_level"` Size types.String `tfsdk:"size"` - Zone types.String `tfsdk:"zone"` EnvironmentMemberID types.String `tfsdk:"environment_member_id"` Stopped types.Bool `tfsdk:"stopped"` Zone types.String `tfsdk:"zone"` @@ -54,7 +53,6 @@ type RuntimeAPIModel struct { Config map[string]interface{} `json:"config"` LogLevel string `json:"loglevel,omitempty"` Size string `json:"size,omitempty"` - Zone string `json:"zone,omitempty"` EnvironmentMemberID string `json:"environmentMemberId,omitempty"` Status string `json:"status,omitempty"` Deleted bool `json:"deleted,omitempty"` @@ -109,10 +107,6 @@ func (r *runtimeResource) Schema(_ context.Context, _ resource.SchemaRequest, re Optional: true, Computed: true, }, - "zone": &schema.StringAttribute{ - Optional: true, - Computed: true, - }, "stopped": &schema.BoolAttribute{ Optional: true, Computed: true, From 9b7cb2f4536b9708eb258cf43231435b286cbb98 Mon Sep 17 00:00:00 2001 From: Alex Wood Date: Tue, 8 Oct 2024 16:05:30 +0100 Subject: [PATCH 8/8] Update service.go --- kaleido/platform/service.go | 64 ------------------------------------- 1 file changed, 64 deletions(-) diff --git a/kaleido/platform/service.go b/kaleido/platform/service.go index d8b573e..ffd2444 100644 --- a/kaleido/platform/service.go +++ b/kaleido/platform/service.go @@ -215,34 +215,6 @@ func (r *serviceResource) Schema(_ context.Context, _ resource.SchemaRequest, re "connectivity_json": &schema.StringAttribute{ Computed: true, }, - /*"connectivity": &schema.SingleNestedAttribute{ - Computed: true, - Optional: true, - Attributes: map[string]schema.Attribute{ - "identity": &schema.StringAttribute{ - Computed: true, - }, - "endpoints": &schema.ListNestedAttribute{ - Computed: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "host": &schema.StringAttribute{ - Computed: true, - }, - "nat": &schema.StringAttribute{ - Computed: true, - }, - "port": &schema.Int64Attribute{ - Computed: true, - }, - "protocol": &schema.StringAttribute{ - Computed: true, - }, - }, - }, - }, - }, - },*/ }, } } @@ -402,42 +374,6 @@ func (api *ServiceAPIModel) toData(data *ServiceResourceModel, diagnostics *diag } else { data.ConnectivityJSON = types.StringValue("") } - - /*connectivity := map[string]attr.Value{} - connEndpointAttrTypes := map[string]attr.Type{ - "host": types.StringType, - "nat": types.StringType, - "port": types.Int64Type, - "protocol": types.StringType, - } - connectivityAttrTypes := map[string]attr.Type{ - "identity": types.StringType, - "endpoints": types.ListType{ElemType: types.ObjectType{ - AttrTypes: connEndpointAttrTypes, - }}, - } - connectivity["identity"] = types.StringValue(api.StatusDetails.Connectivity.Identity) - connEndpoints := make([]attr.Value, len(api.StatusDetails.Connectivity.Endpoints)) - - for i, ce := range api.StatusDetails.Connectivity.Endpoints { - connEndpoint := map[string]attr.Value{} - connEndpoint["host"] = types.StringValue(ce.Host) - connEndpoint["nat"] = types.StringValue(ce.NAT) - connEndpoint["port"] = types.Int64Value(ce.Port) - connEndpoint["protocol"] = types.StringValue(ce.Protocol) - tfConnEndpoint, d := types.ObjectValue(connEndpointAttrTypes, connEndpoint) - diagnostics.Append(d...) - connEndpoints[i] = tfConnEndpoint - } - tfEndpoints, d := types.ListValue(types.ObjectType{ - AttrTypes: connEndpointAttrTypes, - }, connEndpoints) - diagnostics.Append(d...) - connectivity["endpoints"] = tfEndpoints - - data.Connectivity, d = types.ObjectValue(connectivityAttrTypes, connectivity) - diagnostics.Append(d...)*/ - } func (r *serviceResource) apiPath(data *ServiceResourceModel) string {