mirror of
https://codeberg.org/icewind/haze.git
synced 2026-06-03 09:04:12 +02:00
Compare commits
68 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a3f2355dea | |||
| d852f9db4f | |||
| 92fbc74a5b | |||
| 204fb676d6 | |||
| f99238121b | |||
| cd9740675f | |||
| 39ba7a2a53 | |||
| ad999702aa | |||
| b977cd9dfa | |||
| 8771e7dc5f | |||
| 373ce0f3fd | |||
| 9e080f0d54 | |||
| 512b669a7c | |||
| ea3f89bb04 | |||
| b4a77997ab | |||
| 87f6907778 | |||
| 9de626a905 | |||
| 53e30a94aa | |||
| 948d01600e | |||
| 19e60217ea | |||
| 96b7dd671c | |||
| 24b8fd26ca | |||
| 814a1c3121 | |||
| a1ed0571be | |||
| 0a16737398 | |||
| 266b70339b | |||
| 3abf183ae3 | |||
| a80354c922 | |||
| 3b4014b5e4 | |||
| 4ab23610a2 | |||
| 0d98667650 | |||
| ce34f302a1 | |||
| 63e17d609f | |||
| 6fdadd9bad | |||
| 0105c60a09 | |||
| 85ffdcea5a | |||
| 4635ecf3fc | |||
| 87de2dbb21 | |||
| 0096414614 | |||
| ef86840e77 | |||
| da6c6d754b | |||
| 8780fe0754 | |||
| 7e54fbd89f | |||
| b3a1e80f6f | |||
| 80d71bd7a0 | |||
| 88a4100340 | |||
| e76678ec14 | |||
| 04b8ec975d | |||
| 85071d7aa1 | |||
| fdc821cb93 | |||
| 8e79e997a8 | |||
| bf7a8d9a34 | |||
| e9cb4f08e3 | |||
| 1a6dd90410 | |||
| 9629dea8df | |||
| b7ea4e9760 | |||
|
|
862d33b017 | ||
|
|
d89c547f2a |
||
| 903b3d25a8 | |||
| 37248955ab | |||
| 2ea01b6570 | |||
| df38f16f10 | |||
| f569ca17e2 | |||
| 8941c697fb | |||
| cead37fae7 | |||
| 4f6b1fbd9b | |||
| e3161f34ae | |||
| f7b7c1bf37 |
84 changed files with 3823 additions and 1440 deletions
|
|
@ -4,6 +4,7 @@ on:
|
|||
push:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- "Cargo.toml"
|
||||
- ".forgejo/workflows/docker.yaml"
|
||||
- "nix/image/**"
|
||||
|
||||
|
|
@ -16,10 +17,9 @@ jobs:
|
|||
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ["8.2", "8.3", "8.4"]
|
||||
variant: [""]
|
||||
php-version: ["8.0", "8.1", "8.2", "8.3", "8.4", "8.5"]
|
||||
|
||||
name: haze-${{ matrix.php-version }}${{ matrix.variant }}
|
||||
name: haze-${{ matrix.php-version }}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
|
|
|
|||
2005
Cargo.lock
generated
2005
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
58
Cargo.toml
58
Cargo.toml
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "haze"
|
||||
version = "2.1.5"
|
||||
version = "2.2.2"
|
||||
authors = ["Robin Appelman <robin@icewind.nl>"]
|
||||
edition = "2021"
|
||||
repository = "https://codeberg.org/icewind/haze"
|
||||
|
|
@ -8,38 +8,44 @@ license = "MIT"
|
|||
description = "Easy setup and management of Nextcloud test instances using docker"
|
||||
|
||||
[dependencies]
|
||||
bollard = "0.18.1"
|
||||
bollard = "0.20.1"
|
||||
maplit = "1.0.2"
|
||||
camino = { version = "1.1.7", features = ["serde1"] }
|
||||
tokio = { version = "1.38.0", features = ["fs", "macros", "signal", "rt-multi-thread"] }
|
||||
futures-util = "0.3.30"
|
||||
termion = "4.0.1"
|
||||
opener = "0.7.1"
|
||||
toml = "0.8.14"
|
||||
camino = { version = "1.2.2", features = ["serde1"] }
|
||||
tokio = { version = "1.49.0", features = ["fs", "macros", "signal", "rt-multi-thread"] }
|
||||
tokio-stream = { version = "0.1.18", features = ["net"] }
|
||||
futures-util = "0.3.32"
|
||||
termion = "4.0.6"
|
||||
opener = "0.8.4"
|
||||
toml = "1.0.3"
|
||||
directories-next = "2.0.0"
|
||||
serde = "1.0.203"
|
||||
serde_json = "1.0.117"
|
||||
serde = "1.0.228"
|
||||
serde_json = "1.0.149"
|
||||
petname = "2.0.2"
|
||||
reqwest = { version = "0.12.4", default-features = false }
|
||||
tar = "0.4.41"
|
||||
flate2 = "1.0.30"
|
||||
async-trait = "0.1.80"
|
||||
reqwest = { version = "0.13.2", default-features = false, features = ["rustls"] }
|
||||
tar = "0.4.44"
|
||||
flate2 = "1.1.9"
|
||||
async-trait = "0.1.89"
|
||||
enum_dispatch = "0.3.13"
|
||||
miette = { version = "7.2.0", features = ["fancy"] }
|
||||
shell-words = "1.1.0"
|
||||
tracing = "0.1.40"
|
||||
tracing-subscriber = "0.3.18"
|
||||
miette = { version = "7.6.0", features = ["fancy"] }
|
||||
shell-words = "1.1.1"
|
||||
tracing = "0.1.44"
|
||||
tracing-subscriber = "0.3.22"
|
||||
atty = "0.2.14"
|
||||
git2 = { version = "0.20.0", default-features = false }
|
||||
git2 = { version = "0.20.4", default-features = false }
|
||||
itertools = { version = "0.14.0", features = ["use_alloc"] }
|
||||
local-ip-address = "0.6.5"
|
||||
strum = { version = "0.27.2", features = ["derive"] }
|
||||
owo-colors = { version = "4.2.2", features = ["supports-colors"] }
|
||||
local-ip-address = "0.6.10"
|
||||
strum = { version = "0.28.0", features = ["derive"] }
|
||||
owo-colors = { version = "4.3.0", features = ["supports-colors"] }
|
||||
zip = "8.1.0"
|
||||
sha2 = "0.11.0-rc.5"
|
||||
base16ct = { version = "1.0.0", features = ["alloc"] }
|
||||
indicatif = "0.18.4"
|
||||
rayon = "1.12.0"
|
||||
|
||||
hyper-reverse-proxy = { version = "0.5.2-dev", git = "https://github.com/chpio/hyper-reverse-proxy", rev = "6934877eb74465204f605cc1c05ca5a9772db7c0" }
|
||||
hyper = "1.6.0"
|
||||
hyper-util = "0.1.10"
|
||||
axum = { version = "0.8.1", features = ["tokio"] }
|
||||
hyper-reverse-proxy = { version = "0.5.2-dev", git = "https://code.betamike.com/micropelago/hyper-reverse-proxy.git", rev = "d5a6f799189360d9449ae47ab3cdde511f02cf39" }
|
||||
hyper = "1.8.1"
|
||||
hyper-util = "0.1.20"
|
||||
axum = { version = "0.8.8", features = ["tokio", "macros"] }
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
|
|
|
|||
88
README.md
88
README.md
|
|
@ -6,7 +6,7 @@ Easy setup and management of Nextcloud test instances using docker
|
|||
|
||||
## What
|
||||
|
||||
`haze` provides an easy way to setup 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.
|
||||
|
||||
## Setup
|
||||
|
|
@ -56,47 +56,52 @@ See the [configuration section](#configuration) for more options.
|
|||
#### Start an instance
|
||||
|
||||
```bash
|
||||
haze start [database] [php-version] [services]
|
||||
haze start [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.2`, `8.3` or `8.4`, defaults to the maximum version
|
||||
support by the current Nextcloud version. `7.3` till `8.1` are still supported
|
||||
but the docker images for those versions aren't being updated anymore so they
|
||||
might be missing some newer features.
|
||||
`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.
|
||||
|
||||
Each php version also comes with a `-dbg` variant that has php compiled in debug
|
||||
mode and can be used for debugging php itself with gdb.
|
||||
You can specify a version number (e.g. `v32.0.2`) to use the sources from a
|
||||
release instead of using the local sources.
|
||||
|
||||
Additionally, you can use the following options when starting an instance:
|
||||
|
||||
- `s3`: setup an S3 server and configure to Nextcloud to use it as primary
|
||||
- `s3`: set up an S3 server and configure to Nextcloud to use it as primary
|
||||
storage.
|
||||
- `<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.
|
||||
- `ldap`: setup an LDAP server.
|
||||
- `office`: setup a Nextcloud Office server.
|
||||
- `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.
|
||||
- `office`: set up a Nextcloud Office server.
|
||||
- `onlyoffice` setup an onlyoffice document server.
|
||||
- `push` setup [client push](https://github.com/nextcloud/notify_push).
|
||||
- `smb`: setup a samba server for external storage use.
|
||||
- `dav`: setup a WebDAV server for external storage use.
|
||||
- `sftp`: setup a SFTP server for external storage use.
|
||||
- `kaspersky`: setup a kaspersky scan engine server in http mode. ( Requires
|
||||
- `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`: setup a local clam av scanner in executable mode.
|
||||
- `clamav-socket`: setup a clam av scanner in socket mode.
|
||||
- `clamav-icap`: setup a clam av scanner in ICAP mode.
|
||||
- `clamav-icap-tls`: setup a clam av scanner in ICAP mode with TLS encryption.
|
||||
- `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 an [smtp4dev](https://github.com/rnwood/smtp4dev) server and
|
||||
- `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
|
||||
|
|
@ -105,7 +110,7 @@ Additionally, you can use the following options when starting an instance:
|
|||
haze test [database] [php-version] [path]
|
||||
```
|
||||
|
||||
Where `path` is a file or folder to run phpunit in, relative to the sources
|
||||
Where `path` is a file or folder to run PHPUnit in, relative to the sources
|
||||
root.
|
||||
|
||||
### List running instances
|
||||
|
|
@ -129,7 +134,7 @@ 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 it's name.
|
||||
optionally providing a `match` to select a specific instance by its name.
|
||||
|
||||
#### Open an instance
|
||||
|
||||
|
|
@ -206,7 +211,7 @@ 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 intented to run a local
|
||||
This is intended to run a local
|
||||
[push daemon](https://github.com/nextcloud/notify_push) against an instance.
|
||||
|
||||
#### Update the container images
|
||||
|
|
@ -227,7 +232,7 @@ haze [match] edit <path>
|
|||
haze [match] reload
|
||||
```
|
||||
|
||||
The php configuration can edit changed with `haze edit /php.ini`
|
||||
The php configuration can edit changed with `haze edit /config/php.ini`
|
||||
|
||||
#### Checkout a branch for all local apps
|
||||
|
||||
|
|
@ -254,29 +259,30 @@ Performs a pull in all git repositories within the apps folder.
|
|||
|
||||
Multiple instances can reach each other by using their instance name as domain
|
||||
name to allow for testing federation between instances. Alternatively, you can
|
||||
setup the haze proxy and the proxied domains to get https support between
|
||||
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
|
||||
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
|
||||
- A reverse proxy like nginx or Apache
|
||||
- (optionally) a wildcard ssl certificate (can be acquiring using letsencrypt
|
||||
and dns verification)
|
||||
|
||||
### Setup
|
||||
|
||||
- Set a DNS record for `*.haze.exmaple.com` and `haze.example.com` pointing to
|
||||
your development machine. (127.0.0.1 will not work)
|
||||
- Set the `proxy` configuration with your domain and desired listen endpoint
|
||||
- Setup a service to run `haze proxy` in the background as your own user. A
|
||||
systemd user service is recommended.
|
||||
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
|
||||
|
|
@ -286,11 +292,17 @@ proxy to allow using a wildcard domain.
|
|||
|
||||
### Usage
|
||||
|
||||
When the proxy is configured, generated urls for the instances will use a
|
||||
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.
|
||||
|
||||
## Configuration
|
||||
|
||||
Configuration is loaded from `~/.config/haze/haze.toml` and has the following
|
||||
|
|
@ -298,6 +310,7 @@ 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"
|
||||
|
||||
[auto_setup] # optional
|
||||
|
|
@ -309,6 +322,7 @@ 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"
|
||||
|
|
@ -322,7 +336,7 @@ read_only = true
|
|||
|
||||
[proxy] # optional
|
||||
address = "haze.example.com" # base domain
|
||||
https = true # Is the proxy behind an https terminating proxy
|
||||
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
|
||||
|
||||
|
|
|
|||
28
certificates/s3/private.key
Normal file
28
certificates/s3/private.key
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCQuTk+irdXRGbY
|
||||
JUu+AFmMM4/CCtBIBBgGIG18tesgQUeHEPdRHbBypvFKhpXvlKQbVbiaZxFjtlvn
|
||||
L2ReN7gYwjiTjLWuaDgOzGQyObwSJpedlcd5Q957WNlc5OjpoK8zZq9EGnmvjIqe
|
||||
5VVBne85RZVw6+i4ljEiWoXCiy0iOPIL4jKO8kfO8EmTTh6ge2sLCT5jFT/V63/B
|
||||
hjEobA7+vPbAmEo+Qs4adnBSrlX9nLtL1j4gqawAQMmGl/Ti75T0uQvNxdq2gddc
|
||||
n0wyWhyERnZNeMv5sfkwCOTuetNNtGLf/lTkXNdaOCRvW5jJ0EjYYdpQG5R0do1U
|
||||
XFLH1cZZAgMBAAECggEAR0xpTk2Ku5yASlY9dXK4qyCv3znymLgjmckaB4mcN7zR
|
||||
X1JVdYn55tImJ8AcV/bTzn+xvaevYn9x0XiAqwYqVVBCDTcSPsUrcObzKedVp1+J
|
||||
7GHg7vYnwn7oPyKrOIYoKluZVyTv9DN6C4QSN4x2UbHdSM+ATIf51uHf6hMk/ilv
|
||||
4uw3csxSCpLOqqsYCQarES7SypcETjFpNnIfTPt7q2Y2DbIDttTTjzrm0/GBP0WE
|
||||
OYNvzZZPZRPJfy3et3r9vJbqWzGHvOttzQ+EFHjwPTMfW4tsHcCsEKSGWwLpG4bN
|
||||
FPNx0+QCqDiChesdiCHFNSk+u7pRZrHdjuDJEuKSzQKBgQDMDgdCGBQfgaNnkRz/
|
||||
aiv3V200/vXegnc0Jz49Dye5AxEVu0X1m2xZpJv+qEwbOx5B+1PV3gfVP/iRf+FK
|
||||
MAwFbmb7hGcDE8AGNsSpQydjwzKoi/M67YXv7T8dgWKnwz0eyU4K2IOGInGxuFty
|
||||
Ik7+DTqz+Ikh1RiAoGbKfw9yhwKBgQC1kKhjxB7r/uSLcfOSg1mLcR5lTrNDQAPQ
|
||||
GnsIje0nD5Tv52/k6U4tk15vjL4t6KZUFo9SJ4O1kM1veyuOJuol2AfPXS+H/Izo
|
||||
5BjmoZ0jOONOZZiRIB1moQSy1qhTAeZB9S1ORxQ3dIBPqm+oyADPTTsNV67Cwnt4
|
||||
woeZRUPYHwKBgQDE0AcKJcVK+jQMUXfBlrsfTvDjO8MTwYyN/gfWxsZOeXnCFyYM
|
||||
FcO01sMrJVJ6tVOi2nFrB0NQ2Om8FLbMYnlFx82GbJca7bK5i5u1kjLs0zoKPSn1
|
||||
vWEBIDhPEhuAqhxKlGk0ps580r4MZz+0XwkHmuTy7xX9TtbaQVvDljflAwKBgDKy
|
||||
3hJdpTTIzBCUFSuIOezR/WbUfwH8UhQ+ELTmzJ3nn/MNcRU+gHIBgJEtf71aBXfd
|
||||
hM+v8Ps2H+dNQXBENYWzuRqSLr+OKdquNrXP0w0OyYoOnHeJvCv4MlOt1Pq4wQ8R
|
||||
40DEYETL5zhXoy5CCtfX/PFQ1p/Tpp6l0y9dRACJAoGALwUbyyDy85b2xRQB6RtU
|
||||
I+5Vz5cd/1eQdCkoU9mX4qWA/hWpgc7Z2Jd67LW/WWtVjlF9hva/WNDSfGsXo2ew
|
||||
C8OofvlfIuFDOCXrodYdHE1Q4g5TZdESr0XAqopq+QzBs89qbIy05kM9iuE4yFUo
|
||||
xeimCY9oDWTeGw/XrLdHZF4=
|
||||
-----END PRIVATE KEY-----
|
||||
21
certificates/s3/public.crt
Normal file
21
certificates/s3/public.crt
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDXTCCAkWgAwIBAgIULiChaTwmVx6nRTHohmPuf55/4jUwDQYJKoZIhvcNAQEL
|
||||
BQAwPjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxDTALBgNVBAoM
|
||||
BEhhemUxCzAJBgNVBAMMAnMzMB4XDTI2MDMwOTE3NTgwOVoXDTI3MDMwOTE3NTgw
|
||||
OVowPjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxDTALBgNVBAoM
|
||||
BEhhemUxCzAJBgNVBAMMAnMzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
|
||||
AQEAkLk5Poq3V0Rm2CVLvgBZjDOPwgrQSAQYBiBtfLXrIEFHhxD3UR2wcqbxSoaV
|
||||
75SkG1W4mmcRY7Zb5y9kXje4GMI4k4y1rmg4DsxkMjm8EiaXnZXHeUPee1jZXOTo
|
||||
6aCvM2avRBp5r4yKnuVVQZ3vOUWVcOvouJYxIlqFwostIjjyC+IyjvJHzvBJk04e
|
||||
oHtrCwk+YxU/1et/wYYxKGwO/rz2wJhKPkLOGnZwUq5V/Zy7S9Y+IKmsAEDJhpf0
|
||||
4u+U9LkLzcXatoHXXJ9MMlochEZ2TXjL+bH5MAjk7nrTTbRi3/5U5FzXWjgkb1uY
|
||||
ydBI2GHaUBuUdHaNVFxSx9XGWQIDAQABo1MwUTAdBgNVHQ4EFgQUJJ8HiT2zmuF5
|
||||
6WHHFsTHgkrayxYwHwYDVR0jBBgwFoAUJJ8HiT2zmuF56WHHFsTHgkrayxYwDwYD
|
||||
VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAUF0lB/qIrxkgZ4sqNrw4
|
||||
CInHCK29XVaMoqk1QZyS/KhWDM+zgbA92OxxuhCKw4iJEajZvgg0S9RtGkBNmquU
|
||||
l0rf0JdALd0jPkWr7+3OeqlcgOs2EH7PTqrrbXTGsR12D+Ot+OerQeWXmO28Zrl8
|
||||
4O67TwQtslXwZzeCrtiwAA2DrIYpSLzh+qDtwbY5hMG5zmqqjBM20Ysgxszh4rhl
|
||||
KR6skXwZwkVVhKpK76qwnU02PIMr8auL1csx8/uBTd/UzX2veqlkOP5V/Gg6eEbI
|
||||
4fTOzq7k+FyuzSkrEX4Vc9GbWcRvoVZh+qAKUKstqlE2iCrqmZ+Wal6GA8JA5SZ+
|
||||
bQ==
|
||||
-----END CERTIFICATE-----
|
||||
38
certificates/sftp/id_rsa
Normal file
38
certificates/sftp/id_rsa
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
|
||||
NhAAAAAwEAAQAAAYEA323aWqH6YwRLbCBO94UKOkfnJ2m6Zsic0dMt3TmDnjLU0JzpOt7w
|
||||
t5+mMZrEKQTpefozyUHo3z+HkmllLAGOupNy3A+jG2O955UUgw0dGfu6j6OOb66Du9jpqt
|
||||
8BQ6gr3cEYASplPI7B889/cVpJ5l1HiBUgyR7Z16v15qCDtmpFVIECAdICEmPosfmZutt3
|
||||
YYl9xLay5WCmUztWS/amPcGs0DOEGrWeCtdxGKWT3TywdBKyQ0PbdYMamgDIT7JV1ZSzZP
|
||||
aly4sB7E+dpS5AgBFVXmZ61151KN1TJ8gyoUjFhY7ctYEIpncZmyT4PYvyIvxRsbJtvERi
|
||||
eNH8DoX5DwtqcxbgHK0OwYtdl4ydRXToYo3l+qIidf+g8ADVea/mbkfTPegdToo3LOuThX
|
||||
OwExDlukpM8obFDpz1Yl1L6rRJAVNO1KmHWhn6to23jtYjBhczA2nkemQXQbVSjc/hItjQ
|
||||
DIFNMOsLW33P+Y2k9LkpI0TL09ogOxOFZzGZp2tNAAAFgIgMIZ+IDCGfAAAAB3NzaC1yc2
|
||||
EAAAGBAN9t2lqh+mMES2wgTveFCjpH5ydpumbInNHTLd05g54y1NCc6Tre8LefpjGaxCkE
|
||||
6Xn6M8lB6N8/h5JpZSwBjrqTctwPoxtjveeVFIMNHRn7uo+jjm+ug7vY6arfAUOoK93BGA
|
||||
EqZTyOwfPPf3FaSeZdR4gVIMke2der9eagg7ZqRVSBAgHSAhJj6LH5mbrbd2GJfcS2suVg
|
||||
plM7Vkv2pj3BrNAzhBq1ngrXcRilk908sHQSskND23WDGpoAyE+yVdWUs2T2pcuLAexPna
|
||||
UuQIARVV5metdedSjdUyfIMqFIxYWO3LWBCKZ3GZsk+D2L8iL8UbGybbxEYnjR/A6F+Q8L
|
||||
anMW4BytDsGLXZeMnUV06GKN5fqiInX/oPAA1Xmv5m5H0z3oHU6KNyzrk4VzsBMQ5bpKTP
|
||||
KGxQ6c9WJdS+q0SQFTTtSph1oZ+raNt47WIwYXMwNp5HpkF0G1Uo3P4SLY0AyBTTDrC1t9
|
||||
z/mNpPS5KSNEy9PaIDsThWcxmadrTQAAAAMBAAEAAAGAWCkM/TEnztU9e3M+JX253OhNRe
|
||||
h6lB75ffOxh7avgAc3oP8hKkkYu6PDnJQgbb0R8T7wGywmGp0DPhrXQGd27ZjLvBhxeBfB
|
||||
sbTJ7LIKdxu0cAQN6nR2Z3M+NF2dLpiXgn80HRWg76W20yDffRcuzLamyIPptWI2e9rPAw
|
||||
r4HczOAXuMErLOfXotsbg22BvL/dEWLr4WVdruli32LbArxXd73IVPTYi3TTjYV+zRrPzK
|
||||
9WoBK/iFClfKcdT4NTY82llQesuUNu640lEJtT2G3Iba8UZnohyzm/S+UbeU65z8DKD5co
|
||||
P7+QehxQSV+kj2BZnTi0WEwsD+GTznJYR5rvUsJCCAzoISsWrncSSgOQhF2XeW/T4ewvH+
|
||||
njLZViEhdG8R3kkdDjJG91OrSgrEqlk6Qhz1xEsv1rCOR69En7EJP3TNNrymPXPASrAnuE
|
||||
HQkrVgGUfGqyD1sw1e6nBfNWisuw+g99CieIB8EI9WwpxQdKqWNU9Hjx+SAdC3NrPjAAAA
|
||||
wQCo0hUGjSf6xhcgeaNa0gWSKEVuFhxR/FaCPTKadV7Md0APW4toeQZDujzDFlCZbQTZjC
|
||||
0723B4lKugDzXfsOgvOTKp4vEjZOu0YGruS00LFWM7Sutdzx68b/ZMFALzITt/myKVMdpv
|
||||
WpaO+3+PyEYIQH44QrSWw7cKLzNiZ8kt2drPkPktub4o2h5TdIBluEQLJDPMejy8IqQEU8
|
||||
aOyJOMvYxAbGAWY7Ck9DGlcJgaFdORROW8d8ZGrHQkyRl41JQAAADBAPeUMsrbI17wPP/s
|
||||
Tsrkms5ws5yTz0xle2Wn6HwDSzQRSdn5abnIDYb3QSy0nRBvczef6ssH65dl50+2V4BV2L
|
||||
MwHcmKD6/UoFsWwP/RMf1EoacPFiEAWJGxFbOthNX+rx5BpbUHNoQd8xby+88saDI0e8W6
|
||||
36HPBZrAZhQljkMa4OJqZDDCpOJvSndXwkZ789E96uprKopJZGwlLmfMtikQpNXT9R+I0b
|
||||
SQCJj4yNakcdOE/7UifkOR02u+pgux3wAAAMEA5wdelKwGQ0EdkIF2TM844uLPszo3ZSH+
|
||||
Heff/Lbxs1Y+oL0NTJQicwMF0d9WEwBoTZJpuzsQEA1zkfmW0gi2womIRmiY0ZhpxbBuhO
|
||||
6XePMIhUfQmWWjaUbAkrNB0eJkSTuUGzwxVkMXehrMuj4gYe8GMC8GgULbP0A8FjH01fKk
|
||||
jFwgg4WAg6zUTpck12bh49NZRFyXIbXNk/jjxJtb0p//5TRTUQ6mR5IloaNTM23EiF6tle
|
||||
Y6CAchnyhHO0BTAAAACWhhemVAaGF6ZQE=
|
||||
-----END OPENSSH PRIVATE KEY-----
|
||||
1
certificates/sftp/id_rsa.pub
Normal file
1
certificates/sftp/id_rsa.pub
Normal file
|
|
@ -0,0 +1 @@
|
|||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDfbdpaofpjBEtsIE73hQo6R+cnabpmyJzR0y3dOYOeMtTQnOk63vC3n6YxmsQpBOl5+jPJQejfP4eSaWUsAY66k3LcD6MbY73nlRSDDR0Z+7qPo45vroO72Omq3wFDqCvdwRgBKmU8jsHzz39xWknmXUeIFSDJHtnXq/XmoIO2akVUgQIB0gISY+ix+Zm623dhiX3EtrLlYKZTO1ZL9qY9wazQM4QatZ4K13EYpZPdPLB0ErJDQ9t1gxqaAMhPslXVlLNk9qXLiwHsT52lLkCAEVVeZnrXXnUo3VMnyDKhSMWFjty1gQimdxmbJPg9i/Ii/FGxsm28RGJ40fwOhfkPC2pzFuAcrQ7Bi12XjJ1FdOhijeX6oiJ1/6DwANV5r+ZuR9M96B1Oijcs65OFc7ATEOW6SkzyhsUOnPViXUvqtEkBU07UqYdaGfq2jbeO1iMGFzMDaeR6ZBdBtVKNz+Ei2NAMgU0w6wtbfc/5jaT0uSkjRMvT2iA7E4VnMZmna00= haze@haze
|
||||
120
flake.lock
generated
120
flake.lock
generated
|
|
@ -2,11 +2,11 @@
|
|||
"nodes": {
|
||||
"crane": {
|
||||
"locked": {
|
||||
"lastModified": 1760924934,
|
||||
"narHash": "sha256-tuuqY5aU7cUkR71sO2TraVKK2boYrdW3gCSXUkF4i44=",
|
||||
"lastModified": 1763938834,
|
||||
"narHash": "sha256-j8iB0Yr4zAvQLueCZ5abxfk6fnG/SJ5JnGUziETjwfg=",
|
||||
"owner": "ipetkov",
|
||||
"repo": "crane",
|
||||
"rev": "c6b4d5308293d0d04fcfeee92705017537cad02f",
|
||||
"rev": "d9e753122e51cee64eb8d2dddfe11148f339f5a2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -15,6 +15,22 @@
|
|||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1767039857,
|
||||
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
|
||||
"owner": "NixOS",
|
||||
"repo": "flake-compat",
|
||||
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flakelight": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
|
|
@ -22,11 +38,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1764593611,
|
||||
"narHash": "sha256-6SdexcO69Dlu14YN2xuB1A6JHWSrcqMj7Na9oK7IT2M=",
|
||||
"lastModified": 1773062095,
|
||||
"narHash": "sha256-u+cK9IoJokO4YzQwMc2s8Vti0RL/LVSrROOEn2opc5U=",
|
||||
"owner": "nix-community",
|
||||
"repo": "flakelight",
|
||||
"rev": "0d63256401341f528dd628f1a8e96d3afecade7a",
|
||||
"rev": "c99e4d5f40e578cb2d8f460ea2bbd5dc26316d24",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -44,11 +60,11 @@
|
|||
"rust-overlay": "rust-overlay"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1763591898,
|
||||
"narHash": "sha256-aHSMj7CIa9EJYxdf05wOWRGp0KRsT/TAox7uwVSdDb8=",
|
||||
"lastModified": 1772297202,
|
||||
"narHash": "sha256-UEzHO/tCmhPhr8RpWtbm1MTa7ABobwt3nCjrcuDAPm0=",
|
||||
"ref": "refs/heads/main",
|
||||
"rev": "2d9b2da2c9f384f93ef977c48f8ee35ce586529b",
|
||||
"revCount": 66,
|
||||
"rev": "8690e1514863b934de12f2a503c9431d186ce30b",
|
||||
"revCount": 68,
|
||||
"type": "git",
|
||||
"url": "https://codeberg.org/icewind/mill-scale.git"
|
||||
},
|
||||
|
|
@ -59,11 +75,11 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1764522689,
|
||||
"narHash": "sha256-SqUuBFjhl/kpDiVaKLQBoD8TLD+/cTUzzgVFoaHrkqY=",
|
||||
"lastModified": 1772822230,
|
||||
"narHash": "sha256-yf3iYLGbGVlIthlQIk5/4/EQDZNNEmuqKZkQssMljuw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "8bb5646e0bed5dbd3ab08c7a7cc15b75ab4e1d0f",
|
||||
"rev": "71caefce12ba78d84fe618cf61644dce01cf3a96",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -72,11 +88,48 @@
|
|||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1772173633,
|
||||
"narHash": "sha256-MOH58F4AIbCkh6qlQcwMycyk5SWvsqnS/TCfnqDlpj4=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "c0f3d81a7ddbc2b1332be0d8481a672b4f6004d6",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"phps": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"nixpkgs": "nixpkgs_2",
|
||||
"utils": "utils"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1772365008,
|
||||
"narHash": "sha256-/ynkWKeZ1dyRIUkQas0AB35semWAwCbTKXu+/q+8MGg=",
|
||||
"owner": "fossar",
|
||||
"repo": "nix-phps",
|
||||
"rev": "f47eb877bf1c219809e4357eec2fdab8e3263b7b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "fossar",
|
||||
"repo": "nix-phps",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flakelight": "flakelight",
|
||||
"mill-scale": "mill-scale",
|
||||
"nixpkgs": "nixpkgs"
|
||||
"nixpkgs": "nixpkgs",
|
||||
"phps": "phps"
|
||||
}
|
||||
},
|
||||
"rust-overlay": {
|
||||
|
|
@ -88,11 +141,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1761964689,
|
||||
"narHash": "sha256-Zo3LQQDz+64EQ9zor/WmeNTFLoZkjmhp0UY3G0D3seE=",
|
||||
"lastModified": 1764557621,
|
||||
"narHash": "sha256-kX5PoY8hQZ80+amMQgOO9t8Tc1JZ70gYRnzaVD4AA+o=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "63d22578600f70d293aede6bc737efef60ebd97f",
|
||||
"rev": "93316876c2229460a5d6f5f052766cc4cef538ce",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -100,6 +153,39 @@
|
|||
"repo": "rust-overlay",
|
||||
"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",
|
||||
|
|
|
|||
17
flake.nix
17
flake.nix
|
|
@ -9,8 +9,15 @@
|
|||
url = "git+https://codeberg.org/icewind/mill-scale.git";
|
||||
inputs.flakelight.follows = "flakelight";
|
||||
};
|
||||
phps = {
|
||||
url = "github:fossar/nix-phps";
|
||||
};
|
||||
};
|
||||
outputs = {mill-scale, ...}:
|
||||
outputs = {
|
||||
mill-scale,
|
||||
phps,
|
||||
...
|
||||
}:
|
||||
mill-scale ./. {
|
||||
crossTargets = [
|
||||
"x86_64-unknown-linux-gnu"
|
||||
|
|
@ -24,17 +31,23 @@
|
|||
};
|
||||
|
||||
extraPaths = [
|
||||
./redis-certificates
|
||||
./certificates
|
||||
];
|
||||
|
||||
withOverlays = [
|
||||
(import ./nix/overlay.nix)
|
||||
(prev: final: {
|
||||
inherit (phps.packages.${prev.system}) php81 php80;
|
||||
})
|
||||
];
|
||||
|
||||
packages = {
|
||||
"haze-image-php-8.5" = pkgs: pkgs.haze-image-php-85;
|
||||
"haze-image-php-8.4" = pkgs: pkgs.haze-image-php-84;
|
||||
"haze-image-php-8.3" = pkgs: pkgs.haze-image-php-83;
|
||||
"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.0" = pkgs: pkgs.haze-image-php-80;
|
||||
};
|
||||
|
||||
tools = pkgs: with pkgs; [cargo-edit bacon skopeo];
|
||||
|
|
|
|||
10
haze.service
Normal file
10
haze.service
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
[Install]
|
||||
WantedBy=default.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/bin/haze proxy
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
|
||||
[Unit]
|
||||
Description=Haze reverse proxy
|
||||
|
|
@ -9,9 +9,10 @@ with lib; let
|
|||
format = pkgs.formats.toml {};
|
||||
configFile = format.generate "haze.toml" ({
|
||||
sources_root = cfg.sourcesRoot;
|
||||
app_directories = cfg.appDirectories;
|
||||
work_dir = cfg.workDir;
|
||||
auto_setup = {
|
||||
enabled = cfg.autoSetup.enable;
|
||||
inherit (cfg.autoSetup) enable config;
|
||||
post_setup = cfg.autoSetup.postSetup;
|
||||
enable_apps = cfg.autoSetup.enableApps;
|
||||
disable_apps = cfg.autoSetup.disableApps;
|
||||
|
|
@ -61,6 +62,12 @@ in {
|
|||
description = "Directory to store instance data";
|
||||
};
|
||||
|
||||
appDirectories = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
description = "Paths to additional app directories";
|
||||
};
|
||||
|
||||
autoSetup = mkOption {
|
||||
type = types.submodule {
|
||||
options = {
|
||||
|
|
@ -84,6 +91,13 @@ in {
|
|||
default = [];
|
||||
description = "Commands to run post-setup";
|
||||
};
|
||||
config = mkOption {
|
||||
type = types.submodule {
|
||||
freeformType = format.type;
|
||||
};
|
||||
description = "Configuration options to set before install";
|
||||
default = {};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
|||
88
nix/image/bootstrap
Executable file
88
nix/image/bootstrap
Executable file
|
|
@ -0,0 +1,88 @@
|
|||
#!/bin/nu
|
||||
|
||||
touch /var/log/nginx/access.log
|
||||
touch /var/log/nginx/error.log
|
||||
touch /var/log/cron/owncloud.log
|
||||
|
||||
mkdir /config
|
||||
echo "# Options in here overwrite the builtin php.ini\n" | save /config/php.ini
|
||||
echo "# xdebug.mode = debug\n" | save -a /config/php.ini
|
||||
echo "# xdebug.start_with_request = yes\n\n" | save -a /config/php.ini
|
||||
chmod 0777 /config/php.ini
|
||||
let PHP_INI_DIR = php --ini | grep 'Scan' | cut -d ' ' -f7 | tr -d '"'
|
||||
ln -s /config/php.ini $"($PHP_INI_DIR)/zz_extra.ini"
|
||||
|
||||
let HAZE_UID = $env.HAZE_UID | default "1000"
|
||||
let HAZE_GID = $env.HAZE_GID | default "1000"
|
||||
|
||||
nc-auto-config
|
||||
shadow-setup
|
||||
|
||||
echo $"Running as ($HAZE_UID):($HAZE_GID)"
|
||||
|
||||
mkdir /var/www/html/core/skeleton /var/www/html/build/integration/vendor /var/www/html/build/integration/output /var/www/html/build/integration/work /var/www/html/core/skeleton /var/www/.composer/cache /var/www/html/apps/spreed/tests/integration/vendor/composer
|
||||
chown -R $"($HAZE_UID):($HAZE_GID)" /var/www/html/data /var/www/html/config
|
||||
chown $"($HAZE_UID):($HAZE_GID)" /var/www/html/core/skeleton /var/www/html/build/integration/vendor /var/www/html/build/integration/composer.lock /var/www/html/build/integration/output /var/www/html/build/integration/work /var/www/html/core/skeleton /var/www/.composer/cache /var/www/html/apps/spreed/tests/integration/vendor/composer
|
||||
|
||||
echo "{}\n" | save -f /var/www/html/build/integration/composer.lock
|
||||
|
||||
echo $"Starting server using ($env.SQL) database…"
|
||||
|
||||
chmod +sx /sbin/sudo
|
||||
|
||||
mkdir /var/log/nginx /tmp /var/run/blackfire
|
||||
touch /var/log/nginx/access.log
|
||||
touch /var/log/nginx/error.log
|
||||
|
||||
if ((getent group $HAZE_GID | length) > 0) {
|
||||
groupadd haze
|
||||
useradd -u $HAZE_UID -g $HAZE_GID -G haze haze
|
||||
} else {
|
||||
groupadd -g $HAZE_GID haze
|
||||
useradd -u $HAZE_UID -g $HAZE_GID haze
|
||||
}
|
||||
chown -R $"haze:($HAZE_GID)" /home/haze
|
||||
ls -af /etc/home | each {|file| ln -s $file.name $"/home/haze/($file.name | path basename)" }
|
||||
|
||||
if ("/var/run/docker.sock" | path exists) {
|
||||
let dockerGid = stat --format "%g" /var/run/docker.sock
|
||||
groupadd docker -g $dockerGid
|
||||
usermod -a -G docker haze
|
||||
}
|
||||
|
||||
if ("REDIS_TLS" in $env) {
|
||||
cp /etc/supervisor/redis-tls.conf /etc/supervisor/enabled/
|
||||
} else {
|
||||
cp /etc/supervisor/redis-plain.conf /etc/supervisor/enabled/
|
||||
}
|
||||
|
||||
if ("BLACKFIRE_SERVER_ID" in $env) {
|
||||
blackfire agent:config --server-id $env.BLACKFIRE_SERVER_ID --server-token $env.BLACKFIRE_SERVER_TOKEN
|
||||
cp /etc/supervisor/blackfire.conf /etc/supervisor/enabled/
|
||||
}
|
||||
|
||||
if ("PROXY_BASE" in $env) {
|
||||
let UPSTREAM_DNS = cat /etc/resolv.conf | grep nameserver | cut -d' ' -f 2
|
||||
let RC = sed '/nameserver/d' /etc/resolv.conf
|
||||
echo $RC | save -f /etc/resolv.conf
|
||||
|
||||
echo "\nnameserver 127.0.0.22\n" | save -a /etc/resolv.conf
|
||||
|
||||
echo $"s/UPSTREAM_DNS/($UPSTREAM_DNS)"
|
||||
sed -i $"s/UPSTREAM_DNS/($UPSTREAM_DNS)/" /etc/dnsmasq.conf
|
||||
echo $"s/PROXY_BASE/($env.PROXY_BASE)"
|
||||
sed -i $"s/PROXY_BASE/($env.PROXY_BASE)/" /etc/dnsmasq.conf
|
||||
echo $"s/HOST_IP/($env.HOST_IP)"
|
||||
sed -i $"s/HOST_IP/($env.HOST_IP)/" /etc/dnsmasq.conf
|
||||
|
||||
cp /etc/supervisor/dnsmasq.conf /etc/supervisor/enabled/
|
||||
}
|
||||
|
||||
if ("FRANKENPHP" in $env) {
|
||||
cp /etc/supervisor/frankenphp.conf /etc/supervisor/enabled/
|
||||
} else {
|
||||
cp /etc/supervisor/php-fpm.conf /etc/supervisor/enabled/
|
||||
cp /etc/supervisor/nginx.conf /etc/supervisor/enabled/
|
||||
}
|
||||
|
||||
exec supervisord -c /etc/supervisor/supervisord.conf
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
touch /var/log/nginx/access.log
|
||||
touch /var/log/nginx/error.log
|
||||
touch /var/log/cron/owncloud.log
|
||||
|
||||
echo "# Options in here overwrite the builtin php.ini" > /php.ini
|
||||
chmod 0777 /php.ini
|
||||
PHP_INI_DIR="$(php --ini | grep 'Scan' | cut -d ' ' -f7)"
|
||||
ln -s /php.ini "$PHP_INI_DIR/zz_extra.ini"
|
||||
|
||||
HAZE_UID=${HAZE_UID:-www-data}
|
||||
HAZE_GID=${HAZE_GID:-www-data}
|
||||
|
||||
nc-auto-config
|
||||
shadow-setup
|
||||
|
||||
echo "Running as $HAZE_UID:$HAZE_GID"
|
||||
|
||||
mkdir -p /var/www/html/core/skeleton /var/www/html/build/integration/vendor /var/www/html/build/integration/output /var/www/html/build/integration/work /var/www/html/core/skeleton /var/www/.composer/cache /var/www/html/apps/spreed/tests/integration/vendor/composer
|
||||
chown -R "$HAZE_UID":"$HAZE_GID" /var/www/html/data /var/www/html/config
|
||||
chown "$HAZE_UID":"$HAZE_GID" /var/www/html/core/skeleton /var/www/html/build/integration/vendor /var/www/html/build/integration/composer.lock /var/www/html/build/integration/output /var/www/html/build/integration/work /var/www/html/core/skeleton /var/www/.composer/cache /var/www/html/apps/spreed/tests/integration/vendor/composer
|
||||
|
||||
echo "{}" > /var/www/html/build/integration/composer.lock
|
||||
|
||||
echo "Starting server using $SQL database…"
|
||||
|
||||
# tail --follow --retry /var/log/nginx/*.log /var/log/cron/owncloud.log &
|
||||
|
||||
chmod +sx /sbin/sudo
|
||||
|
||||
mkdir -p /var/log/nginx /tmp /var/run/blackfire
|
||||
touch /var/log/nginx/access.log
|
||||
touch /var/log/nginx/error.log
|
||||
|
||||
HAZE_UID=${HAZE_UID:-1000}
|
||||
HAZE_GID=${HAZE_GID:-1000}
|
||||
|
||||
if [ "$(getent group "$HAZE_GID")" ]; then
|
||||
groupadd haze
|
||||
EXTRA_GROUP=" -G haze"
|
||||
else
|
||||
groupadd -g "$HAZE_GID" haze
|
||||
EXTRA_GROUP=""
|
||||
fi
|
||||
|
||||
useradd -u "$HAZE_UID" -g "$HAZE_GID""$EXTRA_GROUP" haze
|
||||
chown -R haze:"$HAZE_GID" /home/haze
|
||||
|
||||
if [ -f "/var/run/docker.sock" ]; then
|
||||
groupadd docker -g "$(stat --format "%g" /var/run/docker.sock)"
|
||||
usermod -a -G docker haze
|
||||
fi
|
||||
|
||||
if [ -n "${REDIS_TLS:-}" ]
|
||||
then
|
||||
redis-server --protected-mode no \
|
||||
--tls-port 6379 --port 0 \
|
||||
--tls-cert-file /redis-certificates/server.crt \
|
||||
--tls-key-file /redis-certificates/server.key \
|
||||
--tls-ca-cert-file /redis-certificates/ca.crt &
|
||||
else
|
||||
redis-server --protected-mode no &
|
||||
fi
|
||||
|
||||
if [ -n "${BLACKFIRE_SERVER_ID:-}" ]
|
||||
then
|
||||
sh -c '
|
||||
yes | blackfire agent:config --server-id=$BLACKFIRE_SERVER_ID --server-token=$BLACKFIRE_SERVER_TOKEN
|
||||
BLACKFIRE_LOG_LEVEL=4 BLACKFIRE_LOG_FILE=/var/log/agent.log blackfire agent &
|
||||
'&
|
||||
fi
|
||||
|
||||
php-fpm --fpm-config /etc/php-fpm.conf&
|
||||
|
||||
nginx -c /etc/nginx.conf
|
||||
|
|
@ -1,12 +1,7 @@
|
|||
{runCommand}:
|
||||
runCommand "configs" {} ''
|
||||
mkdir -p $out/etc
|
||||
mkdir -p $out/etc/sudoers.d
|
||||
mkdir -p $out/conf
|
||||
cp ${./configs/cron.conf} $out/etc/oc-cron.conf
|
||||
cp ${./configs/nginx-app.conf} $out/conf/nginx-app.conf
|
||||
cp ${./configs/sudoers} $out/etc/sudoers.d/haze
|
||||
cp -r ${./configs/nc} $out/etc/nc
|
||||
cp ${./php-fpm.conf} $out/etc/php-fpm.conf
|
||||
cp ${./nginx.conf} $out/etc/nginx.conf
|
||||
mkdir -p $out
|
||||
cp -r ${./configs} $out/etc
|
||||
chmod -R +w $out/etc
|
||||
mkdir $out/etc/supervisor/enabled/
|
||||
''
|
||||
|
|
|
|||
6
nix/image/configs/dnsmasq.conf
Normal file
6
nix/image/configs/dnsmasq.conf
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
listen-address=127.0.0.22
|
||||
no-resolv
|
||||
|
||||
address=/PROXY_BASE/HOST_IP
|
||||
|
||||
server=UPSTREAM_DNS
|
||||
1
nix/image/configs/home/.sqliterc
Normal file
1
nix/image/configs/home/.sqliterc
Normal file
|
|
@ -0,0 +1 @@
|
|||
.mode table
|
||||
|
|
@ -1,12 +1,19 @@
|
|||
<?php $CONFIG=[
|
||||
<?php
|
||||
|
||||
$extra_config = [];
|
||||
if (file_exists(__DIR__ . '/nextcloud.json')) {
|
||||
$extra_config = json_decode(file_get_contents(__DIR__ . '/nextcloud.json'), true);
|
||||
}
|
||||
|
||||
|
||||
$CONFIG = array_merge_recursive($extra_config, [
|
||||
'debug' => true,
|
||||
'appstoreenabled' => false,
|
||||
'memcache.local' => '\\OC\\Memcache\\APCu',
|
||||
'memcache.distributed' => '\\OC\\Memcache\\APCu',
|
||||
'memcache.locking' => '\\OC\\Memcache\\APCu',
|
||||
'memcache.distributed' => '\\OC\\Memcache\\Redis',
|
||||
'memcache.locking' => '\\OC\\Memcache\\Redis',
|
||||
'allow_local_remote_servers' => true,
|
||||
'trusted_domains' => ['cloud'],
|
||||
'profiling.secret' => 'haze',
|
||||
'profiling.path' => '/tmp/profiling',
|
||||
//PLACEHOLDER
|
||||
];
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -1,2 +1 @@
|
|||
'redis' => ['host' => 'localhost'],
|
||||
//PLACEHOLDER
|
||||
|
|
|
|||
|
|
@ -2,10 +2,9 @@
|
|||
'host' => 'tls://127.0.0.1',
|
||||
'port' => 6379,
|
||||
'ssl_context' => [
|
||||
'local_cert' => '/redis-certificates/client.crt',
|
||||
'local_pk' => '/redis-certificates/client.key',
|
||||
'cafile' => '/redis-certificates/ca.crt',
|
||||
'local_cert' => '/certificates/redis/client.crt',
|
||||
'local_pk' => '/certificates/redis/client.key',
|
||||
'cafile' => '/certificates/redis/ca.crt',
|
||||
'verify_peer_name' => false,
|
||||
],
|
||||
],
|
||||
//PLACEHOLDER
|
||||
|
|
|
|||
15
nix/image/configs/nc/s3s.php
Normal file
15
nix/image/configs/nc/s3s.php
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
'objectstore' => [
|
||||
'class' => 'OC\Files\ObjectStore\S3',
|
||||
'arguments' => [
|
||||
'bucket' => 'nextcloud',
|
||||
'autocreate' => true,
|
||||
'key' => 'minio',
|
||||
'secret' => 'minio123',
|
||||
'hostname' => 's3',
|
||||
'port' => 9000,
|
||||
'use_ssl' => true,
|
||||
'use_path_style' => true,
|
||||
'uploadPartSize' => 52428800,
|
||||
'use_nextcloud_bundle' => true,
|
||||
],
|
||||
],
|
||||
|
|
@ -69,7 +69,17 @@ http {
|
|||
access_log off;
|
||||
}
|
||||
|
||||
include /conf/nginx-app.conf;
|
||||
location ^~ /store_apps {
|
||||
root /var/www;
|
||||
try_files $uri /index.php$request_uri;
|
||||
access_log off; # Optional: Don't log access to assets
|
||||
|
||||
location ~ \.wasm$ {
|
||||
default_type application/wasm;
|
||||
}
|
||||
}
|
||||
|
||||
include /etc/nginx-app.conf;
|
||||
|
||||
location ~ \.php(?:$|/) {
|
||||
rewrite ^/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|ocs-provider\/.+|.+\/richdocumentscode\/proxy) /index.php$request_uri;
|
||||
|
|
@ -99,5 +109,16 @@ http {
|
|||
expires 7d; # Cache-Control policy borrowed from `.htaccess`
|
||||
access_log off; # Optional: Don't log access to assets
|
||||
}
|
||||
|
||||
location /fpm-status {
|
||||
include /conf/fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||
fastcgi_pass php-handler;
|
||||
fastcgi_read_timeout 3600;
|
||||
proxy_request_buffering off;
|
||||
fastcgi_request_buffering off;
|
||||
fastcgi_buffering off;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
[global]
|
||||
|
||||
error_log = /proc/self/fd/2
|
||||
error_log = /var/log/php-fpm-error.log
|
||||
daemonize = no
|
||||
|
||||
[www]
|
||||
|
||||
access.log = /proc/self/fd/2
|
||||
access.log =/var/log/php-fpm-access.log
|
||||
|
||||
user = haze
|
||||
group = haze
|
||||
|
|
@ -21,6 +21,7 @@ pm.max_children = 5
|
|||
pm.start_servers = 2
|
||||
pm.min_spare_servers = 1
|
||||
pm.max_spare_servers = 3
|
||||
pm.status_path = /fpm-status
|
||||
|
||||
clear_env = no
|
||||
|
||||
2
nix/image/configs/supervisor/blackfire.conf
Normal file
2
nix/image/configs/supervisor/blackfire.conf
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
[program:blackfire]
|
||||
command = blackfire agent
|
||||
2
nix/image/configs/supervisor/dnsmasq.conf
Normal file
2
nix/image/configs/supervisor/dnsmasq.conf
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
[program:dnsmasq]
|
||||
command = /bin/dnsmasq --keep-in-foreground -u root
|
||||
3
nix/image/configs/supervisor/frankenphp.conf
Normal file
3
nix/image/configs/supervisor/frankenphp.conf
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
[program:frankenphp]
|
||||
command = /bin/frankenphp php-server
|
||||
directory = /var/www/html
|
||||
2
nix/image/configs/supervisor/nginx.conf
Normal file
2
nix/image/configs/supervisor/nginx.conf
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
[program:nginx]
|
||||
command = /bin/nginx -c /etc/nginx.conf
|
||||
2
nix/image/configs/supervisor/php-fpm.conf
Normal file
2
nix/image/configs/supervisor/php-fpm.conf
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
[program:php-fpm]
|
||||
command = /bin/php-fpm --fpm-config /etc/php-fpm.conf
|
||||
2
nix/image/configs/supervisor/redis-plain.conf
Normal file
2
nix/image/configs/supervisor/redis-plain.conf
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
[program:redis]
|
||||
command = /bin/redis-server --protected-mode no
|
||||
2
nix/image/configs/supervisor/redis-tls.conf
Normal file
2
nix/image/configs/supervisor/redis-tls.conf
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
[program:redis-tls]
|
||||
command = /bin/redis-server --protected-mode no --tls-port 6379 --port 0 --tls-cert-file /certificates/redis/server.crt --tls-key-file /certificates/redis/server.key --tls-ca-cert-file /certificates/redis/ca.crt
|
||||
19
nix/image/configs/supervisor/supervisord.conf
Normal file
19
nix/image/configs/supervisor/supervisord.conf
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
[supervisord]
|
||||
logfile = /dev/stdout
|
||||
logfile_maxbytes = 0
|
||||
nodaemon = true
|
||||
pidfile = /var/run/supervisord.pid
|
||||
user = root
|
||||
|
||||
[unix_http_server]
|
||||
file = /var/run/supervisor.sock
|
||||
chmod = 0777
|
||||
|
||||
[rpcinterface:supervisor]
|
||||
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
|
||||
|
||||
[supervisorctl]
|
||||
serverurl = unix:///var/run/supervisor.sock
|
||||
|
||||
[include]
|
||||
files = enabled/*
|
||||
|
|
@ -7,10 +7,9 @@
|
|||
blackfire,
|
||||
coreutils,
|
||||
getent,
|
||||
writers,
|
||||
shadow,
|
||||
buildEnv,
|
||||
runCommand,
|
||||
cacert,
|
||||
callPackage,
|
||||
cronie,
|
||||
redis,
|
||||
|
|
@ -33,18 +32,20 @@
|
|||
writeShellApplication,
|
||||
vim,
|
||||
helix,
|
||||
python3Packages,
|
||||
dnsmasq,
|
||||
frankenphp,
|
||||
nushell,
|
||||
}: let
|
||||
inherit (builtins) toString;
|
||||
inherit (lib) readFile getExe concatStringsSep splitString take;
|
||||
inherit (builtins) toString compareVersions;
|
||||
inherit (lib) readFile getExe concatStringsSep splitString take optionals;
|
||||
|
||||
version = (fromTOML (readFile ../../Cargo.toml)).package.version;
|
||||
|
||||
phpVersion = concatStringsSep "." (take 2 (splitString "." php.version));
|
||||
phpEnv = callPackage ./php.nix {inherit debug php;};
|
||||
|
||||
bootstrap = writeShellApplication {
|
||||
name = "bootstrap";
|
||||
runtimeInputs = [getent];
|
||||
text = readFile ./bootstrap.sh;
|
||||
};
|
||||
bootstrap = writers.writeNuBin "bootstrap" (readFile ./bootstrap);
|
||||
shadowSetupScript = writeShellApplication {
|
||||
name = "shadow-setup";
|
||||
text = dockerTools.shadowSetup;
|
||||
|
|
@ -70,14 +71,6 @@
|
|||
php = phpEnv;
|
||||
};
|
||||
|
||||
phpunitWrapped = majorVersion:
|
||||
writeShellApplication {
|
||||
name = "phpunit${toString majorVersion}";
|
||||
text = ''
|
||||
${phpunitUnwrapped (toString majorVersion)}/bin/phpunit "$@"
|
||||
'';
|
||||
};
|
||||
|
||||
phpunit = writeShellApplication {
|
||||
name = "phpunit";
|
||||
runtimeInputs = [jq];
|
||||
|
|
@ -104,9 +97,9 @@
|
|||
'';
|
||||
};
|
||||
|
||||
redis-certificates = runCommand "scripts" {} ''
|
||||
certificates = runCommand "scripts" {} ''
|
||||
mkdir -p $out
|
||||
cp -r ${../../redis-certificates} $out/redis-certificates
|
||||
cp -r ${../../certificates} $out/certificates
|
||||
'';
|
||||
clamav-data = runCommand "scripts" {} ''
|
||||
mkdir -p $out/etc
|
||||
|
|
@ -145,6 +138,10 @@
|
|||
oracle-instantclient
|
||||
vim
|
||||
helix
|
||||
python3Packages.supervisor
|
||||
dnsmasq
|
||||
nushell
|
||||
getent
|
||||
];
|
||||
};
|
||||
|
||||
|
|
@ -153,11 +150,20 @@
|
|||
tag = phpVersion;
|
||||
fromImage = baseImage;
|
||||
|
||||
copyToRoot = [
|
||||
phpEnv
|
||||
phpEnv.packages.composer
|
||||
phpunit
|
||||
];
|
||||
copyToRoot =
|
||||
[
|
||||
phpEnv
|
||||
phpEnv.packages.composer
|
||||
phpunit
|
||||
]
|
||||
++ optionals ((compareVersions phpVersion "8.2") == 1) [
|
||||
(frankenphp.override {
|
||||
php = php.withExtensions (import ./php-ext.nix {
|
||||
inherit lib php;
|
||||
enableBlackfire = false;
|
||||
});
|
||||
})
|
||||
];
|
||||
};
|
||||
in
|
||||
dockerTools.buildLayeredImage {
|
||||
|
|
@ -170,7 +176,7 @@ in
|
|||
bootstrap
|
||||
configs
|
||||
scripts
|
||||
redis-certificates
|
||||
certificates
|
||||
clamav-data
|
||||
shadowSetupScript
|
||||
];
|
||||
|
|
@ -185,7 +191,14 @@ in
|
|||
'';
|
||||
config = {
|
||||
Cmd = [(getExe bootstrap)];
|
||||
Env = ["EDITOR=hx" "WEBROOT=/var/www/html"];
|
||||
Env = [
|
||||
"EDITOR=hx"
|
||||
"WEBROOT=/var/www/html"
|
||||
"HAZE_IMAGE_VERSION=${toString version}"
|
||||
];
|
||||
WorkingDir = "/var/www/html";
|
||||
Labels = {
|
||||
"nl.icewind.haze.version" = toString version;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
45
nix/image/php-ext.nix
Normal file
45
nix/image/php-ext.nix
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
{
|
||||
lib,
|
||||
php,
|
||||
debug ? false,
|
||||
enableBlackfire ? true,
|
||||
}: let
|
||||
inherit (builtins) compareVersions;
|
||||
inherit (lib) optionals;
|
||||
withBlackfire = enableBlackfire && !debug && ((compareVersions php.version "8.1.0") == 1);
|
||||
in
|
||||
{
|
||||
enabled,
|
||||
all,
|
||||
}:
|
||||
enabled
|
||||
++ (with all;
|
||||
[
|
||||
xdebug
|
||||
excimer
|
||||
inotify
|
||||
redis
|
||||
oci8
|
||||
zip
|
||||
pdo
|
||||
pdo_pgsql
|
||||
pdo_sqlite
|
||||
pdo_mysql
|
||||
pgsql
|
||||
intl
|
||||
curl
|
||||
mbstring
|
||||
pcntl
|
||||
ldap
|
||||
exif
|
||||
gmp
|
||||
apcu
|
||||
ffi
|
||||
imagick
|
||||
]
|
||||
++ optionals (!debug) [
|
||||
# smbclient # this breaks the build for no apparent reason
|
||||
]
|
||||
++ optionals withBlackfire [
|
||||
blackfire
|
||||
])
|
||||
|
|
@ -2,56 +2,23 @@
|
|||
lib,
|
||||
php,
|
||||
debug ? false,
|
||||
}: let
|
||||
inherit (lib) optionals;
|
||||
in
|
||||
php.buildEnv {
|
||||
extensions = {
|
||||
enabled,
|
||||
all,
|
||||
}:
|
||||
enabled
|
||||
++ (with all;
|
||||
[
|
||||
xdebug
|
||||
excimer
|
||||
inotify
|
||||
redis
|
||||
oci8
|
||||
zip
|
||||
pdo
|
||||
pdo_pgsql
|
||||
pdo_sqlite
|
||||
pdo_mysql
|
||||
pgsql
|
||||
intl
|
||||
curl
|
||||
mbstring
|
||||
pcntl
|
||||
ldap
|
||||
exif
|
||||
gmp
|
||||
apcu
|
||||
ffi
|
||||
]
|
||||
++ optionals (!debug) [
|
||||
smbclient # this breaks the build for no apparent reason
|
||||
blackfire
|
||||
]);
|
||||
extraConfig = ''
|
||||
xdebug.mode=debug,trace,profile
|
||||
xdebug.start_with_request=trigger
|
||||
xdebug.discover_client_host=false
|
||||
xdebug.client_host=hazehost
|
||||
xdebug.log_level=0
|
||||
xdebug.output_dir=/tmp/xdebug
|
||||
}:
|
||||
php.buildEnv {
|
||||
extensions = import ./php-ext.nix {inherit lib php debug;};
|
||||
extraConfig = ''
|
||||
xdebug.mode=debug,trace,profile
|
||||
xdebug.start_with_request=trigger
|
||||
xdebug.discover_client_host=false
|
||||
xdebug.client_host=hazehost
|
||||
xdebug.log_level=0
|
||||
xdebug.output_dir=/tmp/xdebug
|
||||
|
||||
memory_limit=512M
|
||||
memory_limit=512M
|
||||
|
||||
post_max_size 10G
|
||||
upload_max_filesize 10G
|
||||
post_max_size 10G
|
||||
upload_max_filesize 10G
|
||||
|
||||
apc.enable_cli=1
|
||||
opcache.enable_cli=1
|
||||
'';
|
||||
}
|
||||
apc.enable_cli=1
|
||||
opcache.enable_cli=1
|
||||
'';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,22 @@
|
|||
#!/bin/sh
|
||||
#!/bin/nu
|
||||
|
||||
USER=$1
|
||||
PASSWORD=$2
|
||||
def main [username: string, password: string] {
|
||||
cd $env.WEBROOT;
|
||||
let sql = match $env.SQL {
|
||||
"oracle" => "oci"
|
||||
"mariadb" => "mysql"
|
||||
_ => $env.SQL
|
||||
}
|
||||
let dbName = match $env.SQL {
|
||||
"oracle" => "xe"
|
||||
_ => "haze"
|
||||
}
|
||||
let dbUser = match $env.SQL {
|
||||
"oracle" => "system"
|
||||
_ => "haze"
|
||||
}
|
||||
let dbPass = "haze"
|
||||
let dbHost = $env.SQL
|
||||
|
||||
if [ -z "$USER" ] || [ -z "$PASSWORD" ]; then
|
||||
echo "Usage: install \$USER \$PASSWORD"
|
||||
exit;
|
||||
fi
|
||||
|
||||
cd $WEBROOT
|
||||
|
||||
if [ "$SQL" = "oci" ]; then
|
||||
# oracle is a special snowflake
|
||||
occ maintenance:install --admin-user=$USER --admin-pass=$PASSWORD --database=$SQL --database-name=xe --database-host=$SQL --database-user=system --database-pass=haze
|
||||
elif [ "$SQL" = "mariadb" ]; then
|
||||
occ maintenance:install --admin-user=$USER --admin-pass=$PASSWORD --database=mysql --database-name=haze --database-host=$SQL --database-user=haze --database-pass=haze
|
||||
else
|
||||
occ maintenance:install --admin-user=$USER --admin-pass=$PASSWORD --database=$SQL --database-name=haze --database-host=$SQL --database-user=haze --database-pass=haze
|
||||
fi;
|
||||
occ maintenance:install --admin-user $username --admin-pass $password --database $sql --database-name $dbName --database-host $dbHost --database-user $dbUser --database-pass $dbPass
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,22 @@
|
|||
#!/bin/sh
|
||||
#!/bin/nu
|
||||
|
||||
cd $WEBROOT/build/integration
|
||||
./run.sh "$@"
|
||||
def --wrapped main [feature: path, ...rest] {
|
||||
mut feature = $feature;
|
||||
mut workdir = $"($env.WEBROOT)/build/integration"
|
||||
if ($feature | str starts-with "apps/") {
|
||||
let parts = $feature | split row '/'
|
||||
occ app:enable $parts.1
|
||||
let parts = $feature | split row -n 2 '/features/'
|
||||
$workdir = $parts.0
|
||||
$feature = $"features/($parts.1)"
|
||||
} else if ($feature | str starts-with "build/integration/") {
|
||||
$feature = $feature | str replace "build/integration/" ""
|
||||
}
|
||||
|
||||
# don't use the proxy urls for generated urls
|
||||
occ config:system:delete overwritehost
|
||||
occ config:system:delete overwriteprotocol
|
||||
|
||||
cd $workdir
|
||||
bash run.sh $feature ...$rest
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env bash
|
||||
#!/bin/nu
|
||||
|
||||
touch /var/log/nginx/access.log
|
||||
touch /var/log/nginx/error.log
|
||||
|
|
@ -7,59 +7,30 @@ touch /var/log/cron/owncloud.log
|
|||
cp /etc/nc/config.php /var/www/html/config/config.php
|
||||
chmod 0755 /var/www/html/config/config.php
|
||||
|
||||
if [ "$SQL" = "mysql" ]
|
||||
then
|
||||
cp /etc/nc/autoconfig_mysql.php /var/www/html/config/autoconfig.php
|
||||
fi
|
||||
let configName = match $env.SQL {
|
||||
"oracle" => "oci"
|
||||
_ => $env.SQL
|
||||
|
||||
if [ "$SQL" = "mariadb" ]
|
||||
then
|
||||
cp /etc/nc/autoconfig_mariadb.php /var/www/html/config/autoconfig.php
|
||||
fi
|
||||
}
|
||||
let configPath = $"/etc/nc/autoconfig_($configName).php"
|
||||
|
||||
if [ "$SQL" = "pgsql" ]
|
||||
then
|
||||
cp /etc/nc/autoconfig_pgsql.php /var/www/html/config/autoconfig.php
|
||||
fi
|
||||
if ($configPath | path exists) {
|
||||
cp $configPath /var/www/html/config/autoconfig.php
|
||||
}
|
||||
|
||||
if [ "$SQL" = "oci" ]
|
||||
then
|
||||
cp /etc/nc/autoconfig_oci.php /var/www/html/config/autoconfig.php
|
||||
fi
|
||||
def loadExtraConfig [name: string] {
|
||||
sed -i $'/\/\/PLACEHOLDER/ r /etc/nc/($name).php' /var/www/html/config/config.php
|
||||
}
|
||||
|
||||
if [ -n "${S3:-}" ]
|
||||
then
|
||||
sed -i '/\/\/PLACEHOLDER/ r /etc/nc/s3.php' /var/www/html/config/config.php
|
||||
fi
|
||||
let extraConfigs = ["S3", "S3S", "S3MB", "S3M", "SWIFT", "SWIFTV3", "AZURE"];
|
||||
$extraConfigs | each {
|
||||
if ($in in $env) {
|
||||
loadExtraConfig ($in | str downcase)
|
||||
}
|
||||
}
|
||||
|
||||
if [ -n "${S3MB:-}" ]
|
||||
then
|
||||
sed -i '/\/\/PLACEHOLDER/ r /etc/nc/s3mb.php' /var/www/html/config/config.php
|
||||
fi
|
||||
|
||||
if [ -n "${S3M:-}" ]
|
||||
then
|
||||
sed -i '/\/\/PLACEHOLDER/ r /etc/nc/s3m.php' /var/www/html/config/config.php
|
||||
fi
|
||||
|
||||
if [ -n "${SWIFT:-}" ]
|
||||
then
|
||||
sed -i '/\/\/PLACEHOLDER/ r /etc/nc/swift.php' /var/www/html/config/config.php
|
||||
fi
|
||||
|
||||
if [ -n "${SWIFTV3:-}" ]
|
||||
then
|
||||
sed -i '/\/\/PLACEHOLDER/ r /etc/nc/swiftv3.php' /var/www/html/config/config.php
|
||||
fi
|
||||
|
||||
if [ -n "${AZURE:-}" ]
|
||||
then
|
||||
sed -i '/\/\/PLACEHOLDER/ r /etc/nc/azure.php' /var/www/html/config/config.php
|
||||
fi
|
||||
|
||||
if [ -n "${REDIS_TLS:-}" ]
|
||||
then
|
||||
sed -i '/\/\/PLACEHOLDER/ r /etc/nc/redis-tls.php' /var/www/html/config/config.php
|
||||
else
|
||||
sed -i '/\/\/PLACEHOLDER/ r /etc/nc/redis-default.php' /var/www/html/config/config.php
|
||||
fi
|
||||
if ("REDIS_TLS" in $env) {
|
||||
loadExtraConfig "redis-tls"
|
||||
} else {
|
||||
loadExtraConfig "redis-default"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#!/bin/sh
|
||||
#!/bin/nu
|
||||
|
||||
export XDEBUG_SESSION=haze
|
||||
|
||||
php $WEBROOT/occ "$@"
|
||||
def --wrapped main [...rest] {
|
||||
XDEBUG_SESSION=haze php $"($env.WEBROOT)/occ" ...$rest
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/bin/sh
|
||||
#!/bin/nu
|
||||
|
||||
cd $WEBROOT
|
||||
def --wrapped main [...rest] {
|
||||
cd $env.WEBROOT
|
||||
|
||||
export XDEBUG_SESSION=haze
|
||||
|
||||
phpunit --configuration $WEBROOT/tests/phpunit-autotest.xml $@
|
||||
XDEBUG_SESSION=haze phpunit --configuration $"($env.WEBROOT)/tests/phpunit-autotest.xml" ...$rest
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
final: prev: {
|
||||
haze = final.callPackage ./package.nix {};
|
||||
haze-image-php-85 = final.callPackage ./image/haze.nix {php = final.php85;};
|
||||
haze-image-php-84 = final.callPackage ./image/haze.nix {php = final.php84;};
|
||||
haze-image-php-83 = final.callPackage ./image/haze.nix {php = final.php83;};
|
||||
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-80 = final.callPackage ./image/haze.nix {php = final.php80;};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,15 @@
|
|||
{
|
||||
rustPlatform,
|
||||
pkg-config,
|
||||
lib,
|
||||
git,
|
||||
}: let
|
||||
inherit (lib) getExe;
|
||||
inherit (lib.sources) sourceByRegex;
|
||||
inherit (builtins) fromTOML readFile;
|
||||
src = sourceByRegex ../. ["Cargo.*" "(src|redis-certificates)(/.*)?"];
|
||||
src = sourceByRegex ../. ["Cargo.*" "(src|certificates)(/.*)?"];
|
||||
version = (fromTOML (readFile ../Cargo.toml)).package.version;
|
||||
in
|
||||
rustPlatform.buildRustPackage rec {
|
||||
rustPlatform.buildRustPackage {
|
||||
pname = "haze";
|
||||
|
||||
inherit src version;
|
||||
|
|
@ -20,7 +19,7 @@ in
|
|||
cargoLock = {
|
||||
lockFile = ../Cargo.lock;
|
||||
outputHashes = {
|
||||
"hyper-reverse-proxy-0.5.2-dev" = "sha256-+ebi4FVVkiOpf75e8K5oGkHJBYQjLNJhUPNj+78zd7Q=";
|
||||
"hyper-reverse-proxy-0.5.2-dev" = "sha256-awmj5aLFTea+kj81cwmfP1HWlWezwEKfyQSUJWjtamk=";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
20
src/args.rs
20
src/args.rs
|
|
@ -124,9 +124,12 @@ pub enum GitOperation {
|
|||
///
|
||||
/// "main" and "master" can be used interchangeably.
|
||||
#[strum(props(
|
||||
Args = "[branch] branch to checkout, defaults to the branch matching the current server version"
|
||||
Args = "[branch] branch to checkout, defaults to the branch matching the current server version [-v] verbose"
|
||||
))]
|
||||
Checkout { branch: Option<String> },
|
||||
Checkout {
|
||||
branch: Option<String>,
|
||||
verbose: bool,
|
||||
},
|
||||
}
|
||||
|
||||
impl SubCommand for GitOperation {
|
||||
|
|
@ -320,18 +323,24 @@ impl HazeArgs {
|
|||
HazeCommand::Checkout => {
|
||||
let branch = args.next().map(S::into);
|
||||
Ok(HazeArgs::Git {
|
||||
operation: GitOperation::Checkout { branch },
|
||||
operation: GitOperation::Checkout {
|
||||
branch,
|
||||
verbose: false,
|
||||
},
|
||||
})
|
||||
}
|
||||
HazeCommand::Git => {
|
||||
let mut args = args.peekable();
|
||||
let operation = args
|
||||
.next()
|
||||
.ok_or_else(|| Report::msg("No git operation provided"))?;
|
||||
match operation.as_ref() {
|
||||
"checkout" => {
|
||||
let verbose = args.next_if(|arg| arg.as_ref() == "-v").is_some();
|
||||
let branch = args.next().map(S::into);
|
||||
let verbose = verbose | args.next_if(|arg| arg.as_ref() == "-v").is_some();
|
||||
Ok(HazeArgs::Git {
|
||||
operation: GitOperation::Checkout { branch },
|
||||
operation: GitOperation::Checkout { branch, verbose },
|
||||
})
|
||||
}
|
||||
"pull" => Ok(HazeArgs::Git {
|
||||
|
|
@ -469,7 +478,7 @@ pub enum HazeCommand {
|
|||
Edit,
|
||||
/// Reload the php configuration in the instance
|
||||
#[strum(props(
|
||||
Details = "note: you can overwrite <yellow>php.ini</yellow> settings with <literal>haze</literal> <arg>[filter]</arg> <literal>edit /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>"
|
||||
))]
|
||||
Reload,
|
||||
}
|
||||
|
|
@ -505,6 +514,7 @@ impl SubCommand for HazeCommand {
|
|||
fn test_arg_parse() {
|
||||
let config = HazeConfig {
|
||||
sources_root: Default::default(),
|
||||
app_directories: Default::default(),
|
||||
work_dir: Default::default(),
|
||||
auto_setup: Default::default(),
|
||||
volume: vec![],
|
||||
|
|
|
|||
167
src/cloud.rs
167
src/cloud.rs
|
|
@ -1,13 +1,14 @@
|
|||
use crate::config::{HazeConfig, HazeVolumeConfig};
|
||||
use crate::database::Database;
|
||||
use crate::exec::{exec, exec_io, exec_tty, ExitCode};
|
||||
use crate::mapping::{default_mappings, Mapping};
|
||||
use crate::mapping::{for_config, Mapping};
|
||||
use crate::php::{PhpVersion, PHP_MEMORY_LIMIT};
|
||||
use crate::service::Service;
|
||||
use crate::service::ServiceTrait;
|
||||
use bollard::container::{ListContainersOptions, RemoveContainerOptions, UpdateContainerOptions};
|
||||
use bollard::models::ContainerState;
|
||||
use bollard::network::CreateNetworkOptions;
|
||||
use crate::sources::download_nc;
|
||||
use bollard::config::NetworkCreateRequest;
|
||||
use bollard::models::{ContainerState, ContainerUpdateBody};
|
||||
use bollard::query_parameters::{ListContainersOptions, RemoveContainerOptions};
|
||||
use bollard::Docker;
|
||||
use camino::{Utf8Path, Utf8PathBuf};
|
||||
use flate2::read::GzDecoder;
|
||||
|
|
@ -19,9 +20,9 @@ use std::borrow::Cow;
|
|||
use std::collections::HashMap;
|
||||
use std::fmt::Display;
|
||||
use std::fs;
|
||||
use std::fs::read_to_string;
|
||||
use std::fs::{read_to_string, write};
|
||||
use std::io::{stdout, Cursor, Read, Stdout, Write};
|
||||
use std::iter::Peekable;
|
||||
use std::iter::{once, Peekable};
|
||||
use std::net::IpAddr;
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
use std::str::FromStr;
|
||||
|
|
@ -57,6 +58,7 @@ pub struct CloudOptions {
|
|||
pub php: PhpVersion,
|
||||
pub services: Vec<Service>,
|
||||
pub app_packages: Vec<Utf8PathBuf>,
|
||||
pub version: Option<String>,
|
||||
}
|
||||
|
||||
impl CloudOptions {
|
||||
|
|
@ -69,6 +71,7 @@ impl CloudOptions {
|
|||
db: Database::default(),
|
||||
services: vec![],
|
||||
app_packages: vec![],
|
||||
version: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -82,6 +85,7 @@ impl CloudOptions {
|
|||
let mut name = None;
|
||||
let mut services = Vec::new();
|
||||
let mut app_package = Vec::new();
|
||||
let mut version = None;
|
||||
|
||||
while let Some(option) = args.peek() {
|
||||
if let Ok(db_option) = Database::from_str(option.as_ref()) {
|
||||
|
|
@ -96,6 +100,9 @@ impl CloudOptions {
|
|||
} else if option.as_ref().ends_with(".tar.gz") {
|
||||
app_package.push(option.to_string().into());
|
||||
let _ = args.next();
|
||||
} else if let Some(v) = option.as_ref().strip_prefix("v") {
|
||||
version = Some(v.into());
|
||||
let _ = args.next();
|
||||
} else if option.as_ref() == "--name" {
|
||||
let _ = args.next();
|
||||
name = args.next().map(|s| s.into());
|
||||
|
|
@ -112,6 +119,7 @@ impl CloudOptions {
|
|||
.unwrap_or_default(),
|
||||
services,
|
||||
app_packages: app_package,
|
||||
version,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -144,44 +152,44 @@ fn test_option_parse() {
|
|||
..Default::default()
|
||||
}
|
||||
);
|
||||
let mut args = vec!["7"].into_iter().peekable();
|
||||
let mut args = vec!["8"].into_iter().peekable();
|
||||
assert_eq!(
|
||||
CloudOptions::parse(&config, &mut args).unwrap(),
|
||||
CloudOptions {
|
||||
php: PhpVersion::Php74,
|
||||
php: PhpVersion::Php80,
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
let mut args = vec!["7", "pgsql", "rest"].into_iter().peekable();
|
||||
let mut args = vec!["8.1", "pgsql", "rest"].into_iter().peekable();
|
||||
assert_eq!(
|
||||
CloudOptions::parse(&config, &mut args).unwrap(),
|
||||
CloudOptions {
|
||||
php: PhpVersion::Php74,
|
||||
php: PhpVersion::Php81,
|
||||
db: Database::Postgres,
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
let mut args = vec!["7", "ldap", "pgsql"].into_iter().peekable();
|
||||
let mut args = vec!["8", "ldap", "pgsql"].into_iter().peekable();
|
||||
assert_eq!(
|
||||
CloudOptions::parse(&config, &mut args).unwrap(),
|
||||
CloudOptions {
|
||||
php: PhpVersion::Php74,
|
||||
php: PhpVersion::Php80,
|
||||
db: Database::Postgres,
|
||||
services: vec![Service::Ldap(Ldap), Service::LdapAdmin(LdapAdmin)],
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
let mut args = vec!["7", "pgsql", "ldap"].into_iter().peekable();
|
||||
let mut args = vec!["8", "pgsql", "ldap"].into_iter().peekable();
|
||||
assert_eq!(
|
||||
CloudOptions::parse(&config, &mut args).unwrap(),
|
||||
CloudOptions {
|
||||
php: PhpVersion::Php74,
|
||||
php: PhpVersion::Php80,
|
||||
db: Database::Postgres,
|
||||
services: vec![Service::Ldap(Ldap), Service::LdapAdmin(LdapAdmin)],
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
let mut args = vec!["7", "pgsql", "ldap", "mypreset"]
|
||||
let mut args = vec!["8", "pgsql", "ldap", "mypreset"]
|
||||
.into_iter()
|
||||
.peekable();
|
||||
|
||||
|
|
@ -198,7 +206,7 @@ fn test_option_parse() {
|
|||
assert_eq!(
|
||||
CloudOptions::parse(&config, &mut args).unwrap(),
|
||||
CloudOptions {
|
||||
php: PhpVersion::Php74,
|
||||
php: PhpVersion::Php80,
|
||||
db: Database::Postgres,
|
||||
services: vec![
|
||||
Service::Ldap(Ldap),
|
||||
|
|
@ -210,7 +218,7 @@ fn test_option_parse() {
|
|||
);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Cloud {
|
||||
pub id: String,
|
||||
pub network: String,
|
||||
|
|
@ -245,6 +253,12 @@ impl Cloud {
|
|||
.wrap_err("Failed to create directory for app packages")?;
|
||||
}
|
||||
|
||||
let source_root = if let Some(version) = options.version.as_deref() {
|
||||
download_nc(config, version).await?
|
||||
} else {
|
||||
config.sources_root.clone()
|
||||
};
|
||||
|
||||
let app_volumes = options
|
||||
.app_packages
|
||||
.iter()
|
||||
|
|
@ -272,11 +286,8 @@ impl Cloud {
|
|||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
let mappings = config
|
||||
.volume
|
||||
.iter()
|
||||
.map(Mapping::from)
|
||||
.chain(default_mappings())
|
||||
|
||||
let mappings = for_config(config)
|
||||
.chain(app_volumes.iter().map(Mapping::from))
|
||||
.collect::<Vec<_>>();
|
||||
for mapping in &mappings {
|
||||
|
|
@ -286,9 +297,56 @@ impl Cloud {
|
|||
.wrap_err_with(|| format!("Failed to setup work directory {}", mapping.source))?;
|
||||
}
|
||||
|
||||
let mut nc_config = Value::Object(
|
||||
config
|
||||
.auto_setup
|
||||
.config
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|(key, value)| (key, serde_json::to_value(value).unwrap()))
|
||||
.collect(),
|
||||
);
|
||||
nc_config["apps_paths"] = Value::Array(
|
||||
once("apps")
|
||||
.chain(
|
||||
config
|
||||
.app_directories
|
||||
.iter()
|
||||
.filter_map(|dir| dir.file_name()),
|
||||
)
|
||||
.map(|name| {
|
||||
[
|
||||
(
|
||||
String::from("path"),
|
||||
Value::from(format!("/var/www/html/{}", name)),
|
||||
),
|
||||
(String::from("url"), Value::from(format!("/{}", name))),
|
||||
(String::from("writable"), Value::from(false)),
|
||||
]
|
||||
.into_iter()
|
||||
.collect()
|
||||
})
|
||||
.chain(once(
|
||||
[
|
||||
(String::from("path"), Value::from("/var/www/store_apps")),
|
||||
(String::from("url"), Value::from("/store_apps")),
|
||||
(String::from("writable"), Value::from(true)),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
))
|
||||
.collect(),
|
||||
);
|
||||
write(
|
||||
workdir.join("config/nextcloud.json"),
|
||||
serde_json::to_string_pretty(&nc_config).unwrap(),
|
||||
)
|
||||
.into_diagnostic()
|
||||
.wrap_err("Failed to write config json")?;
|
||||
|
||||
let network = docker
|
||||
.create_network(CreateNetworkOptions {
|
||||
name: id.as_str(),
|
||||
.create_network(NetworkCreateRequest {
|
||||
name: id.clone(),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
|
|
@ -296,7 +354,7 @@ impl Cloud {
|
|||
.id;
|
||||
|
||||
let network_info = docker
|
||||
.inspect_network::<String>(&network, None)
|
||||
.inspect_network(&network, None)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
let gateway = network_info
|
||||
|
|
@ -327,7 +385,7 @@ impl Cloud {
|
|||
];
|
||||
let volumes: Vec<String> = mappings
|
||||
.into_iter()
|
||||
.filter_map(|mapping| mapping.get_volume_arg(&id, config))
|
||||
.filter_map(|mapping| mapping.get_volume_arg(&id, config, &source_root))
|
||||
.collect();
|
||||
|
||||
if let Some(db_name) = options
|
||||
|
|
@ -382,6 +440,7 @@ impl Cloud {
|
|||
gateway,
|
||||
&options.services,
|
||||
&config.proxy,
|
||||
options.version.as_deref(),
|
||||
)
|
||||
.await
|
||||
.wrap_err("Failed to start php container")
|
||||
|
|
@ -439,9 +498,26 @@ impl Cloud {
|
|||
}
|
||||
};
|
||||
|
||||
for pre_setup in options
|
||||
.services
|
||||
.iter()
|
||||
.flat_map(|service| service.pre_setup(docker, &id, config).into_iter().flatten())
|
||||
{
|
||||
exec(
|
||||
docker,
|
||||
&container,
|
||||
&uid.to_string(),
|
||||
pre_setup,
|
||||
vec!["NC_IS_CONFIG_READ_ONLY=1"],
|
||||
Some(stdout()),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
containers.push(container);
|
||||
|
||||
let options_clone = options.clone();
|
||||
let proxy_config = config.proxy.clone();
|
||||
let cloud_id = id.clone();
|
||||
let docker_clone = docker.clone();
|
||||
spawn(async move {
|
||||
|
|
@ -455,7 +531,10 @@ impl Cloud {
|
|||
return;
|
||||
}
|
||||
for service in options_clone.services {
|
||||
match service.start_message(&docker_clone, &cloud_id).await {
|
||||
match service
|
||||
.start_message(&docker_clone, &cloud_id, &proxy_config)
|
||||
.await
|
||||
{
|
||||
Ok(Some(msg)) => {
|
||||
println!("{}", msg);
|
||||
}
|
||||
|
|
@ -484,6 +563,9 @@ impl Cloud {
|
|||
|
||||
pub async fn destroy(self, docker: &Docker) -> Result<()> {
|
||||
for container in self.containers {
|
||||
let _ = docker
|
||||
.kill_container(container.trim_start_matches('/'), None)
|
||||
.await;
|
||||
docker
|
||||
.remove_container(
|
||||
container.trim_start_matches('/'),
|
||||
|
|
@ -583,7 +665,7 @@ impl Cloud {
|
|||
config: &HazeConfig,
|
||||
) -> Result<Vec<Cloud>> {
|
||||
let containers = docker
|
||||
.list_containers::<String>(Some(ListContainersOptions {
|
||||
.list_containers(Some(ListContainersOptions {
|
||||
all: true,
|
||||
..Default::default()
|
||||
}))
|
||||
|
|
@ -620,6 +702,7 @@ impl Cloud {
|
|||
let labels = cloud.labels?;
|
||||
let db = labels.get("haze-db")?.parse().ok()?;
|
||||
let php = labels.get("haze-php")?.parse().ok()?;
|
||||
let version = labels.get("haze-version").cloned();
|
||||
|
||||
let found_services = labels
|
||||
.get("haze-services")?
|
||||
|
|
@ -665,6 +748,7 @@ impl Cloud {
|
|||
db,
|
||||
services: found_services,
|
||||
app_packages: vec![],
|
||||
version,
|
||||
},
|
||||
pinned,
|
||||
address,
|
||||
|
|
@ -735,9 +819,9 @@ impl Cloud {
|
|||
docker
|
||||
.update_container(
|
||||
&self.id,
|
||||
UpdateContainerOptions::<String> {
|
||||
ContainerUpdateBody {
|
||||
memory: Some(PHP_MEMORY_LIMIT + 1),
|
||||
..UpdateContainerOptions::default()
|
||||
..ContainerUpdateBody::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
|
|
@ -749,9 +833,9 @@ impl Cloud {
|
|||
docker
|
||||
.update_container(
|
||||
&self.id,
|
||||
UpdateContainerOptions::<String> {
|
||||
ContainerUpdateBody {
|
||||
memory: Some(PHP_MEMORY_LIMIT),
|
||||
..UpdateContainerOptions::default()
|
||||
..ContainerUpdateBody::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
|
|
@ -777,20 +861,25 @@ impl Cloud {
|
|||
format!("/var/www/html/{path}").into()
|
||||
};
|
||||
|
||||
let mut mappings = config
|
||||
.volume
|
||||
.iter()
|
||||
.map(Mapping::from)
|
||||
.chain(default_mappings())
|
||||
.collect::<Vec<_>>();
|
||||
let mut mappings = for_config(config).collect::<Vec<_>>();
|
||||
mappings.sort_by_key(|mapping| usize::MAX - mapping.target.as_str().len());
|
||||
|
||||
for mapping in mappings {
|
||||
if let Some(rel_path) = path.strip_prefix(mapping.target.as_str()) {
|
||||
let rel_path = rel_path.trim_matches('/');
|
||||
return Some(mapping.source(&self.id, config).join(rel_path));
|
||||
return Some(
|
||||
mapping
|
||||
.source(&self.id, config, &config.sources_root)
|
||||
.join(rel_path),
|
||||
);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Cloud {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.id == other.id
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,15 +4,17 @@ use miette::{IntoDiagnostic, Report, Result, WrapErr};
|
|||
use serde::Deserialize;
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryFrom;
|
||||
use std::env::var;
|
||||
use std::env::home_dir;
|
||||
use std::fs::read_to_string;
|
||||
use std::net::IpAddr;
|
||||
use toml::map::Map;
|
||||
use toml::Value;
|
||||
|
||||
#[derive(Debug, Deserialize, Default)]
|
||||
#[serde(from = "RawHazeConfig")]
|
||||
pub struct HazeConfig {
|
||||
pub sources_root: Utf8PathBuf,
|
||||
pub app_directories: Vec<Utf8PathBuf>,
|
||||
pub work_dir: Utf8PathBuf,
|
||||
pub auto_setup: HazeAutoSetupConfig,
|
||||
pub volume: Vec<HazeVolumeConfig>,
|
||||
|
|
@ -27,6 +29,8 @@ pub struct RawHazeConfig {
|
|||
#[serde(default = "default_work_dir")]
|
||||
pub work_dir: Utf8PathBuf,
|
||||
#[serde(default)]
|
||||
pub app_directories: Vec<Utf8PathBuf>,
|
||||
#[serde(default)]
|
||||
pub auto_setup: HazeAutoSetupConfig,
|
||||
#[serde(default)]
|
||||
pub volume: Vec<HazeVolumeConfig>,
|
||||
|
|
@ -42,7 +46,11 @@ impl From<RawHazeConfig> for HazeConfig {
|
|||
fn from(raw: RawHazeConfig) -> Self {
|
||||
fn normalize_path(path: Utf8PathBuf) -> Utf8PathBuf {
|
||||
if path.starts_with("~") {
|
||||
let home = var("HOME").expect("HOME not set");
|
||||
let home = home_dir().expect("can't detect home directory");
|
||||
let home = home
|
||||
.into_os_string()
|
||||
.into_string()
|
||||
.expect("non-utf8 home directory");
|
||||
format!("{}{}", home, &path.as_str()[1..]).into()
|
||||
} else {
|
||||
path
|
||||
|
|
@ -51,6 +59,11 @@ impl From<RawHazeConfig> for HazeConfig {
|
|||
|
||||
HazeConfig {
|
||||
sources_root: normalize_path(raw.sources_root),
|
||||
app_directories: raw
|
||||
.app_directories
|
||||
.into_iter()
|
||||
.map(normalize_path)
|
||||
.collect(),
|
||||
work_dir: normalize_path(raw.work_dir),
|
||||
auto_setup: raw.auto_setup,
|
||||
volume: raw.volume,
|
||||
|
|
@ -75,6 +88,8 @@ pub struct HazeAutoSetupConfig {
|
|||
pub disable_apps: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub post_setup: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub config: Map<String, Value>,
|
||||
}
|
||||
|
||||
impl Default for HazeAutoSetupConfig {
|
||||
|
|
@ -86,6 +101,7 @@ impl Default for HazeAutoSetupConfig {
|
|||
enable_apps: Vec::default(),
|
||||
disable_apps: Vec::default(),
|
||||
post_setup: Vec::default(),
|
||||
config: Map::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -169,7 +185,7 @@ fn load_secret(name: &str, path: Option<String>, raw: Option<String>) -> Result<
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Deserialize, Debug)]
|
||||
#[derive(Default, Deserialize, Debug, Clone)]
|
||||
pub struct ProxyConfig {
|
||||
pub listen: String,
|
||||
#[serde(default)]
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
use crate::exec::{exec, exec_tty, ExitCode};
|
||||
use crate::image::pull_image;
|
||||
use bollard::container::{Config, CreateContainerOptions, NetworkingConfig};
|
||||
use bollard::models::{EndpointSettings, HostConfig};
|
||||
use bollard::config::ContainerCreateBody;
|
||||
use bollard::models::{EndpointSettings, HostConfig, NetworkingConfig};
|
||||
use bollard::query_parameters::CreateContainerOptions;
|
||||
use bollard::Docker;
|
||||
use maplit::hashmap;
|
||||
use miette::{IntoDiagnostic, Report, Result, WrapErr};
|
||||
|
|
@ -193,35 +194,35 @@ impl Database {
|
|||
.wrap_err("Failed to pull database image")?;
|
||||
}
|
||||
let options = Some(CreateContainerOptions {
|
||||
name: format!("{}-db{}", cloud_id, postfix),
|
||||
name: Some(format!("{}-db{}", cloud_id, postfix)),
|
||||
..CreateContainerOptions::default()
|
||||
});
|
||||
let config = Config {
|
||||
image: Some(self.image()),
|
||||
env: Some(self.env()),
|
||||
let config = ContainerCreateBody {
|
||||
image: Some(self.image().into()),
|
||||
env: Some(self.env().into_iter().map(String::from).collect()),
|
||||
host_config: Some(HostConfig {
|
||||
network_mode: Some(network.to_string()),
|
||||
..Default::default()
|
||||
}),
|
||||
labels: Some(hashmap! {
|
||||
"haze-type" => "db",
|
||||
"haze-cloud-id" => cloud_id
|
||||
"haze-type".into() => "db".into(),
|
||||
"haze-cloud-id".into() => cloud_id.into()
|
||||
}),
|
||||
networking_config: Some(NetworkingConfig {
|
||||
endpoints_config: hashmap! {
|
||||
network => EndpointSettings {
|
||||
endpoints_config: Some(hashmap! {
|
||||
String::from(network) => EndpointSettings {
|
||||
aliases: Some(vec![
|
||||
format!("{}{}", self.name(), postfix),
|
||||
format!("db{}", postfix),
|
||||
]),
|
||||
..Default::default()
|
||||
}
|
||||
},
|
||||
}),
|
||||
}),
|
||||
cmd: if self.image() == "mysql:8" {
|
||||
Some(vec![
|
||||
"--default-authentication-plugin",
|
||||
"mysql_native_password",
|
||||
"--default-authentication-plugin".into(),
|
||||
"mysql_native_password".into(),
|
||||
])
|
||||
} else {
|
||||
None
|
||||
|
|
@ -233,10 +234,7 @@ impl Database {
|
|||
.await
|
||||
.into_diagnostic()?
|
||||
.id;
|
||||
docker
|
||||
.start_container::<String>(&id, None)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
docker.start_container(&id, None).await.into_diagnostic()?;
|
||||
Ok(Some(id))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use bollard::container::LogsOptions;
|
||||
use bollard::exec::{CreateExecOptions, ResizeExecOptions, StartExecResults};
|
||||
use bollard::query_parameters::LogsOptions;
|
||||
use bollard::Docker;
|
||||
use futures_util::StreamExt;
|
||||
use miette::{IntoDiagnostic, Report, Result, WrapErr};
|
||||
|
|
@ -189,7 +189,7 @@ pub async fn container_logs(
|
|||
count: usize,
|
||||
follow: bool,
|
||||
) -> Result<()> {
|
||||
let mut stream = docker.logs::<String>(
|
||||
let mut stream = docker.logs(
|
||||
container,
|
||||
Some(LogsOptions {
|
||||
stdout: true,
|
||||
|
|
|
|||
125
src/git.rs
125
src/git.rs
|
|
@ -1,22 +1,33 @@
|
|||
use crate::config::HazeConfig;
|
||||
use crate::Result;
|
||||
use git2::build::CheckoutBuilder;
|
||||
use git2::{Branch, BranchType, Repository, RepositoryState};
|
||||
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
|
||||
use miette::{Context, IntoDiagnostic};
|
||||
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
|
||||
use rayon::ThreadPoolBuilder;
|
||||
use std::fs::read_dir;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::iter::once;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use std::time::Duration;
|
||||
|
||||
fn find_app_repos(root: impl AsRef<Path>) -> Result<impl Iterator<Item = PathBuf>> {
|
||||
let apps_dir = root.as_ref().join("apps");
|
||||
Ok(read_dir(apps_dir)
|
||||
.into_diagnostic()?
|
||||
fn find_app_repos(config: &HazeConfig) -> Result<impl Iterator<Item = PathBuf>> {
|
||||
let apps_dirs = once(config.sources_root.as_path().join("apps"))
|
||||
.chain(config.app_directories.iter().cloned());
|
||||
let dir_handles = apps_dirs
|
||||
.map(|dir| read_dir(dir).into_diagnostic())
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
Ok(dir_handles
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.flatten()
|
||||
.filter(|app| app.path().join(".git").is_dir())
|
||||
.map(|app| app.path()))
|
||||
}
|
||||
|
||||
fn longest_app_branch(root: impl AsRef<Path>) -> Result<(usize, usize)> {
|
||||
Ok(find_app_repos(root)?
|
||||
fn longest_app_branch(config: &HazeConfig) -> Result<(usize, usize)> {
|
||||
Ok(find_app_repos(config)?
|
||||
.filter_map(|app_dir| {
|
||||
let app_name = app_dir.file_name()?.to_str()?;
|
||||
let repo = Repository::init(&app_dir).ok()?;
|
||||
|
|
@ -27,27 +38,32 @@ fn longest_app_branch(root: impl AsRef<Path>) -> Result<(usize, usize)> {
|
|||
.unwrap_or_default())
|
||||
}
|
||||
|
||||
pub fn checkout_all<P: AsRef<Path>>(sources_root: P, mut name: &str) -> Result<()> {
|
||||
pub fn checkout_all(config: &HazeConfig, mut name: &str, verbose: bool) -> Result<()> {
|
||||
// "main" and "master" are interchangeable
|
||||
if name == "main" {
|
||||
name = "master";
|
||||
}
|
||||
|
||||
for app_dir in find_app_repos(sources_root)? {
|
||||
for app_dir in find_app_repos(config)? {
|
||||
let repo = Repository::init(&app_dir)
|
||||
.into_diagnostic()
|
||||
.wrap_err_with(|| format!("Failed to open repository {}", app_dir.display()))?;
|
||||
let app_name = app_dir.file_name().unwrap().to_string_lossy();
|
||||
if let Some(branch) = get_branch(&repo, name)? {
|
||||
if !branch.is_head() {
|
||||
let is_remote = branch.get().is_remote();
|
||||
|
||||
print!("{}", app_dir.file_name().unwrap().to_string_lossy());
|
||||
print!("{app_name}");
|
||||
if let Err(e) = checkout(&repo, &branch, is_remote.then_some(name)) {
|
||||
println!(": {:#} ❌", e);
|
||||
} else {
|
||||
println!(" ✓");
|
||||
}
|
||||
} else if verbose {
|
||||
println!("{app_name} -");
|
||||
}
|
||||
} else if verbose {
|
||||
println!("{app_name} 🛇 Branch not found");
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
|
|
@ -68,42 +84,69 @@ const GIT_BINARY: &str = match option_env!("GIT_BINARY") {
|
|||
None => "git",
|
||||
};
|
||||
|
||||
pub fn pull_all<P: AsRef<Path>>(sources_root: P) -> Result<()> {
|
||||
let sources_root = sources_root.as_ref();
|
||||
let (max_app, max_branch) = longest_app_branch(sources_root)?;
|
||||
pub fn pull_all(config: &HazeConfig) -> Result<()> {
|
||||
let (max_app, max_branch) = longest_app_branch(config)?;
|
||||
|
||||
for app_dir in find_app_repos(sources_root)? {
|
||||
let app_name = app_dir.file_name().unwrap().to_string_lossy();
|
||||
let repo = Repository::init(&app_dir)
|
||||
.into_diagnostic()
|
||||
.wrap_err_with(|| format!("Failed to open repository {}", app_dir.display()))?;
|
||||
let branch_name = current_branch_name(&repo).unwrap_or("unknown".into());
|
||||
let progress = MultiProgress::new();
|
||||
let pull_style = ProgressStyle::with_template("{spinner:.green} {msg}").unwrap();
|
||||
|
||||
print!(
|
||||
"{app_name:<app_width$} - {branch_name:<branch_width$}",
|
||||
app_width = max_app,
|
||||
branch_width = max_branch
|
||||
);
|
||||
let pool = ThreadPoolBuilder::new()
|
||||
.num_threads(8)
|
||||
.build()
|
||||
.into_diagnostic()?;
|
||||
let repos = find_app_repos(config)?.collect::<Vec<_>>();
|
||||
|
||||
if repo.state() != RepositoryState::Clean {
|
||||
println!(": repository not clean ❌");
|
||||
continue;
|
||||
}
|
||||
pool.install(|| {
|
||||
repos.par_iter().for_each(|app_dir| {
|
||||
let app_name = app_dir.file_name().unwrap().to_string_lossy();
|
||||
let Ok(repo) = Repository::init(app_dir) else {
|
||||
return;
|
||||
};
|
||||
let branch_name = current_branch_name(&repo).unwrap_or("unknown".into());
|
||||
|
||||
let output = Command::new(GIT_BINARY)
|
||||
.arg("pull")
|
||||
.current_dir(&app_dir)
|
||||
.output()
|
||||
.into_diagnostic()
|
||||
.wrap_err_with(|| format!("Failed to run git pull for {}", app_dir.display()))?;
|
||||
let bar = ProgressBar::new_spinner().with_style(pull_style.clone());
|
||||
bar.enable_steady_tick(Duration::from_millis(100));
|
||||
let bar = progress.add(bar);
|
||||
|
||||
let msg = |state: &str| {
|
||||
format!(
|
||||
"{app_name:<app_width$} - {branch_name:<branch_width$}{state}",
|
||||
app_width = max_app,
|
||||
branch_width = max_branch
|
||||
)
|
||||
};
|
||||
|
||||
if repo.state() != RepositoryState::Clean {
|
||||
bar.set_message(msg(" ⨯ repository not clean"));
|
||||
bar.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
bar.set_message(msg(""));
|
||||
|
||||
let output = match Command::new(GIT_BINARY)
|
||||
.arg("pull")
|
||||
.current_dir(app_dir)
|
||||
.output()
|
||||
{
|
||||
Ok(output) => output,
|
||||
Err(error) => {
|
||||
bar.set_message(msg(&format!(" ⨯ {error}")));
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if output.status.success() {
|
||||
bar.set_message(msg(" ✓"));
|
||||
} else {
|
||||
let err = String::from_utf8_lossy(&output.stderr);
|
||||
let err_line = err.lines().next().unwrap();
|
||||
bar.set_message(msg(&format!(" ⨯ {err_line}")));
|
||||
}
|
||||
bar.finish();
|
||||
});
|
||||
});
|
||||
|
||||
if output.status.success() {
|
||||
println!(" ✓");
|
||||
} else {
|
||||
println!(" ❌");
|
||||
eprintln!("{}", String::from_utf8_lossy(&output.stderr))
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ fn subcommand_help(command: &dyn SubCommand) {
|
|||
print!(" {}", "[php version]".green());
|
||||
print!(" {}", "[database type]".green());
|
||||
print!(" {}", "[services]".green());
|
||||
print!(" {}", "[vX.Y.Z]".green());
|
||||
}
|
||||
|
||||
let args = if let Some(args) = command.get_str("Args") {
|
||||
|
|
|
|||
91
src/image.rs
91
src/image.rs
|
|
@ -1,17 +1,52 @@
|
|||
use bollard::image::CreateImageOptions;
|
||||
use bollard::models::CreateImageInfo;
|
||||
use bollard::query_parameters::CreateImageOptions;
|
||||
use bollard::Docker;
|
||||
use futures_util::StreamExt;
|
||||
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
|
||||
use miette::{IntoDiagnostic, Result, WrapErr};
|
||||
use std::collections::HashMap;
|
||||
use std::io::stdout;
|
||||
use std::io::Write;
|
||||
use termion::cursor;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
|
||||
pub struct ImageVersion {
|
||||
pub major: u8,
|
||||
pub minor: u8,
|
||||
pub patch: u8,
|
||||
}
|
||||
|
||||
impl FromStr for ImageVersion {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
||||
let mut parts = s.split('.');
|
||||
let major = parts.next().ok_or(())?.parse().map_err(|_| ())?;
|
||||
let minor = parts.next().ok_or(())?.parse().map_err(|_| ())?;
|
||||
let patch = parts.next().ok_or(())?.parse().map_err(|_| ())?;
|
||||
Ok(ImageVersion {
|
||||
major,
|
||||
minor,
|
||||
patch,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ImageVersion {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn image_exists(docker: &Docker, image: &str) -> bool {
|
||||
docker.inspect_image(image).await.is_ok()
|
||||
}
|
||||
|
||||
pub async fn image_version(docker: &Docker, image: &str) -> Option<ImageVersion> {
|
||||
let labels = docker.inspect_image(image).await.ok()?.config?.labels?;
|
||||
let label = labels.get("nl.icewind.haze.version")?;
|
||||
ImageVersion::from_str(label).ok()
|
||||
}
|
||||
|
||||
pub async fn update_image(docker: &Docker, image: &str) -> Result<()> {
|
||||
if image_exists(docker, image).await {
|
||||
force_pull_image(docker, image).await?;
|
||||
|
|
@ -32,9 +67,9 @@ pub async fn force_pull_image(docker: &Docker, image: &str) -> Result<()> {
|
|||
let mut info_stream = docker.create_image(
|
||||
Some(CreateImageOptions {
|
||||
from_image: if image.contains(':') {
|
||||
image.to_string()
|
||||
Some(image.to_string())
|
||||
} else {
|
||||
format!("{}:latest", image)
|
||||
Some(format!("{}:latest", image))
|
||||
},
|
||||
..Default::default()
|
||||
}),
|
||||
|
|
@ -42,35 +77,33 @@ pub async fn force_pull_image(docker: &Docker, image: &str) -> Result<()> {
|
|||
None,
|
||||
);
|
||||
|
||||
let mut bars: HashMap<String, u16> = HashMap::new();
|
||||
let bar_style = ProgressStyle::with_template(
|
||||
"{spinner:.green} {msg} [{elapsed_precise}] [{bar:.cyan/blue}] {bytes:>12}/{total_bytes}",
|
||||
)
|
||||
.unwrap();
|
||||
let mut bars: HashMap<String, ProgressBar> = HashMap::new();
|
||||
let mp = MultiProgress::new();
|
||||
|
||||
let mut stdout = stdout();
|
||||
while let Some(info) = info_stream.next().await {
|
||||
let info: CreateImageInfo = info
|
||||
.into_diagnostic()
|
||||
.wrap_err_with(|| format!("Error while pulling image {}", image))?;
|
||||
if let (Some(id), Some(status), Some(progress)) = (info.id, info.status, info.progress) {
|
||||
match bars.get(&id) {
|
||||
Some(pos) => {
|
||||
let offset = bars.len() as u16 - pos;
|
||||
write!(
|
||||
stdout,
|
||||
"{}{}{} - {:12} {}{}",
|
||||
cursor::Save,
|
||||
cursor::Up(offset),
|
||||
id,
|
||||
status,
|
||||
progress,
|
||||
cursor::Restore
|
||||
)
|
||||
.into_diagnostic()?;
|
||||
}
|
||||
None => {
|
||||
writeln!(stdout, "{} - {:12} {}", id, status, progress).into_diagnostic()?;
|
||||
bars.insert(id, bars.len() as u16);
|
||||
}
|
||||
if let (Some(id), Some(status), Some(progress)) =
|
||||
(info.id, info.status, info.progress_detail)
|
||||
{
|
||||
let bar = bars.entry(id.clone()).or_insert_with(|| {
|
||||
let bar = ProgressBar::new(progress.total.unwrap_or_default() as u64)
|
||||
.with_style(bar_style.clone())
|
||||
.with_message(format!("{id:20} - {status:10}"));
|
||||
mp.add(bar)
|
||||
});
|
||||
bar.set_message(format!("{id:10} - {status:20}"));
|
||||
if let Some(total) = progress.total {
|
||||
bar.set_length(total as u64);
|
||||
}
|
||||
if let Some(current) = progress.current {
|
||||
bar.set_position(current as u64);
|
||||
}
|
||||
stdout.flush().into_diagnostic()?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
|||
59
src/main.rs
59
src/main.rs
|
|
@ -103,13 +103,18 @@ async fn main() -> Result<ExitCode> {
|
|||
services.push(cloud.db().name());
|
||||
let services = services.join(", ");
|
||||
let pin = if cloud.pinned { "*" } else { "" };
|
||||
let version = match cloud.options.version.as_ref() {
|
||||
Some(version) => format!(", v{version}"),
|
||||
None => String::new(),
|
||||
};
|
||||
println!(
|
||||
"Cloud {}{}, {}, {}, running on {}",
|
||||
"Cloud {}{}, {}, {}{}, running on {}",
|
||||
cloud.id,
|
||||
pin,
|
||||
cloud.php().name(),
|
||||
services,
|
||||
cloud.address
|
||||
version,
|
||||
cloud.address,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -269,26 +274,8 @@ async fn main() -> Result<ExitCode> {
|
|||
return Ok(result.into());
|
||||
}
|
||||
HazeArgs::Integration { options, mut args } => {
|
||||
let cloud = Cloud::create(&docker, options, &config).await?;
|
||||
println!("Waiting for servers to start");
|
||||
cloud.wait_for_start(&docker).await?;
|
||||
println!("Installing");
|
||||
if let Err(e) = cloud
|
||||
.exec(
|
||||
&docker,
|
||||
vec![
|
||||
"install",
|
||||
&config.auto_setup.username,
|
||||
&config.auto_setup.password,
|
||||
],
|
||||
false,
|
||||
Vec::<String>::default(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
cloud.destroy(&docker).await?;
|
||||
return Err(e);
|
||||
}
|
||||
let cloud = setup(&docker, options, &config).await?;
|
||||
|
||||
args.insert(0, "integration".to_string());
|
||||
cloud.exec(&docker, args, false, get_forward_env()).await?;
|
||||
cloud.destroy(&docker).await?;
|
||||
|
|
@ -386,15 +373,16 @@ async fn main() -> Result<ExitCode> {
|
|||
proxy(docker, config).await?;
|
||||
}
|
||||
HazeArgs::Git { operation } => match operation {
|
||||
GitOperation::Checkout { branch } => {
|
||||
GitOperation::Checkout { branch, verbose } => {
|
||||
let sources = Sources::new(&config.sources_root)?;
|
||||
checkout_all(
|
||||
&config.sources_root,
|
||||
&config,
|
||||
&branch.unwrap_or_else(|| sources.get_server_version_branch()),
|
||||
verbose,
|
||||
)?;
|
||||
}
|
||||
GitOperation::Pull => {
|
||||
pull_all(&config.sources_root)?;
|
||||
pull_all(&config)?;
|
||||
}
|
||||
},
|
||||
HazeArgs::Env {
|
||||
|
|
@ -433,16 +421,16 @@ async fn main() -> Result<ExitCode> {
|
|||
);
|
||||
|
||||
if cloud.services().contains(&Service::RedisTls(RedisTls)) {
|
||||
create_dir_all(config.work_dir.join("redis_certificates"))
|
||||
create_dir_all(config.work_dir.join("certificates/redis"))
|
||||
.into_diagnostic()
|
||||
.wrap_err("Failed to create redis certificate directory")?;
|
||||
let redis_cert_path = config.work_dir.join("redis_certificates/client.cert");
|
||||
let redis_key_path = config.work_dir.join("redis_certificates/client.key");
|
||||
let redis_ca_path = config.work_dir.join("redis_certificates/ca.cert");
|
||||
let redis_cert_path = config.work_dir.join("certificates/redis/client.cert");
|
||||
let redis_key_path = config.work_dir.join("certificates/redis/client.key");
|
||||
let redis_ca_path = config.work_dir.join("certificates/redis/ca.cert");
|
||||
if !redis_cert_path.exists() {
|
||||
write(
|
||||
&redis_cert_path,
|
||||
include_bytes!("../redis-certificates/client.crt"),
|
||||
include_bytes!("../certificates/redis/client.crt"),
|
||||
)
|
||||
.into_diagnostic()
|
||||
.wrap_err("Failed to write redis client certificate")?;
|
||||
|
|
@ -450,7 +438,7 @@ async fn main() -> Result<ExitCode> {
|
|||
if !redis_key_path.exists() {
|
||||
write(
|
||||
&redis_key_path,
|
||||
include_bytes!("../redis-certificates/client.key"),
|
||||
include_bytes!("../certificates/redis/client.key"),
|
||||
)
|
||||
.into_diagnostic()
|
||||
.wrap_err("Failed to write redis client key")?;
|
||||
|
|
@ -458,7 +446,7 @@ async fn main() -> Result<ExitCode> {
|
|||
if !redis_ca_path.exists() {
|
||||
write(
|
||||
&redis_ca_path,
|
||||
include_bytes!("../redis-certificates/ca.crt"),
|
||||
include_bytes!("../certificates/redis/ca.crt"),
|
||||
)
|
||||
.into_diagnostic()
|
||||
.wrap_err("Failed to write redis ca certificate")?;
|
||||
|
|
@ -645,12 +633,7 @@ async fn setup(docker: &Docker, options: CloudOptions, config: &HazeConfig) -> R
|
|||
for service in cloud.services() {
|
||||
for cmd in service.post_setup(docker, &cloud.id, config).await? {
|
||||
cloud
|
||||
.exec(
|
||||
docker,
|
||||
shell_words::split(&cmd).into_diagnostic()?,
|
||||
false,
|
||||
Vec::<String>::default(),
|
||||
)
|
||||
.exec(docker, cmd, false, Vec::<String>::default())
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
use crate::config::{HazeConfig, HazeVolumeConfig};
|
||||
use camino::{Utf8Path, Utf8PathBuf};
|
||||
use miette::{IntoDiagnostic, Result};
|
||||
use std::borrow::Cow;
|
||||
use tokio::fs::{create_dir_all, write};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Mapping<'a> {
|
||||
source_type: MappingSourceType,
|
||||
pub source: &'a Utf8Path,
|
||||
pub target: &'a Utf8Path,
|
||||
pub source: Cow<'a, Utf8Path>,
|
||||
pub target: Cow<'a, Utf8Path>,
|
||||
mapping_type: MappingType,
|
||||
read_only: bool,
|
||||
map: bool,
|
||||
|
|
@ -23,6 +24,26 @@ impl<'a> Mapping<'a> {
|
|||
where
|
||||
Target: Into<&'a Utf8Path>,
|
||||
Source: Into<&'a Utf8Path>,
|
||||
{
|
||||
Mapping {
|
||||
source_type,
|
||||
source: Cow::Borrowed(source.into()),
|
||||
target: Cow::Borrowed(target.into()),
|
||||
mapping_type: MappingType::Folder,
|
||||
read_only: false,
|
||||
map: true,
|
||||
create: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn owned<Source, Target>(
|
||||
source_type: MappingSourceType,
|
||||
source: Source,
|
||||
target: Target,
|
||||
) -> Self
|
||||
where
|
||||
Target: Into<Cow<'a, Utf8Path>>,
|
||||
Source: Into<Cow<'a, Utf8Path>>,
|
||||
{
|
||||
Mapping {
|
||||
source_type,
|
||||
|
|
@ -65,10 +86,10 @@ impl<'a> Mapping<'a> {
|
|||
return Ok(());
|
||||
}
|
||||
let source = match self.source_type {
|
||||
MappingSourceType::WorkDir => config.work_dir.join(id).join(self.source),
|
||||
MappingSourceType::GlobalWorkDir => config.work_dir.join(self.source),
|
||||
MappingSourceType::WorkDir => config.work_dir.join(id).join(self.source.as_ref()),
|
||||
MappingSourceType::GlobalWorkDir => config.work_dir.join(self.source.as_ref()),
|
||||
MappingSourceType::Sources => return Ok(()),
|
||||
MappingSourceType::Absolute => self.source.into(),
|
||||
MappingSourceType::Absolute => self.source.as_ref().into(),
|
||||
};
|
||||
match self.mapping_type {
|
||||
MappingType::Folder => create_dir_all(source).await.into_diagnostic()?,
|
||||
|
|
@ -78,20 +99,25 @@ impl<'a> Mapping<'a> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn source(&self, id: &str, config: &HazeConfig) -> Utf8PathBuf {
|
||||
pub fn source(&self, id: &str, config: &HazeConfig, source_root: &Utf8Path) -> Utf8PathBuf {
|
||||
match self.source_type {
|
||||
MappingSourceType::WorkDir => config.work_dir.join(id).join(self.source),
|
||||
MappingSourceType::GlobalWorkDir => config.work_dir.join(self.source),
|
||||
MappingSourceType::Sources => config.sources_root.join(self.source),
|
||||
MappingSourceType::Absolute => self.source.into(),
|
||||
MappingSourceType::WorkDir => config.work_dir.join(id).join(self.source.as_ref()),
|
||||
MappingSourceType::GlobalWorkDir => config.work_dir.join(self.source.as_ref()),
|
||||
MappingSourceType::Sources => source_root.join(self.source.as_ref()),
|
||||
MappingSourceType::Absolute => self.source.as_ref().into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_volume_arg(&self, id: &str, config: &HazeConfig) -> Option<String> {
|
||||
pub fn get_volume_arg(
|
||||
&self,
|
||||
id: &str,
|
||||
config: &HazeConfig,
|
||||
source_root: &Utf8Path,
|
||||
) -> Option<String> {
|
||||
if !self.map {
|
||||
return None;
|
||||
}
|
||||
let source = self.source(id, config);
|
||||
let source = self.source(id, config, source_root);
|
||||
Some(if self.read_only {
|
||||
format!("{}:{}:ro", source, self.target)
|
||||
} else {
|
||||
|
|
@ -107,6 +133,7 @@ pub fn default_mappings<'a>() -> impl IntoIterator<Item = Mapping<'a>> {
|
|||
Mapping::new(Sources, "", "/var/www/html"),
|
||||
Mapping::new(WorkDir, "data", "/var/www/html/data"),
|
||||
Mapping::new(WorkDir, "config", "/var/www/html/config"),
|
||||
Mapping::new(WorkDir, "store_apps", "/var/www/store_apps"),
|
||||
Mapping::new(WorkDir, "data-autotest", "/var/www/html/data-autotest"),
|
||||
Mapping::new(WorkDir, "skeleton", "/var/www/html/core/skeleton"),
|
||||
Mapping::new(
|
||||
|
|
@ -161,11 +188,32 @@ pub fn default_mappings<'a>() -> impl IntoIterator<Item = Mapping<'a>> {
|
|||
.dont_create(),
|
||||
Mapping::new(WorkDir, "xdebug", "/tmp/xdebug"),
|
||||
Mapping::new(WorkDir, "profiling", "/tmp/profiling"),
|
||||
Mapping::new(WorkDir, "php.ini", "/php.ini").file(),
|
||||
Mapping::new(WorkDir, "php-config", "/config"),
|
||||
];
|
||||
|
||||
IntoIterator::into_iter(mappings)
|
||||
}
|
||||
|
||||
pub fn for_config<'a>(config: &'a HazeConfig) -> impl Iterator<Item = Mapping<'a>> {
|
||||
let app_dir_mappings = config.app_directories.iter().map(|dir| {
|
||||
Mapping::owned(
|
||||
MappingSourceType::Absolute,
|
||||
dir.as_path(),
|
||||
Cow::Owned(Utf8PathBuf::from(format!(
|
||||
"/var/www/html/{}",
|
||||
dir.file_name().unwrap()
|
||||
))),
|
||||
)
|
||||
});
|
||||
|
||||
config
|
||||
.volume
|
||||
.iter()
|
||||
.map(Mapping::from)
|
||||
.chain(app_dir_mappings)
|
||||
.chain(default_mappings())
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum MappingSourceType {
|
||||
Sources,
|
||||
|
|
@ -189,8 +237,8 @@ impl<'a> From<&'a HazeVolumeConfig> for Mapping<'a> {
|
|||
};
|
||||
Mapping {
|
||||
source_type: MappingSourceType::Absolute,
|
||||
source: config.source.as_path(),
|
||||
target: config.target.as_path(),
|
||||
source: Cow::Borrowed(config.source.as_path()),
|
||||
target: Cow::Borrowed(config.target.as_path()),
|
||||
mapping_type: ty,
|
||||
read_only: config.read_only,
|
||||
map: true,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
use crate::cloud::Cloud;
|
||||
use bollard::network::CreateNetworkOptions;
|
||||
use bollard::config::NetworkCreateRequest;
|
||||
use bollard::Docker;
|
||||
use miette::{IntoDiagnostic, Result, WrapErr};
|
||||
|
||||
pub async fn clear_networks(docker: &Docker, instances: &[Cloud]) -> Result<()> {
|
||||
let networks = docker
|
||||
.list_networks::<&str>(None)
|
||||
.list_networks(None)
|
||||
.await
|
||||
.into_diagnostic()
|
||||
.wrap_err("Failed to list docker networks")?;
|
||||
|
|
@ -23,7 +23,7 @@ pub async fn clear_networks(docker: &Docker, instances: &[Cloud]) -> Result<()>
|
|||
|
||||
async fn get_network_id(docker: &Docker, name: &str) -> Result<Option<String>> {
|
||||
let networks = docker
|
||||
.list_networks::<&str>(None)
|
||||
.list_networks(None)
|
||||
.await
|
||||
.into_diagnostic()
|
||||
.wrap_err("Failed to list docker networks")?;
|
||||
|
|
@ -41,9 +41,8 @@ pub async fn ensure_network_exists(docker: &Docker, name: &str) -> Result<String
|
|||
Ok(id)
|
||||
} else {
|
||||
Ok(docker
|
||||
.create_network(CreateNetworkOptions {
|
||||
name,
|
||||
check_duplicate: true,
|
||||
.create_network(NetworkCreateRequest {
|
||||
name: name.into(),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
|
|
|
|||
126
src/php.rs
126
src/php.rs
|
|
@ -1,12 +1,13 @@
|
|||
use owo_colors::OwoColorize;
|
||||
use crate::config::ProxyConfig;
|
||||
use crate::database::Database;
|
||||
use crate::image::pull_image;
|
||||
use crate::image::{image_version, pull_image, ImageVersion};
|
||||
use crate::network::ensure_network_exists;
|
||||
use crate::service::Service;
|
||||
use crate::service::ServiceTrait;
|
||||
use bollard::container::{Config, CreateContainerOptions, NetworkingConfig};
|
||||
use bollard::config::{ContainerCreateBody, NetworkConnectRequest, NetworkingConfig};
|
||||
use bollard::models::{EndpointSettings, HostConfig};
|
||||
use bollard::network::ConnectNetworkOptions;
|
||||
use bollard::query_parameters::CreateContainerOptions;
|
||||
use bollard::Docker;
|
||||
use itertools::Itertools;
|
||||
use maplit::hashmap;
|
||||
|
|
@ -21,20 +22,12 @@ use tokio::time::{sleep, timeout};
|
|||
#[allow(dead_code)]
|
||||
pub enum PhpVersion {
|
||||
#[default]
|
||||
Php85,
|
||||
Php84,
|
||||
Php83,
|
||||
Php82,
|
||||
Php81,
|
||||
Php80,
|
||||
Php74,
|
||||
Php73,
|
||||
Php84Dbg,
|
||||
Php83Dbg,
|
||||
Php82Dbg,
|
||||
Php81Dbg,
|
||||
Php80Dbg,
|
||||
Php74Dbg,
|
||||
Php73Dbg,
|
||||
}
|
||||
|
||||
pub const PHP_MEMORY_LIMIT: i64 = 2 * 1024 * 1024 * 1024;
|
||||
|
|
@ -44,24 +37,13 @@ impl FromStr for PhpVersion {
|
|||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"7" => Ok(PhpVersion::Php74),
|
||||
"7.3" => Ok(PhpVersion::Php73),
|
||||
"7.4" => Ok(PhpVersion::Php74),
|
||||
"8" => Ok(PhpVersion::Php81),
|
||||
"8" => Ok(PhpVersion::Php80),
|
||||
"8.0" => Ok(PhpVersion::Php80),
|
||||
"8.1" => Ok(PhpVersion::Php81),
|
||||
"8.2" => Ok(PhpVersion::Php82),
|
||||
"8.3" => Ok(PhpVersion::Php83),
|
||||
"8.4" => Ok(PhpVersion::Php84),
|
||||
"7-dbg" => Ok(PhpVersion::Php74Dbg),
|
||||
"7.3-dbg" => Ok(PhpVersion::Php73Dbg),
|
||||
"7.4-dbg" => Ok(PhpVersion::Php74Dbg),
|
||||
"8-dbg" => Ok(PhpVersion::Php80Dbg),
|
||||
"8.0-dbg" => Ok(PhpVersion::Php80Dbg),
|
||||
"8.1-dbg" => Ok(PhpVersion::Php81Dbg),
|
||||
"8.2-dbg" => Ok(PhpVersion::Php82Dbg),
|
||||
"8.3-dbg" => Ok(PhpVersion::Php83Dbg),
|
||||
"8.4-dbg" => Ok(PhpVersion::Php84Dbg),
|
||||
"8.5" => Ok(PhpVersion::Php85),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
|
@ -73,53 +55,35 @@ impl PhpVersion {
|
|||
}
|
||||
|
||||
pub fn image(&self) -> &'static str {
|
||||
// for now only 7.4
|
||||
match self {
|
||||
PhpVersion::Php73 => "icewind1991/haze:7.3",
|
||||
PhpVersion::Php74 => "icewind1991/haze:7.4",
|
||||
PhpVersion::Php80 => "icewind1991/haze:8.0",
|
||||
PhpVersion::Php81 => "icewind1991/haze:8.1",
|
||||
PhpVersion::Php82 => "icewind1991/haze:8.2",
|
||||
PhpVersion::Php83 => "icewind1991/haze:8.3",
|
||||
PhpVersion::Php84 => "icewind1991/haze:8.4",
|
||||
PhpVersion::Php73Dbg => "icewind1991/haze:7.3-dbg",
|
||||
PhpVersion::Php74Dbg => "icewind1991/haze:7.4-dbg",
|
||||
PhpVersion::Php80Dbg => "icewind1991/haze:8.0-dbg",
|
||||
PhpVersion::Php81Dbg => "icewind1991/haze:8.1-dbg",
|
||||
PhpVersion::Php82Dbg => "icewind1991/haze:8.2-dbg",
|
||||
PhpVersion::Php83Dbg => "icewind1991/haze:8.3-dbg",
|
||||
PhpVersion::Php84Dbg => "icewind1991/haze:8.4-dbg",
|
||||
PhpVersion::Php85 => "icewind1991/haze:8.5",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &'static str {
|
||||
match self {
|
||||
PhpVersion::Php73 => "7.3",
|
||||
PhpVersion::Php74 => "7.4",
|
||||
PhpVersion::Php80 => "8.0",
|
||||
PhpVersion::Php81 => "8.1",
|
||||
PhpVersion::Php82 => "8.2",
|
||||
PhpVersion::Php83 => "8.3",
|
||||
PhpVersion::Php84 => "8.4",
|
||||
PhpVersion::Php73Dbg => "7.3-dbg",
|
||||
PhpVersion::Php74Dbg => "7.4-dbg",
|
||||
PhpVersion::Php80Dbg => "8.0-dbg",
|
||||
PhpVersion::Php81Dbg => "8.1-dbg",
|
||||
PhpVersion::Php82Dbg => "8.2-dbg",
|
||||
PhpVersion::Php83Dbg => "8.3-dbg",
|
||||
PhpVersion::Php84Dbg => "8.4-dbg",
|
||||
PhpVersion::Php85 => "8.4",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_number(major: u8, minor: u8) -> Option<Self> {
|
||||
match (major, minor) {
|
||||
(7, 3) => Some(PhpVersion::Php73),
|
||||
(7, 4) => Some(PhpVersion::Php74),
|
||||
(8, 0) => Some(PhpVersion::Php80),
|
||||
(8, 1) => Some(PhpVersion::Php81),
|
||||
(8, 2) => Some(PhpVersion::Php82),
|
||||
(8, 3) => Some(PhpVersion::Php83),
|
||||
(8, 4) => Some(PhpVersion::Php84),
|
||||
(8, 5) => Some(PhpVersion::Php85),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -127,27 +91,19 @@ impl PhpVersion {
|
|||
pub fn max_minor(major: u8) -> u8 {
|
||||
match major {
|
||||
7 => 4,
|
||||
8 => 4,
|
||||
8 => 5,
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn all() -> impl Iterator<Item = Self> {
|
||||
[
|
||||
PhpVersion::Php73,
|
||||
PhpVersion::Php74,
|
||||
PhpVersion::Php80,
|
||||
PhpVersion::Php81,
|
||||
PhpVersion::Php82,
|
||||
PhpVersion::Php83,
|
||||
PhpVersion::Php84,
|
||||
PhpVersion::Php73Dbg,
|
||||
PhpVersion::Php74Dbg,
|
||||
PhpVersion::Php80Dbg,
|
||||
PhpVersion::Php81Dbg,
|
||||
PhpVersion::Php82Dbg,
|
||||
PhpVersion::Php83Dbg,
|
||||
PhpVersion::Php84Dbg,
|
||||
PhpVersion::Php85,
|
||||
]
|
||||
.into_iter()
|
||||
}
|
||||
|
|
@ -164,11 +120,23 @@ impl PhpVersion {
|
|||
host: &str,
|
||||
services: &[Service],
|
||||
proxy_config: &ProxyConfig,
|
||||
version: Option<&str>,
|
||||
) -> Result<String> {
|
||||
ensure_network_exists(docker, "haze").await?;
|
||||
pull_image(docker, self.image()).await?;
|
||||
|
||||
let image_version = image_version(docker, self.image()).await;
|
||||
let haze_version = ImageVersion::from_str(env!("CARGO_PKG_VERSION"));
|
||||
if let (Some(image_version), Ok(haze_version)) = (image_version, haze_version) {
|
||||
if image_version < haze_version {
|
||||
eprintln!("{}: image version is out of date, run {} to update.", "Warning".red(), "haze update".blue());
|
||||
eprintln!(" Haze version: {}", haze_version.bright_yellow());
|
||||
eprintln!(" Image version: {}", image_version.bright_yellow());
|
||||
}
|
||||
}
|
||||
|
||||
let options = Some(CreateContainerOptions {
|
||||
name: id.to_string(),
|
||||
name: Some(id.to_string()),
|
||||
..CreateContainerOptions::default()
|
||||
});
|
||||
let clean_id = id.strip_prefix("haze-").unwrap_or(id);
|
||||
|
|
@ -192,7 +160,23 @@ impl PhpVersion {
|
|||
proxy_config.addr(id, IpAddr::V4(Ipv4Addr::LOCALHOST))
|
||||
));
|
||||
|
||||
let config = Config {
|
||||
env.push(format!("HOST_IP={host}"));
|
||||
if !proxy_config.address.is_empty() {
|
||||
env.push(format!("PROXY_BASE={}", proxy_config.address));
|
||||
}
|
||||
|
||||
let mut labels = hashmap! {
|
||||
"haze-type".to_string() => "cloud".to_string(),
|
||||
"haze-db".to_string() => db.name().to_string(),
|
||||
"haze-php".to_string() => self.name().to_string(),
|
||||
"haze-cloud-id".to_string() => id.to_string(),
|
||||
"haze-services".to_string() => services.iter().map(|s| s.name()).join(","),
|
||||
};
|
||||
if let Some(version) = version {
|
||||
labels.insert("haze-version".to_string(), version.to_string());
|
||||
}
|
||||
|
||||
let config = ContainerCreateBody {
|
||||
image: Some(self.image().to_string()),
|
||||
env: Some(env),
|
||||
host_config: Some(HostConfig {
|
||||
|
|
@ -204,20 +188,14 @@ impl PhpVersion {
|
|||
..Default::default()
|
||||
}),
|
||||
networking_config: Some(NetworkingConfig {
|
||||
endpoints_config: hashmap! {
|
||||
endpoints_config: Some(hashmap! {
|
||||
network.to_string() => EndpointSettings {
|
||||
aliases: Some(vec!["cloud".to_string()]),
|
||||
..Default::default()
|
||||
}
|
||||
},
|
||||
}),
|
||||
labels: Some(hashmap! {
|
||||
"haze-type".to_string() => "cloud".to_string(),
|
||||
"haze-db".to_string() => db.name().to_string(),
|
||||
"haze-php".to_string() => self.name().to_string(),
|
||||
"haze-cloud-id".to_string() => id.to_string(),
|
||||
"haze-services".to_string() => services.iter().map(|s| s.name()).join(","),
|
||||
}),
|
||||
}),
|
||||
labels: Some(labels),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
|
|
@ -227,11 +205,7 @@ impl PhpVersion {
|
|||
.into_diagnostic()?
|
||||
.id;
|
||||
|
||||
if let Err(e) = docker
|
||||
.start_container::<String>(&id, None)
|
||||
.await
|
||||
.into_diagnostic()
|
||||
{
|
||||
if let Err(e) = docker.start_container(&id, None).await.into_diagnostic() {
|
||||
docker.remove_container(&id, None).await.ok();
|
||||
return Err(e);
|
||||
}
|
||||
|
|
@ -239,12 +213,12 @@ impl PhpVersion {
|
|||
if let Err(e) = docker
|
||||
.connect_network(
|
||||
"haze",
|
||||
ConnectNetworkOptions {
|
||||
container: id.as_str(),
|
||||
endpoint_config: EndpointSettings {
|
||||
NetworkConnectRequest {
|
||||
container: id.to_string(),
|
||||
endpoint_config: Some(EndpointSettings {
|
||||
aliases: Some(vec![id.to_string()]),
|
||||
..Default::default()
|
||||
},
|
||||
}),
|
||||
},
|
||||
)
|
||||
.await
|
||||
|
|
|
|||
147
src/proxy.rs
147
src/proxy.rs
|
|
@ -1,34 +1,43 @@
|
|||
use crate::service::ServiceTrait;
|
||||
use crate::service::{ServiceTrait, ServiceType};
|
||||
use crate::Result;
|
||||
use crate::{Cloud, HazeConfig};
|
||||
use axum::http::header::HOST;
|
||||
use axum::http::HeaderValue;
|
||||
use axum::{
|
||||
body::Body,
|
||||
extract::{Request, State},
|
||||
extract::Request,
|
||||
response::{IntoResponse, Response},
|
||||
Router,
|
||||
};
|
||||
use bollard::Docker;
|
||||
use futures_util::StreamExt;
|
||||
use hyper::body::Incoming;
|
||||
use hyper::server::conn::http1;
|
||||
use hyper::service::service_fn;
|
||||
use hyper::StatusCode;
|
||||
use hyper_util::rt::TokioIo;
|
||||
use hyper_util::{client::legacy::connect::HttpConnector, rt::TokioExecutor};
|
||||
use miette::{miette, IntoDiagnostic};
|
||||
use std::collections::HashMap;
|
||||
use std::convert::Infallible;
|
||||
use std::fs::{create_dir_all, set_permissions};
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::path::PathBuf;
|
||||
use std::pin::pin;
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
use tokio::net::UnixListener;
|
||||
use tokio::signal::ctrl_c;
|
||||
use tokio::spawn;
|
||||
use tokio::time::sleep;
|
||||
use tokio_stream::wrappers::{TcpListenerStream, UnixListenerStream};
|
||||
use tracing::{debug, error, info};
|
||||
|
||||
struct ActiveInstances {
|
||||
known: Mutex<HashMap<String, SocketAddr>>,
|
||||
last: Mutex<Option<SocketAddr>>,
|
||||
last: Mutex<Option<Cloud>>,
|
||||
docker: Docker,
|
||||
config: HazeConfig,
|
||||
}
|
||||
|
|
@ -48,15 +57,9 @@ impl ActiveInstances {
|
|||
return Some(ip);
|
||||
}
|
||||
|
||||
// service proxy
|
||||
let addr = if name.matches('-').count() == 2 {
|
||||
let (name, service_name) = name.rsplit_once('-').unwrap();
|
||||
let cloud = Cloud::get_by_filter(&self.docker, Some(name.into()), &self.config)
|
||||
.await
|
||||
.ok()?;
|
||||
let service = cloud
|
||||
.services()
|
||||
.find(|service| service.name() == service_name)?;
|
||||
let addr = if ServiceType::from_str(name).is_ok() {
|
||||
let cloud = self.last()?;
|
||||
let service = cloud.services().find(|service| service.name() == name)?;
|
||||
let ip = service
|
||||
.get_ips(&self.docker, &cloud.id)
|
||||
.await
|
||||
|
|
@ -64,13 +67,34 @@ impl ActiveInstances {
|
|||
.next()?;
|
||||
SocketAddr::new(ip, service.proxy_port())
|
||||
} else {
|
||||
SocketAddr::new(
|
||||
Cloud::get_by_filter(&self.docker, Some(name.into()), &self.config)
|
||||
.await
|
||||
.ok()?
|
||||
.ip?,
|
||||
80,
|
||||
)
|
||||
match name.matches('-').count() {
|
||||
// instance
|
||||
1 => SocketAddr::new(
|
||||
Cloud::get_by_filter(&self.docker, Some(name.into()), &self.config)
|
||||
.await
|
||||
.ok()?
|
||||
.ip?,
|
||||
80,
|
||||
),
|
||||
// service with instance
|
||||
2.. => {
|
||||
let service_name = name.splitn(3, '-').last()?;
|
||||
let name = &name[0..(name.len() - service_name.len() - 1)];
|
||||
let cloud = Cloud::get_by_filter(&self.docker, Some(name.into()), &self.config)
|
||||
.await
|
||||
.ok()?;
|
||||
let service = cloud
|
||||
.services()
|
||||
.find(|service| service.name() == service_name)?;
|
||||
let ip = service
|
||||
.get_ips(&self.docker, &cloud.id)
|
||||
.await
|
||||
.ok()?
|
||||
.next()?;
|
||||
SocketAddr::new(ip, service.proxy_port())
|
||||
}
|
||||
_ => return None,
|
||||
}
|
||||
};
|
||||
|
||||
println!("{name} => {addr}");
|
||||
|
|
@ -79,18 +103,31 @@ impl ActiveInstances {
|
|||
Some(addr)
|
||||
}
|
||||
|
||||
pub fn last(&self) -> Option<SocketAddr> {
|
||||
*self.last.lock().unwrap()
|
||||
pub fn last_addr(&self) -> Option<SocketAddr> {
|
||||
self.last
|
||||
.lock()
|
||||
.unwrap()
|
||||
.as_ref()
|
||||
.and_then(|cloud| Some(SocketAddr::new(cloud.ip?, 80)))
|
||||
}
|
||||
|
||||
pub fn last(&self) -> Option<Cloud> {
|
||||
self.last.lock().unwrap().clone()
|
||||
}
|
||||
|
||||
async fn update_last(&self) {
|
||||
let last = Cloud::get_by_filter(&self.docker, None, &self.config)
|
||||
.await
|
||||
.ok()
|
||||
.and_then(|cloud| Some(SocketAddr::new(cloud.ip?, 80)));
|
||||
.ok();
|
||||
let mut old = self.last.lock().unwrap();
|
||||
if old.as_ref() != last.as_ref() {
|
||||
info!(instance = ?last, "Found new instance");
|
||||
|
||||
// remove cached base-service mappings
|
||||
self.known
|
||||
.lock()
|
||||
.unwrap()
|
||||
.retain(|key, _| ServiceType::from_str(key).is_err());
|
||||
*old = last;
|
||||
}
|
||||
}
|
||||
|
|
@ -134,20 +171,26 @@ async fn serve(instances: ActiveInstances, listen: String, base_address: String)
|
|||
ctrl_c().await.ok();
|
||||
};
|
||||
|
||||
let app = Router::new().fallback(handler).with_state(AppState {
|
||||
let state = AppState {
|
||||
instances: instances.clone(),
|
||||
base_address: base_address.clone(),
|
||||
proxy_client: Arc::new(proxy_client),
|
||||
});
|
||||
};
|
||||
|
||||
if !listen.starts_with('/') {
|
||||
let addr: SocketAddr = listen.parse().into_diagnostic()?;
|
||||
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
|
||||
println!("listening on {}", listener.local_addr().unwrap());
|
||||
axum::serve(listener, app)
|
||||
.with_graceful_shutdown(cancel)
|
||||
.await
|
||||
.unwrap();
|
||||
let mut connections = pin!(TcpListenerStream::new(listener).take_until(cancel));
|
||||
|
||||
while let Some(stream) = connections.next().await {
|
||||
match stream {
|
||||
Ok(stream) => handle_connection(state.clone(), stream),
|
||||
Err(error) => {
|
||||
error!(%error, "connection failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let listen: PathBuf = listen.into();
|
||||
if let Some(parent) = listen.parent() {
|
||||
|
|
@ -158,18 +201,42 @@ async fn serve(instances: ActiveInstances, listen: String, base_address: String)
|
|||
}
|
||||
let _ = tokio::fs::remove_file(&listen).await;
|
||||
|
||||
let uds = UnixListener::bind(&listen).unwrap();
|
||||
let listener = UnixListener::bind(&listen).unwrap();
|
||||
println!("listening on {}", listen.display());
|
||||
set_permissions(&listen, PermissionsExt::from_mode(0o666)).into_diagnostic()?;
|
||||
|
||||
axum::serve(uds, app)
|
||||
.with_graceful_shutdown(cancel)
|
||||
.await
|
||||
.unwrap();
|
||||
let mut connections = pin!(UnixListenerStream::new(listener).take_until(cancel));
|
||||
|
||||
while let Some(stream) = connections.next().await {
|
||||
match stream {
|
||||
Ok(stream) => handle_connection(state.clone(), stream),
|
||||
Err(error) => {
|
||||
error!(%error, "connection failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_connection<I: AsyncRead + AsyncWrite + Unpin + Send + 'static>(
|
||||
state: AppState,
|
||||
stream: I,
|
||||
) {
|
||||
let io = TokioIo::new(stream);
|
||||
// Spawn a tokio task to serve multiple connections concurrently
|
||||
tokio::task::spawn(async move {
|
||||
if let Err(err) = http1::Builder::new()
|
||||
.serve_connection(io, service_fn(move |req| handler(state.clone(), req)))
|
||||
.with_upgrades()
|
||||
.await
|
||||
{
|
||||
eprintln!("Error serving connection: {:?}", err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async fn get_remote(
|
||||
host: Option<&HeaderValue>,
|
||||
instances: &ActiveInstances,
|
||||
|
|
@ -181,7 +248,7 @@ async fn get_remote(
|
|||
};
|
||||
let ip = if host == base_address {
|
||||
instances
|
||||
.last()
|
||||
.last_addr()
|
||||
.ok_or_else(|| String::from("No running instance known"))
|
||||
} else {
|
||||
let requested_instance = host.split('.').next().unwrap();
|
||||
|
|
@ -203,9 +270,9 @@ async fn get_remote(
|
|||
}
|
||||
}
|
||||
|
||||
type Client = hyper_util::client::legacy::Client<HttpConnector, Body>;
|
||||
type Client = hyper_util::client::legacy::Client<HttpConnector, Incoming>;
|
||||
|
||||
async fn handler(State(state): State<AppState>, mut req: Request) -> Result<Response, StatusCode> {
|
||||
async fn handler(state: AppState, mut req: Request<Incoming>) -> Result<Response, Infallible> {
|
||||
let host = req.headers().get(HOST).cloned();
|
||||
let remote = match get_remote(host.as_ref(), &state.instances, &state.base_address).await {
|
||||
Ok(remote) => remote,
|
||||
|
|
@ -230,13 +297,13 @@ async fn handler(State(state): State<AppState>, mut req: Request) -> Result<Resp
|
|||
IpAddr::V4(Ipv4Addr::UNSPECIFIED),
|
||||
&uri,
|
||||
req,
|
||||
&state.proxy_client,
|
||||
state.proxy_client.as_ref(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(response) => Ok(response.map(Body::new)),
|
||||
Err(error) => {
|
||||
error!(%error, "error while proxying request");
|
||||
error!(?error, "error while proxying request");
|
||||
Ok(StatusCode::BAD_REQUEST.into_response())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,9 +14,10 @@ mod sftp;
|
|||
mod redis;
|
||||
mod sharded;
|
||||
mod smb;
|
||||
mod webhook;
|
||||
|
||||
use crate::cloud::CloudOptions;
|
||||
use crate::config::{HazeConfig, Preset};
|
||||
use crate::config::{HazeConfig, Preset, ProxyConfig};
|
||||
pub use crate::service::clam::{Clam, ClamIcap, ClamIcapTls, ClamSocket};
|
||||
use crate::service::dav::Dav;
|
||||
use crate::service::imaginary::Imaginary;
|
||||
|
|
@ -29,9 +30,10 @@ pub use crate::service::office::Office;
|
|||
pub use crate::service::onlyoffice::OnlyOffice;
|
||||
pub use crate::service::push::NotifyPush;
|
||||
use crate::service::redis::Redis;
|
||||
use crate::service::sftp::Sftp;
|
||||
use crate::service::sftp::{Sftp, SftpKey};
|
||||
use crate::service::sharded::{Sharding, ShardingMigrate, ShardingMigrateUnset, SingleShard};
|
||||
use crate::service::smb::Smb;
|
||||
use crate::service::webhook::Webhook;
|
||||
use bollard::models::ContainerState;
|
||||
use bollard::Docker;
|
||||
use enum_dispatch::enum_dispatch;
|
||||
|
|
@ -78,7 +80,12 @@ pub trait ServiceTrait {
|
|||
None
|
||||
}
|
||||
|
||||
async fn start_message(&self, _docker: &Docker, _cloud_id: &str) -> Result<Option<String>> {
|
||||
async fn start_message(
|
||||
&self,
|
||||
_docker: &Docker,
|
||||
_cloud_id: &str,
|
||||
_proxy: &ProxyConfig,
|
||||
) -> Result<Option<String>> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
|
|
@ -95,12 +102,21 @@ pub trait ServiceTrait {
|
|||
Ok(HashMap::default())
|
||||
}
|
||||
|
||||
fn pre_setup(
|
||||
&self,
|
||||
_docker: &Docker,
|
||||
_cloud_id: &str,
|
||||
_config: &HazeConfig,
|
||||
) -> Result<Vec<Vec<String>>> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
async fn post_setup(
|
||||
&self,
|
||||
_docker: &Docker,
|
||||
_cloud_id: &str,
|
||||
_config: &HazeConfig,
|
||||
) -> Result<Vec<String>> {
|
||||
) -> Result<Vec<Vec<String>>> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
|
|
@ -142,7 +158,7 @@ pub trait ServiceTrait {
|
|||
return Ok(Box::new(empty()));
|
||||
};
|
||||
docker
|
||||
.start_container::<String>(&container, None)
|
||||
.start_container(&container, None)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
self.wait_for_running(docker, cloud_id).await?;
|
||||
|
|
@ -193,6 +209,19 @@ impl ServiceTrait for RedisTls {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||
pub struct FrankenPhp;
|
||||
|
||||
impl ServiceTrait for FrankenPhp {
|
||||
fn name(&self) -> &str {
|
||||
"franken-php"
|
||||
}
|
||||
|
||||
fn env(&self) -> &[&str] {
|
||||
&["FRANKENPHP=1"]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Copy, Clone, Debug, PartialEq, EnumString, EnumMessage, EnumIter, IntoStaticStr, Display,
|
||||
)]
|
||||
|
|
@ -200,6 +229,8 @@ impl ServiceTrait for RedisTls {
|
|||
pub enum ServiceType {
|
||||
/// S3 Primary storage and external storage
|
||||
S3,
|
||||
/// S3 Primary storage with TLS
|
||||
S3s,
|
||||
/// S3 multi-object store Primary storage and external storage
|
||||
S3m,
|
||||
/// S3 multi-bucket Primary storage and external storage
|
||||
|
|
@ -208,6 +239,8 @@ pub enum ServiceType {
|
|||
Azure,
|
||||
/// Ldap user backend
|
||||
Ldap,
|
||||
/// Ldap admin interface
|
||||
LdapAdmin,
|
||||
/// OnlyOffice
|
||||
OnlyOffice,
|
||||
/// Libre office online
|
||||
|
|
@ -234,6 +267,8 @@ pub enum ServiceType {
|
|||
Dav,
|
||||
/// Sftp external storage
|
||||
Sftp,
|
||||
/// Sftp external storage with public key authentication
|
||||
SftpKey,
|
||||
/// ownCloud instance for migration
|
||||
Oc,
|
||||
/// Imaginary for preview generation
|
||||
|
|
@ -263,6 +298,10 @@ pub enum ServiceType {
|
|||
Redis,
|
||||
/// External redis instance with TLS
|
||||
RedisTls,
|
||||
/// Use FrankenPHP instead of PHP-FPM
|
||||
FrankenPhp,
|
||||
/// Webhook test listener
|
||||
Webhook,
|
||||
}
|
||||
|
||||
#[enum_dispatch]
|
||||
|
|
@ -281,6 +320,7 @@ pub enum Service {
|
|||
ShardingMigrate(ShardingMigrate),
|
||||
ShardingMigrateUnset(ShardingMigrateUnset),
|
||||
Sftp(Sftp),
|
||||
SftpKey(SftpKey),
|
||||
Kaspersky(Kaspersky),
|
||||
KasperskyIcap(KasperskyIcap),
|
||||
Clam(Clam),
|
||||
|
|
@ -292,6 +332,8 @@ pub enum Service {
|
|||
Mail(Mail),
|
||||
Redis(Redis),
|
||||
RedisTls(RedisTls),
|
||||
FrankenPhp(FrankenPhp),
|
||||
Webhook(Webhook),
|
||||
Preset(PresetService),
|
||||
}
|
||||
|
||||
|
|
@ -300,10 +342,14 @@ impl Service {
|
|||
if let Ok(ty) = ServiceType::from_str(ty) {
|
||||
match ty {
|
||||
ServiceType::S3 => Some(vec![Service::ObjectStore(ObjectStore::S3)]),
|
||||
ServiceType::S3s => Some(vec![Service::ObjectStore(ObjectStore::S3s)]),
|
||||
ServiceType::S3m => Some(vec![Service::ObjectStore(ObjectStore::S3m)]),
|
||||
ServiceType::S3mb => Some(vec![Service::ObjectStore(ObjectStore::S3mb)]),
|
||||
ServiceType::Azure => Some(vec![Service::ObjectStore(ObjectStore::Azure)]),
|
||||
ServiceType::Ldap => Some(vec![Service::Ldap(Ldap), Service::LdapAdmin(LdapAdmin)]),
|
||||
ServiceType::LdapAdmin => {
|
||||
Some(vec![Service::Ldap(Ldap), Service::LdapAdmin(LdapAdmin)])
|
||||
}
|
||||
ServiceType::OnlyOffice => Some(vec![Service::OnlyOffice(OnlyOffice)]),
|
||||
ServiceType::Office => Some(vec![Service::Office(Office)]),
|
||||
ServiceType::Push => Some(vec![Service::Push(NotifyPush)]),
|
||||
|
|
@ -318,6 +364,7 @@ impl Service {
|
|||
}
|
||||
ServiceType::Dav => Some(vec![Service::Dav(Dav)]),
|
||||
ServiceType::Sftp => Some(vec![Service::Sftp(Sftp)]),
|
||||
ServiceType::SftpKey => Some(vec![Service::SftpKey(SftpKey)]),
|
||||
ServiceType::Oc => Some(vec![Service::Oc(Oc)]),
|
||||
ServiceType::Imaginary => Some(vec![Service::Imaginary(Imaginary)]),
|
||||
ServiceType::Kaspersky => Some(vec![Service::Kaspersky(Kaspersky)]),
|
||||
|
|
@ -330,6 +377,8 @@ impl Service {
|
|||
ServiceType::Mail => Some(vec![Service::Mail(Mail)]),
|
||||
ServiceType::Redis => Some(vec![Service::Redis(Redis)]),
|
||||
ServiceType::RedisTls => Some(vec![Service::RedisTls(RedisTls)]),
|
||||
ServiceType::FrankenPhp => Some(vec![Service::FrankenPhp(FrankenPhp)]),
|
||||
ServiceType::Webhook => Some(vec![Service::Webhook(Webhook)]),
|
||||
}
|
||||
} else {
|
||||
presets
|
||||
|
|
@ -392,15 +441,29 @@ impl ServiceTrait for PresetService {
|
|||
_docker: &Docker,
|
||||
_cloud_id: &str,
|
||||
config: &HazeConfig,
|
||||
) -> Result<Vec<String>> {
|
||||
) -> Result<Vec<Vec<String>>> {
|
||||
let preset =
|
||||
get_preset(&config.preset, &self.0).ok_or_else(|| Report::msg("invalid preset"))?;
|
||||
let mut commands: Vec<_> = preset
|
||||
.apps
|
||||
.iter()
|
||||
.map(|app| format!("occ app:enable {app} --force"))
|
||||
.map(|app| {
|
||||
vec![
|
||||
"occ".into(),
|
||||
"app:enable".into(),
|
||||
app.clone(),
|
||||
"--force".into(),
|
||||
]
|
||||
})
|
||||
.collect();
|
||||
commands.extend_from_slice(&preset.commands);
|
||||
for cmnd in &preset.commands {
|
||||
commands.push(shell_words::split(cmnd).into_diagnostic()?);
|
||||
}
|
||||
|
||||
Ok(commands)
|
||||
}
|
||||
}
|
||||
|
||||
fn split_cmnd(s: &str) -> Vec<String> {
|
||||
s.split(' ').map(String::from).collect()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@ use crate::cloud::CloudOptions;
|
|||
use crate::config::HazeConfig;
|
||||
use crate::exec::exec;
|
||||
use crate::image::pull_image;
|
||||
use crate::service::ServiceTrait;
|
||||
use crate::service::{split_cmnd, ServiceTrait};
|
||||
use crate::Result;
|
||||
use bollard::container::{Config, CreateContainerOptions, NetworkingConfig};
|
||||
use bollard::models::{EndpointSettings, HostConfig};
|
||||
use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig};
|
||||
use bollard::query_parameters::CreateContainerOptions;
|
||||
use bollard::Docker;
|
||||
use maplit::hashmap;
|
||||
use miette::{IntoDiagnostic, WrapErr};
|
||||
|
|
@ -40,26 +40,26 @@ impl ServiceTrait for ClamIcap {
|
|||
let image = "ghcr.io/icewind1991/icap-clamav-service-tls";
|
||||
pull_image(docker, image).await?;
|
||||
let options = Some(CreateContainerOptions {
|
||||
name: self.container_name(cloud_id).unwrap(),
|
||||
name: self.container_name(cloud_id),
|
||||
..CreateContainerOptions::default()
|
||||
});
|
||||
let config = Config {
|
||||
image: Some(image),
|
||||
let config = ContainerCreateBody {
|
||||
image: Some(image.into()),
|
||||
host_config: Some(HostConfig {
|
||||
network_mode: Some(network.to_string()),
|
||||
..Default::default()
|
||||
}),
|
||||
labels: Some(hashmap! {
|
||||
"haze-type" => self.name(),
|
||||
"haze-cloud-id" => cloud_id
|
||||
"haze-type".into() => self.name().into(),
|
||||
"haze-cloud-id".into() => cloud_id.into()
|
||||
}),
|
||||
networking_config: Some(NetworkingConfig {
|
||||
endpoints_config: hashmap! {
|
||||
network => EndpointSettings {
|
||||
endpoints_config: Some(hashmap! {
|
||||
network.into() => EndpointSettings {
|
||||
aliases: Some(vec![self.name().to_string()]),
|
||||
..Default::default()
|
||||
}
|
||||
},
|
||||
}),
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
|
|
@ -68,10 +68,7 @@ impl ServiceTrait for ClamIcap {
|
|||
.await
|
||||
.into_diagnostic()?
|
||||
.id;
|
||||
docker
|
||||
.start_container::<String>(&id, None)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
docker.start_container(&id, None).await.into_diagnostic()?;
|
||||
Ok(vec![id])
|
||||
}
|
||||
|
||||
|
|
@ -88,14 +85,13 @@ impl ServiceTrait for ClamIcap {
|
|||
_docker: &Docker,
|
||||
_cloud_id: &str,
|
||||
_config: &HazeConfig,
|
||||
) -> Result<Vec<String>> {
|
||||
) -> Result<Vec<Vec<String>>> {
|
||||
Ok(vec![
|
||||
"occ config:app:set files_antivirus av_mode --value=icap".into(),
|
||||
"occ config:app:set files_antivirus av_host --value=clamav-icap".into(),
|
||||
"occ config:app:set files_antivirus av_port --value=1344".into(),
|
||||
"occ config:app:set files_antivirus av_icap_request_service --value=avscan".into(),
|
||||
"occ config:app:set files_antivirus av_icap_response_header --value=X-Infection-Found"
|
||||
.into(),
|
||||
split_cmnd("occ config:app:set files_antivirus av_mode --value=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_icap_request_service --value=avscan"),
|
||||
split_cmnd("occ config:app:set files_antivirus av_icap_response_header --value=X-Infection-Found"),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
|
@ -129,26 +125,26 @@ impl ServiceTrait for ClamIcapTls {
|
|||
let image = "ghcr.io/icewind1991/icap-clamav-service-tls";
|
||||
pull_image(docker, image).await?;
|
||||
let options = Some(CreateContainerOptions {
|
||||
name: self.container_name(cloud_id).unwrap(),
|
||||
name: self.container_name(cloud_id),
|
||||
..CreateContainerOptions::default()
|
||||
});
|
||||
let config = Config {
|
||||
image: Some(image),
|
||||
let config = ContainerCreateBody {
|
||||
image: Some(image.into()),
|
||||
host_config: Some(HostConfig {
|
||||
network_mode: Some(network.to_string()),
|
||||
..Default::default()
|
||||
}),
|
||||
labels: Some(hashmap! {
|
||||
"haze-type" => self.name(),
|
||||
"haze-cloud-id" => cloud_id
|
||||
"haze-type".into() => self.name().into(),
|
||||
"haze-cloud-id".into() => cloud_id.into()
|
||||
}),
|
||||
networking_config: Some(NetworkingConfig {
|
||||
endpoints_config: hashmap! {
|
||||
network => EndpointSettings {
|
||||
endpoints_config: Some(hashmap! {
|
||||
network.into() => EndpointSettings {
|
||||
aliases: Some(vec![self.name().to_string()]),
|
||||
..Default::default()
|
||||
}
|
||||
},
|
||||
}),
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
|
|
@ -157,10 +153,7 @@ impl ServiceTrait for ClamIcapTls {
|
|||
.await
|
||||
.into_diagnostic()?
|
||||
.id;
|
||||
docker
|
||||
.start_container::<String>(&id, None)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
docker.start_container(&id, None).await.into_diagnostic()?;
|
||||
Ok(vec![id])
|
||||
}
|
||||
|
||||
|
|
@ -177,7 +170,7 @@ impl ServiceTrait for ClamIcapTls {
|
|||
docker: &Docker,
|
||||
cloud_id: &str,
|
||||
config: &HazeConfig,
|
||||
) -> Result<Vec<String>> {
|
||||
) -> Result<Vec<Vec<String>>> {
|
||||
let mut cert = Vec::new();
|
||||
exec(
|
||||
docker,
|
||||
|
|
@ -197,14 +190,13 @@ impl ServiceTrait for ClamIcapTls {
|
|||
.wrap_err("Failed to write icap certificate")?;
|
||||
|
||||
Ok(vec![
|
||||
"occ config:app:set files_antivirus av_mode --value=icap".into(),
|
||||
"occ config:app:set files_antivirus av_icap_tls --value=1".into(),
|
||||
"occ config:app:set files_antivirus av_host --value=clamav-icap-tls".into(),
|
||||
"occ config:app:set files_antivirus av_port --value=1345".into(),
|
||||
"occ config:app:set files_antivirus av_icap_request_service --value=avscan".into(),
|
||||
"occ config:app:set files_antivirus av_icap_response_header --value=X-Infection-Found"
|
||||
.into(),
|
||||
"occ security:certificates:import data/icap-cert.pem".into(),
|
||||
split_cmnd("occ config:app:set files_antivirus av_mode --value=icap"),
|
||||
split_cmnd("occ config:app:set files_antivirus av_icap_tls --value=1"),
|
||||
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_icap_request_service --value=avscan"),
|
||||
split_cmnd("occ config:app:set files_antivirus av_icap_response_header --value=X-Infection-Found"),
|
||||
split_cmnd("occ security:certificates:import data/icap-cert.pem"),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
|
@ -227,10 +219,10 @@ impl ServiceTrait for Clam {
|
|||
_docker: &Docker,
|
||||
_cloud_id: &str,
|
||||
_config: &HazeConfig,
|
||||
) -> Result<Vec<String>> {
|
||||
) -> Result<Vec<Vec<String>>> {
|
||||
Ok(vec![
|
||||
"occ config:app:set files_antivirus av_mode --value=executable".into(),
|
||||
"occ config:app:set files_antivirus av_path --value=/bin/clamscan".into(),
|
||||
split_cmnd("occ config:app:set files_antivirus av_mode --value=executable"),
|
||||
split_cmnd("occ config:app:set files_antivirus av_path --value=/bin/clamscan"),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
|
@ -255,26 +247,26 @@ impl ServiceTrait for ClamSocket {
|
|||
let image = "clamav/clamav";
|
||||
pull_image(docker, image).await?;
|
||||
let options = Some(CreateContainerOptions {
|
||||
name: self.container_name(cloud_id).unwrap(),
|
||||
name: self.container_name(cloud_id),
|
||||
..CreateContainerOptions::default()
|
||||
});
|
||||
let config = Config {
|
||||
image: Some(image),
|
||||
let config = ContainerCreateBody {
|
||||
image: Some(image.into()),
|
||||
host_config: Some(HostConfig {
|
||||
network_mode: Some(network.to_string()),
|
||||
..Default::default()
|
||||
}),
|
||||
labels: Some(hashmap! {
|
||||
"haze-type" => self.name(),
|
||||
"haze-cloud-id" => cloud_id
|
||||
"haze-type".into() => self.name().into(),
|
||||
"haze-cloud-id".into() => cloud_id.into()
|
||||
}),
|
||||
networking_config: Some(NetworkingConfig {
|
||||
endpoints_config: hashmap! {
|
||||
network => EndpointSettings {
|
||||
endpoints_config: Some(hashmap! {
|
||||
network.into() => EndpointSettings {
|
||||
aliases: Some(vec![self.name().to_string()]),
|
||||
..Default::default()
|
||||
}
|
||||
},
|
||||
}),
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
|
|
@ -283,10 +275,7 @@ impl ServiceTrait for ClamSocket {
|
|||
.await
|
||||
.into_diagnostic()?
|
||||
.id;
|
||||
docker
|
||||
.start_container::<String>(&id, None)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
docker.start_container(&id, None).await.into_diagnostic()?;
|
||||
Ok(vec![id])
|
||||
}
|
||||
|
||||
|
|
@ -303,10 +292,12 @@ impl ServiceTrait for ClamSocket {
|
|||
_docker: &Docker,
|
||||
_cloud_id: &str,
|
||||
_config: &HazeConfig,
|
||||
) -> Result<Vec<String>> {
|
||||
) -> Result<Vec<Vec<String>>> {
|
||||
Ok(vec![
|
||||
"occ config:app:set files_antivirus av_mode --value=socket".into(),
|
||||
"occ config:app:set files_antivirus av_socket --value=tcp://clamav-socket:3310".into(),
|
||||
split_cmnd("occ config:app:set files_antivirus av_mode --value=socket"),
|
||||
split_cmnd(
|
||||
"occ config:app:set files_antivirus av_socket --value=tcp://clamav-socket:3310",
|
||||
),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
use crate::cloud::CloudOptions;
|
||||
use crate::config::HazeConfig;
|
||||
use crate::image::pull_image;
|
||||
use crate::service::ServiceTrait;
|
||||
use crate::service::{split_cmnd, ServiceTrait};
|
||||
use crate::Result;
|
||||
use bollard::container::{Config, CreateContainerOptions, NetworkingConfig};
|
||||
use bollard::models::{EndpointSettings, HostConfig};
|
||||
use bollard::config::ContainerCreateBody;
|
||||
use bollard::models::{EndpointSettings, HostConfig, NetworkingConfig};
|
||||
use bollard::query_parameters::CreateContainerOptions;
|
||||
use bollard::Docker;
|
||||
use maplit::hashmap;
|
||||
use miette::IntoDiagnostic;
|
||||
|
|
@ -29,27 +30,27 @@ impl ServiceTrait for Dav {
|
|||
let image = "ugeek/webdav:amd64";
|
||||
pull_image(docker, image).await?;
|
||||
let options = Some(CreateContainerOptions {
|
||||
name: self.container_name(cloud_id).unwrap(),
|
||||
name: self.container_name(cloud_id),
|
||||
..CreateContainerOptions::default()
|
||||
});
|
||||
let config = Config {
|
||||
image: Some(image),
|
||||
let config = ContainerCreateBody {
|
||||
image: Some(image.into()),
|
||||
host_config: Some(HostConfig {
|
||||
network_mode: Some(network.to_string()),
|
||||
..Default::default()
|
||||
}),
|
||||
env: Some(vec!["USERNAME=test", "PASSWORD=test"]),
|
||||
env: Some(vec!["USERNAME=test".into(), "PASSWORD=test".into()]),
|
||||
labels: Some(hashmap! {
|
||||
"haze-type" => self.name(),
|
||||
"haze-cloud-id" => cloud_id
|
||||
"haze-type".into() => self.name().into(),
|
||||
"haze-cloud-id".into() => cloud_id.into()
|
||||
}),
|
||||
networking_config: Some(NetworkingConfig {
|
||||
endpoints_config: hashmap! {
|
||||
network => EndpointSettings {
|
||||
endpoints_config: Some(hashmap! {
|
||||
network.into() => EndpointSettings {
|
||||
aliases: Some(vec![self.name().to_string()]),
|
||||
..Default::default()
|
||||
}
|
||||
},
|
||||
}),
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
|
|
@ -58,10 +59,7 @@ impl ServiceTrait for Dav {
|
|||
.await
|
||||
.into_diagnostic()?
|
||||
.id;
|
||||
docker
|
||||
.start_container::<String>(&id, None)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
docker.start_container(&id, None).await.into_diagnostic()?;
|
||||
Ok(vec![id])
|
||||
}
|
||||
|
||||
|
|
@ -78,12 +76,12 @@ impl ServiceTrait for Dav {
|
|||
_docker: &Docker,
|
||||
_cloud_id: &str,
|
||||
_config: &HazeConfig,
|
||||
) -> Result<Vec<String>> {
|
||||
) -> Result<Vec<Vec<String>>> {
|
||||
Ok(vec![
|
||||
"occ files_external:create dav dav password::password".into(),
|
||||
"occ files_external:config 1 host dav".into(),
|
||||
"occ files_external:config 1 user test".into(),
|
||||
"occ files_external:config 1 password test".into(),
|
||||
split_cmnd("occ files_external:create dav dav password::password"),
|
||||
split_cmnd("occ files_external:config 1 host dav"),
|
||||
split_cmnd("occ files_external:config 1 user test"),
|
||||
split_cmnd("occ files_external:config 1 password test"),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
use crate::cloud::CloudOptions;
|
||||
use crate::config::HazeConfig;
|
||||
use crate::image::pull_image;
|
||||
use crate::service::ServiceTrait;
|
||||
use crate::service::{split_cmnd, ServiceTrait};
|
||||
use crate::Result;
|
||||
use bollard::container::{Config, CreateContainerOptions, NetworkingConfig};
|
||||
use bollard::models::{EndpointSettings, HostConfig};
|
||||
use bollard::config::NetworkingConfig;
|
||||
use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig};
|
||||
use bollard::query_parameters::CreateContainerOptions;
|
||||
use bollard::Docker;
|
||||
use maplit::hashmap;
|
||||
use miette::IntoDiagnostic;
|
||||
|
|
@ -29,26 +30,26 @@ impl ServiceTrait for Imaginary {
|
|||
let image = "nextcloud/aio-imaginary:latest";
|
||||
pull_image(docker, image).await?;
|
||||
let options = Some(CreateContainerOptions {
|
||||
name: self.container_name(cloud_id).unwrap(),
|
||||
name: self.container_name(cloud_id),
|
||||
..CreateContainerOptions::default()
|
||||
});
|
||||
let config = Config {
|
||||
image: Some(image),
|
||||
let config = ContainerCreateBody {
|
||||
image: Some(image.into()),
|
||||
host_config: Some(HostConfig {
|
||||
network_mode: Some(network.to_string()),
|
||||
..Default::default()
|
||||
}),
|
||||
labels: Some(hashmap! {
|
||||
"haze-type" => self.name(),
|
||||
"haze-cloud-id" => cloud_id
|
||||
"haze-type".into() => self.name().into(),
|
||||
"haze-cloud-id".into() => cloud_id.into()
|
||||
}),
|
||||
networking_config: Some(NetworkingConfig {
|
||||
endpoints_config: hashmap! {
|
||||
network => EndpointSettings {
|
||||
endpoints_config: Some(hashmap! {
|
||||
network.into() => EndpointSettings {
|
||||
aliases: Some(vec![self.name().to_string()]),
|
||||
..Default::default()
|
||||
}
|
||||
},
|
||||
}),
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
|
|
@ -57,10 +58,7 @@ impl ServiceTrait for Imaginary {
|
|||
.await
|
||||
.into_diagnostic()?
|
||||
.id;
|
||||
docker
|
||||
.start_container::<String>(&id, None)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
docker.start_container(&id, None).await.into_diagnostic()?;
|
||||
Ok(vec![id])
|
||||
}
|
||||
|
||||
|
|
@ -73,11 +71,14 @@ impl ServiceTrait for Imaginary {
|
|||
_docker: &Docker,
|
||||
_cloud_id: &str,
|
||||
_config: &HazeConfig,
|
||||
) -> Result<Vec<String>> {
|
||||
) -> Result<Vec<Vec<String>>> {
|
||||
Ok(vec![
|
||||
"occ config:system:set enabledPreviewProviders 0 --value='OC\\Preview\\Imaginary'"
|
||||
.into(),
|
||||
"occ config:system:set preview_imaginary_url --value='http://imaginary:9000'".into(),
|
||||
split_cmnd(
|
||||
"occ config:system:set enabledPreviewProviders 0 --value='OC\\Preview\\Imaginary'",
|
||||
),
|
||||
split_cmnd(
|
||||
"occ config:system:set preview_imaginary_url --value='http://imaginary:9000'",
|
||||
),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@ use crate::cloud::CloudOptions;
|
|||
use crate::config::HazeConfig;
|
||||
use crate::exec::exec;
|
||||
use crate::image::{image_exists, pull_image};
|
||||
use crate::service::ServiceTrait;
|
||||
use crate::service::{split_cmnd, ServiceTrait};
|
||||
use crate::Result;
|
||||
use bollard::container::{Config, CreateContainerOptions, NetworkingConfig};
|
||||
use bollard::models::{EndpointSettings, HostConfig};
|
||||
use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig};
|
||||
use bollard::query_parameters::CreateContainerOptions;
|
||||
use bollard::Docker;
|
||||
use maplit::hashmap;
|
||||
use miette::{bail, IntoDiagnostic};
|
||||
|
|
@ -38,26 +38,26 @@ impl ServiceTrait for Kaspersky {
|
|||
}
|
||||
pull_image(docker, image).await?;
|
||||
let options = Some(CreateContainerOptions {
|
||||
name: self.container_name(cloud_id).unwrap(),
|
||||
name: self.container_name(cloud_id),
|
||||
..CreateContainerOptions::default()
|
||||
});
|
||||
let config = Config {
|
||||
image: Some(image),
|
||||
let config = ContainerCreateBody {
|
||||
image: Some(image.into()),
|
||||
host_config: Some(HostConfig {
|
||||
network_mode: Some(network.to_string()),
|
||||
..Default::default()
|
||||
}),
|
||||
labels: Some(hashmap! {
|
||||
"haze-type" => self.name(),
|
||||
"haze-cloud-id" => cloud_id
|
||||
"haze-type".into() => self.name().into(),
|
||||
"haze-cloud-id".into() => cloud_id.into()
|
||||
}),
|
||||
networking_config: Some(NetworkingConfig {
|
||||
endpoints_config: hashmap! {
|
||||
network => EndpointSettings {
|
||||
endpoints_config: Some(hashmap! {
|
||||
network.into() => EndpointSettings {
|
||||
aliases: Some(vec![self.name().to_string()]),
|
||||
..Default::default()
|
||||
}
|
||||
},
|
||||
}),
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
|
|
@ -66,10 +66,7 @@ impl ServiceTrait for Kaspersky {
|
|||
.await
|
||||
.into_diagnostic()?
|
||||
.id;
|
||||
docker
|
||||
.start_container::<String>(&id, None)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
docker.start_container(&id, None).await.into_diagnostic()?;
|
||||
Ok(vec![id])
|
||||
}
|
||||
|
||||
|
|
@ -104,11 +101,11 @@ impl ServiceTrait for Kaspersky {
|
|||
_docker: &Docker,
|
||||
_cloud_id: &str,
|
||||
_config: &HazeConfig,
|
||||
) -> Result<Vec<String>> {
|
||||
) -> Result<Vec<Vec<String>>> {
|
||||
Ok(vec![
|
||||
"occ config:app:set files_antivirus av_mode --value=kaspersky".into(),
|
||||
"occ config:app:set files_antivirus av_host --value=kaspersky".into(),
|
||||
"occ config:app:set files_antivirus av_port --value=80".into(),
|
||||
split_cmnd("occ config:app:set files_antivirus av_mode --value=kaspersky"),
|
||||
split_cmnd("occ config:app:set files_antivirus av_host --value=kaspersky"),
|
||||
split_cmnd("occ config:app:set files_antivirus av_port --value=80"),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
|
@ -145,26 +142,26 @@ impl ServiceTrait for KasperskyIcap {
|
|||
}
|
||||
pull_image(docker, image).await?;
|
||||
let options = Some(CreateContainerOptions {
|
||||
name: self.container_name(cloud_id).unwrap(),
|
||||
name: self.container_name(cloud_id),
|
||||
..CreateContainerOptions::default()
|
||||
});
|
||||
let config = Config {
|
||||
image: Some(image),
|
||||
let config = ContainerCreateBody {
|
||||
image: Some(image.into()),
|
||||
host_config: Some(HostConfig {
|
||||
network_mode: Some(network.to_string()),
|
||||
..Default::default()
|
||||
}),
|
||||
labels: Some(hashmap! {
|
||||
"haze-type" => self.name(),
|
||||
"haze-cloud-id" => cloud_id
|
||||
"haze-type".into() => self.name().into(),
|
||||
"haze-cloud-id".into() => cloud_id.into(),
|
||||
}),
|
||||
networking_config: Some(NetworkingConfig {
|
||||
endpoints_config: hashmap! {
|
||||
network => EndpointSettings {
|
||||
endpoints_config: Some(hashmap! {
|
||||
network.into() => EndpointSettings {
|
||||
aliases: Some(vec![self.name().to_string()]),
|
||||
..Default::default()
|
||||
}
|
||||
},
|
||||
}),
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
|
|
@ -173,10 +170,7 @@ impl ServiceTrait for KasperskyIcap {
|
|||
.await
|
||||
.into_diagnostic()?
|
||||
.id;
|
||||
docker
|
||||
.start_container::<String>(&id, None)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
docker.start_container(&id, None).await.into_diagnostic()?;
|
||||
Ok(vec![id])
|
||||
}
|
||||
|
||||
|
|
@ -193,13 +187,15 @@ impl ServiceTrait for KasperskyIcap {
|
|||
_docker: &Docker,
|
||||
_cloud_id: &str,
|
||||
_config: &HazeConfig,
|
||||
) -> Result<Vec<String>> {
|
||||
) -> Result<Vec<Vec<String>>> {
|
||||
Ok(vec![
|
||||
"occ config:app:set files_antivirus av_mode --value=icap".into(),
|
||||
"occ config:app:set files_antivirus av_host --value=kaspersky-icap".into(),
|
||||
"occ config:app:set files_antivirus av_port --value=1344".into(),
|
||||
"occ config:app:set files_antivirus av_icap_request_service --value=req".into(),
|
||||
"occ config:app:set files_antivirus av_icap_response_header --value=X-Virus-ID".into(),
|
||||
split_cmnd("occ config:app:set files_antivirus av_mode --value=icap"),
|
||||
split_cmnd("occ config:app:set files_antivirus av_host --value=kaspersky-icap"),
|
||||
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=req"),
|
||||
split_cmnd(
|
||||
"occ config:app:set files_antivirus av_icap_response_header --value=X-Virus-ID",
|
||||
),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,16 @@
|
|||
use crate::cloud::CloudOptions;
|
||||
use crate::config::HazeConfig;
|
||||
use crate::config::{HazeConfig, ProxyConfig};
|
||||
use crate::image::pull_image;
|
||||
use crate::service::ServiceTrait;
|
||||
use crate::service::{split_cmnd, ServiceTrait};
|
||||
use crate::Result;
|
||||
use bollard::container::{Config, CreateContainerOptions, NetworkingConfig};
|
||||
use bollard::models::{ContainerState, EndpointSettings, HostConfig};
|
||||
use bollard::config::NetworkingConfig;
|
||||
use bollard::models::{ContainerCreateBody, ContainerState, EndpointSettings, HostConfig};
|
||||
use bollard::query_parameters::CreateContainerOptions;
|
||||
use bollard::Docker;
|
||||
use maplit::hashmap;
|
||||
use miette::{IntoDiagnostic, Report};
|
||||
use std::net::IpAddr;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct Ldap;
|
||||
|
|
@ -33,29 +36,29 @@ impl ServiceTrait for Ldap {
|
|||
let image = "icewind1991/haze-ldap";
|
||||
pull_image(docker, image).await?;
|
||||
let options = Some(CreateContainerOptions {
|
||||
name: self.container_name(cloud_id).unwrap(),
|
||||
name: self.container_name(cloud_id),
|
||||
..CreateContainerOptions::default()
|
||||
});
|
||||
let config = Config {
|
||||
image: Some(image),
|
||||
env: Some(vec!["LDAP_ADMIN_PASSWORD=haze"]),
|
||||
let config = ContainerCreateBody {
|
||||
image: Some(image.into()),
|
||||
env: Some(vec!["LDAP_ADMIN_PASSWORD=haze".into()]),
|
||||
host_config: Some(HostConfig {
|
||||
network_mode: Some(network.to_string()),
|
||||
..Default::default()
|
||||
}),
|
||||
labels: Some(hashmap! {
|
||||
"haze-type" => self.name(),
|
||||
"haze-cloud-id" => cloud_id
|
||||
"haze-type".into() => self.name().into(),
|
||||
"haze-cloud-id".into() => cloud_id.into()
|
||||
}),
|
||||
networking_config: Some(NetworkingConfig {
|
||||
endpoints_config: hashmap! {
|
||||
network => EndpointSettings {
|
||||
endpoints_config: Some(hashmap! {
|
||||
network.into() => EndpointSettings {
|
||||
aliases: Some(vec![self.name().to_string()]),
|
||||
..Default::default()
|
||||
}
|
||||
},
|
||||
}),
|
||||
}),
|
||||
cmd: Some(vec!["--copy-service"]),
|
||||
cmd: Some(vec!["--copy-service".into()]),
|
||||
..Default::default()
|
||||
};
|
||||
let id = docker
|
||||
|
|
@ -63,10 +66,7 @@ impl ServiceTrait for Ldap {
|
|||
.await
|
||||
.into_diagnostic()?
|
||||
.id;
|
||||
docker
|
||||
.start_container::<String>(&id, None)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
docker.start_container(&id, None).await.into_diagnostic()?;
|
||||
Ok(vec![id])
|
||||
}
|
||||
|
||||
|
|
@ -86,6 +86,46 @@ impl ServiceTrait for Ldap {
|
|||
) -> Result<bool> {
|
||||
self.is_running(docker, cloud_id).await
|
||||
}
|
||||
|
||||
async fn post_setup(
|
||||
&self,
|
||||
_docker: &Docker,
|
||||
_cloud_id: &str,
|
||||
_config: &HazeConfig,
|
||||
) -> Result<Vec<Vec<String>>> {
|
||||
Ok(vec![
|
||||
split_cmnd("occ ldap:create-empty-config"),
|
||||
split_cmnd("occ ldap:set-config s01 ldapHost ldap://ldap"),
|
||||
split_cmnd("occ ldap:set-config s01 ldapPort 389"),
|
||||
split_cmnd("occ ldap:set-config s01 ldapAgentName cn=admin,dc=example,dc=org"),
|
||||
split_cmnd("occ ldap:set-config s01 ldapAgentPassword haze"),
|
||||
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 ldapBaseGroups dc=example,dc=org"),
|
||||
split_cmnd("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 ldapUserFilterMode 0"),
|
||||
split_cmnd("occ ldap:set-config s01 ldapUserDisplayName sn"),
|
||||
split_cmnd("occ ldap:set-config s01 ldapUserFilterObjectclass inetOrgPerson"),
|
||||
split_cmnd("occ ldap:set-config s01 ldapGroupFilter (&(|(objectclass=posixGroup)))"),
|
||||
split_cmnd("occ ldap:set-config s01 ldapGroupFilterObjectclass posixGroup"),
|
||||
split_cmnd("occ ldap:set-config s01 ldapEmailAttribute email"),
|
||||
split_cmnd("occ ldap:set-config s01 ldapUuidUserAttribute email"),
|
||||
split_cmnd("occ ldap:set-config s01 ldapUuidUserAttribute auto"),
|
||||
split_cmnd("occ ldap:set-config s01 ldapUuidGroupAttribute auto"),
|
||||
split_cmnd("occ ldap:set-config s01 ldapLoginFilterUsername 1"),
|
||||
split_cmnd("occ ldap:set-config s01 ldapConfigurationActive 1"),
|
||||
])
|
||||
}
|
||||
|
||||
async fn start_message(
|
||||
&self,
|
||||
_docker: &Docker,
|
||||
_cloud_id: &str,
|
||||
_proxy: &ProxyConfig,
|
||||
) -> Result<Option<String>> {
|
||||
Ok(Some("\nLdap users provisioned:\n\t'cn=admin,dc=example,dc=org' and password 'haze'\n\t'cn=ldaptest,dc=example,dc=org' and password 'test'\n\nldaptest is available for login\n".into()))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
|
|
@ -112,29 +152,32 @@ impl ServiceTrait for LdapAdmin {
|
|||
let image = "osixia/phpldapadmin";
|
||||
pull_image(docker, image).await?;
|
||||
let options = Some(CreateContainerOptions {
|
||||
name: self.container_name(cloud_id).unwrap(),
|
||||
name: self.container_name(cloud_id),
|
||||
..CreateContainerOptions::default()
|
||||
});
|
||||
let config = Config {
|
||||
image: Some(image),
|
||||
env: Some(vec!["PHPLDAPADMIN_LDAP_HOSTS=ldap"]),
|
||||
let config = ContainerCreateBody {
|
||||
image: Some(image.into()),
|
||||
env: Some(vec![
|
||||
"PHPLDAPADMIN_LDAP_HOSTS=ldap".into(),
|
||||
"PHPLDAPADMIN_HTTPS=false".into(),
|
||||
]),
|
||||
host_config: Some(HostConfig {
|
||||
network_mode: Some(network.to_string()),
|
||||
..Default::default()
|
||||
}),
|
||||
labels: Some(hashmap! {
|
||||
"haze-type" => self.name(),
|
||||
"haze-cloud-id" => cloud_id
|
||||
"haze-type".into() => self.name().into(),
|
||||
"haze-cloud-id".into() => cloud_id.into(),
|
||||
}),
|
||||
networking_config: Some(NetworkingConfig {
|
||||
endpoints_config: hashmap! {
|
||||
network => EndpointSettings {
|
||||
endpoints_config: Some(hashmap! {
|
||||
network.into() => EndpointSettings {
|
||||
aliases: Some(vec![self.name().to_string()]),
|
||||
..Default::default()
|
||||
}
|
||||
},
|
||||
}),
|
||||
}),
|
||||
cmd: Some(vec!["--copy-service"]),
|
||||
cmd: Some(vec!["--copy-service".into()]),
|
||||
..Default::default()
|
||||
};
|
||||
let id = docker
|
||||
|
|
@ -142,10 +185,7 @@ impl ServiceTrait for LdapAdmin {
|
|||
.await
|
||||
.into_diagnostic()?
|
||||
.id;
|
||||
docker
|
||||
.start_container::<String>(&id, None)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
docker.start_container(&id, None).await.into_diagnostic()?;
|
||||
Ok(vec![id])
|
||||
}
|
||||
|
||||
|
|
@ -153,9 +193,15 @@ impl ServiceTrait for LdapAdmin {
|
|||
Some(format!("{}-ldap-admin", cloud_id))
|
||||
}
|
||||
|
||||
async fn start_message(&self, docker: &Docker, cloud_id: &str) -> Result<Option<String>> {
|
||||
async fn start_message(
|
||||
&self,
|
||||
docker: &Docker,
|
||||
cloud_id: &str,
|
||||
proxy: &ProxyConfig,
|
||||
) -> Result<Option<String>> {
|
||||
let id = self.container_name(cloud_id).unwrap();
|
||||
let info = docker
|
||||
.inspect_container(&self.container_name(cloud_id).unwrap(), None)
|
||||
.inspect_container(&id, None)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
let ip = if matches!(
|
||||
|
|
@ -178,9 +224,7 @@ impl ServiceTrait for LdapAdmin {
|
|||
} else {
|
||||
return Err(Report::msg("ldap admin not started"));
|
||||
};
|
||||
Ok(Some(format!(
|
||||
"Ldap admin running at: https://{} with 'cn=admin,dc=example,dc=org' and password 'haze'",
|
||||
ip
|
||||
)))
|
||||
let addr = proxy.addr(&id, IpAddr::from_str(&ip).unwrap());
|
||||
Ok(Some(format!("Ldap admin running at: {addr}")))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
use crate::cloud::CloudOptions;
|
||||
use crate::config::HazeConfig;
|
||||
use crate::image::pull_image;
|
||||
use crate::service::ServiceTrait;
|
||||
use crate::service::{split_cmnd, ServiceTrait};
|
||||
use crate::Result;
|
||||
use bollard::container::{Config, CreateContainerOptions, NetworkingConfig};
|
||||
use bollard::models::{EndpointSettings, HostConfig};
|
||||
use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig};
|
||||
use bollard::query_parameters::CreateContainerOptions;
|
||||
use bollard::Docker;
|
||||
use maplit::hashmap;
|
||||
use miette::IntoDiagnostic;
|
||||
|
|
@ -29,26 +29,26 @@ impl ServiceTrait for Mail {
|
|||
let image = "rnwood/smtp4dev";
|
||||
pull_image(docker, image).await?;
|
||||
let options = Some(CreateContainerOptions {
|
||||
name: self.container_name(cloud_id).unwrap(),
|
||||
name: self.container_name(cloud_id),
|
||||
..CreateContainerOptions::default()
|
||||
});
|
||||
let config = Config {
|
||||
image: Some(image),
|
||||
let config = ContainerCreateBody {
|
||||
image: Some(image.into()),
|
||||
host_config: Some(HostConfig {
|
||||
network_mode: Some(network.to_string()),
|
||||
..Default::default()
|
||||
}),
|
||||
labels: Some(hashmap! {
|
||||
"haze-type" => self.name(),
|
||||
"haze-cloud-id" => cloud_id
|
||||
"haze-type".into() => self.name().into(),
|
||||
"haze-cloud-id".into() => cloud_id.into(),
|
||||
}),
|
||||
networking_config: Some(NetworkingConfig {
|
||||
endpoints_config: hashmap! {
|
||||
network => EndpointSettings {
|
||||
endpoints_config: Some(hashmap! {
|
||||
network.into() => EndpointSettings {
|
||||
aliases: Some(vec![self.name().to_string()]),
|
||||
..Default::default()
|
||||
}
|
||||
},
|
||||
}),
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
|
|
@ -57,10 +57,7 @@ impl ServiceTrait for Mail {
|
|||
.await
|
||||
.into_diagnostic()?
|
||||
.id;
|
||||
docker
|
||||
.start_container::<String>(&id, None)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
docker.start_container(&id, None).await.into_diagnostic()?;
|
||||
Ok(vec![id])
|
||||
}
|
||||
|
||||
|
|
@ -73,14 +70,14 @@ impl ServiceTrait for Mail {
|
|||
_docker: &Docker,
|
||||
_cloud_id: &str,
|
||||
_config: &HazeConfig,
|
||||
) -> Result<Vec<String>> {
|
||||
) -> Result<Vec<Vec<String>>> {
|
||||
Ok(vec![
|
||||
"occ config:system:set mail_smtpmode --value smtp".into(),
|
||||
"occ config:system:set mail_sendmailmode --value smtp".into(),
|
||||
"occ config:system:set mail_domain --value haze".into(),
|
||||
"occ config:system:set mail_smtphost --value mail".into(),
|
||||
"occ config:system:set mail_smtpport --value 25".into(),
|
||||
"occ user:setting admin settings email admin@haze".into(),
|
||||
split_cmnd("occ config:system:set mail_smtpmode --value smtp"),
|
||||
split_cmnd("occ config:system:set mail_sendmailmode --value smtp"),
|
||||
split_cmnd("occ config:system:set mail_domain --value haze"),
|
||||
split_cmnd("occ config:system:set mail_smtphost --value mail"),
|
||||
split_cmnd("occ config:system:set mail_smtpport --value 25"),
|
||||
split_cmnd("occ user:setting admin settings email admin@haze"),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,17 +2,23 @@ use crate::cloud::CloudOptions;
|
|||
use crate::config::HazeConfig;
|
||||
use crate::exec::exec;
|
||||
use crate::image::pull_image;
|
||||
use crate::service::ServiceTrait;
|
||||
use crate::service::{split_cmnd, ServiceTrait};
|
||||
use crate::Result;
|
||||
use bollard::container::{Config, CreateContainerOptions, NetworkingConfig};
|
||||
use bollard::models::{ContainerState, EndpointSettings, HostConfig};
|
||||
use bollard::models::{
|
||||
ContainerCreateBody, ContainerState, EndpointSettings, HostConfig, NetworkingConfig,
|
||||
};
|
||||
use bollard::query_parameters::CreateContainerOptions;
|
||||
use bollard::Docker;
|
||||
use maplit::hashmap;
|
||||
use miette::IntoDiagnostic;
|
||||
use miette::{IntoDiagnostic, WrapErr};
|
||||
use serde_json::Value;
|
||||
use std::collections::HashMap;
|
||||
use std::fs::{create_dir_all, write};
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum ObjectStore {
|
||||
S3,
|
||||
S3s,
|
||||
S3m,
|
||||
S3mb,
|
||||
Azure,
|
||||
|
|
@ -21,7 +27,7 @@ pub enum ObjectStore {
|
|||
impl ObjectStore {
|
||||
fn image(&self) -> &str {
|
||||
match self {
|
||||
ObjectStore::S3 | ObjectStore::S3m | ObjectStore::S3mb => {
|
||||
ObjectStore::S3 | ObjectStore::S3m | ObjectStore::S3mb | ObjectStore::S3s => {
|
||||
"minio/minio:RELEASE.2024-07-16T23-46-41Z"
|
||||
}
|
||||
ObjectStore::Azure => "arafato/azurite:2.6.5",
|
||||
|
|
@ -30,7 +36,7 @@ impl ObjectStore {
|
|||
|
||||
fn self_env(&self) -> Vec<&str> {
|
||||
match self {
|
||||
ObjectStore::S3 | ObjectStore::S3m | ObjectStore::S3mb => {
|
||||
ObjectStore::S3 | ObjectStore::S3m | ObjectStore::S3mb | ObjectStore::S3s => {
|
||||
vec!["MINIO_ACCESS_KEY=minio", "MINIO_SECRET_KEY=minio123"]
|
||||
}
|
||||
ObjectStore::Azure => vec![],
|
||||
|
|
@ -39,17 +45,54 @@ impl ObjectStore {
|
|||
|
||||
fn host_name(&self) -> &str {
|
||||
match self {
|
||||
ObjectStore::S3 | ObjectStore::S3m | ObjectStore::S3mb => "s3",
|
||||
ObjectStore::S3 | ObjectStore::S3m | ObjectStore::S3mb | ObjectStore::S3s => "s3",
|
||||
ObjectStore::Azure => "azure",
|
||||
}
|
||||
}
|
||||
|
||||
fn args(&self) -> &[&str] {
|
||||
match self {
|
||||
ObjectStore::S3 | ObjectStore::S3m | ObjectStore::S3mb => &["server", "/data"],
|
||||
ObjectStore::S3 | ObjectStore::S3m | ObjectStore::S3mb | ObjectStore::S3s => {
|
||||
&["server", "/data"]
|
||||
}
|
||||
_ => &[],
|
||||
}
|
||||
}
|
||||
|
||||
fn volumes(&self, config: &HazeConfig) -> Option<Vec<String>> {
|
||||
match self {
|
||||
ObjectStore::S3s => {
|
||||
let cert_dir = config.work_dir.join("certificates/s3");
|
||||
create_dir_all(&cert_dir)
|
||||
.into_diagnostic()
|
||||
.wrap_err("Failed to create redis certificate directory")
|
||||
.unwrap();
|
||||
let s3_cert_path = config.work_dir.join("certificates/s3/public.crt");
|
||||
let s3_key_path = config.work_dir.join("certificates/s3/private.key");
|
||||
if !s3_cert_path.exists() {
|
||||
write(
|
||||
&s3_cert_path,
|
||||
include_bytes!("../../certificates/s3/public.crt"),
|
||||
)
|
||||
.into_diagnostic()
|
||||
.wrap_err("Failed to write s3 certificate")
|
||||
.unwrap();
|
||||
}
|
||||
if !s3_key_path.exists() {
|
||||
write(
|
||||
&s3_key_path,
|
||||
include_bytes!("../../certificates/s3/private.key"),
|
||||
)
|
||||
.into_diagnostic()
|
||||
.wrap_err("Failed to write s3 key")
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
Some(vec![format!("{cert_dir}:/root/.minio/certs:ro")])
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
|
|
@ -57,6 +100,7 @@ impl ServiceTrait for ObjectStore {
|
|||
fn name(&self) -> &str {
|
||||
match self {
|
||||
ObjectStore::S3 => "s3",
|
||||
ObjectStore::S3s => "s3s",
|
||||
ObjectStore::S3m => "s3m",
|
||||
ObjectStore::S3mb => "s3mb",
|
||||
ObjectStore::Azure => "azure",
|
||||
|
|
@ -66,8 +110,9 @@ impl ServiceTrait for ObjectStore {
|
|||
fn env(&self) -> &[&str] {
|
||||
match self {
|
||||
ObjectStore::S3 => &["S3=1"],
|
||||
ObjectStore::S3s => &["S3S=1"],
|
||||
ObjectStore::S3m => &["S3M=1"],
|
||||
ObjectStore::S3mb => &["S3MB=1"],
|
||||
ObjectStore::S3mb => &["S3MB =1"],
|
||||
ObjectStore::Azure => &["AZURE=1"],
|
||||
}
|
||||
}
|
||||
|
|
@ -77,33 +122,34 @@ impl ServiceTrait for ObjectStore {
|
|||
docker: &Docker,
|
||||
cloud_id: &str,
|
||||
network: &str,
|
||||
_config: &HazeConfig,
|
||||
config: &HazeConfig,
|
||||
_options: &CloudOptions,
|
||||
) -> Result<Vec<String>> {
|
||||
pull_image(docker, self.image()).await?;
|
||||
let options = Some(CreateContainerOptions {
|
||||
name: format!("{}-object", cloud_id),
|
||||
name: Some(format!("{}-object", cloud_id)),
|
||||
..CreateContainerOptions::default()
|
||||
});
|
||||
let config = Config {
|
||||
image: Some(self.image()),
|
||||
env: Some(self.self_env()),
|
||||
let config = ContainerCreateBody {
|
||||
image: Some(self.image().into()),
|
||||
env: Some(self.self_env().into_iter().map(String::from).collect()),
|
||||
host_config: Some(HostConfig {
|
||||
network_mode: Some(network.to_string()),
|
||||
binds: self.volumes(config),
|
||||
..Default::default()
|
||||
}),
|
||||
labels: Some(hashmap! {
|
||||
"haze-type" => self.name(),
|
||||
"haze-cloud-id" => cloud_id
|
||||
"haze-type".into() => self.name().into(),
|
||||
"haze-cloud-id".into() => cloud_id.into(),
|
||||
}),
|
||||
cmd: Some(self.args().into()),
|
||||
cmd: Some(self.args().iter().copied().map(String::from).collect()),
|
||||
networking_config: Some(NetworkingConfig {
|
||||
endpoints_config: hashmap! {
|
||||
network => EndpointSettings {
|
||||
endpoints_config: Some(hashmap! {
|
||||
network.into() => EndpointSettings {
|
||||
aliases: Some(vec![self.host_name().to_string()]),
|
||||
..Default::default()
|
||||
}
|
||||
},
|
||||
}),
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
|
|
@ -112,10 +158,7 @@ impl ServiceTrait for ObjectStore {
|
|||
.await
|
||||
.into_diagnostic()?
|
||||
.id;
|
||||
docker
|
||||
.start_container::<String>(&id, None)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
docker.start_container(&id, None).await.into_diagnostic()?;
|
||||
Ok(vec![id])
|
||||
}
|
||||
|
||||
|
|
@ -166,26 +209,76 @@ impl ServiceTrait for ObjectStore {
|
|||
&["files_external"]
|
||||
}
|
||||
|
||||
fn config(
|
||||
&self,
|
||||
_docker: &Docker,
|
||||
_cloud_id: &str,
|
||||
_config: &HazeConfig,
|
||||
) -> Result<HashMap<String, Value>> {
|
||||
match self {
|
||||
ObjectStore::S3s => Ok(hashmap![
|
||||
"default_certificates_bundle_path".into() => Value::String("/var/www/html/data/ca-bundle.crt".into()),
|
||||
]),
|
||||
_ => Ok(HashMap::default()),
|
||||
}
|
||||
}
|
||||
|
||||
fn pre_setup(
|
||||
&self,
|
||||
_docker: &Docker,
|
||||
_cloud_id: &str,
|
||||
_config: &HazeConfig,
|
||||
) -> Result<Vec<Vec<String>>> {
|
||||
match self {
|
||||
ObjectStore::S3s => Ok(vec![
|
||||
vec!["mkdir".into(), "-p".into(), "/var/www/html/data".into()],
|
||||
vec![
|
||||
"sh".into(),
|
||||
"-c".into(),
|
||||
"cat /var/www/html/resources/config/ca-bundle.crt /certificates/s3/public.crt > /var/www/html/data/ca-bundle.crt".into(),
|
||||
],
|
||||
]),
|
||||
_ => Ok(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
async fn post_setup(
|
||||
&self,
|
||||
_docker: &Docker,
|
||||
_cloud_id: &str,
|
||||
_config: &HazeConfig,
|
||||
) -> Result<Vec<String>> {
|
||||
if *self == ObjectStore::S3 {
|
||||
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 false".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 http://s3:9000 minio minio123".into(),
|
||||
])
|
||||
} else {
|
||||
Ok(Vec::new())
|
||||
) -> Result<Vec<Vec<String>>> {
|
||||
match self {
|
||||
ObjectStore::S3 => Ok(vec![
|
||||
split_cmnd("occ files_external:create s3 amazons3 amazons3::accesskey"),
|
||||
split_cmnd("occ files_external:config 1 bucket ext"),
|
||||
split_cmnd("occ files_external:config 1 hostname s3"),
|
||||
split_cmnd("occ files_external:config 1 port 9000"),
|
||||
split_cmnd("occ files_external:config 1 use_ssl false"),
|
||||
split_cmnd("occ files_external:config 1 use_path_style true"),
|
||||
split_cmnd("occ files_external:config 1 key minio"),
|
||||
split_cmnd("occ files_external:config 1 secret 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()),
|
||||
}
|
||||
}
|
||||
|
||||
fn proxy_port(&self) -> u16 {
|
||||
match self {
|
||||
ObjectStore::S3 | ObjectStore::S3m | ObjectStore::S3mb | ObjectStore::S3s => 9000,
|
||||
ObjectStore::Azure => 10000,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@ use crate::exec::exec;
|
|||
use crate::image::pull_image;
|
||||
use crate::service::ServiceTrait;
|
||||
use crate::Result;
|
||||
use bollard::container::{Config, CreateContainerOptions, NetworkingConfig};
|
||||
use bollard::models::{EndpointSettings, HostConfig};
|
||||
use bollard::config::NetworkingConfig;
|
||||
use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig};
|
||||
use bollard::query_parameters::CreateContainerOptions;
|
||||
use bollard::Docker;
|
||||
use maplit::hashmap;
|
||||
use miette::IntoDiagnostic;
|
||||
|
|
@ -33,7 +34,7 @@ impl ServiceTrait for Oc {
|
|||
let image = "owncloud/server:10.12.2";
|
||||
pull_image(docker, image).await?;
|
||||
let options = Some(CreateContainerOptions {
|
||||
name: self.container_name(cloud_id).unwrap(),
|
||||
name: self.container_name(cloud_id),
|
||||
..CreateContainerOptions::default()
|
||||
});
|
||||
let addr = config.proxy.addr(
|
||||
|
|
@ -43,24 +44,24 @@ impl ServiceTrait for Oc {
|
|||
let domain = addr.split_once("://").unwrap().1;
|
||||
let env_trusted_domain = format!("OWNCLOUD_TRUSTED_DOMAINS={domain}");
|
||||
let env_domain = format!("OWNCLOUD_DOMAIN={domain}");
|
||||
let config = Config {
|
||||
image: Some(image),
|
||||
let config = ContainerCreateBody {
|
||||
image: Some(image.into()),
|
||||
host_config: Some(HostConfig {
|
||||
network_mode: Some(network.to_string()),
|
||||
..Default::default()
|
||||
}),
|
||||
env: Some(vec![&env_trusted_domain, &env_domain]),
|
||||
env: Some(vec![env_trusted_domain, env_domain]),
|
||||
labels: Some(hashmap! {
|
||||
"haze-type" => self.name(),
|
||||
"haze-cloud-id" => cloud_id
|
||||
"haze-type".into() => self.name().into(),
|
||||
"haze-cloud-id".into() => cloud_id.into(),
|
||||
}),
|
||||
networking_config: Some(NetworkingConfig {
|
||||
endpoints_config: hashmap! {
|
||||
network => EndpointSettings {
|
||||
endpoints_config: Some(hashmap! {
|
||||
network.into() => EndpointSettings {
|
||||
aliases: Some(vec![self.name().to_string()]),
|
||||
..Default::default()
|
||||
}
|
||||
},
|
||||
}),
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
|
|
@ -69,10 +70,7 @@ impl ServiceTrait for Oc {
|
|||
.await
|
||||
.into_diagnostic()?
|
||||
.id;
|
||||
docker
|
||||
.start_container::<String>(&id, None)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
docker.start_container(&id, None).await.into_diagnostic()?;
|
||||
Ok(vec![id])
|
||||
}
|
||||
|
||||
|
|
@ -85,7 +83,7 @@ impl ServiceTrait for Oc {
|
|||
docker: &Docker,
|
||||
cloud_id: &str,
|
||||
config: &HazeConfig,
|
||||
) -> Result<Vec<String>> {
|
||||
) -> Result<Vec<Vec<String>>> {
|
||||
if let Some(ip) = self.get_ips(docker, cloud_id).await?.next() {
|
||||
let container = self.container_name(cloud_id).unwrap();
|
||||
let addr = config.proxy.addr(&container, ip);
|
||||
|
|
|
|||
|
|
@ -3,8 +3,10 @@ use crate::config::HazeConfig;
|
|||
use crate::image::pull_image;
|
||||
use crate::service::ServiceTrait;
|
||||
use crate::Result;
|
||||
use bollard::container::{Config, CreateContainerOptions, NetworkingConfig};
|
||||
use bollard::models::{ContainerState, EndpointSettings, HostConfig};
|
||||
use bollard::models::{
|
||||
ContainerCreateBody, ContainerState, EndpointSettings, HostConfig, NetworkingConfig,
|
||||
};
|
||||
use bollard::query_parameters::CreateContainerOptions;
|
||||
use bollard::Docker;
|
||||
use maplit::hashmap;
|
||||
use miette::{IntoDiagnostic, Report};
|
||||
|
|
@ -30,48 +32,68 @@ impl ServiceTrait for Office {
|
|||
config: &HazeConfig,
|
||||
_options: &CloudOptions,
|
||||
) -> Result<Vec<String>> {
|
||||
let network_info = docker
|
||||
.inspect_network(network, None)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
let gateway = network_info
|
||||
.ipam
|
||||
.as_ref()
|
||||
.ok_or_else(|| Report::msg("Network has no ip info"))?
|
||||
.config
|
||||
.as_deref()
|
||||
.ok_or_else(|| Report::msg("Network has no ip info"))?
|
||||
.first()
|
||||
.ok_or_else(|| Report::msg("Network has no ip info"))?
|
||||
.gateway
|
||||
.as_deref()
|
||||
.ok_or_else(|| Report::msg("Network has no ip info"))?;
|
||||
|
||||
let image = "collabora/code";
|
||||
pull_image(docker, image).await?;
|
||||
let container_id = self.container_name(cloud_id).unwrap();
|
||||
let options = Some(CreateContainerOptions {
|
||||
name: container_id.clone(),
|
||||
name: Some(container_id.clone()),
|
||||
..CreateContainerOptions::default()
|
||||
});
|
||||
let mut env = vec!["extra_params=--o:ssl.enable=false --o:ssl.termination=true"];
|
||||
let mut env =
|
||||
vec![r#"extra_params=--o:ssl.enable=false --o:ssl.termination=true --o:net.frame_ancestors=*"#.to_string()];
|
||||
|
||||
let proxy_base = &config.proxy.address;
|
||||
let clean_id = container_id.strip_prefix("haze-").unwrap_or(&container_id);
|
||||
let server_name_opt = match (&config.proxy.address, config.proxy.https) {
|
||||
(public, true) if !public.is_empty() => {
|
||||
format!("server_name={clean_id}.{public}")
|
||||
}
|
||||
(public, false) if !public.is_empty() => {
|
||||
format!("server_name={clean_id}.{public}")
|
||||
}
|
||||
_ => "".to_string(),
|
||||
};
|
||||
|
||||
if !server_name_opt.is_empty() {
|
||||
env.push(&server_name_opt);
|
||||
if !proxy_base.is_empty() {
|
||||
env.push(format!("server_name={clean_id}.{}", config.proxy.address));
|
||||
}
|
||||
|
||||
let config = Config {
|
||||
image: Some(image),
|
||||
let clean_cloud_id = cloud_id.strip_prefix("haze-").unwrap_or(cloud_id);
|
||||
let hosts = if proxy_base.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
vec![
|
||||
format!("{proxy_base}:{gateway}"),
|
||||
format!("{clean_cloud_id}.{proxy_base}:{gateway}"),
|
||||
]
|
||||
};
|
||||
|
||||
let config = ContainerCreateBody {
|
||||
image: Some(image.into()),
|
||||
env: Some(env),
|
||||
host_config: Some(HostConfig {
|
||||
network_mode: Some(network.to_string()),
|
||||
extra_hosts: Some(hosts),
|
||||
..Default::default()
|
||||
}),
|
||||
labels: Some(hashmap! {
|
||||
"haze-type" => self.name(),
|
||||
"haze-cloud-id" => cloud_id
|
||||
"haze-type".into() => self.name().into(),
|
||||
"haze-cloud-id".into() => cloud_id.into(),
|
||||
}),
|
||||
networking_config: Some(NetworkingConfig {
|
||||
endpoints_config: hashmap! {
|
||||
network => EndpointSettings {
|
||||
endpoints_config: Some(hashmap! {
|
||||
network.into() => EndpointSettings {
|
||||
aliases: Some(vec![self.name().to_string()]),
|
||||
..Default::default()
|
||||
}
|
||||
},
|
||||
}),
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
|
|
@ -80,10 +102,7 @@ impl ServiceTrait for Office {
|
|||
.await
|
||||
.into_diagnostic()?
|
||||
.id;
|
||||
docker
|
||||
.start_container::<String>(&id, None)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
docker.start_container(&id, None).await.into_diagnostic()?;
|
||||
Ok(vec![id])
|
||||
}
|
||||
|
||||
|
|
@ -100,7 +119,7 @@ impl ServiceTrait for Office {
|
|||
docker: &Docker,
|
||||
cloud_id: &str,
|
||||
config: &HazeConfig,
|
||||
) -> Result<Vec<String>> {
|
||||
) -> Result<Vec<Vec<String>>> {
|
||||
let container = &self.container_name(cloud_id).unwrap();
|
||||
let info = docker
|
||||
.inspect_container(container, None)
|
||||
|
|
@ -128,19 +147,27 @@ impl ServiceTrait for Office {
|
|||
} else {
|
||||
return Err(Report::msg("office not started"));
|
||||
};
|
||||
let public = config
|
||||
.proxy
|
||||
.addr_with_port(container, ip, self.proxy_port());
|
||||
|
||||
Ok(vec![
|
||||
format!(
|
||||
r#"occ config:app:set richdocuments wopi_url --value="http://{}:9980""#,
|
||||
ip
|
||||
),
|
||||
format!(
|
||||
r#"occ config:app:set richdocuments public_wopi_url --value="{}""#,
|
||||
config.proxy.addr_with_port(container, ip, 9980)
|
||||
),
|
||||
format!(
|
||||
r#"occ config:app:set richdocuments wopi_root --value="http://{}""#,
|
||||
cloud_id
|
||||
),
|
||||
vec![
|
||||
"occ".into(),
|
||||
"config:app:set".into(),
|
||||
"richdocuments".into(),
|
||||
"public_wopi_url".into(),
|
||||
"--value".into(),
|
||||
public,
|
||||
],
|
||||
vec![
|
||||
"occ".into(),
|
||||
"richdocuments:setup".into(),
|
||||
"--wopi-url".into(),
|
||||
"http://office:9980".into(),
|
||||
"--callback-url".into(),
|
||||
"http://cloud".into(),
|
||||
],
|
||||
])
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,10 +2,12 @@ use crate::cloud::CloudOptions;
|
|||
use crate::config::HazeConfig;
|
||||
use crate::exec::exec;
|
||||
use crate::image::pull_image;
|
||||
use crate::service::ServiceTrait;
|
||||
use crate::service::{split_cmnd, ServiceTrait};
|
||||
use crate::Result;
|
||||
use bollard::container::{Config, CreateContainerOptions, NetworkingConfig};
|
||||
use bollard::models::{ContainerState, EndpointSettings, HostConfig};
|
||||
use bollard::models::{
|
||||
ContainerCreateBody, ContainerState, EndpointSettings, HostConfig, NetworkingConfig,
|
||||
};
|
||||
use bollard::query_parameters::CreateContainerOptions;
|
||||
use bollard::Docker;
|
||||
use maplit::hashmap;
|
||||
use miette::{IntoDiagnostic, Report};
|
||||
|
|
@ -35,26 +37,26 @@ impl ServiceTrait for OnlyOffice {
|
|||
let image = "onlyoffice/documentserver";
|
||||
pull_image(docker, image).await?;
|
||||
let options = Some(CreateContainerOptions {
|
||||
name: self.container_name(cloud_id).unwrap(),
|
||||
name: self.container_name(cloud_id),
|
||||
..CreateContainerOptions::default()
|
||||
});
|
||||
let config = Config {
|
||||
image: Some(image),
|
||||
let config = ContainerCreateBody {
|
||||
image: Some(image.into()),
|
||||
host_config: Some(HostConfig {
|
||||
network_mode: Some(network.to_string()),
|
||||
..Default::default()
|
||||
}),
|
||||
labels: Some(hashmap! {
|
||||
"haze-type" => self.name(),
|
||||
"haze-cloud-id" => cloud_id
|
||||
"haze-type".into() => self.name().into(),
|
||||
"haze-cloud-id".into() => cloud_id.into(),
|
||||
}),
|
||||
networking_config: Some(NetworkingConfig {
|
||||
endpoints_config: hashmap! {
|
||||
network => EndpointSettings {
|
||||
endpoints_config: Some(hashmap! {
|
||||
network.into() => EndpointSettings {
|
||||
aliases: Some(vec![self.name().to_string()]),
|
||||
..Default::default()
|
||||
}
|
||||
},
|
||||
}),
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
|
|
@ -63,10 +65,7 @@ impl ServiceTrait for OnlyOffice {
|
|||
.await
|
||||
.into_diagnostic()?
|
||||
.id;
|
||||
docker
|
||||
.start_container::<String>(&id, None)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
docker.start_container(&id, None).await.into_diagnostic()?;
|
||||
Ok(vec![id])
|
||||
}
|
||||
|
||||
|
|
@ -83,7 +82,7 @@ impl ServiceTrait for OnlyOffice {
|
|||
docker: &Docker,
|
||||
cloud_id: &str,
|
||||
config: &HazeConfig,
|
||||
) -> Result<Vec<String>> {
|
||||
) -> Result<Vec<Vec<String>>> {
|
||||
let info = docker
|
||||
.inspect_container(&self.container_name(cloud_id).unwrap(), None)
|
||||
.await
|
||||
|
|
@ -138,16 +137,44 @@ impl ServiceTrait for OnlyOffice {
|
|||
);
|
||||
|
||||
Ok(vec![
|
||||
format!("occ config:app:set onlyoffice DocumentServerUrl --value {addr}/"),
|
||||
format!("occ config:app:set onlyoffice jwt_secret --value {secret}"),
|
||||
"occ onlyoffice:documentserver --check".into(),
|
||||
vec![
|
||||
"occ".into(),
|
||||
"config:app:set".into(),
|
||||
"onlyoffice".into(),
|
||||
"DocumentServerUrl".into(),
|
||||
"--value".into(),
|
||||
addr,
|
||||
],
|
||||
vec![
|
||||
"occ".into(),
|
||||
"config:app:set".into(),
|
||||
"onlyoffice".into(),
|
||||
"jwt_secret".into(),
|
||||
"--value".into(),
|
||||
secret.into(),
|
||||
],
|
||||
split_cmnd("occ onlyoffice:documentserver --check"),
|
||||
])
|
||||
} else {
|
||||
Ok(vec![
|
||||
format!("occ config:app:set onlyoffice DocumentServerUrl --value https://{ip}/"),
|
||||
"occ config:app:set onlyoffice verify_peer_off --value true".into(),
|
||||
format!("occ config:app:set onlyoffice jwt_secret --value {secret}"),
|
||||
"occ onlyoffice:documentserver --check".into(),
|
||||
vec![
|
||||
"occ".into(),
|
||||
"config:app:set".into(),
|
||||
"onlyoffice".into(),
|
||||
"DocumentServerUrl".into(),
|
||||
"--value".into(),
|
||||
format!("https://{ip}/"),
|
||||
],
|
||||
split_cmnd("occ config:app:set onlyoffice verify_peer_off --value true"),
|
||||
vec![
|
||||
"occ".into(),
|
||||
"config:app:set".into(),
|
||||
"onlyoffice".into(),
|
||||
"jwt_secret".into(),
|
||||
"--value".into(),
|
||||
secret.into(),
|
||||
],
|
||||
split_cmnd("occ onlyoffice:documentserver --check"),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ use crate::cloud::CloudOptions;
|
|||
use crate::config::HazeConfig;
|
||||
use crate::image::pull_image;
|
||||
use crate::service::ServiceTrait;
|
||||
use bollard::container::{Config, CreateContainerOptions, NetworkingConfig};
|
||||
use bollard::models::{EndpointSettings, HostConfig};
|
||||
use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig};
|
||||
use bollard::query_parameters::CreateContainerOptions;
|
||||
use bollard::Docker;
|
||||
use local_ip_address::list_afinet_netifas;
|
||||
use maplit::hashmap;
|
||||
|
|
@ -33,11 +33,11 @@ impl ServiceTrait for NotifyPush {
|
|||
let image = "icewind1991/notify_push";
|
||||
pull_image(docker, image).await?;
|
||||
let options = Some(CreateContainerOptions {
|
||||
name: self.container_name(cloud_id).unwrap(),
|
||||
name: self.container_name(cloud_id),
|
||||
..CreateContainerOptions::default()
|
||||
});
|
||||
let config = Config {
|
||||
image: Some(image),
|
||||
let config = ContainerCreateBody {
|
||||
image: Some(image.into()),
|
||||
host_config: Some(HostConfig {
|
||||
network_mode: Some(network.to_string()),
|
||||
binds: Some(vec![
|
||||
|
|
@ -47,23 +47,23 @@ impl ServiceTrait for NotifyPush {
|
|||
..Default::default()
|
||||
}),
|
||||
env: Some(vec![
|
||||
"NEXTCLOUD_URL=http://cloud/",
|
||||
"LOG=debug",
|
||||
"REDIS_URL=redis://cloud/",
|
||||
"NEXTCLOUD_URL=http://cloud/".into(),
|
||||
"LOG=debug".into(),
|
||||
"REDIS_URL=redis://cloud/".into(),
|
||||
]),
|
||||
labels: Some(hashmap! {
|
||||
"haze-type" => self.name(),
|
||||
"haze-cloud-id" => cloud_id
|
||||
"haze-type".into() => self.name().into(),
|
||||
"haze-cloud-id".into() => cloud_id.into(),
|
||||
}),
|
||||
networking_config: Some(NetworkingConfig {
|
||||
endpoints_config: hashmap! {
|
||||
network => EndpointSettings {
|
||||
endpoints_config: Some(hashmap! {
|
||||
network.into() => EndpointSettings {
|
||||
aliases: Some(vec![self.name().to_string()]),
|
||||
..Default::default()
|
||||
}
|
||||
},
|
||||
}),
|
||||
}),
|
||||
cmd: Some(vec!["/notify_push", "/config/config.php"]),
|
||||
cmd: Some(vec!["/notify_push".into(), "/config/config.php".into()]),
|
||||
..Default::default()
|
||||
};
|
||||
let id = docker
|
||||
|
|
@ -87,7 +87,7 @@ impl ServiceTrait for NotifyPush {
|
|||
docker: &Docker,
|
||||
cloud_id: &str,
|
||||
config: &HazeConfig,
|
||||
) -> Result<Vec<String>> {
|
||||
) -> Result<Vec<Vec<String>>> {
|
||||
let mut ips: Vec<_> = self.get_ips(docker, cloud_id).await?.collect();
|
||||
if let Ok(local_interfaces) = list_afinet_netifas() {
|
||||
ips.extend(local_interfaces.into_iter().map(|(_, ip)| ip));
|
||||
|
|
@ -97,10 +97,14 @@ impl ServiceTrait for NotifyPush {
|
|||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, ip)| {
|
||||
format!(
|
||||
"occ config:system:set trusted_proxies {} --value {ip}",
|
||||
i + 1
|
||||
)
|
||||
vec![
|
||||
"occ".into(),
|
||||
"config:system:set".into(),
|
||||
"trusted_proxies".into(),
|
||||
(i + 1).to_string(),
|
||||
"--value".into(),
|
||||
ip.to_string(),
|
||||
]
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
|
@ -108,7 +112,7 @@ impl ServiceTrait for NotifyPush {
|
|||
config
|
||||
.proxy
|
||||
.addr_with_port(&self.container_name(cloud_id).unwrap(), ips[0], 7867);
|
||||
commands.push(format!("occ notify_push:setup {}", addr));
|
||||
commands.push(vec!["occ".into(), "notify_push:setup".into(), addr]);
|
||||
Ok(commands)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
use crate::cloud::CloudOptions;
|
||||
use crate::config::HazeConfig;
|
||||
use crate::image::pull_image;
|
||||
use crate::service::ServiceTrait;
|
||||
use crate::service::{split_cmnd, ServiceTrait};
|
||||
use crate::Result;
|
||||
use bollard::container::{Config, CreateContainerOptions, NetworkingConfig};
|
||||
use bollard::models::{EndpointSettings, HostConfig};
|
||||
use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig};
|
||||
use bollard::query_parameters::CreateContainerOptions;
|
||||
use bollard::Docker;
|
||||
use maplit::hashmap;
|
||||
use miette::IntoDiagnostic;
|
||||
|
|
@ -29,26 +29,26 @@ impl ServiceTrait for Redis {
|
|||
let image = "redis:8-alpine";
|
||||
pull_image(docker, image).await?;
|
||||
let options = Some(CreateContainerOptions {
|
||||
name: self.container_name(cloud_id).unwrap(),
|
||||
name: self.container_name(cloud_id),
|
||||
..CreateContainerOptions::default()
|
||||
});
|
||||
let config = Config {
|
||||
image: Some(image),
|
||||
let config = ContainerCreateBody {
|
||||
image: Some(image.into()),
|
||||
host_config: Some(HostConfig {
|
||||
network_mode: Some(network.to_string()),
|
||||
..Default::default()
|
||||
}),
|
||||
labels: Some(hashmap! {
|
||||
"haze-type" => self.name(),
|
||||
"haze-cloud-id" => cloud_id
|
||||
"haze-type".into() => self.name().into(),
|
||||
"haze-cloud-id".into() => cloud_id.into(),
|
||||
}),
|
||||
networking_config: Some(NetworkingConfig {
|
||||
endpoints_config: hashmap! {
|
||||
network => EndpointSettings {
|
||||
endpoints_config: Some(hashmap! {
|
||||
network.into() => EndpointSettings {
|
||||
aliases: Some(vec![self.name().to_string()]),
|
||||
..Default::default()
|
||||
}
|
||||
},
|
||||
}),
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
|
|
@ -57,10 +57,7 @@ impl ServiceTrait for Redis {
|
|||
.await
|
||||
.into_diagnostic()?
|
||||
.id;
|
||||
docker
|
||||
.start_container::<String>(&id, None)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
docker.start_container(&id, None).await.into_diagnostic()?;
|
||||
Ok(vec![id])
|
||||
}
|
||||
|
||||
|
|
@ -73,7 +70,9 @@ impl ServiceTrait for Redis {
|
|||
_docker: &Docker,
|
||||
_cloud_id: &str,
|
||||
_config: &HazeConfig,
|
||||
) -> Result<Vec<String>> {
|
||||
Ok(vec!["occ config:system:set redis host --value redis".into()])
|
||||
) -> Result<Vec<Vec<String>>> {
|
||||
Ok(vec![split_cmnd(
|
||||
"occ config:system:set redis host --value redis",
|
||||
)])
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
use crate::cloud::CloudOptions;
|
||||
use crate::config::HazeConfig;
|
||||
use crate::image::pull_image;
|
||||
use crate::service::ServiceTrait;
|
||||
use crate::service::{split_cmnd, ServiceTrait};
|
||||
use crate::Result;
|
||||
use bollard::container::{Config, CreateContainerOptions, NetworkingConfig};
|
||||
use bollard::models::{EndpointSettings, HostConfig};
|
||||
use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig};
|
||||
use bollard::query_parameters::CreateContainerOptions;
|
||||
use bollard::Docker;
|
||||
use maplit::hashmap;
|
||||
use miette::IntoDiagnostic;
|
||||
use miette::{Context, IntoDiagnostic};
|
||||
use std::fs::{create_dir_all, write};
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct Sftp;
|
||||
|
|
@ -29,28 +30,31 @@ impl ServiceTrait for Sftp {
|
|||
let image = "atmoz/sftp:alpine";
|
||||
pull_image(docker, image).await?;
|
||||
let options = Some(CreateContainerOptions {
|
||||
name: self.container_name(cloud_id).unwrap(),
|
||||
name: self.container_name(cloud_id),
|
||||
..CreateContainerOptions::default()
|
||||
});
|
||||
let config = Config {
|
||||
image: Some(image),
|
||||
let config = ContainerCreateBody {
|
||||
image: Some(image.into()),
|
||||
host_config: Some(HostConfig {
|
||||
network_mode: Some(network.to_string()),
|
||||
..Default::default()
|
||||
}),
|
||||
labels: Some(hashmap! {
|
||||
"haze-type" => self.name(),
|
||||
"haze-cloud-id" => cloud_id
|
||||
"haze-type".into() => self.name().into(),
|
||||
"haze-cloud-id".into() => cloud_id.into(),
|
||||
}),
|
||||
networking_config: Some(NetworkingConfig {
|
||||
endpoints_config: hashmap! {
|
||||
network => EndpointSettings {
|
||||
endpoints_config: Some(hashmap! {
|
||||
network.into() => EndpointSettings {
|
||||
aliases: Some(vec![self.name().to_string()]),
|
||||
..Default::default()
|
||||
}
|
||||
},
|
||||
}),
|
||||
}),
|
||||
cmd: Some(vec!["test:test:::data"]),
|
||||
cmd: Some(vec![
|
||||
"test:test:::data".into(),
|
||||
"ldaptest:test:::data".into(),
|
||||
]),
|
||||
..Default::default()
|
||||
};
|
||||
let id = docker
|
||||
|
|
@ -58,10 +62,7 @@ impl ServiceTrait for Sftp {
|
|||
.await
|
||||
.into_diagnostic()?
|
||||
.id;
|
||||
docker
|
||||
.start_container::<String>(&id, None)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
docker.start_container(&id, None).await.into_diagnostic()?;
|
||||
Ok(vec![id])
|
||||
}
|
||||
|
||||
|
|
@ -78,13 +79,119 @@ impl ServiceTrait for Sftp {
|
|||
_docker: &Docker,
|
||||
_cloud_id: &str,
|
||||
_config: &HazeConfig,
|
||||
) -> Result<Vec<String>> {
|
||||
) -> Result<Vec<Vec<String>>> {
|
||||
Ok(vec![
|
||||
"occ files_external:create sftp sftp password::password".into(),
|
||||
"occ files_external:config 1 host sftp".into(),
|
||||
"occ files_external:config 1 user test".into(),
|
||||
"occ files_external:config 1 root data".into(),
|
||||
"occ files_external:config 1 password test".into(),
|
||||
split_cmnd("occ files_external:create sftp sftp password::password"),
|
||||
split_cmnd("occ files_external:config 1 host sftp"),
|
||||
split_cmnd("occ files_external:config 1 user test"),
|
||||
split_cmnd("occ files_external:config 1 root data"),
|
||||
split_cmnd("occ files_external:config 1 password test"),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct SftpKey;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ServiceTrait for SftpKey {
|
||||
fn name(&self) -> &str {
|
||||
"sftp-key"
|
||||
}
|
||||
|
||||
async fn spawn(
|
||||
&self,
|
||||
docker: &Docker,
|
||||
cloud_id: &str,
|
||||
network: &str,
|
||||
config: &HazeConfig,
|
||||
_options: &CloudOptions,
|
||||
) -> Result<Vec<String>> {
|
||||
let image = "atmoz/sftp:alpine";
|
||||
pull_image(docker, image).await?;
|
||||
let options = Some(CreateContainerOptions {
|
||||
name: self.container_name(cloud_id),
|
||||
..CreateContainerOptions::default()
|
||||
});
|
||||
let key_dir = config.work_dir.join("certificates/sftp");
|
||||
create_dir_all(&key_dir)
|
||||
.into_diagnostic()
|
||||
.wrap_err("Failed to create sftp certificate directory")?;
|
||||
let private_path = key_dir.join("id_rsa");
|
||||
let public_path = key_dir.join("id_rsa.pub");
|
||||
let private_key = include_str!("../../certificates/sftp/id_rsa");
|
||||
let public_key = include_str!("../../certificates/sftp/id_rsa.pub");
|
||||
if !private_path.exists() {
|
||||
write(&private_path, private_key)
|
||||
.into_diagnostic()
|
||||
.wrap_err("Failed to write sftp client certificate")?;
|
||||
}
|
||||
if !public_path.exists() {
|
||||
write(&public_path, public_key)
|
||||
.into_diagnostic()
|
||||
.wrap_err("Failed to write sftp client key")?;
|
||||
}
|
||||
|
||||
let volumes = vec![format!("{public_path}:/home/test/.ssh/keys/id_rsa:ro")];
|
||||
|
||||
let config = ContainerCreateBody {
|
||||
image: Some(image.into()),
|
||||
host_config: Some(HostConfig {
|
||||
network_mode: Some(network.to_string()),
|
||||
binds: Some(volumes),
|
||||
..Default::default()
|
||||
}),
|
||||
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()
|
||||
}
|
||||
}),
|
||||
}),
|
||||
cmd: Some(vec!["test::::data".into()]),
|
||||
..Default::default()
|
||||
};
|
||||
let id = docker
|
||||
.create_container(options, config)
|
||||
.await
|
||||
.into_diagnostic()?
|
||||
.id;
|
||||
docker.start_container(&id, None).await.into_diagnostic()?;
|
||||
Ok(vec![id])
|
||||
}
|
||||
|
||||
fn container_name(&self, cloud_id: &str) -> Option<String> {
|
||||
Some(format!("{}-sftp-key", cloud_id))
|
||||
}
|
||||
|
||||
fn apps(&self) -> &'static [&'static str] {
|
||||
&["files_external"]
|
||||
}
|
||||
|
||||
async fn post_setup(
|
||||
&self,
|
||||
_docker: &Docker,
|
||||
_cloud_id: &str,
|
||||
_config: &HazeConfig,
|
||||
) -> Result<Vec<Vec<String>>> {
|
||||
Ok(vec![
|
||||
split_cmnd("occ files_external:create sftp sftp publickey::rsa_private"),
|
||||
split_cmnd("occ files_external:config 1 host sftp-key"),
|
||||
split_cmnd("occ files_external:config 1 user test"),
|
||||
split_cmnd("occ files_external:config 1 root data"),
|
||||
vec![
|
||||
"occ".into(),
|
||||
"files_external:config".into(),
|
||||
"--value-from-file".into(),
|
||||
"1".into(),
|
||||
"private_key".into(),
|
||||
"/certificates/sftp/id_rsa".into(),
|
||||
],
|
||||
])
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
use crate::cloud::CloudOptions;
|
||||
use crate::config::HazeConfig;
|
||||
use crate::image::pull_image;
|
||||
use crate::service::ServiceTrait;
|
||||
use crate::service::{split_cmnd, ServiceTrait};
|
||||
use crate::Result;
|
||||
use bollard::container::{Config, CreateContainerOptions, NetworkingConfig};
|
||||
use bollard::models::{EndpointSettings, HostConfig};
|
||||
use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig};
|
||||
use bollard::query_parameters::CreateContainerOptions;
|
||||
use bollard::Docker;
|
||||
use maplit::hashmap;
|
||||
use miette::IntoDiagnostic;
|
||||
|
|
@ -29,31 +29,33 @@ impl ServiceTrait for Smb {
|
|||
let image = "ghcr.io/servercontainers/samba:smbd-only-a3.18.0-s4.18.2-r0";
|
||||
pull_image(docker, image).await?;
|
||||
let options = Some(CreateContainerOptions {
|
||||
name: self.container_name(cloud_id).unwrap(),
|
||||
name: self.container_name(cloud_id),
|
||||
..CreateContainerOptions::default()
|
||||
});
|
||||
let config = Config {
|
||||
image: Some(image),
|
||||
let config = ContainerCreateBody {
|
||||
image: Some(image.into()),
|
||||
host_config: Some(HostConfig {
|
||||
network_mode: Some(network.to_string()),
|
||||
..Default::default()
|
||||
}),
|
||||
env: Some(vec![
|
||||
"ACCOUNT_test=test",
|
||||
"UID_test=1000",
|
||||
"SAMBA_VOLUME_CONFIG_test=[test]; path=/tmp; valid users = test; guest ok = no; read only = no; browseable = yes",
|
||||
"ACCOUNT_test=test".into(),
|
||||
"ACCOUNT_ldaptest=test".into(),
|
||||
"UID_test=1000".into(),
|
||||
"SAMBA_VOLUME_CONFIG_test=[test]; path=/tmp; valid users = test; guest ok = no; read only = no; browseable = yes".into(),
|
||||
"SAMBA_VOLUME_CONFIG_ldaptest=[ldaptest]; path=/tmp; valid users = ldaptest; guest ok = no; read only = no; browseable = yes".into(),
|
||||
]),
|
||||
labels: Some(hashmap! {
|
||||
"haze-type" => self.name(),
|
||||
"haze-cloud-id" => cloud_id
|
||||
"haze-type".into() => self.name().into(),
|
||||
"haze-cloud-id".into() => cloud_id.into(),
|
||||
}),
|
||||
networking_config: Some(NetworkingConfig {
|
||||
endpoints_config: hashmap! {
|
||||
network => EndpointSettings {
|
||||
endpoints_config: Some(hashmap! {
|
||||
network.into() => EndpointSettings {
|
||||
aliases: Some(vec![self.name().to_string()]),
|
||||
..Default::default()
|
||||
}
|
||||
},
|
||||
}),
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
|
|
@ -62,10 +64,7 @@ impl ServiceTrait for Smb {
|
|||
.await
|
||||
.into_diagnostic()?
|
||||
.id;
|
||||
docker
|
||||
.start_container::<String>(&id, None)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
docker.start_container(&id, None).await.into_diagnostic()?;
|
||||
Ok(vec![id])
|
||||
}
|
||||
|
||||
|
|
@ -82,13 +81,13 @@ impl ServiceTrait for Smb {
|
|||
_docker: &Docker,
|
||||
_cloud_id: &str,
|
||||
_config: &HazeConfig,
|
||||
) -> Result<Vec<String>> {
|
||||
) -> Result<Vec<Vec<String>>> {
|
||||
Ok(vec![
|
||||
"occ files_external:create smb smb password::password".into(),
|
||||
"occ files_external:config 1 host smb".into(),
|
||||
"occ files_external:config 1 user test".into(),
|
||||
"occ files_external:config 1 password test".into(),
|
||||
"occ files_external:config 1 share test".into(),
|
||||
split_cmnd("occ files_external:create smb smb password::password"),
|
||||
split_cmnd("occ files_external:config 1 host smb"),
|
||||
split_cmnd("occ files_external:config 1 user test"),
|
||||
split_cmnd("occ files_external:config 1 password test"),
|
||||
split_cmnd("occ files_external:config 1 share test"),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
|
|
|||
71
src/service/webhook.rs
Normal file
71
src/service/webhook.rs
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
use crate::cloud::CloudOptions;
|
||||
use crate::config::HazeConfig;
|
||||
use crate::image::pull_image;
|
||||
use crate::service::ServiceTrait;
|
||||
use crate::Result;
|
||||
use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig};
|
||||
use bollard::query_parameters::CreateContainerOptions;
|
||||
use bollard::Docker;
|
||||
use maplit::hashmap;
|
||||
use miette::IntoDiagnostic;
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct Webhook;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ServiceTrait for Webhook {
|
||||
fn name(&self) -> &str {
|
||||
"webhook"
|
||||
}
|
||||
|
||||
async fn spawn(
|
||||
&self,
|
||||
docker: &Docker,
|
||||
cloud_id: &str,
|
||||
network: &str,
|
||||
_config: &HazeConfig,
|
||||
_options: &CloudOptions,
|
||||
) -> Result<Vec<String>> {
|
||||
let image = "ghcr.io/tarampampam/webhook-tester";
|
||||
pull_image(docker, image).await?;
|
||||
let options = Some(CreateContainerOptions {
|
||||
name: self.container_name(cloud_id),
|
||||
..CreateContainerOptions::default()
|
||||
});
|
||||
let config = ContainerCreateBody {
|
||||
image: Some(image.into()),
|
||||
host_config: Some(HostConfig {
|
||||
network_mode: Some(network.to_string()),
|
||||
..Default::default()
|
||||
}),
|
||||
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(options, config)
|
||||
.await
|
||||
.into_diagnostic()?
|
||||
.id;
|
||||
docker.start_container(&id, None).await.into_diagnostic()?;
|
||||
Ok(vec![id])
|
||||
}
|
||||
|
||||
fn container_name(&self, cloud_id: &str) -> Option<String> {
|
||||
Some(format!("{}-webhook", cloud_id))
|
||||
}
|
||||
|
||||
fn proxy_port(&self) -> u16 {
|
||||
8080
|
||||
}
|
||||
}
|
||||
124
src/sources.rs
124
src/sources.rs
|
|
@ -1,6 +1,17 @@
|
|||
use crate::config::HazeConfig;
|
||||
use camino::Utf8PathBuf;
|
||||
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
|
||||
use miette::{Context, IntoDiagnostic, Report, Result};
|
||||
use reqwest::header::HeaderName;
|
||||
use reqwest::{Client, IntoUrl, Response};
|
||||
use sha2::{Digest, Sha512};
|
||||
use std::fs::read_to_string;
|
||||
use std::io::Cursor;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::Duration;
|
||||
use tokio::fs::create_dir_all;
|
||||
use zip::read::root_dir_common_filter;
|
||||
use zip::ZipArchive;
|
||||
|
||||
pub struct Sources {
|
||||
#[allow(dead_code)]
|
||||
|
|
@ -49,3 +60,116 @@ impl Sources {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn download_nc(config: &HazeConfig, version: &str) -> Result<Utf8PathBuf> {
|
||||
if !version.chars().all(|c| c.is_ascii_digit() || c == '.') {
|
||||
return Err(Report::msg(format!("Invalid version: {version}")));
|
||||
}
|
||||
let root = config.work_dir.join("sources");
|
||||
create_dir_all(&root)
|
||||
.await
|
||||
.into_diagnostic()
|
||||
.wrap_err("failed to create parent directory for sources")?;
|
||||
let dest = root.join(version);
|
||||
if !dest.exists() {
|
||||
let progress = MultiProgress::new();
|
||||
let download_style = ProgressStyle::with_template("{spinner:.green} {msg} [{elapsed_precise}] [{bar:.cyan/blue}] {bytes}/{total_bytes} ({bytes_per_sec}, {eta})")
|
||||
.unwrap();
|
||||
let download_bar = ProgressBar::new(0)
|
||||
.with_message("Downloading")
|
||||
.with_style(download_style.clone());
|
||||
let download_bar = progress.add(download_bar);
|
||||
|
||||
let archive = download_url(
|
||||
format!("https://download.nextcloud.com/server/releases/nextcloud-{version}.zip"),
|
||||
|size| {
|
||||
download_bar.set_length(size);
|
||||
},
|
||||
|count| {
|
||||
download_bar.inc(count);
|
||||
},
|
||||
)
|
||||
.await
|
||||
.wrap_err_with(|| format!("Failed to download archive for {}", version))?;
|
||||
download_bar.finish();
|
||||
|
||||
let expected_hash = download_text(format!(
|
||||
"https://download.nextcloud.com/server/releases/nextcloud-{version}.zip.sha512"
|
||||
))
|
||||
.await
|
||||
.wrap_err_with(|| format!("Failed to download hash for {}", version))?;
|
||||
let expected_hash = expected_hash
|
||||
.split_once(' ')
|
||||
.map(|(hash, _)| hash)
|
||||
.unwrap_or(expected_hash.trim());
|
||||
|
||||
let hash_bar = ProgressBar::new(download_bar.length().unwrap_or_default())
|
||||
.with_message("Validating")
|
||||
.with_style(download_style.clone());
|
||||
let hash_bar = progress.add(hash_bar);
|
||||
let mut hasher = Sha512::new();
|
||||
for chunk in archive.chunks(1014 * 1024) {
|
||||
hash_bar.inc(chunk.len() as u64);
|
||||
hasher.update(chunk);
|
||||
}
|
||||
let hash = hasher.finalize();
|
||||
|
||||
let hash = base16ct::lower::encode_string(&hash);
|
||||
if expected_hash != hash {
|
||||
return Err(Report::msg(format!(
|
||||
"Invalid hash for downloaded: {version}, expected {expected_hash} but got {hash}"
|
||||
)));
|
||||
}
|
||||
hash_bar.finish();
|
||||
|
||||
let extract_bar = ProgressBar::new_spinner().with_message("Extracting");
|
||||
extract_bar.enable_steady_tick(Duration::from_millis(100));
|
||||
let extract_bar = progress.add(extract_bar);
|
||||
let mut archive = ZipArchive::new(Cursor::new(archive)).into_diagnostic()?;
|
||||
archive
|
||||
.extract_unwrapped_root_dir(&dest, root_dir_common_filter)
|
||||
.into_diagnostic()
|
||||
.wrap_err("Failed to extract archive")?;
|
||||
extract_bar.finish();
|
||||
}
|
||||
Ok(dest)
|
||||
}
|
||||
|
||||
async fn download_url<U: IntoUrl, SizeFN: FnOnce(u64), ProgressFN: Fn(u64)>(
|
||||
url: U,
|
||||
size: SizeFN,
|
||||
progress: ProgressFN,
|
||||
) -> Result<Vec<u8>> {
|
||||
let mut res = get_url(url).await?.error_for_status().into_diagnostic()?;
|
||||
let mut buff = Vec::new();
|
||||
|
||||
size(res.content_length().unwrap_or_default());
|
||||
while let Some(chunk) = res.chunk().await.into_diagnostic()? {
|
||||
progress(chunk.len() as u64);
|
||||
buff.extend(chunk);
|
||||
}
|
||||
Ok(buff)
|
||||
}
|
||||
async fn download_text<U: IntoUrl>(url: U) -> Result<String> {
|
||||
get_url(url)
|
||||
.await?
|
||||
.error_for_status()
|
||||
.into_diagnostic()?
|
||||
.text()
|
||||
.await
|
||||
.into_diagnostic()
|
||||
}
|
||||
|
||||
async fn get_url<U: IntoUrl>(url: U) -> Result<Response> {
|
||||
Client::builder()
|
||||
.build()
|
||||
.into_diagnostic()?
|
||||
.get(url)
|
||||
.header(
|
||||
HeaderName::from_static("user-agent"),
|
||||
format!("haze {}", env!("CARGO_PKG_VERSION")),
|
||||
)
|
||||
.send()
|
||||
.await
|
||||
.into_diagnostic()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue