diff --git a/.github/resources/goomy.png b/.github/resources/goomy.png deleted file mode 100644 index 5f4d86f..0000000 Binary files a/.github/resources/goomy.png and /dev/null differ diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml new file mode 100644 index 0000000..0b9450e --- /dev/null +++ b/.github/workflows/docker-build.yml @@ -0,0 +1,58 @@ +name: Docker + +# Trigger on pushes to master branch, new semantic version tags, and pull request updates +on: + workflow_dispatch: + inputs: + tag: + description: Git branch, or tag to build from. + required: false + target: + description: Target to build. + required: false + type: choice + options: + - spamooor + + merge_group: + push: + branches: + - "main" + tags: + - "**-v[0-9]+.[0-9]+.[0-9]+" + - "**-v[0-9]+.[0-9]+.[0-9]+-alpha.[0-9]+" + - "**-v[0-9]+.[0-9]+.[0-9]+-beta.[0-9]+" + - "**-v[0-9]+.[0-9]+.[0-9]+-rc[0-9]+" + + pull_request: + types: + - opened + - synchronize + - reopened + - labeled + +jobs: + run_checker: + uses: ./.github/workflows/reusable-run-checker.yml + + spamooor: + needs: run_checker + if: needs.run_checker.outputs.run_docker == 'true' || (github.event_name == 'workflow_dispatch' && github.event.inputs.target == 'spamooor') + uses: "./.github/workflows/reusable-docker-build.yml" + permissions: + contents: read + id-token: write + packages: write + with: + # depot-project-id: mhgvgvsjnx + package-name: spamooor + target-binary: astria-spamooor + tag: ${{ inputs.tag }} + secrets: inherit + + docker: + if: ${{ always() && !cancelled() }} + needs: [spamooor] + uses: ./.github/workflows/reusable-success.yml + with: + success: ${{ !contains(needs.*.result, 'failure') }} diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..797720c --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,46 @@ +name: Lint +on: + pull_request: + merge_group: + push: + branches: + - "main" + +jobs: + run_checker: + uses: ./.github/workflows/reusable-run-checker.yml + + charts: + runs-on: ubuntu-latest + needs: run_checker + if: needs.run_checker.outputs.run_lint_charts == 'true' + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Set up Helm + uses: azure/setup-helm@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.9' + check-latest: true + - name: Set up chart-testing + uses: helm/chart-testing-action@v2.6.1 + - name: Run chart-testing (list-changed) + id: list-changed + run: | + changed=$(ct list-changed --target-branch ${{ github.event.repository.default_branch }}) + if [[ -n "$changed" ]]; then + echo "changed=true" >> "$GITHUB_OUTPUT" + fi + - name: Run chart-testing (lint) + if: steps.list-changed.outputs.changed == 'true' + run: ct lint --target-branch ${{ github.event.repository.default_branch }} + + lint: + needs: [charts] + if: ${{ always() && !cancelled() }} + uses: ./.github/workflows/reusable-success.yml + with: + success: ${{ !contains(needs.*.result, 'failure') }} diff --git a/.github/workflows/reusable-docker-build.yml b/.github/workflows/reusable-docker-build.yml new file mode 100644 index 0000000..4afbd6c --- /dev/null +++ b/.github/workflows/reusable-docker-build.yml @@ -0,0 +1,101 @@ +name: Reusable Docker Build && Push Workflow + +on: + workflow_call: + inputs: + # depot-project-id: + # required: true + # type: string + package-name: + required: true + type: string + target-binary: + required: true + type: string + tag: + required: false + type: string + secrets: + DOCKER_TOKEN: + required: false + DOCKER_USER: + required: false +env: + REGISTRY: ghcr.io + FULL_REF: ${{ inputs.tag && format('refs/tags/{0}', inputs.tag) || github.ref }} + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + packages: write + if: startsWith(inputs.tag, inputs.package-name) || !inputs.tag && (startsWith(github.ref, format('refs/tags/{0}-v', inputs.package-name)) || github.ref == 'refs/heads/main' || github.event_name == 'pull_request' || github.event_name == 'merge_group') + steps: + # Checking out the repo + - uses: actions/checkout@v4 + with: + ref: ${{ inputs.tag }} + # - uses: depot/setup-action@v1 + - name: Login to Docker Hub + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'astriaorg/spamooor' + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKER_USER }} + password: ${{ secrets.DOCKER_TOKEN }} + - name: Log in to GHCR + uses: docker/login-action@v2 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v2 + # Generate correct tabs and labels + - name: Docker metadata + id: metadata + uses: docker/metadata-action@v4 + with: + images: ${{ format('ghcr.io/astriaorg/{0}', inputs.package-name) }} + tags: | + type=ref,event=pr + type=match,pattern=refs/tags/${{ inputs.package-name }}-v(.*),group=1,enable=${{ startsWith(env.FULL_REF, 'refs/tags/') }},value=${{ env.FULL_REF }} + type=sha + # set latest tag for `main` branch + type=raw,value=latest,enable=${{ env.FULL_REF == format('refs/heads/{0}', 'main') }} + - name: Build and push + uses: docker/build-push-action@v4 + with: + # this gets rid of the unknown/unknown image that is created without this setting + # https://github.com/docker/build-push-action/issues/820#issuecomment-1455687416 + provenance: false + context: . + file: Dockerfile + build-args: | + TARGETBINARY=${{ inputs.target-binary }} + platforms: 'linux/amd64,linux/arm64' + push: ${{ github.repository_owner == 'astriaorg'}} + tags: ${{ steps.metadata.outputs.tags }} + labels: ${{ steps.metadata.outputs.labels }} + # - name: Build and push (Depot) + # uses: depot/build-push-action@v1 + # with: + # # this gets rid of the unknown/unknown image that is created without this setting + # # https://github.com/docker/build-push-action/issues/820#issuecomment-1455687416 + # provenance: false + # context: . + # file: containerfiles/Dockerfile + # build-args: | + # TARGETBINARY=${{ inputs.target-binary }} + # platforms: "linux/amd64,linux/arm64" + # push: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'astriaorg/spamooor' }} + # tags: ${{ steps.metadata.outputs.tags }} + # labels: ${{ steps.metadata.outputs.labels }} + # project: ${{ inputs.depot-project-id }} + + + \ No newline at end of file diff --git a/.github/workflows/reusable-run-checker.yml b/.github/workflows/reusable-run-checker.yml new file mode 100644 index 0000000..b4a9eb2 --- /dev/null +++ b/.github/workflows/reusable-run-checker.yml @@ -0,0 +1,38 @@ +name: Reusable Run Checker Workflow + +on: + workflow_call: + outputs: + run_docker: + description: If docker workflow needs to be run, will be 'true' + value: ${{ github.event_name != 'pull_request' || jobs.changes.outputs.docker_workflow == 'true' || contains(github.event.pull_request.labels.*.name, 'docker-build') }} + run_lint_charts: + description: If lint for charts needs to be run, will be 'true' + value: ${{ github.event_name != 'pull_request' || jobs.changes.outputs.lint_workflow == 'true' || jobs.changes.outputs.charts == 'true' }} + +jobs: + changes: + runs-on: ubuntu-latest + permissions: + pull-requests: read + outputs: + docker_workflow: ${{ steps.filters.outputs.docker_workflow }} + lint_workflow: ${{ steps.filters.outputs.lint_workflow }} + charts: ${{ steps.filters.outputs.charts }} + steps: + - uses: actions/checkout@v4 + - uses: dorny/paths-filter@v3 + id: filters + with: + list-files: json + filters: | + docker_workflow: + - '.github/workflows/docker-build.yml' + - '.github/workflows/reusable-docker-build.yml' + - '.dockerignore' + lint_workflow: + - '.github/workflows/lint.yml' + markdown: + - '**/*.md' + charts: + - 'charts/**' diff --git a/.github/workflows/reusable-success.yml b/.github/workflows/reusable-success.yml new file mode 100644 index 0000000..e2c198e --- /dev/null +++ b/.github/workflows/reusable-success.yml @@ -0,0 +1,18 @@ +name: Reusable Success Check + +on: + workflow_call: + inputs: + success: + required: true + type: boolean + +jobs: + success: + runs-on: ubuntu-latest + if: ${{ always() && !cancelled() }} + steps: + - if: ${{ !inputs.success }} + run: exit 1 + - if: ${{ inputs.success }} + run: exit 0 diff --git a/Dockerfile b/Dockerfile index 1e74deb..1afe2aa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,8 +11,7 @@ RUN < valueFiles + # -> values + # -> valuesObject + # highest -> parameters + + valueFiles: + - values.yaml + + syncPolicy: + automated: + prune: true + selfHeal: false + allowEmpty: true + syncOptions: + - CreateNamespace=true diff --git a/charts/spamooor-cronjobs/.helmignore b/charts/spamooor-cronjobs/.helmignore new file mode 100644 index 0000000..4032ec6 --- /dev/null +++ b/charts/spamooor-cronjobs/.helmignore @@ -0,0 +1 @@ +.git/ \ No newline at end of file diff --git a/charts/spamooor-cronjobs/Chart.yaml b/charts/spamooor-cronjobs/Chart.yaml new file mode 100644 index 0000000..5314b73 --- /dev/null +++ b/charts/spamooor-cronjobs/Chart.yaml @@ -0,0 +1,14 @@ +apiVersion: v2 +appVersion: 0.1.0 +description: A Helm chart for Kubernetes +name: spamooor-cronjobs +type: application +version: 0.1.2 + +maintainers: + - name: bharath-123 + url: astria.org + - name: aajimal + url: astria.org + - name: joroshiba + url: astria.org diff --git a/charts/spamooor-cronjobs/LICENSE b/charts/spamooor-cronjobs/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/charts/spamooor-cronjobs/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/charts/spamooor-cronjobs/README.md b/charts/spamooor-cronjobs/README.md new file mode 100644 index 0000000..9f1005a --- /dev/null +++ b/charts/spamooor-cronjobs/README.md @@ -0,0 +1,135 @@ +# spamooor-cronjobs +You can define an array of jobs in values.yaml helm will take care of creating all of the CronJobs. + +Credit: [helm-cronjobs](https://github.com/bambash/helm-cronjobs) used as scafolding for this chart. + +## Getting started + +`helm create -p ` + + ``` + helm create -p helm-cronjobs spamooor-cronjobs + ``` + +## For debugging +`helm template --dry-run --debug -name ` +``` +helm template --dry-run --debug -name cronjobs spamooor-cronjobs +``` + + +## Configuration + +Via `values.yaml` + +### Overview + +```yaml +jobs: + jobname-1: + # job definition + jobname-2: + # job definition + jobname-n: + # job definition +``` + +### Details + +```yaml +jobs: + ### REQUIRED ### + : + image: + repository: + tag: + imagePullPolicy: + schedule: "" + failedJobsHistoryLimit: + successfulJobsHistoryLimit: + concurrencyPolicy: + restartPolicy: + ### OPTIONAL ### + imagePullSecrets: + - username: + password: + email: + registry: + env: + - name: ENV_VAR + value: ENV_VALUE + envFrom: + - secretRef: + name: + - configMapRef: + name: + command: [""] + args: + - "" + - "" + resources: + limits: + cpu: + memory: + requests: + cpu: + memory: + serviceAccount: + name: + annotations: # Optional + my-annotation-1: + my-annotation-2: + nodeSelector: + key: + tolerations: + - effect: NoSchedule + operator: Exists + volumes: + - name: config-mount + configMap: + name: configmap-name + items: + - key: configuration.yml + path: configuration.yml + volumeMounts: + - name: config-mount + mountPath: /etc/config + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/e2e-az-name + operator: In + values: + - e2e-az1 + - e2e-az2 +``` + +## Examples +``` +$ helm install test-cron-job . +NAME: test-cron-job +LAST DEPLOYED: Tue Jun 4 12:27:32 2024 +NAMESPACE: spamooor-cronjobs +STATUS: deployed +REVISION: 1 +TEST SUITE: None + +RESOURCES: +==> v1/CronJob +NAME AGE +test-cronjob-curl 1s +``` +list cronjobs: +``` +$ kubectl get cronjob +NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE +test-cron-job-curl * * * * * False 1 5m23s 5m49s +``` +list jobs: +``` +$ kubectl get jobs +NAME DESIRED SUCCESSFUL AGE +test-cron-job-curl-28625488 0/1 6m6s 6m6s +``` diff --git a/charts/spamooor-cronjobs/templates/_helpers.tpl b/charts/spamooor-cronjobs/templates/_helpers.tpl new file mode 100644 index 0000000..9356ff5 --- /dev/null +++ b/charts/spamooor-cronjobs/templates/_helpers.tpl @@ -0,0 +1,55 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "cronjobs.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "cronjobs.labels" -}} +helm.sh/chart: {{ include "cronjobs.chart" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Expand the release name of the chart. +*/}} +{{- define "cronjobs.releaseName" -}} +{{- default .Release.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + + +{{/* +Create payload for any image pull secret. +One kube secret will be created containing all the auths +and will be shared by all the job pods requiring it. +*/}} +{{- define "cronjobs.imageSecrets" -}} + {{- $secrets := dict -}} + {{- range $jobname, $job := .Values.jobs -}} + {{- if hasKey $job "imagePullSecrets" -}} + {{- range $ips := $job.imagePullSecrets -}} + {{- $userInfo := dict "username" $ips.username "password" $ips.password "auth" (printf "%s:%s" $ips.username $ips.password | b64enc) -}} + {{- if hasKey $ips "email" -}} + {{ $_ := set $userInfo "email" $ips.email -}} + {{- end -}} + {{- $_ := set $secrets $ips.registry $userInfo -}} + {{- end -}} + {{- end -}} + {{- end -}} + {{- if gt (len $secrets) 0 -}} + {{- $auth := dict "auths" $secrets -}} + {{/* Emit secret content as base64 */}} + {{- print ($auth | toJson | b64enc) -}} + {{- else -}} + {{/* There are no secrets*/}} + {{- print "" -}} + {{- end -}} +{{- end -}} \ No newline at end of file diff --git a/charts/spamooor-cronjobs/templates/cronjob.yaml b/charts/spamooor-cronjobs/templates/cronjob.yaml new file mode 100644 index 0000000..71a6339 --- /dev/null +++ b/charts/spamooor-cronjobs/templates/cronjob.yaml @@ -0,0 +1,89 @@ +{{- range $jobname, $job := .Values.jobs }} +--- +apiVersion: batch/v1 +kind: CronJob +metadata: + name: {{ include "cronjobs.releaseName" $ }}-{{ $jobname }} + namespace: {{ $.Chart.Name }} + labels: + {{- include "cronjobs.labels" $ | nindent 4 }} +spec: + concurrencyPolicy: {{ $job.concurrencyPolicy }} + failedJobsHistoryLimit: {{ $job.failedJobsHistoryLimit }} + jobTemplate: + spec: + template: + metadata: + labels: + app: {{ include "cronjobs.releaseName" $ }} + cron: {{ $jobname }} + spec: + {{- if hasKey $job "imagePullSecrets" }} + imagePullSecrets: + - name: {{ $.Release.Name }}-docker + {{- end }} + {{- if and (hasKey $job "serviceAccount") (hasKey $job.serviceAccount "name") }} + serviceAccountName: {{ $job.serviceAccount.name }} + {{- else }} + serviceAccountName: {{ $.Release.Name}}-{{ $jobname }} + {{- end }} + {{- if hasKey $job "securityContext" }} + {{- if $job.securityContext.runAsUser }} + securityContext: + runAsUser: {{ $job.securityContext.runAsUser }} + {{- if $job.securityContext.runAsGroup }} + runAsGroup: {{ $job.securityContext.runAsGroup }} + {{- end }} + {{- if $job.securityContext.fsGroup }} + fsGroup: {{ $job.securityContext.fsGroup }} + {{- end }} + {{- end }} + {{- end }} + containers: + - image: {{ $job.image.repository }}:{{ $job.image.tag }} + imagePullPolicy: {{ $job.image.imagePullPolicy }} + name: {{ $jobname }} + {{- with $job.env }} + env: +{{ toYaml . | indent 12 }} + {{- end }} + {{- with $job.envFrom }} + envFrom: +{{ toYaml . | indent 12 }} + {{- end }} + {{- with $job.command }} + command: +{{ toYaml . | indent 12 }} + {{- end }} + {{- with $job.args }} + args: +{{ toYaml . | indent 12 }} + {{- end }} + {{- with $job.resources }} + resources: +{{ toYaml . | indent 14 }} + {{- end }} + {{- with $job.volumeMounts }} + volumeMounts: +{{ toYaml . | indent 12 }} + {{- end }} + {{- with $job.nodeSelector }} + nodeSelector: +{{ toYaml . | indent 12 }} + {{- end }} + {{- with $job.affinity }} + affinity: +{{ toYaml . | indent 12 }} + {{- end }} + {{- with $job.tolerations }} + tolerations: +{{ toYaml . | indent 12 }} + {{- end }} + restartPolicy: {{ $job.restartPolicy }} + {{- with $job.volumes }} + volumes: +{{ toYaml . | indent 12 }} + {{- end }} + schedule: {{ $job.schedule | quote }} + successfulJobsHistoryLimit: {{ $job.successfulJobsHistoryLimit }} +{{- end }} diff --git a/charts/spamooor-cronjobs/templates/image-pull-secret.yaml b/charts/spamooor-cronjobs/templates/image-pull-secret.yaml new file mode 100644 index 0000000..a7d984f --- /dev/null +++ b/charts/spamooor-cronjobs/templates/image-pull-secret.yaml @@ -0,0 +1,13 @@ +{{- $secret := (include "cronjobs.imageSecrets" .) -}} +{{- if gt (len $secret) 0 }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Release.Name }}-docker + namespace: {{ $.Chart.Name }} + labels: + {{- include "cronjobs.labels" . | nindent 4 }} +type: kubernetes.io/dockerconfigjson +data: + .dockerconfigjson: {{ $secret }} +{{- end -}} diff --git a/charts/spamooor-cronjobs/templates/serviceaccount.yaml b/charts/spamooor-cronjobs/templates/serviceaccount.yaml new file mode 100644 index 0000000..334ddad --- /dev/null +++ b/charts/spamooor-cronjobs/templates/serviceaccount.yaml @@ -0,0 +1,21 @@ +{{- range $jobname, $job := .Values.jobs }} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + {{- if and (hasKey $job "serviceAccount") (hasKey $job "serviceAccount.name") }} + name: {{ $job.serviceAccount.name }} + {{- else }} + name: {{ $.Release.Name}}-{{ $jobname }} + {{- end }} + namespace: {{ $.Chart.Name }} + labels: + {{- include "cronjobs.labels" $ | nindent 4 }} + cron: {{ $jobname }} + {{- if and (hasKey $job "serviceAccount") (hasKey $job "serviceAccount.annotations") }} + {{- with $job.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/spamooor-cronjobs/values.yaml b/charts/spamooor-cronjobs/values.yaml new file mode 100644 index 0000000..933518e --- /dev/null +++ b/charts/spamooor-cronjobs/values.yaml @@ -0,0 +1,158 @@ +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. +jobs: + erctxsjob: + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 2000 + image: + repository: ghcr.io/astriaorg/spamooor + tag: pr-12 + imagePullPolicy: Always + schedule: "*/20 * * * *" + command: ["/app/spamooor"] + args: + - "--privkey" + - "386f0870b016136deaf37a24e7fc958dfdcb3bcc7a80cb7b110eb474c88f6934" + - "--rpchost" + - "https://rpc.evm.dusk-7.devnet.astria.org" # Alternatively: ..svc.cluster.local: + - "erctx" + - "--max-wallets" + - "10" + - "--throughput" + - "100" + - "--max-pending" + - "100" + - "--count" + - "1000" + - "--timeout" + - "60" + resources: + limits: + cpu: 50m + memory: 256Mi + requests: + cpu: 50m + memory: 256Mi + failedJobsHistoryLimit: 1 + successfulJobsHistoryLimit: 1 + concurrencyPolicy: Forbid + restartPolicy: Never + eoatxsjob: + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 2000 + image: + repository: ghcr.io/astriaorg/spamooor + tag: pr-12 + imagePullPolicy: Always + schedule: "*/15 * * * *" + command: ["/app/spamooor"] + args: + - "--privkey" + - "2bff74e4775a2aabb8325554e2034e6eca26635318b2502a5049a359b4d0cd2f" + - "--rpchost" + - "https://rpc.evm.dusk-7.devnet.astria.org" # Alternatively: ..svc.cluster.local: + - "eoatx" + - "--max-wallets" + - "10" + - "--throughput" + - "100" + - "--max-pending" + - "100" + - "--count" + - "1000" + - "--timeout" + - "60" + resources: + limits: + cpu: 50m + memory: 256Mi + requests: + cpu: 50m + memory: 256Mi + failedJobsHistoryLimit: 1 + successfulJobsHistoryLimit: 1 + concurrencyPolicy: Forbid + restartPolicy: Never + univ2txsjob: + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 2000 + image: + repository: ghcr.io/astriaorg/spamooor + tag: pr-12 + imagePullPolicy: Always + # run every 10mins + schedule: "*/25 * * * *" + command: ["/app/spamooor"] + args: + - "--privkey" + - "64b947d57a7e2633b109875ca71acf13fe8c11237b55616ef87d4a1b907133c8" + - "--rpchost" + - "https://rpc.evm.dusk-7.devnet.astria.org" # Alternatively: ..svc.cluster.local: + - "univ2tx" + - "--max-wallets" + - "10" + - "--throughput" + - "100" + - "--max-pending" + - "100" + - "--count" + - "1000" + - "--timeout" + - "60" + resources: + limits: + cpu: 50m + memory: 256Mi + requests: + cpu: 50m + memory: 256Mi + failedJobsHistoryLimit: 1 + successfulJobsHistoryLimit: 1 + concurrencyPolicy: Forbid + restartPolicy: Never + gasburnertxsjob: + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 2000 + image: + repository: ghcr.io/astriaorg/spamooor + tag: pr-12 + imagePullPolicy: Always + schedule: "*/30 * * * *" + command: ["/app/spamooor"] + args: + - "--privkey" + - "57b5fb1ad7f7e0d6e2a085ef7cc761c0576d23760d0bc0db7f1368bd14836e0e" + - "--rpchost" + - "https://rpc.evm.dusk-7.devnet.astria.org" # Alternatively: ..svc.cluster.local: + - "gasburnertx" + - "--max-wallets" + - "10" + - "--throughput" + - "5" + - "--max-pending" + - "5" + - "--count" + - "5" + - "--gas-units-to-burn" + - "5000000" + - "--timeout" + - "60" + resources: + limits: + cpu: 50m + memory: 256Mi + requests: + cpu: 50m + memory: 256Mi + failedJobsHistoryLimit: 1 + successfulJobsHistoryLimit: 1 + concurrencyPolicy: Forbid + restartPolicy: Never diff --git a/cmd/spamooor/main.go b/cmd/spamooor/main.go new file mode 100644 index 0000000..f7afabf --- /dev/null +++ b/cmd/spamooor/main.go @@ -0,0 +1,131 @@ +package main + +import ( + "fmt" + "os" + "sort" + "strings" + + "github.com/holiman/uint256" + "github.com/sirupsen/logrus" + "github.com/spf13/pflag" + + "github.com/astriaorg/spamooor/scenarios" + "github.com/astriaorg/spamooor/scenariotypes" + "github.com/astriaorg/spamooor/tester" + "github.com/astriaorg/spamooor/utils" +) + +type CliArgs struct { + verbose bool + trace bool + rpchosts []string + rpchostsFile string + privkey string + seed string +} + +func mainArgs(flags *pflag.FlagSet, cliArgs *CliArgs) { + +} + +func main() { + cliArgs := CliArgs{} + flags := pflag.NewFlagSet("main", pflag.ContinueOnError) + + flags.BoolVarP(&cliArgs.verbose, "verbose", "v", false, "Run the script with verbose output") + flags.BoolVar(&cliArgs.trace, "trace", false, "Run the script with tracing output") + flags.StringArrayVarP(&cliArgs.rpchosts, "rpchost", "h", []string{}, "The RPC host to send transactions to.") + flags.StringVar(&cliArgs.rpchostsFile, "rpchost-file", "", "File with a list of RPC hosts to send transactions to.") + flags.StringVarP(&cliArgs.privkey, "privkey", "p", "", "The private key of the wallet to send funds from.") + flags.StringVarP(&cliArgs.seed, "seed", "s", "", "The child wallet seed.") + + flags.Parse(os.Args) + + invalidScenario := false + var scenarioName string + var scenarioBuilder func() scenariotypes.Scenario + if flags.NArg() < 2 { + invalidScenario = true + } else { + scenarioName = flags.Args()[1] + scenarioBuilder = scenarios.Scenarios[scenarioName] + if scenarioBuilder == nil { + invalidScenario = true + } + } + if invalidScenario { + fmt.Printf("invalid or missing scenario\n\n") + fmt.Printf("implemented scenarios:\n") + scenarioNames := []string{} + for sn := range scenarios.Scenarios { + scenarioNames = append(scenarioNames, sn) + } + sort.Slice(scenarioNames, func(a int, b int) bool { + return strings.Compare(scenarioNames[a], scenarioNames[b]) > 0 + }) + for _, name := range scenarioNames { + fmt.Printf(" %v\n", name) + } + return + } + + scenario := scenarioBuilder() + if scenario == nil { + panic("could not create scenario instance") + } + + flags.Init(fmt.Sprintf("%v %v", flags.Args()[0], scenarioName), pflag.ExitOnError) + scenario.Flags(flags) + cliArgs = CliArgs{} + flags.Parse(os.Args) + + if cliArgs.trace { + logrus.SetLevel(logrus.TraceLevel) + } else if cliArgs.verbose { + logrus.SetLevel(logrus.DebugLevel) + } + + rpcHosts := []string{} + for _, rpcHost := range strings.Split(strings.Join(cliArgs.rpchosts, ","), ",") { + if rpcHost != "" { + rpcHosts = append(rpcHosts, rpcHost) + } + } + + if cliArgs.rpchostsFile != "" { + fileLines, err := utils.ReadFileLinesTrimmed(cliArgs.rpchostsFile) + if err != nil { + panic(err) + } + rpcHosts = append(rpcHosts, fileLines...) + } + + testerConfig := &tester.TesterConfig{ + RpcHosts: rpcHosts, + WalletPrivkey: cliArgs.privkey, + WalletCount: 100, + WalletPrefund: utils.EtherToWei(uint256.NewInt(3)), + WalletMinfund: utils.EtherToWei(uint256.NewInt(3)), + } + err := scenario.Init(testerConfig) + if err != nil { + panic(err) + } + tester := tester.NewTester(testerConfig) + err = tester.Start(cliArgs.seed) + if err != nil { + panic(err) + } + defer tester.Stop() + + err = scenario.Setup(tester) + if err != nil { + panic(err) + } + + err = scenario.Run() + if err != nil { + panic(err) + } +}