#
tokens: 49896/50000 70/898 files (page 3/126)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 3 of 126. Use http://codebase.md/controlplaneio-fluxcd/flux-operator?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .github
│   ├── actions
│   │   └── runner-cleanup
│   │       └── action.yml
│   ├── copilot-instructions.md
│   ├── dependabot.yaml
│   └── workflows
│       ├── actions-test.yaml
│       ├── e2e-olm.yaml
│       ├── preview.yaml
│       ├── push-manifests.yaml
│       ├── release.yaml
│       └── test.yaml
├── .gitignore
├── .golangci.yml
├── .goreleaser.yml
├── actions
│   └── setup
│       ├── action.yaml
│       └── README.md
├── AGENTS.md
├── api
│   └── v1
│       ├── common_types_test.go
│       ├── common_types.go
│       ├── fluxinstance_types.go
│       ├── fluxreport_types.go
│       ├── groupversion_info.go
│       ├── history_types_test.go
│       ├── history_types.go
│       ├── resourceset_types.go
│       ├── resourcesetinputprovider_types.go
│       ├── schedule_types.go
│       └── zz_generated.deepcopy.go
├── cmd
│   ├── cli
│   │   ├── build_instance.go
│   │   ├── build_resourceset_test.go
│   │   ├── build_resourceset.go
│   │   ├── build.go
│   │   ├── client.go
│   │   ├── completion_bash.go
│   │   ├── completion_fish.go
│   │   ├── completion_powershell.go
│   │   ├── completion_zsh.go
│   │   ├── completion.go
│   │   ├── create_secret_basicauth_test.go
│   │   ├── create_secret_basicauth.go
│   │   ├── create_secret_githubapp.go
│   │   ├── create_secret_proxy_test.go
│   │   ├── create_secret_proxy.go
│   │   ├── create_secret_registry_test.go
│   │   ├── create_secret_registry.go
│   │   ├── create_secret_sops_test.go
│   │   ├── create_secret_sops.go
│   │   ├── create_secret_ssh.go
│   │   ├── create_secret_tls.go
│   │   ├── create_secret.go
│   │   ├── create.go
│   │   ├── debug_web_cookie.go
│   │   ├── debug_web.go
│   │   ├── debug.go
│   │   ├── delete_inputprovider_test.go
│   │   ├── delete_inputprovider.go
│   │   ├── delete_instance_test.go
│   │   ├── delete_instance.go
│   │   ├── delete_resourceset_test.go
│   │   ├── delete_resourceset.go
│   │   ├── delete.go
│   │   ├── distro_decrypt_manifests_test.go
│   │   ├── distro_decrypt_manifests.go
│   │   ├── distro_decrypt_token_test.go
│   │   ├── distro_decrypt_token.go
│   │   ├── distro_decrypt.go
│   │   ├── distro_encrypt_manifests_test.go
│   │   ├── distro_encrypt_manifests.go
│   │   ├── distro_encrypt_token_test.go
│   │   ├── distro_encrypt_token.go
│   │   ├── distro_encrypt.go
│   │   ├── distro_keygen_enc_test.go
│   │   ├── distro_keygen_enc.go
│   │   ├── distro_keygen_sig_test.go
│   │   ├── distro_keygen_sig.go
│   │   ├── distro_keygen.go
│   │   ├── distro_revoke_license_key_test.go
│   │   ├── distro_revoke_license_key.go
│   │   ├── distro_revoke.go
│   │   ├── distro_sign_artifacts_test.go
│   │   ├── distro_sign_artifacts.go
│   │   ├── distro_sign_license_key_test.go
│   │   ├── distro_sign_license_key.go
│   │   ├── distro_sign_manifests_test.go
│   │   ├── distro_sign_manifests.go
│   │   ├── distro_sign.go
│   │   ├── distro_verify_artifacts_test.go
│   │   ├── distro_verify_artifacts.go
│   │   ├── distro_verify_license_key_test.go
│   │   ├── distro_verify_license_key.go
│   │   ├── distro_verify_manifests_test.go
│   │   ├── distro_verify_manifests.go
│   │   ├── distro_verify.go
│   │   ├── distro.go
│   │   ├── Dockerfile
│   │   ├── export_report_test.go
│   │   ├── export_report.go
│   │   ├── export_resource_test.go
│   │   ├── export_resource.go
│   │   ├── export.go
│   │   ├── get_inputprovider_test.go
│   │   ├── get_inputprovider.go
│   │   ├── get_instance.go
│   │   ├── get_resources.go
│   │   ├── get_resourceset_test.go
│   │   ├── get_resourceset.go
│   │   ├── get.go
│   │   ├── install.go
│   │   ├── main.go
│   │   ├── README.md
│   │   ├── reconcile_inputprovider.go
│   │   ├── reconcile_instance.go
│   │   ├── reconcile_resource.go
│   │   ├── reconcile_resources.go
│   │   ├── reconcile_resourceset.go
│   │   ├── reconcile.go
│   │   ├── resume_inputprovider.go
│   │   ├── resume_instance.go
│   │   ├── resume_resource.go
│   │   ├── resume_resourceset.go
│   │   ├── resume.go
│   │   ├── stats.go
│   │   ├── suite_test.go
│   │   ├── suspend_inputprovider.go
│   │   ├── suspend_instance.go
│   │   ├── suspend_resource.go
│   │   ├── suspend_resourceset.go
│   │   ├── suspend.go
│   │   ├── testdata
│   │   │   └── build_resourceset
│   │   │       ├── golden-labeled.yaml
│   │   │       ├── golden-named.yaml
│   │   │       ├── golden-permuted.yaml
│   │   │       ├── golden.yaml
│   │   │       ├── inputs.yaml
│   │   │       ├── rset-standalone.yaml
│   │   │       ├── rset-with-rsip-labeled.yaml
│   │   │       ├── rset-with-rsip-named.yaml
│   │   │       ├── rset-with-rsip-permuted.yaml
│   │   │       ├── rset-with-rsip.yaml
│   │   │       ├── rsip-labeled.yaml
│   │   │       ├── rsip-named.yaml
│   │   │       └── rsip.yaml
│   │   ├── trace_test.go
│   │   ├── trace_types.go
│   │   ├── trace.go
│   │   ├── tree_helmrelease.go
│   │   ├── tree_kustomization.go
│   │   ├── tree_resourceset_test.go
│   │   ├── tree_resourceset.go
│   │   ├── tree.go
│   │   ├── uninstall.go
│   │   ├── version_test.go
│   │   ├── version.go
│   │   ├── wait_inputprovider_test.go
│   │   ├── wait_inputprovider.go
│   │   ├── wait_instance_test.go
│   │   ├── wait_instance.go
│   │   ├── wait_resourceset_test.go
│   │   ├── wait_resourceset.go
│   │   └── wait.go
│   ├── mcp
│   │   ├── Dockerfile
│   │   ├── k8s
│   │   │   ├── actions_test.go
│   │   │   ├── actions.go
│   │   │   ├── client_test.go
│   │   │   ├── client.go
│   │   │   ├── config.go
│   │   │   ├── events_test.go
│   │   │   ├── events.go
│   │   │   ├── export_test.go
│   │   │   ├── export.go
│   │   │   ├── helm.go
│   │   │   ├── logs.go
│   │   │   ├── metrics.go
│   │   │   └── suite_test.go
│   │   ├── main.go
│   │   ├── prompter
│   │   │   ├── debug_helmrelease_test.go
│   │   │   ├── debug_helmrelease.go
│   │   │   ├── debug_kustomization_test.go
│   │   │   ├── debug_kustomization.go
│   │   │   ├── index.go
│   │   │   └── manager.go
│   │   ├── README.md
│   │   └── toolbox
│   │       ├── apply_manifest_test.go
│   │       ├── apply_manifest.go
│   │       ├── delete_resource_test.go
│   │       ├── delete_resource.go
│   │       ├── get_apis_test.go
│   │       ├── get_apis.go
│   │       ├── get_contexts_test.go
│   │       ├── get_contexts.go
│   │       ├── get_instance_test.go
│   │       ├── get_instance.go
│   │       ├── get_logs_test.go
│   │       ├── get_logs.go
│   │       ├── get_metrics_test.go
│   │       ├── get_metrics.go
│   │       ├── get_resource_test.go
│   │       ├── get_resource.go
│   │       ├── helpers.go
│   │       ├── indexer
│   │       │   └── main.go
│   │       ├── install_instance_test.go
│   │       ├── install_instance.go
│   │       ├── library
│   │       │   ├── bm25_test.go
│   │       │   ├── bm25.go
│   │       │   ├── index.go
│   │       │   ├── index.gob
│   │       │   ├── library.go
│   │       │   ├── search_test.go
│   │       │   ├── search.go
│   │       │   ├── tokenizer_test.go
│   │       │   └── tokenizer.go
│   │       ├── manager_test.go
│   │       ├── manager.go
│   │       ├── reconcile_helmrelease_test.go
│   │       ├── reconcile_helmrelease.go
│   │       ├── reconcile_kustomization_test.go
│   │       ├── reconcile_kustomization.go
│   │       ├── reconcile_resourceset_test.go
│   │       ├── reconcile_resourceset.go
│   │       ├── reconcile_source_test.go
│   │       ├── reconcile_source.go
│   │       ├── resume_reconciliation_test.go
│   │       ├── resume_reconciliation.go
│   │       ├── scopes_test.go
│   │       ├── scopes.go
│   │       ├── search_flux_docs_test.go
│   │       ├── search_flux_docs.go
│   │       ├── set_context_test.go
│   │       ├── set_context.go
│   │       ├── suspend_reconciliation_test.go
│   │       ├── suspend_reconciliation.go
│   │       └── testdata
│   │           ├── kubeconfig_golden.yaml
│   │           └── kubeconfig.yaml
│   └── operator
│       └── main.go
├── config
│   ├── crd
│   │   ├── bases
│   │   │   ├── fluxcd.controlplane.io_fluxinstances.yaml
│   │   │   ├── fluxcd.controlplane.io_fluxreports.yaml
│   │   │   ├── fluxcd.controlplane.io_resourcesetinputproviders.yaml
│   │   │   └── fluxcd.controlplane.io_resourcesets.yaml
│   │   ├── kustomization.yaml
│   │   └── kustomizeconfig.yaml
│   ├── data
│   │   ├── flux
│   │   │   ├── v2.2.3
│   │   │   │   ├── helm-controller.yaml
│   │   │   │   ├── image-automation-controller.yaml
│   │   │   │   ├── image-reflector-controller.yaml
│   │   │   │   ├── kustomize-controller.yaml
│   │   │   │   ├── notification-controller.yaml
│   │   │   │   ├── policies.yaml
│   │   │   │   ├── rbac.yaml
│   │   │   │   └── source-controller.yaml
│   │   │   ├── v2.3.0
│   │   │   │   ├── helm-controller.yaml
│   │   │   │   ├── image-automation-controller.yaml
│   │   │   │   ├── image-reflector-controller.yaml
│   │   │   │   ├── kustomize-controller.yaml
│   │   │   │   ├── notification-controller.yaml
│   │   │   │   ├── policies.yaml
│   │   │   │   ├── rbac.yaml
│   │   │   │   └── source-controller.yaml
│   │   │   ├── v2.4.0
│   │   │   │   ├── helm-controller.yaml
│   │   │   │   ├── image-automation-controller.yaml
│   │   │   │   ├── image-reflector-controller.yaml
│   │   │   │   ├── kustomize-controller.yaml
│   │   │   │   ├── notification-controller.yaml
│   │   │   │   ├── policies.yaml
│   │   │   │   ├── rbac.yaml
│   │   │   │   └── source-controller.yaml
│   │   │   ├── v2.5.0
│   │   │   │   ├── helm-controller.yaml
│   │   │   │   ├── image-automation-controller.yaml
│   │   │   │   ├── image-reflector-controller.yaml
│   │   │   │   ├── kustomize-controller.yaml
│   │   │   │   ├── notification-controller.yaml
│   │   │   │   ├── policies.yaml
│   │   │   │   ├── rbac.yaml
│   │   │   │   └── source-controller.yaml
│   │   │   ├── v2.5.1
│   │   │   │   ├── helm-controller.yaml
│   │   │   │   ├── image-automation-controller.yaml
│   │   │   │   ├── image-reflector-controller.yaml
│   │   │   │   ├── kustomize-controller.yaml
│   │   │   │   ├── notification-controller.yaml
│   │   │   │   ├── policies.yaml
│   │   │   │   ├── rbac.yaml
│   │   │   │   └── source-controller.yaml
│   │   │   ├── v2.6.0
│   │   │   │   ├── helm-controller.yaml
│   │   │   │   ├── image-automation-controller.yaml
│   │   │   │   ├── image-reflector-controller.yaml
│   │   │   │   ├── kustomize-controller.yaml
│   │   │   │   ├── notification-controller.yaml
│   │   │   │   ├── policies.yaml
│   │   │   │   ├── rbac.yaml
│   │   │   │   └── source-controller.yaml
│   │   │   ├── v2.6.1
│   │   │   │   ├── helm-controller.yaml
│   │   │   │   ├── image-automation-controller.yaml
│   │   │   │   ├── image-reflector-controller.yaml
│   │   │   │   ├── kustomize-controller.yaml
│   │   │   │   ├── notification-controller.yaml
│   │   │   │   ├── policies.yaml
│   │   │   │   ├── rbac.yaml
│   │   │   │   └── source-controller.yaml
│   │   │   ├── v2.6.2
│   │   │   │   ├── helm-controller.yaml
│   │   │   │   ├── image-automation-controller.yaml
│   │   │   │   ├── image-reflector-controller.yaml
│   │   │   │   ├── kustomize-controller.yaml
│   │   │   │   ├── notification-controller.yaml
│   │   │   │   ├── policies.yaml
│   │   │   │   ├── rbac.yaml
│   │   │   │   └── source-controller.yaml
│   │   │   ├── v2.6.3
│   │   │   │   ├── helm-controller.yaml
│   │   │   │   ├── image-automation-controller.yaml
│   │   │   │   ├── image-reflector-controller.yaml
│   │   │   │   ├── kustomize-controller.yaml
│   │   │   │   ├── notification-controller.yaml
│   │   │   │   ├── policies.yaml
│   │   │   │   ├── rbac.yaml
│   │   │   │   └── source-controller.yaml
│   │   │   ├── v2.6.4
│   │   │   │   ├── helm-controller.yaml
│   │   │   │   ├── image-automation-controller.yaml
│   │   │   │   ├── image-reflector-controller.yaml
│   │   │   │   ├── kustomize-controller.yaml
│   │   │   │   ├── notification-controller.yaml
│   │   │   │   ├── policies.yaml
│   │   │   │   ├── rbac.yaml
│   │   │   │   └── source-controller.yaml
│   │   │   ├── v2.7.0
│   │   │   │   ├── helm-controller.yaml
│   │   │   │   ├── image-automation-controller.yaml
│   │   │   │   ├── image-reflector-controller.yaml
│   │   │   │   ├── kustomize-controller.yaml
│   │   │   │   ├── notification-controller.yaml
│   │   │   │   ├── policies.yaml
│   │   │   │   ├── rbac.yaml
│   │   │   │   ├── source-controller.yaml
│   │   │   │   └── source-watcher.yaml
│   │   │   ├── v2.7.1
│   │   │   │   ├── helm-controller.yaml
│   │   │   │   ├── image-automation-controller.yaml
│   │   │   │   ├── image-reflector-controller.yaml
│   │   │   │   ├── kustomize-controller.yaml
│   │   │   │   ├── notification-controller.yaml
│   │   │   │   ├── policies.yaml
│   │   │   │   ├── rbac.yaml
│   │   │   │   ├── source-controller.yaml
│   │   │   │   └── source-watcher.yaml
│   │   │   ├── v2.7.2
│   │   │   │   ├── helm-controller.yaml
│   │   │   │   ├── image-automation-controller.yaml
│   │   │   │   ├── image-reflector-controller.yaml
│   │   │   │   ├── kustomize-controller.yaml
│   │   │   │   ├── notification-controller.yaml
│   │   │   │   ├── policies.yaml
│   │   │   │   ├── rbac.yaml
│   │   │   │   ├── source-controller.yaml
│   │   │   │   └── source-watcher.yaml
│   │   │   ├── v2.7.3
│   │   │   │   ├── helm-controller.yaml
│   │   │   │   ├── image-automation-controller.yaml
│   │   │   │   ├── image-reflector-controller.yaml
│   │   │   │   ├── kustomize-controller.yaml
│   │   │   │   ├── notification-controller.yaml
│   │   │   │   ├── policies.yaml
│   │   │   │   ├── rbac.yaml
│   │   │   │   ├── source-controller.yaml
│   │   │   │   └── source-watcher.yaml
│   │   │   ├── v2.7.4
│   │   │   │   ├── helm-controller.yaml
│   │   │   │   ├── image-automation-controller.yaml
│   │   │   │   ├── image-reflector-controller.yaml
│   │   │   │   ├── kustomize-controller.yaml
│   │   │   │   ├── notification-controller.yaml
│   │   │   │   ├── policies.yaml
│   │   │   │   ├── rbac.yaml
│   │   │   │   ├── source-controller.yaml
│   │   │   │   └── source-watcher.yaml
│   │   │   └── v2.7.5
│   │   │       ├── helm-controller.yaml
│   │   │       ├── image-automation-controller.yaml
│   │   │       ├── image-reflector-controller.yaml
│   │   │       ├── kustomize-controller.yaml
│   │   │       ├── notification-controller.yaml
│   │   │       ├── policies.yaml
│   │   │       ├── rbac.yaml
│   │   │       ├── source-controller.yaml
│   │   │       └── source-watcher.yaml
│   │   ├── flux-images
│   │   │   ├── v2.2.0
│   │   │   │   ├── enterprise-alpine.yaml
│   │   │   │   ├── enterprise-distroless.yaml
│   │   │   │   └── upstream-alpine.yaml
│   │   │   ├── v2.2.1
│   │   │   │   ├── enterprise-alpine.yaml
│   │   │   │   ├── enterprise-distroless.yaml
│   │   │   │   └── upstream-alpine.yaml
│   │   │   ├── v2.2.2
│   │   │   │   ├── enterprise-alpine.yaml
│   │   │   │   ├── enterprise-distroless.yaml
│   │   │   │   └── upstream-alpine.yaml
│   │   │   ├── v2.2.3
│   │   │   │   ├── enterprise-alpine.yaml
│   │   │   │   ├── enterprise-distroless.yaml
│   │   │   │   └── upstream-alpine.yaml
│   │   │   ├── v2.3.0
│   │   │   │   ├── enterprise-alpine.yaml
│   │   │   │   ├── enterprise-distroless.yaml
│   │   │   │   └── upstream-alpine.yaml
│   │   │   ├── v2.4.0
│   │   │   │   ├── enterprise-alpine.yaml
│   │   │   │   ├── enterprise-distroless.yaml
│   │   │   │   └── upstream-alpine.yaml
│   │   │   ├── v2.5.0
│   │   │   │   ├── enterprise-alpine.yaml
│   │   │   │   ├── enterprise-distroless.yaml
│   │   │   │   └── upstream-alpine.yaml
│   │   │   ├── v2.5.1
│   │   │   │   ├── enterprise-alpine.yaml
│   │   │   │   ├── enterprise-distroless-fips.yaml
│   │   │   │   ├── enterprise-distroless.yaml
│   │   │   │   └── upstream-alpine.yaml
│   │   │   ├── v2.6.0
│   │   │   │   ├── enterprise-alpine.yaml
│   │   │   │   ├── enterprise-distroless.yaml
│   │   │   │   └── upstream-alpine.yaml
│   │   │   ├── v2.6.1
│   │   │   │   ├── enterprise-alpine.yaml
│   │   │   │   ├── enterprise-distroless.yaml
│   │   │   │   └── upstream-alpine.yaml
│   │   │   ├── v2.6.2
│   │   │   │   ├── enterprise-alpine.yaml
│   │   │   │   ├── enterprise-distroless.yaml
│   │   │   │   └── upstream-alpine.yaml
│   │   │   ├── v2.6.3
│   │   │   │   ├── enterprise-alpine.yaml
│   │   │   │   ├── enterprise-distroless.yaml
│   │   │   │   └── upstream-alpine.yaml
│   │   │   ├── v2.6.4
│   │   │   │   ├── enterprise-alpine.yaml
│   │   │   │   ├── enterprise-distroless-fips.yaml
│   │   │   │   ├── enterprise-distroless.yaml
│   │   │   │   └── upstream-alpine.yaml
│   │   │   ├── v2.7.0
│   │   │   │   ├── enterprise-alpine.yaml
│   │   │   │   ├── enterprise-distroless.yaml
│   │   │   │   └── upstream-alpine.yaml
│   │   │   ├── v2.7.1
│   │   │   │   ├── enterprise-alpine.yaml
│   │   │   │   ├── enterprise-distroless.yaml
│   │   │   │   └── upstream-alpine.yaml
│   │   │   ├── v2.7.2
│   │   │   │   ├── enterprise-alpine.yaml
│   │   │   │   ├── enterprise-distroless.yaml
│   │   │   │   └── upstream-alpine.yaml
│   │   │   ├── v2.7.3
│   │   │   │   ├── enterprise-alpine.yaml
│   │   │   │   ├── enterprise-distroless.yaml
│   │   │   │   └── upstream-alpine.yaml
│   │   │   ├── v2.7.4
│   │   │   │   ├── enterprise-alpine.yaml
│   │   │   │   ├── enterprise-distroless.yaml
│   │   │   │   └── upstream-alpine.yaml
│   │   │   ├── v2.7.5
│   │   │   │   ├── enterprise-alpine.yaml
│   │   │   │   ├── enterprise-distroless-fips.yaml
│   │   │   │   ├── enterprise-distroless.yaml
│   │   │   │   └── upstream-alpine.yaml
│   │   │   └── VERSION
│   │   └── flux-vex
│   │       ├── v2.2.json
│   │       ├── v2.3.json
│   │       ├── v2.4.json
│   │       ├── v2.5.json
│   │       ├── v2.6.json
│   │       └── v2.7.json
│   ├── default
│   │   ├── kustomization.yaml
│   │   ├── namespace.yaml
│   │   └── rbac.yaml
│   ├── manager
│   │   ├── account.yaml
│   │   ├── deployment.yaml
│   │   ├── kustomization.yaml
│   │   └── service.yaml
│   ├── mcp
│   │   ├── deployment.yaml
│   │   ├── kustomization.yaml
│   │   └── service.yaml
│   ├── monitoring
│   │   ├── dashboards
│   │   │   ├── flux-k8s-api-performance.json
│   │   │   └── flux-performance.json
│   │   ├── flux-controllers.yaml
│   │   ├── flux-operator.yaml
│   │   └── kustomization.yaml
│   ├── olm
│   │   ├── build
│   │   │   └── Dockerfile
│   │   ├── bundle
│   │   │   ├── manifests
│   │   │   │   ├── flux-operator.clusterserviceversion.yaml
│   │   │   │   ├── flux-operator.service.yaml
│   │   │   │   ├── fluxinstances.fluxcd.controlplane.io.crd.yaml
│   │   │   │   ├── fluxreports.fluxcd.controlplane.io.crd.yaml
│   │   │   │   ├── resourcesetinputproviders.fluxcd.controlplane.io.crd.yaml
│   │   │   │   └── resourcesets.fluxcd.controlplane.io.crd.yaml
│   │   │   ├── metadata
│   │   │   │   └── annotations.yaml
│   │   │   └── tests
│   │   │       └── scorecard
│   │   │           └── config.yaml
│   │   ├── ci.yaml
│   │   └── test
│   │       ├── bundle.Dockerfile
│   │       ├── olm.yaml
│   │       └── opm.Dockerfile
│   ├── rbac
│   │   ├── fluxinstance_editor_role.yaml
│   │   ├── fluxinstance_viewer_role.yaml
│   │   ├── fluxreport_editor_role.yaml
│   │   ├── fluxreport_viewer_role.yaml
│   │   ├── kustomization.yaml
│   │   ├── leader_election_role_binding.yaml
│   │   ├── leader_election_role.yaml
│   │   ├── resourceset_editor_role.yaml
│   │   ├── resourceset_viewer_role.yaml
│   │   ├── role_binding.yaml
│   │   ├── role.yaml
│   │   └── service_account.yaml
│   ├── samples
│   │   ├── fluxcd_v1_fluxinstance.yaml
│   │   ├── fluxcd_v1_fluxreport.yaml
│   │   ├── fluxcd_v1_resourceset.yaml
│   │   ├── fluxcd_v1_resourcesetinputprovider.yaml
│   │   └── kustomization.yaml
│   └── terraform
│       ├── main.tf
│       ├── outputs.tf
│       ├── providers.tf
│       ├── README.md
│       ├── values
│       │   └── components.yaml
│       ├── variables.tf
│       └── versions.tf
├── CONTRIBUTING.md
├── Dockerfile
├── docs
│   ├── api
│   │   └── v1
│   │       ├── fluxinstance.md
│   │       ├── fluxreport.md
│   │       ├── resourceset.md
│   │       └── resourcesetinputprovider.md
│   ├── dev
│   │   └── README.md
│   ├── guides
│   │   ├── instance
│   │   │   ├── instance-controllers.md
│   │   │   ├── instance-customization.md
│   │   │   ├── instance-monitoring.md
│   │   │   ├── instance-sharding.md
│   │   │   └── instance-sync.md
│   │   ├── operator
│   │   │   ├── operator-install.md
│   │   │   └── operator-migration.md
│   │   └── resourcesets
│   │       ├── rset-app-definition.md
│   │       ├── rset-github-pull-requests.md
│   │       ├── rset-gitlab-environments.md
│   │       ├── rset-gitlab-merge-requests.md
│   │       ├── rset-image-automation.md
│   │       ├── rset-introduction.md
│   │       └── rset-time-based-delivery.md
│   ├── lkm
│   │   └── README.md
│   ├── logo
│   │   ├── flux-operator-banner.png
│   │   ├── flux-operator-banner.svg
│   │   ├── flux-operator-icon.png
│   │   ├── flux-operator-icon.svg
│   │   ├── flux-operator-logo.png
│   │   └── flux-operator-logo.svg
│   ├── mcp
│   │   ├── instructions.md
│   │   ├── mcp-config.md
│   │   ├── mcp-install.md
│   │   ├── mcp-prompting.md
│   │   ├── prompts.md
│   │   └── tools.md
│   └── web
│       ├── web-config-api.md
│       ├── web-ingress.md
│       ├── web-sso-dex.md
│       ├── web-sso-keycloak.md
│       ├── web-sso-openshift.md
│       ├── web-standalone.md
│       └── web-user-management.md
├── go.mod
├── go.sum
├── hack
│   ├── boilerplate.go.txt
│   ├── build-dist-manifests.sh
│   ├── build-olm-images.sh
│   ├── build-olm-manifests.sh
│   ├── install-operator-sdk.sh
│   ├── prep-release.sh
│   ├── vendor-flux-manifests.sh
│   └── web-ui-load-test.sh
├── internal
│   ├── builder
│   │   ├── build_test.go
│   │   ├── build.go
│   │   ├── components.go
│   │   ├── digest.go
│   │   ├── images_test.go
│   │   ├── images.go
│   │   ├── options.go
│   │   ├── preflight_test.go
│   │   ├── preflight.go
│   │   ├── profiles.go
│   │   ├── pull.go
│   │   ├── resourceset_test.go
│   │   ├── resourceset.go
│   │   ├── result.go
│   │   ├── semver_test.go
│   │   ├── semver.go
│   │   ├── templates.go
│   │   ├── testdata
│   │   │   ├── flux
│   │   │   │   ├── v2.2.0
│   │   │   │   │   └── .gitkeep
│   │   │   │   ├── v2.2.1
│   │   │   │   │   └── .gitkeep
│   │   │   │   └── v2.3.0
│   │   │   │       └── .gitkeep
│   │   │   ├── flux-images
│   │   │   │   └── v2.3.0
│   │   │   │       ├── enterprise-alpine.yaml
│   │   │   │       ├── enterprise-distroless.yaml
│   │   │   │       └── upstream-alpine.yaml
│   │   │   ├── resourceset
│   │   │   │   ├── dedup.golden.yaml
│   │   │   │   ├── dedup.yaml
│   │   │   │   ├── empty.yaml
│   │   │   │   ├── exclude.golden.yaml
│   │   │   │   ├── exclude.yaml
│   │   │   │   ├── invalid-output.yaml
│   │   │   │   ├── missing-inputs.yaml
│   │   │   │   ├── multi-doc-template.golden.yaml
│   │   │   │   ├── multi-doc-template.yaml
│   │   │   │   ├── nestedinputs.golden.yaml
│   │   │   │   ├── nestedinputs.yaml
│   │   │   │   ├── noinputs.golden.yaml
│   │   │   │   ├── noinputs.yaml
│   │   │   │   ├── slugify.golden.yaml
│   │   │   │   └── slugify.yaml
│   │   │   ├── v2.3.0
│   │   │   │   ├── helm-controller.yaml
│   │   │   │   ├── image-automation-controller.yaml
│   │   │   │   ├── image-reflector-controller.yaml
│   │   │   │   ├── kustomize-controller.yaml
│   │   │   │   ├── notification-controller.yaml
│   │   │   │   ├── policies.yaml
│   │   │   │   ├── rbac.yaml
│   │   │   │   └── source-controller.yaml
│   │   │   ├── v2.3.0-golden
│   │   │   │   ├── default.kustomization.yaml
│   │   │   │   ├── patches.kustomization.yaml
│   │   │   │   ├── profiles.kustomization.yaml
│   │   │   │   ├── sharding.kustomization.yaml
│   │   │   │   ├── storage.kustomization.yaml
│   │   │   │   └── sync.kustomization.yaml
│   │   │   ├── v2.6.0
│   │   │   │   ├── helm-controller.yaml
│   │   │   │   ├── image-automation-controller.yaml
│   │   │   │   ├── image-reflector-controller.yaml
│   │   │   │   ├── kustomize-controller.yaml
│   │   │   │   ├── notification-controller.yaml
│   │   │   │   ├── policies.yaml
│   │   │   │   ├── rbac.yaml
│   │   │   │   └── source-controller.yaml
│   │   │   ├── v2.6.0-golden
│   │   │   │   ├── shard1.kustomization.yaml
│   │   │   │   ├── shard2.kustomization.yaml
│   │   │   │   ├── sharding.kustomization.yaml
│   │   │   │   ├── size.large.kustomization.yaml
│   │   │   │   ├── size.medium.kustomization.yaml
│   │   │   │   └── size.small.kustomization.yaml
│   │   │   ├── v2.7.0
│   │   │   │   ├── helm-controller.yaml
│   │   │   │   ├── image-automation-controller.yaml
│   │   │   │   ├── image-reflector-controller.yaml
│   │   │   │   ├── kustomize-controller.yaml
│   │   │   │   ├── notification-controller.yaml
│   │   │   │   ├── policies.yaml
│   │   │   │   ├── rbac.yaml
│   │   │   │   ├── source-controller.yaml
│   │   │   │   └── source-watcher.yaml
│   │   │   └── v2.7.0-golden
│   │   │       └── source-watcher.kustomization.yaml
│   │   └── workload_identity.go
│   ├── controller
│   │   ├── common.go
│   │   ├── entitlement_controller_test.go
│   │   ├── entitlement_controller.go
│   │   ├── fluxinstance_artifact_controller_test.go
│   │   ├── fluxinstance_artifact_controller.go
│   │   ├── fluxinstance_artifact_manager_test.go
│   │   ├── fluxinstance_artifact_manager.go
│   │   ├── fluxinstance_controller_test.go
│   │   ├── fluxinstance_controller.go
│   │   ├── fluxinstance_manager.go
│   │   ├── fluxinstance_migrator.go
│   │   ├── fluxinstance_uninstaller.go
│   │   ├── fluxreport_controller_test.go
│   │   ├── fluxreport_controller.go
│   │   ├── resourceset_controller_test.go
│   │   ├── resourceset_controller.go
│   │   ├── resourceset_manager_test.go
│   │   ├── resourceset_manager.go
│   │   ├── resourcesetinputprovider_controller_git_test.go
│   │   ├── resourcesetinputprovider_controller_oci_test.go
│   │   ├── resourcesetinputprovider_controller_test.go
│   │   ├── resourcesetinputprovider_controller.go
│   │   ├── resourcesetinputprovider_manager.go
│   │   ├── suite_test.go
│   │   └── testdata
│   │       └── rsa-private-key.pem
│   ├── entitlement
│   │   ├── aws.go
│   │   ├── client_test.go
│   │   ├── client.go
│   │   ├── default_test.go
│   │   └── default.go
│   ├── filtering
│   │   ├── filters_test.go
│   │   └── filters.go
│   ├── gitprovider
│   │   ├── azuredevops_test.go
│   │   ├── azuredevops.go
│   │   ├── github_test.go
│   │   ├── github.go
│   │   ├── gitlab_test.go
│   │   ├── gitlab.go
│   │   ├── interface.go
│   │   ├── options.go
│   │   ├── result_test.go
│   │   └── result.go
│   ├── inputs
│   │   ├── combine_test.go
│   │   ├── combine.go
│   │   ├── flattener.go
│   │   ├── id.go
│   │   ├── json_test.go
│   │   ├── json.go
│   │   ├── keys_test.go
│   │   ├── keys.go
│   │   ├── permuter_test.go
│   │   ├── permuter.go
│   │   └── provider.go
│   ├── install
│   │   ├── autoupdate.go
│   │   ├── client.go
│   │   ├── credentials.go
│   │   ├── deploy.go
│   │   ├── download.go
│   │   ├── events.go
│   │   ├── installer.go
│   │   ├── options.go
│   │   └── uninstall.go
│   ├── inventory
│   │   ├── inventory_test.go
│   │   ├── inventory.go
│   │   ├── reader_test.go
│   │   ├── reader.go
│   │   └── testdata
│   │       ├── inventory1.yaml
│   │       └── inventory2.yaml
│   ├── lkm
│   │   ├── artifacts_attestation_test.go
│   │   ├── artifacts_attestation.go
│   │   ├── attestation_test.go
│   │   ├── attestation.go
│   │   ├── doc.go
│   │   ├── errors.go
│   │   ├── fetch_test.go
│   │   ├── fetch.go
│   │   ├── jwe_test.go
│   │   ├── jwe.go
│   │   ├── jwt_test.go
│   │   ├── jwt.go
│   │   ├── keygen_test.go
│   │   ├── keygen.go
│   │   ├── keyset_test.go
│   │   ├── keyset.go
│   │   ├── license_test.go
│   │   ├── license.go
│   │   ├── licensekey.go
│   │   ├── manifests_attestation_test.go
│   │   ├── manifests_attestation.go
│   │   ├── revocation_test.go
│   │   └── revocation.go
│   ├── notifier
│   │   └── notifier.go
│   ├── reporter
│   │   ├── cluster.go
│   │   ├── components.go
│   │   ├── crds.go
│   │   ├── distribution.go
│   │   ├── metrics_test.go
│   │   ├── metrics.go
│   │   ├── reconcilers.go
│   │   ├── reporter.go
│   │   └── sync.go
│   ├── schedule
│   │   ├── scheduler_test.go
│   │   └── scheduler.go
│   ├── tests
│   │   ├── fluxinstance
│   │   │   ├── health_check_test.go
│   │   │   └── suite_test.go
│   │   └── resourceset
│   │       ├── health_check_test.go
│   │       └── suite_test.go
│   ├── testutils
│   │   ├── log.go
│   │   └── time.go
│   └── web
│       ├── action_test.go
│       ├── action.go
│       ├── auth
│       │   ├── claims_test.go
│       │   ├── claims.go
│       │   ├── cookies_test.go
│       │   ├── cookies.go
│       │   ├── errors_test.go
│       │   ├── errors.go
│       │   ├── middlewares_test.go
│       │   ├── middlewares.go
│       │   ├── oauth2_test.go
│       │   ├── oauth2.go
│       │   └── oidc.go
│       ├── config
│       │   ├── authentication_types_test.go
│       │   ├── authentication_types.go
│       │   ├── config_types_test.go
│       │   ├── config_types.go
│       │   ├── groupversion_info.go
│       │   ├── loader_test.go
│       │   ├── loader.go
│       │   ├── user_actions_types_test.go
│       │   ├── user_actions_types.go
│       │   └── watcher.go
│       ├── events_test.go
│       ├── events.go
│       ├── favorites_test.go
│       ├── favorites.go
│       ├── fs.go
│       ├── handler.go
│       ├── inventory.go
│       ├── kubeclient
│       │   ├── client_test.go
│       │   ├── client.go
│       │   └── suite_test.go
│       ├── middlewares_test.go
│       ├── middlewares.go
│       ├── report_test.go
│       ├── report.go
│       ├── resource_test.go
│       ├── resource.go
│       ├── resources_test.go
│       ├── resources.go
│       ├── search_test.go
│       ├── search.go
│       ├── server_test.go
│       ├── server.go
│       ├── source.go
│       ├── suite_test.go
│       ├── user
│       │   ├── user_test.go
│       │   └── user.go
│       ├── workload_test.go
│       ├── workload.go
│       ├── workloads_test.go
│       └── workloads.go
├── LICENSE
├── Makefile
├── PROJECT
├── README.md
├── SECURITY.md
├── test
│   ├── e2e
│   │   ├── e2e_suite_test.go
│   │   ├── e2e_test.go
│   │   ├── instance_test.go
│   │   └── utils.go
│   └── olm
│       ├── e2e_suite_test.go
│       ├── e2e_test.go
│       ├── instance_test.go
│       └── scorecard_test.go
└── web
    ├── .gitignore
    ├── embed.go
    ├── eslint.config.js
    ├── index.html
    ├── package-lock.json
    ├── package.json
    ├── postcss.config.js
    ├── public
    │   ├── favicon.svg
    │   └── fonts
    │       └── inter.woff2
    ├── README.md
    ├── src
    │   ├── app.jsx
    │   ├── app.test.jsx
    │   ├── components
    │   │   ├── auth
    │   │   │   ├── LoginPage.jsx
    │   │   │   └── LoginPage.test.jsx
    │   │   ├── dashboards
    │   │   │   ├── cluster
    │   │   │   │   ├── ClusterPage.jsx
    │   │   │   │   ├── ClusterPage.test.jsx
    │   │   │   │   ├── ControllersPanel.jsx
    │   │   │   │   ├── ControllersPanel.test.jsx
    │   │   │   │   ├── InfoPanel.jsx
    │   │   │   │   ├── InfoPanel.test.jsx
    │   │   │   │   ├── OverallStatusPanel.jsx
    │   │   │   │   ├── OverallStatusPanel.test.jsx
    │   │   │   │   ├── ReconcilersPanel.jsx
    │   │   │   │   ├── ReconcilersPanel.test.jsx
    │   │   │   │   ├── SyncPanel.jsx
    │   │   │   │   └── SyncPanel.test.jsx
    │   │   │   ├── common
    │   │   │   │   ├── panel.jsx
    │   │   │   │   ├── panel.test.jsx
    │   │   │   │   ├── yaml.jsx
    │   │   │   │   └── yaml.test.jsx
    │   │   │   └── resource
    │   │   │       ├── ActionBar.jsx
    │   │   │       ├── ActionBar.test.jsx
    │   │   │       ├── ArtifactPanel.jsx
    │   │   │       ├── ArtifactPanel.test.jsx
    │   │   │       ├── ExportedInputsPanel.jsx
    │   │   │       ├── ExportedInputsPanel.test.jsx
    │   │   │       ├── GraphTabContent.jsx
    │   │   │       ├── GraphTabContent.test.jsx
    │   │   │       ├── HistoryTimeline.jsx
    │   │   │       ├── HistoryTimeline.test.jsx
    │   │   │       ├── InputsPanel.jsx
    │   │   │       ├── InputsPanel.test.jsx
    │   │   │       ├── InventoryPanel.jsx
    │   │   │       ├── InventoryPanel.test.jsx
    │   │   │       ├── ReconcilerPanel.jsx
    │   │   │       ├── ReconcilerPanel.test.jsx
    │   │   │       ├── ResourcePage.jsx
    │   │   │       ├── ResourcePage.test.jsx
    │   │   │       ├── SourcePanel.jsx
    │   │   │       ├── SourcePanel.test.jsx
    │   │   │       ├── WorkloadsTabContent.jsx
    │   │   │       └── WorkloadsTabContent.test.jsx
    │   │   ├── favorites
    │   │   │   ├── FavoriteCard.jsx
    │   │   │   ├── FavoriteCard.test.jsx
    │   │   │   ├── FavoritesHeader.jsx
    │   │   │   ├── FavoritesHeader.test.jsx
    │   │   │   ├── FavoritesPage.jsx
    │   │   │   ├── FavoritesPage.test.jsx
    │   │   │   ├── FavoritesSearch.jsx
    │   │   │   └── FavoritesSearch.test.jsx
    │   │   ├── layout
    │   │   │   ├── ConnectionStatus.jsx
    │   │   │   ├── ConnectionStatus.test.jsx
    │   │   │   ├── Footer.jsx
    │   │   │   ├── Footer.test.jsx
    │   │   │   ├── Header.jsx
    │   │   │   ├── Header.test.jsx
    │   │   │   ├── Icons.jsx
    │   │   │   ├── NotFoundPage.jsx
    │   │   │   ├── NotFoundPage.test.jsx
    │   │   │   ├── ThemeToggle.jsx
    │   │   │   ├── ThemeToggle.test.jsx
    │   │   │   ├── UserMenu.jsx
    │   │   │   └── UserMenu.test.jsx
    │   │   └── search
    │   │       ├── EventList.jsx
    │   │       ├── EventList.test.jsx
    │   │       ├── FilterForm.jsx
    │   │       ├── FilterForm.test.jsx
    │   │       ├── QuickSearch.jsx
    │   │       ├── QuickSearch.test.jsx
    │   │       ├── ResourceDetailsView.jsx
    │   │       ├── ResourceDetailsView.test.jsx
    │   │       ├── ResourceList.jsx
    │   │       ├── ResourceList.test.jsx
    │   │       ├── StatusChart.jsx
    │   │       └── StatusChart.test.jsx
    │   ├── index.css
    │   ├── main.jsx
    │   ├── mock
    │   │   ├── action.js
    │   │   ├── events.js
    │   │   ├── events.test.js
    │   │   ├── report.js
    │   │   ├── resource.js
    │   │   ├── resources.js
    │   │   ├── resources.test.js
    │   │   ├── workload.js
    │   │   └── workload.test.js
    │   └── utils
    │       ├── constants.js
    │       ├── cookies.js
    │       ├── cookies.test.js
    │       ├── favorites.js
    │       ├── favorites.test.js
    │       ├── fetch.js
    │       ├── fetch.test.js
    │       ├── hash.js
    │       ├── hash.test.js
    │       ├── meta.js
    │       ├── meta.test.js
    │       ├── navHistory.js
    │       ├── navHistory.test.js
    │       ├── routing.js
    │       ├── routing.test.js
    │       ├── scroll.js
    │       ├── scroll.test.js
    │       ├── status.js
    │       ├── status.test.js
    │       ├── theme.js
    │       ├── theme.test.js
    │       ├── time.js
    │       ├── time.test.js
    │       ├── version.js
    │       └── version.test.js
    ├── tailwind.config.js
    ├── vite.config.js
    └── vitest.setup.js
```

# Files

--------------------------------------------------------------------------------
/internal/builder/testdata/resourceset/slugify.golden.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | apiVersion: source.toolkit.fluxcd.io/v1beta2
 2 | kind: OCIRepository
 3 | metadata:
 4 |   labels:
 5 |     app.kubernetes.io/semver: 1-0-0-rc-0
 6 |   name: team1-app1
 7 |   namespace: apps
 8 | spec:
 9 |   interval: 10m
10 |   ref:
11 |     semver: '>=1.0.0-rc.0'
12 |   url: oci://ghcr.io/org/charts/app1
13 | ---
14 | apiVersion: source.toolkit.fluxcd.io/v1beta2
15 | kind: OCIRepository
16 | metadata:
17 |   labels:
18 |     app.kubernetes.io/semver: 1-0-0
19 |   name: team2-app1
20 |   namespace: apps
21 | spec:
22 |   interval: 10m
23 |   ref:
24 |     semver: '>=1.0.0'
25 |   url: oci://ghcr.io/org/charts/app1
26 | ---
27 | apiVersion: helm.toolkit.fluxcd.io/v2
28 | kind: HelmRelease
29 | metadata:
30 |   labels:
31 |     replicas: "2"
32 |   name: team1-app1
33 |   namespace: apps
34 | spec:
35 |   chartRef:
36 |     kind: OCIRepository
37 |     name: team1-app1
38 |   interval: 1h
39 |   releaseName: team1-app1
40 |   values:
41 |     domain: t1.example.com
42 |     replicas: 2
43 | ---
44 | apiVersion: helm.toolkit.fluxcd.io/v2
45 | kind: HelmRelease
46 | metadata:
47 |   labels:
48 |     replicas: "3"
49 |   name: team2-app1
50 |   namespace: apps
51 | spec:
52 |   chartRef:
53 |     kind: OCIRepository
54 |     name: team2-app1
55 |   interval: 1h
56 |   releaseName: team2-app1
57 |   values:
58 |     domain: t2.example.com
59 |     replicas: 3
60 | ---
61 | 
```

--------------------------------------------------------------------------------
/config/data/flux-images/v2.5.1/enterprise-distroless-fips.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | images:
 2 |   - name: ghcr.io/controlplaneio-fluxcd/distroless-fips/source-controller
 3 |     newTag: v1.5.0
 4 |     digest: sha256:0c99727da7ed40ca0ce180dc4ff3ee8eba24b49afb8083adde091dffc8045231
 5 |   - name: ghcr.io/controlplaneio-fluxcd/distroless-fips/kustomize-controller
 6 |     newTag: v1.5.1
 7 |     digest: sha256:ceadae54d6c78fa26009f6a4709da06ef9bb23176c119bb0623da4dd2dae2a54
 8 |   - name: ghcr.io/controlplaneio-fluxcd/distroless-fips/helm-controller
 9 |     newTag: v1.2.0
10 |     digest: sha256:cdbcb851cbee811787df2c6df822f044418245c7635ee2f6138d69737837cec6
11 |   - name: ghcr.io/controlplaneio-fluxcd/distroless-fips/notification-controller
12 |     newTag: v1.5.0
13 |     digest: sha256:e7f42e34cdffb65f0b42c6b64d80bc55a3975403b0673fc53c926f93072b65cf
14 |   - name: ghcr.io/controlplaneio-fluxcd/distroless-fips/image-reflector-controller
15 |     newTag: v0.34.0
16 |     digest: sha256:08416bfab878a3f6c1b99ac1e7229c1abb2c71ddf347f4b05334b22afb00d738
17 |   - name: ghcr.io/controlplaneio-fluxcd/distroless-fips/image-automation-controller
18 |     newTag: v0.40.0
19 |     digest: sha256:c1612f87d85ea5c77bebd894b56fe5f52e66ad4c0848014c8c22ad8f2308d75d
20 | 
```

--------------------------------------------------------------------------------
/config/data/flux-images/v2.6.4/enterprise-distroless-fips.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | images:
 2 |   - name: ghcr.io/controlplaneio-fluxcd/distroless-fips/source-controller
 3 |     newTag: v1.6.2
 4 |     digest: sha256:9689a297295ca2d312ddfe3b8abea64497bed40914a85a53abb76c430ca80df0
 5 |   - name: ghcr.io/controlplaneio-fluxcd/distroless-fips/kustomize-controller
 6 |     newTag: v1.6.1
 7 |     digest: sha256:68b7d0c900bc6c48a000c8a6014867640a2e7ff9e2228c0338462a8f36659559
 8 |   - name: ghcr.io/controlplaneio-fluxcd/distroless-fips/helm-controller
 9 |     newTag: v1.3.0
10 |     digest: sha256:98d24bcc5509542b3e38f9966d411f72547d69f6696e19419146ce61fb45c5e3
11 |   - name: ghcr.io/controlplaneio-fluxcd/distroless-fips/notification-controller
12 |     newTag: v1.6.0
13 |     digest: sha256:09827eab87f9e6f39182513429da268ab294d7e7d823d2d60236bb05d2499922
14 |   - name: ghcr.io/controlplaneio-fluxcd/distroless-fips/image-reflector-controller
15 |     newTag: v0.35.2
16 |     digest: sha256:8120009a3afab2ccf97aeec6d0950ad2b48798b7eb5ab27798fe4ab8d4d093d6
17 |   - name: ghcr.io/controlplaneio-fluxcd/distroless-fips/image-automation-controller
18 |     newTag: v0.41.2
19 |     digest: sha256:98827ce06ef92904e0aea38c3c91de0fcf7940831bf53fa8bb7a11b11108df4c
20 | 
```

--------------------------------------------------------------------------------
/cmd/mcp/toolbox/get_contexts.go:
--------------------------------------------------------------------------------

```go
 1 | // Copyright 2025 Stefan Prodan.
 2 | // SPDX-License-Identifier: AGPL-3.0
 3 | 
 4 | package toolbox
 5 | 
 6 | import (
 7 | 	"context"
 8 | 
 9 | 	"github.com/modelcontextprotocol/go-sdk/mcp"
10 | 	"sigs.k8s.io/yaml"
11 | )
12 | 
13 | const (
14 | 	// ToolGetKubeConfigContexts is the name of the get_kubeconfig_contexts tool.
15 | 	ToolGetKubeConfigContexts = "get_kubeconfig_contexts"
16 | )
17 | 
18 | func init() {
19 | 	systemTools[ToolGetKubeConfigContexts] = systemTool{
20 | 		readOnly:  true,
21 | 		inCluster: false,
22 | 	}
23 | }
24 | 
25 | // HandleGetKubeconfigContexts is the handler function for the get_kubeconfig_contexts tool.
26 | func (m *Manager) HandleGetKubeconfigContexts(ctx context.Context, request *mcp.CallToolRequest, input struct{}) (*mcp.CallToolResult, any, error) {
27 | 	if err := CheckScopes(ctx, ToolGetKubeConfigContexts, m.readOnly); err != nil {
28 | 		return NewToolResultError(err.Error())
29 | 	}
30 | 
31 | 	err := m.kubeconfig.Load()
32 | 	if err != nil {
33 | 		return NewToolResultErrorFromErr("Failed to read kubeconfig", err)
34 | 	}
35 | 
36 | 	data, err := yaml.Marshal(m.kubeconfig.Contexts())
37 | 	if err != nil {
38 | 		return NewToolResultErrorFromErr("Failed marshalling data", err)
39 | 	}
40 | 
41 | 	return NewToolResultText(string(data))
42 | }
43 | 
```

--------------------------------------------------------------------------------
/cmd/cli/suspend_instance.go:
--------------------------------------------------------------------------------

```go
 1 | // Copyright 2025 Stefan Prodan.
 2 | // SPDX-License-Identifier: AGPL-3.0
 3 | 
 4 | package main
 5 | 
 6 | import (
 7 | 	"context"
 8 | 	"fmt"
 9 | 
10 | 	"github.com/spf13/cobra"
11 | 
12 | 	fluxcdv1 "github.com/controlplaneio-fluxcd/flux-operator/api/v1"
13 | )
14 | 
15 | var suspendInstanceCmd = &cobra.Command{
16 | 	Use:               "instance [name]",
17 | 	Short:             "Suspend FluxInstance reconciliation",
18 | 	Args:              cobra.ExactArgs(1),
19 | 	RunE:              suspendInstanceCmdRun,
20 | 	ValidArgsFunction: resourceNamesCompletionFunc(fluxcdv1.GroupVersion.WithKind(fluxcdv1.FluxInstanceKind)),
21 | }
22 | 
23 | func init() {
24 | 	suspendCmd.AddCommand(suspendInstanceCmd)
25 | }
26 | 
27 | func suspendInstanceCmdRun(cmd *cobra.Command, args []string) error {
28 | 	if len(args) != 1 {
29 | 		return fmt.Errorf("name is required")
30 | 	}
31 | 
32 | 	name := args[0]
33 | 	now := timeNow()
34 | 	gvk := fluxcdv1.GroupVersion.WithKind(fluxcdv1.FluxInstanceKind)
35 | 
36 | 	ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
37 | 	defer cancel()
38 | 
39 | 	err := toggleSuspension(ctx, gvk, name, *kubeconfigArgs.Namespace, now, true)
40 | 	if err != nil {
41 | 		return err
42 | 	}
43 | 
44 | 	rootCmd.Println(`✔`, "Reconciliation suspended")
45 | 	return nil
46 | }
47 | 
```

--------------------------------------------------------------------------------
/internal/web/fs.go:
--------------------------------------------------------------------------------

```go
 1 | // Copyright 2025 Stefan Prodan.
 2 | // SPDX-License-Identifier: AGPL-3.0
 3 | 
 4 | package web
 5 | 
 6 | import (
 7 | 	"io/fs"
 8 | 	"path/filepath"
 9 | 	"strings"
10 | )
11 | 
12 | // FileSystem wraps an embedded filesystem to handle SPA routing.
13 | // It serves index.html for all non-existent file requests (404s).
14 | type FileSystem struct {
15 | 	fs fs.FS
16 | }
17 | 
18 | // NewFileSystem creates a new filesystem handler for SPA routing.
19 | func NewFileSystem(efs fs.FS) *FileSystem {
20 | 	return &FileSystem{fs: efs}
21 | }
22 | 
23 | // Open implements fs.FS interface for SPA routing.
24 | func (w *FileSystem) Open(name string) (fs.File, error) {
25 | 	// Normalize path
26 | 	name = strings.TrimPrefix(name, "/")
27 | 
28 | 	// Try to open the requested file
29 | 	f, err := w.fs.Open(filepath.Join("dist", name))
30 | 	if err == nil {
31 | 		// File exists, return it
32 | 		return f, nil
33 | 	}
34 | 
35 | 	// If it's not a directory request and the file doesn't exist,
36 | 	// serve index.html for SPA routing
37 | 	if !strings.HasSuffix(name, "/") {
38 | 		indexPath := filepath.Join("dist", "index.html")
39 | 		f, err := w.fs.Open(indexPath)
40 | 		if err == nil {
41 | 			return f, nil
42 | 		}
43 | 	}
44 | 
45 | 	// Return original error if we can't fall back to index.html
46 | 	return nil, err
47 | }
48 | 
```

--------------------------------------------------------------------------------
/internal/builder/testdata/resourceset/multi-doc-template.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | apiVersion: fluxcd.controlplane.io/v1
 2 | kind: ResourceSet
 3 | metadata:
 4 |   name: bundles
 5 |   namespace: flux-system
 6 | spec:
 7 |   inputs:
 8 |     - bundle: addons
 9 |       decryption: false
10 |       apps:
11 |         - ingress-nginx
12 |         - cert-manager
13 |     - bundle: apps
14 |       decryption: true
15 |       apps:
16 |         - frontend
17 |         - backend
18 |   resourcesTemplate: |
19 |     ---
20 |     apiVersion: source.toolkit.fluxcd.io/v1beta2
21 |     kind: OCIRepository
22 |     metadata:
23 |       name: << inputs.bundle >>
24 |       namespace: flux-system
25 |     spec:
26 |       interval: 10m
27 |       url: oci://registry.example.com/<< inputs.bundle >>
28 |       ref:
29 |         tag: latest
30 |     <<- range $app := inputs.apps >>
31 |     ---
32 |     apiVersion: kustomize.toolkit.fluxcd.io/v1
33 |     kind: Kustomization
34 |     metadata:
35 |       name: << $app >>
36 |       namespace: flux-system
37 |     spec:
38 |       interval: 10m
39 |       prune: true
40 |       <<- if inputs.decryption >>
41 |       decryption:
42 |         provider: sops
43 |         secretRef:
44 |           name: << inputs.bundle >>-sops
45 |       <<- end >>
46 |       sourceRef:
47 |         kind: OCIRepository
48 |         name: << inputs.bundle >>
49 |       path: ./<< $app >>
50 |     <<- end >>
51 | 
```

--------------------------------------------------------------------------------
/internal/entitlement/default_test.go:
--------------------------------------------------------------------------------

```go
 1 | // Copyright 2024 Stefan Prodan.
 2 | // SPDX-License-Identifier: AGPL-3.0
 3 | 
 4 | package entitlement
 5 | 
 6 | import (
 7 | 	"context"
 8 | 	"testing"
 9 | 
10 | 	. "github.com/onsi/gomega"
11 | 	"github.com/opencontainers/go-digest"
12 | )
13 | 
14 | func TestRegisterUsage(t *testing.T) {
15 | 	g := NewWithT(t)
16 | 	client := DefaultClient{Vendor: "testVendor"}
17 | 	ctx := context.Background()
18 | 	id := "testID"
19 | 
20 | 	token, err := client.RegisterUsage(ctx, id)
21 | 	g.Expect(err).ToNot(HaveOccurred())
22 | 
23 | 	expectedDigest := digest.FromString("testVendor-testID").Encoded()
24 | 	g.Expect(token).To(Equal(expectedDigest))
25 | }
26 | 
27 | func TestVerify(t *testing.T) {
28 | 	g := NewWithT(t)
29 | 	client := DefaultClient{Vendor: "testVendor"}
30 | 	id := "testID"
31 | 	token := digest.FromString("testVendor-testID").Encoded()
32 | 
33 | 	valid, err := client.Verify(token, id)
34 | 	g.Expect(err).ToNot(HaveOccurred())
35 | 	g.Expect(valid).To(BeTrue())
36 | 
37 | 	invalidToken := "invalidToken"
38 | 	valid, err = client.Verify(invalidToken, id)
39 | 	g.Expect(err).ToNot(HaveOccurred())
40 | 	g.Expect(valid).To(BeFalse())
41 | }
42 | 
43 | func TestGetVendor(t *testing.T) {
44 | 	g := NewWithT(t)
45 | 	client := DefaultClient{Vendor: "testVendor"}
46 | 	vendor := client.GetVendor()
47 | 	g.Expect(vendor).To(Equal("testVendor"))
48 | }
49 | 
```

--------------------------------------------------------------------------------
/internal/controller/resourcesetinputprovider_manager.go:
--------------------------------------------------------------------------------

```go
 1 | // Copyright 2025 Stefan Prodan.
 2 | // SPDX-License-Identifier: AGPL-3.0
 3 | 
 4 | package controller
 5 | 
 6 | import (
 7 | 	"k8s.io/client-go/util/workqueue"
 8 | 	ctrl "sigs.k8s.io/controller-runtime"
 9 | 	"sigs.k8s.io/controller-runtime/pkg/builder"
10 | 	"sigs.k8s.io/controller-runtime/pkg/controller"
11 | 	"sigs.k8s.io/controller-runtime/pkg/predicate"
12 | 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
13 | 
14 | 	fluxcdv1 "github.com/controlplaneio-fluxcd/flux-operator/api/v1"
15 | )
16 | 
17 | // ResourceSetInputProviderReconcilerOptions contains options for the reconciler.
18 | type ResourceSetInputProviderReconcilerOptions struct {
19 | 	RateLimiter workqueue.TypedRateLimiter[reconcile.Request]
20 | }
21 | 
22 | // SetupWithManager sets up the controller with the Manager.
23 | func (r *ResourceSetInputProviderReconciler) SetupWithManager(mgr ctrl.Manager, opts ResourceSetInputProviderReconcilerOptions) error {
24 | 	return ctrl.NewControllerManagedBy(mgr).
25 | 		For(&fluxcdv1.ResourceSetInputProvider{},
26 | 			builder.WithPredicates(
27 | 				predicate.Or(
28 | 					predicate.GenerationChangedPredicate{},
29 | 					predicate.AnnotationChangedPredicate{},
30 | 				),
31 | 			)).
32 | 		WithOptions(controller.Options{
33 | 			RateLimiter: opts.RateLimiter,
34 | 		}).Complete(r)
35 | }
36 | 
```

--------------------------------------------------------------------------------
/cmd/cli/suspend_resourceset.go:
--------------------------------------------------------------------------------

```go
 1 | // Copyright 2025 Stefan Prodan.
 2 | // SPDX-License-Identifier: AGPL-3.0
 3 | 
 4 | package main
 5 | 
 6 | import (
 7 | 	"context"
 8 | 	"fmt"
 9 | 
10 | 	"github.com/spf13/cobra"
11 | 
12 | 	fluxcdv1 "github.com/controlplaneio-fluxcd/flux-operator/api/v1"
13 | )
14 | 
15 | var suspendResourceSetCmd = &cobra.Command{
16 | 	Use:               "resourceset [name]",
17 | 	Aliases:           []string{"rset"},
18 | 	Short:             "Suspend ResourceSet reconciliation",
19 | 	Args:              cobra.ExactArgs(1),
20 | 	RunE:              suspendResourceSetCmdRun,
21 | 	ValidArgsFunction: resourceNamesCompletionFunc(fluxcdv1.GroupVersion.WithKind(fluxcdv1.ResourceSetKind)),
22 | }
23 | 
24 | func init() {
25 | 	suspendCmd.AddCommand(suspendResourceSetCmd)
26 | }
27 | 
28 | func suspendResourceSetCmdRun(cmd *cobra.Command, args []string) error {
29 | 	if len(args) != 1 {
30 | 		return fmt.Errorf("name is required")
31 | 	}
32 | 
33 | 	name := args[0]
34 | 	now := timeNow()
35 | 	gvk := fluxcdv1.GroupVersion.WithKind(fluxcdv1.ResourceSetKind)
36 | 
37 | 	ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
38 | 	defer cancel()
39 | 
40 | 	err := toggleSuspension(ctx, gvk, name, *kubeconfigArgs.Namespace, now, true)
41 | 	if err != nil {
42 | 		return err
43 | 	}
44 | 
45 | 	rootCmd.Println(`✔`, "Reconciliation suspended")
46 | 	return nil
47 | }
48 | 
```

--------------------------------------------------------------------------------
/internal/reporter/cluster.go:
--------------------------------------------------------------------------------

```go
 1 | // Copyright 2025 Stefan Prodan.
 2 | // SPDX-License-Identifier: AGPL-3.0
 3 | 
 4 | package reporter
 5 | 
 6 | import (
 7 | 	"context"
 8 | 	"fmt"
 9 | 
10 | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
11 | 	"k8s.io/apimachinery/pkg/runtime/schema"
12 | 	"k8s.io/client-go/kubernetes"
13 | 	"sigs.k8s.io/controller-runtime/pkg/client/config"
14 | 
15 | 	fluxcdv1 "github.com/controlplaneio-fluxcd/flux-operator/api/v1"
16 | )
17 | 
18 | func (r *FluxStatusReporter) getClusterInfo(ctx context.Context) (*fluxcdv1.ClusterInfo, error) {
19 | 	restConfig, err := config.GetConfig()
20 | 	if err != nil {
21 | 		return nil, err
22 | 	}
23 | 
24 | 	clientSet, err := kubernetes.NewForConfig(restConfig)
25 | 	if err != nil {
26 | 		return nil, err
27 | 	}
28 | 
29 | 	sv, err := clientSet.Discovery().ServerVersion()
30 | 	if err != nil {
31 | 		return nil, fmt.Errorf("failed to get server version: %w", err)
32 | 	}
33 | 
34 | 	nodes := &metav1.PartialObjectMetadataList{}
35 | 	nodes.SetGroupVersionKind(schema.GroupVersionKind{
36 | 		Group:   "",
37 | 		Version: "v1",
38 | 		Kind:    "NodeList",
39 | 	})
40 | 	if err := r.List(ctx, nodes); err != nil {
41 | 		return nil, fmt.Errorf("failed to list nodes: %w", err)
42 | 	}
43 | 
44 | 	return &fluxcdv1.ClusterInfo{
45 | 		ServerVersion: sv.String(),
46 | 		Platform:      sv.Platform,
47 | 		Nodes:         len(nodes.Items),
48 | 	}, nil
49 | }
50 | 
```

--------------------------------------------------------------------------------
/internal/builder/testdata/resourceset/slugify.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | apiVersion: fluxcd.controlplane.io/v1
 2 | kind: ResourceSet
 3 | metadata:
 4 |   name: app1
 5 |   namespace: apps
 6 | spec:
 7 |   inputs:
 8 |     - tenant: team1
 9 |       domain: t1.example.com
10 |       semver: ">=1.0.0-rc.0"
11 |       replicas: "2"
12 |     - tenant: team2
13 |       domain: t2.example.com
14 |       semver: ">=1.0.0"
15 |       replicas: "3"
16 |   resources:
17 |     - apiVersion: source.toolkit.fluxcd.io/v1beta2
18 |       kind: OCIRepository
19 |       metadata:
20 |         name: << inputs.tenant >>-app1
21 |         namespace: apps
22 |         labels:
23 |           app.kubernetes.io/semver: << inputs.semver | slugify >>
24 |       spec:
25 |         interval: 10m
26 |         url: oci://ghcr.io/org/charts/app1
27 |         ref:
28 |           semver: << inputs.semver | quote >>
29 |     - apiVersion: helm.toolkit.fluxcd.io/v2
30 |       kind: HelmRelease
31 |       metadata:
32 |         name: << inputs.tenant >>-app1
33 |         namespace: apps
34 |         labels:
35 |           replicas: << inputs.replicas | quote >>
36 |       spec:
37 |         interval: 1h
38 |         releaseName: << inputs.tenant >>-app1
39 |         chartRef:
40 |           kind: OCIRepository
41 |           name: << inputs.tenant >>-app1
42 |         values:
43 |           domain: << inputs.domain >>
44 |           replicas: << inputs.replicas | int >>
45 | 
```

--------------------------------------------------------------------------------
/config/default/rbac.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | apiVersion: rbac.authorization.k8s.io/v1
 2 | kind: ClusterRoleBinding
 3 | metadata:
 4 |   name: flux-operator-cluster-admin
 5 | roleRef:
 6 |   apiGroup: rbac.authorization.k8s.io
 7 |   kind: ClusterRole
 8 |   name: cluster-admin
 9 | subjects:
10 |   - kind: ServiceAccount
11 |     name: flux-operator
12 |     namespace: flux-system
13 | ---
14 | apiVersion: rbac.authorization.k8s.io/v1
15 | kind: ClusterRole
16 | metadata:
17 |   name: flux-operator-edit
18 |   labels:
19 |     rbac.authorization.k8s.io/aggregate-to-edit: "true"
20 |     rbac.authorization.k8s.io/aggregate-to-admin: "true"
21 | rules:
22 |   - apiGroups:
23 |       - fluxcd.controlplane.io
24 |     resources:
25 |       - resourcesets
26 |       - resourcesetinputproviders
27 |     verbs:
28 |       - create
29 |       - delete
30 |       - deletecollection
31 |       - patch
32 |       - update
33 | ---
34 | apiVersion: rbac.authorization.k8s.io/v1
35 | kind: ClusterRole
36 | metadata:
37 |   name: flux-operator-view
38 |   labels:
39 |     rbac.authorization.k8s.io/aggregate-to-admin: "true"
40 |     rbac.authorization.k8s.io/aggregate-to-edit: "true"
41 |     rbac.authorization.k8s.io/aggregate-to-view: "true"
42 | rules:
43 |   - apiGroups:
44 |       - fluxcd.controlplane.io
45 |     resources:
46 |       - resourcesets
47 |       - resourcesetinputproviders
48 |     verbs:
49 |       - get
50 |       - list
51 |       - watch
52 | 
```

--------------------------------------------------------------------------------
/cmd/cli/testdata/build_resourceset/rset-with-rsip.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | ---
 2 | apiVersion: fluxcd.controlplane.io/v1
 3 | kind: ResourceSet
 4 | metadata:
 5 |   name: apps
 6 |   namespace: test
 7 | spec:
 8 |   inputsFrom:
 9 |     - selector:
10 |         matchLabels:
11 |           fluxcd.controlplane.io/role: provisioning
12 |   resourcesTemplate: |
13 |     <<- $id := inputs.id >>
14 |     <<- $tenant := inputs.tenantName >>
15 |     <<- range $app := inputs.applications >>
16 |     <<- $appName := $app.name >>
17 |     <<- range $env := $app.envs >>
18 |     ---
19 |     apiVersion: source.toolkit.fluxcd.io/v1
20 |     kind: OCIRepository
21 |     metadata:
22 |       name: << $appName >>
23 |       namespace: << $tenant >>-<< $env.name >>
24 |       annotations:
25 |         fluxcd.controlplane.io/id: << $id | quote >>
26 |     spec:
27 |       interval: 10m
28 |       url: oci://registry.example.com/<< $appName >>
29 |       ref:
30 |         tag: << $env.version >>
31 |     ---
32 |     apiVersion: kustomize.toolkit.fluxcd.io/v1
33 |     kind: Kustomization
34 |     metadata:
35 |       name: << $appName >>
36 |       namespace: << $tenant >>-<< $env.name >>
37 |       annotations:
38 |         fluxcd.controlplane.io/id: << $id | quote >>
39 |     spec:
40 |       interval: 1h
41 |       prune: true
42 |       sourceRef:
43 |         kind: OCIRepository
44 |         name: << $appName >>
45 |       path: ./
46 |     <<- end >>
47 |     <<- end >>
48 | 
```

--------------------------------------------------------------------------------
/cmd/mcp/toolbox/get_apis.go:
--------------------------------------------------------------------------------

```go
 1 | // Copyright 2025 Stefan Prodan.
 2 | // SPDX-License-Identifier: AGPL-3.0
 3 | 
 4 | package toolbox
 5 | 
 6 | import (
 7 | 	"context"
 8 | 
 9 | 	"github.com/modelcontextprotocol/go-sdk/mcp"
10 | )
11 | 
12 | const (
13 | 	// ToolGetKubernetesAPIVersions is the name of the get_kubernetes_api_versions tool.
14 | 	ToolGetKubernetesAPIVersions = "get_kubernetes_api_versions"
15 | )
16 | 
17 | func init() {
18 | 	systemTools[ToolGetKubernetesAPIVersions] = systemTool{
19 | 		readOnly:  true,
20 | 		inCluster: true,
21 | 	}
22 | }
23 | 
24 | // HandleGetAPIVersions is the handler function for the get_kubernetes_api_versions tool.
25 | func (m *Manager) HandleGetAPIVersions(ctx context.Context, request *mcp.CallToolRequest, input struct{}) (*mcp.CallToolResult, any, error) {
26 | 	if err := CheckScopes(ctx, ToolGetKubernetesAPIVersions, m.readOnly); err != nil {
27 | 		return NewToolResultError(err.Error())
28 | 	}
29 | 
30 | 	ctx, cancel := context.WithTimeout(ctx, m.timeout)
31 | 	defer cancel()
32 | 
33 | 	kubeClient, err := m.kubeClient.GetClient(ctx)
34 | 	if err != nil {
35 | 		return NewToolResultErrorFromErr("Failed to get Kubernetes client", err)
36 | 	}
37 | 
38 | 	result, err := kubeClient.ExportAPIs(ctx)
39 | 	if err != nil {
40 | 		return NewToolResultErrorFromErr("Failed to export Kubernetes APIs", err)
41 | 	}
42 | 
43 | 	return NewToolResultText(result)
44 | }
45 | 
```

--------------------------------------------------------------------------------
/api/v1/schedule_types.go:
--------------------------------------------------------------------------------

```go
 1 | // Copyright 2025 Stefan Prodan.
 2 | // SPDX-License-Identifier: AGPL-3.0
 3 | 
 4 | package v1
 5 | 
 6 | import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 7 | 
 8 | const (
 9 | 	ReasonInvalidSchedule      = "InvalidSchedule"
10 | 	ReasonSkippedDueToSchedule = "SkippedDueToSchedule"
11 | )
12 | 
13 | // Schedule defines a schedule for something to run.
14 | type Schedule struct {
15 | 	// Cron specifies the cron expression for the schedule.
16 | 	// +required
17 | 	Cron string `json:"cron"`
18 | 
19 | 	// TimeZone specifies the time zone for the cron schedule. Defaults to UTC.
20 | 	// +kubebuilder:default:="UTC"
21 | 	// +optional
22 | 	TimeZone string `json:"timeZone,omitempty"`
23 | 
24 | 	// Window defines the time window during which the execution is allowed.
25 | 	// Defaults to 0s, meaning no window is applied.
26 | 	// +kubebuilder:validation:Type=string
27 | 	// +kubebuilder:validation:Pattern="^([0-9]+(\\.[0-9]+)?(ms|s|m|h))+$"
28 | 	// +kubebuilder:default:="0s"
29 | 	// +optional
30 | 	Window metav1.Duration `json:"window"`
31 | }
32 | 
33 | // NextSchedule defines the next time a schedule will run.
34 | type NextSchedule struct {
35 | 	// Schedule has the configuration of the next schedule.
36 | 	Schedule `json:",inline"`
37 | 
38 | 	// When is the next time the schedule will run.
39 | 	// +required
40 | 	When metav1.Time `json:"when"`
41 | }
42 | 
```

--------------------------------------------------------------------------------
/cmd/mcp/toolbox/get_contexts_test.go:
--------------------------------------------------------------------------------

```go
 1 | // Copyright 2025 Stefan Prodan.
 2 | // SPDX-License-Identifier: AGPL-3.0
 3 | 
 4 | package toolbox
 5 | 
 6 | import (
 7 | 	"context"
 8 | 	"os"
 9 | 	"testing"
10 | 
11 | 	"github.com/modelcontextprotocol/go-sdk/mcp"
12 | 	. "github.com/onsi/gomega"
13 | 	cli "k8s.io/cli-runtime/pkg/genericclioptions"
14 | 
15 | 	"github.com/controlplaneio-fluxcd/flux-operator/cmd/mcp/k8s"
16 | )
17 | 
18 | func TestManager_HandleGetKubeconfigContexts(t *testing.T) {
19 | 	g := NewWithT(t)
20 | 
21 | 	configFile := "testdata/kubeconfig.yaml"
22 | 	goldenFile := "testdata/kubeconfig_golden.yaml"
23 | 	t.Setenv("KUBECONFIG", configFile)
24 | 
25 | 	m := &Manager{
26 | 		kubeconfig: k8s.NewKubeConfig(),
27 | 		kubeClient: k8s.NewClientFactory(cli.NewConfigFlags(false)),
28 | 	}
29 | 
30 | 	request := &mcp.CallToolRequest{
31 | 		Params: &mcp.CallToolParamsRaw{
32 | 			Name: "get_kubeconfig_contexts",
33 | 		},
34 | 	}
35 | 
36 | 	result, _, err := m.HandleGetKubeconfigContexts(context.Background(), request, struct{}{})
37 | 	g.Expect(err).ToNot(HaveOccurred())
38 | 	g.Expect(result).ToNot(BeNil())
39 | 	g.Expect(result.Content).ToNot(BeEmpty())
40 | 
41 | 	goldenContent, err := os.ReadFile(goldenFile)
42 | 	g.Expect(err).ToNot(HaveOccurred())
43 | 
44 | 	textContent, ok := result.Content[0].(*mcp.TextContent)
45 | 	g.Expect(ok).To(BeTrue())
46 | 	g.Expect(textContent.Text).To(MatchYAML(string(goldenContent)))
47 | }
48 | 
```

--------------------------------------------------------------------------------
/config/data/flux-images/v2.7.0/enterprise-alpine.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | images:
 2 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/source-controller
 3 |     newTag: v1.7.0
 4 |     digest: sha256:e6eb74be173cdc7247ae3d6cc23b7d8389bb61b7cebaf63caee9f19814b2d796
 5 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/kustomize-controller
 6 |     newTag: v1.7.0
 7 |     digest: sha256:52dbb5be888c8e68df554bf84f4b809cc503d064eb0dc97cfb168d10a817138a
 8 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/helm-controller
 9 |     newTag: v1.4.0
10 |     digest: sha256:7cd42255075dff8a9cab32ad524e699dc970fd73f74cb3c8ca1554c5808f7f86
11 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/notification-controller
12 |     newTag: v1.7.1
13 |     digest: sha256:852a2675ac1cb014fc7b5f72f8c7ecc600398866b0b23409a0a71fa8d5f8465a
14 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/image-reflector-controller
15 |     newTag: v1.0.1
16 |     digest: sha256:fbb855dc322ab2358e837fbf68c02a58e961f9077ce8de9ab9a3dc34930c580e
17 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/image-automation-controller
18 |     newTag: v1.0.1
19 |     digest: sha256:2e6b1fde971901c27df79942b9a637c3fb8300eac3545ddbbbc02d8fee21e3f1
20 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/source-watcher
21 |     newTag: v2.0.1
22 |     digest: sha256:9b3c2a45d6cec611254bd6305caec2b15d6fa85f55527750aa4e4c270d33bfff
23 | 
```

--------------------------------------------------------------------------------
/config/data/flux-images/v2.7.1/enterprise-alpine.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | images:
 2 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/source-controller
 3 |     newTag: v1.7.1
 4 |     digest: sha256:4babcdd5933ea86257a33f5c4e96a686842be584f8c909c26dde059aabcba0ce
 5 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/kustomize-controller
 6 |     newTag: v1.7.0
 7 |     digest: sha256:52dbb5be888c8e68df554bf84f4b809cc503d064eb0dc97cfb168d10a817138a
 8 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/helm-controller
 9 |     newTag: v1.4.1
10 |     digest: sha256:3d0cb07ec58bad707de14e67de1a0e498b357210d57153150e5183698d108257
11 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/notification-controller
12 |     newTag: v1.7.2
13 |     digest: sha256:25e4a0a3b4cf8a6c96b3863e877ce7a7735dc3ea16c30722f8598dd08448112a
14 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/image-reflector-controller
15 |     newTag: v1.0.1
16 |     digest: sha256:fbb855dc322ab2358e837fbf68c02a58e961f9077ce8de9ab9a3dc34930c580e
17 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/image-automation-controller
18 |     newTag: v1.0.1
19 |     digest: sha256:2e6b1fde971901c27df79942b9a637c3fb8300eac3545ddbbbc02d8fee21e3f1
20 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/source-watcher
21 |     newTag: v2.0.1
22 |     digest: sha256:9b3c2a45d6cec611254bd6305caec2b15d6fa85f55527750aa4e4c270d33bfff
23 | 
```

--------------------------------------------------------------------------------
/config/data/flux-images/v2.7.2/enterprise-alpine.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | images:
 2 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/source-controller
 3 |     newTag: v1.7.2
 4 |     digest: sha256:51ad5e23a0795f16c04ef9ab3c558ad8855d10a06ec27a42f1bc5902c8f84399
 5 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/kustomize-controller
 6 |     newTag: v1.7.1
 7 |     digest: sha256:85fa71d01229c91e17b31ee119bb9b08f2a80758bb07bc19348a43dee5172d08
 8 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/helm-controller
 9 |     newTag: v1.4.2
10 |     digest: sha256:18e2b1aed49041fe86c151e830c3bb0c6a1e8c44a1962c29c02f7730027b1c90
11 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/notification-controller
12 |     newTag: v1.7.3
13 |     digest: sha256:c2e25a5cac4b53255b9dffa24ab0b780b29308fec8a3e27924723e0185c8a9ba
14 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/image-reflector-controller
15 |     newTag: v1.0.2
16 |     digest: sha256:7bc19a6c2b49dc77608734539419b1a678f65c43922aa3dc59408d8eed40c073
17 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/image-automation-controller
18 |     newTag: v1.0.2
19 |     digest: sha256:1a482ff3457d5489f180a5aea958032ebb09c22709696a4be535fb4928a8a765
20 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/source-watcher
21 |     newTag: v2.0.2
22 |     digest: sha256:578c80d01aba477c961b531299e5c6d98497f8c50fd8a0deda14c78543b754b1
23 | 
```

--------------------------------------------------------------------------------
/config/data/flux-images/v2.7.3/enterprise-alpine.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | images:
 2 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/source-controller
 3 |     newTag: v1.7.3
 4 |     digest: sha256:68ccb51a9dc79c7a9433a0767256a641200982b776865fffad27b3e12232affb
 5 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/kustomize-controller
 6 |     newTag: v1.7.2
 7 |     digest: sha256:255bdeed339c33f4efcacc7a2c0aeeba70d359f61dbbac4a3a5cb259759192c3
 8 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/helm-controller
 9 |     newTag: v1.4.3
10 |     digest: sha256:69c775beb8c5a3016319a2f4e0956995dba2f40eff3cd9e79379911caf9de78d
11 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/notification-controller
12 |     newTag: v1.7.4
13 |     digest: sha256:a76ae274c1a05ba4f9df0b56f70ae804c9d5f309d30ce4ab1ba5d7c668f9d33f
14 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/image-reflector-controller
15 |     newTag: v1.0.3
16 |     digest: sha256:68c8317c97816e3c75ee369c7c743ee6ede9368c1ae9cefe74584b459a3c7df5
17 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/image-automation-controller
18 |     newTag: v1.0.3
19 |     digest: sha256:f78186e2adaa3445dec769da248a103d3d35fbe6bfc706f309c0f018a384d189
20 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/source-watcher
21 |     newTag: v2.0.2
22 |     digest: sha256:a90b234933be65a632c39dcf61e9cd7edd0b26791c82b30c3e6d621a5aef084a
23 | 
```

--------------------------------------------------------------------------------
/config/data/flux-images/v2.7.4/enterprise-alpine.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | images:
 2 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/source-controller
 3 |     newTag: v1.7.4
 4 |     digest: sha256:10fe0db20abde0ecd2c6d73d28fe1b6da18f7291b80f311d8a2329ba234af0de
 5 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/kustomize-controller
 6 |     newTag: v1.7.3
 7 |     digest: sha256:162179b9678ea4759ed623e273faef3184b1744b5dda7c2cda828016b76497d8
 8 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/helm-controller
 9 |     newTag: v1.4.4
10 |     digest: sha256:d2b3347621fa0edd6a1a4817f37d92df561bb75ee3b0c18930d30e0f6ad0562c
11 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/notification-controller
12 |     newTag: v1.7.5
13 |     digest: sha256:292880d055ccc111796d746c58e03d9c96e119380b7e3fb9c94bb540c582f48c
14 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/image-reflector-controller
15 |     newTag: v1.0.4
16 |     digest: sha256:58c5ca0821c7e3cb26b120bebfd8e8a3bf2bc236f71aef63318634c202d845ab
17 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/image-automation-controller
18 |     newTag: v1.0.4
19 |     digest: sha256:3a00b11e03e443cea76ebf54ca148cbf045ed3d3d2d4b539ff192a99c227b5ff
20 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/source-watcher
21 |     newTag: v2.0.3
22 |     digest: sha256:0e8ea6d213dc292eaf6cb1bb6cde5fbda7d0f66f60db49e9337079d79c0089b9
23 | 
```

--------------------------------------------------------------------------------
/config/data/flux-images/v2.7.5/enterprise-alpine.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | images:
 2 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/source-controller
 3 |     newTag: v1.7.4
 4 |     digest: sha256:ab1747b1dab33c83014d84f70b35cbdc530b50841d7d7aec56e5802188749bde
 5 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/kustomize-controller
 6 |     newTag: v1.7.3
 7 |     digest: sha256:fae42429c9a2709099f2781d88286e44fbec9defca03d98dc0692d22b942bed1
 8 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/helm-controller
 9 |     newTag: v1.4.5
10 |     digest: sha256:d5d1640d82c427ea19e343563735d82bcaa2412489770ec982a522209808cf23
11 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/notification-controller
12 |     newTag: v1.7.5
13 |     digest: sha256:61c2957bde56c97a944bfe7bafa7bb837a2f2ba1e3f1828e3801d3febb02c7c9
14 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/image-reflector-controller
15 |     newTag: v1.0.4
16 |     digest: sha256:950eeda2a0d7ccfcad5bda64f181f1b99b9bf4d9581d950d7127c7a1c268e880
17 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/image-automation-controller
18 |     newTag: v1.0.4
19 |     digest: sha256:9c97d7d0818308fbc89860470890ebb1f24de4345bd5013824d10fef81f620ec
20 |   - name: ghcr.io/controlplaneio-fluxcd/alpine/source-watcher
21 |     newTag: v2.0.3
22 |     digest: sha256:ae219de6b17a1e1c848f5953ed1a49f40fa2da60bb3d262e18743c2b1eed3199
23 | 
```

--------------------------------------------------------------------------------
/config/samples/fluxcd_v1_fluxinstance.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | apiVersion: fluxcd.controlplane.io/v1
 2 | kind: FluxInstance
 3 | metadata:
 4 |   name: flux
 5 | spec:
 6 |   distribution:
 7 |     version: "2.x"
 8 |     registry: "ghcr.io/fluxcd"
 9 |     artifact: "oci://ghcr.io/controlplaneio-fluxcd/flux-operator-manifests:latest"
10 |   components:
11 |     - source-controller
12 |     - kustomize-controller
13 |     - helm-controller
14 |     - notification-controller
15 |     - image-reflector-controller
16 |     - image-automation-controller
17 |   cluster:
18 |     type: openshift
19 |     multitenant: false
20 |     networkPolicy: true
21 |     domain: "cluster.local"
22 |   storage:
23 |     class: "standard"
24 |     size: "1Gi"
25 |   sharding:
26 |     shards: [ "shard1" ]
27 |     storage: persistent
28 |   sync:
29 |     kind: GitRepository
30 |     url: "https://github.com/controlplaneio-fluxcd/distribution.git"
31 |     ref: "refs/heads/main"
32 |     path: "tests/v2.3/sources"
33 |   kustomize:
34 |     patches:
35 |       - target:
36 |           kind: Deployment
37 |           labelSelector: "app.kubernetes.io/component in (kustomize-controller, helm-controller)"
38 |         patch: |
39 |           - op: add
40 |             path: /spec/template/spec/containers/0/args/-
41 |             value: --concurrent=10
42 |           - op: add
43 |             path: /spec/template/spec/containers/0/args/-
44 |             value: --requeue-dependency=10s
45 | 
```

--------------------------------------------------------------------------------
/hack/vendor-flux-manifests.sh:
--------------------------------------------------------------------------------

```bash
 1 | #!/usr/bin/env bash
 2 | 
 3 | # Copyright 2024 Stefan Prodan.
 4 | # SPDX-License-Identifier: AGPL-3.0
 5 | 
 6 | set -euo pipefail
 7 | 
 8 | REPOSITORY_ROOT=$(git rev-parse --show-toplevel)
 9 | DEST_DIR="${REPOSITORY_ROOT}/config/data/flux"
10 | IMG_DIR="${REPOSITORY_ROOT}/config/data/flux-images"
11 | VEX_DIR="${REPOSITORY_ROOT}/config/data/flux-vex"
12 | 
13 | info() {
14 |     echo '[INFO] ' "$@"
15 | }
16 | 
17 | fatal() {
18 |     echo '[ERROR] ' "$@" >&2
19 |     exit 1
20 | }
21 | 
22 | vendor() {
23 |   info "extracting manifests for Flux ${1}"
24 |   curl -sLO https://github.com/fluxcd/flux2/releases/download/${1}/manifests.tar.gz
25 |   mkdir -p "${DEST_DIR}/${1}"
26 |   tar xzf manifests.tar.gz -C "${DEST_DIR}/${1}"
27 |   rm -rf manifests.tar.gz
28 | }
29 | 
30 | for var in "$@"
31 | do
32 |     vendor "$var"
33 | done
34 | 
35 | info "downloading distro repository"
36 | curl -sLO https://github.com/controlplaneio-fluxcd/distribution/archive/refs/heads/main.tar.gz
37 | tar xzf main.tar.gz -C "${DEST_DIR}"
38 | 
39 | mkdir -p "${IMG_DIR}"
40 | cp -rf ${DEST_DIR}/distribution-main/images/* ${IMG_DIR}/
41 | info "flux image manifests copied to flux-images"
42 | mkdir -p "${VEX_DIR}"
43 | cp -rf ${DEST_DIR}/distribution-main/vex/* ${VEX_DIR}/
44 | info "flux OpenVEX documents copied to flux-vex"
45 | rm -rf ${DEST_DIR}/distribution-main
46 | rm -rf main.tar.gz
47 | 
48 | info "all manifests extracted to ${DEST_DIR}"
49 | 
```

--------------------------------------------------------------------------------
/internal/inputs/keys.go:
--------------------------------------------------------------------------------

```go
 1 | // Copyright 2025 Stefan Prodan.
 2 | // SPDX-License-Identifier: AGPL-3.0
 3 | 
 4 | package inputs
 5 | 
 6 | import (
 7 | 	"strings"
 8 | 	"unicode"
 9 | )
10 | 
11 | // NormalizedKey is a type alias for string that represents
12 | // a normalized key for use in templates.
13 | type NormalizedKey string
14 | 
15 | // NormalizeKeyForTemplate normalizes the given string
16 | // to a consistent format for use as a key in maps fed
17 | // to Go templates.
18 | //
19 | // We convert uppercase letters to lowercase, replace
20 | // spaces and punctuation with '_', and remove any
21 | // characters not in [a-z0-9_]. Then we split the
22 | // words by '_' and join only the resulting non-empty
23 | // words back together with '_'.
24 | func NormalizeKeyForTemplate(key string) NormalizedKey {
25 | 	// Map characters according to the rules above.
26 | 	key = strings.Map(func(r rune) rune {
27 | 		r = unicode.ToLower(r)
28 | 		if unicode.IsSpace(r) || unicode.IsPunct(r) {
29 | 			return '_'
30 | 		}
31 | 		if ('a' <= r && r <= 'z') || ('0' <= r && r <= '9') {
32 | 			return r
33 | 		}
34 | 		return -1
35 | 	}, key)
36 | 
37 | 	// Split by '_' and rejoin non-empty words with '_'.
38 | 	var nonEmptyWords []string
39 | 	for word := range strings.SplitSeq(key, "_") {
40 | 		if word != "" {
41 | 			nonEmptyWords = append(nonEmptyWords, word)
42 | 		}
43 | 	}
44 | 	key = strings.Join(nonEmptyWords, "_")
45 | 
46 | 	return NormalizedKey(key)
47 | }
48 | 
```

--------------------------------------------------------------------------------
/internal/web/kubeclient/suite_test.go:
--------------------------------------------------------------------------------

```go
 1 | // Copyright 2025 Stefan Prodan.
 2 | // SPDX-License-Identifier: AGPL-3.0
 3 | 
 4 | package kubeclient_test
 5 | 
 6 | import (
 7 | 	"context"
 8 | 	"testing"
 9 | 
10 | 	authzv1 "k8s.io/api/authorization/v1"
11 | 	corev1 "k8s.io/api/core/v1"
12 | 	rbacv1 "k8s.io/api/rbac/v1"
13 | 	"k8s.io/apimachinery/pkg/runtime"
14 | 	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
15 | 	"k8s.io/client-go/rest"
16 | 	ctrl "sigs.k8s.io/controller-runtime"
17 | 	"sigs.k8s.io/controller-runtime/pkg/client"
18 | 	"sigs.k8s.io/controller-runtime/pkg/envtest"
19 | )
20 | 
21 | var (
22 | 	ctx         context.Context
23 | 	cancel      context.CancelFunc
24 | 	testEnv     = &envtest.Environment{}
25 | 	testEnvConf *rest.Config
26 | 	testScheme  *runtime.Scheme
27 | 	testClient  client.Client
28 | )
29 | 
30 | func init() {
31 | 	testScheme = runtime.NewScheme()
32 | 	utilruntime.Must(corev1.AddToScheme(testScheme))
33 | 	utilruntime.Must(rbacv1.AddToScheme(testScheme))
34 | 	utilruntime.Must(authzv1.AddToScheme(testScheme))
35 | }
36 | 
37 | func TestMain(m *testing.M) {
38 | 	ctx, cancel = context.WithCancel(ctrl.SetupSignalHandler())
39 | 
40 | 	var err error
41 | 	testEnvConf, err = testEnv.Start()
42 | 	if err != nil {
43 | 		panic(err)
44 | 	}
45 | 
46 | 	testClient, err = client.New(testEnvConf, client.Options{Scheme: testScheme})
47 | 	if err != nil {
48 | 		panic(err)
49 | 	}
50 | 
51 | 	m.Run()
52 | 
53 | 	cancel()
54 | 	err = testEnv.Stop()
55 | 	if err != nil {
56 | 		panic(err)
57 | 	}
58 | }
59 | 
```

--------------------------------------------------------------------------------
/config/data/flux-images/v2.7.0/enterprise-distroless.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | images:
 2 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/source-controller
 3 |     newTag: v1.7.0
 4 |     digest: sha256:4b9352ef6068529cea3ac18b40daad2963ee02bc38a5b360f43bc35893e7f991
 5 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/kustomize-controller
 6 |     newTag: v1.7.0
 7 |     digest: sha256:4b6829a666bc19f732101b3f1e7c548e61f19d3e7c9c16b8fb7cace741861bbc
 8 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/helm-controller
 9 |     newTag: v1.4.0
10 |     digest: sha256:1c4fdf9193348afe5f3a358f6aa1920afa34435746e983f2c2f2c8d1b6a7cfaf
11 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/notification-controller
12 |     newTag: v1.7.1
13 |     digest: sha256:9059043f21cb1a8dfe236eef18031c6d5869b2c482b7f3954976d81a833ed772
14 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/image-reflector-controller
15 |     newTag: v1.0.1
16 |     digest: sha256:fbe17f6a304f32a1a518c6ee411bcdb57be1bc140670d44fc442334bcbc6bae6
17 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/image-automation-controller
18 |     newTag: v1.0.1
19 |     digest: sha256:148ce85e42191d216dbb3118b290cf0be3d926196e687dcb9655a66ca54214f2
20 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/source-watcher
21 |     newTag: v2.0.1
22 |     digest: sha256:67a5f6225f369bba4b211e5bd4642177ef2ac1d2ae3d988aaace3f90e081cc38
23 | 
```

--------------------------------------------------------------------------------
/config/data/flux-images/v2.7.1/enterprise-distroless.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | images:
 2 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/source-controller
 3 |     newTag: v1.7.1
 4 |     digest: sha256:487ad9d4f2c99fe3e70686b146bb61e40fddec32862c42e2dc1d3b0e144aca13
 5 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/kustomize-controller
 6 |     newTag: v1.7.0
 7 |     digest: sha256:4b6829a666bc19f732101b3f1e7c548e61f19d3e7c9c16b8fb7cace741861bbc
 8 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/helm-controller
 9 |     newTag: v1.4.1
10 |     digest: sha256:4266b34c51627784e61bfd622fee375ed87f2f17148a4bb0931155182ca94af5
11 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/notification-controller
12 |     newTag: v1.7.2
13 |     digest: sha256:1d46aab370cdb5cf20242eeff76add133b78e0f7730d8346997432620e4c4266
14 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/image-reflector-controller
15 |     newTag: v1.0.1
16 |     digest: sha256:fbe17f6a304f32a1a518c6ee411bcdb57be1bc140670d44fc442334bcbc6bae6
17 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/image-automation-controller
18 |     newTag: v1.0.1
19 |     digest: sha256:148ce85e42191d216dbb3118b290cf0be3d926196e687dcb9655a66ca54214f2
20 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/source-watcher
21 |     newTag: v2.0.1
22 |     digest: sha256:67a5f6225f369bba4b211e5bd4642177ef2ac1d2ae3d988aaace3f90e081cc38
23 | 
```

--------------------------------------------------------------------------------
/config/data/flux-images/v2.7.2/enterprise-distroless.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | images:
 2 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/source-controller
 3 |     newTag: v1.7.2
 4 |     digest: sha256:caa418fa715d0a68f2fa38017a2979bd98b789e98c57e21237ec3f308079702e
 5 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/kustomize-controller
 6 |     newTag: v1.7.1
 7 |     digest: sha256:457477ebb8c8c2bc44063c2b5684d1c82047cbda03fa1bd55e9465341cd8ee38
 8 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/helm-controller
 9 |     newTag: v1.4.2
10 |     digest: sha256:006fc8803a6efdcc175ac62cfd8574b14fd108c75b6dd5ca73f26fdc08f74d0e
11 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/notification-controller
12 |     newTag: v1.7.3
13 |     digest: sha256:c2f67cd68af7f0a9e5b5a50c68991b68c51f2d1aeb8b38e9fd4c40ed05b5f50a
14 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/image-reflector-controller
15 |     newTag: v1.0.2
16 |     digest: sha256:83f9c5025de4275b245217a8d5291292134332ac617bb2ba3f458b292285484d
17 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/image-automation-controller
18 |     newTag: v1.0.2
19 |     digest: sha256:1df274310a0fe7b5858ec737803c522e02935071b9aa69e0112f222d9e89858b
20 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/source-watcher
21 |     newTag: v2.0.2
22 |     digest: sha256:0b115ba1f9979ca9fcdc0940f8ee5d317513dd1143f19651462f99aa6159111b
23 | 
```

--------------------------------------------------------------------------------
/config/data/flux-images/v2.7.3/enterprise-distroless.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | images:
 2 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/source-controller
 3 |     newTag: v1.7.3
 4 |     digest: sha256:1eae8f938d1b62e9b43c5d714b833619737e5f400ab8fdcab3e22516448a1324
 5 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/kustomize-controller
 6 |     newTag: v1.7.2
 7 |     digest: sha256:5d92fc3276326078bb6fc2928ed1ab66f4b6b14c6a35f62ce693212c9a091777
 8 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/helm-controller
 9 |     newTag: v1.4.3
10 |     digest: sha256:8bbe14a7bcfd4bcfa1d987f222044ddb8e309fe79e5beefee8063a3fe9e19dfc
11 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/notification-controller
12 |     newTag: v1.7.4
13 |     digest: sha256:aab6f2451f51e3fe83ea15d83d03923aef40ea4e997e16e24dd5db6f5b770ac9
14 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/image-reflector-controller
15 |     newTag: v1.0.3
16 |     digest: sha256:f152d228d6052c0a7cbf23ce1422d575c44afcaca9cbd4607aaedfc9508d7afa
17 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/image-automation-controller
18 |     newTag: v1.0.3
19 |     digest: sha256:58c31306303795ba3ad104d87fdc17e60f4cb62925ad6b8dde02c965882c7aeb
20 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/source-watcher
21 |     newTag: v2.0.2
22 |     digest: sha256:1ccf7dcfc63a42ea298b2667ea764e56def20912d9f53e03449f57bae1ba4be0
23 | 
```

--------------------------------------------------------------------------------
/config/data/flux-images/v2.7.4/enterprise-distroless.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | images:
 2 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/source-controller
 3 |     newTag: v1.7.4
 4 |     digest: sha256:246e64b5950d686bba9dcc524f2381eb62d9be7c274db507769787dd0caaa149
 5 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/kustomize-controller
 6 |     newTag: v1.7.3
 7 |     digest: sha256:b63036a6c75c34575f71d9bd5e20617c5e8b66aea9e4d56e39e76899f541c1cf
 8 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/helm-controller
 9 |     newTag: v1.4.4
10 |     digest: sha256:eeb31b9cd64208a39094e3f85fe93ed3a985cbc00159b27dd13f21f70d734efa
11 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/notification-controller
12 |     newTag: v1.7.5
13 |     digest: sha256:9c4ce4620a8fdaf0ef943a07177bbf464b06ba436fc2f6efd083680b73aa24cf
14 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/image-reflector-controller
15 |     newTag: v1.0.4
16 |     digest: sha256:e7bf730ebb063eb73cac51e09adbb2c88bd3a5589cb4d635a1844c0606653cb9
17 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/image-automation-controller
18 |     newTag: v1.0.4
19 |     digest: sha256:e8f9486dd83b5dfac0648dc7ffea173b559676f1c828e845a29e4d406f893531
20 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/source-watcher
21 |     newTag: v2.0.3
22 |     digest: sha256:6ebd76169b79755b500854eab2ad4fc981d0d7e06133fbbca1c5edf476ecfa3d
23 | 
```

--------------------------------------------------------------------------------
/config/data/flux-images/v2.7.5/enterprise-distroless.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | images:
 2 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/source-controller
 3 |     newTag: v1.7.4
 4 |     digest: sha256:6128e1ca88038f956639005144704da3d377997359e20f66d22b2c9e44d9be71
 5 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/kustomize-controller
 6 |     newTag: v1.7.3
 7 |     digest: sha256:5f8dc3afea8322bf5476dd07aec8c0a6bad3b3e76657560cd92cf5875036f661
 8 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/helm-controller
 9 |     newTag: v1.4.5
10 |     digest: sha256:566671ec53feefd15431e3f2a33065b141d1592086ca26a18adf32347845899e
11 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/notification-controller
12 |     newTag: v1.7.5
13 |     digest: sha256:7b0a784dee0d40241128e08114b5ed915510c834c56a52fa363dee915e2de0de
14 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/image-reflector-controller
15 |     newTag: v1.0.4
16 |     digest: sha256:fff291ea8d362dba609658c0e2be50eba6351b94c96c1d420211f110d77723ff
17 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/image-automation-controller
18 |     newTag: v1.0.4
19 |     digest: sha256:ddb71fccea5ea121920d958ab27c9ad2b96714c5b1c921daccee504f4bf760dc
20 |   - name: ghcr.io/controlplaneio-fluxcd/distroless/source-watcher
21 |     newTag: v2.0.3
22 |     digest: sha256:a1c228d518cc836b66227ab65f1c7723006437c075c2679b7ee6b5bb4d6d09d6
23 | 
```

--------------------------------------------------------------------------------
/cmd/cli/suspend_inputprovider.go:
--------------------------------------------------------------------------------

```go
 1 | // Copyright 2025 Stefan Prodan.
 2 | // SPDX-License-Identifier: AGPL-3.0
 3 | 
 4 | package main
 5 | 
 6 | import (
 7 | 	"context"
 8 | 	"fmt"
 9 | 
10 | 	"github.com/spf13/cobra"
11 | 
12 | 	fluxcdv1 "github.com/controlplaneio-fluxcd/flux-operator/api/v1"
13 | )
14 | 
15 | var suspendInputProviderCmd = &cobra.Command{
16 | 	Use:               "inputprovider [name]",
17 | 	Aliases:           []string{"rsip", "resourcesetinputprovider"},
18 | 	Short:             "Suspend ResourceSetInputProvider reconciliation",
19 | 	Args:              cobra.ExactArgs(1),
20 | 	RunE:              suspendInputProviderCmdRun,
21 | 	ValidArgsFunction: resourceNamesCompletionFunc(fluxcdv1.GroupVersion.WithKind(fluxcdv1.ResourceSetInputProviderKind)),
22 | }
23 | 
24 | func init() {
25 | 	suspendCmd.AddCommand(suspendInputProviderCmd)
26 | }
27 | 
28 | func suspendInputProviderCmdRun(cmd *cobra.Command, args []string) error {
29 | 	if len(args) != 1 {
30 | 		return fmt.Errorf("name is required")
31 | 	}
32 | 
33 | 	name := args[0]
34 | 	now := timeNow()
35 | 	gvk := fluxcdv1.GroupVersion.WithKind(fluxcdv1.ResourceSetInputProviderKind)
36 | 
37 | 	ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
38 | 	defer cancel()
39 | 
40 | 	err := toggleSuspension(ctx, gvk, name, *kubeconfigArgs.Namespace, now, true)
41 | 	if err != nil {
42 | 		return err
43 | 	}
44 | 
45 | 	rootCmd.Println(`✔`, "Reconciliation suspended")
46 | 	return nil
47 | }
48 | 
```

--------------------------------------------------------------------------------
/config/data/flux-vex/v2.2.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "@context": "https://openvex.dev/ns/v0.2.0",
 3 |   "@id": "https://openvex.dev/docs/public/vex-f843e58a33079b9f9344d4b4e72a3dcc0ee7ad51825e087b96692dccaf21f2d8",
 4 |   "author": "[email protected]",
 5 |   "role": "Enterprise Flux Maintainers",
 6 |   "timestamp": "2024-05-23T17:26:04.422544+03:00",
 7 |   "last_updated": "2024-05-24T00:38:58.404614+03:00",
 8 |   "version": 3,
 9 |   "statements": [
10 |     {
11 |       "vulnerability": {
12 |         "name": "CVE-2019-25210"
13 |       },
14 |       "timestamp": "2024-05-24T00:38:51.232345+03:00",
15 |       "products": [
16 |         {
17 |           "@id": "pkg:oci/source-controller"
18 |         }
19 |       ],
20 |       "status": "not_affected",
21 |       "justification": "vulnerable_code_not_present",
22 |       "impact_statement": "This CVE has been dismissed by the Helm team https://helm.sh/blog/response-cve-2019-25210/"
23 |     },
24 |     {
25 |       "vulnerability": {
26 |         "name": "CVE-2019-25210"
27 |       },
28 |       "timestamp": "2024-05-24T00:38:58.404614+03:00",
29 |       "products": [
30 |         {
31 |           "@id": "pkg:oci/helm-controller"
32 |         }
33 |       ],
34 |       "status": "not_affected",
35 |       "justification": "vulnerable_code_not_present",
36 |       "impact_statement": "This CVE has been dismissed by the Helm team https://helm.sh/blog/response-cve-2019-25210/"
37 |     }
38 |   ]
39 | }
40 | 
```

--------------------------------------------------------------------------------
/cmd/mcp/toolbox/helpers.go:
--------------------------------------------------------------------------------

```go
 1 | // Copyright 2025 Stefan Prodan.
 2 | // SPDX-License-Identifier: AGPL-3.0
 3 | 
 4 | package toolbox
 5 | 
 6 | import (
 7 | 	"fmt"
 8 | 
 9 | 	"github.com/modelcontextprotocol/go-sdk/mcp"
10 | )
11 | 
12 | // NewToolResultText creates a new CallToolResult with a text content.
13 | func NewToolResultText(text string) (*mcp.CallToolResult, any, error) {
14 | 	return &mcp.CallToolResult{
15 | 		Content: []mcp.Content{&mcp.TextContent{Text: text}},
16 | 	}, nil, nil
17 | }
18 | 
19 | // NewToolResultError creates a new CallToolResult with an error message.
20 | // Any errors that originate from the tool SHOULD be reported inside the result object.
21 | func NewToolResultError(text string) (*mcp.CallToolResult, any, error) {
22 | 	return &mcp.CallToolResult{
23 | 		Content: []mcp.Content{&mcp.TextContent{Text: text}},
24 | 		IsError: true,
25 | 	}, nil, nil
26 | }
27 | 
28 | // NewToolResultErrorFromErr creates a new CallToolResult with an error message.
29 | // If an error is provided, its details will be appended to the text message.
30 | // Any errors that originate from the tool SHOULD be reported inside the result object.
31 | func NewToolResultErrorFromErr(text string, err error) (*mcp.CallToolResult, any, error) {
32 | 	if err != nil {
33 | 		text = fmt.Sprintf("%s: %v", text, err)
34 | 	}
35 | 	return &mcp.CallToolResult{
36 | 		Content: []mcp.Content{&mcp.TextContent{Text: text}},
37 | 		IsError: true,
38 | 	}, nil, nil
39 | }
40 | 
```

--------------------------------------------------------------------------------
/config/data/flux-images/v2.7.5/enterprise-distroless-fips.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | images:
 2 |   - name: ghcr.io/controlplaneio-fluxcd/distroless-fips/source-controller
 3 |     newTag: v1.7.4
 4 |     digest: sha256:6153f67c81d1e48b8118726263eb37ab89f3fd31ba66b1422f6455d0fb565b07
 5 |   - name: ghcr.io/controlplaneio-fluxcd/distroless-fips/kustomize-controller
 6 |     newTag: v1.7.3
 7 |     digest: sha256:e7dce531664c2b6139034d8fcb0f7d675ca4a6b958841d1d8532435b374ae5da
 8 |   - name: ghcr.io/controlplaneio-fluxcd/distroless-fips/helm-controller
 9 |     newTag: v1.4.5
10 |     digest: sha256:ad99779a601a8afd860dfd74f6723e4642b3597ea3580b9333002b5d77ebc4b9
11 |   - name: ghcr.io/controlplaneio-fluxcd/distroless-fips/notification-controller
12 |     newTag: v1.7.5
13 |     digest: sha256:9dd5a21dae13d3550a80b310e4143e9b0da8103247cde50c177bfa23df8ba7ea
14 |   - name: ghcr.io/controlplaneio-fluxcd/distroless-fips/image-reflector-controller
15 |     newTag: v1.0.4
16 |     digest: sha256:623297748715d21a4ecc0ecd1f51cafa71624134b4990f2472f2691353a95cec
17 |   - name: ghcr.io/controlplaneio-fluxcd/distroless-fips/image-automation-controller
18 |     newTag: v1.0.4
19 |     digest: sha256:577da18b6f608112a52fe8db9edacb6bac3441e91fca80c0d8c8fcd782c3e1e3
20 |   - name: ghcr.io/controlplaneio-fluxcd/distroless-fips/source-watcher
21 |     newTag: v2.0.3
22 |     digest: sha256:aafb8d2e494bc48e4e476c0d38c16991a508fb294db82481a91dee5445976b95
23 | 
```

--------------------------------------------------------------------------------
/internal/reporter/crds.go:
--------------------------------------------------------------------------------

```go
 1 | // Copyright 2024 Stefan Prodan.
 2 | // SPDX-License-Identifier: AGPL-3.0
 3 | 
 4 | package reporter
 5 | 
 6 | import (
 7 | 	"context"
 8 | 	"errors"
 9 | 	"fmt"
10 | 
11 | 	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
12 | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
13 | 	"sigs.k8s.io/controller-runtime/pkg/client"
14 | )
15 | 
16 | func (r *FluxStatusReporter) listCRDs(ctx context.Context) ([]metav1.GroupVersionKind, error) {
17 | 	var list apiextensionsv1.CustomResourceDefinitionList
18 | 	if err := r.List(ctx, &list, client.InNamespace(""), r.labelSelector); err != nil {
19 | 		return nil, fmt.Errorf("failed to list CRDs: %w", err)
20 | 	}
21 | 
22 | 	if len(list.Items) == 0 {
23 | 		return nil, errors.New("no Flux CRDs found")
24 | 	}
25 | 
26 | 	gvkList := make([]metav1.GroupVersionKind, len(list.Items))
27 | 	for i, crd := range list.Items {
28 | 		gvk := metav1.GroupVersionKind{
29 | 			Group: crd.Spec.Group,
30 | 			Kind:  crd.Spec.Names.Kind,
31 | 		}
32 | 		versions := crd.Status.StoredVersions
33 | 		if len(versions) > 0 {
34 | 			gvk.Version = versions[len(versions)-1]
35 | 		} else {
36 | 			return nil, fmt.Errorf("no stored versions found for CRD %s", crd.Name)
37 | 		}
38 | 		gvkList[i] = gvk
39 | 	}
40 | 
41 | 	return gvkList, nil
42 | }
43 | 
44 | func gvkFor(kind string, crds []metav1.GroupVersionKind) *metav1.GroupVersionKind {
45 | 	for _, gvk := range crds {
46 | 		if gvk.Kind == kind {
47 | 			return &gvk
48 | 		}
49 | 	}
50 | 	return nil
51 | }
52 | 
```

--------------------------------------------------------------------------------
/internal/builder/testdata/resourceset/exclude.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | apiVersion: fluxcd.controlplane.io/v1
 2 | kind: ResourceSet
 3 | metadata:
 4 |   name: tenants
 5 |   namespace: flux-system
 6 | spec:
 7 |   inputs:
 8 |     - tenant: sre
 9 |       role: "cluster-admin"
10 |     - tenant: dev
11 |       role: "namespace-admin"
12 |   resources:
13 |     - apiVersion: v1
14 |       kind: Namespace
15 |       metadata:
16 |         name: << inputs.tenant >>
17 |     - apiVersion: v1
18 |       kind: ServiceAccount
19 |       metadata:
20 |         name: flux
21 |         namespace: << inputs.tenant >>
22 |     - apiVersion: rbac.authorization.k8s.io/v1
23 |       kind: RoleBinding
24 |       metadata:
25 |         name: flux
26 |         namespace: << inputs.tenant >>
27 |       subjects:
28 |         - kind: ServiceAccount
29 |           name: flux
30 |           namespace: << inputs.tenant >>
31 |       roleRef:
32 |         apiGroup: rbac.authorization.k8s.io
33 |         kind: ClusterRole
34 |         name: admin
35 |     - apiVersion: rbac.authorization.k8s.io/v1
36 |       kind: ClusterRoleBinding
37 |       metadata:
38 |         name: flux-<< inputs.tenant >>
39 |         annotations:
40 |           fluxcd.controlplane.io/reconcile: << if eq inputs.role "cluster-admin" >>enabled<< else >>disabled<< end >>
41 |       subjects:
42 |         - kind: ServiceAccount
43 |           name: flux
44 |           namespace: << inputs.tenant >>
45 |       roleRef:
46 |         apiGroup: rbac.authorization.k8s.io
47 |         kind: ClusterRole
48 |         name: cluster-admin
49 | 
```

--------------------------------------------------------------------------------
/cmd/mcp/k8s/client_test.go:
--------------------------------------------------------------------------------

```go
 1 | // Copyright 2025 Stefan Prodan.
 2 | // SPDX-License-Identifier: AGPL-3.0
 3 | 
 4 | package k8s
 5 | 
 6 | import (
 7 | 	"testing"
 8 | 
 9 | 	. "github.com/onsi/gomega"
10 | 	"sigs.k8s.io/controller-runtime/pkg/client/fake"
11 | )
12 | 
13 | func TestParseGroupVersionKind(t *testing.T) {
14 | 	kubeClient := Client{
15 | 		Client: fake.NewClientBuilder().WithScheme(NewTestScheme()).Build(),
16 | 	}
17 | 
18 | 	tests := []struct {
19 | 		name       string
20 | 		apiVersion string
21 | 		kind       string
22 | 		result     string
23 | 		matchErr   string
24 | 	}{
25 | 		{
26 | 			name:       "valid inputs",
27 | 			apiVersion: "fluxcd.controlplane.io/v1",
28 | 			kind:       "ResourceSet",
29 | 			result:     "fluxcd.controlplane.io/v1, Kind=ResourceSet",
30 | 		},
31 | 		{
32 | 			name:       "invalid api version",
33 | 			apiVersion: "helm.toolkit.fluxcd.io/v1/v2",
34 | 			kind:       "HelmRelease",
35 | 			matchErr:   "unexpected",
36 | 		},
37 | 		{
38 | 			name:       "invalid kind",
39 | 			apiVersion: "fluxcd.controlplane.io/v1",
40 | 			matchErr:   "not specified",
41 | 		},
42 | 	}
43 | 
44 | 	for _, tt := range tests {
45 | 		t.Run(tt.name, func(t *testing.T) {
46 | 			g := NewWithT(t)
47 | 
48 | 			gvk, err := kubeClient.ParseGroupVersionKind(tt.apiVersion, tt.kind)
49 | 			if tt.matchErr != "" {
50 | 				g.Expect(err).To(HaveOccurred())
51 | 				g.Expect(err.Error()).To(ContainSubstring(tt.matchErr))
52 | 			} else {
53 | 				g.Expect(err).NotTo(HaveOccurred())
54 | 				g.Expect(gvk.String()).To(Equal(tt.result))
55 | 			}
56 | 		})
57 | 	}
58 | 
59 | }
60 | 
```

--------------------------------------------------------------------------------
/cmd/cli/testdata/build_resourceset/rset-standalone.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | ---
 2 | apiVersion: fluxcd.controlplane.io/v1
 3 | kind: ResourceSet
 4 | metadata:
 5 |   name: apps
 6 |   namespace: test
 7 | spec:
 8 |   inputs:
 9 |     - tenantName: team1
10 |       id: 340788154
11 |       applications:
12 |         - name: app1
13 |           envs:
14 |             - name: staging
15 |               version: v1.0.1
16 |             - name: production
17 |               version: v1.0.0
18 |   resourcesTemplate: |
19 |     <<- $id := inputs.id >>    
20 |     <<- $tenant := inputs.tenantName >>
21 |     <<- range $app := inputs.applications >>
22 |     <<- $appName := $app.name >>
23 |     <<- range $env := $app.envs >>
24 |     ---
25 |     apiVersion: source.toolkit.fluxcd.io/v1
26 |     kind: OCIRepository
27 |     metadata:
28 |       name: << $appName >>
29 |       namespace: << $tenant >>-<< $env.name >>
30 |       annotations:
31 |         fluxcd.controlplane.io/id: << $id | quote >>
32 |     spec:
33 |       interval: 10m
34 |       url: oci://registry.example.com/<< $appName >>
35 |       ref:
36 |         tag: << $env.version >>
37 |     ---
38 |     apiVersion: kustomize.toolkit.fluxcd.io/v1
39 |     kind: Kustomization
40 |     metadata:
41 |       name: << $appName >>
42 |       namespace: << $tenant >>-<< $env.name >>
43 |       annotations:
44 |         fluxcd.controlplane.io/id: << $id | quote >>
45 |     spec:
46 |       interval: 1h
47 |       prune: true
48 |       sourceRef:
49 |         kind: OCIRepository
50 |         name: << $appName >>
51 |       path: ./
52 |     <<- end >>
53 |     <<- end >>
54 | 
```

--------------------------------------------------------------------------------
/cmd/mcp/toolbox/get_instance_test.go:
--------------------------------------------------------------------------------

```go
 1 | // Copyright 2025 Stefan Prodan.
 2 | // SPDX-License-Identifier: AGPL-3.0
 3 | 
 4 | package toolbox
 5 | 
 6 | import (
 7 | 	"context"
 8 | 	"testing"
 9 | 	"time"
10 | 
11 | 	"github.com/modelcontextprotocol/go-sdk/mcp"
12 | 	. "github.com/onsi/gomega"
13 | 	cli "k8s.io/cli-runtime/pkg/genericclioptions"
14 | 
15 | 	"github.com/controlplaneio-fluxcd/flux-operator/cmd/mcp/k8s"
16 | )
17 | 
18 | func TestManager_HandleGetFluxInstance(t *testing.T) {
19 | 	configFile := "testdata/kubeconfig.yaml"
20 | 	t.Setenv("KUBECONFIG", configFile)
21 | 
22 | 	m := &Manager{
23 | 		kubeconfig: k8s.NewKubeConfig(),
24 | 		kubeClient: k8s.NewClientFactory(cli.NewConfigFlags(false)),
25 | 		timeout:    time.Second,
26 | 	}
27 | 
28 | 	request := &mcp.CallToolRequest{
29 | 		Params: &mcp.CallToolParamsRaw{
30 | 			Name: "get_flux_instance",
31 | 		},
32 | 	}
33 | 
34 | 	tests := []struct {
35 | 		testName string
36 | 		matchErr string
37 | 	}{
38 | 		{
39 | 			testName: "fails with invalid kubeconfig",
40 | 			matchErr: "Failed to get Kubernetes client",
41 | 		},
42 | 	}
43 | 
44 | 	for _, test := range tests {
45 | 		t.Run(test.testName, func(t *testing.T) {
46 | 			g := NewWithT(t)
47 | 
48 | 			result, content, err := m.HandleGetFluxInstance(context.Background(), request, struct{}{})
49 | 			g.Expect(err).ToNot(HaveOccurred())
50 | 			textContent, ok := result.Content[0].(*mcp.TextContent)
51 | 			g.Expect(ok).To(BeTrue())
52 | 
53 | 			g.Expect(result.IsError).To(BeTrue())
54 | 			g.Expect(textContent.Text).To(ContainSubstring(test.matchErr))
55 | 			_ = content
56 | 		})
57 | 	}
58 | }
59 | 
```

--------------------------------------------------------------------------------
/test/olm/instance_test.go:
--------------------------------------------------------------------------------

```go
 1 | // Copyright 2024 Stefan Prodan.
 2 | // SPDX-License-Identifier: AGPL-3.0
 3 | 
 4 | package e2eolm
 5 | 
 6 | import (
 7 | 	"os/exec"
 8 | 	"time"
 9 | 
10 | 	. "github.com/onsi/ginkgo/v2"
11 | 	. "github.com/onsi/gomega"
12 | 
13 | 	utils "github.com/controlplaneio-fluxcd/flux-operator/test/e2e"
14 | )
15 | 
16 | var _ = Describe("FluxInstance", Ordered, func() {
17 | 	Context("installation", func() {
18 | 		It("should run successfully", func() {
19 | 			By("reconcile FluxInstance")
20 | 			verifyFluxInstanceReconcile := func() error {
21 | 				cmd := exec.Command("kubectl", "apply",
22 | 					"-f", "config/samples/fluxcd_v1_fluxinstance.yaml", "-n", namespace,
23 | 				)
24 | 				_, err := utils.Run(cmd, "/test/olm")
25 | 				ExpectWithOffset(2, err).NotTo(HaveOccurred())
26 | 
27 | 				cmd = exec.Command("kubectl", "wait", "FluxInstance/flux", "-n", namespace,
28 | 					"--for=condition=Ready", "--timeout=5m",
29 | 				)
30 | 				_, err = utils.Run(cmd, "/test/olm")
31 | 				ExpectWithOffset(2, err).NotTo(HaveOccurred())
32 | 				return nil
33 | 			}
34 | 			EventuallyWithOffset(1, verifyFluxInstanceReconcile, 5*time.Minute, 10*time.Second).Should(Succeed())
35 | 		})
36 | 	})
37 | 
38 | 	Context("uninstallation", func() {
39 | 		It("should run successfully", func() {
40 | 			By("delete FluxInstance")
41 | 			cmd := exec.Command("kubectl", "delete", "FluxInstance/flux",
42 | 				"--timeout=30s", "-n", namespace)
43 | 			_, err := utils.Run(cmd, "/test/olm")
44 | 			Expect(err).NotTo(HaveOccurred())
45 | 		})
46 | 	})
47 | })
48 | 
```

--------------------------------------------------------------------------------
/cmd/cli/testdata/build_resourceset/golden-labeled.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | ---
 2 | apiVersion: source.toolkit.fluxcd.io/v1
 3 | kind: OCIRepository
 4 | metadata:
 5 |   labels:
 6 |     resourceset.fluxcd.controlplane.io/name: app1
 7 |     resourceset.fluxcd.controlplane.io/namespace: apps
 8 |   name: app1-tenant1
 9 |   namespace: apps
10 | spec:
11 |   interval: 10m
12 |   ref:
13 |     semver: 6.7.x
14 |   url: oci://my.registry/org/charts/app1
15 | ---
16 | apiVersion: source.toolkit.fluxcd.io/v1
17 | kind: OCIRepository
18 | metadata:
19 |   labels:
20 |     resourceset.fluxcd.controlplane.io/name: app1
21 |     resourceset.fluxcd.controlplane.io/namespace: apps
22 |   name: app1-tenant2
23 |   namespace: apps
24 | spec:
25 |   interval: 10m
26 |   ref:
27 |     semver: 6.6.x
28 |   url: oci://my.registry/org/charts/app1
29 | ---
30 | apiVersion: helm.toolkit.fluxcd.io/v2
31 | kind: HelmRelease
32 | metadata:
33 |   labels:
34 |     resourceset.fluxcd.controlplane.io/name: app1
35 |     resourceset.fluxcd.controlplane.io/namespace: apps
36 |   name: app1-tenant1
37 |   namespace: apps
38 | spec:
39 |   chartRef:
40 |     kind: OCIRepository
41 |     name: app1-tenant1
42 |   interval: 1h
43 |   releaseName: app1-tenant1
44 |   values:
45 |     replicaCount: 2
46 | ---
47 | apiVersion: helm.toolkit.fluxcd.io/v2
48 | kind: HelmRelease
49 | metadata:
50 |   labels:
51 |     resourceset.fluxcd.controlplane.io/name: app1
52 |     resourceset.fluxcd.controlplane.io/namespace: apps
53 |   name: app1-tenant2
54 |   namespace: apps
55 | spec:
56 |   chartRef:
57 |     kind: OCIRepository
58 |     name: app1-tenant2
59 |   interval: 1h
60 |   releaseName: app1-tenant2
61 |   values:
62 |     replicaCount: 3
63 | 
```

--------------------------------------------------------------------------------
/cmd/cli/testdata/build_resourceset/golden-named.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | ---
 2 | apiVersion: source.toolkit.fluxcd.io/v1
 3 | kind: OCIRepository
 4 | metadata:
 5 |   labels:
 6 |     resourceset.fluxcd.controlplane.io/name: app1
 7 |     resourceset.fluxcd.controlplane.io/namespace: apps
 8 |   name: app1-tenant1
 9 |   namespace: apps
10 | spec:
11 |   interval: 10m
12 |   ref:
13 |     semver: 7.8.x
14 |   url: oci://my.registry/org/charts/app1
15 | ---
16 | apiVersion: source.toolkit.fluxcd.io/v1
17 | kind: OCIRepository
18 | metadata:
19 |   labels:
20 |     resourceset.fluxcd.controlplane.io/name: app1
21 |     resourceset.fluxcd.controlplane.io/namespace: apps
22 |   name: app1-tenant2
23 |   namespace: apps
24 | spec:
25 |   interval: 10m
26 |   ref:
27 |     semver: 5.9.x
28 |   url: oci://my.registry/org/charts/app1
29 | ---
30 | apiVersion: helm.toolkit.fluxcd.io/v2
31 | kind: HelmRelease
32 | metadata:
33 |   labels:
34 |     resourceset.fluxcd.controlplane.io/name: app1
35 |     resourceset.fluxcd.controlplane.io/namespace: apps
36 |   name: app1-tenant1
37 |   namespace: apps
38 | spec:
39 |   chartRef:
40 |     kind: OCIRepository
41 |     name: app1-tenant1
42 |   interval: 1h
43 |   releaseName: app1-tenant1
44 |   values:
45 |     replicaCount: 1
46 | ---
47 | apiVersion: helm.toolkit.fluxcd.io/v2
48 | kind: HelmRelease
49 | metadata:
50 |   labels:
51 |     resourceset.fluxcd.controlplane.io/name: app1
52 |     resourceset.fluxcd.controlplane.io/namespace: apps
53 |   name: app1-tenant2
54 |   namespace: apps
55 | spec:
56 |   chartRef:
57 |     kind: OCIRepository
58 |     name: app1-tenant2
59 |   interval: 1h
60 |   releaseName: app1-tenant2
61 |   values:
62 |     replicaCount: 2
63 | 
```

--------------------------------------------------------------------------------
/config/samples/fluxcd_v1_resourceset.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | apiVersion: fluxcd.controlplane.io/v1
 2 | kind: ResourceSet
 3 | metadata:
 4 |   name: podinfo
 5 |   namespace: default
 6 |   annotations:
 7 |     fluxcd.controlplane.io/reconcile: "enabled"
 8 |     fluxcd.controlplane.io/reconcileEvery: "30m"
 9 |     fluxcd.controlplane.io/reconcileTimeout: "5m"
10 | spec:
11 |   dependsOn:
12 |     - apiVersion: apiextensions.k8s.io/v1
13 |       kind: CustomResourceDefinition
14 |       name: helmreleases.helm.toolkit.fluxcd.io
15 |       ready: true
16 |   commonMetadata:
17 |     labels:
18 |       app.kubernetes.io/name: podinfo
19 |   inputs:
20 |     - tenant: "team1"
21 |       version: "6.7.x"
22 |       replicas: "2"
23 |     - tenant: "team2"
24 |       version: "6.6.x"
25 |       replicas: "3"
26 |   resources:
27 |     - apiVersion: source.toolkit.fluxcd.io/v1beta2
28 |       kind: OCIRepository
29 |       metadata:
30 |         name: podinfo-<< inputs.tenant >>
31 |         namespace: default
32 |       spec:
33 |         interval: 10m
34 |         url: oci://ghcr.io/stefanprodan/charts/podinfo
35 |         ref:
36 |           semver: << inputs.version | quote >>
37 |     - apiVersion: helm.toolkit.fluxcd.io/v2
38 |       kind: HelmRelease
39 |       metadata:
40 |         name: podinfo-<< inputs.tenant >>
41 |         namespace: default
42 |       spec:
43 |         interval: 1h
44 |         releaseName: podinfo-<< inputs.tenant >>
45 |         chartRef:
46 |           kind: OCIRepository
47 |           name: podinfo-<< inputs.tenant >>
48 |         values:
49 |           replicaCount: << inputs.replicas | int >>
50 | 
```

--------------------------------------------------------------------------------
/cmd/cli/wait_instance.go:
--------------------------------------------------------------------------------

```go
 1 | // Copyright 2025 Stefan Prodan.
 2 | // SPDX-License-Identifier: AGPL-3.0
 3 | 
 4 | package main
 5 | 
 6 | import (
 7 | 	"context"
 8 | 	"fmt"
 9 | 
10 | 	"github.com/spf13/cobra"
11 | 
12 | 	fluxcdv1 "github.com/controlplaneio-fluxcd/flux-operator/api/v1"
13 | )
14 | 
15 | var waitInstanceCmd = &cobra.Command{
16 | 	Use:   "instance [name]",
17 | 	Short: "Wait for FluxInstance to become ready",
18 | 	Example: `  # Wait for an instance to become ready
19 |   flux-operator -n flux-system wait instance flux
20 | 
21 |   # Wait for an instance to become ready with a custom timeout
22 |   flux-operator -n flux-system wait instance flux --timeout=5m
23 | `,
24 | 	Args:              cobra.ExactArgs(1),
25 | 	RunE:              waitInstanceCmdRun,
26 | 	ValidArgsFunction: resourceNamesCompletionFunc(fluxcdv1.GroupVersion.WithKind(fluxcdv1.FluxInstanceKind)),
27 | }
28 | 
29 | func init() {
30 | 	waitCmd.AddCommand(waitInstanceCmd)
31 | }
32 | 
33 | func waitInstanceCmdRun(cmd *cobra.Command, args []string) error {
34 | 	if len(args) != 1 {
35 | 		return fmt.Errorf("name is required")
36 | 	}
37 | 
38 | 	name := args[0]
39 | 	gvk := fluxcdv1.GroupVersion.WithKind(fluxcdv1.FluxInstanceKind)
40 | 
41 | 	ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
42 | 	defer cancel()
43 | 
44 | 	rootCmd.Println(`◎`, "Waiting for instance to become ready...")
45 | 	msg, err := waitForResourceReconciliation(ctx, gvk, name, *kubeconfigArgs.Namespace, "", rootArgs.timeout)
46 | 	if err != nil {
47 | 		return err
48 | 	}
49 | 
50 | 	rootCmd.Println(`✔`, msg)
51 | 	return nil
52 | }
53 | 
```

--------------------------------------------------------------------------------
/hack/build-dist-manifests.sh:
--------------------------------------------------------------------------------

```bash
 1 | #!/usr/bin/env bash
 2 | 
 3 | # Copyright 2024 Stefan Prodan.
 4 | # SPDX-License-Identifier: AGPL-3.0
 5 | 
 6 | set -euo pipefail
 7 | 
 8 | REPOSITORY_ROOT=$(git rev-parse --show-toplevel)
 9 | DEST_DIR="${REPOSITORY_ROOT}/disto"
10 | 
11 | info() {
12 |     echo '[INFO] ' "$@"
13 | }
14 | 
15 | fatal() {
16 |     echo '[ERROR] ' "$@" >&2
17 |     exit 1
18 | }
19 | 
20 | rm -rf ${DEST_DIR}
21 | mkdir -p ${DEST_DIR}/flux-operator
22 | kustomize build config/default > ${DEST_DIR}/flux-operator/install.yaml
23 | info "operator manifests generated to disto/flux-operator"
24 | 
25 | mkdir -p ${DEST_DIR}/flux-operator-mcp
26 | kustomize build config/mcp > ${DEST_DIR}/flux-operator-mcp/install.yaml
27 | info "MCP server manifests generated to disto/flux-operator-mcp"
28 | 
29 | mkdir -p ${DEST_DIR}/flux
30 | cp -r config/data/flux/* ${DEST_DIR}/flux/
31 | info "flux manifests copied to disto/flux"
32 | 
33 | info "downloading distro repository"
34 | curl -sLO https://github.com/controlplaneio-fluxcd/distribution/archive/refs/heads/main.tar.gz
35 | tar xzf main.tar.gz -C "${DEST_DIR}"
36 | 
37 | mkdir -p "${DEST_DIR}/flux-images"
38 | cp -r ${DEST_DIR}/distribution-main/images/* ${DEST_DIR}/flux-images/
39 | info "flux image manifests copied to disto/flux-images"
40 | 
41 | mkdir -p "${DEST_DIR}/flux-vex"
42 | cp -r ${DEST_DIR}/distribution-main/vex/* ${DEST_DIR}/flux-vex/
43 | info "flux OpenVEX documents copied to disto/flux-vex"
44 | 
45 | rm -rf ${DEST_DIR}/distribution-main
46 | rm -rf main.tar.gz
47 | 
48 | info "all manifests generated to disto/"
49 | tree -d ${DEST_DIR}
50 | 
```

--------------------------------------------------------------------------------
/internal/inputs/keys_test.go:
--------------------------------------------------------------------------------

```go
 1 | // Copyright 2025 Stefan Prodan.
 2 | // SPDX-License-Identifier: AGPL-3.0
 3 | 
 4 | package inputs_test
 5 | 
 6 | import (
 7 | 	"testing"
 8 | 
 9 | 	. "github.com/onsi/gomega"
10 | 
11 | 	"github.com/controlplaneio-fluxcd/flux-operator/internal/inputs"
12 | )
13 | 
14 | func TestNormalizeKeyForTemplate(t *testing.T) {
15 | 	for _, tt := range []struct {
16 | 		key         string
17 | 		expectedKey string
18 | 	}{
19 | 		{key: "My_ResourceSet", expectedKey: "my_resourceset"},
20 | 		{key: "My-ResourceSet", expectedKey: "my_resourceset"},
21 | 		{key: "My/ResourceSet", expectedKey: "my_resourceset"},
22 | 		{key: "My,ResourceSet", expectedKey: "my_resourceset"},
23 | 		{key: "My:ResourceSet", expectedKey: "my_resourceset"},
24 | 		{key: "My.ResourceSet", expectedKey: "my_resourceset"},
25 | 		{key: "My!ResourceSet", expectedKey: "my_resourceset"},
26 | 		{key: "My?ResourceSet", expectedKey: "my_resourceset"},
27 | 		{key: "My@ResourceSet", expectedKey: "my_resourceset"},
28 | 		{key: "My#ResourceSet", expectedKey: "my_resourceset"},
29 | 		{key: "My ResourceSet", expectedKey: "my_resourceset"},
30 | 		{key: "_a---b_-_", expectedKey: "a_b"},
31 | 		{key: "A0..B", expectedKey: "a0_b"},
32 | 		{key: "A  B", expectedKey: "a_b"},
33 | 		{key: "A@@B", expectedKey: "a_b"},
34 | 		{key: "A##B", expectedKey: "a_b"},
35 | 		{key: "__", expectedKey: ""},
36 | 	} {
37 | 		t.Run(tt.key, func(t *testing.T) {
38 | 			g := NewWithT(t)
39 | 			key := inputs.NormalizeKeyForTemplate(tt.key)
40 | 			g.Expect(string(key)).To(Equal(tt.expectedKey))
41 | 		})
42 | 	}
43 | }
44 | 
```

--------------------------------------------------------------------------------
/cmd/cli/Dockerfile:
--------------------------------------------------------------------------------

```dockerfile
 1 | # Build the Flux Operator CLI binary using Docker's Debian image.
 2 | FROM --platform=${BUILDPLATFORM} golang:1.25 AS builder
 3 | ARG TARGETOS
 4 | ARG TARGETARCH
 5 | ARG VERSION
 6 | ARG KUBECTL_VER=1.33.0
 7 | WORKDIR /workspace
 8 | 
 9 | RUN apt-get -y install curl
10 | 
11 | # Copy the Go Modules manifests.
12 | COPY go.mod go.mod
13 | COPY go.sum go.sum
14 | 
15 | # Cache the Go Modules
16 | RUN go mod download
17 | 
18 | # Copy the Go sources.
19 | COPY cmd/cli/ cmd/cli/
20 | COPY api/ api/
21 | COPY internal/ internal/
22 | 
23 | # Build the Flux Operator CLI binary.
24 | RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \
25 |     go build -ldflags="-s -w -X main.VERSION=${VERSION}" \
26 |     -a -o /usr/local/bin/flux-operator ./cmd/cli/
27 | 
28 | # Verify the Flux Operator CLI binary.
29 | RUN flux-operator version --client
30 | 
31 | # Download the kubectl binary.
32 | RUN curl -sL https://dl.k8s.io/release/v${KUBECTL_VER}/bin/${TARGETOS}/${TARGETARCH}/kubectl \
33 |     -o /usr/local/bin/kubectl && chmod +x /usr/local/bin/kubectl
34 | 
35 | # Verify the kubectl binary.
36 | RUN kubectl version --client
37 | 
38 | # Distribute the binaries using Google's Distroless image.
39 | FROM gcr.io/distroless/static:nonroot
40 | 
41 | # Copy the license.
42 | COPY LICENSE /licenses/LICENSE
43 | 
44 | # Copy the binaries.
45 | COPY --from=builder --chmod=777 /usr/local/bin/flux-operator /usr/local/bin/
46 | COPY --from=builder --chmod=777 /usr/local/bin/kubectl /usr/local/bin/
47 | 
48 | # Run the binaries under a non-root user.
49 | USER 65532:65532
50 | ENTRYPOINT ["flux-operator"]
51 | 
```

--------------------------------------------------------------------------------
/cmd/cli/suspend_resource.go:
--------------------------------------------------------------------------------

```go
 1 | // Copyright 2025 Stefan Prodan.
 2 | // SPDX-License-Identifier: AGPL-3.0
 3 | 
 4 | package main
 5 | 
 6 | import (
 7 | 	"context"
 8 | 	"fmt"
 9 | 	"strings"
10 | 
11 | 	"github.com/spf13/cobra"
12 | )
13 | 
14 | var suspendResourceCmd = &cobra.Command{
15 | 	Use:   "resource [kind/name]",
16 | 	Short: "suspend Flux resource reconciliation",
17 | 	Example: `  # Suspend the reconciliation of a Flux Kustomization
18 |   flux-operator -n apps suspend resource Kustomization/my-app
19 | `,
20 | 	Args:              cobra.ExactArgs(1),
21 | 	RunE:              suspendResourceCmdRun,
22 | 	ValidArgsFunction: resourceKindNameCompletionFunc(true),
23 | }
24 | 
25 | func init() {
26 | 	suspendCmd.AddCommand(suspendResourceCmd)
27 | }
28 | 
29 | func suspendResourceCmdRun(cmd *cobra.Command, args []string) error {
30 | 	if len(args) != 1 {
31 | 		return fmt.Errorf("resource name is required")
32 | 	}
33 | 
34 | 	parts := strings.Split(args[0], "/")
35 | 	if len(parts) != 2 {
36 | 		return fmt.Errorf("resource name must be in the format <kind>/<name>, e.g., HelmRelease/my-app")
37 | 	}
38 | 
39 | 	kind := parts[0]
40 | 	name := parts[1]
41 | 	now := timeNow()
42 | 
43 | 	gvk, err := preferredFluxGVK(kind, kubeconfigArgs)
44 | 	if err != nil {
45 | 		return fmt.Errorf("unable to get gvk for kind %s : %w", kind, err)
46 | 	}
47 | 
48 | 	ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
49 | 	defer cancel()
50 | 
51 | 	err = toggleSuspension(ctx, *gvk, name, *kubeconfigArgs.Namespace, now, true)
52 | 	if err != nil {
53 | 		return err
54 | 	}
55 | 
56 | 	rootCmd.Println(`✔`, "Reconciliation suspended")
57 | 	return nil
58 | }
59 | 
```

--------------------------------------------------------------------------------
/web/public/favicon.svg:
--------------------------------------------------------------------------------

```
1 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
2 |     <path id="spiral-left" fill="#326ce5" d="M49.0358 52.966C39.6585 60.5986 26.248 61.0543 16.4252 54.0796C6.13019 46.767 2.30811 33.2284 6.99163 21.7686L13.4211 25.0927C10.4886 32.8982 12.7235 41.7046 19.2592 47.1881C23.9542 51.1254 30.4604 52.5884 36.662 51.101L35.5459 46.438C30.8112 47.5734 25.8768 46.4802 22.3401 43.5158C16.9155 38.9615 15.4012 31.389 18.655 25.1004L19.7584 22.9692L4.83995 15.2598L3.73915 17.3884C-3.50565 31.375 0.756753 48.8303 13.6476 57.99C21.2828 63.4095 30.7536 65.0236 39.4857 62.9129C43.9926 61.8236 48.3036 59.7449 52.063 56.6857C53.174 55.7794 54.2032 54.7746 55.1862 53.7302L50.7484 51.4364C50.1968 51.9689 49.6297 52.4834 49.0358 52.966Z"/>
3 |     <path id="spiral-right" fill="#5fb7ff" d="M50.3696 6.01048C38.9315 -2.1252 23.2451 -1.69128 12.2268 7.06904C10.9865 8.05208 9.84603 9.14776 8.77467 10.2934L13.2099 12.5846C13.8486 11.9689 14.5116 11.375 15.2092 10.8194C24.5686 3.38264 37.8819 3.00888 47.5907 9.9132C57.8742 17.2284 61.6873 30.7618 57.5222 42.4802L51.0928 39.1561C53.5056 31.101 51.2835 22.3049 44.7696 16.8201C39.9516 12.7586 33.2867 11.3506 26.9238 12.9865L28.0694 17.6418C32.9104 16.3593 38.0534 17.4294 41.6835 20.4886C47.0915 25.0428 48.5955 32.605 45.3468 38.8847L44.2435 41.0159L59.1644 48.7254L60.2627 46.5967C67.4985 32.6166 63.2476 15.1714 50.3708 6.0092"/>
4 | </svg>
5 | 
```

--------------------------------------------------------------------------------
/config/olm/bundle/tests/scorecard/config.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | # see https://sdk.operatorframework.io/docs/testing-operators/scorecard/ for more information
 2 | apiVersion: scorecard.operatorframework.io/v1alpha3
 3 | kind: Configuration
 4 | metadata:
 5 |   name: config
 6 | stages:
 7 |   - parallel: true
 8 |     tests:
 9 |       - entrypoint:
10 |           - scorecard-test
11 |           - basic-check-spec
12 |         image: quay.io/operator-framework/scorecard-test:v1.27.0
13 |         labels:
14 |           suite: basic
15 |           test: basic-check-spec-test
16 |         storage:
17 |           spec:
18 |             mountPath: {}
19 |       - entrypoint:
20 |           - scorecard-test
21 |           - olm-bundle-validation
22 |         image: quay.io/operator-framework/scorecard-test:v1.27.0
23 |         labels:
24 |           suite: olm
25 |           test: olm-bundle-validation-test
26 |         storage:
27 |           spec:
28 |             mountPath: {}
29 |       - entrypoint:
30 |           - scorecard-test
31 |           - olm-crds-have-validation
32 |         image: quay.io/operator-framework/scorecard-test:v1.27.0
33 |         labels:
34 |           suite: olm
35 |           test: olm-crds-have-validation-test
36 |         storage:
37 |           spec:
38 |             mountPath: {}
39 |       - entrypoint:
40 |           - scorecard-test
41 |           - olm-crds-have-validation
42 |         image: quay.io/operator-framework/scorecard-test:v1.7.1
43 |         labels:
44 |           suite: olm
45 |           test: olm-crds-have-validation-test
46 |         storage:
47 |           spec:
48 |             mountPath: {}
49 | storage:
50 |   spec:
51 |     mountPath: {}
52 | 
```

--------------------------------------------------------------------------------
/web/src/utils/time.js:
--------------------------------------------------------------------------------

```javascript
 1 | // Copyright 2025 Stefan Prodan.
 2 | // SPDX-License-Identifier: AGPL-3.0
 3 | 
 4 | /**
 5 |  * Format a timestamp into a human-readable relative time string.
 6 |  *
 7 |  * @param {string|Date} timestamp - The timestamp to format
 8 |  * @returns {string} Formatted time string (e.g., "just now", "5m ago", "3h ago", or absolute date)
 9 |  *
10 |  * Examples:
11 |  * - Less than 1 minute: "just now"
12 |  * - Less than 60 minutes: "5m ago"
13 |  * - Less than 24 hours: "3h ago"
14 |  * - Older: "Jan 15, 02:30 PM"
15 |  */
16 | export const formatTimestamp = (timestamp) => {
17 |   const date = new Date(timestamp)
18 |   const now = new Date()
19 |   const diffMs = now - date
20 |   const diffMins = Math.floor(diffMs / 60000)
21 | 
22 |   if (diffMins < 1) return 'just now'
23 |   if (diffMins < 60) return `${diffMins}m ago`
24 |   if (diffMins < 1440) return `${Math.floor(diffMins / 60)}h ago`
25 |   return date.toLocaleString('en-US', {
26 |     month: 'short',
27 |     day: 'numeric',
28 |     hour: '2-digit',
29 |     minute: '2-digit'
30 |   })
31 | }
32 | 
33 | /**
34 |  * Format a date into an absolute time string (HH:MM:SS format).
35 |  *
36 |  * @param {Date|null} date - The date to format
37 |  * @returns {string} Formatted time string (e.g., "02:30:45 PM") or "Never" if date is null
38 |  *
39 |  * Examples:
40 |  * - Valid date: "02:30:45 PM"
41 |  * - Null/undefined: "Never"
42 |  */
43 | export const formatTime = (date) => {
44 |   if (!date) return 'Never'
45 |   return new Intl.DateTimeFormat('en-US', {
46 |     hour: '2-digit',
47 |     minute: '2-digit',
48 |     second: '2-digit',
49 |   }).format(date)
50 | }
```

--------------------------------------------------------------------------------
/cmd/cli/wait_resourceset.go:
--------------------------------------------------------------------------------

```go
 1 | // Copyright 2025 Stefan Prodan.
 2 | // SPDX-License-Identifier: AGPL-3.0
 3 | 
 4 | package main
 5 | 
 6 | import (
 7 | 	"context"
 8 | 	"fmt"
 9 | 
10 | 	"github.com/spf13/cobra"
11 | 
12 | 	fluxcdv1 "github.com/controlplaneio-fluxcd/flux-operator/api/v1"
13 | )
14 | 
15 | var waitResourceSetCmd = &cobra.Command{
16 | 	Use:     "resourceset [name]",
17 | 	Aliases: []string{"rset"},
18 | 	Short:   "Wait for ResourceSet to become ready",
19 | 	Example: `  # Wait for a resourceset to become ready
20 |   flux-operator -n flux-system wait resourceset my-resourceset
21 | 
22 |   # Wait for a resourceset to become ready with a custom timeout
23 |   flux-operator -n flux-system wait rset my-resourceset --timeout=5m
24 | `,
25 | 	Args:              cobra.ExactArgs(1),
26 | 	RunE:              waitResourceSetCmdRun,
27 | 	ValidArgsFunction: resourceNamesCompletionFunc(fluxcdv1.GroupVersion.WithKind(fluxcdv1.ResourceSetKind)),
28 | }
29 | 
30 | func init() {
31 | 	waitCmd.AddCommand(waitResourceSetCmd)
32 | }
33 | 
34 | func waitResourceSetCmdRun(cmd *cobra.Command, args []string) error {
35 | 	if len(args) != 1 {
36 | 		return fmt.Errorf("name is required")
37 | 	}
38 | 
39 | 	name := args[0]
40 | 	gvk := fluxcdv1.GroupVersion.WithKind(fluxcdv1.ResourceSetKind)
41 | 
42 | 	ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
43 | 	defer cancel()
44 | 
45 | 	rootCmd.Println(`◎`, "Waiting for resourceset to become ready...")
46 | 	msg, err := waitForResourceReconciliation(ctx, gvk, name, *kubeconfigArgs.Namespace, "", rootArgs.timeout)
47 | 	if err != nil {
48 | 		return err
49 | 	}
50 | 
51 | 	rootCmd.Println(`✔`, msg)
52 | 	return nil
53 | }
54 | 
```

--------------------------------------------------------------------------------
/internal/builder/testdata/resourceset/multi-doc-template.golden.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | apiVersion: source.toolkit.fluxcd.io/v1beta2
 2 | kind: OCIRepository
 3 | metadata:
 4 |   name: addons
 5 |   namespace: flux-system
 6 | spec:
 7 |   interval: 10m
 8 |   ref:
 9 |     tag: latest
10 |   url: oci://registry.example.com/addons
11 | ---
12 | apiVersion: kustomize.toolkit.fluxcd.io/v1
13 | kind: Kustomization
14 | metadata:
15 |   name: ingress-nginx
16 |   namespace: flux-system
17 | spec:
18 |   interval: 10m
19 |   path: ./ingress-nginx
20 |   prune: true
21 |   sourceRef:
22 |     kind: OCIRepository
23 |     name: addons
24 | ---
25 | apiVersion: kustomize.toolkit.fluxcd.io/v1
26 | kind: Kustomization
27 | metadata:
28 |   name: cert-manager
29 |   namespace: flux-system
30 | spec:
31 |   interval: 10m
32 |   path: ./cert-manager
33 |   prune: true
34 |   sourceRef:
35 |     kind: OCIRepository
36 |     name: addons
37 | ---
38 | apiVersion: source.toolkit.fluxcd.io/v1beta2
39 | kind: OCIRepository
40 | metadata:
41 |   name: apps
42 |   namespace: flux-system
43 | spec:
44 |   interval: 10m
45 |   ref:
46 |     tag: latest
47 |   url: oci://registry.example.com/apps
48 | ---
49 | apiVersion: kustomize.toolkit.fluxcd.io/v1
50 | kind: Kustomization
51 | metadata:
52 |   name: frontend
53 |   namespace: flux-system
54 | spec:
55 |   decryption:
56 |     provider: sops
57 |     secretRef:
58 |       name: apps-sops
59 |   interval: 10m
60 |   path: ./frontend
61 |   prune: true
62 |   sourceRef:
63 |     kind: OCIRepository
64 |     name: apps
65 | ---
66 | apiVersion: kustomize.toolkit.fluxcd.io/v1
67 | kind: Kustomization
68 | metadata:
69 |   name: backend
70 |   namespace: flux-system
71 | spec:
72 |   decryption:
73 |     provider: sops
74 |     secretRef:
75 |       name: apps-sops
76 |   interval: 10m
77 |   path: ./backend
78 |   prune: true
79 |   sourceRef:
80 |     kind: OCIRepository
81 |     name: apps
82 | ---
83 | 
```

--------------------------------------------------------------------------------
/internal/builder/pull.go:
--------------------------------------------------------------------------------

```go
 1 | // Copyright 2024 Stefan Prodan.
 2 | // SPDX-License-Identifier: AGPL-3.0
 3 | 
 4 | package builder
 5 | 
 6 | import (
 7 | 	"context"
 8 | 	"fmt"
 9 | 	"strings"
10 | 
11 | 	untar "github.com/fluxcd/pkg/tar"
12 | 	"github.com/google/go-containerregistry/pkg/authn"
13 | 	"github.com/google/go-containerregistry/pkg/crane"
14 | )
15 | 
16 | // PullArtifact downloads an artifact from an OCI repository and extracts the content
17 | // of the first tgz layer to the given destination directory.
18 | // It returns the digest of the artifact.
19 | func PullArtifact(ctx context.Context, ociURL, dstDir string, keyChain authn.Keychain) (string, error) {
20 | 	img, err := crane.Pull(strings.TrimPrefix(ociURL, "oci://"), crane.WithContext(ctx), crane.WithAuthFromKeychain(keyChain))
21 | 	if err != nil {
22 | 		return "", fmt.Errorf("pulling artifact %s failed: %w", ociURL, err)
23 | 	}
24 | 
25 | 	digest, err := img.Digest()
26 | 	if err != nil {
27 | 		return "", fmt.Errorf("parsing digest for artifact %s failed: %w", ociURL, err)
28 | 	}
29 | 
30 | 	layers, err := img.Layers()
31 | 	if err != nil {
32 | 		return "", fmt.Errorf("listing layers in artifact %s failed: %w", ociURL, err)
33 | 	}
34 | 
35 | 	if len(layers) < 1 {
36 | 		return "", fmt.Errorf("no layers found in artifact %s", ociURL)
37 | 	}
38 | 
39 | 	blob, err := layers[0].Compressed()
40 | 	if err != nil {
41 | 		return "", fmt.Errorf("extracting layer from artifact %s failed: %w", ociURL, err)
42 | 	}
43 | 
44 | 	if err = untar.Untar(blob, dstDir, untar.WithMaxUntarSize(-1)); err != nil {
45 | 		return "", fmt.Errorf("extracting layer from artifact %s failed: %w", ociURL, err)
46 | 	}
47 | 
48 | 	return digest.String(), nil
49 | }
50 | 
```

