Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
RuslanGlaznyov committed May 29, 2024
0 parents commit ca20136
Show file tree
Hide file tree
Showing 19 changed files with 693 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
resources/playbook/env/
venv/
53 changes: 53 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# CRUN

CRUN is a simple command line tool to spin up blockchain node. It is designed to be simple and easy to use.

## Installation

To install simply download the latest release from the [releases page]()

## Usage


### Install a blockchain node example

```bash
crun install lava_testnet
```

### Change default settings

1. Show the current settings for network

```bash
crun show lava_testnet
```

2. You can check how setting will be changed by running the following command

```bash
crun show lava_testnet -e "install_from=state_sync"
```

3. Run install with new settings

```bash
crun install lava_testnet -e "install_from=state_sync"
```

## Install crun from source

1. Clone the repository

2. Create venv and install dependencies

```bash
python3 -m venv venv
source venv/bin/activate
pip install .
```
3. Run crun

```bash
python crun.py
```
141 changes: 141 additions & 0 deletions crun.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import os
import sys
# set the path for ansible
new_path = sys.exec_prefix + '/bin'
current_path = os.environ.get('PATH', '')
os.environ['PATH'] = new_path + os.pathsep + current_path

import json

from pygments import highlight
from pygments.formatters import TerminalFormatter
from pygments.lexers import PythonLexer
from pygments.formatters import TerminalFormatter

import click
import tempfile
from pathlib import Path
import yaml
import ansible_runner

def pretty_print_with_highlight(data):
"""Pretty print a Python dictionary with syntax highlighting."""
# Convert the dictionary to a formatted string
formatted_str = json.dumps(data, indent=4)
# Apply syntax highlighting using Pygments
return highlight(formatted_str, PythonLexer(), TerminalFormatter())

def running_from_pex() -> bool:
pex_root = os.environ.get("PEX_ROOT", os.path.expanduser("~/.pex"))
return any([pex_root in p for p in sys.path])

def print_error(msg):
click.echo(click.style(msg, fg='red'), err=True)

def convert_extra_vars(extra_vars):
extra_vars_dict = {}
extra_vars = filter(lambda x: x.strip() != '', extra_vars.split(';'))

for var in extra_vars:
key, value = var.strip().split('=')
if value.strip().isdigit():
value = int(value)
if type(value) == str and value.strip().lower() == 'true':
value = True
if type(value) == str and value.strip().lower() == 'false':
value = False
extra_vars_dict[key] = value
return extra_vars_dict

default_vars = {
'node_name': "CRUN_MELLIFERA_NODE",
'download_cosmovisor_url': 'https://storage.mellifera.network/bins/general/cosmovisor',
'download_cosmovisor': True,
'install_from': 'snapshot'
}

def get_playbook_folder():
is_pex = running_from_pex()
current_dir = Path(__file__).parent
if is_pex:
return current_dir / 'playbook'
else:
return current_dir / 'resources/playbook'

@click.group()
def cli():
pass

@cli.command()
def list():
playbook_folder = get_playbook_folder()
list = os.listdir(playbook_folder / 'group_vars')
click.echo('List of available networks:')
click.echo('---------------------------')

for network in list:
netname = network.split('.')[0]
if netname != 'all':
click.echo(netname)

def get_config_by_netname(netname):
playbook_folder = get_playbook_folder()
list_files = os.listdir(playbook_folder / 'group_vars')
netnames = [{ "name": network.split('.')[0], "file": network } for network in list_files]

if netname not in map(lambda x: x['name'], netnames):
return None
netfile = next(filter(lambda x: x['name'] == netname, netnames))['file']

with open(playbook_folder / 'group_vars' / netfile) as f:
content = f.read()
return yaml.full_load(content)

@cli.command()
@click.argument('netname')
@click.option('--extra-vars','-e', type=str , help="Show the configuration with the extra vars. Example: --extra-vars 'node_name=MELLIFERA;use_state_sync=true'")
def show(netname, extra_vars):
ext = {}
if extra_vars is not None:
ext = convert_extra_vars(extra_vars)

config = get_config_by_netname(netname)
if config is None:
print_error('Network %s not found' % netname)
return
final_config = {**default_vars, **config, **ext}
click.echo('------ %s config ------' % netname)
click.echo(pretty_print_with_highlight(final_config))
click.echo('------ %s config ------' % netname)

@cli.command()
@click.argument('network_name')
@click.option('--extra-vars','-e',
type=str ,
help="Run the installation with the overrided vars. Example: --extra-vars 'node_name=MELLIFERA;install_from=state_sync'")
def install(network_name, extra_vars):
playbook_folder = get_playbook_folder()
ext = {}
if extra_vars is not None:
ext = convert_extra_vars(extra_vars)
config = get_config_by_netname(network_name)
if config is None:
print_error('Network %s not found' % network_name)
return
final_config = {**default_vars, **config, **ext}
if os.path.exists(final_config['cosmos_folder']):
print_error('Cosmos folder %s already exists. Please remove it before running the installation or change "cosmos_folder" var with --extra-var option' % final_config['cosmos_folder'])
return

if os.geteuid() != 0:
print_error('Installation requires root privileges. Creating services file and start systemd unit.\nPlease run: sudo crun install %s' % network_name)
return

click.echo('Running installation with the following configuration:')
click.echo(pretty_print_with_highlight(final_config))
with tempfile.TemporaryDirectory() as temp_dir_artifacts:
ansible_runner.run(private_data_dir=playbook_folder, artifact_dir=temp_dir_artifacts, playbook='local.yml', passwords={}, extravars=final_config)


if __name__ == '__main__':
cli()
23 changes: 23 additions & 0 deletions resources/playbook/group_vars/lava_testnet.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
chain_id: lava-testnet-2
netname: lava_testnet
repo: https://github.com/lavanet/lava.git
version: v2.0.0
binary: lavad
genesis: https://storage.mellifera.network/lava_testnet/genesis.json
addressbook: https://storage.mellifera.network/lava_testnet/addrbook.json
snapshot: https://storage.mellifera.network/lava_testnet/snapshot_latest.tar.lz4
go_version: go1.20.14
cosmos_folder: '.lava'
seeds: "[email protected]:14456"
peers: [email protected]:24656,[email protected]:656,[email protected]:14456,[email protected]:14456,[email protected]:14456,[email protected]:19956,[email protected]:24656,[email protected]:23656,[email protected]:27066,[email protected]:36656,[email protected]:26656,[email protected]:17656,[email protected]:24656,[email protected]:21156,[email protected]:14456,[email protected]:19656
custom_port_prefix: 199
service_name: lavad
download_binary_url: "https://storage.mellifera.network/bins/lava-testnet/{{ version }}/lavad"

state_sync_rpc: "https://lava-testnet-rpc.mellifera.network:443"
state_sync_peer: "[email protected]:14456"

download_binary: true
download_cosmovisor: true


10 changes: 10 additions & 0 deletions resources/playbook/local.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
- name: Install node locally
hosts: localhost
become: true # Enables privilege escalation
roles:
- cosmos_software
- cosmos_install
- cosmos_init
- cosmos_configure
- cosmos_start

81 changes: 81 additions & 0 deletions resources/playbook/roles/cosmos_configure/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
---
- name: register public ip
uri:
url: 'https://api.ipify.org?format=json'
register: public_ip

- name: Set up external address
lineinfile:
path: '{{ ansible_env.HOME }}/{{ cosmos_folder }}/config/config.toml'
regexp: 'external_address = "'
line: 'external_address = "{{ public_ip.json.ip }}:{{ custom_port_prefix }}56"'
state: present

- name: Adjust config.toml port
lineinfile:
path: '{{ ansible_env.HOME }}/{{ cosmos_folder }}/config/config.toml'
regexp: '{{ item.key }}'
line: '{{ item.value }}'
loop: '{{ config_port_changes | dict2items }}'

- name: Adjust app.toml ports
lineinfile:
path: '{{ ansible_env.HOME }}/{{ cosmos_folder }}/config/app.toml'
regexp: '{{ item.key }}'
line: '{{ item.value }}'
backrefs: true
loop: '{{ app_port_changes | dict2items }}'

- name: Set pruning settings
lineinfile:
path: '{{ ansible_env.HOME }}/{{ cosmos_folder }}/config/app.toml'
regexp: '{{ item.key }}'
line: '{{ item.value }}'
loop: '{{ pruning_default | dict2items }}'
when: type is not defined

- name: Adjust pruning setting for relayer
lineinfile:
path: '{{ ansible_env.HOME }}/{{ cosmos_folder }}/config/app.toml'
regexp: '{{ item.key }}'
line: '{{ item.value }}'
loop: '{{ pruning_relayer | dict2items }}'
when: type is defined and type == 'relayer'

- name: Dowload addrbook
ansible.builtin.get_url:
url: '{{ addrbook }}'
dest: '{{ ansible_env.HOME }}/{{ cosmos_folder }}/config/addrbook.json'
mode: '0644'
force: true
when: addrbook is defined

- name: Update peers in config.toml file
lineinfile:
path: '{{ ansible_env.HOME }}/{{ cosmos_folder }}/config/config.toml'
regexp: '^persistent_peers ='
line: 'persistent_peers = "{{ peers }}"'
state: present
when: peers is defined

- name: Update seeds in config.toml file
lineinfile:
path: '{{ ansible_env.HOME }}/{{ cosmos_folder}}/config/config.toml'
regexp: '^seeds ='
line: 'seeds = "{{ seeds }}"'
state: present
when: seeds is defined

- name: Check if after config script exists
ansible.builtin.stat:
path: '{{ role_path }}/templates/after_config_{{ netname }}.sh.j2'
register: after_config_script
delegate_to: localhost

- name: execute after config script
shell: "{{ lookup('template', 'after_config_{{ netname }}.sh.j2') }}"
args:
chdir: '{{ ansible_env.HOME }}/{{ cosmos_folder }}'
executable: /bin/bash
when: after_config_script.stat.exists

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
sed -i \
-e 's/timeout_commit = ".*"/timeout_commit = "30s"/g' \
-e 's/timeout_propose = ".*"/timeout_propose = "1s"/g' \
-e 's/timeout_precommit = ".*"/timeout_precommit = "1s"/g' \
-e 's/timeout_precommit_delta = ".*"/timeout_precommit_delta = "500ms"/g' \
-e 's/timeout_prevote = ".*"/timeout_prevote = "1s"/g' \
-e 's/timeout_prevote_delta = ".*"/timeout_prevote_delta = "500ms"/g' \
-e 's/timeout_propose_delta = ".*"/timeout_propose_delta = "500ms"/g' \
-e 's/skip_timeout_commit = ".*"/skip_timeout_commit = false/g' \
{{ ansible_env.HOME }}/{{ cosmos_folder }}/config/config.toml
24 changes: 24 additions & 0 deletions resources/playbook/roles/cosmos_configure/vars/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
config_port_changes:
'laddr = "tcp://0.0.0.0:26656"': 'laddr = "tcp://0.0.0.0:{{ custom_port_prefix }}56"'
'laddr = "tcp://127.0.0.1:26657"': 'laddr = "tcp://0.0.0.0:{{ custom_port_prefix }}57"'
'proxy_app = "tcp://127.0.0.1:26658"': 'proxy_app = "tcp://127.0.0.1:{{ custom_port_prefix }}58"'
'prometheus_listen_addr = ":26660"': 'prometheus_listen_addr = ":{{ custom_port_prefix }}61"'
'pprof_laddr = "localhost:6060"': 'pprof_laddr = "localhost:{{ custom_port_prefix }}60"'

app_port_changes:
':1317': 'address = "tcp://0.0.0.0:{{ custom_port_prefix }}17"'
':8080': 'address = ":{{ custom_port_prefix }}80"'
':9090': 'address = "0.0.0.0:{{ custom_port_prefix }}90"'
':9091': 'address = "0.0.0.0:{{ custom_port_prefix }}91"'

pruning_default:
'pruning = "': 'pruning = "custom"'
'pruning-keep-recent = "': 'pruning-keep-recent = "100"'
'pruning-interval = "': 'pruning-interval = "19"'

pruning_relay:
'pruning = "': 'pruning = "custom"'
'pruning-keep-recent = "': 'pruning-keep-recent = "100"'
'pruning-interval = "': 'pruning-interval = "19"'
'snapshot-interval = ': 'snapshot-interval = 1000'
'snapshot-keep-recent = ': 'snapshot-keep-recent = 2'
23 changes: 23 additions & 0 deletions resources/playbook/roles/cosmos_init/tasks/main.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
- name: Set cosmos binary
ansible.builtin.set_fact:
cosmos_binary: "{{ ansible_env.HOME }}/.crun/bins/{{ netname }}/{{ version }}/{{ binary }}"
- name: Ensure that the folder does not exist
ansible.builtin.file:
path: '{{ ansible_env.HOME }}/{{ cosmos_folder }}'
state: absent

- name: Init Node
ansible.builtin.command:
cmd: '{{ cosmos_binary }} init {{ node_name }} --home={{ ansible_env.HOME }}/{{ cosmos_folder }} --chain-id {{ chain_id }}'

- name: Copy genesis.json
ansible.builtin.get_url:
url: '{{ genesis }}'
dest: '{{ ansible_env.HOME }}/{{ cosmos_folder }}/config/genesis.json'
mode: '0644'
force: true

- name: Set chain-id
ansible.builtin.command:
cmd: '{{cosmos_binary}} config chain-id {{ chain_id }} --home={{ ansible_env.HOME }}/{{ cosmos_folder }}'
1 change: 1 addition & 0 deletions resources/playbook/roles/cosmos_init/vars/main.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
cosmos_binary: "{{ ansible_env.HOME }}/ansible_build/{{ netname }}/{{ version }}/{{ binary }}"
Loading

0 comments on commit ca20136

Please sign in to comment.