| 
									
										
										
										
											2024-02-02 06:27:30 +08:00
										 |  |  | package builder | 
					
						
							| 
									
										
										
										
											2024-01-10 23:30:16 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2024-07-12 04:23:31 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2025-01-07 17:13:08 +08:00
										 |  |  | 	"encoding/csv" | 
					
						
							|  |  |  | 	"encoding/json" | 
					
						
							|  |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2024-01-10 23:30:16 +08:00
										 |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2025-01-07 17:13:08 +08:00
										 |  |  | 	"os" | 
					
						
							| 
									
										
										
										
											2024-05-09 01:35:11 +08:00
										 |  |  | 	"regexp" | 
					
						
							| 
									
										
										
										
											2025-01-07 17:13:08 +08:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2024-01-10 23:30:16 +08:00
										 |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-20 22:53:07 +08:00
										 |  |  | 	"github.com/prometheus/client_golang/prometheus" | 
					
						
							| 
									
										
										
										
											2024-01-31 07:17:14 +08:00
										 |  |  | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 
					
						
							| 
									
										
										
										
											2024-02-02 06:27:30 +08:00
										 |  |  | 	"k8s.io/apimachinery/pkg/runtime" | 
					
						
							| 
									
										
										
										
											2024-07-12 04:23:31 +08:00
										 |  |  | 	"k8s.io/apimachinery/pkg/runtime/schema" | 
					
						
							| 
									
										
										
										
											2024-02-02 06:27:30 +08:00
										 |  |  | 	"k8s.io/apimachinery/pkg/runtime/serializer" | 
					
						
							| 
									
										
										
										
											2024-06-20 15:10:03 +08:00
										 |  |  | 	utilruntime "k8s.io/apimachinery/pkg/util/runtime" | 
					
						
							| 
									
										
										
										
											2024-01-10 23:30:16 +08:00
										 |  |  | 	openapinamer "k8s.io/apiserver/pkg/endpoints/openapi" | 
					
						
							| 
									
										
										
										
											2024-08-28 00:31:40 +08:00
										 |  |  | 	k8srequest "k8s.io/apiserver/pkg/endpoints/request" | 
					
						
							| 
									
										
										
										
											2024-01-10 23:30:16 +08:00
										 |  |  | 	"k8s.io/apiserver/pkg/registry/generic" | 
					
						
							|  |  |  | 	genericapiserver "k8s.io/apiserver/pkg/server" | 
					
						
							|  |  |  | 	"k8s.io/apiserver/pkg/util/openapi" | 
					
						
							| 
									
										
										
										
											2024-02-02 06:27:30 +08:00
										 |  |  | 	k8sscheme "k8s.io/client-go/kubernetes/scheme" | 
					
						
							| 
									
										
										
										
											2024-04-04 05:39:09 +08:00
										 |  |  | 	k8stracing "k8s.io/component-base/tracing" | 
					
						
							| 
									
										
										
										
											2024-09-11 01:07:06 +08:00
										 |  |  | 	"k8s.io/klog/v2" | 
					
						
							| 
									
										
										
										
											2024-01-31 07:17:14 +08:00
										 |  |  | 	"k8s.io/kube-openapi/pkg/common" | 
					
						
							| 
									
										
										
										
											2024-03-02 09:32:59 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/apiserver/endpoints/filters" | 
					
						
							| 
									
										
										
										
											2024-07-12 04:23:31 +08:00
										 |  |  | 	grafanarest "github.com/grafana/grafana/pkg/apiserver/rest" | 
					
						
							| 
									
										
										
										
											2024-08-28 00:31:40 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request" | 
					
						
							| 
									
										
										
										
											2024-06-01 01:29:59 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/apiserver/options" | 
					
						
							| 
									
										
										
										
											2025-02-19 22:50:39 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/storage/legacysql/dualwrite" | 
					
						
							| 
									
										
										
										
											2025-01-06 23:20:35 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/storage/unified/apistore" | 
					
						
							| 
									
										
										
										
											2024-01-10 23:30:16 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-15 10:08:00 +08:00
										 |  |  | type BuildHandlerChainFuncFromBuilders = func([]APIGroupBuilder) BuildHandlerChainFunc | 
					
						
							| 
									
										
										
										
											2024-09-07 05:12:12 +08:00
										 |  |  | type BuildHandlerChainFunc = func(delegateHandler http.Handler, c *genericapiserver.Config) http.Handler | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-15 10:08:00 +08:00
										 |  |  | func ProvideDefaultBuildHandlerChainFuncFromBuilders() BuildHandlerChainFuncFromBuilders { | 
					
						
							|  |  |  | 	return GetDefaultBuildHandlerChainFunc | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-07 05:12:12 +08:00
										 |  |  | // PathRewriters is a temporary hack to make rest.Connecter work with resource level routes (TODO)
 | 
					
						
							|  |  |  | var PathRewriters = []filters.PathRewriter{ | 
					
						
							| 
									
										
										
										
											2024-05-09 01:35:11 +08:00
										 |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2024-06-05 23:47:36 +08:00
										 |  |  | 		Pattern: regexp.MustCompile(`(/apis/scope.grafana.app/v0alpha1/namespaces/.*/)find/(.*)$`), | 
					
						
							| 
									
										
										
										
											2024-05-09 01:35:11 +08:00
										 |  |  | 		ReplaceFunc: func(matches []string) string { | 
					
						
							| 
									
										
										
										
											2024-06-05 23:47:36 +08:00
										 |  |  | 			return matches[1] + matches[2] + "/name" // connector requires a name
 | 
					
						
							| 
									
										
										
										
											2024-05-09 01:35:11 +08:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 	}, | 
					
						
							| 
									
										
										
										
											2024-05-11 02:01:17 +08:00
										 |  |  | 	{ | 
					
						
							|  |  |  | 		Pattern: regexp.MustCompile(`(/apis/query.grafana.app/v0alpha1/namespaces/.*/query$)`), | 
					
						
							|  |  |  | 		ReplaceFunc: func(matches []string) string { | 
					
						
							|  |  |  | 			return matches[1] + "/name" // connector requires a name
 | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	}, | 
					
						
							| 
									
										
										
										
											2024-09-25 21:10:19 +08:00
										 |  |  | 	{ | 
					
						
							|  |  |  | 		Pattern: regexp.MustCompile(`(/apis/.*/v0alpha1/namespaces/.*/queryconvert$)`), | 
					
						
							|  |  |  | 		ReplaceFunc: func(matches []string) string { | 
					
						
							|  |  |  | 			return matches[1] + "/name" // connector requires a name
 | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	}, | 
					
						
							| 
									
										
										
										
											2024-05-09 01:35:11 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-15 10:08:00 +08:00
										 |  |  | func GetDefaultBuildHandlerChainFunc(builders []APIGroupBuilder) BuildHandlerChainFunc { | 
					
						
							| 
									
										
										
										
											2024-09-07 05:12:12 +08:00
										 |  |  | 	return func(delegateHandler http.Handler, c *genericapiserver.Config) http.Handler { | 
					
						
							|  |  |  | 		requestHandler, err := GetCustomRoutesHandler( | 
					
						
							|  |  |  | 			delegateHandler, | 
					
						
							|  |  |  | 			c.LoopbackClientConfig, | 
					
						
							|  |  |  | 			builders) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			panic(fmt.Sprintf("could not build the request handler for specified API builders: %s", err.Error())) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Needs to run last in request chain to function as expected, hence we register it first.
 | 
					
						
							|  |  |  | 		handler := filters.WithTracingHTTPLoggingAttributes(requestHandler) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// filters.WithRequester needs to be after the K8s chain because it depends on the K8s user in context
 | 
					
						
							|  |  |  | 		handler = filters.WithRequester(handler) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Call DefaultBuildHandlerChain on the main entrypoint http.Handler
 | 
					
						
							|  |  |  | 		// See https://github.com/kubernetes/apiserver/blob/v0.28.0/pkg/server/config.go#L906
 | 
					
						
							|  |  |  | 		// DefaultBuildHandlerChain provides many things, notably CORS, HSTS, cache-control, authz and latency tracking
 | 
					
						
							|  |  |  | 		handler = genericapiserver.DefaultBuildHandlerChain(handler, c) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		handler = filters.WithAcceptHeader(handler) | 
					
						
							|  |  |  | 		handler = filters.WithPathRewriters(handler, PathRewriters) | 
					
						
							|  |  |  | 		handler = k8stracing.WithTracing(handler, c.TracerProvider, "KubernetesAPI") | 
					
						
							| 
									
										
										
										
											2024-10-02 23:07:31 +08:00
										 |  |  | 		handler = filters.WithExtractJaegerTrace(handler) | 
					
						
							| 
									
										
										
										
											2024-09-07 05:12:12 +08:00
										 |  |  | 		// Configure filters.WithPanicRecovery to not crash on panic
 | 
					
						
							|  |  |  | 		utilruntime.ReallyCrash = false | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return handler | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-02 06:27:30 +08:00
										 |  |  | func SetupConfig( | 
					
						
							|  |  |  | 	scheme *runtime.Scheme, | 
					
						
							|  |  |  | 	serverConfig *genericapiserver.RecommendedConfig, | 
					
						
							|  |  |  | 	builders []APIGroupBuilder, | 
					
						
							| 
									
										
										
										
											2024-02-24 04:15:43 +08:00
										 |  |  | 	buildTimestamp int64, | 
					
						
							|  |  |  | 	buildVersion string, | 
					
						
							|  |  |  | 	buildCommit string, | 
					
						
							|  |  |  | 	buildBranch string, | 
					
						
							| 
									
										
										
										
											2025-02-15 10:08:00 +08:00
										 |  |  | 	buildHandlerChainFuncFromBuilders BuildHandlerChainFuncFromBuilders, | 
					
						
							| 
									
										
										
										
											2024-02-02 06:27:30 +08:00
										 |  |  | ) error { | 
					
						
							| 
									
										
										
										
											2024-10-28 23:40:25 +08:00
										 |  |  | 	serverConfig.AdmissionControl = NewAdmissionFromBuilders(builders) | 
					
						
							| 
									
										
										
										
											2024-01-10 23:30:16 +08:00
										 |  |  | 	defsGetter := GetOpenAPIDefinitions(builders) | 
					
						
							|  |  |  | 	serverConfig.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig( | 
					
						
							|  |  |  | 		openapi.GetOpenAPIDefinitionsWithoutDisabledFeatures(defsGetter), | 
					
						
							| 
									
										
										
										
											2024-02-02 06:27:30 +08:00
										 |  |  | 		openapinamer.NewDefinitionNamer(scheme, k8sscheme.Scheme)) | 
					
						
							| 
									
										
										
										
											2024-01-10 23:30:16 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	serverConfig.OpenAPIV3Config = genericapiserver.DefaultOpenAPIV3Config( | 
					
						
							|  |  |  | 		openapi.GetOpenAPIDefinitionsWithoutDisabledFeatures(defsGetter), | 
					
						
							| 
									
										
										
										
											2024-02-02 06:27:30 +08:00
										 |  |  | 		openapinamer.NewDefinitionNamer(scheme, k8sscheme.Scheme)) | 
					
						
							| 
									
										
										
										
											2024-01-10 23:30:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-02 06:27:30 +08:00
										 |  |  | 	// Add the custom routes to service discovery
 | 
					
						
							| 
									
										
										
										
											2024-02-24 04:15:43 +08:00
										 |  |  | 	serverConfig.OpenAPIV3Config.PostProcessSpec = getOpenAPIPostProcessor(buildVersion, builders) | 
					
						
							| 
									
										
										
										
											2024-01-31 07:17:14 +08:00
										 |  |  | 	serverConfig.OpenAPIV3Config.GetOperationIDAndTagsFromRoute = func(r common.Route) (string, []string, error) { | 
					
						
							| 
									
										
										
										
											2025-01-07 17:13:08 +08:00
										 |  |  | 		meta := r.Metadata() | 
					
						
							|  |  |  | 		kind := "" | 
					
						
							|  |  |  | 		action := "" | 
					
						
							|  |  |  | 		sub := "" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-31 07:17:14 +08:00
										 |  |  | 		tags := []string{} | 
					
						
							| 
									
										
										
										
											2025-01-07 17:13:08 +08:00
										 |  |  | 		prop, ok := meta["x-kubernetes-group-version-kind"] | 
					
						
							| 
									
										
										
										
											2024-01-31 07:17:14 +08:00
										 |  |  | 		if ok { | 
					
						
							|  |  |  | 			gvk, ok := prop.(metav1.GroupVersionKind) | 
					
						
							|  |  |  | 			if ok && gvk.Kind != "" { | 
					
						
							| 
									
										
										
										
											2025-01-07 17:13:08 +08:00
										 |  |  | 				kind = gvk.Kind | 
					
						
							| 
									
										
										
										
											2024-01-31 07:17:14 +08:00
										 |  |  | 				tags = append(tags, gvk.Kind) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2025-01-07 17:13:08 +08:00
										 |  |  | 		prop, ok = meta["x-kubernetes-action"] | 
					
						
							|  |  |  | 		if ok { | 
					
						
							|  |  |  | 			action = fmt.Sprintf("%v", prop) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		isNew := false | 
					
						
							|  |  |  | 		if _, err := os.Stat("test.csv"); errors.Is(err, os.ErrNotExist) { | 
					
						
							|  |  |  | 			isNew = true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if action == "connect" { | 
					
						
							|  |  |  | 			idx := strings.LastIndex(r.Path(), "/{name}/") | 
					
						
							|  |  |  | 			if idx > 0 { | 
					
						
							|  |  |  | 				sub = r.Path()[(idx + len("/{name}/")):] | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		operationAlt := r.OperationName() | 
					
						
							|  |  |  | 		if action != "" { | 
					
						
							|  |  |  | 			if action == "connect" { | 
					
						
							|  |  |  | 				idx := strings.Index(r.OperationName(), "Namespaced") | 
					
						
							|  |  |  | 				if idx > 0 { | 
					
						
							|  |  |  | 					operationAlt = strings.ToLower(r.Method()) + | 
					
						
							|  |  |  | 						r.OperationName()[idx:] | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		operationAlt = strings.ReplaceAll(operationAlt, "Namespaced", "") | 
					
						
							|  |  |  | 		if strings.HasPrefix(operationAlt, "post") { | 
					
						
							|  |  |  | 			operationAlt = "create" + operationAlt[len("post"):] | 
					
						
							|  |  |  | 		} else if strings.HasPrefix(operationAlt, "read") { | 
					
						
							|  |  |  | 			operationAlt = "get" + operationAlt[len("read"):] | 
					
						
							|  |  |  | 		} else if strings.HasPrefix(operationAlt, "patch") { | 
					
						
							|  |  |  | 			operationAlt = "update" + operationAlt[len("patch"):] | 
					
						
							| 
									
										
										
										
											2025-03-25 15:59:03 +08:00
										 |  |  | 		} else if strings.HasPrefix(operationAlt, "put") { | 
					
						
							|  |  |  | 			operationAlt = "replace" + operationAlt[len("put"):] | 
					
						
							| 
									
										
										
										
											2025-01-07 17:13:08 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Audit our options here
 | 
					
						
							|  |  |  | 		if false { | 
					
						
							|  |  |  | 			// Safe to ignore G304 -- this will be removed before merging to main, and just helps audit the conversion
 | 
					
						
							|  |  |  | 			// nolint:gosec
 | 
					
						
							|  |  |  | 			f, err := os.OpenFile("test.csv", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				fmt.Printf("ERROR: %s\n", err) | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				metastr, _ := json.Marshal(meta) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				prop, ok = meta["x-kubernetes-group-version-kind"] | 
					
						
							|  |  |  | 				if ok { | 
					
						
							|  |  |  | 					gvk, ok := prop.(metav1.GroupVersionKind) | 
					
						
							|  |  |  | 					if ok { | 
					
						
							|  |  |  | 						kind = gvk.Kind | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				w := csv.NewWriter(f) | 
					
						
							|  |  |  | 				if isNew { | 
					
						
							|  |  |  | 					_ = w.Write([]string{ | 
					
						
							|  |  |  | 						"#Path", | 
					
						
							|  |  |  | 						"Method", | 
					
						
							|  |  |  | 						"action", | 
					
						
							|  |  |  | 						"kind", | 
					
						
							|  |  |  | 						"sub", | 
					
						
							|  |  |  | 						"OperationName", | 
					
						
							|  |  |  | 						"OperationNameAlt", | 
					
						
							|  |  |  | 						"Description", | 
					
						
							|  |  |  | 						"metadata", | 
					
						
							|  |  |  | 					}) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				_ = w.Write([]string{ | 
					
						
							|  |  |  | 					r.Path(), | 
					
						
							|  |  |  | 					r.Method(), | 
					
						
							|  |  |  | 					action, | 
					
						
							|  |  |  | 					kind, | 
					
						
							|  |  |  | 					sub, | 
					
						
							|  |  |  | 					r.OperationName(), | 
					
						
							|  |  |  | 					operationAlt, | 
					
						
							|  |  |  | 					r.Description(), | 
					
						
							|  |  |  | 					string(metastr), | 
					
						
							|  |  |  | 				}) | 
					
						
							|  |  |  | 				w.Flush() | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return operationAlt, tags, nil | 
					
						
							| 
									
										
										
										
											2024-01-31 07:17:14 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-10 23:30:16 +08:00
										 |  |  | 	// Set the swagger build versions
 | 
					
						
							| 
									
										
										
										
											2025-04-17 15:30:35 +08:00
										 |  |  | 	serverConfig.OpenAPIConfig.Info.Title = "Grafana API Server" | 
					
						
							| 
									
										
										
										
											2024-02-24 04:15:43 +08:00
										 |  |  | 	serverConfig.OpenAPIConfig.Info.Version = buildVersion | 
					
						
							|  |  |  | 	serverConfig.OpenAPIV3Config.Info.Version = buildVersion | 
					
						
							| 
									
										
										
										
											2024-01-10 23:30:16 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	serverConfig.SkipOpenAPIInstallation = false | 
					
						
							| 
									
										
										
										
											2025-02-15 10:08:00 +08:00
										 |  |  | 	serverConfig.BuildHandlerChainFunc = buildHandlerChainFuncFromBuilders(builders) | 
					
						
							| 
									
										
										
										
											2024-01-10 23:30:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-28 05:13:18 +08:00
										 |  |  | 	serverConfig.EffectiveVersion = getEffectiveVersion(buildTimestamp, buildVersion, buildCommit, buildBranch) | 
					
						
							| 
									
										
										
										
											2025-02-28 23:39:41 +08:00
										 |  |  | 	// set priority for aggregated discovery
 | 
					
						
							|  |  |  | 	for i, b := range builders { | 
					
						
							|  |  |  | 		gvs := GetGroupVersions(b) | 
					
						
							|  |  |  | 		if len(gvs) == 0 { | 
					
						
							|  |  |  | 			return fmt.Errorf("builder did not return any API group versions: %T", b) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		pvs := scheme.PrioritizedVersionsForGroup(gvs[0].Group) | 
					
						
							|  |  |  | 		for j, gv := range pvs { | 
					
						
							|  |  |  | 			serverConfig.AggregatedDiscoveryGroupManager.SetGroupVersionPriority(metav1.GroupVersion(gv), 15000+i, len(pvs)-j) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-12 01:13:42 +08:00
										 |  |  | 	if err := AddPostStartHooks(serverConfig, builders); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-10 23:30:16 +08:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-12 04:23:31 +08:00
										 |  |  | type ServerLockService interface { | 
					
						
							|  |  |  | 	LockExecuteAndRelease(ctx context.Context, actionName string, maxInterval time.Duration, fn func(ctx context.Context)) error | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-28 00:31:40 +08:00
										 |  |  | func getRequestInfo(gr schema.GroupResource, namespaceMapper request.NamespaceMapper) *k8srequest.RequestInfo { | 
					
						
							|  |  |  | 	return &k8srequest.RequestInfo{ | 
					
						
							|  |  |  | 		APIGroup:  gr.Group, | 
					
						
							|  |  |  | 		Resource:  gr.Resource, | 
					
						
							|  |  |  | 		Name:      "", | 
					
						
							|  |  |  | 		Namespace: namespaceMapper(int64(1)), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-02 06:27:30 +08:00
										 |  |  | func InstallAPIs( | 
					
						
							|  |  |  | 	scheme *runtime.Scheme, | 
					
						
							|  |  |  | 	codecs serializer.CodecFactory, | 
					
						
							|  |  |  | 	server *genericapiserver.GenericAPIServer, | 
					
						
							| 
									
										
										
										
											2024-01-10 23:30:16 +08:00
										 |  |  | 	optsGetter generic.RESTOptionsGetter, | 
					
						
							|  |  |  | 	builders []APIGroupBuilder, | 
					
						
							| 
									
										
										
										
											2024-06-01 01:29:59 +08:00
										 |  |  | 	storageOpts *options.StorageOptions, | 
					
						
							| 
									
										
										
										
											2024-06-14 17:01:49 +08:00
										 |  |  | 	reg prometheus.Registerer, | 
					
						
							| 
									
										
										
										
											2024-08-28 00:31:40 +08:00
										 |  |  | 	namespaceMapper request.NamespaceMapper, | 
					
						
							| 
									
										
										
										
											2024-07-12 04:23:31 +08:00
										 |  |  | 	kvStore grafanarest.NamespacedKVStore, | 
					
						
							|  |  |  | 	serverLock ServerLockService, | 
					
						
							| 
									
										
										
										
											2025-02-19 22:50:39 +08:00
										 |  |  | 	dualWriteService dualwrite.Service, | 
					
						
							| 
									
										
										
										
											2024-11-09 13:09:46 +08:00
										 |  |  | 	optsregister apistore.StorageOptionsRegister, | 
					
						
							| 
									
										
										
										
											2024-01-10 23:30:16 +08:00
										 |  |  | ) error { | 
					
						
							| 
									
										
										
										
											2024-06-01 01:29:59 +08:00
										 |  |  | 	// dual writing is only enabled when the storage type is not legacy.
 | 
					
						
							|  |  |  | 	// this is needed to support setting a default RESTOptionsGetter for new APIs that don't
 | 
					
						
							|  |  |  | 	// support the legacy storage type.
 | 
					
						
							| 
									
										
										
										
											2024-07-12 04:23:31 +08:00
										 |  |  | 	var dualWrite grafanarest.DualWriteBuilder | 
					
						
							| 
									
										
										
										
											2025-05-12 15:18:06 +08:00
										 |  |  | 	metrics := newBuilderMetrics(reg) | 
					
						
							| 
									
										
										
										
											2024-12-17 22:00:35 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// nolint:staticcheck
 | 
					
						
							| 
									
										
										
										
											2024-07-12 04:23:31 +08:00
										 |  |  | 	if storageOpts.StorageType != options.StorageTypeLegacy { | 
					
						
							| 
									
										
										
										
											2025-02-27 18:27:28 +08:00
										 |  |  | 		dualWrite = func(gr schema.GroupResource, legacy grafanarest.Storage, storage grafanarest.Storage) (grafanarest.Storage, error) { | 
					
						
							| 
									
										
										
										
											2025-02-19 22:50:39 +08:00
										 |  |  | 			// Dashboards + Folders may be managed (depends on feature toggles and database state)
 | 
					
						
							|  |  |  | 			if dualWriteService != nil && dualWriteService.ShouldManage(gr) { | 
					
						
							|  |  |  | 				return dualWriteService.NewStorage(gr, legacy, storage) // eventually this can replace this whole function
 | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-12 04:23:31 +08:00
										 |  |  | 			key := gr.String() // ${resource}.{group} eg playlists.playlist.grafana.app
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// Get the option from custom.ini/command line
 | 
					
						
							|  |  |  | 			// when missing this will default to mode zero (legacy only)
 | 
					
						
							| 
									
										
										
										
											2024-08-30 17:59:42 +08:00
										 |  |  | 			var mode = grafanarest.DualWriterMode(0) | 
					
						
							| 
									
										
										
										
											2024-09-25 03:03:15 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-13 04:41:01 +08:00
										 |  |  | 			var ( | 
					
						
							|  |  |  | 				dualWriterPeriodicDataSyncJobEnabled bool | 
					
						
							| 
									
										
										
										
											2025-04-25 19:43:35 +08:00
										 |  |  | 				dualWriterMigrationDataSyncDisabled  bool | 
					
						
							| 
									
										
										
										
											2024-12-13 04:41:01 +08:00
										 |  |  | 				dataSyncerInterval                   = time.Hour | 
					
						
							|  |  |  | 				dataSyncerRecordsLimit               = 1000 | 
					
						
							|  |  |  | 			) | 
					
						
							| 
									
										
										
										
											2024-09-25 03:03:15 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-30 17:59:42 +08:00
										 |  |  | 			resourceConfig, resourceExists := storageOpts.UnifiedStorageConfig[key] | 
					
						
							|  |  |  | 			if resourceExists { | 
					
						
							|  |  |  | 				mode = resourceConfig.DualWriterMode | 
					
						
							| 
									
										
										
										
											2024-09-25 03:03:15 +08:00
										 |  |  | 				dualWriterPeriodicDataSyncJobEnabled = resourceConfig.DualWriterPeriodicDataSyncJobEnabled | 
					
						
							| 
									
										
										
										
											2025-04-25 19:43:35 +08:00
										 |  |  | 				dualWriterMigrationDataSyncDisabled = resourceConfig.DualWriterMigrationDataSyncDisabled | 
					
						
							| 
									
										
										
										
											2024-12-13 04:41:01 +08:00
										 |  |  | 				dataSyncerInterval = resourceConfig.DataSyncerInterval | 
					
						
							|  |  |  | 				dataSyncerRecordsLimit = resourceConfig.DataSyncerRecordsLimit | 
					
						
							| 
									
										
										
										
											2024-08-30 17:59:42 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2024-07-12 04:23:31 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-11 01:07:06 +08:00
										 |  |  | 			// Force using storage only -- regardless of internal synchronization state
 | 
					
						
							|  |  |  | 			if mode == grafanarest.Mode5 { | 
					
						
							|  |  |  | 				return storage, nil | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-28 00:31:40 +08:00
										 |  |  | 			// TODO: inherited context from main Grafana process
 | 
					
						
							|  |  |  | 			ctx := context.Background() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-12 04:23:31 +08:00
										 |  |  | 			// Moving from one version to the next can only happen after the previous step has
 | 
					
						
							|  |  |  | 			// successfully synchronized.
 | 
					
						
							| 
									
										
										
										
											2024-09-11 01:07:06 +08:00
										 |  |  | 			requestInfo := getRequestInfo(gr, namespaceMapper) | 
					
						
							| 
									
										
										
										
											2024-12-13 04:41:01 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			syncerCfg := &grafanarest.SyncerConfig{ | 
					
						
							|  |  |  | 				Kind:                   key, | 
					
						
							|  |  |  | 				RequestInfo:            requestInfo, | 
					
						
							|  |  |  | 				Mode:                   mode, | 
					
						
							| 
									
										
										
										
											2025-04-25 19:43:35 +08:00
										 |  |  | 				SkipDataSync:           dualWriterMigrationDataSyncDisabled, | 
					
						
							| 
									
										
										
										
											2024-12-13 04:41:01 +08:00
										 |  |  | 				LegacyStorage:          legacy, | 
					
						
							|  |  |  | 				Storage:                storage, | 
					
						
							|  |  |  | 				ServerLockService:      serverLock, | 
					
						
							|  |  |  | 				DataSyncerInterval:     dataSyncerInterval, | 
					
						
							|  |  |  | 				DataSyncerRecordsLimit: dataSyncerRecordsLimit, | 
					
						
							|  |  |  | 				Reg:                    reg, | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// This also sets the currentMode on the syncer config.
 | 
					
						
							|  |  |  | 			currentMode, err := grafanarest.SetDualWritingMode(ctx, kvStore, syncerCfg) | 
					
						
							| 
									
										
										
										
											2024-07-12 04:23:31 +08:00
										 |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return nil, err | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2025-05-12 15:18:06 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			metrics.recordDualWriterModes(gr.Resource, gr.Group, mode, currentMode) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-29 19:57:44 +08:00
										 |  |  | 			switch currentMode { | 
					
						
							|  |  |  | 			case grafanarest.Mode0: | 
					
						
							| 
									
										
										
										
											2024-07-12 04:23:31 +08:00
										 |  |  | 				return legacy, nil | 
					
						
							| 
									
										
										
										
											2024-09-11 01:07:06 +08:00
										 |  |  | 			case grafanarest.Mode4, grafanarest.Mode5: | 
					
						
							| 
									
										
										
										
											2024-07-29 19:57:44 +08:00
										 |  |  | 				return storage, nil | 
					
						
							|  |  |  | 			default: | 
					
						
							| 
									
										
										
										
											2024-07-12 04:23:31 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2025-05-12 15:18:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-25 03:03:15 +08:00
										 |  |  | 			if dualWriterPeriodicDataSyncJobEnabled { | 
					
						
							| 
									
										
										
										
											2024-12-13 04:41:01 +08:00
										 |  |  | 				// The mode might have changed in SetDualWritingMode, so apply current mode first.
 | 
					
						
							|  |  |  | 				syncerCfg.Mode = currentMode | 
					
						
							|  |  |  | 				if err := grafanarest.StartPeriodicDataSyncer(ctx, syncerCfg); err != nil { | 
					
						
							|  |  |  | 					return nil, err | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2024-09-11 01:07:06 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// when unable to use
 | 
					
						
							|  |  |  | 			if currentMode != mode { | 
					
						
							|  |  |  | 				klog.Warningf("Requested DualWrite mode: %d, but using %d for %+v", mode, currentMode, gr) | 
					
						
							| 
									
										
										
										
											2024-08-28 00:31:40 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2025-03-02 02:26:14 +08:00
										 |  |  | 			return dualwrite.NewDualWriter(gr, currentMode, legacy, storage) | 
					
						
							| 
									
										
										
										
											2024-07-12 04:23:31 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-06-01 01:29:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-24 10:07:52 +08:00
										 |  |  | 	// NOTE: we build a map structure by version only for the purposes of InstallAPIGroup
 | 
					
						
							|  |  |  | 	// in other places, working with a flat []APIGroupBuilder list is much nicer
 | 
					
						
							|  |  |  | 	buildersGroupMap := make(map[string][]APIGroupBuilder, 0) | 
					
						
							| 
									
										
										
										
											2024-01-10 23:30:16 +08:00
										 |  |  | 	for _, b := range builders { | 
					
						
							| 
									
										
										
										
											2025-02-15 01:29:43 +08:00
										 |  |  | 		group, err := getGroup(b) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-09-24 10:07:52 +08:00
										 |  |  | 		if _, ok := buildersGroupMap[group]; !ok { | 
					
						
							|  |  |  | 			buildersGroupMap[group] = make([]APIGroupBuilder, 0) | 
					
						
							| 
									
										
										
										
											2024-01-10 23:30:16 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-09-24 10:07:52 +08:00
										 |  |  | 		buildersGroupMap[group] = append(buildersGroupMap[group], b) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for group, buildersForGroup := range buildersGroupMap { | 
					
						
							|  |  |  | 		g := genericapiserver.NewDefaultAPIGroupInfo(group, scheme, metav1.ParameterCodec, codecs) | 
					
						
							|  |  |  | 		for _, b := range buildersForGroup { | 
					
						
							| 
									
										
										
										
											2024-10-15 12:46:08 +08:00
										 |  |  | 			if err := b.UpdateAPIGroupInfo(&g, APIGroupOptions{ | 
					
						
							| 
									
										
										
										
											2025-04-08 16:50:35 +08:00
										 |  |  | 				Scheme:              scheme, | 
					
						
							|  |  |  | 				OptsGetter:          optsGetter, | 
					
						
							|  |  |  | 				DualWriteBuilder:    dualWrite, | 
					
						
							|  |  |  | 				MetricsRegister:     reg, | 
					
						
							|  |  |  | 				StorageOptsRegister: optsregister, | 
					
						
							|  |  |  | 				StorageOpts:         storageOpts, | 
					
						
							| 
									
										
										
										
											2024-10-15 12:46:08 +08:00
										 |  |  | 			}); err != nil { | 
					
						
							| 
									
										
										
										
											2024-09-24 10:07:52 +08:00
										 |  |  | 				return err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if len(g.PrioritizedVersions) < 1 { | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2024-01-10 23:30:16 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-09-24 10:07:52 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		err := server.InstallAPIGroup(&g) | 
					
						
							| 
									
										
										
										
											2024-01-10 23:30:16 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-09-24 10:07:52 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-10 23:30:16 +08:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2024-12-12 01:13:42 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // AddPostStartHooks adds post start hooks to a generic API server config
 | 
					
						
							|  |  |  | func AddPostStartHooks( | 
					
						
							|  |  |  | 	config *genericapiserver.RecommendedConfig, | 
					
						
							|  |  |  | 	builders []APIGroupBuilder, | 
					
						
							|  |  |  | ) error { | 
					
						
							|  |  |  | 	for _, b := range builders { | 
					
						
							|  |  |  | 		hookProvider, ok := b.(APIGroupPostStartHookProvider) | 
					
						
							|  |  |  | 		if !ok { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		hooks, err := hookProvider.GetPostStartHooks() | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		for name, hook := range hooks { | 
					
						
							|  |  |  | 			if err := config.AddPostStartHook(name, hook); err != nil { | 
					
						
							|  |  |  | 				return err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } |