66aefd6161
In bash with 'set -u', an undefined variable triggers an unbound-variable
error. Functions don't satisfy '$FUNCNAME' expansion under set -u, so
the previous fix that kept '$PKG_INSTALL pkg...' as the call syntax
broke every call site:
docker.sh: line 53: PKG_INSTALL: unbound variable
Fix: drop the leading '$' in all 47 call sites across 6 generator
files. 'PKG_INSTALL pkg...' is a normal command/function lookup and
behaves identically under set -u or not.
Verified: web UI served docker.sh now has 'PKG_INSTALL' (no $);
'bash -n' passes; smoke run with stubbed PATH hits real line-53 call
site, mock apt-get receives correct args, exit 0, no unbound variable.
Sampled 6 other generators also syntax-OK with $PKG_INSTALL count 0.
213 lines
10 KiB
Python
213 lines
10 KiB
Python
"""
|
|
Extra databases (Memcached, SQLite, Elasticsearch, ClickHouse).
|
|
"""
|
|
from . import register, Generator, Field
|
|
from . import bash_header, quote, bool_str, yes
|
|
|
|
|
|
# ========================== Memcached ==========================
|
|
class Memcached(Generator):
|
|
id = "memcached"
|
|
title = "Memcached"
|
|
category = "databases"
|
|
icon = "🧠"
|
|
tags = ["memcached", "cache"]
|
|
description = "安装 memcached 并配置监听地址/内存限制/连接数。"
|
|
fields = [
|
|
Field("port", "端口", "number", default="11211", min_=1, max_=65535),
|
|
Field("memory_mb", "最大内存 (MB)", "number", default="256"),
|
|
Field("max_connections", "最大连接数", "number", default="1024"),
|
|
Field("listen", "监听地址", "text", default="0.0.0.0"),
|
|
]
|
|
|
|
def render(self, p):
|
|
port = str(p.get("port", "11211"))
|
|
mem = str(p.get("memory_mb", "256"))
|
|
conn = str(p.get("max_connections", "1024"))
|
|
listen = p.get("listen", "0.0.0.0")
|
|
out = [bash_header(self.title)]
|
|
out.append('log "Installing memcached..."')
|
|
out.append('PKG_INSTALL memcached')
|
|
out.append('cp /etc/memcached.conf /etc/memcached.conf.bak.$(date +%s) || true')
|
|
out.append('sed -i "s/^-m .*/-m ' + mem + '/" /etc/memcached.conf')
|
|
out.append('sed -i "s/^-p .*/-p ' + port + '/" /etc/memcached.conf')
|
|
out.append('sed -i "s/^-l .*/-l ' + listen + '/" /etc/memcached.conf')
|
|
out.append('sed -i "s/^-c .*/-c ' + conn + '/" /etc/memcached.conf')
|
|
out.append('systemctl enable --now memcached')
|
|
out.append('echo "stats" | nc -q1 127.0.0.1 ' + port + ' | head -5')
|
|
return "\n".join(out) + "\n"
|
|
|
|
|
|
# ========================== SQLite3 (system tools, no server) ==========================
|
|
class SqliteTools(Generator):
|
|
id = "sqlite"
|
|
title = "SQLite 命令行工具"
|
|
category = "databases"
|
|
icon = "📁"
|
|
tags = ["sqlite"]
|
|
description = "安装最新版 sqlite3 + sqlite-utils。"
|
|
fields = [
|
|
Field("install_python_bindings", "安装 Python sqlite-utils", "checkbox", default="yes"),
|
|
]
|
|
|
|
def render(self, p):
|
|
pyb = bool_str(p.get("install_python_bindings", True))
|
|
out = [bash_header(self.title)]
|
|
out.append('log "Installing sqlite3..."')
|
|
out.append('PKG_INSTALL sqlite3 libsqlite3-dev || PKG_INSTALL sqlite sqlite-devel')
|
|
if pyb:
|
|
out.append('command -v pip3 >/dev/null || PKG_INSTALL python3-pip')
|
|
out.append('pip3 install --break-system-packages sqlite-utils || pip3 install sqlite-utils')
|
|
out.append('sqlite3 --version')
|
|
return "\n".join(out) + "\n"
|
|
|
|
|
|
# ========================== Elasticsearch ==========================
|
|
class Elasticsearch(Generator):
|
|
id = "elasticsearch"
|
|
title = "Elasticsearch"
|
|
category = "databases"
|
|
icon = "🔍"
|
|
tags = ["elasticsearch", "es", "search"]
|
|
description = "安装 Elasticsearch (单节点开发模式)。"
|
|
warnings = [
|
|
"ES JVM 默认 1G,小内存机器请调小 -Xms -Xmx。",
|
|
"生产环境必须设置 xpack.security.enabled=true。",
|
|
]
|
|
fields = [
|
|
Field("version", "版本", "select", default="8.13.4",
|
|
options=["7.17.20", "8.11.4", "8.13.4", "8.14.3"]),
|
|
Field("port", "HTTP 端口", "number", default="9200", min_=1, max_=65535),
|
|
Field("jvm_heap", "JVM 堆", "text", default="1g"),
|
|
Field("discovery", "discovery.type", "select", default="single-node",
|
|
options=["single-node"]),
|
|
Field("enable_security", "启用 xpack security", "checkbox", default="no"),
|
|
]
|
|
|
|
def render(self, p):
|
|
ver = p.get("version", "8.13.4")
|
|
port = str(p.get("port", "9200"))
|
|
heap = p.get("jvm_heap", "1g")
|
|
sec = bool_str(p.get("enable_security"))
|
|
out = [bash_header(self.title)]
|
|
out.append('log "Installing Elasticsearch ' + ver + '..."')
|
|
out.append('useradd -r -s /bin/false elasticsearch 2>/dev/null || true')
|
|
out.append('cd /tmp')
|
|
out.append('curl -fsSL -O https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-' + ver + '-linux-x86_64.tar.gz')
|
|
out.append('rm -rf /opt/elasticsearch')
|
|
out.append('tar -xzf elasticsearch-' + ver + '-linux-x86_64.tar.gz -C /opt/')
|
|
out.append('mv /opt/elasticsearch-' + ver + ' /opt/elasticsearch')
|
|
out.append('rm -f elasticsearch-' + ver + '-linux-x86_64.tar.gz')
|
|
out.append('chown -R elasticsearch:elasticsearch /opt/elasticsearch')
|
|
out.append('sed -i "s/^-Xms.*/-Xms' + heap + '/" /opt/elasticsearch/config/jvm.options')
|
|
out.append('sed -i "s/^-Xmx.*/-Xmx' + heap + '/" /opt/elasticsearch/config/jvm.options')
|
|
out.append('sed -i "s/^#http.port.*/http.port: ' + port + '/" /opt/elasticsearch/config/elasticsearch.yml')
|
|
out.append('echo "discovery.type: single-node" >> /opt/elasticsearch/config/elasticsearch.yml')
|
|
out.append('echo "network.host: 0.0.0.0" >> /opt/elasticsearch/config/elasticsearch.yml')
|
|
out.append('echo "xpack.security.enabled: ' + ('true' if sec else 'false') + '" >> /opt/elasticsearch/config/elasticsearch.yml')
|
|
out.append('cat > /etc/systemd/system/elasticsearch.service <<UNIT_EOF\n'
|
|
'[Unit]\nDescription=Elasticsearch\nAfter=network.target\n\n'
|
|
'[Service]\nType=simple\n'
|
|
'User=elasticsearch\nGroup=elasticsearch\n'
|
|
'LimitNOFILE=65536\n'
|
|
'Environment=ES_HOME=/opt/elasticsearch\n'
|
|
'Environment=ES_PATH_CONF=/opt/elasticsearch/config\n'
|
|
'ExecStart=/opt/elasticsearch/bin/elasticsearch\n'
|
|
'Restart=on-failure\n\n'
|
|
'[Install]\nWantedBy=multi-user.target\nUNIT_EOF')
|
|
out.append('systemctl daemon-reload')
|
|
out.append('systemctl enable --now elasticsearch')
|
|
out.append('log "Wait ~30s then: curl http://127.0.0.1:' + port + '"')
|
|
return "\n".join(out) + "\n"
|
|
|
|
|
|
# ========================== ClickHouse ==========================
|
|
class ClickHouse(Generator):
|
|
id = "clickhouse"
|
|
title = "ClickHouse"
|
|
category = "databases"
|
|
icon = "🏛️"
|
|
tags = ["clickhouse", "olap", "columnar"]
|
|
description = "安装 ClickHouse 服务端 + 客户端。"
|
|
fields = [
|
|
Field("tcp_port", "TCP 端口", "number", default="9000"),
|
|
Field("http_port", "HTTP 端口", "number", default="8123"),
|
|
Field("listen_host", "Listen host", "text", default="0.0.0.0"),
|
|
Field("data_path", "数据目录", "text", default="/var/lib/clickhouse"),
|
|
]
|
|
|
|
def render(self, p):
|
|
tcp = str(p.get("tcp_port", "9000"))
|
|
http = str(p.get("http_port", "8123"))
|
|
lh = p.get("listen_host", "0.0.0.0")
|
|
dp = p.get("data_path", "/var/lib/clickhouse")
|
|
out = [bash_header(self.title)]
|
|
out.append('log "Installing ClickHouse..."')
|
|
out.append('PKG_INSTALL apt-transport-https ca-certificates curl || PKG_INSTALL ca-certificates curl')
|
|
out.append('mkdir -p /etc/apt/keyrings')
|
|
out.append('curl -fsSL https://clickhouse.com/keys/clickhouse.asc | gpg --dearmor -o /etc/apt/keyrings/clickhouse.gpg')
|
|
out.append('echo "deb [signed-by=/etc/apt/keyrings/clickhouse.gpg] https://packages.clickhouse.com/deb stable main" > /etc/apt/sources.list.d/clickhouse.list')
|
|
out.append('apt-get update')
|
|
out.append('DEBIAN_FRONTEND=noninteractive apt-get install -y clickhouse-server clickhouse-client')
|
|
out.append('mkdir -p ' + dp)
|
|
out.append('chown -R clickhouse:clickhouse ' + dp)
|
|
out.append('sed -i "s|<tcp_port>.*</tcp_port>|<tcp_port>' + tcp + '</tcp_port>|" /etc/clickhouse-server/config.xml')
|
|
out.append('sed -i "s|<http_port>.*</http_port>|<http_port>' + http + '</http_port>|" /etc/clickhouse-server/config.xml')
|
|
out.append('sed -i "s|<listen_host>.*</listen_host>|<listen_host>' + lh + '</listen_host>|" /etc/clickhouse-server/config.xml')
|
|
out.append('sed -i "s|<path>.*</path>|<path>' + dp + '</path>|" /etc/clickhouse-server/config.xml')
|
|
out.append('systemctl enable --now clickhouse-server')
|
|
out.append('sleep 3')
|
|
out.append('clickhouse-client -q "SELECT version()"')
|
|
return "\n".join(out) + "\n"
|
|
|
|
|
|
# ========================== Consul (service mesh) ==========================
|
|
class Consul(Generator):
|
|
id = "consul"
|
|
title = "HashiCorp Consul"
|
|
category = "middleware"
|
|
icon = "🌐"
|
|
tags = ["consul", "service-discovery"]
|
|
description = "安装 Consul 单节点开发模式。"
|
|
fields = [
|
|
Field("version", "Consul 版本", "text", default="1.18.1"),
|
|
Field("datacenter", "Datacenter", "text", default="dc1"),
|
|
Field("ui", "启用 Web UI", "checkbox", default="yes"),
|
|
]
|
|
|
|
def render(self, p):
|
|
ver = p.get("version", "1.18.1")
|
|
dc = p.get("datacenter", "dc1")
|
|
ui = bool_str(p.get("ui", True))
|
|
out = [bash_header(self.title)]
|
|
out.append('useradd -r -s /bin/false consul 2>/dev/null || true')
|
|
out.append('mkdir -p /etc/consul.d /var/lib/consul')
|
|
out.append('cd /tmp')
|
|
out.append('curl -fsSL -O https://releases.hashicorp.com/consul/' + ver + '/consul_' + ver + '_linux_amd64.zip')
|
|
out.append('unzip -o consul_' + ver + '_linux_amd64.zip -d /usr/local/bin/')
|
|
out.append('rm -f consul_' + ver + '_linux_amd64.zip')
|
|
out.append('chown -R consul:consul /etc/consul.d /var/lib/consul')
|
|
out.append('cat > /etc/consul.d/consul.hcl <<CFG_EOF\n'
|
|
'datacenter = "' + dc + '"\n'
|
|
'data_dir = "/var/lib/consul"\n'
|
|
'client_addr = "0.0.0.0"\n'
|
|
'ui_config { enabled = ' + ('true' if ui else 'false') + ' }\n'
|
|
'server = true\n'
|
|
'bootstrap_expect = 1\n'
|
|
'CFG_EOF')
|
|
out.append('cat > /etc/systemd/system/consul.service <<UNIT_EOF\n'
|
|
'[Unit]\nDescription=HashiCorp Consul\nAfter=network.target\n\n'
|
|
'[Service]\nType=simple\nUser=consul\nGroup=consul\n'
|
|
'ExecStart=/usr/local/bin/consul agent -config-dir=/etc/consul.d/\n'
|
|
'Restart=on-failure\nLimitNOFILE=65536\n\n'
|
|
'[Install]\nWantedBy=multi-user.target\nUNIT_EOF')
|
|
out.append('systemctl daemon-reload')
|
|
out.append('systemctl enable --now consul')
|
|
out.append('sleep 3')
|
|
out.append('consul members || true')
|
|
return "\n".join(out) + "\n"
|
|
|
|
|
|
for _g in [Memcached, SqliteTools, Elasticsearch, ClickHouse, Consul]:
|
|
register(_g())
|