From b0b04e26ba524f62b58819322a2f545791cfd414 Mon Sep 17 00:00:00 2001 From: Sebastian Widmer Date: Tue, 14 Nov 2023 14:39:55 +0100 Subject: [PATCH 1/4] Collect OCP OAuth route and additional facts --- go.mod | 2 +- go.sum | 4 ++ main.go | 6 +++ pkg/agent/agent.go | 17 +++++--- pkg/agent/{ => facts}/facts.go | 43 +++++++++++++++++--- pkg/agent/{ => facts}/facts_test.go | 2 +- pkg/agent/{ => facts}/factsopenshift.go | 30 ++++++++++++-- pkg/agent/{ => facts}/factsopenshift_test.go | 2 +- tools/dynamic_fact_collector/main.go | 42 +++++++++++++++++++ 9 files changed, 131 insertions(+), 17 deletions(-) rename pkg/agent/{ => facts}/facts.go (57%) rename pkg/agent/{ => facts}/facts_test.go (98%) rename pkg/agent/{ => facts}/factsopenshift.go (73%) rename pkg/agent/{ => facts}/factsopenshift_test.go (99%) create mode 100644 tools/dynamic_fact_collector/main.go diff --git a/go.mod b/go.mod index 5639b45..c7ef22b 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( k8s.io/apimachinery v0.28.3 k8s.io/client-go v0.28.3 k8s.io/klog v1.0.0 + sigs.k8s.io/controller-runtime v0.16.3 ) require ( @@ -118,7 +119,6 @@ require ( k8s.io/klog/v2 v2.110.1 // indirect k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect - sigs.k8s.io/controller-runtime v0.16.3 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect sigs.k8s.io/yaml v1.4.0 // indirect diff --git a/go.sum b/go.sum index 23496d2..44db0a4 100644 --- a/go.sum +++ b/go.sum @@ -64,6 +64,8 @@ github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SU github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo= +github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= @@ -273,6 +275,8 @@ github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 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.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c= +go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.6.0 h1:S0JTfE48HbRj80+4tbvZDYsJ3tGv6BUU3XxyZ7CirAc= golang.org/x/arch v0.6.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= diff --git a/main.go b/main.go index a67243e..ea2d36a 100644 --- a/main.go +++ b/main.go @@ -48,6 +48,12 @@ func main() { app.Flag("operator-namespace", "Namespace in which the ArgoCD operator will be running").Default("syn-argocd-operator").StringVar(&agent.OperatorNamespace) app.Flag("argo-image", "Image to be used for the Argo CD deployments").Default(images.DefaultArgoCDImage).StringVar(&agent.ArgoCDImage) app.Flag("redis-image", "Image to be used for the Argo CD Redis deployment").Default(images.DefaultRedisImage).StringVar(&agent.RedisImage) + app. + Flag( + "additional-facts-config-map", + "Additional facts added to the dynamic facts in the cluster object. Keys in the config maps data field override existing keys."). + Default("additional-facts"). + StringVar(&agent.AdditionalFactsConfigMap) kingpin.MustParse(app.Parse(os.Args[1:])) } diff --git a/pkg/agent/agent.go b/pkg/agent/agent.go index 9041f89..dfcf595 100644 --- a/pkg/agent/agent.go +++ b/pkg/agent/agent.go @@ -12,11 +12,13 @@ import ( "github.com/deepmap/oapi-codegen/v2/pkg/securityprovider" "github.com/projectsyn/lieutenant-api/pkg/api" - "github.com/projectsyn/steward/pkg/argocd" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" "k8s.io/klog" + + "github.com/projectsyn/steward/pkg/agent/facts" + "github.com/projectsyn/steward/pkg/argocd" ) // Agent configures the cluster agent @@ -31,8 +33,10 @@ type Agent struct { OperatorNamespace string ArgoCDImage string RedisImage string + // The configmap containing additional facts to be added to the dynamic facts + AdditionalFactsConfigMap string - facts factCollector + facts facts.FactCollector } // Run starts the cluster agent @@ -58,8 +62,11 @@ func (a *Agent) Run(ctx context.Context) error { return err } - a.facts = factCollector{ - client: client, + a.facts = facts.FactCollector{ + Client: client, + + AdditionalFactsConfigMapNamespace: a.Namespace, + AdditionalFactsConfigMapName: a.AdditionalFactsConfigMap, } ticker := time.NewTicker(1 * time.Minute) @@ -91,7 +98,7 @@ func (a *Agent) registerCluster(ctx context.Context, config *rest.Config, apiCli DeployKey: &publicKey, }, } - patchCluster.DynamicFacts, err = a.facts.fetchDynamicFacts(ctx) + patchCluster.DynamicFacts, err = a.facts.FetchDynamicFacts(ctx) if err != nil { klog.Errorf("Error fetching dynamic facts: %v", err) } diff --git a/pkg/agent/facts.go b/pkg/agent/facts/facts.go similarity index 57% rename from pkg/agent/facts.go rename to pkg/agent/facts/facts.go index 97e475c..9b1e3b5 100644 --- a/pkg/agent/facts.go +++ b/pkg/agent/facts/facts.go @@ -1,4 +1,4 @@ -package agent +package facts import ( "context" @@ -7,16 +7,20 @@ import ( "unicode" "github.com/projectsyn/lieutenant-api/pkg/api" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/version" "k8s.io/client-go/kubernetes" "k8s.io/klog" ) -type factCollector struct { - client *kubernetes.Clientset +type FactCollector struct { + Client *kubernetes.Clientset + + AdditionalFactsConfigMapNamespace string + AdditionalFactsConfigMapName string } -func (col factCollector) fetchDynamicFacts(ctx context.Context) (*api.DynamicClusterFacts, error) { +func (col FactCollector) FetchDynamicFacts(ctx context.Context) (*api.DynamicClusterFacts, error) { facts := api.DynamicClusterFacts{} kubeVersion, err := col.fetchKubernetesVersion(ctx) if err != nil { @@ -34,12 +38,28 @@ func (col factCollector) fetchDynamicFacts(ctx context.Context) (*api.DynamicClu facts["openshiftVersion"] = ocpVersion } + ocpOAuthRoute, err := col.fetchOpenshiftOAuthRoute(ctx) + if err != nil { + klog.Errorf("Error fetching openshift oauth route: %v", err) + } + if ocpOAuthRoute != "" { + facts["openshiftOAuthRoute"] = ocpOAuthRoute + } + + additionalFacts, err := col.fetchAdditionalFacts(ctx) + if err != nil { + klog.Errorf("Error fetching additional facts: %v", err) + } + for k, v := range additionalFacts { + facts[k] = v + } + return &facts, nil } -func (col factCollector) fetchKubernetesVersion(ctx context.Context) (*version.Info, error) { +func (col FactCollector) fetchKubernetesVersion(ctx context.Context) (*version.Info, error) { // We are not using `col.client.ServerVersion()` to get context support - body, err := col.client.RESTClient().Get().AbsPath("/version").Do(ctx).Raw() + body, err := col.Client.RESTClient().Get().AbsPath("/version").Do(ctx).Raw() if err != nil { return nil, err } @@ -70,6 +90,17 @@ func processKubernetesVersion(v version.Info) (version.Info, error) { return v, nil } +func (col FactCollector) fetchAdditionalFacts(ctx context.Context) (map[string]string, error) { + if col.AdditionalFactsConfigMapName == "" { + return nil, nil + } + cm, err := col.Client.CoreV1().ConfigMaps(col.AdditionalFactsConfigMapNamespace).Get(ctx, col.AdditionalFactsConfigMapName, metav1.GetOptions{}) + if err != nil { + return nil, fmt.Errorf("unable to fetch the additional facts config map: %w", err) + } + return cm.Data, nil +} + func trimVersion(v string) string { res := []rune{} for _, r := range v { diff --git a/pkg/agent/facts_test.go b/pkg/agent/facts/facts_test.go similarity index 98% rename from pkg/agent/facts_test.go rename to pkg/agent/facts/facts_test.go index 7fe3492..26bcb72 100644 --- a/pkg/agent/facts_test.go +++ b/pkg/agent/facts/facts_test.go @@ -1,4 +1,4 @@ -package agent +package facts import ( "testing" diff --git a/pkg/agent/factsopenshift.go b/pkg/agent/facts/factsopenshift.go similarity index 73% rename from pkg/agent/factsopenshift.go rename to pkg/agent/facts/factsopenshift.go index 7e4ae36..3c18c73 100644 --- a/pkg/agent/factsopenshift.go +++ b/pkg/agent/facts/factsopenshift.go @@ -1,4 +1,4 @@ -package agent +package facts import ( "context" @@ -30,8 +30,14 @@ type OpenshiftVersion struct { Status OpenshiftVersionStatus } -func (col factCollector) fetchOpenshiftVersion(ctx context.Context) (*SemanticVersion, error) { - body, err := col.client.RESTClient().Get().AbsPath("/apis/config.openshift.io/v1/clusterversions/version").Do(ctx).Raw() +type OpenshiftRoute struct { + Spec struct { + Host string + } +} + +func (col FactCollector) fetchOpenshiftVersion(ctx context.Context) (*SemanticVersion, error) { + body, err := col.Client.RESTClient().Get().AbsPath("/apis/config.openshift.io/v1/clusterversions/version").Do(ctx).Raw() if err != nil { if errors.IsNotFound(err) { // API server doesn't know `clusterversions` or there is no resource, so we are not running on openshift. @@ -48,6 +54,24 @@ func (col factCollector) fetchOpenshiftVersion(ctx context.Context) (*SemanticVe return processOpenshiftVersion(version) } +func (col FactCollector) fetchOpenshiftOAuthRoute(ctx context.Context) (string, error) { + body, err := col.Client.RESTClient().Get().AbsPath("/apis/route.openshift.io/v1/namespaces/openshift-authentication/routes/oauth-openshift").Do(ctx).Raw() + if err != nil { + if errors.IsNotFound(err) { + // API server doesn't know `routes` or there is no resource, so we are not running on openshift. + return "", nil + } + return "", fmt.Errorf("unable to fetch the openshift route: %w", err) + } + var route OpenshiftRoute + err = json.Unmarshal(body, &route) + if err != nil { + return "", fmt.Errorf("unable to parse the openshift route: %w", err) + } + + return route.Spec.Host, nil +} + type SemanticVersion struct { Major string Minor string diff --git a/pkg/agent/factsopenshift_test.go b/pkg/agent/facts/factsopenshift_test.go similarity index 99% rename from pkg/agent/factsopenshift_test.go rename to pkg/agent/facts/factsopenshift_test.go index 4ecfa9d..fa38465 100644 --- a/pkg/agent/factsopenshift_test.go +++ b/pkg/agent/facts/factsopenshift_test.go @@ -1,4 +1,4 @@ -package agent +package facts import ( "testing" diff --git a/tools/dynamic_fact_collector/main.go b/tools/dynamic_fact_collector/main.go new file mode 100644 index 0000000..3a2eb65 --- /dev/null +++ b/tools/dynamic_fact_collector/main.go @@ -0,0 +1,42 @@ +package main + +import ( + "context" + "encoding/json" + "flag" + "fmt" + + "k8s.io/client-go/kubernetes" + "sigs.k8s.io/controller-runtime/pkg/client/config" + + "github.com/projectsyn/steward/pkg/agent/facts" +) + +func main() { + ns := flag.String("namespace", "syn", "namespace in which steward is running") + additionalFactsConfigMap := flag.String("additional-facts-config-map", "additional-facts", "configmap containing additional facts to be added to the dynamic facts") + + cfg := config.GetConfigOrDie() + + client, err := kubernetes.NewForConfig(cfg) + if err != nil { + panic(err) + } + + c := facts.FactCollector{ + Client: client, + + AdditionalFactsConfigMapNamespace: *ns, + AdditionalFactsConfigMapName: *additionalFactsConfigMap, + } + + fcts, err := c.FetchDynamicFacts(context.Background()) + if err != nil { + panic(err) + } + out, err := json.MarshalIndent(fcts, "", "\t") + if err != nil { + panic(err) + } + fmt.Println(string(out)) +} From 0d740fc393a914dc4c0b3d6f75e28886b7afe440 Mon Sep 17 00:00:00 2001 From: Sebastian Widmer Date: Tue, 14 Nov 2023 16:07:12 +0100 Subject: [PATCH 2/4] Allow configuring OAuth route reference --- main.go | 12 ++++++++++++ pkg/agent/agent.go | 7 +++++++ pkg/agent/facts/facts.go | 3 +++ pkg/agent/facts/factsopenshift.go | 6 +++++- tools/dynamic_fact_collector/main.go | 7 +++++++ 5 files changed, 34 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index ea2d36a..22ccb10 100644 --- a/main.go +++ b/main.go @@ -54,6 +54,18 @@ func main() { "Additional facts added to the dynamic facts in the cluster object. Keys in the config maps data field override existing keys."). Default("additional-facts"). StringVar(&agent.AdditionalFactsConfigMap) + app. + Flag( + "ocp-oauth-route-namespace", + "Namespace for the OpenShift OAuth route"). + Default("openshift-authentication"). + StringVar(&agent.OCPOAuthRouteNamespace) + app. + Flag( + "ocp-oauth-route-name", + "Name of the OpenShift OAuth route"). + Default("oauth-openshift"). + StringVar(&agent.OCPOAuthRouteName) kingpin.MustParse(app.Parse(os.Args[1:])) } diff --git a/pkg/agent/agent.go b/pkg/agent/agent.go index dfcf595..969b9d3 100644 --- a/pkg/agent/agent.go +++ b/pkg/agent/agent.go @@ -36,6 +36,10 @@ type Agent struct { // The configmap containing additional facts to be added to the dynamic facts AdditionalFactsConfigMap string + // Reference to the OpenShift OAuth route to be added to the dynamic facts + OCPOAuthRouteNamespace string + OCPOAuthRouteName string + facts facts.FactCollector } @@ -65,6 +69,9 @@ func (a *Agent) Run(ctx context.Context) error { a.facts = facts.FactCollector{ Client: client, + OAuthRouteNamespace: a.OCPOAuthRouteNamespace, + OAuthRouteName: a.OCPOAuthRouteName, + AdditionalFactsConfigMapNamespace: a.Namespace, AdditionalFactsConfigMapName: a.AdditionalFactsConfigMap, } diff --git a/pkg/agent/facts/facts.go b/pkg/agent/facts/facts.go index 9b1e3b5..1e8010d 100644 --- a/pkg/agent/facts/facts.go +++ b/pkg/agent/facts/facts.go @@ -16,6 +16,9 @@ import ( type FactCollector struct { Client *kubernetes.Clientset + OAuthRouteNamespace string + OAuthRouteName string + AdditionalFactsConfigMapNamespace string AdditionalFactsConfigMapName string } diff --git a/pkg/agent/facts/factsopenshift.go b/pkg/agent/facts/factsopenshift.go index 3c18c73..3b3cab8 100644 --- a/pkg/agent/facts/factsopenshift.go +++ b/pkg/agent/facts/factsopenshift.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "path" "strings" "time" @@ -55,7 +56,10 @@ func (col FactCollector) fetchOpenshiftVersion(ctx context.Context) (*SemanticVe } func (col FactCollector) fetchOpenshiftOAuthRoute(ctx context.Context) (string, error) { - body, err := col.Client.RESTClient().Get().AbsPath("/apis/route.openshift.io/v1/namespaces/openshift-authentication/routes/oauth-openshift").Do(ctx).Raw() + body, err := col.Client.RESTClient().Get(). + AbsPath( + path.Join("/apis/route.openshift.io/v1/namespaces", col.OAuthRouteNamespace, "routes", col.OAuthRouteName), + ).Do(ctx).Raw() if err != nil { if errors.IsNotFound(err) { // API server doesn't know `routes` or there is no resource, so we are not running on openshift. diff --git a/tools/dynamic_fact_collector/main.go b/tools/dynamic_fact_collector/main.go index 3a2eb65..a44f9f2 100644 --- a/tools/dynamic_fact_collector/main.go +++ b/tools/dynamic_fact_collector/main.go @@ -15,6 +15,10 @@ import ( func main() { ns := flag.String("namespace", "syn", "namespace in which steward is running") additionalFactsConfigMap := flag.String("additional-facts-config-map", "additional-facts", "configmap containing additional facts to be added to the dynamic facts") + ocpOAuthRouteNamespace := flag.String("ocp-oauth-route-namespace", "openshift-authentication", "Namespace for the OpenShift OAuth route") + ocpOAuthRouteName := flag.String("ocp-oauth-route-name", "oauth-openshift", "Name of the OpenShift OAuth route") + + flag.Parse() cfg := config.GetConfigOrDie() @@ -26,6 +30,9 @@ func main() { c := facts.FactCollector{ Client: client, + OAuthRouteNamespace: *ocpOAuthRouteNamespace, + OAuthRouteName: *ocpOAuthRouteName, + AdditionalFactsConfigMapNamespace: *ns, AdditionalFactsConfigMapName: *additionalFactsConfigMap, } From 47423b4c4af7873e426db57b1a8e01855d4cc0dc Mon Sep 17 00:00:00 2001 From: Sebastian Widmer Date: Tue, 14 Nov 2023 16:08:17 +0100 Subject: [PATCH 3/4] Improve wording in parameter documentation Co-authored-by: Simon Gerber --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index 22ccb10..508d217 100644 --- a/main.go +++ b/main.go @@ -51,7 +51,7 @@ func main() { app. Flag( "additional-facts-config-map", - "Additional facts added to the dynamic facts in the cluster object. Keys in the config maps data field override existing keys."). + "Additional facts added to the dynamic facts in the cluster object. Keys in the configmap's data field override existing keys."). Default("additional-facts"). StringVar(&agent.AdditionalFactsConfigMap) app. From 506f6bf2a01386edab80eda87a12e2aba3d5d657 Mon Sep 17 00:00:00 2001 From: Sebastian Widmer Date: Tue, 14 Nov 2023 16:14:19 +0100 Subject: [PATCH 4/4] Don't allow config map to override existing facts --- main.go | 2 +- pkg/agent/facts/facts.go | 17 +++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/main.go b/main.go index 508d217..eb4ef3b 100644 --- a/main.go +++ b/main.go @@ -51,7 +51,7 @@ func main() { app. Flag( "additional-facts-config-map", - "Additional facts added to the dynamic facts in the cluster object. Keys in the configmap's data field override existing keys."). + "Additional facts added to the dynamic facts in the cluster object. Keys in the configmap's data field can't override existing keys."). Default("additional-facts"). StringVar(&agent.AdditionalFactsConfigMap) app. diff --git a/pkg/agent/facts/facts.go b/pkg/agent/facts/facts.go index 1e8010d..8ec351a 100644 --- a/pkg/agent/facts/facts.go +++ b/pkg/agent/facts/facts.go @@ -25,6 +25,15 @@ type FactCollector struct { func (col FactCollector) FetchDynamicFacts(ctx context.Context) (*api.DynamicClusterFacts, error) { facts := api.DynamicClusterFacts{} + + additionalFacts, err := col.fetchAdditionalFacts(ctx) + if err != nil { + klog.Errorf("Error fetching additional facts: %v", err) + } + for k, v := range additionalFacts { + facts[k] = v + } + kubeVersion, err := col.fetchKubernetesVersion(ctx) if err != nil { klog.Errorf("Error fetching kubernetes version: %v", err) @@ -49,14 +58,6 @@ func (col FactCollector) FetchDynamicFacts(ctx context.Context) (*api.DynamicClu facts["openshiftOAuthRoute"] = ocpOAuthRoute } - additionalFacts, err := col.fetchAdditionalFacts(ctx) - if err != nil { - klog.Errorf("Error fetching additional facts: %v", err) - } - for k, v := range additionalFacts { - facts[k] = v - } - return &facts, nil }