From 14b345cd3a0e43bd82a78970930086c4916de327 Mon Sep 17 00:00:00 2001 From: Robert Graeff Date: Thu, 14 Aug 2025 08:55:54 +0200 Subject: [PATCH] feat: deploy-flux command --- cmd/deploy_flux.go | 72 +++++ go.mod | 57 +++- go.sum | 182 ++++++++++-- internal/flux_deployer/constants.go | 9 + internal/flux_deployer/deployer.go | 261 ++++++++++++++++++ internal/flux_deployer/deployer_test.go | 47 ++++ internal/flux_deployer/git_credentials.go | 84 ++++++ internal/flux_deployer/images.go | 32 +++ internal/flux_deployer/images_test.go | 33 +++ .../resources/gotk-components.yaml | 10 + .../testdata/01/root-component-version-1.yaml | 13 + .../testdata/01/root-component-version-2.yaml | 13 + .../testdata/01/test-resource.yaml | 4 + internal/flux_deployer/testdata_test.go | 20 ++ 14 files changed, 816 insertions(+), 21 deletions(-) create mode 100644 cmd/deploy_flux.go create mode 100644 internal/flux_deployer/constants.go create mode 100644 internal/flux_deployer/deployer.go create mode 100644 internal/flux_deployer/deployer_test.go create mode 100644 internal/flux_deployer/git_credentials.go create mode 100644 internal/flux_deployer/images.go create mode 100644 internal/flux_deployer/images_test.go create mode 100644 internal/flux_deployer/testdata/01/download_dir/resources/gotk-components.yaml create mode 100644 internal/flux_deployer/testdata/01/root-component-version-1.yaml create mode 100644 internal/flux_deployer/testdata/01/root-component-version-2.yaml create mode 100644 internal/flux_deployer/testdata/01/test-resource.yaml create mode 100644 internal/flux_deployer/testdata_test.go diff --git a/cmd/deploy_flux.go b/cmd/deploy_flux.go new file mode 100644 index 0000000..4470dda --- /dev/null +++ b/cmd/deploy_flux.go @@ -0,0 +1,72 @@ +package cmd + +import ( + "fmt" + + "github.com/openmcp-project/controller-utils/pkg/clusters" + "github.com/spf13/cobra" + + "github.com/openmcp-project/bootstrapper/internal/flux_deployer" + logging "github.com/openmcp-project/bootstrapper/internal/log" +) + +const ( + flagOCMConfig = "ocm-config" + flagGitCredentials = "git-credentials" + flagKubeconfig = "kubeconfig" + flagFluxCDNamespace = "fluxcd-namespace" +) + +// deployFluxCmd represents the "deploy flux" command +var deployFluxCmd = &cobra.Command{ + Use: "deploy-flux source target", + Short: "Transfer an OCM component from a source to a target location", + Long: `Transfers the specified OCM component version from the source location to the target location.`, + Args: cobra.ExactArgs(5), + ArgAliases: []string{ + "component-location", + "deployment-templates", + "deployment-repository", + "deployment-repository-branch", + "deployment-repository-path", + }, + RunE: func(cmd *cobra.Command, args []string) error { + log := logging.GetLogger() + log.Infof("Starting flux deployment with component-location: %s, deployment-templates: %s, "+ + "deployment-repository: %s, deployment-repository-branch: %s, deployment-repository-path: %s", + args[0], args[1], args[2], args[3], args[4]) + + platformKubeconfig := cmd.Flag(flagKubeconfig).Value.String() + platformCluster := clusters.New("platform").WithConfigPath(platformKubeconfig) + if err := platformCluster.InitializeRESTConfig(); err != nil { + return fmt.Errorf("error initializing REST config for platform cluster: %w", err) + } + if err := platformCluster.InitializeClient(nil); err != nil { + return fmt.Errorf("error initializing client for platform cluster: %w", err) + } + + d := flux_deployer.NewFluxDeployer(args[0], args[1], args[2], args[3], args[4], + cmd.Flag(flagOCMConfig).Value.String(), + cmd.Flag(flagGitCredentials).Value.String(), + cmd.Flag(flagFluxCDNamespace).Value.String(), + platformKubeconfig, + platformCluster, log) + err := d.Deploy(cmd.Context()) + if err != nil { + log.Errorf("Flux deployment failed: %v", err) + return err + } + + log.Info("Flux deployment completed") + return nil + }, +} + +func init() { + RootCmd.AddCommand(deployFluxCmd) + + deployFluxCmd.Flags().StringP(flagOCMConfig, "c", "", "ocm configuration file") + deployFluxCmd.Flags().StringP(flagGitCredentials, "g", "", "git credentials configuration file that configures basic auth, personal access token, ssh private key. This will be used in the fluxcd GitSource for spec.secretRef to authenticate against the deploymentRepository. If not set, no authentication will be configured.") + deployFluxCmd.Flags().StringP(flagKubeconfig, "k", "", "kubeconfig of the Kubernetes cluster on which the flux deployment will be created/updated. If not set, the current context will be used.") + deployFluxCmd.Flags().StringP(flagFluxCDNamespace, "n", "", "namespace on the Kubernetes cluster in which the namespaced fluxcd resources will be deployed. Default 'flux-system'.") +} diff --git a/go.mod b/go.mod index 5f42d41..d10070a 100644 --- a/go.mod +++ b/go.mod @@ -4,10 +4,14 @@ go 1.25.0 require ( github.com/go-git/go-git/v5 v5.16.2 + github.com/openmcp-project/controller-utils v0.17.0 github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.9.1 github.com/stretchr/testify v1.11.0 + k8s.io/api v0.33.3 + k8s.io/apimachinery v0.33.3 k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d + sigs.k8s.io/controller-runtime v0.21.0 sigs.k8s.io/yaml v1.6.0 ) @@ -15,26 +19,69 @@ require ( dario.cat/mergo v1.0.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/ProtonMail/go-crypto v1.1.6 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cloudflare/circl v1.6.1 // indirect github.com/cyphar/filepath-securejoin v0.4.1 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/emicklei/go-restful/v3 v3.12.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect + github.com/evanphx/json-patch/v5 v5.9.11 // indirect + github.com/fsnotify/fsnotify v1.8.0 // indirect + github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.6.2 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect + github.com/go-openapi/swag v0.23.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect + github.com/google/btree v1.1.3 // indirect + github.com/google/gnostic-models v0.6.9 // indirect + github.com/google/go-cmp v0.7.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect + github.com/mailru/easyjson v0.9.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pjbgf/sha1cd v0.3.2 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_golang v1.22.0 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.62.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect github.com/skeema/knownhosts v1.3.1 // indirect github.com/spf13/pflag v1.0.7 // indirect + github.com/x448/float16 v0.8.4 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect - golang.org/x/crypto v0.37.0 // indirect - golang.org/x/net v0.39.0 // indirect - golang.org/x/sys v0.32.0 // indirect + golang.org/x/crypto v0.41.0 // indirect + golang.org/x/net v0.43.0 // indirect + golang.org/x/oauth2 v0.27.0 // indirect + golang.org/x/sync v0.16.0 // indirect + golang.org/x/sys v0.35.0 // indirect + golang.org/x/term v0.34.0 // indirect + golang.org/x/text v0.28.0 // indirect + golang.org/x/time v0.10.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect + google.golang.org/protobuf v1.36.6 // indirect + gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/apiextensions-apiserver v0.33.3 // indirect + k8s.io/client-go v0.33.3 // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect + sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect + sigs.k8s.io/randfill v1.0.0 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect ) diff --git a/go.sum b/go.sum index 539ffa2..91a3c83 100644 --- a/go.sum +++ b/go.sum @@ -9,18 +9,35 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFI github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= +github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU= +github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= +github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= +github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= +github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= +github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= @@ -31,16 +48,50 @@ github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMj github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= github.com/go-git/go-git/v5 v5.16.2 h1:fT6ZIOjE5iEnkzKyxTHK1W4HGAsPhqEqiSAssSO77hM= github.com/go-git/go-git/v5 v5.16.2/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= +github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= +github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= +github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= +github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -48,14 +99,38 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= -github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= +github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus= +github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8= +github.com/onsi/gomega v1.38.0 h1:c/WX+w8SLAinvuKKQFh77WEucCnPk4j2OTUr7lt7BeY= +github.com/onsi/gomega v1.38.0/go.mod h1:OcXcwId0b9QsE7Y49u+BTrL4IdKOBOKnD6VQNTJEB6o= +github.com/openmcp-project/controller-utils v0.17.0 h1:dZsMX2ur/b1759+aKJmcJRdkOVJ131czE6AtIGKX1dE= +github.com/openmcp-project/controller-utils v0.17.0/go.mod h1:RgatwIEftAvHbhd3FIyXb2Sm0N6/AK8A2aF8zBxK930= github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= +github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= +github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -72,44 +147,97 @@ github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.11.0 h1:ib4sjIrwZKxE5u/Japgo/7SJV3PvgjGiRNAvTVGqQl8= github.com/stretchr/testify v1.11.0/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= +go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE= go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= -golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= +golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= -golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= +golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= +golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= -golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= -golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= +golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= +golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= -golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4= +golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= +golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= +gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= +gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -117,7 +245,29 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/api v0.33.3 h1:SRd5t//hhkI1buzxb288fy2xvjubstenEKL9K51KBI8= +k8s.io/api v0.33.3/go.mod h1:01Y/iLUjNBM3TAvypct7DIj0M0NIZc+PzAHCIo0CYGE= +k8s.io/apiextensions-apiserver v0.33.3 h1:qmOcAHN6DjfD0v9kxL5udB27SRP6SG/MTopmge3MwEs= +k8s.io/apiextensions-apiserver v0.33.3/go.mod h1:oROuctgo27mUsyp9+Obahos6CWcMISSAPzQ77CAQGz8= +k8s.io/apimachinery v0.33.3 h1:4ZSrmNa0c/ZpZJhAgRdcsFcZOw1PQU1bALVQ0B3I5LA= +k8s.io/apimachinery v0.33.3/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= +k8s.io/client-go v0.33.3 h1:M5AfDnKfYmVJif92ngN532gFqakcGi6RvaOF16efrpA= +k8s.io/client-go v0.33.3/go.mod h1:luqKBQggEf3shbxHY4uVENAxrDISLOarxpTKMiUuujg= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d h1:wAhiDyZ4Tdtt7e46e9M5ZSAJ/MnPGPs+Ki1gHw4w1R0= k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8= +sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= +sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/structured-merge-diff/v4 v4.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI= +sigs.k8s.io/structured-merge-diff/v4 v4.7.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= diff --git a/internal/flux_deployer/constants.go b/internal/flux_deployer/constants.go new file mode 100644 index 0000000..3d5f681 --- /dev/null +++ b/internal/flux_deployer/constants.go @@ -0,0 +1,9 @@ +package flux_deployer + +const ( + // Names of ocm resources of the root component + + FluxcdHelmController = "fluxcd-helm-controller" + FluxcdKustomizeController = "fluxcd-kustomize-controller" + FluxcdSourceController = "fluxcd-source-controller" +) diff --git a/internal/flux_deployer/deployer.go b/internal/flux_deployer/deployer.go new file mode 100644 index 0000000..253cf0d --- /dev/null +++ b/internal/flux_deployer/deployer.go @@ -0,0 +1,261 @@ +package flux_deployer + +import ( + "bytes" + "context" + "fmt" + "io" + "os" + "path" + + "github.com/openmcp-project/controller-utils/pkg/clusters" + "github.com/sirupsen/logrus" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/util/yaml" + "sigs.k8s.io/controller-runtime/pkg/client" + + ocmcli "github.com/openmcp-project/bootstrapper/internal/ocm-cli" + "github.com/openmcp-project/bootstrapper/internal/template" +) + +type FluxDeployer struct { + componentLocation string + + // deploymentTemplates is the path to the deployment templates in the templates component. + // It consists of segments separated by slashes. All segments except the last one are names of component references. + // The last segment is the name of a resource in the component, which is reached by starting at the root component and going through the component references. + // For example, "gitops-templates/fluxcd" means: start at the root component, go to the component reference "gitops-templates", and then use the resource named "fluxcd" in that component. + deploymentTemplates string + deploymentRepository string + deploymentRepositoryBranch string + deploymentRepositoryPath string + ocmConfig string + gitCredentials string + fluxcdNamespace string + platformKubeconfig string + platformCluster *clusters.Cluster + log *logrus.Logger +} + +func NewFluxDeployer(componentLocation, deploymentTemplates, deploymentRepository, deploymentRepositoryBranch, deploymentRepositoryPath, + ocmConfig, gitCredentials, fluxcdNamespace, platformKubeconfig string, platformCluster *clusters.Cluster, log *logrus.Logger) *FluxDeployer { + + return &FluxDeployer{ + componentLocation: componentLocation, + deploymentTemplates: deploymentTemplates, + deploymentRepository: deploymentRepository, + deploymentRepositoryBranch: deploymentRepositoryBranch, + deploymentRepositoryPath: deploymentRepositoryPath, + ocmConfig: ocmConfig, + gitCredentials: gitCredentials, + fluxcdNamespace: fluxcdNamespace, + platformKubeconfig: platformKubeconfig, + platformCluster: platformCluster, + log: log, + } +} + +func (d *FluxDeployer) Deploy(ctx context.Context) error { + + // Get root component and gitops-templates component. + d.log.Info("Loading root component and gitops-templates component") + componentGetter := ocmcli.NewComponentGetter(d.componentLocation, d.deploymentTemplates, d.ocmConfig) + if err := componentGetter.InitializeComponents(ctx); err != nil { + return err + } + + // Create a temporary directory to store the downloaded resource + d.log.Info("Creating download directory for gitops-templates") + downloadDir, err := os.MkdirTemp("", "flux-resource-") + if err != nil { + return fmt.Errorf("error creating temporary download directory for flux resource: %w", err) + } + defer func() { + if err := os.RemoveAll(downloadDir); err != nil { + fmt.Printf("error removing temporary download directory for flux resource: %v\n", err) + } + }() + d.log.Debugf("Download directory: %s", downloadDir) + + // Download resource from gitops-templates component into the download directory + d.log.Info("Downloading gitops-templates") + if err := componentGetter.DownloadTemplatesResource(ctx, downloadDir); err != nil { + return fmt.Errorf("error downloading templates: %w", err) + } + + if err := d.DeployFluxControllers(ctx, componentGetter.RootComponentVersion(), downloadDir); err != nil { + return fmt.Errorf("error deploying flux controllers: %w", err) + } + + if err := d.establishFluxSync(ctx, downloadDir); err != nil { + return fmt.Errorf("error establishing flux synchronization: %w", err) + } + + return nil +} + +func (d *FluxDeployer) DeployFluxControllers(ctx context.Context, rootComponentVersion *ocmcli.ComponentVersion, downloadDir string) error { + d.log.Info("Deploying flux") + + images, err := GetFluxCDImages(rootComponentVersion) + if err != nil { + return fmt.Errorf("error getting images for flux controllers: %w", err) + } + + // Read manifest file + filepath := path.Join(downloadDir, "resources", "gotk-components.yaml") + d.log.Debugf("Reading flux deployment objects from file %s", filepath) + manifestTpl, err := d.readFileContent(filepath) + if err != nil { + return fmt.Errorf("error reading flux deployment objects from file %s: %w", filepath, err) + } + + // Template + values := map[string]any{ + "Values": map[string]any{ + "namespace": d.fluxcdNamespace, + "images": images, + }, + } + d.log.Debug("Templating flux deployment objects") + manifest, err := template.NewTemplateExecution().Execute("flux-deployment", string(manifestTpl), values) + if err != nil { + return fmt.Errorf("error templating flux deployment objects: %w", err) + } + + // Apply + d.log.Debug("Applying flux deployment objects") + if err := d.applyManifests(ctx, manifest); err != nil { + return err + } + + return nil +} + +func (d *FluxDeployer) establishFluxSync(ctx context.Context, downloadDir string) error { + d.log.Info("Establishing flux synchronization with deployment repository") + + const secretName = "git" + + if err := CreateGitCredentialsSecret(ctx, d.log, d.gitCredentials, secretName, d.fluxcdNamespace, d.platformCluster.Client()); err != nil { + return err + } + + // Read manifest file + filepath := path.Join(downloadDir, "resources", "gotk-sync.yaml") + d.log.Debugf("Reading flux synchronization objects from file %s", filepath) + manifestTpl, err := d.readFileContent(filepath) + if err != nil { + return fmt.Errorf("error reading manifests for flux sync: %w", err) + } + + // Template + d.log.Debug("Templating flux synchronization objects") + values := map[string]any{ + "Values": map[string]any{ + "namespace": d.fluxcdNamespace, + "git": map[string]any{ + "repoUrl": d.deploymentRepository, + "mainBranch": d.deploymentRepositoryBranch, + "path": d.deploymentRepositoryPath, + "secretName": secretName, + }, + }, + } + manifest, err := template.NewTemplateExecution().Execute("flux-sync", string(manifestTpl), values) + if err != nil { + return fmt.Errorf("error templating flux synchronization objects: %w", err) + } + + // Apply + d.log.Debug("Applying flux synchronization objects") + if err := d.applyManifests(ctx, manifest); err != nil { + return err + } + + return nil +} + +func (d *FluxDeployer) readFileContent(filepath string) ([]byte, error) { + d.log.Debugf("Reading file: %s", filepath) + + if _, err := os.Stat(filepath); os.IsNotExist(err) { + return nil, fmt.Errorf("file does not exist at path: %s", filepath) + } + + content, err := os.ReadFile(filepath) + if err != nil { + return nil, fmt.Errorf("error reading file %s: %w", filepath, err) + } + + return content, nil +} + +func (d *FluxDeployer) applyManifests(ctx context.Context, manifests []byte) error { + + // Parse manifests into unstructured objects + reader := bytes.NewReader(manifests) + unstructuredObjects, err := d.parseManifests(reader) + if err != nil { + return fmt.Errorf("error parsing manifests: %w", err) + } + + // Apply objects to the platform cluster + for _, u := range unstructuredObjects { + if err := d.applyUnstructuredObject(ctx, u); err != nil { + return err + } + } + + return nil +} + +func (d *FluxDeployer) parseManifests(reader io.Reader) ([]*unstructured.Unstructured, error) { + decoder := yaml.NewYAMLOrJSONDecoder(reader, 4096) + var result []*unstructured.Unstructured + for { + u := &unstructured.Unstructured{} + err := decoder.Decode(u) + if err != nil { + if err.Error() == "EOF" { + break + } + return nil, err + } + if len(u.Object) == 0 { + continue + } + result = append(result, u) + } + return result, nil +} + +func (d *FluxDeployer) applyUnstructuredObject(ctx context.Context, u *unstructured.Unstructured) error { + objectKey := client.ObjectKeyFromObject(u) + objectLogString := fmt.Sprintf("%s %s", u.GetObjectKind(), objectKey.String()) + fmt.Printf("Applying object %s\n", objectLogString) + + existingObj := &unstructured.Unstructured{} + existingObj.SetGroupVersionKind(u.GroupVersionKind()) + getErr := d.platformCluster.Client().Get(ctx, objectKey, existingObj) + if getErr != nil { + if apierrors.IsNotFound(getErr) { + // create object + createErr := d.platformCluster.Client().Create(ctx, u) + if createErr != nil { + return fmt.Errorf("error creating object %s: %w", objectLogString, createErr) + } + } else { + return fmt.Errorf("error reading object %s: %w", objectLogString, getErr) + } + } else { + // update object + u.SetResourceVersion(existingObj.GetResourceVersion()) + updateErr := d.platformCluster.Client().Update(ctx, u) + if updateErr != nil { + return fmt.Errorf("error updating object %s: %w", objectLogString, updateErr) + } + } + return nil +} diff --git a/internal/flux_deployer/deployer_test.go b/internal/flux_deployer/deployer_test.go new file mode 100644 index 0000000..969fa15 --- /dev/null +++ b/internal/flux_deployer/deployer_test.go @@ -0,0 +1,47 @@ +package flux_deployer_test + +import ( + "testing" + + "github.com/openmcp-project/controller-utils/pkg/clusters" + "github.com/stretchr/testify/assert" + v1 "k8s.io/api/apps/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + + "github.com/openmcp-project/bootstrapper/internal/flux_deployer" + logging "github.com/openmcp-project/bootstrapper/internal/log" + ocmcli "github.com/openmcp-project/bootstrapper/internal/ocm-cli" +) + +func TestDeployFluxController(t *testing.T) { + rootComponentVersion1 := LoadComponentVersion(t, "./testdata/01/root-component-version-1.yaml") + rootComponentVersion2 := LoadComponentVersion(t, "./testdata/01/root-component-version-2.yaml") + downloadDir := "./testdata/01/download_dir" + + platformClient := fake.NewClientBuilder().Build() + platformCluster := clusters.NewTestClusterFromClient("platform", platformClient) + namespace := "flux-system-test" + + d := flux_deployer.NewFluxDeployer("", "", "", + "", "", ocmcli.NoOcmConfig, "", namespace, "", platformCluster, logging.GetLogger()) + + // Create a deployment + err := d.DeployFluxControllers(t.Context(), rootComponentVersion1, downloadDir) + assert.NoError(t, err, "Error deploying flux controllers") + + deployment := &v1.Deployment{} + err = platformClient.Get(t.Context(), client.ObjectKey{Name: "source-controller", Namespace: namespace}, deployment) + assert.NoError(t, err, "Error getting source-controller deployment") + assert.Equal(t, namespace, deployment.Namespace, "Deployment namespace does not match expected namespace") + assert.Equal(t, "test-source-controller-image:v0.0.1", deployment.Spec.Template.Spec.Containers[0].Image, "Deployment image does not match expected image") + + // Update the deployment + err = d.DeployFluxControllers(t.Context(), rootComponentVersion2, downloadDir) + assert.NoError(t, err, "Error updating flux controllers") + + err = platformClient.Get(t.Context(), client.ObjectKey{Name: "source-controller", Namespace: namespace}, deployment) + assert.NoError(t, err, "Error getting source-controller deployment") + assert.Equal(t, namespace, deployment.Namespace, "Deployment namespace does not match expected namespace") + assert.Equal(t, "test-source-controller-image:v0.0.2", deployment.Spec.Template.Spec.Containers[0].Image, "Deployment image does not match expected image") +} diff --git a/internal/flux_deployer/git_credentials.go b/internal/flux_deployer/git_credentials.go new file mode 100644 index 0000000..d843f58 --- /dev/null +++ b/internal/flux_deployer/git_credentials.go @@ -0,0 +1,84 @@ +package flux_deployer + +import ( + "context" + "fmt" + "os" + + "github.com/openmcp-project/controller-utils/pkg/resources" + "github.com/sirupsen/logrus" + corev1 "k8s.io/api/core/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + + gitconfig "github.com/openmcp-project/bootstrapper/internal/git-config" +) + +const ( + username = "username" + password = "password" + token = "token" + identity = "identity" + knownHosts = "known_hosts" +) + +// CreateGitCredentialsSecret creates or updates a Secret with name "git" in the fluxcd namespace. +// The secret contains git credentials for flux sync, read from the file d.gitCredentials. +// The file should contain a YAML of a map[string]string, whose keys are described +// in https://fluxcd.io/flux/components/source/gitrepositories/#secret-reference, e.g. username and password. +func CreateGitCredentialsSecret(ctx context.Context, log *logrus.Logger, gitCredentialsPath string, secretName, secretNamespace string, platformClient client.Client) error { + log.Debug("Creating or updating git credentials secret") + + gitCredentialsData := map[string][]byte{} + + if gitCredentialsPath != "" { + log.Debugf("Reading and parsing git credentials from path: %s", gitCredentialsPath) + config, err := gitconfig.ParseConfig(gitCredentialsPath) + if err != nil { + return fmt.Errorf("error reading and parsing git credentials for flux sync: %w", err) + } + + log.Debugf("Validating git credentials configuration") + if err = config.Validate(); err != nil { + return fmt.Errorf("error validating git credentials for flux sync: %w", err) + } + + if config.Authentication.BasicAuth != nil { + log.Debug("Using basic auth credentials for git operations") + gitCredentialsData[username] = []byte(config.Authentication.BasicAuth.Username) + gitCredentialsData[password] = []byte(config.Authentication.BasicAuth.Password) + } + if config.Authentication.BearerToken != nil { + log.Debug("Using bearer token for git operations") + gitCredentialsData[token] = []byte(config.Authentication.BearerToken.Token) + } + if config.Authentication.SSHPrivateKey != nil { + log.Debug("Using ssh private key for git operations") + privateKey, err := config.Authentication.SSHPrivateKey.DecodePrivateKey() + if err != nil { + return err + } + + gitCredentialsData[identity] = privateKey + + if config.Authentication.SSHPrivateKey.KnownHosts != "" { + knownHostsPath := config.Authentication.SSHPrivateKey.KnownHosts + if _, err := os.Stat(knownHostsPath); os.IsNotExist(err) { + return fmt.Errorf("known hosts file does not exist at path: %s", knownHostsPath) + } + knownHostsContent, err := os.ReadFile(knownHostsPath) + if err != nil { + return fmt.Errorf("error reading known hosts file %s: %w", knownHostsPath, err) + } + gitCredentialsData[knownHosts] = knownHostsContent + } + } + } + + secretMutator := resources.NewSecretMutator(secretName, secretNamespace, gitCredentialsData, corev1.SecretTypeOpaque) + log.Debugf("Storing git credentials in secret %s", secretMutator.String()) + if err := resources.CreateOrUpdateResource(ctx, platformClient, secretMutator); err != nil { + return fmt.Errorf("error creating or updating git credentials secret: %w", err) + } + + return nil +} diff --git a/internal/flux_deployer/images.go b/internal/flux_deployer/images.go new file mode 100644 index 0000000..af851e1 --- /dev/null +++ b/internal/flux_deployer/images.go @@ -0,0 +1,32 @@ +package flux_deployer + +import ( + "fmt" + + ocmcli "github.com/openmcp-project/bootstrapper/internal/ocm-cli" +) + +// GetFluxCDImages retrieves the images of the FluxCD controllers from the root component version. +func GetFluxCDImages(rootCV *ocmcli.ComponentVersion) (map[string]any, error) { + return GetImages(rootCV, FluxcdHelmController, FluxcdKustomizeController, FluxcdSourceController) +} + +// GetImages retrieves the images references for a list of resources of a component version. +// It returns a map with the resource names as keys and their image references as values. +// The returned map can be used to build the values for templating. +func GetImages(cv *ocmcli.ComponentVersion, resourceNames ...string) (map[string]any, error) { + images := map[string]any{} + + for _, resourceName := range resourceNames { + res, err := cv.GetResource(resourceName) + if err != nil { + return nil, err + } + if res == nil || res.Access.ImageReference == nil { + return nil, fmt.Errorf("image reference of resource %s not found", resourceName) + } + images[resourceName] = *res.Access.ImageReference + } + + return images, nil +} diff --git a/internal/flux_deployer/images_test.go b/internal/flux_deployer/images_test.go new file mode 100644 index 0000000..38e91b9 --- /dev/null +++ b/internal/flux_deployer/images_test.go @@ -0,0 +1,33 @@ +package flux_deployer_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/openmcp-project/bootstrapper/internal/flux_deployer" +) + +func TestGetFluxCDImages(t *testing.T) { + cv := LoadComponentVersion(t, "./testdata/01/root-component-version-1.yaml") + + imageMap, err := flux_deployer.GetFluxCDImages(cv) + assert.NoError(t, err, "error getting images") + img, found := imageMap[flux_deployer.FluxcdSourceController] + assert.True(t, found, "fluxcd source controller image should be found") + assert.Equal(t, "test-source-controller-image:v0.0.1", img, "fluxcd source controller image should match") + img, found = imageMap[flux_deployer.FluxcdHelmController] + assert.True(t, found, "fluxcd helm controller image should be found") + assert.Equal(t, "test-helm-controller-image:v0.0.1", img, "fluxcd helm controller image should match") + img, found = imageMap[flux_deployer.FluxcdKustomizeController] + assert.True(t, found, "fluxcd kustomize controller image should be found") + assert.Equal(t, "test-kustomize-controller-image:v0.0.1", img, "fluxcd kustomize controller image should match") +} + +func TestGetFluxCDImagesError(t *testing.T) { + cv := LoadComponentVersion(t, "./testdata/01/root-component-version-1.yaml") + cv.Component.Resources = cv.Component.Resources[0:1] // remove all but one resource + + _, err := flux_deployer.GetFluxCDImages(cv) + assert.Error(t, err, "expected error getting images") +} diff --git a/internal/flux_deployer/testdata/01/download_dir/resources/gotk-components.yaml b/internal/flux_deployer/testdata/01/download_dir/resources/gotk-components.yaml new file mode 100644 index 0000000..72e4904 --- /dev/null +++ b/internal/flux_deployer/testdata/01/download_dir/resources/gotk-components.yaml @@ -0,0 +1,10 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: source-controller + namespace: {{ .Values.namespace }} +spec: + template: + spec: + containers: + - image: {{ index .Values.images "fluxcd-source-controller" }} diff --git a/internal/flux_deployer/testdata/01/root-component-version-1.yaml b/internal/flux_deployer/testdata/01/root-component-version-1.yaml new file mode 100644 index 0000000..df19989 --- /dev/null +++ b/internal/flux_deployer/testdata/01/root-component-version-1.yaml @@ -0,0 +1,13 @@ +component: + name: github.com/openmcp-project/openmcp + version: v0.0.1 + resources: + - name: fluxcd-source-controller + access: + imageReference: "test-source-controller-image:v0.0.1" + - name: fluxcd-helm-controller + access: + imageReference: "test-helm-controller-image:v0.0.1" + - name: fluxcd-kustomize-controller + access: + imageReference: "test-kustomize-controller-image:v0.0.1" diff --git a/internal/flux_deployer/testdata/01/root-component-version-2.yaml b/internal/flux_deployer/testdata/01/root-component-version-2.yaml new file mode 100644 index 0000000..f7699f5 --- /dev/null +++ b/internal/flux_deployer/testdata/01/root-component-version-2.yaml @@ -0,0 +1,13 @@ +component: + name: github.com/openmcp-project/openmcp + version: v0.0.2 + resources: + - name: fluxcd-source-controller + access: + imageReference: "test-source-controller-image:v0.0.2" + - name: fluxcd-helm-controller + access: + imageReference: "test-helm-controller-image:v0.0.2" + - name: fluxcd-kustomize-controller + access: + imageReference: "test-kustomize-controller-image:v0.0.2" diff --git a/internal/flux_deployer/testdata/01/test-resource.yaml b/internal/flux_deployer/testdata/01/test-resource.yaml new file mode 100644 index 0000000..2ea1df8 --- /dev/null +++ b/internal/flux_deployer/testdata/01/test-resource.yaml @@ -0,0 +1,4 @@ +config: + vars: + - a: "a" + - b: "b" diff --git a/internal/flux_deployer/testdata_test.go b/internal/flux_deployer/testdata_test.go new file mode 100644 index 0000000..2c1e7bd --- /dev/null +++ b/internal/flux_deployer/testdata_test.go @@ -0,0 +1,20 @@ +package flux_deployer_test + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" + "sigs.k8s.io/yaml" + + ocmcli "github.com/openmcp-project/bootstrapper/internal/ocm-cli" +) + +func LoadComponentVersion(t *testing.T, path string) *ocmcli.ComponentVersion { + cv := &ocmcli.ComponentVersion{} + content, err := os.ReadFile(path) + assert.NoError(t, err, "error reading component version file") + err = yaml.Unmarshal(content, cv) + assert.NoError(t, err, "error unmarshalling component version") + return cv +}