diff --git a/README.md b/README.md index 769baf1..cfbcfa2 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,9 @@ the lifecycle of CNCF [Flux CD](https://fluxcd.io) and the ## Features -- Provide a declarative API for the installation and upgrade of the Flux distribution. +- Provide a declarative API for the installation, sync configuration and upgrade of the Flux distribution. - Automate patching for hotfixes and CVEs affecting the Flux controllers container images. +- Support for syncing the custer state from Git repositories, OCI artifacts and S3-compatible storage. - Provide first-class support for OpenShift, Azure, AWS, GCP and other marketplaces. - Simplify the configuration of multi-tenancy lockdown on shared Kubernetes clusters. - Provide a security-first approach to the Flux deployment and FIPS compliance. @@ -144,14 +145,58 @@ kubectl create secret docker-registry flux-enterprise-auth \ --docker-password=$ENTERPRISE_TOKEN ``` -### Migration of a bootstrap cluster +### Cluster Sync + +The `FluxInstance` resource can be configured to instruct the operator to generate +a Flux source (`GitRepository`, `OCIRepository` or `Bucket`) and a Flux `Kustomization` +to sync the cluster state with the source repository. + +The Flux objects are created in the same namespace where the `FluxInstance` is deployed +using the namespace name as the Flux source and Kustomization name. The naming convention +matches the one used by `flux bootstrap` to ensure compatibility with upstream, and +to allow [transitioning](#migration-of-a-bootstrapped-cluster) +a bootstrapped cluster to a `FluxInstance` managed one. + +To sync the cluster state from a Git repository, add the following configuration to the `FluxInstance`: + +```yaml +apiVersion: fluxcd.controlplane.io/v1 +kind: FluxInstance +metadata: + name: flux + namespace: flux-system +spec: + sync: + kind: GitRepository + url: "https://github.com/my-org/my-fleet.git" + ref: "refs/heads/main" + path: "clusters/my-cluster" + pullSecret: "flux-system" +``` + +If the source repository is private, the Kubernetes secret must be created in the `flux-system` namespace +and should contain the credentials to clone the repository: + +```shell +flux create secret git flux-system \ + --url=https://github.com/my-org/my-fleet.git \ + --username=git \ + --password=$GITHUB_TOKEN +``` + +> [!NOTE] +> For more information on how to configure the cluster sync, refer to the +> [FluxInstance API documentation](https://github.com/controlplaneio-fluxcd/flux-operator/blob/main/docs/api/v1/fluxinstance.md#sync-configuration). + +### Migration of a bootstrapped cluster To migrate a cluster that was bootstrapped, after the flux-operator is installed -and the `FluxInstance` resource is created, the following steps are required: +and the `FluxInstance` resource is created with a [sync source](#cluster-sync), +the following steps can be followed: 1. Checkout the branch of the Flux repository that was used to bootstrap the cluster. -2. Replace the contents of the `flux-system/gok-components.yaml` with the `FluxInstance` YAML manifest. -3. Remove all controllers patches from the `flux-system/kustomization.yaml`. +2. Delete the `flux-system` directory from the repository `clusters/my-cluster` directory. +3. Optionally, place the `FluxInstance` YAML manifest in the `clusters/my-cluster` directory. 4. Commit and push the changes to the Flux repository. ## Supply Chain Security diff --git a/docs/api/v1/fluxinstance.md b/docs/api/v1/fluxinstance.md index 5ee2322..ca81729 100644 --- a/docs/api/v1/fluxinstance.md +++ b/docs/api/v1/fluxinstance.md @@ -329,6 +329,174 @@ The reconciliation behaviour can be configured using the following annotations: - `fluxcd.controlplane.io/reconcileEvery`: Set the reconciliation interval. Default is `1h`. - `fluxcd.controlplane.io/reconcileTimeout`: Set the reconciliation timeout. Default is `5m`. +### Sync configuration + +The `.spec.sync` field is optional and specifies the Flux sync configuration. +When set, a Flux source and a Flux Kustomization are generated to sync +the cluster state with the source repository. + +The Flux objects are created in the same namespace where the FluxInstance is deployed +using the namespace name as the Flux source and Kustomization name. The naming convention +matches the one used by `flux bootstrap` to ensure compatibility with upstream, and +to allow transitioning a bootstrapped cluster to a FluxInstance managed one. + +Sync fields: + +- `kind`: The source kind, supported values are `GitRepository`, `OCIRepository` and `Bucket`. +- `url`: The URL of the source repository, can be a Git repository HTTP/S or SSH address, an OCI repository address or a Bucket endpoint. +- `ref`: The source reference, can be a Git ref name e.g. `refs/heads/main`, an OCI tag e.g. `latest` or a Bucket name. +- `path`: The path to the source directory containing the kustomize overlay or plain Kubernetes manifests to sync from. +- `pullSecret`: The name of the Kubernetes secret that contains the credentials to pull the source repository. This field is optional. +- `interval`: The sync interval. This field is optional, when not set the default is `1m`. + +### Sync from Git over HTTP/S + +Example: + +```yaml +spec: + sync: + kind: GitRepository + url: "https://gitlab.com/my-group/my-fleet.git" + ref: "refs/heads/main" + path: "clusters/my-cluster" + pullSecret: "git-token-auth" +``` + +If the source repository is private, the Kubernetes secret must be created +in the same namespace where the FluxInstance is deployed, and have the following format: + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: git-token-auth + namespace: flux-system +type: Opaque +stringData: + username: "git-username" + password: "git-token" +``` + +To generate the secret with the Flux CLI: + +```sh +flux create secret git git-token-auth \ + --namespace flux-system \ + --url=https://gitlab.com/my-group/my-fleet.git \ + --username=git-username \ + --password=git-token +``` + +### Sync from Git over SSH + +Example: + +```yaml +spec: + sync: + kind: GitRepository + url: "ssh://git@github.com/my-org/my-fleet.git" + ref: "refs/heads/main" + path: "clusters/my-cluster" + pullSecret: "git-ssh-auth" +``` + +If the source repository is private, the Kubernetes secret must be created +in the same namespace where the FluxInstance is deployed, and have the following format: + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: git-ssh-auth + namespace: flux-system +type: Opaque +stringData: + identity: | + -----BEGIN OPENSSH PRIVATE KEY----- + ... + -----END OPENSSH PRIVATE KEY----- + known_hosts: | + github.com ecdsa-sha2-nistp256 AAAA... +``` + +To generate the secret with the Flux CLI: + +```sh +flux create secret git git-ssh-auth \ + --namespace flux-system \ + --url=ssh://git@github.com/my-org/my-fleet.git \ + --private-key-file=my-private.key +``` + +### Sync from OCI over HTTP/S + +Example: + +```yaml +spec: + sync: + kind: OCIRepository + url: "oci://ghcr.io/my-org/my-fleet-manifests" + ref: "latest" + path: "clusters/my-cluster" + pullSecret: "oci-token-auth" +``` + +If the container registry is private, the Kubernetes secret must be created +in the same namespace where the FluxInstance is deployed, and be of type `kubernetes.io/dockerconfigjson`: + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: oci-token-auth + namespace: flux-system +type: kubernetes.io/dockerconfigjson +data: + .dockerconfigjson: "base64-encoded-docker-config" +``` + +To generate the secret with the Flux CLI: + +```sh +flux create secret oci oci-token-auth \ + --namespace flux-system \ + --url=ghcr.io \ + --username=ghcr-username \ + --password=ghcr-token +``` + +### Sync from S3-compatible storage over HTTP/S + +Example: + +```yaml +spec: + sync: + kind: Bucket + url: "minio.my-org.com" + ref: "my-bucket-fleet" + path: "clusters/my-cluster" + pullSecret: "bucket-auth" +``` + +If the Bucket is private, the Kubernetes secret must be created +in the same namespace where the FluxInstance is deployed, and have the following format: + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: bucket-auth + namespace: flux-system +type: Opaque +stringData: + accesskey: "my-accesskey" + secretkey: "my-secretkey" +``` + ## FluxInstance Status ### Conditions diff --git a/internal/controller/fluxinstance_controller_test.go b/internal/controller/fluxinstance_controller_test.go index 5bdf556..84a3685 100644 --- a/internal/controller/fluxinstance_controller_test.go +++ b/internal/controller/fluxinstance_controller_test.go @@ -476,6 +476,7 @@ func TestFluxInstanceReconciler_Profiles(t *testing.T) { sync.SetAPIVersion("kustomize.toolkit.fluxcd.io/v1") sync.SetKind("Kustomization") err = testClient.Get(ctx, types.NamespacedName{Name: ns.Name, Namespace: ns.Name}, &sync) + g.Expect(err).ToNot(HaveOccurred()) // Check multitenant profile. nestedString, b, err := unstructured.NestedString(sync.Object, "spec", "serviceAccountName")