diff --git a/README.md b/README.md index 38ff767..4e9f24f 100644 --- a/README.md +++ b/README.md @@ -16,37 +16,17 @@ This repository contains two main components: - [Listener](./docs/listener.md): watches a cluster and stores its openAPI spec in a directory. - [Gateway](./docs/gateway.md): exposes the openAPI spec as a GraphQL endpoints. -## MultiCluster Support +## Operation Modes -The system supports three modes of operation: +The system supports two modes of operation: -1. **Single Cluster** (`ENABLE_KCP=false`, `MULTICLUSTER=false`): Gateway connects to the same cluster as the listener -2. **KCP Mode** (`ENABLE_KCP=true`): Designed for KCP-based multi-cluster scenarios -3. **MultiCluster Mode** (`ENABLE_KCP=false`, `MULTICLUSTER=true`): Gateway connects to multiple external clusters via ClusterAccess resources +1. **KCP Mode** (`ENABLE_KCP=true`): Designed for KCP-based multi-cluster scenarios + - See [Virtual Workspaces](./docs/virtual-workspaces.md) for advanced KCP configuration +2. **ClusterAccess Mode** (`ENABLE_KCP=false`): Designed for support of multiple standard clusters. -### MultiCluster with ClusterAccess +## ClusterAccess -In MultiCluster mode, the system uses ClusterAccess resources to store kubeconfig data and connection information. The listener processes these resources and embeds connection metadata into schema files, which the gateway then uses to establish cluster-specific connections. - -For complete setup instructions, see: -- [ClusterAccess documentation](./docs/clusteraccess.md) - Manual setup -- [MultiCluster Kubeconfig Flow](./docs/multicluster-kubeconfig-flow.md) - Detailed flow explanation - -### Quick Setup Scripts - -```bash -# Create ClusterAccess with secure token authentication -./scripts/create-clusteraccess.sh --target-kubeconfig ~/.kube/prod-config - -# Test end-to-end integration -./scripts/test-clusteraccess-integration.sh -``` - -### Gateway Requirements - -- **Single Cluster Mode**: Requires KUBECONFIG to connect to the local cluster -- **KCP Mode**: Requires KUBECONFIG to connect to KCP management cluster -- **MultiCluster Mode**: Does NOT require KUBECONFIG - gets all connection info from schema files +For detailed information, see [Clusteraccess](./docs/clusteraccess.md) section. ## Authorization @@ -71,3 +51,4 @@ If you find any bug that may be a security problem, please follow our instructio ## Licensing Copyright 2025 SAP SE or an SAP affiliate company and platform-mesh contributors. Please see our [LICENSE](LICENSE) for copyright and license information. Detailed information including third-party components and their licensing/copyright information is available [via the REUSE tool](https://api.reuse.software/info/github.com/platform-mesh/kubernetes-graphql-gateway). + diff --git a/docs/authorization.md b/docs/authorization.md index bf59bd8..d069072 100644 --- a/docs/authorization.md +++ b/docs/authorization.md @@ -3,7 +3,7 @@ All requests must contain an `Authorization` header with a valid Bearer token by default: ```shell { - "Authorization": $YOUR_TOKEN + "Authorization": "Bearer $YOUR_TOKEN" } ``` You can disable authorization by setting the following environment variable: @@ -14,21 +14,21 @@ This is useful for local development and testing purposes. ## Introspection authentication -By default, introspection requests (i.e. the requests that are made to fetch the GraphQL schema) are **not** protected by authorization. +By default, introspection requests (i.e., the requests that are made to fetch the GraphQL schema) are **not** protected by authorization. You can protect those requests by setting the following environment variable: ```shell -export INTROSPECTION_AUTHENTICATION=true +export GATEWAY_INTROSPECTION_AUTHENTICATION=true ``` -### Error fetching schema +### Error fetching schema in documentation explorer -When GraphiQL page is loaded, it makes a request to fetch the GraphQL schema and there is no way to add the `Authorization` header to that request. +When the GraphiQL page is loaded, it makes a request to fetch the GraphQL schema, and there is no way to add the `Authorization` header to that request. We have this [issue](https://github.com/openmfp/kubernetes-graphql-gateway/issues/217) open to fix this. But for now, you can use the following workaround: 1. Open the GraphiQL page in your browser. -2. Add the `Authorization` header in the `Headers` section of the GraphiQL user interface like so: -3. Press `Re-fetch GraphQL schema` button in the left sidebar(third button from the top). -4. Now the GraphQL schema should be fetched, and you can use the GraphiQL interface as usual. +2. Add the `Authorization` header in the `Headers` section of the GraphiQL user interface. +3. Press the `Re-fetch GraphQL schema` button in the left sidebar (third button from the top). +4. The GraphQL schema should now be fetched, and you can use the GraphiQL interface as usual. diff --git a/docs/clusteraccess-setup.md b/docs/clusteraccess-setup.md new file mode 100644 index 0000000..e39cfed --- /dev/null +++ b/docs/clusteraccess-setup.md @@ -0,0 +1,184 @@ +# ClusterAccess Resource Setup + +To enable the gateway to access external Kubernetes clusters, you need to create ClusterAccess resources. This section provides both an automated script and manual step-by-step instructions. + +## Quick Setup (Recommended) + +For development purposes, use the provided script to automatically create ClusterAccess resources: + +**Example:** +```bash +./hack/create-clusteraccess.sh --target-kubeconfig ~/.kube/platform-mesh-config --management-kubeconfig ~/.kube/platform-mesh-config +``` + +The script will: +- Extract cluster name, server URL, and CA certificate from the target kubeconfig +- Create a ServiceAccount with cluster-admin access in the target cluster +- Generate a long-lived token for the ServiceAccount +- Create the admin kubeconfig and CA secrets in the management cluster +- Create the ClusterAccess resource with kubeconfig-based authentication +- Output a copy-paste ready bearer token for direct API access + +## Manual Setup + +## Prerequisites + +- Access to the target cluster (the cluster you want to expose via GraphQL) +- Access to the management cluster (the cluster where the gateway runs) +- ClusterAccess CRDs installed in the management cluster +- Target cluster kubeconfig file + +## Step 1: Create ServiceAccount with Admin Access in Target Cluster + +```bash +# Switch to target cluster +export KUBECONFIG=/path/to/target-cluster-kubeconfig + +# Create ServiceAccount with cluster-admin access +cat </graphql` which will be used to query the resources of that workspace. -It watches for changes in the directory and update the schema accordingly. +Each file in that directory corresponds to a KCP workspace or a target cluster (via ClusterAccess). +It creates a separate URL for each file, like `//graphql` which will be used to query the resources of that workspace or cluster. +It watches for changes in the directory and updates the schema accordingly. So, if there are two files in the directory - `root` and `root:alpha`, then we will have two URLs: - `http://localhost:3000/root/graphql` @@ -15,13 +15,23 @@ See example queries in the [Queries Examples](./quickstart.md#first-steps-and-ba ## Packages Overview -### Workspace Manager -Holds the logic for watching a directory, triggering schema generation, and binding it to an HTTP handler. +### Manager (`gateway/manager/`) -### Schema +Manages the gateway lifecycle and cluster connections: +- **Watcher**: Watches the definitions directory for schema file changes +- **Target Cluster**: Manages cluster registry and GraphQL endpoint routing +- **Round Tripper**: Handles HTTP request routing and authentication -Is responsible for the conversion from OpenAPI spec into the GraphQL schema. +### Schema (`gateway/schema/`) -### Resolver +Converts OpenAPI specifications into GraphQL schemas: +- Generates GraphQL types from OpenAPI definitions +- Handles custom queries and relations between resources +- Manages scalar type mappings -Holds the logic of interaction with the cluster. +### Resolver (`gateway/resolver/`) + +Executes GraphQL queries against Kubernetes clusters: +- Resolves GraphQL queries to Kubernetes API calls +- Handles subscriptions for real-time updates +- Processes query arguments and filters diff --git a/docs/listener.md b/docs/listener.md index c0c8e29..761bd0f 100644 --- a/docs/listener.md +++ b/docs/listener.md @@ -1,6 +1,40 @@ # Listener -The Listener component is responsible for watching a Kubernetes cluster and generating OpenAPI specifications for discovered resources. +The Listener component is responsible for watching Kubernetes clusters and generating OpenAPI specifications for discovered resources. It stores these specifications in a directory, which can then be used by the [Gateway](./gateway.md) component to expose them as GraphQL endpoints. -The Listener creates a separate file for each KCP workspace in the specified directory. -The Gateway will then watch this directory for changes and update the GraphQL schema accordingly. + +In **KCP mode**, it creates a separate file for each KCP workspace. In **ClusterAccess mode**, it creates a file for each ClusterAccess resource representing a target cluster. + +The Gateway watches this directory for changes and updates the GraphQL schema accordingly. + +## Packages Overview + +### Reconciler (`listener/reconciler/`) + +Contains reconciliation logic for different operational modes: + +#### ClusterAccess Reconciler (`reconciler/clusteraccess/`) +- Watches ClusterAccess resources in the management cluster +- Connects to target clusters using kubeconfig-based authentication +- Generates schema files with embedded cluster connection metadata +- Injects `x-cluster-metadata` into schema files for gateway consumption + +#### KCP Reconciler (`reconciler/kcp/`) +- Watches APIBinding resources in KCP workspaces +- Discovers virtual workspaces and their API resources +- Handles cluster path resolution for KCP workspace hierarchies +- Generates schema files for each workspace + +### Packages (`listener/pkg/`) + +Supporting packages for schema generation: + +#### API Schema (`pkg/apischema/`) +- Builds OpenAPI specifications from Kubernetes API resources +- Resolves Custom Resource Definitions (CRDs) +- Converts Kubernetes API schemas to OpenAPI format +- Handles resource relationships and dependencies + +#### Workspace File (`pkg/workspacefile/`) +- Manages reading and writing schema files to the definitions directory +- Handles file I/O operations for schema persistence diff --git a/docs/local_test.md b/docs/local_test.md index 7d75180..957374a 100644 --- a/docs/local_test.md +++ b/docs/local_test.md @@ -1,21 +1,20 @@ # Test Locally -**Warning!** This test is for those who have access to `helm-charts-priv`. - ## Run and check cluster 1. Create and run a cluster if it is not running yet. ```shell -git clone https://github.com/platform-mesh/helm-charts-priv.git -cd helm-charts-priv +git clone https://github.com/platform-mesh/helm-charts.git +cd helm-charts task local-setup ``` +If this task fails, you can try to run `task local-setup:iterate` to complete it. 2. Verify that the cluster is running. -Run k9s, go to `:pods`. All pods must have a status of "Running". -It may take some time before they are all ready. The possible issues may be insufficient RAM and/or CPU cores. In this case, increase the limits in Docker settings. +Run k9s and go to `:pods`. All pods should have a status of "Running". +It may take some time before they are all ready. Possible issues include insufficient RAM and/or CPU cores. In this case, increase the limits in Docker settings. 3. In k9s, go to `:pods`, then open pod `kubernetes-graphql-gateway-...`. @@ -23,7 +22,7 @@ Open container `kubernetes-graphql-gateway-gateway` to see the logs. The logs must contain more than a single line (with "Starting server..."). If you see only this single line, the problem might be in the container called "kubernetes-graphql-gateway-listener". -Note the `IMAGE` column, corresponding to the two `kubernetes-...` container. It contains the name and the currently used version of the build, i.e. +Note the image name from one of the `kubernetes-...` containers. It contains the name and the currently used version of the build, e.g.: ``` ghcr.io/platform-mesh/kubernetes-graphql-gateway:v0.75.1 ``` @@ -33,11 +32,11 @@ ghcr.io/platform-mesh/kubernetes-graphql-gateway:v0.75.1 task docker ``` -5. Tag the newly built image with the version used in local-setup -- that image is going to be replaced with the one built on step 4. +5. Tag the newly built image with the version used in local-setup: ```shell docker tag ghcr.io/platform-mesh/kubernetes-graphql-gateway:latest ghcr.io/platform-mesh/kubernetes-graphql-gateway:v0.75.1 ``` -Use the name you and version got from the `IMAGE` column on step 3. Leave the version number unchanged. +Use the name and version you got from the `IMAGE` column in step 3. 6. Check your cluster name: ```shell @@ -50,7 +49,7 @@ In this example, the cluster name is `platform-mesh`. ```shell kind load docker-image ghcr.io/platform-mesh/kubernetes-graphql-gateway:v0.75.1 -n platform-mesh ``` -The argument `-n platform-mesh` is to change the default value of the cluster name, which is `kind`. +The argument `-n platform-mesh` targets the platform-mesh kind cluster. ***Podman-based kind:*** - Pull (or build) the image locally with Podman: @@ -70,7 +69,7 @@ kind load image-archive kubernetes-graphql-gateway_v0.75.1.tar -n platform-mesh 8. In k9s, go to `:pods` and delete the pod (not the container) called `kubernetes-graphql-gateway-...`. -Kubernetes will immediately recreate the pod -- but this time it will use the new version of the build. +Kubernetes will immediately recreate the pod - but this time it will use the new version of the build. 9. Once the pod is recreated, go to [https://portal.dev.local:8443](https://portal.dev.local:8443) and check if everything works fine. diff --git a/docs/multicluster-kubeconfig-flow.md b/docs/multicluster-kubeconfig-flow.md deleted file mode 100644 index 53682fe..0000000 --- a/docs/multicluster-kubeconfig-flow.md +++ /dev/null @@ -1,160 +0,0 @@ -# MultiCluster Kubeconfig Flow - -This document explains how the kubeconfig storage and usage flow works when `ENABLE_KCP=false` and `MULTICLUSTER=true`. - -## Overview - -The system is designed to work in the following way: - -1. **ClusterAccess Resources**: Store connection information for target clusters, including kubeconfig data -2. **Listener**: Processes ClusterAccess resources and generates schema files with embedded connection metadata -3. **Gateway**: Reads schema files and uses embedded metadata to connect to specific clusters - -## Flow Details - -### 1. ClusterAccess Resource Creation - -```yaml -apiVersion: gateway.platform-mesh.io/v1alpha1 -kind: ClusterAccess -metadata: - name: my-target-cluster -spec: - path: my-target-cluster # Used as schema filename - host: https://my-cluster-api-server:6443 - auth: - kubeconfigSecretRef: - name: my-cluster-kubeconfig - namespace: default - ca: - secretRef: - name: my-cluster-ca - namespace: default - key: ca.crt -``` - -### 2. Listener Processing - -When running with `ENABLE_KCP=false` and `MULTICLUSTER=true`: - -```bash -export ENABLE_KCP=false -export MULTICLUSTER=true -export KUBECONFIG=/path/to/management-cluster-config -./listener -``` - -The listener: -- Uses the `ClusterAccessReconciler` -- Watches for ClusterAccess resources -- For each ClusterAccess: - - Extracts cluster connection info (host, auth, CA) - - Connects to the target cluster to discover API schema - - Generates schema JSON with Kubernetes API definitions - - Injects `x-cluster-metadata` with connection information - - Saves schema file to `definitions/{cluster-name}.json` - -### 3. Schema File Structure - -Generated schema files contain: - -```json -{ - "definitions": { - // ... Kubernetes API definitions - }, - "x-cluster-metadata": { - "host": "https://my-cluster-api-server:6443", - "path": "my-target-cluster", - "auth": { - "type": "kubeconfig", - "kubeconfig": "base64-encoded-kubeconfig" - }, - "ca": { - "data": "base64-encoded-ca-cert" - } - } -} -``` - -### 4. Gateway Usage - -When running the gateway with `ENABLE_KCP=false` and `MULTICLUSTER=true`: - -```bash -export ENABLE_KCP=false -export MULTICLUSTER=true -# NOTE: KUBECONFIG is NOT needed for gateway in multicluster mode -./gateway -``` - -The gateway: -- Watches the definitions directory for schema files -- For each schema file: - - Reads the `x-cluster-metadata` section - - Creates a `rest.Config` using the embedded connection info - - Establishes a Kubernetes client connection to the target cluster - - Serves GraphQL API at `/{cluster-name}/graphql` -- **Does NOT require KUBECONFIG** - all connection info comes from schema files - -## Authentication Methods Supported - -### 1. Bearer Token -```yaml -auth: - secretRef: - name: my-cluster-token - namespace: default - key: token -``` - -### 2. Kubeconfig -```yaml -auth: - kubeconfigSecretRef: - name: my-cluster-kubeconfig - namespace: default -``` - -### 3. Client Certificates -```yaml -auth: - clientCertificateRef: - name: my-cluster-certs - namespace: default -``` - -## Key Benefits - -1. **Centralized Management**: All cluster access is managed through ClusterAccess resources -2. **Secure Storage**: Credentials stored in Kubernetes secrets -3. **Automatic Discovery**: API schemas automatically discovered from target clusters -4. **Standard Patterns**: Uses `ctrl.GetConfigOrDie()` pattern for configuration loading -5. **Simple Gateway Logic**: Gateway doesn't need complex certificate/token handling - -## Testing - -Use the provided integration test: - -```bash -./scripts/test-clusteraccess-integration.sh -``` - -This test verifies the end-to-end flow with kubeconfig-based authentication. - -## Troubleshooting - -### Schema files not generated -- Check that ClusterAccess CRD is installed: `kubectl apply -f config/crd/` -- Verify ClusterAccess resources exist: `kubectl get clusteraccess` -- Check listener logs for connection errors to target clusters - -### Gateway not connecting to clusters -- Verify schema files contain `x-cluster-metadata` -- Check gateway logs for authentication errors -- Ensure credentials in secrets are valid - -### Connection errors -- Verify target cluster URLs are accessible -- Check CA certificates are correct -- Validate authentication credentials have required permissions \ No newline at end of file diff --git a/docs/quickstart.md b/docs/quickstart.md index f4e37a1..2987e67 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -1,34 +1,26 @@ # Quick Start -This page shows you how to get started to use the GraphQL Gateway for Kubernetes. +This page shows you how to get started using the GraphQL Gateway for Kubernetes. ## Prerequisites - Installed [Golang](https://go.dev/doc/install) - Installed [Taskfile](https://taskfile.dev/installation) - A Kubernetes cluster to connect to (some options below) - - Option A: Prexisting standard Kuberentes cluster - - Option B: Preexisting Kuberentes cluster that is available through [Kuberentes Control Plane (KCP)](https://docs.kcp.io/kcp/main/setup/quickstart/) - - Option C: Create your own locally running Kuberentes cluster using [kind](https://kind.sigs.k8s.io/) + - Option A: Preexisting standard Kubernetes cluster + - Option B: Preexisting Kubernetes cluster that is available through [Kubernetes Control Plane (KCP)](https://docs.kcp.io/kcp/main/setup/quickstart/) + - Option C: Create your own locally running Kubernetes cluster using [kind](https://kind.sigs.k8s.io/) - Clone the `kubernetes-graphql-gateway` repository and change to the root directory ```shell -git clone git@github.com:openmfp/kubernetes-graphql-gateway.git && cd kubernetes-graphql-gateway +git clone https://github.com/platform-mesh/kubernetes-graphql-gateway.git && cd kubernetes-graphql-gateway ``` +## Operation Modes -## Setup the environment: -```shell -# this will disable authorization -export LOCAL_DEVELOPMENT=true -# kcp is enabled by default, in case you want to run it against a standard Kubernetes cluster -export ENABLE_KCP=false -# you must point to the config of the cluster you want to run against -export KUBECONFIG=YOUR_KUBECONFIG_PATH -``` - +Don't skip this step. Please go to the [operation modes](../README.md#operation-modes) page and complete the required setup. ## Running the Listener -Make sure you have done steps from the [setup section](#setup-the-environment). +Make sure you have completed the steps from the [Prerequisites](#prerequisites) and [Operation Modes](#operation-modes) sections. ```shell task listener @@ -39,9 +31,9 @@ The file will contain the API definitions for the resources in that workspace. ## Running the Gateway -Make sure you have done steps from the [setup section](#setup-the-environment). +Make sure you have completed the steps from the [Prerequisites](#prerequisites) section. -In the root directory of the `kubernetes-graphql-gateway` repository, open a new shell and run the Graphql gateway as follows: +In the root directory of the `kubernetes-graphql-gateway` repository, open a new shell and run the GraphQL gateway as follows: ```shell task gateway ``` @@ -52,21 +44,21 @@ Check the console output to get the localhost URL of the GraphQL playground. ## First Steps and Basic Examples -As said above, the GraphQL Gateway allows you do CRUD operations on any of the Kubernetes resources in the cluster. -You may checkout the following copy & paste examples to get started: -- Examples on [CRUD operations on ConfigMaps](./configmap_queries.md). -- Examples on [CRUD operations on Pods](./pod_queries.md). -- Subscribe to events using [Subscriptions](./subscriptions.md). -- There are also [Custom Queries](./custom_queries.md) that go beyond what. +As mentioned above, the GraphQL Gateway allows you to do CRUD operations on any of the Kubernetes resources in the cluster. +You may check out the following copy & paste examples to get started: +- Examples on [CRUD operations on ConfigMaps](./configmap_queries.md) +- Examples on [CRUD operations on Pods](./pod_queries.md) +- Subscribe to events using [Subscriptions](./subscriptions.md) +- There are also [Custom Queries](./custom_queries.md) that go beyond standard CRUD operations -## Authorization with Remote Kuberenetes Clusters +## Authorization with Remote Kubernetes Clusters -If you run the GraphQL gateway with an shell environment that sets `LOCAL_DEVELOPMENT=false`, you need to add the `Authorization` header to any of your GraphQL queries you are executing. +If you run the GraphQL gateway with a shell environment that sets `LOCAL_DEVELOPMENT=false`, you need to add the `Authorization` header to any of your GraphQL queries you are executing. When using the GraphQL playground, you can add the header in the `Headers` section of the playground user interface like so: ```shell { - "Authorization": "YOUR_TOKEN" + "Authorization": "Bearer YOUR_TOKEN" } ``` diff --git a/docs/subscriptions.md b/docs/subscriptions.md index 11f1261..12c6cbf 100644 --- a/docs/subscriptions.md +++ b/docs/subscriptions.md @@ -1,33 +1,33 @@ # Subscriptions To subscribe to events, you should use the SSE (Server-Sent Events) protocol. -Since GraphQL playground doesn't support (see [Quick Start Guide](./quickstart.md)) we won't use the GraphQL playground to execute the queries. -Instead we use the `curl` command line tool to execute the queries. +Since GraphQL playground doesn't support SSE (see [Quick Start Guide](./quickstart.md)), we won't use the GraphQL playground to execute the queries. +Instead, we use the `curl` command line tool to execute the queries. ## Prerequisites ```shell -export GRAPHQL_URL=http://localhost:8080/root/graphql # update with your actual GraphQL endpoint -export AUTHORIZATION_TOKEN= # update this with your token, if LOCAL_DEVELOPMENT=false +export GRAPHQL_URL=http://localhost:8080/root/graphql # Update with your actual GraphQL endpoint +export AUTHORIZATION_TOKEN="Bearer " # Update with your token if LOCAL_DEVELOPMENT=false ``` ## Parameters -- `subscribeToAll`: if true, any field change will be sent to the client. +- `subscribeToAll`: If true, any field change will be sent to the client. Otherwise, only fields defined within the `{}` brackets will be listened to. -Please note that only fields specified in `{}` brackets will be returned, even if `subscribeToAll: true` +**Note:** Only fields specified in `{}` brackets will be returned, even if `subscribeToAll: true`. -### Return parameters +### Return Parameters -- `data` field contains the data returned by the subscription. -- `errors` field contains the errors if any occurred during the subscription. +- `data`: Contains the data returned by the subscription. +- `errors`: Contains the errors if any occurred during the subscription. ## Subscribe to the ConfigMap Resource ConfigMap is present in both KCP and standard Kubernetes clusters, so we can use it right away without any additional setup. -After subscription, you can run mutations from [Configmap Queries](./configmap_queries.md) to see the changes in the subscription. +After subscription, you can run mutations from [ConfigMap Queries](./configmap_queries.md) to see the changes in the subscription. -### Subscribe to a Change of a Data Field in All ConfigMaps: +### Subscribe to a Change of a Data Field in All ConfigMaps ```shell curl \ -H "Accept: text/event-stream" \ @@ -36,7 +36,7 @@ curl \ -d '{"query": "subscription { core_configmaps { metadata { name } data }}"}' \ $GRAPHQL_URL ``` -### Subscribe to a Change of a Data Field in a Specific ConfigMap: +### Subscribe to a Change of a Data Field in a Specific ConfigMap ```shell curl \ @@ -47,9 +47,7 @@ curl \ $GRAPHQL_URL ``` -### Subscribe to a Change of All Fields in a Specific ConfigMap: - -Please note that only fields specified in `{}` brackets will be returned, even if `subscribeToAll: true` +### Subscribe to a Change of All Fields in a Specific ConfigMap ```shell curl \ @@ -86,7 +84,7 @@ curl \ $GRAPHQL_URL ``` -### Subscribe to a Change of a DisplayName Field in All Accounts +### Subscribe to a Change of All Fields in a Specific Account ```shell curl \ -H "Accept: text/event-stream" \ diff --git a/docs/virtual-workspaces.md b/docs/virtual-workspaces.md index 07b530a..3b691ad 100644 --- a/docs/virtual-workspaces.md +++ b/docs/virtual-workspaces.md @@ -21,7 +21,7 @@ virtualWorkspaces: - `virtualWorkspaces`: Array of virtual workspace definitions - `name`: Unique identifier for the virtual workspace (used in URL paths) - `url`: Full URL to the virtual workspace or API export - - `kubeconfig`: path to kcp kubeconfig + - `kubeconfig`: Path to KCP kubeconfig ## Environment Variables diff --git a/hack/create-clusteraccess.md b/hack/create-clusteraccess.md new file mode 100644 index 0000000..5056457 --- /dev/null +++ b/hack/create-clusteraccess.md @@ -0,0 +1,14 @@ +# Create ClusterAccess script + +This script is used to create a ClusterAccess resource, which is needed for kubernetes-graphql-gateway to work with Standard K8S cluster. + +More details about it you can find at [this readme](../docs/clusteraccess.md) + +## Usage + +```shell +./hack/create-clusteraccess.sh --target-kubeconfig $TARGET_CLUSTER_KUBECONFIG --management-kubeconfig $MANAGEMENT_CLUSTER_KUBECONFIG +``` +Where +- TARGET_CLUSTER_KUBECONFIG - path to the kubeconfig of the cluster we want the gateway to generate graphql schema +- MANAGEMENT_CLUSTER_KUBECONFIG - path to the kubeconfig of the cluster where ClusterAccess object will be created. It can be the same cluster as TARGET_CLUSTER_KUBECONFIG. \ No newline at end of file diff --git a/scripts/create-clusteraccess.sh b/hack/create-clusteraccess.sh similarity index 69% rename from scripts/create-clusteraccess.sh rename to hack/create-clusteraccess.sh index 78cba30..a370f2b 100755 --- a/scripts/create-clusteraccess.sh +++ b/hack/create-clusteraccess.sh @@ -99,9 +99,38 @@ if [[ -z "$CLUSTER_NAME" ]]; then fi log_info "Cluster name: $CLUSTER_NAME" +ensure_crd_installed() { + log_info "Ensuring ClusterAccess CRD is installed in management cluster..." + + SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + CRD_PATH="$SCRIPT_DIR/../config/crd/gateway.platform-mesh.io_clusteraccesses.yaml" + + if [[ ! -f "$CRD_PATH" ]]; then + log_error "CRD file not found at: $CRD_PATH" + log_error "Please ensure the CRD file exists in the expected location" + exit 1 + fi + + if ! KUBECONFIG="$MANAGEMENT_KUBECONFIG" kubectl apply -f "$CRD_PATH"; then + log_error "Failed to apply ClusterAccess CRD" + exit 1 + fi + + log_info "Waiting for ClusterAccess CRD to become established..." + if ! KUBECONFIG="$MANAGEMENT_KUBECONFIG" kubectl wait --for=condition=Established crd/clusteraccesses.gateway.platform-mesh.io --timeout=60s; then + log_error "ClusterAccess CRD failed to reach Established condition within 60 seconds" + exit 1 + fi + + log_info "ClusterAccess CRD is established and ready" +} + cleanup_existing_resources() { log_info "Checking for existing ClusterAccess resource '$CLUSTER_NAME'..." + SA_NAME="kubernetes-graphql-gateway-admin" + SA_NAMESPACE="default" + # Check if ClusterAccess exists in management cluster if KUBECONFIG="$MANAGEMENT_KUBECONFIG" kubectl get clusteraccess "$CLUSTER_NAME" &>/dev/null; then log_warn "ClusterAccess '$CLUSTER_NAME' already exists. Cleaning up existing resources..." @@ -120,6 +149,14 @@ cleanup_existing_resources() { else log_info "No existing ClusterAccess found. Creating new resources..." fi + + # Clean up ServiceAccount and related resources in target cluster + if KUBECONFIG="$TARGET_KUBECONFIG" kubectl get serviceaccount "$SA_NAME" -n "$SA_NAMESPACE" &>/dev/null; then + log_info "Cleaning up existing ServiceAccount and related resources in target cluster..." + KUBECONFIG="$TARGET_KUBECONFIG" kubectl delete secret "${SA_NAME}-token" -n "$SA_NAMESPACE" --ignore-not-found=true + KUBECONFIG="$TARGET_KUBECONFIG" kubectl delete clusterrolebinding "${SA_NAME}-cluster-admin" --ignore-not-found=true + KUBECONFIG="$TARGET_KUBECONFIG" kubectl delete serviceaccount "$SA_NAME" -n "$SA_NAMESPACE" --ignore-not-found=true + fi } log_info "Creating ClusterAccess resource '$CLUSTER_NAME'" @@ -174,6 +211,54 @@ if ! KUBECONFIG="$MANAGEMENT_KUBECONFIG" kubectl cluster-info &>/dev/null; then fi log_info "Management cluster is accessible" +# Create ServiceAccount with admin access in target cluster +log_info "Creating ServiceAccount with admin access in target cluster..." +SA_NAME="kubernetes-graphql-gateway-admin" +SA_NAMESPACE="default" + +cat </dev/null; then - log_error "Cannot connect to management cluster" - exit 1 -fi - -if ! kubectl --kubeconfig="$MANAGEMENT_KUBECONFIG" get clusteraccess &>/dev/null; then - log_error "ClusterAccess CRD not installed. Please run: kubectl apply -f config/crd/" - exit 1 -fi - -log_info "Prerequisites verified" - -# Create test kubeconfig secret -log_step "2. Creating test kubeconfig secret" - -# Use the same kubeconfig for testing (in real scenarios this would be different) -KUBECONFIG_B64=$(base64 -w 0 < "$MANAGEMENT_KUBECONFIG") - -cat </dev/null; then - log_info "ClusterAccess resource exists" -else - log_error "ClusterAccess resource not found" - exit 1 -fi - -# Start listener to process ClusterAccess -log_step "6. Starting listener to process ClusterAccess" - -export ENABLE_KCP=false -export LOCAL_DEVELOPMENT=false -export MULTICLUSTER=true -export KUBECONFIG="$MANAGEMENT_KUBECONFIG" -export OPENAPI_DEFINITIONS_PATH="$DEFINITIONS_DIR" - -log_info "Starting listener with ENABLE_KCP=false, MULTICLUSTER=true" -log_info "This should use the ClusterAccess reconciler..." - -# Run listener in background for a short time to generate schema -timeout 30s go run . listener || true - -# Check if schema file was generated -log_step "7. Checking if schema file was generated" - -SCHEMA_FILE="$DEFINITIONS_DIR/${TEST_CLUSTER_NAME}.json" -if [ -f "$SCHEMA_FILE" ]; then - log_info "Schema file generated: $SCHEMA_FILE" - - # Check if it contains x-cluster-metadata - if grep -q "x-cluster-metadata" "$SCHEMA_FILE"; then - log_info "Schema file contains x-cluster-metadata ✓" - - # Show the metadata - log_info "Cluster metadata:" - jq '.["x-cluster-metadata"]' "$SCHEMA_FILE" 2>/dev/null || echo " (Could not parse metadata)" - else - log_warn "Schema file does not contain x-cluster-metadata" - fi -else - log_error "Schema file not generated: $SCHEMA_FILE" - exit 1 -fi - -# Test gateway reading the schema -log_step "8. Testing gateway configuration" - -export ENABLE_KCP=false -export LOCAL_DEVELOPMENT=false -export MULTICLUSTER=true -# NOTE: KUBECONFIG not needed for gateway in multicluster mode -unset KUBECONFIG -export OPENAPI_DEFINITIONS_PATH="$DEFINITIONS_DIR" -export GATEWAY_PORT=17080 - -log_info "Starting gateway with the generated schema..." -log_info "Gateway should read x-cluster-metadata and connect to the specified cluster" -log_info "KUBECONFIG is NOT needed for gateway in multicluster mode" - -# Start gateway in background for a short test -timeout 10s go run . gateway & -GATEWAY_PID=$! - -# Wait a bit for gateway to start -sleep 3 - -# Test gateway endpoint -log_step "9. Testing gateway endpoint" -if curl -s "http://localhost:$GATEWAY_PORT/${TEST_CLUSTER_NAME}/graphql" -H "Content-Type: application/json" -d '{"query": "{ __schema { types { name } } }"}' | grep -q "data"; then - log_info "Gateway endpoint responds correctly ✓" -else - log_warn "Gateway endpoint test failed or timed out" -fi - -# Cleanup -log_step "10. Cleanup" - -# Kill gateway if still running -if kill -0 $GATEWAY_PID 2>/dev/null; then - kill $GATEWAY_PID 2>/dev/null || true -fi - -# Remove test resources -kubectl --kubeconfig="$MANAGEMENT_KUBECONFIG" delete clusteraccess "$TEST_CLUSTER_NAME" --ignore-not-found=true -kubectl --kubeconfig="$MANAGEMENT_KUBECONFIG" delete secret "${TEST_CLUSTER_NAME}-kubeconfig" --ignore-not-found=true - -# Remove generated schema -rm -f "$SCHEMA_FILE" - -log_info "Cleanup completed" -log_info "Integration test completed successfully!" - -echo "" -log_info "Summary:" -echo " ✓ ClusterAccess reconciler processes kubeconfig-based authentication" -echo " ✓ Schema files are generated with x-cluster-metadata" -echo " ✓ Gateway reads x-cluster-metadata for cluster-specific connections" -echo " ✓ End-to-end integration works with ENABLE_KCP=false and MULTICLUSTER=true" \ No newline at end of file