Showing preview only (523K chars total). Download the full file or copy to clipboard to get everything.
Repository: natrontech/kubelab
Branch: main
Commit: 5708ca17b031
Files: 240
Total size: 463.1 KB
Directory structure:
gitextract_b2npil12/
├── .dockerignore
├── .editorconfig
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ ├── dependabot.yml
│ └── workflows/
│ ├── ci.yml
│ ├── docker-release.yml
│ └── pr-labels.yml
├── .gitignore
├── .pre-commit-config.yaml
├── CODEOWNERS
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE
├── README.md
├── SECURITY.md
├── assets/
│ └── drawio/
│ └── architecture.drawio
├── deploy/
│ ├── clusterrole.yaml
│ ├── clusterrolebinding.yaml
│ ├── configmap.yaml
│ ├── ingress.yaml
│ ├── kustomization.yaml
│ ├── ns.yaml
│ ├── persistentvolume.yaml
│ ├── service.yaml
│ ├── serviceaccount.yaml
│ └── statefulset.yaml
├── docker-compose.yaml
├── kubelab-backend/
│ ├── .gitignore
│ ├── README.md
│ ├── entrypoint.sh
│ ├── example-hook-script.sh
│ ├── go.mod
│ ├── go.sum
│ ├── hooks/
│ │ └── hooks.go
│ ├── main.go
│ ├── modd.conf
│ ├── pb_migrations/
│ │ ├── 1671835039_created_hooks.js
│ │ ├── 1671926343_updated_hooks.js
│ │ ├── 1685691966_created_sessions.js
│ │ ├── 1686128320_updated_users.js
│ │ ├── 1686128357_created_labs.js
│ │ ├── 1686128492_updated_sessions.js
│ │ ├── 1686128857_updated_sessions.js
│ │ ├── 1686129123_created_exercises.js
│ │ ├── 1686129154_updated_labs.js
│ │ ├── 1686129166_updated_sessions.js
│ │ ├── 1686144343_updated_labs.js
│ │ ├── 1686311410_updated_sessions.js
│ │ ├── 1686311431_updated_sessions.js
│ │ ├── 1686311476_created_exercise_sessions.js
│ │ ├── 1686311483_updated_exercises.js
│ │ ├── 1686311638_updated_exercise_sessions.js
│ │ ├── 1686311654_updated_lab_sessions.js
│ │ ├── 1686311927_created_user_exercise_score.js
│ │ ├── 1686312000_updated_user_exercise_score.js
│ │ ├── 1686312029_updated_user_exercise_score.js
│ │ ├── 1686312069_updated_user_exercise_score.js
│ │ ├── 1686312095_updated_user_exercise_score.js
│ │ ├── 1686312146_updated_user_exercise_score.js
│ │ ├── 1686312270_deleted_user_exercise_score.js
│ │ ├── 1686312735_updated_labs.js
│ │ ├── 1686312758_updated_exercises.js
│ │ ├── 1686485128_updated_labs.js
│ │ ├── 1686485135_updated_lab_sessions.js
│ │ ├── 1686485142_updated_exercises.js
│ │ ├── 1686485148_updated_exercise_sessions.js
│ │ ├── 1686568901_updated_exercise_sessions.js
│ │ ├── 1686568909_updated_lab_sessions.js
│ │ ├── 1686569025_updated_lab_sessions.js
│ │ ├── 1686569075_updated_lab_sessions.js
│ │ ├── 1686569086_updated_lab_sessions.js
│ │ ├── 1686569099_updated_lab_sessions.js
│ │ ├── 1686569141_updated_exercise_sessions.js
│ │ ├── 1686600246_updated_exercise_sessions.js
│ │ ├── 1686600263_updated_exercise_sessions.js
│ │ ├── 1686990480_updated_exercise_sessions.js
│ │ ├── 1686990524_updated_lab_sessions.js
│ │ ├── 1687092568_updated_labs.js
│ │ ├── 1687092574_updated_exercises.js
│ │ ├── 1687092614_updated_exercises.js
│ │ ├── 1687092623_updated_labs.js
│ │ ├── 1687116582_updated_exercises.js
│ │ ├── 1687116588_updated_labs.js
│ │ ├── 1687200869_updated_users.js
│ │ ├── 1687200885_updated_exercises.js
│ │ ├── 1687201122_updated_users.js
│ │ ├── 1687201342_updated_exercises.js
│ │ ├── 1687201352_updated_labs.js
│ │ ├── 1687208987_created_material.js
│ │ ├── 1687209270_updated_material.js
│ │ ├── 1687209711_deleted_material.js
│ │ ├── 1692004564_created_plans.js
│ │ ├── 1692004578_created_features.js
│ │ ├── 1692004634_updated_plans.js
│ │ ├── 1692004755_updated_plans.js
│ │ ├── 1692006976_updated_users.js
│ │ ├── 1692013150_updated_users.js
│ │ ├── 1692013264_updated_plans.js
│ │ ├── 1692013276_updated_plans.js
│ │ ├── 1692013682_updated_features.js
│ │ ├── 1692029251_created_faqs.js
│ │ ├── 1692030049_created_companies.js
│ │ ├── 1692030210_updated_companies.js
│ │ ├── 1697824916_updated_users.js
│ │ ├── 1697824932_updated_users.js
│ │ ├── 1697982589_created_exercise_logs.js
│ │ ├── 1697982994_updated_exercise_logs.js
│ │ ├── 1697983253_updated_exercise_logs.js
│ │ ├── 1697983426_updated_exercise_session_logs.js
│ │ ├── 1697983434_updated_exercise_session_logs.js
│ │ ├── 1697983470_updated_users.js
│ │ ├── 1697983479_updated_exercise_sessions.js
│ │ ├── 1697983500_updated_lab_sessions.js
│ │ ├── 1697983560_updated_exercise_session_logs.js
│ │ ├── 1697984665_updated_exercise_session_logs.js
│ │ ├── 1697984707_updated_exercise_session_logs.js
│ │ ├── 1697986578_created_notifications.js
│ │ ├── 1697987216_updated_notifications.js
│ │ ├── 1697988059_updated_users.js
│ │ ├── 1697992209_updated_exercise_session_logs.js
│ │ ├── 1697992245_updated_notifications.js
│ │ ├── 1697992793_updated_notifications.js
│ │ └── 1697995266_updated_users.js
│ ├── pkg/
│ │ ├── controller/
│ │ │ ├── exercise.go
│ │ │ ├── handler.go
│ │ │ ├── lab.go
│ │ │ ├── sessions.go
│ │ │ └── util.go
│ │ ├── env/
│ │ │ └── env.go
│ │ ├── helm/
│ │ │ └── helm.go
│ │ ├── k8s/
│ │ │ ├── config.go
│ │ │ ├── deployment.go
│ │ │ ├── ingress.go
│ │ │ ├── namespace.go
│ │ │ ├── pod.go
│ │ │ ├── resourcequota.go
│ │ │ ├── secret.go
│ │ │ └── service.go
│ │ └── util/
│ │ └── helpers.go
│ └── vcluster-values.yaml
├── kubelab-fill/
│ ├── example_users.csv
│ ├── upload.py
│ └── users_import.py
├── kubelab-score/
│ ├── score.csv
│ └── score_calculation.py
└── kubelab-ui/
├── .dockerignore
├── .env.example
├── .eslintignore
├── .eslintrc.cjs
├── .gitignore
├── .npmrc
├── .prettierignore
├── .prettierrc
├── .vscode/
│ └── settings.json
├── Dockerfile
├── README.md
├── next-env.d.ts
├── package.json
├── playwright.config.ts
├── postcss.config.cjs
├── src/
│ ├── app.css
│ ├── app.d.ts
│ ├── app.html
│ ├── app.postcss
│ ├── hooks.client.ts
│ ├── lib/
│ │ ├── components/
│ │ │ ├── Console.svelte
│ │ │ ├── base/
│ │ │ │ ├── BackButton.svelte
│ │ │ │ ├── Badges.svelte
│ │ │ │ ├── Card.svelte
│ │ │ │ ├── Desktop.svelte
│ │ │ │ ├── Nav.svelte
│ │ │ │ ├── PlaceholderComponent.svelte
│ │ │ │ ├── SideOver.svelte
│ │ │ │ └── ToggleConfetti.svelte
│ │ │ ├── dashboard/
│ │ │ │ ├── Chart.svelte
│ │ │ │ ├── RunningExercises.svelte
│ │ │ │ └── data.ts
│ │ │ ├── labs/
│ │ │ │ ├── Exercise.svelte
│ │ │ │ └── Lab.svelte
│ │ │ ├── landingpage/
│ │ │ │ ├── Companies.svelte
│ │ │ │ ├── Cta.svelte
│ │ │ │ ├── Faq.svelte
│ │ │ │ ├── Features.svelte
│ │ │ │ ├── Footer.svelte
│ │ │ │ ├── Header.svelte
│ │ │ │ └── Hero.svelte
│ │ │ └── markdown/
│ │ │ ├── CodeComponent.svelte
│ │ │ ├── CodeSpanComponent.svelte
│ │ │ ├── LinkComponent.svelte
│ │ │ └── ListComponent.svelte
│ │ ├── config.ts
│ │ ├── mock-data.ts
│ │ ├── pocketbase/
│ │ │ ├── generated-types.ts
│ │ │ ├── index.ts
│ │ │ └── ui.ts
│ │ ├── stores/
│ │ │ ├── codeView.ts
│ │ │ ├── data.ts
│ │ │ ├── layout_store.ts
│ │ │ ├── loading.ts
│ │ │ ├── metadata.ts
│ │ │ ├── sidebar.ts
│ │ │ ├── tableView.ts
│ │ │ ├── terminal.ts
│ │ │ └── theme.ts
│ │ ├── terminal.ts
│ │ ├── types.ts
│ │ ├── utils/
│ │ │ ├── clickOutside.ts
│ │ │ ├── enums.ts
│ │ │ ├── interfaces.ts
│ │ │ └── time.ts
│ │ └── xterm.css
│ ├── routes/
│ │ ├── +layout.svelte
│ │ ├── +layout.ts
│ │ ├── +page.svelte
│ │ ├── +page.ts
│ │ ├── admin/
│ │ │ └── [id]/
│ │ │ ├── +page.svelte
│ │ │ └── +page.ts
│ │ ├── app/
│ │ │ ├── +layout.svelte
│ │ │ ├── +page.svelte
│ │ │ ├── +page.ts
│ │ │ └── profile/
│ │ │ └── +page.svelte
│ │ ├── labs/
│ │ │ ├── +layout.svelte
│ │ │ ├── +page.svelte
│ │ │ ├── +page.ts
│ │ │ └── [id]/
│ │ │ ├── +page.svelte
│ │ │ ├── +page.ts
│ │ │ └── [id]/
│ │ │ ├── +layout.svelte
│ │ │ ├── +layout.ts
│ │ │ └── +page.svelte
│ │ ├── login/
│ │ │ └── +page.svelte
│ │ └── material/
│ │ ├── +layout.svelte
│ │ └── +page.svelte
│ └── styles/
│ ├── prism.css
│ └── xterm.css
├── svelte.config.js
├── tailwind.config.cjs
├── tsconfig.json
└── vite.config.ts
================================================
FILE CONTENTS
================================================
================================================
FILE: .dockerignore
================================================
assets/
README.md
SECURITY.md
CODE_OF_CONDUCT.md
CODEOWNERS
LICENSE
================================================
FILE: .editorconfig
================================================
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[{*.go,Makefile,.gitmodules,go.mod,go.sum}]
indent_style = tab
[*.md]
indent_style = tab
trim_trailing_whitespace = false
[*.{yml,yaml,json}]
indent_style = space
indent_size = 2
[*.{js,jsx,ts,tsx,css,less,sass,scss,vue,py}]
indent_style = space
indent_size = 4
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
================================================
FILE: .github/dependabot.yml
================================================
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- package-ecosystem: "npm" # See documentation for possible values
directory: "kubelab-ui/" # Location of package manifests
schedule:
interval: "weekly"
commit-message:
prefix: ":robot:"
- package-ecosystem: "gomod" # See documentation for possible values
directory: "kubelab-backend/" # Location of package manifests
schedule:
interval: "weekly"
commit-message:
prefix: ":robot:"
# GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
commit-message:
prefix: ":seedling:"
================================================
FILE: .github/workflows/ci.yml
================================================
name: CI
on:
push:
branches: [ main ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ '*' ]
jobs:
go-build:
name: Backend Build
runs-on: ubuntu-latest
defaults:
run:
working-directory: kubelab-backend
strategy:
matrix:
goVer: [1.22]
steps:
- name: Set up Go ${{ matrix.goVer }}
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.goVer }}
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v4
- name: Get dependencies
run: |
go get -v -t -d ./...
go mod vendor
- name: Test
run: |
go test -v ./...
- name: Build
run: |
go build -v ./...
ui-build:
name: Frontend Build
runs-on: ubuntu-latest
defaults:
run:
working-directory: kubelab-ui
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- name: Install dependencies
run: npm install --legacy-peer-deps
- name: Build
run: npm run build
================================================
FILE: .github/workflows/docker-release.yml
================================================
name: Docker Image Build & Push
on:
release:
types: [created]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push-image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Log in to the Container registry
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@60a0d343a0d8a18aedee9d34e62251f752153bdb
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Build and push Docker image
uses: docker/build-push-action@5176d81f87c23d6fc96624dfdbcd9f3830bbe445
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
================================================
FILE: .github/workflows/pr-labels.yml
================================================
name: Size Label
on: pull_request
jobs:
size-label:
runs-on: ubuntu-latest
if: github.actor != 'dependabot[bot]'
steps:
- name: size-label
uses: "pascalgn/size-label-action@v0.5.4"
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
================================================
FILE: .gitignore
================================================
# Mac OS X files
.DS_Store
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
.glide/
# Dependency directories (remove the comment below to include it)
vendor/
pb_data/
temp/
tmp/
dist/
.builds/
.cache
.local
.npm
.env
.ash_history
/docker-compose.override.yml
_temp/
local_userlist_november.csv
venv/
================================================
FILE: .pre-commit-config.yaml
================================================
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
exclude: README.md
- repo: https://github.com/psf/black
rev: 22.8.0
hooks:
- id: black
- repo: https://github.com/dnephin/pre-commit-golang
rev: v0.5.0
hooks:
- id: go-fmt
- id: no-go-testing
# - id: golangci-lint
# exclude: '^tmp/'
# - id: go-unit-tests
- repo: https://github.com/pre-commit/mirrors-eslint
rev: "v8.25.0"
hooks:
- id: eslint
additional_dependencies:
- eslint-config-next@12.1.6
files: ^ui/
types_or: [ts, tsx]
- repo: https://github.com/pre-commit/mirrors-prettier
rev: "v2.7.1"
hooks:
- id: prettier
files: ^ui/
types_or: [javascript, jsx, ts, tsx, json, css, scss, markdown]
additional_dependencies:
- prettier
- prettier-plugin-svelte
- svelte
================================================
FILE: CODEOWNERS
================================================
* @janlauber
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
info@natron.io.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
<https://www.contributor-covenant.org/version/2/0/code_of_conduct.html>.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
<https://www.contributor-covenant.org/faq>. Translations are available at
<https://www.contributor-covenant.org/translations>.
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing
When contributing to this repository, please first discuss the change you wish to make via issue,
email, or any other method with the owners of this repository before making a change.
Please note we have a code of conduct, please follow it in all your interactions with the project.
## Pull Request Process
1. Ensure any install or build dependencies are removed before the end of the layer when doing a
build.
2. Update the README.md with details of changes to the interface, this includes new environment
variables, exposed ports, useful file locations and container parameters.
3. Increase the version numbers in any examples files and the README.md to the new version that this
Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/).
4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you
do not have permission to do that, you may request the second reviewer to merge it for you.
## Code of Conduct
### Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
### Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
### Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
### Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
### Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at [INSERT EMAIL ADDRESS]. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
### Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/
================================================
FILE: Dockerfile
================================================
FROM golang:1.22-alpine AS backend-builder
WORKDIR /build
COPY kubelab-backend/go.mod kubelab-backend/go.sum kubelab-backend/main.go ./
COPY kubelab-backend/hooks ./hooks
COPY kubelab-backend/pkg ./pkg
COPY kubelab-backend/vcluster-values.yaml ./vcluster-values.yaml
RUN apk --no-cache add upx make git gcc libtool musl-dev ca-certificates dumb-init \
&& go mod tidy \
&& CGO_ENABLED=0 go build \
&& upx kubelab
FROM node:lts-slim as ui-builder
WORKDIR /build
COPY ./kubelab-ui/package*.json ./
RUN rm -rf ./node_modules
RUN rm -rf ./build
COPY ./kubelab-ui .
RUN npm install --legacy-peer-deps
RUN npm run build
FROM alpine as runtime
WORKDIR /app/kubelab
COPY --from=backend-builder /build/kubelab /app/kubelab/kubelab
COPY --from=backend-builder /build/vcluster-values.yaml /app/kubelab/vcluster-values.yaml
COPY ./kubelab-backend/pb_migrations ./pb_migrations
COPY --from=ui-builder /build/build /app/kubelab/pb_public
EXPOSE 8090
CMD ["/app/kubelab/kubelab","serve", "--http", "0.0.0.0:8090"]
================================================
FILE: LICENSE
================================================
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 2023 Natron Tech GmbH / Natron Tech AG
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.
================================================
FILE: README.md
================================================
# KubeLab: The Ultimate Kubernetes Learning Platform
<p align="center">
<a href="https://kubelab.natron.io">
<img height="130px" src="assets/kubelab-logo.png" />
</a>
</p>
<p align="center">
<strong>
<a href="https://kubelab.natron.io/">KubeLab</a>
<br />
Embark on your Kubernetes Journey through Hands-on Practice
</strong>
</p>
<p align="center">
<a href="https://github.com/natrontech/kubelab/issues"><img
src="https://img.shields.io/github/issues/natrontech/kubelab"
alt="Build"
/></a>
<a href="https://github.com/natrontech/kubelab"><img
src="https://img.shields.io/github/license/natrontech/kubelab"
alt="License"
/></a>
<img alt="GitHub go.mod Go version" src="https://img.shields.io/github/go-mod/go-version/natrontech/kubelab/main/kubelab-backend?label=Go%20Version" />
<img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/natrontech/kubelab/ci.yml?label=CI" />
<img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/natrontech/kubelab/codeql.yml?label=CodeQL" />
<img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/natrontech/kubelab/docker-release.yml?label=Docker%20Release" />
</p>
<h2></h2>
Welcome to KubeLab! Our advanced web-based platform offers a rich set of interactive labs, specifically crafted for Kubernetes workshops. We aim to revolutionize your learning experience by making it more interactive, engaging, and practical. Our labs will help you grasp and apply complex Kubernetes concepts in a real-world context.
KubeLab is a proud offering by [Natron Tech](https://natron.io), and if you're interested in a tailor-made Kubernetes services or workshops for your company, do not hesitate to reach out to us!
KubeLab is built using:
- [kubelab-agent](https://github.com/natrontech/kubelab-agent)
- [xterm.js](https://xtermjs.org/)
- [code-server](https://docs.linuxserver.io/images/docker-code-server/)
- [pocketbase](https://pocketbase.io)
- [vcluster](https://vcluster.com)
<p align="center">
<img height="500px" src="assets/screenrecording.gif" />
</p>
**Please note:** This project is still in its early stages, and we're diligently working to enhance your experience. However, bugs might appear, and your patience and feedback will be greatly appreciated.
---
## Cloud Version
You can access the cloud version of KubeLab at [kubelab.ch](https://kubelab.ch). This version is hosted by us. As of security reasons, we do not let you to sign up for the cloud version. If you want to use KubeLab for your company, please contact us at [info@natron.io](mailto:info@natron.io). Also, when you want to host a KubeLab instance for your company, we can provide you with a hosted version of KubeLab.
## Features
### Web Terminal
KubeLab features a smooth in-browser terminal, letting you execute commands and interact with your Kubernetes cluster in real-time, without needing any additional setup or software.
### Code Editor
KubeLab comes with a vscode-based code editor, allowing you to edit and run code directly from your browser. The editor supports syntax highlighting, code completion, and more. Watch out for the code editor button in the bottom left corner of your screen.
### Dedicated Cluster Per Session
Every learning session on KubeLab has its own isolated Kubernetes cluster. This design ensures a secure and dedicated learning environment, enabling you to experiment with Kubernetes without impacting others.
### Custom Kubernetes Labs
With KubeLab, you can define your own labs and exercises. Check out our workshops [here](https://github.com/natrontech/kubelab-workshops).
All the labs and exercises follow the same structure and can be easily created and shared.
The structure of a lab with an exercise is as follows:
```bash
kubelab-workshops/kubernetes-basics # Example workshop
└── 01_introduction # Name of the lab
├── 01_get_kubectl_version # Name of the exercise
│ ├── bootstrap.sh # The script that will be executed when the exercise starts
│ ├── check.sh # The script that will be executed to check if the exercise is solved
│ ├── docs.md # The text that will be displayed to the user
│ ├── hint.md # The hint that will be displayed to the user
│ └── solution.md # The solution that will be displayed to the user
└── docs.md # The text that will be displayed to the user for the lab
```
### Workshop Mode
KubeLab offers a workshop mode, allowing you to hold your own Kubernetes workshops. Each user can have the `workshop` set to `true` and a `company` assigned. This will allow you to filter the users by company and see their progress. Then you can create a user with the role `admin` and when you log in with this user you can see the dashboards of all the companies. The users then also have an addional `request for help` button in the UI which will send a real-time notification to the dashboard.
---
## Development
Interested in contributing to KubeLab? Please make sure you have the following prerequisites:
- [Docker](https://docs.docker.com/get-docker/)
- [Docker Compose](https://docs.docker.com/compose/install/)
- [Node.js](https://nodejs.org/en/download/) (v18+)
- [Go](https://golang.org/doc/install) (v1.22+)
- [modd](https://github.com/cortesi/modd/releases)
Please refer to our detailed development guides for the [backend](./kubelab-backend/README.md) and [frontend](./kubelab-ui/README.md) to get started. For contributing, please read our [CONTRIBUTING.md](CONTRIBUTING.md) for information on code conduct and the process for submitting pull requests.
## Deployment
To get started, ensure that you have a Kubernetes cluster (v1.27+).
1. Take a look at the [deployment](./deployment) folder for an example deployment.
2. Create a wildcard certificate and store it in a secret. (You can use [cert-manager](https://cert-manager.io/docs/) for this)
```yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: kubelab-ch-wildcard-cert
namespace: cert-manager
spec:
secretName: kubelab-ch-wildcard-cert
issuerRef:
name: letsencrypt
kind: ClusterIssuer
dnsNames:
- "*.kubelab.ch"
secretTemplate:
annotations:
reflector.v1.k8s.emberstack.com/reflection-allowed: "true"
reflector.v1.k8s.emberstack.com/reflection-allowed-namespaces: ""
reflector.v1.k8s.emberstack.com/reflection-auto-enabled: "true"
reflector.v1.k8s.emberstack.com/reflection-auto-namespaces: ""
```
3. Deploy the [reflector](https://github.com/emberstack/kubernetes-reflector/tree/main/src/helm/reflector) to sync the TLS secret with each namespace.
4. Deploy the KubeLab as described in the [deployment](./deployment) folder.
5. Access the Pocketbase UI and create a admin user. (https://<ingress-url>/_)
6. Disable the `create` authentication for the `labs` and `exercises` collections in Pocketbase. (This is because you wan't to use the `kubelab-fill` script to fill the labs and exercises collections.)

7. Run the [kubelab-fill](./kubelab-fill) script to fill the labs and exercises collections in Pocketbase. (Make sure to set the environment variables in the script `.env.example`)
8. Now disable the `create` authentication for the `labs` and `exercises` collections in Pocketbase again.
9. Create a user in the Pocketbase UI in the `users` collection and set the `role` to `user`.
10. Access the KubeLab UI and login with the user you created in step 9. (https://<ingress-url>/)
### Environment Variables
The following environment variables are required for KubeLab to function properly:
| Variable Name | Default | Description |
| --------------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| `LOCAL` | `false` | Set to `true` if you're running KubeLab locally. It will take your local kubeconfig under .kube/config |
| `KUBELAB_AGENT_IMAGE` | `ghcr.io/natrontech/kubelab-agent:latest` | The image for the agent |
| `CODE_SERVER_IMAGE` | `ghcr.io/natrontech/kubelab-code-server:latest` | The image for the code-server |
| `ALLOWED_HOSTS` | `*` | The allowed hosts for the backend |
| `RESOURCE_NAME` | `kubelab` | The name of the resource |
| `AGENT_INGRESS_CLASS` | `nginx` | The ingress class for the agent |
| `PODS_LIMIT` | `70` | The maximum number of pods allowed per session |
| `STORAGE_LIMIT` | `50Gi` | The maximum storage allowed per session |
| `VCLUSTER_CHART_VERSION` | `0.16.4` | The version of the vcluster chart |
| `VCLUSTER_VALUES_FILE_PATH` | `./vcluster-values.yaml` | The path to the vcluster values file |
| `CronTick` | `* * * * *` | The cron tick which creates user sessions for each lab and exercise |
| `TlsSecretName` | `kubelab-tls` | The name of the TLS secret which will be for each agent ingress instance (use a wildcard certificate) |
## Known Issues
- Either you need to create a wildcard Certificate and use it as the default TLS secret or you need to use something like [reflector](https://github.com/emberstack/kubernetes-reflector/tree/main/src/helm/reflector) to sync the TLS secret with each namespace.
- The labs need to be manually created via an upload script in [./kubelab-fill](./kubelab-fill). This will be automated in the future.
- Signup is not yet implemented. We're working on it and want to make a free signup with a limited number of sessions available soon.
- The frontend is not yet optimized for mobile devices. This is **not a priority** for us at the moment, but we'll get to it eventually.
---
Begin your Kubernetes journey with KubeLab today!
================================================
FILE: SECURITY.md
================================================
# Security Policy
## Supported Versions
| Version | Supported |
| ------- | ------------------ |
| latest | :white_check_mark: |
## Reporting a Vulnerability
Open up an issue :)
================================================
FILE: assets/drawio/architecture.drawio
================================================
<mxfile host="app.diagrams.net" modified="2023-06-12T06:58:34.998Z" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36" etag="NxmnkzNyEjIMfRW3cUqh" version="21.3.8" type="device">
<diagram name="Page-1" id="WUQg41ddvhskJoTHI96X">
<mxGraphModel dx="1010" dy="1143" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="UOWZPONmYOvx8WyAluzy-37" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.25;entryDx=0;entryDy=0;" parent="1" source="UOWZPONmYOvx8WyAluzy-5" target="UOWZPONmYOvx8WyAluzy-9" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-38" value="1" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="UOWZPONmYOvx8WyAluzy-37" vertex="1" connectable="0">
<mxGeometry x="-0.8627" y="-1" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-39" value="n" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="UOWZPONmYOvx8WyAluzy-37" vertex="1" connectable="0">
<mxGeometry x="0.8064" y="2" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-5" value="Users" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=none;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="100" y="100" width="180" height="182" as="geometry" />
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-6" value="+ username: type" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" parent="UOWZPONmYOvx8WyAluzy-5" vertex="1">
<mxGeometry y="26" width="180" height="26" as="geometry" />
</mxCell>
<mxCell id="7reBWwmH2FoituciZp9q-1" value="+ email: type" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" parent="UOWZPONmYOvx8WyAluzy-5" vertex="1">
<mxGeometry y="52" width="180" height="26" as="geometry" />
</mxCell>
<mxCell id="7reBWwmH2FoituciZp9q-2" value="+ name: type" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" parent="UOWZPONmYOvx8WyAluzy-5" vertex="1">
<mxGeometry y="78" width="180" height="26" as="geometry" />
</mxCell>
<mxCell id="7reBWwmH2FoituciZp9q-3" value="+ avatar: type" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" parent="UOWZPONmYOvx8WyAluzy-5" vertex="1">
<mxGeometry y="104" width="180" height="26" as="geometry" />
</mxCell>
<mxCell id="7reBWwmH2FoituciZp9q-4" value="+ totalScore: type" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" parent="UOWZPONmYOvx8WyAluzy-5" vertex="1">
<mxGeometry y="130" width="180" height="26" as="geometry" />
</mxCell>
<mxCell id="7reBWwmH2FoituciZp9q-5" value="+ avgMinutesToSolution: type" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" parent="UOWZPONmYOvx8WyAluzy-5" vertex="1">
<mxGeometry y="156" width="180" height="26" as="geometry" />
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-25" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="UOWZPONmYOvx8WyAluzy-9" target="UOWZPONmYOvx8WyAluzy-17" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-29" value="1" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="UOWZPONmYOvx8WyAluzy-25" vertex="1" connectable="0">
<mxGeometry x="0.6766" y="1" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-30" value="1" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="UOWZPONmYOvx8WyAluzy-25" vertex="1" connectable="0">
<mxGeometry x="-0.7247" y="-1" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-31" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="UOWZPONmYOvx8WyAluzy-21" target="UOWZPONmYOvx8WyAluzy-13" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-32" value="1" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="UOWZPONmYOvx8WyAluzy-31" vertex="1" connectable="0">
<mxGeometry x="0.7067" y="-1" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-33" value="1" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="UOWZPONmYOvx8WyAluzy-31" vertex="1" connectable="0">
<mxGeometry x="-0.7495" y="1" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-34" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="UOWZPONmYOvx8WyAluzy-9" target="UOWZPONmYOvx8WyAluzy-15" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-35" value="1" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="UOWZPONmYOvx8WyAluzy-34" vertex="1" connectable="0">
<mxGeometry x="-0.8376" y="-1" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-36" value="1" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="UOWZPONmYOvx8WyAluzy-34" vertex="1" connectable="0">
<mxGeometry x="0.7732" y="-1" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-9" value="Sessions" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=none;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="460" y="210" width="140" height="156" as="geometry" />
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-10" value="+ user: type" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" parent="UOWZPONmYOvx8WyAluzy-9" vertex="1">
<mxGeometry y="26" width="140" height="26" as="geometry" />
</mxCell>
<mxCell id="7reBWwmH2FoituciZp9q-7" value="+ startTime: dateTime" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" parent="UOWZPONmYOvx8WyAluzy-9" vertex="1">
<mxGeometry y="52" width="140" height="26" as="geometry" />
</mxCell>
<mxCell id="7reBWwmH2FoituciZp9q-8" value="+ endTime: dateTime" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" parent="UOWZPONmYOvx8WyAluzy-9" vertex="1">
<mxGeometry y="78" width="140" height="26" as="geometry" />
</mxCell>
<mxCell id="7reBWwmH2FoituciZp9q-9" value="+ lab: relation" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" parent="UOWZPONmYOvx8WyAluzy-9" vertex="1">
<mxGeometry y="104" width="140" height="26" as="geometry" />
</mxCell>
<mxCell id="7reBWwmH2FoituciZp9q-11" value="+ clusterRunning: bool" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" parent="UOWZPONmYOvx8WyAluzy-9" vertex="1">
<mxGeometry y="130" width="140" height="26" as="geometry" />
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-13" value="Kubelab Agent" style="html=1;dropTarget=0;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="470" y="530" width="130" height="60" as="geometry" />
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-14" value="" style="shape=module;jettyWidth=8;jettyHeight=4;" parent="UOWZPONmYOvx8WyAluzy-13" vertex="1">
<mxGeometry x="1" width="20" height="20" relative="1" as="geometry">
<mxPoint x="-27" y="7" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-15" value="vCluster" style="html=1;dropTarget=0;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="310" y="400" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-16" value="" style="shape=module;jettyWidth=8;jettyHeight=4;" parent="UOWZPONmYOvx8WyAluzy-15" vertex="1">
<mxGeometry x="1" width="20" height="20" relative="1" as="geometry">
<mxPoint x="-27" y="7" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-26" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="UOWZPONmYOvx8WyAluzy-17" target="UOWZPONmYOvx8WyAluzy-21" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-27" value="n" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="UOWZPONmYOvx8WyAluzy-26" vertex="1" connectable="0">
<mxGeometry x="0.4171" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-28" value="1" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="UOWZPONmYOvx8WyAluzy-26" vertex="1" connectable="0">
<mxGeometry x="-0.6667" y="-1" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-17" value="Labs" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=none;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="630" y="140" width="140" height="130" as="geometry" />
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-18" value="+ title: text" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" parent="UOWZPONmYOvx8WyAluzy-17" vertex="1">
<mxGeometry y="26" width="140" height="26" as="geometry" />
</mxCell>
<mxCell id="7reBWwmH2FoituciZp9q-14" value="+ description: text" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" parent="UOWZPONmYOvx8WyAluzy-17" vertex="1">
<mxGeometry y="52" width="140" height="26" as="geometry" />
</mxCell>
<mxCell id="7reBWwmH2FoituciZp9q-15" value="+ docs: url" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" parent="UOWZPONmYOvx8WyAluzy-17" vertex="1">
<mxGeometry y="78" width="140" height="26" as="geometry" />
</mxCell>
<mxCell id="7reBWwmH2FoituciZp9q-16" value="+ exercises: relation" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" parent="UOWZPONmYOvx8WyAluzy-17" vertex="1">
<mxGeometry y="104" width="140" height="26" as="geometry" />
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-21" value="Exercises" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=none;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="630" y="352" width="140" height="208" as="geometry" />
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-24" value="+ title: text" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" parent="UOWZPONmYOvx8WyAluzy-21" vertex="1">
<mxGeometry y="26" width="140" height="26" as="geometry" />
</mxCell>
<mxCell id="7reBWwmH2FoituciZp9q-13" value="+ description: text" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" parent="UOWZPONmYOvx8WyAluzy-21" vertex="1">
<mxGeometry y="52" width="140" height="26" as="geometry" />
</mxCell>
<mxCell id="7reBWwmH2FoituciZp9q-12" value="+ docs: url" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" parent="UOWZPONmYOvx8WyAluzy-21" vertex="1">
<mxGeometry y="78" width="140" height="26" as="geometry" />
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-41" value="+ hint: url" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" parent="UOWZPONmYOvx8WyAluzy-21" vertex="1">
<mxGeometry y="104" width="140" height="26" as="geometry" />
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-42" value="+ solution: url" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" parent="UOWZPONmYOvx8WyAluzy-21" vertex="1">
<mxGeometry y="130" width="140" height="26" as="geometry" />
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-43" value="+ check: url" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" parent="UOWZPONmYOvx8WyAluzy-21" vertex="1">
<mxGeometry y="156" width="140" height="26" as="geometry" />
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-44" value="+ bootstrap: url" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" parent="UOWZPONmYOvx8WyAluzy-21" vertex="1">
<mxGeometry y="182" width="140" height="26" as="geometry" />
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-45" value="" style="strokeWidth=1;shadow=0;dashed=0;align=center;html=1;shape=mxgraph.mockup.containers.browserWindow;rSize=0;strokeColor=#666666;strokeColor2=#008cff;strokeColor3=#c4c4c4;mainText=,;recursiveResize=0;" parent="1" vertex="1">
<mxGeometry x="220" y="710" width="550" height="380" as="geometry" />
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-46" value="Page 1" style="strokeWidth=1;shadow=0;dashed=0;align=center;html=1;shape=mxgraph.mockup.containers.anchor;fontSize=17;fontColor=#666666;align=left;whiteSpace=wrap;" parent="UOWZPONmYOvx8WyAluzy-45" vertex="1">
<mxGeometry x="60" y="12" width="110" height="26" as="geometry" />
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-47" value="https://www.draw.io" style="strokeWidth=1;shadow=0;dashed=0;align=center;html=1;shape=mxgraph.mockup.containers.anchor;rSize=0;fontSize=17;fontColor=#666666;align=left;" parent="UOWZPONmYOvx8WyAluzy-45" vertex="1">
<mxGeometry x="130" y="60" width="250" height="26" as="geometry" />
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-48" value="Lab1" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" parent="UOWZPONmYOvx8WyAluzy-45" vertex="1">
<mxGeometry x="50" y="120" width="80" height="30" as="geometry" />
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-49" value="Lab2" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" parent="UOWZPONmYOvx8WyAluzy-45" vertex="1">
<mxGeometry x="50" y="150" width="80" height="30" as="geometry" />
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-50" value="Lab3" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" parent="UOWZPONmYOvx8WyAluzy-45" vertex="1">
<mxGeometry x="50" y="180" width="80" height="30" as="geometry" />
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-51" value="start" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" parent="UOWZPONmYOvx8WyAluzy-45" vertex="1">
<mxGeometry x="350" y="120" width="40" height="30" as="geometry" />
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-52" value="stop" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" parent="UOWZPONmYOvx8WyAluzy-45" vertex="1">
<mxGeometry x="350" y="150" width="40" height="30" as="geometry" />
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-53" value="console" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" parent="UOWZPONmYOvx8WyAluzy-45" vertex="1">
<mxGeometry x="300" y="150" width="50" height="30" as="geometry" />
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-54" value="1/3 Session" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" parent="UOWZPONmYOvx8WyAluzy-45" vertex="1">
<mxGeometry x="460" y="110" width="80" height="30" as="geometry" />
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-55" value="start" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" parent="UOWZPONmYOvx8WyAluzy-45" vertex="1">
<mxGeometry x="350" y="175" width="40" height="30" as="geometry" />
</mxCell>
<mxCell id="UOWZPONmYOvx8WyAluzy-56" value="8/10 done" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" parent="UOWZPONmYOvx8WyAluzy-45" vertex="1">
<mxGeometry x="100" y="150" width="70" height="30" as="geometry" />
</mxCell>
<mxCell id="cBvfc4a4BUQHZ_fMePWL-1" value="" style="strokeWidth=1;shadow=0;dashed=0;align=center;html=1;shape=mxgraph.mockup.containers.browserWindow;rSize=0;strokeColor=#666666;strokeColor2=#008cff;strokeColor3=#c4c4c4;mainText=,;recursiveResize=0;" vertex="1" parent="1">
<mxGeometry x="150" y="1200" width="770" height="620" as="geometry" />
</mxCell>
<mxCell id="cBvfc4a4BUQHZ_fMePWL-2" value="Page 1" style="strokeWidth=1;shadow=0;dashed=0;align=center;html=1;shape=mxgraph.mockup.containers.anchor;fontSize=17;fontColor=#666666;align=left;whiteSpace=wrap;" vertex="1" parent="cBvfc4a4BUQHZ_fMePWL-1">
<mxGeometry x="60" y="12" width="110" height="26" as="geometry" />
</mxCell>
<mxCell id="cBvfc4a4BUQHZ_fMePWL-3" value="https://www.draw.io" style="strokeWidth=1;shadow=0;dashed=0;align=center;html=1;shape=mxgraph.mockup.containers.anchor;rSize=0;fontSize=17;fontColor=#666666;align=left;" vertex="1" parent="cBvfc4a4BUQHZ_fMePWL-1">
<mxGeometry x="130" y="60" width="250" height="26" as="geometry" />
</mxCell>
<mxCell id="cBvfc4a4BUQHZ_fMePWL-5" value="CLI" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="cBvfc4a4BUQHZ_fMePWL-1">
<mxGeometry y="140" width="390" height="450" as="geometry" />
</mxCell>
<mxCell id="cBvfc4a4BUQHZ_fMePWL-10" value="Docs" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="cBvfc4a4BUQHZ_fMePWL-1">
<mxGeometry x="390" y="140" width="380" height="320" as="geometry" />
</mxCell>
<mxCell id="cBvfc4a4BUQHZ_fMePWL-11" value="Hints / Solution" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="cBvfc4a4BUQHZ_fMePWL-1">
<mxGeometry x="390" y="460" width="380" height="130" as="geometry" />
</mxCell>
<mxCell id="cBvfc4a4BUQHZ_fMePWL-12" value="Check" style="strokeWidth=1;shadow=0;dashed=0;align=center;html=1;shape=mxgraph.mockup.buttons.button;strokeColor=#666666;fontColor=#ffffff;mainText=;buttonStyle=round;fontSize=17;fontStyle=1;fillColor=#008cff;whiteSpace=wrap;" vertex="1" parent="cBvfc4a4BUQHZ_fMePWL-1">
<mxGeometry x="660" y="600" width="100" height="20" as="geometry" />
</mxCell>
<mxCell id="cBvfc4a4BUQHZ_fMePWL-13" value="Reveal Hint / Solution" style="strokeWidth=1;shadow=0;dashed=0;align=center;html=1;shape=mxgraph.mockup.buttons.button;strokeColor=#666666;fontColor=#ffffff;mainText=;buttonStyle=round;fontSize=17;fontStyle=1;fillColor=#008cff;whiteSpace=wrap;" vertex="1" parent="cBvfc4a4BUQHZ_fMePWL-1">
<mxGeometry x="490" y="480" width="180" height="20" as="geometry" />
</mxCell>
<mxCell id="cBvfc4a4BUQHZ_fMePWL-14" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" vertex="1" parent="cBvfc4a4BUQHZ_fMePWL-1">
<mxGeometry x="10" y="600" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="cBvfc4a4BUQHZ_fMePWL-15" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" vertex="1" parent="cBvfc4a4BUQHZ_fMePWL-1">
<mxGeometry x="60" y="600" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="cBvfc4a4BUQHZ_fMePWL-16" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" vertex="1" parent="cBvfc4a4BUQHZ_fMePWL-1">
<mxGeometry x="110" y="600" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="cBvfc4a4BUQHZ_fMePWL-17" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" vertex="1" parent="cBvfc4a4BUQHZ_fMePWL-1">
<mxGeometry x="160" y="600" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="cBvfc4a4BUQHZ_fMePWL-18" value="Start Exercise" style="strokeWidth=1;shadow=0;dashed=0;align=center;html=1;shape=mxgraph.mockup.buttons.button;strokeColor=#666666;fontColor=#ffffff;mainText=;buttonStyle=round;fontSize=17;fontStyle=1;fillColor=#008cff;whiteSpace=wrap;" vertex="1" parent="cBvfc4a4BUQHZ_fMePWL-1">
<mxGeometry x="120" y="250" width="150" height="40" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>
================================================
FILE: deploy/clusterrole.yaml
================================================
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cluster-admin
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]
================================================
FILE: deploy/clusterrolebinding.yaml
================================================
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: kubelab-admin
subjects:
- kind: ServiceAccount
name: kubelab-admin
namespace: kubelab
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io
================================================
FILE: deploy/configmap.yaml
================================================
apiVersion: v1
kind: ConfigMap
metadata:
name: kubelab-config
data:
KUBELAB_AGENT_IMAGE: ghcr.io/natrontech/kubelab-agent:latest
ALLOWED_HOSTS: kubelab.natr-demo.k8s.natron.cloud
PODS_LIMIT: "70"
STORAGE_LIMIT: "50Gi"
AGENT_INGRESS_CLASS: nginx-external
================================================
FILE: deploy/ingress.yaml
================================================
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: kubelab-ingress
namespace: kubelab
annotations:
# cert-manager.io/cluster-issuer: letsencrypt-prod-natron-cloud
cert-manager.io/private-key-rotation-policy: Always
ingress.kubernetes.io/force-ssl-redirect: "true"
spec:
ingressClassName: nginx-external
rules:
- host: kubelab.natr-demo.k8s.natron.cloud
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: kubelab-service
port:
number: 8090
# tls:
# - hosts:
# - kubelab.natr-demo.k8s.natron.cloud
# secretName: kubelab-tls
================================================
FILE: deploy/kustomization.yaml
================================================
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: kubelab
resources:
- ns.yaml
- service.yaml
- statefulset.yaml
- ingress.yaml
- serviceaccount.yaml
- clusterrole.yaml
- clusterrolebinding.yaml
- configmap.yaml
================================================
FILE: deploy/ns.yaml
================================================
apiVersion: v1
kind: Namespace
metadata:
name: kubelab
================================================
FILE: deploy/persistentvolume.yaml
================================================
apiVersion: v1
kind: PersistentVolume
metadata:
name: hostpath-pv
namespace: kubelab
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 10Gi # Adjust size as needed
accessModes:
- ReadWriteOnce
hostPath:
path: "/data/kubelab" # Directory on the host
================================================
FILE: deploy/service.yaml
================================================
apiVersion: v1
kind: Service
metadata:
name: kubelab-service
namespace: kubelab
spec:
selector:
app: kubelab
ports:
- protocol: TCP
port: 8090
================================================
FILE: deploy/serviceaccount.yaml
================================================
apiVersion: v1
kind: ServiceAccount
metadata:
name: kubelab-admin
namespace: kubelab
================================================
FILE: deploy/statefulset.yaml
================================================
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: kubelab-statefulset
namespace: kubelab
spec:
serviceName: "kubelab-service"
replicas: 1
selector:
matchLabels:
app: kubelab
template:
metadata:
labels:
app: kubelab
spec:
serviceAccountName: kubelab-admin
containers:
- name: kubelab
image: ghcr.io/natrontech/kubelab:latest
# image: kubelab:local
ports:
- containerPort: 8090
volumeMounts:
- name: pb-data-hostpath
mountPath: /app/kubelab/pb_data
envFrom:
- configMapRef:
name: kubelab-config
resources:
requests:
memory: "16Gi"
cpu: "4"
limits:
memory: "16Gi"
cpu: "8"
nodeSelector:
hostpath-node: "true"
volumes:
- name: pb-data-hostpath
persistentVolumeClaim:
claimName: hostpath-pvc
volumeClaimTemplates:
- metadata:
name: pb-data-hostpath
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 10Gi
storageClassName: manual
volumeMode: Filesystem
volumeName: hostpath-pv
================================================
FILE: docker-compose.yaml
================================================
version: '3.5'
services:
kubelab:
build: .
# image: ghcr.io/natrontech/kubelab:latest
ports:
- "8090:8090"
volumes:
# * This is the path to the data folder on your local machine
- $PWD/kubelab-backend/pb_data:/app/kubelab/pb_data
================================================
FILE: kubelab-backend/.gitignore
================================================
/.cache
/pocketbase
/pocketbase*.zip
/pb_data
/pb_data_old
/tmp
/bin
./pocketbase
kubelab
================================================
FILE: kubelab-backend/README.md
================================================
# KubeLab Backend
There are two flavors of the backend:
1. standard release downloaded from https://github.com/pocketbase/pocketbase/releases. This one is a good start, but most real-world applications would require more (see next).
2. custom compiled (`go build`), possibly with my customizations and perhaps yours too.
Out of the box, the project assumes #2 (custom compiled with my customizations).
## standard (official) release of pocketbase
Download from release archive from https://github.com/pocketbase/pocketbase/releases/latest, unzip it and place the `pocketbase` binary in this folder, and you're done.
## custom build
If you would like to extend PocketBase and use it as a framework then there is a `main.go` in this folder that you can customize and build using `go build` or do live development using `modd`.
See https://pocketbase.io/docs/use-as-framework/ for details.
# Setup
## Architecture
> **Note:** For optimal set up, ensure you are using a standard distribution of Linux. For other operating systems, you may run into issues, or need additional configuration.
> A docker-compose setup is included with the project, which can be used on any OS.
## Build
Assuming you have Go language tools installed ...
`go build`
If you don't have Go and don't want to install it, you can use docker-compose setup. Otherwise, your only choice is to download the binary from https://github.com/pocketbase/pocketbase/releases/latest, and placing it in this folder. But then you will not be able to use any of the custom code (such as "config-driven hooks")
## Run migrations
Before you can run the actual backend, you must run the migrations using `./pocketbase migrate up` in the current directory. It will create appropriate schema tables/collections.
## Run the backend
You can run the PocketBase backend direct with `./pocketbase serve` or using `npm run backend` in the `sk` directory. Note that if you want the backend to also serve the frontend assets, then you must add the `--publicDir ../frontend/build` option.
## Docker
A highly recommended option is to run it inside a Docker container. A `Dockerfile` is included that builds a production Docker image. Also, a `docker-compose.yml` along with an _override_ file example are included, which should be used during development.
## Active development with `modd`
Finally, if you are going to actively develop using Go using PocketBase as a framework, then you probably want to use [modd](https://github.com/cortesi/modd), a development tool that rebuilds and restarts your Go binary everytime a source file changes (live reload on change). An basic `modd.conf` config file is included in this setup. You can run it by installing `modd` (`go install github.com/cortesi/modd/cmd/modd@latest`) and then running `modd`. All this is done automatically for you if you are using Docker.
# Schema (Collections)
With the 0.9 version of PocketBase, JavaScript auto-migrations as implemented. The JS files in `pb_migrations` can create/drop/modify collections and data. These are executed automatically by PocketBase on startup.
Not only that, they are also generated automatically whenever you change the schema! So go ahead and make changes to the schema and watch new JS files generated in the `pb_migrations` folder. Just remember to commit them to version control.
## Generated Types
The file `generated-types.ts` contains TypeScript definitions of `Record` types mirroring the fields in your database collections. But it needs to be regenerated every time you modify the schema. This can be done by simply running the `typegen` script in the frontend's `package.json`. So remember to do that.
# Hooks
PocketBase provides API's like .OnModelBefore* and .OnModelAfter* to run
callbacks when records change. This app builds on top of that by providing
a "hooks" table that drives those hooks using configuration. It has the
following fields:
- collection: name of the collection that triggers an action
- event: insert/update/delete event that triggers the action
- action_type: "command" if you want to run a program/script or "post" if
you want to POST to a webhook endpoint. The record will be marshaled to
JSON and passed to the command as STDIN or to the webhook POST as
request body (with header 'content-type: application/json')
- action: path to the command/script or URL of the webhook to POST to
- action_params: a string that will be passed as argument to the action
So now by configuring the above table, you can execute external commands/scripts
and POST data to external webhooks in reaction to insert/update/delete of
records.
Most web services these days provide webhook endpoints (e.g. sendgrid, mailchimp, stripe, etc) which you can POST directly to. But if you need special
processing then you can write a script that receives changed data as JSON, parses and manipulates it using [`jq`](https://github.com/stedolan/jq) before
sending it on its way.
See `example-hook-script.sh` for a demonstration.
Possible use cases:
- Clone git repo when a record is inserted to "repositories" table
- Execute a terraform script when a new cluster is inserted to "clusters" table
- Send an acknowledgement email when a "contact" form table is inserted to.
- Charge a credit card when payment_token table is inserted to and then
send email upon success/failure
- Recalculate inventory levels as "orders" table is inserted to, and then
send notifications when inventory becomes low.
================================================
FILE: kubelab-backend/entrypoint.sh
================================================
#!/bin/sh
set -e # exit on any non-zero status (error)
# this entrypoint script checks that all required setup is done
# if not done, does it
# and then proceeds to execute the main "command" for this container
# build if needed
go mod tidy
go build
if [ ! -x "$(which modd)" ]; then
echo "go install modd"
go install github.com/cortesi/modd/cmd/modd@latest
fi
exec "$@"
================================================
FILE: kubelab-backend/example-hook-script.sh
================================================
#!/usr/bin/env bash
# This example script, along with hooks.go, shows how to trigger a command
# when a record changes in PocketBase and how to feed the changed record to this
# script.
params=$1 # `action_params` field passed from the "hooks" table
echo "PARAMS=$params"
# The body of the record (as JSON) is fed to this script as stdin.
# The following just reformats it and pretty-prints it.
cat | jq -C
================================================
FILE: kubelab-backend/go.mod
================================================
module github.com/natrontech/kubelab
go 1.22
toolchain go1.22.5
require (
github.com/caarlos0/env/v8 v8.0.0
github.com/pocketbase/dbx v1.10.1
github.com/pocketbase/pocketbase v0.22.18
helm.sh/helm/v3 v3.15.4
k8s.io/api v0.30.3
k8s.io/apimachinery v0.30.3
k8s.io/utils v0.0.0-20240102154912-e7106e64919e
)
require (
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/BurntSushi/toml v1.3.2 // indirect
github.com/MakeNowJust/heredoc v1.0.0 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.2.1 // indirect
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
github.com/Masterminds/squirrel v1.5.4 // indirect
github.com/Microsoft/hcsshim v0.11.4 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chai2010/gettext-go v1.0.2 // indirect
github.com/containerd/containerd v1.7.12 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/distribution/reference v0.5.0 // indirect
github.com/docker/cli v25.0.1+incompatible // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker v25.0.6+incompatible // indirect
github.com/docker/docker-credential-helpers v0.8.0 // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-metrics v0.0.1 // indirect
github.com/emicklei/go-restful/v3 v3.11.1 // indirect
github.com/evanphx/json-patch v5.7.0+incompatible // indirect
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-errors/errors v1.5.1 // indirect
github.com/go-gorp/gorp/v3 v3.1.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.20.2 // indirect
github.com/go-openapi/jsonreference v0.20.4 // indirect
github.com/go-openapi/swag v0.22.7 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/goccy/go-json v0.10.3 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/gorilla/websocket v1.5.1 // indirect
github.com/gosuri/uitable v0.0.4 // indirect
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/huandu/xstrings v1.4.0 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/jmoiron/sqlx v1.3.5 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.4 // indirect
github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61 // indirect
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
github.com/lib/pq v1.10.9 // indirect
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/locker v1.0.1 // indirect
github.com/moby/spdystream v0.2.0 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0-rc6 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_golang v1.18.0 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.45.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/rubenv/sql-migrate v1.6.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
github.com/xlab/treeprint v1.2.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
go.opentelemetry.io/otel v1.24.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
go.starlark.net v0.0.0-20231121155337-90ade8b19d09 // indirect
golang.org/x/sync v0.7.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240723171418-e6d459c13d2a // indirect
gopkg.in/evanphx/json-patch.v5 v5.7.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiextensions-apiserver v0.30.3 // indirect
k8s.io/apiserver v0.30.3 // indirect
k8s.io/cli-runtime v0.30.3 // indirect
k8s.io/component-base v0.30.3 // indirect
k8s.io/klog/v2 v2.120.1 // indirect
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
k8s.io/kubectl v0.30.3 // indirect
modernc.org/gc/v3 v3.0.0-20240722195230-4a140ff9c08e // indirect
oras.land/oras-go v1.2.5 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/kustomize/api v0.16.0 // indirect
sigs.k8s.io/kustomize/kyaml v0.16.0 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
)
require (
github.com/AlecAivazis/survey/v2 v2.3.7 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/aws/aws-sdk-go-v2 v1.30.3 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 // indirect
github.com/aws/aws-sdk-go-v2/config v1.27.27 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.27 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 // indirect
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.8 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15 // indirect
github.com/aws/aws-sdk-go-v2/service/s3 v1.58.2 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect
github.com/aws/smithy-go v1.20.3 // indirect
github.com/disintegration/imaging v1.6.2 // indirect
github.com/dlclark/regexp2 v1.11.0 // indirect
github.com/domodwyer/mailyak/v3 v3.6.2 // indirect
github.com/dop251/goja v0.0.0-20240627195025-eb1f15ee67d2 // indirect
github.com/dop251/goja_nodejs v0.0.0-20240418154818-2aae10d4cbcf // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/fatih/color v1.17.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.4 // indirect
github.com/ganigeorgiev/fexpr v0.4.1 // indirect
github.com/go-ozzo/ozzo-validation/v4 v4.3.0 // indirect
github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/pprof v0.0.0-20240625030939-27f56978b8b0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/gax-go/v2 v2.13.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-sqlite3 v1.14.22 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/mittwald/go-helm-client v0.12.13
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/cobra v1.8.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
go.opencensus.io v0.24.0 // indirect
gocloud.dev v0.37.0 // indirect
golang.org/x/crypto v0.25.0 // indirect
golang.org/x/image v0.18.0 // indirect
golang.org/x/net v0.27.0 // indirect
golang.org/x/oauth2 v0.21.0 // indirect
golang.org/x/sys v0.22.0 // indirect
golang.org/x/term v0.22.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 // indirect
google.golang.org/api v0.189.0 // indirect
google.golang.org/grpc v1.65.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
k8s.io/client-go v0.30.3
modernc.org/libc v1.55.3 // indirect
modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.8.0 // indirect
modernc.org/sqlite v1.31.1 // indirect
modernc.org/strutil v1.2.0 // indirect
modernc.org/token v1.1.0 // indirect
)
================================================
FILE: kubelab-backend/go.sum
================================================
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.115.0 h1:CnFSK6Xo3lDYRoBKEcAtia6VSC837/ZkJuRduSFnr14=
cloud.google.com/go v0.115.0/go.mod h1:8jIM5vVgoAEoiVxQ/O4BFTfHqulPZgs/ufEzMcFMdWU=
cloud.google.com/go/auth v0.7.2 h1:uiha352VrCDMXg+yoBtaD0tUF4Kv9vrtrWPYXwutnDE=
cloud.google.com/go/auth v0.7.2/go.mod h1:VEc4p5NNxycWQTMQEDQF0bd6aTMb6VgYDXEwiJJQAbs=
cloud.google.com/go/auth/oauth2adapt v0.2.3 h1:MlxF+Pd3OmSudg/b1yZ5lJwoXCEaeedAguodky1PcKI=
cloud.google.com/go/auth/oauth2adapt v0.2.3/go.mod h1:tMQXOfZzFuNuUxOypHlQEXgdfX5cuhwU+ffUuXRJE8I=
cloud.google.com/go/compute v1.25.0 h1:H1/4SqSUhjPFE7L5ddzHOfY2bCAvjwNRZPNl6Ni5oYU=
cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY=
cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY=
cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc=
cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI=
cloud.google.com/go/storage v1.39.1 h1:MvraqHKhogCOTXTlct/9C3K3+Uy2jBmFYb3/Sp6dVtY=
cloud.google.com/go/storage v1.39.1/go.mod h1:xK6xZmxZmo+fyP7+DEF6FhNc24/JAe95OLyOHCXFH1o=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM=
github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8=
github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs=
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/aws/aws-sdk-go v1.51.11 h1:El5VypsMIz7sFwAAj/j06JX9UGs4KAbAIEaZ57bNY4s=
github.com/aws/aws-sdk-go v1.51.11/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
github.com/aws/aws-sdk-go-v2 v1.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY=
github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 h1:tW1/Rkad38LA15X4UQtjXZXNKsCgkshC3EbmcUmghTg=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3/go.mod h1:UbnqO+zjqk3uIt9yCACHJ9IVNhyhOCnYk8yA19SAWrM=
github.com/aws/aws-sdk-go-v2/config v1.27.27 h1:HdqgGt1OAP0HkEDDShEl0oSYa9ZZBSOmKpdpsDMdO90=
github.com/aws/aws-sdk-go-v2/config v1.27.27/go.mod h1:MVYamCg76dFNINkZFu4n4RjDixhVr51HLj4ErWzrVwg=
github.com/aws/aws-sdk-go-v2/credentials v1.17.27 h1:2raNba6gr2IfA0eqqiP2XiQ0UVOpGPgDSi0I9iAP+UI=
github.com/aws/aws-sdk-go-v2/credentials v1.17.27/go.mod h1:gniiwbGahQByxan6YjQUMcW4Aov6bLC3m+evgcoN4r4=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 h1:KreluoV8FZDEtI6Co2xuNk/UqI9iwMrOx/87PBNIKqw=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11/go.mod h1:SeSUYBLsMYFoRvHE0Tjvn7kbxaUhl75CJi1sbfhMxkU=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.8 h1:u1KOU1S15ufyZqmH/rA3POkiRH6EcDANHj2xHRzq+zc=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.8/go.mod h1:WPv2FRnkIOoDv/8j2gSUsI4qDc7392w5anFB/I89GZ8=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 h1:SoNJ4RlFEQEbtDcCEt+QG56MY4fm4W8rYirAmq+/DdU=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15/go.mod h1:U9ke74k1n2bf+RIgoX1SXFed1HLs51OgUSs+Ph0KJP8=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 h1:C6WHdGnTDIYETAm5iErQUiVNsclNx9qbJVPIt03B6bI=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15/go.mod h1:ZQLZqhcu+JhSrA9/NXRm8SkDvsycE+JkV3WGY41e+IM=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15 h1:Z5r7SycxmSllHYmaAZPpmN8GviDrSGhMS6bldqtXZPw=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15/go.mod h1:CetW7bDE00QoGEmPUoZuRog07SGVAUVW6LFpNP0YfIg=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 h1:dT3MqvGhSoaIhRseqw2I0yH81l7wiR2vjs57O51EAm8=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17 h1:YPYe6ZmvUfDDDELqEKtAd6bo8zxhkm+XEFEzQisqUIE=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17/go.mod h1:oBtcnYua/CgzCWYN7NZ5j7PotFDaFSUjCYVTtfyn7vw=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 h1:HGErhhrxZlQ044RiM+WdoZxp0p+EGM62y3L6pwA4olE=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17/go.mod h1:RkZEx4l0EHYDJpWppMJ3nD9wZJAa8/0lq9aVC+r2UII=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15 h1:246A4lSTXWJw/rmlQI+TT2OcqeDMKBdyjEQrafMaQdA=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15/go.mod h1:haVfg3761/WF7YPuJOER2MP0k4UAXyHaLclKXB6usDg=
github.com/aws/aws-sdk-go-v2/service/s3 v1.58.2 h1:sZXIzO38GZOU+O0C+INqbH7C2yALwfMWpd64tONS/NE=
github.com/aws/aws-sdk-go-v2/service/s3 v1.58.2/go.mod h1:Lcxzg5rojyVPU/0eFwLtcyTaek/6Mtic5B1gJo7e/zE=
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 h1:BXx0ZIxvrJdSgSvKTZ+yRBeSqqgPM89VPlulEcl37tM=
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4/go.mod h1:ooyCOXjvJEsUw7x+ZDHeISPMhtwI3ZCB7ggFMcFfWLU=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 h1:yiwVzJW2ZxZTurVbYWA7QOrAaCYQR72t0wrSBfoesUE=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4/go.mod h1:0oxfLkpz3rQ/CHlx5hB7H69YUpFiI1tql6Q6Ne+1bCw=
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 h1:ZsDKRLXGWHk8WdtyYMoGNO7bTudrvuKpDKgMVRlepGE=
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3/go.mod h1:zwySh8fpFyXp9yOr/KVzxOl8SRqgf/IDw5aUt9UKFcQ=
github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE=
github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70=
github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZkEHchZRSq9OQbsSzIT/OrI8YFFmRIng=
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ=
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o=
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
github.com/caarlos0/env/v8 v8.0.0 h1:POhxHhSpuxrLMIdvTGARuZqR4Jjm8AYmoi/JKlcScs0=
github.com/caarlos0/env/v8 v8.0.0/go.mod h1:7K4wMY9bH0esiXSSHlfHLX5xKGQMnkH5Fk4TDSSSzfo=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk=
github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
github.com/containerd/containerd v1.7.12 h1:+KQsnv4VnzyxWcfO9mlxxELaoztsDEjOuCMPAuPqgU0=
github.com/containerd/containerd v1.7.12/go.mod h1:/5OMpE1p0ylxtEUGY8kuCYkDRzJm9NO1TFMWjUpdevk=
github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM=
github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2 h1:aBfCb7iqHmDEIp6fBvC/hQUddQfg+3qdYjwzaiP9Hnc=
github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2/go.mod h1:WHNsWjnIn2V1LYOrME7e8KxSeKunYHsxEm4am0BUtcI=
github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/docker/cli v25.0.1+incompatible h1:mFpqnrS6Hsm3v1k7Wa/BO23oz0k121MTbTO1lpcGSkU=
github.com/docker/cli v25.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v25.0.6+incompatible h1:5cPwbwriIcsua2REJe8HqQV+6WlWc1byg2QSXzBxBGg=
github.com/docker/docker v25.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.8.0 h1:YQFtbBQb4VrpoPxhFuzEBPQ9E16qz5SpHLS+uswaCp8=
github.com/docker/docker-credential-helpers v0.8.0/go.mod h1:UGFXcuoQ5TxPiB54nHOZ32AWRqQdECoh/Mg0AlEYb40=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8=
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4=
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
github.com/domodwyer/mailyak/v3 v3.6.2 h1:x3tGMsyFhTCaxp6ycgR0FE/bu5QiNp+hetUuCOBXMn8=
github.com/domodwyer/mailyak/v3 v3.6.2/go.mod h1:lOm/u9CyCVWHeaAmHIdF4RiKVxKUT/H5XX10lIKAL6c=
github.com/dop251/goja v0.0.0-20240627195025-eb1f15ee67d2 h1:4Ew88p5s9dwIk5/woUyqI9BD89NgZoUNH4/rM/h2UDg=
github.com/dop251/goja v0.0.0-20240627195025-eb1f15ee67d2/go.mod h1:o31y53rb/qiIAONF7w3FHJZRqqP3fzHUr1HqanthByw=
github.com/dop251/goja_nodejs v0.0.0-20240418154818-2aae10d4cbcf h1:2JoVYP9iko8uuIW33BQafzaylDixXbdXCRw/vCoxL+s=
github.com/dop251/goja_nodejs v0.0.0-20240418154818-2aae10d4cbcf/go.mod h1:bhGPmCgCCTSRfiMYWjpS46IDo9EUZXlsuUaPXSWGbv0=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/emicklei/go-restful/v3 v3.11.1 h1:S+9bSbua1z3FgCnV0KKOSSZ3mDthb5NyEPL5gEpCvyk=
github.com/emicklei/go-restful/v3 v3.11.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI=
github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f h1:Wl78ApPPB2Wvf/TIe2xdyJxTlb6obmF18d8QdkxNDu4=
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f/go.mod h1:OSYXu++VVOHnXeitef/D8n/6y4QV8uLHSFXX4NeXMGc=
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/foxcpp/go-mockdns v1.0.0 h1:7jBqxd3WDWwi/6WhDvacvH1XsN3rOLXyHM1uhvIx6FI=
github.com/foxcpp/go-mockdns v1.0.0/go.mod h1:lgRN6+KxQBawyIghpnl5CezHFGS9VLzvtVlwxvzXTQ4=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/gabriel-vasile/mimetype v1.4.4 h1:QjV6pZ7/XZ7ryI2KuyeEDE8wnh7fHP9YnQy+R0LnH8I=
github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSeEuJfJE+TtfvdB/s=
github.com/ganigeorgiev/fexpr v0.4.1 h1:hpUgbUEEWIZhSDBtf4M9aUNfQQ0BZkGRaMePy7Gcx5k=
github.com/ganigeorgiev/fexpr v0.4.1/go.mod h1:RyGiGqmeXhEQ6+mlGdnUleLHgtzzu/VGO2WtJkF5drE=
github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs=
github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q=
github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs=
github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU=
github.com/go-openapi/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4=
github.com/go-openapi/swag v0.22.7 h1:JWrc1uc/P9cSomxfnsFSVWoE1FW6bNbrVPmpQYpCcR8=
github.com/go-openapi/swag v0.22.7/go.mod h1:Gl91UqO+btAM0plGGxHqJcQZ1ZTy6jbmridBTsDy8A0=
github.com/go-ozzo/ozzo-validation/v4 v4.3.0 h1:byhDUpfEwjsVQb1vBunvIjh2BHQ9ead57VkAEY4V+Es=
github.com/go-ozzo/ozzo-validation/v4 v4.3.0/go.mod h1:2NKgrcHl3z6cJs+3Oo940FPRiTzuqKbvfrL2RxCj6Ew=
github.com/go-sourcemap/sourcemap v2.1.4+incompatible h1:a+iTbH5auLKxaNwQFg0B+TCYl6lbukKPc7b5x0n1s6Q=
github.com/go-sourcemap/sourcemap v2.1.4+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-sql-driver/mysql v1.8.0 h1:UtktXaU2Nb64z/pLiGIxY4431SJ4/dR5cjMmlVHgnT4=
github.com/go-sql-driver/mysql v1.8.0/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k=
github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20240625030939-27f56978b8b0 h1:e+8XbKB6IMn8A4OAyZccO4pYfB3s7bt6azNIPE7AnPg=
github.com/google/pprof v0.0.0-20240625030939-27f56978b8b0/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI=
github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA=
github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s=
github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A=
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY=
github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU=
github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61 h1:FwuzbVh87iLiUQj1+uQUsuw9x5t9m5n5g7rG7o4svW4=
github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61/go.mod h1:paQfF1YtHe+GrGg5fOgjsjoCX/UKDr9bc1DoWpZfns8=
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw=
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg=
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/miekg/dns v1.1.25 h1:dFwPR6SfLtrSwgDcIq2bcU/gVutB4sNApq2HBdqcakg=
github.com/miekg/dns v1.1.25/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mittwald/go-helm-client v0.12.13 h1:TzoHH3NmlUdgy4cbo2tAuGQTcXkUKdORhZSE/Cq72bA=
github.com/mittwald/go-helm-client v0.12.13/go.mod h1:BMoJyfs5n2MTe1RmWjTHuRl7b5wXfe6l7Eik1ZaZ0JU=
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8=
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78=
github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY=
github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM=
github.com/onsi/gomega v1.31.0 h1:54UJxxj6cPInHS3a35wm6BK/F9nHYueZ1NVujHDrnXE=
github.com/onsi/gomega v1.31.0/go.mod h1:DW9aCi7U6Yi40wNVAvT6kzFnEVEI5n3DloYBiKiT6zk=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0-rc6 h1:XDqvyKsJEbRtATzkgItUqBA7QHk58yxX1Ov9HERHNqU=
github.com/opencontainers/image-spec v1.1.0-rc6/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pocketbase/dbx v1.10.1 h1:cw+vsyfCJD8YObOVeqb93YErnlxwYMkNZ4rwN0G0AaA=
github.com/pocketbase/dbx v1.10.1/go.mod h1:xXRCIAKTHMgUCyCKZm55pUOdvFziJjQfXaWKhu2vhMs=
github.com/pocketbase/pocketbase v0.22.18 h1:yVckUhi5GDORqCb0BbtlvRB1CVxHY9HO9btEaeZHVJU=
github.com/pocketbase/pocketbase v0.22.18/go.mod h1:0QFvDOOW7ANId78ChZSagyHbmP6CgMxDQrQFXzeaDpA=
github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY=
github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjzg=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM=
github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/rubenv/sql-migrate v1.6.0 h1:IZpcTlAx/VKXphWEpwWJ7BaMq05tYtE80zYz+8a5Il8=
github.com/rubenv/sql-migrate v1.6.0/go.mod h1:m3ilnKP7sNb4eYkLsp6cGdPOl4OBcXM6rcbzU+Oqc5k=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ=
github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI=
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE=
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY=
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
go.starlark.net v0.0.0-20231121155337-90ade8b19d09 h1:hzy3LFnSN8kuQK8h9tHl4ndF6UruMj47OqwqsS+/Ai4=
go.starlark.net v0.0.0-20231121155337-90ade8b19d09/go.mod h1:LcLNIzVOMp4oV+uusnpk+VU+SzXaJakUuBjoCSWH5dM=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
gocloud.dev v0.37.0 h1:XF1rN6R0qZI/9DYjN16Uy0durAmSlf58DHOcb28GPro=
gocloud.dev v0.37.0/go.mod h1:7/O4kqdInCNsc6LqgmuFnS0GRew4XNNYWpA44yQnwco=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 h1:LLhsEBxRTBLuKlQxFBYUOU8xyFgXv6cOTp2HASDlsDk=
golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
google.golang.org/api v0.189.0 h1:equMo30LypAkdkLMBqfeIqtyAnlyig1JSZArl4XPwdI=
google.golang.org/api v0.189.0/go.mod h1:FLWGJKb0hb+pU2j+rJqwbnsF+ym+fQs73rbJ+KAUgy8=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20240722135656-d784300faade h1:lKFsS7wpngDgSCeFn7MoLy+wBDQZ1UQIJD4UNM1Qvkg=
google.golang.org/genproto v0.0.0-20240722135656-d784300faade/go.mod h1:FfBgJBJg9GcpPvKIuHSZ/aE1g2ecGL74upMzGZjiGEY=
google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d h1:kHjw/5UfflP/L5EbledDrcG4C2597RtymmGRZvHiCuY=
google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d/go.mod h1:mw8MG/Qz5wfgYr6VqVCiZcHe/GJEfI+oGGDCohaVgB0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240723171418-e6d459c13d2a h1:hqK4+jJZXCU4pW7jsAdGOVFIfLHQeV7LaizZKnZ84HI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240723171418-e6d459c13d2a/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/evanphx/json-patch.v5 v5.7.0 h1:dGKGylPlZ/jus2g1YqhhyzfH0gPy2R8/MYUpW/OslTY=
gopkg.in/evanphx/json-patch.v5 v5.7.0/go.mod h1:/kvTRh1TVm5wuM6OkHxqXtE/1nUZZpihg29RtuIyfvk=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
helm.sh/helm/v3 v3.15.4 h1:UFHd6oZ1IN3FsUZ7XNhOQDyQ2QYknBNWRHH57e9cbHY=
helm.sh/helm/v3 v3.15.4/go.mod h1:phOwlxqGSgppCY/ysWBNRhG3MtnpsttOzxaTK+Mt40E=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
k8s.io/api v0.30.3 h1:ImHwK9DCsPA9uoU3rVh4QHAHHK5dTSv1nxJUapx8hoQ=
k8s.io/api v0.30.3/go.mod h1:GPc8jlzoe5JG3pb0KJCSLX5oAFIW3/qNJITlDj8BH04=
k8s.io/apiextensions-apiserver v0.30.3 h1:oChu5li2vsZHx2IvnGP3ah8Nj3KyqG3kRSaKmijhB9U=
k8s.io/apiextensions-apiserver v0.30.3/go.mod h1:uhXxYDkMAvl6CJw4lrDN4CPbONkF3+XL9cacCT44kV4=
k8s.io/apimachinery v0.30.3 h1:q1laaWCmrszyQuSQCfNB8cFgCuDAoPszKY4ucAjDwHc=
k8s.io/apimachinery v0.30.3/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
k8s.io/apiserver v0.30.3 h1:QZJndA9k2MjFqpnyYv/PH+9PE0SHhx3hBho4X0vE65g=
k8s.io/apiserver v0.30.3/go.mod h1:6Oa88y1CZqnzetd2JdepO0UXzQX4ZnOekx2/PtEjrOg=
k8s.io/cli-runtime v0.30.3 h1:aG69oRzJuP2Q4o8dm+f5WJIX4ZBEwrvdID0+MXyUY6k=
k8s.io/cli-runtime v0.30.3/go.mod h1:hwrrRdd9P84CXSKzhHxrOivAR9BRnkMt0OeP5mj7X30=
k8s.io/client-go v0.30.3 h1:bHrJu3xQZNXIi8/MoxYtZBBWQQXwy16zqJwloXXfD3k=
k8s.io/client-go v0.30.3/go.mod h1:8d4pf8vYu665/kUbsxWAQ/JDBNWqfFeZnvFiVdmx89U=
k8s.io/component-base v0.30.3 h1:Ci0UqKWf4oiwy8hr1+E3dsnliKnkMLZMVbWzeorlk7s=
k8s.io/component-base v0.30.3/go.mod h1:C1SshT3rGPCuNtBs14RmVD2xW0EhRSeLvBh7AGk1quA=
k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
k8s.io/kubectl v0.30.3 h1:YIBBvMdTW0xcDpmrOBzcpUVsn+zOgjMYIu7kAq+yqiI=
k8s.io/kubectl v0.30.3/go.mod h1:IcR0I9RN2+zzTRUa1BzZCm4oM0NLOawE6RzlDvd1Fpo=
k8s.io/utils v0.0.0-20240102154912-e7106e64919e h1:eQ/4ljkx21sObifjzXwlPKpdGLrCfRziVtos3ofG/sQ=
k8s.io/utils v0.0.0-20240102154912-e7106e64919e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ=
modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
modernc.org/ccgo/v4 v4.19.2 h1:lwQZgvboKD0jBwdaeVCTouxhxAyN6iawF3STraAal8Y=
modernc.org/ccgo/v4 v4.19.2/go.mod h1:ysS3mxiMV38XGRTTcgo0DQTeTmAO4oCmJl1nX9VFI3s=
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw=
modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
modernc.org/gc/v3 v3.0.0-20240722195230-4a140ff9c08e h1:WPC4v0rNIFb2PY+nBBEEKyugPPRHPzUgyN3xZPpGK58=
modernc.org/gc/v3 v3.0.0-20240722195230-4a140ff9c08e/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
modernc.org/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U=
modernc.org/libc v1.55.3/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w=
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
modernc.org/sqlite v1.31.1 h1:XVU0VyzxrYHlBhIs1DiEgSl0ZtdnPtbLVy8hSkzxGrs=
modernc.org/sqlite v1.31.1/go.mod h1:UqoylwmTb9F+IqXERT8bW9zzOWN8qwAIcLdzeBZs4hA=
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
oras.land/oras-go v1.2.5 h1:XpYuAwAb0DfQsunIyMfeET92emK8km3W4yEzZvUbsTo=
oras.land/oras-go v1.2.5/go.mod h1:PuAwRShRZCsZb7g8Ar3jKKQR/2A/qN+pkYxIOd/FAoo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/kustomize/api v0.16.0 h1:/zAR4FOQDCkgSDmVzV2uiFbuy9bhu3jEzthrHCuvm1g=
sigs.k8s.io/kustomize/api v0.16.0/go.mod h1:MnFZ7IP2YqVyVwMWoRxPtgl/5hpA+eCCrQR/866cm5c=
sigs.k8s.io/kustomize/kyaml v0.16.0 h1:6J33uKSoATlKZH16unr2XOhDI+otoe2sR3M8PDzW3K0=
sigs.k8s.io/kustomize/kyaml v0.16.0/go.mod h1:xOK/7i+vmE14N2FdFyugIshB8eF6ALpy7jI87Q2nRh4=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
================================================
FILE: kubelab-backend/hooks/hooks.go
================================================
package hooks
import (
"encoding/json"
"errors"
"fmt"
"io"
"log"
"net/http"
"os"
"os/exec"
"strings"
"github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase"
"github.com/pocketbase/pocketbase/core"
"github.com/pocketbase/pocketbase/models"
)
func PocketBaseInit(app *pocketbase.PocketBase) error {
modelHandler := func(event string) func(e *core.ModelEvent) error {
return func(e *core.ModelEvent) error {
table := e.Model.TableName()
// we don't want to executeEventActions if the event is a system event (e.g. "_collections" changes)
if record, ok := e.Model.(*models.Record); ok {
executeEventActions(app, event, table, record)
} else {
log.Println("Skipping executeEventActions for table:", table)
}
return nil
}
}
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
app.OnModelAfterCreate().Add(modelHandler("insert"))
app.OnModelAfterUpdate().Add(modelHandler("update"))
app.OnModelAfterDelete().Add(modelHandler("delete"))
return nil
})
return nil
}
func executeEventActions(app *pocketbase.PocketBase, event string, table string, record *models.Record) {
// TODO: Load and cache this. Reload only on changes to "hooks" table
rows := []dbx.NullStringMap{}
app.DB().Select("action_type", "action", "action_params", "expands").
From("hooks").
Where(dbx.HashExp{"collection": table, "event": event, "disabled": false}).
All(&rows)
for _, row := range rows {
action_type := row["action_type"].String
action := row["action"].String
action_params := row["action_params"].String
expands := strings.Split(row["expands"].String, ",")
app.Dao().ExpandRecord(record, expands, func(c *models.Collection, ids []string) ([]*models.Record, error) {
return app.Dao().FindRecordsByIds(c.Name, ids, nil)
})
if err := executeEventAction(event, table, action_type, action, action_params, record); err != nil {
log.Println("ERROR", err)
}
}
}
func executeEventAction(event, table, action_type, action, action_params string, record *models.Record) error {
log.Printf("event:%s, table: %s, action: %s\n", event, table, action)
switch action_type {
case "command":
return doCommand(action, action_params, record)
case "post":
return doPost(action, action_params, record)
default:
return errors.New(fmt.Sprintf("Unknown action_type: %s", action_type))
}
}
func doCommand(action, action_params string, record *models.Record) error {
cmd := exec.Command(action, action_params)
if w, err := cmd.StdinPipe(); err != nil {
return err
} else {
if r, err := cmd.StdoutPipe(); err != nil {
return err
} else {
go func() {
defer w.Close()
defer r.Close()
log.Println("-------------------------------")
defer log.Println("-------------------------------")
if err := cmd.Start(); err != nil {
log.Printf("command start failed: %s %+v\n", action, err)
} else {
// write JSON into the pipe and close
json.NewEncoder(w).Encode(record)
w.Close()
if err := cmd.Wait(); err != nil {
log.Printf("command wait failed: %s %+v\n", action, err)
}
}
}()
// read pipe's stdout and copy to ours (in parallel to the above goroutine)
io.Copy(os.Stdout, r)
}
}
return nil
}
func doPost(action, action_params string, record *models.Record) error {
r, w := io.Pipe()
defer w.Close()
go func() {
defer r.Close()
if resp, err := http.Post(action, "application/json", r); err != nil {
log.Println("POST failed", action, err)
} else {
io.Copy(os.Stdout, resp.Body)
}
}()
if err := json.NewEncoder(w).Encode(record); err != nil {
log.Println("ERROR writing to pipe", err)
}
return nil
}
================================================
FILE: kubelab-backend/main.go
================================================
package main
import (
"log"
"os"
"path/filepath"
"strings"
"github.com/natrontech/kubelab/hooks"
"github.com/natrontech/kubelab/pkg/controller"
"github.com/natrontech/kubelab/pkg/env"
"github.com/natrontech/kubelab/pkg/k8s"
"github.com/pocketbase/pocketbase"
"github.com/pocketbase/pocketbase/apis"
"github.com/pocketbase/pocketbase/core"
"github.com/pocketbase/pocketbase/plugins/jsvm"
"github.com/pocketbase/pocketbase/plugins/migratecmd"
"github.com/pocketbase/pocketbase/tools/cron"
)
func defaultPublicDir() string {
if strings.HasPrefix(os.Args[0], os.TempDir()) {
// most likely ran with go run
return "./pb_public"
}
return filepath.Join(os.Args[0], "../pb_public")
}
func init() {
// set the default public dir
env.Init()
k8s.Init()
}
func main() {
app := pocketbase.New()
var publicDirFlag string
// add "--publicDir" option flag
app.RootCmd.PersistentFlags().StringVar(
&publicDirFlag,
"publicDir",
defaultPublicDir(),
"the directory to serve static files",
)
migrationsDir := "" // default to "pb_migrations" (for js) and "migrations" (for go)
// load js files to allow loading external JavaScript migrations
jsvm.MustRegister(app, jsvm.Config{
// Dir: migrationsDir,
MigrationsDir: migrationsDir,
})
// register the `migrate` command
migratecmd.MustRegister(app, app.RootCmd, migratecmd.Config{
TemplateLang: migratecmd.TemplateLangJS, // or migratecmd.TemplateLangGo (default)
Dir: migrationsDir,
Automigrate: true,
})
// call this only if you want to use the configurable "hooks" functionality
hooks.PocketBaseInit(app)
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
// serves static files from the provided public dir (if exists)
e.Router.GET("/*", apis.StaticDirectoryHandler(os.DirFS(publicDirFlag), true))
return nil
})
app.OnRecordBeforeUpdateRequest().Add(func(e *core.RecordUpdateEvent) error {
switch e.Collection.Name {
case "lab_sessions":
return controller.HandleLabSessions(e, app)
case "exercise_sessions":
return controller.HandleExerciseSessions(e, app)
}
return nil
})
// scheduler for syncing lab and exercise sessions
app.OnBeforeBootstrap().Add(func(e *core.BootstrapEvent) error {
scheduler := cron.New()
// Run sync every minute
scheduler.MustAdd("sessions_syncer", env.Config.CronTick, func() {
err := controller.AutoSessionSyncController(app)
if err != nil {
log.Printf("Error syncing sessions: %v\n", err)
}
})
scheduler.Start()
return nil
})
if err := app.Start(); err != nil {
log.Fatal(err)
}
}
================================================
FILE: kubelab-backend/modd.conf
================================================
# Run go test on ALL modules on startup, and subsequently only on modules
# containing changes.
**/*.go {
prep: go build
# prep: go test @dirmods
daemon +sigterm: ./kubelab serve --http 0.0.0.0:8090 --publicDir ../kubelab-ui/build
}
================================================
FILE: kubelab-backend/pb_migrations/1671835039_created_hooks.js
================================================
migrate(
(db) => {
const collection = new Collection({
id: "3fhw2mfr9zrgodj",
created: "2022-12-23 22:30:35.443Z",
updated: "2022-12-23 22:30:35.443Z",
name: "hooks",
type: "base",
system: false,
schema: [
{
system: false,
id: "j8mewfur",
name: "collection",
type: "text",
required: true,
unique: false,
options: {
min: null,
max: null,
pattern: "",
},
},
{
system: false,
id: "4xcxcfuv",
name: "event",
type: "select",
required: true,
unique: false,
options: {
maxSelect: 1,
values: ["insert", "update", "delete"],
},
},
{
system: false,
id: "u3bmgjpb",
name: "action_type",
type: "select",
required: true,
unique: false,
options: {
maxSelect: 1,
values: ["command", "post"],
},
},
{
system: false,
id: "kayyu1l3",
name: "action",
type: "text",
required: true,
unique: false,
options: {
min: null,
max: null,
pattern: "",
},
},
{
system: false,
id: "zkengev8",
name: "action_params",
type: "text",
required: false,
unique: false,
options: {
min: null,
max: null,
pattern: "",
},
},
],
listRule: null,
viewRule: null,
createRule: null,
updateRule: null,
deleteRule: null,
options: {},
});
return Dao(db).saveCollection(collection);
},
(db) => {
const dao = new Dao(db);
const collection = dao.findCollectionByNameOrId("3fhw2mfr9zrgodj");
return dao.deleteCollection(collection);
}
);
================================================
FILE: kubelab-backend/pb_migrations/1671926343_updated_hooks.js
================================================
migrate(
(db) => {
const dao = new Dao(db);
const collection = dao.findCollectionByNameOrId("3fhw2mfr9zrgodj");
// add
collection.schema.addField(
new SchemaField({
system: false,
id: "balsaeka",
name: "expands",
type: "text",
required: false,
unique: false,
options: {
min: null,
max: null,
pattern: "",
},
})
);
// add
collection.schema.addField(
new SchemaField({
system: false,
id: "emgxgcok",
name: "disabled",
type: "bool",
required: false,
unique: false,
options: {},
})
);
return dao.saveCollection(collection);
},
(db) => {
const dao = new Dao(db);
const collection = dao.findCollectionByNameOrId("3fhw2mfr9zrgodj");
// remove
collection.schema.removeField("balsaeka");
// remove
collection.schema.removeField("emgxgcok");
return dao.saveCollection(collection);
}
);
================================================
FILE: kubelab-backend/pb_migrations/1685691966_created_sessions.js
================================================
migrate(
(db) => {
const collection = new Collection({
id: "n7tne53bbobf2bt",
created: "2023-06-02 07:46:06.017Z",
updated: "2023-06-02 07:46:06.017Z",
name: "sessions",
type: "base",
system: false,
schema: [
{
system: false,
id: "rse3zear",
name: "user",
type: "relation",
required: false,
unique: false,
options: {
collectionId: "_pb_users_auth_",
cascadeDelete: false,
minSelect: null,
maxSelect: 1,
displayFields: [],
},
},
],
indexes: [],
listRule: null,
viewRule: null,
createRule: null,
updateRule: null,
deleteRule: null,
options: {},
});
return Dao(db).saveCollection(collection);
},
(db) => {
const dao = new Dao(db);
const collection = dao.findCollectionByNameOrId("n7tne53bbobf2bt");
return dao.deleteCollection(collection);
}
);
================================================
FILE: kubelab-backend/pb_migrations/1686128320_updated_users.js
================================================
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("_pb_users_auth_")
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "1hbozave",
"name": "totalScore",
"type": "number",
"required": false,
"unique": false,
"options": {
"min": null,
"max": null
}
}))
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "q6psj4sr",
"name": "avgMinutesToSolution",
"type": "number",
"required": false,
"unique": false,
"options": {
"min": null,
"max": null
}
}))
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("_pb_users_auth_")
// remove
collection.schema.removeField("1hbozave")
// remove
collection.schema.removeField("q6psj4sr")
return dao.saveCollection(collection)
})
================================================
FILE: kubelab-backend/pb_migrations/1686128357_created_labs.js
================================================
migrate((db) => {
const collection = new Collection({
"id": "a1s1vqlm7141lcr",
"created": "2023-06-07 08:59:17.658Z",
"updated": "2023-06-07 08:59:17.658Z",
"name": "labs",
"type": "base",
"system": false,
"schema": [
{
"system": false,
"id": "00op3jnz",
"name": "title",
"type": "text",
"required": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "abvux9fb",
"name": "description",
"type": "text",
"required": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
}
],
"indexes": [],
"listRule": null,
"viewRule": null,
"createRule": null,
"updateRule": null,
"deleteRule": null,
"options": {}
});
return Dao(db).saveCollection(collection);
}, (db) => {
const dao = new Dao(db);
const collection = dao.findCollectionByNameOrId("a1s1vqlm7141lcr");
return dao.deleteCollection(collection);
})
================================================
FILE: kubelab-backend/pb_migrations/1686128492_updated_sessions.js
================================================
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("n7tne53bbobf2bt")
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "ituarijg",
"name": "title",
"type": "text",
"required": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
}))
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "thwhdlu1",
"name": "startTime",
"type": "date",
"required": false,
"unique": false,
"options": {
"min": "",
"max": ""
}
}))
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "mapevfhq",
"name": "endTime",
"type": "date",
"required": false,
"unique": false,
"options": {
"min": "",
"max": ""
}
}))
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "l3csgcmv",
"name": "lab",
"type": "relation",
"required": false,
"unique": false,
"options": {
"collectionId": "a1s1vqlm7141lcr",
"cascadeDelete": false,
"minSelect": null,
"maxSelect": 1,
"displayFields": [
"title"
]
}
}))
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("n7tne53bbobf2bt")
// remove
collection.schema.removeField("ituarijg")
// remove
collection.schema.removeField("thwhdlu1")
// remove
collection.schema.removeField("mapevfhq")
// remove
collection.schema.removeField("l3csgcmv")
return dao.saveCollection(collection)
})
================================================
FILE: kubelab-backend/pb_migrations/1686128857_updated_sessions.js
================================================
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("n7tne53bbobf2bt")
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "6zraywm2",
"name": "score",
"type": "number",
"required": false,
"unique": false,
"options": {
"min": 0,
"max": null
}
}))
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "tr8kf1qh",
"name": "clusterRunning",
"type": "bool",
"required": false,
"unique": false,
"options": {}
}))
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("n7tne53bbobf2bt")
// remove
collection.schema.removeField("6zraywm2")
// remove
collection.schema.removeField("tr8kf1qh")
return dao.saveCollection(collection)
})
================================================
FILE: kubelab-backend/pb_migrations/1686129123_created_exercises.js
================================================
migrate((db) => {
const collection = new Collection({
"id": "s4f0lpy3ibkgfqp",
"created": "2023-06-07 09:12:03.931Z",
"updated": "2023-06-07 09:12:03.931Z",
"name": "exercises",
"type": "base",
"system": false,
"schema": [
{
"system": false,
"id": "ow94rlnx",
"name": "title",
"type": "text",
"required": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "ukrbpgyw",
"name": "description",
"type": "text",
"required": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "qpleqnzf",
"name": "docs",
"type": "url",
"required": false,
"unique": false,
"options": {
"exceptDomains": null,
"onlyDomains": null
}
},
{
"system": false,
"id": "lnv6grdi",
"name": "hint",
"type": "url",
"required": false,
"unique": false,
"options": {
"exceptDomains": null,
"onlyDomains": null
}
},
{
"system": false,
"id": "lslokljx",
"name": "solution",
"type": "url",
"required": false,
"unique": false,
"options": {
"exceptDomains": null,
"onlyDomains": null
}
},
{
"system": false,
"id": "1opfp3c3",
"name": "check",
"type": "url",
"required": false,
"unique": false,
"options": {
"exceptDomains": null,
"onlyDomains": null
}
},
{
"system": false,
"id": "oud4eihs",
"name": "bootstrap",
"type": "url",
"required": false,
"unique": false,
"options": {
"exceptDomains": null,
"onlyDomains": null
}
},
{
"system": false,
"id": "uao3gxwr",
"name": "agentRunning",
"type": "bool",
"required": false,
"unique": false,
"options": {}
}
],
"indexes": [],
"listRule": null,
"viewRule": null,
"createRule": null,
"updateRule": null,
"deleteRule": null,
"options": {}
});
return Dao(db).saveCollection(collection);
}, (db) => {
const dao = new Dao(db);
const collection = dao.findCollectionByNameOrId("s4f0lpy3ibkgfqp");
return dao.deleteCollection(collection);
})
================================================
FILE: kubelab-backend/pb_migrations/1686129154_updated_labs.js
================================================
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("a1s1vqlm7141lcr")
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "pnavjyzl",
"name": "exercises",
"type": "relation",
"required": true,
"unique": false,
"options": {
"collectionId": "s4f0lpy3ibkgfqp",
"cascadeDelete": false,
"minSelect": null,
"maxSelect": null,
"displayFields": [
"title"
]
}
}))
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("a1s1vqlm7141lcr")
// remove
collection.schema.removeField("pnavjyzl")
return dao.saveCollection(collection)
})
================================================
FILE: kubelab-backend/pb_migrations/1686129166_updated_sessions.js
================================================
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("n7tne53bbobf2bt")
// update
collection.schema.addField(new SchemaField({
"system": false,
"id": "l3csgcmv",
"name": "lab",
"type": "relation",
"required": true,
"unique": false,
"options": {
"collectionId": "a1s1vqlm7141lcr",
"cascadeDelete": false,
"minSelect": null,
"maxSelect": 1,
"displayFields": [
"title"
]
}
}))
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("n7tne53bbobf2bt")
// update
collection.schema.addField(new SchemaField({
"system": false,
"id": "l3csgcmv",
"name": "lab",
"type": "relation",
"required": false,
"unique": false,
"options": {
"collectionId": "a1s1vqlm7141lcr",
"cascadeDelete": false,
"minSelect": null,
"maxSelect": 1,
"displayFields": [
"title"
]
}
}))
return dao.saveCollection(collection)
})
================================================
FILE: kubelab-backend/pb_migrations/1686144343_updated_labs.js
================================================
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("a1s1vqlm7141lcr")
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "qe7ybzdk",
"name": "docs",
"type": "url",
"required": false,
"unique": false,
"options": {
"exceptDomains": null,
"onlyDomains": null
}
}))
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("a1s1vqlm7141lcr")
// remove
collection.schema.removeField("qe7ybzdk")
return dao.saveCollection(collection)
})
================================================
FILE: kubelab-backend/pb_migrations/1686311410_updated_sessions.js
================================================
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("n7tne53bbobf2bt")
// remove
collection.schema.removeField("ituarijg")
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("n7tne53bbobf2bt")
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "ituarijg",
"name": "title",
"type": "text",
"required": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
}))
return dao.saveCollection(collection)
})
================================================
FILE: kubelab-backend/pb_migrations/1686311431_updated_sessions.js
================================================
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("n7tne53bbobf2bt")
collection.name = "lab_sessions"
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("n7tne53bbobf2bt")
collection.name = "sessions"
return dao.saveCollection(collection)
})
================================================
FILE: kubelab-backend/pb_migrations/1686311476_created_exercise_sessions.js
================================================
migrate((db) => {
const collection = new Collection({
"id": "qj6ssich32lcxru",
"created": "2023-06-09 11:51:16.171Z",
"updated": "2023-06-09 11:51:16.171Z",
"name": "exercise_sessions",
"type": "base",
"system": false,
"schema": [
{
"system": false,
"id": "nc43tbmx",
"name": "agentRunning",
"type": "bool",
"required": false,
"unique": false,
"options": {}
}
],
"indexes": [],
"listRule": null,
"viewRule": null,
"createRule": null,
"updateRule": null,
"deleteRule": null,
"options": {}
});
return Dao(db).saveCollection(collection);
}, (db) => {
const dao = new Dao(db);
const collection = dao.findCollectionByNameOrId("qj6ssich32lcxru");
return dao.deleteCollection(collection);
})
================================================
FILE: kubelab-backend/pb_migrations/1686311483_updated_exercises.js
================================================
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("s4f0lpy3ibkgfqp")
// remove
collection.schema.removeField("uao3gxwr")
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("s4f0lpy3ibkgfqp")
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "uao3gxwr",
"name": "agentRunning",
"type": "bool",
"required": false,
"unique": false,
"options": {}
}))
return dao.saveCollection(collection)
})
================================================
FILE: kubelab-backend/pb_migrations/1686311638_updated_exercise_sessions.js
================================================
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("qj6ssich32lcxru")
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "iousqpjz",
"name": "user",
"type": "relation",
"required": true,
"unique": false,
"options": {
"collectionId": "_pb_users_auth_",
"cascadeDelete": false,
"minSelect": null,
"maxSelect": 1,
"displayFields": [
"email"
]
}
}))
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "g8s5seza",
"name": "startTime",
"type": "date",
"required": true,
"unique": false,
"options": {
"min": "",
"max": ""
}
}))
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "a3yguusb",
"name": "endTime",
"type": "date",
"required": false,
"unique": false,
"options": {
"min": "",
"max": ""
}
}))
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "9oftn8f7",
"name": "exercise",
"type": "relation",
"required": true,
"unique": false,
"options": {
"collectionId": "s4f0lpy3ibkgfqp",
"cascadeDelete": false,
"minSelect": null,
"maxSelect": 1,
"displayFields": [
"title"
]
}
}))
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("qj6ssich32lcxru")
// remove
collection.schema.removeField("iousqpjz")
// remove
collection.schema.removeField("g8s5seza")
// remove
collection.schema.removeField("a3yguusb")
// remove
collection.schema.removeField("9oftn8f7")
return dao.saveCollection(collection)
})
================================================
FILE: kubelab-backend/pb_migrations/1686311654_updated_lab_sessions.js
================================================
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("n7tne53bbobf2bt")
// remove
collection.schema.removeField("6zraywm2")
// update
collection.schema.addField(new SchemaField({
"system": false,
"id": "rse3zear",
"name": "user",
"type": "relation",
"required": true,
"unique": false,
"options": {
"collectionId": "_pb_users_auth_",
"cascadeDelete": false,
"minSelect": null,
"maxSelect": 1,
"displayFields": []
}
}))
// update
collection.schema.addField(new SchemaField({
"system": false,
"id": "thwhdlu1",
"name": "startTime",
"type": "date",
"required": true,
"unique": false,
"options": {
"min": "",
"max": ""
}
}))
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("n7tne53bbobf2bt")
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "6zraywm2",
"name": "score",
"type": "number",
"required": false,
"unique": false,
"options": {
"min": 0,
"max": null
}
}))
// update
collection.schema.addField(new SchemaField({
"system": false,
"id": "rse3zear",
"name": "user",
"type": "relation",
"required": false,
"unique": false,
"options": {
"collectionId": "_pb_users_auth_",
"cascadeDelete": false,
"minSelect": null,
"maxSelect": 1,
"displayFields": []
}
}))
// update
collection.schema.addField(new SchemaField({
"system": false,
"id": "thwhdlu1",
"name": "startTime",
"type": "date",
"required": false,
"unique": false,
"options": {
"min": "",
"max": ""
}
}))
return dao.saveCollection(collection)
})
================================================
FILE: kubelab-backend/pb_migrations/1686311927_created_user_exercise_score.js
================================================
migrate((db) => {
const collection = new Collection({
"id": "c0gc1ph97tdim19",
"created": "2023-06-09 11:58:47.771Z",
"updated": "2023-06-09 11:58:47.771Z",
"name": "user_exercise_score",
"type": "view",
"system": false,
"schema": [],
"indexes": [],
"listRule": null,
"viewRule": null,
"createRule": null,
"updateRule": null,
"deleteRule": null,
"options": {
"query": "SELECT lab_sessions.id\nFROM lab_sessions"
}
});
return Dao(db).saveCollection(collection);
}, (db) => {
const dao = new Dao(db);
const collection = dao.findCollectionByNameOrId("c0gc1ph97tdim19");
return dao.deleteCollection(collection);
})
================================================
FILE: kubelab-backend/pb_migrations/1686312000_updated_user_exercise_score.js
================================================
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("c0gc1ph97tdim19")
collection.options = {
"query": "SELECT lab_sessions.id, COUNT(lab_sessions.id) as sum\nFROM lab_sessions"
}
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "wmj3s2se",
"name": "sum",
"type": "number",
"required": false,
"unique": false,
"options": {
"min": null,
"max": null
}
}))
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("c0gc1ph97tdim19")
collection.options = {
"query": "SELECT lab_sessions.id\nFROM lab_sessions"
}
// remove
collection.schema.removeField("wmj3s2se")
return dao.saveCollection(collection)
})
================================================
FILE: kubelab-backend/pb_migrations/1686312029_updated_user_exercise_score.js
================================================
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("c0gc1ph97tdim19")
collection.options = {
"query": "SELECT (ROW_NUMBER() OVER()) as id, COUNT(lab_sessions.id) as sum\nFROM lab_sessions"
}
// remove
collection.schema.removeField("wmj3s2se")
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "jgwml4ai",
"name": "sum",
"type": "number",
"required": false,
"unique": false,
"options": {
"min": null,
"max": null
}
}))
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("c0gc1ph97tdim19")
collection.options = {
"query": "SELECT lab_sessions.id, COUNT(lab_sessions.id) as sum\nFROM lab_sessions"
}
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "wmj3s2se",
"name": "sum",
"type": "number",
"required": false,
"unique": false,
"options": {
"min": null,
"max": null
}
}))
// remove
collection.schema.removeField("jgwml4ai")
return dao.saveCollection(collection)
})
================================================
FILE: kubelab-backend/pb_migrations/1686312069_updated_user_exercise_score.js
================================================
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("c0gc1ph97tdim19")
collection.options = {
"query": "SELECT (ROW_NUMBER() OVER()) as id, COUNT(lab_sessions.id) as sum\nFROM lab_sessions WHERE lab_sessions.endTime NOTNULL;"
}
// remove
collection.schema.removeField("jgwml4ai")
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "4nfqwv0a",
"name": "sum",
"type": "number",
"required": false,
"unique": false,
"options": {
"min": null,
"max": null
}
}))
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("c0gc1ph97tdim19")
collection.options = {
"query": "SELECT (ROW_NUMBER() OVER()) as id, COUNT(lab_sessions.id) as sum\nFROM lab_sessions"
}
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "jgwml4ai",
"name": "sum",
"type": "number",
"required": false,
"unique": false,
"options": {
"min": null,
"max": null
}
}))
// remove
collection.schema.removeField("4nfqwv0a")
return dao.saveCollection(collection)
})
================================================
FILE: kubelab-backend/pb_migrations/1686312095_updated_user_exercise_score.js
================================================
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("c0gc1ph97tdim19")
collection.options = {
"query": "SELECT (ROW_NUMBER() OVER()) as id, COUNT(exercise_sessions.id) as sum\nFROM exercise_sessions WHERE exercise_sessions.endTime NOTNULL;"
}
// remove
collection.schema.removeField("4nfqwv0a")
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "swlqrmos",
"name": "sum",
"type": "number",
"required": false,
"unique": false,
"options": {
"min": null,
"max": null
}
}))
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("c0gc1ph97tdim19")
collection.options = {
"query": "SELECT (ROW_NUMBER() OVER()) as id, COUNT(lab_sessions.id) as sum\nFROM lab_sessions WHERE lab_sessions.endTime NOTNULL;"
}
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "4nfqwv0a",
"name": "sum",
"type": "number",
"required": false,
"unique": false,
"options": {
"min": null,
"max": null
}
}))
// remove
collection.schema.removeField("swlqrmos")
return dao.saveCollection(collection)
})
================================================
FILE: kubelab-backend/pb_migrations/1686312146_updated_user_exercise_score.js
================================================
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("c0gc1ph97tdim19")
collection.options = {
"query": "SELECT exercise_sessions.user as id, COUNT(exercise_sessions.id) as sum\nFROM exercise_sessions WHERE exercise_sessions.endTime NOTNULL;"
}
// remove
collection.schema.removeField("swlqrmos")
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "dvvwd0w4",
"name": "sum",
"type": "number",
"required": false,
"unique": false,
"options": {
"min": null,
"max": null
}
}))
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("c0gc1ph97tdim19")
collection.options = {
"query": "SELECT (ROW_NUMBER() OVER()) as id, COUNT(exercise_sessions.id) as sum\nFROM exercise_sessions WHERE exercise_sessions.endTime NOTNULL;"
}
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "swlqrmos",
"name": "sum",
"type": "number",
"required": false,
"unique": false,
"options": {
"min": null,
"max": null
}
}))
// remove
collection.schema.removeField("dvvwd0w4")
return dao.saveCollection(collection)
})
================================================
FILE: kubelab-backend/pb_migrations/1686312270_deleted_user_exercise_score.js
================================================
migrate((db) => {
const dao = new Dao(db);
const collection = dao.findCollectionByNameOrId("c0gc1ph97tdim19");
return dao.deleteCollection(collection);
}, (db) => {
const collection = new Collection({
"id": "c0gc1ph97tdim19",
"created": "2023-06-09 11:58:47.771Z",
"updated": "2023-06-09 12:02:26.128Z",
"name": "user_exercise_score",
"type": "view",
"system": false,
"schema": [
{
"system": false,
"id": "dvvwd0w4",
"name": "sum",
"type": "number",
"required": false,
"unique": false,
"options": {
"min": null,
"max": null
}
}
],
"indexes": [],
"listRule": null,
"viewRule": null,
"createRule": null,
"updateRule": null,
"deleteRule": null,
"options": {
"query": "SELECT exercise_sessions.user as id, COUNT(exercise_sessions.id) as sum\nFROM exercise_sessions WHERE exercise_sessions.endTime NOTNULL;"
}
});
return Dao(db).saveCollection(collection);
})
================================================
FILE: kubelab-backend/pb_migrations/1686312735_updated_labs.js
================================================
migra
gitextract_b2npil12/
├── .dockerignore
├── .editorconfig
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ ├── dependabot.yml
│ └── workflows/
│ ├── ci.yml
│ ├── docker-release.yml
│ └── pr-labels.yml
├── .gitignore
├── .pre-commit-config.yaml
├── CODEOWNERS
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE
├── README.md
├── SECURITY.md
├── assets/
│ └── drawio/
│ └── architecture.drawio
├── deploy/
│ ├── clusterrole.yaml
│ ├── clusterrolebinding.yaml
│ ├── configmap.yaml
│ ├── ingress.yaml
│ ├── kustomization.yaml
│ ├── ns.yaml
│ ├── persistentvolume.yaml
│ ├── service.yaml
│ ├── serviceaccount.yaml
│ └── statefulset.yaml
├── docker-compose.yaml
├── kubelab-backend/
│ ├── .gitignore
│ ├── README.md
│ ├── entrypoint.sh
│ ├── example-hook-script.sh
│ ├── go.mod
│ ├── go.sum
│ ├── hooks/
│ │ └── hooks.go
│ ├── main.go
│ ├── modd.conf
│ ├── pb_migrations/
│ │ ├── 1671835039_created_hooks.js
│ │ ├── 1671926343_updated_hooks.js
│ │ ├── 1685691966_created_sessions.js
│ │ ├── 1686128320_updated_users.js
│ │ ├── 1686128357_created_labs.js
│ │ ├── 1686128492_updated_sessions.js
│ │ ├── 1686128857_updated_sessions.js
│ │ ├── 1686129123_created_exercises.js
│ │ ├── 1686129154_updated_labs.js
│ │ ├── 1686129166_updated_sessions.js
│ │ ├── 1686144343_updated_labs.js
│ │ ├── 1686311410_updated_sessions.js
│ │ ├── 1686311431_updated_sessions.js
│ │ ├── 1686311476_created_exercise_sessions.js
│ │ ├── 1686311483_updated_exercises.js
│ │ ├── 1686311638_updated_exercise_sessions.js
│ │ ├── 1686311654_updated_lab_sessions.js
│ │ ├── 1686311927_created_user_exercise_score.js
│ │ ├── 1686312000_updated_user_exercise_score.js
│ │ ├── 1686312029_updated_user_exercise_score.js
│ │ ├── 1686312069_updated_user_exercise_score.js
│ │ ├── 1686312095_updated_user_exercise_score.js
│ │ ├── 1686312146_updated_user_exercise_score.js
│ │ ├── 1686312270_deleted_user_exercise_score.js
│ │ ├── 1686312735_updated_labs.js
│ │ ├── 1686312758_updated_exercises.js
│ │ ├── 1686485128_updated_labs.js
│ │ ├── 1686485135_updated_lab_sessions.js
│ │ ├── 1686485142_updated_exercises.js
│ │ ├── 1686485148_updated_exercise_sessions.js
│ │ ├── 1686568901_updated_exercise_sessions.js
│ │ ├── 1686568909_updated_lab_sessions.js
│ │ ├── 1686569025_updated_lab_sessions.js
│ │ ├── 1686569075_updated_lab_sessions.js
│ │ ├── 1686569086_updated_lab_sessions.js
│ │ ├── 1686569099_updated_lab_sessions.js
│ │ ├── 1686569141_updated_exercise_sessions.js
│ │ ├── 1686600246_updated_exercise_sessions.js
│ │ ├── 1686600263_updated_exercise_sessions.js
│ │ ├── 1686990480_updated_exercise_sessions.js
│ │ ├── 1686990524_updated_lab_sessions.js
│ │ ├── 1687092568_updated_labs.js
│ │ ├── 1687092574_updated_exercises.js
│ │ ├── 1687092614_updated_exercises.js
│ │ ├── 1687092623_updated_labs.js
│ │ ├── 1687116582_updated_exercises.js
│ │ ├── 1687116588_updated_labs.js
│ │ ├── 1687200869_updated_users.js
│ │ ├── 1687200885_updated_exercises.js
│ │ ├── 1687201122_updated_users.js
│ │ ├── 1687201342_updated_exercises.js
│ │ ├── 1687201352_updated_labs.js
│ │ ├── 1687208987_created_material.js
│ │ ├── 1687209270_updated_material.js
│ │ ├── 1687209711_deleted_material.js
│ │ ├── 1692004564_created_plans.js
│ │ ├── 1692004578_created_features.js
│ │ ├── 1692004634_updated_plans.js
│ │ ├── 1692004755_updated_plans.js
│ │ ├── 1692006976_updated_users.js
│ │ ├── 1692013150_updated_users.js
│ │ ├── 1692013264_updated_plans.js
│ │ ├── 1692013276_updated_plans.js
│ │ ├── 1692013682_updated_features.js
│ │ ├── 1692029251_created_faqs.js
│ │ ├── 1692030049_created_companies.js
│ │ ├── 1692030210_updated_companies.js
│ │ ├── 1697824916_updated_users.js
│ │ ├── 1697824932_updated_users.js
│ │ ├── 1697982589_created_exercise_logs.js
│ │ ├── 1697982994_updated_exercise_logs.js
│ │ ├── 1697983253_updated_exercise_logs.js
│ │ ├── 1697983426_updated_exercise_session_logs.js
│ │ ├── 1697983434_updated_exercise_session_logs.js
│ │ ├── 1697983470_updated_users.js
│ │ ├── 1697983479_updated_exercise_sessions.js
│ │ ├── 1697983500_updated_lab_sessions.js
│ │ ├── 1697983560_updated_exercise_session_logs.js
│ │ ├── 1697984665_updated_exercise_session_logs.js
│ │ ├── 1697984707_updated_exercise_session_logs.js
│ │ ├── 1697986578_created_notifications.js
│ │ ├── 1697987216_updated_notifications.js
│ │ ├── 1697988059_updated_users.js
│ │ ├── 1697992209_updated_exercise_session_logs.js
│ │ ├── 1697992245_updated_notifications.js
│ │ ├── 1697992793_updated_notifications.js
│ │ └── 1697995266_updated_users.js
│ ├── pkg/
│ │ ├── controller/
│ │ │ ├── exercise.go
│ │ │ ├── handler.go
│ │ │ ├── lab.go
│ │ │ ├── sessions.go
│ │ │ └── util.go
│ │ ├── env/
│ │ │ └── env.go
│ │ ├── helm/
│ │ │ └── helm.go
│ │ ├── k8s/
│ │ │ ├── config.go
│ │ │ ├── deployment.go
│ │ │ ├── ingress.go
│ │ │ ├── namespace.go
│ │ │ ├── pod.go
│ │ │ ├── resourcequota.go
│ │ │ ├── secret.go
│ │ │ └── service.go
│ │ └── util/
│ │ └── helpers.go
│ └── vcluster-values.yaml
├── kubelab-fill/
│ ├── example_users.csv
│ ├── upload.py
│ └── users_import.py
├── kubelab-score/
│ ├── score.csv
│ └── score_calculation.py
└── kubelab-ui/
├── .dockerignore
├── .env.example
├── .eslintignore
├── .eslintrc.cjs
├── .gitignore
├── .npmrc
├── .prettierignore
├── .prettierrc
├── .vscode/
│ └── settings.json
├── Dockerfile
├── README.md
├── next-env.d.ts
├── package.json
├── playwright.config.ts
├── postcss.config.cjs
├── src/
│ ├── app.css
│ ├── app.d.ts
│ ├── app.html
│ ├── app.postcss
│ ├── hooks.client.ts
│ ├── lib/
│ │ ├── components/
│ │ │ ├── Console.svelte
│ │ │ ├── base/
│ │ │ │ ├── BackButton.svelte
│ │ │ │ ├── Badges.svelte
│ │ │ │ ├── Card.svelte
│ │ │ │ ├── Desktop.svelte
│ │ │ │ ├── Nav.svelte
│ │ │ │ ├── PlaceholderComponent.svelte
│ │ │ │ ├── SideOver.svelte
│ │ │ │ └── ToggleConfetti.svelte
│ │ │ ├── dashboard/
│ │ │ │ ├── Chart.svelte
│ │ │ │ ├── RunningExercises.svelte
│ │ │ │ └── data.ts
│ │ │ ├── labs/
│ │ │ │ ├── Exercise.svelte
│ │ │ │ └── Lab.svelte
│ │ │ ├── landingpage/
│ │ │ │ ├── Companies.svelte
│ │ │ │ ├── Cta.svelte
│ │ │ │ ├── Faq.svelte
│ │ │ │ ├── Features.svelte
│ │ │ │ ├── Footer.svelte
│ │ │ │ ├── Header.svelte
│ │ │ │ └── Hero.svelte
│ │ │ └── markdown/
│ │ │ ├── CodeComponent.svelte
│ │ │ ├── CodeSpanComponent.svelte
│ │ │ ├── LinkComponent.svelte
│ │ │ └── ListComponent.svelte
│ │ ├── config.ts
│ │ ├── mock-data.ts
│ │ ├── pocketbase/
│ │ │ ├── generated-types.ts
│ │ │ ├── index.ts
│ │ │ └── ui.ts
│ │ ├── stores/
│ │ │ ├── codeView.ts
│ │ │ ├── data.ts
│ │ │ ├── layout_store.ts
│ │ │ ├── loading.ts
│ │ │ ├── metadata.ts
│ │ │ ├── sidebar.ts
│ │ │ ├── tableView.ts
│ │ │ ├── terminal.ts
│ │ │ └── theme.ts
│ │ ├── terminal.ts
│ │ ├── types.ts
│ │ ├── utils/
│ │ │ ├── clickOutside.ts
│ │ │ ├── enums.ts
│ │ │ ├── interfaces.ts
│ │ │ └── time.ts
│ │ └── xterm.css
│ ├── routes/
│ │ ├── +layout.svelte
│ │ ├── +layout.ts
│ │ ├── +page.svelte
│ │ ├── +page.ts
│ │ ├── admin/
│ │ │ └── [id]/
│ │ │ ├── +page.svelte
│ │ │ └── +page.ts
│ │ ├── app/
│ │ │ ├── +layout.svelte
│ │ │ ├── +page.svelte
│ │ │ ├── +page.ts
│ │ │ └── profile/
│ │ │ └── +page.svelte
│ │ ├── labs/
│ │ │ ├── +layout.svelte
│ │ │ ├── +page.svelte
│ │ │ ├── +page.ts
│ │ │ └── [id]/
│ │ │ ├── +page.svelte
│ │ │ ├── +page.ts
│ │ │ └── [id]/
│ │ │ ├── +layout.svelte
│ │ │ ├── +layout.ts
│ │ │ └── +page.svelte
│ │ ├── login/
│ │ │ └── +page.svelte
│ │ └── material/
│ │ ├── +layout.svelte
│ │ └── +page.svelte
│ └── styles/
│ ├── prism.css
│ └── xterm.css
├── svelte.config.js
├── tailwind.config.cjs
├── tsconfig.json
└── vite.config.ts
SYMBOL INDEX (140 symbols across 31 files)
FILE: kubelab-backend/hooks/hooks.go
function PocketBaseInit (line 20) | func PocketBaseInit(app *pocketbase.PocketBase) error {
function executeEventActions (line 42) | func executeEventActions(app *pocketbase.PocketBase, event string, table...
function executeEventAction (line 63) | func executeEventAction(event, table, action_type, action, action_params...
function doCommand (line 75) | func doCommand(action, action_params string, record *models.Record) error {
function doPost (line 106) | func doPost(action, action_params string, record *models.Record) error {
FILE: kubelab-backend/main.go
function defaultPublicDir (line 21) | func defaultPublicDir() string {
function init (line 30) | func init() {
function main (line 36) | func main() {
FILE: kubelab-backend/pkg/controller/exercise.go
function setupExerciseResources (line 14) | func setupExerciseResources(e *core.RecordUpdateEvent, app *pocketbase.P...
function deleteExerciseResources (line 119) | func deleteExerciseResources(e *core.RecordUpdateEvent, app *pocketbase....
FILE: kubelab-backend/pkg/controller/handler.go
function HandleLabSessions (line 8) | func HandleLabSessions(e *core.RecordUpdateEvent, app *pocketbase.Pocket...
function HandleExerciseSessions (line 15) | func HandleExerciseSessions(e *core.RecordUpdateEvent, app *pocketbase.P...
FILE: kubelab-backend/pkg/controller/lab.go
function deployVCluster (line 16) | func deployVCluster(e *core.RecordUpdateEvent, app *pocketbase.PocketBas...
function deleteClusterResources (line 80) | func deleteClusterResources(e *core.RecordUpdateEvent, app *pocketbase.P...
FILE: kubelab-backend/pkg/controller/sessions.go
function AutoSessionSyncController (line 12) | func AutoSessionSyncController(app *pocketbase.PocketBase) error {
FILE: kubelab-backend/pkg/controller/util.go
function namespaceName (line 12) | func namespaceName(e *core.RecordUpdateEvent, lab string) string {
function logAndReturnErr (line 16) | func logAndReturnErr(err error) error {
function fetchBodyFromURL (line 21) | func fetchBodyFromURL(url string) ([]byte, error) {
FILE: kubelab-backend/pkg/env/env.go
type config (line 9) | type config struct
function Init (line 26) | func Init() {
FILE: kubelab-backend/pkg/helm/helm.go
constant Prefix (line 14) | Prefix = "kubelab"
function GetNamespaceName (line 16) | func GetNamespaceName(labName, username string) string {
function CreateHelmClient (line 20) | func CreateHelmClient(labName, username string) (helmclient.Client, erro...
function AddHelmRepositoryToClient (line 31) | func AddHelmRepositoryToClient(helmClient helmclient.Client, repositoryN...
function CreateOrUpdateHelmRelease (line 40) | func CreateOrUpdateHelmRelease(helmClient helmclient.Client, chartName, ...
function GetHelmRelease (line 55) | func GetHelmRelease(helmClient helmclient.Client, releaseName string) (*...
FILE: kubelab-backend/pkg/k8s/config.go
function Init (line 23) | func Init() {
function GetClusterVersion (line 70) | func GetClusterVersion() (string, error) {
function GetClusterApi (line 82) | func GetClusterApi() (string, error) {
FILE: kubelab-backend/pkg/k8s/deployment.go
type DeploymentParams (line 17) | type DeploymentParams struct
function CreateDeployment (line 30) | func CreateDeployment(params DeploymentParams) (*appsv1.Deployment, erro...
function createConfigMap (line 47) | func createConfigMap(namespace, name string, data map[string]string) {
function constructDeployment (line 57) | func constructDeployment(name, namespace, image string, replicas int32, ...
function DeleteDeployment (line 247) | func DeleteDeployment(namespace, name string) error {
function deleteConfigMap (line 254) | func deleteConfigMap(namespace, name string) {
function WaitForDeployment (line 260) | func WaitForDeployment(namespace, name string) error {
FILE: kubelab-backend/pkg/k8s/ingress.go
type IngressParams (line 11) | type IngressParams struct
function CreateIngress (line 22) | func CreateIngress(params IngressParams) (*networkingv1.Ingress, error) {
function DeleteIngress (line 118) | func DeleteIngress(namespace string, name string) error {
FILE: kubelab-backend/pkg/k8s/namespace.go
function GetTotalNamespaces (line 13) | func GetTotalNamespaces() (string, error) {
type NamespaceParams (line 27) | type NamespaceParams struct
function CreateNamespace (line 32) | func CreateNamespace(params NamespaceParams) error {
function DeleteNamespace (line 54) | func DeleteNamespace(namespace string) error {
function GetTotalNamespacesByPrefix (line 58) | func GetTotalNamespacesByPrefix(prefix string) (int, error) {
FILE: kubelab-backend/pkg/k8s/pod.go
function GetPodByName (line 8) | func GetPodByName(namespace string, name string) (*v1.Pod, error) {
FILE: kubelab-backend/pkg/k8s/resourcequota.go
function CreateResourceQuota (line 9) | func CreateResourceQuota(namespace string, name string, pods string, sto...
FILE: kubelab-backend/pkg/k8s/secret.go
function GetSecretByName (line 8) | func GetSecretByName(namespace string, name string) (*v1.Secret, error) {
FILE: kubelab-backend/pkg/k8s/service.go
type ServiceParams (line 10) | type ServiceParams struct
function CreateService (line 17) | func CreateService(params ServiceParams) (*v1.Service, error) {
function DeleteService (line 50) | func DeleteService(namespace string, name string) error {
FILE: kubelab-backend/pkg/util/helpers.go
function StringParser (line 9) | func StringParser(s string) string {
FILE: kubelab-fill/upload.py
class Lab (line 6) | class Lab:
method __init__ (line 7) | def __init__(self, title, description, docs):
class Exercise (line 13) | class Exercise:
method __init__ (line 14) | def __init__(self, title, description, docs, hint, solution, check, bo...
function post_request (line 25) | def post_request(url, data):
function main (line 34) | def main():
function get_labs_from_dir (line 84) | def get_labs_from_dir(path):
function get_exercises_from_dir (line 103) | def get_exercises_from_dir(path, level=1, parent=None):
function parse_name (line 129) | def parse_name(name):
FILE: kubelab-fill/users_import.py
class User (line 8) | class User:
method __init__ (line 9) | def __init__(self, email, password, passwordConfirm, name):
function post_request (line 16) | def post_request(url, data):
function main (line 24) | def main():
function get_users_from_csv (line 40) | def get_users_from_csv(path):
FILE: kubelab-score/score_calculation.py
class User (line 7) | class User:
method __init__ (line 8) | def __init__(self, id, name, email, username):
class ExerciseSession (line 15) | class ExerciseSession:
method __init__ (line 16) | def __init__(self, id, userId, startTime, endTime):
function get_request (line 23) | def get_request(url):
function main (line 32) | def main():
function get_users_from_users_url (line 54) | def get_users_from_users_url(users_url):
function get_exercise_sessions_from_exercise_sessions_url (line 69) | def get_exercise_sessions_from_exercise_sessions_url(exercises_sessions_...
FILE: kubelab-ui/src/lib/pocketbase/generated-types.ts
type Collections (line 5) | enum Collections {
type IsoDateString (line 21) | type IsoDateString = string
type RecordIdString (line 22) | type RecordIdString = string
type HTMLString (line 23) | type HTMLString = string
type BaseSystemFields (line 26) | type BaseSystemFields<T = never> = {
type AuthSystemFields (line 35) | type AuthSystemFields<T = never> = {
type CompaniesRecord (line 44) | type CompaniesRecord = {
type ExerciseSessionLogsTypeOptions (line 49) | enum ExerciseSessionLogsTypeOptions {
type ExerciseSessionLogsRecord (line 53) | type ExerciseSessionLogsRecord = {
type ExerciseSessionsRecord (line 60) | type ExerciseSessionsRecord = {
type ExercisesRecord (line 68) | type ExercisesRecord = {
type FaqsRecord (line 79) | type FaqsRecord = {
type FeaturesRecord (line 84) | type FeaturesRecord = {
type HooksEventOptions (line 88) | enum HooksEventOptions {
type HooksActionTypeOptions (line 94) | enum HooksActionTypeOptions {
type HooksRecord (line 98) | type HooksRecord = {
type LabSessionsRecord (line 108) | type LabSessionsRecord = {
type LabsRecord (line 116) | type LabsRecord = {
type NotificationsTypeOptions (line 122) | enum NotificationsTypeOptions {
type NotificationsRecord (line 125) | type NotificationsRecord = {
type PlansRecord (line 132) | type PlansRecord = {
type UsersRoleOptions (line 140) | enum UsersRoleOptions {
type UsersRecord (line 144) | type UsersRecord = {
type CompaniesResponse (line 154) | type CompaniesResponse = Required<CompaniesRecord> & BaseSystemFields
type ExerciseSessionLogsResponse (line 155) | type ExerciseSessionLogsResponse<Texpand = unknown> = Required<ExerciseS...
type ExerciseSessionsResponse (line 156) | type ExerciseSessionsResponse<Texpand = unknown> = Required<ExerciseSess...
type ExercisesResponse (line 157) | type ExercisesResponse<Texpand = unknown> = Required<ExercisesRecord> & ...
type FaqsResponse (line 158) | type FaqsResponse = Required<FaqsRecord> & BaseSystemFields
type FeaturesResponse (line 159) | type FeaturesResponse = Required<FeaturesRecord> & BaseSystemFields
type HooksResponse (line 160) | type HooksResponse = Required<HooksRecord> & BaseSystemFields
type LabSessionsResponse (line 161) | type LabSessionsResponse<Texpand = unknown> = Required<LabSessionsRecord...
type LabsResponse (line 162) | type LabsResponse = Required<LabsRecord> & BaseSystemFields
type NotificationsResponse (line 163) | type NotificationsResponse<Texpand = unknown> = Required<NotificationsRe...
type PlansResponse (line 164) | type PlansResponse<Texpand = unknown> = Required<PlansRecord> & BaseSyst...
type UsersResponse (line 165) | type UsersResponse<Texpand = unknown> = Required<UsersRecord> & AuthSyst...
type CollectionRecords (line 169) | type CollectionRecords = {
type CollectionResponses (line 184) | type CollectionResponses = {
FILE: kubelab-ui/src/lib/pocketbase/index.ts
function login (line 10) | async function login(
function logout (line 23) | function logout() {
FILE: kubelab-ui/src/lib/pocketbase/ui.ts
function alertOnFailure (line 4) | async function alertOnFailure(request: () => void) {
FILE: kubelab-ui/src/lib/stores/data.ts
function getLabSession (line 28) | async function getLabSession(labId: string) {
function getLabSessionsByLab (line 32) | async function getLabSessionsByLab(labId: string) {
function filterExerciseSessionsByLab (line 36) | function filterExerciseSessionsByLab(labId: string) {
function filterExercisesByLab (line 46) | function filterExercisesByLab(labId: string) {
function getExerciseSessionByExercise (line 51) | function getExerciseSessionByExercise(exerciseId: string) {
function checkIfExerciseIsDone (line 57) | function checkIfExerciseIsDone(exercise_id: string) {
function setExerciseByExerciseSession (line 65) | async function setExerciseByExerciseSession(exerciseSessionId: string) {
function setExerciseSessionByExercise (line 81) | async function setExerciseSessionByExercise(exerciseId: string) {
type UpdateFilterEnum (line 97) | enum UpdateFilterEnum {
type UpdateFilter (line 102) | interface UpdateFilter {
function updateDataStores (line 107) | async function updateDataStores(filter: UpdateFilter = { filter: UpdateF...
function setLabStartTime (line 171) | async function setLabStartTime(labSessionId: string) {
function setLabEndTime (line 188) | async function setLabEndTime(labSessionId: string) {
function setExerciseStartTime (line 205) | async function setExerciseStartTime(exerciseSessionId: string) {
function setExerciseEndTime (line 222) | async function setExerciseEndTime(exerciseSessionId: string) {
FILE: kubelab-ui/src/lib/stores/layout_store.ts
type LayoutStore (line 3) | interface LayoutStore {
function toggle_state (line 17) | function toggle_state(key: keyof LayoutStore) {
type LayoutKeysByType (line 24) | type LayoutKeysByType<T> = keyof {
function toggle_number (line 28) | function toggle_number(key: LayoutKeysByType<number>) {
FILE: kubelab-ui/src/lib/stores/metadata.ts
type Metadata (line 3) | interface Metadata {
FILE: kubelab-ui/src/lib/types.ts
type DeploymentStatus (line 1) | enum DeploymentStatus {
type Deployment (line 8) | interface Deployment {
type NavRoute (line 15) | interface NavRoute {
FILE: kubelab-ui/src/lib/utils/clickOutside.ts
function clickOutside (line 1) | function clickOutside(node: any) {
FILE: kubelab-ui/src/lib/utils/enums.ts
type Color (line 1) | enum Color {
FILE: kubelab-ui/src/lib/utils/time.ts
function getTimeAgo (line 1) | function getTimeAgo(isoString: string) {
function getDeltaTime (line 29) | function getDeltaTime(startIsoString: string, endIsoString: string) {
Condensed preview — 240 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (519K chars).
[
{
"path": ".dockerignore",
"chars": 68,
"preview": "assets/\nREADME.md\nSECURITY.md\nCODE_OF_CONDUCT.md\nCODEOWNERS\nLICENSE\n"
},
{
"path": ".editorconfig",
"chars": 376,
"preview": "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\ntrim_trailing_whitespace = true\n\n[{*.go,Ma"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 834,
"preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the b"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.md",
"chars": 595,
"preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your fea"
},
{
"path": ".github/dependabot.yml",
"chars": 963,
"preview": "# To get started with Dependabot version updates, you'll need to specify which\n# package ecosystems to update and where "
},
{
"path": ".github/workflows/ci.yml",
"chars": 1119,
"preview": "name: CI\non:\n push:\n branches: [ main ]\n pull_request:\n # The branches below must be a subset of the branches ab"
},
{
"path": ".github/workflows/docker-release.yml",
"chars": 1101,
"preview": "name: Docker Image Build & Push\n\non:\n release:\n types: [created]\n\nenv:\n REGISTRY: ghcr.io\n IMAGE_NAME: ${{ github."
},
{
"path": ".github/workflows/pr-labels.yml",
"chars": 276,
"preview": "name: Size Label\non: pull_request\njobs:\n size-label:\n runs-on: ubuntu-latest\n if: github.actor != 'dependabot[bot"
},
{
"path": ".gitignore",
"chars": 529,
"preview": "# Mac OS X files\n.DS_Store\n\n# Binaries for programs and plugins\n*.exe\n*.exe~\n*.dll\n*.so\n*.dylib\n\n# Test binary, build wi"
},
{
"path": ".pre-commit-config.yaml",
"chars": 1025,
"preview": "repos:\n - repo: https://github.com/pre-commit/pre-commit-hooks\n rev: v4.3.0\n hooks:\n - id: check-yaml\n "
},
{
"path": "CODEOWNERS",
"chars": 15,
"preview": "* @janlauber\n"
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 5222,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participa"
},
{
"path": "CONTRIBUTING.md",
"chars": 4217,
"preview": "# Contributing\n\nWhen contributing to this repository, please first discuss the change you wish to make via issue,\nemail,"
},
{
"path": "Dockerfile",
"chars": 1006,
"preview": "FROM golang:1.22-alpine AS backend-builder\nWORKDIR /build\nCOPY kubelab-backend/go.mod kubelab-backend/go.sum kubelab-bac"
},
{
"path": "LICENSE",
"chars": 10418,
"preview": "Apache License\n==============\n\n_Version 2.0, January 2004_\n_<<http://www.apache.org/licenses/>>_\n\n### Terms and Co"
},
{
"path": "README.md",
"chars": 11070,
"preview": "# KubeLab: The Ultimate Kubernetes Learning Platform \n\n<p align=\"center\">\n <a href=\"https://kubelab.natron.io\">\n "
},
{
"path": "SECURITY.md",
"chars": 192,
"preview": "# Security Policy\n\n## Supported Versions\n\n| Version | Supported |\n| ------- | ------------------ |\n| latest | "
},
{
"path": "assets/drawio/architecture.drawio",
"chars": 26229,
"preview": "<mxfile host=\"app.diagrams.net\" modified=\"2023-06-12T06:58:34.998Z\" agent=\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_"
},
{
"path": "deploy/clusterrole.yaml",
"chars": 151,
"preview": "apiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRole\nmetadata:\n name: cluster-admin\nrules:\n- apiGroups: [\"*\"]\n r"
},
{
"path": "deploy/clusterrolebinding.yaml",
"chars": 263,
"preview": "apiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRoleBinding\nmetadata:\n name: kubelab-admin\nsubjects:\n- kind: Serv"
},
{
"path": "deploy/configmap.yaml",
"chars": 266,
"preview": "apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: kubelab-config\ndata:\n KUBELAB_AGENT_IMAGE: ghcr.io/natrontech/kubelab-"
},
{
"path": "deploy/ingress.yaml",
"chars": 665,
"preview": "apiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n name: kubelab-ingress\n namespace: kubelab\n annotations:\n "
},
{
"path": "deploy/kustomization.yaml",
"chars": 237,
"preview": "apiVersion: kustomize.config.k8s.io/v1beta1\nkind: Kustomization\nnamespace: kubelab\nresources:\n- ns.yaml\n- service.yaml\n-"
},
{
"path": "deploy/ns.yaml",
"chars": 57,
"preview": "apiVersion: v1\nkind: Namespace\nmetadata:\n name: kubelab\n"
},
{
"path": "deploy/persistentvolume.yaml",
"chars": 299,
"preview": "apiVersion: v1\nkind: PersistentVolume\nmetadata:\n name: hostpath-pv\n namespace: kubelab\n labels:\n type: local\nspec:"
},
{
"path": "deploy/service.yaml",
"chars": 165,
"preview": "apiVersion: v1\nkind: Service\nmetadata:\n name: kubelab-service\n namespace: kubelab\nspec:\n selector:\n app: kubelab\n "
},
{
"path": "deploy/serviceaccount.yaml",
"chars": 89,
"preview": "apiVersion: v1\nkind: ServiceAccount\nmetadata:\n name: kubelab-admin\n namespace: kubelab\n"
},
{
"path": "deploy/statefulset.yaml",
"chars": 1224,
"preview": "apiVersion: apps/v1\nkind: StatefulSet\nmetadata:\n name: kubelab-statefulset\n namespace: kubelab\nspec:\n serviceName: \"k"
},
{
"path": "docker-compose.yaml",
"chars": 267,
"preview": "version: '3.5'\n\nservices:\n kubelab:\n build: .\n # image: ghcr.io/natrontech/kubelab:latest\n ports:\n - \"809"
},
{
"path": "kubelab-backend/.gitignore",
"chars": 90,
"preview": "/.cache\n/pocketbase\n/pocketbase*.zip\n/pb_data\n/pb_data_old\n/tmp\n/bin\n./pocketbase\nkubelab\n"
},
{
"path": "kubelab-backend/README.md",
"chars": 5492,
"preview": "# KubeLab Backend\n\nThere are two flavors of the backend:\n\n1. standard release downloaded from https://github.com/pocketb"
},
{
"path": "kubelab-backend/entrypoint.sh",
"chars": 379,
"preview": "#!/bin/sh\nset -e # exit on any non-zero status (error)\n\n# this entrypoint script checks that all required setup is done\n"
},
{
"path": "kubelab-backend/example-hook-script.sh",
"chars": 410,
"preview": "#!/usr/bin/env bash\n\n# This example script, along with hooks.go, shows how to trigger a command\n# when a record changes "
},
{
"path": "kubelab-backend/go.mod",
"chars": 10653,
"preview": "module github.com/natrontech/kubelab\n\ngo 1.22\ntoolchain go1.22.5\n\nrequire (\n\tgithub.com/caarlos0/env/v8 v8.0.0\n\tgithub.c"
},
{
"path": "kubelab-backend/go.sum",
"chars": 69625,
"preview": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.115.0 h1:CnFSK"
},
{
"path": "kubelab-backend/hooks/hooks.go",
"chars": 3668,
"preview": "package hooks\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"os/exec\"\n\t\"strings\"\n\n\t\"github"
},
{
"path": "kubelab-backend/main.go",
"chars": 2589,
"preview": "package main\n\nimport (\n\t\"log\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/natrontech/kubelab/hooks\"\n\t\"github.com/nat"
},
{
"path": "kubelab-backend/modd.conf",
"chars": 245,
"preview": "# Run go test on ALL modules on startup, and subsequently only on modules\n# containing changes.\n**/*.go {\n prep: go b"
},
{
"path": "kubelab-backend/pb_migrations/1671835039_created_hooks.js",
"chars": 2790,
"preview": "migrate(\n (db) => {\n const collection = new Collection({\n id: \"3fhw2mfr9zrgodj\",\n create"
},
{
"path": "kubelab-backend/pb_migrations/1671926343_updated_hooks.js",
"chars": 1274,
"preview": "migrate(\n (db) => {\n const dao = new Dao(db);\n const collection = dao.findCollectionByNameOrId(\"3fhw2mf"
},
{
"path": "kubelab-backend/pb_migrations/1685691966_created_sessions.js",
"chars": 1300,
"preview": "migrate(\n (db) => {\n const collection = new Collection({\n id: \"n7tne53bbobf2bt\",\n create"
},
{
"path": "kubelab-backend/pb_migrations/1686128320_updated_users.js",
"chars": 946,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"_pb_users_auth_\")\n\n // a"
},
{
"path": "kubelab-backend/pb_migrations/1686128357_created_labs.js",
"chars": 1162,
"preview": "migrate((db) => {\n const collection = new Collection({\n \"id\": \"a1s1vqlm7141lcr\",\n \"created\": \"2023-06-07 08:59:17"
},
{
"path": "kubelab-backend/pb_migrations/1686128492_updated_sessions.js",
"chars": 1689,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"n7tne53bbobf2bt\")\n\n // a"
},
{
"path": "kubelab-backend/pb_migrations/1686128857_updated_sessions.js",
"chars": 888,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"n7tne53bbobf2bt\")\n\n // a"
},
{
"path": "kubelab-backend/pb_migrations/1686129123_created_exercises.js",
"chars": 2680,
"preview": "migrate((db) => {\n const collection = new Collection({\n \"id\": \"s4f0lpy3ibkgfqp\",\n \"created\": \"2023-06-07 09:12:03"
},
{
"path": "kubelab-backend/pb_migrations/1686129154_updated_labs.js",
"chars": 754,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"a1s1vqlm7141lcr\")\n\n // a"
},
{
"path": "kubelab-backend/pb_migrations/1686129166_updated_sessions.js",
"chars": 1077,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"n7tne53bbobf2bt\")\n\n // u"
},
{
"path": "kubelab-backend/pb_migrations/1686144343_updated_labs.js",
"chars": 630,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"a1s1vqlm7141lcr\")\n\n // a"
},
{
"path": "kubelab-backend/pb_migrations/1686311410_updated_sessions.js",
"chars": 635,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"n7tne53bbobf2bt\")\n\n // r"
},
{
"path": "kubelab-backend/pb_migrations/1686311431_updated_sessions.js",
"chars": 374,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"n7tne53bbobf2bt\")\n\n coll"
},
{
"path": "kubelab-backend/pb_migrations/1686311476_created_exercise_sessions.js",
"chars": 827,
"preview": "migrate((db) => {\n const collection = new Collection({\n \"id\": \"qj6ssich32lcxru\",\n \"created\": \"2023-06-09 11:51:16"
},
{
"path": "kubelab-backend/pb_migrations/1686311483_updated_exercises.js",
"chars": 579,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"s4f0lpy3ibkgfqp\")\n\n // r"
},
{
"path": "kubelab-backend/pb_migrations/1686311638_updated_exercise_sessions.js",
"chars": 1803,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"qj6ssich32lcxru\")\n\n // a"
},
{
"path": "kubelab-backend/pb_migrations/1686311654_updated_lab_sessions.js",
"chars": 1847,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"n7tne53bbobf2bt\")\n\n // r"
},
{
"path": "kubelab-backend/pb_migrations/1686311927_created_user_exercise_score.js",
"chars": 691,
"preview": "migrate((db) => {\n const collection = new Collection({\n \"id\": \"c0gc1ph97tdim19\",\n \"created\": \"2023-06-09 11:58:47"
},
{
"path": "kubelab-backend/pb_migrations/1686312000_updated_user_exercise_score.js",
"chars": 819,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"c0gc1ph97tdim19\")\n\n coll"
},
{
"path": "kubelab-backend/pb_migrations/1686312029_updated_user_exercise_score.js",
"chars": 1170,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"c0gc1ph97tdim19\")\n\n coll"
},
{
"path": "kubelab-backend/pb_migrations/1686312069_updated_user_exercise_score.js",
"chars": 1218,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"c0gc1ph97tdim19\")\n\n coll"
},
{
"path": "kubelab-backend/pb_migrations/1686312095_updated_user_exercise_score.js",
"chars": 1269,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"c0gc1ph97tdim19\")\n\n coll"
},
{
"path": "kubelab-backend/pb_migrations/1686312146_updated_user_exercise_score.js",
"chars": 1285,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"c0gc1ph97tdim19\")\n\n coll"
},
{
"path": "kubelab-backend/pb_migrations/1686312270_deleted_user_exercise_score.js",
"chars": 1035,
"preview": "migrate((db) => {\n const dao = new Dao(db);\n const collection = dao.findCollectionByNameOrId(\"c0gc1ph97tdim19\");\n\n re"
},
{
"path": "kubelab-backend/pb_migrations/1686312735_updated_labs.js",
"chars": 754,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"a1s1vqlm7141lcr\")\n\n // r"
},
{
"path": "kubelab-backend/pb_migrations/1686312758_updated_exercises.js",
"chars": 745,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"s4f0lpy3ibkgfqp\")\n\n // a"
},
{
"path": "kubelab-backend/pb_migrations/1686485128_updated_labs.js",
"chars": 600,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"a1s1vqlm7141lcr\")\n\n coll"
},
{
"path": "kubelab-backend/pb_migrations/1686485135_updated_lab_sessions.js",
"chars": 600,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"n7tne53bbobf2bt\")\n\n coll"
},
{
"path": "kubelab-backend/pb_migrations/1686485142_updated_exercises.js",
"chars": 600,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"s4f0lpy3ibkgfqp\")\n\n coll"
},
{
"path": "kubelab-backend/pb_migrations/1686485148_updated_exercise_sessions.js",
"chars": 600,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"qj6ssich32lcxru\")\n\n coll"
},
{
"path": "kubelab-backend/pb_migrations/1686568901_updated_exercise_sessions.js",
"chars": 735,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"qj6ssich32lcxru\")\n\n coll"
},
{
"path": "kubelab-backend/pb_migrations/1686568909_updated_lab_sessions.js",
"chars": 735,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"n7tne53bbobf2bt\")\n\n coll"
},
{
"path": "kubelab-backend/pb_migrations/1686569025_updated_lab_sessions.js",
"chars": 600,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"n7tne53bbobf2bt\")\n\n coll"
},
{
"path": "kubelab-backend/pb_migrations/1686569075_updated_lab_sessions.js",
"chars": 391,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"n7tne53bbobf2bt\")\n\n coll"
},
{
"path": "kubelab-backend/pb_migrations/1686569086_updated_lab_sessions.js",
"chars": 391,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"n7tne53bbobf2bt\")\n\n coll"
},
{
"path": "kubelab-backend/pb_migrations/1686569099_updated_lab_sessions.js",
"chars": 569,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"n7tne53bbobf2bt\")\n\n coll"
},
{
"path": "kubelab-backend/pb_migrations/1686569141_updated_exercise_sessions.js",
"chars": 735,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"qj6ssich32lcxru\")\n\n coll"
},
{
"path": "kubelab-backend/pb_migrations/1686600246_updated_exercise_sessions.js",
"chars": 600,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"qj6ssich32lcxru\")\n\n coll"
},
{
"path": "kubelab-backend/pb_migrations/1686600263_updated_exercise_sessions.js",
"chars": 730,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"qj6ssich32lcxru\")\n\n coll"
},
{
"path": "kubelab-backend/pb_migrations/1686990480_updated_exercise_sessions.js",
"chars": 813,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"qj6ssich32lcxru\")\n\n // u"
},
{
"path": "kubelab-backend/pb_migrations/1686990524_updated_lab_sessions.js",
"chars": 813,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"n7tne53bbobf2bt\")\n\n // u"
},
{
"path": "kubelab-backend/pb_migrations/1687092568_updated_labs.js",
"chars": 420,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"a1s1vqlm7141lcr\")\n\n coll"
},
{
"path": "kubelab-backend/pb_migrations/1687092574_updated_exercises.js",
"chars": 420,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"s4f0lpy3ibkgfqp\")\n\n coll"
},
{
"path": "kubelab-backend/pb_migrations/1687092614_updated_exercises.js",
"chars": 600,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"s4f0lpy3ibkgfqp\")\n\n coll"
},
{
"path": "kubelab-backend/pb_migrations/1687092623_updated_labs.js",
"chars": 600,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"a1s1vqlm7141lcr\")\n\n coll"
},
{
"path": "kubelab-backend/pb_migrations/1687116582_updated_exercises.js",
"chars": 488,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"s4f0lpy3ibkgfqp\")\n\n coll"
},
{
"path": "kubelab-backend/pb_migrations/1687116588_updated_labs.js",
"chars": 488,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"a1s1vqlm7141lcr\")\n\n coll"
},
{
"path": "kubelab-backend/pb_migrations/1687200869_updated_users.js",
"chars": 389,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"_pb_users_auth_\")\n\n coll"
},
{
"path": "kubelab-backend/pb_migrations/1687200885_updated_exercises.js",
"chars": 600,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"s4f0lpy3ibkgfqp\")\n\n coll"
},
{
"path": "kubelab-backend/pb_migrations/1687201122_updated_users.js",
"chars": 368,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"_pb_users_auth_\")\n\n coll"
},
{
"path": "kubelab-backend/pb_migrations/1687201342_updated_exercises.js",
"chars": 420,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"s4f0lpy3ibkgfqp\")\n\n coll"
},
{
"path": "kubelab-backend/pb_migrations/1687201352_updated_labs.js",
"chars": 488,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"a1s1vqlm7141lcr\")\n\n coll"
},
{
"path": "kubelab-backend/pb_migrations/1687208987_created_material.js",
"chars": 1220,
"preview": "migrate((db) => {\n const collection = new Collection({\n \"id\": \"19zg2e2qeca2b7d\",\n \"created\": \"2023-06-19 21:09:47"
},
{
"path": "kubelab-backend/pb_migrations/1687209270_updated_material.js",
"chars": 971,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"19zg2e2qeca2b7d\")\n\n // u"
},
{
"path": "kubelab-backend/pb_migrations/1687209711_deleted_material.js",
"chars": 1221,
"preview": "migrate((db) => {\n const dao = new Dao(db);\n const collection = dao.findCollectionByNameOrId(\"19zg2e2qeca2b7d\");\n\n re"
},
{
"path": "kubelab-backend/pb_migrations/1692004564_created_plans.js",
"chars": 1405,
"preview": "migrate((db) => {\n const collection = new Collection({\n \"id\": \"twr8eflpoom78k9\",\n \"created\": \"2023-08-14 09:16:04"
},
{
"path": "kubelab-backend/pb_migrations/1692004578_created_features.js",
"chars": 892,
"preview": "migrate((db) => {\n const collection = new Collection({\n \"id\": \"vknl4jpc8e5wlbv\",\n \"created\": \"2023-08-14 09:16:18"
},
{
"path": "kubelab-backend/pb_migrations/1692004634_updated_plans.js",
"chars": 2832,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"twr8eflpoom78k9\")\n\n // a"
},
{
"path": "kubelab-backend/pb_migrations/1692004755_updated_plans.js",
"chars": 817,
"preview": "migrate((db) => {\n const dao = new Dao(db)\n const collection = dao.findCollectionByNameOrId(\"twr8eflpoom78k9\")\n\n // u"
},
{
"path": "kubelab-backend/pb_migrations/1692006976_updated_users.js",
"chars": 993,
"preview": "/// <reference path=\"../pb_data/types.d.ts\" />\nmigrate((db) => {\n const dao = new Dao(db)\n const collection = dao.find"
},
{
"path": "kubelab-backend/pb_migrations/1692013150_updated_users.js",
"chars": 771,
"preview": "/// <reference path=\"../pb_data/types.d.ts\" />\nmigrate((db) => {\n const dao = new Dao(db)\n const collection = dao.find"
},
{
"path": "kubelab-backend/pb_migrations/1692013264_updated_plans.js",
"chars": 1140,
"preview": "/// <reference path=\"../pb_data/types.d.ts\" />\nmigrate((db) => {\n const dao = new Dao(db)\n const collection = dao.find"
},
{
"path": "kubelab-backend/pb_migrations/1692013276_updated_plans.js",
"chars": 1140,
"preview": "/// <reference path=\"../pb_data/types.d.ts\" />\nmigrate((db) => {\n const dao = new Dao(db)\n const collection = dao.find"
},
{
"path": "kubelab-backend/pb_migrations/1692013682_updated_features.js",
"chars": 467,
"preview": "/// <reference path=\"../pb_data/types.d.ts\" />\nmigrate((db) => {\n const dao = new Dao(db)\n const collection = dao.find"
},
{
"path": "kubelab-backend/pb_migrations/1692029251_created_faqs.js",
"chars": 1203,
"preview": "/// <reference path=\"../pb_data/types.d.ts\" />\nmigrate((db) => {\n const collection = new Collection({\n \"id\": \"jibt2v"
},
{
"path": "kubelab-backend/pb_migrations/1692030049_created_companies.js",
"chars": 1411,
"preview": "/// <reference path=\"../pb_data/types.d.ts\" />\nmigrate((db) => {\n const collection = new Collection({\n \"id\": \"w8voar"
},
{
"path": "kubelab-backend/pb_migrations/1692030210_updated_companies.js",
"chars": 467,
"preview": "/// <reference path=\"../pb_data/types.d.ts\" />\nmigrate((db) => {\n const dao = new Dao(db)\n const collection = dao.find"
},
{
"path": "kubelab-backend/pb_migrations/1697824916_updated_users.js",
"chars": 731,
"preview": "/// <reference path=\"../pb_data/types.d.ts\" />\nmigrate((db) => {\n const dao = new Dao(db)\n const collection = dao.find"
},
{
"path": "kubelab-backend/pb_migrations/1697824932_updated_users.js",
"chars": 1000,
"preview": "/// <reference path=\"../pb_data/types.d.ts\" />\nmigrate((db) => {\n const dao = new Dao(db)\n const collection = dao.find"
},
{
"path": "kubelab-backend/pb_migrations/1697982589_created_exercise_logs.js",
"chars": 1743,
"preview": "/// <reference path=\"../pb_data/types.d.ts\" />\nmigrate((db) => {\n const collection = new Collection({\n \"id\": \"juzwmx"
},
{
"path": "kubelab-backend/pb_migrations/1697982994_updated_exercise_logs.js",
"chars": 515,
"preview": "/// <reference path=\"../pb_data/types.d.ts\" />\nmigrate((db) => {\n const dao = new Dao(db)\n const collection = dao.find"
},
{
"path": "kubelab-backend/pb_migrations/1697983253_updated_exercise_logs.js",
"chars": 1341,
"preview": "/// <reference path=\"../pb_data/types.d.ts\" />\nmigrate((db) => {\n const dao = new Dao(db)\n const collection = dao.find"
},
{
"path": "kubelab-backend/pb_migrations/1697983426_updated_exercise_session_logs.js",
"chars": 491,
"preview": "/// <reference path=\"../pb_data/types.d.ts\" />\nmigrate((db) => {\n const dao = new Dao(db)\n const collection = dao.find"
},
{
"path": "kubelab-backend/pb_migrations/1697983434_updated_exercise_session_logs.js",
"chars": 845,
"preview": "/// <reference path=\"../pb_data/types.d.ts\" />\nmigrate((db) => {\n const dao = new Dao(db)\n const collection = dao.find"
},
{
"path": "kubelab-backend/pb_migrations/1697983470_updated_users.js",
"chars": 615,
"preview": "/// <reference path=\"../pb_data/types.d.ts\" />\nmigrate((db) => {\n const dao = new Dao(db)\n const collection = dao.find"
},
{
"path": "kubelab-backend/pb_migrations/1697983479_updated_exercise_sessions.js",
"chars": 635,
"preview": "/// <reference path=\"../pb_data/types.d.ts\" />\nmigrate((db) => {\n const dao = new Dao(db)\n const collection = dao.find"
},
{
"path": "kubelab-backend/pb_migrations/1697983500_updated_lab_sessions.js",
"chars": 637,
"preview": "/// <reference path=\"../pb_data/types.d.ts\" />\nmigrate((db) => {\n const dao = new Dao(db)\n const collection = dao.find"
},
{
"path": "kubelab-backend/pb_migrations/1697983560_updated_exercise_session_logs.js",
"chars": 1159,
"preview": "/// <reference path=\"../pb_data/types.d.ts\" />\nmigrate((db) => {\n const dao = new Dao(db)\n const collection = dao.find"
},
{
"path": "kubelab-backend/pb_migrations/1697984665_updated_exercise_session_logs.js",
"chars": 730,
"preview": "/// <reference path=\"../pb_data/types.d.ts\" />\nmigrate((db) => {\n const dao = new Dao(db)\n const collection = dao.find"
},
{
"path": "kubelab-backend/pb_migrations/1697984707_updated_exercise_session_logs.js",
"chars": 997,
"preview": "/// <reference path=\"../pb_data/types.d.ts\" />\nmigrate((db) => {\n const dao = new Dao(db)\n const collection = dao.find"
},
{
"path": "kubelab-backend/pb_migrations/1697986578_created_notifications.js",
"chars": 1596,
"preview": "/// <reference path=\"../pb_data/types.d.ts\" />\nmigrate((db) => {\n const collection = new Collection({\n \"id\": \"r2q6rr"
},
{
"path": "kubelab-backend/pb_migrations/1697987216_updated_notifications.js",
"chars": 903,
"preview": "/// <reference path=\"../pb_data/types.d.ts\" />\nmigrate((db) => {\n const dao = new Dao(db)\n const collection = dao.find"
},
{
"path": "kubelab-backend/pb_migrations/1697988059_updated_users.js",
"chars": 801,
"preview": "/// <reference path=\"../pb_data/types.d.ts\" />\nmigrate((db) => {\n const dao = new Dao(db)\n const collection = dao.find"
},
{
"path": "kubelab-backend/pb_migrations/1697992209_updated_exercise_session_logs.js",
"chars": 1048,
"preview": "/// <reference path=\"../pb_data/types.d.ts\" />\nmigrate((db) => {\n const dao = new Dao(db)\n const collection = dao.find"
},
{
"path": "kubelab-backend/pb_migrations/1697992245_updated_notifications.js",
"chars": 767,
"preview": "/// <reference path=\"../pb_data/types.d.ts\" />\nmigrate((db) => {\n const dao = new Dao(db)\n const collection = dao.find"
},
{
"path": "kubelab-backend/pb_migrations/1697992793_updated_notifications.js",
"chars": 803,
"preview": "/// <reference path=\"../pb_data/types.d.ts\" />\nmigrate((db) => {\n const dao = new Dao(db)\n const collection = dao.find"
},
{
"path": "kubelab-backend/pb_migrations/1697995266_updated_users.js",
"chars": 647,
"preview": "/// <reference path=\"../pb_data/types.d.ts\" />\nmigrate((db) => {\n const dao = new Dao(db)\n const collection = dao.find"
},
{
"path": "kubelab-backend/pkg/controller/exercise.go",
"chars": 3824,
"preview": "package controller\n\nimport (\n\t\"log\"\n\t\"time\"\n\n\t\"github.com/natrontech/kubelab/pkg/env\"\n\t\"github.com/natrontech/kubelab/pk"
},
{
"path": "kubelab-backend/pkg/controller/handler.go",
"chars": 528,
"preview": "package controller\n\nimport (\n\t\"github.com/pocketbase/pocketbase\"\n\t\"github.com/pocketbase/pocketbase/core\"\n)\n\nfunc Handle"
},
{
"path": "kubelab-backend/pkg/controller/lab.go",
"chars": 2515,
"preview": "package controller\n\nimport (\n\t\"log\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/natrontech/kubelab/pkg/env\"\n\t\"github.com/natrontech/kube"
},
{
"path": "kubelab-backend/pkg/controller/sessions.go",
"chars": 3580,
"preview": "package controller\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/pocketbase/dbx\"\n\t\"github.com/pocketbase/pocketbase\"\n\t\"github.com/pocke"
},
{
"path": "kubelab-backend/pkg/controller/util.go",
"chars": 551,
"preview": "package controller\n\nimport (\n\t\"io\"\n\t\"log\"\n\t\"net/http\"\n\n\t\"github.com/natrontech/kubelab/pkg/helm\"\n\t\"github.com/pocketbase"
},
{
"path": "kubelab-backend/pkg/env/env.go",
"chars": 1236,
"preview": "package env\n\nimport (\n\t\"log\"\n\n\t\"github.com/caarlos0/env/v8\"\n)\n\ntype config struct {\n\tLocal bool `env:"
},
{
"path": "kubelab-backend/pkg/helm/helm.go",
"chars": 1603,
"preview": "package helm\n\nimport (\n\t\"context\"\n\t\"strings\"\n\t\"time\"\n\n\thelmclient \"github.com/mittwald/go-helm-client\"\n\t\"github.com/natr"
},
{
"path": "kubelab-backend/pkg/k8s/config.go",
"chars": 1886,
"preview": "package k8s\n\nimport (\n\t\"context\"\n\t\"flag\"\n\t\"path/filepath\"\n\n\t\"github.com/natrontech/kubelab/pkg/env\"\n\t\"k8s.io/client-go/d"
},
{
"path": "kubelab-backend/pkg/k8s/deployment.go",
"chars": 7894,
"preview": "package k8s\n\nimport (\n\t\"log\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/natrontech/kubelab/pkg/env\"\n\t\"github.com/natrontech/kubela"
},
{
"path": "kubelab-backend/pkg/k8s/ingress.go",
"chars": 4199,
"preview": "package k8s\n\nimport (\n\t\"github.com/natrontech/kubelab/pkg/env\"\n\t\"github.com/natrontech/kubelab/pkg/util\"\n\t\"github.com/po"
},
{
"path": "kubelab-backend/pkg/k8s/namespace.go",
"chars": 2007,
"preview": "package k8s\n\nimport (\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/natrontech/kubelab/pkg/util\"\n\t\"github.com/pocketbase/pocketbas"
},
{
"path": "kubelab-backend/pkg/k8s/pod.go",
"chars": 354,
"preview": "package k8s\n\nimport (\n\tv1 \"k8s.io/api/core/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n)\n\nfunc GetPodByName(names"
},
{
"path": "kubelab-backend/pkg/k8s/resourcequota.go",
"chars": 661,
"preview": "package k8s\n\nimport (\n\tv1 \"k8s.io/api/core/v1\"\n\t\"k8s.io/apimachinery/pkg/api/resource\"\n\tmetav1 \"k8s.io/apimachinery/pkg/"
},
{
"path": "kubelab-backend/pkg/k8s/secret.go",
"chars": 369,
"preview": "package k8s\n\nimport (\n\tv1 \"k8s.io/api/core/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n)\n\nfunc GetSecretByName(na"
},
{
"path": "kubelab-backend/pkg/k8s/service.go",
"chars": 1450,
"preview": "package k8s\n\nimport (\n\t\"github.com/natrontech/kubelab/pkg/util\"\n\t\"github.com/pocketbase/pocketbase/models\"\n\tv1 \"k8s.io/a"
},
{
"path": "kubelab-backend/pkg/util/helpers.go",
"chars": 414,
"preview": "package util\n\nimport \"strings\"\n\nvar (\n\tSpecialCharacters string = \"!@#$%^&*()_+-=[]{}|;':,./<>?`~\"\n)\n\nfunc StringParser("
},
{
"path": "kubelab-backend/vcluster-values.yaml",
"chars": 600,
"preview": "sync:\n persistentvolumes:\n enabled: true\n storageclasses:\n enabled: false\n ingresses:\n enabled: true\n hosts"
},
{
"path": "kubelab-fill/example_users.csv",
"chars": 34,
"preview": "firstname,lastname,email,password\n"
},
{
"path": "kubelab-fill/upload.py",
"chars": 4545,
"preview": "import json\nimport os\nimport requests\n\n\nclass Lab:\n def __init__(self, title, description, docs):\n self.title "
},
{
"path": "kubelab-fill/users_import.py",
"chars": 1432,
"preview": "\n\nimport csv\nimport json\nimport os\nimport requests\n\nclass User:\n def __init__(self, email, password, passwordConfirm,"
},
{
"path": "kubelab-score/score.csv",
"chars": 63,
"preview": "userId,username,email,exercise_session_id,start_time,end_time\r\n"
},
{
"path": "kubelab-score/score_calculation.py",
"chars": 2683,
"preview": "import csv\nimport json\nimport os\nimport requests\n\n\nclass User:\n def __init__(self, id, name, email, username):\n "
},
{
"path": "kubelab-ui/.dockerignore",
"chars": 36,
"preview": ".git\n.svelte-kit\nbuild\nnode_modules\n"
},
{
"path": "kubelab-ui/.env.example",
"chars": 92,
"preview": "export AGENT_INGRESS_CLASS=nginx-external\nexport ALLOWED_HOSTS=kubelab.ch\nexport LOCAL=true\n"
},
{
"path": "kubelab-ui/.eslintignore",
"chars": 160,
"preview": ".DS_Store\nnode_modules\n/build\n/.svelte-kit\n/package\n.env\n.env.*\n!.env.example\n\n# Ignore files for PNPM, NPM and YARN\npnp"
},
{
"path": "kubelab-ui/.eslintrc.cjs",
"chars": 518,
"preview": "module.exports = {\n root: true,\n parser: \"@typescript-eslint/parser\",\n extends: [\"eslint:recommended\", \"plugin:@types"
},
{
"path": "kubelab-ui/.gitignore",
"chars": 132,
"preview": ".DS_Store\nnode_modules\n/build\n/.svelte-kit\n/package\n.env\n.env.*\n!.env.example\nvite.config.js.timestamp-*\nvite.config.ts."
},
{
"path": "kubelab-ui/.npmrc",
"chars": 19,
"preview": "engine-strict=true\n"
},
{
"path": "kubelab-ui/.prettierignore",
"chars": 160,
"preview": ".DS_Store\nnode_modules\n/build\n/.svelte-kit\n/package\n.env\n.env.*\n!.env.example\n\n# Ignore files for PNPM, NPM and YARN\npnp"
},
{
"path": "kubelab-ui/.prettierrc",
"chars": 242,
"preview": "{\n \"useTabs\": false,\n \"singleQuote\": false,\n \"trailingComma\": \"none\",\n \"printWidth\": 100,\n \"plugins\": [\"prettier-pl"
},
{
"path": "kubelab-ui/.vscode/settings.json",
"chars": 1326,
"preview": "{\n \"tailwindCSS.classAttributes\": [\n \"class\",\n \"accent\",\n \"active\",\n \"background\",\n \"badge\",\n \"border"
},
{
"path": "kubelab-ui/Dockerfile",
"chars": 357,
"preview": "FROM node:lts-slim as build\n\nWORKDIR /app\n\nCOPY package*.json ./\nRUN rm -rf node_modules\nRUN rm -rf build\nCOPY . .\nRUN n"
},
{
"path": "kubelab-ui/README.md",
"chars": 1388,
"preview": "# KubeLab UI\n\n## Setup\n\n```bash\nnpm install # install dependencies\nnpm run build # compile frontend\n```\n\nThe above produ"
},
{
"path": "kubelab-ui/next-env.d.ts",
"chars": 201,
"preview": "/// <reference types=\"next\" />\n/// <reference types=\"next/image-types/global\" />\n\n// NOTE: This file should not be edite"
},
{
"path": "kubelab-ui/package.json",
"chars": 2504,
"preview": "{\n \"name\": \"ui\",\n \"version\": \"1.0.0\",\n \"private\": true,\n \"scripts\": {\n \"dev\": \"vite dev\",\n \"dev:backend\": \"cd "
},
{
"path": "kubelab-ui/playwright.config.ts",
"chars": 246,
"preview": "import type { PlaywrightTestConfig } from \"@playwright/test\";\n\nconst config: PlaywrightTestConfig = {\n webServer: {\n "
},
{
"path": "kubelab-ui/postcss.config.cjs",
"chars": 83,
"preview": "module.exports = {\n plugins: [require(\"tailwindcss\"), require(\"autoprefixer\")]\n};\n"
},
{
"path": "kubelab-ui/src/app.css",
"chars": 715,
"preview": "a {\n text-decoration: none !important;\n}\n@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\nh1 {\n font-si"
},
{
"path": "kubelab-ui/src/app.d.ts",
"chars": 263,
"preview": "// See https://kit.svelte.dev/docs/types#app\n// for information about these interfaces\n// and what to do when importing "
},
{
"path": "kubelab-ui/src/app.html",
"chars": 323,
"preview": "<!DOCTYPE html>\n<html lang=\"en\" >\n <head>\n <meta charset=\"utf-8\" />\n <link rel=\"icon\" href=\"%sveltekit.assets%/fa"
},
{
"path": "kubelab-ui/src/app.postcss",
"chars": 311,
"preview": "/*place global styles here */\n/* html,\nbody {\n @apply h-full overflow-hidden;\n} */\nhtml,\nbody {\n @apply h-full scrollb"
},
{
"path": "kubelab-ui/src/hooks.client.ts",
"chars": 265,
"preview": "import { client, currentUser } from \"$lib/pocketbase\";\n\nclient.authStore.loadFromCookie(document.cookie);\nclient.authSto"
},
{
"path": "kubelab-ui/src/lib/components/Console.svelte",
"chars": 4632,
"preview": "<script lang=\"ts\">\n import { client } from \"$lib/pocketbase\";\n import { layout_store } from \"$lib/stores/layout_store\""
},
{
"path": "kubelab-ui/src/lib/components/base/BackButton.svelte",
"chars": 333,
"preview": "<script lang=\"ts\">\n import Icon from \"svelte-icons-pack\";\n import { ArrowBigLeft } from \"lucide-svelte\";\n\n function o"
},
{
"path": "kubelab-ui/src/lib/components/base/Badges.svelte",
"chars": 702,
"preview": "<script lang=\"ts\">\n import { Color } from \"$lib/utils/enums\";\n\n export let color: Color = Color.Gray;\n export let siz"
},
{
"path": "kubelab-ui/src/lib/components/base/Card.svelte",
"chars": 91,
"preview": "<div class=\"card h-64\">\n <div class=\"card-header\" />\n <div class=\"card-footer\" />\n</div>\n"
},
{
"path": "kubelab-ui/src/lib/components/base/Desktop.svelte",
"chars": 383,
"preview": "<script lang=\"ts\">\n import darkTheme from \"$lib/stores/theme\";\n import type { ComponentType, SvelteComponentTyped } fr"
},
{
"path": "kubelab-ui/src/lib/components/base/Nav.svelte",
"chars": 4290,
"preview": "<script lang=\"ts\">\n import { navigating } from \"$app/stores\";\n import { client, logout } from \"$lib/pocketbase\";\n i"
},
{
"path": "kubelab-ui/src/lib/components/base/PlaceholderComponent.svelte",
"chars": 113,
"preview": "<div />\n\n<style>\n div {\n background-color: var(--sk-back-1);\n width: 100%;\n height: 100%;\n }\n</style>\n"
},
{
"path": "kubelab-ui/src/lib/components/base/SideOver.svelte",
"chars": 10127,
"preview": "<script lang=\"ts\">\n import type {\n ExerciseSessionsRecord,\n ExerciseSessionsResponse,\n LabSessionsRecord,\n "
},
{
"path": "kubelab-ui/src/lib/components/base/ToggleConfetti.svelte",
"chars": 721,
"preview": "<script>\n import { onMount, tick } from \"svelte\";\n\n export let toggleOnce = false;\n export let relative = true;\n\n le"
},
{
"path": "kubelab-ui/src/lib/components/dashboard/Chart.svelte",
"chars": 394,
"preview": "<script>\n import { Line } from \"svelte-chartjs\";\n\n import {\n Chart as ChartJS,\n Title,\n Tooltip,\n Legend,\n"
},
{
"path": "kubelab-ui/src/lib/components/dashboard/RunningExercises.svelte",
"chars": 3878,
"preview": "<script lang=\"ts\">\n import { goto } from \"$app/navigation\";\n import { client } from \"$lib/pocketbase\";\n import type {"
},
{
"path": "kubelab-ui/src/lib/components/dashboard/data.ts",
"chars": 1550,
"preview": "export const data: any = {\n labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],\n datasets: ["
},
{
"path": "kubelab-ui/src/lib/components/labs/Exercise.svelte",
"chars": 10554,
"preview": "<script lang=\"ts\">\n import { goto } from \"$app/navigation\";\n import { client } from \"$lib/pocketbase\";\n import {\n "
},
{
"path": "kubelab-ui/src/lib/components/labs/Lab.svelte",
"chars": 4734,
"preview": "<script lang=\"ts\">\n import type {\n ExerciseSessionsRecord,\n ExerciseSessionsResponse,\n ExercisesResponse,\n "
},
{
"path": "kubelab-ui/src/lib/components/landingpage/Companies.svelte",
"chars": 1399,
"preview": "<script lang=\"ts\">\n import type { CompaniesResponse } from \"$lib/pocketbase/generated-types\";\n\n export let companies: "
},
{
"path": "kubelab-ui/src/lib/components/landingpage/Cta.svelte",
"chars": 1868,
"preview": "<div class=\"relative -z-10 mt-32 px-6 lg:px-8\">\n <div\n class=\"absolute inset-x-0 top-1/2 -z-10 flex -translate-y-1/2"
},
{
"path": "kubelab-ui/src/lib/components/landingpage/Faq.svelte",
"chars": 772,
"preview": "<script lang=\"ts\">\n import type { FaqsResponse } from \"$lib/pocketbase/generated-types\";\n\n export let faqs: FaqsRespon"
},
{
"path": "kubelab-ui/src/lib/components/landingpage/Features.svelte",
"chars": 3816,
"preview": "<script>\n import { PocketKnife, RefreshCcw, ShipWheel, Terminal } from \"lucide-svelte\";\n</script>\n\n<div class=\"mx-auto "
},
{
"path": "kubelab-ui/src/lib/components/landingpage/Footer.svelte",
"chars": 3897,
"preview": "<div class=\"mx-auto mt-32 max-w-7xl px-6 lg:px-8\">\n <footer\n aria-labelledby=\"footer-heading\"\n class=\"relative bo"
},
{
"path": "kubelab-ui/src/lib/components/landingpage/Header.svelte",
"chars": 1228,
"preview": "<script lang=\"ts\">\n import darkTheme from \"$lib/stores/theme\";\n import { Moon, Sun } from \"lucide-svelte\";\n</script>\n\n"
},
{
"path": "kubelab-ui/src/lib/components/landingpage/Hero.svelte",
"chars": 2799,
"preview": "<script>\n import darkTheme from \"$lib/stores/theme\";\n</script>\n\n<div class=\"relative pt-14\">\n <div\n class=\"absolute"
},
{
"path": "kubelab-ui/src/lib/components/markdown/CodeComponent.svelte",
"chars": 621,
"preview": "<script lang=\"ts\">\n import { Copy } from \"lucide-svelte\";\n import Prism from \"prismjs\";\n import toast from \"svelte-fr"
},
{
"path": "kubelab-ui/src/lib/components/markdown/CodeSpanComponent.svelte",
"chars": 552,
"preview": "<script lang=\"ts\">\n import { Copy } from \"lucide-svelte\";\n import toast from \"svelte-french-toast\";\n\n export let ra"
},
{
"path": "kubelab-ui/src/lib/components/markdown/LinkComponent.svelte",
"chars": 380,
"preview": "<script lang=\"ts\">\n import { ExternalLink } from \"lucide-svelte\";\n\n export let href: string = \"\";\n export let title"
},
{
"path": "kubelab-ui/src/lib/components/markdown/ListComponent.svelte",
"chars": 57,
"preview": "<script lang=\"ts\">\n</script>\n\n<slot />\n\n<style>\n</style>\n"
},
{
"path": "kubelab-ui/src/lib/config.ts",
"chars": 402,
"preview": "export const site = {\n name: \"KubeLab\",\n description:\n \"KubeLab is a state-of-the-art web platform offering"
},
{
"path": "kubelab-ui/src/lib/mock-data.ts",
"chars": 1283,
"preview": "import { DeploymentStatus, type Deployment } from \"./types\";\n\nexport const mockDeployments: Deployment[] = [\n {\n "
},
{
"path": "kubelab-ui/src/lib/pocketbase/generated-types.ts",
"chars": 4959,
"preview": "/**\n* This file was @generated using pocketbase-typegen\n*/\n\nexport enum Collections {\n\tCompanies = \"companies\",\n\tExercis"
},
{
"path": "kubelab-ui/src/lib/pocketbase/index.ts",
"chars": 785,
"preview": "import PocketBase from \"pocketbase\";\nimport { writable } from \"svelte/store\";\nimport toast from \"svelte-french-toast\";\ni"
},
{
"path": "kubelab-ui/src/lib/pocketbase/ui.ts",
"chars": 523,
"preview": "import toast from \"svelte-french-toast\";\n\n// wrapper to execute a pocketbase client request and generate alerts on failu"
},
{
"path": "kubelab-ui/src/lib/stores/codeView.ts",
"chars": 401,
"preview": "import { writable } from \"svelte/store\";\n\nconst defaultValue = false;\nconst initialCodeViewValue =\n localStorage.getI"
},
{
"path": "kubelab-ui/src/lib/stores/data.ts",
"chars": 7518,
"preview": "import { client } from \"$lib/pocketbase\";\nimport type {\n CompaniesResponse,\n ExerciseSessionLogsResponse,\n Exer"
},
{
"path": "kubelab-ui/src/lib/stores/layout_store.ts",
"chars": 1195,
"preview": "import { persisted } from \"svelte-local-storage-store\";\n\ninterface LayoutStore {\n file_tree: number;\n terminal: nu"
}
]
// ... and 40 more files (download for full content)
About this extraction
This page contains the full source code of the natrontech/kubelab GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 240 files (463.1 KB), approximately 151.9k tokens, and a symbol index with 140 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.