diff --git a/README.md b/README.md index dc72517..3cd5b2a 100644 --- a/README.md +++ b/README.md @@ -1,114 +1,695 @@ -# ui-operator -// TODO(user): Add simple overview of use/purpose +# UI Operator + +A Kubernetes operator for managing micro-frontend applications and UI components in a declarative way. + +[![Go Report Card](https://goreportcard.com/badge/github.com/ui-operator/ui-operator)](https://goreportcard.com/report/github.com/ui-operator/ui-operator) +[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) + +## Overview + +The UI Operator enables organizations to deploy and manage complex web applications built with micro-frontend architecture. It provides a declarative approach to: + +- **Deploy UI applications**: Manage main UI shell applications that host micro-frontends +- **Orchestrate UI components**: Deploy and configure individual micro-frontend components +- **Configure runtime exposure**: Set up routing, authentication, and runtime configuration for components +- **Handle dependencies**: Automatically manage relationships between UI applications and their components + +### Architecture + +The operator manages three Custom Resource Definitions (CRDs): + +- **`ScalityUI`** (cluster-scoped): Defines the main UI application with global configuration +- **`ScalityUIComponent`** (namespaced): Represents individual micro-frontend components +- **`ScalityUIComponentExposer`** (namespaced): Configures how components are exposed and integrated + +> **Note:** The architecture diagram below uses Mermaid syntax which may not render in all Markdown viewers. A static image version is available [here](https://github.com/scality/ui-operator/blob/main/docs/images/architecture.png). + +```mermaid +graph TD + subgraph ControlPlane["Config & Orchestration Layer"] + User(["User/Admin"]) + Operator["UI Operator
(Reconciliation Loop)"] + K8sAPI["Kubernetes API"] + CRDs["Custom Resource Definitions"] + + subgraph CustomResources["Custom Resources"] + ScalityUI["ScalityUI
(Main Shell Config)"] + ScalityUIComp["ScalityUIComponent
(Remote Module)"] + ScalityUIExp["ScalityUIComponentExposer
(Integration Config)"] + end + + K8sResources["K8s Resources
(Deployments, Services, etc.)"] + Status["Status & Events"] + end + + subgraph DataPlane["Application Layer"] + Pods["Running Pods
(UI Components)"] + Ingress["Ingress/Service
(Exposure)"] + ShellUI["Shell Frontend
(React Application)"] + EndUser(["End User Browser"]) + end + + User ==>|"Installs"| CRDs + User ==>|"Installs"| Operator + User ==>|"Creates"| ScalityUI + User ==>|"Creates"| ScalityUIComp + User ==>|"Creates"| ScalityUIExp + + ScalityUI -.->|"Stored in"| K8sAPI + ScalityUIComp -.->|"Stored in"| K8sAPI + ScalityUIExp -.->|"Stored in"| K8sAPI + + K8sAPI ==>|"Watch events"| Operator + Operator ==>|"Creates/Updates"| K8sResources + K8sResources ==>|"Creates"| Pods + Pods ==>|"Exposed via"| Ingress + + Operator -.->|"Updates"| Status + Status -.->|"Available via"| K8sAPI + + ShellUI ==>|"Loads dynamically"| Ingress + EndUser ==>|"Accesses"| ShellUI + + classDef operator fill:#1E88E5,color:white,stroke:none + classDef crd fill:#43A047,color:white,stroke:none + classDef resource fill:#FF8F00,color:white,stroke:none + classDef k8sComponents fill:#FF8F00,color:white,stroke:none + classDef plane fill:#121212,stroke:#424242,stroke-width:1px,color:white + classDef user fill:#9C27B0,color:white,stroke:none + classDef status fill:#F44336,color:white,stroke:none + + class Operator operator + class CRDs crd + class ScalityUI,ScalityUIComp,ScalityUIExp,K8sResources resource + class K8sAPI,Ingress,Pods,ShellUI k8sComponents + class Status status + class User,EndUser user + class ControlPlane,DataPlane plane +``` + +## Table of Contents -## Description -// TODO(user): An in-depth paragraph about your project and overview of use +- [Quick Start](#quick-start) +- [Installation](#installation) +- [Basic Usage](#basic-usage) +- [Configuration](#configuration) +- [API Reference](#api-reference) +- [Features](#features) +- [Examples](#examples) +- [Operations](#operations) +- [Development](#development) -## Getting Started +## Quick Start ### Prerequisites -- go version v1.22.0+ -- docker version 17.03+. -- kubectl version v1.11.3+. -- Access to a Kubernetes v1.11.3+ cluster. -### To Deploy on the cluster -**Build and push your image to the location specified by `IMG`:** +- Kubernetes cluster v1.11.3+ +- kubectl v1.11.3+ +- Cluster admin permissions + +### Installation + +1. **Install the operator:** + + ```bash + # Using latest stable release (recommended for production) + kubectl apply -f https://raw.githubusercontent.com/ui-operator/ui-operator/0.1.0/dist/install.yaml + + # Using specific version + kubectl apply -f https://raw.githubusercontent.com/ui-operator/ui-operator/{version}/dist/install.yaml + + # Using main branch (not recommended for production) + kubectl apply -f https://raw.githubusercontent.com/ui-operator/ui-operator/main/dist/install.yaml + ``` + +2. **Verify installation:** + + ```bash + kubectl get pods -n ui-operator-system + ``` + +3. **Check CRDs are installed:** + ```bash + kubectl get crd | grep ui.scality.com + ``` + +### Basic Usage + +1. **Create a UI application:** + + ```yaml + apiVersion: ui.scality.com/v1alpha1 + kind: ScalityUI + metadata: + name: my-ui-app + spec: + image: "my-registry/ui-shell:v1.0.0" + productName: "My Application" + ``` + +2. **Deploy a UI component:** + + ```yaml + apiVersion: ui.scality.com/v1alpha1 + kind: ScalityUIComponent + metadata: + name: dashboard-component + namespace: my-app + spec: + image: "my-registry/dashboard:v1.0.0" + mountPath: "/app/config" + ``` + +3. **Expose the component:** + ```yaml + apiVersion: ui.scality.com/v1alpha1 + kind: ScalityUIComponentExposer + metadata: + name: dashboard-exposer + namespace: my-app + spec: + scalityUI: "my-ui-app" + scalityUIComponent: "dashboard-component" + appHistoryBasePath: "/dashboard" + ``` + +## Installation + +### Method 1: Using Pre-built Manifests (Recommended) + +```bash +# Using latest stable release (recommended for production) +kubectl apply -f https://raw.githubusercontent.com/ui-operator/ui-operator/0.1.0/dist/install.yaml + +# Using specific version +kubectl apply -f https://raw.githubusercontent.com/ui-operator/ui-operator/{version}/dist/install.yaml + +# Using main branch (not recommended for production) +kubectl apply -f https://raw.githubusercontent.com/ui-operator/ui-operator/main/dist/install.yaml +``` + +### Method 2: Using Kustomize + +1. **Clone the repository:** + + ```bash + git clone https://github.com/ui-operator/ui-operator.git + cd ui-operator + ``` + +2. **Install CRDs:** + + ```bash + make install + ``` + +3. **Deploy the operator:** + ```bash + make deploy IMG=ui-operator/ui-operator:latest + ``` + +### Method 3: Building from Source -```sh -make docker-build docker-push IMG=/ui-operator:tag +1. **Build and push the operator image:** + + ```bash + make docker-build docker-push IMG=/ui-operator:tag + ``` + +2. **Deploy with your custom image:** + ```bash + make deploy IMG=/ui-operator:tag + ``` + +### Verification + +Check that the operator is running: + +```bash +# Check operator pod +kubectl get pods -n ui-operator-system + +# Check CRDs +kubectl get crd | grep ui.scality.com + +# Check operator logs +kubectl logs -n ui-operator-system deployment/ui-operator-controller-manager ``` -**NOTE:** This image ought to be published in the personal registry you specified. -And it is required to have access to pull the image from the working environment. -Make sure you have the proper permission to the registry if the above commands don’t work. +## Configuration + +### ScalityUI Resource + +The `ScalityUI` resource defines the main UI application: + +```yaml +apiVersion: ui.scality.com/v1alpha1 +kind: ScalityUI +metadata: + name: my-shell-ui-app +spec: + image: "my-registry/ui-shell:v1.0.0" + productName: "My Application" + themes: + light: + type: "core-ui" + name: "light-theme" + logo: + type: "path" + value: "/assets/logo.png" + navbar: + main: + - internal: + kind: "navigation" + view: "dashboard" + icon: "dashboard" + label: + en: "Dashboard" + networks: + ingressClassName: "nginx" + host: "my-app.example.com" + tls: + - secretName: "ui-tls" + hosts: + - "my-app.example.com" + auth: + kind: "OIDC" + providerUrl: "https://auth.example.com" + clientId: "my-ui-app" + scopes: "openid email profile" +``` -**Install the CRDs into the cluster:** +### ScalityUIComponent Resource + +The `ScalityUIComponent` resource defines individual micro-frontend components: + +```yaml +apiVersion: ui.scality.com/v1alpha1 +kind: ScalityUIComponent +metadata: + name: monitoring-component + namespace: ui +spec: + image: "my-registry/monitoring-ui:v1.2.0" + mountPath: "/app/configs" + imagePullSecrets: + - name: registry-secret +``` -```sh -make install +### ScalityUIComponentExposer Resource + +The `ScalityUIComponentExposer` resource configures how components are exposed: + +```yaml +apiVersion: ui.scality.com/v1alpha1 +kind: ScalityUIComponentExposer +metadata: + name: monitoring-exposer + namespace: ui +spec: + scalityUI: "my-shell-ui-app" + scalityUIComponent: "monitoring-component" + appHistoryBasePath: "/monitoring" + selfConfiguration: + apiEndpoint: "https://api.example.com/monitoring" + features: + - "metrics" + - "alerts" +``` + +## API Reference + +### ScalityUI Fields + +| Field | Type | Required | Description | +| ------------------ | ------ | -------- | ----------------------------------------- | +| `image` | string | Yes | Container image for the main UI shell | +| `productName` | string | Yes | Product name displayed in the UI | +| `themes` | object | No | Light and dark theme configurations | +| `navbar` | object | No | Navigation bar configuration | +| `networks` | object | No | Networking and ingress configuration | +| `auth` | object | No | Default authentication configuration | +| `imagePullSecrets` | array | No | Image pull secrets for private registries | + +### ScalityUIComponent Fields + +| Field | Type | Required | Description | +| ------------------ | ------ | -------- | ------------------------------------------ | +| `image` | string | Yes | Container image for the UI component | +| `mountPath` | string | Yes | Path where configuration files are mounted | +| `imagePullSecrets` | array | No | Image pull secrets for private registries | + +### ScalityUIComponentExposer Fields + +| Field | Type | Required | Description | +| -------------------- | ------ | -------- | ---------------------------------------- | +| `scalityUI` | string | Yes | Reference to ScalityUI resource | +| `scalityUIComponent` | string | Yes | Reference to ScalityUIComponent resource | +| `appHistoryBasePath` | string | Yes | Base path for the component in the URL | +| `selfConfiguration` | object | No | Runtime configuration for the component | + +## Features + +### ✅ Core Features + +- [x] **Micro-frontend Management**: Deploy and manage multiple UI components +- [x] **Runtime Configuration**: Dynamic configuration injection for components +- [x] **Authentication Integration**: OIDC and other auth providers +- [x] **Ingress Management**: Automatic routing and exposure +- [x] **Dependency Management**: Automatic relationship handling +- [x] **Rolling Updates**: Zero-downtime updates for components + +## Examples + +### Basic Setup + +```yaml +# UI Application +apiVersion: ui.scality.com/v1alpha1 +kind: ScalityUI +metadata: + name: basic-ui +spec: + image: "nginx:latest" + productName: "Basic App" + +--- +# UI Component +apiVersion: ui.scality.com/v1alpha1 +kind: ScalityUIComponent +metadata: + name: hello-component + namespace: default +spec: + image: "nginx:latest" + mountPath: "/app/config" + +--- +# Component Exposer +apiVersion: ui.scality.com/v1alpha1 +kind: ScalityUIComponentExposer +metadata: + name: hello-exposer + namespace: default +spec: + scalityUI: "basic-ui" + scalityUIComponent: "hello-component" + appHistoryBasePath: "/hello" +``` + +### With Authentication + +```yaml +apiVersion: ui.scality.com/v1alpha1 +kind: ScalityUI +metadata: + name: secure-ui +spec: + image: "my-registry/ui-shell:v1.0.0" + productName: "Secure Application" + auth: + kind: "OIDC" + providerUrl: "https://auth.example.com" + clientId: "secure-ui" + responseType: "code" + scopes: "openid email profile" + providerLogout: true +``` + +### Multi-Component Setup + +```yaml +# Multiple components with different configurations +apiVersion: ui.scality.com/v1alpha1 +kind: ScalityUIComponentExposer +metadata: + name: dashboard-exposer + namespace: default +spec: + scalityUI: "main-ui" + scalityUIComponent: "dashboard-component" + appHistoryBasePath: "/dashboard" + selfConfiguration: + theme: "dark" + refreshInterval: 30 + +--- +apiVersion: ui.scality.com/v1alpha1 +kind: ScalityUIComponentExposer +metadata: + name: settings-exposer + namespace: default +spec: + scalityUI: "main-ui" + scalityUIComponent: "settings-component" + appHistoryBasePath: "/settings" +``` + +## Operations + +### Monitoring + +The operator exposes Prometheus metrics at `/metrics`: + +```yaml +# ServiceMonitor for Prometheus +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: ui-operator-metrics +spec: + selector: + matchLabels: + app.kubernetes.io/name: ui-operator + endpoints: + - port: metrics + interval: 30s + path: /metrics ``` -**Deploy the Manager to the cluster with the image specified by `IMG`:** +### Troubleshooting -```sh -make deploy IMG=/ui-operator:tag +#### Check Resource Status + +```bash +# Check UI status +kubectl get scalityui my-ui-app -o yaml + +# Check component status +kubectl describe scalityuicomponent my-component + +# Check exposer status +kubectl get scalityuicomponentexposer my-exposer -o yaml ``` -> **NOTE**: If you encounter RBAC errors, you may need to grant yourself cluster-admin -privileges or be logged in as admin. +#### View Logs -**Create instances of your solution** -You can apply the samples (examples) from the config/sample: +```bash +# Operator logs +kubectl logs -n ui-operator-system deployment/ui-operator-controller-manager -```sh -kubectl apply -k config/samples/ +# Component logs +kubectl logs deployment/my-component ``` ->**NOTE**: Ensure that the samples has default values to test it out. +#### Common Issues + +1. **Component not starting**: Check image pull secrets and registry access +2. **Configuration not applied**: Verify exposer references correct UI and component +3. **Ingress not working**: Check ingress controller and network configuration + +### Backup and Restore -### To Uninstall -**Delete the instances (CRs) from the cluster:** +```bash +# Backup all UI resources +kubectl get scalityui,scalityuicomponent,scalityuicomponentexposer -o yaml > ui-backup.yaml -```sh -kubectl delete -k config/samples/ +# Restore resources +kubectl apply -f ui-backup.yaml ``` -**Delete the APIs(CRDs) from the cluster:** +### Upgrading -```sh -make uninstall +```bash +# Update to latest stable release (recommended) +kubectl apply -f https://raw.githubusercontent.com/ui-operator/ui-operator/0.1.0/dist/install.yaml + +# Update to specific version +kubectl apply -f https://raw.githubusercontent.com/ui-operator/ui-operator/{version}/dist/install.yaml + +# Update using main branch (not recommended for production) +kubectl apply -f https://raw.githubusercontent.com/ui-operator/ui-operator/main/dist/install.yaml + +# Check operator version +kubectl get deployment -n ui-operator-system ui-operator-controller-manager -o jsonpath='{.spec.template.spec.containers[0].image}' ``` -**UnDeploy the controller from the cluster:** +### Uninstalling + +```bash +# Delete all custom resources +kubectl delete scalityui --all +kubectl delete scalityuicomponent --all +kubectl delete scalityuicomponentexposer --all -```sh -make undeploy +# Remove operator (use the same version you installed) +kubectl delete -f https://raw.githubusercontent.com/ui-operator/ui-operator/0.1.0/dist/install.yaml + +# Remove CRDs (optional) +kubectl delete crd scalityuis.ui.scality.com +kubectl delete crd scalityuicomponents.ui.scality.com +kubectl delete crd scalityuicomponentexposers.ui.scality.com ``` -## Project Distribution +## Security & RBAC + +The UI Operator leverages Kubernetes Role-Based Access Control (RBAC) to secure access to resources. When installed, the operator creates several roles and bindings to manage permissions. + +### Roles Created by the Operator + +The following roles are created during installation: + +1. **Manager Role (ClusterRole)**: + + - Used by the operator to manage resources + - Permissions to manage deployments, services, configmaps, ingresses, etc. + - Full access to the CRDs it manages + +2. **Editor Roles (ClusterRole)**: + + - `scalityui-editor-role`: For users who need to create/update/delete ScalityUI resources + - `scalityuicomponent-editor-role`: For users who need to create/update/delete ScalityUIComponent resources + - `scalityuicomponentexposer-editor-role`: For users who need to create/update/delete ScalityUIComponentExposer resources + +3. **Viewer Roles (ClusterRole)**: + + - `scalityui-viewer-role`: For users who only need read access to ScalityUI resources + - `scalityuicomponent-viewer-role`: For users who only need read access to ScalityUIComponent resources + - `scalityuicomponentexposer-viewer-role`: For users who only need read access to ScalityUIComponentExposer resources + +4. **Support Roles**: + - `metrics-reader-role`: For accessing Prometheus metrics + - `leader-election-role`: For the operator's leader election mechanism + +### Required Permissions for Users + +Different users require different levels of permissions: + +#### Cluster Administrators + +- Need permissions to install the operator and CRDs +- Required permissions: `cluster-admin` or equivalent + +#### Application Operators + +- Need permissions to create and manage ScalityUI resources (cluster-scoped) +- Required ClusterRoleBinding: `scalityui-editor-role` + +#### Application Developers + +- Need permissions to create and manage components in their namespaces +- Required RoleBindings in relevant namespaces: + - `scalityuicomponent-editor-role` + - `scalityuicomponentexposer-editor-role` -Following are the steps to build the installer and distribute this project to users. +#### Read-only Users -1. Build the installer for the image built and published in the registry: +- Need permissions to view but not modify resources +- Required RoleBindings: Viewer roles for respective resource types -```sh -make build-installer IMG=/ui-operator:tag +### Assigning RBAC Permissions + +To assign permissions to a user or group: + +```yaml +# Example: Granting a user edit permissions for UI Components +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: developer-ui-component-editor + namespace: my-app-namespace +subjects: + - kind: User + name: developer@example.com + apiGroup: rbac.authorization.k8s.io +roleRef: + kind: ClusterRole + name: scalityuicomponent-editor-role + apiGroup: rbac.authorization.k8s.io ``` -NOTE: The makefile target mentioned above generates an 'install.yaml' -file in the dist directory. This file contains all the resources built -with Kustomize, which are necessary to install this project without -its dependencies. +### Security Best Practices + +1. **Follow Least Privilege Principle**: + + - Assign the minimum necessary permissions + - Use viewer roles for users who only need read access + +2. **Namespace Isolation**: + + - Create components and exposers in dedicated namespaces + - Limit access to those namespaces with RoleBindings + +3. **Audit Role Assignments**: + + - Regularly review who has access to create/modify resources + - Use `kubectl auth can-i` to verify permissions + +4. **Secure Access to the Main UI Resource**: + - Since ScalityUI is cluster-scoped, carefully control who can create/modify them -2. Using the installer +## Development -Users can just run kubectl apply -f to install the project, i.e.: +### Building from Source -```sh -kubectl apply -f https://raw.githubusercontent.com//ui-operator//dist/install.yaml +```bash +# Clone the repository +git clone https://github.com/ui-operator/ui-operator.git +cd ui-operator + +# Build the operator +make build + +# Run tests +make test + +# Build Docker image +make docker-build IMG=/ui-operator:tag ``` -## Contributing -// TODO(user): Add detailed information on how you would like others to contribute to this project +### Running Locally -**NOTE:** Run `make help` for more information on all potential `make` targets +```bash +# Install CRDs +make install -More information can be found via the [Kubebuilder Documentation](https://book.kubebuilder.io/introduction.html) +# Run operator locally (outside cluster) +make run +``` -## License +### Testing -Copyright 2025. +```bash +# Run unit tests +make test -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at +# Run tests with coverage +make test-cover - http://www.apache.org/licenses/LICENSE-2.0 +# Run e2e tests +make test-e2e -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. +# Run specific controller tests +go test ./internal/controller/scalityuicomponentexposer -v +``` + +### Project Structure +``` +├── api/v1alpha1/ # CRD type definitions +├── internal/controller/ # Controller implementations +├── config/ # Kubernetes manifests +├── examples/ # Example configurations +├── test/ # Test files +└── Makefile # Build targets +``` diff --git a/docs/images/architecture.png b/docs/images/architecture.png new file mode 100644 index 0000000..661ab51 Binary files /dev/null and b/docs/images/architecture.png differ