From 6b73c8fe6c008f96a029547dc43209383e8bd32c Mon Sep 17 00:00:00 2001 From: Tamal Saha Date: Mon, 11 Nov 2024 18:53:53 -0800 Subject: [PATCH] Add client org reconciler Signed-off-by: Tamal Saha --- go.mod | 4 +- go.sum | 8 +- pkg/apiserver/apiserver.go | 12 + .../namespace/namespace_controller.go | 416 ++++++++++++++++++ .../prometheus/prometheus_controller.go | 26 +- pkg/registry/ui/dashboardgroup/storage.go | 55 ++- .../kmodules.xyz/client-go/api/v1/cluster.go | 15 + .../client-go/api/v1/zz_generated.deepcopy.go | 48 ++ .../monitoring-agent-api/api/v1/appbinding.go | 9 +- .../api/v1/openapi_generated.go | 6 + vendor/modules.txt | 4 +- 11 files changed, 569 insertions(+), 34 deletions(-) create mode 100644 pkg/controllers/namespace/namespace_controller.go diff --git a/go.mod b/go.mod index c93f61625..caf1653ae 100644 --- a/go.mod +++ b/go.mod @@ -34,9 +34,9 @@ require ( k8s.io/klog/v2 v2.130.1 k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 kmodules.xyz/authorizer v0.29.1 - kmodules.xyz/client-go v0.30.32 + kmodules.xyz/client-go v0.30.37 kmodules.xyz/custom-resources v0.30.0 - kmodules.xyz/monitoring-agent-api v0.30.2 + kmodules.xyz/monitoring-agent-api v0.30.3 moul.io/http2curl/v2 v2.3.1-0.20221024080105-10c404f653f7 sigs.k8s.io/controller-runtime v0.18.4 x-helm.dev/apimachinery v0.0.16 diff --git a/go.sum b/go.sum index 59dbc7669..0e63919f8 100644 --- a/go.sum +++ b/go.sum @@ -774,14 +774,14 @@ kmodules.xyz/apiversion v0.2.0 h1:vAQYqZFm4xu4pbB1cAdHbFEPES6EQkcR4wc06xdTOWk= kmodules.xyz/apiversion v0.2.0/go.mod h1:oPX8g8LvlPdPX3Yc5YvCzJHQnw3YF/X4/jdW0b1am80= kmodules.xyz/authorizer v0.29.1 h1:uByGGoryKbZcfiEAhjcK/Y345I9mygNQP7DVpkMbNQQ= kmodules.xyz/authorizer v0.29.1/go.mod h1:kZRhclL8twzyt2bQuJQJbpYww2sc+qFr8I5PPoq/sWY= -kmodules.xyz/client-go v0.30.32 h1:y1qb4IJwYdkROLcc7e0UcJSDj8D2YeLsawAWHnCF+JU= -kmodules.xyz/client-go v0.30.32/go.mod h1:CAu+JlA8RVGtj6LQHu0Q1w2mnFUajuti49c7T1AvGdM= +kmodules.xyz/client-go v0.30.37 h1:hj4BMsNDgRVc2aDPB6Y3x5iCylXTZDZeQPJp/oA6lxs= +kmodules.xyz/client-go v0.30.37/go.mod h1:CAu+JlA8RVGtj6LQHu0Q1w2mnFUajuti49c7T1AvGdM= kmodules.xyz/crd-schema-fuzz v0.29.1 h1:zJTlWYOrT5dsVVHW8HGcnR/vaWfxQfNh11QwTtkYpcs= kmodules.xyz/crd-schema-fuzz v0.29.1/go.mod h1:n708z9YQqLMP2KNLQVgBcRJw1QpSWLvpNCEi+KJDOYE= kmodules.xyz/custom-resources v0.30.0 h1:vR3CbseHMLwR4GvtcJJuRuwIV8voKqFqNii27rMcm1o= kmodules.xyz/custom-resources v0.30.0/go.mod h1:ZsTuI2mLG2s3byre7bHmpxJ9w0HDqAkRTL1+izGFI24= -kmodules.xyz/monitoring-agent-api v0.30.2 h1:sAgz5P5EXZqhlj1NzJ+QltAgeIx5bGSMj+aYy2EiKaw= -kmodules.xyz/monitoring-agent-api v0.30.2/go.mod h1:BoZFPDDRB7J39CcUsSDlzgW8PQCwik4ILPleyUob+Mg= +kmodules.xyz/monitoring-agent-api v0.30.3 h1:DVGwcRvtaTmRA7Wj4PJ5xR5ADzLo/IB3X2XmAJikbEo= +kmodules.xyz/monitoring-agent-api v0.30.3/go.mod h1:L586zE2BvTQeOmf7VHkuBY+QM1UWGdNzwyVJDtVcZsI= moul.io/http2curl/v2 v2.3.1-0.20221024080105-10c404f653f7 h1:NykkTlRB+X40z86cLHdEmuoTxhNKhQebLT379b1EumA= moul.io/http2curl/v2 v2.3.1-0.20221024080105-10c404f653f7/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index 63bf47b00..242b64f01 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -27,6 +27,7 @@ import ( "go.openviz.dev/apimachinery/apis/ui" uiinstall "go.openviz.dev/apimachinery/apis/ui/install" uiapi "go.openviz.dev/apimachinery/apis/ui/v1alpha1" + namespacecontroller "go.openviz.dev/grafana-tools/pkg/controllers/namespace" promtehsucontroller "go.openviz.dev/grafana-tools/pkg/controllers/prometheus" "go.openviz.dev/grafana-tools/pkg/controllers/ranchertoken" servicemonitorcontroller "go.openviz.dev/grafana-tools/pkg/controllers/servicemonitor" @@ -202,6 +203,17 @@ func (c completedConfig) New(ctx context.Context) (*UIServer, error) { Group: monitoring.GroupName, Kind: monitoringv1.PrometheusesKind, }, func(ctx context.Context, mgr ctrl.Manager) { + if err = namespacecontroller.NewReconciler( + mgr.GetClient(), + bc, + cid, + c.ExtraConfig.HubUID, + d, + ).SetupWithManager(mgr); err != nil { + klog.Error(err, "unable to create controller", "controller", "ClientOrg") + os.Exit(1) + } + if err = promtehsucontroller.NewReconciler( mgr.GetClient(), bc, diff --git a/pkg/controllers/namespace/namespace_controller.go b/pkg/controllers/namespace/namespace_controller.go new file mode 100644 index 000000000..97681606d --- /dev/null +++ b/pkg/controllers/namespace/namespace_controller.go @@ -0,0 +1,416 @@ +package namespace + +import ( + "context" + "fmt" + + "go.openviz.dev/apimachinery/apis/openviz/v1alpha1" + openvizapi "go.openviz.dev/apimachinery/apis/openviz/v1alpha1" + "go.openviz.dev/grafana-tools/pkg/controllers/prometheus" + "go.openviz.dev/grafana-tools/pkg/detector" + + monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + core "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/json" + "k8s.io/klog/v2" + "k8s.io/utils/ptr" + kmapi "kmodules.xyz/client-go/api/v1" + cu "kmodules.xyz/client-go/client" + clustermeta "kmodules.xyz/client-go/cluster" + core_util "kmodules.xyz/client-go/core/v1" + "kmodules.xyz/client-go/meta" + appcatalog "kmodules.xyz/custom-resources/apis/appcatalog/v1alpha1" + mona "kmodules.xyz/monitoring-agent-api/api/v1" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/predicate" + "sigs.k8s.io/controller-runtime/pkg/reconcile" +) + +// ClientOrgReconciler reconciles a GatewayConfig object +type ClientOrgReconciler struct { + kc client.Client + scheme *runtime.Scheme + bc *prometheus.Client + clusterUID string + hubUID string + d detector.Detector +} + +func NewReconciler(kc client.Client, bc *prometheus.Client, clusterUID, hubUID string, d detector.Detector) *ClientOrgReconciler { + return &ClientOrgReconciler{ + kc: kc, + scheme: kc.Scheme(), + bc: bc, + clusterUID: clusterUID, + hubUID: hubUID, + d: d, + } +} + +const ( + srcRefKey = "meta.appcode.com/source" + srcHashKey = "meta.appcode.com/hash" +) + +func (r *ClientOrgReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + log := log.FromContext(ctx) + + var ns core.Namespace + if err := r.kc.Get(ctx, req.NamespacedName, &ns); err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + if ns.Labels[kmapi.ClientOrgKey] == "" { + return ctrl.Result{}, nil + } + clientOrgId := ns.Annotations[kmapi.ClientOrgKey] + if clientOrgId == "" { + return ctrl.Result{}, nil + } + + if ready, err := r.d.Ready(); err != nil || !ready { + return ctrl.Result{}, err + } + if r.d.RancherManaged() && r.d.Federated() { + return ctrl.Result{}, fmt.Errorf("client organization mode is not supported when federated prometheus is used in a Rancher managed cluster") + } + + if ns.DeletionTimestamp != nil { + if r.bc != nil { + err := r.bc.Unregister(mona.PrometheusContext{ + ClusterUID: r.clusterUID, + ProjectId: "", + Default: false, + ClientOrgID: clientOrgId, + }) + if err != nil { + return ctrl.Result{}, err + } + } + + vt, err := cu.CreateOrPatch(context.TODO(), r.kc, &ns, func(in client.Object, createOp bool) client.Object { + obj := in.(*monitoringv1.Prometheus) + obj.ObjectMeta = core_util.RemoveFinalizer(obj.ObjectMeta, mona.PrometheusKey) + + return obj + }) + if err != nil { + return ctrl.Result{}, err + } + klog.Infof("%s Namespace %s to remove finalizer %s", vt, ns.Name, mona.PrometheusKey) + return ctrl.Result{}, nil + } + + vt, err := cu.CreateOrPatch(context.TODO(), r.kc, &ns, func(in client.Object, createOp bool) client.Object { + obj := in.(*monitoringv1.Prometheus) + obj.ObjectMeta = core_util.AddFinalizer(obj.ObjectMeta, mona.PrometheusKey) + + return obj + }) + if err != nil { + return ctrl.Result{}, err + } + klog.Infof("%s Namespace %s to add finalizer %s", vt, ns.Name, mona.PrometheusKey) + + // create {client}-monitoring namespace + monNamespace := core.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-monitoring", ns.Name), + }, + } + if err := r.kc.Get(ctx, client.ObjectKeyFromObject(&monNamespace), &monNamespace); apierrors.IsNotFound(err) { + if err := r.kc.Create(ctx, &monNamespace); err != nil { + return ctrl.Result{}, err + } + } else if err != nil { + return ctrl.Result{}, err + } + + var dashboardList v1alpha1.GrafanaDashboardList + if err := r.kc.List(ctx, &dashboardList); err != nil { + return ctrl.Result{}, err + } + for _, dashboard := range dashboardList.Items { + if _, found := dashboard.Annotations[srcRefKey]; found { + continue + } + copiedDashboard := dashboard.DeepCopy() + copiedDashboard.Namespace = monNamespace.Name + + opresult, err := controllerutil.CreateOrPatch(ctx, r.kc, copiedDashboard, func() error { + if copiedDashboard.Annotations == nil { + copiedDashboard.Annotations = map[string]string{} + } + copiedDashboard.Annotations[srcRefKey] = client.ObjectKeyFromObject(&dashboard).String() + copiedDashboard.Annotations[srcHashKey] = meta.ObjectHash(&dashboard) + + // hardcode namespace + // datasource + + return nil + }) + if err != nil { + return ctrl.Result{}, err + } + if opresult != controllerutil.OperationResultNone { + log.Info(fmt.Sprintf("%s GrafanaDashboard %s/%s", opresult, copiedDashboard.Namespace, copiedDashboard.Name)) + } + } + + // confirm trickter sa registered + var saList core.ServiceAccountList + if err := r.kc.List(ctx, &saList); err != nil { + return ctrl.Result{}, err + } + var saKey types.NamespacedName + for _, sa := range saList.Items { + if sa.Name != prometheus.ServiceAccountTrickster { + continue + } + if sa.Annotations[prometheus.RegisteredKey] != "" { + } + + saKey = types.NamespacedName{ + Namespace: sa.Namespace, + Name: sa.Name, + } + break + } + if saKey.Namespace == "" { + return ctrl.Result{}, fmt.Errorf("service account %s is not registered", prometheus.ServiceAccountTrickster) + } + + var promList monitoringv1.PrometheusList + if err := r.kc.List(ctx, &promList, client.InNamespace(saKey.Namespace)); err != nil { + return ctrl.Result{}, err + } else if len(promList.Items) == 0 { + return ctrl.Result{}, fmt.Errorf("prometheus not found in namespace %s", saKey.Namespace) + } else if len(promList.Items) > 1 { + return ctrl.Result{}, fmt.Errorf("more than one prometheus found in namespace %s", saKey.Namespace) + } + // prom := promList.Items[0] + + var promService core.Service + err = r.kc.Get(context.TODO(), client.ObjectKeyFromObject(promList.Items[0]), &promService) + if err != nil { + return ctrl.Result{}, err + } + + var saToken string + var caCrt string + s, err := cu.GetServiceAccountTokenSecret(r.kc, saKey) + if err != nil { + return ctrl.Result{}, err + } + saToken = string(s.Data["token"]) + caCrt = string(s.Data["ca.crt"]) + + var pcfg mona.PrometheusConfig + pcfg.Service = mona.ServiceSpec{ + Scheme: "http", + Name: promService.Name, + Namespace: promService.Namespace, + Port: "", + Path: "", + Query: "", + } + for _, p := range promService.Spec.Ports { + if p.Name == prometheus.PortPrometheus { + pcfg.Service.Port = fmt.Sprintf("%d", p.Port) + } + } + // pcfg.URL = fmt.Sprintf("%s/api/v1/namespaces/%s/services/%s:%s:%s/proxy/", r.cfg.Host, pcfg.Service.Namespace, pcfg.Service.Scheme, pcfg.Service.Name, pcfg.Service.Port) + + // remove basic auth and client cert auth + //if rancherToken != nil { + // pcfg.BearerToken = rancherToken.Token + //} else { + pcfg.BearerToken = saToken + // } + pcfg.BasicAuth = mona.BasicAuth{} + pcfg.TLS.Cert = "" + pcfg.TLS.Key = "" + pcfg.TLS.Ca = caCrt + + cm, err := clustermeta.ClusterMetadata(r.kc) + if err != nil { + return ctrl.Result{}, err + } + state := cm.State() + + applyMarkers := func(in client.Object, createOp bool) client.Object { + obj := in.(*core.Namespace) + if obj.Annotations == nil { + obj.Annotations = map[string]string{} + } + obj.Annotations[prometheus.RegisteredKey] = state + return obj + } + + if r.bc != nil && + monNamespace.Annotations[prometheus.RegisteredKey] != state { + resp, err := r.bc.Register(mona.PrometheusContext{ + HubUID: r.hubUID, + ClusterUID: r.clusterUID, + ProjectId: "", + Default: false, + ClientOrgID: clientOrgId, + }, pcfg) + if err != nil { + return ctrl.Result{}, err + } + + err = r.CreateGrafanaAppBinding(monNamespace.Name, resp) + if err != nil { + return ctrl.Result{}, err + } + + rbvt, err := cu.CreateOrPatch(context.TODO(), r.kc, &monNamespace, applyMarkers) + if err != nil { + return ctrl.Result{}, err + } + klog.Infof("%s namespace %s with %s annotation", rbvt, monNamespace.Name, prometheus.RegisteredKey) + } + + return ctrl.Result{}, nil +} + +func (r *ClientOrgReconciler) CreateGrafanaAppBinding(monNamespace string, resp *prometheus.GrafanaDatasourceResponse) error { + ab := appcatalog.AppBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "grafana", + Namespace: monNamespace, + }, + } + + abvt, err := cu.CreateOrPatch(context.TODO(), r.kc, &ab, func(in client.Object, createOp bool) client.Object { + obj := in.(*appcatalog.AppBinding) + + //ref := metav1.NewControllerRef(prom, schema.GroupVersionKind{ + // Group: monitoring.GroupName, + // Version: monitoringv1.Version, + // Kind: "Prometheus", + //}) + //obj.OwnerReferences = []metav1.OwnerReference{*ref} + // + //if obj.Annotations == nil { + // obj.Annotations = make(map[string]string) + //} + //obj.Annotations["monitoring.appscode.com/is-default-grafana"] = "true" + + obj.Spec.Type = "Grafana" + obj.Spec.AppRef = nil + obj.Spec.ClientConfig = appcatalog.ClientConfig{ + URL: ptr.To(resp.Grafana.URL), + //Service: &appcatalog.ServiceReference{ + // Scheme: "http", + // Namespace: svc.Namespace, + // Name: svc.Name, + // Port: 0, + // Path: "", + // Query: "", + //}, + //InsecureSkipTLSVerify: false, + //CABundle: nil, + //ServerName: "", + } + obj.Spec.Secret = &core.LocalObjectReference{ + Name: ab.Name + "-auth", + } + + // TODO: handle TLS config returned in resp + if caCert := r.bc.CACert(); len(caCert) > 0 { + obj.Spec.ClientConfig.CABundle = caCert + } + + params := openvizapi.GrafanaConfiguration{ + TypeMeta: metav1.TypeMeta{ + Kind: "GrafanaConfiguration", + APIVersion: openvizapi.SchemeGroupVersion.String(), + }, + Datasource: resp.Datasource, + FolderID: resp.FolderID, + } + paramBytes, err := json.Marshal(params) + if err != nil { + panic(err) + } + obj.Spec.Parameters = &runtime.RawExtension{ + Raw: paramBytes, + } + + return obj + }) + if err == nil { + klog.Infof("%s AppBinding %s/%s", abvt, ab.Namespace, ab.Name) + + authSecret := core.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: ab.Name + "-auth", + Namespace: monNamespace, + }, + } + + svt, e2 := cu.CreateOrPatch(context.TODO(), r.kc, &authSecret, func(in client.Object, createOp bool) client.Object { + obj := in.(*core.Secret) + + ref := metav1.NewControllerRef(&ab, schema.GroupVersionKind{ + Group: appcatalog.SchemeGroupVersion.Group, + Version: appcatalog.SchemeGroupVersion.Version, + Kind: "AppBinding", + }) + obj.OwnerReferences = []metav1.OwnerReference{*ref} + + obj.StringData = map[string]string{ + "token": resp.Grafana.BearerToken, + } + + return obj + }) + if e2 == nil { + klog.Infof("%s Grafana auth secret %s/%s", svt, authSecret.Namespace, authSecret.Name) + } + } + + return err +} + +// SetupWithManager sets up the controller with the Manager. +func (r *ClientOrgReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&core.Namespace{}, builder.WithPredicates(predicate.NewPredicateFuncs(func(obj client.Object) bool { + return obj.GetLabels()[kmapi.ClientOrgKey] == "true" + }))). + Watches(&core.ServiceAccount{}, handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, _ client.Object) []reconcile.Request { + var list core.NamespaceList + if err := r.kc.List(ctx, &list, client.MatchingLabels{ + kmapi.ClientOrgKey: "true", + }); err != nil { + return nil + } + reqs := make([]reconcile.Request, 0, len(list.Items)) + for _, ns := range list.Items { + reqs = append(reqs, reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: ns.Name, + }, + }) + } + return reqs + }), builder.WithPredicates(predicate.NewPredicateFuncs(func(obj client.Object) bool { + return obj.GetName() == prometheus.ServiceAccountTrickster && + obj.GetLabels()[prometheus.RegisteredKey] != "" + }))). + Named("namespace"). + Complete(r) +} diff --git a/pkg/controllers/prometheus/prometheus_controller.go b/pkg/controllers/prometheus/prometheus_controller.go index afbf70196..083125edd 100644 --- a/pkg/controllers/prometheus/prometheus_controller.go +++ b/pkg/controllers/prometheus/prometheus_controller.go @@ -54,11 +54,11 @@ import ( ) const ( - portPrometheus = "http-web" - saTrickster = "trickster" - crTrickster = "appscode:trickster:proxy" + PortPrometheus = "http-web" + ServiceAccountTrickster = "trickster" + crTrickster = "appscode:trickster:proxy" - registeredKey = mona.GroupName + "/registered" + RegisteredKey = mona.GroupName + "/registered" tokenIDKey = mona.GroupName + "/token-id" presetsMonitoring = "monitoring-presets" appBindingPrometheus = "default-prometheus" @@ -227,7 +227,7 @@ func (r *PrometheusReconciler) SetupClusterForPrometheus(ctx context.Context, pr } else { sa := core.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ - Name: saTrickster, + Name: ServiceAccountTrickster, Namespace: key.Namespace, }, } @@ -310,7 +310,7 @@ func (r *PrometheusReconciler) SetupClusterForPrometheus(ctx context.Context, pr obj.Subjects = []rbac.Subject{ { Kind: "ServiceAccount", - Name: saTrickster, + Name: ServiceAccountTrickster, Namespace: key.Namespace, }, } @@ -338,7 +338,7 @@ func (r *PrometheusReconciler) SetupClusterForPrometheus(ctx context.Context, pr Query: "", } for _, p := range svc.Spec.Ports { - if p.Name == portPrometheus { + if p.Name == PortPrometheus { pcfg.Service.Port = fmt.Sprintf("%d", p.Port) } } @@ -360,7 +360,7 @@ func (r *PrometheusReconciler) SetupClusterForPrometheus(ctx context.Context, pr if obj.Annotations == nil { obj.Annotations = map[string]string{} } - obj.Annotations[registeredKey] = state + obj.Annotations[RegisteredKey] = state if rancherToken != nil { obj.Annotations[tokenIDKey] = rancherToken.TokenID } else { @@ -370,16 +370,16 @@ func (r *PrometheusReconciler) SetupClusterForPrometheus(ctx context.Context, pr } // fix legacy deployments - if rb.Annotations[registeredKey] == "true" { + if rb.Annotations[RegisteredKey] == "true" { rbvt, err = cu.CreateOrPatch(context.TODO(), r.kc, &rb, applyMarkers) if err != nil { return err } - klog.Infof("%s rolebinding %s/%s with %s annotation", rbvt, rb.Namespace, rb.Name, registeredKey) + klog.Infof("%s rolebinding %s/%s with %s annotation", rbvt, rb.Namespace, rb.Name, RegisteredKey) return nil } else if r.bc != nil && - (rb.Annotations[registeredKey] != state || + (rb.Annotations[RegisteredKey] != state || (rancherToken != nil && rb.Annotations[tokenIDKey] != rancherToken.TokenID)) { var projectId string if !isDefault { @@ -414,7 +414,7 @@ func (r *PrometheusReconciler) SetupClusterForPrometheus(ctx context.Context, pr if err != nil { return err } - klog.Infof("%s rolebinding %s/%s with %s annotation", rbvt, rb.Namespace, rb.Name, registeredKey) + klog.Infof("%s rolebinding %s/%s with %s annotation", rbvt, rb.Namespace, rb.Name, RegisteredKey) } return nil @@ -634,7 +634,7 @@ func (r *PrometheusReconciler) CreatePrometheusAppBinding(prom *monitoringv1.Pro // ServerName: "", } for _, p := range svc.Spec.Ports { - if p.Name == portPrometheus { + if p.Name == PortPrometheus { obj.Spec.ClientConfig.Service.Port = p.Port } } diff --git a/pkg/registry/ui/dashboardgroup/storage.go b/pkg/registry/ui/dashboardgroup/storage.go index f316c0ef5..d91aca0f6 100644 --- a/pkg/registry/ui/dashboardgroup/storage.go +++ b/pkg/registry/ui/dashboardgroup/storage.go @@ -30,11 +30,13 @@ import ( "github.com/grafana-tools/sdk" "github.com/pkg/errors" + core "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/json" + "k8s.io/apiserver/pkg/authentication/user" "k8s.io/apiserver/pkg/authorization/authorizer" apirequest "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/apiserver/pkg/registry/rest" @@ -182,6 +184,11 @@ func (r *Storage) getDashboardLink( timeRange *uiapi.TimeRange, embed bool, ) (*uiapi.DashboardResponse, error) { + user, ok := apirequest.UserFrom(ctx) + if !ok { + return nil, apierrors.NewBadRequest("missing user info") + } + var d openvizapi.GrafanaDashboard if req.ObjectReference != nil { ns := req.Namespace @@ -194,11 +201,23 @@ func (r *Storage) getDashboardLink( } } else if req.Title != "" { var dashboardList openvizapi.GrafanaDashboardList - // any namespace, using default grafana and with the given title - if err := r.kc.List(ctx, &dashboardList, client.MatchingFields{ - mona.DefaultGrafanaKey: "true", - openvizapi.GrafanaDashboardTitleKey: req.Title, - }); err != nil { + dsNamespace, useClientDashboard, err := useClientOrgDashboard(ctx, r.kc, user) + if err != nil { + return nil, err + } + if useClientDashboard { + // in {client}-monitoring namespace + err = r.kc.List(ctx, &dashboardList, client.InNamespace(dsNamespace), client.MatchingFields{ + openvizapi.GrafanaDashboardTitleKey: req.Title, + }) + } else { + // any namespace, using default grafana and with the given title + err = r.kc.List(ctx, &dashboardList, client.MatchingFields{ + mona.DefaultGrafanaKey: "true", + openvizapi.GrafanaDashboardTitleKey: req.Title, + }) + } + if err != nil { return nil, err } if len(dashboardList.Items) == 0 { @@ -211,11 +230,7 @@ func (r *Storage) getDashboardLink( return nil, apierrors.NewBadRequest(fmt.Sprintf("multiple dashboards %s with title %s uses the default Grafana", strings.Join(names, ","), req.Title)) } d = dashboardList.Items[0] - } - user, ok := apirequest.UserFrom(ctx) - if !ok { - return nil, apierrors.NewBadRequest("missing user info") } { @@ -336,6 +351,28 @@ func (r *Storage) getDashboardLink( return resp, nil } +func useClientOrgDashboard(ctx context.Context, kc client.Client, user user.Info) (string, bool, error) { + orgId, found := user.GetExtra()[kmapi.AceOrgIDKey] + if !found || len(orgId) == 0 || len(orgId) > 1 { + return "", false, nil + } + + var nsList core.NamespaceList + err := kc.List(ctx, &nsList, client.MatchingLabels{ + kmapi.ClientOrgKey: "true", + }) + if err != nil { + return "", false, err + } + + for _, ns := range nsList.Items { + if ns.Annotations[kmapi.AceOrgIDKey] == orgId[0] { + return fmt.Sprintf("%s-monitoring", ns.Name), true, nil + } + } + return "", false, nil +} + func toEmbeddedPanel(p *sdk.Panel, grafanaHost string, d openvizapi.GrafanaDashboard, refreshInterval string, timeRange *uiapi.TimeRange, req *uiapi.DashboardRequest, panelMap map[string]int) (*uiapi.PanelLinkResponse, error) { includePanel := func(title string) bool { if len(panelMap) == 0 { diff --git a/vendor/kmodules.xyz/client-go/api/v1/cluster.go b/vendor/kmodules.xyz/client-go/api/v1/cluster.go index ca9579cff..079bceb34 100644 --- a/vendor/kmodules.xyz/client-go/api/v1/cluster.go +++ b/vendor/kmodules.xyz/client-go/api/v1/cluster.go @@ -48,8 +48,13 @@ const ( ClusterDisplayNameKey string = "cluster.appscode.com/display-name" ClusterProviderNameKey string = "cluster.appscode.com/provider" + AceOrgIDKey string = "ace.appscode.com/org-id" ClientOrgKey string = "ace.appscode.com/client-org" ClientKeyPrefix string = "client.ace.appscode.com/" + + ClusterClaimKeyID string = "id.k8s.io" + ClusterClaimKeyInfo string = "cluster.ace.info" + ClusterClaimKeyFeatures string = "features.ace.info" ) type ClusterMetadata struct { @@ -186,3 +191,13 @@ const ( CAPIProviderCAPZ CAPIProvider = "capz" CAPIProviderCAPH CAPIProvider = "caph" ) + +type ClusterClaimInfo struct { + ClusterMetadata ClusterInfo `json:"clusterMetadata"` +} + +type ClusterClaimFeatures struct { + EnabledFeatures []string `json:"enabledFeatures,omitempty"` + ExternallyManagedFeatures []string `json:"externallyManagedFeatures,omitempty"` + DisabledFeatures []string `json:"disabledFeatures,omitempty"` +} diff --git a/vendor/kmodules.xyz/client-go/api/v1/zz_generated.deepcopy.go b/vendor/kmodules.xyz/client-go/api/v1/zz_generated.deepcopy.go index f44550fbd..dcf3b711c 100644 --- a/vendor/kmodules.xyz/client-go/api/v1/zz_generated.deepcopy.go +++ b/vendor/kmodules.xyz/client-go/api/v1/zz_generated.deepcopy.go @@ -119,6 +119,54 @@ func (in *CertificateSpec) DeepCopy() *CertificateSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterClaimFeatures) DeepCopyInto(out *ClusterClaimFeatures) { + *out = *in + if in.EnabledFeatures != nil { + in, out := &in.EnabledFeatures, &out.EnabledFeatures + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ExternallyManagedFeatures != nil { + in, out := &in.ExternallyManagedFeatures, &out.ExternallyManagedFeatures + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.DisabledFeatures != nil { + in, out := &in.DisabledFeatures, &out.DisabledFeatures + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterClaimFeatures. +func (in *ClusterClaimFeatures) DeepCopy() *ClusterClaimFeatures { + if in == nil { + return nil + } + out := new(ClusterClaimFeatures) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterClaimInfo) DeepCopyInto(out *ClusterClaimInfo) { + *out = *in + in.ClusterMetadata.DeepCopyInto(&out.ClusterMetadata) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterClaimInfo. +func (in *ClusterClaimInfo) DeepCopy() *ClusterClaimInfo { + if in == nil { + return nil + } + out := new(ClusterClaimInfo) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterInfo) DeepCopyInto(out *ClusterInfo) { *out = *in diff --git a/vendor/kmodules.xyz/monitoring-agent-api/api/v1/appbinding.go b/vendor/kmodules.xyz/monitoring-agent-api/api/v1/appbinding.go index ea27ea10d..5a6d1440b 100644 --- a/vendor/kmodules.xyz/monitoring-agent-api/api/v1/appbinding.go +++ b/vendor/kmodules.xyz/monitoring-agent-api/api/v1/appbinding.go @@ -61,10 +61,11 @@ type TLSConfig struct { } type PrometheusContext struct { - HubUID string `json:"hubUID,omitempty"` - ClusterUID string `json:"clusterUID"` - ProjectId string `json:"projectId,omitempty"` - Default bool `json:"default"` + HubUID string `json:"hubUID,omitempty"` + ClusterUID string `json:"clusterUID"` + ProjectId string `json:"projectId,omitempty"` + Default bool `json:"default"` + ClientOrgID string `json:"clientOrgID,omitempty"` } type GrafanaContext struct { diff --git a/vendor/kmodules.xyz/monitoring-agent-api/api/v1/openapi_generated.go b/vendor/kmodules.xyz/monitoring-agent-api/api/v1/openapi_generated.go index a98bb2c93..464c2914d 100644 --- a/vendor/kmodules.xyz/monitoring-agent-api/api/v1/openapi_generated.go +++ b/vendor/kmodules.xyz/monitoring-agent-api/api/v1/openapi_generated.go @@ -389,6 +389,12 @@ func schema_kmodulesxyz_monitoring_agent_api_api_v1_PrometheusContext(ref common Format: "", }, }, + "clientOrgID": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, }, Required: []string{"clusterUID", "default"}, }, diff --git a/vendor/modules.txt b/vendor/modules.txt index df6f2c05a..670b5b86d 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1414,7 +1414,7 @@ kmodules.xyz/authorizer/apiserver kmodules.xyz/authorizer/rbac kmodules.xyz/authorizer/rbac/helpers kmodules.xyz/authorizer/rbac/validation -# kmodules.xyz/client-go v0.30.32 +# kmodules.xyz/client-go v0.30.37 ## explicit; go 1.22.0 kmodules.xyz/client-go kmodules.xyz/client-go/api/v1 @@ -1435,7 +1435,7 @@ kmodules.xyz/client-go/tools/clientcmd kmodules.xyz/custom-resources/apis/appcatalog kmodules.xyz/custom-resources/apis/appcatalog/v1alpha1 kmodules.xyz/custom-resources/crds -# kmodules.xyz/monitoring-agent-api v0.30.2 +# kmodules.xyz/monitoring-agent-api v0.30.3 ## explicit; go 1.22.0 kmodules.xyz/monitoring-agent-api/api/v1 # moul.io/http2curl/v2 v2.3.1-0.20221024080105-10c404f653f7