diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index b0da3bf..ffc329b 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -19,7 +19,7 @@ jobs: go-version-file: 'go.mod' - name: Go Format - run: gofmt -s -w . && git diff --exit-code + run: make format - name: Go Build run: go build ./... @@ -31,10 +31,10 @@ jobs: uses: golangci/golangci-lint-action@v9 - name: Test - run: go test -v -count=1 -race -shuffle=on -coverprofile=coverage.txt -json ./... > test.json + run: make test - name: Annotate tests if: always() uses: guyarb/golang-test-annotations@v0.8.0 with: - test-results: test.json \ No newline at end of file + test-results: test.json diff --git a/.github/workflows/release-create.yml b/.github/workflows/release-create.yml index 2209c63..5e276f0 100644 --- a/.github/workflows/release-create.yml +++ b/.github/workflows/release-create.yml @@ -33,7 +33,7 @@ jobs: go-version-file: 'go.mod' - name: Build binaries - run: go build ./cmd/osmanage + run: make build-prod - name: Create release uses: marvinpinto/action-automatic-releases@latest diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..bf369c1 --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ + +# Build + +build-prod osmanage: + CGO_ENABLED=0 go build -a -ldflags="-s -w" -o osmanage ./cmd/osmanage + +build-dev: + go build -o osmanage ./cmd/osmanage + + +# Tests / Linting + +test: + go test -v -count=1 -race -shuffle=on -coverprofile=coverage.txt ./... + +format: + gofmt -s -w . && git diff --exit-code + diff --git a/README.md b/README.md index 7680394..2bcd5bd 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,8 @@ -> **๐Ÿ“‹ Pre-Release Notice** -> -> `osmanage` is currently in **alpha/pre-release stage** and is being actively developed for production use at Intevation GmbH. While the tool is functional, it is not yet ready for general community adoption: -> -> - APIs and command interfaces may change -> - Some features are still being finalized -> - Documentation is work-in-progress -> - Production hardening is ongoing -> -> We plan to release a stable, community-ready version in the future. For now, use at your own risk or for experimentation only. Contributions and feedback are welcome! # osmanage -A command-line interface for managing OpenSlides instances. This tool provides deployment automation, Kubernetes orchestration, direct access to OpenSlides backend actions, datastore queries, and database migrations. +A command-line interface for managing OpenSlides instances. This tool provides deployment automation, Kubernetes orchestration, direct access to OpenSlides backend actions, database queries, and database migrations. + ## Table of Contents @@ -23,31 +14,20 @@ A command-line interface for managing OpenSlides instances. This tool provides d - [Instance Management](#instance-management) - [setup](#setup) - [config](#config) - - [create](#create) - - [remove](#remove) - - [Kubernetes Operations](#kubernetes-operations) - - [k8s start](#k8s-start) - - [k8s stop](#k8s-stop) - - [k8s update-instance](#k8s-update-instance) - - [k8s update-backendmanage](#k8s-update-backendmanage) - - [k8s scale](#k8s-scale) - - [k8s health](#k8s-health) - - [k8s cluster-status](#k8s-cluster-status) - [Backend Actions](#backend-actions) - - [action](#action) + - [migrations](#migrations) + - [initial-data](#initial-data) - [create-user](#create-user) + - [set-password](#set-password) - [get](#get) - - [initial-data](#initial-data) - - [migrations](#migrations) - [set](#set) - - [set-password](#set-password) -- [Configuration](#configuration) + - [action](#action) + - [Kubernetes Operations](#kubernetes-operations) - [Examples](#examples) - [Development](#development) - [Credits](#credits) - [License](#license) ---- ## Overview @@ -57,18 +37,19 @@ A command-line interface for managing OpenSlides instances. This tool provides d - **Deployment Setup**: Generate Docker Compose and Kubernetes configurations - **Kubernetes Management**: Deploy, update, and manage OpenSlides in Kubernetes - **Secrets Management**: Automatic generation of secure passwords and certificates -- **Datastore Queries**: Direct PostgreSQL access with advanced filtering +- **Database Queries**: Direct PostgreSQL access with advanced filtering - **Database Migrations**: Manage OpenSlides schema migrations with progress tracking - **User Management**: Create users and manage passwords - **Backend Actions**: Execute arbitrary OpenSlides actions ---- ## Installation + ### Binary Release Download the latest binary from the [releases page](https://github.com/OpenSlides/openslides-cli/releases): + ```bash # Linux AMD64 curl -L https://github.com/OpenSlides/openslides-cli/releases/latest/download/osmanage -o osmanage @@ -76,28 +57,31 @@ chmod +x osmanage sudo mv osmanage /usr/local/bin/ ``` + ### From Source **Requirements:** - Go 1.23 or later + ```bash git clone https://github.com/OpenSlides/openslides-cli.git cd openslides-cli CGO_ENABLED=0 go build -a -ldflags="-s -w" ./cmd/osmanage ``` ---- ## Quick Start + ### Docker Compose Setup Standard workflow for Docker Compose deployments: + ```bash # 1. Generate deployment configuration with random secrets osmanage setup ./my.instance.dir.org \ --config config.yml \ - --template docker-compose.yml + --template docker-compose.yml.tmpl # 2. Start services cd my.instance.dir.org @@ -117,40 +101,32 @@ docker compose down docker compose down -v ``` ---- ## Commands +This is giving an overview of a selection of available commands. See `--help` messages for more complete information. + + ### Instance Management Commands for creating and managing OpenSlides instance directories. ---- #### `setup` Creates a new instance directory with deployment configuration, secrets, and SSL certificates. **Usage:** + ```bash osmanage setup [flags] ``` -**Flags:** -- `-t, --template `: Template file or directory (required) -- `-c, --config `: YAML config file(s) (can be used multiple times, required) -- `-f, --force`: Overwrite existing files - **Generated Structure:** + ``` my.instance.dir.org/ โ”œโ”€โ”€ docker-compose.yml # (if using Docker Compose template) -โ”œโ”€โ”€ namespace.yaml # (if using Kubernetes template) -โ”œโ”€โ”€ stack/ # (if using Kubernetes template) -โ”‚ โ”œโ”€โ”€ autoupdate-deployment.yaml -โ”‚ โ”œโ”€โ”€ backend-deployment.yaml -โ”‚ โ”œโ”€โ”€ postgres-deployment.yaml -โ”‚ โ””โ”€โ”€ ... โ””โ”€โ”€ secrets/ โ”œโ”€โ”€ auth_token_key โ”œโ”€โ”€ auth_cookie_key @@ -162,46 +138,37 @@ my.instance.dir.org/ ``` **Examples:** + ```bash # Docker Compose deployment osmanage setup ./my.instance.dir.org \ --config config.yml \ - --template docker-compose.yml - -# Kubernetes deployment -osmanage setup ./my.instance.dir.org \ - --config k8s-config.yml \ - --template k8s-template-dir + --template docker-compose.yml.tmpl # Multiple config files (merged, later files override earlier) osmanage setup ./my.instance.dir.org \ --config base-config.yml \ --config prod-overrides.yml \ - --template k8s-template-dir + --template docker-compose.yml.tmpl # Overwrite existing instance osmanage setup ./my.instance.dir.org \ --config config.yml \ - --template k8s-template-dir \ + --template docker-compose.yml.tmpl \ --force ``` ---- #### `config` (Re)creates deployment configuration files from templates and YAML config files. **Usage:** + ```bash osmanage config [flags] ``` -**Flags:** -- `-t, --template `: Template file or directory (required) -- `-c, --config `: YAML config file(s) (can be used multiple times, required) -- `-f, --force`: Overwrite existing files - **Behavior:** - Merges multiple YAML config files (later file's fields override earlier ones) - Renders templates with merged configuration @@ -214,6 +181,7 @@ osmanage config [flags] - Fix or modify deployment manifests **Examples:** + ```bash # Regenerate deployment files osmanage config ./my.instance.dir.org \ @@ -235,428 +203,105 @@ osmanage config ./my.instance.dir.org \ **Note:** This command does NOT regenerate secrets - it only (re)creates deployment files. Use `osmanage setup` for initial instance creation with secrets, or `osmanage create` to update passwords. ---- - -#### `create` - -Updates an existing instance with new passwords. - -**Usage:** -```bash -osmanage create [flags] -``` - -**Flags:** -- `--db-password `: Set PostgreSQL password (required) -- `--superadmin-password `: Set superadmin password (required) - -**Use Cases:** -- Set specific passwords instead of random ones -- Rotate passwords for security -- Fix incorrect secret file permissions - -**Examples:** -```bash -# Set specific passwords -osmanage create ./my.instance.dir.org \ - --db-password "MySecureDBPassword123" \ - --superadmin-password "AdminPassword456" - -# Password rotation -osmanage create ./my.instance.dir.org \ - --db-password "$NEW_DB_PASS" \ - --superadmin-password "$NEW_ADMIN_PASS" -``` - ---- - -#### `remove` - -Deletes an instance directory and all its contents. - -**Usage:** -```bash -osmanage remove [flags] -``` - -**Flags:** -- `-f, --force`: Skip confirmation prompt - -**Warning:** This permanently deletes all files in the instance directory, including secrets and manifests. - -**Examples:** -```bash -# With confirmation prompt -osmanage remove ./my.instance.dir.org - -# Skip confirmation -osmanage remove ./my.instance.dir.org --force -``` - ---- - -### Kubernetes Operations - -Commands for managing OpenSlides instances in Kubernetes. - -**Requirements:** -- Valid kubeconfig file with cluster access (typically `~/.kube/config`) - - Or running inside a Kubernetes cluster with service account permissions -- Sufficient Kubernetes RBAC permissions to create/manage namespaces and resources - -**Note:** `osmanage` uses the Kubernetes Go client library and does **not** require `kubectl` to be installed. - ---- - -#### `k8s start` - -Deploys an OpenSlides instance to Kubernetes. - -**Usage:** -```bash -osmanage k8s start [flags] -``` - -**Flags:** -- `--kubeconfig `: Path to kubeconfig file (optional) -- `--skip-ready-check`: Skip waiting for instance to become ready -- `--timeout `: Maximum time to wait for deployment (default: 3m) - -**Features:** -- Creates dedicated namespace from namespace.yaml -- Creates secrets from instance secrets/ directory (base64-encoded) -- Applies all Kubernetes manifests from stack/ directory -- Shows progress bars for deployment readiness -- Waits for all pods to be healthy - -**Examples:** -```bash -# Standard deployment -osmanage k8s start ./my.instance.dir.org - -# Custom timeout -osmanage k8s start ./my.instance.dir.org --timeout 5m - -# Skip health check -osmanage k8s start ./my.instance.dir.org --skip-ready-check -``` - -**Output:** -``` -Applying manifest: my.instance.dir.org/namespace.yaml -Applied namespace: myinstancedirorg -Applying stack manifests from: my.instance.dir.org/stack/ -... - -[โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ] Pods ready (13/13) - -Instance is healthy: 13/13 pods ready -Instance started successfully -``` - ---- - -#### `k8s stop` - -Stops and removes an OpenSlides instance from Kubernetes. - -**Usage:** -```bash -osmanage k8s stop [flags] -``` - -**Flags:** -- `--kubeconfig `: Path to kubeconfig file (optional) -- `--timeout `: Maximum time to wait for deletion (default: 5m) - -**Behavior:** -- Saves TLS certificate secret (if exists) to `secrets/tls-letsencrypt-secret.yaml` -- Deletes the namespace and all resources - -**Warning:** This deletes the namespace and all resources, including persistent volumes. - -**Examples:** -```bash -# Stop instance -osmanage k8s stop ./my.instance.dir.org - -# Custom timeout -osmanage k8s stop ./my.instance.dir.org --timeout 10m -``` - ---- - -#### `k8s update-instance` - -Updates an existing Kubernetes instance with new manifests. - -**Usage:** -```bash -osmanage k8s update-instance [flags] -``` - -**Flags:** -- `--kubeconfig `: Path to kubeconfig file (optional) -- `--skip-ready-check`: Skip waiting for instance to become ready -- `--timeout `: Maximum time to wait for rollout (default: 3m) - -**Use Cases:** -- Apply configuration changes -- Update resource limits -- Modify service definitions -- Change replica counts - -**Examples:** -```bash -# Update after config changes -osmanage k8s update-instance ./my.instance.dir.org - -# Update with custom timeout -osmanage k8s update-instance ./my.instance.dir.org --timeout 5m - -# Skip health check -osmanage k8s update-instance ./my.instance.dir.org --skip-ready-check -``` - ---- - -#### `k8s update-backendmanage` - -Updates the backendmanage container image. - -**Usage:** -```bash -osmanage k8s update-backendmanage [flags] -``` - -**Flags (Required):** -- `--tag `: OpenSlides version tag (required) -- `--container-registry `: Container registry (required) -**Flags (Optional):** -- `--kubeconfig `: Path to kubeconfig file -- `--timeout `: Maximum time to wait for rollout (default: 3m) -- `--revert`: Revert to previous image (uses tag and registry as revert target) - -**Examples:** -```bash -# Update to specific version -osmanage k8s update-backendmanage ./my.instance.dir.org \ - --tag 4.2.0 \ - --container-registry myregistry - -# Update to latest -osmanage k8s update-backendmanage ./my.instance.dir.org \ - --tag latest \ - --container-registry myregistry - -# Revert to previous version -osmanage k8s update-backendmanage ./my.instance.dir.org \ - --tag 4.1.9 \ - --container-registry myregistry - --revert - -# Custom timeout -osmanage k8s update-backendmanage ./my.instance.dir.org \ - --tag 4.2.1 \ - --container-registry myregistry - --timeout 5m -``` - ---- +### Backend Actions -#### `k8s scale` +Commands for interacting with the OpenSlides backend API. -Scales a specific service deployment. +**Note:** All backend action commands require `--address` and `--password-file` flags. -**Usage:** -```bash -osmanage k8s scale [flags] -``` -**Flags (Required):** -- `--service `: Service deployment to scale (required) +#### `migrations` -**Flags (Optional):** -- `--kubeconfig `: Path to kubeconfig file -- `--skip-ready-check`: Skip waiting for deployment to become ready -- `--timeout `: Maximum time to wait for scaling (default: 3m) +Manage OpenSlides database migrations. -**Note:** You must edit the deployment manifest file (`stack/-deployment.yaml`) to change replica count before running this command. +**Subcommands:** +- `migrate`: Run migrations on auxiliary tables +- `finalize`: Apply migrations to live tables +- `reset`: Reset unapplied migrations +- `stats`: Show migration statistics +- `progress`: Check running migration progress **Examples:** -```bash -# Scale backend deployment (after editing manifest) -osmanage k8s scale ./my.instance.dir.org --service backend - -# Scale autoupdate without health check -osmanage k8s scale ./my.instance.dir.org --service autoupdate --skip-ready-check - -# Scale with custom timeout -osmanage k8s scale ./my.instance.dir.org --service backend --timeout 5m -``` - ---- - -#### `k8s health` - -Checks the health status of an OpenSlides instance. - -**Usage:** -```bash -osmanage k8s health [flags] -``` - -**Flags:** -- `--kubeconfig `: Path to kubeconfig file (optional) -- `--wait`: Wait for instance to become healthy -- `--timeout `: Timeout for health check (default: 3m, only with --wait) - -**Features:** -- Reports pod status for all deployments -- Shows ready/total pod counts -- Indicates overall instance health -**Example:** ```bash -# Check current health -osmanage k8s health ./my.instance.dir.org - -# Wait for instance to become healthy -osmanage k8s health ./my.instance.dir.org --wait --timeout 5m -``` - -**Output:** -``` -Namespace: myinstancedirorg -Ready: 13/13 pods - -Pod Status: - โœ“ auth-abc123 Running - โœ“ autoupdate-def456 Running - โœ“ backendaction-ghi789 Running - โœ“ backendmanage-jkl012 Running - โœ“ backendpresenter-mno345 Running - โœ“ client-pqr678 Running - โœ“ datastorereader-stu901 Running - โœ“ datastorewriter-vwx234 Running - โœ“ icc-yza567 Running - โœ“ media-bcd890 Running - โœ“ redis-efg123 Running - โœ“ search-hij456 Running - โœ“ vote-klm789 Running -``` - ---- +# Check migration status +osmanage migrations stats \ + --address localhost:9002 \ + --password-file ./secrets/internal_auth_password -#### `k8s cluster-status` +# Run migrations +osmanage migrations migrate \ + --address localhost:9002 \ + --password-file ./secrets/internal_auth_password -Displays comprehensive cluster status. +# Apply migrations +osmanage migrations finalize \ + --address localhost:9002 \ + --password-file ./secrets/internal_auth_password -**Usage:** -```bash -osmanage k8s cluster-status [flags] +# Apply without progress output +osmanage migrations finalize \ + --address localhost:9002 \ + --password-file ./secrets/internal_auth_password \ + --interval 0 ``` -**Flags:** -- `--kubeconfig `: Path to kubeconfig file (optional) - -**Features:** -- Shows cluster-wide node health -- Reports ready vs total nodes - -**Example:** -```bash -osmanage k8s cluster-status -``` +**Migration Stats Output:** -**Output:** ``` -cluster_status: 3 3 - -Total nodes: 3 -Ready nodes: 3 -Node node1: Ready -Node node2: Ready -Node node3: Ready -Cluster is healthy +current_migration_index: 15 +target_migration_index: 20 +positions: 1500 +events: 5000 +partially_migrated_positions: 500 +fully_migrated_positions: 1000 +status: migration_running ``` ---- - -### Backend Actions - -Commands for interacting with the OpenSlides backend API. -**Note:** All backend action commands require `--address` and `--password-file` flags. - ---- - -#### `action` +#### `initial-data` -Execute arbitrary OpenSlides backend actions. +Initialize a new OpenSlides database. **Usage:** -```bash -osmanage action [payload] [flags] -``` -**Flags (Required):** -- `-a, --address `: Backend service address (required) -- `--password-file `: Authorization password file (required) - -**Flags (Optional):** -- `-f, --file `: JSON payload file or `-` for stdin - -**Examples:** -```bash -# Docker Compose (localhost) -osmanage action meeting.create '[{"name": "Annual Meeting", "committee_id": 1, "language": "de", "admin_ids": [1]}]' \ - --address localhost:9002 \ - --password-file ./my.instance.dir.org/secrets/internal_auth_password +```bash +osmanage initial-data [flags] +``` -# Kubernetes (port-forwarded) -kubectl port-forward -n myinstancedirorg svc/backendmanage 9002:9002 & -osmanage action meeting.create '[{"name": "Board Meeting", "committee_id": 1, "language": "de", "admin_ids": [1]}]' \ - --address localhost:9002 \ - --password-file ./my.instance.dir.org/secrets/internal_auth_password +**Behavior:** +- Sets up organization and default data +- Sets superadmin (user ID 1) password +- Returns error if database is not empty (exit code 2) -# From file -osmanage action meeting.create \ - --file meeting.json \ - --address localhost:9002 \ - --password-file ./secrets/internal_auth_password +**Examples:** -# From stdin -echo '[{"name": "Test", "committee_id": 1, "language": "de", "admin_ids": [1]}]' | \ - osmanage action meeting.create --file - \ +```bash +# Docker Compose +osmanage initial-data \ --address localhost:9002 \ - --password-file ./secrets/internal_auth_password + --password-file ./secrets/internal_auth_password \ + --superadmin-password-file ./secrets/superadmin ``` ---- #### `create-user` Create a new OpenSlides user. **Usage:** + ```bash osmanage create-user [user-data] [flags] ``` -**Flags (Required):** -- `-a, --address `: Backend service address (required) -- `--password-file `: Authorization password file (required) - -**Flags (Optional):** -- `-f, --file `: JSON user data file or `-` for stdin - **Required JSON Fields:** - `username`: User login name - `default_password`: Initial password **Examples:** + ```bash # Inline JSON osmanage create-user '{"username": "admin", "default_password": "secret123"}' \ @@ -671,6 +316,7 @@ osmanage create-user \ ``` **user.json:** + ```json { "username": "mmax", @@ -682,13 +328,37 @@ osmanage create-user \ } ``` ---- + +#### `set-password` + +Change a user's password. + +**Usage:** + +```bash +osmanage set-password [flags] +``` + +**Example:** + +```bash +osmanage set-password \ + --address localhost:9002 \ + --password-file ./secrets/internal_auth_password \ + --user_id 5 \ + --password "newSecurePassword123" +``` + #### `get` -Query the OpenSlides datastore with advanced filtering. +Query the OpenSlides database with advanced filtering. + +> [!IMPORTANT] +> Requires access to postgres. Examples assume port is forwarded. **Usage:** + ```bash osmanage get [flags] ``` @@ -698,19 +368,6 @@ osmanage get [flags] - `meeting` - `organization` -**Flags (Required):** -- `--postgres-host `: PostgreSQL host (required) -- `--postgres-port `: PostgreSQL port (required) -- `--postgres-user `: PostgreSQL user (required) -- `--postgres-database `: PostgreSQL database (required) -- `--postgres-password-file `: PostgreSQL password file (required) - -**Flags (Optional):** -- `--fields `: Comma-separated field list -- `--filter `: Simple equality filters (can be used multiple times, AND'ed together) -- `--filter-raw `: Complex JSON filter with operators -- `--exists`: Return boolean instead of data (requires filter) - **Supported Operators (in `--filter-raw`):** - `=`: Equal - `!=`: Not equal @@ -722,7 +379,6 @@ osmanage get [flags] **Examples:** -**Docker Compose:** ```bash # Simple query osmanage get user --fields first_name,last_name,email \ @@ -742,6 +398,7 @@ osmanage get user --filter is_active=true \ ``` **Complex filters:** + ```bash # Regex matching osmanage get user \ @@ -772,208 +429,200 @@ osmanage get meeting \ --postgres-password-file ./secrets/postgres_password ``` -**Output Format:** -```json -{ - "1": { - "id": 1, - "username": "admin", - "first_name": "Admin", - "last_name": "User", - "is_active": true - }, - "2": { - "id": 2, - "username": "mmax", - "first_name": "Max", - "last_name": "Mustermann", - "is_active": true - } -} -``` - ---- -#### `initial-data` +#### `set` -Initialize a new OpenSlides datastore. +Update OpenSlides objects using backend actions. **Usage:** + ```bash -osmanage initial-data [flags] +osmanage set [payload] [flags] ``` -**Flags (Required):** -- `-a, --address `: Backend service address (required) -- `--password-file `: Authorization password file (required) -- `--superadmin-password-file `: Superadmin password file (required) - -**Flags (Optional):** -- `-f, --file `: JSON initial data file or `-` for stdin - -**Behavior:** -- Sets up organization and default data -- Sets superadmin (user ID 1) password -- Returns error if datastore is not empty (exit code 2) +**Supported Actions:** +- `agenda_item`, `committee`, `group`, `meeting`, `motion`, `organization`, `organization_tag`, `projector`, `theme`, `topic`, `user` **Examples:** + ```bash -# Docker Compose -osmanage initial-data \ +# Update user +osmanage set user '[{"id": 5, "first_name": "Jane", "last_name": "Smith"}]' \ --address localhost:9002 \ - --password-file ./secrets/internal_auth_password \ - --superadmin-password-file ./secrets/superadmin -``` + --password-file ./secrets/internal_auth_password ---- +# Update from file +osmanage set meeting \ + --file meeting-update.json \ + --address localhost:9002 \ + --password-file ./secrets/internal_auth_password +``` -#### `migrations` -Manage OpenSlides database migrations. +#### `action` -**Subcommands:** -- `migrate`: Prepare migrations (dry-run) -- `finalize`: Apply migrations to datastore -- `reset`: Reset unapplied migrations -- `clear-collectionfield-tables`: Clear auxiliary tables (offline only) -- `stats`: Show migration statistics -- `progress`: Check running migration progress +Execute arbitrary OpenSlides backend actions. -**Flags (Required):** -- `-a, --address `: Backend service address (required) -- `--password-file `: Authorization password file (required) +**Usage:** -**Flags (Optional):** -- `--interval `: Progress check interval (default: `1s`, use `0` to disable, only for migrate/finalize) +```bash +osmanage action [payload] [flags] +``` **Examples:** + ```bash -# Check migration status -osmanage migrations stats \ +# Docker Compose (localhost) +osmanage action meeting.create '[{"name": "Annual Meeting", "committee_id": 1, "language": "de", "admin_ids": [1]}]' \ --address localhost:9002 \ - --password-file ./secrets/internal_auth_password + --password-file ./my.instance.dir.org/secrets/internal_auth_password -# Prepare migrations (dry-run) -osmanage migrations migrate \ +# Kubernetes (port-forwarded) +kubectl port-forward -n myinstancedirorg svc/backendmanage 9002:9002 & +osmanage action meeting.create '[{"name": "Board Meeting", "committee_id": 1, "language": "de", "admin_ids": [1]}]' \ --address localhost:9002 \ - --password-file ./secrets/internal_auth_password + --password-file ./my.instance.dir.org/secrets/internal_auth_password -# Apply migrations -osmanage migrations finalize \ +# From file +osmanage action meeting.create \ + --file meeting.json \ --address localhost:9002 \ --password-file ./secrets/internal_auth_password -# Apply without progress output -osmanage migrations finalize \ +# From stdin +echo '[{"name": "Test", "committee_id": 1, "language": "de", "admin_ids": [1]}]' | \ + osmanage action meeting.create --file - \ --address localhost:9002 \ - --password-file ./secrets/internal_auth_password \ - --interval 0 + --password-file ./secrets/internal_auth_password ``` -**Migration Stats Output:** -``` -current_migration_index: 15 -target_migration_index: 20 -positions: 1500 -events: 5000 -partially_migrated_positions: 500 -fully_migrated_positions: 1000 -status: migration_running -``` ---- +### Kubernetes Operations -#### `set` +Commands for managing OpenSlides instances in Kubernetes. -Update OpenSlides objects using backend actions. +> [!CAUTION] +> These commands are still experimental. Use with caution! + +**Requirements:** +- Valid kubeconfig file with cluster access (typically `~/.kube/config`) + - Or running inside a Kubernetes cluster with service account permissions +- Sufficient Kubernetes RBAC permissions to create/manage namespaces and resources + +**Note:** `osmanage` uses the Kubernetes Go client library and does **not** require `kubectl` to be installed. + + +#### `k8s start` + +Deploys an OpenSlides instance to Kubernetes. **Usage:** + ```bash -osmanage set [payload] [flags] +osmanage k8s start [flags] ``` -**Supported Actions:** -- `agenda_item`, `committee`, `group`, `meeting`, `motion`, `organization`, `organization_tag`, `projector`, `theme`, `topic`, `user` +**Features:** +- Creates dedicated namespace from namespace.yaml +- Creates secrets from instance secrets/ directory (base64-encoded) +- Applies all Kubernetes manifests from stack/ directory +- Shows progress bars for deployment readiness +- Waits for all pods to be healthy -**Flags (Required):** -- `-a, --address `: Backend service address (required) -- `--password-file `: Authorization password file (required) -**Flags (Optional):** -- `-f, --file `: JSON payload file or `-` for stdin +#### `k8s stop` -**Examples:** -```bash -# Update user -osmanage set user '[{"id": 5, "first_name": "Jane", "last_name": "Smith"}]' \ - --address localhost:9002 \ - --password-file ./secrets/internal_auth_password +Stops and removes an OpenSlides instance from Kubernetes. -# Update from file -osmanage set meeting \ - --file meeting-update.json \ - --address localhost:9002 \ - --password-file ./secrets/internal_auth_password +**Usage:** + +```bash +osmanage k8s stop [flags] ``` ---- +**Behavior:** +- Saves TLS certificate secret (if exists) to `secrets/tls-letsencrypt-secret.yaml` +- Deletes the namespace and all resources + +**Warning:** This deletes the namespace and all resources, including persistent volumes. -#### `set-password` -Change a user's password. +#### `k8s update-instance` + +Updates an existing Kubernetes instance with new manifests. **Usage:** + ```bash -osmanage set-password [flags] +osmanage k8s update-instance [flags] ``` -**Flags (Required):** -- `-a, --address `: Backend service address (required) -- `--password-file `: Authorization password file (required) -- `-u, --user_id `: User ID (required) -- `-p, --password `: New password (required) +**Use Cases:** +- Apply configuration changes +- Update resource limits +- Modify service definitions +- Change replica counts + + +#### `k8s update-backendmanage` + +Updates the backendmanage container image. + +**Usage:** -**Example:** ```bash -osmanage set-password \ - --address localhost:9002 \ - --password-file ./secrets/internal_auth_password \ - --user_id 5 \ - --password "newSecurePassword123" +osmanage k8s update-backendmanage [flags] ``` ---- -## Configuration +#### `k8s scale` + +Scales a specific service deployment. -### Logging Levels +**Usage:** -Control verbosity with the global `--log-level` flag: ```bash -osmanage --log-level debug k8s start ./my.instance.dir.org +osmanage k8s scale [flags] ``` -**Available levels:** -- `debug`: Detailed diagnostic information -- `info`: General informational messages -- `warn`: Warning messages only (default) -- `error`: Error messages only +**Note:** You must edit the deployment manifest file (`stack/-deployment.yaml`) to change replica count before running this command. + + +#### `k8s health` + +Checks the health status of an OpenSlides instance. + +**Usage:** -**Example output:** +```bash +osmanage k8s health [flags] ``` -[INFO] === K8S START === -[DEBUG] Namespace: myinstancedirorg -[INFO] Applying Kubernetes manifests... -[DEBUG] Applied manifest: namespace.yaml -[INFO] Waiting for instance to become ready... -[INFO] Instance started successfully + +**Features:** +- Reports pod status for all deployments +- Shows ready/total pod counts +- Indicates overall instance health + + +#### `k8s cluster-status` + +Displays comprehensive cluster status. + +**Usage:** + +```bash +osmanage k8s cluster-status [flags] ``` ---- +**Features:** +- Shows cluster-wide node health +- Reports ready vs total nodes + ## Examples ### Complete Kubernetes Workflow + ```bash # 1. Generate instance osmanage setup ./prod.instance.org \ @@ -1003,9 +652,9 @@ osmanage k8s update-backendmanage ./prod.instance.org \ osmanage k8s stop ./prod.instance.org ``` ---- -### Backup User Data +### Query User Data + ```bash # Export all users osmanage get user \ @@ -1027,9 +676,9 @@ osmanage get user \ > backup-users-minimal.json ``` ---- ### Query Active Meetings + ```bash # Get all active meetings with details osmanage get meeting \ @@ -1062,86 +711,12 @@ osmanage get meeting \ --postgres-password-file ./secrets/postgres_password ``` ---- ## Development -### Project Structure -``` -openslides-cli/ -โ”œโ”€โ”€ cmd/ -โ”‚ โ””โ”€โ”€ osmanage/ # Main entry point -โ”‚ โ”œโ”€โ”€ main.go -โ”‚ โ””โ”€โ”€ main_test.go -โ”œโ”€โ”€ internal/ -โ”‚ โ”œโ”€โ”€ constants/ # Project-wide constants -โ”‚ โ”‚ โ””โ”€โ”€ constants.go -โ”‚ โ”œโ”€โ”€ instance/ # Instance management -โ”‚ โ”‚ โ”œโ”€โ”€ config/ -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ config.go -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ config_test.go -โ”‚ โ”‚ โ”œโ”€โ”€ create/ -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ create.go -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ create_test.go -โ”‚ โ”‚ โ”œโ”€โ”€ remove/ -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ remove.go -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ remove_test.go -โ”‚ โ”‚ โ””โ”€โ”€ setup/ -โ”‚ โ”‚ โ”œโ”€โ”€ setup.go -โ”‚ โ”‚ โ””โ”€โ”€ setup_test.go -โ”‚ โ”œโ”€โ”€ k8s/ # Kubernetes operations -โ”‚ โ”‚ โ”œโ”€โ”€ actions/ -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ apply.go -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ cluster_status.go -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ cluster_status_test.go -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ health.go -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ health_check.go -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ health_check_test.go -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ scale.go -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ start.go -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ stop.go -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ update_backendmanage.go -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ update_instance.go -โ”‚ โ”‚ โ””โ”€โ”€ client/ -โ”‚ โ”‚ โ””โ”€โ”€ client.go -โ”‚ โ”œโ”€โ”€ manage/ # Backend action commands -โ”‚ โ”‚ โ”œโ”€โ”€ actions/ -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ action/ -โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ action.go -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ createuser/ -โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ createuser.go -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ get/ -โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ get.go -โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ get_test.go -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ initialdata/ -โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ initialdata.go -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ integration_test.go -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ migrations/ -โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ migrations.go -โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ migrations_test.go -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ set/ -โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ set.go -โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ set_test.go -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ setpassword/ -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ setpassword.go -โ”‚ โ”‚ โ””โ”€โ”€ client/ -โ”‚ โ”‚ โ”œโ”€โ”€ client.go -โ”‚ โ”‚ โ””โ”€โ”€ client_test.go -โ”‚ โ”œโ”€โ”€ logger/ # Logging utilities -โ”‚ โ”‚ โ”œโ”€โ”€ logger.go -โ”‚ โ”‚ โ””โ”€โ”€ logger_test.go -โ”‚ โ””โ”€โ”€ utils/ # Common utilities -โ”‚ โ”œโ”€โ”€ utils.go -โ”‚ โ””โ”€โ”€ utils_test.go -โ”œโ”€โ”€ go.mod -โ”œโ”€โ”€ go.sum -โ”œโ”€โ”€ README.md -โ””โ”€โ”€ LICENSE -``` - ---- ### Running Tests + ```bash # Run all tests go test ./... @@ -1156,9 +731,9 @@ go test ./internal/k8s/actions go test -v ./... ``` ---- ### Building + ```bash # Development build (larger binary, debuggable) go build -o osmanage ./cmd/osmanage @@ -1167,7 +742,6 @@ go build -o osmanage ./cmd/osmanage CGO_ENABLED=0 go build -a -ldflags="-s -w" -o osmanage ./cmd/osmanage ``` ---- ### Contributing @@ -1187,7 +761,6 @@ CGO_ENABLED=0 go build -a -ldflags="-s -w" -o osmanage ./cmd/osmanage - Update documentation as needed - Use project constants from `internal/constants` ---- ## Credits @@ -1210,15 +783,13 @@ This tool represents a significant refactor and expansion of the original [opens **Original work by:** Norman Jรคckel ---- ## License This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. ---- ## Support - **Issues**: [GitHub Issues](https://github.com/OpenSlides/openslides-cli/issues) -- **Discussions**: [GitHub Discussions](https://github.com/OpenSlides/openslides-cli/discussions) \ No newline at end of file +- **Discussions**: [GitHub Discussions](https://github.com/OpenSlides/openslides-cli/discussions) diff --git a/internal/constants/constants.go b/internal/constants/constants.go index fcd9821..084421b 100644 --- a/internal/constants/constants.go +++ b/internal/constants/constants.go @@ -148,7 +148,7 @@ const ( BackendContentType string = "application/json" ) -// PostgreSQL datastore environment variable keys (used by get command) +// Environment variable keys (used by get command) const ( // EnvDatabaseHost is the environment variable for PostgreSQL host EnvDatabaseHost string = "DATABASE_HOST" @@ -169,7 +169,7 @@ const ( EnvOpenSlidesDevelopment string = "OPENSLIDES_DEVELOPMENT" ) -// PostgreSQL datastore environment variable values +// Environment variable values const ( // DevelopmentModeDisabled is the value to disable OpenSlides development mode DevelopmentModeDisabled string = "false" @@ -178,7 +178,7 @@ const ( DevelopmentModeEnabled string = "true" ) -// OpenSlides datastore defaults +// OpenSlides database defaults const ( // DefaultOrganizationID is the organization ID in OpenSlides (always 1) DefaultOrganizationID int = 1 diff --git a/internal/manage/actions/get/get.go b/internal/manage/actions/get/get.go index 5918dbd..616e816 100644 --- a/internal/manage/actions/get/get.go +++ b/internal/manage/actions/get/get.go @@ -23,7 +23,7 @@ import ( ) const ( - GetHelp = "Get models from the datastore" + GetHelp = "Get models from the database" GetHelpExtra = `Provide a collection to list contained models. Use options to narrow down output. @@ -201,7 +201,7 @@ func ExecuteGetCollection(ctx context.Context, dbConfig *pb.DatabaseConfig, para } } - // Create environment map for datastore connection + // Create environment map for database connection envMap := map[string]string{ constants.EnvDatabaseHost: dbConfig.Host, constants.EnvDatabasePort: dbConfig.Port, diff --git a/internal/manage/actions/initialdata/initialdata.go b/internal/manage/actions/initialdata/initialdata.go index 62e2cab..01c15f2 100644 --- a/internal/manage/actions/initialdata/initialdata.go +++ b/internal/manage/actions/initialdata/initialdata.go @@ -15,13 +15,13 @@ import ( ) const ( - InitialDataHelp = "Creates initial data if the datastore is empty" + InitialDataHelp = "Creates initial data if the database is empty" InitialDataHelpExtra = `This command sets up initial data for a new OpenSlides instance. Provide initial data via --file flag with a JSON file path, or use --file=- to read from stdin. If no file is provided, empty initialization data will be used. This command also sets the superadmin (user 1) password from the superadmin password file. -It returns an error if the datastore is not empty. +It returns an error if the database is not empty. Examples: osmanage initial-data \ @@ -99,9 +99,9 @@ func Cmd() *cobra.Command { body, err := client.CheckResponse(resp) if err != nil { - if bytes.Contains(body, []byte("Datastore is not empty")) { - logger.Warn("Datastore is not empty") - fmt.Fprintln(os.Stderr, "Datastore contains data, initial data were NOT set") + if bytes.Contains(body, []byte("is not empty")) { + logger.Warn("Database is not empty") + fmt.Fprintln(os.Stderr, "Database contains data, initial data were NOT set") os.Exit(2) } return err diff --git a/internal/manage/actions/migrations/migrations.go b/internal/manage/actions/migrations/migrations.go index 033fa85..09933c0 100644 --- a/internal/manage/actions/migrations/migrations.go +++ b/internal/manage/actions/migrations/migrations.go @@ -18,7 +18,7 @@ import ( const ( MigrationsHelp = "Wrapper to the OpenSlides backend migration tool" - MigrationsHelpExtra = `Run database migrations on the OpenSlides datastore. + MigrationsHelpExtra = `Run database migrations. Examples: # Check migration status @@ -26,12 +26,12 @@ Examples: --address :9002 \ --password-file my.instance.dir/secrets/internal_auth_password - # Prepare migrations (dry run) + # Run migrations on auxiliary tables osmanage migrations migrate \ --address :9002 \ --password-file my.instance.dir/secrets/internal_auth_password - # Apply migrations to datastore + # Apply migrations to live tables osmanage migrations finalize \ --address :9002 \ --password-file my.instance.dir/secrets/internal_auth_password @@ -53,10 +53,9 @@ Examples: --interval 2s Available commands: - migrate Prepare migrations (dry run) - finalize Apply migrations to datastore + migrate Run migrations on auxiliary tables + finalize Apply migrations to live tables reset Reset unapplied migrations - clear-collectionfield-tables Clear auxiliary tables (offline only) stats Show migration statistics progress Check running migration progress` ) @@ -81,17 +80,18 @@ func Cmd() *cobra.Command { } func migrateCmd() *cobra.Command { - return createMigrationCmd("migrate", "Prepare migrations but do not apply them to the datastore", true) + return createMigrationCmd("migrate", "Prepare migrations but do not apply them to the live tables", true) } func finalizeCmd() *cobra.Command { - return createMigrationCmd("finalize", "Prepare migrations and apply them to the datastore", true) + return createMigrationCmd("finalize", "Prepare migrations and apply them to the live tables", true) } func resetCmd() *cobra.Command { return createMigrationCmd("reset", "Reset unapplied migrations", false) } +// TODO: unavailable after 4.3.0, delete when backward compatibility no longer needed func clearCollectionfieldTablesCmd() *cobra.Command { return createMigrationCmd("clear-collectionfield-tables", "Clear all data from auxiliary tables (only when OpenSlides is offline)", false) }