Skip to content

Commit 0418553

Browse files
committed
circuit breaker plugin skeleton setup
1 parent aef4f9c commit 0418553

File tree

6 files changed

+174
-0
lines changed

6 files changed

+174
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Patterns to ignore when building packages.
2+
# This supports shell glob matching, relative path matching, and
3+
# negation (prefixed with !). Only one pattern per line.
4+
.DS_Store
5+
# Common VCS dirs
6+
.git/
7+
.gitignore
8+
.bzr/
9+
.bzrignore
10+
.hg/
11+
.hgignore
12+
.svn/
13+
# Common backup files
14+
*.swp
15+
*.bak
16+
*.tmp
17+
*.orig
18+
*~
19+
# Various IDEs
20+
.project
21+
.idea/
22+
*.tmproj
23+
.vscode/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
apiVersion: v2
2+
name: circuitbreaker
3+
description: A Helm chart to deploy Circuit Breaker
4+
version: 0.1.0
5+
appVersion: "0.1.0"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{{- define "mychart.name" -}}
2+
{{- .Chart.Name | trunc 63 | trimSuffix "-" -}}
3+
{{- end -}}
4+
5+
{{- define "mychart.fullname" -}}
6+
{{- printf "%s-%s" (include "mychart.name" .) .Release.Name | trunc 63 | trimSuffix "-" -}}
7+
{{- end -}}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
apiVersion: v1
2+
kind: Pod
3+
metadata:
4+
name: {{ include "mychart.fullname" . }}
5+
labels:
6+
app: {{ include "mychart.name" . }}
7+
mission: {{ .Values.name }}
8+
spec:
9+
containers:
10+
- name: {{ .Values.name }}
11+
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
12+
imagePullPolicy: {{ .Values.image.pullPolicy }}
13+
command: ["sh", "-c"]
14+
args:
15+
- echo "Hello {{ .Values.mode }}";
16+
resources: {}
17+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
name: "circuitbreaker"
2+
image:
3+
repository: "camillarhi/circuitbreaker"
4+
tag: "0.2.3"
5+
pullPolicy: IfNotPresent
+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
#!/usr/bin/env python3
2+
import json
3+
import logging
4+
from enum import Enum
5+
from pathlib import Path
6+
import time
7+
from typing import Optional
8+
9+
import click
10+
11+
from warnet.constants import PLUGIN_ANNEX, AnnexMember, HookValue, WarnetContent
12+
from warnet.process import run_command
13+
14+
MISSION = "circuitbreaker"
15+
PRIMARY_CONTAINER = MISSION
16+
17+
PLUGIN_DIR_TAG = "plugin_dir"
18+
19+
20+
class PluginError(Exception):
21+
pass
22+
23+
24+
log = logging.getLogger(MISSION)
25+
if not log.hasHandlers():
26+
console_handler = logging.StreamHandler()
27+
console_handler.setLevel(logging.DEBUG)
28+
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
29+
console_handler.setFormatter(formatter)
30+
log.addHandler(console_handler)
31+
log.setLevel(logging.DEBUG)
32+
log.propagate = True
33+
34+
class PluginContent(Enum):
35+
MODE = "mode"
36+
MAX_PENDING_HTLCS = "maxPendingHtlcs"
37+
RATE_LIMIT = "rateLimit"
38+
39+
@click.group()
40+
@click.pass_context
41+
def circuitbreaker(ctx):
42+
"""Commands for the Circuit Breaker plugin"""
43+
ctx.ensure_object(dict)
44+
plugin_dir = Path(__file__).resolve().parent
45+
ctx.obj[PLUGIN_DIR_TAG] = Path(plugin_dir)
46+
47+
48+
@circuitbreaker.command()
49+
@click.argument("plugin_content", type=str)
50+
@click.argument("warnet_content", type=str)
51+
@click.pass_context
52+
def entrypoint(ctx, plugin_content: str, warnet_content: str):
53+
"""Plugin entrypoint"""
54+
plugin_content: dict = json.loads(plugin_content)
55+
warnet_content: dict = json.loads(warnet_content)
56+
57+
hook_value = warnet_content.get(WarnetContent.HOOK_VALUE.value)
58+
59+
assert hook_value in {
60+
item.value for item in HookValue
61+
}, f"{hook_value} is not a valid HookValue"
62+
63+
if warnet_content.get(PLUGIN_ANNEX):
64+
for annex_member in [annex_item for annex_item in warnet_content.get(PLUGIN_ANNEX)]:
65+
assert annex_member in {
66+
item.value for item in AnnexMember
67+
}, f"{annex_member} is not a valid AnnexMember"
68+
69+
warnet_content[WarnetContent.HOOK_VALUE.value] = HookValue(hook_value)
70+
71+
_entrypoint(ctx, plugin_content, warnet_content)
72+
73+
74+
def _entrypoint(ctx, plugin_content: dict, warnet_content: dict):
75+
"""Called by entrypoint"""
76+
hook_value = warnet_content[WarnetContent.HOOK_VALUE.value]
77+
78+
match hook_value:
79+
case (
80+
HookValue.PRE_NETWORK
81+
| HookValue.POST_NETWORK
82+
| HookValue.PRE_DEPLOY
83+
| HookValue.POST_DEPLOY
84+
):
85+
data = get_data(plugin_content)
86+
if data:
87+
_launch_circuit_breaker(ctx, node_name=hook_value.value.lower())
88+
else:
89+
_launch_circuit_breaker(ctx, node_name=hook_value.value.lower())
90+
case HookValue.PRE_NODE:
91+
name = warnet_content[PLUGIN_ANNEX][AnnexMember.NODE_NAME.value] + "-pre-pod"
92+
_launch_circuit_breaker(ctx, node_name=hook_value.value.lower() + "-" + name)
93+
case HookValue.POST_NODE:
94+
name = warnet_content[PLUGIN_ANNEX][AnnexMember.NODE_NAME.value] + "-post-pod"
95+
_launch_circuit_breaker(ctx, node_name=hook_value.value.lower() + "-" + name)
96+
97+
def get_data(plugin_content: dict) -> Optional[dict]:
98+
data = {
99+
key: plugin_content.get(key)
100+
for key in (PluginContent.MAX_PENDING_HTLCS.value, PluginContent.RATE_LIMIT.value)
101+
if plugin_content.get(key)
102+
}
103+
return data or None
104+
105+
106+
def _launch_circuit_breaker(ctx, node_name: str):
107+
timestamp = int(time.time())
108+
release_name = f"cb-{node_name}-{timestamp}"
109+
110+
command = f"helm upgrade --install {node_name} {ctx.obj[PLUGIN_DIR_TAG]}/charts/circuitbreaker --set node={node_name}"
111+
112+
log.info(command)
113+
log.info(run_command(command))
114+
115+
116+
if __name__ == "__main__":
117+
circuitbreaker()

0 commit comments

Comments
 (0)