Compare commits
329 Commits
fb2a9f39fb
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 71d298ea68 | |||
| 836f6f2494 | |||
| 6c306407e9 | |||
| 0786b77967 | |||
| 6bf4337b73 | |||
| 157b174554 | |||
| 72e4f02d2d | |||
| ccdb1a5767 | |||
| 4758dc2108 | |||
| c9ba4ba870 | |||
| 2aa5318288 | |||
| d67add7acb | |||
| 89922318d1 | |||
| b2e7825276 | |||
| ca024de5a3 | |||
| 83aaab4538 | |||
| a96a82f323 | |||
| c53520a99d | |||
| f2e1a838ef | |||
| 36b7597126 | |||
| f0acbed60a | |||
| 6b9a6edb75 | |||
| 4f2833c0b2 | |||
| a1abf8b31f | |||
| c66efe3e8e | |||
| 8f072d150c | |||
| 472f7e4035 | |||
| 146ee613e3 | |||
| 51c3f331f3 | |||
| 3da6618b01 | |||
| fc85ca165a | |||
| 23cd317c00 | |||
| ed629641f3 | |||
| e511380996 | |||
| e21bad2c83 | |||
| ee3e7168dc | |||
| 10bbb2bef0 | |||
| 2fcea62a5b | |||
| 642a439bfb | |||
| 87f524c074 | |||
| a38dc9b813 | |||
| 23be47cf17 | |||
| 61637f6962 | |||
| b7908eef4b | |||
| f9d5b55353 | |||
| 679af0cfd8 | |||
| 6c1d80076a | |||
| 867e39a1f5 | |||
| aee78677b5 | |||
| ce063e93f0 | |||
| 44709504e9 | |||
| 050e80b803 | |||
| b3a4b7f3dd | |||
| f93bbfcc1e | |||
| 0c73e3af05 | |||
| 56823c0338 | |||
| 297aa8b658 | |||
| cf3e9349e0 | |||
| 7c69feb25e | |||
| 3d7db5a1be | |||
| 76e7bc8c46 | |||
| c0b4154e67 | |||
| 260089b2ad | |||
| 7b8b0cff3e | |||
| 55f8df39bb | |||
| 3819f1615e | |||
| 3f82e21851 | |||
| 9a593142d1 | |||
| 24fd439284 | |||
| f199a27d89 | |||
| b0d6705660 | |||
| ab996ac653 | |||
| 95617f62de | |||
| 53498a0559 | |||
| acc0a93525 | |||
| c7793fb612 | |||
| 730a10de85 | |||
| 31fe40adc0 | |||
| 9af64c541c | |||
| 354a2e172d | |||
| bdbc84c106 | |||
| dda7f18f6c | |||
| f4eeeaf40e | |||
| 80d7c19d85 | |||
| 924e0bef4d | |||
| bf8a9e2b1c | |||
| a73665a4be | |||
| 22ff100ff0 | |||
| 1ffe9c3d32 | |||
| 8bd30b06dd | |||
| 109b1c3612 | |||
| 86a0281968 | |||
| 0371f8786b | |||
| e19eef70ff | |||
| b83bb64198 | |||
| b690342754 | |||
| 4660f91ce5 | |||
| ee5a31021c | |||
| 8d8942d360 | |||
| eb4eda83c8 | |||
| 6f8949d7df | |||
| f97c552508 | |||
| 51dff7010d | |||
| 952ec0a90c | |||
| d428170479 | |||
| ba295c0da8 | |||
| 05e5f4d015 | |||
| 29f45f9b6f | |||
| d8ba84bed4 | |||
| 40c3f44244 | |||
| 3a80bc9843 | |||
| 02f78f89d3 | |||
| d8f6f4c0b9 | |||
| 7a8123fb5d | |||
| 78c4c6a99c | |||
| 8cbd717db4 | |||
| ccacbcadc4 | |||
| 974fbe4518 | |||
| fc307104e9 | |||
| 8b9af80441 | |||
| 8eb0bae828 | |||
| fd679e2f20 | |||
| d2f5a716b3 | |||
| 746bd36363 | |||
| 8b08692a4a | |||
| d3142d69a6 | |||
| 27627f4a11 | |||
| 362d7ba047 | |||
| 08f33fbbfe | |||
| e2e52f9c02 | |||
| d761cc646b | |||
| e55c0a42cb | |||
| 1e825c7239 | |||
| a7e2183574 | |||
| 5ffb8a3a9d | |||
| c70d85e0f8 | |||
| c173e028e6 | |||
| 14c332faec | |||
| 356fca6f1f | |||
| 152aa3622e | |||
| 92e270d6c3 | |||
| 45eb6d6f99 | |||
| 30874f51a4 | |||
| e8d891d919 | |||
| b3c3b1aa1c | |||
| f655aa6ee7 | |||
| 3af17a9d1d | |||
| c29d860975 | |||
| 817fb8e0bd | |||
| 7472253933 | |||
| 6c5f995ef0 | |||
| a7dd1ef5e3 | |||
| 82e3bf90b5 | |||
| 5b4036a6ba | |||
| be307810b2 | |||
| eef4a7fc2b | |||
| d5d641cdbe | |||
| 5a81558610 | |||
| d0c5904eec | |||
| 254c4a063e | |||
| b2c62b3d7f | |||
| e68771e901 | |||
| b8eaa7cc7a | |||
| 054eb8125c | |||
| 9063c6e525 | |||
| 26092a8f65 | |||
| 3d69456740 | |||
| 0cf5afea07 | |||
| ade8ac2877 | |||
| 3c1a452f75 | |||
| f2df26d847 | |||
| 15de023b41 | |||
| a0864eeddb | |||
| 8f5b707854 | |||
| 07c271d396 | |||
| f3483766c6 | |||
| 2bcf138c8b | |||
| 2af17dcd9c | |||
| 2c3eaacd0f | |||
| 4a47c1dfed | |||
| 310d4e0555 | |||
| 20403c7272 | |||
| 7c431f04b8 | |||
| f9e4dcf758 | |||
| a971edee38 | |||
| cced881732 | |||
| 982c7dd855 | |||
| 91bd0fba37 | |||
| 164121f50d | |||
| 47e7411b49 | |||
| ba5de7ccf8 | |||
| 46537a8c4f | |||
| f896d8e699 | |||
| 974933e9a0 | |||
| 9986f39bd7 | |||
| ff123154f3 | |||
| 0d83f1fa5a | |||
| 40c3687c6e | |||
| 2913a8e443 | |||
| 14fb313b8b | |||
| 79fd2dd914 | |||
| aaf36359a9 | |||
| b5c61db6e8 | |||
| 0bde06c7f2 | |||
| b3d32f29ef | |||
| 7d7dc3edfc | |||
| 0a31daf763 | |||
| 6f105a1a5b | |||
| fedcf7ad9d | |||
| 2cb04bb0a0 | |||
| 6ba76a7830 | |||
| bd83ebf148 | |||
| 9fa73a8a02 | |||
| 162ff44d5a | |||
| 8a9901f03d | |||
| 290d2963b2 | |||
| 391420e417 | |||
| 3f93598156 | |||
| 98d891d3b4 | |||
| 8e97bdbe0e | |||
| de05180ba2 | |||
| 5fbbe548cf | |||
| 1cf40a0b94 | |||
| 5c5bb26deb | |||
| 58b02237c2 | |||
| ae3dec359b | |||
| 5891f44efb | |||
| c3e2e8cde6 | |||
| 4cc7df1031 | |||
| a143034689 | |||
| dfc1e83c50 | |||
| e7694c1bda | |||
| f0d0e3ec70 | |||
| 9af0dd87fd | |||
| 6dea641f6a | |||
| 5119552186 | |||
| 1c8287e04b | |||
| 31f660da3f | |||
| e1a7b42807 | |||
| 76584ef475 | |||
| 2252dfaf37 | |||
| d033e75782 | |||
| fff38115c4 | |||
| df0cc997cf | |||
| baf2964a09 | |||
| 7300def6dc | |||
| b8a102bd0b | |||
| a39f2139df | |||
| 5eff31c336 | |||
| 0eaaf6d577 | |||
| a02f8e4dea | |||
| 90356554a8 | |||
| e9f5ecddd5 | |||
| 05cb9ad33d | |||
| dfa8c1ccb6 | |||
| e6bd7ac854 | |||
| d347a48c2a | |||
| a0fb764842 | |||
| 7b25a9cc96 | |||
| 270d96389d | |||
| 0569d41777 | |||
| 894e4e8cde | |||
| af2ab44c30 | |||
| 20c23ae947 | |||
| ab8bffdb1f | |||
| 5312cbf6e7 | |||
| 1ebcc23ba2 | |||
| 613679c604 | |||
| bcd25dbc3b | |||
| e51828d6e3 | |||
| 6efd8d2e08 | |||
| c0bd23caa2 | |||
| d23507e6ba | |||
| 0d632f654b | |||
| 01072fb86c | |||
| a471a37bbd | |||
| 31c35f6ced | |||
| 40085304d0 | |||
| 01f11edd6b | |||
| d849d8c367 | |||
| af2a2d78db | |||
| 92b6bd3f7c | |||
| a229688408 | |||
| 6687d863fc | |||
| d1063e9d29 | |||
| 1c7735519e | |||
| 658ea43cf3 | |||
| 2a41955e80 | |||
| 67fd1604e8 | |||
| 5a08b0ea4c | |||
| 2f55ecc44b | |||
| 2308dc63ab | |||
| 673f7c865b | |||
| 9c8c14fc95 | |||
| 2bd6f27d21 | |||
| 25a8107d1d | |||
| e166edfe23 | |||
| adf077d437 | |||
| 751b96da65 | |||
| d5d1510ef2 | |||
| 9145d48d69 | |||
| b2817f0850 | |||
| 73eeacf61b | |||
| 6f2863b4ae | |||
| d6299ef26b | |||
| aa08b693d3 | |||
| 8d7ade0764 | |||
| 44fe28c2fc | |||
| dc992c00d4 | |||
| baf36a1418 | |||
| e2075cbdf0 | |||
| 1b2db8732b | |||
| 4342602ea7 | |||
| 54bf41da81 | |||
| 706f768c72 | |||
| b9b359ac12 | |||
| 8479144846 | |||
| 9e3e26b7b2 | |||
| 37193cfcd5 | |||
| b77b15fad2 | |||
| b85974c9cc | |||
| fc3a865c9b | |||
| f250395c66 | |||
| 5655295720 | |||
| 279c2befff | |||
| a1ba3c8868 | |||
| 921c70a785 | |||
| 0969d6dcd9 | |||
| e2598d7509 |
67
.drone.yml
Normal file
67
.drone.yml
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: default
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: fetch_dependencies
|
||||||
|
image: rust
|
||||||
|
volumes:
|
||||||
|
- name: rust_registry
|
||||||
|
path: /usr/local/cargo/registry
|
||||||
|
commands:
|
||||||
|
- cargo fetch
|
||||||
|
|
||||||
|
- name: code_quality
|
||||||
|
image: rust
|
||||||
|
volumes:
|
||||||
|
- name: rust_registry
|
||||||
|
path: /usr/local/cargo/registry
|
||||||
|
depends_on:
|
||||||
|
- fetch_dependencies
|
||||||
|
commands:
|
||||||
|
- rustup component add clippy
|
||||||
|
- cargo clippy -- -D warnings
|
||||||
|
|
||||||
|
- name: test
|
||||||
|
image: rust
|
||||||
|
depends_on:
|
||||||
|
- code_quality
|
||||||
|
volumes:
|
||||||
|
- name: rust_registry
|
||||||
|
path: /usr/local/cargo/registry
|
||||||
|
commands:
|
||||||
|
- wget -O /usr/bin/minio https://dl.min.io/server/minio/release/linux-amd64/minio
|
||||||
|
- wget -O /usr/bin/mc https://dl.min.io/client/mc/release/linux-amd64/mc
|
||||||
|
- chmod +x /usr/bin/minio /usr/bin/mc
|
||||||
|
- cargo test
|
||||||
|
|
||||||
|
- name: build_doc
|
||||||
|
image: python
|
||||||
|
environment:
|
||||||
|
AWS_ACCESS_KEY_ID:
|
||||||
|
from_secret: AWS_ACCESS_KEY_ID
|
||||||
|
AWS_SECRET_ACCESS_KEY:
|
||||||
|
from_secret: AWS_SECRET_ACCESS_KEY
|
||||||
|
AWS_DEFAULT_REGION: us-east-1
|
||||||
|
when:
|
||||||
|
branch:
|
||||||
|
- master
|
||||||
|
event:
|
||||||
|
exclude:
|
||||||
|
- pull_request
|
||||||
|
commands:
|
||||||
|
# Build website
|
||||||
|
- pip install mkdocs-material
|
||||||
|
- mkdocs build --site-dir public
|
||||||
|
# Install AWS
|
||||||
|
- curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
|
||||||
|
- unzip awscliv2.zip
|
||||||
|
- ./aws/install
|
||||||
|
- aws configure set default.s3.signature_version s3v4
|
||||||
|
# Upload to bucket
|
||||||
|
- cd public && aws --endpoint-url https://s3.communiquons.org s3 sync . s3://miniok8sbucketsoperator-website
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- name: rust_registry
|
||||||
|
temp: {}
|
||||||
2129
Cargo.lock
generated
2129
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
28
Cargo.toml
28
Cargo.toml
@@ -6,17 +6,17 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
log = "0.4.17"
|
log = "0.4.28"
|
||||||
env_logger = "0.10.0"
|
env_logger = "0.11.8"
|
||||||
anyhow = "1.0.71"
|
anyhow = "1.0.100"
|
||||||
serde = { version = "1.0.162", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
serde_json = "1.0.96"
|
serde_json = "1.0.145"
|
||||||
schemars = "0.8.12"
|
schemars = "1.1.0"
|
||||||
tokio = { version = "1.28.0", features = ["full"] }
|
tokio = { version = "1.48.0", features = ["full"] }
|
||||||
kube = { version = "0.82.2", features = ["runtime", "derive"] }
|
kube = { version = "2.0.1", features = ["runtime", "derive"] }
|
||||||
k8s-openapi = { version = "0.18.0", features = ["v1_26"] } # TODO : switch to v1_27
|
k8s-openapi = { version = "0.26.0", features = ["v1_31"] }
|
||||||
futures = "0.3.28"
|
futures = "0.3.31"
|
||||||
thiserror = "1.0.40"
|
thiserror = "2.0.17"
|
||||||
rand = "0.8.5"
|
rand = "0.9.2"
|
||||||
mktemp = "0.5.0"
|
mktemp = "0.5.1"
|
||||||
reqwest = "0.11.17"
|
reqwest = "0.12.24"
|
||||||
|
|||||||
10
Dockerfile
Normal file
10
Dockerfile
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
FROM debian:bookworm-slim
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
libssl3 \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
COPY minio-operator /usr/local/bin/minio-operator
|
||||||
|
COPY mc /usr/local/bin/mc
|
||||||
|
|
||||||
|
ENTRYPOINT ["/usr/local/bin/minio-operator"]
|
||||||
13
README.md
13
README.md
@@ -1,13 +1,6 @@
|
|||||||
# MinioK8sBuckets
|
# MinioK8sBuckets
|
||||||
|
[](https://drone.communiquons.org/pierre/MinioK8sBuckets)
|
||||||
|
|
||||||
Automatically create Minio buckets based on K8S CRD.
|
Automatically create Minio buckets based on K8S Custom Resources.
|
||||||
|
|
||||||
WIP, early project
|
See the [docs](docs) to learn more.
|
||||||
|
|
||||||
Apply all K8s config files manually:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cat yaml/*.yaml | kubectl apply -f -
|
|
||||||
```
|
|
||||||
|
|
||||||
Note : [mc tool](https://min.io/download) is required
|
|
||||||
14
build_docker_image.sh
Executable file
14
build_docker_image.sh
Executable file
@@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
cargo build --release
|
||||||
|
|
||||||
|
TEMP_DIR=$(mktemp -d)
|
||||||
|
cp target/release/minio-operator "$TEMP_DIR"
|
||||||
|
|
||||||
|
# Download mc
|
||||||
|
wget -O "$TEMP_DIR/mc" https://dl.min.io/client/mc/release/linux-amd64/mc
|
||||||
|
chmod +x "$TEMP_DIR/mc"
|
||||||
|
|
||||||
|
docker build -f Dockerfile "$TEMP_DIR" -t pierre42100/minio_operator
|
||||||
|
|
||||||
|
rm -r $TEMP_DIR
|
||||||
|
|
||||||
109
docs/DevelopmentSetup.md
Normal file
109
docs/DevelopmentSetup.md
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
# Setup for development
|
||||||
|
This guide will present you how to prepare your computer to update features of MinioK8SBucket
|
||||||
|
|
||||||
|
|
||||||
|
## Install Rust
|
||||||
|
As this project has been written using Rust, you will need to install it prior working on MinioK8SBucket. Please follow the official instructions: [https://www.rust-lang.org/tools/install](https://www.rust-lang.org/tools/install)
|
||||||
|
|
||||||
|
## Install Minikube
|
||||||
|
First, you need to install Minikube on your computer to have a K8S environment. In order to do this, please follow the official instructions: [https://minikube.sigs.k8s.io/docs/start](https://minikube.sigs.k8s.io/docs/start)
|
||||||
|
|
||||||
|
|
||||||
|
## Start Minikube
|
||||||
|
You will then need to start Minikube using the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
minikube start
|
||||||
|
```
|
||||||
|
|
||||||
|
You can then make sure that Minikube is working properly:
|
||||||
|
|
||||||
|
```
|
||||||
|
minikube kubectl get nodes
|
||||||
|
```
|
||||||
|
|
||||||
|
You should get a response similar to this one:
|
||||||
|
|
||||||
|
```
|
||||||
|
NAME STATUS ROLES AGE VERSION
|
||||||
|
minikube Ready control-plane 2m16s v1.32.0
|
||||||
|
```
|
||||||
|
|
||||||
|
## Clone repository
|
||||||
|
Clone this repository using:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
https://gitea.communiquons.org/pierre/MinioK8sBuckets
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! note "Gitea account request"
|
||||||
|
If you want to get a Gitea account to make pull request on this repository, you will need to contact me at: `pierre.git@communiquons.org`
|
||||||
|
|
||||||
|
## Deploy Minio
|
||||||
|
First, enable Minikube tunnel:
|
||||||
|
```bash
|
||||||
|
minikube tunnel --bind-address '127.0.0.1'
|
||||||
|
```
|
||||||
|
|
||||||
|
You will then need to deploy Minio in Minikube. Apply the Minio deployment located at the in MinioK8SBucket repository:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
minikube kubectl -- apply -f yaml/minio-dev-deployment.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
Wait for the pod to become ready:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
minikube kubectl -- get pods -w
|
||||||
|
```
|
||||||
|
|
||||||
|
Check for the availability of the service that expose Minio to your host computer:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
minikube kubectl -- get services
|
||||||
|
```
|
||||||
|
|
||||||
|
You should get a result similar to this one:
|
||||||
|
|
||||||
|
```
|
||||||
|
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||||
|
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 31m
|
||||||
|
minio LoadBalancer 10.103.82.87 127.0.0.1 9000:30656/TCP,9090:31369/TCP 6m40s
|
||||||
|
```
|
||||||
|
|
||||||
|
You should be able to access minio at the following address: [http://127.0.0.1:9090](http://127.0.0.1:9090/)
|
||||||
|
|
||||||
|
Minio API should be available at: [http://127.0.0.1:9000/](http://127.0.0.1:9000/)
|
||||||
|
|
||||||
|
## Deploy CRD
|
||||||
|
You will need then to deploy the Custom Resource Definitions of MinioK8SBucket using the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
minikube kubectl -- apply -f yaml/crd.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Run operator
|
||||||
|
You can then run the project using the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo fmt && cargo clippy && RUST_LOG=debug cargo run --
|
||||||
|
```
|
||||||
|
|
||||||
|
## Create a first bucket
|
||||||
|
You should be able to create a first bucket using the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
minikube kubectl -- apply -f test/test-outside-cluster.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
The bucket should then appear in buckets list:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
minikube kubectl -- get buckets
|
||||||
|
```
|
||||||
|
```
|
||||||
|
NAME AGE
|
||||||
|
first-bucket 8m43s
|
||||||
|
```
|
||||||
|
|
||||||
|
Have fun working for MinioK8SBucket!
|
||||||
116
docs/README.md
Normal file
116
docs/README.md
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
# Minio K8S bucket operator
|
||||||
|
|
||||||
|
An operator to automatically create and update S3 buckets on Minio, with their accounts.
|
||||||
|
|
||||||
|
One deployed, this tool will allow you to automatically create Minio accounts associated with buckets.
|
||||||
|
|
||||||
|
|
||||||
|
## Pre-requisites
|
||||||
|
You will need:
|
||||||
|
|
||||||
|
* `kubectl` access to the target cluster
|
||||||
|
* A running Minio instance, and especially:
|
||||||
|
* The URL where the API of the instance can be reached
|
||||||
|
* The root credentials
|
||||||
|
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
The operator can be installed using the following commands:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl apply -f https://raw.githubusercontent.com/pierre42100/MinioK8sBuckets/master/yaml/crd.yaml
|
||||||
|
kubectl apply -f https://raw.githubusercontent.com/pierre42100/MinioK8sBuckets/master/yaml/deployment.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! warning "Known limitation"
|
||||||
|
The operator install a deployment on the `default` namespace. Currently, only this namespace is supported!
|
||||||
|
|
||||||
|
## Configure instance
|
||||||
|
In order to create buckets, the operator needs to know how to reach the Minio instance.
|
||||||
|
|
||||||
|
You first need to secret similar to that one:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: minio-root
|
||||||
|
type: Opaque
|
||||||
|
dyringData:
|
||||||
|
accessKey: <MINIO_ROOT_ACCESS_KEY>
|
||||||
|
secretKey: <MINIO_ROOT_SECRET_KEY>
|
||||||
|
```
|
||||||
|
|
||||||
|
Replace `<MINIO_ROOT_ACCESS_KEY>` and `<MINIO_ROOT_SECRET_KEY>` with the appropriate values.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
You can then declare a Minio instance simiarl to that one:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: "communiquons.org/v1"
|
||||||
|
kind: MinioInstance
|
||||||
|
metadata:
|
||||||
|
name: my-minio-instance
|
||||||
|
spec:
|
||||||
|
endpoint: https://minio.example.com/
|
||||||
|
credentials: minio-root
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
Minio itself can be located outside of the Kubernetes cluster.
|
||||||
|
|
||||||
|
|
||||||
|
## Create a bucket
|
||||||
|
You are now ready to create your first bucket!
|
||||||
|
|
||||||
|
Here is a basic bucket example:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: "communiquons.org/v1"
|
||||||
|
kind: MinioBucket
|
||||||
|
metadata:
|
||||||
|
name: first-bucket
|
||||||
|
spec:
|
||||||
|
# The name of the minio instance
|
||||||
|
instance: my-minio-instance
|
||||||
|
# The name of the bucket to create
|
||||||
|
name: first-bucket
|
||||||
|
# The name of the secret that will be created
|
||||||
|
# by the operator which contains credentials to
|
||||||
|
# use to access the bucket
|
||||||
|
secret: first-bucket-secret
|
||||||
|
```
|
||||||
|
|
||||||
|
## More complete example
|
||||||
|
Here is a more complete example that makes use of all the available options:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: "communiquons.org/v1"
|
||||||
|
kind: MinioBucket
|
||||||
|
metadata:
|
||||||
|
name: my-bucket
|
||||||
|
spec:
|
||||||
|
instance: my-minio-instance
|
||||||
|
name: my-bucket
|
||||||
|
secret: my-bucket-secret
|
||||||
|
# This must be set to true to allow unauthenticated
|
||||||
|
# access to the bucket resources. Use this to host a
|
||||||
|
# static website for example
|
||||||
|
anonymous_read_access: true
|
||||||
|
# Enable versioning on the bucket => keep old versions
|
||||||
|
# of uploaded files
|
||||||
|
versioning: true
|
||||||
|
# If specified, a quota will be applied to the bucket, in bytes
|
||||||
|
quota: 1000000000
|
||||||
|
# Prevent files from being removed from the bucket. This parameter
|
||||||
|
# can not be changed, once the bucket has been created
|
||||||
|
lock: true
|
||||||
|
# Data retention policy. Versioning must be enabled to allow this
|
||||||
|
retention:
|
||||||
|
# The number of days data shall be kept
|
||||||
|
validity: 100
|
||||||
|
# compliance => nobody can bypass the policy
|
||||||
|
# governance => users with privileges might bypass policy restrictions
|
||||||
|
mode: compliance
|
||||||
|
```
|
||||||
20
mkdocs.yml
Normal file
20
mkdocs.yml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
site_name: Minio K8S buckets operator
|
||||||
|
theme:
|
||||||
|
language: en
|
||||||
|
name: material
|
||||||
|
palette:
|
||||||
|
# Palette toggle for dark mode
|
||||||
|
- media: "(prefers-color-scheme: dark)"
|
||||||
|
scheme: slate
|
||||||
|
|
||||||
|
|
||||||
|
markdown_extensions:
|
||||||
|
- admonition
|
||||||
|
- pymdownx.details
|
||||||
|
- pymdownx.superfences
|
||||||
|
|
||||||
|
repo_url: https://gitea.communiquons.org/pierre/MinioK8sBuckets
|
||||||
|
edit_uri: src/branch/master/docs/
|
||||||
|
|
||||||
|
plugins:
|
||||||
|
- search
|
||||||
3
renovate.json
Normal file
3
renovate.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"extends": ["local>renovate/presets"]
|
||||||
|
}
|
||||||
@@ -14,7 +14,7 @@ pub struct MinioInstanceSpec {
|
|||||||
pub credentials: String,
|
pub credentials: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Default, Clone, JsonSchema)]
|
#[derive(Debug, Serialize, Deserialize, Default, Copy, Clone, JsonSchema, PartialEq, Eq)]
|
||||||
pub enum RetentionType {
|
pub enum RetentionType {
|
||||||
#[default]
|
#[default]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
@@ -23,7 +23,7 @@ pub enum RetentionType {
|
|||||||
Governance,
|
Governance,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Default, Clone, JsonSchema)]
|
#[derive(Debug, Serialize, Deserialize, Default, Clone, Copy, JsonSchema, PartialEq, Eq)]
|
||||||
pub struct BucketRetention {
|
pub struct BucketRetention {
|
||||||
pub validity: usize,
|
pub validity: usize,
|
||||||
pub r#type: RetentionType,
|
pub r#type: RetentionType,
|
||||||
|
|||||||
@@ -4,4 +4,5 @@ pub mod minio;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod minio_test_server;
|
pub mod minio_test_server;
|
||||||
pub mod secrets;
|
pub mod secrets;
|
||||||
|
pub mod temp;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|||||||
32
src/main.rs
32
src/main.rs
@@ -10,6 +10,7 @@ use minio_operator::crd::{MinioBucket, MinioInstance};
|
|||||||
use minio_operator::minio::{MinioService, MinioUser};
|
use minio_operator::minio::{MinioService, MinioUser};
|
||||||
use minio_operator::secrets::{create_secret, read_secret_str};
|
use minio_operator::secrets::{create_secret, read_secret_str};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
@@ -54,6 +55,17 @@ async fn apply_bucket(b: &MinioBucket, client: &Client) -> anyhow::Result<()> {
|
|||||||
secret_key: read_secret_str(&instance_secret, SECRET_MINIO_INSTANCE_SECRET_KEY)?,
|
secret_key: read_secret_str(&instance_secret, SECRET_MINIO_INSTANCE_SECRET_KEY)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Check if Minio is responding
|
||||||
|
let mut ready_count = 0;
|
||||||
|
while !service.is_ready().await {
|
||||||
|
if ready_count > 10 {
|
||||||
|
panic!("Minio is unreachable!");
|
||||||
|
}
|
||||||
|
ready_count += 1;
|
||||||
|
tokio::time::sleep(Duration::from_millis(500)).await;
|
||||||
|
log::warn!("Minio is not responding yet, will try again to connect soon...");
|
||||||
|
}
|
||||||
|
|
||||||
// Get user key & password
|
// Get user key & password
|
||||||
let user_secret = match secrets.get_opt(&b.spec.secret).await? {
|
let user_secret = match secrets.get_opt(&b.spec.secret).await? {
|
||||||
Some(s) => s,
|
Some(s) => s,
|
||||||
@@ -65,7 +77,7 @@ async fn apply_bucket(b: &MinioBucket, client: &Client) -> anyhow::Result<()> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// The secret needs to be created
|
// The secret needs to be created
|
||||||
let new_user = MinioUser::gen_random();
|
let new_user = MinioUser::gen_random(&b.spec.name);
|
||||||
create_secret(
|
create_secret(
|
||||||
&secrets,
|
&secrets,
|
||||||
&b.spec.secret,
|
&b.spec.secret,
|
||||||
@@ -88,8 +100,22 @@ async fn apply_bucket(b: &MinioBucket, client: &Client) -> anyhow::Result<()> {
|
|||||||
password: read_secret_str(&user_secret, SECRET_MINIO_BUCKET_SECRET_KEY)?,
|
password: read_secret_str(&user_secret, SECRET_MINIO_BUCKET_SECRET_KEY)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
println!("{:?}", service);
|
log::debug!("Create or update bucket...");
|
||||||
println!("{:?}", user);
|
service.bucket_apply(&b.spec).await?;
|
||||||
|
|
||||||
|
let policy_name = format!("bucket-{}", b.spec.name);
|
||||||
|
log::debug!("Create or update policy '{policy_name}'...");
|
||||||
|
let policy_content =
|
||||||
|
include_str!("policy_template.json").replace("{{ bucket }}", b.spec.name.as_str());
|
||||||
|
service.policy_apply(&policy_name, &policy_content).await?;
|
||||||
|
|
||||||
|
log::debug!("Create or update user '{}'...", user.username);
|
||||||
|
service.user_apply(&user).await?;
|
||||||
|
|
||||||
|
log::debug!("Attach policy '{policy_name}' to user...");
|
||||||
|
service.policy_attach_user(&user, &policy_name).await?;
|
||||||
|
|
||||||
|
log::debug!("Successfully applied desired configuration!");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
684
src/minio.rs
684
src/minio.rs
@@ -4,7 +4,8 @@ use serde::de::DeserializeOwned;
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::constants::{MC_EXE, SECRET_MINIO_BUCKET_ACCESS_LEN, SECRET_MINIO_BUCKET_SECRET_LEN};
|
use crate::constants::{MC_EXE, SECRET_MINIO_BUCKET_ACCESS_LEN, SECRET_MINIO_BUCKET_SECRET_LEN};
|
||||||
use crate::crd::{MinioBucketSpec, RetentionType};
|
use crate::crd::{BucketRetention, MinioBucketSpec, RetentionType};
|
||||||
|
use crate::temp;
|
||||||
use crate::utils::rand_str;
|
use crate::utils::rand_str;
|
||||||
|
|
||||||
const MC_ALIAS_NAME: &str = "managedminioinst";
|
const MC_ALIAS_NAME: &str = "managedminioinst";
|
||||||
@@ -23,6 +24,10 @@ enum MinioError {
|
|||||||
SetQuotaFailed,
|
SetQuotaFailed,
|
||||||
#[error("Failed to set bucket retention!")]
|
#[error("Failed to set bucket retention!")]
|
||||||
SetRetentionFailed,
|
SetRetentionFailed,
|
||||||
|
#[error("Failed to set policy!")]
|
||||||
|
ApplyPolicyFailed,
|
||||||
|
#[error("Failed to create user!")]
|
||||||
|
CreateUserFailed,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -39,9 +44,9 @@ pub struct MinioUser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MinioUser {
|
impl MinioUser {
|
||||||
pub fn gen_random() -> Self {
|
pub fn gen_random(prefix: &str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
username: rand_str(SECRET_MINIO_BUCKET_ACCESS_LEN),
|
username: format!("{prefix}_{}", rand_str(SECRET_MINIO_BUCKET_ACCESS_LEN)),
|
||||||
password: rand_str(SECRET_MINIO_BUCKET_SECRET_LEN),
|
password: rand_str(SECRET_MINIO_BUCKET_SECRET_LEN),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -79,6 +84,57 @@ struct MinioAnonymousAccess {
|
|||||||
pub permission: String,
|
pub permission: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
struct MinioQuota {
|
||||||
|
pub quota: Option<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
struct MinioRetentionResult {
|
||||||
|
pub enabled: Option<String>,
|
||||||
|
pub mode: Option<String>,
|
||||||
|
pub validity: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
struct MinioPolicy {
|
||||||
|
pub policy: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
struct MinioPolicyInfo {
|
||||||
|
pub policyInfo: PolicyInfo,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
struct PolicyInfo {
|
||||||
|
Policy: serde_json::Value,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
struct MinioUserListRes {
|
||||||
|
accessKey: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
struct MinioPoliciesUserEntities {
|
||||||
|
result: MinioPoliciesUserEntitiesInner,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
struct MinioPoliciesUserEntitiesInner {
|
||||||
|
userMappings: Option<Vec<MinioPoliciesUserEntitiesInnerUser>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
struct MinioPoliciesUserEntitiesInnerUser {
|
||||||
|
policies: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
impl BasicMinioResult {
|
impl BasicMinioResult {
|
||||||
pub fn success(&self) -> bool {
|
pub fn success(&self) -> bool {
|
||||||
self.status == "success"
|
self.status == "success"
|
||||||
@@ -108,7 +164,7 @@ impl MinioService {
|
|||||||
|
|
||||||
/// Get bucket name prefixed by mc alias name
|
/// Get bucket name prefixed by mc alias name
|
||||||
fn absolute_bucket_name(&self, name: &str) -> String {
|
fn absolute_bucket_name(&self, name: &str) -> String {
|
||||||
format!("{}/{name}", MC_ALIAS_NAME)
|
format!("{MC_ALIAS_NAME}/{name}")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute a minio mc command
|
/// Execute a minio mc command
|
||||||
@@ -116,9 +172,9 @@ impl MinioService {
|
|||||||
where
|
where
|
||||||
A: DeserializeOwned,
|
A: DeserializeOwned,
|
||||||
{
|
{
|
||||||
log::debug!("exec_mc_cmd with args {:?}", args);
|
log::debug!("exec_mc_cmd with args {args:?}");
|
||||||
|
|
||||||
let conf_dir = mktemp::Temp::new_dir()?;
|
let conf_dir = temp::create_temp_dir()?;
|
||||||
let global_flags = ["--config-dir", conf_dir.to_str().unwrap(), "--json"];
|
let global_flags = ["--config-dir", conf_dir.to_str().unwrap(), "--json"];
|
||||||
|
|
||||||
// First, set our alias to mc in a temporary directory
|
// First, set our alias to mc in a temporary directory
|
||||||
@@ -195,67 +251,30 @@ impl MinioService {
|
|||||||
.any(|b| b.bucket_name().eq(name)))
|
.any(|b| b.bucket_name().eq(name)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a bucket
|
/// Apply bucket desired configuration. If bucket already exists, it is not dropped
|
||||||
pub async fn create_bucket(&self, b: &MinioBucketSpec) -> anyhow::Result<()> {
|
pub async fn bucket_apply(&self, b: &MinioBucketSpec) -> anyhow::Result<()> {
|
||||||
// Set base parameters
|
// Set base parameters
|
||||||
let bucket_name = format!("{}/{}", MC_ALIAS_NAME, b.name);
|
let bucket_name = format!("{}/{}", MC_ALIAS_NAME, b.name);
|
||||||
let mut args = ["mb", bucket_name.as_str()].to_vec();
|
let mut args = ["mb", bucket_name.as_str(), "-p"].to_vec();
|
||||||
|
|
||||||
if b.lock || b.retention.is_some() {
|
if b.lock {
|
||||||
args.push("--with-lock");
|
args.push("--with-lock");
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = self.exec_mc_cmd::<BasicMinioResult>(&args).await?;
|
let res = self.exec_mc_cmd::<BasicMinioResult>(&args).await?;
|
||||||
if res.get(0).map(|r| r.success()) != Some(true) {
|
if res.first().map(|r| r.success()) != Some(true) {
|
||||||
return Err(MinioError::MakeBucketFailed.into());
|
return Err(MinioError::MakeBucketFailed.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.bucket_set_versioning(&b.name, b.versioning).await?;
|
self.bucket_set_versioning(&b.name, b.versioning || b.lock)
|
||||||
|
.await?;
|
||||||
self.bucket_set_anonymous_access(&b.name, b.anonymous_read_access)
|
self.bucket_set_anonymous_access(&b.name, b.anonymous_read_access)
|
||||||
.await?;
|
.await?;
|
||||||
|
self.bucket_set_quota(&b.name, b.quota).await?;
|
||||||
// Set quota, if requested
|
if b.lock {
|
||||||
if let Some(quota) = &b.quota {
|
self.bucket_set_default_retention(&b.name, b.retention)
|
||||||
let quota = format!("{}MB", quota);
|
|
||||||
|
|
||||||
let res = self
|
|
||||||
.exec_mc_cmd::<BasicMinioResult>(&[
|
|
||||||
"quota",
|
|
||||||
"set",
|
|
||||||
bucket_name.as_str(),
|
|
||||||
"--size",
|
|
||||||
quota.as_str(),
|
|
||||||
])
|
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if res.get(0).map(|r| r.success()) != Some(true) {
|
|
||||||
return Err(MinioError::SetQuotaFailed.into());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set retention, if requested
|
|
||||||
if let Some(retention) = &b.retention {
|
|
||||||
let days = format!("{}d", retention.validity);
|
|
||||||
|
|
||||||
let res = self
|
|
||||||
.exec_mc_cmd::<BasicMinioResult>(&[
|
|
||||||
"retention",
|
|
||||||
"set",
|
|
||||||
"--default",
|
|
||||||
match retention.r#type {
|
|
||||||
RetentionType::Compliance => "compliance",
|
|
||||||
RetentionType::Governance => "governance",
|
|
||||||
},
|
|
||||||
days.as_str(),
|
|
||||||
bucket_name.as_str(),
|
|
||||||
])
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
if res.get(0).map(|r| r.success()) != Some(true) {
|
|
||||||
return Err(MinioError::SetRetentionFailed.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,7 +293,7 @@ impl MinioService {
|
|||||||
])
|
])
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if res.get(0).map(|r| r.success()) != Some(true) {
|
if res.first().map(|r| r.success()) != Some(true) {
|
||||||
return Err(MinioError::SetQuotaFailed.into());
|
return Err(MinioError::SetQuotaFailed.into());
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -298,7 +317,7 @@ impl MinioService {
|
|||||||
bucket_name: &str,
|
bucket_name: &str,
|
||||||
access: bool,
|
access: bool,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
let target = self.absolute_bucket_name(bucket_name);
|
let target = format!("{}/*", self.absolute_bucket_name(bucket_name));
|
||||||
|
|
||||||
let res = self
|
let res = self
|
||||||
.exec_mc_cmd::<BasicMinioResult>(&[
|
.exec_mc_cmd::<BasicMinioResult>(&[
|
||||||
@@ -312,7 +331,7 @@ impl MinioService {
|
|||||||
])
|
])
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if res.get(0).map(|r| r.success()) != Some(true) {
|
if res.first().map(|r| r.success()) != Some(true) {
|
||||||
return Err(MinioError::SetAnonymousAcccessFailed.into());
|
return Err(MinioError::SetAnonymousAcccessFailed.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,7 +340,7 @@ impl MinioService {
|
|||||||
|
|
||||||
/// Get current bucket anonymous access status
|
/// Get current bucket anonymous access status
|
||||||
pub async fn bucket_get_anonymous_access(&self, bucket_name: &str) -> anyhow::Result<bool> {
|
pub async fn bucket_get_anonymous_access(&self, bucket_name: &str) -> anyhow::Result<bool> {
|
||||||
let bucket_name = self.absolute_bucket_name(bucket_name);
|
let bucket_name = format!("{}/*", self.absolute_bucket_name(bucket_name));
|
||||||
Ok(self
|
Ok(self
|
||||||
.exec_mc_cmd::<MinioAnonymousAccess>(&["anonymous", "get", bucket_name.as_str()])
|
.exec_mc_cmd::<MinioAnonymousAccess>(&["anonymous", "get", bucket_name.as_str()])
|
||||||
.await?
|
.await?
|
||||||
@@ -329,14 +348,255 @@ impl MinioService {
|
|||||||
.permission
|
.permission
|
||||||
== "download")
|
== "download")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set bucket quota, in bytes
|
||||||
|
pub async fn bucket_set_quota(&self, bucket: &str, quota: Option<usize>) -> anyhow::Result<()> {
|
||||||
|
let bucket_name = self.absolute_bucket_name(bucket);
|
||||||
|
|
||||||
|
let res = if let Some(quota) = "a {
|
||||||
|
let quota = format!("{quota}B");
|
||||||
|
self.exec_mc_cmd::<BasicMinioResult>(&[
|
||||||
|
"quota",
|
||||||
|
"set",
|
||||||
|
bucket_name.as_str(),
|
||||||
|
"--size",
|
||||||
|
quota.as_str(),
|
||||||
|
])
|
||||||
|
.await?
|
||||||
|
} else {
|
||||||
|
self.exec_mc_cmd::<BasicMinioResult>(&["quota", "clear", bucket_name.as_str()])
|
||||||
|
.await?
|
||||||
|
};
|
||||||
|
|
||||||
|
if res.first().map(|r| r.success()) != Some(true) {
|
||||||
|
return Err(MinioError::SetQuotaFailed.into());
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get current bucket quota, in bytes
|
||||||
|
pub async fn bucket_get_quota(&self, bucket_name: &str) -> anyhow::Result<Option<usize>> {
|
||||||
|
let bucket_name = self.absolute_bucket_name(bucket_name);
|
||||||
|
Ok(self
|
||||||
|
.exec_mc_cmd::<MinioQuota>(&["quota", "info", bucket_name.as_str()])
|
||||||
|
.await?
|
||||||
|
.remove(0)
|
||||||
|
.quota)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set bucket default retention policy
|
||||||
|
pub async fn bucket_set_default_retention(
|
||||||
|
&self,
|
||||||
|
bucket_name: &str,
|
||||||
|
retention: Option<BucketRetention>,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let bucket_name = self.absolute_bucket_name(bucket_name);
|
||||||
|
let res = if let Some(retention) = &retention {
|
||||||
|
let days = format!("{}d", retention.validity);
|
||||||
|
|
||||||
|
self.exec_mc_cmd::<BasicMinioResult>(&[
|
||||||
|
"retention",
|
||||||
|
"set",
|
||||||
|
"--default",
|
||||||
|
match retention.r#type {
|
||||||
|
RetentionType::Compliance => "compliance",
|
||||||
|
RetentionType::Governance => "governance",
|
||||||
|
},
|
||||||
|
days.as_str(),
|
||||||
|
bucket_name.as_str(),
|
||||||
|
])
|
||||||
|
.await?
|
||||||
|
} else {
|
||||||
|
self.exec_mc_cmd::<BasicMinioResult>(&[
|
||||||
|
"retention",
|
||||||
|
"clear",
|
||||||
|
"--default",
|
||||||
|
bucket_name.as_str(),
|
||||||
|
])
|
||||||
|
.await?
|
||||||
|
};
|
||||||
|
|
||||||
|
if res.first().map(|r| r.success()) != Some(true) {
|
||||||
|
return Err(MinioError::SetRetentionFailed.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get bucket default retention policy
|
||||||
|
pub async fn bucket_get_default_retention(
|
||||||
|
&self,
|
||||||
|
bucket: &str,
|
||||||
|
) -> anyhow::Result<Option<BucketRetention>> {
|
||||||
|
let bucket_name = self.absolute_bucket_name(bucket);
|
||||||
|
let res = self
|
||||||
|
.exec_mc_cmd::<MinioRetentionResult>(&[
|
||||||
|
"retention",
|
||||||
|
"info",
|
||||||
|
bucket_name.as_str(),
|
||||||
|
"--default",
|
||||||
|
])
|
||||||
|
.await?
|
||||||
|
.remove(0);
|
||||||
|
|
||||||
|
if let (Some(mode), Some(validity), Some(enabled)) = (res.mode, res.validity, res.enabled) {
|
||||||
|
if enabled.to_lowercase().eq("enabled") {
|
||||||
|
return Ok(Some(BucketRetention {
|
||||||
|
validity: validity.to_lowercase().replace("days", "").parse()?,
|
||||||
|
r#type: match mode.to_lowercase().as_str() {
|
||||||
|
"governance" => RetentionType::Governance,
|
||||||
|
"compliance" => RetentionType::Compliance,
|
||||||
|
o => {
|
||||||
|
log::error!("Unknown retention type: {o}");
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Apply a bucket policy
|
||||||
|
pub async fn policy_apply(&self, name: &str, content: &str) -> anyhow::Result<()> {
|
||||||
|
let tmp_file = temp::create_temp_file()?;
|
||||||
|
std::fs::write(&tmp_file, content)?;
|
||||||
|
|
||||||
|
let res = self
|
||||||
|
.exec_mc_cmd::<BasicMinioResult>(&[
|
||||||
|
"admin",
|
||||||
|
"policy",
|
||||||
|
"create",
|
||||||
|
MC_ALIAS_NAME,
|
||||||
|
name,
|
||||||
|
tmp_file.to_str().unwrap(),
|
||||||
|
])
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if res.first().map(|r| r.success()) != Some(true) {
|
||||||
|
return Err(MinioError::ApplyPolicyFailed.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the list of existing policies
|
||||||
|
pub async fn policy_list(&self) -> anyhow::Result<Vec<String>> {
|
||||||
|
Ok(self
|
||||||
|
.exec_mc_cmd::<MinioPolicy>(&["admin", "policy", "list", MC_ALIAS_NAME])
|
||||||
|
.await?
|
||||||
|
.iter()
|
||||||
|
.map(|p| p.policy.to_string())
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the content of a given policy
|
||||||
|
pub async fn policy_content(&self, name: &str) -> anyhow::Result<String> {
|
||||||
|
let policy = self
|
||||||
|
.exec_mc_cmd::<MinioPolicyInfo>(&["admin", "policy", "info", MC_ALIAS_NAME, name])
|
||||||
|
.await?
|
||||||
|
.remove(0);
|
||||||
|
|
||||||
|
Ok(serde_json::to_string(&policy.policyInfo.Policy)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Apply a user
|
||||||
|
pub async fn user_apply(&self, user: &MinioUser) -> anyhow::Result<()> {
|
||||||
|
let res = self
|
||||||
|
.exec_mc_cmd::<BasicMinioResult>(&[
|
||||||
|
"admin",
|
||||||
|
"user",
|
||||||
|
"add",
|
||||||
|
MC_ALIAS_NAME,
|
||||||
|
user.username.as_str(),
|
||||||
|
user.password.as_str(),
|
||||||
|
])
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if res.first().map(|r| r.success()) != Some(true) {
|
||||||
|
return Err(MinioError::CreateUserFailed.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the list of users
|
||||||
|
pub async fn user_list(&self) -> anyhow::Result<Vec<String>> {
|
||||||
|
Ok(self
|
||||||
|
.exec_mc_cmd::<MinioUserListRes>(&["admin", "user", "list", MC_ALIAS_NAME])
|
||||||
|
.await?
|
||||||
|
.iter()
|
||||||
|
.map(|p| p.accessKey.to_string())
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attach a user to a policy
|
||||||
|
pub async fn policy_attach_user(&self, user: &MinioUser, policy: &str) -> anyhow::Result<()> {
|
||||||
|
// Check if the policy has already been attached to the user
|
||||||
|
if self
|
||||||
|
.policy_attach_get_user_list(user)
|
||||||
|
.await?
|
||||||
|
.contains(&policy.to_string())
|
||||||
|
{
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = self
|
||||||
|
.exec_mc_cmd::<BasicMinioResult>(&[
|
||||||
|
"admin",
|
||||||
|
"policy",
|
||||||
|
"attach",
|
||||||
|
MC_ALIAS_NAME,
|
||||||
|
policy,
|
||||||
|
"--user",
|
||||||
|
user.username.as_str(),
|
||||||
|
])
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if res.first().map(|r| r.success()) != Some(true) {
|
||||||
|
return Err(MinioError::CreateUserFailed.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the list of entities attached to a user
|
||||||
|
pub async fn policy_attach_get_user_list(
|
||||||
|
&self,
|
||||||
|
user: &MinioUser,
|
||||||
|
) -> anyhow::Result<Vec<String>> {
|
||||||
|
let res = self
|
||||||
|
.exec_mc_cmd::<MinioPoliciesUserEntities>(&[
|
||||||
|
"admin",
|
||||||
|
"policy",
|
||||||
|
"entities",
|
||||||
|
MC_ALIAS_NAME,
|
||||||
|
"--user",
|
||||||
|
user.username.as_str(),
|
||||||
|
])
|
||||||
|
.await?
|
||||||
|
.remove(0)
|
||||||
|
.result
|
||||||
|
.userMappings;
|
||||||
|
|
||||||
|
if let Some(mapping) = res {
|
||||||
|
if let Some(e) = mapping.first() {
|
||||||
|
return Ok(e.policies.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(vec![])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::crd::MinioBucketSpec;
|
use crate::crd::{BucketRetention, MinioBucketSpec, RetentionType};
|
||||||
|
use crate::minio::MinioUser;
|
||||||
use crate::minio_test_server::MinioTestServer;
|
use crate::minio_test_server::MinioTestServer;
|
||||||
|
|
||||||
const TEST_BUCKET_NAME: &str = "mybucket";
|
const TEST_BUCKET_NAME: &str = "mybucket";
|
||||||
|
const TEST_POLICY_NAME: &str = "mypolicy";
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn list_buckets_empty_instance() {
|
async fn list_buckets_empty_instance() {
|
||||||
@@ -358,7 +618,7 @@ mod test {
|
|||||||
let srv = MinioTestServer::start().await.unwrap();
|
let srv = MinioTestServer::start().await.unwrap();
|
||||||
let service = srv.as_service();
|
let service = srv.as_service();
|
||||||
service
|
service
|
||||||
.create_bucket(&MinioBucketSpec {
|
.bucket_apply(&MinioBucketSpec {
|
||||||
instance: "".to_string(),
|
instance: "".to_string(),
|
||||||
name: TEST_BUCKET_NAME.to_string(),
|
name: TEST_BUCKET_NAME.to_string(),
|
||||||
secret: "".to_string(),
|
secret: "".to_string(),
|
||||||
@@ -380,7 +640,7 @@ mod test {
|
|||||||
let srv = MinioTestServer::start().await.unwrap();
|
let srv = MinioTestServer::start().await.unwrap();
|
||||||
let service = srv.as_service();
|
let service = srv.as_service();
|
||||||
service
|
service
|
||||||
.create_bucket(&MinioBucketSpec {
|
.bucket_apply(&MinioBucketSpec {
|
||||||
instance: "".to_string(),
|
instance: "".to_string(),
|
||||||
name: TEST_BUCKET_NAME.to_string(),
|
name: TEST_BUCKET_NAME.to_string(),
|
||||||
secret: "".to_string(),
|
secret: "".to_string(),
|
||||||
@@ -415,7 +675,7 @@ mod test {
|
|||||||
let srv = MinioTestServer::start().await.unwrap();
|
let srv = MinioTestServer::start().await.unwrap();
|
||||||
let service = srv.as_service();
|
let service = srv.as_service();
|
||||||
service
|
service
|
||||||
.create_bucket(&MinioBucketSpec {
|
.bucket_apply(&MinioBucketSpec {
|
||||||
instance: "".to_string(),
|
instance: "".to_string(),
|
||||||
name: TEST_BUCKET_NAME.to_string(),
|
name: TEST_BUCKET_NAME.to_string(),
|
||||||
secret: "".to_string(),
|
secret: "".to_string(),
|
||||||
@@ -446,7 +706,7 @@ mod test {
|
|||||||
let srv = MinioTestServer::start().await.unwrap();
|
let srv = MinioTestServer::start().await.unwrap();
|
||||||
let service = srv.as_service();
|
let service = srv.as_service();
|
||||||
service
|
service
|
||||||
.create_bucket(&MinioBucketSpec {
|
.bucket_apply(&MinioBucketSpec {
|
||||||
instance: "".to_string(),
|
instance: "".to_string(),
|
||||||
name: TEST_BUCKET_NAME.to_string(),
|
name: TEST_BUCKET_NAME.to_string(),
|
||||||
secret: "".to_string(),
|
secret: "".to_string(),
|
||||||
@@ -492,7 +752,6 @@ mod test {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// With versioning
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn bucket_with_versioning() {
|
async fn bucket_with_versioning() {
|
||||||
let _ = env_logger::builder().is_test(true).try_init();
|
let _ = env_logger::builder().is_test(true).try_init();
|
||||||
@@ -500,7 +759,7 @@ mod test {
|
|||||||
let srv = MinioTestServer::start().await.unwrap();
|
let srv = MinioTestServer::start().await.unwrap();
|
||||||
let service = srv.as_service();
|
let service = srv.as_service();
|
||||||
service
|
service
|
||||||
.create_bucket(&MinioBucketSpec {
|
.bucket_apply(&MinioBucketSpec {
|
||||||
instance: "".to_string(),
|
instance: "".to_string(),
|
||||||
name: TEST_BUCKET_NAME.to_string(),
|
name: TEST_BUCKET_NAME.to_string(),
|
||||||
secret: "".to_string(),
|
secret: "".to_string(),
|
||||||
@@ -527,7 +786,7 @@ mod test {
|
|||||||
let srv = MinioTestServer::start().await.unwrap();
|
let srv = MinioTestServer::start().await.unwrap();
|
||||||
let service = srv.as_service();
|
let service = srv.as_service();
|
||||||
service
|
service
|
||||||
.create_bucket(&MinioBucketSpec {
|
.bucket_apply(&MinioBucketSpec {
|
||||||
instance: "".to_string(),
|
instance: "".to_string(),
|
||||||
name: TEST_BUCKET_NAME.to_string(),
|
name: TEST_BUCKET_NAME.to_string(),
|
||||||
secret: "".to_string(),
|
secret: "".to_string(),
|
||||||
@@ -554,7 +813,7 @@ mod test {
|
|||||||
let srv = MinioTestServer::start().await.unwrap();
|
let srv = MinioTestServer::start().await.unwrap();
|
||||||
let service = srv.as_service();
|
let service = srv.as_service();
|
||||||
service
|
service
|
||||||
.create_bucket(&MinioBucketSpec {
|
.bucket_apply(&MinioBucketSpec {
|
||||||
instance: "".to_string(),
|
instance: "".to_string(),
|
||||||
name: TEST_BUCKET_NAME.to_string(),
|
name: TEST_BUCKET_NAME.to_string(),
|
||||||
secret: "".to_string(),
|
secret: "".to_string(),
|
||||||
@@ -590,10 +849,291 @@ mod test {
|
|||||||
.unwrap());
|
.unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO : with quota
|
#[tokio::test]
|
||||||
// TODO : without quota
|
async fn bucket_without_quota() {
|
||||||
// TODO : with lock
|
let _ = env_logger::builder().is_test(true).try_init();
|
||||||
// TODO : without lock
|
|
||||||
// TODO : with retention
|
let srv = MinioTestServer::start().await.unwrap();
|
||||||
// TODO : without retention
|
let service = srv.as_service();
|
||||||
|
service
|
||||||
|
.bucket_apply(&MinioBucketSpec {
|
||||||
|
instance: "".to_string(),
|
||||||
|
name: TEST_BUCKET_NAME.to_string(),
|
||||||
|
secret: "".to_string(),
|
||||||
|
anonymous_read_access: false,
|
||||||
|
versioning: false,
|
||||||
|
quota: None,
|
||||||
|
lock: false,
|
||||||
|
retention: None,
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert!(service.bucket_exists(TEST_BUCKET_NAME).await.unwrap());
|
||||||
|
assert_eq!(
|
||||||
|
service.bucket_get_quota(TEST_BUCKET_NAME).await.unwrap(),
|
||||||
|
None
|
||||||
|
);
|
||||||
|
|
||||||
|
service
|
||||||
|
.bucket_set_quota(TEST_BUCKET_NAME, Some(5122600))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
service.bucket_get_quota(TEST_BUCKET_NAME).await.unwrap(),
|
||||||
|
Some(5122600)
|
||||||
|
);
|
||||||
|
|
||||||
|
service
|
||||||
|
.bucket_set_quota(TEST_BUCKET_NAME, None)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
service.bucket_get_quota(TEST_BUCKET_NAME).await.unwrap(),
|
||||||
|
None
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn bucket_with_quota() {
|
||||||
|
let _ = env_logger::builder().is_test(true).try_init();
|
||||||
|
|
||||||
|
let srv = MinioTestServer::start().await.unwrap();
|
||||||
|
let service = srv.as_service();
|
||||||
|
service
|
||||||
|
.bucket_apply(&MinioBucketSpec {
|
||||||
|
instance: "".to_string(),
|
||||||
|
name: TEST_BUCKET_NAME.to_string(),
|
||||||
|
secret: "".to_string(),
|
||||||
|
anonymous_read_access: false,
|
||||||
|
versioning: false,
|
||||||
|
quota: Some(42300),
|
||||||
|
lock: false,
|
||||||
|
retention: None,
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert!(service.bucket_exists(TEST_BUCKET_NAME).await.unwrap());
|
||||||
|
assert_eq!(
|
||||||
|
service.bucket_get_quota(TEST_BUCKET_NAME).await.unwrap(),
|
||||||
|
Some(42300)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn bucket_with_retention() {
|
||||||
|
let _ = env_logger::builder().is_test(true).try_init();
|
||||||
|
|
||||||
|
let srv = MinioTestServer::start().await.unwrap();
|
||||||
|
let service = srv.as_service();
|
||||||
|
service
|
||||||
|
.bucket_apply(&MinioBucketSpec {
|
||||||
|
instance: "".to_string(),
|
||||||
|
name: TEST_BUCKET_NAME.to_string(),
|
||||||
|
secret: "".to_string(),
|
||||||
|
anonymous_read_access: false,
|
||||||
|
versioning: false,
|
||||||
|
quota: Some(42300),
|
||||||
|
lock: true,
|
||||||
|
retention: Some(BucketRetention {
|
||||||
|
validity: 10,
|
||||||
|
r#type: RetentionType::Governance,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert!(service.bucket_exists(TEST_BUCKET_NAME).await.unwrap());
|
||||||
|
assert_eq!(
|
||||||
|
service
|
||||||
|
.bucket_get_default_retention(TEST_BUCKET_NAME)
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
Some(BucketRetention {
|
||||||
|
validity: 10,
|
||||||
|
r#type: RetentionType::Governance
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
service
|
||||||
|
.bucket_set_default_retention(TEST_BUCKET_NAME, None)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
service
|
||||||
|
.bucket_get_default_retention(TEST_BUCKET_NAME)
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
None
|
||||||
|
);
|
||||||
|
|
||||||
|
service
|
||||||
|
.bucket_set_default_retention(
|
||||||
|
TEST_BUCKET_NAME,
|
||||||
|
Some(BucketRetention {
|
||||||
|
validity: 42,
|
||||||
|
r#type: RetentionType::Compliance,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
service
|
||||||
|
.bucket_get_default_retention(TEST_BUCKET_NAME)
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
Some(BucketRetention {
|
||||||
|
validity: 42,
|
||||||
|
r#type: RetentionType::Compliance
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
service
|
||||||
|
.bucket_set_default_retention(
|
||||||
|
TEST_BUCKET_NAME,
|
||||||
|
Some(BucketRetention {
|
||||||
|
validity: 21,
|
||||||
|
r#type: RetentionType::Governance,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
service
|
||||||
|
.bucket_get_default_retention(TEST_BUCKET_NAME)
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
Some(BucketRetention {
|
||||||
|
validity: 21,
|
||||||
|
r#type: RetentionType::Governance
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn bucket_without_retention() {
|
||||||
|
let _ = env_logger::builder().is_test(true).try_init();
|
||||||
|
|
||||||
|
let srv = MinioTestServer::start().await.unwrap();
|
||||||
|
let service = srv.as_service();
|
||||||
|
service
|
||||||
|
.bucket_apply(&MinioBucketSpec {
|
||||||
|
instance: "".to_string(),
|
||||||
|
name: TEST_BUCKET_NAME.to_string(),
|
||||||
|
secret: "".to_string(),
|
||||||
|
anonymous_read_access: false,
|
||||||
|
versioning: false,
|
||||||
|
quota: Some(42300),
|
||||||
|
lock: true,
|
||||||
|
retention: None,
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert!(service.bucket_exists(TEST_BUCKET_NAME).await.unwrap());
|
||||||
|
assert_eq!(
|
||||||
|
service
|
||||||
|
.bucket_get_default_retention(TEST_BUCKET_NAME)
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
None
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unify_policy(p: &str) -> String {
|
||||||
|
serde_json::to_string(&serde_json::from_str::<serde_json::Value>(p).unwrap()).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn policy_apply() {
|
||||||
|
let _ = env_logger::builder().is_test(true).try_init();
|
||||||
|
|
||||||
|
let srv = MinioTestServer::start().await.unwrap();
|
||||||
|
let service = srv.as_service();
|
||||||
|
|
||||||
|
let policy_1 = unify_policy(include_str!("../test/test-policy1.json"));
|
||||||
|
let policy_2 = unify_policy(include_str!("../test/test-policy2.json"));
|
||||||
|
|
||||||
|
assert_ne!(policy_1, policy_2);
|
||||||
|
|
||||||
|
assert!(!service
|
||||||
|
.policy_list()
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.contains(&TEST_POLICY_NAME.to_string()));
|
||||||
|
|
||||||
|
service
|
||||||
|
.policy_apply(TEST_POLICY_NAME, &policy_1)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert!(service
|
||||||
|
.policy_list()
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.contains(&TEST_POLICY_NAME.to_string()));
|
||||||
|
assert_eq!(
|
||||||
|
unify_policy(&service.policy_content(TEST_POLICY_NAME).await.unwrap()),
|
||||||
|
policy_1
|
||||||
|
);
|
||||||
|
|
||||||
|
service
|
||||||
|
.policy_apply(TEST_POLICY_NAME, &policy_2)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert!(service
|
||||||
|
.policy_list()
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.contains(&TEST_POLICY_NAME.to_string()));
|
||||||
|
assert_eq!(
|
||||||
|
unify_policy(&service.policy_content(TEST_POLICY_NAME).await.unwrap()),
|
||||||
|
policy_2
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn policy_user() {
|
||||||
|
let _ = env_logger::builder().is_test(true).try_init();
|
||||||
|
|
||||||
|
let srv = MinioTestServer::start().await.unwrap();
|
||||||
|
let service = srv.as_service();
|
||||||
|
|
||||||
|
let user = MinioUser::gen_random("policy_user");
|
||||||
|
|
||||||
|
assert!(!service.user_list().await.unwrap().contains(&user.username));
|
||||||
|
service.user_apply(&user).await.unwrap();
|
||||||
|
assert!(service.user_list().await.unwrap().contains(&user.username));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn attach_policy_user() {
|
||||||
|
let _ = env_logger::builder().is_test(true).try_init();
|
||||||
|
|
||||||
|
let srv = MinioTestServer::start().await.unwrap();
|
||||||
|
let service = srv.as_service();
|
||||||
|
|
||||||
|
let user = MinioUser::gen_random("attach_policy_user");
|
||||||
|
|
||||||
|
service.user_apply(&user).await.unwrap();
|
||||||
|
service
|
||||||
|
.policy_apply(TEST_POLICY_NAME, include_str!("../test/test-policy1.json"))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert!(!service
|
||||||
|
.policy_attach_get_user_list(&user)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.contains(&TEST_POLICY_NAME.to_string()));
|
||||||
|
service
|
||||||
|
.policy_attach_user(&user, TEST_POLICY_NAME)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert!(service
|
||||||
|
.policy_attach_get_user_list(&user)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.contains(&TEST_POLICY_NAME.to_string()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,12 @@
|
|||||||
//! Used for testing only
|
//! Used for testing only
|
||||||
|
|
||||||
use crate::minio::MinioService;
|
use crate::minio::MinioService;
|
||||||
|
use crate::temp;
|
||||||
use crate::utils::rand_str;
|
use crate::utils::rand_str;
|
||||||
use rand::RngCore;
|
use rand::RngCore;
|
||||||
use std::io::ErrorKind;
|
use std::io::ErrorKind;
|
||||||
use std::process::{Child, Command};
|
use std::process::{Child, Command};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
pub struct MinioTestServer {
|
pub struct MinioTestServer {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@@ -19,11 +21,11 @@ pub struct MinioTestServer {
|
|||||||
|
|
||||||
impl MinioTestServer {
|
impl MinioTestServer {
|
||||||
pub async fn start() -> anyhow::Result<Self> {
|
pub async fn start() -> anyhow::Result<Self> {
|
||||||
let storage_dir = mktemp::Temp::new_dir()?;
|
let storage_dir = temp::create_temp_dir()?;
|
||||||
|
|
||||||
let root_user = rand_str(30);
|
let root_user = rand_str(30);
|
||||||
let root_password = rand_str(30);
|
let root_password = rand_str(30);
|
||||||
let api_port = (2000 + rand::thread_rng().next_u64() % 5000) as u16;
|
let api_port = (2000 + rand::rng().next_u64() % 5000) as u16;
|
||||||
log::info!(
|
log::info!(
|
||||||
"Spwan a new Minio server on port {} with root credentials {}:{}",
|
"Spwan a new Minio server on port {} with root credentials {}:{}",
|
||||||
api_port,
|
api_port,
|
||||||
@@ -50,6 +52,7 @@ impl MinioTestServer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Wait for Minio to become ready
|
// Wait for Minio to become ready
|
||||||
|
std::thread::sleep(Duration::from_millis(500));
|
||||||
let mut check_count = 0;
|
let mut check_count = 0;
|
||||||
loop {
|
loop {
|
||||||
if check_count >= 100 {
|
if check_count >= 100 {
|
||||||
@@ -62,6 +65,8 @@ impl MinioTestServer {
|
|||||||
}
|
}
|
||||||
check_count += 1;
|
check_count += 1;
|
||||||
|
|
||||||
|
std::thread::sleep(Duration::from_millis(100));
|
||||||
|
|
||||||
if instance.as_service().is_ready().await {
|
if instance.as_service().is_ready().await {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
17
src/policy_template.json
Normal file
17
src/policy_template.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"Version": "2012-10-17",
|
||||||
|
"Statement": [
|
||||||
|
{
|
||||||
|
"Sid": "ListObjectsInBucket",
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": ["s3:ListBucket"],
|
||||||
|
"Resource": ["arn:aws:s3:::{{ bucket }}"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Sid": "AllObjectActions",
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": ["s3:DeleteObject", "s3:Get*", "s3:PutObject", "s3:*Object"],
|
||||||
|
"Resource": ["arn:aws:s3:::{{ bucket }}/*"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
26
src/temp.rs
Normal file
26
src/temp.rs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
/// Get the directory where temp files should be created
|
||||||
|
fn temp_path() -> Option<PathBuf> {
|
||||||
|
std::env::var("TEMP_DIR")
|
||||||
|
.as_deref()
|
||||||
|
.ok()
|
||||||
|
.map(Path::new)
|
||||||
|
.map(|p| p.to_path_buf())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a temporary directory
|
||||||
|
pub fn create_temp_dir() -> std::io::Result<mktemp::Temp> {
|
||||||
|
match temp_path() {
|
||||||
|
None => mktemp::Temp::new_dir(),
|
||||||
|
Some(p) => mktemp::Temp::new_dir_in(p),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a temporary file
|
||||||
|
pub fn create_temp_file() -> std::io::Result<mktemp::Temp> {
|
||||||
|
match temp_path() {
|
||||||
|
None => mktemp::Temp::new_file(),
|
||||||
|
Some(p) => mktemp::Temp::new_file_in(p),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,6 @@
|
|||||||
use rand::distributions::Alphanumeric;
|
use rand::distr::{Alphanumeric, SampleString};
|
||||||
use rand::Rng;
|
|
||||||
|
|
||||||
/// Generate a random string of a given size
|
/// Generate a random string of a given size
|
||||||
pub fn rand_str(len: usize) -> String {
|
pub fn rand_str(len: usize) -> String {
|
||||||
rand::thread_rng()
|
Alphanumeric.sample_string(&mut rand::rng(), len)
|
||||||
.sample_iter(&Alphanumeric)
|
|
||||||
.take(len)
|
|
||||||
.map(char::from)
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
{
|
|
||||||
"Version": "2012-10-17",
|
|
||||||
"Statement": [
|
|
||||||
{
|
|
||||||
"Sid": "ListObjectsInBucket",
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Action": [
|
|
||||||
"s3:ListBucket"
|
|
||||||
],
|
|
||||||
"Resource": [
|
|
||||||
"arn:aws:s3:::bucket"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Sid": "AllObjectActions",
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Action": [
|
|
||||||
"s3:DeleteObject",
|
|
||||||
"s3:Get*",
|
|
||||||
"s3:PutObject",
|
|
||||||
"s3:*Object"
|
|
||||||
],
|
|
||||||
"Resource": [
|
|
||||||
"arn:aws:s3:::bucket/*"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -6,4 +6,5 @@ metadata:
|
|||||||
spec:
|
spec:
|
||||||
instance: my-minio-instance
|
instance: my-minio-instance
|
||||||
name: second-bucket
|
name: second-bucket
|
||||||
secret: second-bucket-secret
|
secret: second-bucket-secret
|
||||||
|
versioning: false
|
||||||
25
test/test-inside-cluster.yaml
Normal file
25
test/test-inside-cluster.yaml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: minio-root
|
||||||
|
type: Opaque
|
||||||
|
data:
|
||||||
|
accessKey: bWluaW9hZG1pbg==
|
||||||
|
secretKey: bWluaW9hZG1pbg==
|
||||||
|
---
|
||||||
|
apiVersion: "communiquons.org/v1"
|
||||||
|
kind: MinioInstance
|
||||||
|
metadata:
|
||||||
|
name: my-minio-instance
|
||||||
|
spec:
|
||||||
|
endpoint: http://192.168.2.103:9000/
|
||||||
|
credentials: minio-root
|
||||||
|
---
|
||||||
|
apiVersion: "communiquons.org/v1"
|
||||||
|
kind: MinioBucket
|
||||||
|
metadata:
|
||||||
|
name: first-bucket
|
||||||
|
spec:
|
||||||
|
instance: my-minio-instance
|
||||||
|
name: first-bucket
|
||||||
|
secret: first-bucket-secret
|
||||||
25
test/test-outside-cluster.yaml
Normal file
25
test/test-outside-cluster.yaml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: minio-root
|
||||||
|
type: Opaque
|
||||||
|
data:
|
||||||
|
accessKey: bWluaW9hZG1pbg==
|
||||||
|
secretKey: bWluaW9hZG1pbg==
|
||||||
|
---
|
||||||
|
apiVersion: "communiquons.org/v1"
|
||||||
|
kind: MinioInstance
|
||||||
|
metadata:
|
||||||
|
name: my-minio-instance
|
||||||
|
spec:
|
||||||
|
endpoint: http://localhost:9000
|
||||||
|
credentials: minio-root
|
||||||
|
---
|
||||||
|
apiVersion: "communiquons.org/v1"
|
||||||
|
kind: MinioBucket
|
||||||
|
metadata:
|
||||||
|
name: first-bucket
|
||||||
|
spec:
|
||||||
|
instance: my-minio-instance
|
||||||
|
name: first-bucket
|
||||||
|
secret: first-bucket-secret
|
||||||
15
test/test-policy1.json
Normal file
15
test/test-policy1.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"Version": "2012-10-17",
|
||||||
|
"Statement": [
|
||||||
|
{
|
||||||
|
"Sid": "ListObjectsInBucket",
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": [
|
||||||
|
"s3:ListBucket"
|
||||||
|
],
|
||||||
|
"Resource": [
|
||||||
|
"arn:aws:s3:::bucket"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
11
test/test-policy2.json
Normal file
11
test/test-policy2.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"Version": "2012-10-17",
|
||||||
|
"Statement": [
|
||||||
|
{
|
||||||
|
"Sid": "ListObjectsInBucket",
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": ["s3:ListBucket"],
|
||||||
|
"Resource": ["arn:aws:s3:::bucketdos"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,5 +1,54 @@
|
|||||||
apiVersion: apiextensions.k8s.io/v1
|
apiVersion: apiextensions.k8s.io/v1
|
||||||
kind: CustomResourceDefinition
|
kind: CustomResourceDefinition
|
||||||
|
metadata:
|
||||||
|
# name must match the spec fields below, and be in the form: <plural>.<group>
|
||||||
|
name: minioinstances.communiquons.org
|
||||||
|
spec:
|
||||||
|
# group name to use for REST API: /apis/<group>/<version>
|
||||||
|
group: communiquons.org
|
||||||
|
# list of versions supported by this CustomResourceDefinition
|
||||||
|
versions:
|
||||||
|
- name: v1
|
||||||
|
# Each version can be enabled/disabled by Served flag.
|
||||||
|
served: true
|
||||||
|
# One and only one version must be marked as the storage version.
|
||||||
|
storage: true
|
||||||
|
schema:
|
||||||
|
openAPIV3Schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
spec:
|
||||||
|
type: object
|
||||||
|
description: Information about how to reach the Minio bucket
|
||||||
|
properties:
|
||||||
|
endpoint:
|
||||||
|
description: The URL where the Minio API can be reached
|
||||||
|
example: https://minio.communiquons.org
|
||||||
|
type: string
|
||||||
|
credentials:
|
||||||
|
description: |
|
||||||
|
The name of the secret containings privilegied / root credentials of Minio instance
|
||||||
|
|
||||||
|
The secret must contains two fields :
|
||||||
|
* An access key named `accessKey`
|
||||||
|
* A secret key named `secretKey`
|
||||||
|
type: string
|
||||||
|
example: minio-root
|
||||||
|
# either Namespaced or Cluster
|
||||||
|
scope: Namespaced
|
||||||
|
names:
|
||||||
|
# plural name to be used in the URL: /apis/<group>/<version>/<plural>
|
||||||
|
plural: minioinstances
|
||||||
|
# singular name to be used as an alias on the CLI and for display
|
||||||
|
singular: minioinstance
|
||||||
|
# kind is normally the CamelCased singular type. Your resource manifests use this.
|
||||||
|
kind: MinioInstance
|
||||||
|
# shortNames allow shorter string to match your resource on the CLI
|
||||||
|
shortNames:
|
||||||
|
- mis
|
||||||
|
---
|
||||||
|
apiVersion: apiextensions.k8s.io/v1
|
||||||
|
kind: CustomResourceDefinition
|
||||||
metadata:
|
metadata:
|
||||||
# name must match the spec fields below, and be in the form: <plural>.<group>
|
# name must match the spec fields below, and be in the form: <plural>.<group>
|
||||||
name: miniobuckets.communiquons.org
|
name: miniobuckets.communiquons.org
|
||||||
@@ -47,10 +96,10 @@ spec:
|
|||||||
default: false
|
default: false
|
||||||
quota:
|
quota:
|
||||||
type: integer
|
type: integer
|
||||||
description: Limits the amount of data in the bucket, in Megabytes. By default it is unlimited
|
description: Limits the amount of data in the bucket, in bytes. By default it is unlimited
|
||||||
example: 100
|
example: 1000000000
|
||||||
lock:
|
lock:
|
||||||
description: Object locking prevent objects from being deleted. Will be considered as set to true when retention is defined.
|
description: Object locking prevent objects from being deleted. MUST be set to true when retention is defined. Cannot be changed.
|
||||||
type: boolean
|
type: boolean
|
||||||
default: false
|
default: false
|
||||||
retention:
|
retention:
|
||||||
81
yaml/deployment.yaml
Normal file
81
yaml/deployment.yaml
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
automountServiceAccountToken: true
|
||||||
|
metadata:
|
||||||
|
name: minio-operator
|
||||||
|
namespace: default
|
||||||
|
labels:
|
||||||
|
app: minio-operator
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
name: minio-operator
|
||||||
|
namespace: default
|
||||||
|
rules:
|
||||||
|
- apiGroups: ["communiquons.org"]
|
||||||
|
resources: ["minioinstances", "miniobuckets"]
|
||||||
|
verbs: ["get", "list", "watch"]
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["secrets"]
|
||||||
|
verbs: ["get", "create"]
|
||||||
|
---
|
||||||
|
kind: RoleBinding
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
metadata:
|
||||||
|
name: minio-operator
|
||||||
|
namespace: default
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: minio-operator
|
||||||
|
namespace: default
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: Role
|
||||||
|
name: minio-operator
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: minio-operator
|
||||||
|
labels:
|
||||||
|
app: minio-operator
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
strategy:
|
||||||
|
type: Recreate
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: minio-operator
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: minio-operator
|
||||||
|
spec:
|
||||||
|
serviceAccountName: minio-operator
|
||||||
|
containers:
|
||||||
|
- name: minio-operator
|
||||||
|
image: pierre42100/minio_operator
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 300Mi
|
||||||
|
cpu: "0.1"
|
||||||
|
requests:
|
||||||
|
memory: 150Mi
|
||||||
|
cpu: "0.01"
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /tmp
|
||||||
|
readOnly: false
|
||||||
|
name: tempdir
|
||||||
|
securityContext:
|
||||||
|
allowPrivilegeEscalation: false
|
||||||
|
readOnlyRootFilesystem: true
|
||||||
|
runAsUser: 1000
|
||||||
|
runAsGroup: 1000
|
||||||
|
capabilities:
|
||||||
|
drop:
|
||||||
|
- ALL
|
||||||
|
volumes:
|
||||||
|
- name: tempdir
|
||||||
|
emptyDir:
|
||||||
|
sizeLimit: 500Mi
|
||||||
89
yaml/minio-dev-deployment.yml
Normal file
89
yaml/minio-dev-deployment.yml
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: minio
|
||||||
|
labels:
|
||||||
|
app: minio
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: minio
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: minio
|
||||||
|
spec:
|
||||||
|
volumes:
|
||||||
|
- name: data
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: minio
|
||||||
|
containers:
|
||||||
|
- name: minio
|
||||||
|
image: minio/minio
|
||||||
|
imagePullPolicy: Always
|
||||||
|
ports:
|
||||||
|
- containerPort: 9000
|
||||||
|
protocol: TCP
|
||||||
|
name: api
|
||||||
|
- containerPort: 9090
|
||||||
|
protocol: TCP
|
||||||
|
name: console
|
||||||
|
args:
|
||||||
|
- server
|
||||||
|
- /data
|
||||||
|
- --console-address
|
||||||
|
- ":9090"
|
||||||
|
env:
|
||||||
|
- name: MINIO_ROOT_USER
|
||||||
|
value: minioadmin
|
||||||
|
- name: MINIO_ROOT_PASSWORD
|
||||||
|
value: minioadmin
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: "/data"
|
||||||
|
name: data
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolume
|
||||||
|
metadata:
|
||||||
|
name: minio
|
||||||
|
spec:
|
||||||
|
storageClassName: manual
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
capacity:
|
||||||
|
storage: 5Gi
|
||||||
|
hostPath:
|
||||||
|
path: /data/minio/
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: minio
|
||||||
|
spec:
|
||||||
|
storageClassName: manual
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 3Gi
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: minio
|
||||||
|
labels:
|
||||||
|
app: minio
|
||||||
|
spec:
|
||||||
|
type: LoadBalancer
|
||||||
|
selector:
|
||||||
|
app: minio
|
||||||
|
ports:
|
||||||
|
- name: api
|
||||||
|
port: 9000
|
||||||
|
targetPort: api
|
||||||
|
- name: console
|
||||||
|
port: 9090
|
||||||
|
targetPort: console
|
||||||
|
externalTrafficPolicy: Local
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
apiVersion: apiextensions.k8s.io/v1
|
|
||||||
kind: CustomResourceDefinition
|
|
||||||
metadata:
|
|
||||||
# name must match the spec fields below, and be in the form: <plural>.<group>
|
|
||||||
name: minioinstances.communiquons.org
|
|
||||||
spec:
|
|
||||||
# group name to use for REST API: /apis/<group>/<version>
|
|
||||||
group: communiquons.org
|
|
||||||
# list of versions supported by this CustomResourceDefinition
|
|
||||||
versions:
|
|
||||||
- name: v1
|
|
||||||
# Each version can be enabled/disabled by Served flag.
|
|
||||||
served: true
|
|
||||||
# One and only one version must be marked as the storage version.
|
|
||||||
storage: true
|
|
||||||
schema:
|
|
||||||
openAPIV3Schema:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
spec:
|
|
||||||
type: object
|
|
||||||
description: Information about how to reach the Minio bucket
|
|
||||||
properties:
|
|
||||||
endpoint:
|
|
||||||
description: The URL where the Minio API can be reached
|
|
||||||
example: https://minio.communiquons.org
|
|
||||||
type: string
|
|
||||||
credentials:
|
|
||||||
description: |
|
|
||||||
The name of the secret containings privilegied / root credentials of Minio instance
|
|
||||||
|
|
||||||
The secret must contains two fields :
|
|
||||||
* An access key named `accessKey`
|
|
||||||
* A secret key named `secretKey`
|
|
||||||
type: string
|
|
||||||
example: minio-root
|
|
||||||
# either Namespaced or Cluster
|
|
||||||
scope: Namespaced
|
|
||||||
names:
|
|
||||||
# plural name to be used in the URL: /apis/<group>/<version>/<plural>
|
|
||||||
plural: minioinstances
|
|
||||||
# singular name to be used as an alias on the CLI and for display
|
|
||||||
singular: minioinstance
|
|
||||||
# kind is normally the CamelCased singular type. Your resource manifests use this.
|
|
||||||
kind: MinioInstance
|
|
||||||
# shortNames allow shorter string to match your resource on the CLI
|
|
||||||
shortNames:
|
|
||||||
- mis
|
|
||||||
---
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: ServiceAccount
|
|
||||||
automountServiceAccountToken: true
|
|
||||||
metadata:
|
|
||||||
name: minio-buckets
|
|
||||||
namespace: default
|
|
||||||
labels:
|
|
||||||
app: minio
|
|
||||||
---
|
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
kind: Role
|
|
||||||
metadata:
|
|
||||||
name: minio-buckets
|
|
||||||
namespace: default
|
|
||||||
rules:
|
|
||||||
- apiGroups: ["communiquons.org"]
|
|
||||||
resources: ["minioinstances", "miniobuckets"]
|
|
||||||
verbs: ["get", "watch"]
|
|
||||||
---
|
|
||||||
kind: RoleBinding
|
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
metadata:
|
|
||||||
name: minio-buckets
|
|
||||||
namespace: default
|
|
||||||
subjects:
|
|
||||||
- kind: ServiceAccount
|
|
||||||
name: minio-buckets
|
|
||||||
namespace: default
|
|
||||||
roleRef:
|
|
||||||
apiGroup: rbac.authorization.k8s.io
|
|
||||||
kind: Role
|
|
||||||
name: minio-buckets
|
|
||||||
---
|
|
||||||
Reference in New Issue
Block a user