--------------------------------------------------------------------------------
/cmd/mcp/toolbox/set_context.go:
--------------------------------------------------------------------------------

```go
 1 | // Copyright 2025 Stefan Prodan.
 2 | // SPDX-License-Identifier: AGPL-3.0
 3 | 
 4 | package toolbox
 5 | 
 6 | import (
 7 | 	"context"
 8 | 	"fmt"
 9 | 
10 | 	"github.com/modelcontextprotocol/go-sdk/mcp"
11 | )
12 | 
13 | const (
14 | 	// ToolSetKubeConfigContext is the name of the set_kubeconfig_context tool.
15 | 	ToolSetKubeConfigContext = "set_kubeconfig_context"
16 | )
17 | 
18 | func init() {
19 | 	systemTools[ToolSetKubeConfigContext] = systemTool{
20 | 		readOnly:  true,
21 | 		inCluster: false,
22 | 	}
23 | }
24 | 
25 | // setKubeconfigContextInput defines the input parameters for setting the kubeconfig context.
26 | type setKubeconfigContextInput struct {
27 | 	Name string `json:"name" jsonschema:"The name of the kubeconfig context."`
28 | }
29 | 
30 | // HandleSetKubeconfigContext is the handler function for the set_kubeconfig_context tool.
31 | func (m *Manager) HandleSetKubeconfigContext(ctx context.Context, request *mcp.CallToolRequest, input setKubeconfigContextInput) (*mcp.CallToolResult, any, error) {
32 | 	if err := CheckScopes(ctx, ToolSetKubeConfigContext, m.readOnly); err != nil {
33 | 		return NewToolResultError(err.Error())
34 | 	}
35 | 
36 | 	if input.Name == "" {
37 | 		return NewToolResultError("name is required")
38 | 	}
39 | 
40 | 	err := m.kubeconfig.Load()
41 | 	if err != nil {
42 | 		return NewToolResultErrorFromErr("error reading kubeconfig contexts", err)
43 | 	}
44 | 
45 | 	err = m.kubeconfig.SetCurrentContext(input.Name)
46 | 	if err != nil {
47 | 		return NewToolResultErrorFromErr("error setting kubeconfig context", err)
48 | 	}
49 | 	m.kubeClient.SetCurrentContext(input.Name)
50 | 
51 | 	return NewToolResultText(fmt.Sprintf("Context changed to %s", input.Name))
52 | }
53 | 
```

--------------------------------------------------------------------------------
/cmd/mcp/toolbox/get_apis_test.go:
--------------------------------------------------------------------------------

```go
 1 | // Copyright 2025 Stefan Prodan.
 2 | // SPDX-License-Identifier: AGPL-3.0
 3 | 
 4 | package toolbox
 5 | 
 6 | import (
 7 | 	"context"
 8 | 	"encoding/json"
 9 | 	"testing"
10 | 	"time"
11 | 
12 | 	"github.com/modelcontextprotocol/go-sdk/mcp"
13 | 	. "github.com/onsi/gomega"
14 | 	cli "k8s.io/cli-runtime/pkg/genericclioptions"
15 | 
16 | 	"github.com/controlplaneio-fluxcd/flux-operator/cmd/mcp/k8s"
17 | )
18 | 
19 | func TestManager_HandleGetAPIVersions(t *testing.T) {
20 | 	configFile := "testdata/kubeconfig.yaml"
21 | 	t.Setenv("KUBECONFIG", configFile)
22 | 
23 | 	m := &Manager{
24 | 		kubeconfig: k8s.NewKubeConfig(),
25 | 		kubeClient: k8s.NewClientFactory(cli.NewConfigFlags(false)),
26 | 		timeout:    time.Second,
27 | 	}
28 | 
29 | 	request := &mcp.CallToolRequest{
30 | 		Params: &mcp.CallToolParamsRaw{
31 | 			Name: "get_kubernetes_api_versions",
32 | 		},
33 | 	}
34 | 
35 | 	tests := []struct {
36 | 		testName  string
37 | 		arguments map[string]any
38 | 		matchErr  string
39 | 	}{
40 | 		{
41 | 			testName:  "fails with invalid kubeconfig",
42 | 			arguments: map[string]any{},
43 | 			matchErr:  "Failed to get Kubernetes client",
44 | 		},
45 | 	}
46 | 
47 | 	for _, test := range tests {
48 | 		t.Run(test.testName, func(t *testing.T) {
49 | 			g := NewWithT(t)
50 | 			argsJSON, _ := json.Marshal(test.arguments)
51 | 			request.Params.Arguments = argsJSON
52 | 
53 | 			result, content, err := m.HandleGetAPIVersions(context.Background(), request, struct{}{})
54 | 			g.Expect(err).ToNot(HaveOccurred())
55 | 			textContent, ok := result.Content[0].(*mcp.TextContent)
56 | 			g.Expect(ok).To(BeTrue())
57 | 
58 | 			g.Expect(result.IsError).To(BeTrue())
59 | 			g.Expect(textContent.Text).To(ContainSubstring(test.matchErr))
60 | 			_ = content
61 | 		})
62 | 	}
63 | }
64 | 
```

--------------------------------------------------------------------------------
/cmd/cli/testdata/build_resourceset/rset-with-rsip-permuted.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | ---
 2 | apiVersion: fluxcd.controlplane.io/v1
 3 | kind: ResourceSet
 4 | metadata:
 5 |   name: color-environments
 6 |   namespace: test
 7 | spec:
 8 |   inputStrategy:
 9 |     name: Permute
