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.
Files changed (92) hide show
  1. mystack_rig-1.0.0/.coveragerc +12 -0
  2. mystack_rig-1.0.0/.flake8 +5 -0
  3. mystack_rig-1.0.0/.github/workflows/release.yml +76 -0
  4. mystack_rig-1.0.0/.github/workflows/tests.yml +169 -0
  5. mystack_rig-1.0.0/.gitignore +105 -0
  6. mystack_rig-1.0.0/.rig/playbooks/README.md +45 -0
  7. mystack_rig-1.0.0/.rig/playbooks/vpn-gateway.yml +75 -0
  8. mystack_rig-1.0.0/CLI_STORAGE_SUMMARY.md +170 -0
  9. mystack_rig-1.0.0/Dockerfile +12 -0
  10. mystack_rig-1.0.0/GIT_CLONE_FEATURE.md +274 -0
  11. mystack_rig-1.0.0/HETZNER_STORAGE_GUIDE.md +168 -0
  12. mystack_rig-1.0.0/HETZNER_STORAGE_SECURITY_GUIDE.md +155 -0
  13. mystack_rig-1.0.0/LICENSE +21 -0
  14. mystack_rig-1.0.0/Makefile +55 -0
  15. mystack_rig-1.0.0/PKG-INFO +285 -0
  16. mystack_rig-1.0.0/README.md +262 -0
  17. mystack_rig-1.0.0/README_REAL_E2E.md +90 -0
  18. mystack_rig-1.0.0/STORAGE_CLI_GUIDE.md +155 -0
  19. mystack_rig-1.0.0/docs/architecture.md +84 -0
  20. mystack_rig-1.0.0/docs/architecture.png +0 -0
  21. mystack_rig-1.0.0/docs/architecture.svg +87 -0
  22. mystack_rig-1.0.0/forged-idea.md +155 -0
  23. mystack_rig-1.0.0/pyproject.toml +42 -0
  24. mystack_rig-1.0.0/pytest.ini +13 -0
  25. mystack_rig-1.0.0/requirements.txt +10 -0
  26. mystack_rig-1.0.0/sample.env +12 -0
  27. mystack_rig-1.0.0/scripts/coverage_badge.py +82 -0
  28. mystack_rig-1.0.0/scripts/pypi_version.py +118 -0
  29. mystack_rig-1.0.0/setup.cfg +4 -0
  30. mystack_rig-1.0.0/src/__init__.py +0 -0
  31. mystack_rig-1.0.0/src/cli/__init__.py +6 -0
  32. mystack_rig-1.0.0/src/cli/__main__.py +8 -0
  33. mystack_rig-1.0.0/src/cli/agents.py +575 -0
  34. mystack_rig-1.0.0/src/cli/config.py +264 -0
  35. mystack_rig-1.0.0/src/cli/encrypt.py +1182 -0
  36. mystack_rig-1.0.0/src/cli/git.py +317 -0
  37. mystack_rig-1.0.0/src/cli/last_env.py +64 -0
  38. mystack_rig-1.0.0/src/cli/last_session.py +171 -0
  39. mystack_rig-1.0.0/src/cli/log.py +105 -0
  40. mystack_rig-1.0.0/src/cli/main.py +1234 -0
  41. mystack_rig-1.0.0/src/cli/remote_dev.py +5547 -0
  42. mystack_rig-1.0.0/src/cli/requirements.txt +4 -0
  43. mystack_rig-1.0.0/src/cli/snapshots.py +627 -0
  44. mystack_rig-1.0.0/src/cli/storage.py +348 -0
  45. mystack_rig-1.0.0/src/cli/vault_keys.py +451 -0
  46. mystack_rig-1.0.0/src/cli/vpn.py +323 -0
  47. mystack_rig-1.0.0/src/mystack_rig.egg-info/PKG-INFO +285 -0
  48. mystack_rig-1.0.0/src/mystack_rig.egg-info/SOURCES.txt +90 -0
  49. mystack_rig-1.0.0/src/mystack_rig.egg-info/dependency_links.txt +1 -0
  50. mystack_rig-1.0.0/src/mystack_rig.egg-info/entry_points.txt +2 -0
  51. mystack_rig-1.0.0/src/mystack_rig.egg-info/requires.txt +8 -0
  52. mystack_rig-1.0.0/src/mystack_rig.egg-info/scm_file_list.json +86 -0
  53. mystack_rig-1.0.0/src/mystack_rig.egg-info/scm_version.json +8 -0
  54. mystack_rig-1.0.0/src/mystack_rig.egg-info/top_level.txt +1 -0
  55. mystack_rig-1.0.0/test_hetzner_integration.py +442 -0
  56. mystack_rig-1.0.0/tests/conftest.py +153 -0
  57. mystack_rig-1.0.0/tests/test_agents.py +208 -0
  58. mystack_rig-1.0.0/tests/test_ansible_galaxy.py +139 -0
  59. mystack_rig-1.0.0/tests/test_attach_command.py +456 -0
  60. mystack_rig-1.0.0/tests/test_credential_security.py +151 -0
  61. mystack_rig-1.0.0/tests/test_destroy_command.py +182 -0
  62. mystack_rig-1.0.0/tests/test_dev_command.py +461 -0
  63. mystack_rig-1.0.0/tests/test_encrypt_command.py +368 -0
  64. mystack_rig-1.0.0/tests/test_encrypt_coverage.py +201 -0
  65. mystack_rig-1.0.0/tests/test_firewall_client_ip.py +259 -0
  66. mystack_rig-1.0.0/tests/test_git_clone_unit.py +259 -0
  67. mystack_rig-1.0.0/tests/test_git_manager.py +95 -0
  68. mystack_rig-1.0.0/tests/test_gitignore_init.py +77 -0
  69. mystack_rig-1.0.0/tests/test_hetzner_storage.py +242 -0
  70. mystack_rig-1.0.0/tests/test_init_command.py +790 -0
  71. mystack_rig-1.0.0/tests/test_last_env.py +47 -0
  72. mystack_rig-1.0.0/tests/test_last_session.py +75 -0
  73. mystack_rig-1.0.0/tests/test_log.py +53 -0
  74. mystack_rig-1.0.0/tests/test_main_coverage.py +573 -0
  75. mystack_rig-1.0.0/tests/test_pypi_version.py +71 -0
  76. mystack_rig-1.0.0/tests/test_real_e2e_integration.py +244 -0
  77. mystack_rig-1.0.0/tests/test_real_e2e_storage.py +264 -0
  78. mystack_rig-1.0.0/tests/test_real_e2e_vpn.py +219 -0
  79. mystack_rig-1.0.0/tests/test_real_e2e_vpn_tunnel.py +210 -0
  80. mystack_rig-1.0.0/tests/test_remote_dev.py +476 -0
  81. mystack_rig-1.0.0/tests/test_remote_dev_coverage_extensions.py +2372 -0
  82. mystack_rig-1.0.0/tests/test_rig_ansible_vars.py +125 -0
  83. mystack_rig-1.0.0/tests/test_rig_config.py +110 -0
  84. mystack_rig-1.0.0/tests/test_setup_provision.py +745 -0
  85. mystack_rig-1.0.0/tests/test_simulation_storage.py +314 -0
  86. mystack_rig-1.0.0/tests/test_small_module_coverage.py +223 -0
  87. mystack_rig-1.0.0/tests/test_snapshots.py +705 -0
  88. mystack_rig-1.0.0/tests/test_storage_basic.py +679 -0
  89. mystack_rig-1.0.0/tests/test_vault_keys.py +144 -0
  90. mystack_rig-1.0.0/tests/test_vpn_optional.py +155 -0
  91. mystack_rig-1.0.0/tests/test_vpn_unit.py +188 -0
  92. mystack_rig-1.0.0/tests/test_workspace_git_clone.py +274 -0
@@ -0,0 +1,12 @@
1
+ [run]
2
+ source = src
3
+ omit =
4
+ # Entry trampoline only
5
+ src/cli/__main__.py
6
+
7
+ [report]
8
+ fail_under = 90
9
+ exclude_lines =
10
+ pragma: no cover
11
+ if __name__ == .__main__.:
12
+ if TYPE_CHECKING:
@@ -0,0 +1,5 @@
1
+ [flake8]
2
+ max-line-length = 120
3
+ extend-ignore = E501,W293,W291,E302,E305,E306,F401,F841,E128,E402,F541,E502
4
+ per-file-ignores =
5
+ tests/*: F401,F841,W293,E501,E306,E128,E302
@@ -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
@@ -0,0 +1,12 @@
1
+ FROM python:3.11-slim
2
+
3
+ WORKDIR /app
4
+
5
+ COPY requirements.txt setup.py README.md ./
6
+ COPY src ./src
7
+
8
+ RUN pip install --no-cache-dir -r requirements.txt \
9
+ && pip install --no-cache-dir .
10
+
11
+ ENTRYPOINT ["rig"]
12
+ CMD ["--help"]