├── hack └── boilerplate.go.txt ├── controllers ├── ethereum │ ├── types.go │ ├── nethermind_copy_keystore.sh │ ├── geth_init_genesis.sh │ ├── geth_import_account.sh │ ├── nethermind_convert_enode_privatekey.sh │ └── keystore.go ├── filecoin │ ├── types.go │ └── copy_config_toml.sh ├── near │ ├── types.go │ ├── copy_node_key.sh │ ├── copy_validator_key.sh │ └── init_near_node.sh ├── polkadot │ ├── types.go │ └── convert_node_private_key.sh ├── ipfs │ ├── copy_swarm_key.sh │ ├── init_ipfs_config.sh │ ├── init_ipfs_cluster_config.sh │ └── config_ipfs.sh ├── shared │ ├── types.go │ ├── webhook.go │ ├── env.go │ ├── errors.go │ ├── host.go │ ├── host_test.go │ ├── webhook_test.go │ ├── secret.go │ ├── security_context.go │ ├── path_test.go │ ├── path.go │ ├── reconciler.go │ ├── security_context_test.go │ ├── labels.go │ └── labels_test.go ├── aptos │ ├── download_waypoint.sh │ └── download_genesis_block.sh ├── chainlink │ └── copy_api_credentials.sh ├── ethereum2 │ ├── nimbus_copy_validators.sh │ ├── lighthouse_import_keystore.sh │ └── prysm_import_keystore.sh └── graph │ └── suite_test.go ├── config ├── prometheus │ ├── kustomization.yaml │ └── monitor.yaml ├── certmanager │ ├── kustomization.yaml │ ├── kustomizeconfig.yaml │ └── certificate.yaml ├── rbac │ ├── service_account.yaml │ ├── auth_proxy_client_clusterrole.yaml │ ├── role_binding.yaml │ ├── auth_proxy_role_binding.yaml │ ├── leader_election_role_binding.yaml │ ├── auth_proxy_service.yaml │ ├── auth_proxy_role.yaml │ ├── ipfs_peer_viewer_role.yaml │ ├── graph_node_viewer_role.yaml │ ├── near_node_viewer_role.yaml │ ├── stacks_node_viewer_role.yaml │ ├── bitcoin_node_viewer_role.yaml │ ├── ethereum_node_viewer_role.yaml │ ├── filecoin_node_viewer_role.yaml │ ├── polkadot_node_viewer_role.yaml │ ├── chainlink_node_viewer_role.yaml │ ├── ipfs_peer_editor_role.yaml │ ├── clusterpeer_viewer_role.yaml │ ├── kustomization.yaml │ ├── ethereum2_validator_viewer_role.yaml │ ├── ethereum2_beaconnode_viewer_role.yaml │ ├── graph_node_editor_role.yaml │ ├── near_node_editor_role.yaml │ ├── bitcoin_node_editor_role.yaml │ ├── stacks_node_editor_role.yaml │ ├── ethereum_node_editor_role.yaml │ ├── filecoin_node_editor_role.yaml │ ├── polkadot_node_editor_role.yaml │ ├── chainlink_node_editor_role.yaml │ ├── clusterpeer_editor_role.yaml │ ├── ethereum2_validator_editor_role.yaml │ ├── ethereum2_beaconnode_editor_role.yaml │ └── leader_election_role.yaml ├── webhook │ ├── kustomization.yaml │ ├── service.yaml │ └── kustomizeconfig.yaml ├── samples │ ├── graph │ │ └── graph_v1alpha1_node.yaml │ ├── ipfs │ │ ├── ipfs_v1alpha1_bare_peer.yaml │ │ ├── ipfs_v1alpha1_swarm_peer.yaml │ │ └── ipfs_v1alpha1_cluster_peer.yaml │ ├── near │ │ ├── near_v1alpha1_rpc_node.yaml │ │ ├── near_v1alpha1_archive_node.yaml │ │ ├── near_v1alpha1_validator_node.yaml │ │ └── near_v1alpha1_node.yaml │ ├── filecoin │ │ ├── v1alpha1_mainnet_node.yaml │ │ └── v1alpha1_calibration_node.yaml │ ├── ethereum2 │ │ ├── ethereum2_v1alpha1_nimbus_validator.yaml │ │ ├── ethereum2_v1alpha1_lighthouse_validator.yaml │ │ ├── ethereum2_v1alpha1_prysm_validator.yaml │ │ ├── ethereum2_v1alpha1_teku_validator.yaml │ │ ├── ethereum2_v1alpha1_nimbus_beacon_node.yaml │ │ ├── ethereum2_v1alpha1_lighthouse_beacon_node.yaml │ │ ├── ethereum2_v1alpha1_teku_beacon_node.yaml │ │ └── ethereum2_v1alpha1_prysm_beacon_node.yaml │ ├── polkadot │ │ ├── polkadot_v1alpha1_kusama_validator_node.yaml │ │ └── polkadot_v1alpha1_kusama_node.yaml │ ├── ethereum │ │ ├── ethereum_v1alpha1_mainnet_besu_node.yaml │ │ ├── ethereum_v1alpha1_mainnet_geth_node.yaml │ │ ├── ethereum_v1alpha1_mainnet_nethermind_node.yaml │ │ ├── ethereum_v1alpha1_goerli_besu_node.yaml │ │ ├── ethereum_v1alpha1_goerli_geth_node.yaml │ │ ├── ethereum_v1alpha1_poa_besu_node_no_genesis_no_clique.yaml │ │ ├── ethereum_v1alpha1_poa_geth_node_no_genesis_no_clique.yaml │ │ ├── ethereum_v1alpha1_goerli_nethermind_node.yaml │ │ ├── ethereum_v1alpha1_pow_besu_node_no_genesis_no_forks.yaml │ │ ├── ethereum_v1alpha1_poa_besu_node_no_genesis.yaml │ │ ├── ethereum_v1alpha1_ibft2_besu_node_no_forks.yaml │ │ ├── ethereum_v1alpha1_pow_geth_node_no_genesis_no_forks.yaml │ │ ├── ethereum_v1alpha1_pow_besu_node.yaml │ │ ├── ethereum_v1alpha1_pow_geth_node.yaml │ │ ├── ethereum_v1alpha1_poa_besu_node.yaml │ │ ├── ethereum_v1alpha1_ibft2_besu_node.yaml │ │ └── ethereum_v1alpha1_pow_nethermind_node.yaml │ ├── bitcoin │ │ └── bitcoin_v1alpha1_node.yaml │ ├── stacks │ │ ├── stacks_v1alpha1_node.yaml │ │ └── stacks_v1alpha1_miner_node.yaml │ └── aptos │ │ └── aptos_devnet_v1alpha1_node.yaml ├── manager │ ├── kustomization.yaml │ └── manager.yaml ├── crd │ ├── patches │ │ ├── cainjection_in_aptos_nodes.yaml │ │ ├── cainjection_in_graph_nodes.yaml │ │ ├── cainjection_in_bitcoin_nodes.yaml │ │ ├── cainjection_in_ethereum_nodes.yaml │ │ ├── cainjection_in_chainlink_nodes.yaml │ │ ├── cainjection_in_ipfs_peers.yaml │ │ ├── cainjection_in_near_nodes.yaml │ │ ├── cainjection_in_stacks_nodes.yaml │ │ ├── cainjection_in_filecoin_nodes.yaml │ │ ├── cainjection_in_polkadot_nodes.yaml │ │ ├── cainjection_in_ipfs_clusterpeers.yaml │ │ ├── cainjection_in_ethereum2_validators.yaml │ │ ├── cainjection_in_ethereum2_beaconnodes.yaml │ │ ├── webhook_in_near_nodes.yaml │ │ ├── webhook_in_aptos_nodes.yaml │ │ ├── webhook_in_stacks_nodes.yaml │ │ ├── webhook_in_bitcoin_nodes.yaml │ │ ├── webhook_in_chainlink_nodes.yaml │ │ ├── webhook_in_polkadot_nodes.yaml │ │ ├── webhook_in_graph_nodes.yaml │ │ ├── webhook_in_ipfs_peers.yaml │ │ ├── webhook_in_ethereum_nodes.yaml │ │ ├── webhook_in_filecoin_nodes.yaml │ │ ├── webhook_in_ipfs_clusterpeers.yaml │ │ ├── webhook_in_ethereum2_beaconnodes.yaml │ │ └── webhook_in_ethereum2_validators.yaml │ └── kustomizeconfig.yaml └── default │ ├── manager_webhook_patch.yaml │ ├── webhookcainjection_patch.yaml │ └── manager_auth_proxy_patch.yaml ├── clients ├── graph │ ├── types.go │ ├── suite_test.go │ ├── client.go │ ├── graph_node_client_test.go │ └── graph_node_client.go ├── aptos │ ├── types.go │ ├── suite_test.go │ ├── client.go │ ├── aptos_core_client_test.go │ └── aptos_core_client.go ├── chainlink │ ├── types.go │ ├── suite_test.go │ ├── client.go │ ├── chainlink_client.go │ └── chainlink_client_test.go ├── interface.go ├── ipfs │ ├── suite_test.go │ ├── client.go │ ├── kubo_client.go │ ├── kubo_client_test.go │ ├── go_ipfs_cluster_client.go │ └── go_ipfs_cluster_client_test.go ├── near │ ├── client.go │ ├── suite_test.go │ └── types.go ├── filecoin │ ├── types.go │ ├── suite_test.go │ ├── client.go │ ├── lotus_client.go │ └── lotus_client_test.go ├── ethereum │ ├── suite_test.go │ ├── client.go │ └── client_test.go ├── polkadot │ ├── suite_test.go │ └── client.go ├── stacks │ ├── client.go │ ├── suite_test.go │ ├── types.go │ ├── stacks_node_client_test.go │ └── stacks_node_client.go ├── bitcoin │ ├── suite_test.go │ └── client.go └── ethereum2 │ ├── suite_test.go │ ├── lighthouse_validator_client.go │ ├── client.go │ └── lighthouse_validator_client_test.go ├── apis ├── shared │ ├── types.go │ ├── suite_test.go │ ├── extraargs.go │ ├── utils.go │ ├── zz_generated.deepcopy.go │ └── logging.go ├── graph │ └── v1alpha1 │ │ ├── defaults.go │ │ ├── groupversion_info.go │ │ └── node.go ├── ipfs │ └── v1alpha1 │ │ ├── suite_test.go │ │ ├── peer_webhook.go │ │ ├── cluster_peer_webhook.go │ │ ├── groupversion_info.go │ │ ├── cluster_peer_defaulting_webhook_test.go │ │ ├── peer_defaulting_webhook_test.go │ │ ├── defaults.go │ │ ├── cluster_peer_defaulting_webhook.go │ │ └── peer_defaulting_webhook.go ├── near │ └── v1alpha1 │ │ ├── suite_test.go │ │ ├── node_webhook.go │ │ ├── groupversion_info.go │ │ ├── node_defaulting_webhook_test.go │ │ ├── defaults.go │ │ └── node_defaulting_webhook.go ├── aptos │ └── v1alpha1 │ │ ├── suite_test.go │ │ ├── node_webhook.go │ │ ├── groupversion_info.go │ │ ├── node_defaulting_webhook_test.go │ │ ├── defaults.go │ │ └── node_defaulting_webhook.go ├── bitcoin │ └── v1alpha1 │ │ ├── suite_test.go │ │ ├── node_webhook.go │ │ ├── groupversion_info.go │ │ ├── node_defaulting_webhook_test.go │ │ └── defaults.go ├── chainlink │ └── v1alpha1 │ │ ├── suite_test.go │ │ ├── node_webhook.go │ │ ├── groupversion_info.go │ │ ├── defaults.go │ │ ├── node_defaulting_webhook_test.go │ │ └── node_defaulting_webhook.go ├── ethereum │ └── v1alpha1 │ │ ├── suite_test.go │ │ ├── node_webhook.go │ │ ├── groupversion_info.go │ │ ├── network.go │ │ └── genesis_defaulting.go ├── ethereum2 │ └── v1alpha1 │ │ ├── suite_test.go │ │ ├── beacon_node_webhook.go │ │ ├── validator_webhook.go │ │ ├── groupversion_info.go │ │ ├── validator_defaulting_webhook_test.go │ │ └── client.go ├── filecoin │ └── v1alpha1 │ │ ├── suite_test.go │ │ ├── node_webhook.go │ │ ├── groupversion_info.go │ │ └── node_validation_webhook_test.go ├── polkadot │ └── v1alpha1 │ │ ├── suite_test.go │ │ ├── node_webhook.go │ │ ├── groupversion_info.go │ │ └── node_defaulting_webhook_test.go └── stacks │ └── v1alpha1 │ ├── suite_test.go │ ├── node_webhook.go │ ├── groupversion_info.go │ ├── node_defaulting_webhook_test.go │ ├── defaults.go │ └── node_defaulting_webhook.go ├── .gitignore ├── .github └── workflows │ ├── go.yml │ └── docker.yml ├── Dockerfile └── helpers └── node.go /hack/boilerplate.go.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /controllers/ethereum/types.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | -------------------------------------------------------------------------------- /controllers/filecoin/types.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | -------------------------------------------------------------------------------- /controllers/near/types.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | -------------------------------------------------------------------------------- /controllers/polkadot/types.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | -------------------------------------------------------------------------------- /config/prometheus/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - monitor.yaml 3 | -------------------------------------------------------------------------------- /config/certmanager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - certificate.yaml 3 | 4 | configurations: 5 | - kustomizeconfig.yaml 6 | -------------------------------------------------------------------------------- /controllers/ipfs/copy_swarm_key.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | mkdir -p $IPFS_PATH && 6 | cp $KOTAL_SECRETS_PATH/swarm.key $IPFS_PATH -------------------------------------------------------------------------------- /config/rbac/service_account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: controller-manager 5 | namespace: system 6 | -------------------------------------------------------------------------------- /config/webhook/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - manifests.yaml 3 | - service.yaml 4 | 5 | configurations: 6 | - kustomizeconfig.yaml 7 | -------------------------------------------------------------------------------- /clients/graph/types.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | const ( 4 | // GraphNodeCommand is graph node exec command 5 | GraphNodeCommand = "graph-node" 6 | ) 7 | -------------------------------------------------------------------------------- /config/samples/graph/graph_v1alpha1_node.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: graph.kotal.io/v1alpha1 2 | kind: Node 3 | metadata: 4 | name: node-sample 5 | spec: {} 6 | -------------------------------------------------------------------------------- /clients/aptos/types.go: -------------------------------------------------------------------------------- 1 | package aptos 2 | 3 | const ( 4 | // AptosArgConfig is argument used to locate node config file 5 | AptosArgConfig = "--config" 6 | ) 7 | -------------------------------------------------------------------------------- /controllers/filecoin/copy_config_toml.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | mkdir -p $KOTAL_DATA_PATH 6 | 7 | cp $KOTAL_CONFIG_PATH/config.toml $KOTAL_DATA_PATH -------------------------------------------------------------------------------- /controllers/shared/types.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | const ( 4 | // BusyboxImage is the busybox images used by init containers 5 | BusyboxImage = "busybox:1.34.1" 6 | ) 7 | -------------------------------------------------------------------------------- /controllers/aptos/download_waypoint.sh: -------------------------------------------------------------------------------- 1 | curl https://raw.githubusercontent.com/aptos-labs/aptos-networks/main/$KOTAL_NETWORK/waypoint.txt -o $KOTAL_DATA_PATH/kotal_waypoint.txt -------------------------------------------------------------------------------- /apis/shared/types.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | // EthereumAddress is ethereum address 4 | // +kubebuilder:validation:Pattern="^0[xX][0-9a-fA-F]{40}$" 5 | type EthereumAddress string 6 | -------------------------------------------------------------------------------- /config/samples/ipfs/ipfs_v1alpha1_bare_peer.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: ipfs.kotal.io/v1alpha1 2 | kind: Peer 3 | metadata: 4 | name: bare-peer 5 | spec: 6 | api: true 7 | gateway: true 8 | -------------------------------------------------------------------------------- /clients/chainlink/types.go: -------------------------------------------------------------------------------- 1 | package chainlink 2 | 3 | // arguments 4 | const ( 5 | // ChainlinkAPI is the argument used to locate api credentials file 6 | ChainlinkAPI = "--api" 7 | ) 8 | -------------------------------------------------------------------------------- /config/samples/near/near_v1alpha1_rpc_node.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: near.kotal.io/v1alpha1 2 | kind: Node 3 | metadata: 4 | name: near-rpc-node 5 | spec: 6 | network: mainnet 7 | rpc: true 8 | -------------------------------------------------------------------------------- /apis/graph/v1alpha1/defaults.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | // DefaultGraphNodeImage is the default Graph node client image 4 | const DefaultGraphNodeImage = "graphprotocol/graph-node:v0.27.0" 5 | -------------------------------------------------------------------------------- /config/samples/near/near_v1alpha1_archive_node.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: near.kotal.io/v1alpha1 2 | kind: Node 3 | metadata: 4 | name: near-archive-node 5 | spec: 6 | network: mainnet 7 | archive: true 8 | -------------------------------------------------------------------------------- /controllers/ethereum/nethermind_copy_keystore.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | mkdir -p $KOTAL_DATA_PATH/keystore 6 | 7 | cp $KOTAL_SECRETS_PATH/account $KOTAL_DATA_PATH/keystore/key-$KOTAL_COINBASE -------------------------------------------------------------------------------- /controllers/shared/webhook.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import "os" 4 | 5 | // IsWebhookEnabled checks if webhooks are enabled 6 | func IsWebhookEnabled() bool { 7 | return os.Getenv("ENABLE_WEBHOOKS") != "false" 8 | } 9 | -------------------------------------------------------------------------------- /config/manager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - manager.yaml 3 | apiVersion: kustomize.config.k8s.io/v1beta1 4 | kind: Kustomization 5 | images: 6 | - name: controller 7 | newName: kotalco/kotal 8 | newTag: v0.3.0 9 | -------------------------------------------------------------------------------- /controllers/near/copy_node_key.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | echo "Copying node key from secrets dir to data dir" 6 | 7 | mkdir -p ${KOTAL_DATA_PATH} 8 | cp ${KOTAL_SECRETS_PATH}/node_key.json ${KOTAL_DATA_PATH} 9 | -------------------------------------------------------------------------------- /controllers/chainlink/copy_api_credentials.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | mkdir -p ${KOTAL_DATA_PATH} 6 | echo $KOTAL_API_EMAIL > ${KOTAL_DATA_PATH}/.api 7 | cat ${KOTAL_SECRETS_PATH}/api-password >> ${KOTAL_DATA_PATH}/.api 8 | -------------------------------------------------------------------------------- /controllers/near/copy_validator_key.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | echo "Copying validator key from secrets dir to data dir" 6 | 7 | mkdir -p ${KOTAL_DATA_PATH} 8 | cp ${KOTAL_SECRETS_PATH}/validator_key.json ${KOTAL_DATA_PATH} 9 | -------------------------------------------------------------------------------- /controllers/polkadot/convert_node_private_key.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | mkdir -p $KOTAL_DATA_PATH 6 | 7 | # convert node private key to binary format 8 | xxd -r -p -c 32 $KOTAL_SECRETS_PATH/nodekey > $KOTAL_DATA_PATH/kotal_nodekey -------------------------------------------------------------------------------- /config/rbac/auth_proxy_client_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: metrics-reader 5 | rules: 6 | - nonResourceURLs: 7 | - "/metrics" 8 | verbs: 9 | - get 10 | -------------------------------------------------------------------------------- /controllers/ethereum2/nimbus_copy_validators.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | mkdir -p ${KOTAL_VALIDATORS_PATH} 6 | cp -RL ${KOTAL_SECRETS_PATH}/validator-keys ${KOTAL_VALIDATORS_PATH} 7 | cp -RL ${KOTAL_SECRETS_PATH}/validator-secrets ${KOTAL_VALIDATORS_PATH} -------------------------------------------------------------------------------- /controllers/shared/env.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | const ( 4 | EnvDataPath = "KOTAL_DATA_PATH" 5 | EnvConfigPath = "KOTAL_CONFIG_PATH" 6 | EnvSecretsPath = "KOTAL_SECRETS_PATH" 7 | EnvUseExistingCluster = "USE_EXISTING_CLUSTER" 8 | ) 9 | -------------------------------------------------------------------------------- /controllers/shared/errors.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import "k8s.io/apimachinery/pkg/api/errors" 4 | 5 | // IgnoreConflicts ignore conflict errors 6 | func IgnoreConflicts(err *error) { 7 | if errors.IsConflict(*err) { 8 | *err = nil 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /clients/interface.go: -------------------------------------------------------------------------------- 1 | package clients 2 | 3 | import corev1 "k8s.io/api/core/v1" 4 | 5 | // Interface is client interface 6 | type Interface interface { 7 | Args() []string 8 | Command() []string 9 | Env() []corev1.EnvVar 10 | HomeDir() string 11 | } 12 | -------------------------------------------------------------------------------- /config/webhook/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: webhook-service 5 | namespace: system 6 | spec: 7 | ports: 8 | - port: 443 9 | targetPort: 9443 10 | selector: 11 | control-plane: controller-manager 12 | -------------------------------------------------------------------------------- /apis/shared/suite_test.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestShared(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Shared Suite") 13 | } 14 | -------------------------------------------------------------------------------- /controllers/ipfs/init_ipfs_config.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | if [ -e $IPFS_PATH/config ] 6 | then 7 | echo "ipfs config has already been initialized" 8 | else 9 | echo "initializing ipfs config" 10 | ipfs init --empty-repo --profile $IPFS_INIT_PROFILES 11 | fi -------------------------------------------------------------------------------- /apis/ipfs/v1alpha1/suite_test.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestWebhooks(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Webhooks Suite") 13 | } 14 | -------------------------------------------------------------------------------- /apis/near/v1alpha1/suite_test.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestWebhooks(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Webhooks Suite") 13 | } 14 | -------------------------------------------------------------------------------- /clients/ipfs/suite_test.go: -------------------------------------------------------------------------------- 1 | package ipfs 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestIPFSClients(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "IPFS Clients Suite") 13 | } 14 | -------------------------------------------------------------------------------- /clients/near/client.go: -------------------------------------------------------------------------------- 1 | package near 2 | 3 | import ( 4 | nearv1alpha1 "github.com/kotalco/kotal/apis/near/v1alpha1" 5 | clients "github.com/kotalco/kotal/clients" 6 | ) 7 | 8 | func NewClient(node *nearv1alpha1.Node) clients.Interface { 9 | return &NearClient{node} 10 | } 11 | -------------------------------------------------------------------------------- /controllers/shared/host.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | // Host returns localhost if toggle is not enabled 4 | // otherwise returns the wildcard address 5 | func Host(toggle bool) string { 6 | if toggle { 7 | return "0.0.0.0" 8 | } else { 9 | return "127.0.0.1" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /apis/aptos/v1alpha1/suite_test.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestWebhooks(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Webhooks Suite") 13 | } 14 | -------------------------------------------------------------------------------- /apis/bitcoin/v1alpha1/suite_test.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestWebhooks(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Webhooks Suite") 13 | } 14 | -------------------------------------------------------------------------------- /apis/chainlink/v1alpha1/suite_test.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestWebhooks(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Webhooks Suite") 13 | } 14 | -------------------------------------------------------------------------------- /apis/ethereum/v1alpha1/suite_test.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestWebhooks(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Webhooks Suite") 13 | } 14 | -------------------------------------------------------------------------------- /apis/ethereum2/v1alpha1/suite_test.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestWebhooks(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Webhooks Suite") 13 | } 14 | -------------------------------------------------------------------------------- /apis/filecoin/v1alpha1/suite_test.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestWebhooks(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Webhooks Suite") 13 | } 14 | -------------------------------------------------------------------------------- /apis/polkadot/v1alpha1/suite_test.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestWebhooks(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Webhooks Suite") 13 | } 14 | -------------------------------------------------------------------------------- /apis/stacks/v1alpha1/suite_test.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestWebhooks(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Webhooks Suite") 13 | } 14 | -------------------------------------------------------------------------------- /clients/filecoin/types.go: -------------------------------------------------------------------------------- 1 | package filecoin 2 | 3 | const ( 4 | // EnvLotusPath is the environment variable used for lotus data directory 5 | EnvLotusPath = "LOTUS_PATH" 6 | // EnvLogLevel is the environment variable used for log verbosity level 7 | EnvLogLevel = "GOLOG_LOG_LEVEL" 8 | ) 9 | -------------------------------------------------------------------------------- /clients/near/suite_test.go: -------------------------------------------------------------------------------- 1 | package near 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestEthereum2Client(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "NEAR Core Client Suite") 13 | } 14 | -------------------------------------------------------------------------------- /clients/aptos/suite_test.go: -------------------------------------------------------------------------------- 1 | package aptos 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestAptosCoreClient(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Aptos Core Client Suite") 13 | } 14 | -------------------------------------------------------------------------------- /clients/graph/suite_test.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestGraphNodeClient(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Graph Node Client Suite") 13 | } 14 | -------------------------------------------------------------------------------- /clients/ethereum/suite_test.go: -------------------------------------------------------------------------------- 1 | package ethereum 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestEthereumClient(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Ethereum Clients Suite") 13 | } 14 | -------------------------------------------------------------------------------- /clients/filecoin/suite_test.go: -------------------------------------------------------------------------------- 1 | package filecoin 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestFilecoinClient(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Filecoin Clients Suite") 13 | } 14 | -------------------------------------------------------------------------------- /clients/polkadot/suite_test.go: -------------------------------------------------------------------------------- 1 | package polkadot 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestEthereum2Client(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Polkadot Client Suite") 13 | } 14 | -------------------------------------------------------------------------------- /clients/stacks/client.go: -------------------------------------------------------------------------------- 1 | package stacks 2 | 3 | import ( 4 | stacksv1alpha1 "github.com/kotalco/kotal/apis/stacks/v1alpha1" 5 | clients "github.com/kotalco/kotal/clients" 6 | ) 7 | 8 | func NewClient(node *stacksv1alpha1.Node) clients.Interface { 9 | return &StacksNodeClient{node} 10 | } 11 | -------------------------------------------------------------------------------- /clients/stacks/suite_test.go: -------------------------------------------------------------------------------- 1 | package stacks 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestStacksNodeClient(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Stacks Node Client Suite") 13 | } 14 | -------------------------------------------------------------------------------- /controllers/shared/host_test.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestHost(t *testing.T) { 8 | expected := "127.0.0.1" 9 | got := Host(false) 10 | 11 | if got != expected { 12 | t.Errorf("expected host to be %s but got %s", expected, got) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /clients/bitcoin/suite_test.go: -------------------------------------------------------------------------------- 1 | package bitcoin 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestBitcoinCoreClient(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Bitcoin Core Client Suite") 13 | } 14 | -------------------------------------------------------------------------------- /clients/chainlink/suite_test.go: -------------------------------------------------------------------------------- 1 | package chainlink 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestChainlinkClients(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Chainlink Client Suite") 13 | } 14 | -------------------------------------------------------------------------------- /clients/ethereum2/suite_test.go: -------------------------------------------------------------------------------- 1 | package ethereum2 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestEthereum2Client(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Ethereum 2.0 Clients Suite") 13 | } 14 | -------------------------------------------------------------------------------- /clients/filecoin/client.go: -------------------------------------------------------------------------------- 1 | package filecoin 2 | 3 | import ( 4 | filecoinv1alpha1 "github.com/kotalco/kotal/apis/filecoin/v1alpha1" 5 | clients "github.com/kotalco/kotal/clients" 6 | ) 7 | 8 | func NewClient(node *filecoinv1alpha1.Node) clients.Interface { 9 | return &LotusClient{node} 10 | } 11 | -------------------------------------------------------------------------------- /controllers/shared/webhook_test.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import "testing" 4 | 5 | func TestIsWebhookEnabled(t *testing.T) { 6 | expected := true 7 | got := IsWebhookEnabled() 8 | if got != expected { 9 | t.Errorf("Expected webhook enabled to be %t , got %t", expected, got) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /clients/polkadot/client.go: -------------------------------------------------------------------------------- 1 | package polkadot 2 | 3 | import ( 4 | polkadotv1alpha1 "github.com/kotalco/kotal/apis/polkadot/v1alpha1" 5 | clients "github.com/kotalco/kotal/clients" 6 | ) 7 | 8 | func NewClient(node *polkadotv1alpha1.Node) clients.Interface { 9 | return &PolkadotClient{node} 10 | } 11 | -------------------------------------------------------------------------------- /controllers/ethereum/geth_init_genesis.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | if [ ! -d $KOTAL_DATA_PATH/geth ] 6 | then 7 | echo "initializing geth genesis block" 8 | geth init --datadir $KOTAL_DATA_PATH $KOTAL_CONFIG_PATH/genesis.json 9 | else 10 | echo "genesis block has been initialized before!" 11 | fi -------------------------------------------------------------------------------- /config/samples/ipfs/ipfs_v1alpha1_swarm_peer.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: ipfs.kotal.io/v1alpha1 2 | kind: Peer 3 | metadata: 4 | name: peer-sample 5 | spec: 6 | swarmKeySecretName: my-swarm-key 7 | routing: dhtclient 8 | apiPort: 8999 9 | gatewayPort: 4444 10 | resources: 11 | cpu: "1" 12 | memory: "1Gi" 13 | -------------------------------------------------------------------------------- /controllers/ethereum2/lighthouse_import_keystore.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | lighthouse account validator import --datadir ${KOTAL_DATA_PATH} --network ${KOTAL_NETWORK} \ 6 | --keystore ${KOTAL_KEY_DIR}/keystore-${KOTAL_KEYSTORE_INDEX}.json \ 7 | --reuse-password \ 8 | --password-file ${KOTAL_KEY_DIR}/password.txt -------------------------------------------------------------------------------- /controllers/ipfs/init_ipfs_cluster_config.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | if [ -e $IPFS_CLUSTER_PATH/service.json ] 6 | then 7 | echo "ipfs cluster config has already been initialized" 8 | else 9 | echo "initializing ipfs cluster config" 10 | ipfs-cluster-service init --consensus $IPFS_CLUSTER_CONSENSUS 11 | fi -------------------------------------------------------------------------------- /clients/aptos/client.go: -------------------------------------------------------------------------------- 1 | package aptos 2 | 3 | import ( 4 | aptosv1alpha1 "github.com/kotalco/kotal/apis/aptos/v1alpha1" 5 | clients "github.com/kotalco/kotal/clients" 6 | ) 7 | 8 | // NewClient returns new Aptos client 9 | func NewClient(node *aptosv1alpha1.Node) clients.Interface { 10 | return &AptosCoreClient{node} 11 | } 12 | -------------------------------------------------------------------------------- /clients/graph/client.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | graphv1alpha1 "github.com/kotalco/kotal/apis/graph/v1alpha1" 5 | clients "github.com/kotalco/kotal/clients" 6 | ) 7 | 8 | // NewClient creates new graph node client 9 | func NewClient(node *graphv1alpha1.Node) clients.Interface { 10 | return &GraphNodeClient{node} 11 | } 12 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_aptos_nodes.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 7 | name: nodes.aptos.kotal.io 8 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_graph_nodes.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 7 | name: nodes.graph.kotal.io 8 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_bitcoin_nodes.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 7 | name: nodes.bitcoin.kotal.io 8 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_ethereum_nodes.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 7 | name: nodes.ethereum.kotal.io 8 | -------------------------------------------------------------------------------- /config/rbac/role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: manager-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: manager-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: controller-manager 12 | namespace: system 13 | -------------------------------------------------------------------------------- /controllers/aptos/download_genesis_block.sh: -------------------------------------------------------------------------------- 1 | if [ -z "$(ls -A $KOTAL_DATA_PATH/kotal_genesis.blob)" ] 2 | then 3 | echo "downloading genesis block" 4 | curl https://raw.githubusercontent.com/aptos-labs/aptos-networks/main/$KOTAL_NETWORK/genesis.blob -o $KOTAL_DATA_PATH/kotal_genesis.blob 5 | else 6 | echo "genesis block has been downloaded before" 7 | fi -------------------------------------------------------------------------------- /controllers/ethereum/geth_import_account.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | if [ -z "$(ls -A $KOTAL_DATA_PATH/keystore)" ] 6 | then 7 | echo "importing account" 8 | geth account import --datadir $KOTAL_DATA_PATH --password $KOTAL_SECRETS_PATH/account.password $KOTAL_SECRETS_PATH/account.key 9 | else 10 | echo "account has been imported before!" 11 | fi -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_chainlink_nodes.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 7 | name: nodes.chainlink.kotal.io 8 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: proxy-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: proxy-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: controller-manager 12 | namespace: system 13 | -------------------------------------------------------------------------------- /clients/chainlink/client.go: -------------------------------------------------------------------------------- 1 | package chainlink 2 | 3 | import ( 4 | chainlinkv1alpha1 "github.com/kotalco/kotal/apis/chainlink/v1alpha1" 5 | clients "github.com/kotalco/kotal/clients" 6 | ) 7 | 8 | // NewClient returns chainlink client for the given node 9 | func NewClient(node *chainlinkv1alpha1.Node) clients.Interface { 10 | return &ChainlinkClient{node} 11 | } 12 | -------------------------------------------------------------------------------- /config/rbac/leader_election_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | name: leader-election-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: Role 8 | name: leader-election-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: controller-manager 12 | namespace: system 13 | -------------------------------------------------------------------------------- /clients/bitcoin/client.go: -------------------------------------------------------------------------------- 1 | package bitcoin 2 | 3 | import ( 4 | bitcoinv1alpha1 "github.com/kotalco/kotal/apis/bitcoin/v1alpha1" 5 | clients "github.com/kotalco/kotal/clients" 6 | "sigs.k8s.io/controller-runtime/pkg/client" 7 | ) 8 | 9 | func NewClient(node *bitcoinv1alpha1.Node, client client.Client) clients.Interface { 10 | return &BitcoinCoreClient{node, client} 11 | } 12 | -------------------------------------------------------------------------------- /controllers/ipfs/config_ipfs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | ipfs config Addresses.API /ip4/$IPFS_API_HOST/tcp/$IPFS_API_PORT 6 | ipfs config Addresses.Gateway /ip4/$IPFS_GATEWAY_HOST/tcp/$IPFS_GATEWAY_PORT 7 | 8 | export IFS=";" 9 | for profile in $IPFS_PROFILES; do 10 | ipfs config profile apply $profile 11 | echo "$profile profile has been applied" 12 | done 13 | -------------------------------------------------------------------------------- /controllers/near/init_near_node.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | 6 | if [ -z "$(ls -A $KOTAL_DATA_PATH/genesis.json)" ] 7 | then 8 | echo "Initializing NEAR node" 9 | neard --home $KOTAL_DATA_PATH init --chain-id $KOTAL_NEAR_NETWORK --download-genesis --download-config --account-id validator 10 | else 11 | echo "NEAR node has already been initialized before!" 12 | fi -------------------------------------------------------------------------------- /clients/stacks/types.go: -------------------------------------------------------------------------------- 1 | package stacks 2 | 3 | const ( 4 | // StacksNodeCommand is stacks node exec command 5 | StacksNodeCommand = "stacks-node" 6 | // StacksStartCommand is command used to start stacks node 7 | StacksStartCommand = "start" 8 | ) 9 | 10 | const ( 11 | // StacksArgConfig is argument used to set configuration file 12 | StacksArgConfig = "--config" 13 | ) 14 | -------------------------------------------------------------------------------- /apis/polkadot/v1alpha1/node_webhook.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | ctrl "sigs.k8s.io/controller-runtime" 5 | logf "sigs.k8s.io/controller-runtime/pkg/log" 6 | ) 7 | 8 | var nodelog = logf.Log.WithName("node-resource") 9 | 10 | func (r *Node) SetupWebhookWithManager(mgr ctrl.Manager) error { 11 | return ctrl.NewWebhookManagedBy(mgr). 12 | For(r). 13 | Complete() 14 | } 15 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_ipfs_peers.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | annotations: 7 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 8 | name: peers.ipfs.kotal.io 9 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_near_nodes.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | annotations: 7 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 8 | name: nodes.near.kotal.io 9 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | control-plane: controller-manager 6 | name: controller-manager-metrics-service 7 | namespace: system 8 | spec: 9 | ports: 10 | - name: https 11 | port: 8443 12 | protocol: TCP 13 | targetPort: https 14 | selector: 15 | control-plane: controller-manager 16 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_stacks_nodes.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | annotations: 7 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 8 | name: nodes.stacks.kotal.io 9 | -------------------------------------------------------------------------------- /controllers/ethereum/nethermind_convert_enode_privatekey.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | mkdir -p $KOTAL_DATA_PATH 6 | 7 | # convert enode private key to binary format 8 | # nethermind doesn't accept text format 9 | # more info: https://discord.com/channels/629004402170134531/629004402170134537/862516237477347338 10 | xxd -r -p -c 32 $KOTAL_SECRETS_PATH/nodekey > $KOTAL_DATA_PATH/kotal_nodekey -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_filecoin_nodes.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | annotations: 7 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 8 | name: nodes.filecoin.kotal.io 9 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_polkadot_nodes.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | annotations: 7 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 8 | name: nodes.polkadot.kotal.io 9 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_ipfs_clusterpeers.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | annotations: 7 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 8 | name: clusterpeers.ipfs.kotal.io 9 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_ethereum2_validators.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | annotations: 7 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 8 | name: validators.ethereum2.kotal.io 9 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_ethereum2_beaconnodes.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | annotations: 7 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 8 | name: beaconnodes.ethereum2.kotal.io 9 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: proxy-role 5 | rules: 6 | - apiGroups: 7 | - authentication.k8s.io 8 | resources: 9 | - tokenreviews 10 | verbs: 11 | - create 12 | - apiGroups: 13 | - authorization.k8s.io 14 | resources: 15 | - subjectaccessreviews 16 | verbs: 17 | - create 18 | -------------------------------------------------------------------------------- /controllers/ethereum2/prysm_import_keystore.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | validator accounts import --accept-terms-of-use \ 6 | --${KOTAL_NETWORK} \ 7 | --wallet-dir=${KOTAL_DATA_PATH}/prysm-wallet \ 8 | --keys-dir=${KOTAL_KEY_DIR}/keystore-${KOTAL_KEYSTORE_INDEX}.json \ 9 | --account-password-file=${KOTAL_KEY_DIR}/password.txt \ 10 | --wallet-password-file=${KOTAL_SECRETS_PATH}/prysm-wallet/prysm-wallet-password.txt -------------------------------------------------------------------------------- /apis/aptos/v1alpha1/node_webhook.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | ctrl "sigs.k8s.io/controller-runtime" 5 | logf "sigs.k8s.io/controller-runtime/pkg/log" 6 | ) 7 | 8 | // log is for logging in this package. 9 | var nodelog = logf.Log.WithName("node-resource") 10 | 11 | func (r *Node) SetupWebhookWithManager(mgr ctrl.Manager) error { 12 | return ctrl.NewWebhookManagedBy(mgr). 13 | For(r). 14 | Complete() 15 | } 16 | -------------------------------------------------------------------------------- /apis/bitcoin/v1alpha1/node_webhook.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | ctrl "sigs.k8s.io/controller-runtime" 5 | logf "sigs.k8s.io/controller-runtime/pkg/log" 6 | ) 7 | 8 | // log is for logging in this package. 9 | var nodelog = logf.Log.WithName("node-resource") 10 | 11 | func (r *Node) SetupWebhookWithManager(mgr ctrl.Manager) error { 12 | return ctrl.NewWebhookManagedBy(mgr). 13 | For(r). 14 | Complete() 15 | } 16 | -------------------------------------------------------------------------------- /apis/ipfs/v1alpha1/peer_webhook.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | ctrl "sigs.k8s.io/controller-runtime" 5 | logf "sigs.k8s.io/controller-runtime/pkg/log" 6 | ) 7 | 8 | // log is for logging in this package. 9 | var peerlog = logf.Log.WithName("peer-resource") 10 | 11 | func (r *Peer) SetupWebhookWithManager(mgr ctrl.Manager) error { 12 | return ctrl.NewWebhookManagedBy(mgr). 13 | For(r). 14 | Complete() 15 | } 16 | -------------------------------------------------------------------------------- /apis/near/v1alpha1/node_webhook.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | ctrl "sigs.k8s.io/controller-runtime" 5 | logf "sigs.k8s.io/controller-runtime/pkg/log" 6 | ) 7 | 8 | // log is for logging in this package. 9 | var nodelog = logf.Log.WithName("node-resource") 10 | 11 | func (n *Node) SetupWebhookWithManager(mgr ctrl.Manager) error { 12 | return ctrl.NewWebhookManagedBy(mgr). 13 | For(n). 14 | Complete() 15 | } 16 | -------------------------------------------------------------------------------- /apis/stacks/v1alpha1/node_webhook.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | ctrl "sigs.k8s.io/controller-runtime" 5 | logf "sigs.k8s.io/controller-runtime/pkg/log" 6 | ) 7 | 8 | // log is for logging in this package. 9 | var nodelog = logf.Log.WithName("node-resource") 10 | 11 | func (r *Node) SetupWebhookWithManager(mgr ctrl.Manager) error { 12 | return ctrl.NewWebhookManagedBy(mgr). 13 | For(r). 14 | Complete() 15 | } 16 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_near_nodes.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: nodes.near.kotal.io 5 | spec: 6 | conversion: 7 | strategy: Webhook 8 | webhook: 9 | clientConfig: 10 | service: 11 | namespace: system 12 | name: webhook-service 13 | path: /convert 14 | conversionReviewVersions: 15 | - v1 16 | -------------------------------------------------------------------------------- /apis/chainlink/v1alpha1/node_webhook.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | ctrl "sigs.k8s.io/controller-runtime" 5 | logf "sigs.k8s.io/controller-runtime/pkg/log" 6 | ) 7 | 8 | // log is for logging in this package. 9 | var nodelog = logf.Log.WithName("node-resource") 10 | 11 | func (r *Node) SetupWebhookWithManager(mgr ctrl.Manager) error { 12 | return ctrl.NewWebhookManagedBy(mgr). 13 | For(r). 14 | Complete() 15 | } 16 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_aptos_nodes.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: nodes.aptos.kotal.io 5 | spec: 6 | conversion: 7 | strategy: Webhook 8 | webhook: 9 | clientConfig: 10 | service: 11 | namespace: system 12 | name: webhook-service 13 | path: /convert 14 | conversionReviewVersions: 15 | - v1 16 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_stacks_nodes.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: nodes.stacks.kotal.io 5 | spec: 6 | conversion: 7 | strategy: Webhook 8 | webhook: 9 | clientConfig: 10 | service: 11 | namespace: system 12 | name: webhook-service 13 | path: /convert 14 | conversionReviewVersions: 15 | - v1 16 | -------------------------------------------------------------------------------- /config/rbac/ipfs_peer_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view peers. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: peer-viewer-role 6 | rules: 7 | - apiGroups: 8 | - ipfs.kotal.io 9 | resources: 10 | - peers 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - ipfs.kotal.io 17 | resources: 18 | - peers/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_bitcoin_nodes.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: nodes.bitcoin.kotal.io 5 | spec: 6 | conversion: 7 | strategy: Webhook 8 | webhook: 9 | clientConfig: 10 | service: 11 | namespace: system 12 | name: webhook-service 13 | path: /convert 14 | conversionReviewVersions: 15 | - v1 16 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_chainlink_nodes.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: nodes.chainlink.kotal.io 5 | spec: 6 | conversion: 7 | strategy: Webhook 8 | webhook: 9 | clientConfig: 10 | service: 11 | namespace: system 12 | name: webhook-service 13 | path: /convert 14 | conversionReviewVersions: 15 | - v1 16 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_polkadot_nodes.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: nodes.polkadot.kotal.io 5 | spec: 6 | conversion: 7 | strategy: Webhook 8 | webhook: 9 | clientConfig: 10 | service: 11 | namespace: system 12 | name: webhook-service 13 | path: /convert 14 | conversionReviewVersions: 15 | - v1 16 | -------------------------------------------------------------------------------- /config/prometheus/monitor.yaml: -------------------------------------------------------------------------------- 1 | 2 | # Prometheus Monitor Service (Metrics) 3 | apiVersion: monitoring.coreos.com/v1 4 | kind: ServiceMonitor 5 | metadata: 6 | labels: 7 | control-plane: controller-manager 8 | name: controller-manager-metrics-monitor 9 | namespace: system 10 | spec: 11 | endpoints: 12 | - path: /metrics 13 | port: https 14 | selector: 15 | matchLabels: 16 | control-plane: controller-manager 17 | -------------------------------------------------------------------------------- /config/samples/filecoin/v1alpha1_mainnet_node.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: filecoin.kotal.io/v1alpha1 2 | kind: Node 3 | metadata: 4 | name: my-node 5 | spec: 6 | network: mainnet 7 | api: true 8 | apiPort: 1234 9 | apiRequestTimeout: 30 10 | disableMetadataLog: false 11 | logging: debug 12 | p2pPort: 4444 13 | ipfsPeerEndpoint: /dns4/peer-sample/tcp/5001 14 | ipfsOnlineMode: false 15 | resources: 16 | cpu: "1" 17 | memory: "1Gi" 18 | -------------------------------------------------------------------------------- /apis/ipfs/v1alpha1/cluster_peer_webhook.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | ctrl "sigs.k8s.io/controller-runtime" 5 | logf "sigs.k8s.io/controller-runtime/pkg/log" 6 | ) 7 | 8 | // log is for logging in this package. 9 | var clusterpeerlog = logf.Log.WithName("clusterpeer-resource") 10 | 11 | func (r *ClusterPeer) SetupWebhookWithManager(mgr ctrl.Manager) error { 12 | return ctrl.NewWebhookManagedBy(mgr). 13 | For(r). 14 | Complete() 15 | } 16 | -------------------------------------------------------------------------------- /config/samples/filecoin/v1alpha1_calibration_node.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: filecoin.kotal.io/v1alpha1 2 | kind: Node 3 | metadata: 4 | name: calibration-node 5 | spec: 6 | network: calibration 7 | api: true 8 | apiPort: 1234 9 | logging: info 10 | apiRequestTimeout: 30 11 | disableMetadataLog: false 12 | p2pPort: 4444 13 | ipfsPeerEndpoint: /dns4/peer-sample/tcp/5001 14 | ipfsOnlineMode: false 15 | resources: 16 | cpu: "1" 17 | memory: "1Gi" 18 | -------------------------------------------------------------------------------- /config/rbac/graph_node_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view nodes. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: node-viewer-role 6 | rules: 7 | - apiGroups: 8 | - graph.kotal.io 9 | resources: 10 | - nodes 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - graph.kotal.io 17 | resources: 18 | - nodes/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/rbac/near_node_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view nodes. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: node-viewer-role 6 | rules: 7 | - apiGroups: 8 | - near.kotal.io 9 | resources: 10 | - nodes 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - near.kotal.io 17 | resources: 18 | - nodes/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/rbac/stacks_node_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view nodes. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: node-viewer-role 6 | rules: 7 | - apiGroups: 8 | - stacks.kotal.io 9 | resources: 10 | - nodes 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - stacks.kotal.io 17 | resources: 18 | - nodes/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/rbac/bitcoin_node_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view nodes. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: node-viewer-role 6 | rules: 7 | - apiGroups: 8 | - bitcoin.kotal.io 9 | resources: 10 | - nodes 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - bitcoin.kotal.io 17 | resources: 18 | - nodes/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/rbac/ethereum_node_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view nodes. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: node-viewer-role 6 | rules: 7 | - apiGroups: 8 | - ethereum.kotal.io 9 | resources: 10 | - nodes 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - ethereum.kotal.io 17 | resources: 18 | - nodes/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/rbac/filecoin_node_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view nodes. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: node-viewer-role 6 | rules: 7 | - apiGroups: 8 | - filecoin.kotal.io 9 | resources: 10 | - nodes 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - filecoin.kotal.io 17 | resources: 18 | - nodes/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/rbac/polkadot_node_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view nodes. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: node-viewer-role 6 | rules: 7 | - apiGroups: 8 | - polkadot.kotal.io 9 | resources: 10 | - nodes 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - polkadot.kotal.io 17 | resources: 18 | - nodes/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/certmanager/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | # This configuration is for teaching kustomize how to update name ref and var substitution 2 | nameReference: 3 | - kind: Issuer 4 | group: cert-manager.io 5 | fieldSpecs: 6 | - kind: Certificate 7 | group: cert-manager.io 8 | path: spec/issuerRef/name 9 | 10 | varReference: 11 | - kind: Certificate 12 | group: cert-manager.io 13 | path: spec/commonName 14 | - kind: Certificate 15 | group: cert-manager.io 16 | path: spec/dnsNames 17 | -------------------------------------------------------------------------------- /config/rbac/chainlink_node_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view nodes. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: node-viewer-role 6 | rules: 7 | - apiGroups: 8 | - chainlink.kotal.io 9 | resources: 10 | - nodes 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - chainlink.kotal.io 17 | resources: 18 | - nodes/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_graph_nodes.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables a conversion webhook for the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: nodes.graph.kotal.io 6 | spec: 7 | conversion: 8 | strategy: Webhook 9 | webhook: 10 | clientConfig: 11 | service: 12 | namespace: system 13 | name: webhook-service 14 | path: /convert 15 | conversionReviewVersions: 16 | - v1 17 | -------------------------------------------------------------------------------- /config/rbac/ipfs_peer_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit peers. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: peer-editor-role 6 | rules: 7 | - apiGroups: 8 | - ipfs.kotal.io 9 | resources: 10 | - peers 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - ipfs.kotal.io 21 | resources: 22 | - peers/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/rbac/clusterpeer_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view clusterpeers. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: clusterpeer-viewer-role 6 | rules: 7 | - apiGroups: 8 | - ipfs.kotal.io 9 | resources: 10 | - clusterpeers 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - ipfs.kotal.io 17 | resources: 18 | - clusterpeers/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/rbac/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - service_account.yaml 3 | - role.yaml 4 | - role_binding.yaml 5 | - leader_election_role.yaml 6 | - leader_election_role_binding.yaml 7 | # Comment the following 4 lines if you want to disable 8 | # the auth proxy (https://github.com/brancz/kube-rbac-proxy) 9 | # which protects your /metrics endpoint. 10 | - auth_proxy_service.yaml 11 | - auth_proxy_role.yaml 12 | - auth_proxy_role_binding.yaml 13 | - auth_proxy_client_clusterrole.yaml 14 | -------------------------------------------------------------------------------- /apis/ethereum/v1alpha1/node_webhook.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | ctrl "sigs.k8s.io/controller-runtime" 5 | logf "sigs.k8s.io/controller-runtime/pkg/log" 6 | ) 7 | 8 | // log is for logging in this package. 9 | var nodelog = logf.Log.WithName("node-resource") 10 | 11 | // SetupWebhookWithManager sets up the webook with a given controller manager 12 | func (r *Node) SetupWebhookWithManager(mgr ctrl.Manager) error { 13 | return ctrl.NewWebhookManagedBy(mgr). 14 | For(r). 15 | Complete() 16 | } 17 | -------------------------------------------------------------------------------- /apis/filecoin/v1alpha1/node_webhook.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | ctrl "sigs.k8s.io/controller-runtime" 5 | logf "sigs.k8s.io/controller-runtime/pkg/log" 6 | ) 7 | 8 | // log is for logging in this package. 9 | var nodelog = logf.Log.WithName("node-resource") 10 | 11 | // SetupWebhookWithManager sets up the webook with a given controller manager 12 | func (n *Node) SetupWebhookWithManager(mgr ctrl.Manager) error { 13 | return ctrl.NewWebhookManagedBy(mgr). 14 | For(n). 15 | Complete() 16 | } 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.tmp 2 | .vscode 3 | # Binaries for programs and plugins 4 | *.exe 5 | *.exe~ 6 | *.dll 7 | *.so 8 | *.dylib 9 | bin 10 | testbin/* 11 | 12 | # Test binary, build with `go test -c` 13 | *.test 14 | 15 | # Output of the go coverage tool, specifically when used with LiteIDE 16 | *.out 17 | 18 | # Kubernetes Generated files - skip generated files, except for vendored files 19 | 20 | !vendor/**/zz_generated.* 21 | 22 | # editor and IDE paraphernalia 23 | .vscode 24 | .idea 25 | *.swp 26 | *.swo 27 | *~ 28 | -------------------------------------------------------------------------------- /config/rbac/ethereum2_validator_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view validators. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: validator-viewer-role 6 | rules: 7 | - apiGroups: 8 | - ethereum2.kotal.io 9 | resources: 10 | - validators 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - ethereum2.kotal.io 17 | resources: 18 | - validators/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/rbac/ethereum2_beaconnode_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view beaconnodes. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: beaconnode-viewer-role 6 | rules: 7 | - apiGroups: 8 | - ethereum2.kotal.io 9 | resources: 10 | - beaconnodes 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - ethereum2.kotal.io 17 | resources: 18 | - beaconnodes/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /apis/ethereum2/v1alpha1/beacon_node_webhook.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | ctrl "sigs.k8s.io/controller-runtime" 5 | logf "sigs.k8s.io/controller-runtime/pkg/log" 6 | ) 7 | 8 | // log is for logging in this package. 9 | var nodelog = logf.Log.WithName("node-resource") 10 | 11 | // SetupWebhookWithManager sets up the webook with a given controller manager 12 | func (r *BeaconNode) SetupWebhookWithManager(mgr ctrl.Manager) error { 13 | return ctrl.NewWebhookManagedBy(mgr). 14 | For(r). 15 | Complete() 16 | } 17 | -------------------------------------------------------------------------------- /apis/ethereum2/v1alpha1/validator_webhook.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | ctrl "sigs.k8s.io/controller-runtime" 5 | logf "sigs.k8s.io/controller-runtime/pkg/log" 6 | ) 7 | 8 | // log is for logging in this package. 9 | var validatorlog = logf.Log.WithName("validator-resource") 10 | 11 | // SetupWebhookWithManager sets up the webook with a given controller manager 12 | func (r *Validator) SetupWebhookWithManager(mgr ctrl.Manager) error { 13 | return ctrl.NewWebhookManagedBy(mgr). 14 | For(r). 15 | Complete() 16 | } 17 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_ipfs_peers.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables conversion webhook for CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | name: peers.ipfs.kotal.io 7 | spec: 8 | conversion: 9 | strategy: Webhook 10 | webhook: 11 | clientConfig: 12 | service: 13 | namespace: system 14 | name: webhook-service 15 | path: /convert 16 | conversionReviewVersions: 17 | - v1 18 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_ethereum_nodes.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables conversion webhook for CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | name: nodes.ethereum.kotal.io 7 | spec: 8 | conversion: 9 | strategy: Webhook 10 | webhook: 11 | clientConfig: 12 | service: 13 | namespace: system 14 | name: webhook-service 15 | path: /convert 16 | conversionReviewVersions: 17 | - v1 18 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_filecoin_nodes.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables conversion webhook for CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | name: nodes.filecoin.kotal.io 7 | spec: 8 | conversion: 9 | strategy: Webhook 10 | webhook: 11 | clientConfig: 12 | service: 13 | namespace: system 14 | name: webhook-service 15 | path: /convert 16 | conversionReviewVersions: 17 | - v1 18 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_ipfs_clusterpeers.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables conversion webhook for CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | name: clusterpeers.ipfs.kotal.io 7 | spec: 8 | conversion: 9 | strategy: Webhook 10 | webhook: 11 | clientConfig: 12 | service: 13 | namespace: system 14 | name: webhook-service 15 | path: /convert 16 | conversionReviewVersions: 17 | - v1 18 | -------------------------------------------------------------------------------- /config/rbac/graph_node_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit nodes. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: node-editor-role 6 | rules: 7 | - apiGroups: 8 | - graph.kotal.io 9 | resources: 10 | - nodes 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - graph.kotal.io 21 | resources: 22 | - nodes/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/rbac/near_node_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit nodes. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: node-editor-role 6 | rules: 7 | - apiGroups: 8 | - near.kotal.io 9 | resources: 10 | - nodes 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - near.kotal.io 21 | resources: 22 | - nodes/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_ethereum2_beaconnodes.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables conversion webhook for CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | name: beaconnodes.ethereum2.kotal.io 7 | spec: 8 | conversion: 9 | strategy: Webhook 10 | webhook: 11 | clientConfig: 12 | service: 13 | namespace: system 14 | name: webhook-service 15 | path: /convert 16 | conversionReviewVersions: 17 | - v1 18 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_ethereum2_validators.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables conversion webhook for CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | name: validators.ethereum2.kotal.io 7 | spec: 8 | conversion: 9 | strategy: Webhook 10 | webhook: 11 | clientConfig: 12 | service: 13 | namespace: system 14 | name: webhook-service 15 | path: /convert 16 | conversionReviewVersions: 17 | - v1 18 | -------------------------------------------------------------------------------- /config/rbac/bitcoin_node_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit nodes. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: node-editor-role 6 | rules: 7 | - apiGroups: 8 | - bitcoin.kotal.io 9 | resources: 10 | - nodes 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - bitcoin.kotal.io 21 | resources: 22 | - nodes/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/rbac/stacks_node_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit nodes. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: node-editor-role 6 | rules: 7 | - apiGroups: 8 | - stacks.kotal.io 9 | resources: 10 | - nodes 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - stacks.kotal.io 21 | resources: 22 | - nodes/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/rbac/ethereum_node_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit nodes. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: node-editor-role 6 | rules: 7 | - apiGroups: 8 | - ethereum.kotal.io 9 | resources: 10 | - nodes 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - ethereum.kotal.io 21 | resources: 22 | - nodes/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/rbac/filecoin_node_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit nodes. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: node-editor-role 6 | rules: 7 | - apiGroups: 8 | - filecoin.kotal.io 9 | resources: 10 | - nodes 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - filecoin.kotal.io 21 | resources: 22 | - nodes/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/rbac/polkadot_node_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit nodes. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: node-editor-role 6 | rules: 7 | - apiGroups: 8 | - polkadot.kotal.io 9 | resources: 10 | - nodes 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - polkadot.kotal.io 21 | resources: 22 | - nodes/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/rbac/chainlink_node_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit nodes. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: node-editor-role 6 | rules: 7 | - apiGroups: 8 | - chainlink.kotal.io 9 | resources: 10 | - nodes 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - chainlink.kotal.io 21 | resources: 22 | - nodes/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/rbac/clusterpeer_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit clusterpeers. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: clusterpeer-editor-role 6 | rules: 7 | - apiGroups: 8 | - ipfs.kotal.io 9 | resources: 10 | - clusterpeers 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - ipfs.kotal.io 21 | resources: 22 | - clusterpeers/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /controllers/shared/secret.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import ( 4 | "context" 5 | 6 | "sigs.k8s.io/controller-runtime/pkg/client" 7 | 8 | corev1 "k8s.io/api/core/v1" 9 | "k8s.io/apimachinery/pkg/types" 10 | ) 11 | 12 | // GetSecret returns k8s secret stored at key 13 | func GetSecret(ctx context.Context, client client.Client, name types.NamespacedName, key string) (value string, err error) { 14 | secret := &corev1.Secret{} 15 | 16 | if err = client.Get(ctx, name, secret); err != nil { 17 | return 18 | } 19 | 20 | value = string(secret.Data[key]) 21 | 22 | return 23 | } 24 | -------------------------------------------------------------------------------- /apis/shared/extraargs.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import "fmt" 4 | 5 | // ExtraArgs is extra arguments to add to the cli 6 | // if kv is true, arguments will bey key=val format 7 | type ExtraArgs map[string]string 8 | 9 | func (extra ExtraArgs) Encode(kv bool) (args []string) { 10 | 11 | for key, val := range extra { 12 | // for toggles 13 | if val == "" { 14 | args = append(args, key) 15 | continue 16 | } 17 | 18 | if kv { 19 | args = append(args, fmt.Sprintf("%s=%s", key, val)) 20 | } else { 21 | args = append(args, key, val) 22 | } 23 | } 24 | 25 | return 26 | } 27 | -------------------------------------------------------------------------------- /config/rbac/ethereum2_validator_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit validators. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: validator-editor-role 6 | rules: 7 | - apiGroups: 8 | - ethereum2.kotal.io 9 | resources: 10 | - validators 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - ethereum2.kotal.io 21 | resources: 22 | - validators/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/rbac/ethereum2_beaconnode_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit beaconnodes. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: beaconnode-editor-role 6 | rules: 7 | - apiGroups: 8 | - ethereum2.kotal.io 9 | resources: 10 | - beaconnodes 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - ethereum2.kotal.io 21 | resources: 22 | - beaconnodes/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /apis/shared/utils.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import ( 4 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 5 | "k8s.io/apimachinery/pkg/util/validation/field" 6 | ) 7 | 8 | // ErrorsToCauses converts error list into array of status cause 9 | func ErrorsToCauses(errs field.ErrorList) []metav1.StatusCause { 10 | causes := make([]metav1.StatusCause, 0, len(errs)) 11 | 12 | for i := range errs { 13 | err := errs[i] 14 | causes = append(causes, metav1.StatusCause{ 15 | Type: metav1.CauseType(err.Type), 16 | Message: err.ErrorBody(), 17 | Field: err.Field, 18 | }) 19 | } 20 | 21 | return causes 22 | } 23 | -------------------------------------------------------------------------------- /config/samples/ethereum2/ethereum2_v1alpha1_nimbus_validator.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: ethereum2.kotal.io/v1alpha1 2 | kind: Validator 3 | metadata: 4 | name: nimbus-validator 5 | spec: 6 | client: nimbus 7 | network: mainnet 8 | logging: info 9 | beaconEndpoints: 10 | - http://nimbus-beacon-node:8888 11 | graffiti: Validated by Kotal 12 | keystores: 13 | - secretName: my-validator 14 | feeRecipient: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045" 15 | resources: 16 | # these resources are only for testing 17 | # change resources depending on your use case 18 | cpu: "1" 19 | memory: "1Gi" 20 | -------------------------------------------------------------------------------- /config/crd/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | # This file is for teaching kustomize how to substitute name and namespace reference in CRD 2 | nameReference: 3 | - kind: Service 4 | version: v1 5 | fieldSpecs: 6 | - kind: CustomResourceDefinition 7 | version: v1 8 | group: apiextensions.k8s.io 9 | path: spec/conversion/webhook/clientConfig/service/name 10 | 11 | namespace: 12 | - kind: CustomResourceDefinition 13 | version: v1 14 | group: apiextensions.k8s.io 15 | path: spec/conversion/webhook/clientConfig/service/namespace 16 | create: false 17 | 18 | varReference: 19 | - path: metadata/annotations 20 | -------------------------------------------------------------------------------- /config/default/manager_webhook_patch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: controller-manager 5 | namespace: system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: manager 11 | ports: 12 | - containerPort: 9443 13 | name: webhook-server 14 | protocol: TCP 15 | volumeMounts: 16 | - mountPath: /tmp/k8s-webhook-server/serving-certs 17 | name: cert 18 | readOnly: true 19 | volumes: 20 | - name: cert 21 | secret: 22 | defaultMode: 420 23 | secretName: webhook-server-cert 24 | -------------------------------------------------------------------------------- /controllers/shared/security_context.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import corev1 "k8s.io/api/core/v1" 4 | 5 | // SecurityContext is the pod security policy used by all containers 6 | func SecurityContext() *corev1.PodSecurityContext { 7 | var userId int64 = 1000 8 | var groupId int64 = 3000 9 | var fsGroupId int64 = 2000 10 | var nonRoot = true 11 | policy := corev1.FSGroupChangeOnRootMismatch 12 | 13 | return &corev1.PodSecurityContext{ 14 | RunAsUser: &userId, 15 | RunAsGroup: &groupId, 16 | RunAsNonRoot: &nonRoot, 17 | FSGroup: &fsGroupId, 18 | FSGroupChangePolicy: &policy, 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /config/samples/near/near_v1alpha1_validator_node.yaml: -------------------------------------------------------------------------------- 1 | # WARNING: DON'T use the following validator key in production 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: validator-key 6 | stringData: 7 | key: |- 8 | { 9 | "account_id": "validator", 10 | "public_key": "ed25519:CdTBLXMmu9gzoCbNKdUnej1tJNMz5tzRjJF2a9DwAgUr", 11 | "secret_key": "ed25519:4k83DwbSpD3zzai4ZPdeRJcfXttU3Uq68mWWhni6ra2RKnG3jyVKEZyP14gDJZ9W1oqFujpAkidoNrYY4TLqijsG" 12 | } 13 | --- 14 | apiVersion: near.kotal.io/v1alpha1 15 | kind: Node 16 | metadata: 17 | name: near-validator-node 18 | spec: 19 | network: mainnet 20 | validatorSecretName: validator-key 21 | -------------------------------------------------------------------------------- /config/default/webhookcainjection_patch.yaml: -------------------------------------------------------------------------------- 1 | # This patch add annotation to admission webhook config and 2 | # the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize. 3 | apiVersion: admissionregistration.k8s.io/v1 4 | kind: MutatingWebhookConfiguration 5 | metadata: 6 | name: mutating-webhook-configuration 7 | annotations: 8 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 9 | --- 10 | apiVersion: admissionregistration.k8s.io/v1 11 | kind: ValidatingWebhookConfiguration 12 | metadata: 13 | name: validating-webhook-configuration 14 | annotations: 15 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 16 | -------------------------------------------------------------------------------- /clients/ipfs/client.go: -------------------------------------------------------------------------------- 1 | package ipfs 2 | 3 | import ( 4 | "fmt" 5 | 6 | ipfsv1alpha1 "github.com/kotalco/kotal/apis/ipfs/v1alpha1" 7 | "github.com/kotalco/kotal/clients" 8 | "k8s.io/apimachinery/pkg/runtime" 9 | ) 10 | 11 | // IPFSClient is IPFS peer client 12 | type IPFSClient interface { 13 | clients.Interface 14 | } 15 | 16 | // NewClient creates a new client for ipfs peer or cluster peer 17 | func NewClient(obj runtime.Object) (IPFSClient, error) { 18 | switch peer := obj.(type) { 19 | case *ipfsv1alpha1.Peer: 20 | return &KuboClient{peer}, nil 21 | case *ipfsv1alpha1.ClusterPeer: 22 | return &GoIPFSClusterClient{peer}, nil 23 | } 24 | return nil, fmt.Errorf("no client support for %s", obj) 25 | } 26 | -------------------------------------------------------------------------------- /config/samples/ethereum2/ethereum2_v1alpha1_lighthouse_validator.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: ethereum2.kotal.io/v1alpha1 2 | kind: Validator 3 | metadata: 4 | name: lighthouse-validator 5 | spec: 6 | client: lighthouse 7 | network: goerli 8 | logging: info 9 | beaconEndpoints: 10 | - http://lighthouse-beacon-node:8888 11 | graffiti: Validated by Kotal 12 | keystores: 13 | - secretName: my-validator 14 | publicKey: "0x83dbb18e088cb16a07fca598db2ac24da3e8549601eedd75eb28d8a9d4be405f49f7dbdcad5c9d7df54a8a40a143e852" 15 | feeRecipient: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045" 16 | resources: 17 | # these resources are only for testing 18 | # change resources depending on your use case 19 | cpu: "1" 20 | memory: "1Gi" 21 | -------------------------------------------------------------------------------- /config/samples/polkadot/polkadot_v1alpha1_kusama_validator_node.yaml: -------------------------------------------------------------------------------- 1 | # WARNING: DON'T use the following secret in production 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: polkadot-node-key 6 | stringData: 7 | # Identity 12D3KooWFEHU9FXqGCL6ify4rqcon31WYViCnUfA6aVYTjRXqJ5F 8 | key: 2938f18547765c18d44c8b8ebf649e97f9a6f0c5afcb241ae93a9ff252ea4a2c 9 | --- 10 | apiVersion: polkadot.kotal.io/v1alpha1 11 | kind: Node 12 | metadata: 13 | name: validator-node-sample 14 | spec: 15 | network: kusama 16 | nodePrivateKeySecretName: "polkadot-node-key" 17 | logging: info 18 | syncMode: full 19 | pruning: false 20 | validator: true 21 | telemetry: true 22 | prometheus: true 23 | resources: 24 | cpu: "1" 25 | memory: "1Gi" 26 | -------------------------------------------------------------------------------- /config/samples/ethereum2/ethereum2_v1alpha1_prysm_validator.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: ethereum2.kotal.io/v1alpha1 2 | kind: Validator 3 | metadata: 4 | name: prysm-validator 5 | spec: 6 | client: prysm 7 | network: mainnet 8 | logging: info 9 | beaconEndpoints: 10 | # it gives too many colons error when protocol (https://) is added 11 | - prysm-beacon-node:8888 12 | graffiti: Validated by Kotal 13 | certSecretName: "beaconnode-cert" 14 | walletPasswordSecret: wallet-password 15 | keystores: 16 | - secretName: my-validator 17 | feeRecipient: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045" 18 | resources: 19 | # these resources are only for testing 20 | # change resources depending on your use case 21 | cpu: "1" 22 | memory: "1Gi" 23 | -------------------------------------------------------------------------------- /config/rbac/leader_election_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions to do leader election. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | name: leader-election-role 6 | rules: 7 | - apiGroups: 8 | - "" 9 | resources: 10 | - configmaps 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - create 16 | - update 17 | - patch 18 | - delete 19 | - apiGroups: 20 | - coordination.k8s.io 21 | resources: 22 | - leases 23 | verbs: 24 | - get 25 | - list 26 | - watch 27 | - create 28 | - update 29 | - patch 30 | - delete 31 | - apiGroups: 32 | - "" 33 | resources: 34 | - events 35 | verbs: 36 | - create 37 | - patch 38 | -------------------------------------------------------------------------------- /apis/ipfs/v1alpha1/groupversion_info.go: -------------------------------------------------------------------------------- 1 | // Package v1alpha1 contains API Schema definitions for the ipfs v1alpha1 API group 2 | // +kubebuilder:object:generate=true 3 | // +groupName=ipfs.kotal.io 4 | package v1alpha1 5 | 6 | import ( 7 | "k8s.io/apimachinery/pkg/runtime/schema" 8 | "sigs.k8s.io/controller-runtime/pkg/scheme" 9 | ) 10 | 11 | var ( 12 | // GroupVersion is group version used to register these objects 13 | GroupVersion = schema.GroupVersion{Group: "ipfs.kotal.io", Version: "v1alpha1"} 14 | 15 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme 16 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} 17 | 18 | // AddToScheme adds the types in this group-version to the given scheme. 19 | AddToScheme = SchemeBuilder.AddToScheme 20 | ) 21 | -------------------------------------------------------------------------------- /apis/near/v1alpha1/groupversion_info.go: -------------------------------------------------------------------------------- 1 | // Package v1alpha1 contains API Schema definitions for the near v1alpha1 API group 2 | // +kubebuilder:object:generate=true 3 | // +groupName=near.kotal.io 4 | package v1alpha1 5 | 6 | import ( 7 | "k8s.io/apimachinery/pkg/runtime/schema" 8 | "sigs.k8s.io/controller-runtime/pkg/scheme" 9 | ) 10 | 11 | var ( 12 | // GroupVersion is group version used to register these objects 13 | GroupVersion = schema.GroupVersion{Group: "near.kotal.io", Version: "v1alpha1"} 14 | 15 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme 16 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} 17 | 18 | // AddToScheme adds the types in this group-version to the given scheme. 19 | AddToScheme = SchemeBuilder.AddToScheme 20 | ) 21 | -------------------------------------------------------------------------------- /apis/aptos/v1alpha1/groupversion_info.go: -------------------------------------------------------------------------------- 1 | // Package v1alpha1 contains API Schema definitions for the aptos v1alpha1 API group 2 | // +kubebuilder:object:generate=true 3 | // +groupName=aptos.kotal.io 4 | package v1alpha1 5 | 6 | import ( 7 | "k8s.io/apimachinery/pkg/runtime/schema" 8 | "sigs.k8s.io/controller-runtime/pkg/scheme" 9 | ) 10 | 11 | var ( 12 | // GroupVersion is group version used to register these objects 13 | GroupVersion = schema.GroupVersion{Group: "aptos.kotal.io", Version: "v1alpha1"} 14 | 15 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme 16 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} 17 | 18 | // AddToScheme adds the types in this group-version to the given scheme. 19 | AddToScheme = SchemeBuilder.AddToScheme 20 | ) 21 | -------------------------------------------------------------------------------- /apis/graph/v1alpha1/groupversion_info.go: -------------------------------------------------------------------------------- 1 | // Package v1alpha1 contains API Schema definitions for the graph v1alpha1 API group 2 | // +kubebuilder:object:generate=true 3 | // +groupName=graph.kotal.io 4 | package v1alpha1 5 | 6 | import ( 7 | "k8s.io/apimachinery/pkg/runtime/schema" 8 | "sigs.k8s.io/controller-runtime/pkg/scheme" 9 | ) 10 | 11 | var ( 12 | // GroupVersion is group version used to register these objects 13 | GroupVersion = schema.GroupVersion{Group: "graph.kotal.io", Version: "v1alpha1"} 14 | 15 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme 16 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} 17 | 18 | // AddToScheme adds the types in this group-version to the given scheme. 19 | AddToScheme = SchemeBuilder.AddToScheme 20 | ) 21 | -------------------------------------------------------------------------------- /apis/bitcoin/v1alpha1/groupversion_info.go: -------------------------------------------------------------------------------- 1 | // Package v1alpha1 contains API Schema definitions for the bitcoin v1alpha1 API group 2 | // +kubebuilder:object:generate=true 3 | // +groupName=bitcoin.kotal.io 4 | package v1alpha1 5 | 6 | import ( 7 | "k8s.io/apimachinery/pkg/runtime/schema" 8 | "sigs.k8s.io/controller-runtime/pkg/scheme" 9 | ) 10 | 11 | var ( 12 | // GroupVersion is group version used to register these objects 13 | GroupVersion = schema.GroupVersion{Group: "bitcoin.kotal.io", Version: "v1alpha1"} 14 | 15 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme 16 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} 17 | 18 | // AddToScheme adds the types in this group-version to the given scheme. 19 | AddToScheme = SchemeBuilder.AddToScheme 20 | ) 21 | -------------------------------------------------------------------------------- /apis/stacks/v1alpha1/groupversion_info.go: -------------------------------------------------------------------------------- 1 | // Package v1alpha1 contains API Schema definitions for the stacks v1alpha1 API group 2 | // +kubebuilder:object:generate=true 3 | // +groupName=stacks.kotal.io 4 | package v1alpha1 5 | 6 | import ( 7 | "k8s.io/apimachinery/pkg/runtime/schema" 8 | "sigs.k8s.io/controller-runtime/pkg/scheme" 9 | ) 10 | 11 | var ( 12 | // GroupVersion is group version used to register these objects 13 | GroupVersion = schema.GroupVersion{Group: "stacks.kotal.io", Version: "v1alpha1"} 14 | 15 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme 16 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} 17 | 18 | // AddToScheme adds the types in this group-version to the given scheme. 19 | AddToScheme = SchemeBuilder.AddToScheme 20 | ) 21 | -------------------------------------------------------------------------------- /config/samples/ethereum2/ethereum2_v1alpha1_teku_validator.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: ethereum2.kotal.io/v1alpha1 2 | kind: Validator 3 | metadata: 4 | name: teku-validator 5 | spec: 6 | client: teku 7 | network: mainnet 8 | beaconEndpoints: 9 | - http://teku-beacon-node:8888 10 | graffiti: Validated by Kotal 11 | # my-vaidator secret must exist before deploying the validator 12 | # my-validator secret must has [keystore] and [password] keys 13 | # key is the keystore file 14 | # password is the password file 15 | keystores: 16 | - secretName: my-validator 17 | feeRecipient: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045" 18 | resources: 19 | # these resources are only for testing 20 | # change resources depending on your use case 21 | cpu: "1" 22 | memory: "1Gi" 23 | -------------------------------------------------------------------------------- /apis/ethereum/v1alpha1/groupversion_info.go: -------------------------------------------------------------------------------- 1 | // Package v1alpha1 contains API Schema definitions for the ethereum v1alpha1 API group 2 | // +kubebuilder:object:generate=true 3 | // +groupName=ethereum.kotal.io 4 | package v1alpha1 5 | 6 | import ( 7 | "k8s.io/apimachinery/pkg/runtime/schema" 8 | "sigs.k8s.io/controller-runtime/pkg/scheme" 9 | ) 10 | 11 | var ( 12 | // GroupVersion is group version used to register these objects 13 | GroupVersion = schema.GroupVersion{Group: "ethereum.kotal.io", Version: "v1alpha1"} 14 | 15 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme 16 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} 17 | 18 | // AddToScheme adds the types in this group-version to the given scheme. 19 | AddToScheme = SchemeBuilder.AddToScheme 20 | ) 21 | -------------------------------------------------------------------------------- /apis/filecoin/v1alpha1/groupversion_info.go: -------------------------------------------------------------------------------- 1 | // Package v1alpha1 contains API Schema definitions for the filecoin v1alpha1 API group 2 | // +kubebuilder:object:generate=true 3 | // +groupName=filecoin.kotal.io 4 | package v1alpha1 5 | 6 | import ( 7 | "k8s.io/apimachinery/pkg/runtime/schema" 8 | "sigs.k8s.io/controller-runtime/pkg/scheme" 9 | ) 10 | 11 | var ( 12 | // GroupVersion is group version used to register these objects 13 | GroupVersion = schema.GroupVersion{Group: "filecoin.kotal.io", Version: "v1alpha1"} 14 | 15 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme 16 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} 17 | 18 | // AddToScheme adds the types in this group-version to the given scheme. 19 | AddToScheme = SchemeBuilder.AddToScheme 20 | ) 21 | -------------------------------------------------------------------------------- /apis/polkadot/v1alpha1/groupversion_info.go: -------------------------------------------------------------------------------- 1 | // Package v1alpha1 contains API Schema definitions for the polkadot v1alpha1 API group 2 | // +kubebuilder:object:generate=true 3 | // +groupName=polkadot.kotal.io 4 | package v1alpha1 5 | 6 | import ( 7 | "k8s.io/apimachinery/pkg/runtime/schema" 8 | "sigs.k8s.io/controller-runtime/pkg/scheme" 9 | ) 10 | 11 | var ( 12 | // GroupVersion is group version used to register these objects 13 | GroupVersion = schema.GroupVersion{Group: "polkadot.kotal.io", Version: "v1alpha1"} 14 | 15 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme 16 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} 17 | 18 | // AddToScheme adds the types in this group-version to the given scheme. 19 | AddToScheme = SchemeBuilder.AddToScheme 20 | ) 21 | -------------------------------------------------------------------------------- /apis/chainlink/v1alpha1/groupversion_info.go: -------------------------------------------------------------------------------- 1 | // Package v1alpha1 contains API Schema definitions for the chainlink v1alpha1 API group 2 | // +kubebuilder:object:generate=true 3 | // +groupName=chainlink.kotal.io 4 | package v1alpha1 5 | 6 | import ( 7 | "k8s.io/apimachinery/pkg/runtime/schema" 8 | "sigs.k8s.io/controller-runtime/pkg/scheme" 9 | ) 10 | 11 | var ( 12 | // GroupVersion is group version used to register these objects 13 | GroupVersion = schema.GroupVersion{Group: "chainlink.kotal.io", Version: "v1alpha1"} 14 | 15 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme 16 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} 17 | 18 | // AddToScheme adds the types in this group-version to the given scheme. 19 | AddToScheme = SchemeBuilder.AddToScheme 20 | ) 21 | -------------------------------------------------------------------------------- /apis/ethereum2/v1alpha1/groupversion_info.go: -------------------------------------------------------------------------------- 1 | // Package v1alpha1 contains API Schema definitions for the ethereum2 v1alpha1 API group 2 | // +kubebuilder:object:generate=true 3 | // +groupName=ethereum2.kotal.io 4 | package v1alpha1 5 | 6 | import ( 7 | "k8s.io/apimachinery/pkg/runtime/schema" 8 | "sigs.k8s.io/controller-runtime/pkg/scheme" 9 | ) 10 | 11 | var ( 12 | // GroupVersion is group version used to register these objects 13 | GroupVersion = schema.GroupVersion{Group: "ethereum2.kotal.io", Version: "v1alpha1"} 14 | 15 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme 16 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} 17 | 18 | // AddToScheme adds the types in this group-version to the given scheme. 19 | AddToScheme = SchemeBuilder.AddToScheme 20 | ) 21 | -------------------------------------------------------------------------------- /config/samples/ethereum/ethereum_v1alpha1_mainnet_besu_node.yaml: -------------------------------------------------------------------------------- 1 | # WARNING: DON'T use the following secrets in production 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: mainnet-besu-nodekey 6 | stringData: 7 | key: 5df5eff7ef9e4e82739b68a34c6b23608d79ee8daf3b598a01ffb0dd7aa3a2fd 8 | --- 9 | apiVersion: ethereum.kotal.io/v1alpha1 10 | kind: Node 11 | metadata: 12 | name: mainnet-besu-node 13 | spec: 14 | network: mainnet 15 | client: besu 16 | nodePrivateKeySecretName: mainnet-besu-nodekey 17 | rpc: true 18 | rpcPort: 8599 19 | corsDomains: 20 | - example.kotal.io 21 | rpcAPI: 22 | - web3 23 | - net 24 | - eth 25 | graphql: true 26 | graphqlPort: 8777 27 | resources: 28 | cpu: "1" 29 | cpuLimit: "1" 30 | memory: "1Gi" 31 | memoryLimit: "2Gi" 32 | -------------------------------------------------------------------------------- /config/samples/ethereum/ethereum_v1alpha1_mainnet_geth_node.yaml: -------------------------------------------------------------------------------- 1 | # WARNING: DON'T use the following secrets in production 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: mainnet-geth-nodekey 6 | stringData: 7 | key: 5df5eff7ef9e4e82739b68a34c6b23608d79ee8daf3b598a01ffb0dd7aa3a2fd 8 | --- 9 | apiVersion: ethereum.kotal.io/v1alpha1 10 | kind: Node 11 | metadata: 12 | name: mainnet-geth-node 13 | spec: 14 | network: mainnet 15 | client: geth 16 | nodePrivateKeySecretName: mainnet-geth-nodekey 17 | rpc: true 18 | rpcPort: 8599 19 | corsDomains: 20 | - example.kotal.io 21 | rpcAPI: 22 | - web3 23 | - net 24 | - eth 25 | graphql: true 26 | graphqlPort: 8777 27 | resources: 28 | cpu: "1" 29 | cpuLimit: "1" 30 | memory: "1Gi" 31 | memoryLimit: "2Gi" 32 | -------------------------------------------------------------------------------- /config/samples/polkadot/polkadot_v1alpha1_kusama_node.yaml: -------------------------------------------------------------------------------- 1 | # WARNING: DON'T use the following secret in production 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: polkadot-node-key 6 | stringData: 7 | # Identity 12D3KooWFEHU9FXqGCL6ify4rqcon31WYViCnUfA6aVYTjRXqJ5F 8 | key: 2938f18547765c18d44c8b8ebf649e97f9a6f0c5afcb241ae93a9ff252ea4a2c 9 | --- 10 | apiVersion: polkadot.kotal.io/v1alpha1 11 | kind: Node 12 | metadata: 13 | name: node-sample 14 | spec: 15 | network: kusama 16 | nodePrivateKeySecretName: "polkadot-node-key" 17 | logging: info 18 | syncMode: fast 19 | pruning: true 20 | retainedBlocks: 1024 21 | rpc: true 22 | rpcPort: 9933 23 | ws: true 24 | wsPort: 9944 25 | telemetry: true 26 | prometheus: true 27 | resources: 28 | cpu: "1" 29 | memory: "1Gi" 30 | -------------------------------------------------------------------------------- /config/samples/ethereum2/ethereum2_v1alpha1_nimbus_beacon_node.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: jwt-secret 5 | stringData: 6 | secret: fbe0c28a10274b27babf3c51e88a7435318e25fad4de877e5a63a67d0d65fdbb 7 | --- 8 | apiVersion: ethereum2.kotal.io/v1alpha1 9 | kind: BeaconNode 10 | metadata: 11 | name: nimbus-beacon-node 12 | spec: 13 | network: goerli 14 | client: nimbus 15 | logging: info 16 | executionEngineEndpoint: http://goerli-geth-node:8551 17 | jwtSecretName: "jwt-secret" 18 | feeRecipient: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045" 19 | checkpointSyncUrl: "https://goerli.beaconstate.ethstaker.cc" 20 | rest: true 21 | resources: 22 | # these resources are only for testing 23 | # change resources depending on your use case 24 | cpu: "1" 25 | memory: "1Gi" 26 | -------------------------------------------------------------------------------- /apis/shared/zz_generated.deepcopy.go: -------------------------------------------------------------------------------- 1 | //go:build !ignore_autogenerated 2 | // +build !ignore_autogenerated 3 | 4 | // Code generated by controller-gen. DO NOT EDIT. 5 | 6 | package shared 7 | 8 | import () 9 | 10 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 11 | func (in *Resources) DeepCopyInto(out *Resources) { 12 | *out = *in 13 | if in.StorageClass != nil { 14 | in, out := &in.StorageClass, &out.StorageClass 15 | *out = new(string) 16 | **out = **in 17 | } 18 | } 19 | 20 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Resources. 21 | func (in *Resources) DeepCopy() *Resources { 22 | if in == nil { 23 | return nil 24 | } 25 | out := new(Resources) 26 | in.DeepCopyInto(out) 27 | return out 28 | } 29 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go build and test 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | 9 | env: 10 | K8S_VERSION: 1.23.3 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Clone repo 17 | uses: actions/checkout@v3 18 | 19 | - name: Set up Go 20 | uses: actions/setup-go@v3 21 | with: 22 | go-version: 1.21 23 | 24 | - name: Build 25 | run: go build -v ./... 26 | 27 | - name: Download envtest binaries 28 | run: | 29 | curl -sSLo envtest-bins.tar.gz "https://go.kubebuilder.io/test-tools/${K8S_VERSION}/$(go env GOOS)/$(go env GOARCH)" 30 | sudo tar -vxzf envtest-bins.tar.gz -C /usr/local/ 31 | 32 | - name: Test 33 | run: go test -v ./... 34 | -------------------------------------------------------------------------------- /config/samples/ethereum2/ethereum2_v1alpha1_lighthouse_beacon_node.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: jwt-secret 5 | stringData: 6 | secret: fbe0c28a10274b27babf3c51e88a7435318e25fad4de877e5a63a67d0d65fdbb 7 | --- 8 | apiVersion: ethereum2.kotal.io/v1alpha1 9 | kind: BeaconNode 10 | metadata: 11 | name: lighthouse-beacon-node 12 | spec: 13 | network: goerli 14 | client: lighthouse 15 | logging: info 16 | rest: true 17 | restPort: 8888 18 | executionEngineEndpoint: http://goerli-geth-node:8551 19 | jwtSecretName: "jwt-secret" 20 | checkpointSyncUrl: "https://goerli.beaconstate.ethstaker.cc" 21 | feeRecipient: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045" 22 | resources: 23 | # these resources are only for testing 24 | # change resources depending on your use case 25 | cpu: "1" 26 | memory: "1Gi" 27 | -------------------------------------------------------------------------------- /config/samples/bitcoin/bitcoin_v1alpha1_node.yaml: -------------------------------------------------------------------------------- 1 | # WARNING: DON'T use the following secrets in production 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: kotal-json-rpc-user-password 6 | stringData: 7 | password: secret 8 | --- 9 | # WARNING: DON'T use the following secrets in production 10 | apiVersion: v1 11 | kind: Secret 12 | metadata: 13 | name: dummy-json-rpc-user-password 14 | stringData: 15 | password: s3cr3t 16 | --- 17 | apiVersion: bitcoin.kotal.io/v1alpha1 18 | kind: Node 19 | metadata: 20 | name: bitcoin-node 21 | spec: 22 | network: mainnet 23 | wallet: false 24 | txIndex: true 25 | rpc: true 26 | rpcPort: 7777 27 | p2pPort: 8888 28 | rpcUsers: 29 | - username: kotal 30 | passwordSecretName: kotal-json-rpc-user-password 31 | - username: dummy 32 | passwordSecretName: dummy-json-rpc-user-password 33 | -------------------------------------------------------------------------------- /config/samples/ethereum2/ethereum2_v1alpha1_teku_beacon_node.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: jwt-secret 5 | stringData: 6 | secret: fbe0c28a10274b27babf3c51e88a7435318e25fad4de877e5a63a67d0d65fdbb 7 | --- 8 | apiVersion: ethereum2.kotal.io/v1alpha1 9 | kind: BeaconNode 10 | metadata: 11 | name: teku-beacon-node 12 | spec: 13 | network: goerli 14 | client: teku 15 | logging: info 16 | rest: true 17 | restPort: 8888 18 | executionEngineEndpoint: http://goerli-geth-node:8551 19 | checkpointSyncUrl: "https://beaconstate-goerli.chainsafe.io/eth/v2/debug/beacon/states/finalized" 20 | jwtSecretName: "jwt-secret" 21 | feeRecipient: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045" 22 | resources: 23 | # these resources are only for testing 24 | # change resources depending on your use case 25 | cpu: "1" 26 | memory: "1Gi" 27 | -------------------------------------------------------------------------------- /controllers/shared/path_test.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import "testing" 4 | 5 | const ( 6 | testHomeDir = "/users/test" 7 | ) 8 | 9 | func TestPathData(t *testing.T) { 10 | expected := "/users/test/kotal-data" 11 | got := PathData(testHomeDir) 12 | 13 | if got != expected { 14 | t.Errorf("expected data directory to be %s, got %s", expected, got) 15 | } 16 | } 17 | 18 | func TestPathConfig(t *testing.T) { 19 | expected := "/users/test/kotal-config" 20 | got := PathConfig(testHomeDir) 21 | 22 | if got != expected { 23 | t.Errorf("expected configuration directory to be %s, got %s", expected, got) 24 | } 25 | } 26 | 27 | func TestPathSecrets(t *testing.T) { 28 | expected := "/users/test/.kotal-secrets" 29 | got := PathSecrets(testHomeDir) 30 | 31 | if got != expected { 32 | t.Errorf("expected secrets directory to be %s, got %s", expected, got) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /controllers/ethereum/keystore.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | 7 | "github.com/ethereum/go-ethereum/accounts/keystore" 8 | "github.com/ethereum/go-ethereum/crypto" 9 | ) 10 | 11 | // KeyStoreFromPrivateKey generates key store from private key (hex without 0x) 12 | func KeyStoreFromPrivateKey(key, password string) (content []byte, err error) { 13 | dir, err := ioutil.TempDir(os.TempDir(), "tmp") 14 | if err != nil { 15 | return 16 | } 17 | defer os.RemoveAll(dir) 18 | 19 | ks := keystore.NewKeyStore(dir, keystore.StandardScryptN, keystore.StandardScryptP) 20 | privateKey, err := crypto.HexToECDSA(key) 21 | if err != nil { 22 | return 23 | } 24 | 25 | acc, err := ks.ImportECDSA(privateKey, password) 26 | if err != nil { 27 | return 28 | } 29 | 30 | content, err = ks.Export(acc, password, password) 31 | 32 | return 33 | } 34 | -------------------------------------------------------------------------------- /clients/ethereum/client.go: -------------------------------------------------------------------------------- 1 | package ethereum 2 | 3 | import ( 4 | "fmt" 5 | 6 | ethereumv1alpha1 "github.com/kotalco/kotal/apis/ethereum/v1alpha1" 7 | "github.com/kotalco/kotal/clients" 8 | ) 9 | 10 | // EthereumClient is Ethereum client 11 | type EthereumClient interface { 12 | clients.Interface 13 | Genesis() (string, error) 14 | EncodeStaticNodes() string 15 | } 16 | 17 | // NewClient returns an Ethereum client instance 18 | func NewClient(node *ethereumv1alpha1.Node) (EthereumClient, error) { 19 | switch node.Spec.Client { 20 | case ethereumv1alpha1.BesuClient: 21 | return &BesuClient{node}, nil 22 | case ethereumv1alpha1.GethClient: 23 | return &GethClient{node}, nil 24 | case ethereumv1alpha1.NethermindClient: 25 | return &NethermindClient{&ParityGenesis{}, node}, nil 26 | default: 27 | return nil, fmt.Errorf("client %s is not supported", node.Spec.Client) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /controllers/shared/path.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import "fmt" 4 | 5 | const ( 6 | // BlockchainDataSubDir is the blockchain data sub directory 7 | BlockchainDataSubDir = "kotal-data" 8 | // SecretsSubDir is the secrets (private keys, password ... etc) sub directory 9 | SecretsSubDir = ".kotal-secrets" 10 | // ConfigSubDir is the configuration sub directory 11 | ConfigSubDir = "kotal-config" 12 | ) 13 | 14 | // PathData returns blockchain data directory 15 | func PathData(homeDir string) string { 16 | return fmt.Sprintf("%s/%s", homeDir, BlockchainDataSubDir) 17 | } 18 | 19 | // PathSecrets returns secrets directory 20 | func PathSecrets(homeDir string) string { 21 | return fmt.Sprintf("%s/%s", homeDir, SecretsSubDir) 22 | } 23 | 24 | // PathConfig returns configuration directory 25 | func PathConfig(homeDir string) string { 26 | return fmt.Sprintf("%s/%s", homeDir, ConfigSubDir) 27 | } 28 | -------------------------------------------------------------------------------- /clients/ethereum/client_test.go: -------------------------------------------------------------------------------- 1 | package ethereum 2 | 3 | import ( 4 | "testing" 5 | 6 | ethereumv1alpha1 "github.com/kotalco/kotal/apis/ethereum/v1alpha1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | func TestNewClient(t *testing.T) { 11 | node := ðereumv1alpha1.Node{ 12 | ObjectMeta: metav1.ObjectMeta{ 13 | Name: "node-with-invalid-client", 14 | }, 15 | Spec: ethereumv1alpha1.NodeSpec{ 16 | Client: ethereumv1alpha1.EthereumClient("nokia"), 17 | Network: ethereumv1alpha1.MainNetwork, 18 | }, 19 | } 20 | 21 | client, err := NewClient(node) 22 | if err == nil { 23 | t.Error("expecting an error") 24 | } 25 | 26 | if client != nil { 27 | t.Error("expecting client to be nil") 28 | } 29 | 30 | expected := "client nokia is not supported" 31 | got := err.Error() 32 | if expected != got { 33 | t.Errorf("expected error message to be: %s", expected) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Build the manager binary 2 | FROM golang:1.21 as builder 3 | 4 | WORKDIR /workspace 5 | # Copy the Go Modules manifests 6 | COPY go.mod go.mod 7 | COPY go.sum go.sum 8 | # cache deps before building and copying source so that we don't need to re-download as much 9 | # and so that source changes don't invalidate our downloaded layer 10 | RUN go mod download 11 | 12 | # Copy the go source 13 | COPY main.go main.go 14 | COPY apis/ apis/ 15 | COPY clients/ clients/ 16 | COPY controllers/ controllers/ 17 | COPY helpers/ helpers/ 18 | 19 | # Build 20 | RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o manager main.go 21 | 22 | # Use distroless as minimal base image to package the manager binary 23 | # Refer to https://github.com/GoogleContainerTools/distroless for more details 24 | FROM gcr.io/distroless/static:nonroot 25 | WORKDIR / 26 | COPY --from=builder /workspace/manager . 27 | USER 65532:65532 28 | 29 | ENTRYPOINT ["/manager"] 30 | -------------------------------------------------------------------------------- /config/samples/ethereum/ethereum_v1alpha1_mainnet_nethermind_node.yaml: -------------------------------------------------------------------------------- 1 | # WARNING: DON'T use the following secrets in production 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: mainnet-nethermind-nodekey 6 | stringData: 7 | key: 608e9b6f67c65e47531e08e8e501386dfae63a540fa3c48802c8aad854510b4e 8 | --- 9 | apiVersion: ethereum.kotal.io/v1alpha1 10 | kind: Node 11 | metadata: 12 | name: mainnet-nethermind-node 13 | spec: 14 | network: mainnet 15 | client: nethermind 16 | nodePrivateKeySecretName: mainnet-nethermind-nodekey 17 | logging: info 18 | rpc: true 19 | rpcPort: 9999 20 | rpcAPI: 21 | - eth 22 | - admin 23 | p2pPort: 30304 24 | ws: true 25 | wsPort: 8888 26 | syncMode: fast 27 | staticNodes: 28 | - "enode://2281549869465d98e90cebc45e1d6834a01465a990add7bcf07a49287e7e66b50ca27f9c70a46190cef7ad746dd5d5b6b9dfee0c9954104c8e9bd0d42758ec58@10.5.0.2:30300" 29 | resources: 30 | cpu: "1" 31 | memory: "1Gi" 32 | -------------------------------------------------------------------------------- /config/webhook/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | # the following config is for teaching kustomize where to look at when substituting vars. 2 | # It requires kustomize v2.1.0 or newer to work properly. 3 | nameReference: 4 | - kind: Service 5 | version: v1 6 | fieldSpecs: 7 | - kind: MutatingWebhookConfiguration 8 | group: admissionregistration.k8s.io 9 | path: webhooks/clientConfig/service/name 10 | - kind: ValidatingWebhookConfiguration 11 | group: admissionregistration.k8s.io 12 | path: webhooks/clientConfig/service/name 13 | 14 | namespace: 15 | - kind: MutatingWebhookConfiguration 16 | group: admissionregistration.k8s.io 17 | path: webhooks/clientConfig/service/namespace 18 | create: true 19 | - kind: ValidatingWebhookConfiguration 20 | group: admissionregistration.k8s.io 21 | path: webhooks/clientConfig/service/namespace 22 | create: true 23 | 24 | varReference: 25 | - path: metadata/annotations 26 | -------------------------------------------------------------------------------- /config/samples/stacks/stacks_v1alpha1_node.yaml: -------------------------------------------------------------------------------- 1 | # WARNING: DON'T use the following secrets in production 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: bitcoin-node-rpc-password 6 | stringData: 7 | password: blockstacksystem 8 | --- 9 | apiVersion: v1 10 | kind: Secret 11 | metadata: 12 | name: node-private-key 13 | stringData: 14 | key: 7d6774f1bfcd3b0e0ed81ae6094f17232056ed4a560408353ba92ee70d53647001 15 | --- 16 | apiVersion: stacks.kotal.io/v1alpha1 17 | kind: Node 18 | metadata: 19 | name: stacks-node 20 | spec: 21 | network: mainnet 22 | nodePrivateKeySecretName: node-private-key 23 | p2pPort: 8888 24 | rpcPort: 7777 25 | bitcoinNode: 26 | endpoint: bitcoin.blockstack.com 27 | rpcPort: 8332 28 | p2pPort: 8333 29 | rpcUsername: blockstack 30 | rpcPasswordSecretName: bitcoin-node-rpc-password 31 | resources: 32 | cpu: "2" 33 | cpuLimit: "4" 34 | memory: "2Gi" 35 | memoryLimit: "4Gi" 36 | storage: "10Gi" 37 | -------------------------------------------------------------------------------- /config/samples/ethereum/ethereum_v1alpha1_goerli_besu_node.yaml: -------------------------------------------------------------------------------- 1 | # WARNING: DON'T use the following secrets in production 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: goerli-besu-nodekey 6 | stringData: 7 | key: 5df5eff7ef9e4e82739b68a34c6b23608d79ee8daf3b598a01ffb0dd7aa3a2fd 8 | --- 9 | apiVersion: v1 10 | kind: Secret 11 | metadata: 12 | name: jwt-secret 13 | stringData: 14 | secret: fbe0c28a10274b27babf3c51e88a7435318e25fad4de877e5a63a67d0d65fdbb 15 | --- 16 | apiVersion: ethereum.kotal.io/v1alpha1 17 | kind: Node 18 | metadata: 19 | name: goerli-besu-node 20 | spec: 21 | network: goerli 22 | client: besu 23 | nodePrivateKeySecretName: goerli-besu-nodekey 24 | rpc: true 25 | rpcPort: 8599 26 | corsDomains: 27 | - example.kotal.io 28 | rpcAPI: 29 | - web3 30 | - net 31 | - eth 32 | engine: true 33 | jwtSecretName: jwt-secret 34 | graphql: true 35 | graphqlPort: 8777 36 | resources: 37 | cpu: "1" 38 | cpuLimit: "1" 39 | memory: "1Gi" 40 | memoryLimit: "2Gi" 41 | -------------------------------------------------------------------------------- /config/samples/ethereum/ethereum_v1alpha1_goerli_geth_node.yaml: -------------------------------------------------------------------------------- 1 | # WARNING: DON'T use the following secrets in production 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: goerli-geth-nodekey 6 | stringData: 7 | key: 5df5eff7ef9e4e82739b68a34c6b23608d79ee8daf3b598a01ffb0dd7aa3a2fd 8 | --- 9 | apiVersion: v1 10 | kind: Secret 11 | metadata: 12 | name: jwt-secret 13 | stringData: 14 | secret: fbe0c28a10274b27babf3c51e88a7435318e25fad4de877e5a63a67d0d65fdbb 15 | --- 16 | apiVersion: ethereum.kotal.io/v1alpha1 17 | kind: Node 18 | metadata: 19 | name: goerli-geth-node 20 | spec: 21 | network: goerli 22 | client: geth 23 | nodePrivateKeySecretName: goerli-geth-nodekey 24 | rpc: true 25 | engine: true 26 | jwtSecretName: jwt-secret 27 | rpcPort: 8599 28 | corsDomains: 29 | - example.kotal.io 30 | rpcAPI: 31 | - web3 32 | - net 33 | - eth 34 | graphql: true 35 | graphqlPort: 8777 36 | resources: 37 | cpu: "1" 38 | cpuLimit: "1" 39 | memory: "1Gi" 40 | memoryLimit: "2Gi" 41 | -------------------------------------------------------------------------------- /clients/graph/graph_node_client_test.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | graphv1alpha1 "github.com/kotalco/kotal/apis/graph/v1alpha1" 5 | . "github.com/onsi/ginkgo/v2" 6 | . "github.com/onsi/gomega" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | var _ = Describe("Graph node client", func() { 11 | 12 | node := &graphv1alpha1.Node{ 13 | ObjectMeta: metav1.ObjectMeta{ 14 | Name: "graph-node", 15 | Namespace: "default", 16 | }, 17 | Spec: graphv1alpha1.NodeSpec{}, 18 | } 19 | 20 | // TODO: default node 21 | 22 | client := NewClient(node) 23 | 24 | It("Should get correct command", func() { 25 | Expect(client.Command()).To(Equal( 26 | []string{ 27 | GraphNodeCommand, 28 | }, 29 | )) 30 | }) 31 | 32 | It("Should get correct home directory", func() { 33 | Expect(client.HomeDir()).To(Equal(GraphNodeHomeDir)) 34 | }) 35 | 36 | It("Should generate correct client arguments", func() { 37 | Expect(client.Args()).To(ContainElements( 38 | []string{}, 39 | )) 40 | }) 41 | 42 | }) 43 | -------------------------------------------------------------------------------- /config/certmanager/certificate.yaml: -------------------------------------------------------------------------------- 1 | # The following manifests contain a self-signed issuer CR and a certificate CR. 2 | # More document can be found at https://docs.cert-manager.io 3 | # WARNING: Targets CertManager 0.11 check https://docs.cert-manager.io/en/latest/tasks/upgrading/index.html for 4 | # breaking changes 5 | apiVersion: cert-manager.io/v1 6 | kind: Issuer 7 | metadata: 8 | name: selfsigned-issuer 9 | namespace: system 10 | spec: 11 | selfSigned: {} 12 | --- 13 | apiVersion: cert-manager.io/v1 14 | kind: Certificate 15 | metadata: 16 | name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml 17 | namespace: system 18 | spec: 19 | # $(SERVICE_NAME) and $(SERVICE_NAMESPACE) will be substituted by kustomize 20 | dnsNames: 21 | - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc 22 | - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc.cluster.local 23 | issuerRef: 24 | kind: Issuer 25 | name: selfsigned-issuer 26 | secretName: webhook-server-cert # this secret will not be prefixed, since it's not managed by kustomize 27 | -------------------------------------------------------------------------------- /controllers/shared/reconciler.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import ( 4 | "context" 5 | 6 | "k8s.io/apimachinery/pkg/runtime" 7 | ctrl "sigs.k8s.io/controller-runtime" 8 | "sigs.k8s.io/controller-runtime/pkg/client" 9 | ) 10 | 11 | type Reconciler struct { 12 | client.Client 13 | Scheme *runtime.Scheme 14 | } 15 | 16 | func (r *Reconciler) GetClient() client.Client { 17 | return r.Client 18 | } 19 | 20 | func (r *Reconciler) GetScheme() *runtime.Scheme { 21 | return r.Scheme 22 | } 23 | 24 | // ReconcileOwned reconciles k8s object according to custom resource spec 25 | func (r Reconciler) ReconcileOwned(ctx context.Context, cr CustomResource, obj client.Object, updateFn func(client.Object) error) error { 26 | 27 | obj.SetName(cr.GetName()) 28 | obj.SetNamespace(cr.GetNamespace()) 29 | 30 | _, err := ctrl.CreateOrUpdate(ctx, r.GetClient(), obj, func() error { 31 | if err := ctrl.SetControllerReference(cr, obj, r.GetScheme()); err != nil { 32 | return err 33 | } 34 | 35 | updateFn(obj) 36 | 37 | return nil 38 | }) 39 | 40 | return err 41 | } 42 | -------------------------------------------------------------------------------- /apis/stacks/v1alpha1/node_defaulting_webhook_test.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo/v2" 5 | . "github.com/onsi/gomega" 6 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 7 | ) 8 | 9 | var _ = Describe("Stacks node defaulting", func() { 10 | It("Should default Stacks node", func() { 11 | node := Node{ 12 | ObjectMeta: metav1.ObjectMeta{}, 13 | Spec: NodeSpec{ 14 | Network: Mainnet, 15 | }, 16 | } 17 | 18 | node.Default() 19 | 20 | Expect(node.Spec.Image).To(Equal(DefaultStacksNodeImage)) 21 | Expect(*node.Spec.Replicas).To(Equal(DefaltReplicas)) 22 | Expect(node.Spec.P2PPort).To(Equal(DefaultP2PPort)) 23 | Expect(node.Spec.RPCPort).To(Equal(DefaultRPCPort)) 24 | Expect(node.Spec.CPU).To(Equal(DefaultNodeCPURequest)) 25 | Expect(node.Spec.CPULimit).To(Equal(DefaultNodeCPULimit)) 26 | Expect(node.Spec.Memory).To(Equal(DefaultNodeMemoryRequest)) 27 | Expect(node.Spec.MemoryLimit).To(Equal(DefaultNodeMemoryLimit)) 28 | Expect(node.Spec.Storage).To(Equal(DefaultNodeStorageRequest)) 29 | 30 | }) 31 | }) 32 | -------------------------------------------------------------------------------- /config/samples/stacks/stacks_v1alpha1_miner_node.yaml: -------------------------------------------------------------------------------- 1 | # WARNING: DON'T use the following secrets in production 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: bitcoin-node-rpc-password 6 | stringData: 7 | password: blockstacksystem 8 | --- 9 | # WARNING: DON'T use the following secrets in production 10 | apiVersion: v1 11 | kind: Secret 12 | metadata: 13 | name: seed-private-key 14 | stringData: 15 | key: 8310d8220c30bbf667e4f09071ae73a8fdff4e3cb508e67b4f9f962f830c130c01 16 | --- 17 | apiVersion: stacks.kotal.io/v1alpha1 18 | kind: Node 19 | metadata: 20 | name: stacks-miner-node 21 | spec: 22 | network: mainnet 23 | p2pPort: 8888 24 | rpcPort: 7777 25 | miner: true 26 | mineMicroblocks: true 27 | seedPrivateKeySecretName: seed-private-key 28 | bitcoinNode: 29 | endpoint: bitcoin.blockstack.com 30 | rpcPort: 8332 31 | p2pPort: 8333 32 | rpcUsername: blockstack 33 | rpcPasswordSecretName: bitcoin-node-rpc-password 34 | resources: 35 | cpu: "2" 36 | cpuLimit: "4" 37 | memory: "2Gi" 38 | memoryLimit: "4Gi" 39 | storage: "10Gi" 40 | -------------------------------------------------------------------------------- /apis/graph/v1alpha1/node.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 5 | ) 6 | 7 | // NodeSpec defines the desired state of Node 8 | type NodeSpec struct { 9 | // TODO: default node image 10 | // Image is Graph node client image 11 | Image string `json:"image,omitempty"` 12 | } 13 | 14 | // NodeStatus defines the observed state of Node 15 | type NodeStatus struct { 16 | } 17 | 18 | //+kubebuilder:object:root=true 19 | //+kubebuilder:subresource:status 20 | 21 | // Node is the Schema for the nodes API 22 | type Node struct { 23 | metav1.TypeMeta `json:",inline"` 24 | metav1.ObjectMeta `json:"metadata,omitempty"` 25 | 26 | Spec NodeSpec `json:"spec,omitempty"` 27 | Status NodeStatus `json:"status,omitempty"` 28 | } 29 | 30 | //+kubebuilder:object:root=true 31 | 32 | // NodeList contains a list of Node 33 | type NodeList struct { 34 | metav1.TypeMeta `json:",inline"` 35 | metav1.ListMeta `json:"metadata,omitempty"` 36 | Items []Node `json:"items"` 37 | } 38 | 39 | func init() { 40 | SchemeBuilder.Register(&Node{}, &NodeList{}) 41 | } 42 | -------------------------------------------------------------------------------- /apis/shared/logging.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | // VerbosityLevel is logging verbosity levels 4 | type VerbosityLevel string 5 | 6 | const ( 7 | // NoLogs outputs no logs 8 | NoLogs VerbosityLevel = "off" 9 | // FatalLogs outputs only fatal logs 10 | FatalLogs VerbosityLevel = "fatal" 11 | // ErrorLogs outputs only error logs 12 | ErrorLogs VerbosityLevel = "error" 13 | // WarnLogs outputs only warning logs 14 | WarnLogs VerbosityLevel = "warn" 15 | // InfoLogs outputs only informational logs 16 | InfoLogs VerbosityLevel = "info" 17 | // DebugLogs outputs only debugging logs 18 | DebugLogs VerbosityLevel = "debug" 19 | // TraceLogs outputs only tracing logs 20 | TraceLogs VerbosityLevel = "trace" 21 | // AllLogs outputs only all logs 22 | AllLogs VerbosityLevel = "all" 23 | // NoticeLogs outputs only notice logs 24 | NoticeLogs VerbosityLevel = "notice" 25 | // CriticalLogs outputs only critical logs 26 | CriticalLogs VerbosityLevel = "crit" 27 | // PanicLogs outputs only panic logs 28 | PanicLogs VerbosityLevel = "panic" 29 | // NoneLogs outputs no logs 30 | NoneLogs VerbosityLevel = "none" 31 | ) 32 | -------------------------------------------------------------------------------- /controllers/shared/security_context_test.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import ( 4 | "testing" 5 | 6 | corev1 "k8s.io/api/core/v1" 7 | ) 8 | 9 | func TestSecurityContext(t *testing.T) { 10 | var userId int64 = 1000 11 | var groupId int64 = 3000 12 | var fsGroupId int64 = 2000 13 | var nonRoot = true 14 | policy := corev1.FSGroupChangeOnRootMismatch 15 | 16 | context := SecurityContext() 17 | 18 | if *context.RunAsUser != userId { 19 | t.Errorf("expected user id to be %d, got %d", userId, *context.RunAsUser) 20 | } 21 | 22 | if *context.RunAsGroup != groupId { 23 | t.Errorf("expected group id to be %d, got %d", groupId, *context.RunAsGroup) 24 | } 25 | 26 | if *context.FSGroup != fsGroupId { 27 | t.Errorf("expected fs group id to be %d, got %d", fsGroupId, *context.FSGroup) 28 | } 29 | 30 | if *context.RunAsNonRoot != nonRoot { 31 | t.Errorf("expected non root to be %t, got %t", nonRoot, *context.RunAsNonRoot) 32 | } 33 | 34 | if *context.FSGroupChangePolicy != policy { 35 | t.Errorf("expected fs group change policy to be %s, got %s", policy, *context.FSGroupChangePolicy) 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /apis/stacks/v1alpha1/defaults.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | const ( 4 | // DefaltReplicas is the default replicas 5 | DefaltReplicas uint = 1 6 | // DefaultHost is the default JSON-RPC server host 7 | DefaultHost = "0.0.0.0" 8 | // DefaultRPCPort is the default JSON-RPC port 9 | DefaultRPCPort uint = 20443 10 | // DefaultP2PPort is the default p2p bind port 11 | DefaultP2PPort uint = 20444 12 | ) 13 | 14 | const ( 15 | // DefaultStacksNodeImage is the default Stacks node client image 16 | DefaultStacksNodeImage = "kotalco/stacks:v2.5.0.0.3" 17 | ) 18 | 19 | // Resources 20 | const ( 21 | // DefaultNodeCPURequest is the cpu requested by Stacks node 22 | DefaultNodeCPURequest = "2" 23 | // DefaultNodeCPULimit is the cpu limit for Stacks node 24 | DefaultNodeCPULimit = "4" 25 | 26 | // DefaultNodeMemoryRequest is the memory requested by Stacks node 27 | DefaultNodeMemoryRequest = "4Gi" 28 | // DefaultNodeMemoryLimit is the memory limit for Stacks node 29 | DefaultNodeMemoryLimit = "8Gi" 30 | 31 | // DefaultNodeStorageRequest is the Storage requested by Stacks node 32 | DefaultNodeStorageRequest = "100Gi" 33 | ) 34 | -------------------------------------------------------------------------------- /clients/near/types.go: -------------------------------------------------------------------------------- 1 | package near 2 | 3 | const ( 4 | // NearArgHome is argument used to set home directory 5 | NearArgHome = "--home" 6 | // NearArgDisableRPC is argument used to disable JSON-RPC server 7 | NearArgDisableRPC = "--disable-rpc" 8 | // NearArgRPCAddress is argument used to set JSON-RPC address 9 | NearArgRPCAddress = "--rpc-addr" 10 | // NearArgPrometheusAddress is argument used to set prometheus exporter address 11 | NearArgPrometheusAddress = "--rpc-prometheus-addr" 12 | // NearArgBootnodes is argument used to set the boot nodes to bootstrap network from 13 | NearArgBootnodes = "--boot-nodes" 14 | // NearArgNetworkAddress is argument used to set network listening address 15 | NearArgNetworkAddress = "--network-addr" 16 | // NearArgMinimumPeers is argument used to set minimum number of peers required to start syncing/producing blocks 17 | NearArgMinimumPeers = "--min-peers" 18 | // NearArgTelemetryURL is argument used to set telemetry URL 19 | NearArgTelemetryURL = "--telemetry-url" 20 | // NearArgArchive is argument used to keeps old blocks in the storage 21 | NearArgArchive = "--archive" 22 | ) 23 | -------------------------------------------------------------------------------- /apis/ipfs/v1alpha1/cluster_peer_defaulting_webhook_test.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo/v2" 5 | . "github.com/onsi/gomega" 6 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 7 | ) 8 | 9 | var _ = Describe("IPFS cluster peer defaulting", func() { 10 | It("Should default ipfs cluster peer", func() { 11 | peer := ClusterPeer{ 12 | ObjectMeta: metav1.ObjectMeta{}, 13 | Spec: ClusterPeerSpec{}, 14 | } 15 | 16 | peer.Default() 17 | 18 | Expect(peer.Spec.Image).To(Equal(DefaultGoIPFSClusterImage)) 19 | Expect(*peer.Spec.Replicas).To(Equal(DefaltReplicas)) 20 | Expect(peer.Spec.Logging).To(Equal(DefaultLogging)) 21 | Expect(peer.Spec.Resources.CPU).To(Equal(DefaultNodeCPURequest)) 22 | Expect(peer.Spec.Resources.CPULimit).To(Equal(DefaultNodeCPULimit)) 23 | Expect(peer.Spec.Resources.Memory).To(Equal(DefaultNodeMemoryRequest)) 24 | Expect(peer.Spec.Resources.MemoryLimit).To(Equal(DefaultNodeMemoryLimit)) 25 | Expect(peer.Spec.Resources.Storage).To(Equal(DefaultNodeStorageRequest)) 26 | Expect(peer.Spec.Consensus).To(Equal(DefaultIPFSClusterConsensus)) 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /apis/ethereum/v1alpha1/network.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | const ( 4 | // MainNetwork is ethereum main network 5 | MainNetwork = "mainnet" 6 | // RopstenNetwork is ropsten pos network 7 | RopstenNetwork = "ropsten" 8 | // RinkebyNetwork is rinkeby poa network 9 | RinkebyNetwork = "rinkeby" 10 | // GoerliNetwork is goerli pos cross-client network 11 | GoerliNetwork = "goerli" 12 | // SepoliaNetwork is sepolia pos network 13 | SepoliaNetwork = "sepolia" 14 | // XDaiNetwork is xdai pos network 15 | XDaiNetwork = "xdai" 16 | // KottiNetwork is kotti poa ethereum classic test network 17 | KottiNetwork = "kotti" 18 | // ClassicNetwork is ethereum classic network 19 | ClassicNetwork = "classic" 20 | // MordorNetwork is mordon poe ethereum classic test network 21 | MordorNetwork = "mordor" 22 | // DevNetwork is local development network 23 | DevNetwork = "dev" 24 | ) 25 | 26 | // HexString is String in hexadecial format 27 | // +kubebuilder:validation:Pattern="^0[xX][0-9a-fA-F]+$" 28 | type HexString string 29 | 30 | // Hash is KECCAK-256 hash 31 | // +kubebuilder:validation:Pattern="^0[xX][0-9a-fA-F]{64}$" 32 | type Hash string 33 | -------------------------------------------------------------------------------- /config/samples/ethereum/ethereum_v1alpha1_poa_besu_node_no_genesis_no_clique.yaml: -------------------------------------------------------------------------------- 1 | # WARNING: DON'T use the following secrets in production 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: poa-besu-nodekey 6 | stringData: 7 | key: 608e9b6f67c65e47531e08e8e501386dfae63a540fa3c48802c8aad854510b4e 8 | --- 9 | apiVersion: ethereum.kotal.io/v1alpha1 10 | kind: Node 11 | metadata: 12 | name: poa-besu-node 13 | spec: 14 | ########### Genesis block spec ########### 15 | genesis: 16 | chainId: 20189 17 | networkId: 11 18 | clique: 19 | signers: 20 | - "0xd2c21213027cbf4d46c16b55fa98e5252b048706" 21 | accounts: 22 | - address: "0x48c5F25a884116d58A6287B72C9b069F936C9489" 23 | balance: "0xffffffffffffffffffff" 24 | ########### node spec ########### 25 | client: besu 26 | rpc: true 27 | nodePrivateKeySecretName: poa-besu-nodekey 28 | rpcPort: 8599 29 | corsDomains: 30 | - all 31 | hosts: 32 | - all 33 | rpcAPI: 34 | - web3 35 | - net 36 | - eth 37 | - clique 38 | resources: 39 | cpu: "1" 40 | cpuLimit: "1" 41 | memory: "1Gi" 42 | memoryLimit: "2Gi" 43 | -------------------------------------------------------------------------------- /config/samples/ethereum/ethereum_v1alpha1_poa_geth_node_no_genesis_no_clique.yaml: -------------------------------------------------------------------------------- 1 | # WARNING: DON'T use the following secrets in production 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: poa-besu-nodekey 6 | stringData: 7 | key: 608e9b6f67c65e47531e08e8e501386dfae63a540fa3c48802c8aad854510b4e 8 | --- 9 | apiVersion: ethereum.kotal.io/v1alpha1 10 | kind: Node 11 | metadata: 12 | name: poa-geth-node 13 | spec: 14 | ########### Genesis block spec ########### 15 | genesis: 16 | chainId: 20189 17 | networkId: 11 18 | clique: 19 | signers: 20 | - "0xd2c21213027cbf4d46c16b55fa98e5252b048706" 21 | accounts: 22 | - address: "0x48c5F25a884116d58A6287B72C9b069F936C9489" 23 | balance: "0xffffffffffffffffffff" 24 | ########### node spec ########### 25 | client: geth 26 | rpc: true 27 | nodePrivateKeySecretName: poa-besu-nodekey 28 | rpcPort: 8599 29 | corsDomains: 30 | - all 31 | hosts: 32 | - all 33 | rpcAPI: 34 | - web3 35 | - net 36 | - eth 37 | - clique 38 | resources: 39 | cpu: "1" 40 | cpuLimit: "1" 41 | memory: "1Gi" 42 | memoryLimit: "2Gi" 43 | -------------------------------------------------------------------------------- /clients/graph/graph_node_client.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | graphv1alpha1 "github.com/kotalco/kotal/apis/graph/v1alpha1" 5 | corev1 "k8s.io/api/core/v1" 6 | ) 7 | 8 | // GraphNodeClient is graph node client 9 | // https://github.com/graphprotocol/graph-node 10 | type GraphNodeClient struct { 11 | node *graphv1alpha1.Node 12 | } 13 | 14 | // Images 15 | const ( 16 | // GraphNodeHomeDir is Graph node image home dir 17 | // TODO: update home dir after creating a new docker image 18 | GraphNodeHomeDir = "/root" 19 | ) 20 | 21 | // Command returns environment variables for the client 22 | func (c *GraphNodeClient) Env() (env []corev1.EnvVar) { 23 | return 24 | } 25 | 26 | // Command is Graph node client entrypoint 27 | func (c *GraphNodeClient) Command() (command []string) { 28 | 29 | command = append(command, GraphNodeCommand) 30 | 31 | return 32 | } 33 | 34 | // Args returns Graph node client args 35 | func (c *GraphNodeClient) Args() (args []string) { 36 | _ = c.node 37 | 38 | return 39 | } 40 | 41 | // HomeDir is the home directory of Graph node client image 42 | func (c *GraphNodeClient) HomeDir() string { 43 | return GraphNodeHomeDir 44 | } 45 | -------------------------------------------------------------------------------- /config/samples/ethereum/ethereum_v1alpha1_goerli_nethermind_node.yaml: -------------------------------------------------------------------------------- 1 | # WARNING: DON'T use the following secrets in production 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: goerli-nethermind-nodekey 6 | stringData: 7 | key: 608e9b6f67c65e47531e08e8e501386dfae63a540fa3c48802c8aad854510b4e 8 | --- 9 | apiVersion: v1 10 | kind: Secret 11 | metadata: 12 | name: jwt-secret 13 | stringData: 14 | secret: fbe0c28a10274b27babf3c51e88a7435318e25fad4de877e5a63a67d0d65fdbb 15 | --- 16 | apiVersion: ethereum.kotal.io/v1alpha1 17 | kind: Node 18 | metadata: 19 | name: goerli-nethermind-node 20 | spec: 21 | network: goerli 22 | client: nethermind 23 | nodePrivateKeySecretName: goerli-nethermind-nodekey 24 | logging: info 25 | engine: true 26 | jwtSecretName: jwt-secret 27 | rpc: true 28 | rpcPort: 9999 29 | rpcAPI: 30 | - eth 31 | - admin 32 | p2pPort: 30304 33 | ws: true 34 | wsPort: 8888 35 | syncMode: fast 36 | staticNodes: 37 | - "enode://2281549869465d98e90cebc45e1d6834a01465a990add7bcf07a49287e7e66b50ca27f9c70a46190cef7ad746dd5d5b6b9dfee0c9954104c8e9bd0d42758ec58@10.5.0.2:30300" 38 | resources: 39 | cpu: "1" 40 | memory: "1Gi" 41 | -------------------------------------------------------------------------------- /controllers/shared/labels.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | "k8s.io/apimachinery/pkg/runtime/schema" 9 | ) 10 | 11 | type CustomResource interface { 12 | metav1.Object 13 | GroupVersionKind() schema.GroupVersionKind 14 | } 15 | 16 | // UpdateLabels adds missing labels to the resource 17 | func UpdateLabels(cr CustomResource, client, network string) { 18 | 19 | gvk := cr.GroupVersionKind() 20 | group := strings.Replace(gvk.Group, ".kotal.io", "", 1) 21 | kind := strings.ToLower(gvk.Kind) 22 | 23 | labels := cr.GetLabels() 24 | if labels == nil { 25 | labels = map[string]string{} 26 | } 27 | 28 | labels["app.kubernetes.io/name"] = client 29 | labels["app.kubernetes.io/instance"] = cr.GetName() 30 | labels["app.kubernetes.io/component"] = fmt.Sprintf("%s-%s", group, kind) 31 | labels["app.kubernetes.io/managed-by"] = "kotal-operator" 32 | labels["app.kubernetes.io/created-by"] = fmt.Sprintf("%s-%s-controller", group, kind) 33 | labels["kotal.io/protocol"] = group 34 | if network != "" { 35 | labels["kotal.io/network"] = network 36 | } 37 | 38 | cr.SetLabels(labels) 39 | 40 | } 41 | -------------------------------------------------------------------------------- /config/samples/ethereum/ethereum_v1alpha1_pow_besu_node_no_genesis_no_forks.yaml: -------------------------------------------------------------------------------- 1 | # WARNING: DON'T use the following secrets in production 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: pow-besu-nodekey 6 | stringData: 7 | # address 0x5A0b54D5dc17e0AadC383d2db43B0a0D3E029c4c 8 | key: 608e9b6f67c65e47531e08e8e501386dfae63a540fa3c48802c8aad854510b4e 9 | --- 10 | apiVersion: ethereum.kotal.io/v1alpha1 11 | kind: Node 12 | metadata: 13 | name: pow-besu-node 14 | spec: 15 | ########### Genesis block spec ########### 16 | genesis: 17 | chainId: 9999 18 | networkId: 11 19 | ethash: {} 20 | accounts: 21 | - address: "0x48c5F25a884116d58A6287B72C9b069F936C9489" 22 | balance: "0xffffffffffffffffffff" 23 | ########### node spec ########### 24 | client: besu 25 | miner: true 26 | coinbase: "0x5A0b54D5dc17e0AadC383d2db43B0a0D3E029c4c" 27 | nodePrivateKeySecretName: pow-besu-nodekey 28 | rpc: true 29 | rpcPort: 8599 30 | corsDomains: 31 | - all 32 | hosts: 33 | - all 34 | rpcAPI: 35 | - web3 36 | - net 37 | - eth 38 | resources: 39 | cpu: "1" 40 | cpuLimit: "1" 41 | memory: "1Gi" 42 | memoryLimit: "2Gi" 43 | -------------------------------------------------------------------------------- /config/samples/ethereum/ethereum_v1alpha1_poa_besu_node_no_genesis.yaml: -------------------------------------------------------------------------------- 1 | # WARNING: DON'T use the following secrets in production 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: poa-besu-nodekey 6 | stringData: 7 | key: 608e9b6f67c65e47531e08e8e501386dfae63a540fa3c48802c8aad854510b4e 8 | --- 9 | apiVersion: ethereum.kotal.io/v1alpha1 10 | kind: Node 11 | metadata: 12 | name: poa-besu-node 13 | spec: 14 | ########### Genesis block spec ########### 15 | genesis: 16 | chainId: 20189 17 | networkId: 11 18 | clique: 19 | blockPeriod: 15 20 | epochLength: 100 21 | signers: 22 | - "0xd2c21213027cbf4d46c16b55fa98e5252b048706" 23 | accounts: 24 | - address: "0x48c5F25a884116d58A6287B72C9b069F936C9489" 25 | balance: "0xffffffffffffffffffff" 26 | ########### network nodes spec ########### 27 | client: besu 28 | rpc: true 29 | nodePrivateKeySecretName: poa-besu-nodekey 30 | rpcPort: 8599 31 | corsDomains: 32 | - all 33 | hosts: 34 | - all 35 | rpcAPI: 36 | - web3 37 | - net 38 | - eth 39 | - clique 40 | resources: 41 | cpu: "1" 42 | cpuLimit: "1" 43 | memory: "1Gi" 44 | memoryLimit: "2Gi" 45 | -------------------------------------------------------------------------------- /config/default/manager_auth_proxy_patch.yaml: -------------------------------------------------------------------------------- 1 | # This patch inject a sidecar container which is a HTTP proxy for the 2 | # controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews. 3 | apiVersion: apps/v1 4 | kind: Deployment 5 | metadata: 6 | name: controller-manager 7 | namespace: system 8 | spec: 9 | template: 10 | spec: 11 | containers: 12 | - name: kube-rbac-proxy 13 | image: gcr.io/kubebuilder/kube-rbac-proxy:v0.11.0 14 | args: 15 | - "--secure-listen-address=0.0.0.0:8443" 16 | - "--upstream=http://127.0.0.1:8080/" 17 | - "--logtostderr=true" 18 | - "--v=0" 19 | ports: 20 | - containerPort: 8443 21 | protocol: TCP 22 | name: https 23 | resources: 24 | limits: 25 | cpu: 500m 26 | memory: 128Mi 27 | requests: 28 | cpu: 5m 29 | memory: 64Mi 30 | - name: manager 31 | args: 32 | - "--health-probe-bind-address=:8081" 33 | - "--metrics-bind-address=127.0.0.1:8080" 34 | - "--leader-elect" 35 | -------------------------------------------------------------------------------- /config/samples/ethereum/ethereum_v1alpha1_ibft2_besu_node_no_forks.yaml: -------------------------------------------------------------------------------- 1 | # WARNING: DON'T use the following secrets in production 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: ibft2-besu-nodekey 6 | stringData: 7 | key: 608e9b6f67c65e47531e08e8e501386dfae63a540fa3c48802c8aad854510b4e 8 | --- 9 | apiVersion: ethereum.kotal.io/v1alpha1 10 | kind: Node 11 | metadata: 12 | name: ibft2-besu-node 13 | spec: 14 | ########### Genesis block spec ########### 15 | genesis: 16 | chainId: 20189 17 | networkId: 11 18 | ibft2: 19 | validators: 20 | - "0x427e2c7cecd72bc4cdd4f7ebb8bb6e49789c8044" 21 | - "0xd2c21213027cbf4d46c16b55fa98e5252b048706" 22 | - "0x8e1f6c7c76a1d7f74eda342d330ca9749f31cc2b" 23 | accounts: 24 | - address: "0x48c5F25a884116d58A6287B72C9b069F936C9489" 25 | balance: "0xffffffffffffffffffff" 26 | ########### node spec ########### 27 | client: besu 28 | rpc: true 29 | nodePrivateKeySecretName: ibft2-besu-nodekey 30 | rpcPort: 8599 31 | corsDomains: 32 | - all 33 | hosts: 34 | - all 35 | rpcAPI: 36 | - web3 37 | - net 38 | - eth 39 | - ibft 40 | resources: 41 | cpu: "1" 42 | memory: "1Gi" 43 | -------------------------------------------------------------------------------- /apis/aptos/v1alpha1/node_defaulting_webhook_test.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo/v2" 5 | . "github.com/onsi/gomega" 6 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 7 | ) 8 | 9 | var _ = Describe("Aptos node defaulting", func() { 10 | It("Should default Aptos node", func() { 11 | node := Node{ 12 | ObjectMeta: metav1.ObjectMeta{}, 13 | Spec: NodeSpec{ 14 | Network: Devnet, 15 | Validator: true, 16 | }, 17 | } 18 | // TODO: create a test for full node, p2p port 19 | 20 | node.Default() 21 | 22 | Expect(node.Spec.Image).To(Equal(DefaultAptosCoreImage)) 23 | Expect(*node.Spec.Replicas).To(Equal(DefaltReplicas)) 24 | Expect(node.Spec.CPU).To(Equal(DefaultNodeCPURequest)) 25 | Expect(node.Spec.CPULimit).To(Equal(DefaultNodeCPULimit)) 26 | Expect(node.Spec.Memory).To(Equal(DefaultNodeMemoryRequest)) 27 | Expect(node.Spec.MemoryLimit).To(Equal(DefaultNodeMemoryLimit)) 28 | Expect(node.Spec.Storage).To(Equal(DefaultNodeStorageRequest)) 29 | Expect(node.Spec.APIPort).To(Equal(DefaultAPIPort)) 30 | Expect(node.Spec.MetricsPort).To(Equal(DefaultMetricsPort)) 31 | Expect(node.Spec.P2PPort).To(Equal(DefaultValidatorP2PPort)) 32 | 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /apis/ethereum2/v1alpha1/validator_defaulting_webhook_test.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | "github.com/kotalco/kotal/apis/shared" 5 | . "github.com/onsi/ginkgo/v2" 6 | . "github.com/onsi/gomega" 7 | ) 8 | 9 | var _ = Describe("Ethereum 2.0 validator client defaulting", func() { 10 | 11 | It("Should default validator client with missing client, graffiti, and resources", func() { 12 | node := Validator{ 13 | Spec: ValidatorSpec{ 14 | Network: "mainnet", 15 | Client: TekuClient, 16 | }, 17 | } 18 | node.Default() 19 | Expect(node.Spec.Image).To(Equal(DefaultTekuValidatorImage)) 20 | Expect(*node.Spec.Replicas).To(Equal(DefaltReplicas)) 21 | Expect(node.Spec.Graffiti).To(Equal(DefaultGraffiti)) 22 | Expect(node.Spec.FeeRecipient).To(Equal(shared.EthereumAddress(ZeroAddress))) 23 | Expect(node.Spec.Logging).To(Equal(DefaultLogging)) 24 | Expect(node.Spec.Resources.CPU).To(Equal(DefaultCPURequest)) 25 | Expect(node.Spec.Resources.CPULimit).To(Equal(DefaultCPULimit)) 26 | Expect(node.Spec.Resources.Memory).To(Equal(DefaultMemoryRequest)) 27 | Expect(node.Spec.Resources.MemoryLimit).To(Equal(DefaultMemoryLimit)) 28 | Expect(node.Spec.Resources.Storage).To(Equal(DefaultStorage)) 29 | }) 30 | 31 | }) 32 | -------------------------------------------------------------------------------- /config/samples/ethereum/ethereum_v1alpha1_pow_geth_node_no_genesis_no_forks.yaml: -------------------------------------------------------------------------------- 1 | # WARNING: DON'T use the following secrets in production 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: pow-geth-account-key 6 | stringData: 7 | # address 0x2b3430337f12Ce89EaBC7b0d865F4253c7744c0d 8 | key: 5df5eff7ef9e4e82739b68a34c6b23608d79ee8daf3b598a01ffb0dd7aa3a2fd 9 | --- 10 | apiVersion: v1 11 | kind: Secret 12 | metadata: 13 | name: pow-geth-account-password 14 | stringData: 15 | password: secret 16 | --- 17 | apiVersion: ethereum.kotal.io/v1alpha1 18 | kind: Node 19 | metadata: 20 | name: pow-geth-node 21 | spec: 22 | ########### Genesis block spec ########### 23 | genesis: 24 | chainId: 9999 25 | networkId: 11 26 | ethash: {} 27 | accounts: 28 | - address: "0x48c5F25a884116d58A6287B72C9b069F936C9489" 29 | balance: "0xffffffffffffffffffff" 30 | ########### node spec ########### 31 | client: geth 32 | miner: true 33 | coinbase: "0x2b3430337f12Ce89EaBC7b0d865F4253c7744c0d" 34 | import: 35 | privateKeySecretName: pow-geth-account-key 36 | passwordSecretName: pow-geth-account-password 37 | resources: 38 | cpu: "1" 39 | cpuLimit: "1" 40 | memory: "1Gi" 41 | memoryLimit: "2Gi" 42 | -------------------------------------------------------------------------------- /.github/workflows/docker.yml: -------------------------------------------------------------------------------- 1 | name: Build-Push kotal 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | tags: 7 | - 'v*' 8 | pull_request: 9 | branches: [ master ] 10 | 11 | jobs: 12 | 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | 20 | - name: Generate Docker metadata 21 | id: meta 22 | uses: docker/metadata-action@v3 23 | with: 24 | images: | 25 | docker.io/kotalco/kotal 26 | tags: | 27 | type=ref,event=tag 28 | type=sha,prefix=,suffix=,format=short 29 | flavor: | 30 | latest=true 31 | 32 | 33 | - name: Set up QEMU 34 | uses: docker/setup-qemu-action@v2 35 | 36 | - name: Set up Docker Buildx 37 | uses: docker/setup-buildx-action@v1 38 | 39 | - name: Login to DockerHub 40 | uses: docker/login-action@v2 41 | with: 42 | username: kotalco 43 | password: ${{ secrets.DOCKERHUB_PASSWORD }} 44 | 45 | - name: Build and push 46 | uses: docker/build-push-action@v3 47 | with: 48 | context: . 49 | push: true 50 | tags: ${{ steps.meta.outputs.tags }} 51 | labels: ${{ steps.meta.outputs.labels }} -------------------------------------------------------------------------------- /apis/near/v1alpha1/node_defaulting_webhook_test.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo/v2" 5 | . "github.com/onsi/gomega" 6 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 7 | ) 8 | 9 | var _ = Describe("NEAR node defaulting", func() { 10 | It("Should default NEAR node", func() { 11 | node := Node{ 12 | ObjectMeta: metav1.ObjectMeta{}, 13 | Spec: NodeSpec{ 14 | Network: "mainnet", 15 | RPC: true, 16 | }, 17 | } 18 | 19 | node.Default() 20 | 21 | Expect(node.Spec.Image).To(Equal(DefaultNearImage)) 22 | Expect(*node.Spec.Replicas).To(Equal(DefaltReplicas)) 23 | Expect(node.Spec.RPCPort).To(Equal(DefaultRPCPort)) 24 | Expect(node.Spec.P2PPort).To(Equal(DefaultP2PPort)) 25 | Expect(node.Spec.MinPeers).To(Equal(DefaultMinPeers)) 26 | Expect(node.Spec.PrometheusPort).To(Equal(DefaultPrometheusPort)) 27 | 28 | Expect(node.Spec.Resources.CPU).To(Equal(DefaultNodeCPURequest)) 29 | Expect(node.Spec.Resources.CPULimit).To(Equal(DefaultNodeCPULimit)) 30 | Expect(node.Spec.Resources.Memory).To(Equal(DefaultNodeMemoryRequest)) 31 | Expect(node.Spec.Resources.MemoryLimit).To(Equal(DefaultNodeMemoryLimit)) 32 | Expect(node.Spec.Resources.Storage).To(Equal(DefaultNodeStorageRequest)) 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /apis/aptos/v1alpha1/defaults.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | // Resources 4 | const ( 5 | // DefaultNodeCPURequest is the cpu requested by Aptos node 6 | DefaultNodeCPURequest = "2" 7 | // DefaultNodeCPULimit is the cpu limit for Aptos node 8 | DefaultNodeCPULimit = "4" 9 | 10 | // DefaultNodeMemoryRequest is the memory requested by Aptos node 11 | DefaultNodeMemoryRequest = "4Gi" 12 | // DefaultNodeMemoryLimit is the memory limit for Aptos node 13 | DefaultNodeMemoryLimit = "8Gi" 14 | 15 | // DefaultNodeStorageRequest is the Storage requested by Aptos node 16 | DefaultNodeStorageRequest = "250Gi" 17 | ) 18 | 19 | const ( 20 | // DefaultAptosCoreImage is the default Aptos core client image 21 | DefaultAptosCoreImage = "aptoslabs/validator:aptos-node-v1.11.2" 22 | ) 23 | 24 | const ( 25 | // DefaltReplicas is the default replicas 26 | DefaltReplicas uint = 1 27 | // DefaultMetricsPort is the default metrics server port 28 | DefaultMetricsPort uint = 9101 29 | // DefaultAPIPort is the default API server port 30 | DefaultAPIPort uint = 8080 31 | // DefaultFullnodeP2PPort is the default full node p2p port 32 | DefaultFullnodeP2PPort uint = 6182 33 | // DefaultValidatorP2PPort is the default validator node p2p port 34 | DefaultValidatorP2PPort uint = 6180 35 | ) 36 | -------------------------------------------------------------------------------- /apis/chainlink/v1alpha1/defaults.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | const ( 4 | // DefaltReplicas is the default replicas 5 | DefaltReplicas uint = 1 6 | // DefaultAPIPort is the default API and GUI port 7 | DefaultAPIPort uint = 6688 8 | // DefaultTLSPort is the default tls port 9 | DefaultTLSPort uint = 6689 10 | // DefaultP2PPort is the default p2p port 11 | DefaultP2PPort uint = 30303 12 | ) 13 | 14 | var ( 15 | // DefaultCorsDomains is the default cors domains from which to accept requests 16 | DefaultCorsDomains = []string{"*"} 17 | ) 18 | 19 | const ( 20 | // DefaultChainlinkImage is the default chainlink client image 21 | DefaultChainlinkImage = "kotalco/chainlink:v2.9.1" 22 | ) 23 | 24 | // Resources 25 | const ( 26 | // DefaultNodeCPURequest is the cpu requested by chainlink node 27 | DefaultNodeCPURequest = "2" 28 | // DefaultNodeCPULimit is the cpu limit for chainlink node 29 | DefaultNodeCPULimit = "4" 30 | 31 | // DefaultNodeMemoryRequest is the memory requested by chainlink node 32 | DefaultNodeMemoryRequest = "2Gi" 33 | // DefaultNodeMemoryLimit is the memory limit for chainlink node 34 | DefaultNodeMemoryLimit = "4Gi" 35 | 36 | // DefaultNodeStorageRequest is the Storage requested by chainlink node 37 | DefaultNodeStorageRequest = "20Gi" 38 | ) 39 | -------------------------------------------------------------------------------- /apis/bitcoin/v1alpha1/node_defaulting_webhook_test.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo/v2" 5 | . "github.com/onsi/gomega" 6 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 7 | ) 8 | 9 | var _ = Describe("Bitcoin node defaulting", func() { 10 | It("Should default Bitcoin node", func() { 11 | node := Node{ 12 | ObjectMeta: metav1.ObjectMeta{}, 13 | Spec: NodeSpec{ 14 | Network: Mainnet, 15 | }, 16 | } 17 | 18 | node.Default() 19 | 20 | Expect(node.Spec.Image).To(Equal(DefaultBitcoinCoreImage)) 21 | Expect(node.Spec.P2PPort).To(Equal(DefaultMainnetP2PPort)) 22 | Expect(*node.Spec.Listen).To(Equal(DefaultListen)) 23 | Expect(*node.Spec.MaxConnections).To(Equal(DefaultMaxConnections)) 24 | Expect(node.Spec.DBCacheSize).To(Equal(DefaultDBCacheSize)) 25 | Expect(node.Spec.RPCPort).To(Equal(DefaultMainnetRPCPort)) 26 | Expect(*node.Spec.Replicas).To(Equal(DefaltReplicas)) 27 | Expect(node.Spec.CPU).To(Equal(DefaultNodeCPURequest)) 28 | Expect(node.Spec.CPULimit).To(Equal(DefaultNodeCPULimit)) 29 | Expect(node.Spec.Memory).To(Equal(DefaultNodeMemoryRequest)) 30 | Expect(node.Spec.MemoryLimit).To(Equal(DefaultNodeMemoryLimit)) 31 | Expect(node.Spec.Storage).To(Equal(DefaultNodeStorageRequest)) 32 | 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /apis/ipfs/v1alpha1/peer_defaulting_webhook_test.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo/v2" 5 | . "github.com/onsi/gomega" 6 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 7 | ) 8 | 9 | var _ = Describe("IPFS peer defaulting", func() { 10 | It("Should default ipfs peer", func() { 11 | peer := Peer{ 12 | ObjectMeta: metav1.ObjectMeta{}, 13 | Spec: PeerSpec{}, 14 | } 15 | 16 | peer.Default() 17 | 18 | Expect(peer.Spec.Image).To(Equal(DefaultGoIPFSImage)) 19 | Expect(*peer.Spec.Replicas).To(Equal(DefaltReplicas)) 20 | Expect(peer.Spec.Logging).To(Equal(DefaultLogging)) 21 | Expect(peer.Spec.InitProfiles).To(ContainElements(DefaultDatastoreProfile)) 22 | Expect(peer.Spec.APIPort).To(Equal(DefaultAPIPort)) 23 | Expect(peer.Spec.GatewayPort).To(Equal(DefaultGatewayPort)) 24 | Expect(peer.Spec.Routing).To(Equal(DefaultRoutingMode)) 25 | Expect(peer.Spec.Resources.CPU).To(Equal(DefaultNodeCPURequest)) 26 | Expect(peer.Spec.Resources.CPULimit).To(Equal(DefaultNodeCPULimit)) 27 | Expect(peer.Spec.Resources.Memory).To(Equal(DefaultNodeMemoryRequest)) 28 | Expect(peer.Spec.Resources.MemoryLimit).To(Equal(DefaultNodeMemoryLimit)) 29 | Expect(peer.Spec.Resources.Storage).To(Equal(DefaultNodeStorageRequest)) 30 | }) 31 | }) 32 | -------------------------------------------------------------------------------- /clients/filecoin/lotus_client.go: -------------------------------------------------------------------------------- 1 | package filecoin 2 | 3 | import ( 4 | filecoinv1alpha1 "github.com/kotalco/kotal/apis/filecoin/v1alpha1" 5 | "github.com/kotalco/kotal/controllers/shared" 6 | corev1 "k8s.io/api/core/v1" 7 | ) 8 | 9 | // LotusClient is lotus filecoin client 10 | // https://github.com/filecoin-project/lotus 11 | type LotusClient struct { 12 | node *filecoinv1alpha1.Node 13 | } 14 | 15 | // Images 16 | const ( 17 | // LotusHomeDir is lotus client image home dir 18 | LotusHomeDir = "/home/fc" 19 | ) 20 | 21 | // Command is lotus image command 22 | func (c *LotusClient) Command() (command []string) { 23 | command = append(command, "lotus", "daemon") 24 | return 25 | } 26 | 27 | // Command returns environment variables for the client 28 | func (c *LotusClient) Env() []corev1.EnvVar { 29 | return []corev1.EnvVar{ 30 | { 31 | Name: EnvLotusPath, 32 | Value: shared.PathData(c.HomeDir()), 33 | }, 34 | { 35 | Name: EnvLogLevel, 36 | Value: string(c.node.Spec.Logging), 37 | }, 38 | } 39 | } 40 | 41 | // Args returns lotus client args from node spec 42 | func (c *LotusClient) Args() []string { 43 | return nil 44 | } 45 | 46 | // HomeDir returns lotus image home directory 47 | func (c *LotusClient) HomeDir() string { 48 | return LotusHomeDir 49 | } 50 | -------------------------------------------------------------------------------- /clients/stacks/stacks_node_client_test.go: -------------------------------------------------------------------------------- 1 | package stacks 2 | 3 | import ( 4 | "fmt" 5 | 6 | stacksv1alpha1 "github.com/kotalco/kotal/apis/stacks/v1alpha1" 7 | "github.com/kotalco/kotal/controllers/shared" 8 | . "github.com/onsi/ginkgo/v2" 9 | . "github.com/onsi/gomega" 10 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 11 | ) 12 | 13 | var _ = Describe("Stacks node client", func() { 14 | 15 | node := &stacksv1alpha1.Node{ 16 | ObjectMeta: metav1.ObjectMeta{ 17 | Name: "stacks-node", 18 | Namespace: "default", 19 | }, 20 | Spec: stacksv1alpha1.NodeSpec{ 21 | Network: "mainnet", 22 | }, 23 | } 24 | 25 | node.Default() 26 | 27 | client := NewClient(node) 28 | 29 | It("Should get correct command", func() { 30 | Expect(client.Command()).To(Equal( 31 | []string{ 32 | StacksNodeCommand, 33 | StacksStartCommand, 34 | }, 35 | )) 36 | }) 37 | 38 | It("Should get correct home directory", func() { 39 | Expect(client.HomeDir()).To(Equal(StacksNodeHomeDir)) 40 | }) 41 | 42 | It("Should generate correct client arguments", func() { 43 | Expect(client.Args()).To(ContainElements( 44 | []string{ 45 | StacksArgConfig, 46 | fmt.Sprintf("%s/config.toml", shared.PathConfig(client.HomeDir())), 47 | }, 48 | )) 49 | }) 50 | 51 | }) 52 | -------------------------------------------------------------------------------- /config/samples/near/near_v1alpha1_node.yaml: -------------------------------------------------------------------------------- 1 | # WARNING: DON'T use the following node key in production 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: my-node-key 6 | stringData: 7 | key: |- 8 | { 9 | "account_id": "node", 10 | "public_key": "ed25519:xLKUX6m2yyPWrzwqMRiCMNxcNMyo3JwBMUbS8XuDYYg", 11 | "secret_key": "ed25519:umba2vvWKENdjjfkNSdMHA6Hjs6w8Jh5EwTrgFut4SFrQ87bM3j3HZsbhWiVdUgu3BPmEE57XSRML996YrdEXda" 12 | } 13 | --- 14 | # WARNING: DON'T use the following validator key in production 15 | apiVersion: v1 16 | kind: Secret 17 | metadata: 18 | name: validator-key 19 | stringData: 20 | key: |- 21 | { 22 | "account_id": "validator", 23 | "public_key": "ed25519:CdTBLXMmu9gzoCbNKdUnej1tJNMz5tzRjJF2a9DwAgUr", 24 | "secret_key": "ed25519:4k83DwbSpD3zzai4ZPdeRJcfXttU3Uq68mWWhni6ra2RKnG3jyVKEZyP14gDJZ9W1oqFujpAkidoNrYY4TLqijsG" 25 | } 26 | --- 27 | apiVersion: near.kotal.io/v1alpha1 28 | kind: Node 29 | metadata: 30 | name: near-node 31 | spec: 32 | network: mainnet 33 | nodePrivateKeySecretName: my-node-key 34 | validatorSecretName: validator-key 35 | minPeers: 3 36 | archive: true 37 | rpc: true 38 | rpcPort: 8888 39 | prometheusPort: 9999 40 | telemetryURL: "https://explorer.mainnet.near.org/api/nodes" 41 | -------------------------------------------------------------------------------- /clients/ipfs/kubo_client.go: -------------------------------------------------------------------------------- 1 | package ipfs 2 | 3 | import ( 4 | ipfsv1alpha1 "github.com/kotalco/kotal/apis/ipfs/v1alpha1" 5 | "github.com/kotalco/kotal/controllers/shared" 6 | corev1 "k8s.io/api/core/v1" 7 | ) 8 | 9 | // KuboClient is an ipfs implementation in golang 10 | // https://github.com/ipfs/kubo 11 | type KuboClient struct { 12 | peer *ipfsv1alpha1.Peer 13 | } 14 | 15 | // Images 16 | const ( 17 | // GoIPFSHomeDir is go ipfs image home dir 18 | GoIPFSHomeDir = "/home/ipfs" 19 | ) 20 | 21 | // Command is kubo entrypoint 22 | func (c *KuboClient) Command() []string { 23 | return []string{"ipfs"} 24 | } 25 | 26 | // Command returns environment variables for the client 27 | func (c *KuboClient) Env() []corev1.EnvVar { 28 | return []corev1.EnvVar{ 29 | { 30 | Name: EnvIPFSPath, 31 | Value: shared.PathData(c.HomeDir()), 32 | }, 33 | { 34 | Name: EnvIPFSLogging, 35 | Value: string(c.peer.Spec.Logging), 36 | }, 37 | } 38 | } 39 | 40 | // Args returns kubo args 41 | func (c *KuboClient) Args() (args []string) { 42 | 43 | peer := c.peer 44 | 45 | args = append(args, GoIPFSDaemonArg) 46 | 47 | args = append(args, GoIPFSRoutingArg, string(peer.Spec.Routing)) 48 | 49 | return 50 | } 51 | 52 | func (c *KuboClient) HomeDir() string { 53 | return GoIPFSHomeDir 54 | } 55 | -------------------------------------------------------------------------------- /clients/ipfs/kubo_client_test.go: -------------------------------------------------------------------------------- 1 | package ipfs 2 | 3 | import ( 4 | ipfsv1alpha1 "github.com/kotalco/kotal/apis/ipfs/v1alpha1" 5 | "github.com/kotalco/kotal/controllers/shared" 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | corev1 "k8s.io/api/core/v1" 9 | ) 10 | 11 | var _ = Describe("Go IPFS Client", func() { 12 | peer := &ipfsv1alpha1.Peer{ 13 | Spec: ipfsv1alpha1.PeerSpec{ 14 | Routing: ipfsv1alpha1.DHTClientRouting, 15 | }, 16 | } 17 | 18 | client, _ := NewClient(peer) 19 | 20 | It("Should get correct command", func() { 21 | Expect(client.Command()).To(ConsistOf("ipfs")) 22 | }) 23 | 24 | It("Should get correct env", func() { 25 | Expect(client.Env()).To(Equal( 26 | []corev1.EnvVar{ 27 | { 28 | Name: EnvIPFSPath, 29 | Value: shared.PathData(client.HomeDir()), 30 | }, 31 | { 32 | Name: EnvIPFSLogging, 33 | Value: string(peer.Spec.Logging), 34 | }, 35 | }, 36 | )) 37 | }) 38 | 39 | It("Should get correct home dir", func() { 40 | Expect(client.HomeDir()).To(Equal(GoIPFSHomeDir)) 41 | }) 42 | 43 | It("Should get correct args", func() { 44 | Expect(client.Args()).To(ContainElements( 45 | GoIPFSDaemonArg, 46 | GoIPFSRoutingArg, 47 | string(ipfsv1alpha1.DHTClientRouting), 48 | )) 49 | }) 50 | 51 | }) 52 | -------------------------------------------------------------------------------- /clients/aptos/aptos_core_client_test.go: -------------------------------------------------------------------------------- 1 | package aptos 2 | 3 | import ( 4 | "fmt" 5 | 6 | aptosv1alpha1 "github.com/kotalco/kotal/apis/aptos/v1alpha1" 7 | "github.com/kotalco/kotal/controllers/shared" 8 | . "github.com/onsi/ginkgo/v2" 9 | . "github.com/onsi/gomega" 10 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 11 | ) 12 | 13 | var _ = Describe("Aptos core client", func() { 14 | 15 | node := &aptosv1alpha1.Node{ 16 | ObjectMeta: metav1.ObjectMeta{ 17 | Name: "aptos-node", 18 | Namespace: "default", 19 | }, 20 | Spec: aptosv1alpha1.NodeSpec{ 21 | Network: aptosv1alpha1.Testnet, 22 | }, 23 | } 24 | 25 | node.Default() 26 | 27 | client := NewClient(node) 28 | 29 | It("Should get correct command", func() { 30 | Expect(client.Command()).To(ConsistOf("aptos-node")) 31 | }) 32 | 33 | It("Should get correct environment variables", func() { 34 | Expect(client.Env()).To(BeNil()) 35 | }) 36 | 37 | It("Should get correct home directory", func() { 38 | Expect(client.HomeDir()).To(Equal(AptosCoreHomeDir)) 39 | }) 40 | 41 | It("Should generate correct client arguments", func() { 42 | 43 | Expect(client.Args()).To(ContainElements([]string{ 44 | AptosArgConfig, 45 | fmt.Sprintf("%s/config.yaml", shared.PathConfig(client.HomeDir())), 46 | })) 47 | }) 48 | 49 | }) 50 | -------------------------------------------------------------------------------- /config/samples/ipfs/ipfs_v1alpha1_cluster_peer.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: ipfs.kotal.io/v1alpha1 2 | kind: ClusterPeer 3 | metadata: 4 | name: cluster-peer 5 | spec: 6 | # the follwing and has been created using ipfs-key utility 7 | # https://github.com/whyrusleeping/ipfs-key 8 | # ipfs-key -type ed25519 | base64 9 | id: "12D3KooWBcEtY8GH4mNkri9kM3haeWhEXtQV7mi81ErWrqLYGuiq" 10 | # WARNING: DON'T use the following private key in production 11 | # KEY=CAESQOH/DvUJmeJ9z6m3wAStpkrlBwJQxIyNSK0YGf0EI5ZRGpwsWxl4wmgReqmHl8LQjTC2iPM0QbYAjeY3Z63AFnI= 12 | # kubectl create secret generic my-cluster-privatekey --from-literal=key=$KEY 13 | privateKeySecretName: my-cluster-privatekey 14 | consensus: crdt 15 | trustedPeers: 16 | - 12D3KooWBcEtY8GH4mNkri9kM3haeWhEXtQV7mi81ErWrqLYGuiq 17 | - 12D3KooWQ9yZnqowEDme3gSgS45KY9ZoEmAiGYRxusdEaqtFa9pr 18 | # can be obtained by deploying bare-peer sample 19 | # kubectl apply -f ipfs_v1alpha1_bare_peer.yaml 20 | peerEndpoint: /dns4/bare-peer/tcp/5001 21 | # WARNING: DON'T use the following cluster secret in production 22 | # CLUSTER_SECRET=$(openssl rand -hex 32) 23 | # kubectl create secret generic cluster-secret --from-literal=secret=$CLUSTER_SECRET 24 | clusterSecretName: cluster-secret 25 | logging: info 26 | resources: 27 | cpu: "1" 28 | memory: "1Gi" 29 | -------------------------------------------------------------------------------- /clients/aptos/aptos_core_client.go: -------------------------------------------------------------------------------- 1 | package aptos 2 | 3 | import ( 4 | "fmt" 5 | 6 | aptosv1alpha1 "github.com/kotalco/kotal/apis/aptos/v1alpha1" 7 | "github.com/kotalco/kotal/controllers/shared" 8 | corev1 "k8s.io/api/core/v1" 9 | ) 10 | 11 | // AptosCoreClient is Aptos core client 12 | // https://github.com/aptos-labs/aptos-core 13 | type AptosCoreClient struct { 14 | node *aptosv1alpha1.Node 15 | } 16 | 17 | // Images 18 | const ( 19 | // AptosCoreHomeDir is Aptos Core image home dir 20 | // TODO: create aptos image with non root user and /home/aptos home directory 21 | AptosCoreHomeDir = "/opt/aptos" 22 | ) 23 | 24 | // Command returns environment variables for the client 25 | func (c *AptosCoreClient) Env() (env []corev1.EnvVar) { 26 | return 27 | } 28 | 29 | // Command is Aptos Core client entrypoint 30 | func (c *AptosCoreClient) Command() (command []string) { 31 | command = append(command, "aptos-node") 32 | return 33 | } 34 | 35 | // Args returns Aptos Core client args 36 | func (c *AptosCoreClient) Args() (args []string) { 37 | configPath := fmt.Sprintf("%s/config.yaml", shared.PathConfig(c.HomeDir())) 38 | args = append(args, AptosArgConfig, configPath) 39 | return 40 | } 41 | 42 | // HomeDir is the home directory of Aptos Core client image 43 | func (c *AptosCoreClient) HomeDir() string { 44 | return AptosCoreHomeDir 45 | } 46 | -------------------------------------------------------------------------------- /helpers/node.go: -------------------------------------------------------------------------------- 1 | package helpers 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | "errors" 6 | "github.com/ethereum/go-ethereum/common/hexutil" 7 | "github.com/ethereum/go-ethereum/crypto" 8 | ) 9 | 10 | func derive(fromPrivateKey string) (publicKeyECDSA *ecdsa.PublicKey, err error) { 11 | // private key 12 | privateKey, err := crypto.HexToECDSA(fromPrivateKey) 13 | if err != nil { 14 | return 15 | } 16 | 17 | // public key 18 | publicKey := privateKey.Public() 19 | publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) 20 | if !ok { 21 | err = errors.New("publicKey is not of type *ecdsa.PublicKey") 22 | return 23 | } 24 | 25 | return 26 | } 27 | 28 | // DerivePublicKey drives node public key from private key 29 | func DerivePublicKey(fromPrivateKey string) (publicKeyHex string, err error) { 30 | publicKeyECDSA, err := derive(fromPrivateKey) 31 | if err != nil { 32 | return 33 | } 34 | 35 | publicKeyBytes := crypto.FromECDSAPub(publicKeyECDSA) 36 | publicKeyHex = hexutil.Encode(publicKeyBytes)[4:] 37 | 38 | return 39 | 40 | } 41 | 42 | // DeriveAddress drives ethereum address from private key 43 | func DeriveAddress(fromPrivateKey string) (addressHex string, err error) { 44 | publicKeyECDSA, err := derive(fromPrivateKey) 45 | if err != nil { 46 | return 47 | } 48 | 49 | addressHex = crypto.PubkeyToAddress(*publicKeyECDSA).Hex() 50 | return 51 | 52 | } 53 | -------------------------------------------------------------------------------- /apis/near/v1alpha1/defaults.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | const ( 4 | // DefaltReplicas is the default replicas 5 | DefaltReplicas uint = 1 6 | // DefaultRPCPort is the default JSON-RPC port 7 | DefaultRPCPort uint = 3030 8 | // DefaultP2PPort is the default p2p port 9 | DefaultP2PPort uint = 24567 10 | // DefaultMinPeers is the default minimum number of peers required to start syncing/producing blocks 11 | DefaultMinPeers uint = 5 12 | // DefaultPrometheusPort is the default prometheus exporter port 13 | DefaultPrometheusPort uint = 9615 14 | ) 15 | 16 | const ( 17 | // DefaultNearImage is the default NEAR core client image 18 | DefaultNearImage = "kotalco/nearcore:v1.39.1" 19 | ) 20 | 21 | // Resources 22 | const ( 23 | // DefaultNodeCPURequest is the cpu requested by NEAR node 24 | DefaultNodeCPURequest = "4" 25 | // DefaultNodeCPULimit is the cpu limit for NEAR node 26 | DefaultNodeCPULimit = "8" 27 | 28 | // DefaultNodeMemoryRequest is the memory requested by NEAR node 29 | DefaultNodeMemoryRequest = "4Gi" 30 | // DefaultNodeMemoryLimit is the memory limit for NEAR node 31 | DefaultNodeMemoryLimit = "8Gi" 32 | 33 | // DefaultNodeStorageRequest is the Storage requested by NEAR node 34 | DefaultNodeStorageRequest = "250Gi" 35 | // DefaultArchivalNodeStorageRequest is the Storage requested by NEAR archival node 36 | DefaultArchivalNodeStorageRequest = "4Ti" 37 | ) 38 | -------------------------------------------------------------------------------- /clients/stacks/stacks_node_client.go: -------------------------------------------------------------------------------- 1 | package stacks 2 | 3 | import ( 4 | "fmt" 5 | 6 | stacksv1alpha1 "github.com/kotalco/kotal/apis/stacks/v1alpha1" 7 | "github.com/kotalco/kotal/controllers/shared" 8 | corev1 "k8s.io/api/core/v1" 9 | ) 10 | 11 | // StacksNodeClient is Stacks blockchain node client 12 | // https://github.com/stacks-network/stacks-blockchain 13 | type StacksNodeClient struct { 14 | node *stacksv1alpha1.Node 15 | } 16 | 17 | // Images 18 | const ( 19 | // StacksNodeHomeDir is Stacks node image home dir 20 | // TODO: update home dir after creating a new docker image 21 | StacksNodeHomeDir = "/home/stacks" 22 | ) 23 | 24 | // Command returns environment variables for the client 25 | func (c *StacksNodeClient) Env() (env []corev1.EnvVar) { 26 | return 27 | } 28 | 29 | // Command is Stacks node client entrypoint 30 | func (c *StacksNodeClient) Command() (command []string) { 31 | 32 | command = append(command, StacksNodeCommand, StacksStartCommand) 33 | 34 | return 35 | } 36 | 37 | // Args returns Stacks node client args 38 | func (c *StacksNodeClient) Args() (args []string) { 39 | _ = c.node 40 | 41 | args = append(args, StacksArgConfig, fmt.Sprintf("%s/config.toml", shared.PathConfig(c.HomeDir()))) 42 | 43 | return 44 | } 45 | 46 | // HomeDir is the home directory of Stacks node client image 47 | func (c *StacksNodeClient) HomeDir() string { 48 | return StacksNodeHomeDir 49 | } 50 | -------------------------------------------------------------------------------- /apis/chainlink/v1alpha1/node_defaulting_webhook_test.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | "github.com/kotalco/kotal/apis/shared" 5 | . "github.com/onsi/ginkgo/v2" 6 | . "github.com/onsi/gomega" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | var _ = Describe("Chainlink node defaulting", func() { 11 | It("Should default node", func() { 12 | 13 | node := Node{ 14 | ObjectMeta: metav1.ObjectMeta{ 15 | Name: "my-node", 16 | }, 17 | Spec: NodeSpec{ 18 | CertSecretName: "my-certificate", 19 | }, 20 | } 21 | 22 | node.Default() 23 | 24 | Expect(node.Spec.Image).To(Equal(DefaultChainlinkImage)) 25 | Expect(*node.Spec.Replicas).To(Equal(DefaltReplicas)) 26 | Expect(node.Spec.TLSPort).To(Equal(DefaultTLSPort)) 27 | Expect(node.Spec.P2PPort).To(Equal(DefaultP2PPort)) 28 | Expect(node.Spec.APIPort).To(Equal(DefaultAPIPort)) 29 | Expect(node.Spec.Logging).To(Equal(shared.InfoLogs)) 30 | Expect(node.Spec.CORSDomains).To(Equal(DefaultCorsDomains)) 31 | // resources 32 | Expect(node.Spec.Resources.CPU).To(Equal(DefaultNodeCPURequest)) 33 | Expect(node.Spec.Resources.CPULimit).To(Equal(DefaultNodeCPULimit)) 34 | Expect(node.Spec.Resources.Memory).To(Equal(DefaultNodeMemoryRequest)) 35 | Expect(node.Spec.Resources.MemoryLimit).To(Equal(DefaultNodeMemoryLimit)) 36 | Expect(node.Spec.Resources.Storage).To(Equal(DefaultNodeStorageRequest)) 37 | 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /clients/filecoin/lotus_client_test.go: -------------------------------------------------------------------------------- 1 | package filecoin 2 | 3 | import ( 4 | filecoinv1alpha1 "github.com/kotalco/kotal/apis/filecoin/v1alpha1" 5 | "github.com/kotalco/kotal/controllers/shared" 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | corev1 "k8s.io/api/core/v1" 9 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 | ) 11 | 12 | var _ = Describe("Lotus Filecoin Client", func() { 13 | node := filecoinv1alpha1.Node{ 14 | ObjectMeta: metav1.ObjectMeta{ 15 | Name: "calibration-node", 16 | Namespace: "filecoin", 17 | }, 18 | Spec: filecoinv1alpha1.NodeSpec{ 19 | Network: filecoinv1alpha1.CalibrationNetwork, 20 | }, 21 | } 22 | 23 | client := NewClient(&node) 24 | 25 | It("Should get correct args", func() { 26 | Expect(client.Args()).To(BeNil()) 27 | }) 28 | 29 | It("Should get correct env", func() { 30 | Expect(client.Env()).To(ContainElements( 31 | corev1.EnvVar{ 32 | Name: EnvLotusPath, 33 | Value: shared.PathData(client.HomeDir()), 34 | }, 35 | corev1.EnvVar{ 36 | Name: EnvLogLevel, 37 | Value: string(node.Spec.Logging), 38 | }, 39 | )) 40 | }) 41 | 42 | It("Should get correct command", func() { 43 | Expect(client.Command()).To(ContainElements( 44 | "lotus", 45 | "daemon", 46 | )) 47 | }) 48 | 49 | It("Should get image home directory", func() { 50 | Expect(client.HomeDir()).To(Equal(LotusHomeDir)) 51 | }) 52 | }) 53 | -------------------------------------------------------------------------------- /clients/chainlink/chainlink_client.go: -------------------------------------------------------------------------------- 1 | package chainlink 2 | 3 | import ( 4 | "fmt" 5 | 6 | chainlinkv1alpha1 "github.com/kotalco/kotal/apis/chainlink/v1alpha1" 7 | "github.com/kotalco/kotal/controllers/shared" 8 | corev1 "k8s.io/api/core/v1" 9 | ) 10 | 11 | // ChainlinkClient is chainlink official client 12 | // https://github.com/smartcontractkit/chainlink 13 | type ChainlinkClient struct { 14 | node *chainlinkv1alpha1.Node 15 | } 16 | 17 | // Images 18 | const ( 19 | // ChainlinkHomeDir is chainlink image home dir 20 | // TODO: update the home directory 21 | ChainlinkHomeDir = "/home/chainlink" 22 | ) 23 | 24 | // Command is chainlink entrypoint 25 | func (c *ChainlinkClient) Command() []string { 26 | return []string{"chainlink"} 27 | } 28 | 29 | // Args returns chainlink args 30 | func (c *ChainlinkClient) Args() []string { 31 | args := []string{ 32 | "local", 33 | "--config", 34 | fmt.Sprintf("%s/config.toml", shared.PathConfig(c.HomeDir())), 35 | "--secrets", 36 | fmt.Sprintf("%s/secrets.toml", shared.PathConfig(c.HomeDir())), 37 | "node", 38 | } 39 | 40 | args = append(args, ChainlinkAPI, fmt.Sprintf("%s/.api", shared.PathData(c.HomeDir()))) 41 | 42 | return args 43 | } 44 | 45 | func (c *ChainlinkClient) Env() []corev1.EnvVar { 46 | return nil 47 | } 48 | 49 | // HomeDir returns chainlink image home directory 50 | func (c *ChainlinkClient) HomeDir() string { 51 | return ChainlinkHomeDir 52 | } 53 | -------------------------------------------------------------------------------- /apis/filecoin/v1alpha1/node_validation_webhook_test.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/kotalco/kotal/apis/shared" 7 | . "github.com/onsi/ginkgo/v2" 8 | . "github.com/onsi/gomega" 9 | "k8s.io/apimachinery/pkg/api/errors" 10 | "k8s.io/apimachinery/pkg/util/validation/field" 11 | ) 12 | 13 | var _ = Describe("Filecoin node validation", func() { 14 | 15 | updateCases := []struct { 16 | Title string 17 | OldNode *Node 18 | NewNode *Node 19 | Errors field.ErrorList 20 | }{ 21 | { 22 | Title: "network #1", 23 | OldNode: &Node{ 24 | Spec: NodeSpec{ 25 | Network: MainNetwork, 26 | }, 27 | }, 28 | NewNode: &Node{ 29 | Spec: NodeSpec{ 30 | Network: CalibrationNetwork, 31 | }, 32 | }, 33 | Errors: field.ErrorList{ 34 | { 35 | Type: field.ErrorTypeInvalid, 36 | Field: "spec.network", 37 | BadValue: CalibrationNetwork, 38 | Detail: "field is immutable", 39 | }, 40 | }, 41 | }, 42 | } 43 | 44 | Context("While updating node", func() { 45 | for _, c := range updateCases { 46 | func() { 47 | cc := c 48 | It(fmt.Sprintf("Should validate %s", cc.Title), func() { 49 | cc.NewNode.Default() 50 | cc.OldNode.Default() 51 | _, err := cc.NewNode.ValidateUpdate(cc.OldNode) 52 | 53 | errStatus := err.(*errors.StatusError) 54 | 55 | causes := shared.ErrorsToCauses(cc.Errors) 56 | 57 | Expect(errStatus.ErrStatus.Details.Causes).To(ContainElements(causes)) 58 | }) 59 | }() 60 | } 61 | }) 62 | 63 | }) 64 | -------------------------------------------------------------------------------- /config/samples/ethereum2/ethereum2_v1alpha1_prysm_beacon_node.yaml: -------------------------------------------------------------------------------- 1 | # Assuming cert manager is installed in the environment 2 | # we will use cert manager to issue certificates 3 | apiVersion: cert-manager.io/v1 4 | kind: Issuer 5 | metadata: 6 | name: self-signed-issuer 7 | spec: 8 | selfSigned: {} 9 | --- 10 | # create certificate for prysm beacon node 11 | apiVersion: cert-manager.io/v1 12 | kind: Certificate 13 | metadata: 14 | name: beaconnode-cert 15 | spec: 16 | dnsNames: 17 | - "prysm-beacon-node" 18 | - "prysm-beacon-node.svc" 19 | - "prysm-beacon-node.svc.cluster.local" 20 | secretName: beaconnode-cert 21 | issuerRef: 22 | name: self-signed-issuer 23 | --- 24 | apiVersion: v1 25 | kind: Secret 26 | metadata: 27 | name: jwt-secret 28 | stringData: 29 | secret: fbe0c28a10274b27babf3c51e88a7435318e25fad4de877e5a63a67d0d65fdbb 30 | --- 31 | apiVersion: ethereum2.kotal.io/v1alpha1 32 | kind: BeaconNode 33 | metadata: 34 | name: prysm-beacon-node 35 | spec: 36 | network: goerli 37 | client: prysm 38 | logging: info 39 | rpc: true 40 | rpcPort: 8888 41 | grpc: true 42 | grpcPort: 9999 43 | executionEngineEndpoint: http://goerli-geth-node:8551 44 | jwtSecretName: "jwt-secret" 45 | checkpointSyncUrl: "https://goerli.checkpoint-sync.ethpandaops.io" 46 | feeRecipient: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045" 47 | certSecretName: "beaconnode-cert" 48 | resources: 49 | # these resources are only for testing 50 | # change resources depending on your use case 51 | cpu: "1" 52 | memory: "1Gi" 53 | -------------------------------------------------------------------------------- /config/manager/manager.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | labels: 5 | control-plane: controller-manager 6 | name: kotal 7 | --- 8 | apiVersion: apps/v1 9 | kind: Deployment 10 | metadata: 11 | name: controller-manager 12 | namespace: system 13 | labels: 14 | control-plane: controller-manager 15 | spec: 16 | selector: 17 | matchLabels: 18 | control-plane: controller-manager 19 | replicas: 1 20 | template: 21 | metadata: 22 | annotations: 23 | kubectl.kubernetes.io/default-container: manager 24 | labels: 25 | control-plane: controller-manager 26 | spec: 27 | securityContext: 28 | runAsNonRoot: true 29 | containers: 30 | - name: manager 31 | securityContext: 32 | allowPrivilegeEscalation: false 33 | image: controller:latest 34 | command: 35 | - /manager 36 | args: 37 | - "--leader-elect" 38 | livenessProbe: 39 | httpGet: 40 | path: /healthz 41 | port: 8081 42 | initialDelaySeconds: 15 43 | periodSeconds: 20 44 | readinessProbe: 45 | httpGet: 46 | path: /readyz 47 | port: 8081 48 | initialDelaySeconds: 5 49 | periodSeconds: 10 50 | resources: 51 | requests: 52 | cpu: "50m" 53 | memory: "100Mi" 54 | serviceAccountName: controller-manager 55 | terminationGracePeriodSeconds: 10 56 | -------------------------------------------------------------------------------- /config/samples/aptos/aptos_devnet_v1alpha1_node.yaml: -------------------------------------------------------------------------------- 1 | # 🔥 Fetch devnet genesis block 2 | # $ wget https://devnet.aptoslabs.com/genesis.blob 3 | # 🔥 Create k8s configmap from genesis blob file 4 | # $ kubectl create configmap devnet-genesis --from-file genesis.blob 5 | --- 6 | # WARNING: DON'T use the following secret in production 7 | # https://aptos.dev/tutorials/full-node/network-identity-fullnode/ 8 | apiVersion: v1 9 | kind: Secret 10 | metadata: 11 | name: node-private-key 12 | stringData: 13 | key: 755EFCB7A02248562228CC7AF80FC667B7B9360541B165A1A2B97742AAC30AB7 14 | --- 15 | apiVersion: aptos.kotal.io/v1alpha1 16 | kind: Node 17 | metadata: 18 | name: aptos-devnet-node 19 | spec: 20 | network: devnet 21 | api: true 22 | apiPort: 6789 23 | p2pPort: 7555 24 | metricsPort: 9102 25 | waypoint: "0:2f485c632e01f81a4ea23e5ed963335527c0d14e727e48ba188169df56987872" 26 | genesisConfigmapName: devnet-genesis 27 | nodePrivateKeySecretName: node-private-key 28 | peerId: "76c8ca8bb75d1abd853fc17b70cc72cb78a63425fa85be96743825d93cc57d6f" 29 | seedPeers: 30 | - id: "bb14af025d226288a3488b4433cf5cb54d6a710365a2d95ac6ffbd9b9198a86a" 31 | addresses: 32 | - "/dns4/pfn0.node.devnet.aptoslabs.com/tcp/6182/noise-ik/bb14af025d226288a3488b4433cf5cb54d6a710365a2d95ac6ffbd9b9198a86a/handshake/0" 33 | - id: "7fe8523388084607cdf78ff40e3e717652173b436ae1809df4a5fcfc67f8fc61" 34 | addresses: 35 | - "/dns4/pfn2.node.devnet.aptoslabs.com/tcp/6182/noise-ik/f6b135a59591677afc98168791551a0a476222516fdc55869d2b649c614d965b/handshake/0" 36 | -------------------------------------------------------------------------------- /apis/ipfs/v1alpha1/defaults.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import "github.com/kotalco/kotal/apis/shared" 4 | 5 | const ( 6 | // DefaltReplicas is the default replicas 7 | DefaltReplicas uint = 1 8 | // DefaultRoutingMode is the default content routing mechanism 9 | DefaultRoutingMode = DHTRouting 10 | // DefaultAPIPort is the default API port 11 | DefaultAPIPort uint = 5001 12 | // DefaultGatewayPort is the default local gateway port 13 | DefaultGatewayPort uint = 8080 14 | // DefaultLogging is the default logging verbosity level 15 | DefaultLogging = shared.InfoLogs 16 | ) 17 | 18 | const ( 19 | // DefaultGoIPFSImage is the default go ipfs client image 20 | DefaultGoIPFSImage = "kotalco/kubo:v0.28.0" 21 | // DefaultGoIPFSClusterImage is the default go ipfs cluster client image 22 | DefaultGoIPFSClusterImage = "kotalco/ipfs-cluster:v1.0.8" 23 | ) 24 | 25 | // Resources 26 | const ( 27 | // DefaultNodeCPURequest is the cpu requested by ipfs node 28 | DefaultNodeCPURequest = "1" 29 | // DefaultNodeCPULimit is the cpu limit for ipfs node 30 | DefaultNodeCPULimit = "2" 31 | 32 | // DefaultNodeMemoryRequest is the memory requested by ipfs node 33 | DefaultNodeMemoryRequest = "2Gi" 34 | // DefaultNodeMemoryLimit is the memory limit for ipfs node 35 | DefaultNodeMemoryLimit = "4Gi" 36 | 37 | // DefaultNodeStorageRequest is the Storage requested by ipfs node 38 | DefaultNodeStorageRequest = "10Gi" 39 | ) 40 | 41 | // Cluster peer 42 | const ( 43 | // DefaultIPFSClusterConsensus is the default ipfs cluster consensus algorithm 44 | DefaultIPFSClusterConsensus = CRDT 45 | ) 46 | -------------------------------------------------------------------------------- /apis/bitcoin/v1alpha1/defaults.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | const ( 4 | // DefaultMainnetRPCPort is the default JSON-RPC port for mainnet 5 | DefaultMainnetRPCPort uint = 8332 6 | // DefaultTestnetRPCPort is the default JSON-RPC port for testnet 7 | DefaultTestnetRPCPort uint = 18332 8 | // DefaultMainnetP2PPort is the default p2p port for mainnet 9 | DefaultMainnetP2PPort uint = 8333 10 | // DefaultTestnetP2PPort is the default p2p port for testnet 11 | DefaultTestnetP2PPort uint = 18333 12 | // DefaltReplicas is the default replicas 13 | DefaltReplicas uint = 1 14 | // DefaultListen is the default connection to outside strategy 15 | DefaultListen = true 16 | // DefaultDBCacheSize is the default database cache size 17 | DefaultDBCacheSize uint = 450 18 | // DefaultMaxConnections is the default maximum connections to peers 19 | DefaultMaxConnections uint = 125 20 | ) 21 | 22 | const ( 23 | // DefaultBitcoinCoreImage is the default Bitcoin core client image 24 | DefaultBitcoinCoreImage = "lncm/bitcoind:v26.0" 25 | ) 26 | 27 | // Resources 28 | const ( 29 | // DefaultNodeCPURequest is the cpu requested by Bitcoin node 30 | DefaultNodeCPURequest = "2" 31 | // DefaultNodeCPULimit is the cpu limit for Bitcoin node 32 | DefaultNodeCPULimit = "4" 33 | 34 | // DefaultNodeMemoryRequest is the memory requested by Bitcoin node 35 | DefaultNodeMemoryRequest = "4Gi" 36 | // DefaultNodeMemoryLimit is the memory limit for Bitcoin node 37 | DefaultNodeMemoryLimit = "8Gi" 38 | 39 | // DefaultNodeStorageRequest is the Storage requested by Bitcoin node 40 | DefaultNodeStorageRequest = "100Gi" 41 | ) 42 | -------------------------------------------------------------------------------- /controllers/shared/labels_test.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import ( 4 | "testing" 5 | 6 | ethereumv1alpha1 "github.com/kotalco/kotal/apis/ethereum/v1alpha1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | "k8s.io/client-go/kubernetes/scheme" 9 | ) 10 | 11 | func TestUpdateLabels(t *testing.T) { 12 | 13 | if err := ethereumv1alpha1.AddToScheme(scheme.Scheme); err != nil { 14 | t.Error(err) 15 | } 16 | 17 | ethereumNode := ethereumv1alpha1.Node{ 18 | TypeMeta: metav1.TypeMeta{ 19 | Kind: "Node", 20 | APIVersion: "ethereum/v1alpha1", 21 | }, 22 | ObjectMeta: metav1.ObjectMeta{ 23 | Name: "my-node", 24 | Namespace: "default", 25 | }, 26 | Spec: ethereumv1alpha1.NodeSpec{ 27 | Client: ethereumv1alpha1.BesuClient, 28 | Network: ethereumv1alpha1.GoerliNetwork, 29 | }, 30 | } 31 | 32 | UpdateLabels(ðereumNode, string(ethereumNode.Spec.Client), ethereumv1alpha1.GoerliNetwork) 33 | 34 | labels := map[string]string{ 35 | "app.kubernetes.io/name": string(ethereumNode.Spec.Client), 36 | "app.kubernetes.io/instance": ethereumNode.Name, 37 | "app.kubernetes.io/component": "ethereum-node", 38 | "app.kubernetes.io/managed-by": "kotal-operator", 39 | "app.kubernetes.io/created-by": "ethereum-node-controller", 40 | "kotal.io/protocol": "ethereum", 41 | "kotal.io/network": ethereumv1alpha1.GoerliNetwork, 42 | } 43 | 44 | for k, v := range labels { 45 | if ethereumNode.Labels[k] != v { 46 | t.Errorf("Expecting label with key %s to have value %s, but got %s", k, v, ethereumNode.Labels[k]) 47 | } 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /config/samples/ethereum/ethereum_v1alpha1_pow_besu_node.yaml: -------------------------------------------------------------------------------- 1 | # WARNING: DON'T use the following secrets in production 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: pow-besu-nodekey 6 | stringData: 7 | # address 0x5A0b54D5dc17e0AadC383d2db43B0a0D3E029c4c 8 | key: 608e9b6f67c65e47531e08e8e501386dfae63a540fa3c48802c8aad854510b4e 9 | --- 10 | apiVersion: ethereum.kotal.io/v1alpha1 11 | kind: Node 12 | metadata: 13 | name: pow-besu-node 14 | spec: 15 | ########### Genesis block spec ########### 16 | genesis: 17 | chainId: 20189 18 | networkId: 20189 19 | ethash: {} 20 | forks: 21 | homestead: 0 22 | eip150: 0 23 | eip155: 0 24 | eip158: 0 25 | byzantium: 0 26 | constantinople: 0 27 | petersburg: 0 28 | istanbul: 0 29 | muirglacier: 0 30 | berlin: 0 31 | london: 0 32 | arrowGlacier: 0 33 | coinbase: "0x071e2c1067c24607ff00ceebbe83a38063bdedd8" 34 | difficulty: "0x1" 35 | gasLimit: "0x47b760" 36 | nonce: "0x0" 37 | timestamp: "0x5f69fcd9" 38 | accounts: 39 | - address: "0x48c5F25a884116d58A6287B72C9b069F936C9489" 40 | balance: "0xffffffffffffffffffff" 41 | ########### node spec ########### 42 | client: besu 43 | miner: true 44 | nodePrivateKeySecretName: pow-besu-nodekey 45 | coinbase: "0x5A0b54D5dc17e0AadC383d2db43B0a0D3E029c4c" 46 | rpc: true 47 | rpcPort: 8599 48 | corsDomains: 49 | - all 50 | hosts: 51 | - all 52 | rpcAPI: 53 | - web3 54 | - net 55 | - eth 56 | resources: 57 | cpu: "1" 58 | cpuLimit: "1" 59 | memory: "1Gi" 60 | memoryLimit: "2Gi" 61 | -------------------------------------------------------------------------------- /apis/stacks/v1alpha1/node_defaulting_webhook.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | "sigs.k8s.io/controller-runtime/pkg/webhook" 5 | ) 6 | 7 | // +kubebuilder:webhook:path=/mutate-stacks-kotal-io-v1alpha1-node,mutating=true,failurePolicy=fail,groups=stacks.kotal.io,resources=nodes,verbs=create;update,versions=v1alpha1,name=mutate-stacks-v1alpha1-node.kb.io,sideEffects=None,admissionReviewVersions=v1 8 | 9 | var _ webhook.Defaulter = &Node{} 10 | 11 | func (r *Node) DefaultNodeResources() { 12 | if r.Spec.Resources.CPU == "" { 13 | r.Spec.Resources.CPU = DefaultNodeCPURequest 14 | } 15 | 16 | if r.Spec.Resources.CPULimit == "" { 17 | r.Spec.Resources.CPULimit = DefaultNodeCPULimit 18 | } 19 | 20 | if r.Spec.Resources.Memory == "" { 21 | r.Spec.Resources.Memory = DefaultNodeMemoryRequest 22 | } 23 | 24 | if r.Spec.Resources.MemoryLimit == "" { 25 | r.Spec.Resources.MemoryLimit = DefaultNodeMemoryLimit 26 | } 27 | 28 | if r.Spec.Resources.Storage == "" { 29 | r.Spec.Resources.Storage = DefaultNodeStorageRequest 30 | } 31 | } 32 | 33 | // Default implements webhook.Defaulter so a webhook will be registered for the type 34 | func (r *Node) Default() { 35 | nodelog.Info("default", "name", r.Name) 36 | 37 | r.DefaultNodeResources() 38 | 39 | if r.Spec.Image == "" { 40 | r.Spec.Image = DefaultStacksNodeImage 41 | } 42 | 43 | if r.Spec.Replicas == nil { 44 | // constants are not addressable 45 | replicas := DefaltReplicas 46 | r.Spec.Replicas = &replicas 47 | } 48 | 49 | if r.Spec.P2PPort == 0 { 50 | r.Spec.P2PPort = DefaultP2PPort 51 | } 52 | 53 | if r.Spec.RPCPort == 0 { 54 | r.Spec.RPCPort = DefaultRPCPort 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /clients/ipfs/go_ipfs_cluster_client.go: -------------------------------------------------------------------------------- 1 | package ipfs 2 | 3 | import ( 4 | "strings" 5 | 6 | ipfsv1alpha1 "github.com/kotalco/kotal/apis/ipfs/v1alpha1" 7 | "github.com/kotalco/kotal/controllers/shared" 8 | corev1 "k8s.io/api/core/v1" 9 | ) 10 | 11 | // GoIPFSClusterClient is ipfs cluster service client 12 | // https://github.com/ipfs/ipfs-cluster 13 | type GoIPFSClusterClient struct { 14 | peer *ipfsv1alpha1.ClusterPeer 15 | } 16 | 17 | const ( 18 | // GoIPFSClusterHomeDir is go ipfs cluster image home dir 19 | GoIPFSClusterHomeDir = "/home/ipfs-cluster" 20 | ) 21 | 22 | // Command returns go ipfs cluster entrypoint 23 | func (c *GoIPFSClusterClient) Command() []string { 24 | return []string{"ipfs-cluster-service"} 25 | } 26 | 27 | // Command returns environment variables for the client 28 | func (c *GoIPFSClusterClient) Env() []corev1.EnvVar { 29 | return []corev1.EnvVar{ 30 | { 31 | Name: EnvIPFSClusterPath, 32 | Value: shared.PathData(c.HomeDir()), 33 | }, 34 | { 35 | Name: EnvIPFSClusterPeerName, 36 | Value: c.peer.Name, 37 | }, 38 | { 39 | Name: EnvIPFSLogging, 40 | Value: string(c.peer.Spec.Logging), 41 | }, 42 | } 43 | } 44 | 45 | // Arg returns go ipfs cluster arguments 46 | func (c *GoIPFSClusterClient) Args() (args []string) { 47 | args = append(args, GoIPFSClusterDaemonArg) 48 | 49 | if len(c.peer.Spec.BootstrapPeers) != 0 { 50 | args = append(args, GoIPFSClusterBootstrapArg, strings.Join(c.peer.Spec.BootstrapPeers, ",")) 51 | } 52 | 53 | return 54 | } 55 | 56 | // HomeDir returns go ipfs cluster image home directory 57 | func (c *GoIPFSClusterClient) HomeDir() string { 58 | return GoIPFSClusterHomeDir 59 | } 60 | -------------------------------------------------------------------------------- /config/samples/ethereum/ethereum_v1alpha1_pow_geth_node.yaml: -------------------------------------------------------------------------------- 1 | # WARNING: DON'T use the following secrets in production 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: pow-geth-account-key 6 | stringData: 7 | # address 0x2b3430337f12Ce89EaBC7b0d865F4253c7744c0d 8 | key: 5df5eff7ef9e4e82739b68a34c6b23608d79ee8daf3b598a01ffb0dd7aa3a2fd 9 | --- 10 | apiVersion: v1 11 | kind: Secret 12 | metadata: 13 | name: pow-geth-account-password 14 | stringData: 15 | password: secret 16 | --- 17 | apiVersion: ethereum.kotal.io/v1alpha1 18 | kind: Node 19 | metadata: 20 | name: pow-geth-node 21 | spec: 22 | ########### Genesis block spec ########### 23 | genesis: 24 | chainId: 20189 25 | networkId: 20189 26 | ethash: {} 27 | forks: 28 | homestead: 0 29 | eip150: 0 30 | eip155: 0 31 | eip158: 0 32 | byzantium: 0 33 | constantinople: 0 34 | petersburg: 0 35 | istanbul: 0 36 | muirglacier: 0 37 | berlin: 0 38 | london: 0 39 | arrowGlacier: 0 40 | coinbase: "0x071e2c1067c24607ff00ceebbe83a38063bdedd8" 41 | difficulty: "0x1" 42 | gasLimit: "0x47b760" 43 | nonce: "0x0" 44 | timestamp: "0x5f69fcd9" 45 | accounts: 46 | - address: "0x48c5F25a884116d58A6287B72C9b069F936C9489" 47 | balance: "0xffffffffffffffffffff" 48 | ########### node spec ########### 49 | client: geth 50 | miner: true 51 | coinbase: "0x2b3430337f12Ce89EaBC7b0d865F4253c7744c0d" 52 | import: 53 | privateKeySecretName: pow-geth-account-key 54 | passwordSecretName: pow-geth-account-password 55 | resources: 56 | cpu: "1" 57 | cpuLimit: "1" 58 | memory: "1Gi" 59 | memoryLimit: "2Gi" 60 | -------------------------------------------------------------------------------- /controllers/graph/suite_test.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | "path/filepath" 5 | "testing" 6 | 7 | . "github.com/onsi/ginkgo/v2" 8 | . "github.com/onsi/gomega" 9 | "k8s.io/client-go/kubernetes/scheme" 10 | "k8s.io/client-go/rest" 11 | "sigs.k8s.io/controller-runtime/pkg/client" 12 | "sigs.k8s.io/controller-runtime/pkg/envtest" 13 | logf "sigs.k8s.io/controller-runtime/pkg/log" 14 | "sigs.k8s.io/controller-runtime/pkg/log/zap" 15 | //+kubebuilder:scaffold:imports 16 | ) 17 | 18 | var cfg *rest.Config 19 | var k8sClient client.Client 20 | var testEnv *envtest.Environment 21 | 22 | func TestAPIs(t *testing.T) { 23 | RegisterFailHandler(Fail) 24 | 25 | suiteConfig, reporterConfig := GinkgoConfiguration() 26 | 27 | RunSpecs(t, 28 | "Controller Suite", 29 | suiteConfig, 30 | reporterConfig) 31 | } 32 | 33 | var _ = BeforeSuite(func() { 34 | logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) 35 | 36 | By("bootstrapping test environment") 37 | testEnv = &envtest.Environment{ 38 | CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, 39 | ErrorIfCRDPathMissing: false, 40 | } 41 | 42 | var err error 43 | // cfg is defined in this file globally. 44 | cfg, err = testEnv.Start() 45 | Expect(err).NotTo(HaveOccurred()) 46 | Expect(cfg).NotTo(BeNil()) 47 | 48 | //+kubebuilder:scaffold:scheme 49 | 50 | k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) 51 | Expect(err).NotTo(HaveOccurred()) 52 | Expect(k8sClient).NotTo(BeNil()) 53 | 54 | }) 55 | 56 | var _ = AfterSuite(func() { 57 | By("tearing down the test environment") 58 | err := testEnv.Stop() 59 | Expect(err).NotTo(HaveOccurred()) 60 | }) 61 | -------------------------------------------------------------------------------- /clients/ipfs/go_ipfs_cluster_client_test.go: -------------------------------------------------------------------------------- 1 | package ipfs 2 | 3 | import ( 4 | ipfsv1alpha1 "github.com/kotalco/kotal/apis/ipfs/v1alpha1" 5 | "github.com/kotalco/kotal/controllers/shared" 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | corev1 "k8s.io/api/core/v1" 9 | ) 10 | 11 | var _ = Describe("Go IPFS Cluster Client", func() { 12 | peer := &ipfsv1alpha1.ClusterPeer{ 13 | Spec: ipfsv1alpha1.ClusterPeerSpec{ 14 | Consensus: ipfsv1alpha1.Raft, 15 | PeerEndpoint: "/dns4/bare-peer/tcp/5001", 16 | BootstrapPeers: []string{ 17 | "/ip4/95.111.253.236/tcp/4001/p2p/Qmd3FERyCvxvkC8su1DYhjybRaLueHveKysUVPxWAqR4U7", 18 | }, 19 | ClusterSecretName: "cluster-secret", 20 | }, 21 | } 22 | 23 | client, _ := NewClient(peer) 24 | 25 | It("Should get correct env", func() { 26 | Expect(client.Env()).To(Equal( 27 | []corev1.EnvVar{ 28 | { 29 | Name: EnvIPFSClusterPath, 30 | Value: shared.PathData(client.HomeDir()), 31 | }, 32 | { 33 | Name: EnvIPFSClusterPeerName, 34 | Value: peer.Name, 35 | }, 36 | { 37 | Name: EnvIPFSLogging, 38 | Value: string(peer.Spec.Logging), 39 | }, 40 | }, 41 | )) 42 | }) 43 | 44 | It("Should get correct command", func() { 45 | Expect(client.Command()).To(ConsistOf("ipfs-cluster-service")) 46 | }) 47 | 48 | It("Should get correct home dir", func() { 49 | Expect(client.HomeDir()).To(Equal(GoIPFSClusterHomeDir)) 50 | }) 51 | 52 | It("Should get correct args", func() { 53 | Expect(client.Args()).To(ContainElements( 54 | GoIPFSDaemonArg, 55 | GoIPFSClusterBootstrapArg, 56 | "/ip4/95.111.253.236/tcp/4001/p2p/Qmd3FERyCvxvkC8su1DYhjybRaLueHveKysUVPxWAqR4U7", 57 | )) 58 | }) 59 | 60 | }) 61 | -------------------------------------------------------------------------------- /apis/ethereum/v1alpha1/genesis_defaulting.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | // Default defaults genesis block parameters 4 | func (g *Genesis) Default() { 5 | if g.Coinbase == "" { 6 | g.Coinbase = DefaultCoinbase 7 | } 8 | 9 | if g.Difficulty == "" { 10 | g.Difficulty = DefaultDifficulty 11 | } 12 | 13 | if g.Forks == nil { 14 | g.Forks = &Forks{} 15 | } 16 | 17 | if g.MixHash == "" { 18 | g.MixHash = DefaultMixHash 19 | } 20 | 21 | if g.GasLimit == "" { 22 | g.GasLimit = DefaultGasLimit 23 | } 24 | 25 | if g.Nonce == "" { 26 | g.Nonce = DefaultNonce 27 | } 28 | 29 | if g.Timestamp == "" { 30 | g.Timestamp = DefaultTimestamp 31 | } 32 | 33 | if g.Clique != nil { 34 | if g.Clique.BlockPeriod == 0 { 35 | g.Clique.BlockPeriod = DefaultCliqueBlockPeriod 36 | } 37 | if g.Clique.EpochLength == 0 { 38 | g.Clique.EpochLength = DefaultCliqueEpochLength 39 | } 40 | } 41 | 42 | if g.IBFT2 != nil { 43 | if g.IBFT2.BlockPeriod == 0 { 44 | g.IBFT2.BlockPeriod = DefaultIBFT2BlockPeriod 45 | } 46 | if g.IBFT2.EpochLength == 0 { 47 | g.IBFT2.EpochLength = DefaultIBFT2EpochLength 48 | } 49 | if g.IBFT2.RequestTimeout == 0 { 50 | g.IBFT2.RequestTimeout = DefaultIBFT2RequestTimeout 51 | } 52 | if g.IBFT2.MessageQueueLimit == 0 { 53 | g.IBFT2.MessageQueueLimit = DefaultIBFT2MessageQueueLimit 54 | } 55 | if g.IBFT2.DuplicateMessageLimit == 0 { 56 | g.IBFT2.DuplicateMessageLimit = DefaultIBFT2DuplicateMessageLimit 57 | } 58 | if g.IBFT2.FutureMessagesLimit == 0 { 59 | g.IBFT2.FutureMessagesLimit = DefaultIBFT2FutureMessagesLimit 60 | } 61 | if g.IBFT2.FutureMessagesMaxDistance == 0 { 62 | g.IBFT2.FutureMessagesMaxDistance = DefaultIBFT2FutureMessagesMaxDistance 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /apis/near/v1alpha1/node_defaulting_webhook.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | "sigs.k8s.io/controller-runtime/pkg/webhook" 5 | ) 6 | 7 | // +kubebuilder:webhook:path=/mutate-near-kotal-io-v1alpha1-node,mutating=true,failurePolicy=fail,groups=near.kotal.io,resources=nodes,verbs=create;update,versions=v1alpha1,name=mutate-near-v1alpha1-node.kb.io,sideEffects=None,admissionReviewVersions=v1 8 | 9 | var _ webhook.Defaulter = &Node{} 10 | 11 | // Default implements webhook.Defaulter so a webhook will be registered for the type 12 | func (n *Node) Default() { 13 | nodelog.Info("default", "name", n.Name) 14 | 15 | if n.Spec.Image == "" { 16 | n.Spec.Image = DefaultNearImage 17 | } 18 | 19 | if n.Spec.Replicas == nil { 20 | // constants are not addressable 21 | replicas := DefaltReplicas 22 | n.Spec.Replicas = &replicas 23 | } 24 | 25 | if n.Spec.MinPeers == 0 { 26 | n.Spec.MinPeers = DefaultMinPeers 27 | } 28 | 29 | if n.Spec.RPCPort == 0 { 30 | n.Spec.RPCPort = DefaultRPCPort 31 | } 32 | 33 | if n.Spec.PrometheusPort == 0 { 34 | n.Spec.PrometheusPort = DefaultPrometheusPort 35 | } 36 | 37 | if n.Spec.P2PPort == 0 { 38 | n.Spec.P2PPort = DefaultP2PPort 39 | } 40 | 41 | if n.Spec.CPU == "" { 42 | n.Spec.CPU = DefaultNodeCPURequest 43 | } 44 | if n.Spec.CPULimit == "" { 45 | n.Spec.CPULimit = DefaultNodeCPULimit 46 | } 47 | 48 | if n.Spec.Memory == "" { 49 | n.Spec.Memory = DefaultNodeMemoryRequest 50 | } 51 | if n.Spec.MemoryLimit == "" { 52 | n.Spec.MemoryLimit = DefaultNodeMemoryLimit 53 | } 54 | 55 | if n.Spec.Storage == "" { 56 | storage := DefaultNodeStorageRequest 57 | if n.Spec.Archive { 58 | storage = DefaultArchivalNodeStorageRequest 59 | } 60 | n.Spec.Storage = storage 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /config/samples/ethereum/ethereum_v1alpha1_poa_besu_node.yaml: -------------------------------------------------------------------------------- 1 | # WARNING: DON'T use the following secrets in production 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: poa-besu-nodekey 6 | stringData: 7 | key: "7fb5b8b783a35f3fafbd8e36582ec5eec507e509a6077aa6c84a48afd4d4cf06" 8 | --- 9 | apiVersion: ethereum.kotal.io/v1alpha1 10 | kind: Node 11 | metadata: 12 | name: poa-besu-node 13 | spec: 14 | ########### Genesis block spec ########### 15 | genesis: 16 | chainId: 20189 17 | networkId: 20189 18 | clique: 19 | blockPeriod: 15 20 | epochLength: 100 21 | signers: 22 | - "0xcF2C3fB8F36A863FD1A8c72E2473f81744B4CA6C" 23 | - "0x1990E5760d9f8Ae0ec55dF8B0819C77e59846Ff2" 24 | - "0xB87c1c66b36D98D1A74a9875EbA12c001e0bcEda" 25 | forks: 26 | homestead: 0 27 | eip150: 0 28 | eip155: 0 29 | eip158: 0 30 | byzantium: 0 31 | constantinople: 0 32 | petersburg: 0 33 | istanbul: 0 34 | muirglacier: 0 35 | berlin: 0 36 | london: 0 37 | arrowGlacier: 0 38 | coinbase: "0x071E2c1067c24607fF00cEEBbe83a38063BDEDd8" 39 | difficulty: "0xfff" 40 | gasLimit: "0x47b760" 41 | nonce: "0x0" 42 | timestamp: "0x0" 43 | accounts: 44 | - address: "0x48c5F25a884116d58A6287B72C9b069F936C9489" 45 | balance: "0xffffffffffffffffffff" 46 | ########### node spec ########### 47 | client: besu 48 | rpc: true 49 | nodePrivateKeySecretName: poa-besu-nodekey 50 | rpcPort: 8599 51 | corsDomains: 52 | - all 53 | hosts: 54 | - all 55 | rpcAPI: 56 | - web3 57 | - net 58 | - eth 59 | - clique 60 | resources: 61 | cpu: "1" 62 | cpuLimit: "1" 63 | memory: "1Gi" 64 | memoryLimit: "2Gi" 65 | -------------------------------------------------------------------------------- /clients/ethereum2/lighthouse_validator_client.go: -------------------------------------------------------------------------------- 1 | package ethereum2 2 | 3 | import ( 4 | "strings" 5 | 6 | ethereum2v1alpha1 "github.com/kotalco/kotal/apis/ethereum2/v1alpha1" 7 | "github.com/kotalco/kotal/controllers/shared" 8 | corev1 "k8s.io/api/core/v1" 9 | ) 10 | 11 | // LighthouseValidatorClient is SigmaPrime Ethereum 2.0 validator client 12 | // https://github.com/sigp/lighthouse 13 | type LighthouseValidatorClient struct { 14 | validator *ethereum2v1alpha1.Validator 15 | } 16 | 17 | // HomeDir returns container home directory 18 | func (t *LighthouseValidatorClient) HomeDir() string { 19 | return LighthouseHomeDir 20 | } 21 | 22 | // Command returns environment variables for the client 23 | func (t *LighthouseValidatorClient) Env() []corev1.EnvVar { 24 | return nil 25 | } 26 | 27 | // Args returns command line arguments required for client 28 | func (t *LighthouseValidatorClient) Args() (args []string) { 29 | 30 | validator := t.validator 31 | 32 | args = append(args, LighthouseDataDir, shared.PathData(t.HomeDir())) 33 | 34 | args = append(args, LighthouseDebugLevel, string(t.validator.Spec.Logging)) 35 | 36 | args = append(args, LighthouseNetwork, validator.Spec.Network) 37 | 38 | args = append(args, LighthouseFeeRecipient, string(validator.Spec.FeeRecipient)) 39 | 40 | if len(validator.Spec.BeaconEndpoints) != 0 { 41 | args = append(args, LighthouseBeaconNodeEndpoints, strings.Join(validator.Spec.BeaconEndpoints, ",")) 42 | } 43 | 44 | if validator.Spec.Graffiti != "" { 45 | args = append(args, LighthouseGraffiti, validator.Spec.Graffiti) 46 | } 47 | 48 | return 49 | } 50 | 51 | // Command returns command for running the client 52 | func (t *LighthouseValidatorClient) Command() (command []string) { 53 | command = []string{"lighthouse", "vc"} 54 | return 55 | } 56 | -------------------------------------------------------------------------------- /config/samples/ethereum/ethereum_v1alpha1_ibft2_besu_node.yaml: -------------------------------------------------------------------------------- 1 | # WARNING: DON'T use the following secrets in production 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: ibft2-besu-nodekey 6 | stringData: 7 | key: 608e9b6f67c65e47531e08e8e501386dfae63a540fa3c48802c8aad854510b4e 8 | --- 9 | apiVersion: ethereum.kotal.io/v1alpha1 10 | kind: Node 11 | metadata: 12 | name: ibft2-besu-node 13 | spec: 14 | ########### Genesis block spec ########### 15 | genesis: 16 | chainId: 20189 17 | networkId: 11 18 | ibft2: 19 | blockPeriod: 2 20 | epochLength: 30000 21 | requestTimeout: 10 22 | validators: 23 | - "0x427e2c7cecd72bc4cdd4f7ebb8bb6e49789c8044" 24 | - "0xd2c21213027cbf4d46c16b55fa98e5252b048706" 25 | - "0x8e1f6c7c76a1d7f74eda342d330ca9749f31cc2b" 26 | forks: 27 | homestead: 0 28 | eip150: 0 29 | eip155: 0 30 | eip158: 0 31 | byzantium: 0 32 | constantinople: 0 33 | petersburg: 0 34 | istanbul: 0 35 | muirglacier: 0 36 | berlin: 0 37 | london: 0 38 | arrowGlacier: 0 39 | coinbase: "0x071E2c1067c24607fF00cEEBbe83a38063BDEDd8" 40 | difficulty: "0xfff" 41 | gasLimit: "0x47b760" 42 | nonce: "0x0" 43 | timestamp: "0x0" 44 | accounts: 45 | - address: "0x48c5F25a884116d58A6287B72C9b069F936C9489" 46 | balance: "0xffffffffffffffffffff" 47 | ########### node spec ########### 48 | client: besu 49 | rpc: true 50 | nodePrivateKeySecretName: ibft2-besu-nodekey 51 | rpcPort: 8599 52 | corsDomains: 53 | - all 54 | hosts: 55 | - all 56 | rpcAPI: 57 | - web3 58 | - net 59 | - eth 60 | - ibft 61 | resources: 62 | cpu: "1" 63 | cpuLimit: "1" 64 | memory: "1Gi" 65 | memoryLimit: "2Gi" 66 | -------------------------------------------------------------------------------- /apis/chainlink/v1alpha1/node_defaulting_webhook.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | "github.com/kotalco/kotal/apis/shared" 5 | "sigs.k8s.io/controller-runtime/pkg/webhook" 6 | ) 7 | 8 | // +kubebuilder:webhook:path=/mutate-chainlink-kotal-io-v1alpha1-node,mutating=true,failurePolicy=fail,groups=chainlink.kotal.io,resources=nodes,verbs=create;update,versions=v1alpha1,name=mutate-chainlink-v1alpha1-node.kb.io,sideEffects=None,admissionReviewVersions=v1 9 | 10 | var _ webhook.Defaulter = &Node{} 11 | 12 | // Default implements webhook.Defaulter so a webhook will be registered for the type 13 | func (r *Node) Default() { 14 | nodelog.Info("default", "name", r.Name) 15 | 16 | if r.Spec.Image == "" { 17 | r.Spec.Image = DefaultChainlinkImage 18 | } 19 | 20 | if r.Spec.Replicas == nil { 21 | // constants are not addressable 22 | replicas := DefaltReplicas 23 | r.Spec.Replicas = &replicas 24 | } 25 | 26 | if r.Spec.P2PPort == 0 { 27 | r.Spec.P2PPort = DefaultP2PPort 28 | } 29 | 30 | if r.Spec.APIPort == 0 { 31 | r.Spec.APIPort = DefaultAPIPort 32 | } 33 | 34 | if r.Spec.CPU == "" { 35 | r.Spec.CPU = DefaultNodeCPURequest 36 | } 37 | 38 | if r.Spec.CPULimit == "" { 39 | r.Spec.CPULimit = DefaultNodeCPULimit 40 | } 41 | 42 | if r.Spec.Memory == "" { 43 | r.Spec.Memory = DefaultNodeMemoryRequest 44 | } 45 | 46 | if r.Spec.MemoryLimit == "" { 47 | r.Spec.MemoryLimit = DefaultNodeMemoryLimit 48 | } 49 | 50 | if r.Spec.Storage == "" { 51 | r.Spec.Storage = DefaultNodeStorageRequest 52 | } 53 | 54 | if r.Spec.TLSPort == 0 { 55 | r.Spec.TLSPort = DefaultTLSPort 56 | } 57 | 58 | if r.Spec.Logging == "" { 59 | r.Spec.Logging = shared.InfoLogs 60 | } 61 | 62 | if len(r.Spec.CORSDomains) == 0 { 63 | r.Spec.CORSDomains = DefaultCorsDomains 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /clients/chainlink/chainlink_client_test.go: -------------------------------------------------------------------------------- 1 | package chainlink 2 | 3 | import ( 4 | "fmt" 5 | 6 | chainlinkv1alpha1 "github.com/kotalco/kotal/apis/chainlink/v1alpha1" 7 | sharedAPI "github.com/kotalco/kotal/apis/shared" 8 | "github.com/kotalco/kotal/controllers/shared" 9 | . "github.com/onsi/ginkgo/v2" 10 | . "github.com/onsi/gomega" 11 | ) 12 | 13 | var _ = Describe("Chainlink Client", func() { 14 | node := &chainlinkv1alpha1.Node{ 15 | Spec: chainlinkv1alpha1.NodeSpec{ 16 | EthereumChainId: 1, 17 | EthereumWSEndpoint: "ws://my-eth-node:8546", 18 | EthereumHTTPEndpoints: []string{ 19 | "http://my-eth-node:8545", 20 | "http://my-eth-node2:8545", 21 | "http://my-eth-node3:8545", 22 | }, 23 | LinkContractAddress: "0x01BE23585060835E02B77ef475b0Cc51aA1e0709", 24 | DatabaseURL: "postgresql://postgres:secret@postgres:5432/postgres", 25 | CertSecretName: "my-certificate", 26 | TLSPort: 9999, 27 | P2PPort: 4444, 28 | APIPort: 7777, 29 | Logging: sharedAPI.PanicLogs, 30 | CORSDomains: []string{"*"}, 31 | SecureCookies: true, 32 | }, 33 | } 34 | 35 | client := NewClient(node) 36 | 37 | It("Should get correct command", func() { 38 | Expect(client.Command()).To(ConsistOf("chainlink")) 39 | }) 40 | 41 | It("Should get correct environment variables", func() { 42 | Expect(client.Env()).To(BeNil()) 43 | }) 44 | 45 | It("Should get correct home dir", func() { 46 | Expect(client.HomeDir()).To(Equal(ChainlinkHomeDir)) 47 | }) 48 | 49 | It("Should get correct args", func() { 50 | Expect(client.Args()).To(ContainElements( 51 | "local", 52 | "node", 53 | ChainlinkAPI, 54 | fmt.Sprintf("%s/.api", shared.PathData(client.HomeDir())), 55 | )) 56 | }) 57 | 58 | }) 59 | -------------------------------------------------------------------------------- /apis/polkadot/v1alpha1/node_defaulting_webhook_test.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo/v2" 5 | . "github.com/onsi/gomega" 6 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 7 | ) 8 | 9 | var _ = Describe("Polkadot node defaulting", func() { 10 | It("Should default node", func() { 11 | 12 | node := Node{ 13 | ObjectMeta: metav1.ObjectMeta{ 14 | Name: "my-node", 15 | }, 16 | Spec: NodeSpec{ 17 | RPC: true, 18 | WS: true, 19 | Telemetry: true, 20 | Prometheus: true, 21 | // TODO: create test for node with pruning enabled 22 | }, 23 | } 24 | 25 | t := true 26 | 27 | node.Default() 28 | 29 | Expect(node.Spec.Image).To(Equal(DefaultPolkadotImage)) 30 | Expect(*node.Spec.Replicas).To(Equal(DefaltReplicas)) 31 | Expect(node.Spec.P2PPort).To(Equal(DefaultP2PPort)) 32 | Expect(node.Spec.Resources.CPU).To(Equal(DefaultNodeCPURequest)) 33 | Expect(node.Spec.Resources.CPULimit).To(Equal(DefaultNodeCPULimit)) 34 | Expect(node.Spec.Resources.Memory).To(Equal(DefaultNodeMemoryRequest)) 35 | Expect(node.Spec.Resources.MemoryLimit).To(Equal(DefaultNodeMemoryLimit)) 36 | Expect(node.Spec.Resources.Storage).To(Equal(DefaultNodeStorageRequest)) 37 | Expect(node.Spec.SyncMode).To(Equal(DefaultSyncMode)) 38 | Expect(node.Spec.Logging).To(Equal(DefaultLoggingVerbosity)) 39 | Expect(node.Spec.RPCPort).To(Equal(DefaultRPCPort)) 40 | Expect(node.Spec.WSPort).To(Equal(DefaultWSPort)) 41 | Expect(node.Spec.TelemetryURL).To(Equal(DefaultTelemetryURL)) 42 | Expect(node.Spec.PrometheusPort).To(Equal(DefaultPrometheusPort)) 43 | Expect(node.Spec.Pruning).To(Equal(&t)) 44 | Expect(node.Spec.Database).To(Equal(DefaultDatabaseBackend)) 45 | Expect(node.Spec.CORSDomains).To(ContainElement(DefaultCORSDomain)) 46 | 47 | }) 48 | }) 49 | -------------------------------------------------------------------------------- /clients/ethereum2/client.go: -------------------------------------------------------------------------------- 1 | package ethereum2 2 | 3 | import ( 4 | "fmt" 5 | 6 | ethereum2v1alpha1 "github.com/kotalco/kotal/apis/ethereum2/v1alpha1" 7 | "github.com/kotalco/kotal/clients" 8 | "k8s.io/apimachinery/pkg/runtime" 9 | ) 10 | 11 | // Ethereum2Client is Ethereum 2.0 beacon node or validator client 12 | type Ethereum2Client interface { 13 | clients.Interface 14 | } 15 | 16 | // NewClient creates new ethereum 2.0 beacon node or validator client 17 | func NewClient(obj runtime.Object) (Ethereum2Client, error) { 18 | 19 | switch component := obj.(type) { 20 | 21 | // create beacon nodes 22 | case *ethereum2v1alpha1.BeaconNode: 23 | switch component.Spec.Client { 24 | case ethereum2v1alpha1.TekuClient: 25 | return &TekuBeaconNode{component}, nil 26 | case ethereum2v1alpha1.PrysmClient: 27 | return &PrysmBeaconNode{component}, nil 28 | case ethereum2v1alpha1.LighthouseClient: 29 | return &LighthouseBeaconNode{component}, nil 30 | case ethereum2v1alpha1.NimbusClient: 31 | return &NimbusBeaconNode{component}, nil 32 | default: 33 | return nil, fmt.Errorf("client %s is not supported", component.Spec.Client) 34 | } 35 | 36 | // create validator clients 37 | case *ethereum2v1alpha1.Validator: 38 | switch component.Spec.Client { 39 | case ethereum2v1alpha1.TekuClient: 40 | return &TekuValidatorClient{component}, nil 41 | case ethereum2v1alpha1.PrysmClient: 42 | return &PrysmValidatorClient{component}, nil 43 | case ethereum2v1alpha1.LighthouseClient: 44 | return &LighthouseValidatorClient{component}, nil 45 | case ethereum2v1alpha1.NimbusClient: 46 | return &NimbusValidatorClient{component}, nil 47 | default: 48 | return nil, fmt.Errorf("client %s is not supported", component.Spec.Client) 49 | } 50 | default: 51 | return nil, fmt.Errorf("no client support for %s", obj) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /apis/ipfs/v1alpha1/cluster_peer_defaulting_webhook.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import "sigs.k8s.io/controller-runtime/pkg/webhook" 4 | 5 | // +kubebuilder:webhook:path=/mutate-ipfs-kotal-io-v1alpha1-clusterpeer,mutating=true,failurePolicy=fail,groups=ipfs.kotal.io,resources=clusterpeers,verbs=create;update,versions=v1alpha1,name=mutate-ipfs-v1alpha1-clusterpeer.kb.io,sideEffects=None,admissionReviewVersions=v1 6 | 7 | var _ webhook.Defaulter = &ClusterPeer{} 8 | 9 | // DefaultResources defaults cluster peer resources 10 | func (r *ClusterPeer) DefaultResources() { 11 | if r.Spec.Resources.CPU == "" { 12 | r.Spec.Resources.CPU = DefaultNodeCPURequest 13 | } 14 | 15 | if r.Spec.Resources.CPULimit == "" { 16 | r.Spec.Resources.CPULimit = DefaultNodeCPULimit 17 | } 18 | 19 | if r.Spec.Resources.Memory == "" { 20 | r.Spec.Resources.Memory = DefaultNodeMemoryRequest 21 | } 22 | 23 | if r.Spec.Resources.MemoryLimit == "" { 24 | r.Spec.Resources.MemoryLimit = DefaultNodeMemoryLimit 25 | } 26 | 27 | if r.Spec.Resources.Storage == "" { 28 | r.Spec.Resources.Storage = DefaultNodeStorageRequest 29 | } 30 | } 31 | 32 | // Default implements webhook.Defaulter so a webhook will be registered for the type 33 | func (r *ClusterPeer) Default() { 34 | clusterpeerlog.Info("default", "name", r.Name) 35 | 36 | if r.Spec.Image == "" { 37 | r.Spec.Image = DefaultGoIPFSClusterImage 38 | } 39 | 40 | if r.Spec.Replicas == nil { 41 | // constants are not addressable 42 | replicas := DefaltReplicas 43 | r.Spec.Replicas = &replicas 44 | } 45 | 46 | if r.Spec.Logging == "" { 47 | r.Spec.Logging = DefaultLogging 48 | } 49 | 50 | if r.Spec.Consensus == "" { 51 | r.Spec.Consensus = DefaultIPFSClusterConsensus 52 | } 53 | 54 | if len(r.Spec.TrustedPeers) == 0 { 55 | r.Spec.TrustedPeers = []string{"*"} 56 | } 57 | 58 | r.DefaultResources() 59 | } 60 | -------------------------------------------------------------------------------- /clients/ethereum2/lighthouse_validator_client_test.go: -------------------------------------------------------------------------------- 1 | package ethereum2 2 | 3 | import ( 4 | ethereum2v1alpha1 "github.com/kotalco/kotal/apis/ethereum2/v1alpha1" 5 | sharedAPI "github.com/kotalco/kotal/apis/shared" 6 | "github.com/kotalco/kotal/controllers/shared" 7 | 8 | . "github.com/onsi/ginkgo/v2" 9 | . "github.com/onsi/gomega" 10 | ) 11 | 12 | var _ = Describe("Lighthouse validator client", func() { 13 | 14 | validator := ðereum2v1alpha1.Validator{ 15 | Spec: ethereum2v1alpha1.ValidatorSpec{ 16 | Client: ethereum2v1alpha1.LighthouseClient, 17 | Network: "mainnet", 18 | BeaconEndpoints: []string{ 19 | "http://localhost:8899", 20 | "http://localhost:9988", 21 | }, 22 | Graffiti: "Validated by Kotal", 23 | Logging: sharedAPI.WarnLogs, 24 | FeeRecipient: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", 25 | }, 26 | } 27 | 28 | validator.Default() 29 | client, _ := NewClient(validator) 30 | 31 | It("Should get correct command", func() { 32 | Expect(client.Command()).To(ConsistOf("lighthouse", "vc")) 33 | }) 34 | 35 | It("Should get correct env", func() { 36 | Expect(client.Env()).To(BeNil()) 37 | }) 38 | 39 | It("Should get correct home dir", func() { 40 | Expect(client.HomeDir()).To(Equal(LighthouseHomeDir)) 41 | }) 42 | 43 | It("Should generate correct client arguments", func() { 44 | args := client.Args() 45 | 46 | Expect(args).To(ContainElements([]string{ 47 | LighthouseDataDir, 48 | shared.PathData(client.HomeDir()), 49 | LighthouseNetwork, 50 | "mainnet", 51 | LighthouseBeaconNodeEndpoints, 52 | "http://localhost:8899,http://localhost:9988", 53 | LighthouseGraffiti, 54 | "Validated by Kotal", 55 | LighthouseDebugLevel, 56 | string(sharedAPI.WarnLogs), 57 | LighthouseFeeRecipient, 58 | "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", 59 | })) 60 | }) 61 | 62 | }) 63 | -------------------------------------------------------------------------------- /apis/aptos/v1alpha1/node_defaulting_webhook.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | "sigs.k8s.io/controller-runtime/pkg/webhook" 5 | ) 6 | 7 | // +kubebuilder:webhook:path=/mutate-aptos-kotal-io-v1alpha1-node,mutating=true,failurePolicy=fail,groups=aptos.kotal.io,resources=nodes,verbs=create;update,versions=v1alpha1,name=mutate-aptos-v1alpha1-node.kb.io,sideEffects=None,admissionReviewVersions=v1 8 | 9 | var _ webhook.Defaulter = &Node{} 10 | 11 | func (r *Node) DefaultNodeResources() { 12 | 13 | if r.Spec.Resources.CPU == "" { 14 | r.Spec.Resources.CPU = DefaultNodeCPURequest 15 | } 16 | 17 | if r.Spec.Resources.CPULimit == "" { 18 | r.Spec.Resources.CPULimit = DefaultNodeCPULimit 19 | } 20 | 21 | if r.Spec.Resources.Memory == "" { 22 | r.Spec.Resources.Memory = DefaultNodeMemoryRequest 23 | } 24 | 25 | if r.Spec.Resources.MemoryLimit == "" { 26 | r.Spec.Resources.MemoryLimit = DefaultNodeMemoryLimit 27 | } 28 | 29 | if r.Spec.Resources.Storage == "" { 30 | r.Spec.Resources.Storage = DefaultNodeStorageRequest 31 | } 32 | 33 | } 34 | 35 | // Default implements webhook.Defaulter so a webhook will be registered for the type 36 | func (r *Node) Default() { 37 | nodelog.Info("default", "name", r.Name) 38 | 39 | r.DefaultNodeResources() 40 | 41 | if r.Spec.Image == "" { 42 | r.Spec.Image = DefaultAptosCoreImage 43 | } 44 | 45 | if r.Spec.Replicas == nil { 46 | // constants are not addressable 47 | replicas := DefaltReplicas 48 | r.Spec.Replicas = &replicas 49 | } 50 | 51 | if r.Spec.MetricsPort == 0 { 52 | r.Spec.MetricsPort = DefaultMetricsPort 53 | } 54 | 55 | if r.Spec.APIPort == 0 { 56 | r.Spec.APIPort = DefaultAPIPort 57 | } 58 | 59 | if r.Spec.P2PPort == 0 { 60 | if r.Spec.Validator { 61 | r.Spec.P2PPort = DefaultValidatorP2PPort 62 | } else { 63 | r.Spec.P2PPort = DefaultFullnodeP2PPort 64 | } 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /config/samples/ethereum/ethereum_v1alpha1_pow_nethermind_node.yaml: -------------------------------------------------------------------------------- 1 | # WARNING: DON'T use the following secrets in production 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: pow-nethermind-account-key 6 | stringData: 7 | # address 0x1990E5760d9f8Ae0ec55dF8B0819C77e59846Ff2 8 | key: bb30a68be4d1f8b54755398f81704fcbd00df4aa7d3cad02547d61291ac252b0 9 | --- 10 | apiVersion: v1 11 | kind: Secret 12 | metadata: 13 | name: pow-nethermind-account-password 14 | stringData: 15 | password: secret 16 | --- 17 | apiVersion: ethereum.kotal.io/v1alpha1 18 | kind: Node 19 | metadata: 20 | name: pow-nethermind-node 21 | spec: 22 | ########### Genesis block spec ########### 23 | genesis: 24 | chainId: 20189 25 | networkId: 20189 26 | ethash: {} 27 | forks: 28 | homestead: 0 29 | eip150: 0 30 | eip155: 0 31 | eip158: 0 32 | byzantium: 0 33 | constantinople: 0 34 | petersburg: 0 35 | istanbul: 0 36 | muirglacier: 0 37 | berlin: 0 38 | london: 0 39 | arrowGlacier: 0 40 | coinbase: "0x071e2c1067c24607ff00ceebbe83a38063bdedd8" 41 | difficulty: "0x1" 42 | gasLimit: "0x47b760" 43 | nonce: "0x0" 44 | timestamp: "0x5f69fcd9" 45 | accounts: 46 | - address: "0x48c5F25a884116d58A6287B72C9b069F936C9489" 47 | balance: "0xffffffffffffffffffff" 48 | ########### node spec ########### 49 | client: nethermind 50 | logging: warn 51 | miner: true 52 | coinbase: "0x1990E5760d9f8Ae0ec55dF8B0819C77e59846Ff2" 53 | import: 54 | privateKeySecretName: pow-nethermind-account-key 55 | passwordSecretName: pow-nethermind-account-password 56 | syncMode: full 57 | staticNodes: 58 | - "enode://e578d440680b323545a4eb0a9c792ec1921bee933efa81238cf78c89768d15d1b1a817515825f9ab5f9cc8398ffad656e1d816e232ac30a0ab53b83f6125d946@10.96.232.189:30303" 59 | resources: 60 | cpu: "1" 61 | memory: "1Gi" 62 | -------------------------------------------------------------------------------- /apis/ethereum2/v1alpha1/client.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import "github.com/kotalco/kotal/apis/shared" 4 | 5 | // Ethereum2Client is Ethereum 2.0 client 6 | // +kubebuilder:validation:Enum=teku;prysm;lighthouse;nimbus 7 | type Ethereum2Client string 8 | 9 | const ( 10 | // TekuClient is ConsenSys Pegasys Ethereum 2.0 client 11 | TekuClient Ethereum2Client = "teku" 12 | // PrysmClient is Prysmatic Labs Ethereum 2.0 client 13 | PrysmClient Ethereum2Client = "prysm" 14 | // LighthouseClient is SigmaPrime Ethereum 2.0 client 15 | LighthouseClient Ethereum2Client = "lighthouse" 16 | // NimbusClient is Status Ethereum 2.0 client 17 | NimbusClient Ethereum2Client = "nimbus" 18 | ) 19 | 20 | func (client Ethereum2Client) SupportsVerbosityLevel(level shared.VerbosityLevel, validator bool) bool { 21 | switch client { 22 | 23 | case TekuClient: 24 | switch level { 25 | case shared.NoLogs, 26 | shared.FatalLogs, 27 | shared.ErrorLogs, 28 | shared.WarnLogs, 29 | shared.InfoLogs, 30 | shared.DebugLogs, 31 | shared.TraceLogs, 32 | shared.AllLogs: 33 | return true 34 | } 35 | 36 | case PrysmClient: 37 | switch level { 38 | case shared.TraceLogs, 39 | shared.DebugLogs, 40 | shared.InfoLogs, 41 | shared.WarnLogs, 42 | shared.ErrorLogs, 43 | shared.FatalLogs, 44 | shared.PanicLogs: 45 | return true 46 | } 47 | 48 | case LighthouseClient: 49 | switch level { 50 | case shared.InfoLogs, 51 | shared.DebugLogs, 52 | shared.TraceLogs, 53 | shared.WarnLogs, 54 | shared.ErrorLogs, 55 | shared.CriticalLogs: 56 | return true 57 | } 58 | 59 | case NimbusClient: 60 | switch level { 61 | case shared.TraceLogs, 62 | shared.DebugLogs, 63 | shared.InfoLogs, 64 | shared.NoticeLogs, 65 | shared.WarnLogs, 66 | shared.ErrorLogs, 67 | shared.FatalLogs, 68 | shared.NoneLogs: 69 | return true 70 | } 71 | } 72 | 73 | return false 74 | } 75 | -------------------------------------------------------------------------------- /apis/ipfs/v1alpha1/peer_defaulting_webhook.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import "sigs.k8s.io/controller-runtime/pkg/webhook" 4 | 5 | // +kubebuilder:webhook:path=/mutate-ipfs-kotal-io-v1alpha1-peer,mutating=true,failurePolicy=fail,groups=ipfs.kotal.io,resources=peers,verbs=create;update,versions=v1alpha1,name=mutate-ipfs-v1alpha1-peer.kb.io,sideEffects=None,admissionReviewVersions=v1 6 | 7 | var _ webhook.Defaulter = &Peer{} 8 | 9 | // DefaultPeerResources defaults peer resources 10 | func (r *Peer) DefaultPeerResources() { 11 | if r.Spec.Resources.CPU == "" { 12 | r.Spec.Resources.CPU = DefaultNodeCPURequest 13 | } 14 | 15 | if r.Spec.Resources.CPULimit == "" { 16 | r.Spec.Resources.CPULimit = DefaultNodeCPULimit 17 | } 18 | 19 | if r.Spec.Resources.Memory == "" { 20 | r.Spec.Resources.Memory = DefaultNodeMemoryRequest 21 | } 22 | 23 | if r.Spec.Resources.MemoryLimit == "" { 24 | r.Spec.Resources.MemoryLimit = DefaultNodeMemoryLimit 25 | } 26 | 27 | if r.Spec.Resources.Storage == "" { 28 | r.Spec.Resources.Storage = DefaultNodeStorageRequest 29 | } 30 | } 31 | 32 | // Default implements webhook.Defaulter so a webhook will be registered for the type 33 | func (r *Peer) Default() { 34 | peerlog.Info("default", "name", r.Name) 35 | 36 | if r.Spec.Image == "" { 37 | r.Spec.Image = DefaultGoIPFSImage 38 | } 39 | 40 | if r.Spec.Replicas == nil { 41 | // constants are not addressable 42 | replicas := DefaltReplicas 43 | r.Spec.Replicas = &replicas 44 | } 45 | 46 | if r.Spec.Routing == "" { 47 | r.Spec.Routing = DefaultRoutingMode 48 | } 49 | 50 | if r.Spec.APIPort == 0 { 51 | r.Spec.APIPort = DefaultAPIPort 52 | } 53 | 54 | if r.Spec.GatewayPort == 0 { 55 | r.Spec.GatewayPort = DefaultGatewayPort 56 | } 57 | 58 | if len(r.Spec.InitProfiles) == 0 { 59 | r.Spec.InitProfiles = []Profile{DefaultDatastoreProfile} 60 | } 61 | 62 | if r.Spec.Logging == "" { 63 | r.Spec.Logging = DefaultLogging 64 | } 65 | 66 | r.DefaultPeerResources() 67 | 68 | } 69 | --------------------------------------------------------------------------------