10 |   inputs:
11 |     - id: blue
12 |     - id: green
13 |     - id: red
14 |     - id: yellow
15 |   inputsFrom:
16 |     - selector:
17 |         matchLabels:
18 |           fluxcd.controlplane.io/role: provisioning
19 |   resourcesTemplate: |
20 |     <<- $id := inputs.team1_apps.id >>
21 |     <<- $tenant := inputs.team1_apps.tenantName >>
22 |     <<- range $app := inputs.team1_apps.applications >>
23 |     <<- $appName := $app.name >>
24 |     <<- range $env := $app.envs >>
25 |     ---
26 |     apiVersion: source.toolkit.fluxcd.io/v1
27 |     kind: OCIRepository
28 |     metadata:
29 |       name: << $appName >>
30 |       namespace: << $tenant >>-<< $env.name >>-<< inputs.color_environments.id >>
31 |       annotations:
32 |         permutation-id: << inputs.id | quote >>
33 |         fluxcd.controlplane.io/id: << $id | quote >>
34 |     spec:
35 |       interval: 10m
36 |       url: oci://registry.example.com/<< $appName >>
37 |       ref:
38 |         tag: << $env.version >>
39 |     ---
40 |     apiVersion: kustomize.toolkit.fluxcd.io/v1
41 |     kind: Kustomization
42 |     metadata:
43 |       name: << $appName >>
44 |       namespace: << $tenant >>-<< $env.name >>-<< inputs.color_environments.id >>
45 |       annotations:
46 |         permutation-id: << inputs.id | quote >>
47 |         fluxcd.controlplane.io/id: << $id | quote >>
48 |     spec:
49 |       interval: 1h
50 |       prune: true
51 |       sourceRef:
52 |         kind: OCIRepository
53 |         name: << $appName >>
54 |       path: ./
55 |     <<- end >>
56 |     <<- end >>
57 | 
```

--------------------------------------------------------------------------------
/cmd/mcp/k8s/logs.go:
--------------------------------------------------------------------------------

```go
 1 | // Copyright 2025 Stefan Prodan.
 2 | // SPDX-License-Identifier: AGPL-3.0
 3 | 
 4 | package k8s
 5 | 
 6 | import (
 7 | 	"bytes"
 8 | 	"context"
 9 | 	"fmt"
10 | 	"io"
11 | 
12 | 	corev1 "k8s.io/api/core/v1"
13 | 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
14 | 	"k8s.io/client-go/kubernetes"
15 | )
16 | 
17 | // GetLogs retrieves the logs for a specific pod container in the given namespace.
18 | func (k *Client) GetLogs(ctx context.Context, pod, container, namespace string, limit int64) (*unstructured.Unstructured, error) {
19 | 	limitBytes := int64(64 * 1024)
20 | 	podLogOpts := corev1.PodLogOptions{
21 | 		Container:  container,
22 | 		TailLines:  &limit,
23 | 		LimitBytes: &limitBytes,
24 | 	}
25 | 
26 | 	clientset, err := kubernetes.NewForConfig(k.cfg)
27 | 	if err != nil {
28 | 		return nil, err
29 | 	}
30 | 
31 | 	req := clientset.CoreV1().Pods(namespace).GetLogs(pod, &podLogOpts)
32 | 	logs, err := req.Stream(ctx)
33 | 	if err != nil {
34 | 		return nil, fmt.Errorf("unable to get logs for pod %s/%s: %w", namespace, pod, err)
35 | 	}
36 | 	defer logs.Close()
37 | 
38 | 	logsBuffer := new(bytes.Buffer)
39 | 	_, err = io.Copy(logsBuffer, logs)
40 | 	if err != nil {
41 | 		return nil, fmt.Errorf("failed to read logs for pod %s/%s: %w", namespace, pod, err)
42 | 	}
43 | 
44 | 	logsContent := logsBuffer.String()
45 | 
46 | 	if logsContent == "" {
47 | 		logsContent = fmt.Sprintf("no logs found for container %s", container)
48 | 	}
49 | 
50 | 	return &unstructured.Unstructured{
51 | 		Object: map[string]any{
52 | 			"apiVersion": "v1",
53 | 			"kind":       "Pod",
54 | 			"metadata": map[string]any{
55 | 				"name":      pod,
56 | 				"namespace": namespace,
57 | 			},
58 | 			"container": container,
59 | 			"logs":      logsContent,
60 | 		},
61 | 	}, nil
62 | }
63 | 
```

--------------------------------------------------------------------------------
/cmd/mcp/toolbox/library/library.go:
--------------------------------------------------------------------------------

```go
 1 | // Copyright 2025 Stefan Prodan.
 2 | // SPDX-License-Identifier: AGPL-3.0
 3 | 
 4 | package library
 5 | 
 6 | import (
 7 | 	"bytes"
 8 | 	_ "embed"
 9 | 	"encoding/gob"
10 | 	"fmt"
11 | )
12 | 
13 | //go:embed index.gob
14 | var embeddedIndex []byte
15 | 
16 | var searchIndex *SearchIndex
17 | 
18 | // DocumentMetadata holds the metadata for a Flux document,
19 | // including its URL, group, kind, and keywords.
20 | type DocumentMetadata struct {
21 | 	URL      string   `json:"url"`
22 | 	Group    string   `json:"group"`
23 | 	Kind     string   `json:"kind"`
24 | 	Keywords []string `json:"keywords"`
25 | }
26 | 
27 | // Library holds a collection of document references represented by the DocumentMetadata type.
28 | // It provides methods to search in the metadata and fetch the document content.
29 | type Library struct {
30 | 	Documents []DocumentMetadata `json:"documents"`
31 | }
32 | 
33 | func init() {
34 | 	var err error
35 | 	searchIndex, err = loadIndex()
36 | 	if err != nil {
37 | 		// If index doesn't exist yet (e.g., during first build), that's okay
38 | 		// The index will be generated by the indexer tool
39 | 		searchIndex = nil
40 | 	}
41 | }
42 | 
43 | // loadIndex deserializes the embedded search index.
44 | func loadIndex() (*SearchIndex, error) {
45 | 	if len(embeddedIndex) == 0 {
46 | 		return nil, fmt.Errorf("embedded index is empty")
47 | 	}
48 | 
49 | 	reader := bytes.NewReader(embeddedIndex)
50 | 	decoder := gob.NewDecoder(reader)
51 | 
52 | 	var index SearchIndex
53 | 	if err := decoder.Decode(&index); err != nil {
54 | 		return nil, fmt.Errorf("failed to decode index: %w", err)
55 | 	}
56 | 
57 | 	return &index, nil
58 | }
59 | 
60 | // GetSearchIndex returns the loaded search index.
61 | func GetSearchIndex() *SearchIndex {
62 | 	return searchIndex
63 | }
64 | 
```

--------------------------------------------------------------------------------
/web/index.html:
--------------------------------------------------------------------------------

```html
 1 | <!DOCTYPE html>
 2 | <html lang="en">
 3 |   <head>
 4 |     <meta charset="UTF-8" />
 5 |     <meta name="robots" content="noindex, nofollow" />
 6 |     <link rel="preload" href="/fonts/inter.woff2" as="font" type="font/woff2" crossorigin />
 7 |     <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
 8 |     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 9 |     <meta name="description" content="Real-time visibility into your GitOps pipelines" />
10 |     <meta property="og:type" content="website" />
11 |     <meta property="og:title" content="Flux Status" />
12 |     <meta property="og:description" content="Real-time visibility into your GitOps pipelines" />
13 |     <title>Flux Status</title>
14 |     <script>
15 |       // Apply theme before page renders to prevent flash
16 |       (function() {
17 |         const theme = localStorage.getItem('theme') || 'auto';
18 |         const isDark = theme === 'dark' ||
19 |           (theme === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches);
20 |         if (isDark) {
21 |           document.documentElement.classList.add('dark');
22 |         }
23 |       })();
24 |     </script>
25 |     <style>
26 |       /* Critical CSS to prevent flash of white background */
27 |       html {
28 |         background-color: #f9fafb; /* gray-50 for light */
29 |       }
30 |       html.dark {
31 |         background-color: #111827; /* gray-900 for dark */
32 |       }
33 |       body {
34 |         background-color: transparent;
35 |       }
36 |     </style>
37 |   </head>
38 |   <body>
39 |     <div id="app"></div>
40 |     <script type="module" src="/src/main.jsx"></script>
41 |   </body>
42 | </html>
43 | 
```

--------------------------------------------------------------------------------
/cmd/cli/testdata/build_resourceset/golden.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | ---
 2 | apiVersion: source.toolkit.fluxcd.io/v1
 3 | kind: OCIRepository
 4 | metadata:
 5 |   annotations:
 6 |     fluxcd.controlplane.io/id: "340788154"
 7 |   labels:
 8 |     resourceset.fluxcd.controlplane.io/name: apps
 9 |     resourceset.fluxcd.controlplane.io/namespace: test
10 |   name: app1
11 |   namespace: team1-staging
12 | spec:
13 |   interval: 10m
14 |   ref:
15 |     tag: v1.0.1
16 |   url: oci://registry.example.com/app1
17 | ---
18 | apiVersion: kustomize.toolkit.fluxcd.io/v1
19 | kind: Kustomization
20 | metadata:
21 |   annotations:
22 |     fluxcd.controlplane.io/id: "340788154"
23 |   labels:
24 |     resourceset.fluxcd.controlplane.io/name: apps
25 |     resourceset.fluxcd.controlplane.io/namespace: test
26 |   name: app1
27 |   namespace: team1-staging
28 | spec:
29 |   interval: 1h
30 |   path: ./
31 |   prune: true
32 |   sourceRef:
33 |     kind: OCIRepository
34 |     name: app1
35 | ---
36 | apiVersion: source.toolkit.fluxcd.io/v1
37 | kind: OCIRepository
38 | metadata:
39 |   annotations:
40 |     fluxcd.controlplane.io/id: "340788154"
41 |   labels:
42 |     resourceset.fluxcd.controlplane.io/name: apps
43 |     resourceset.fluxcd.controlplane.io/namespace: test
44 |   name: app1
45 |   namespace: team1-production
46 | spec:
47 |   interval: 10m
48 |   ref:
49 |     tag: v1.0.0
50 |   url: oci://registry.example.com/app1
51 | ---
52 | apiVersion: kustomize.toolkit.fluxcd.io/v1
53 | kind: Kustomization
54 | metadata:
55 |   annotations:
56 |     fluxcd.controlplane.io/id: "340788154"
57 |   labels:
58 |     resourceset.fluxcd.controlplane.io/name: apps
59 |     resourceset.fluxcd.controlplane.io/namespace: test
60 |   name: app1
61 |   namespace: team1-production
62 | spec:
63 |   interval: 1h
64 |   path: ./
65 |   prune: true
66 |   sourceRef:
67 |     kind: OCIRepository
68 |     name: app1
69 | 
```

--------------------------------------------------------------------------------
/internal/controller/fluxinstance_manager.go:
--------------------------------------------------------------------------------

```go
 1 | // Copyright 2024 Stefan Prodan.
 2 | // SPDX-License-Identifier: AGPL-3.0
 3 | 
 4 | package controller
 5 | 
 6 | import (
 7 | 	"k8s.io/client-go/util/workqueue"
 8 | 	ctrl "sigs.k8s.io/controller-runtime"
 9 | 	"sigs.k8s.io/controller-runtime/pkg/builder"
10 | 	"sigs.k8s.io/controller-runtime/pkg/controller"
11 | 	"sigs.k8s.io/controller-runtime/pkg/predicate"
12 | 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
13 | 
14 | 	runtimectrl "github.com/fluxcd/pkg/runtime/controller"
15 | 
16 | 	fluxcdv1 "github.com/controlplaneio-fluxcd/flux-operator/api/v1"
17 | )
18 | 
19 | // FluxInstanceReconcilerOptions contains options for the reconciler.
20 | type FluxInstanceReconcilerOptions struct {
21 | 	RateLimiter             workqueue.TypedRateLimiter[reconcile.Request]
22 | 	DisableWaitInterruption bool
23 | }
24 | 
25 | // SetupWithManager sets up the controller with the Manager.
26 | func (r *FluxInstanceReconciler) SetupWithManager(mgr ctrl.Manager, opts FluxInstanceReconcilerOptions) error {
27 | 	var blder *builder.Builder
28 | 	var toComplete reconcile.TypedReconciler[reconcile.Request]
29 | 
30 | 	pred := predicate.Or(
31 | 		predicate.GenerationChangedPredicate{},
32 | 		predicate.AnnotationChangedPredicate{},
33 | 	)
34 | 
35 | 	if opts.DisableWaitInterruption {
36 | 		toComplete = r
37 | 		blder = ctrl.NewControllerManagedBy(mgr).
38 | 			For(&fluxcdv1.FluxInstance{}, builder.WithPredicates(pred))
39 | 	} else {
40 | 		wr := runtimectrl.WrapReconciler(r)
41 | 		toComplete = wr
42 | 		blder = runtimectrl.NewControllerManagedBy(mgr, wr).
43 | 			For(&fluxcdv1.FluxInstance{}, pred).Builder
44 | 	}
45 | 
46 | 	return blder.
47 | 		WithOptions(controller.Options{
48 | 			RateLimiter: opts.RateLimiter,
49 | 		}).Complete(toComplete)
50 | }
51 | 
```

--------------------------------------------------------------------------------
/hack/build-olm-images.sh:
--------------------------------------------------------------------------------

```bash
 1 | #!/usr/bin/env bash
 2 | 
 3 | # Copyright 2024 Stefan Prodan.
 4 | # SPDX-License-Identifier: AGPL-3.0
 5 | 
 6 | set -euo pipefail
 7 | 
 8 | VERSION=$1
 9 | ARCH=""
10 | case $(uname -m) in
11 |     x86_64)   ARCH="x86_64" ;;
12 |     aarch64)  ARCH="aarch64" ;;
13 |     arm64)    ARCH="aarch64" ;;
14 |     *)        echo "Unsupported architecture"
15 |               exit 1
16 |               ;;
17 | esac
18 | REPOSITORY_ROOT=$(git rev-parse --show-toplevel)
19 | OCI_IMAGE_PREFIX="ghcr.io/controlplaneio-fluxcd/openshift-flux-operator"
20 | DEST_DIR="${REPOSITORY_ROOT}/bin/olm"
21 | 
22 | info() {
23 |     echo '[INFO] ' "$@"
24 | }
25 | 
26 | fatal() {
27 |     echo '[ERROR] ' "$@" >&2
28 |     exit 1
29 | }
30 | 
31 | if [ ! -d "${DEST_DIR}/${VERSION}" ]; then
32 |   fatal "${DEST_DIR}/${VERSION} does not exist"
33 | fi
34 | 
35 | # build catalog image
36 | docker build -t ${OCI_IMAGE_PREFIX}-catalog:bundle-${VERSION} \
37 | -f "${DEST_DIR}/test/bundle.Dockerfile" "${DEST_DIR}/${VERSION}"
38 | 
39 | # push catalog image
40 | docker push ${OCI_IMAGE_PREFIX}-catalog:bundle-${VERSION}
41 | 
42 | # build opm image
43 | docker build -t opm --build-arg ARCH=$ARCH -f "${DEST_DIR}/test/opm.Dockerfile" "${DEST_DIR}"
44 | 
45 | # build index image
46 | docker run --rm --privileged \
47 |   -v /var/lib/docker:/var/lib/docker \
48 |   -v /var/run/docker.sock:/var/run/docker.sock \
49 |   opm:latest index add \
50 |   --container-tool docker \
51 |   --bundles ${OCI_IMAGE_PREFIX}-catalog:bundle-${VERSION} \
52 |   --tag ${OCI_IMAGE_PREFIX}-index:v${VERSION}
53 | 
54 | # push index image
55 | docker push ${OCI_IMAGE_PREFIX}-index:v${VERSION}
56 | 
57 | info "OLM catalog pushed to ${OCI_IMAGE_PREFIX}-catalog:bundle-${VERSION}"
58 | info "OLM index pushed to ${OCI_IMAGE_PREFIX}-index:v${VERSION}"
59 | 
```

--------------------------------------------------------------------------------
/docs/logo/flux-operator-icon.svg:
--------------------------------------------------------------------------------

```
 1 | <svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
 2 | <g clip-path="url(#clip0_1_4)">
 3 | <path d="M392.294 423.731C317.276 484.792 209.992 488.438 131.41 432.64C49.0496 374.139 18.473 265.83 55.9411 174.152L107.377 200.745C83.9168 263.188 101.796 333.64 154.081 377.508C191.642 409.006 243.692 420.71 293.304 408.811L284.375 371.507C246.497 380.59 207.022 371.845 178.729 348.129C135.332 311.695 123.218 251.116 149.248 200.806L158.075 183.757L38.7277 122.081L29.9213 139.11C-28.0371 251.003 6.06208 390.646 109.189 463.923C170.271 507.279 246.036 520.192 315.894 503.306C351.949 494.592 386.437 477.962 416.512 453.489C425.4 446.239 433.633 438.2 441.498 429.844L405.996 411.494C401.582 415.754 397.046 419.871 392.294 423.731Z" fill="#326CE5"/>
 4 | <path d="M402.964 48.087C311.46 -16.9984 185.969 -13.527 97.8227 56.5555C87.9002 64.4198 78.7763 73.1853 70.2054 82.3501L105.687 100.68C110.797 95.7542 116.101 91.0029 121.682 86.5587C196.557 27.0643 303.063 24.0742 380.733 79.3088C463.002 137.83 493.507 246.098 460.186 339.845L408.75 313.252C428.052 248.811 410.276 178.442 358.164 134.564C319.621 102.072 266.301 90.8083 215.398 103.895L224.563 141.138C263.291 130.877 304.435 139.438 333.476 163.912C376.74 200.346 388.772 260.844 362.783 311.081L353.956 328.131L473.324 389.806L482.109 372.777C539.996 260.936 505.989 121.375 402.975 48.0768L402.964 48.087Z" fill="#5FB7FF"/>
 5 | </g>
 6 | <defs>
 7 | <clipPath id="clip0_1_4">
 8 | <rect width="512" height="512" fill="white"/>
 9 | </clipPath>
10 | </defs>
11 | </svg>
12 | 
```

--------------------------------------------------------------------------------
/cmd/cli/wait_inputprovider.go:
--------------------------------------------------------------------------------

```go
 1 | // Copyright 2025 Stefan Prodan.
 2 | // SPDX-License-Identifier: AGPL-3.0
 3 | 
 4 | package main
 5 | 
 6 | import (
 7 | 	"context"
 8 | 	"fmt"
 9 | 
10 | 	"github.com/spf13/cobra"
11 | 
12 | 	fluxcdv1 "github.com/controlplaneio-fluxcd/flux-operator/api/v1"
13 | )
14 | 
15 | var waitInputProviderCmd = &cobra.Command{
16 | 	Use:     "inputprovider [name]",
17 | 	Aliases: []string{"rsip", "resourcesetinputprovider"},
18 | 	Short:   "Wait for ResourceSetInputProvider to become ready",
19 | 	Example: `  # Wait for an ResourceSetInputProvider to become ready
20 |   flux-operator -n flux-system wait inputprovider my-inputprovider
21 | 
22 |   # Wait for an ResourceSetInputProvider to become ready with a custom timeout
23 |   flux-operator -n flux-system wait rsip my-inputprovider --timeout=5m
24 | `,
25 | 	Args:              cobra.ExactArgs(1),
26 | 	RunE:              waitInputProviderCmdRun,
27 | 	ValidArgsFunction: resourceNamesCompletionFunc(fluxcdv1.GroupVersion.WithKind(fluxcdv1.ResourceSetInputProviderKind)),
28 | }
29 | 
30 | func init() {
31 | 	waitCmd.AddCommand(waitInputProviderCmd)
32 | }
33 | 
34 | func waitInputProviderCmdRun(cmd *cobra.Command, args []string) error {
35 | 	if len(args) != 1 {
36 | 		return fmt.Errorf("name is required")
37 | 	}
38 | 
39 | 	name := args[0]
40 | 	gvk := fluxcdv1.GroupVersion.WithKind(fluxcdv1.ResourceSetInputProviderKind)
41 | 
42 | 	ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
43 | 	defer cancel()
44 | 
45 | 	rootCmd.Println(`◎`, "Waiting for inputprovider to become ready...")
46 | 	msg, err := waitForResourceReconciliation(ctx, gvk, name, *kubeconfigArgs.Namespace, "", rootArgs.timeout)
47 | 	if err != nil {
48 | 		return err
49 | 	}
50 | 
51 | 	rootCmd.Println(`✔`, msg)
52 | 	return nil
53 | }
54 | 
```

--------------------------------------------------------------------------------
/cmd/mcp/toolbox/manager_test.go:
--------------------------------------------------------------------------------

```go
 1 | // Copyright 2025 Stefan Prodan.
 2 | // SPDX-License-Identifier: AGPL-3.0
 3 | 
 4 | package toolbox
 5 | 
 6 | import (
 7 | 	"testing"
 8 | 
 9 | 	"github.com/modelcontextprotocol/go-sdk/mcp"
10 | 	. "github.com/onsi/gomega"
11 | )
12 | 
13 | func TestManager_RegisterToolsDoesNotPanic(t *testing.T) {
14 | 	g := NewWithT(t)
15 | 
16 | 	server := mcp.NewServer(&mcp.Implementation{
17 | 		Name:    "flux-operator-mcp",
18 | 		Version: "test-version",
19 | 	}, &mcp.ServerOptions{
20 | 		HasTools: true,
21 | 	})
22 | 
23 | 	manager := NewManager(nil, 0, false, false, nil)
24 | 	registeredTools := manager.RegisterTools(server, false)
25 | 	g.Expect(registeredTools).To(Equal([]string{
26 | 		"install_flux_instance",
27 | 		"get_flux_instance",
28 | 		"get_kubernetes_api_versions",
29 | 		"get_kubernetes_logs",
30 | 		"get_kubernetes_metrics",
31 | 		"get_kubernetes_resources",
32 | 		"search_flux_docs",
33 | 		"apply_kubernetes_manifest",
34 | 		"delete_kubernetes_resource",
35 | 		"reconcile_flux_source",
36 | 		"reconcile_flux_kustomization",
37 | 		"reconcile_flux_helmrelease",
38 | 		"reconcile_flux_resourceset",
39 | 		"suspend_flux_reconciliation",
40 | 		"resume_flux_reconciliation",
41 | 		"get_kubeconfig_contexts",
42 | 		"set_kubeconfig_context",
43 | 	}))
44 | }
45 | 
46 | func TestManager_RegisterSpecificTools(t *testing.T) {
47 | 	g := NewWithT(t)
48 | 
49 | 	server := mcp.NewServer(&mcp.Implementation{
50 | 		Name:    "flux-operator-mcp",
51 | 		Version: "test-version",
52 | 	}, &mcp.ServerOptions{
53 | 		HasTools: true,
54 | 	})
55 | 
56 | 	manager := NewManager(nil, 0, false, false, []string{
57 | 		"get_kubeconfig_contexts",
58 | 		"set_kubeconfig_context",
59 | 	})
60 | 	registeredTools := manager.RegisterTools(server, false)
61 | 	g.Expect(registeredTools).To(Equal([]string{
62 | 		"get_kubeconfig_contexts",
63 | 		"set_kubeconfig_context",
64 | 	}))
65 | }
66 | 
```

--------------------------------------------------------------------------------
/cmd/cli/resume_instance.go:
--------------------------------------------------------------------------------

```go
 1 | // Copyright 2025 Stefan Prodan.
 2 | // SPDX-License-Identifier: AGPL-3.0
 3 | 
 4 | package main
 5 | 
 6 | import (
 7 | 	"context"
 8 | 	"fmt"
 9 | 
10 | 	"github.com/spf13/cobra"
11 | 
12 | 	fluxcdv1 "github.com/controlplaneio-fluxcd/flux-operator/api/v1"
13 | )
14 | 
15 | var resumeInstanceCmd = &cobra.Command{
16 | 	Use:               "instance [name]",
17 | 	Short:             "Resume FluxInstance reconciliation",
18 | 	Args:              cobra.ExactArgs(1),
19 | 	RunE:              resumeInstanceCmdRun,
20 | 	ValidArgsFunction: resourceNamesCompletionFunc(fluxcdv1.GroupVersion.WithKind(fluxcdv1.FluxInstanceKind)),
21 | }
22 | 
23 | type resumeInstanceFlags struct {
24 | 	wait bool
25 | }
26 | 
27 | var resumeInstanceArgs resumeInstanceFlags
28 | 
29 | func init() {
30 | 	resumeInstanceCmd.Flags().BoolVar(&resumeInstanceArgs.wait, "wait", true,
31 | 		"Wait for the resource to become ready.")
32 | 	resumeCmd.AddCommand(resumeInstanceCmd)
33 | }
34 | 
35 | func resumeInstanceCmdRun(cmd *cobra.Command, args []string) error {
36 | 	if len(args) != 1 {
37 | 		return fmt.Errorf("name is required")
38 | 	}
39 | 
40 | 	name := args[0]
41 | 	now := timeNow()
42 | 	gvk := fluxcdv1.GroupVersion.WithKind(fluxcdv1.FluxInstanceKind)
43 | 
44 | 	ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
45 | 	defer cancel()
46 | 
47 | 	err := toggleSuspension(ctx, gvk, name, *kubeconfigArgs.Namespace, now, false)
48 | 	if err != nil {
49 | 		return err
50 | 	}
51 | 
52 | 	if resumeInstanceArgs.wait {
53 | 		rootCmd.Println(`◎`, "Waiting for reconciliation...")
54 | 		msg, err := waitForResourceReconciliation(ctx, gvk, name, *kubeconfigArgs.Namespace, now, rootArgs.timeout)
55 | 		if err != nil {
56 | 			return err
57 | 		}
58 | 
59 | 		rootCmd.Println(`✔`, msg)
60 | 	} else {
61 | 		rootCmd.Println(`✔`, "Reconciliation resumed")
62 | 	}
63 | 
64 | 	return nil
65 | }
66 | 
```
Page 3/126FirstPrevNextLast