mirror of
https://codeberg.org/icewind/haze.git
synced 2026-10-01 08:44:09 +02:00
Compare commits
No commits in common. "main" and "v2.4.0" have entirely different histories.
75 changed files with 723 additions and 1853 deletions
|
|
@ -1,61 +0,0 @@
|
||||||
name: "Book - PR"
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types:
|
|
||||||
- opened
|
|
||||||
- reopened
|
|
||||||
- synchronize
|
|
||||||
paths:
|
|
||||||
- ".forgejo/workflows/book-pr.yaml"
|
|
||||||
- "book/**"
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: page-preview
|
|
||||||
cancel-in-progress: false
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
createPreview:
|
|
||||||
runs-on: nix
|
|
||||||
env:
|
|
||||||
SITE_ORIGIN:
|
|
||||||
"https://${{ forge.event.repository.owner.username
|
|
||||||
}}.preview.codeberg.page"
|
|
||||||
SITE_BASEPATH:
|
|
||||||
"/${{ forge.event.repository.name }}@${{ forge.event.number }}/"
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: https://codeberg.org/icewind/attic-action@v1
|
|
||||||
with:
|
|
||||||
name: link
|
|
||||||
instance: https://cache.icewind.link
|
|
||||||
authToken: "${{ secrets.ATTIC_TOKEN }}"
|
|
||||||
- name: Build
|
|
||||||
run: |
|
|
||||||
# git-pages/action doesn't like symlinks
|
|
||||||
cp -r $(nix build .#haze-book --print-out-paths --no-link) dist
|
|
||||||
- name: "Deploy Site"
|
|
||||||
uses: https://code.forgejo.org/actions/git-pages@v2
|
|
||||||
with:
|
|
||||||
site: "${{ env.SITE_ORIGIN }}${{ env.SITE_BASEPATH }}" #
|
|
||||||
token: "${{ forge.token }}" #
|
|
||||||
source: "dist/" #
|
|
||||||
- name: "Find comment"
|
|
||||||
uses: "https://github.com/peter-evans/find-comment@v4" #
|
|
||||||
id: comment
|
|
||||||
with:
|
|
||||||
issue-number: ${{ forge.event.number }} #
|
|
||||||
body-includes: "<!-- preview-comment -->" #
|
|
||||||
- name: "Create or update comment"
|
|
||||||
uses: "https://github.com/peter-evans/create-or-update-comment@v5" #
|
|
||||||
with:
|
|
||||||
issue-number: ${{ forge.event.number }} #
|
|
||||||
comment-id: "${{ steps.comment.outputs.comment-id }}" #
|
|
||||||
edit-mode: "replace"
|
|
||||||
body: |-
|
|
||||||
# Pull request preview ready!
|
|
||||||
|
|
||||||
The Preview of commit ${{ forge.event.pull_request.head.sha }} has been created.
|
|
||||||
You can find it here: ${{ env.SITE_ORIGIN }}${{ env.SITE_BASEPATH }}
|
|
||||||
|
|
||||||
<!-- preview-comment -->
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
name: "Book"
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
preview:
|
|
||||||
runs-on: nix
|
|
||||||
env:
|
|
||||||
SITE_ORIGIN: "${{forge.event.repository.owner.username}}.codeberg.page"
|
|
||||||
SITE_BASEPATH: "/${{forge.event.repository.name}}/"
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: https://codeberg.org/icewind/attic-action@v1
|
|
||||||
with:
|
|
||||||
name: link
|
|
||||||
instance: https://cache.icewind.link
|
|
||||||
authToken: "${{ secrets.ATTIC_TOKEN }}"
|
|
||||||
- name: Build
|
|
||||||
run: |
|
|
||||||
# git-pages/action doesn't like symlinks
|
|
||||||
cp -r $(nix build .#haze-book --print-out-paths --no-link) dist
|
|
||||||
- uses: https://codeberg.org/git-pages/action@v2
|
|
||||||
with:
|
|
||||||
site: https://${{ env.SITE_ORIGIN }}${{ env.SITE_BASEPATH }}
|
|
||||||
token: ${{ forge.token }}
|
|
||||||
source: dist/
|
|
||||||
|
|
@ -4,11 +4,13 @@ on:
|
||||||
push:
|
push:
|
||||||
branches: ["main"]
|
branches: ["main"]
|
||||||
paths:
|
paths:
|
||||||
- "flake.*"
|
|
||||||
- "Cargo.toml"
|
- "Cargo.toml"
|
||||||
- ".forgejo/workflows/docker.yaml"
|
- ".forgejo/workflows/docker.yaml"
|
||||||
- "nix/image/**"
|
- "nix/image/**"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-images:
|
build-images:
|
||||||
runs-on: nix
|
runs-on: nix
|
||||||
|
|
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -2,5 +2,3 @@
|
||||||
.direnv
|
.direnv
|
||||||
.env
|
.env
|
||||||
result
|
result
|
||||||
.vale/*
|
|
||||||
!.vale/config
|
|
||||||
|
|
|
||||||
13
.vale.ini
13
.vale.ini
|
|
@ -1,13 +0,0 @@
|
||||||
StylesPath = .vale
|
|
||||||
MinAlertLevel = suggestion
|
|
||||||
Vocab = haze
|
|
||||||
|
|
||||||
[formats]
|
|
||||||
mdx = md
|
|
||||||
|
|
||||||
[*]
|
|
||||||
BasedOnStyles = Vale
|
|
||||||
|
|
||||||
[*.{md,mdx}]
|
|
||||||
BasedOnStyles = Vale
|
|
||||||
Microsoft.Contractions = NO
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
Nextcloud
|
|
||||||
ownCloud
|
|
||||||
appstore
|
|
||||||
Authentik
|
|
||||||
Caddy
|
|
||||||
cron
|
|
||||||
FrankenPHP
|
|
||||||
PHPUnit
|
|
||||||
mdBook
|
|
||||||
boolean
|
|
||||||
Codeberg
|
|
||||||
Kaspersky
|
|
||||||
Redis
|
|
||||||
Podman
|
|
||||||
Xdebug
|
|
||||||
occ
|
|
||||||
dnsmasq
|
|
||||||
notify_push
|
|
||||||
sharding
|
|
||||||
tokio
|
|
||||||
worktree
|
|
||||||
worktrees
|
|
||||||
config
|
|
||||||
stdin
|
|
||||||
stdout
|
|
||||||
direnv
|
|
||||||
44
CHANGELOG.md
44
CHANGELOG.md
|
|
@ -1,51 +1,37 @@
|
||||||
## 2.4.2
|
|
||||||
|
|
||||||
- Support having a `#!` inside a haze script to specify the interpreter to use
|
|
||||||
- Add WebUI for accessing the database.
|
|
||||||
- Allow opening services with `haze open [service]`
|
|
||||||
|
|
||||||
## 2.4.1
|
|
||||||
|
|
||||||
- Show database location on start
|
|
||||||
- Direct TLS listening support for the proxy
|
|
||||||
- Set `SERVER_NAME` for FrankenPHP
|
|
||||||
- Expose Caddy admin endpoints when using FrankenPHP
|
|
||||||
- Use cron for background jobs
|
|
||||||
|
|
||||||
## 2.4.0
|
## 2.4.0
|
||||||
|
|
||||||
- Show instance details in shell prompt
|
- Show instance details in shell prompt
|
||||||
- Add option to start an instance with a detached worktree
|
- Add option to start an instance with a detached worktree
|
||||||
- Add "haze scripts" that bundle a setup configuration and script to run in the
|
- Add "haze scripts" that bundle a setup configuration and script to run in the
|
||||||
instance
|
instance
|
||||||
- Add option to specify PHPUnit version when running tests
|
- Allow specifying phpunit version when running tests
|
||||||
- Add SAML service using Authentik
|
- Add SAML service using authentik
|
||||||
- Add OIDC service using Authentik
|
- Add OIDC service using authentik
|
||||||
- Add SCIM service using Authentik
|
- Add SCIM service using authentik
|
||||||
- Fix using a single word as instance name
|
- Fix using a single word as instance name
|
||||||
- Fixed cleanup not removing some cache files
|
- Fixed cleanup not removing some cache files
|
||||||
- Remove memory and CPU limits from containers
|
- Remove memory and cpu limits from containers
|
||||||
- Fix max upload size not being set correctly
|
- Fix max upload size not being set correctly
|
||||||
|
|
||||||
## 2.3.0
|
## 2.3.0
|
||||||
|
|
||||||
- Add option to `exec` in service containers
|
- Allow execing into service containers
|
||||||
- Add SFTP with key authentication service
|
- Add sftp with key authentication service
|
||||||
- Fix MySQL 8 support
|
- Fix mysql 8 support
|
||||||
|
|
||||||
## 2.2.2
|
## 2.2.2
|
||||||
|
|
||||||
- parallelize `git pull`
|
- parallelize `git pull`
|
||||||
- add webhook tester
|
- add webhook tester
|
||||||
- automatically configure LDAP when enabled
|
- automatically configure ldap when enabled
|
||||||
- improve compatibility with integration tests
|
- improve compatibility with intergration tests
|
||||||
- add `php-imagick` module
|
- add `php-imagick` module
|
||||||
|
|
||||||
## 2.1.1
|
## 2.1.1
|
||||||
|
|
||||||
- Add basic [FrankenPHP](https://github.com/php/frankenphp) support
|
- Add basic [frankenphp](https://github.com/php/frankenphp) support
|
||||||
- Allow running integration tests from (some) apps
|
- Allow running integration tests from (some) apps
|
||||||
- Support federation with proxy and using 127.0.0.1 as proxy IP
|
- Support federation with proxy and using 127.0.0.1 as proxy ip
|
||||||
- Fix office not working
|
- Fix office not working
|
||||||
- Warn when using out-of-date images.
|
- Warn when using out-of-date images.
|
||||||
|
|
||||||
|
|
@ -55,8 +41,8 @@
|
||||||
- Faster stopping of instances, by @provokateurin
|
- Faster stopping of instances, by @provokateurin
|
||||||
- Support extra app directories
|
- Support extra app directories
|
||||||
- Enable appstore
|
- Enable appstore
|
||||||
- Allow setting configuration options pre-setup
|
- Allow setting config options pre-setup
|
||||||
- Improved access to service containers
|
- Improved access to service containers
|
||||||
- Add S3 with TLS options
|
- Add S3 with TLS options
|
||||||
- Updated PHP 8.0 and 8.1 images
|
- Updated php 8.0 and 8.1 images
|
||||||
- Add PHP 8.5 support
|
- Add php 8.5 support
|
||||||
|
|
|
||||||
4
Cargo.lock
generated
4
Cargo.lock
generated
|
|
@ -899,7 +899,7 @@ checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "haze"
|
name = "haze"
|
||||||
version = "2.4.2"
|
version = "2.4.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"atty",
|
"atty",
|
||||||
|
|
@ -933,7 +933,6 @@ dependencies = [
|
||||||
"tar",
|
"tar",
|
||||||
"termion",
|
"termion",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-rustls",
|
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
"toml",
|
"toml",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
|
@ -2017,7 +2016,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06"
|
checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aws-lc-rs",
|
"aws-lc-rs",
|
||||||
"log",
|
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"rustls-webpki",
|
"rustls-webpki",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "haze"
|
name = "haze"
|
||||||
version = "2.4.2"
|
version = "2.4.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
description = "Easy setup and management of Nextcloud test instances using docker"
|
description = "Easy setup and management of Nextcloud test instances using docker"
|
||||||
repository = "https://codeberg.org/icewind/haze"
|
repository = "https://codeberg.org/icewind/haze"
|
||||||
|
|
@ -44,7 +44,6 @@ tokio = { version = "1.53.1", features = [
|
||||||
"rt-multi-thread",
|
"rt-multi-thread",
|
||||||
"signal"
|
"signal"
|
||||||
] }
|
] }
|
||||||
tokio-rustls = "0.26"
|
|
||||||
tokio-stream = { version = "0.1.19", features = ["net"] }
|
tokio-stream = { version = "0.1.19", features = ["net"] }
|
||||||
toml = "1.1.4"
|
toml = "1.1.4"
|
||||||
tracing = "0.1.44"
|
tracing = "0.1.44"
|
||||||
|
|
|
||||||
|
|
@ -1,80 +0,0 @@
|
||||||
# Developing
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
### Nix based development environment
|
|
||||||
|
|
||||||
The recommended way to setup the development environment is to use
|
|
||||||
[Lix](https://lix.systems/install/)/[Nix](https://nixos.org/download/).
|
|
||||||
|
|
||||||
Once installed, you can enter the development shell using `nix develop` or,
|
|
||||||
setup [direnv](https://direnv.net/) to automatically enter the development shell
|
|
||||||
when you enter the folder.
|
|
||||||
|
|
||||||
The development shell provides all necessary tooling to develop haze.
|
|
||||||
|
|
||||||
### Without Nix
|
|
||||||
|
|
||||||
The fooling tools need to be setup to develop without using Nix to manage the
|
|
||||||
development setup:
|
|
||||||
|
|
||||||
- [`cargo`](https://doc.rust-lang.org/cargo/getting-started/installation.html) -
|
|
||||||
For building the program
|
|
||||||
- [`mdbook`](https://github.com/rust-lang/mdBook) - For building the
|
|
||||||
documentation
|
|
||||||
- [`prettier`](https://prettier.io/) - For formatting the non-rust files
|
|
||||||
- [`vale`](https://vale.sh/) - For checking documentation
|
|
||||||
|
|
||||||
## Building
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cargo build
|
|
||||||
```
|
|
||||||
|
|
||||||
The binary can then be found at `target/debug/haze`.
|
|
||||||
|
|
||||||
## Formatting
|
|
||||||
|
|
||||||
### With Nix
|
|
||||||
|
|
||||||
```bash
|
|
||||||
nix fmt
|
|
||||||
```
|
|
||||||
|
|
||||||
### Without Nix
|
|
||||||
|
|
||||||
#### Rust code
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cargo fmt
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Documentation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
prettier --write '**/*.md'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Building documentation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mdbook serve
|
|
||||||
```
|
|
||||||
|
|
||||||
### Checking documentation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
vale src/ book/src/ book/README.md *.md
|
|
||||||
```
|
|
||||||
|
|
||||||
### Run all checks
|
|
||||||
|
|
||||||
```bash
|
|
||||||
nix flake check
|
|
||||||
```
|
|
||||||
|
|
||||||
### Building docker images
|
|
||||||
|
|
||||||
```bash
|
|
||||||
nix run .#'"haze-image-php-8.4"'.copyToDockerDaemon
|
|
||||||
```
|
|
||||||
468
README.md
468
README.md
|
|
@ -7,35 +7,473 @@ Easy setup and management of Nextcloud test instances using docker
|
||||||
## What
|
## What
|
||||||
|
|
||||||
`haze` provides an easy way to set up Nextcloud test instances with a choice of
|
`haze` provides an easy way to set up Nextcloud test instances with a choice of
|
||||||
PHP version, database server, optional s3 or LDAP setup and more.
|
php version, database server, optional s3 or ldap setup and more.
|
||||||
|
|
||||||
## Documentation
|
## Setup
|
||||||
|
|
||||||
Documentation for haze can be found in the
|
### Requirements
|
||||||
[book](https://icewind.codeberg.page/haze/)
|
|
||||||
|
|
||||||
## Quickstart
|
- Docker
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
- Grab a binary from the
|
- Grab a binary from the
|
||||||
[Codeberg releases](https://codeberg.org/icewind/haze/releases) and place it
|
[Codeberg releases](https://codeberg.org/icewind/haze/releases) and place it
|
||||||
in your `$PATH`
|
in your `$PATH`
|
||||||
|
|
||||||
- Create a file `~/.config/haze/haze.toml` with the following content:
|
### Config
|
||||||
|
|
||||||
```toml
|
Create a file `~/.config/haze/haze.toml` with the following options:
|
||||||
sources_root = "/path/to/nextcloud/sources"
|
|
||||||
```
|
|
||||||
|
|
||||||
- Start a basic Nextcloud instance:
|
```toml
|
||||||
|
sources_root = "/path/to/nextcloud/sources"
|
||||||
|
```
|
||||||
|
|
||||||
|
See the [configuration section](#configuration) for more options.
|
||||||
|
|
||||||
|
### Quick examples
|
||||||
|
|
||||||
|
- Start a Nextcloud instance with `postgresql`, and `s3` primary storage:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
haze start
|
haze start pgsql s3
|
||||||
```
|
```
|
||||||
|
|
||||||
- Navigate to the address that is provided in the output.
|
- Start a Nextcloud instance with `sqlite`, `php 8.3` and an `smb` external
|
||||||
|
storage:
|
||||||
|
|
||||||
See the [book](https://icewind.codeberg.page/haze/) for more details.
|
```bash
|
||||||
|
haze start 8.3 smb
|
||||||
|
```
|
||||||
|
|
||||||
## Developing
|
- Run specific units test against an `oracle` database
|
||||||
|
```bash
|
||||||
|
haze test oracle apps/dav/tests/unit/Connector/Sabre
|
||||||
|
```
|
||||||
|
|
||||||
See [DEVELOPING.md](./DEVELOPING.md) for information on how to work on `haze`.
|
## Managing instances
|
||||||
|
|
||||||
|
#### Start an instance
|
||||||
|
|
||||||
|
```bash
|
||||||
|
haze start [--name <name>] [--detach] [database] [php-version] [services] [vX.Y.Z]
|
||||||
|
```
|
||||||
|
|
||||||
|
Where `database` is one of `sqlite`, `mysql`, `mariadb`, `pgsql` or `oracle`
|
||||||
|
with an optional version (e.g. `pgsql:12`), defaults to `sqlite`. And
|
||||||
|
`php-version` is one of `8.0`, `8.1`, `8.2`, `8.3`, `8.4` or `8.5`, defaults to
|
||||||
|
the maximum version support by the current Nextcloud version.
|
||||||
|
|
||||||
|
You can specify a version number (e.g. `v32.0.2`) to use the sources from a
|
||||||
|
release instead of using the local sources.
|
||||||
|
|
||||||
|
Use `--name <name>` to give the instance a specific name instead of a randomly
|
||||||
|
generated one. For example:
|
||||||
|
|
||||||
|
Use `--detach` to give the instance
|
||||||
|
[its own sources](#instances-with-their-own-sources) instead of sharing
|
||||||
|
`sources_root` with all other instances.
|
||||||
|
|
||||||
|
Additionally, you can use the following options when starting an instance:
|
||||||
|
|
||||||
|
- `s3`: set up an S3 server and configure to Nextcloud to use it as primary
|
||||||
|
storage.
|
||||||
|
- `s3s`: enable TLS for the S3 setup.
|
||||||
|
- `s3mb`: enable multi-bucket S3 setup.
|
||||||
|
- `s3m`: enable multi-instance S3 setup.
|
||||||
|
- `ldap`: set up an LDAP server.
|
||||||
|
- `saml`: set up authentik as a SAML IDP.
|
||||||
|
- `oidc`: set up authentik as an OIDC IDP.
|
||||||
|
- `scim`: set up authentik as a SCIM server.
|
||||||
|
- `office`: set up a Nextcloud Office server.
|
||||||
|
- `onlyoffice` setup an onlyoffice document server.
|
||||||
|
- `push` set up [client push](https://github.com/nextcloud/notify_push).
|
||||||
|
- `smb`: set up a samba server for external storage use.
|
||||||
|
- `dav`: set up a WebDAV server for external storage use.
|
||||||
|
- `sftp`: set up a SFTP server for external storage use.
|
||||||
|
- `sftp-key`: set up a SFTP server for external storage use with public key
|
||||||
|
authentication.
|
||||||
|
- `kaspersky`: set up a kaspersky scan engine server in http mode. ( Requires
|
||||||
|
[manually setting up the image](https://github.com/icewind1991/kaspersky-docker))
|
||||||
|
- `kaspersky-icap`: setup a kaspersky scan engine server in ICAP mode.
|
||||||
|
- `clamav`: set up a local clam av scanner in executable mode.
|
||||||
|
- `clamav-socket`: set up a clam av scanner in socket mode.
|
||||||
|
- `clamav-icap`: set up a clam av scanner in ICAP mode.
|
||||||
|
- `clamav-icap-tls`: set up a clam av scanner in ICAP mode with TLS encryption.
|
||||||
|
- `oc`: start an ownCloud instance in the same network.
|
||||||
|
- `imaginary`: start an Imaginary service and configure it for preview
|
||||||
|
generation.
|
||||||
|
- `mail`: start a [smtp4dev](https://github.com/rnwood/smtp4dev) server and
|
||||||
|
configure it the mail server.
|
||||||
|
- `webhook` start a
|
||||||
|
[webhook tester](https://github.com/tarampampam/webhook-tester)
|
||||||
|
- `redis`: start a separate container for redis.
|
||||||
|
- `redis-tls`: connect to redis over TLS.
|
||||||
|
- `<path to app.tar.gz>`: by specifying the path to an app package this package
|
||||||
|
will be extracted into the apps. directory of the new instance (overwriting
|
||||||
|
any existing app code). This can be used to quickly test a packaged app.
|
||||||
|
- The name of any configured preset.
|
||||||
|
|
||||||
|
#### Run tests in a new instance
|
||||||
|
|
||||||
|
```bash
|
||||||
|
haze test [database] [php-version] [phpunit version] [path]
|
||||||
|
```
|
||||||
|
|
||||||
|
Where `path` is a file or folder to run PHPUnit in, relative to the sources
|
||||||
|
root.
|
||||||
|
|
||||||
|
### List running instances
|
||||||
|
|
||||||
|
```bash
|
||||||
|
haze
|
||||||
|
```
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
```bash
|
||||||
|
haze list
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Remove all running instances
|
||||||
|
|
||||||
|
```bash
|
||||||
|
haze clean
|
||||||
|
```
|
||||||
|
|
||||||
|
### Instances with their own sources
|
||||||
|
|
||||||
|
By default every instance shares the sources configured as `sources_root`, so
|
||||||
|
all instances always run the same code. An instance started with `--detach` gets
|
||||||
|
its own `git worktree` of `sources_root` instead, created in `worktree_dir`,
|
||||||
|
which lets you run several instances on different branches at the same time.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
haze start --name my-fix --detach
|
||||||
|
```
|
||||||
|
|
||||||
|
The worktree is checked out with a detached head. Every app in one of the
|
||||||
|
`app_directories` that gets enabled during setup receives its own worktree as
|
||||||
|
well, other apps keep running from the shared app directory.
|
||||||
|
|
||||||
|
The worktrees are removed together with the instance, by `haze stop` or
|
||||||
|
`haze clean`.
|
||||||
|
|
||||||
|
## Controlling running instances
|
||||||
|
|
||||||
|
The following commands run against the most recently started instance and allow
|
||||||
|
optionally providing a `match` to select a specific instance by its name.
|
||||||
|
|
||||||
|
#### Open an instance
|
||||||
|
|
||||||
|
```bash
|
||||||
|
haze [match] open
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Open the database of an instance
|
||||||
|
|
||||||
|
```bash
|
||||||
|
haze [match] db
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Execute a command on an instance
|
||||||
|
|
||||||
|
```bash
|
||||||
|
haze [match] [service] [cmd]
|
||||||
|
```
|
||||||
|
|
||||||
|
If no `cmd` is specified it will launch `bash`
|
||||||
|
|
||||||
|
If a service name or `db` is provided, the command will be in the container of
|
||||||
|
the service or database.
|
||||||
|
|
||||||
|
#### Create a new instance and run a command
|
||||||
|
|
||||||
|
```bash
|
||||||
|
haze [match] shell [cmd]
|
||||||
|
```
|
||||||
|
|
||||||
|
If no `cmd` is specified it will launch `bash`
|
||||||
|
|
||||||
|
#### Execute an occ command on an instance
|
||||||
|
|
||||||
|
```bash
|
||||||
|
haze [match] occ [cmd]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Connect to the database on an instance
|
||||||
|
|
||||||
|
```bash
|
||||||
|
haze [match] db
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Show the logs of an instance
|
||||||
|
|
||||||
|
```bash
|
||||||
|
haze [match] logs
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Stop an instance
|
||||||
|
|
||||||
|
```bash
|
||||||
|
haze [match] stop
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Pin an instance
|
||||||
|
|
||||||
|
```bash
|
||||||
|
haze [match] pin
|
||||||
|
```
|
||||||
|
|
||||||
|
Pinned instances will not be removed by `haze clean`.
|
||||||
|
|
||||||
|
#### Unpin an instance
|
||||||
|
|
||||||
|
```bash
|
||||||
|
haze [match] unpin
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Run a command with instance environment variables set
|
||||||
|
|
||||||
|
```bash
|
||||||
|
haze [match] env <cmd> [args]
|
||||||
|
```
|
||||||
|
|
||||||
|
Runs the provided command with `NEXTCLOUD_URL`, `DATABASE_URL` and `REDIS_URL`
|
||||||
|
environment variables set for the matched instance.
|
||||||
|
|
||||||
|
This is intended to run a local
|
||||||
|
[push daemon](https://github.com/nextcloud/notify_push) against an instance.
|
||||||
|
|
||||||
|
#### Update the container images
|
||||||
|
|
||||||
|
```bash
|
||||||
|
haze update
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Edit a file in an instance with the local $EDITOR
|
||||||
|
|
||||||
|
```bash
|
||||||
|
haze [match] edit <path>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Reload the php config of an instance
|
||||||
|
|
||||||
|
```bash
|
||||||
|
haze [match] reload
|
||||||
|
```
|
||||||
|
|
||||||
|
The php configuration can edit changed with `haze edit /config/php.ini`
|
||||||
|
|
||||||
|
#### Checkout a branch for all local apps
|
||||||
|
|
||||||
|
```bash
|
||||||
|
haze git checkout [branch]
|
||||||
|
```
|
||||||
|
|
||||||
|
Checks out the branch in all git repositories within the apps folder.
|
||||||
|
|
||||||
|
Defaults to the branch matching the current checked out server versions (e.g.
|
||||||
|
`master` or `stable33`).
|
||||||
|
|
||||||
|
`master` and `main` can be used interchangeably.
|
||||||
|
|
||||||
|
#### Pull remote changes for all local apps
|
||||||
|
|
||||||
|
```bash
|
||||||
|
haze git pull
|
||||||
|
```
|
||||||
|
|
||||||
|
Performs a pull in all git repositories within the apps folder.
|
||||||
|
|
||||||
|
## Federation
|
||||||
|
|
||||||
|
Multiple instances can reach each other by using their instance name as domain
|
||||||
|
name to allow for testing federation between instances. Alternatively, you can
|
||||||
|
set up the haze proxy and the proxied domains to get https support between
|
||||||
|
instances.
|
||||||
|
|
||||||
|
## Proxy
|
||||||
|
|
||||||
|
By default, instances can be accessed by their IP. In order to get more
|
||||||
|
memorable URLs and allow supporting https, haze comes with a builtin reverse
|
||||||
|
proxy to allow using a wildcard domain.
|
||||||
|
|
||||||
|
### Requirements
|
||||||
|
|
||||||
|
- A domain name you can set wildcard DNS records for
|
||||||
|
- A reverse proxy like Nginx or Apache
|
||||||
|
- (optionally) a wildcard ssl certificate (can be acquiring using letsencrypt
|
||||||
|
and dns verification)
|
||||||
|
|
||||||
|
### DNS Setup
|
||||||
|
|
||||||
|
- Set a DNS record for `*.haze.example.com` and `haze.example.com` pointing to
|
||||||
|
your development machine.
|
||||||
|
- Set the `proxy` configuration with your domain and desired listen endpoint.
|
||||||
|
- Set up a service to run `haze proxy` in the background as your own user. A
|
||||||
|
systemd user service is recommended (see [haze.service](./haze.service) for an
|
||||||
|
example).
|
||||||
|
- Configure your reverse proxy of choice to proxy `*.haze.example.com` and
|
||||||
|
`haze.example.com` to the proxy's listen endpoint
|
||||||
|
- (optional) acquire a wildcard ssl certificate for your domain and set your
|
||||||
|
reverse proxy to use it. This will be highly dependent on your DNS provider,
|
||||||
|
[this](https://community.letsencrypt.org/t/dns-providers-who-easily-integrate-with-lets-encrypt-dns-validation/86438)
|
||||||
|
lists some DNS providers and supported ACME clients.
|
||||||
|
|
||||||
|
### Local Setup
|
||||||
|
|
||||||
|
- Setup `dnsmasq` to resolve `*.haze.test` to your development machine.
|
||||||
|
- Generate a wildcard ssl certificate for `*.haze.test` using `mkcert`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate local wildcard certificate
|
||||||
|
mkcert -cert-file <path-to-your-certificats>haze.test.crt -key-file <path-to-your-certificats>haze.test.key '*.haze.test'
|
||||||
|
```
|
||||||
|
|
||||||
|
- Set up a service to run `haze proxy` in the background as your own user. A
|
||||||
|
systemd user service is recommended (see [haze.service](./haze.service) for an
|
||||||
|
example).
|
||||||
|
- Setup a reverse proxy to proxy `*.haze.test` and `haze.test` to the
|
||||||
|
`haze proxy`'s socket. Example for Nginx:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
upstream haze-handler {
|
||||||
|
server unix:/run/haze/haze.sock;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
listen 443 ssl;
|
||||||
|
http2 on;
|
||||||
|
server_name *.haze.test;
|
||||||
|
|
||||||
|
ssl_certificate <path-to-your-certificats>/haze.test.crt;
|
||||||
|
ssl_certificate_key <path-to-your-certificats>/haze.test.key;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://haze-handler;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
When the proxy is configured, generated URLs for the instances will use a
|
||||||
|
subdomain of the configured domain, e.g. the `rolling-bees` instance will be
|
||||||
|
available at `rolling-bees.haze.example.com`. Additionally, `haze.example.com`
|
||||||
|
will automatically point to the last created instance.
|
||||||
|
|
||||||
|
Additionally, the proxy allows access to the server containers trough either
|
||||||
|
`<instance id>-<service id>.haze.example.com` for a specific instance, or
|
||||||
|
`<service-id>.haze.example.com` for the last created instance. For example
|
||||||
|
`rolling-bees-mail.haze.example.com` will give access to the smtp4dev web
|
||||||
|
interface of the `rolling-bees` instance.
|
||||||
|
|
||||||
|
## Haze scripts
|
||||||
|
|
||||||
|
Haze scripts combine a set of instance options and a script to run in the
|
||||||
|
instance.
|
||||||
|
|
||||||
|
Haze scripts are intended to way to create automated ways of running more
|
||||||
|
complex tests are setting up more complex instances.
|
||||||
|
|
||||||
|
A script contains of 3 paths
|
||||||
|
|
||||||
|
1. An optional shebang line setting `haze` as the interpreter, e.g.
|
||||||
|
`#! /usr/bin/env -S haze script`
|
||||||
|
2. A shebang line setting the options for the instance creation as the
|
||||||
|
interpreter, e.g. `#! haze shell pgsql s3`
|
||||||
|
3. The rest that is ran as a script inside the created instance.
|
||||||
|
|
||||||
|
The first sheband is set, the script can be ran directly. Else it needs to be
|
||||||
|
run with `haze script [path-to-script]`.
|
||||||
|
|
||||||
|
For example, the following script will create an instance with `postgresql` and
|
||||||
|
`s3` primary storage. Then creates a new file, gets the file id, read the object
|
||||||
|
of the file from S3, validate that it contains the expected contents, and
|
||||||
|
cleanup the instance.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#! #! /usr/bin/env -S haze script
|
||||||
|
#! haze shell pgsql s3
|
||||||
|
|
||||||
|
echo 'test' | occ file:put '-' /admin/files/test.txt
|
||||||
|
FILE_ID=$(occ info:file /admin/files/test.txt | grep fileid: | grep -oE '[0-9]+')
|
||||||
|
OBJECT_CONTENTS=$(occ file:object:get urn:oid:$FILE_ID -)
|
||||||
|
if [ "$OBJECT_CONTENTS" == 'test' ]; then
|
||||||
|
echo "object contains expected contents"
|
||||||
|
else
|
||||||
|
echo "object does not contains expected contents!"
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
### Script modes
|
||||||
|
|
||||||
|
Scripts can be either `shell` or `start` scripts.
|
||||||
|
|
||||||
|
`shell` scripts will automatically remove the created instance once the script
|
||||||
|
is done, just like `haze shell` will. The intended use case for this is
|
||||||
|
performing some tests in the created instance without leaving state behind.
|
||||||
|
|
||||||
|
`start` scripts on the other hand will keep the instance, like `haze start`
|
||||||
|
will. The intended use case for this is creating more complex instances without
|
||||||
|
having to configure a dedicated preset.
|
||||||
|
|
||||||
|
The script mode is determined based on the shebang line. The mode set in a
|
||||||
|
script can be overriden by running the script with
|
||||||
|
`haze script [shell|start] path-to-script.sh`.
|
||||||
|
|
||||||
|
## Nushell scripts
|
||||||
|
|
||||||
|
By default, the script contents are executed as a `bash` script, you can instead
|
||||||
|
execute the script as a `nushell` script by using `.nu` as the file extention.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Configuration is loaded from `~/.config/haze/haze.toml` and has the following
|
||||||
|
options
|
||||||
|
|
||||||
|
```toml
|
||||||
|
sources_root = "/path/to/sources" # path of the nextcloud sources. required
|
||||||
|
app_directories = ["/path/to/sources/more_app"] # paths to additional app directories.
|
||||||
|
work_dir = "/path/to/temp/dir" # path to temporary directory. optional, defaults to "/tmp/haze"
|
||||||
|
worktree_dir = "/path/to/worktrees" # where to create the worktrees of detached instances. optional, defaults to "<work_dir>/worktrees"
|
||||||
|
|
||||||
|
[auto_setup] # optional
|
||||||
|
enabled = false # whether or not to automatically install nextcloud on `haze start`. enabled by default
|
||||||
|
username = "foo" # username for admin user during auto setup. optional, defaults to "admin"
|
||||||
|
password = "bar" # password for admin user during auto setup. optional, defaults to "admin"
|
||||||
|
enable_apps = ["files_external"] # apps to enable after setup, defaults to []
|
||||||
|
disable_apps = ["contacts"] # apps to disable after setup, defaults to []
|
||||||
|
post_setup = [# commands to execute after setup, defaults to []
|
||||||
|
"occ group:add test",
|
||||||
|
]
|
||||||
|
config = { "foo" = "bar" } # configuration options to set before install
|
||||||
|
|
||||||
|
[[volume]] # optional
|
||||||
|
source = "/tmp/haze-shared"
|
||||||
|
target = "/shared"
|
||||||
|
create = true
|
||||||
|
|
||||||
|
[[volume]]
|
||||||
|
source = "/home/me/Downloads"
|
||||||
|
target = "/Downloads"
|
||||||
|
read_only = true
|
||||||
|
|
||||||
|
[proxy] # optional
|
||||||
|
address = "haze.example.com" # base domain
|
||||||
|
https = true # Is the proxy behind a https terminating proxy
|
||||||
|
listen = "/run/haze/haze.sock" # either a unix socket path
|
||||||
|
#listen = "127.0.0.1:8080" # or a socket address
|
||||||
|
|
||||||
|
# presets allow for easy usage of commonly used setups
|
||||||
|
[[preset]]
|
||||||
|
name = "groupfolders" # name of the preset
|
||||||
|
apps = ["groupfolders"] # app to enable
|
||||||
|
commands = ["occ groupfolders:create gf", "occ groupfolders:group 1 admin read write share delete"] # commands to run post-setup
|
||||||
|
```
|
||||||
|
|
|
||||||
1
book/.gitignore
vendored
1
book/.gitignore
vendored
|
|
@ -1 +0,0 @@
|
||||||
book
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
## Build requirements
|
|
||||||
|
|
||||||
- [mdBook](https://github.com/rust-lang/mdBook)
|
|
||||||
|
|
||||||
## Development
|
|
||||||
|
|
||||||
```
|
|
||||||
mdbook serve
|
|
||||||
```
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
[book]
|
|
||||||
title = "Haze"
|
|
||||||
authors = ["Robin Appelman"]
|
|
||||||
language = "en"
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
# Haze
|
|
||||||
|
|
||||||
Hazy with a chance of clouds.
|
|
||||||
|
|
||||||
`haze` is a tool that provides an easy way to set up Nextcloud test instances
|
|
||||||
with a choice of PHP version, database server, optional s3 or LDAP setup and
|
|
||||||
much more.
|
|
||||||
|
|
||||||
## Quickstart
|
|
||||||
|
|
||||||
- Grab a binary from the
|
|
||||||
[Codeberg releases](https://codeberg.org/icewind/haze/releases) and place it
|
|
||||||
in your `$PATH`
|
|
||||||
|
|
||||||
- Create a file `~/.config/haze/haze.toml` with the following content:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
sources_root = "/path/to/nextcloud/sources"
|
|
||||||
```
|
|
||||||
|
|
||||||
- Start a basic Nextcloud instance:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
haze start
|
|
||||||
```
|
|
||||||
|
|
||||||
- Navigate to the address that is provided in the output.
|
|
||||||
|
|
||||||
See the [setup](./setup.html), [configuration](./configuration.html) and
|
|
||||||
[usage](./usage.html) for a more details.
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
# Summary
|
|
||||||
|
|
||||||
[Introduction](README.md)
|
|
||||||
|
|
||||||
- [Setup](setup.md)
|
|
||||||
- [Usage](usage.md)
|
|
||||||
- [Configuration](configuration.md)
|
|
||||||
- [Proxy](proxy/README.md)
|
|
||||||
- [DNS](proxy/dns.md)
|
|
||||||
- [HTTPS](proxy/https.md)
|
|
||||||
- [Services](services.md)
|
|
||||||
- [Database](database.md)
|
|
||||||
- [Federation](federation.md)
|
|
||||||
- [Haze scripts](scripts.md)
|
|
||||||
- [Xdebug](xdebug.md)
|
|
||||||
- [FrankenPHP (experimental)](frankenphp.md)
|
|
||||||
|
|
@ -1,271 +0,0 @@
|
||||||
# Configuration
|
|
||||||
|
|
||||||
Configuration is loaded from `~/.config/haze/haze.toml`.
|
|
||||||
|
|
||||||
The minimum required configuration needed to get started is just the
|
|
||||||
`sources_root` options.
|
|
||||||
|
|
||||||
The full list of supported options is:
|
|
||||||
|
|
||||||
### `sources_root`
|
|
||||||
|
|
||||||
The local path of the Nextcloud sources. This options is **required**.
|
|
||||||
|
|
||||||
Type: string
|
|
||||||
|
|
||||||
### `app_directories`
|
|
||||||
|
|
||||||
A list of additional app directory paths to look for apps into
|
|
||||||
|
|
||||||
Default `[]`
|
|
||||||
|
|
||||||
Type: list of strings
|
|
||||||
|
|
||||||
### `work_dir`
|
|
||||||
|
|
||||||
The location where haze keeps it's temporary files and caches
|
|
||||||
|
|
||||||
Default: `/tmp/haze`
|
|
||||||
|
|
||||||
Type: string
|
|
||||||
|
|
||||||
### `worktree_dir`
|
|
||||||
|
|
||||||
The location to store git worktrees when using instances with detached sources.
|
|
||||||
|
|
||||||
Default: `<work_dir>/worktrees`
|
|
||||||
|
|
||||||
Type: string
|
|
||||||
|
|
||||||
## `auto_setup`
|
|
||||||
|
|
||||||
Options for automatically setting up the newly created Nextcloud instance.
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[auto_setup]
|
|
||||||
username = "foo"
|
|
||||||
password = "bar"
|
|
||||||
enable_apps = ["files_external"]
|
|
||||||
disable_apps = ["contacts"]
|
|
||||||
post_setup = [
|
|
||||||
"occ group:add test",
|
|
||||||
]
|
|
||||||
config = { "enforce_theme" = "dark" }
|
|
||||||
```
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[auto_setup]
|
|
||||||
enabled = false
|
|
||||||
```
|
|
||||||
|
|
||||||
### `auto_setup.enabled`
|
|
||||||
|
|
||||||
Whether to automatically setup Nextcloud inside a created instance.
|
|
||||||
|
|
||||||
Default: `true`
|
|
||||||
|
|
||||||
Type: boolean
|
|
||||||
|
|
||||||
### `auto_setup.username`
|
|
||||||
|
|
||||||
The username to use for the admin account during auto setup.
|
|
||||||
|
|
||||||
Default: `admin`
|
|
||||||
|
|
||||||
Type: string
|
|
||||||
|
|
||||||
### `auto_setup.password`
|
|
||||||
|
|
||||||
The password to use for the admin account during auto setup.
|
|
||||||
|
|
||||||
Default: `admin`
|
|
||||||
|
|
||||||
Type: string
|
|
||||||
|
|
||||||
### `auto_setup.enabled_apps`
|
|
||||||
|
|
||||||
Extra apps to enable after auto setup
|
|
||||||
|
|
||||||
Default: `[]`
|
|
||||||
|
|
||||||
Type: list of strings
|
|
||||||
|
|
||||||
### `auto_setup.disabled_apps`
|
|
||||||
|
|
||||||
Apps to disabled after auto setup
|
|
||||||
|
|
||||||
Default: `[]`
|
|
||||||
|
|
||||||
Type: list of strings
|
|
||||||
|
|
||||||
### `auto_setup.post_setup`
|
|
||||||
|
|
||||||
Commands to execute after auto setup
|
|
||||||
|
|
||||||
Default: `[]`
|
|
||||||
|
|
||||||
Type: list of strings
|
|
||||||
|
|
||||||
### `auto_setup.config`
|
|
||||||
|
|
||||||
System configuration options to set before auto setup
|
|
||||||
|
|
||||||
Default: `{}`
|
|
||||||
|
|
||||||
Type: Object
|
|
||||||
|
|
||||||
## `volume`
|
|
||||||
|
|
||||||
Additional files or directories to bind-mount into instances.
|
|
||||||
|
|
||||||
Any number of volume options can be configured
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[[volume]]
|
|
||||||
source = "/tmp/haze-shared"
|
|
||||||
target = "/shared"
|
|
||||||
create = true
|
|
||||||
```
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[[volume]]
|
|
||||||
source = "/home/me/Downloads"
|
|
||||||
target = "/Downloads"
|
|
||||||
read_only = true
|
|
||||||
```
|
|
||||||
|
|
||||||
### `volume.source`
|
|
||||||
|
|
||||||
The source path on the host
|
|
||||||
|
|
||||||
Type: string
|
|
||||||
|
|
||||||
### `volume.target`
|
|
||||||
|
|
||||||
The target path inside the container
|
|
||||||
|
|
||||||
Type: string
|
|
||||||
|
|
||||||
### `volume.create`
|
|
||||||
|
|
||||||
Create the source directory on the host if it doesn't exist already.
|
|
||||||
|
|
||||||
Default: `false`
|
|
||||||
|
|
||||||
Type: boolean
|
|
||||||
|
|
||||||
### `volume.read_only`
|
|
||||||
|
|
||||||
Whether to mount the file or directory as read only
|
|
||||||
|
|
||||||
Default: `false`
|
|
||||||
|
|
||||||
Type: `boolean`
|
|
||||||
|
|
||||||
### preset
|
|
||||||
|
|
||||||
Configured presets that can be used when creating instances.
|
|
||||||
|
|
||||||
Any number of presets can be configured.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[[preset]]
|
|
||||||
name = "groupfolders"
|
|
||||||
apps = ["groupfolders"]
|
|
||||||
commands = [
|
|
||||||
"occ groupfolders:create gf",
|
|
||||||
"occ groupfolders:group 1 admin read write share delete"
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
### `preset.name`
|
|
||||||
|
|
||||||
The name of the preset, this is used when creating instances to select the
|
|
||||||
preset.
|
|
||||||
|
|
||||||
Type: string without whitespace
|
|
||||||
|
|
||||||
### `preset.apps`
|
|
||||||
|
|
||||||
A list of apps to enable when the preset is used.
|
|
||||||
|
|
||||||
Default: `[]`
|
|
||||||
|
|
||||||
Type: list of strings
|
|
||||||
|
|
||||||
### `preset.commands`
|
|
||||||
|
|
||||||
A list of commands to run post-setup when the preset is used.
|
|
||||||
|
|
||||||
Default: `[]`
|
|
||||||
|
|
||||||
Type: list of strings
|
|
||||||
|
|
||||||
## `proxy`
|
|
||||||
|
|
||||||
Configuration for the haze proxy. Only required when using the proxy.
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[proxy]
|
|
||||||
address = "haze.example.com"
|
|
||||||
listen = "/run/haze/haze.sock"
|
|
||||||
https = true
|
|
||||||
cert = "/path/to/haze.test.crt"
|
|
||||||
key = "/path/to/haze.test.key"
|
|
||||||
```
|
|
||||||
|
|
||||||
### `proxy.listen`
|
|
||||||
|
|
||||||
The IP and port or Unix socket for the proxy to listen on.
|
|
||||||
|
|
||||||
**Required** when the proxy is enabled.
|
|
||||||
|
|
||||||
Type: string
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
listen = "/run/haze/haze.sock"
|
|
||||||
```
|
|
||||||
|
|
||||||
```toml
|
|
||||||
listen = "127.0.0.1:8080"
|
|
||||||
```
|
|
||||||
|
|
||||||
### `proxy.address`
|
|
||||||
|
|
||||||
The base domain the proxy is accessible on.
|
|
||||||
|
|
||||||
**Required** if the proxy is enabled.
|
|
||||||
|
|
||||||
Type: string
|
|
||||||
|
|
||||||
### `proxy.https`
|
|
||||||
|
|
||||||
Whether to enable built-in HTTPS support for the proxy.
|
|
||||||
|
|
||||||
Default: `false`
|
|
||||||
|
|
||||||
Type: boolean
|
|
||||||
|
|
||||||
### `proxy.cert`
|
|
||||||
|
|
||||||
The path of the PEM encoded certificate chain to use for HTTPS.
|
|
||||||
|
|
||||||
**Required** if HTTPS is enabled for the proxy.
|
|
||||||
|
|
||||||
Type: string.
|
|
||||||
|
|
||||||
### `proxy.cert`
|
|
||||||
|
|
||||||
The path of the PEM encoded private key to use for HTTPS.
|
|
||||||
|
|
||||||
**Required** if HTTPS is enabled for the proxy.
|
|
||||||
|
|
||||||
Type: string.
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
# Database
|
|
||||||
|
|
||||||
There are 3 ways of accessing the database of a haze instance.
|
|
||||||
|
|
||||||
## CLI
|
|
||||||
|
|
||||||
```bash
|
|
||||||
haze [filter] db [query]
|
|
||||||
```
|
|
||||||
|
|
||||||
Opens the command line client for the database. If a query is specified, it will
|
|
||||||
be executed. If no query is specified, an interactive shell will be started.
|
|
||||||
|
|
||||||
Example
|
|
||||||
|
|
||||||
```
|
|
||||||
haze db 'select fileid, storage, path from oc_filecache'
|
|
||||||
```
|
|
||||||
|
|
||||||
## WebUI
|
|
||||||
|
|
||||||
A web UI (based on [DbGate](https://www.dbgate.io/)) is available with every
|
|
||||||
instance.
|
|
||||||
|
|
||||||
It can be accessed by using `haze open db`, or, if the proxy is setup at
|
|
||||||
`db.haze.example.com`.
|
|
||||||
|
|
||||||
## External tools
|
|
||||||
|
|
||||||
For usage with external tools, the database connection URL is printed when the
|
|
||||||
instance is created.
|
|
||||||
|
|
||||||
Details on how to use the connection URL is dependent on the external tool.
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
# Federation
|
|
||||||
|
|
||||||
Multiple instances can reach each other by using their instance name as domain
|
|
||||||
name to allow for testing federation between instances.
|
|
||||||
|
|
||||||
For example, if you have a `rover-beavers` and `splendid-couch` instance, you
|
|
||||||
can create a federated share from the `rover-beavers` instance to
|
|
||||||
`http://admin@splendid-couch`.
|
|
||||||
|
|
||||||
If the proxy is setup with HTTP, you can use HTTP between the instances by using
|
|
||||||
the full proxy URLs.
|
|
||||||
|
|
||||||
For example sharing to `admin@splendid-couch.haze.example.com`.
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
# FrankenPHP (experimental)
|
|
||||||
|
|
||||||
You can have Nextcloud run on FrankenPHP by starting the instance with the
|
|
||||||
`franken-php` option.
|
|
||||||
|
|
||||||
Caddy's admin metrics are then accessible through
|
|
||||||
`http://<cloud-id>-franken-php.haze.test`. With ember you can run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ember --addr http://<cloud-id>-franken-php.haze.test
|
|
||||||
```
|
|
||||||
|
|
@ -1,91 +0,0 @@
|
||||||
# Proxy
|
|
||||||
|
|
||||||
By default, instances can be accessed by their IP. In order to get more
|
|
||||||
memorable URLs and allow supporting HTTPS. haze comes with a builtin reverse
|
|
||||||
proxy to allow using a wildcard domain.
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
- [Setup a DNS record](./dns.html) for `*.haze.example.com` and
|
|
||||||
`haze.example.com` pointing to your development machine.
|
|
||||||
- Set the `proxy` configuration with your domain and desired listen endpoint.
|
|
||||||
- Set up a service to run `haze proxy` in the background as your own user. A
|
|
||||||
SystemD user service is recommended (see
|
|
||||||
[haze.service](<[./haze.service](https://codeberg.org/icewind/haze/src/branch/main/haze.service)>)
|
|
||||||
for an example).
|
|
||||||
- If you're already running a reverse proxy, configure your reverse proxy of
|
|
||||||
choice to proxy `*.haze.example.com` and `haze.example.com` to the proxy's
|
|
||||||
listen endpoint.
|
|
||||||
- (Optionally) [setup HTTPS](./https.html) for the proxy.
|
|
||||||
|
|
||||||
### Configuration
|
|
||||||
|
|
||||||
Add the following configuration to the `haze.toml` configuration file:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[proxy]
|
|
||||||
address = "haze.example.com" # the base domain for the proxy to use
|
|
||||||
listen = "127.0.0.1:8080" # the port+ip to listen on
|
|
||||||
# listen = "/var/run/haze/haze.sock" # or a unix socket path
|
|
||||||
```
|
|
||||||
|
|
||||||
### Without a reverse proxy
|
|
||||||
|
|
||||||
If you have no other http(s) servers on your development machine, you can setup
|
|
||||||
things without a reverse proxy.
|
|
||||||
|
|
||||||
Simply configure the proxy to listen on port `80` (or `443` when using HTTPS).
|
|
||||||
|
|
||||||
Binding to port 80 or443 as a regular user requires either giving the haze
|
|
||||||
binary the `net_bind_service` capability with
|
|
||||||
`sudo setcap cap_net_bind_service=+ep $(which haze)` (this will have to be done
|
|
||||||
every time your upgrade haze) or configure your system to allow unprivileged
|
|
||||||
users to bind on the low port numbers. Using
|
|
||||||
`sysctl net.ipv4.ip_unprivileged_port_start=80` and writing
|
|
||||||
|
|
||||||
```
|
|
||||||
net.ipv4.ip_unprivileged_port_start=80
|
|
||||||
```
|
|
||||||
|
|
||||||
to `/etc/sysctl.d/bind.conf` to make it persistent across reboot.
|
|
||||||
|
|
||||||
### With a reverse proxy
|
|
||||||
|
|
||||||
If you're already have other http(s) services listening on your machine, you'll
|
|
||||||
probably want to setup a reverse proxy to allow them to all be served on your
|
|
||||||
machine.
|
|
||||||
|
|
||||||
The setup for this will depend on your reverse proxy of the choice, the
|
|
||||||
following example configuration is for `nginx`.
|
|
||||||
|
|
||||||
```nginx
|
|
||||||
upstream haze-handler {
|
|
||||||
server unix:/var/run/haze/haze.sock;
|
|
||||||
}
|
|
||||||
|
|
||||||
server {
|
|
||||||
listen 80;
|
|
||||||
server_name *.haze.example.com;
|
|
||||||
|
|
||||||
location / {
|
|
||||||
proxy_pass http://haze-handler;
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
When the proxy is configured, generated URLs for the instances will use a
|
|
||||||
subdomain of the configured domain, e.g. the `rolling-bees` instance will be
|
|
||||||
available at `rolling-bees.haze.example.com`. Additionally, `haze.example.com`
|
|
||||||
will automatically point to the last created instance.
|
|
||||||
|
|
||||||
Additionally, the proxy allows access to the service containers trough either
|
|
||||||
`<instance id>-<service id>.haze.example.com` for a specific instance, or
|
|
||||||
`<service-id>.haze.example.com` for the last created instance. For example
|
|
||||||
`rolling-bees-mail.haze.example.com` will give access to the smtp4dev web
|
|
||||||
interface of the `rolling-bees` instance.
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
# DNS
|
|
||||||
|
|
||||||
Since the domain name used for the instance is dynamic, a wildcard DNS record is
|
|
||||||
required.
|
|
||||||
|
|
||||||
## With your domain's DNS provider
|
|
||||||
|
|
||||||
If you own a domain you would like to use, you can create a wildcard domain
|
|
||||||
within the DNS settings of your DNS provider. For example creating a record an
|
|
||||||
`A` record for `*.haze.example.com` with a value of `127.0.0.1` and a similar
|
|
||||||
one for `haze.example.com`.
|
|
||||||
|
|
||||||
## With a local dnsmasq
|
|
||||||
|
|
||||||
If you do not own a "real" domain for using with haze, you can setup `dnsmasq`
|
|
||||||
locally to achieve the same goal instead.
|
|
||||||
|
|
||||||
How to install and enable `dnsmasq` will depend on your Linux distribution of
|
|
||||||
choice and should be documented by it's documentation.
|
|
||||||
|
|
||||||
Once setup, a configuration line like
|
|
||||||
|
|
||||||
```
|
|
||||||
address=/haze.local/172.0.0.1
|
|
||||||
```
|
|
||||||
|
|
||||||
should be enough to point `haze.local` and `*.haze.local` to your local host.
|
|
||||||
|
|
@ -1,78 +0,0 @@
|
||||||
# HTTPS
|
|
||||||
|
|
||||||
The proxy can be setup to enable using HTTPS to access the running instances.
|
|
||||||
Besides the warm and fuzzy feeling of knowing that nobody can snoop on the
|
|
||||||
traffic that is happening completely local inside your machine. Accessing the
|
|
||||||
page over HTTPS is required for some JavaScript features (such as service
|
|
||||||
workers), as they are only available in "secure contexts".
|
|
||||||
|
|
||||||
## Getting a wildcard certificate
|
|
||||||
|
|
||||||
Since the domain name used for the instance is dynamic, a wildcard certificate
|
|
||||||
is required.
|
|
||||||
|
|
||||||
## Let's encrypt
|
|
||||||
|
|
||||||
Let's encrypt allows getting trusted wildcard certificates for free if you can
|
|
||||||
use DNS validation.
|
|
||||||
|
|
||||||
How to setup DNS validation will depend on the specifics of the DNS provider and
|
|
||||||
ACME client.
|
|
||||||
[This](https://community.letsencrypt.org/t/dns-providers-who-easily-integrate-with-lets-encrypt-dns-validation/86438)
|
|
||||||
lists some DNS providers and supported ACME clients.
|
|
||||||
|
|
||||||
## Self signed
|
|
||||||
|
|
||||||
You can also create a self-signed wildcard certificate using a tool like
|
|
||||||
`mkcert`. This certificate will not be trusted by your browser and tools like
|
|
||||||
curl, but you can add manually add it to the trusted certificates on your
|
|
||||||
system, or bypass the certificates warning in the browser/curl every time.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Generate local wildcard certificate
|
|
||||||
mkcert -cert-file <path-to-your-certificates>haze.example.com.crt -key-file <path-to-your-certificates>haze.example.com.key '*.haze.example.com'
|
|
||||||
```
|
|
||||||
|
|
||||||
## Using the certificate
|
|
||||||
|
|
||||||
### Without reverse proxy
|
|
||||||
|
|
||||||
The haze proxy can serve over HTTPS directly, to enable that add the following
|
|
||||||
to the `[proxy]` section of your `haze.toml`.
|
|
||||||
|
|
||||||
```toml
|
|
||||||
https = true
|
|
||||||
cert = "/path/to/haze.example.com.crt"
|
|
||||||
key = "/path/to/haze.example.com.key"
|
|
||||||
```
|
|
||||||
|
|
||||||
You might also want to change the port it's listening on to `443`.
|
|
||||||
|
|
||||||
### With a reverse proxy
|
|
||||||
|
|
||||||
This depends on what reverse proxy you have setup. The following example is for
|
|
||||||
`nginx`.
|
|
||||||
|
|
||||||
```nginx
|
|
||||||
upstream haze-handler {
|
|
||||||
server unix:/run/haze/haze.sock;
|
|
||||||
}
|
|
||||||
|
|
||||||
server {
|
|
||||||
listen 80;
|
|
||||||
listen 443 ssl;
|
|
||||||
http2 on;
|
|
||||||
server_name *.haze.example.com;
|
|
||||||
|
|
||||||
ssl_certificate <path-to-your-certificates>/haze.example.com.crt;
|
|
||||||
ssl_certificate_key <path-to-your-certificates>/haze.example.com.key;
|
|
||||||
|
|
||||||
location / {
|
|
||||||
proxy_pass http://haze-handler;
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
# Haze scripts
|
|
||||||
|
|
||||||
Haze scripts combine a set of instance options and a script to run in the
|
|
||||||
instance.
|
|
||||||
|
|
||||||
Haze scripts are intended to way to create automated ways of running more
|
|
||||||
complex tests are setting up more complex instances.
|
|
||||||
|
|
||||||
A script contains of 3 paths
|
|
||||||
|
|
||||||
1. An optional shebang line setting `haze` as the interpreter, e.g.
|
|
||||||
`#! /usr/bin/env -S haze script`
|
|
||||||
2. A shebang line setting the options for the instance creation as the
|
|
||||||
interpreter, e.g. `#! haze shell pgsql s3`
|
|
||||||
3. The rest that is ran as a script inside the created instance.
|
|
||||||
|
|
||||||
The first shebang is set, the script can be ran directly. Else it needs to be
|
|
||||||
run with `haze script [path-to-script]`.
|
|
||||||
|
|
||||||
For example, the following script will create an instance with `postgresql` and
|
|
||||||
`s3` primary storage. Then creates a new file, gets the file id, read the object
|
|
||||||
of the file from S3, validate that it contains the expected contents, and
|
|
||||||
cleanup the instance.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
#! #! /usr/bin/env -S haze script
|
|
||||||
#! haze shell pgsql s3
|
|
||||||
|
|
||||||
echo 'test' | occ file:put '-' /admin/files/test.txt
|
|
||||||
FILE_ID=$(occ info:file /admin/files/test.txt | grep fileid: | grep -oE '[0-9]+')
|
|
||||||
OBJECT_CONTENTS=$(occ file:object:get urn:oid:$FILE_ID -)
|
|
||||||
if [ "$OBJECT_CONTENTS" == 'test' ]; then
|
|
||||||
echo "object contains expected contents"
|
|
||||||
else
|
|
||||||
echo "object does not contains expected contents!"
|
|
||||||
fi
|
|
||||||
```
|
|
||||||
|
|
||||||
### Script modes
|
|
||||||
|
|
||||||
Scripts can be either `shell` or `start` scripts.
|
|
||||||
|
|
||||||
`shell` scripts will automatically remove the created instance once the script
|
|
||||||
is done, just like `haze shell` will. The intended use case for this is
|
|
||||||
performing some tests in the created instance without leaving state behind.
|
|
||||||
|
|
||||||
`start` scripts on the other hand will keep the instance, like `haze start`
|
|
||||||
will. The intended use case for this is creating more complex instances without
|
|
||||||
having to configure a dedicated preset.
|
|
||||||
|
|
||||||
The script mode is determined based on the shebang line. The mode set in a
|
|
||||||
script can be overridden by running the script with
|
|
||||||
`haze script [shell|start] path-to-script.sh`.
|
|
||||||
|
|
||||||
## Using different interpreters
|
|
||||||
|
|
||||||
By default, the script contents are executed as either `bash` script, or `nu`
|
|
||||||
script based on the extension.
|
|
||||||
|
|
||||||
You can overwrite the interpreter used to execute the script by adding an extra
|
|
||||||
`#!` line to the script, for example:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
#! /usr/bin/env -S haze script
|
|
||||||
#! haze shell pgsql s3
|
|
||||||
#! php -f
|
|
||||||
<?php
|
|
||||||
print("Hello");
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that the interpreter used needs to already be available inside the haze
|
|
||||||
container.
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
# Services
|
|
||||||
|
|
||||||
The following service options are available:
|
|
||||||
|
|
||||||
- `s3`: set up an S3 server and configure to Nextcloud to use it as primary
|
|
||||||
storage.
|
|
||||||
- `s3s`: enable TLS for the S3 setup.
|
|
||||||
- `s3mb`: enable multi-bucket S3 setup.
|
|
||||||
- `s3m`: enable multi-instance S3 setup.
|
|
||||||
- `ldap`: set up an LDAP server.
|
|
||||||
- `saml`: set up Authentik as a SAML IDP.
|
|
||||||
- `oidc`: set up Authentik as an OIDC IDP.
|
|
||||||
- `scim`: set up Authentik as a SCIM server.
|
|
||||||
- `office`: set up a Nextcloud Office server.
|
|
||||||
- `onlyoffice` setup an OnlyOffice document server.
|
|
||||||
- `push` set up [client push](https://github.com/nextcloud/notify_push).
|
|
||||||
- `smb`: set up a samba server for external storage use.
|
|
||||||
- `dav`: set up a WebDAV server for external storage use.
|
|
||||||
- `sftp`: set up a SFTP server for external storage use.
|
|
||||||
- `sftp-key`: set up a SFTP server for external storage use with public key
|
|
||||||
authentication.
|
|
||||||
- `kaspersky`: set up a Kaspersky scan engine server in HTTP mode. ( Requires
|
|
||||||
[manually setting up the image](https://github.com/icewind1991/kaspersky-docker))
|
|
||||||
- `kaspersky-icap`: setup a Kaspersky scan engine server in ICAP mode.
|
|
||||||
- `clamav`: set up a local clam av scanner in executable mode.
|
|
||||||
- `clamav-socket`: set up a clam av scanner in socket mode.
|
|
||||||
- `clamav-icap`: set up a clam av scanner in ICAP mode.
|
|
||||||
- `clamav-icap-tls`: set up a clam av scanner in ICAP mode with TLS encryption.
|
|
||||||
- `oc`: start an ownCloud instance in the same network.
|
|
||||||
- `imaginary`: start an Imaginary service and configure it for preview
|
|
||||||
generation.
|
|
||||||
- `mail`: start a [smtp4dev](https://github.com/rnwood/smtp4dev) server and
|
|
||||||
configure it the mail server.
|
|
||||||
- `webhook` start a
|
|
||||||
[webhook tester](https://github.com/tarampampam/webhook-tester)
|
|
||||||
- `redis`: start a separate container for Redis.
|
|
||||||
- `redis-tls`: connect to Redis over TLS.
|
|
||||||
- `<path to app.tar.gz>`: by specifying the path to an app package this package
|
|
||||||
will be extracted into the apps. directory of the new instance (overwriting
|
|
||||||
any existing app code). This can be used to quickly test a packaged app.
|
|
||||||
- The name of any configured preset.
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
# Setup
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
- Docker
|
|
||||||
|
|
||||||
haze is built around docker containers and manages containers using the docker
|
|
||||||
socket. Using Podman with docker compatibility might also work, but is untested.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
- Grab a binary from the
|
|
||||||
[Codeberg releases](https://codeberg.org/icewind/haze/releases) and place it
|
|
||||||
in your `$PATH`
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
Create a file `~/.config/haze/haze.toml` with the following options:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
sources_root = "/path/to/nextcloud/sources"
|
|
||||||
```
|
|
||||||
|
|
||||||
See the [configuration section](./configuration.html) for more options.
|
|
||||||
|
|
||||||
## Test the Setup
|
|
||||||
|
|
||||||
### Quick examples
|
|
||||||
|
|
||||||
- Start a basic Nextcloud instance:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
haze start
|
|
||||||
```
|
|
||||||
|
|
||||||
- Navigate to the address that is provided in the output.
|
|
||||||
|
|
@ -1,230 +0,0 @@
|
||||||
# Usage
|
|
||||||
|
|
||||||
## Quick examples
|
|
||||||
|
|
||||||
- Start a Nextcloud instance with `postgresql`, and `s3` primary storage:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
haze start pgsql s3
|
|
||||||
```
|
|
||||||
|
|
||||||
- Start a Nextcloud instance with `sqlite`, `php 8.3` and an `smb` external
|
|
||||||
storage:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
haze start 8.3 smb
|
|
||||||
```
|
|
||||||
|
|
||||||
- Run specific units test against an `oracle` database
|
|
||||||
```bash
|
|
||||||
haze test oracle apps/dav/tests/unit/Connector/Sabre
|
|
||||||
```
|
|
||||||
|
|
||||||
## Managing instances
|
|
||||||
|
|
||||||
### Starting an instance
|
|
||||||
|
|
||||||
```bash
|
|
||||||
haze start [--name <name>] [--detach] [database] [php-version] [services] [vX.Y.Z]
|
|
||||||
```
|
|
||||||
|
|
||||||
Where `database` is one of `sqlite`, `mysql`, `mariadb`, `pgsql` or `oracle`
|
|
||||||
with an optional version (e.g. `pgsql:12`), defaults to `sqlite`. And
|
|
||||||
`php-version` is one of `8.0`, `8.1`, `8.2`, `8.3`, `8.4` or `8.5`, defaults to
|
|
||||||
the maximum version support by the current Nextcloud version.
|
|
||||||
|
|
||||||
You can specify a version number (e.g. `v32.0.2`) to use the sources from a
|
|
||||||
release instead of using the local sources.
|
|
||||||
|
|
||||||
Use `--name <name>` to give the instance a specific name instead of a randomly
|
|
||||||
generated one. For example:
|
|
||||||
|
|
||||||
Use `--detach` to give the instance
|
|
||||||
[its own sources](#instances-with-their-own-sources) instead of sharing
|
|
||||||
`sources_root` with all other instances.
|
|
||||||
|
|
||||||
See the [services documentation](./services.html) for a list of available
|
|
||||||
services.
|
|
||||||
|
|
||||||
### List running instances
|
|
||||||
|
|
||||||
```bash
|
|
||||||
haze
|
|
||||||
```
|
|
||||||
|
|
||||||
or
|
|
||||||
|
|
||||||
```bash
|
|
||||||
haze list
|
|
||||||
```
|
|
||||||
|
|
||||||
### Stop an instance
|
|
||||||
|
|
||||||
```bash
|
|
||||||
haze [match] stop
|
|
||||||
```
|
|
||||||
|
|
||||||
### Remove all unpinned running instances
|
|
||||||
|
|
||||||
```bash
|
|
||||||
haze clean
|
|
||||||
```
|
|
||||||
|
|
||||||
### Pin an instance
|
|
||||||
|
|
||||||
```bash
|
|
||||||
haze [match] pin
|
|
||||||
```
|
|
||||||
|
|
||||||
Pinned instances will not be removed by `haze clean`.
|
|
||||||
|
|
||||||
### Unpin an instance
|
|
||||||
|
|
||||||
```bash
|
|
||||||
haze [match] unpin
|
|
||||||
```
|
|
||||||
|
|
||||||
## Run commands in a temporary instance
|
|
||||||
|
|
||||||
```bash
|
|
||||||
haze shell [database] [php-version] [services] [cmd]
|
|
||||||
```
|
|
||||||
|
|
||||||
This will create an instance, run the provided command, and cleanup the
|
|
||||||
instance.
|
|
||||||
|
|
||||||
If no `cmd` is specified it will launch `bash`
|
|
||||||
|
|
||||||
## Run tests in a new instance
|
|
||||||
|
|
||||||
```bash
|
|
||||||
haze test [database] [php-version] [services] [phpunit version] [path]
|
|
||||||
```
|
|
||||||
|
|
||||||
Where `path` is a file or folder to run PHPUnit in, relative to the sources
|
|
||||||
root.
|
|
||||||
|
|
||||||
This will create a fresh instance, run the PHPUnit tests, and clean up the
|
|
||||||
created instance.
|
|
||||||
|
|
||||||
## Interacting with running instances
|
|
||||||
|
|
||||||
The following commands run against the most recently started instance by default
|
|
||||||
and allow optionally providing a `match` to select a specific instance by its
|
|
||||||
name.
|
|
||||||
|
|
||||||
### Open an instance in the browser
|
|
||||||
|
|
||||||
```bash
|
|
||||||
haze [match] open
|
|
||||||
```
|
|
||||||
|
|
||||||
### Execute a command on an instance
|
|
||||||
|
|
||||||
```bash
|
|
||||||
haze [match] [service] [cmd]
|
|
||||||
```
|
|
||||||
|
|
||||||
If no `cmd` is specified it will launch `bash`
|
|
||||||
|
|
||||||
If a service name or `db` is provided, the command will be in the container of
|
|
||||||
the service or database.
|
|
||||||
|
|
||||||
If no `cmd` is specified it will launch `bash`
|
|
||||||
|
|
||||||
### Execute an occ command on an instance
|
|
||||||
|
|
||||||
```bash
|
|
||||||
haze [match] occ [cmd]
|
|
||||||
```
|
|
||||||
|
|
||||||
### Connect to the database on an instance
|
|
||||||
|
|
||||||
```bash
|
|
||||||
haze [match] db
|
|
||||||
```
|
|
||||||
|
|
||||||
### Show the logs of an instance
|
|
||||||
|
|
||||||
```bash
|
|
||||||
haze [match] logs
|
|
||||||
```
|
|
||||||
|
|
||||||
### Edit a file in an instance with the local $EDITOR
|
|
||||||
|
|
||||||
```bash
|
|
||||||
haze [match] edit <path>
|
|
||||||
```
|
|
||||||
|
|
||||||
Where `<path>` is the path of a file inside the container, for example
|
|
||||||
`config/config.php`.
|
|
||||||
|
|
||||||
### Reload the PHP configuration of an instance
|
|
||||||
|
|
||||||
```bash
|
|
||||||
haze [match] reload
|
|
||||||
```
|
|
||||||
|
|
||||||
The PHP configuration can edit changed with `haze edit /config/php.ini`
|
|
||||||
|
|
||||||
### Run a command with instance environment variables set
|
|
||||||
|
|
||||||
```bash
|
|
||||||
haze [match] env <cmd> [args]
|
|
||||||
```
|
|
||||||
|
|
||||||
Runs the provided command with `NEXTCLOUD_URL`, `DATABASE_URL` and `REDIS_URL`
|
|
||||||
environment variables set for the matched instance.
|
|
||||||
|
|
||||||
This is intended to run a local
|
|
||||||
[push daemon](https://github.com/nextcloud/notify_push) against an instance.
|
|
||||||
|
|
||||||
## Git tools
|
|
||||||
|
|
||||||
Haze provides a couple of utilities to make working with many git repositories
|
|
||||||
for apps easier.
|
|
||||||
|
|
||||||
### Checkout a branch for all local apps
|
|
||||||
|
|
||||||
```bash
|
|
||||||
haze git checkout [branch]
|
|
||||||
```
|
|
||||||
|
|
||||||
Checks out the branch in all git repositories within the apps folder.
|
|
||||||
|
|
||||||
Defaults to the branch matching the current checked out server versions (e.g.
|
|
||||||
`master` or `stable33`).
|
|
||||||
|
|
||||||
`master` and `main` can be used interchangeably.
|
|
||||||
|
|
||||||
### Pull remote changes for all local apps
|
|
||||||
|
|
||||||
```bash
|
|
||||||
haze git pull
|
|
||||||
```
|
|
||||||
|
|
||||||
Performs a pull in all git repositories within the apps folder.
|
|
||||||
|
|
||||||
## Update the container images
|
|
||||||
|
|
||||||
```bash
|
|
||||||
haze update
|
|
||||||
```
|
|
||||||
|
|
||||||
## Instances with their own sources
|
|
||||||
|
|
||||||
By default every instance shares the sources configured as `sources_root`, so
|
|
||||||
all instances always run the same code. An instance started with `--detach` gets
|
|
||||||
its own `git worktree` of `sources_root` instead, created in `worktree_dir`,
|
|
||||||
which lets you run several instances on different branches at the same time.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
haze start --name my-fix --detach
|
|
||||||
```
|
|
||||||
|
|
||||||
The worktree is checked out with a detached head. Every app in one of the
|
|
||||||
`app_directories` that gets enabled during setup receives its own worktree as
|
|
||||||
well, other apps keep running from the shared app directory.
|
|
||||||
|
|
||||||
The worktrees are removed together with the instance, by `haze stop` or
|
|
||||||
`haze clean`.
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
# Xdebug
|
|
||||||
|
|
||||||
To use Xdebug running in a haze instance with your IDE for debugging, you need
|
|
||||||
to tell you IDE how to properly map the path from the container to the host. The
|
|
||||||
IDE debugger configuration usually looks like:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"label": "PHP: Debug server within docker",
|
|
||||||
"adapter": "Xdebug",
|
|
||||||
"request": "launch",
|
|
||||||
"port": 9003,
|
|
||||||
"pathMappings": {
|
|
||||||
"/var/www/html": "<sources_root_configured_in_haze.toml>",
|
|
||||||
"/var/www/html/apps-extra": "<app_directories_configured_in_haze.toml>",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
```
|
|
||||||
|
|
||||||
This would have to be adapted for detached instances.
|
|
||||||
|
|
||||||
## Debugging all requests
|
|
||||||
|
|
||||||
By default, Xdebug is configured to only run for requests that containing the
|
|
||||||
trigger (e.g. from using the Xdebug browser extension, or adding
|
|
||||||
`?XDEBUG_SESSION_START=1` to the URL).
|
|
||||||
|
|
||||||
To enable Xdebug for all requests:
|
|
||||||
|
|
||||||
- Un-comment the lines in `haze [cloud-id] edit /config/php.ini`
|
|
||||||
- Then run `haze reload [cloud-id]`
|
|
||||||
|
|
||||||
## Xdebug profile and trace files
|
|
||||||
|
|
||||||
When enabling Xdebug trace or profile mode, the generated files can be found in
|
|
||||||
`<work-dir>/<instance id>/xdebug`.
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
#! /usr/bin/env -S haze script
|
#! /usr/bin/env -S haze script
|
||||||
#! haze shell pgsql s3
|
#! haze shell pgsql s3
|
||||||
#! /usr/bin/env nu
|
|
||||||
|
|
||||||
echo 'test' | occ file:put '-' /admin/files/test.txt
|
echo 'test' | occ file:put '-' /admin/files/test.txt
|
||||||
FILE_ID=$(occ info:file /admin/files/test.txt | grep fileid: | grep -oE '[0-9]+')
|
FILE_ID=$(occ info:file /admin/files/test.txt | grep fileid: | grep -oE '[0-9]+')
|
||||||
|
|
|
||||||
86
flake.lock
generated
86
flake.lock
generated
|
|
@ -2,11 +2,11 @@
|
||||||
"nodes": {
|
"nodes": {
|
||||||
"crane": {
|
"crane": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1788465171,
|
"lastModified": 1780099841,
|
||||||
"narHash": "sha256-Y1/TTVXjYXGF068IThQH9fPSZ0SIE74PABlUxnWTUH0=",
|
"narHash": "sha256-EVZd2RsbpreRUDSi9rBwPY+ZxoyMaiEBbZxxhljbaS4=",
|
||||||
"owner": "ipetkov",
|
"owner": "ipetkov",
|
||||||
"repo": "crane",
|
"repo": "crane",
|
||||||
"rev": "eb35abda9f232cc6610b1d1e3200d15c49b7ac54",
|
"rev": "0532eb17955225173906d671fb36306bdeb1e2dc",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
@ -38,11 +38,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1790013821,
|
"lastModified": 1781543995,
|
||||||
"narHash": "sha256-0OrPYAGfGF5BGPPtzEzeMNhNcvGyQBWRRfvcE0xY750=",
|
"narHash": "sha256-wsSyxNWOSMofu4F5xGYGMrB1d8cepvBNhxhm9bGGmXg=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "flakelight",
|
"repo": "flakelight",
|
||||||
"rev": "a57b0af9d0da4c5389ccf19e310daf0e5518312a",
|
"rev": "fd1d557721e07b5a9d319442e4bcb5d286600011",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
@ -60,11 +60,11 @@
|
||||||
"rust-overlay": "rust-overlay"
|
"rust-overlay": "rust-overlay"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1789480598,
|
"lastModified": 1780231986,
|
||||||
"narHash": "sha256-G1jy1kz2dLJCkVjjY2PnCXEL1B2iqMlSfz01YFCBY+c=",
|
"narHash": "sha256-OyafczPtzE0Xa2zl3j/KvV2+ZVYGhYQHt0MOVWtDXlY=",
|
||||||
"ref": "refs/heads/main",
|
"ref": "refs/heads/main",
|
||||||
"rev": "c1e026caf1750f226a20dab66594dc1ef3a34ec4",
|
"rev": "f5cbda29b945df03256bf63c22fa4cd5fa429e67",
|
||||||
"revCount": 77,
|
"revCount": 72,
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://codeberg.org/icewind/mill-scale.git"
|
"url": "https://codeberg.org/icewind/mill-scale.git"
|
||||||
},
|
},
|
||||||
|
|
@ -80,11 +80,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1790258944,
|
"lastModified": 1775487831,
|
||||||
"narHash": "sha256-3bwtAiifsfGXVLIopwhJ/RNIkDABcLg6DxnlbaiE2xc=",
|
"narHash": "sha256-2lguQpLPQaxpQCJjXhmEEAfabwsAhkP29Z7fgLzHARA=",
|
||||||
"owner": "nlewo",
|
"owner": "nlewo",
|
||||||
"repo": "nix2container",
|
"repo": "nix2container",
|
||||||
"rev": "08d8889b6d2528acfce6e2616b0cd41983dedb02",
|
"rev": "76be9608a7f4d6c985d28b0e7be903ae2547df3e",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
@ -95,11 +95,11 @@
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1790218706,
|
"lastModified": 1781216227,
|
||||||
"narHash": "sha256-6e4Na3z008XpdVyOXgfasXIn1aN+z8AXFG+jXdQSyuI=",
|
"narHash": "sha256-9mUW6gNwoN2SWc/l0fW4svPNOulXLl8ijqKyeSOGgJE=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "c508844df6c28fa6dabc1b6af70f3ccbd65c5201",
|
"rev": "a0374025a863d007d98e3297f6aa46cc3141c2f0",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
@ -110,11 +110,11 @@
|
||||||
},
|
},
|
||||||
"nixpkgs_2": {
|
"nixpkgs_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1789370336,
|
"lastModified": 1781268102,
|
||||||
"narHash": "sha256-6RSEDHIWQtesQKWSu5qRai8L2h4KgCgMEfJHstW99G4=",
|
"narHash": "sha256-Zn5KTggEmUB3lXn/ccERNcBdddE6IaOFber9dWViWDg=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "c7def046b9a883d46974757852106483d741586f",
|
"rev": "49a4bd0573c376468dd7996ddb6f9fa31d8c4d97",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
@ -127,14 +127,15 @@
|
||||||
"phps": {
|
"phps": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-compat": "flake-compat",
|
"flake-compat": "flake-compat",
|
||||||
"nixpkgs": "nixpkgs_2"
|
"nixpkgs": "nixpkgs_2",
|
||||||
|
"utils": "utils"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1790337121,
|
"lastModified": 1781456983,
|
||||||
"narHash": "sha256-phqixEWMLjvdlUpo6uBCYbVYuGCk0FjiIoC0zPTCu3c=",
|
"narHash": "sha256-z3SNuQpkSeIRTS6Y/Q5FgGAuGSY5+YJBXtqWPXw0f0k=",
|
||||||
"owner": "fossar",
|
"owner": "fossar",
|
||||||
"repo": "nix-phps",
|
"repo": "nix-phps",
|
||||||
"rev": "2a7ca28d4634890d2b07a63847cd6d26da9c2cb4",
|
"rev": "6458735dece7e48f27d52ac68eee2d2cfe55b79a",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
@ -161,11 +162,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1789457514,
|
"lastModified": 1780197589,
|
||||||
"narHash": "sha256-Aggle++fTyAifBy+QBPxjM+obO5iepKW/8MDxQtgGvI=",
|
"narHash": "sha256-FVCr2Ij/jKf59a4LW481eeOF6rJRreOBrVgW/aUBTrw=",
|
||||||
"owner": "oxalica",
|
"owner": "oxalica",
|
||||||
"repo": "rust-overlay",
|
"repo": "rust-overlay",
|
||||||
"rev": "89e99bf0778a8f2cd18c9360c3f19c1ee47fc739",
|
"rev": "21632e942d89bf1cce4e5a63d7e58a215a0cbfcc",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
@ -173,6 +174,39 @@
|
||||||
"repo": "rust-overlay",
|
"repo": "rust-overlay",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1731533236,
|
||||||
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"root": "root",
|
"root": "root",
|
||||||
|
|
|
||||||
15
flake.nix
15
flake.nix
|
|
@ -45,6 +45,14 @@
|
||||||
(final: prev: {
|
(final: prev: {
|
||||||
inherit (phps.packages.${prev.system}) php81 php80;
|
inherit (phps.packages.${prev.system}) php81 php80;
|
||||||
inherit (nix2container.packages.x86_64-linux) nix2container;
|
inherit (nix2container.packages.x86_64-linux) nix2container;
|
||||||
|
blackfire = prev.blackfire.overrideAttrs (
|
||||||
|
oldAttrs: finalAttrs: {
|
||||||
|
src = final.fetchurl {
|
||||||
|
url = "https://packages.blackfire.io/debian/pool/any/main/b/blackfire/blackfire_${finalAttrs.version}_amd64.deb";
|
||||||
|
sha256 = "sha256-1fswfZEElLyXWqvNW76BpKTpBomK9cglJtitZgcpxhM=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -55,7 +63,6 @@
|
||||||
"haze-image-php-8.2" = pkgs: pkgs.haze-image-php-82;
|
"haze-image-php-8.2" = pkgs: pkgs.haze-image-php-82;
|
||||||
"haze-image-php-8.1" = pkgs: pkgs.haze-image-php-81;
|
"haze-image-php-8.1" = pkgs: pkgs.haze-image-php-81;
|
||||||
"haze-image-php-8.0" = pkgs: pkgs.haze-image-php-80;
|
"haze-image-php-8.0" = pkgs: pkgs.haze-image-php-80;
|
||||||
haze-book = pkgs: pkgs.haze-book;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
tools = pkgs:
|
tools = pkgs:
|
||||||
|
|
@ -64,14 +71,8 @@
|
||||||
bacon
|
bacon
|
||||||
skopeo
|
skopeo
|
||||||
dive
|
dive
|
||||||
mdbook
|
|
||||||
vale
|
|
||||||
];
|
];
|
||||||
|
|
||||||
checks = {
|
|
||||||
vale = {vale, ...}: "${vale}/bin/vale src/ book/src/ book/README.md *.md";
|
|
||||||
};
|
|
||||||
|
|
||||||
homeModules = {
|
homeModules = {
|
||||||
default = {
|
default = {
|
||||||
pkgs,
|
pkgs,
|
||||||
|
|
|
||||||
20
nix/book.nix
20
nix/book.nix
|
|
@ -1,20 +0,0 @@
|
||||||
{
|
|
||||||
mdbook,
|
|
||||||
stdenv,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
stdenv.mkDerivation {
|
|
||||||
name = "haze-book";
|
|
||||||
src = ../book;
|
|
||||||
|
|
||||||
nativeBuildInputs = [mdbook];
|
|
||||||
|
|
||||||
buildPhase = ''
|
|
||||||
mdbook build
|
|
||||||
'';
|
|
||||||
|
|
||||||
installPhase = ''
|
|
||||||
mkdir -p $out
|
|
||||||
cp -r book/* $out/
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
touch /var/log/nginx/access.log
|
touch /var/log/nginx/access.log
|
||||||
touch /var/log/nginx/error.log
|
touch /var/log/nginx/error.log
|
||||||
|
touch /var/log/cron/owncloud.log
|
||||||
|
|
||||||
mkdir /config
|
mkdir /config
|
||||||
if not ("/config/php.ini" | path exists) {
|
if not ("/config/php.ini" | path exists) {
|
||||||
|
|
@ -34,7 +35,6 @@ let dirs = [
|
||||||
let files = [
|
let files = [
|
||||||
["condition", "path"];
|
["condition", "path"];
|
||||||
["/", "/var/www/html/build/integration/composer.lock"],
|
["/", "/var/www/html/build/integration/composer.lock"],
|
||||||
["/", "/var/log/cron/haze.log"]
|
|
||||||
]
|
]
|
||||||
|
|
||||||
$dirs | each { |dir|
|
$dirs | each { |dir|
|
||||||
|
|
@ -88,7 +88,6 @@ if ("REDIS_TLS" in $env) {
|
||||||
cp /etc/supervisor/redis-tls.conf /etc/supervisor/enabled/
|
cp /etc/supervisor/redis-tls.conf /etc/supervisor/enabled/
|
||||||
} else {
|
} else {
|
||||||
cp /etc/supervisor/redis-plain.conf /etc/supervisor/enabled/
|
cp /etc/supervisor/redis-plain.conf /etc/supervisor/enabled/
|
||||||
cp /etc/supervisor/redis-plain.conf /etc/supervisor/enabled/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("BLACKFIRE_SERVER_ID" in $env) {
|
if ("BLACKFIRE_SERVER_ID" in $env) {
|
||||||
|
|
|
||||||
|
|
@ -3,4 +3,5 @@ runCommand "configs" {} ''
|
||||||
mkdir -p $out
|
mkdir -p $out
|
||||||
cp -r ${./configs} $out/etc
|
cp -r ${./configs} $out/etc
|
||||||
chmod -R +w $out/etc
|
chmod -R +w $out/etc
|
||||||
|
mkdir $out/etc/supervisor/enabled/
|
||||||
''
|
''
|
||||||
|
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
# m h dom mon dow user command
|
|
||||||
* * * * * haze php -f /var/www/html/cron.php -- -v >> /var/log/cron/haze.log 2>&1
|
|
||||||
2
nix/image/configs/oc-cron.conf
Normal file
2
nix/image/configs/oc-cron.conf
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
# m h dom mon dow command
|
||||||
|
*/5 * * * * sudo -u haze php -f /var/www/html/cron.php >> /var/log/cron/haze.log 2>&1
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
[program:cron]
|
|
||||||
command = crond -x sch -f
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
[program:frankenphp]
|
[program:frankenphp]
|
||||||
environment = SERVER_NAME=":80",CADDY_ADMIN=":2019"
|
|
||||||
command = /bin/frankenphp run
|
command = /bin/frankenphp run
|
||||||
directory = /var/www/html
|
directory = /var/www/html
|
||||||
user=haze
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
touch /var/log/nginx/access.log
|
touch /var/log/nginx/access.log
|
||||||
touch /var/log/nginx/error.log
|
touch /var/log/nginx/error.log
|
||||||
touch /var/log/cron/haze.log
|
touch /var/log/cron/owncloud.log
|
||||||
|
|
||||||
if ("/var/www/html/config/config.php" | path exists) {
|
if ("/var/www/html/config/config.php" | path exists) {
|
||||||
exit 0
|
exit 0
|
||||||
|
|
|
||||||
|
|
@ -6,5 +6,4 @@ final: prev: {
|
||||||
haze-image-php-82 = final.callPackage ./image/haze.nix {php = final.php82;};
|
haze-image-php-82 = final.callPackage ./image/haze.nix {php = final.php82;};
|
||||||
haze-image-php-81 = final.callPackage ./image/haze.nix {php = final.php81;};
|
haze-image-php-81 = final.callPackage ./image/haze.nix {php = final.php81;};
|
||||||
haze-image-php-80 = final.callPackage ./image/haze.nix {php = final.php80;};
|
haze-image-php-80 = final.callPackage ./image/haze.nix {php = final.php80;};
|
||||||
haze-book = final.callPackage ./book.nix {};
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
19
src/args.rs
19
src/args.rs
|
|
@ -64,7 +64,6 @@ pub enum HazeArgs {
|
||||||
},
|
},
|
||||||
Open {
|
Open {
|
||||||
filter: Option<String>,
|
filter: Option<String>,
|
||||||
service: Option<Service>,
|
|
||||||
},
|
},
|
||||||
Fmt {
|
Fmt {
|
||||||
path: String,
|
path: String,
|
||||||
|
|
@ -318,20 +317,7 @@ impl HazeArgs {
|
||||||
.into_diagnostic()?,
|
.into_diagnostic()?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
HazeCommand::Open => {
|
HazeCommand::Open => Ok(HazeArgs::Open { filter }),
|
||||||
let mut args = args.peekable();
|
|
||||||
let service = match args.peek() {
|
|
||||||
Some(arg) => Service::from_type(&[], arg.as_ref())
|
|
||||||
.into_iter()
|
|
||||||
.filter_map(|services| services.into_iter().next())
|
|
||||||
.next()
|
|
||||||
.inspect(|_| {
|
|
||||||
args.next();
|
|
||||||
}),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
Ok(HazeArgs::Open { filter, service })
|
|
||||||
}
|
|
||||||
HazeCommand::Fmt => {
|
HazeCommand::Fmt => {
|
||||||
let path = args
|
let path = args
|
||||||
.next()
|
.next()
|
||||||
|
|
@ -486,7 +472,6 @@ pub enum HazeCommand {
|
||||||
))]
|
))]
|
||||||
Logs,
|
Logs,
|
||||||
/// Open an instance in the browser
|
/// Open an instance in the browser
|
||||||
#[strum(props(Args = "[service] service to open, instead of the Nextcloud instance"))]
|
|
||||||
Open,
|
Open,
|
||||||
/// Run code formatting from a new instance
|
/// Run code formatting from a new instance
|
||||||
#[strum(props(Args = "[path] path to format"))]
|
#[strum(props(Args = "[path] path to format"))]
|
||||||
|
|
@ -522,7 +507,7 @@ pub enum HazeCommand {
|
||||||
/// Edit a file in the instance with $EDITOR on the host
|
/// Edit a file in the instance with $EDITOR on the host
|
||||||
#[strum(props(Args = "[path] file to edit"))]
|
#[strum(props(Args = "[path] file to edit"))]
|
||||||
Edit,
|
Edit,
|
||||||
/// Reload the PHP configuration in the instance
|
/// Reload the php configuration in the instance
|
||||||
#[strum(props(
|
#[strum(props(
|
||||||
Details = "note: you can overwrite <yellow>php.ini</yellow> settings with <literal>haze</literal> <arg>[filter]</arg> <literal>edit /config/php.ini</literal>"
|
Details = "note: you can overwrite <yellow>php.ini</yellow> settings with <literal>haze</literal> <arg>[filter]</arg> <literal>edit /config/php.ini</literal>"
|
||||||
))]
|
))]
|
||||||
|
|
|
||||||
29
src/cloud.rs
29
src/cloud.rs
|
|
@ -4,9 +4,9 @@ use crate::exec::{ExitCode, exec, exec_io, exec_tty};
|
||||||
use crate::git::{create_worktree, find_app_repo, init_submodules, remove_worktree};
|
use crate::git::{create_worktree, find_app_repo, init_submodules, remove_worktree};
|
||||||
use crate::mapping::{Mapping, for_config};
|
use crate::mapping::{Mapping, for_config};
|
||||||
use crate::php::PhpVersion;
|
use crate::php::PhpVersion;
|
||||||
|
use crate::service::Service;
|
||||||
use crate::service::ServiceTrait;
|
use crate::service::ServiceTrait;
|
||||||
use crate::service::deduplicate_services;
|
use crate::service::deduplicate_services;
|
||||||
use crate::service::{DbGate, Service};
|
|
||||||
use crate::sources::download_nc;
|
use crate::sources::download_nc;
|
||||||
use bollard::Docker;
|
use bollard::Docker;
|
||||||
use bollard::config::NetworkCreateRequest;
|
use bollard::config::NetworkCreateRequest;
|
||||||
|
|
@ -347,8 +347,6 @@ fn test_option_parse() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static DEFAULT_SERVICES: &[Service] = &[Service::DbGate(DbGate)];
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Cloud {
|
pub struct Cloud {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
|
|
@ -541,8 +539,6 @@ impl Cloud {
|
||||||
options
|
options
|
||||||
.services
|
.services
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|service| !service.hidden())
|
|
||||||
.chain(DEFAULT_SERVICES.iter())
|
|
||||||
.map(|service| service.spawn(docker, &id, &network, config, &options)),
|
.map(|service| service.spawn(docker, &id, &network, config, &options)),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
@ -721,9 +717,9 @@ impl Cloud {
|
||||||
.await
|
.await
|
||||||
.into_diagnostic()
|
.into_diagnostic()
|
||||||
.wrap_err("Failed to remove work directory")
|
.wrap_err("Failed to remove work directory")
|
||||||
{
|
{
|
||||||
eprintln!("{}", e);
|
eprintln!("{}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
remove_worktrees_for_instance(config, &self.id).await?;
|
remove_worktrees_for_instance(config, &self.id).await?;
|
||||||
|
|
||||||
|
|
@ -812,15 +808,14 @@ impl Cloud {
|
||||||
&& match filter.as_ref() {
|
&& match filter.as_ref() {
|
||||||
Some(filter) => cloud_id.contains(filter),
|
Some(filter) => cloud_id.contains(filter),
|
||||||
None => true,
|
None => true,
|
||||||
|
} {
|
||||||
|
let entry = containers_by_id.entry(cloud_id.to_string()).or_default();
|
||||||
|
if labels.get("haze-type").map(String::as_str) == Some("cloud") {
|
||||||
|
entry.0 = Some(container);
|
||||||
|
} else {
|
||||||
|
entry.1.push(container)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
{
|
|
||||||
let entry = containers_by_id.entry(cloud_id.to_string()).or_default();
|
|
||||||
if labels.get("haze-type").map(String::as_str) == Some("cloud") {
|
|
||||||
entry.0 = Some(container);
|
|
||||||
} else {
|
|
||||||
entry.1.push(container)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut sortable_containers: Vec<_> = containers_by_id
|
let mut sortable_containers: Vec<_> = containers_by_id
|
||||||
|
|
@ -974,7 +969,7 @@ impl Cloud {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn services(&self) -> impl Iterator<Item = &Service> {
|
pub fn services(&self) -> impl Iterator<Item = &Service> {
|
||||||
self.options.services.iter().chain(DEFAULT_SERVICES.iter())
|
self.options.services.iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn db(&self) -> &Database {
|
pub fn db(&self) -> &Database {
|
||||||
|
|
|
||||||
|
|
@ -201,14 +201,10 @@ pub struct ProxyConfig {
|
||||||
pub address: String,
|
pub address: String,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub https: bool,
|
pub https: bool,
|
||||||
#[serde(default)]
|
|
||||||
pub cert: Option<String>,
|
|
||||||
#[serde(default)]
|
|
||||||
pub key: Option<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProxyConfig {
|
impl ProxyConfig {
|
||||||
/// Get a public address for a service, either with direct IP or through the proxy
|
/// Get a public address for a service, either with direct ip or through the proxy
|
||||||
pub fn addr(&self, id: &str, ip: IpAddr) -> String {
|
pub fn addr(&self, id: &str, ip: IpAddr) -> String {
|
||||||
let clean_id = id.strip_prefix("haze-").unwrap_or(id);
|
let clean_id = id.strip_prefix("haze-").unwrap_or(id);
|
||||||
match (&self.address, self.https) {
|
match (&self.address, self.https) {
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
use crate::config::HazeConfig;
|
use crate::exec::{exec, exec_tty, ExitCode};
|
||||||
use crate::exec::{ExitCode, exec, exec_tty};
|
|
||||||
use crate::image::pull_image;
|
use crate::image::pull_image;
|
||||||
use bollard::Docker;
|
|
||||||
use bollard::config::ContainerCreateBody;
|
use bollard::config::ContainerCreateBody;
|
||||||
use bollard::models::{EndpointSettings, HostConfig, NetworkingConfig};
|
use bollard::models::{EndpointSettings, HostConfig, NetworkingConfig};
|
||||||
use bollard::query_parameters::CreateContainerOptions;
|
use bollard::query_parameters::CreateContainerOptions;
|
||||||
|
use bollard::Docker;
|
||||||
use maplit::hashmap;
|
use maplit::hashmap;
|
||||||
use miette::{IntoDiagnostic, Report, Result, WrapErr};
|
use miette::{IntoDiagnostic, Report, Result, WrapErr};
|
||||||
use std::io::{Stdout, stdout};
|
use std::io::{stdout, Stdout};
|
||||||
use std::net::{IpAddr, Ipv4Addr};
|
use std::net::IpAddr;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use strum::{Display, EnumIter, EnumProperty, IntoStaticStr};
|
use strum::{Display, EnumIter, EnumProperty, IntoStaticStr};
|
||||||
|
|
@ -32,39 +31,6 @@ impl DatabaseFamily {
|
||||||
pub fn name(&self) -> &'static str {
|
pub fn name(&self) -> &'static str {
|
||||||
self.into()
|
self.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn db_gate_driver(&self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
DatabaseFamily::Mysql => "mysql@dbgate-plugin-mysql",
|
|
||||||
DatabaseFamily::MariaDB => "mariadb@dbgate-plugin-mysql",
|
|
||||||
DatabaseFamily::Postgres => "postgres@dbgate-plugin-postgres",
|
|
||||||
DatabaseFamily::Oracle => "oracle@dbgate-plugin-oracle",
|
|
||||||
DatabaseFamily::Sqlite => "sqlite@dbgate-plugin-sqlite",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn port(&self) -> u16 {
|
|
||||||
match self {
|
|
||||||
DatabaseFamily::Mysql | DatabaseFamily::MariaDB => 3306,
|
|
||||||
DatabaseFamily::Postgres => 5432,
|
|
||||||
DatabaseFamily::Oracle => 1521,
|
|
||||||
DatabaseFamily::Sqlite => 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn username(&self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
DatabaseFamily::Oracle => "system",
|
|
||||||
_ => "haze",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn db(&self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
DatabaseFamily::Oracle => "SYSTEM",
|
|
||||||
_ => "haze",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Default)]
|
#[derive(Clone, Debug, Eq, PartialEq, Default)]
|
||||||
|
|
@ -448,37 +414,6 @@ impl Database {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn location(
|
|
||||||
&self,
|
|
||||||
docker: &Docker,
|
|
||||||
cloud_id: &str,
|
|
||||||
config: &HazeConfig,
|
|
||||||
) -> Result<String> {
|
|
||||||
let ip = match self.family() {
|
|
||||||
DatabaseFamily::Mysql
|
|
||||||
| DatabaseFamily::MariaDB
|
|
||||||
| DatabaseFamily::Postgres
|
|
||||||
| DatabaseFamily::Oracle => self
|
|
||||||
.ip(docker, cloud_id)
|
|
||||||
.await
|
|
||||||
.ok_or_else(|| Report::msg("Failed to get the IP of the database container"))?,
|
|
||||||
DatabaseFamily::Sqlite => IpAddr::V4(Ipv4Addr::LOCALHOST),
|
|
||||||
};
|
|
||||||
|
|
||||||
match self.family() {
|
|
||||||
DatabaseFamily::Mysql | DatabaseFamily::MariaDB => {
|
|
||||||
Ok(format!("mysql://haze:haze@{ip}/haze"))
|
|
||||||
}
|
|
||||||
DatabaseFamily::Postgres => Ok(format!("postgresql://haze:haze@{ip}/haze")),
|
|
||||||
DatabaseFamily::Oracle => Ok(format!("oracle://system:haze@{ip}:1521/XE")),
|
|
||||||
DatabaseFamily::Sqlite => Ok(config
|
|
||||||
.work_dir
|
|
||||||
.join(cloud_id)
|
|
||||||
.join("data/haze.db")
|
|
||||||
.to_string()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn is_healthy(&self, docker: &Docker, cloud_id: &str, postfix: &str) -> Result<bool> {
|
pub async fn is_healthy(&self, docker: &Docker, cloud_id: &str, postfix: &str) -> Result<bool> {
|
||||||
match self.family() {
|
match self.family() {
|
||||||
DatabaseFamily::Sqlite => Ok(true),
|
DatabaseFamily::Sqlite => Ok(true),
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ pub async fn exec_tty<S1: AsRef<str>, S2: Into<String>, Env: Into<String>>(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// set stdout in raw mode so we can do TTY stuff
|
// set stdout in raw mode so we can do tty stuff
|
||||||
let mut stdout = stdout.lock().into_raw_mode().into_diagnostic()?;
|
let mut stdout = stdout.lock().into_raw_mode().into_diagnostic()?;
|
||||||
|
|
||||||
// pipe docker exec output into stdout
|
// pipe docker exec output into stdout
|
||||||
|
|
|
||||||
14
src/help.rs
14
src/help.rs
|
|
@ -151,14 +151,12 @@ fn subcommand_help(command: &dyn SubCommand) {
|
||||||
for service in ServiceType::iter() {
|
for service in ServiceType::iter() {
|
||||||
let service: ServiceType = service;
|
let service: ServiceType = service;
|
||||||
let service_str: &'static str = service.into();
|
let service_str: &'static str = service.into();
|
||||||
if let Some(doc) = service.get_documentation() {
|
println!(
|
||||||
println!(
|
" {}{} {}",
|
||||||
" {}{} {}",
|
service.blue(),
|
||||||
service.blue(),
|
" ".repeat(max_service_len - service_str.len()),
|
||||||
" ".repeat(max_service_len - service_str.len()),
|
service.get_documentation().unwrap_or_default(),
|
||||||
doc,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use bollard::Docker;
|
|
||||||
use bollard::models::CreateImageInfo;
|
use bollard::models::CreateImageInfo;
|
||||||
use bollard::query_parameters::CreateImageOptions;
|
use bollard::query_parameters::CreateImageOptions;
|
||||||
|
use bollard::Docker;
|
||||||
use futures_util::StreamExt;
|
use futures_util::StreamExt;
|
||||||
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
|
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
|
||||||
use miette::{IntoDiagnostic, Result, WrapErr};
|
use miette::{IntoDiagnostic, Result, WrapErr};
|
||||||
|
|
|
||||||
91
src/main.rs
91
src/main.rs
|
|
@ -93,15 +93,8 @@ async fn main() -> Result<ExitCode> {
|
||||||
|
|
||||||
clear_networks(&docker, &retain).await?;
|
clear_networks(&docker, &retain).await?;
|
||||||
|
|
||||||
for cache_dir in config
|
for cache_dir in config.work_dir.read_dir().into_diagnostic()? {
|
||||||
.work_dir
|
let cache_dir = cache_dir.into_diagnostic()?;
|
||||||
.read_dir()
|
|
||||||
.into_diagnostic()
|
|
||||||
.wrap_err_with(|| format!("Failed to read dir {}", config.work_dir))?
|
|
||||||
{
|
|
||||||
let cache_dir = cache_dir
|
|
||||||
.into_diagnostic()
|
|
||||||
.wrap_err_with(|| "Failed to unwrap cache_dir")?;
|
|
||||||
if let Some(id) = cache_dir.file_name().to_str()
|
if let Some(id) = cache_dir.file_name().to_str()
|
||||||
&& id.starts_with("haze-")
|
&& id.starts_with("haze-")
|
||||||
&& !retain.iter().any(|cloud| cloud.id == id)
|
&& !retain.iter().any(|cloud| cloud.id == id)
|
||||||
|
|
@ -124,16 +117,12 @@ async fn main() -> Result<ExitCode> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
prune_worktrees(&config).wrap_err_with(|| "Failed to prune worktree")?;
|
prune_worktrees(&config)?;
|
||||||
}
|
}
|
||||||
HazeArgs::List { filter } => {
|
HazeArgs::List { filter } => {
|
||||||
let list = Cloud::list(&docker, filter, &config).await?;
|
let list = Cloud::list(&docker, filter, &config).await?;
|
||||||
for cloud in list {
|
for cloud in list {
|
||||||
let mut services: Vec<_> = cloud
|
let mut services: Vec<_> = cloud.services().map(Service::name).collect();
|
||||||
.services()
|
|
||||||
.filter(|service| !service.hidden())
|
|
||||||
.map(Service::name)
|
|
||||||
.collect();
|
|
||||||
services.push(cloud.db().name());
|
services.push(cloud.db().name());
|
||||||
let services = services.join(", ");
|
let services = services.join(", ");
|
||||||
let pin = if cloud.is_pinned(&docker).await.unwrap_or(false) {
|
let pin = if cloud.is_pinned(&docker).await.unwrap_or(false) {
|
||||||
|
|
@ -286,26 +275,11 @@ async fn main() -> Result<ExitCode> {
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
HazeArgs::Open { filter, service } => {
|
HazeArgs::Open { filter } => {
|
||||||
let cloud = Cloud::get_by_filter(&docker, filter, &config).await?;
|
let cloud = Cloud::get_by_filter(&docker, filter, &config).await?;
|
||||||
match service {
|
match cloud.ip {
|
||||||
Some(service) => {
|
Some(_) => opener::open(cloud.address).into_diagnostic()?,
|
||||||
let Some(ip) = service.get_ips(&docker, &cloud.id).await?.next() else {
|
None => eprintln!("{} is not running", cloud.id),
|
||||||
eprintln!(
|
|
||||||
"Service {} can't be opened as it has no associated IP",
|
|
||||||
service.name()
|
|
||||||
);
|
|
||||||
return Ok(ExitCode::FAILURE);
|
|
||||||
};
|
|
||||||
let addr = config
|
|
||||||
.proxy
|
|
||||||
.addr(&format!("{}-{}", cloud.id, service.name()), ip);
|
|
||||||
opener::open(addr).into_diagnostic()?;
|
|
||||||
}
|
|
||||||
None => match cloud.ip {
|
|
||||||
Some(_) => opener::open(cloud.address).into_diagnostic()?,
|
|
||||||
None => eprintln!("{} is not running", cloud.id),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
HazeArgs::Test { options, mut args } => {
|
HazeArgs::Test { options, mut args } => {
|
||||||
|
|
@ -448,15 +422,21 @@ async fn main() -> Result<ExitCode> {
|
||||||
let ip = cloud
|
let ip = cloud
|
||||||
.ip
|
.ip
|
||||||
.ok_or_else(|| Report::msg(format!("{} is not running", cloud.id)))?;
|
.ok_or_else(|| Report::msg(format!("{} is not running", cloud.id)))?;
|
||||||
match cloud.db().family() {
|
let db_type = match cloud.db().family() {
|
||||||
DatabaseFamily::Sqlite => {
|
DatabaseFamily::Sqlite => {
|
||||||
return Err(Report::msg("sqlite is not supported with `haze env`"));
|
return Err(Report::msg("sqlite is not supported with `haze env`"));
|
||||||
}
|
}
|
||||||
DatabaseFamily::Oracle => {
|
DatabaseFamily::Oracle => {
|
||||||
return Err(Report::msg("oracle is not supported with `haze env`"));
|
return Err(Report::msg("oracle is not supported with `haze env`"));
|
||||||
}
|
}
|
||||||
_ => {}
|
DatabaseFamily::Mysql | DatabaseFamily::MariaDB => "mysql",
|
||||||
}
|
DatabaseFamily::Postgres => "postgresql",
|
||||||
|
};
|
||||||
|
let db_ip = cloud
|
||||||
|
.db()
|
||||||
|
.ip(&docker, &cloud.id)
|
||||||
|
.await
|
||||||
|
.ok_or_else(|| Report::msg(format!("{}-db is not running", cloud.id)))?;
|
||||||
|
|
||||||
let mut command = Command::new(command);
|
let mut command = Command::new(command);
|
||||||
command
|
command
|
||||||
|
|
@ -465,7 +445,7 @@ async fn main() -> Result<ExitCode> {
|
||||||
.env("NEXTCLOUD_URL", &cloud.address)
|
.env("NEXTCLOUD_URL", &cloud.address)
|
||||||
.env(
|
.env(
|
||||||
"DATABASE_URL",
|
"DATABASE_URL",
|
||||||
cloud.db().location(&docker, &cloud.id, &config).await?,
|
format!("{}://haze:haze@{}/haze", db_type, db_ip),
|
||||||
);
|
);
|
||||||
|
|
||||||
if cloud.services().contains(&Service::RedisTls(RedisTls)) {
|
if cloud.services().contains(&Service::RedisTls(RedisTls)) {
|
||||||
|
|
@ -539,7 +519,16 @@ async fn main() -> Result<ExitCode> {
|
||||||
&docker,
|
&docker,
|
||||||
&cloud.id,
|
&cloud.id,
|
||||||
"root",
|
"root",
|
||||||
vec!["supervisorctl", "restart", "php-fpm"],
|
vec!["pkill", "php-fpm"],
|
||||||
|
Vec::<String>::new(),
|
||||||
|
Some(stdout()),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
exec(
|
||||||
|
&docker,
|
||||||
|
&cloud.id,
|
||||||
|
"root",
|
||||||
|
vec!["sh", "-c", "php-fpm --fpm-config /etc/php-fpm.conf&"],
|
||||||
Vec::<String>::new(),
|
Vec::<String>::new(),
|
||||||
Some(stdout()),
|
Some(stdout()),
|
||||||
)
|
)
|
||||||
|
|
@ -563,16 +552,6 @@ async fn setup(
|
||||||
let cloud = Cloud::create(docker, options, config).await?;
|
let cloud = Cloud::create(docker, options, config).await?;
|
||||||
println!("{}", cloud.address);
|
println!("{}", cloud.address);
|
||||||
let host = cloud.address.split_once("://").expect("no address?").1;
|
let host = cloud.address.split_once("://").expect("no address?").1;
|
||||||
|
|
||||||
match cloud.db().location(docker, &cloud.id, config).await {
|
|
||||||
Ok(value) => {
|
|
||||||
println!("Database: {}", value);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
println!("Failed to print DB location: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if always_setup || config.auto_setup.enabled {
|
if always_setup || config.auto_setup.enabled {
|
||||||
println!("Waiting for servers to start");
|
println!("Waiting for servers to start");
|
||||||
cloud.wait_for_start(docker).await?;
|
cloud.wait_for_start(docker).await?;
|
||||||
|
|
@ -635,20 +614,6 @@ async fn setup(
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
cloud
|
|
||||||
.occ(
|
|
||||||
docker,
|
|
||||||
vec![
|
|
||||||
"config:app:set",
|
|
||||||
"core",
|
|
||||||
"backgroundjobs_mode",
|
|
||||||
"--value",
|
|
||||||
"cron",
|
|
||||||
],
|
|
||||||
None,
|
|
||||||
Vec::<String>::default(),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let domains = [ip_str.as_str(), "cloud", &cloud.id, host];
|
let domains = [ip_str.as_str(), "cloud", &cloud.id, host];
|
||||||
for (i, domain) in domains.iter().enumerate() {
|
for (i, domain) in domains.iter().enumerate() {
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,7 @@ impl<'a> Mapping<'a> {
|
||||||
.wrap_err("Failed to create source file")?,
|
.wrap_err("Failed to create source file")?,
|
||||||
}
|
}
|
||||||
|
|
||||||
// For worktrees, pre-create mount points within /var/www/html to prevent docker from creating them as root.
|
// For worktrees, pre-create mountpoints within /var/www/html to prevent docker from creating them as root.
|
||||||
if self.target.starts_with("/var/www/html") {
|
if self.target.starts_with("/var/www/html") {
|
||||||
let local_target = source_root.join(
|
let local_target = source_root.join(
|
||||||
self.target
|
self.target
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::cloud::Cloud;
|
use crate::cloud::Cloud;
|
||||||
use bollard::Docker;
|
|
||||||
use bollard::config::NetworkCreateRequest;
|
use bollard::config::NetworkCreateRequest;
|
||||||
|
use bollard::Docker;
|
||||||
use miette::{IntoDiagnostic, Result, WrapErr};
|
use miette::{IntoDiagnostic, Result, WrapErr};
|
||||||
|
|
||||||
pub async fn clear_networks(docker: &Docker, instances: &[Cloud]) -> Result<()> {
|
pub async fn clear_networks(docker: &Docker, instances: &[Cloud]) -> Result<()> {
|
||||||
|
|
@ -12,10 +12,9 @@ pub async fn clear_networks(docker: &Docker, instances: &[Cloud]) -> Result<()>
|
||||||
for network in networks {
|
for network in networks {
|
||||||
if let Some(name) = network.name.as_deref()
|
if let Some(name) = network.name.as_deref()
|
||||||
&& let Some(id) = name.strip_prefix("haze-")
|
&& let Some(id) = name.strip_prefix("haze-")
|
||||||
&& !instances.iter().any(|cloud| cloud.id == id)
|
&& !instances.iter().any(|cloud| cloud.id == id) {
|
||||||
{
|
docker.remove_network(name).await.ok();
|
||||||
docker.remove_network(name).await.ok();
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
25
src/php.rs
25
src/php.rs
|
|
@ -1,13 +1,13 @@
|
||||||
use crate::config::ProxyConfig;
|
use crate::config::ProxyConfig;
|
||||||
use crate::database::Database;
|
use crate::database::Database;
|
||||||
use crate::image::{ImageVersion, image_version, pull_image};
|
use crate::image::{image_version, pull_image, ImageVersion};
|
||||||
use crate::network::ensure_network_exists;
|
use crate::network::ensure_network_exists;
|
||||||
use crate::service::Service;
|
use crate::service::Service;
|
||||||
use crate::service::ServiceTrait;
|
use crate::service::ServiceTrait;
|
||||||
use bollard::Docker;
|
|
||||||
use bollard::config::{ContainerCreateBody, NetworkConnectRequest, NetworkingConfig};
|
use bollard::config::{ContainerCreateBody, NetworkConnectRequest, NetworkingConfig};
|
||||||
use bollard::models::{EndpointSettings, HostConfig};
|
use bollard::models::{EndpointSettings, HostConfig};
|
||||||
use bollard::query_parameters::CreateContainerOptions;
|
use bollard::query_parameters::CreateContainerOptions;
|
||||||
|
use bollard::Docker;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use maplit::hashmap;
|
use maplit::hashmap;
|
||||||
use miette::{IntoDiagnostic, Report, Result, WrapErr};
|
use miette::{IntoDiagnostic, Report, Result, WrapErr};
|
||||||
|
|
@ -71,7 +71,7 @@ impl PhpVersion {
|
||||||
PhpVersion::Php82 => "8.2",
|
PhpVersion::Php82 => "8.2",
|
||||||
PhpVersion::Php83 => "8.3",
|
PhpVersion::Php83 => "8.3",
|
||||||
PhpVersion::Php84 => "8.4",
|
PhpVersion::Php84 => "8.4",
|
||||||
PhpVersion::Php85 => "8.5",
|
PhpVersion::Php85 => "8.4",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -127,16 +127,15 @@ impl PhpVersion {
|
||||||
let image_version = image_version(docker, self.image()).await;
|
let image_version = image_version(docker, self.image()).await;
|
||||||
let haze_version = ImageVersion::from_str(env!("CARGO_PKG_VERSION"));
|
let haze_version = ImageVersion::from_str(env!("CARGO_PKG_VERSION"));
|
||||||
if let (Some(image_version), Ok(haze_version)) = (image_version, haze_version)
|
if let (Some(image_version), Ok(haze_version)) = (image_version, haze_version)
|
||||||
&& image_version < haze_version
|
&& image_version < haze_version {
|
||||||
{
|
eprintln!(
|
||||||
eprintln!(
|
"{}: image version is out of date, run {} to update.",
|
||||||
"{}: image version is out of date, run {} to update.",
|
"Warning".red(),
|
||||||
"Warning".red(),
|
"haze update".blue()
|
||||||
"haze update".blue()
|
);
|
||||||
);
|
eprintln!(" Haze version: {}", haze_version.bright_yellow());
|
||||||
eprintln!(" Haze version: {}", haze_version.bright_yellow());
|
eprintln!(" Image version: {}", image_version.bright_yellow());
|
||||||
eprintln!(" Image version: {}", image_version.bright_yellow());
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let options = Some(CreateContainerOptions {
|
let options = Some(CreateContainerOptions {
|
||||||
name: Some(id.to_string()),
|
name: Some(id.to_string()),
|
||||||
|
|
|
||||||
81
src/proxy.rs
81
src/proxy.rs
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::Result;
|
|
||||||
use crate::service::{ServiceTrait, ServiceType};
|
use crate::service::{ServiceTrait, ServiceType};
|
||||||
|
use crate::Result;
|
||||||
use crate::{Cloud, HazeConfig};
|
use crate::{Cloud, HazeConfig};
|
||||||
use axum::http::HeaderValue;
|
|
||||||
use axum::http::header::HOST;
|
use axum::http::header::HOST;
|
||||||
|
use axum::http::HeaderValue;
|
||||||
use axum::{
|
use axum::{
|
||||||
body::Body,
|
body::Body,
|
||||||
extract::Request,
|
extract::Request,
|
||||||
|
|
@ -10,13 +10,13 @@ use axum::{
|
||||||
};
|
};
|
||||||
use bollard::Docker;
|
use bollard::Docker;
|
||||||
use futures_util::StreamExt;
|
use futures_util::StreamExt;
|
||||||
use hyper::StatusCode;
|
|
||||||
use hyper::body::Incoming;
|
use hyper::body::Incoming;
|
||||||
use hyper::server::conn::http1;
|
use hyper::server::conn::http1;
|
||||||
use hyper::service::service_fn;
|
use hyper::service::service_fn;
|
||||||
|
use hyper::StatusCode;
|
||||||
use hyper_util::rt::TokioIo;
|
use hyper_util::rt::TokioIo;
|
||||||
use hyper_util::{client::legacy::connect::HttpConnector, rt::TokioExecutor};
|
use hyper_util::{client::legacy::connect::HttpConnector, rt::TokioExecutor};
|
||||||
use miette::{IntoDiagnostic, miette};
|
use miette::{miette, IntoDiagnostic};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::convert::Infallible;
|
use std::convert::Infallible;
|
||||||
use std::fs::{create_dir_all, set_permissions};
|
use std::fs::{create_dir_all, set_permissions};
|
||||||
|
|
@ -32,10 +32,6 @@ use tokio::net::UnixListener;
|
||||||
use tokio::signal::ctrl_c;
|
use tokio::signal::ctrl_c;
|
||||||
use tokio::spawn;
|
use tokio::spawn;
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
use tokio_rustls::TlsAcceptor;
|
|
||||||
use tokio_rustls::rustls::ServerConfig;
|
|
||||||
use tokio_rustls::rustls::pki_types::pem::PemObject;
|
|
||||||
use tokio_rustls::rustls::pki_types::{CertificateDer, PrivateKeyDer};
|
|
||||||
use tokio_stream::wrappers::{TcpListenerStream, UnixListenerStream};
|
use tokio_stream::wrappers::{TcpListenerStream, UnixListenerStream};
|
||||||
use tracing::{debug, error, info};
|
use tracing::{debug, error, info};
|
||||||
|
|
||||||
|
|
@ -151,34 +147,9 @@ pub async fn proxy(docker: Docker, config: HazeConfig) -> Result<()> {
|
||||||
}
|
}
|
||||||
let listen = config.proxy.listen.clone();
|
let listen = config.proxy.listen.clone();
|
||||||
|
|
||||||
let acceptor = match (&config.proxy.cert, &config.proxy.key) {
|
|
||||||
(None, None) => None,
|
|
||||||
(Some(_), None) => return Err(miette!("`cert` is set without `key`")),
|
|
||||||
(None, Some(_)) => return Err(miette!("`key` is set without `cert`")),
|
|
||||||
(Some(cert), Some(key)) => Some(tls_acceptor(cert, key)?),
|
|
||||||
};
|
|
||||||
|
|
||||||
let base_address = config.proxy.address.clone();
|
let base_address = config.proxy.address.clone();
|
||||||
let instances = ActiveInstances::new(docker, config);
|
let instances = ActiveInstances::new(docker, config);
|
||||||
serve(instances, listen, base_address, acceptor).await
|
serve(instances, listen, base_address).await
|
||||||
}
|
|
||||||
|
|
||||||
/// Build a TLS acceptor from a PEM encoded certificate chain and private key on disk
|
|
||||||
fn tls_acceptor(cert: &str, key: &str) -> Result<TlsAcceptor> {
|
|
||||||
let certs = CertificateDer::pem_file_iter(cert)
|
|
||||||
.map_err(|e| miette!("failed to load certificate from {cert}: {e}"))?
|
|
||||||
.collect::<Result<Vec<_>, _>>()
|
|
||||||
.map_err(|e| miette!("failed to load certificate from {cert}: {e}"))?;
|
|
||||||
let key = PrivateKeyDer::from_pem_file(key)
|
|
||||||
.map_err(|e| miette!("failed to load private key from {key}: {e}"))?;
|
|
||||||
|
|
||||||
let mut server_config = ServerConfig::builder()
|
|
||||||
.with_no_client_auth()
|
|
||||||
.with_single_cert(certs, key)
|
|
||||||
.into_diagnostic()?;
|
|
||||||
server_config.alpn_protocols = vec![b"http/1.1".to_vec()];
|
|
||||||
|
|
||||||
Ok(TlsAcceptor::from(Arc::new(server_config)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
@ -188,12 +159,7 @@ struct AppState {
|
||||||
proxy_client: Arc<Client>,
|
proxy_client: Arc<Client>,
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn serve(
|
async fn serve(instances: ActiveInstances, listen: String, base_address: String) -> Result<()> {
|
||||||
instances: ActiveInstances,
|
|
||||||
listen: String,
|
|
||||||
base_address: String,
|
|
||||||
acceptor: Option<TlsAcceptor>,
|
|
||||||
) -> Result<()> {
|
|
||||||
let instances = Arc::new(instances);
|
let instances = Arc::new(instances);
|
||||||
let base_address = Arc::new(base_address);
|
let base_address = Arc::new(base_address);
|
||||||
let last_instances = instances.clone();
|
let last_instances = instances.clone();
|
||||||
|
|
@ -222,13 +188,12 @@ async fn serve(
|
||||||
if !listen.starts_with('/') {
|
if !listen.starts_with('/') {
|
||||||
let addr: SocketAddr = listen.parse().into_diagnostic()?;
|
let addr: SocketAddr = listen.parse().into_diagnostic()?;
|
||||||
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
|
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
|
||||||
let scheme = if acceptor.is_some() { "https" } else { "http" };
|
println!("listening on {}", listener.local_addr().unwrap());
|
||||||
println!("Listening on {scheme}://{}", listener.local_addr().unwrap());
|
|
||||||
let mut connections = pin!(TcpListenerStream::new(listener).take_until(cancel));
|
let mut connections = pin!(TcpListenerStream::new(listener).take_until(cancel));
|
||||||
|
|
||||||
while let Some(stream) = connections.next().await {
|
while let Some(stream) = connections.next().await {
|
||||||
match stream {
|
match stream {
|
||||||
Ok(stream) => handle_connection(state.clone(), stream, acceptor.clone()).await,
|
Ok(stream) => handle_connection(state.clone(), stream),
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
error!(%error, "connection failed");
|
error!(%error, "connection failed");
|
||||||
}
|
}
|
||||||
|
|
@ -237,11 +202,10 @@ async fn serve(
|
||||||
} else {
|
} else {
|
||||||
let listen: PathBuf = listen.into();
|
let listen: PathBuf = listen.into();
|
||||||
if let Some(parent) = listen.parent()
|
if let Some(parent) = listen.parent()
|
||||||
&& !parent.exists()
|
&& !parent.exists() {
|
||||||
{
|
create_dir_all(parent).into_diagnostic()?;
|
||||||
create_dir_all(parent).into_diagnostic()?;
|
set_permissions(parent, PermissionsExt::from_mode(0o755)).into_diagnostic()?;
|
||||||
set_permissions(parent, PermissionsExt::from_mode(0o755)).into_diagnostic()?;
|
}
|
||||||
}
|
|
||||||
let _ = tokio::fs::remove_file(&listen).await;
|
let _ = tokio::fs::remove_file(&listen).await;
|
||||||
|
|
||||||
let listener = UnixListener::bind(&listen).unwrap();
|
let listener = UnixListener::bind(&listen).unwrap();
|
||||||
|
|
@ -252,7 +216,7 @@ async fn serve(
|
||||||
|
|
||||||
while let Some(stream) = connections.next().await {
|
while let Some(stream) = connections.next().await {
|
||||||
match stream {
|
match stream {
|
||||||
Ok(stream) => handle_connection(state.clone(), stream, None).await,
|
Ok(stream) => handle_connection(state.clone(), stream),
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
error!(%error, "connection failed");
|
error!(%error, "connection failed");
|
||||||
}
|
}
|
||||||
|
|
@ -263,22 +227,7 @@ async fn serve(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_connection<I: AsyncRead + AsyncWrite + Unpin + Send + 'static>(
|
fn handle_connection<I: AsyncRead + AsyncWrite + Unpin + Send + 'static>(
|
||||||
state: AppState,
|
|
||||||
stream: I,
|
|
||||||
acceptor: Option<TlsAcceptor>,
|
|
||||||
) {
|
|
||||||
// Spawn a tokio task to serve multiple connections concurrently
|
|
||||||
match acceptor {
|
|
||||||
Some(acceptor) => match acceptor.accept(stream).await {
|
|
||||||
Ok(stream) => serve_connection(state, stream).await,
|
|
||||||
Err(error) => error!(%error, "tls handshake failed"),
|
|
||||||
},
|
|
||||||
None => serve_connection(state, stream).await,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn serve_connection<I: AsyncRead + AsyncWrite + Unpin + Send + 'static>(
|
|
||||||
state: AppState,
|
state: AppState,
|
||||||
stream: I,
|
stream: I,
|
||||||
) {
|
) {
|
||||||
|
|
@ -338,7 +287,7 @@ async fn handler(state: AppState, mut req: Request<Incoming>) -> Result<Response
|
||||||
return Ok(hyper::Response::builder()
|
return Ok(hyper::Response::builder()
|
||||||
.status(StatusCode::BAD_REQUEST)
|
.status(StatusCode::BAD_REQUEST)
|
||||||
.body(e.into())
|
.body(e.into())
|
||||||
.unwrap());
|
.unwrap())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{fs::read_to_string, iter::once};
|
use std::fs::read_to_string;
|
||||||
|
|
||||||
use bollard::Docker;
|
use bollard::Docker;
|
||||||
use camino::Utf8PathBuf;
|
use camino::Utf8PathBuf;
|
||||||
|
|
@ -36,17 +36,15 @@ pub async fn run_script(
|
||||||
let contents = read_to_string(&path)
|
let contents = read_to_string(&path)
|
||||||
.into_diagnostic()
|
.into_diagnostic()
|
||||||
.wrap_err_with(|| format!("Failed to read script file {path}"))?;
|
.wrap_err_with(|| format!("Failed to read script file {path}"))?;
|
||||||
let script = parse_script(&contents, config)
|
let (mut options, mode) = parse_script(&contents, config)
|
||||||
.wrap_err_with(|| format!("Failed to parse script file {path}"))?;
|
.wrap_err_with(|| format!("Failed to parse script file {path}"))?;
|
||||||
|
let mode = mode_override.unwrap_or(mode);
|
||||||
let mode = mode_override.unwrap_or(script.mode);
|
|
||||||
let mut options = script.options;
|
|
||||||
|
|
||||||
let target_path = Utf8PathBuf::from(format!("/{}", path.file_name().unwrap()));
|
let target_path = Utf8PathBuf::from(format!("/{}", path.file_name().unwrap()));
|
||||||
let shell = match (script.shell, path.extension()) {
|
let shell = if path.extension() == Some("nu") {
|
||||||
(Some(shell), _) => shell,
|
"nu"
|
||||||
(None, Some("nu")) => vec!["nu".into()],
|
} else {
|
||||||
_ => vec!["bash".into()],
|
"bash"
|
||||||
};
|
};
|
||||||
|
|
||||||
options.mappings.push(
|
options.mappings.push(
|
||||||
|
|
@ -63,10 +61,7 @@ pub async fn run_script(
|
||||||
cloud
|
cloud
|
||||||
.exec(
|
.exec(
|
||||||
docker,
|
docker,
|
||||||
shell
|
vec![shell.to_string(), target_path.into_string()],
|
||||||
.into_iter()
|
|
||||||
.chain(once(target_path.into_string()))
|
|
||||||
.collect(),
|
|
||||||
true,
|
true,
|
||||||
get_forward_env(),
|
get_forward_env(),
|
||||||
)
|
)
|
||||||
|
|
@ -79,22 +74,14 @@ pub async fn run_script(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
fn parse_script(script: &str, config: &HazeConfig) -> Result<(CloudOptions, ScriptMode)> {
|
||||||
struct Script {
|
let (options, mode) = script
|
||||||
options: CloudOptions,
|
|
||||||
mode: ScriptMode,
|
|
||||||
shell: Option<Vec<String>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_script(script: &str, config: &HazeConfig) -> Result<Script> {
|
|
||||||
let (options, mode, line_num) = script
|
|
||||||
.lines()
|
.lines()
|
||||||
.enumerate()
|
.find_map(|line| line.strip_prefix("#! haze "))
|
||||||
.find_map(|(i, line)| Some((i, line.strip_prefix("#! haze ")?)))
|
|
||||||
.and_then(
|
.and_then(
|
||||||
|(i, line)| match (line.strip_prefix("shell"), line.strip_prefix("start")) {
|
|line| match (line.strip_prefix("shell"), line.strip_prefix("start")) {
|
||||||
(Some(shell_options), None) => Some((shell_options.trim(), ScriptMode::Shell, i)),
|
(Some(shell_options), None) => Some((shell_options.trim(), ScriptMode::Shell)),
|
||||||
(None, Some(script_options)) => Some((script_options.trim(), ScriptMode::Start, i)),
|
(None, Some(script_options)) => Some((script_options.trim(), ScriptMode::Start)),
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
@ -103,19 +90,5 @@ fn parse_script(script: &str, config: &HazeConfig) -> Result<Script> {
|
||||||
|
|
||||||
let options =
|
let options =
|
||||||
CloudOptions::parse(config, &mut options).wrap_err("Failed to parse shell options")?;
|
CloudOptions::parse(config, &mut options).wrap_err("Failed to parse shell options")?;
|
||||||
|
Ok((options, mode))
|
||||||
let first_content_line = script.lines().nth(line_num + 1).unwrap_or_default();
|
|
||||||
let shell = first_content_line.strip_prefix("#!").map(|bang| {
|
|
||||||
bang.split(" ")
|
|
||||||
.map(str::trim)
|
|
||||||
.filter(|s| !s.is_empty())
|
|
||||||
.map(String::from)
|
|
||||||
.collect()
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(Script {
|
|
||||||
options,
|
|
||||||
mode,
|
|
||||||
shell,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
mod authentik;
|
mod authentik;
|
||||||
mod clam;
|
mod clam;
|
||||||
mod dav;
|
mod dav;
|
||||||
mod dbgate;
|
|
||||||
mod imaginary;
|
mod imaginary;
|
||||||
mod kaspersky;
|
mod kaspersky;
|
||||||
mod ldap;
|
mod ldap;
|
||||||
|
|
@ -23,7 +22,6 @@ use crate::config::{HazeConfig, Preset, ProxyConfig};
|
||||||
pub use crate::service::authentik::{Authentik, AuthentikOidc, AuthentikSaml, AuthentikScim};
|
pub use crate::service::authentik::{Authentik, AuthentikOidc, AuthentikSaml, AuthentikScim};
|
||||||
pub use crate::service::clam::{Clam, ClamIcap, ClamIcapTls, ClamSocket};
|
pub use crate::service::clam::{Clam, ClamIcap, ClamIcapTls, ClamSocket};
|
||||||
use crate::service::dav::Dav;
|
use crate::service::dav::Dav;
|
||||||
pub use crate::service::dbgate::DbGate;
|
|
||||||
use crate::service::imaginary::Imaginary;
|
use crate::service::imaginary::Imaginary;
|
||||||
use crate::service::kaspersky::{Kaspersky, KasperskyIcap};
|
use crate::service::kaspersky::{Kaspersky, KasperskyIcap};
|
||||||
pub use crate::service::ldap::{Ldap, LdapAdmin};
|
pub use crate::service::ldap::{Ldap, LdapAdmin};
|
||||||
|
|
@ -61,10 +59,6 @@ pub trait ServiceTrait {
|
||||||
&[]
|
&[]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hidden(&self) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn spawn(
|
async fn spawn(
|
||||||
&self,
|
&self,
|
||||||
_docker: &Docker,
|
_docker: &Docker,
|
||||||
|
|
@ -212,7 +206,7 @@ pub trait ServiceTrait {
|
||||||
"bash"
|
"bash"
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allow services to delay the completion of the `start` command until they are fully setup
|
/// Allow services to delay the completion of the `start` command untill they are fully setup
|
||||||
///
|
///
|
||||||
/// This is not for services which are critical for the running of the instance (use `is_healthy` for that)
|
/// This is not for services which are critical for the running of the instance (use `is_healthy` for that)
|
||||||
/// But instead for cases where the instance is mostly usable, but might have some missing features
|
/// But instead for cases where the instance is mostly usable, but might have some missing features
|
||||||
|
|
@ -242,14 +236,6 @@ impl ServiceTrait for FrankenPhp {
|
||||||
"franken-php"
|
"franken-php"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn container_name(&self, cloud_id: &str) -> Option<String> {
|
|
||||||
Some(cloud_id.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn proxy_port(&self) -> u16 {
|
|
||||||
2019
|
|
||||||
}
|
|
||||||
|
|
||||||
fn env(&self) -> &[&str] {
|
fn env(&self) -> &[&str] {
|
||||||
&["FRANKENPHP=1"]
|
&["FRANKENPHP=1"]
|
||||||
}
|
}
|
||||||
|
|
@ -270,18 +256,18 @@ pub enum ServiceType {
|
||||||
S3mb,
|
S3mb,
|
||||||
/// Azure Primary storage and external storage
|
/// Azure Primary storage and external storage
|
||||||
Azure,
|
Azure,
|
||||||
/// LDAP user backend
|
/// Ldap user backend
|
||||||
Ldap,
|
Ldap,
|
||||||
/// LDAP admin interface
|
/// Ldap admin interface
|
||||||
LdapAdmin,
|
LdapAdmin,
|
||||||
/// OnlyOffice
|
/// OnlyOffice
|
||||||
#[strum(serialize = "onlyoffice", serialize = "only-office")]
|
#[strum(serialize = "onlyoffice", serialize = "only-office")]
|
||||||
OnlyOffice,
|
OnlyOffice,
|
||||||
/// LibreOffice online
|
/// Libre office online
|
||||||
Office,
|
Office,
|
||||||
/// notify_push
|
/// notify_push
|
||||||
Push,
|
Push,
|
||||||
/// SMB external storage
|
/// Smb external storage
|
||||||
Smb,
|
Smb,
|
||||||
/// Database sharding
|
/// Database sharding
|
||||||
#[strum(serialize = "sharding", serialize = "sharded")]
|
#[strum(serialize = "sharding", serialize = "sharded")]
|
||||||
|
|
@ -299,17 +285,17 @@ pub enum ServiceType {
|
||||||
SingleShard,
|
SingleShard,
|
||||||
/// WebDav external storage
|
/// WebDav external storage
|
||||||
Dav,
|
Dav,
|
||||||
/// SFTP external storage
|
/// Sftp external storage
|
||||||
Sftp,
|
Sftp,
|
||||||
/// SFTP external storage with public key authentication
|
/// Sftp external storage with public key authentication
|
||||||
SftpKey,
|
SftpKey,
|
||||||
/// ownCloud instance for migration
|
/// ownCloud instance for migration
|
||||||
Oc,
|
Oc,
|
||||||
/// Imaginary for preview generation
|
/// Imaginary for preview generation
|
||||||
Imaginary,
|
Imaginary,
|
||||||
/// Kaspersky antivirus in HTTP mode
|
/// Kaspersky antivirus in http mode
|
||||||
Kaspersky,
|
Kaspersky,
|
||||||
/// Kaspersky antivirus in ICAP mode
|
/// Kaspersky antivirus in icap mode
|
||||||
KasperskyIcap,
|
KasperskyIcap,
|
||||||
/// Kaspersky antivirus in local binary
|
/// Kaspersky antivirus in local binary
|
||||||
#[strum(serialize = "clamav", serialize = "clam")]
|
#[strum(serialize = "clamav", serialize = "clam")]
|
||||||
|
|
@ -320,23 +306,23 @@ pub enum ServiceType {
|
||||||
/// Kaspersky antivirus in local socket mode
|
/// Kaspersky antivirus in local socket mode
|
||||||
#[strum(serialize = "clamav-socket", serialize = "clam-socket")]
|
#[strum(serialize = "clamav-socket", serialize = "clam-socket")]
|
||||||
ClamAvSocket,
|
ClamAvSocket,
|
||||||
/// Kaspersky antivirus in ICAP mode
|
/// Kaspersky antivirus in icap mode
|
||||||
#[strum(serialize = "clamav-icap", serialize = "clam-icap")]
|
#[strum(serialize = "clamav-icap", serialize = "clam-icap")]
|
||||||
ClamAvIcap,
|
ClamAvIcap,
|
||||||
/// Kaspersky antivirus in ICAP mode with TLS
|
/// Kaspersky antivirus in icap mode with TLS
|
||||||
#[strum(serialize = "clamav-icap-tls", serialize = "clam-icap-tls")]
|
#[strum(serialize = "clamav-icap-tls", serialize = "clam-icap-tls")]
|
||||||
ClamAvIcapTls,
|
ClamAvIcapTls,
|
||||||
/// Mail server
|
/// Mail server
|
||||||
Mail,
|
Mail,
|
||||||
/// External Redis instance
|
/// External redis instance
|
||||||
Redis,
|
Redis,
|
||||||
/// External Redis instance with TLS
|
/// External redis instance with TLS
|
||||||
RedisTls,
|
RedisTls,
|
||||||
/// Use FrankenPHP instead of PHP-FPM
|
/// Use FrankenPHP instead of PHP-FPM
|
||||||
FrankenPhp,
|
FrankenPhp,
|
||||||
/// Webhook test listener
|
/// Webhook test listener
|
||||||
Webhook,
|
Webhook,
|
||||||
/// Enable DB partitioning for MariaDB
|
/// Enable DB partitioning for mariadb
|
||||||
Partitioning,
|
Partitioning,
|
||||||
/// Authentik identity provider
|
/// Authentik identity provider
|
||||||
Authentik,
|
Authentik,
|
||||||
|
|
@ -346,8 +332,6 @@ pub enum ServiceType {
|
||||||
Oidc,
|
Oidc,
|
||||||
/// Configure Authentik as a SCIM server for Nextcloud
|
/// Configure Authentik as a SCIM server for Nextcloud
|
||||||
Scim,
|
Scim,
|
||||||
#[strum(serialize = "db", serialize = "db-gate")]
|
|
||||||
DbGate,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[enum_dispatch]
|
#[enum_dispatch]
|
||||||
|
|
@ -385,7 +369,6 @@ pub enum Service {
|
||||||
AuthentikSaml(AuthentikSaml),
|
AuthentikSaml(AuthentikSaml),
|
||||||
AuthentikOidc(AuthentikOidc),
|
AuthentikOidc(AuthentikOidc),
|
||||||
AuthentikScim(AuthentikScim),
|
AuthentikScim(AuthentikScim),
|
||||||
DbGate(DbGate),
|
|
||||||
Preset(PresetService),
|
Preset(PresetService),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -445,7 +428,6 @@ impl Service {
|
||||||
Service::Authentik(Authentik),
|
Service::Authentik(Authentik),
|
||||||
Service::AuthentikScim(AuthentikScim),
|
Service::AuthentikScim(AuthentikScim),
|
||||||
]),
|
]),
|
||||||
ServiceType::DbGate => Some(vec![Service::DbGate(DbGate)]),
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
presets
|
presets
|
||||||
|
|
@ -475,7 +457,7 @@ impl Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove duplicate services.
|
// Remove duplicate services.
|
||||||
// Useful for Authentik as it is needed by SAML and OIDC.
|
// Useful for Authentik as it is needed by saml and oidc.
|
||||||
pub fn deduplicate_services(services: impl IntoIterator<Item = Service>) -> Vec<Service> {
|
pub fn deduplicate_services(services: impl IntoIterator<Item = Service>) -> Vec<Service> {
|
||||||
let mut collected = Vec::new();
|
let mut collected = Vec::new();
|
||||||
for service in services {
|
for service in services {
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,7 @@ async fn spawn_authentik(
|
||||||
if !config.proxy.address.is_empty() {
|
if !config.proxy.address.is_empty() {
|
||||||
let url = config.proxy.addr_with_port(
|
let url = config.proxy.addr_with_port(
|
||||||
&container_name(cloud_id, "server"),
|
&container_name(cloud_id, "server"),
|
||||||
IpAddr::V4(Ipv4Addr::LOCALHOST), // IP argument is unused when a proxy is configured
|
IpAddr::V4(Ipv4Addr::LOCALHOST), // ip arg is unused when a proxy is configured
|
||||||
AUTHENTIK_PORT,
|
AUTHENTIK_PORT,
|
||||||
);
|
);
|
||||||
env.push(format!("AUTHENTIK_WEB__BASE_URL={url}"));
|
env.push(format!("AUTHENTIK_WEB__BASE_URL={url}"));
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ impl ServiceTrait for AuthentikSaml {
|
||||||
"--silent".into(),
|
"--silent".into(),
|
||||||
"1".into(),
|
"1".into(),
|
||||||
"--general-idp0_display_name=Authentik SAML".into(),
|
"--general-idp0_display_name=Authentik SAML".into(),
|
||||||
"--general-uid_mapping=http://haze.test/nextcloud/uid".to_string(),
|
format!("--general-uid_mapping=http://haze.test/nextcloud/uid"),
|
||||||
format!("--idp-entityId={authentik_url}/application/saml/nextcloud-saml/metadata/"),
|
format!("--idp-entityId={authentik_url}/application/saml/nextcloud-saml/metadata/"),
|
||||||
format!("--idp-singleSignOnService.url={authentik_url}/application/saml/nextcloud-saml/"),
|
format!("--idp-singleSignOnService.url={authentik_url}/application/saml/nextcloud-saml/"),
|
||||||
format!("--idp-singleLogoutService.url={authentik_url}/application/saml/nextcloud-saml/"),
|
format!("--idp-singleLogoutService.url={authentik_url}/application/saml/nextcloud-saml/"),
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
use crate::Result;
|
|
||||||
use crate::cloud::CloudOptions;
|
use crate::cloud::CloudOptions;
|
||||||
use crate::config::HazeConfig;
|
use crate::config::HazeConfig;
|
||||||
use crate::exec::exec;
|
use crate::exec::exec;
|
||||||
use crate::image::pull_image;
|
use crate::image::pull_image;
|
||||||
use crate::service::{ServiceTrait, split_cmnd};
|
use crate::service::{split_cmnd, ServiceTrait};
|
||||||
use bollard::Docker;
|
use crate::Result;
|
||||||
use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig};
|
use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig};
|
||||||
use bollard::query_parameters::CreateContainerOptions;
|
use bollard::query_parameters::CreateContainerOptions;
|
||||||
|
use bollard::Docker;
|
||||||
use maplit::hashmap;
|
use maplit::hashmap;
|
||||||
use miette::{IntoDiagnostic, WrapErr};
|
use miette::{IntoDiagnostic, WrapErr};
|
||||||
use tokio::fs::write;
|
use tokio::fs::write;
|
||||||
|
|
@ -92,9 +92,7 @@ impl ServiceTrait for ClamIcap {
|
||||||
split_cmnd("occ config:app:set files_antivirus av_host --value=clamav-icap"),
|
split_cmnd("occ config:app:set files_antivirus av_host --value=clamav-icap"),
|
||||||
split_cmnd("occ config:app:set files_antivirus av_port --value=1344"),
|
split_cmnd("occ config:app:set files_antivirus av_port --value=1344"),
|
||||||
split_cmnd("occ config:app:set files_antivirus av_icap_request_service --value=avscan"),
|
split_cmnd("occ config:app:set files_antivirus av_icap_request_service --value=avscan"),
|
||||||
split_cmnd(
|
split_cmnd("occ config:app:set files_antivirus av_icap_response_header --value=X-Infection-Found"),
|
||||||
"occ config:app:set files_antivirus av_icap_response_header --value=X-Infection-Found",
|
|
||||||
),
|
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -198,9 +196,7 @@ impl ServiceTrait for ClamIcapTls {
|
||||||
split_cmnd("occ config:app:set files_antivirus av_host --value=clamav-icap-tls"),
|
split_cmnd("occ config:app:set files_antivirus av_host --value=clamav-icap-tls"),
|
||||||
split_cmnd("occ config:app:set files_antivirus av_port --value=1345"),
|
split_cmnd("occ config:app:set files_antivirus av_port --value=1345"),
|
||||||
split_cmnd("occ config:app:set files_antivirus av_icap_request_service --value=avscan"),
|
split_cmnd("occ config:app:set files_antivirus av_icap_request_service --value=avscan"),
|
||||||
split_cmnd(
|
split_cmnd("occ config:app:set files_antivirus av_icap_response_header --value=X-Infection-Found"),
|
||||||
"occ config:app:set files_antivirus av_icap_response_header --value=X-Infection-Found",
|
|
||||||
),
|
|
||||||
split_cmnd("occ security:certificates:import data/icap-cert.pem"),
|
split_cmnd("occ security:certificates:import data/icap-cert.pem"),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
use crate::Result;
|
|
||||||
use crate::cloud::CloudOptions;
|
use crate::cloud::CloudOptions;
|
||||||
use crate::config::HazeConfig;
|
use crate::config::HazeConfig;
|
||||||
use crate::image::pull_image;
|
use crate::image::pull_image;
|
||||||
use crate::service::{ServiceTrait, split_cmnd};
|
use crate::service::{split_cmnd, ServiceTrait};
|
||||||
use bollard::Docker;
|
use crate::Result;
|
||||||
use bollard::config::ContainerCreateBody;
|
use bollard::config::ContainerCreateBody;
|
||||||
use bollard::models::{EndpointSettings, HostConfig, NetworkingConfig};
|
use bollard::models::{EndpointSettings, HostConfig, NetworkingConfig};
|
||||||
use bollard::query_parameters::CreateContainerOptions;
|
use bollard::query_parameters::CreateContainerOptions;
|
||||||
|
use bollard::Docker;
|
||||||
use maplit::hashmap;
|
use maplit::hashmap;
|
||||||
use miette::IntoDiagnostic;
|
use miette::IntoDiagnostic;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,133 +0,0 @@
|
||||||
use std::io::Stdout;
|
|
||||||
|
|
||||||
use crate::Result;
|
|
||||||
use crate::cloud::CloudOptions;
|
|
||||||
use crate::config::HazeConfig;
|
|
||||||
use crate::database::DatabaseFamily;
|
|
||||||
use crate::exec::exec;
|
|
||||||
use crate::image::pull_image;
|
|
||||||
use crate::mapping::Mapping;
|
|
||||||
use crate::service::ServiceTrait;
|
|
||||||
use bollard::Docker;
|
|
||||||
use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig};
|
|
||||||
use bollard::query_parameters::CreateContainerOptions;
|
|
||||||
use maplit::hashmap;
|
|
||||||
use miette::IntoDiagnostic;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
|
||||||
pub struct DbGate;
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl ServiceTrait for DbGate {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"db"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn hidden(&self) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn spawn(
|
|
||||||
&self,
|
|
||||||
docker: &Docker,
|
|
||||||
cloud_id: &str,
|
|
||||||
network: &str,
|
|
||||||
config: &HazeConfig,
|
|
||||||
options: &CloudOptions,
|
|
||||||
) -> Result<Vec<String>> {
|
|
||||||
let image = "dbgate/dbgate:7.3.1";
|
|
||||||
pull_image(docker, image).await?;
|
|
||||||
let container_options = Some(CreateContainerOptions {
|
|
||||||
name: self.container_name(cloud_id),
|
|
||||||
..CreateContainerOptions::default()
|
|
||||||
});
|
|
||||||
|
|
||||||
let db_family = options.db.family();
|
|
||||||
|
|
||||||
let mut env = vec![
|
|
||||||
"CONNECTIONS=con1".into(),
|
|
||||||
"USAGE_ANALYTICS=false".into(),
|
|
||||||
"LOCAL_DBGATE_CLOUD=1".into(),
|
|
||||||
"LABEL_con1=haze".into(),
|
|
||||||
format!("ENGINE_con1={}", db_family.db_gate_driver()),
|
|
||||||
];
|
|
||||||
|
|
||||||
if db_family == DatabaseFamily::Sqlite {
|
|
||||||
env.extend(["FILE_con1=/haze-data/haze.db".into()]);
|
|
||||||
} else {
|
|
||||||
env.extend([
|
|
||||||
"SERVER_con1=db".into(),
|
|
||||||
format!("USER_con1={}", db_family.username()),
|
|
||||||
format!("PORT_con1={}", db_family.port()),
|
|
||||||
format!("DATABASE_con1={}", db_family.db()),
|
|
||||||
"PASSWORD_con1=haze".into(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if db_family == DatabaseFamily::Oracle {
|
|
||||||
env.push("SERVICE_NAME_con1=xe".into());
|
|
||||||
}
|
|
||||||
|
|
||||||
let mapping = Mapping::new(
|
|
||||||
crate::mapping::MappingSourceType::WorkDir,
|
|
||||||
"data",
|
|
||||||
"/haze-data",
|
|
||||||
);
|
|
||||||
|
|
||||||
let config = ContainerCreateBody {
|
|
||||||
image: Some(image.into()),
|
|
||||||
host_config: Some(HostConfig {
|
|
||||||
network_mode: Some(network.to_string()),
|
|
||||||
binds: Some(vec![
|
|
||||||
mapping
|
|
||||||
.get_volume_arg(cloud_id, config, &config.sources_root)
|
|
||||||
.unwrap(),
|
|
||||||
]),
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
env: Some(env),
|
|
||||||
labels: Some(hashmap! {
|
|
||||||
"haze-type".into() => self.name().into(),
|
|
||||||
"haze-cloud-id".into() => cloud_id.into(),
|
|
||||||
}),
|
|
||||||
networking_config: Some(NetworkingConfig {
|
|
||||||
endpoints_config: Some(hashmap! {
|
|
||||||
network.into() => EndpointSettings {
|
|
||||||
aliases: Some(vec![self.name().to_string()]),
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
let id = docker
|
|
||||||
.create_container(container_options, config)
|
|
||||||
.await
|
|
||||||
.into_diagnostic()?
|
|
||||||
.id;
|
|
||||||
docker.start_container(&id, None).await.into_diagnostic()?;
|
|
||||||
exec(
|
|
||||||
docker,
|
|
||||||
&id,
|
|
||||||
"root",
|
|
||||||
vec![
|
|
||||||
"sed",
|
|
||||||
"-i",
|
|
||||||
"s/attr(t,`title`,`Upgrade to Premium`)/attr(t,`style`,`display:none`)/",
|
|
||||||
"public/build/bundle.js",
|
|
||||||
],
|
|
||||||
Vec::<String>::new(),
|
|
||||||
None::<Stdout>,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
Ok(vec![id])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn container_name(&self, cloud_id: &str) -> Option<String> {
|
|
||||||
Some(format!("{}-db-gate", cloud_id))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn proxy_port(&self) -> u16 {
|
|
||||||
3000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
use crate::Result;
|
|
||||||
use crate::cloud::CloudOptions;
|
use crate::cloud::CloudOptions;
|
||||||
use crate::config::HazeConfig;
|
use crate::config::HazeConfig;
|
||||||
use crate::image::pull_image;
|
use crate::image::pull_image;
|
||||||
use crate::service::{ServiceTrait, split_cmnd};
|
use crate::service::{split_cmnd, ServiceTrait};
|
||||||
use bollard::Docker;
|
use crate::Result;
|
||||||
use bollard::config::NetworkingConfig;
|
use bollard::config::NetworkingConfig;
|
||||||
use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig};
|
use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig};
|
||||||
use bollard::query_parameters::CreateContainerOptions;
|
use bollard::query_parameters::CreateContainerOptions;
|
||||||
|
use bollard::Docker;
|
||||||
use maplit::hashmap;
|
use maplit::hashmap;
|
||||||
use miette::IntoDiagnostic;
|
use miette::IntoDiagnostic;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
use crate::Result;
|
|
||||||
use crate::cloud::CloudOptions;
|
use crate::cloud::CloudOptions;
|
||||||
use crate::config::HazeConfig;
|
use crate::config::HazeConfig;
|
||||||
use crate::exec::exec;
|
use crate::exec::exec;
|
||||||
use crate::image::{image_exists, pull_image};
|
use crate::image::{image_exists, pull_image};
|
||||||
use crate::service::{ServiceTrait, split_cmnd};
|
use crate::service::{split_cmnd, ServiceTrait};
|
||||||
use bollard::Docker;
|
use crate::Result;
|
||||||
use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig};
|
use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig};
|
||||||
use bollard::query_parameters::CreateContainerOptions;
|
use bollard::query_parameters::CreateContainerOptions;
|
||||||
|
use bollard::Docker;
|
||||||
use maplit::hashmap;
|
use maplit::hashmap;
|
||||||
use miette::{IntoDiagnostic, bail};
|
use miette::{bail, IntoDiagnostic};
|
||||||
use std::io::Stdout;
|
use std::io::Stdout;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
use crate::Result;
|
|
||||||
use crate::cloud::CloudOptions;
|
use crate::cloud::CloudOptions;
|
||||||
use crate::config::{HazeConfig, ProxyConfig};
|
use crate::config::{HazeConfig, ProxyConfig};
|
||||||
use crate::image::pull_image;
|
use crate::image::pull_image;
|
||||||
use crate::service::{ServiceTrait, split_cmnd};
|
use crate::service::{split_cmnd, ServiceTrait};
|
||||||
use bollard::Docker;
|
use crate::Result;
|
||||||
use bollard::config::NetworkingConfig;
|
use bollard::config::NetworkingConfig;
|
||||||
use bollard::models::{ContainerCreateBody, ContainerState, EndpointSettings, HostConfig};
|
use bollard::models::{ContainerCreateBody, ContainerState, EndpointSettings, HostConfig};
|
||||||
use bollard::query_parameters::CreateContainerOptions;
|
use bollard::query_parameters::CreateContainerOptions;
|
||||||
|
use bollard::Docker;
|
||||||
use maplit::hashmap;
|
use maplit::hashmap;
|
||||||
use miette::{IntoDiagnostic, Report};
|
use miette::{IntoDiagnostic, Report};
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
|
|
@ -102,9 +102,7 @@ impl ServiceTrait for Ldap {
|
||||||
split_cmnd("occ ldap:set-config s01 ldapBase dc=example,dc=org"),
|
split_cmnd("occ ldap:set-config s01 ldapBase dc=example,dc=org"),
|
||||||
split_cmnd("occ ldap:set-config s01 ldapBaseUsers dc=example,dc=org"),
|
split_cmnd("occ ldap:set-config s01 ldapBaseUsers dc=example,dc=org"),
|
||||||
split_cmnd("occ ldap:set-config s01 ldapBaseGroups dc=example,dc=org"),
|
split_cmnd("occ ldap:set-config s01 ldapBaseGroups dc=example,dc=org"),
|
||||||
split_cmnd(
|
split_cmnd("occ ldap:set-config s01 ldapLoginFilter (&(&(objectclass=inetOrgPerson))(uid=%uid))"),
|
||||||
"occ ldap:set-config s01 ldapLoginFilter (&(&(objectclass=inetOrgPerson))(uid=%uid))",
|
|
||||||
),
|
|
||||||
split_cmnd("occ ldap:set-config s01 ldapUserFilter ((objectclass=inetOrgPerson))"),
|
split_cmnd("occ ldap:set-config s01 ldapUserFilter ((objectclass=inetOrgPerson))"),
|
||||||
split_cmnd("occ ldap:set-config s01 ldapUserFilterMode 0"),
|
split_cmnd("occ ldap:set-config s01 ldapUserFilterMode 0"),
|
||||||
split_cmnd("occ ldap:set-config s01 ldapUserDisplayName sn"),
|
split_cmnd("occ ldap:set-config s01 ldapUserDisplayName sn"),
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
use crate::Result;
|
|
||||||
use crate::cloud::CloudOptions;
|
use crate::cloud::CloudOptions;
|
||||||
use crate::config::HazeConfig;
|
use crate::config::HazeConfig;
|
||||||
use crate::image::pull_image;
|
use crate::image::pull_image;
|
||||||
use crate::service::{ServiceTrait, split_cmnd};
|
use crate::service::{split_cmnd, ServiceTrait};
|
||||||
use bollard::Docker;
|
use crate::Result;
|
||||||
use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig};
|
use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig};
|
||||||
use bollard::query_parameters::CreateContainerOptions;
|
use bollard::query_parameters::CreateContainerOptions;
|
||||||
|
use bollard::Docker;
|
||||||
use maplit::hashmap;
|
use maplit::hashmap;
|
||||||
use miette::IntoDiagnostic;
|
use miette::IntoDiagnostic;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
use crate::Result;
|
|
||||||
use crate::cloud::CloudOptions;
|
use crate::cloud::CloudOptions;
|
||||||
use crate::config::HazeConfig;
|
use crate::config::HazeConfig;
|
||||||
use crate::exec::exec;
|
use crate::exec::exec;
|
||||||
use crate::image::pull_image;
|
use crate::image::pull_image;
|
||||||
use crate::service::{ServiceTrait, split_cmnd};
|
use crate::service::{split_cmnd, ServiceTrait};
|
||||||
use bollard::Docker;
|
use crate::Result;
|
||||||
use bollard::models::{
|
use bollard::models::{
|
||||||
ContainerCreateBody, ContainerState, EndpointSettings, HostConfig, NetworkingConfig,
|
ContainerCreateBody, ContainerState, EndpointSettings, HostConfig, NetworkingConfig,
|
||||||
};
|
};
|
||||||
use bollard::query_parameters::CreateContainerOptions;
|
use bollard::query_parameters::CreateContainerOptions;
|
||||||
|
use bollard::Docker;
|
||||||
use maplit::hashmap;
|
use maplit::hashmap;
|
||||||
use miette::{IntoDiagnostic, WrapErr};
|
use miette::{IntoDiagnostic, WrapErr};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
@ -260,6 +260,17 @@ impl ServiceTrait for ObjectStore {
|
||||||
split_cmnd("occ files_external:config 1 secret minio123"),
|
split_cmnd("occ files_external:config 1 secret minio123"),
|
||||||
split_cmnd("mc alias set s3 http://s3:9000 minio minio123"),
|
split_cmnd("mc alias set s3 http://s3:9000 minio minio123"),
|
||||||
]),
|
]),
|
||||||
|
// ObjectStore::S3s => Ok(vec![
|
||||||
|
// "occ files_external:create s3 amazons3 amazons3::accesskey".into(),
|
||||||
|
// "occ files_external:config 1 bucket ext".into(),
|
||||||
|
// "occ files_external:config 1 hostname s3".into(),
|
||||||
|
// "occ files_external:config 1 port 9000".into(),
|
||||||
|
// "occ files_external:config 1 use_ssl true".into(),
|
||||||
|
// "occ files_external:config 1 use_path_style true".into(),
|
||||||
|
// "occ files_external:config 1 key minio".into(),
|
||||||
|
// "occ files_external:config 1 secret minio123".into(),
|
||||||
|
// "mc alias set s3 https://s3:9000 minio minio123".into(),
|
||||||
|
// ]),
|
||||||
_ => Ok(Vec::new()),
|
_ => Ok(Vec::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
use crate::Result;
|
|
||||||
use crate::cloud::CloudOptions;
|
use crate::cloud::CloudOptions;
|
||||||
use crate::config::HazeConfig;
|
use crate::config::HazeConfig;
|
||||||
use crate::exec::exec;
|
use crate::exec::exec;
|
||||||
use crate::image::pull_image;
|
use crate::image::pull_image;
|
||||||
use crate::service::ServiceTrait;
|
use crate::service::ServiceTrait;
|
||||||
use bollard::Docker;
|
use crate::Result;
|
||||||
use bollard::config::NetworkingConfig;
|
use bollard::config::NetworkingConfig;
|
||||||
use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig};
|
use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig};
|
||||||
use bollard::query_parameters::CreateContainerOptions;
|
use bollard::query_parameters::CreateContainerOptions;
|
||||||
|
use bollard::Docker;
|
||||||
use maplit::hashmap;
|
use maplit::hashmap;
|
||||||
use miette::IntoDiagnostic;
|
use miette::IntoDiagnostic;
|
||||||
use std::io::Stdout;
|
use std::io::Stdout;
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
use crate::Result;
|
|
||||||
use crate::cloud::CloudOptions;
|
use crate::cloud::CloudOptions;
|
||||||
use crate::config::HazeConfig;
|
use crate::config::HazeConfig;
|
||||||
use crate::image::pull_image;
|
use crate::image::pull_image;
|
||||||
use crate::service::ServiceTrait;
|
use crate::service::ServiceTrait;
|
||||||
use bollard::Docker;
|
use crate::Result;
|
||||||
use bollard::models::{
|
use bollard::models::{
|
||||||
ContainerCreateBody, ContainerState, EndpointSettings, HostConfig, NetworkingConfig,
|
ContainerCreateBody, ContainerState, EndpointSettings, HostConfig, NetworkingConfig,
|
||||||
};
|
};
|
||||||
use bollard::query_parameters::CreateContainerOptions;
|
use bollard::query_parameters::CreateContainerOptions;
|
||||||
|
use bollard::Docker;
|
||||||
use maplit::hashmap;
|
use maplit::hashmap;
|
||||||
use miette::{IntoDiagnostic, Report};
|
use miette::{IntoDiagnostic, Report};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
use crate::Result;
|
|
||||||
use crate::cloud::CloudOptions;
|
use crate::cloud::CloudOptions;
|
||||||
use crate::config::HazeConfig;
|
use crate::config::HazeConfig;
|
||||||
use crate::exec::exec;
|
use crate::exec::exec;
|
||||||
use crate::image::pull_image;
|
use crate::image::pull_image;
|
||||||
use crate::service::{ServiceTrait, split_cmnd};
|
use crate::service::{split_cmnd, ServiceTrait};
|
||||||
use bollard::Docker;
|
use crate::Result;
|
||||||
use bollard::models::{
|
use bollard::models::{
|
||||||
ContainerCreateBody, ContainerState, EndpointSettings, HostConfig, NetworkingConfig,
|
ContainerCreateBody, ContainerState, EndpointSettings, HostConfig, NetworkingConfig,
|
||||||
};
|
};
|
||||||
use bollard::query_parameters::CreateContainerOptions;
|
use bollard::query_parameters::CreateContainerOptions;
|
||||||
|
use bollard::Docker;
|
||||||
use maplit::hashmap;
|
use maplit::hashmap;
|
||||||
use miette::{IntoDiagnostic, Report};
|
use miette::{IntoDiagnostic, Report};
|
||||||
use std::net::Ipv4Addr;
|
use std::net::Ipv4Addr;
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
use std::io::stdout;
|
use std::io::stdout;
|
||||||
|
|
||||||
use crate::Result;
|
|
||||||
use crate::cloud::CloudOptions;
|
use crate::cloud::CloudOptions;
|
||||||
use crate::config::HazeConfig;
|
use crate::config::HazeConfig;
|
||||||
use crate::database::DatabaseFamily;
|
use crate::database::DatabaseFamily;
|
||||||
use crate::exec::exec;
|
use crate::exec::exec;
|
||||||
use crate::service::ServiceTrait;
|
use crate::service::ServiceTrait;
|
||||||
|
use crate::Result;
|
||||||
use bollard::Docker;
|
use bollard::Docker;
|
||||||
use miette::Report;
|
use miette::Report;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@ use crate::cloud::CloudOptions;
|
||||||
use crate::config::HazeConfig;
|
use crate::config::HazeConfig;
|
||||||
use crate::image::pull_image;
|
use crate::image::pull_image;
|
||||||
use crate::service::ServiceTrait;
|
use crate::service::ServiceTrait;
|
||||||
use bollard::Docker;
|
|
||||||
use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig};
|
use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig};
|
||||||
use bollard::query_parameters::CreateContainerOptions;
|
use bollard::query_parameters::CreateContainerOptions;
|
||||||
|
use bollard::Docker;
|
||||||
use local_ip_address::list_afinet_netifas;
|
use local_ip_address::list_afinet_netifas;
|
||||||
use maplit::hashmap;
|
use maplit::hashmap;
|
||||||
use miette::{IntoDiagnostic, Result};
|
use miette::{IntoDiagnostic, Result};
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
use crate::Result;
|
|
||||||
use crate::cloud::CloudOptions;
|
use crate::cloud::CloudOptions;
|
||||||
use crate::config::HazeConfig;
|
use crate::config::HazeConfig;
|
||||||
use crate::image::pull_image;
|
use crate::image::pull_image;
|
||||||
use crate::service::{ServiceTrait, split_cmnd};
|
use crate::service::{split_cmnd, ServiceTrait};
|
||||||
use bollard::Docker;
|
use crate::Result;
|
||||||
use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig};
|
use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig};
|
||||||
use bollard::query_parameters::CreateContainerOptions;
|
use bollard::query_parameters::CreateContainerOptions;
|
||||||
|
use bollard::Docker;
|
||||||
use maplit::hashmap;
|
use maplit::hashmap;
|
||||||
use miette::IntoDiagnostic;
|
use miette::IntoDiagnostic;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
use crate::Result;
|
|
||||||
use crate::cloud::CloudOptions;
|
use crate::cloud::CloudOptions;
|
||||||
use crate::config::HazeConfig;
|
use crate::config::HazeConfig;
|
||||||
use crate::image::pull_image;
|
use crate::image::pull_image;
|
||||||
use crate::service::{ServiceTrait, split_cmnd};
|
use crate::service::{split_cmnd, ServiceTrait};
|
||||||
use bollard::Docker;
|
use crate::Result;
|
||||||
use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig};
|
use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig};
|
||||||
use bollard::query_parameters::CreateContainerOptions;
|
use bollard::query_parameters::CreateContainerOptions;
|
||||||
|
use bollard::Docker;
|
||||||
use maplit::hashmap;
|
use maplit::hashmap;
|
||||||
use miette::{Context, IntoDiagnostic};
|
use miette::{Context, IntoDiagnostic};
|
||||||
use std::fs::{create_dir_all, write};
|
use std::fs::{create_dir_all, write};
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
use crate::Result;
|
|
||||||
use crate::cloud::CloudOptions;
|
use crate::cloud::CloudOptions;
|
||||||
use crate::config::HazeConfig;
|
use crate::config::HazeConfig;
|
||||||
use crate::database::DatabaseFamily;
|
use crate::database::DatabaseFamily;
|
||||||
use crate::service::ServiceTrait;
|
use crate::service::ServiceTrait;
|
||||||
|
use crate::Result;
|
||||||
use bollard::Docker;
|
use bollard::Docker;
|
||||||
use futures_util::future::try_join_all;
|
use futures_util::future::try_join_all;
|
||||||
use maplit::hashmap;
|
use maplit::hashmap;
|
||||||
use miette::Report;
|
use miette::Report;
|
||||||
use serde_json::Value;
|
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
use serde_json::Value;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::convert::identity;
|
use std::convert::identity;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
use crate::Result;
|
|
||||||
use crate::cloud::CloudOptions;
|
use crate::cloud::CloudOptions;
|
||||||
use crate::config::HazeConfig;
|
use crate::config::HazeConfig;
|
||||||
use crate::image::pull_image;
|
use crate::image::pull_image;
|
||||||
use crate::service::{ServiceTrait, split_cmnd};
|
use crate::service::{split_cmnd, ServiceTrait};
|
||||||
use bollard::Docker;
|
use crate::Result;
|
||||||
use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig};
|
use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig};
|
||||||
use bollard::query_parameters::CreateContainerOptions;
|
use bollard::query_parameters::CreateContainerOptions;
|
||||||
|
use bollard::Docker;
|
||||||
use maplit::hashmap;
|
use maplit::hashmap;
|
||||||
use miette::IntoDiagnostic;
|
use miette::IntoDiagnostic;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
use crate::Result;
|
|
||||||
use crate::cloud::CloudOptions;
|
use crate::cloud::CloudOptions;
|
||||||
use crate::config::HazeConfig;
|
use crate::config::HazeConfig;
|
||||||
use crate::image::pull_image;
|
use crate::image::pull_image;
|
||||||
use crate::service::ServiceTrait;
|
use crate::service::ServiceTrait;
|
||||||
use bollard::Docker;
|
use crate::Result;
|
||||||
use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig};
|
use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig};
|
||||||
use bollard::query_parameters::CreateContainerOptions;
|
use bollard::query_parameters::CreateContainerOptions;
|
||||||
|
use bollard::Docker;
|
||||||
use maplit::hashmap;
|
use maplit::hashmap;
|
||||||
use miette::IntoDiagnostic;
|
use miette::IntoDiagnostic;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,8 @@ use std::io::Cursor;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::fs::create_dir_all;
|
use tokio::fs::create_dir_all;
|
||||||
use zip::ZipArchive;
|
|
||||||
use zip::read::root_dir_common_filter;
|
use zip::read::root_dir_common_filter;
|
||||||
|
use zip::ZipArchive;
|
||||||
|
|
||||||
pub struct Sources {
|
pub struct Sources {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue