From b2fa8e8482b951c05579b0827077fb89c27e0efd Mon Sep 17 00:00:00 2001 From: Kevin McDermott Date: Wed, 18 Oct 2023 09:29:19 +0100 Subject: [PATCH 1/3] Add documentation for the provider functionality. --- pkg/providers/provider.go | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/pkg/providers/provider.go b/pkg/providers/provider.go index cffbb66..5a6e1da 100644 --- a/pkg/providers/provider.go +++ b/pkg/providers/provider.go @@ -4,13 +4,31 @@ import ( "context" "k8s.io/client-go/tools/clientcmd/api" + "sigs.k8s.io/controller-runtime/pkg/client" ) +// Provider implementations query the API for cluster services e.g. EKS and AKS. type Provider interface { + // ListClusters queries the cluster service for a list of clusters. ListClusters(ctx context.Context) ([]*ProviderCluster, error) + + // ClusterID implementations are responsible for generating a unique ID from + // a cluster. + // How this Unique ID is calculated varies across providers. + // But it must be able to be matched to the UniqueID on a ProviderCluster. + ClusterID(ctx context.Context, kubeClient client.Reader) (string, error) } +// ProviderCluster is a representation of the cluster from the cluster service +// that can be used to create a GitopsCluster. type ProviderCluster struct { - Name string + // This is the name of the cluster as provided by the cluster service. + Name string + // This is the unique ID for this cluster. + UniqueID string + + // This is the KubeConfig from the cluster service used to connect to it. + // This is likely to be a high privilege token. + // The token MUST NOT require the execution of a binary (ExecConfig). KubeConfig *api.Config } From c64882125133b49e22cdcdcdaed332f1e8a64a5c Mon Sep 17 00:00:00 2001 From: Kevin McDermott Date: Wed, 18 Oct 2023 13:01:16 +0100 Subject: [PATCH 2/3] Add support for a unique ID from clusters. Extract cluster ID from ConfigMap and the returned list of clusters. --- go.mod | 24 +--- go.sum | 30 +---- .../automatedclusterdiscovery_controller.go | 1 + ...tomatedclusterdiscovery_controller_test.go | 7 +- pkg/providers/azure/azure.go | 70 ++++++++-- pkg/providers/azure/azure_test.go | 127 +++++++++++++++++- pkg/providers/provider.go | 2 +- 7 files changed, 206 insertions(+), 55 deletions(-) diff --git a/go.mod b/go.mod index 50c9527..dd3330b 100644 --- a/go.mod +++ b/go.mod @@ -3,14 +3,14 @@ module github.com/weaveworks/cluster-reflector-controller go 1.20 require ( + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.1 - github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice v1.0.0 + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v4 v4.3.0 github.com/Azure/go-autorest/autorest v0.11.29 github.com/fluxcd/pkg/apis/meta v1.1.2 - github.com/fluxcd/pkg/ssa v0.27.0 - github.com/go-logr/logr v1.2.4 + github.com/google/go-cmp v0.5.9 github.com/spf13/cobra v1.7.0 - github.com/stretchr/testify v1.8.2 + github.com/stretchr/testify v1.8.4 github.com/weaveworks/cluster-controller v1.5.2 github.com/weaveworks/weave-gitops v0.33.0 k8s.io/api v0.27.5 @@ -22,26 +22,22 @@ require ( ) require ( - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect - github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest/adal v0.9.22 // indirect github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1 // indirect - github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/chai2010/gettext-go v1.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/emicklei/go-restful/v3 v3.10.0 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect - github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-errors/errors v1.4.2 // indirect + github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/zapr v1.2.4 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.1 // indirect @@ -51,31 +47,23 @@ require ( github.com/golang-jwt/jwt/v5 v5.0.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect - github.com/google/btree v1.1.2 // indirect github.com/google/gnostic v0.6.9 // indirect - github.com/google/go-cmp v0.5.9 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.3.0 // indirect - github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect github.com/imdario/mergo v0.3.13 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kylelemons/godebug v1.1.0 // indirect - github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/mitchellh/go-wordwrap v1.0.1 // indirect - github.com/moby/spdystream v0.2.0 // indirect - github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/onsi/ginkgo/v2 v2.11.0 // indirect github.com/onsi/gomega v1.27.10 // indirect - github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect @@ -83,7 +71,6 @@ require ( github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/common v0.42.0 // indirect github.com/prometheus/procfs v0.9.0 // indirect - github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/xlab/treeprint v1.1.0 // indirect go.starlark.net v0.0.0-20221028183056-acb66ad56dd2 // indirect @@ -109,7 +96,6 @@ require ( k8s.io/component-base v0.27.3 // indirect k8s.io/klog/v2 v2.100.1 // indirect k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect - k8s.io/kubectl v0.26.8 // indirect k8s.io/utils v0.0.0-20230505201702-9f6742963106 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/kustomize/api v0.12.1 // indirect diff --git a/go.sum b/go.sum index 4ca5038..d36b4eb 100644 --- a/go.sum +++ b/go.sum @@ -6,10 +6,11 @@ github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.1 h1:LNHhpdK7hzUcx/k1LIcuh github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.1/go.mod h1:uE9zaUfEQT/nbQjVi2IblCG9iaLtZsuYZ8ne+PuQ02M= github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY= github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice v1.0.0 h1:figxyQZXzZQIcP3njhC68bYUiTw45J8/SsHaLW8Ax0M= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice v1.0.0/go.mod h1:TmlMW4W5OvXOmOyKNnor8nlMMiO1ctIyzmHme/VHsrA= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v4 v4.3.0 h1:U73ZEM5QTwb7x/VrXLTi+sb6Aw9DqFJxOpWuj+pDPfk= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v4 v4.3.0/go.mod h1:WpiaNrHqgIy+P5gTYbOA/JuMmxq7uq8onUvVBybjIlI= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.1.2 h1:mLY+pNLjCUeKhgnAJWAKhEUQM+RJQo2H1fuGSw1Ky1E= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0 h1:ECsQtyERDVz3NP3kvDOTLvbQhqWp/x9EsGKtb4ogUr8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.11.29 h1:I4+HL/JDvErx2LjyzaVxllw2lRDB5/BT2Bm4g20iqYw= @@ -29,10 +30,8 @@ github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1 h1:WpB/QDNLpMw github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= -github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -43,7 +42,6 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= -github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -53,7 +51,6 @@ github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnht github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= 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= @@ -72,12 +69,9 @@ github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLi github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f h1:Wl78ApPPB2Wvf/TIe2xdyJxTlb6obmF18d8QdkxNDu4= -github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f/go.mod h1:OSYXu++VVOHnXeitef/D8n/6y4QV8uLHSFXX4NeXMGc= github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= github.com/fluxcd/pkg/apis/meta v1.1.2 h1:Unjo7hxadtB2dvGpeFqZZUdsjpRA08YYSBb7dF2WIAM= github.com/fluxcd/pkg/apis/meta v1.1.2/go.mod h1:BHQyRHCskGMEDf6kDGbgQ+cyiNpUHbLsCOsaMYM2maI= -github.com/fluxcd/pkg/ssa v0.27.0 h1:BJnWDy3xDtYD2U+sVZPkoh6PfnQKoXsklO0pzojU8XU= -github.com/fluxcd/pkg/ssa v0.27.0/go.mod h1:fxvmVf4FxodJi5lTglMcL8JsF6hfJLG99C56/CgchH0= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -123,7 +117,6 @@ github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= -github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0= github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -144,9 +137,7 @@ github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3 github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= -github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= @@ -170,17 +161,13 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 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/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= -github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= -github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA= -github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= 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= @@ -195,7 +182,6 @@ github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7 github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= -github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -214,6 +200,7 @@ github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJf github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= @@ -229,13 +216,12 @@ github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/weaveworks/cluster-controller v1.5.2 h1:HEgiovJSvrLsdU7Y4mswfCzq8ca2hzpnygBVIzbQQW8= github.com/weaveworks/cluster-controller v1.5.2/go.mod h1:rYBv/mUMvXOP6NdSSUvvpT6ptpOPE9cqSl7WMM4LkcY= github.com/weaveworks/weave-gitops v0.33.0 h1:LL3KO3nP+nluIYdXhfGbw9y4JmDk7PRDbgCbo/jG68c= @@ -321,7 +307,6 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -431,7 +416,6 @@ k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= k8s.io/kubectl v0.26.8 h1:8252xsEUAlK1K0J1w+8pE8k/Xl4b4p1OC7S9Ib0AQxU= -k8s.io/kubectl v0.26.8/go.mod h1:zqblts62fYhUOeWKwNHr2KAh4Bf8TnTsbWKTXilELJQ= k8s.io/utils v0.0.0-20230505201702-9f6742963106 h1:EObNQ3TW2D+WptiYXlApGNLVy0zm/JIBVY9i+M4wpAU= k8s.io/utils v0.0.0-20230505201702-9f6742963106/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/cli-utils v0.34.0 h1:zCUitt54f0/MYj/ajVFnG6XSXMhpZ72O/3RewIchW8w= diff --git a/internal/controller/automatedclusterdiscovery_controller.go b/internal/controller/automatedclusterdiscovery_controller.go index a6d5e2a..ad7224b 100644 --- a/internal/controller/automatedclusterdiscovery_controller.go +++ b/internal/controller/automatedclusterdiscovery_controller.go @@ -230,6 +230,7 @@ func (r *AutomatedClusterDiscoveryReconciler) reconcileClusters(ctx context.Cont } for _, cluster := range clustersToDelete { + logger.Info("deleting gitops cluster", "name", cluster.GetName()) if err := r.Client.Delete(ctx, cluster); err != nil { return inventoryResources, fmt.Errorf("failed to delete cluster: %w", err) } diff --git a/internal/controller/automatedclusterdiscovery_controller_test.go b/internal/controller/automatedclusterdiscovery_controller_test.go index 5c523a1..66afe58 100644 --- a/internal/controller/automatedclusterdiscovery_controller_test.go +++ b/internal/controller/automatedclusterdiscovery_controller_test.go @@ -421,13 +421,18 @@ func TestAutomatedClusterDiscoveryReconciler(t *testing.T) { } type stubProvider struct { - response []*providers.ProviderCluster + response []*providers.ProviderCluster + clusterID string } func (s *stubProvider) ListClusters(ctx context.Context) ([]*providers.ProviderCluster, error) { return s.response, nil } +func (s *stubProvider) ClusterID(ctx context.Context, kubeClient client.Reader) (string, error) { + return s.clusterID, nil +} + func deleteObject(t *testing.T, cl client.Client, obj client.Object) { t.Helper() if err := cl.Delete(context.TODO(), obj); err != nil { diff --git a/pkg/providers/azure/azure.go b/pkg/providers/azure/azure.go index 9ec6401..2937cad 100644 --- a/pkg/providers/azure/azure.go +++ b/pkg/providers/azure/azure.go @@ -4,39 +4,47 @@ import ( "context" "fmt" + corev1 "k8s.io/api/core/v1" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" "github.com/Azure/azure-sdk-for-go/sdk/azidentity" - "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice" + acs "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v4" "github.com/Azure/go-autorest/autorest/azure" "github.com/weaveworks/cluster-reflector-controller/pkg/providers" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" + "sigs.k8s.io/controller-runtime/pkg/client" ) +// AKSclusterLister implementations query for AKS clusters. +type AKSClusterClient interface { + NewListPager(options *acs.ManagedClustersClientListOptions) *runtime.Pager[acs.ManagedClustersClientListResponse] + ListClusterAdminCredentials(ctx context.Context, resourceGroupName string, resourceName string, options *acs.ManagedClustersClientListClusterAdminCredentialsOptions) (acs.ManagedClustersClientListClusterAdminCredentialsResponse, error) +} + // AzureProvider queries all AKS clusters for the provided SubscriptionID and // returns the clusters and kubeconfigs for the clusters. type AzureProvider struct { SubscriptionID string + ClientFactory func(string) (AKSClusterClient, error) } // NewAzureProvider creates and returns an AzureProvider ready for use. func NewAzureProvider(subscriptionID string) *AzureProvider { return &AzureProvider{ SubscriptionID: subscriptionID, + ClientFactory: clientFactory, } } func (p *AzureProvider) ListClusters(ctx context.Context) ([]*providers.ProviderCluster, error) { - cred, err := azidentity.NewDefaultAzureCredential(nil) - if err != nil { - return nil, fmt.Errorf("failed to obtain a credential: %v", err) - } - client, err := armcontainerservice.NewManagedClustersClient(p.SubscriptionID, cred, nil) + client, err := p.ClientFactory(p.SubscriptionID) if err != nil { return nil, fmt.Errorf("failed to create client: %v", err) } - clusters := []*providers.ProviderCluster{} pager := client.NewListPager(nil) + clusters := []*providers.ProviderCluster{} for pager.More() { nextResult, err := pager.NextPage(ctx) if err != nil { @@ -50,6 +58,7 @@ func (p *AzureProvider) ListClusters(ctx context.Context) ([]*providers.Provider clusters = append(clusters, &providers.ProviderCluster{ Name: *aksCluster.Name, + ID: *aksCluster.ID, KubeConfig: kubeConfig, }) } @@ -58,8 +67,36 @@ func (p *AzureProvider) ListClusters(ctx context.Context) ([]*providers.Provider return clusters, nil } -func getKubeconfigForCluster(ctx context.Context, client *armcontainerservice.ManagedClustersClient, aksCluster *armcontainerservice.ManagedCluster) (*clientcmdapi.Config, error) { +func (p *AzureProvider) ClusterID(ctx context.Context, kubeClient client.Reader) (string, error) { + configMap := &corev1.ConfigMap{} + if err := kubeClient.Get(ctx, client.ObjectKey{Name: "extension-manager-config", Namespace: "kube-system"}, configMap); err != nil { + return "", client.IgnoreNotFound(err) + } + + keys := keysFromConfigMap(configMap, + "AZURE_RESOURCE_GROUP", "AZURE_RESOURCE_NAME", + "AZURE_SUBSCRIPTION_ID") + + id := fmt.Sprintf("/subscriptions/%s/resourcegroups/%s/providers/Microsoft.ContainerService/managedClusters/%s", + keys["AZURE_SUBSCRIPTION_ID"], keys["AZURE_RESOURCE_GROUP"], + keys["AZURE_RESOURCE_NAME"]) + return id, nil +} + +func keysFromConfigMap(configMap *corev1.ConfigMap, keys ...string) map[string]string { + values := map[string]string{} + for _, k := range keys { + v, ok := configMap.Data[k] + if ok { + values[k] = v + } + } + + return values +} + +func getKubeconfigForCluster(ctx context.Context, client AKSClusterClient, aksCluster *acs.ManagedCluster) (*clientcmdapi.Config, error) { resourceGroup, err := aksClusterResourceGroup(*aksCluster.ID) if err != nil { return nil, fmt.Errorf("failed to get cluster resource group: %v", err) @@ -68,7 +105,7 @@ func getKubeconfigForCluster(ctx context.Context, client *armcontainerservice.Ma credentialsResponse, err := client.ListClusterAdminCredentials(ctx, resourceGroup, *aksCluster.Name, - &armcontainerservice.ManagedClustersClientListClusterAdminCredentialsOptions{ServerFqdn: nil}, + &acs.ManagedClustersClientListClusterAdminCredentialsOptions{ServerFqdn: nil}, ) if err != nil { @@ -94,3 +131,18 @@ func aksClusterResourceGroup(clusterID string) (string, error) { } return resource.ResourceGroup, nil } + +// this is the default client factory which just creates a set of +// AzureCredentials and creates a client from it. +func clientFactory(subscriptionID string) (AKSClusterClient, error) { + cred, err := azidentity.NewDefaultAzureCredential(nil) + if err != nil { + return nil, fmt.Errorf("failed to obtain a credential: %v", err) + } + client, err := acs.NewManagedClustersClient(subscriptionID, cred, nil) + if err != nil { + return nil, fmt.Errorf("failed to create client: %v", err) + } + + return client, nil +} diff --git a/pkg/providers/azure/azure_test.go b/pkg/providers/azure/azure_test.go index cd945e9..27497db 100644 --- a/pkg/providers/azure/azure_test.go +++ b/pkg/providers/azure/azure_test.go @@ -1,4 +1,127 @@ package azure -// TODO: Add tests -// The next release of the Azure SDK will include a mock client for testing. +import ( + "context" + "testing" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + acs "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v4" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + + "github.com/google/go-cmp/cmp" + + "github.com/weaveworks/cluster-reflector-controller/pkg/providers" +) + +const testSubscriptionID = "ace37984-3d07-4051-9002-d5a52c0ae14b" + +var _ providers.Provider = (*AzureProvider)(nil) + +func TestAzureProvider_ClusterID(t *testing.T) { + clusterIDTests := []struct { + name string + objs []client.Object + want string + }{ + { + name: "ConfigMap exists", + objs: []client.Object{ + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "extension-manager-config", + Namespace: "kube-system", + }, + Data: map[string]string{ + "AZURE_RESOURCE_GROUP": "team-pesto-use1", + "AZURE_RESOURCE_NAME": "pestomarketplacetest", + "AZURE_SUBSCRIPTION_ID": testSubscriptionID, + "AKS_USER_ASSIGNED_IDENTITY_CLIENT_ID": "5b3fe934-b769-453b-9374-c672ab0a0a96", + }, + }, + }, + want: "/subscriptions/ace37984-3d07-4051-9002-d5a52c0ae14b/resourcegroups/team-pesto-use1/providers/Microsoft.ContainerService/managedClusters/pestomarketplacetest", + }, + { + // Ths is ok because it should not match an ID provided by the + // ListClusters method. + name: "ConfigMap does not exist", + objs: []client.Object{}, + want: "", + }, + } + + for _, tt := range clusterIDTests { + t.Run(tt.name, func(t *testing.T) { + fc := fake.NewClientBuilder().WithObjects(tt.objs...).Build() + + provider := NewAzureProvider(testSubscriptionID) + + clusterID, err := provider.ClusterID(context.TODO(), fc) + if err != nil { + t.Fatal(err) + } + + if clusterID != tt.want { + t.Fatalf("ClusterID() got %s, want %s", clusterID, tt.want) + } + }) + } +} + +func TestClusterProvider_ListClusters(t *testing.T) { + stubClient := stubManagedClustersClient{ + []*acs.ManagedCluster{ + { + ID: to.Ptr("/subscriptions/ace37984-3d07-4051-9002-d5a52c0ae14b/resourcegroups/team-pesto-use1/providers/Microsoft.ContainerService/managedClusters/pestomarketplacetest"), + Name: to.Ptr("cluster-1"), + }, + }, + } + + provider := NewAzureProvider("test-subscription") + provider.ClientFactory = func(s string) (AKSClusterClient, error) { + return stubClient, nil + } + + provided, err := provider.ListClusters(context.TODO()) + if err != nil { + t.Fatal(err) + } + + want := []*providers.ProviderCluster{ + { + ID: "/subscriptions/ace37984-3d07-4051-9002-d5a52c0ae14b/resourcegroups/team-pesto-use1/providers/Microsoft.ContainerService/managedClusters/pestomarketplacetest", + Name: "cluster-1", + }, + } + if diff := cmp.Diff(want, provided); diff != "" { + t.Fatalf("failed to list clusters:\n%s", diff) + } +} + +type stubManagedClustersClient struct { + Clusters []*acs.ManagedCluster +} + +func (m stubManagedClustersClient) NewListPager(_ *acs.ManagedClustersClientListOptions) *runtime.Pager[acs.ManagedClustersClientListResponse] { + return runtime.NewPager(runtime.PagingHandler[acs.ManagedClustersClientListResponse]{ + More: func(_ acs.ManagedClustersClientListResponse) bool { + return false + }, + Fetcher: func(_ context.Context, _ *acs.ManagedClustersClientListResponse) (acs.ManagedClustersClientListResponse, error) { + return acs.ManagedClustersClientListResponse{ + ManagedClusterListResult: acs.ManagedClusterListResult{ + Value: m.Clusters, + }, + }, nil + }, + }) +} + +func (m stubManagedClustersClient) ListClusterAdminCredentials(ctx context.Context, resourceGroupName string, resourceName string, options *acs.ManagedClustersClientListClusterAdminCredentialsOptions) (acs.ManagedClustersClientListClusterAdminCredentialsResponse, error) { + return acs.ManagedClustersClientListClusterAdminCredentialsResponse{}, nil +} diff --git a/pkg/providers/provider.go b/pkg/providers/provider.go index 5a6e1da..94e4552 100644 --- a/pkg/providers/provider.go +++ b/pkg/providers/provider.go @@ -25,7 +25,7 @@ type ProviderCluster struct { // This is the name of the cluster as provided by the cluster service. Name string // This is the unique ID for this cluster. - UniqueID string + ID string // This is the KubeConfig from the cluster service used to connect to it. // This is likely to be a high privilege token. From 3080880082410fb5678d3e696fac08de60b97c1d Mon Sep 17 00:00:00 2001 From: Kevin McDermott Date: Tue, 24 Oct 2023 09:48:58 +0100 Subject: [PATCH 3/3] Add support for ignoring the current cluster in the list. --- TODO.md | 4 +- .../automatedclusterdiscovery_controller.go | 26 ++++-- ...tomatedclusterdiscovery_controller_test.go | 89 ++++++++++++++++--- 3 files changed, 100 insertions(+), 19 deletions(-) diff --git a/TODO.md b/TODO.md index 6289e54..41d001c 100644 --- a/TODO.md +++ b/TODO.md @@ -5,6 +5,8 @@ Steps before release of the AKS reflector - [X] Add managed-by labels! - [ ] Conditions - ready with count of reflected clusters - [ ] Events - publish when cluster created or removed -- [ ] Suspension +- [X] Suspension - [ ] Manually triggered reconciliation - [ ] Provide for authentication via a Secret? +- [ ] CommonLabels support +- [ ] ObservedGeneration support! diff --git a/internal/controller/automatedclusterdiscovery_controller.go b/internal/controller/automatedclusterdiscovery_controller.go index ad7224b..33e6f80 100644 --- a/internal/controller/automatedclusterdiscovery_controller.go +++ b/internal/controller/automatedclusterdiscovery_controller.go @@ -68,29 +68,39 @@ func (r *AutomatedClusterDiscoveryReconciler) Reconcile(ctx context.Context, req // Skip reconciliation if the AutomatedClusterDiscovery is suspended. if clusterDiscovery.Spec.Suspend { - logger.Info("Reconciliation is suspended for this object") + logger.Info("reconciliation is suspended for this object") return ctrl.Result{}, nil } - logger.Info("Reconciling cluster reflector", + logger.Info("reconciling cluster reflector", "type", clusterDiscovery.Spec.Type, "name", clusterDiscovery.Spec.Name, ) if clusterDiscovery.Spec.Type == "aks" { - logger.Info("Reconciling AKS cluster reflector", + logger.Info("reconciling AKS cluster reflector", "name", clusterDiscovery.Spec.Name, ) azureProvider := r.AKSProvider(clusterDiscovery.Spec.AKS.SubscriptionID) + // We get the clusters and cluster ID separately so that we can return + // the error from the Reconciler without touching the inventory. clusters, err := azureProvider.ListClusters(ctx) if err != nil { - logger.Error(err, "Failed to list AKS clusters") + logger.Error(err, "failed to list AKS clusters") return ctrl.Result{}, err } - inventoryRefs, err := r.reconcileClusters(ctx, clusters, clusterDiscovery) + clusterID, err := azureProvider.ClusterID(ctx, r.Client) + if err != nil { + logger.Error(err, "failed to list get Cluster ID from AKS cluster") + return ctrl.Result{}, err + } + + // TODO: Fix this so that we record the inventoryRefs even if we get an + // error. + inventoryRefs, err := r.reconcileClusters(ctx, clusters, clusterID, clusterDiscovery) if err != nil { return ctrl.Result{}, err } @@ -121,7 +131,7 @@ func (r *AutomatedClusterDiscoveryReconciler) SetupWithManager(mgr ctrl.Manager) Complete(r) } -func (r *AutomatedClusterDiscoveryReconciler) reconcileClusters(ctx context.Context, clusters []*providers.ProviderCluster, cd *clustersv1alpha1.AutomatedClusterDiscovery) ([]clustersv1alpha1.ResourceRef, error) { +func (r *AutomatedClusterDiscoveryReconciler) reconcileClusters(ctx context.Context, clusters []*providers.ProviderCluster, currentClusterID string, cd *clustersv1alpha1.AutomatedClusterDiscovery) ([]clustersv1alpha1.ResourceRef, error) { logger := log.FromContext(ctx) logger.Info("reconciling clusters", "count", len(clusters)) @@ -145,6 +155,10 @@ func (r *AutomatedClusterDiscoveryReconciler) reconcileClusters(ctx context.Cont inventoryResources := []clustersv1alpha1.ResourceRef{} for _, cluster := range clusters { + if currentClusterID != "" && currentClusterID == cluster.ID { + logger.Info("skipping current cluster") + continue + } secretName := fmt.Sprintf("%s-kubeconfig", cluster.Name) gitopsCluster := newGitopsCluster(secretName, types.NamespacedName{ diff --git a/internal/controller/automatedclusterdiscovery_controller_test.go b/internal/controller/automatedclusterdiscovery_controller_test.go index 66afe58..aeea677 100644 --- a/internal/controller/automatedclusterdiscovery_controller_test.go +++ b/internal/controller/automatedclusterdiscovery_controller_test.go @@ -102,7 +102,7 @@ func TestAutomatedClusterDiscoveryReconciler(t *testing.T) { ctx := context.TODO() key := types.NamespacedName{Name: aksCluster.Name, Namespace: aksCluster.Namespace} - err = mgr.GetClient().Create(ctx, aksCluster) + err = k8sClient.Create(ctx, aksCluster) assert.NoError(t, err) defer deleteClusterDiscoveryAndInventory(t, k8sClient, aksCluster) @@ -151,6 +151,70 @@ func TestAutomatedClusterDiscoveryReconciler(t *testing.T) { assertHasOwnerReference(t, secret, clusterRef) }) + t.Run("Reconcile when executing in cluster and cluster matches reflector cluster", func(t *testing.T) { + aksCluster := &clustersv1alpha1.AutomatedClusterDiscovery{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-aks", + Namespace: "default", + }, + Spec: clustersv1alpha1.AutomatedClusterDiscoverySpec{ + Type: "aks", + AKS: &clustersv1alpha1.AKS{ + SubscriptionID: "subscription-123", + }, + Interval: metav1.Duration{Duration: time.Minute}, + }, + } + testClusterID := "/subscriptions/ace37984-aaaa-1234-1234-a1a12c0ae14b/resourcegroups/team-pesto-use1/providers/Microsoft.ContainerService/managedClusters/test-cluster" + + testProvider := stubProvider{ + clusterID: testClusterID, + response: []*providers.ProviderCluster{ + { + Name: "test-cluster", + ID: testClusterID, + KubeConfig: &kubeconfig.Config{ + APIVersion: "v1", + Clusters: map[string]*kubeconfig.Cluster{ + "test-cluster": { + Server: "https://cluster-prod.example.com/", + CertificateAuthorityData: []uint8(testCAData), + }, + }, + }, + }, + }, + } + + reconciler := &AutomatedClusterDiscoveryReconciler{ + Client: k8sClient, + Scheme: scheme, + AKSProvider: func(providerID string) providers.Provider { + return &testProvider + }, + } + + assert.NoError(t, reconciler.SetupWithManager(mgr)) + + ctx := context.TODO() + key := types.NamespacedName{Name: aksCluster.Name, Namespace: aksCluster.Namespace} + err = k8sClient.Create(ctx, aksCluster) + assert.NoError(t, err) + defer deleteClusterDiscoveryAndInventory(t, k8sClient, aksCluster) + + result, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: key}) + assert.NoError(t, err) + assert.Equal(t, ctrl.Result{RequeueAfter: time.Minute}, result) + + gitopsCluster := &gitopsv1alpha1.GitopsCluster{} + err = k8sClient.Get(ctx, types.NamespacedName{Name: "test-cluster", Namespace: aksCluster.Namespace}, gitopsCluster) + assert.True(t, apierrors.IsNotFound(err)) + + secret := &corev1.Secret{} + err = k8sClient.Get(ctx, types.NamespacedName{Name: "test-cluster-kubeconfig", Namespace: aksCluster.Namespace}, secret) + assert.True(t, apierrors.IsNotFound(err)) + }) + t.Run("Reconcile when cluster has been removed from AKS", func(t *testing.T) { ctx := context.TODO() aksCluster := &clustersv1alpha1.AutomatedClusterDiscovery{ @@ -294,7 +358,7 @@ func TestAutomatedClusterDiscoveryReconciler(t *testing.T) { assert.NoError(t, err) assert.Equal(t, value, secret.Data["value"]) }) - t.Run("test suspend option", func(t *testing.T) { + t.Run("Reconcile suspended cluster discovery resource", func(t *testing.T) { ctx := context.TODO() aksCluster := &clustersv1alpha1.AutomatedClusterDiscovery{ ObjectMeta: metav1.ObjectMeta{ @@ -339,7 +403,7 @@ func TestAutomatedClusterDiscoveryReconciler(t *testing.T) { assert.NoError(t, reconciler.SetupWithManager(mgr)) key := types.NamespacedName{Name: aksCluster.Name, Namespace: aksCluster.Namespace} - err = mgr.GetClient().Create(ctx, aksCluster) + err = k8sClient.Create(ctx, aksCluster) assert.NoError(t, err) defer deleteObject(t, k8sClient, aksCluster) @@ -417,7 +481,6 @@ func TestAutomatedClusterDiscoveryReconciler(t *testing.T) { _, err = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: client.ObjectKeyFromObject(aksCluster)}) assert.NoError(t, err) }) - } type stubProvider struct { @@ -444,14 +507,16 @@ func deleteClusterDiscoveryAndInventory(t *testing.T, cl client.Client, cd *clus t.Helper() ctx := context.TODO() - for _, v := range cd.Status.Inventory.Entries { - u, err := unstructuredFromResourceRef(v) - if err != nil { - t.Errorf("failed to convert unstructured from %s", v) - continue - } - if err := client.IgnoreNotFound(cl.Delete(ctx, u)); err != nil { - t.Errorf("failed to delete %v: %s", u, err) + if cd.Status.Inventory != nil { + for _, v := range cd.Status.Inventory.Entries { + u, err := unstructuredFromResourceRef(v) + if err != nil { + t.Errorf("failed to convert unstructured from %s", v) + continue + } + if err := client.IgnoreNotFound(cl.Delete(ctx, u)); err != nil { + t.Errorf("failed to delete %v: %s", u, err) + } } }