mystack-rig 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mystack_rig-1.0.0/.coveragerc +12 -0
- mystack_rig-1.0.0/.flake8 +5 -0
- mystack_rig-1.0.0/.github/workflows/release.yml +76 -0
- mystack_rig-1.0.0/.github/workflows/tests.yml +169 -0
- mystack_rig-1.0.0/.gitignore +105 -0
- mystack_rig-1.0.0/.rig/playbooks/README.md +45 -0
- mystack_rig-1.0.0/.rig/playbooks/vpn-gateway.yml +75 -0
- mystack_rig-1.0.0/CLI_STORAGE_SUMMARY.md +170 -0
- mystack_rig-1.0.0/Dockerfile +12 -0
- mystack_rig-1.0.0/GIT_CLONE_FEATURE.md +274 -0
- mystack_rig-1.0.0/HETZNER_STORAGE_GUIDE.md +168 -0
- mystack_rig-1.0.0/HETZNER_STORAGE_SECURITY_GUIDE.md +155 -0
- mystack_rig-1.0.0/LICENSE +21 -0
- mystack_rig-1.0.0/Makefile +55 -0
- mystack_rig-1.0.0/PKG-INFO +285 -0
- mystack_rig-1.0.0/README.md +262 -0
- mystack_rig-1.0.0/README_REAL_E2E.md +90 -0
- mystack_rig-1.0.0/STORAGE_CLI_GUIDE.md +155 -0
- mystack_rig-1.0.0/docs/architecture.md +84 -0
- mystack_rig-1.0.0/docs/architecture.png +0 -0
- mystack_rig-1.0.0/docs/architecture.svg +87 -0
- mystack_rig-1.0.0/forged-idea.md +155 -0
- mystack_rig-1.0.0/pyproject.toml +42 -0
- mystack_rig-1.0.0/pytest.ini +13 -0
- mystack_rig-1.0.0/requirements.txt +10 -0
- mystack_rig-1.0.0/sample.env +12 -0
- mystack_rig-1.0.0/scripts/coverage_badge.py +82 -0
- mystack_rig-1.0.0/scripts/pypi_version.py +118 -0
- mystack_rig-1.0.0/setup.cfg +4 -0
- mystack_rig-1.0.0/src/__init__.py +0 -0
- mystack_rig-1.0.0/src/cli/__init__.py +6 -0
- mystack_rig-1.0.0/src/cli/__main__.py +8 -0
- mystack_rig-1.0.0/src/cli/agents.py +575 -0
- mystack_rig-1.0.0/src/cli/config.py +264 -0
- mystack_rig-1.0.0/src/cli/encrypt.py +1182 -0
- mystack_rig-1.0.0/src/cli/git.py +317 -0
- mystack_rig-1.0.0/src/cli/last_env.py +64 -0
- mystack_rig-1.0.0/src/cli/last_session.py +171 -0
- mystack_rig-1.0.0/src/cli/log.py +105 -0
- mystack_rig-1.0.0/src/cli/main.py +1234 -0
- mystack_rig-1.0.0/src/cli/remote_dev.py +5547 -0
- mystack_rig-1.0.0/src/cli/requirements.txt +4 -0
- mystack_rig-1.0.0/src/cli/snapshots.py +627 -0
- mystack_rig-1.0.0/src/cli/storage.py +348 -0
- mystack_rig-1.0.0/src/cli/vault_keys.py +451 -0
- mystack_rig-1.0.0/src/cli/vpn.py +323 -0
- mystack_rig-1.0.0/src/mystack_rig.egg-info/PKG-INFO +285 -0
- mystack_rig-1.0.0/src/mystack_rig.egg-info/SOURCES.txt +90 -0
- mystack_rig-1.0.0/src/mystack_rig.egg-info/dependency_links.txt +1 -0
- mystack_rig-1.0.0/src/mystack_rig.egg-info/entry_points.txt +2 -0
- mystack_rig-1.0.0/src/mystack_rig.egg-info/requires.txt +8 -0
- mystack_rig-1.0.0/src/mystack_rig.egg-info/scm_file_list.json +86 -0
- mystack_rig-1.0.0/src/mystack_rig.egg-info/scm_version.json +8 -0
- mystack_rig-1.0.0/src/mystack_rig.egg-info/top_level.txt +1 -0
- mystack_rig-1.0.0/test_hetzner_integration.py +442 -0
- mystack_rig-1.0.0/tests/conftest.py +153 -0
- mystack_rig-1.0.0/tests/test_agents.py +208 -0
- mystack_rig-1.0.0/tests/test_ansible_galaxy.py +139 -0
- mystack_rig-1.0.0/tests/test_attach_command.py +456 -0
- mystack_rig-1.0.0/tests/test_credential_security.py +151 -0
- mystack_rig-1.0.0/tests/test_destroy_command.py +182 -0
- mystack_rig-1.0.0/tests/test_dev_command.py +461 -0
- mystack_rig-1.0.0/tests/test_encrypt_command.py +368 -0
- mystack_rig-1.0.0/tests/test_encrypt_coverage.py +201 -0
- mystack_rig-1.0.0/tests/test_firewall_client_ip.py +259 -0
- mystack_rig-1.0.0/tests/test_git_clone_unit.py +259 -0
- mystack_rig-1.0.0/tests/test_git_manager.py +95 -0
- mystack_rig-1.0.0/tests/test_gitignore_init.py +77 -0
- mystack_rig-1.0.0/tests/test_hetzner_storage.py +242 -0
- mystack_rig-1.0.0/tests/test_init_command.py +790 -0
- mystack_rig-1.0.0/tests/test_last_env.py +47 -0
- mystack_rig-1.0.0/tests/test_last_session.py +75 -0
- mystack_rig-1.0.0/tests/test_log.py +53 -0
- mystack_rig-1.0.0/tests/test_main_coverage.py +573 -0
- mystack_rig-1.0.0/tests/test_pypi_version.py +71 -0
- mystack_rig-1.0.0/tests/test_real_e2e_integration.py +244 -0
- mystack_rig-1.0.0/tests/test_real_e2e_storage.py +264 -0
- mystack_rig-1.0.0/tests/test_real_e2e_vpn.py +219 -0
- mystack_rig-1.0.0/tests/test_real_e2e_vpn_tunnel.py +210 -0
- mystack_rig-1.0.0/tests/test_remote_dev.py +476 -0
- mystack_rig-1.0.0/tests/test_remote_dev_coverage_extensions.py +2372 -0
- mystack_rig-1.0.0/tests/test_rig_ansible_vars.py +125 -0
- mystack_rig-1.0.0/tests/test_rig_config.py +110 -0
- mystack_rig-1.0.0/tests/test_setup_provision.py +745 -0
- mystack_rig-1.0.0/tests/test_simulation_storage.py +314 -0
- mystack_rig-1.0.0/tests/test_small_module_coverage.py +223 -0
- mystack_rig-1.0.0/tests/test_snapshots.py +705 -0
- mystack_rig-1.0.0/tests/test_storage_basic.py +679 -0
- mystack_rig-1.0.0/tests/test_vault_keys.py +144 -0
- mystack_rig-1.0.0/tests/test_vpn_optional.py +155 -0
- mystack_rig-1.0.0/tests/test_vpn_unit.py +188 -0
- mystack_rig-1.0.0/tests/test_workspace_git_clone.py +274 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Publishes to PyPI only when a version tag is pushed.
|
|
4
|
+
# v1.2.3 and 1.2.3 both publish mystack-rig 1.2.3.
|
|
5
|
+
# Create a repository Actions secret named PYPI_API_TOKEN
|
|
6
|
+
# (a PyPI API token, including the pypi- prefix). The token is not printed.
|
|
7
|
+
|
|
8
|
+
on:
|
|
9
|
+
push:
|
|
10
|
+
tags:
|
|
11
|
+
- "*"
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
publish:
|
|
18
|
+
name: Publish to PyPI
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
steps:
|
|
21
|
+
- name: Checkout code
|
|
22
|
+
uses: actions/checkout@v4
|
|
23
|
+
with:
|
|
24
|
+
fetch-depth: 0
|
|
25
|
+
|
|
26
|
+
- name: Set up Python
|
|
27
|
+
uses: actions/setup-python@v5
|
|
28
|
+
with:
|
|
29
|
+
python-version: "3.11"
|
|
30
|
+
|
|
31
|
+
- name: Install dependencies
|
|
32
|
+
run: |
|
|
33
|
+
python -m pip install --upgrade pip
|
|
34
|
+
pip install -e .
|
|
35
|
+
pip install build twine pytest pytest-cov mypy
|
|
36
|
+
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
|
|
37
|
+
|
|
38
|
+
- name: Type check
|
|
39
|
+
run: mypy src/ --ignore-missing-imports
|
|
40
|
+
|
|
41
|
+
- name: Run unit tests
|
|
42
|
+
run: |
|
|
43
|
+
pytest tests/ -v -m "not e2e" --cov=src --cov-report=term-missing --cov-report=xml --cov-fail-under=90 --ignore=tests/test_real_e2e_integration.py --ignore=tests/test_real_e2e_storage.py --ignore=tests/test_hetzner_storage.py
|
|
44
|
+
|
|
45
|
+
- name: Derive version from the release tag
|
|
46
|
+
id: version
|
|
47
|
+
run: |
|
|
48
|
+
version="$(python scripts/pypi_version.py "$GITHUB_REF_NAME")"
|
|
49
|
+
echo "version=$version" >> "$GITHUB_OUTPUT"
|
|
50
|
+
echo "Release version: $version (from tag $GITHUB_REF_NAME)"
|
|
51
|
+
|
|
52
|
+
- name: Build sdist and wheel
|
|
53
|
+
env:
|
|
54
|
+
SETUPTOOLS_SCM_PRETEND_VERSION: ${{ steps.version.outputs.version }}
|
|
55
|
+
run: python -m build
|
|
56
|
+
|
|
57
|
+
- name: Require the built archives to match the tag
|
|
58
|
+
run: python scripts/pypi_version.py --check-dist dist "$GITHUB_REF_NAME"
|
|
59
|
+
|
|
60
|
+
- name: Check package metadata
|
|
61
|
+
run: python -m twine check dist/*
|
|
62
|
+
|
|
63
|
+
- name: Require the PyPI API token secret
|
|
64
|
+
env:
|
|
65
|
+
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
|
|
66
|
+
run: |
|
|
67
|
+
if [ -z "$TWINE_PASSWORD" ]; then
|
|
68
|
+
echo "Create a GitHub Actions secret named PYPI_API_TOKEN with a PyPI API token (include the pypi- prefix)."
|
|
69
|
+
exit 1
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
- name: Publish to PyPI
|
|
73
|
+
env:
|
|
74
|
+
TWINE_USERNAME: __token__
|
|
75
|
+
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
|
|
76
|
+
run: python -m twine upload --non-interactive dist/*
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master, develop]
|
|
6
|
+
paths-ignore:
|
|
7
|
+
- '**.md'
|
|
8
|
+
- 'docs/**'
|
|
9
|
+
- 'LICENSE*'
|
|
10
|
+
- '.editorconfig'
|
|
11
|
+
pull_request:
|
|
12
|
+
branches: [master, develop]
|
|
13
|
+
paths-ignore:
|
|
14
|
+
- '**.md'
|
|
15
|
+
- 'docs/**'
|
|
16
|
+
- 'LICENSE*'
|
|
17
|
+
- '.editorconfig'
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
lint:
|
|
21
|
+
name: Lint
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
steps:
|
|
24
|
+
- name: Checkout code
|
|
25
|
+
uses: actions/checkout@v4
|
|
26
|
+
|
|
27
|
+
- name: Set up Python
|
|
28
|
+
uses: actions/setup-python@v5
|
|
29
|
+
with:
|
|
30
|
+
python-version: '3.11'
|
|
31
|
+
|
|
32
|
+
- name: Install dependencies
|
|
33
|
+
run: |
|
|
34
|
+
python -m pip install --upgrade pip
|
|
35
|
+
pip install flake8 pytest
|
|
36
|
+
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
|
|
37
|
+
|
|
38
|
+
- name: Lint with flake8
|
|
39
|
+
run: |
|
|
40
|
+
flake8 src/ tests/ --count --show-source --statistics
|
|
41
|
+
|
|
42
|
+
type-check:
|
|
43
|
+
name: Type Check
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
steps:
|
|
46
|
+
- name: Checkout code
|
|
47
|
+
uses: actions/checkout@v4
|
|
48
|
+
|
|
49
|
+
- name: Set up Python
|
|
50
|
+
uses: actions/setup-python@v5
|
|
51
|
+
with:
|
|
52
|
+
python-version: '3.11'
|
|
53
|
+
|
|
54
|
+
- name: Install dependencies
|
|
55
|
+
run: |
|
|
56
|
+
python -m pip install --upgrade pip
|
|
57
|
+
pip install mypy
|
|
58
|
+
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
|
|
59
|
+
|
|
60
|
+
- name: Type check with mypy
|
|
61
|
+
run: |
|
|
62
|
+
mypy src/ --ignore-missing-imports
|
|
63
|
+
|
|
64
|
+
unit-tests:
|
|
65
|
+
name: Unit Tests
|
|
66
|
+
runs-on: ubuntu-latest
|
|
67
|
+
# OIDC upload when Codecov GitHub App is linked; token used when present.
|
|
68
|
+
permissions:
|
|
69
|
+
contents: read
|
|
70
|
+
id-token: write
|
|
71
|
+
steps:
|
|
72
|
+
- name: Checkout code
|
|
73
|
+
uses: actions/checkout@v4
|
|
74
|
+
|
|
75
|
+
- name: Set up Python
|
|
76
|
+
uses: actions/setup-python@v5
|
|
77
|
+
with:
|
|
78
|
+
python-version: '3.11'
|
|
79
|
+
|
|
80
|
+
- name: Install dependencies
|
|
81
|
+
run: |
|
|
82
|
+
python -m pip install --upgrade pip
|
|
83
|
+
pip install -e .
|
|
84
|
+
pip install pytest pytest-cov
|
|
85
|
+
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
|
|
86
|
+
|
|
87
|
+
- name: Run unit tests
|
|
88
|
+
run: |
|
|
89
|
+
pytest tests/ -v -m "not e2e" --cov=src --cov-report=term-missing --cov-report=xml --cov-fail-under=90 --ignore=tests/test_real_e2e_integration.py --ignore=tests/test_real_e2e_storage.py --ignore=tests/test_hetzner_storage.py
|
|
90
|
+
|
|
91
|
+
- name: Report coverage
|
|
92
|
+
if: always()
|
|
93
|
+
run: python scripts/coverage_badge.py >> "$GITHUB_STEP_SUMMARY"
|
|
94
|
+
|
|
95
|
+
- name: Upload coverage to Codecov
|
|
96
|
+
if: always()
|
|
97
|
+
uses: codecov/codecov-action@v5
|
|
98
|
+
with:
|
|
99
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
100
|
+
use_oidc: true
|
|
101
|
+
files: ./coverage.xml
|
|
102
|
+
flags: unit-tests
|
|
103
|
+
name: unit-tests
|
|
104
|
+
# Do not fail Unit Tests on upload/auth issues (pytest is the gate).
|
|
105
|
+
fail_ci_if_error: false
|
|
106
|
+
verbose: true
|
|
107
|
+
|
|
108
|
+
integration-tests:
|
|
109
|
+
name: Integration Tests
|
|
110
|
+
runs-on: ubuntu-latest
|
|
111
|
+
needs: unit-tests
|
|
112
|
+
steps:
|
|
113
|
+
- name: Checkout code
|
|
114
|
+
uses: actions/checkout@v4
|
|
115
|
+
|
|
116
|
+
- name: Set up Python
|
|
117
|
+
uses: actions/setup-python@v5
|
|
118
|
+
with:
|
|
119
|
+
python-version: '3.11'
|
|
120
|
+
|
|
121
|
+
- name: Install dependencies
|
|
122
|
+
run: |
|
|
123
|
+
python -m pip install --upgrade pip
|
|
124
|
+
pip install -e .
|
|
125
|
+
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
|
|
126
|
+
|
|
127
|
+
- name: Run integration tests
|
|
128
|
+
run: |
|
|
129
|
+
pytest tests/test_remote_dev.py -v -k "not e2e"
|
|
130
|
+
|
|
131
|
+
e2e-tests:
|
|
132
|
+
name: E2E Tests
|
|
133
|
+
runs-on: ubuntu-latest
|
|
134
|
+
needs: unit-tests
|
|
135
|
+
timeout-minutes: 45
|
|
136
|
+
env:
|
|
137
|
+
HETZNER_CLOUD_API_TOKEN: ${{ secrets.HETZNER_CLOUD_API_TOKEN }}
|
|
138
|
+
HETZNER_S3_ACCESS_KEY: ${{ secrets.HETZNER_S3_ACCESS_KEY }}
|
|
139
|
+
HETZNER_S3_SECRET_KEY: ${{ secrets.HETZNER_S3_SECRET_KEY }}
|
|
140
|
+
S3_ENDPOINT_URL: https://fsn1.your-objectstorage.com
|
|
141
|
+
RIG_GITHUB_PAT: ${{ secrets.RIG_GITHUB_PAT }}
|
|
142
|
+
steps:
|
|
143
|
+
- name: Checkout code
|
|
144
|
+
uses: actions/checkout@v4
|
|
145
|
+
|
|
146
|
+
- name: Set up Python
|
|
147
|
+
uses: actions/setup-python@v5
|
|
148
|
+
with:
|
|
149
|
+
python-version: '3.11'
|
|
150
|
+
|
|
151
|
+
- name: Install dependencies
|
|
152
|
+
run: |
|
|
153
|
+
python -m pip install --upgrade pip
|
|
154
|
+
pip install -e .
|
|
155
|
+
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
|
|
156
|
+
sudo apt-get update
|
|
157
|
+
sudo apt-get install -y wireguard-tools
|
|
158
|
+
|
|
159
|
+
- name: Run E2E tests
|
|
160
|
+
run: |
|
|
161
|
+
# wg-quick needs root. Preserve PATH so the runner's pytest and ansible stay visible.
|
|
162
|
+
sudo env \
|
|
163
|
+
PATH="$PATH" \
|
|
164
|
+
HETZNER_CLOUD_API_TOKEN="$HETZNER_CLOUD_API_TOKEN" \
|
|
165
|
+
HETZNER_S3_ACCESS_KEY="$HETZNER_S3_ACCESS_KEY" \
|
|
166
|
+
HETZNER_S3_SECRET_KEY="$HETZNER_S3_SECRET_KEY" \
|
|
167
|
+
S3_ENDPOINT_URL="$S3_ENDPOINT_URL" \
|
|
168
|
+
RIG_GITHUB_PAT="$RIG_GITHUB_PAT" \
|
|
169
|
+
python -m pytest tests/ -v -m e2e --tb=short
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
env/
|
|
8
|
+
venv/
|
|
9
|
+
ENV/
|
|
10
|
+
.venv/
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
*.egg-info/
|
|
23
|
+
.installed.cfg
|
|
24
|
+
*.egg
|
|
25
|
+
|
|
26
|
+
# Virtual environments
|
|
27
|
+
.venv/
|
|
28
|
+
venv/
|
|
29
|
+
ENV/
|
|
30
|
+
env/
|
|
31
|
+
.venv/bin/
|
|
32
|
+
.venv/lib/
|
|
33
|
+
.venv/include/
|
|
34
|
+
|
|
35
|
+
# IDEs
|
|
36
|
+
.idea/
|
|
37
|
+
.vscode/
|
|
38
|
+
*.swp
|
|
39
|
+
*.swo
|
|
40
|
+
*~
|
|
41
|
+
.project
|
|
42
|
+
.pydevproject
|
|
43
|
+
.settings/
|
|
44
|
+
|
|
45
|
+
# Testing
|
|
46
|
+
.pytest_cache/
|
|
47
|
+
.coverage
|
|
48
|
+
coverage.xml
|
|
49
|
+
htmlcov/
|
|
50
|
+
.tox/
|
|
51
|
+
.nox/
|
|
52
|
+
*.cover
|
|
53
|
+
|
|
54
|
+
# Documentation
|
|
55
|
+
docs/_build/
|
|
56
|
+
docs/_static/
|
|
57
|
+
docs/_templates/
|
|
58
|
+
|
|
59
|
+
# Temporary files
|
|
60
|
+
/tmp/
|
|
61
|
+
*.tmp
|
|
62
|
+
*.temp
|
|
63
|
+
*.log
|
|
64
|
+
*.log.*
|
|
65
|
+
*.swp
|
|
66
|
+
*.swo
|
|
67
|
+
|
|
68
|
+
# YAML temp files (used for state storage)
|
|
69
|
+
state.yml
|
|
70
|
+
*.yml.tmp
|
|
71
|
+
*.yaml.tmp
|
|
72
|
+
|
|
73
|
+
# Environment variables
|
|
74
|
+
.env
|
|
75
|
+
.env.local
|
|
76
|
+
.env.*.local
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# mypy
|
|
82
|
+
.mypy_cache/
|
|
83
|
+
.dmypy.json
|
|
84
|
+
dmypy.json
|
|
85
|
+
|
|
86
|
+
# Pyre type checker
|
|
87
|
+
.pyre/
|
|
88
|
+
|
|
89
|
+
# pytype static type analyzer
|
|
90
|
+
.pytype/
|
|
91
|
+
|
|
92
|
+
# Cython debug symbols
|
|
93
|
+
cython_debug/
|
|
94
|
+
|
|
95
|
+
# Rig specific
|
|
96
|
+
.rig/.vault_password
|
|
97
|
+
.rig/credentials/
|
|
98
|
+
.rig/secrets/
|
|
99
|
+
|
|
100
|
+
# >>> rig managed
|
|
101
|
+
.rig/collections/
|
|
102
|
+
.rig/roles/
|
|
103
|
+
.rig/inventory.yml
|
|
104
|
+
.rig/*.retry
|
|
105
|
+
# <<< rig managed
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Ansible Playbooks
|
|
2
|
+
|
|
3
|
+
This directory contains Ansible playbooks for provisioning remote development environments.
|
|
4
|
+
|
|
5
|
+
## Available Playbooks
|
|
6
|
+
|
|
7
|
+
### WireGuard VPN Gateway
|
|
8
|
+
- **File**: `vpn-gateway.yml`
|
|
9
|
+
- **Purpose**: Provisions a remote VM with WireGuard as a VPN gateway
|
|
10
|
+
- **Usage**: Creates a VPN gateway that allows secure access to development services
|
|
11
|
+
|
|
12
|
+
## Structure
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
.rig/playbooks/
|
|
16
|
+
├── vpn-gateway.yml # WireGuard VPN gateway provisioning
|
|
17
|
+
└── README.md # This file
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## How It Works
|
|
21
|
+
|
|
22
|
+
The WireGuard VPN gateway playbook:
|
|
23
|
+
1. Installs WireGuard tools on the remote VM
|
|
24
|
+
2. Generates WireGuard key pair
|
|
25
|
+
3. Creates WireGuard interface configuration
|
|
26
|
+
4. Enables IP forwarding and masquerading
|
|
27
|
+
5. Starts the WireGuard service
|
|
28
|
+
|
|
29
|
+
## Configuration
|
|
30
|
+
|
|
31
|
+
The playbook uses the following variables (can be overridden):
|
|
32
|
+
- `vpn_gateway_ip`: VPN gateway IP address (default: 10.0.0.1/24)
|
|
33
|
+
- `vpn_gateway_port`: WireGuard listen port (default: 51820)
|
|
34
|
+
- `wg_interface_name`: WireGuard interface name (default: wg0)
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
ansible-playbook .rig/playbooks/vpn-gateway.yml -i <inventory> --private-key ~/.ssh/id_rsa
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## See Also
|
|
43
|
+
|
|
44
|
+
- [VPN CLI Commands](../src/cli/vpn.py) - Management commands for VPN tunnels
|
|
45
|
+
- [VPN E2E Tests](../tests/test_real_e2e_vpn_tunnel.py) - End-to-end VPN tunnel verification
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
---
|
|
2
|
+
- name: Configure WireGuard VPN Gateway
|
|
3
|
+
hosts: remote_vm
|
|
4
|
+
become: yes
|
|
5
|
+
vars:
|
|
6
|
+
vpn_gateway_ip: "10.0.0.1/24"
|
|
7
|
+
vpn_gateway_port: 51820
|
|
8
|
+
wg_interface_name: "wg0"
|
|
9
|
+
|
|
10
|
+
tasks:
|
|
11
|
+
- name: Wait for cloud-init to finish
|
|
12
|
+
command: cloud-init status --wait
|
|
13
|
+
changed_when: false
|
|
14
|
+
failed_when: false
|
|
15
|
+
|
|
16
|
+
- name: Install WireGuard tools
|
|
17
|
+
apt:
|
|
18
|
+
name:
|
|
19
|
+
- wireguard
|
|
20
|
+
- wireguard-tools
|
|
21
|
+
state: present
|
|
22
|
+
update_cache: yes
|
|
23
|
+
register: wg_install
|
|
24
|
+
retries: 12
|
|
25
|
+
delay: 10
|
|
26
|
+
until: wg_install is success
|
|
27
|
+
|
|
28
|
+
- name: Generate WireGuard private key
|
|
29
|
+
shell: wg genkey
|
|
30
|
+
register: wg_private_key
|
|
31
|
+
changed_when: false
|
|
32
|
+
|
|
33
|
+
- name: Generate WireGuard public key
|
|
34
|
+
shell: echo "{{ wg_private_key.stdout }}" | wg pubkey
|
|
35
|
+
register: wg_public_key
|
|
36
|
+
changed_when: false
|
|
37
|
+
|
|
38
|
+
- name: Create WireGuard configuration directory
|
|
39
|
+
file:
|
|
40
|
+
path: /etc/wireguard
|
|
41
|
+
state: directory
|
|
42
|
+
mode: '0700'
|
|
43
|
+
|
|
44
|
+
- name: Generate WireGuard configuration
|
|
45
|
+
copy:
|
|
46
|
+
dest: /etc/wireguard/{{ wg_interface_name }}.conf
|
|
47
|
+
content: |
|
|
48
|
+
[Interface]
|
|
49
|
+
Address = {{ vpn_gateway_ip }}
|
|
50
|
+
ListenPort = {{ vpn_gateway_port }}
|
|
51
|
+
PrivateKey = {{ wg_private_key.stdout }}
|
|
52
|
+
PostUp = iptables -A FORWARD -i {{ wg_interface_name }} -j ACCEPT; iptables -A FORWARD -o {{ wg_interface_name }} -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
|
|
53
|
+
PostDown = iptables -D FORWARD -i {{ wg_interface_name }} -j ACCEPT; iptables -D FORWARD -o {{ wg_interface_name }} -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
|
|
54
|
+
mode: '0600'
|
|
55
|
+
|
|
56
|
+
- name: Enable IP forwarding
|
|
57
|
+
sysctl:
|
|
58
|
+
name: net.ipv4.ip_forward
|
|
59
|
+
value: "1"
|
|
60
|
+
state: present
|
|
61
|
+
reload: yes
|
|
62
|
+
|
|
63
|
+
- name: Start and enable WireGuard service
|
|
64
|
+
systemd:
|
|
65
|
+
name: "wg-quick@{{ wg_interface_name }}"
|
|
66
|
+
state: started
|
|
67
|
+
enabled: yes
|
|
68
|
+
|
|
69
|
+
- name: Output VPN public key
|
|
70
|
+
debug:
|
|
71
|
+
msg: "VPN Gateway Public Key: {{ wg_public_key.stdout }}"
|
|
72
|
+
|
|
73
|
+
- name: Output VPN endpoint
|
|
74
|
+
debug:
|
|
75
|
+
msg: "VPN Gateway Endpoint: {{ ansible_default_ipv4.address }}:{{ vpn_gateway_port }}"
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# CLI-Only Solution with Hetzner S3-Compatible Storage
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This document describes the implementation of a CLI-only solution focused on Hetzner S3-compatible object storage using Hetzner Cloud Token authentication. The solution provides a command-line interface for managing storage operations without requiring explicit S3 credentials.
|
|
6
|
+
|
|
7
|
+
## Implementation Details
|
|
8
|
+
|
|
9
|
+
### 1. Core Components
|
|
10
|
+
|
|
11
|
+
#### Storage Manager Module
|
|
12
|
+
- **File**: `src/cli/storage.py`
|
|
13
|
+
- **Purpose**: Manages all S3-compatible storage operations
|
|
14
|
+
- **Features**:
|
|
15
|
+
- Bucket creation/deletion
|
|
16
|
+
- File upload/download functionality
|
|
17
|
+
- Object listing and deletion
|
|
18
|
+
- Configuration via environment variables
|
|
19
|
+
- Default Hetzner endpoint (`fsn1`)
|
|
20
|
+
|
|
21
|
+
#### Main CLI Entry Point
|
|
22
|
+
- **File**: `src/cli/main.py`
|
|
23
|
+
- **Purpose**: Command-line interface router
|
|
24
|
+
- **Commands**:
|
|
25
|
+
- `storage create-bucket`: Create a new storage bucket
|
|
26
|
+
- `storage list-buckets`: List all storage buckets
|
|
27
|
+
- `storage upload`: Upload a file to storage
|
|
28
|
+
- `storage download`: Download a file from storage
|
|
29
|
+
- `storage list-objects`: List objects in a bucket
|
|
30
|
+
- `storage delete-object`: Delete an object from storage
|
|
31
|
+
- `storage delete-bucket`: Delete a storage bucket
|
|
32
|
+
|
|
33
|
+
#### Configuration Manager
|
|
34
|
+
- **File**: `src/cli/config.py`
|
|
35
|
+
- **Purpose**: Manages CLI configuration
|
|
36
|
+
- **Features**:
|
|
37
|
+
- Loads configuration from file or defaults
|
|
38
|
+
- Supports environment variable overrides
|
|
39
|
+
- Manages configuration persistence
|
|
40
|
+
|
|
41
|
+
### 2. Storage Capabilities
|
|
42
|
+
|
|
43
|
+
The solution provides comprehensive storage management for Hetzner S3-compatible storage:
|
|
44
|
+
|
|
45
|
+
1. **Bucket Management**
|
|
46
|
+
- Create buckets
|
|
47
|
+
- List buckets
|
|
48
|
+
- Delete buckets
|
|
49
|
+
|
|
50
|
+
2. **Object Management**
|
|
51
|
+
- Upload files with custom object names
|
|
52
|
+
- Download files
|
|
53
|
+
- List objects in buckets
|
|
54
|
+
- Delete individual objects
|
|
55
|
+
|
|
56
|
+
3. **Authentication**
|
|
57
|
+
- Uses Hetzner Cloud Token for authentication
|
|
58
|
+
- Default endpoint: `https://fsn1.yourdomain.com`
|
|
59
|
+
- Temporary S3 credentials generated by Hetzner
|
|
60
|
+
|
|
61
|
+
### 3. Credential Management
|
|
62
|
+
|
|
63
|
+
#### Hetzner Cloud Token Authentication
|
|
64
|
+
- **Primary Authentication**: Uses Hetzner Cloud API token for authentication
|
|
65
|
+
- **Token Requirement**: Required for all operations
|
|
66
|
+
- **Security**: Tokens are never stored permanently, only used for authentication
|
|
67
|
+
|
|
68
|
+
#### S3 Credential Handling
|
|
69
|
+
- **Generation Process**: Hetzner generates temporary S3 credentials on demand
|
|
70
|
+
- **Exposure**: S3 credentials are displayed only once during generation
|
|
71
|
+
- **Security**: Generated credentials are handled by Hetzner's secure infrastructure
|
|
72
|
+
- **Replacement**: When credentials expire, new ones are generated automatically
|
|
73
|
+
|
|
74
|
+
### 4. Configuration
|
|
75
|
+
|
|
76
|
+
#### Environment Variables
|
|
77
|
+
```bash
|
|
78
|
+
export HZ_TOKEN="your-hetzner-cloud-token"
|
|
79
|
+
export S3_ENDPOINT_URL="https://fsn1.yourdomain.com" # Optional override
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
#### Configuration File (`config.yaml`)
|
|
83
|
+
```yaml
|
|
84
|
+
hz_token: "your-hetzner-cloud-token"
|
|
85
|
+
s3_endpoint_url: "https://fsn1.yourdomain.com"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### 5. Usage Examples
|
|
89
|
+
|
|
90
|
+
#### List Buckets
|
|
91
|
+
```bash
|
|
92
|
+
python3 src/cli/main.py storage list-buckets
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
#### Create Bucket
|
|
96
|
+
```bash
|
|
97
|
+
python3 src/cli/main.py storage create-bucket --name my-bucket
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
#### Upload File
|
|
101
|
+
```bash
|
|
102
|
+
python3 src/cli/main.py storage upload \
|
|
103
|
+
--bucket my-bucket \
|
|
104
|
+
--file ./local-file.txt \
|
|
105
|
+
--object-name remote-file.txt
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
#### Download File
|
|
109
|
+
```bash
|
|
110
|
+
python3 src/cli/main.py storage download \
|
|
111
|
+
--bucket my-bucket \
|
|
112
|
+
--object remote-file.txt \
|
|
113
|
+
--file ./downloaded-file.txt
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
#### List Objects
|
|
117
|
+
```bash
|
|
118
|
+
python3 src/cli/main.py storage list-objects \
|
|
119
|
+
--bucket my-bucket \
|
|
120
|
+
--prefix "subdirectory/"
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### 6. Benefits of This Approach
|
|
124
|
+
|
|
125
|
+
1. **Secure Authentication**: Uses Hetzner Cloud Token instead of static credentials
|
|
126
|
+
2. **Automatic Credential Management**: Hetzner generates temporary S3 credentials
|
|
127
|
+
3. **Standard Endpoint**: Defaults to `fsn1` region for Hetzner compatibility
|
|
128
|
+
4. **Minimal Dependencies**: Lightweight implementation focused on storage operations
|
|
129
|
+
5. **S3 Compatibility**: Works with Hetzner and other S3-compatible services
|
|
130
|
+
6. **Credential Security**: S3 credentials shown only once, never permanently stored
|
|
131
|
+
|
|
132
|
+
### 7. Integration with Existing Project
|
|
133
|
+
|
|
134
|
+
The solution maintains compatibility with the existing project structure:
|
|
135
|
+
- Uses the same package structure (`src/cli/`)
|
|
136
|
+
- Maintains existing configuration system
|
|
137
|
+
- Integrates seamlessly with existing dependencies
|
|
138
|
+
- Follows the same naming conventions
|
|
139
|
+
|
|
140
|
+
### 8. Security Features
|
|
141
|
+
|
|
142
|
+
1. **Token-Based Authentication**: Uses Hetzner Cloud tokens instead of permanent credentials
|
|
143
|
+
2. **Temporary Credentials**: S3 credentials are generated temporarily by Hetzner
|
|
144
|
+
3. **Secure Environment Variables**: Stores tokens in environment variables
|
|
145
|
+
4. **HTTPS Only**: All operations use secure connections
|
|
146
|
+
5. **Credential Masking**: Tokens displayed securely with masking
|
|
147
|
+
6. **Auto-Renewal**: Automatic credential renewal when needed
|
|
148
|
+
|
|
149
|
+
## Deployment
|
|
150
|
+
|
|
151
|
+
### Installation
|
|
152
|
+
```bash
|
|
153
|
+
pip install .
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Development Setup
|
|
157
|
+
```bash
|
|
158
|
+
# Install dependencies
|
|
159
|
+
pip install -r requirements.txt
|
|
160
|
+
|
|
161
|
+
# Run tests
|
|
162
|
+
python -m pytest tests/
|
|
163
|
+
|
|
164
|
+
# Run linter
|
|
165
|
+
pylint src/
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## License
|
|
169
|
+
|
|
170
|
+
MIT
|