diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..76b7895 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +.lighthouse +charts/* +preview/* +test-net-faucet-bot-config.yaml +test-net-faucet-bot-secret.yaml \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6019ffc --- /dev/null +++ b/.gitignore @@ -0,0 +1,156 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ +test-net-faucet-bot-config.yaml +test-net-faucet-bot-secret.yaml +agung-faucet-bot-config.yaml +agung-faucet-bot-secret.yaml \ No newline at end of file diff --git a/.lighthouse/jenkins-x/pullrequest.yaml b/.lighthouse/jenkins-x/pullrequest.yaml new file mode 100644 index 0000000..0719262 --- /dev/null +++ b/.lighthouse/jenkins-x/pullrequest.yaml @@ -0,0 +1,41 @@ +apiVersion: tekton.dev/v1beta1 +kind: PipelineRun +metadata: + creationTimestamp: null + name: pullrequest +spec: + pipelineSpec: + tasks: + - name: from-build-pack + resources: {} + taskSpec: + metadata: {} + stepTemplate: + image: uses:jenkins-x/jx3-pipeline-catalog/tasks/python/pullrequest.yaml@versionStream + name: "" + resources: + # override limits for all containers here + limits: {} + workingDir: /workspace/source + steps: + - image: uses:jenkins-x/jx3-pipeline-catalog/tasks/git-clone/git-clone-pr.yaml@versionStream + name: "" + resources: {} + - name: jx-variables + resources: {} + # # override requests for the pod here + # requests: + # cpu: 400m + # memory: 512Mi + - name: build-python-unittest + resources: {} + - name: check-registry + resources: {} + - name: build-container-build + resources: {} + - name: promote-jx-preview + resources: {} + podTemplate: {} + serviceAccountName: tekton-bot + timeout: 12h0m0s +status: {} diff --git a/.lighthouse/jenkins-x/release.yaml b/.lighthouse/jenkins-x/release.yaml new file mode 100644 index 0000000..a23b973 --- /dev/null +++ b/.lighthouse/jenkins-x/release.yaml @@ -0,0 +1,47 @@ +apiVersion: tekton.dev/v1beta1 +kind: PipelineRun +metadata: + creationTimestamp: null + name: release +spec: + pipelineSpec: + tasks: + - name: from-build-pack + resources: {} + taskSpec: + metadata: {} + stepTemplate: + image: uses:jenkins-x/jx3-pipeline-catalog/tasks/python/release.yaml@versionStream + name: "" + resources: + # override limits for all containers here + limits: {} + workingDir: /workspace/source + steps: + - image: uses:jenkins-x/jx3-pipeline-catalog/tasks/git-clone/git-clone.yaml@versionStream + name: "" + resources: {} + - name: next-version + resources: {} + - name: jx-variables + resources: {} + # override requests for the pod here + # requests: + # cpu: 400m + # memory: 512Mi + - name: build-python-unittest + resources: {} + - name: check-registry + resources: {} + - name: build-container-build + resources: {} + - name: promote-changelog + resources: {} + - name: promote-helm-release + resources: {} + - name: promote-jx-promote + resources: {} + podTemplate: {} + serviceAccountName: tekton-bot + timeout: 12h0m0s +status: {} diff --git a/.lighthouse/jenkins-x/triggers.yaml b/.lighthouse/jenkins-x/triggers.yaml new file mode 100644 index 0000000..ae2fcfa --- /dev/null +++ b/.lighthouse/jenkins-x/triggers.yaml @@ -0,0 +1,16 @@ +apiVersion: config.lighthouse.jenkins-x.io/v1alpha1 +kind: TriggerConfig +spec: + presubmits: + - name: pr + context: "pr" + always_run: true + optional: false + source: "pullrequest.yaml" + postsubmits: + - name: release + context: "release" + source: "release.yaml" + branches: + - ^main$ + - ^master$ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..3cfe3af --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM python:3.8 + +WORKDIR /app + +COPY ["requirements.txt", "/app/"] +RUN pip install -r requirements.txt + +COPY ["app/faucet.py", "/app/"] + +WORKDIR /app +ENTRYPOINT ["python"] +CMD ["faucet.py"] diff --git a/OWNERS b/OWNERS new file mode 100644 index 0000000..40ced59 --- /dev/null +++ b/OWNERS @@ -0,0 +1,4 @@ +approvers: +- faisalthaheem +reviewers: +- faisalthaheem diff --git a/OWNERS_ALIASES b/OWNERS_ALIASES new file mode 100644 index 0000000..c7b561f --- /dev/null +++ b/OWNERS_ALIASES @@ -0,0 +1,6 @@ +aliases: +- faisalthaheem +best-approvers: +- faisalthaheem +best-reviewers: +- faisalthaheem diff --git a/README.md b/README.md index e842f0b..423297f 100644 --- a/README.md +++ b/README.md @@ -4,18 +4,54 @@ This bot is a really simple bot without security or threshold. It's used to send ## How to use : -- create a discord bot -- create a substrate address -- configure : +A configmap named "test-net-faucet-bot-config" can be used to override the default values as specified in the following table +Please adapt to your target namespace +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-net-faucet-bot-config + namespace: jx-devbr +data: + DISCORD_BOT_TOKEN: very + FAUCET_MNEMONIC: charm ``` -TOKEN = 'Your_Discord_Bot_Token' -``` -``` -faucet_mnemonic = 'Your Substrate Mnemonic' -``` + +Default values are as follows + +|Key|Description| +|---|---| +|DISCORD_BOT_TOKEN|The secret bot token to use for connecting to discord| +|NODE_RPC|wss://wss.test.peaq.network,wss://wss2.test.peaq.network| +|FAUCET_MNEMONIC|use the value from subkey generate,use the value from subkey2 generate| +|TOKENS_TO_SEND|1000000000000000000| +|TOKENS_DECIMAL|1000000000000000000| +|TOKEN_SYMBOL|PEAQ| +|ISSUE_INTERVAL|5| +|REDIS_IP|127.0.0.1| +|REDIS_PORT|6379| +|PROMETHEUS_PORT|8080| + + +Additionally a secret must be created under the name of "test-net-faucet-bot-secret" containing the bot token and key mnemonic. +Remember to change the DISCORD_BOT_TOKEN and FAUCET_MNEMONIC variable's data to actual values. +Use the following command to encode to base64 + +```bash +#encode with disabled line wrapping +echo -n 'secret here' | base64 -w 0 ``` -# usually you run it on the same server than the node -node_rpc = "http://127.0.0.1:9933" + +```yaml +apiVersion: v1 +data: + DISCORD_BOT_TOKEN: bXlwYXNzd29yZA== + FAUCET_MNEMONIC: bXlwYXNzd29yZA== +kind: Secret +metadata: + name: test-net-faucet-bot-secret + namespace: jx-devbr +type: Opaque ``` diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..e0e6347 --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +run.sh \ No newline at end of file diff --git a/app/faucet.py b/app/faucet.py new file mode 100644 index 0000000..f33a73b --- /dev/null +++ b/app/faucet.py @@ -0,0 +1,146 @@ +import os +import time +from threading import Thread +import redis +from discord.ext import commands +from substrateinterface import SubstrateInterface, Keypair +from prometheus_client import start_http_server, Counter + +# prometheus metrics +INVOCATION_COUNT = Counter('invocation_count', 'Number of times tokens have been requested.') +ISSUANCE_TOTAL = Counter('issuance_total', 'Total tokens issued.') +ISSUANCE_THROTTLED = Counter('issuance_throttled', 'Total issuance requests throttled (too quickly requested again).') + + +def notify_exit(): + time.sleep(30) + import sys + sys.exit(1) + + +def get_env_var_or_exit(var_name): + var = os.environ.get(var_name) + + if var is None or len(var) == 0: + print("Please check if [{}] is defined. Will exit after 30s sleep".format(var_name)) + notify_exit() + + return var + + +# Your Discord bot token +DISCORD_BOT_TOKEN = get_env_var_or_exit("DISCORD_BOT_TOKEN") +# the address of our network, seperate by "," and should be "ws" or "wss" +NODE_RPC = get_env_var_or_exit("NODE_RPC") +# to construct the private/public key pair, seperate by "," +FAUCET_MNEMONIC = get_env_var_or_exit("FAUCET_MNEMONIC") +# how many tokens to send, on peaq network 1 PEAQ = 1,000,000,000,000,000,000 +TOKENS_TO_SEND = int(get_env_var_or_exit("TOKENS_TO_SEND")) +# how many tokens in one token, 10 ** 18 +TOKENS_DECIMAL = int(get_env_var_or_exit("TOKENS_DECIMAL")) +# the token symbol +TOKEN_SYMBOL = get_env_var_or_exit("TOKEN_SYMBOL") +# how often can a user ask for a token? in seconds +ISSUE_INTERVAL = int(get_env_var_or_exit("ISSUE_INTERVAL")) +# redis +REDIS_IP = get_env_var_or_exit("REDIS_IP") +REDIS_PORT = int(get_env_var_or_exit("REDIS_PORT")) +# prometheus +PROMETHEUS_PORT = int(get_env_var_or_exit("PROMETHEUS_PORT")) + +# associated address 5CfVS8r8sNiioYi4YmtJjPhYgxcxuMXYg1Gkp91LtHCmkqiQ +bot = commands.Bot(command_prefix='!') + + +def prepare_url_key_pairs(node_rpcs, faucet_mnemonics): + node_rpc_entires = node_rpcs.split(',') + for rpc in node_rpc_entires: + if not rpc.lower().startswith('ws://') and not rpc.lower().startswith('wss://'): + print('{} fails, please check the env again'.format(rpc)) + notify_exit() + + faucet_mnemonics_entries = faucet_mnemonics.split(',') + + if len(node_rpc_entires) != len(faucet_mnemonics_entries): + print('the rpc entry is not the same as the faucet entry, please check and redeploy') + notify_exit() + + return [{ + 'rpc_url': node_rpc_entires[i], + 'faucet_kp': Keypair.create_from_mnemonic(faucet_mnemonics_entries[i]) + } for i in range(0, len(node_rpc_entires))] + + +info_pairs = prepare_url_key_pairs(NODE_RPC, FAUCET_MNEMONIC) + + +@bot.command(name='send', help='Send token from faucet') +async def nine_nine(ctx, arg): + + INVOCATION_COUNT.inc() + + if arg is not None and len(arg) != 48: + # Inform of an invalid address + await ctx.send(str(ctx.author.mention) + " That wallet address seems odd - sure you got that right?") + return + + if (str(ctx.channel.type) == "private"): + # Forbid DM in discord + await ctx.send("Hold on Capt'ain, you can't send me private messages !") + return + + else: + username = str(ctx.author.name) + + # ensure we comply with send frequency + r = redis.Redis(host=REDIS_IP, port=REDIS_PORT) + if r.exists(username): + await ctx.send(str(ctx.author.mention) + " You can only request once every [{}] second(s) !".format(ISSUE_INTERVAL)) + ISSUANCE_THROTTLED.inc() + return + else: + r.set(name=username, value="set") + r.expire(username, ISSUE_INTERVAL) + + extrinsic_url = [] + for info in info_pairs: + rpc_url = info['rpc_url'] + faucet_kp = info['faucet_kp'] + + with SubstrateInterface(url=rpc_url) as substrate: + call = substrate.compose_call( + call_module='Balances', + call_function='transfer', + call_params={ + 'dest': arg, + 'value': TOKENS_TO_SEND} + ) + reply = "" + extrinsic = substrate.create_signed_extrinsic(call=call, keypair=faucet_kp) + reply = substrate.submit_extrinsic(extrinsic, wait_for_inclusion=False) + extrinsic_url.append( + '{1} on {0}'.format(rpc_url, reply['extrinsic_hash'])) + + # inform user + msg = " Awesome, you just received {} Token! Please check these extrinsic hash. \n {}".format( + TOKENS_TO_SEND / TOKENS_DECIMAL, + '\n'.join(extrinsic_url)) + await ctx.send(str(ctx.author.mention) + msg) + + # log to console + print(str(ctx.author.name) + msg) + + # store metrics + ISSUANCE_TOTAL.inc(TOKENS_TO_SEND / TOKENS_DECIMAL) + + +def prometheus_server(): + start_http_server(PROMETHEUS_PORT) + + +print("Starting prometheus server") +t_prometheus = Thread(target=prometheus_server) +t_prometheus.start() + +print("Starting bot") +bot.run(DISCORD_BOT_TOKEN) diff --git a/charts/substrate-faucet/.helmignore b/charts/substrate-faucet/.helmignore new file mode 100755 index 0000000..f0c1319 --- /dev/null +++ b/charts/substrate-faucet/.helmignore @@ -0,0 +1,21 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/charts/substrate-faucet/Chart.yaml b/charts/substrate-faucet/Chart.yaml new file mode 100644 index 0000000..3be8323 --- /dev/null +++ b/charts/substrate-faucet/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +description: A Helm chart for Kubernetes +icon: https://raw.githubusercontent.com/cdfoundation/artwork/master/jenkinsx/icon/color/jenkinsx-icon-color.png +name: substrate-faucet +version: 0.1.0-SNAPSHOT diff --git a/charts/substrate-faucet/Kptfile b/charts/substrate-faucet/Kptfile new file mode 100755 index 0000000..2a814c7 --- /dev/null +++ b/charts/substrate-faucet/Kptfile @@ -0,0 +1,11 @@ +apiVersion: kpt.dev/v1alpha1 +kind: Kptfile +metadata: + name: charts +upstream: + type: git + git: + commit: b52b35506a05e046d54615c3ade97f3aef8bfb08 + repo: https://github.com/jenkins-x/jx3-pipeline-catalog + directory: /helm/charts + ref: master diff --git a/charts/substrate-faucet/Makefile b/charts/substrate-faucet/Makefile new file mode 100755 index 0000000..bdc3a42 --- /dev/null +++ b/charts/substrate-faucet/Makefile @@ -0,0 +1,48 @@ +CHART_REPO := http://jenkins-x-chartmuseum:8080 +CURRENT=$(pwd) +NAME := substrate-faucet +OS := $(shell uname) +RELEASE_VERSION := $(shell cat ../../VERSION) + +build: clean + rm -rf requirements.lock + helm dependency build + helm lint + +install: clean build + helm install . --name ${NAME} + +upgrade: clean build + helm upgrade ${NAME} . + +delete: + helm delete --purge ${NAME} + +clean: + rm -rf charts + rm -rf ${NAME}*.tgz + +release: clean + helm dependency build + helm lint + helm init --client-only + helm package . + curl --fail -u $(CHARTMUSEUM_CREDS_USR):$(CHARTMUSEUM_CREDS_PSW) --data-binary "@$(NAME)-$(shell sed -n 's/^version: //p' Chart.yaml).tgz" $(CHART_REPO)/api/charts + rm -rf ${NAME}*.tgz% + +tag: +ifeq ($(OS),Darwin) + sed -i "" -e "s/version:.*/version: $(RELEASE_VERSION)/" Chart.yaml + sed -i "" -e "s/tag:.*/tag: $(RELEASE_VERSION)/" values.yaml +else ifeq ($(OS),Linux) + sed -i -e "s/version:.*/version: $(RELEASE_VERSION)/" Chart.yaml + sed -i -e "s|repository:.*|repository: $(DOCKER_REGISTRY)\/peaqnetwork\/substrate-faucet|" values.yaml + sed -i -e "s/tag:.*/tag: $(RELEASE_VERSION)/" values.yaml +else + echo "platfrom $(OS) not supported to release from" + exit -1 +endif + git add --all + git commit -m "chore: release $(RELEASE_VERSION)" --allow-empty # if first release then no verion update is performed + git tag -fa v$(RELEASE_VERSION) -m "Release version $(RELEASE_VERSION)" + git push origin v$(RELEASE_VERSION) diff --git a/charts/substrate-faucet/README.md b/charts/substrate-faucet/README.md new file mode 100755 index 0000000..89f8286 --- /dev/null +++ b/charts/substrate-faucet/README.md @@ -0,0 +1 @@ +# my application \ No newline at end of file diff --git a/charts/substrate-faucet/templates/NOTES.txt b/charts/substrate-faucet/templates/NOTES.txt new file mode 100755 index 0000000..97823be --- /dev/null +++ b/charts/substrate-faucet/templates/NOTES.txt @@ -0,0 +1,4 @@ + +Get the application URL by running these commands: + +kubectl get ingress {{ template "fullname" . }} diff --git a/charts/substrate-faucet/templates/_helpers.tpl b/charts/substrate-faucet/templates/_helpers.tpl new file mode 100755 index 0000000..f0d83d2 --- /dev/null +++ b/charts/substrate-faucet/templates/_helpers.tpl @@ -0,0 +1,16 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "fullname" -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/charts/substrate-faucet/templates/canary.yaml b/charts/substrate-faucet/templates/canary.yaml new file mode 100755 index 0000000..b5f55d3 --- /dev/null +++ b/charts/substrate-faucet/templates/canary.yaml @@ -0,0 +1,66 @@ +{{- if .Values.canary.enabled }} +apiVersion: flagger.app/v1beta1 +kind: Canary +metadata: + name: {{ template "fullname" . }} + labels: + draft: {{ default "draft-app" .Values.draft }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" +{{- if .Values.canary.labels }} +{{ toYaml .Values.canary.labels | indent 4 }} +{{- end }} +spec: + provider: istio + targetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ template "fullname" . }} + progressDeadlineSeconds: {{ .Values.canary.progressDeadlineSeconds }} + {{- if .Values.hpa.enabled }} + autoscalerRef: + apiVersion: autoscaling/v2beta1 + kind: HorizontalPodAutoscaler + name: {{ template "fullname" . }} + {{- end }} + service: + port: {{ .Values.service.externalPort }} + targetPort: {{ .Values.service.internalPort }} + gateways: + - {{ template "fullname" . }} + hosts: + - {{ .Values.canary.host | default (printf "%s%s%s" .Values.service.name .Values.jxRequirements.ingress.namespaceSubDomain .Values.jxRequirements.ingress.domain) }} + analysis: + interval: {{ .Values.canary.analysis.interval }} + threshold: {{ .Values.canary.analysis.threshold }} + maxWeight: {{ .Values.canary.analysis.maxWeight }} + stepWeight: {{ .Values.canary.analysis.stepWeight }} + metrics: + - name: latency + templateRef: + name: latency + namespace: istio-system + thresholdRange: + max: {{ .Values.canary.analysis.metrics.latency.threshold }} + interval: {{ .Values.canary.analysis.metrics.latency.interval | quote }} + +--- + +apiVersion: networking.istio.io/v1alpha3 +kind: Gateway +metadata: + name: {{ template "fullname" . }} +{{- if .Values.canary.gatewayLabels }} + labels: +{{ toYaml .Values.canary.gatewayLabels | indent 4 }} +{{- end }} +spec: + selector: + istio: ingressgateway + servers: + - port: + number: {{ .Values.service.externalPort }} + name: http + protocol: HTTP + hosts: + - {{ .Values.canary.host | default (printf "%s%s%s" .Values.service.name .Values.jxRequirements.ingress.namespaceSubDomain .Values.jxRequirements.ingress.domain) }} +{{- end }} diff --git a/charts/substrate-faucet/templates/deployment.yaml b/charts/substrate-faucet/templates/deployment.yaml new file mode 100755 index 0000000..b5a9802 --- /dev/null +++ b/charts/substrate-faucet/templates/deployment.yaml @@ -0,0 +1,118 @@ +{{- if .Values.knativeDeploy }} +{{- else }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "fullname" . }} + labels: + draft: {{ default "draft-app" .Values.draft }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" +{{- if .Values.deploymentLabels }} +{{ toYaml .Values.deploymentLabels | indent 4 }} +{{- end }} +spec: + selector: + matchLabels: + app: {{ template "fullname" . }} +{{- if .Values.hpa.enabled }} +{{- else }} + replicas: {{ .Values.replicaCount }} +{{- end }} + template: + metadata: + labels: + draft: {{ default "draft-app" .Values.draft }} + app: {{ template "fullname" . }} +{{- if .Values.podsLabels }} +{{ toYaml .Values.podsLabels | indent 6 }} +{{- end }} +{{- if .Values.podAnnotations }} + annotations: +{{ toYaml .Values.podAnnotations | indent 8 }} +{{- end }} + spec: +{{- if .Values.serviceAccount.enabled }} +{{- if .Values.serviceAccount.name }} + serviceAccountName: {{ .Values.serviceAccount.name }} +{{- else }} + serviceAccountName: {{ template "fullname" . }} +{{- end }} +{{- end }} + containers: + - name: redis + image: redis:5.0.4 + ports: + - containerPort: 6379 + resources: + requests: {} + readinessProbe: + tcpSocket: + port: 6379 + initialDelaySeconds: 30 + periodSeconds: 10 + livenessProbe: + tcpSocket: + port: 6379 + initialDelaySeconds: 30 + periodSeconds: 20 + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + envFrom: + - configMapRef: + name: {{ .Values.envconfigmap.name }} + env: + - name: VERSION + value: {{ .Chart.Version }} + - name: DISCORD_BOT_TOKEN + valueFrom: + secretKeyRef: + name: {{ .Values.botSecrets.name }} + key: DISCORD_BOT_TOKEN + optional: false + - name: FAUCET_MNEMONIC + valueFrom: + secretKeyRef: + name: {{ .Values.botSecrets.name }} + key: FAUCET_MNEMONIC + optional: false + #- name: NODE_RPC + # value: "{{ .Values.botConfig.NODE_RPC }}" + - name: TOKENS_TO_SEND + value: "{{ .Values.botConfig.TOKENS_TO_SEND }}" + - name: TOKEN_SYMBOL + value: "{{ .Values.botConfig.TOKEN_SYMBOL }}" + - name: ISSUE_INTERVAL + value: "{{ .Values.botConfig.ISSUE_INTERVAL }}" + - name: REDIS_IP + value: "{{ .Values.botConfig.REDIS_IP }}" + - name: REDIS_PORT + value: "{{ .Values.botConfig.REDIS_PORT }}" + - name: PROMETHEUS_PORT + value: "{{ .Values.botConfig.PROMETHEUS_PORT }}" + ports: + - name: http + containerPort: {{ .Values.service.internalPort }} + livenessProbe: + httpGet: + path: {{ .Values.livenessProbe.probePath | default .Values.probePath }} + port: {{ .Values.service.internalPort }} + initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.livenessProbe.periodSeconds }} + successThreshold: {{ .Values.livenessProbe.successThreshold }} + timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }} + readinessProbe: + httpGet: + path: {{ .Values.readinessProbe.probePath | default .Values.probePath }} + port: {{ .Values.service.internalPort }} + periodSeconds: {{ .Values.readinessProbe.periodSeconds }} + successThreshold: {{ .Values.readinessProbe.successThreshold }} + timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }} + resources: +{{ toYaml .Values.resources | indent 12 }} + terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds }} + imagePullSecrets: +{{- range $pval := .Values.jx.imagePullSecrets }} + - name: {{ quote $pval }} +{{- end }} +{{- end }} diff --git a/charts/substrate-faucet/templates/hpa.yaml b/charts/substrate-faucet/templates/hpa.yaml new file mode 100755 index 0000000..a20a001 --- /dev/null +++ b/charts/substrate-faucet/templates/hpa.yaml @@ -0,0 +1,28 @@ +{{- if .Values.hpa.enabled }} +apiVersion: autoscaling/v2beta1 +kind: HorizontalPodAutoscaler +metadata: + name: {{ template "fullname" . }} + labels: + draft: {{ default "draft-app" .Values.draft }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" +{{- if .Values.hpa.labels }} +{{ toYaml .Values.hpa.labels | indent 4 }} +{{- end }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ template "fullname" . }} + minReplicas: {{ .Values.hpa.minReplicas }} + maxReplicas: {{ .Values.hpa.maxReplicas }} + metrics: + - type: Resource + resource: + name: cpu + targetAverageUtilization: {{ .Values.hpa.cpuTargetAverageUtilization }} + - type: Resource + resource: + name: memory + targetAverageUtilization: {{ .Values.hpa.memoryTargetAverageUtilization }} +{{- end }} \ No newline at end of file diff --git a/charts/substrate-faucet/templates/ingress.yaml b/charts/substrate-faucet/templates/ingress.yaml new file mode 100755 index 0000000..8f5c32e --- /dev/null +++ b/charts/substrate-faucet/templates/ingress.yaml @@ -0,0 +1,50 @@ +{{- if and (or .Values.ingress.host .Values.jxRequirements.ingress.domain) (not .Values.knativeDeploy) }} +{{- $host := (.Values.ingress.host | default (printf "%s%s%s" .Values.service.name .Values.jxRequirements.ingress.namespaceSubDomain .Values.jxRequirements.ingress.domain)) | quote }} +{{- $annotations := dict }} +{{- $_ := merge $annotations .Values.ingress.annotations .Values.jxRequirements.ingress.annotations }} +{{- if not (hasKey $annotations "kubernetes.io/ingress.class") }} +{{- $_ := set $annotations "kubernetes.io/ingress.class" (.Values.ingress.classAnnotation | default "nginx") }} +{{- end }} +{{- if and (hasKey .Values.jxRequirements.ingress "serviceType") (.Values.jxRequirements.ingress.serviceType) (eq .Values.jxRequirements.ingress.serviceType "NodePort") (not (hasKey $annotations "jenkins.io/host")) }} +{{- $_ := set $annotations "jenkins.io/host" .Values.jxRequirements.ingress.domain }} +{{- end }} +apiVersion: {{ .Values.jxRequirements.ingress.apiVersion }} +kind: Ingress +metadata: + annotations: +{{- if $annotations }} +{{ toYaml $annotations | indent 4 }} +{{- end }} + name: {{ .Values.service.name }} +{{- if .Values.ingress.labels }} + labels: +{{ toYaml .Values.ingress.labels | indent 4 }} +{{- end }} +spec: + rules: + - http: + paths: + - pathType: {{ .Values.ingress.pathType | default "ImplementationSpecific" }} + backend: + service: + name: {{ .Values.service.name }} + port: + number: {{ .Values.service.externalPort }} +{{- if eq "NodePort" .Values.jxRequirements.ingress.serviceType }} + path: "/{{ .Release.Namespace }}/hook" +{{- else if or .Values.ingress.host .Values.jxRequirements.ingress.domain }} + host: {{ $host }} +{{- end }} +{{- if .Values.jxRequirements.ingress.tls.enabled }} + tls: + - hosts: + - {{ $host }} +{{- if and (hasKey .Values.jxRequirements.ingress.tls "secretName") (.Values.jxRequirements.ingress.tls.secretName) }} + secretName: "{{ .Values.jxRequirements.ingress.tls.secretName }}" +{{- else if .Values.jxRequirements.ingress.tls.production }} + secretName: "tls-{{ .Values.ingress.host | default .Values.jxRequirements.ingress.domain | replace "." "-" }}-p" +{{- else }} + secretName: "tls-{{ .Values.ingress.host | default .Values.jxRequirements.ingress.domain | replace "." "-" }}-s" +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/substrate-faucet/templates/ksvc.yaml b/charts/substrate-faucet/templates/ksvc.yaml new file mode 100755 index 0000000..ebf2a53 --- /dev/null +++ b/charts/substrate-faucet/templates/ksvc.yaml @@ -0,0 +1,51 @@ +{{- if .Values.knativeDeploy }} +apiVersion: serving.knative.dev/v1 +kind: Service +metadata: + {{- if .Values.service.name }} + name: {{ .Values.service.name }} + {{- else }} + name: {{ template "fullname" . }} + {{- end }} + labels: + chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" +{{- if .Values.service.labels }} +{{ toYaml .Values.service.labels | indent 4 }} +{{- end }} +spec: + template: + metadata: + annotations: + autoscaling.knative.dev/minScale: "0" +{{- if .Values.podsLabels }} + labels: +{{ toYaml .Values.podsLabels | indent 8 }} +{{- end }} + spec: + containers: + - image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + env: + - name: VERSION + value: {{ .Chart.Version }} +{{- range $pkey, $pval := .Values.env }} + - name: {{ $pkey }} + value: {{ quote $pval }} +{{- end }} + livenessProbe: + httpGet: + path: {{ .Values.livenessProbe.probePath | default .Values.probePath }} + initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.livenessProbe.periodSeconds }} + successThreshold: {{ .Values.livenessProbe.successThreshold }} + timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }} + readinessProbe: + failureThreshold: {{ .Values.readinessProbe.failureThreshold }} + httpGet: + path: {{ .Values.readinessProbe.probePath | default .Values.probePath }} + periodSeconds: {{ .Values.readinessProbe.periodSeconds }} + successThreshold: {{ .Values.readinessProbe.successThreshold }} + timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }} + resources: +{{ toYaml .Values.resources | indent 14 }} +{{- end }} diff --git a/charts/substrate-faucet/templates/sa.yaml b/charts/substrate-faucet/templates/sa.yaml new file mode 100755 index 0000000..c1f9836 --- /dev/null +++ b/charts/substrate-faucet/templates/sa.yaml @@ -0,0 +1,16 @@ +{{- if .Values.serviceAccount.enabled }} +apiVersion: v1 +kind: ServiceAccount +metadata: +{{- if .Values.serviceAccount.name }} + name: {{ .Values.serviceAccount.name }} +{{- else }} + name: {{ template "fullname" . }} +{{- end }} + {{- if .Values.serviceAccount.labels }} + labels: {{- toYaml .Values.serviceAccount.labels | nindent 4 }} + {{- end }} + {{- if .Values.serviceAccount.annotations }} + annotations: {{ toYaml .Values.serviceAccount.annotations | nindent 4 }} + {{- end }} +{{- end }} diff --git a/charts/substrate-faucet/templates/service.yaml b/charts/substrate-faucet/templates/service.yaml new file mode 100755 index 0000000..67664f3 --- /dev/null +++ b/charts/substrate-faucet/templates/service.yaml @@ -0,0 +1,29 @@ +{{- if or .Values.knativeDeploy .Values.canary.enabled }} +{{- else }} +apiVersion: v1 +kind: Service +metadata: +{{- if .Values.service.name }} + name: {{ .Values.service.name }} +{{- else }} + name: {{ template "fullname" . }} +{{- end }} + labels: + chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" +{{- if .Values.service.labels }} +{{ toYaml .Values.service.labels | indent 4 }} +{{- end }} +{{- if .Values.service.annotations }} + annotations: +{{ toYaml .Values.service.annotations | indent 4 }} +{{- end }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.externalPort }} + targetPort: {{ .Values.service.internalPort }} + protocol: TCP + name: http + selector: + app: {{ template "fullname" . }} +{{- end }} diff --git a/charts/substrate-faucet/values.yaml b/charts/substrate-faucet/values.yaml new file mode 100755 index 0000000..834a49e --- /dev/null +++ b/charts/substrate-faucet/values.yaml @@ -0,0 +1,155 @@ +# Default values for your projects. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. +replicaCount: 1 +# Add annotations to the pods +podAnnotations: {} +# Add labels to the pods +podsLabels: {} +# Add labels to the deployment +deploymentLabels: {} + +image: + repository: draft + tag: dev + pullPolicy: IfNotPresent + +# optional list of image pull secrets to use to pull images +jx: + # optional image pull secrets + imagePullSecrets: [] + + # whether to create a Release CRD when installing charts with Release CRDs included + releaseCRD: true + +# define environment variables here as a map of key: value + +env: +envconfigmap: + name: substrate-faucet-configmap +envFrom: + - configMapRef: + name: agung-faucet-bot-config + +botConfig: + NODE_RPC: "wss://wss.agung.peaq.network" + TOKENS_TO_SEND: "1000000000000000000" + TOKEN_SYMBOL: "AGNG" + ISSUE_INTERVAL: 60 + REDIS_IP: "127.0.0.1" + REDIS_PORT: "6379" + PROMETHEUS_PORT: "8080" + +botSecrets: + name: agung-faucet-bot-secret + +# enable this flag to use knative serve to deploy the app +knativeDeploy: false + +# HorizontalPodAutoscaler +hpa: + enabled: false + minReplicas: 2 + maxReplicas: 6 + cpuTargetAverageUtilization: 80 + memoryTargetAverageUtilization: 80 + # Add labels to the HPA + labels: {} + +# Canary deployments +# If enabled, Istio and Flagger need to be installed in the cluster +canary: + enabled: false + progressDeadlineSeconds: 60 + analysis: + interval: "1m" + threshold: 5 + maxWeight: 60 + stepWeight: 20 + # WARNING: Canary deployments will fail and rollback if there is no traffic that will generate the below specified metrics. + metrics: + latency: + threshold: 500 + interval: "1m" + # The host is using Istio Gateway or the underlying ingress mechanism + # This value is defaulted from the environments jx-requirements.yml ingress configuration + host: "" + # Add labels to the canary + labels: {} + # Add labels to the canary gateway + gatewayLabels: {} + +service: + name: substrate-faucet + type: ClusterIP + externalPort: 80 + internalPort: 8080 + annotations: {} + # Add labels to the service + labels: {} +# resources: +# limits: +# cpu: 400m +# memory: 256Mi +# requests: +# cpu: 200m +# memory: 128Mi +probePath: / +livenessProbe: + initialDelaySeconds: 60 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + # Optional distinct liveness probe path, if empty the probePath is used + probePath: "" +readinessProbe: + failureThreshold: 1 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + # Optional distinct readiness probe path, if empty the probePath is used + probePath: "" + +# custom ingress annotations on this service +ingress: + annotations: {} + # defaults to nginx if no other ingress class annotation specified + classAnnotation: "" + # Add labels to the ingress + labels: {} + # defaults to .service.name + jxRequirements.ingress.namespaceSubDomain + jxRequirements.ingress.domain + host: "" + + # ingress path type + pathType: ImplementationSpecific + +serviceAccount: + enabled: true + name: "" + annotations: {} +# iam.gke.io/gcp-service-account: my-sa-in-gke + # Add labels to the SA + labels: {} +# my-custom-label: value + +# values we use from the `jx-requirements.yml` file if we are using helmfile and helm 3 +jxRequirements: + ingress: + # shared ingress annotations on all services + annotations: {} + # kubernetes.io/ingress.class: nginx + + apiVersion: "networking.k8s.io/v1" + + # the domain for hosts + domain: "" + externalDNS: false + namespaceSubDomain: -jx. + serviceType: "" + tls: + email: "" + enabled: false + production: false + secretName: "" + + diff --git a/faucet.py b/faucet.py deleted file mode 100644 index d33dec4..0000000 --- a/faucet.py +++ /dev/null @@ -1,43 +0,0 @@ -import os -import random -import discord -from discord.ext import commands -from substrateinterface import SubstrateInterface, Keypair -from substrateinterface.exceptions import SubstrateRequestException -from substrateinterface.utils.ss58 import ss58_encode - -# Your Discord bot token -TOKEN = '' -faucet_mnemonic = '' -# associated address 5CfVS8r8sNiioYi4YmtJjPhYgxcxuMXYg1Gkp91LtHCmkqiQ -bot = commands.Bot(command_prefix='!') -keypair = Keypair.create_from_mnemonic('faucet_mnemonic') -# substrate RPC node -node_rpc = "http://127.0.0.1:9933" - -@bot.command(name='send', help='Send token from faucet') -async def nine_nine(ctx, arg): - if (ctx.channel.type == "private"): - # Forbid DM in discord - await ctx.send("Hold on Capt'ain, you can't send me private messages !") - else: - substrate = SubstrateInterface( - url=node_rpc, - address_type=42, - type_registry_preset='substrate-node-template' - ) - call = substrate.compose_call( - call_module='Balances', - call_function='transfer', - call_params={ - 'dest': arg, - 'value': 100 * 10**12 - } - ) - reply = "" - keypair = Keypair.create_from_mnemonic(faucet_mnemonic) - extrinsic = substrate.create_signed_extrinsic(call=call, keypair=keypair) - reply = substrate.submit_extrinsic(extrinsic, wait_for_inclusion=False) - await ctx.send(ctx.author.mention + " Awesome, you just received 100 dPIRL, it has no real value it's only the testnet token :) " + reply['extrinsic_hash'] + str(ctx.channel.type)) - -bot.run(TOKEN) diff --git a/preview/Kptfile b/preview/Kptfile new file mode 100644 index 0000000..6e6ae8a --- /dev/null +++ b/preview/Kptfile @@ -0,0 +1,11 @@ +apiVersion: kpt.dev/v1alpha1 +kind: Kptfile +metadata: + name: preview +upstream: + type: git + git: + commit: b52b35506a05e046d54615c3ade97f3aef8bfb08 + repo: https://github.com/jenkins-x/jx3-pipeline-catalog + directory: /helm/preview + ref: master diff --git a/preview/helmfile.yaml b/preview/helmfile.yaml new file mode 100644 index 0000000..7485be5 --- /dev/null +++ b/preview/helmfile.yaml @@ -0,0 +1,31 @@ +environments: + default: + values: + - jx-values.yaml +repositories: +- name: jx3 + url: https://jenkins-x-charts.github.io/repo +releases: +- chart: jx3/jx-verify + name: jx-verify + namespace: '{{ requiredEnv "PREVIEW_NAMESPACE" }}' +- chart: '../charts/{{ requiredEnv "APP_NAME" }}' + name: preview + wait: true + createNamespace: true + namespace: '{{ requiredEnv "PREVIEW_NAMESPACE" }}' + values: + - jx-values.yaml + - values.yaml.gotmpl + hooks: + - events: ["presync"] + showlogs: true + command: "jx" + args: + - secret + - copy + - --create-namespace + - --selector + - "secret.jenkins-x.io/replica-source=true" + - --to + - '{{ requiredEnv "PREVIEW_NAMESPACE" }}' diff --git a/preview/values.yaml.gotmpl b/preview/values.yaml.gotmpl new file mode 100644 index 0000000..0283df2 --- /dev/null +++ b/preview/values.yaml.gotmpl @@ -0,0 +1,8 @@ +jxRequirements: + ingress: + namespaceSubDomain: "-pr{{ requiredEnv "PULL_NUMBER" }}." + +image: + repository: "{{ requiredEnv "DOCKER_REGISTRY" }}/{{ requiredEnv "DOCKER_REGISTRY_ORG" }}/{{ requiredEnv "APP_NAME" }}" + tag: "{{ requiredEnv "VERSION" }}" + pullPolicy: "Always" diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..903d3ff --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +substrate-interface==1.2.4 +discord.py==1.7.3 +redis==4.1.4 +prometheus-client==0.13.1 +scalecodec==1.0.35