K8s: fix standalone command and add hack scripts (#79052)

Co-authored-by: Charandas Batra <charandas.batra@grafana.com>
This commit is contained in:
Ryan McKinley 2023-12-05 14:31:49 -08:00 committed by GitHub
parent 66df17869d
commit 439edebcd6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 307 additions and 81 deletions

1
.github/CODEOWNERS vendored
View File

@ -65,6 +65,7 @@
/.golangci.toml @grafana/backend-platform
/build.go @grafana/backend-platform
/scripts/modowners/ @grafana/backend-platform
/hack/ @grafana/grafana-app-platform-squad
/pkg/api/ @grafana/backend-platform
/pkg/apis/ @grafana/grafana-app-platform-squad

3
.gitignore vendored
View File

@ -80,6 +80,9 @@ public/css/*.min.css
apiserver.local.config/
default.etcd/
# kubeconfig path used by example apiserver
example-apiserver/
# devenv
/devenv/docker-compose.yaml
/devenv/docker-compose.override.yaml

10
.vscode/launch.json vendored
View File

@ -11,6 +11,16 @@
"cwd": "${workspaceFolder}",
"args": ["server", "--homepath", "${workspaceFolder}", "--packaging", "dev"]
},
{
"name": "Run API Server (k8s)",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}/pkg/cmd/grafana/",
"env": {},
"cwd": "${workspaceFolder}",
"args": ["apiserver", "example.grafana.app"]
},
{
"name": "Attach to Chrome",
"port": 9222,

View File

@ -143,6 +143,7 @@ Experimental features might be changed or removed without prior notice.
| `enableNativeHTTPHistogram` | Enables native HTTP Histograms |
| `formatString` | Enable format string transformer |
| `kubernetesPlaylists` | Use the kubernetes API in the frontend for playlists, and route /api/playlist requests to k8s |
| `kubernetesSnapshots` | Use the kubernetes API in the frontend to support playlists |
| `recoveryThreshold` | Enables feature recovery threshold (aka hysteresis) for threshold server-side expression |
| `lokiStructuredMetadata` | Enables the loki data source to request structured metadata from the Loki server |
| `teamHttpHeaders` | Enables datasources to apply team headers to the client requests |

25
hack/README.md Normal file
View File

@ -0,0 +1,25 @@
# Kubernetes HACK Alert
This is a hack folder for kubernetes codegen scripts. Oddly, a /hack/ folder seems to be standard kubernetes development practice ¯\_(ツ)\_/¯
The workflow is a WIP, however we are trying to leverage as many off-the-shelf patterns as possible.
For these scripts to work, your local GOROOT/src/grafana/grafana must point to this git checkout. For my setup this is:
```
pwd
/Users/ryan/go/src/github.com/grafana
ls -l
total 0
lrwxr-xr-x 1 ryan staff 37 Oct 5 09:34 grafana -> /Users/ryan/workspace/grafana/grafana
```
The current workflow (sorry!) is to:
1. update the script to point to the group+version you want
2. run the `update-codegen.sh` script. This will produce a bunch of new files
3. move `pkg/generated/openapi/zz_generated.openapi.go` to `pkg/apis/{group/version}/zz_generated.openapi.go`.
4. edit the package name so it is {version} and remove the boilerplate k8s kinds
5. `rm -rf pkg/generated` -- we are not yet using most of the generated client stuff
Once we are more comfortable with the outputs and process, we will build these steps into a more standard codegen pattern, but until then... happy hacking!

2
hack/boilerplate.go.txt Normal file
View File

@ -0,0 +1,2 @@
// SPDX-License-Identifier: AGPL-3.0-only

45
hack/update-codegen.sh Executable file
View File

@ -0,0 +1,45 @@
#!/usr/bin/env bash
# Copyright 2017 The Kubernetes Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -o errexit
set -o nounset
set -o pipefail
SCRIPT_ROOT=$(dirname "${BASH_SOURCE[0]}")/..
CODEGEN_PKG=${CODEGEN_PKG:-$(cd "${SCRIPT_ROOT}"; ls -d -1 ./vendor/k8s.io/code-generator 2>/dev/null || echo $GOPATH/pkg/mod/k8s.io/code-generator@v0.27.1)}
OUTDIR="${HOME}/go/src"
echo $OUTDIR
CLIENTSET_NAME_VERSIONED=clientset \
CLIENTSET_PKG_NAME=clientset \
"${CODEGEN_PKG}/generate-groups.sh" "all" \
github.com/grafana/grafana/pkg/generated \
github.com/grafana/grafana/pkg/apis \
"snapshots:v0alpha1" \
--output-base "${OUTDIR}" \
--go-header-file "${SCRIPT_ROOT}/hack/boilerplate.go.txt"
CLIENTSET_NAME_VERSIONED=clientset \
CLIENTSET_PKG_NAME=clientset \
"${CODEGEN_PKG}/generate-internal-groups.sh" "deepcopy,defaulter,conversion,openapi" \
github.com/grafana/grafana/pkg/generated \
github.com/grafana/grafana/pkg/apis \
github.com/grafana/grafana/pkg/apis \
"snapshots:v0alpha1" \
--output-base "${OUTDIR}" \
--go-header-file "${SCRIPT_ROOT}/hack/boilerplate.go.txt"

View File

@ -135,6 +135,7 @@ export interface FeatureToggles {
formatString?: boolean;
transformationsVariableSupport?: boolean;
kubernetesPlaylists?: boolean;
kubernetesSnapshots?: boolean;
cloudWatchBatchQueries?: boolean;
recoveryThreshold?: boolean;
lokiStructuredMetadata?: boolean;

View File

@ -4,22 +4,45 @@ The example-apiserver closely resembles the
[sample-apiserver](https://github.com/kubernetes/sample-apiserver/tree/master) project in code and thus
allows the same
[CLI flags](https://kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/) as kube-apiserver.
It is currently used for testing our deployment pipelines for aggregated servers.
It is currently used for testing our deployment pipelines for aggregated servers. You can optionally omit the
aggregation path altogether and just run this example apiserver as a standalone process.
## Prerequisites:
## Standalone Mode
### Usage
```shell
go run ./pkg/cmd/grafana apiserver example.grafana.app \
--secure-port 8443
```
### Verify that all works
```shell
export KUBECONFIG=./example-apiserver/kubeconfig
kubectl api-resources
NAME SHORTNAMES APIVERSION NAMESPACED KIND
dummy example.grafana.app/v0alpha1 true DummyResource
runtime example.grafana.app/v0alpha1 false RuntimeInfo
```
## Aggregated Mode
### Prerequisites:
1. kind: you will need kind (or another local K8s setup) if you want to test aggregation.
```
go install sigs.k8s.io/kind@v0.20.0 && kind create cluster
```
## Usage
### Usage
You can start the example-apiserver with an invocation as shown below. The Authn / Authz flags are set up so that the kind cluster
can be used as a root server for this example-apiserver (in aggregated mode). Here, it's assumed that you have a local
kind cluster and that you can provide its kubeconfig in the parameters to the example-apiserver.
```shell
go run ./pkg/cmd/grafana apiserver example.grafana.app\
go run ./pkg/cmd/grafana apiserver example.grafana.app \
--authentication-kubeconfig ~/.kube/config \
--authorization-kubeconfig ~/.kube/config \
--kubeconfig ~/.kube/config \
@ -35,7 +58,7 @@ kubectl deploy -k ./deploy/darwin # or /linux
```
## Verify that all works
### Verify that all works
With kubectl configured against `kind-kind` context, you can run the following:

View File

@ -1,7 +1,6 @@
package apiserver
import (
"fmt"
"os"
"github.com/spf13/cobra"
@ -10,9 +9,7 @@ import (
"k8s.io/component-base/cli"
)
func newCommandStartExampleAPIServer(o *ExampleServerOptions, stopCh <-chan struct{}) *cobra.Command {
// While this exists as an experimental feature, we require adding the scarry looking command line
devAcknowledgementFlag := "grafana-enable-experimental-apiserver"
func newCommandStartExampleAPIServer(o *APIServerOptions, stopCh <-chan struct{}) *cobra.Command {
devAcknowledgementNotice := "The apiserver command is in heavy development. The entire setup is subject to change without notice"
cmd := &cobra.Command{
@ -20,22 +17,14 @@ func newCommandStartExampleAPIServer(o *ExampleServerOptions, stopCh <-chan stru
Short: "Run the grafana apiserver",
Long: "Run a standalone kubernetes based apiserver that can be aggregated by a root apiserver. " +
devAcknowledgementNotice,
Example: fmt.Sprintf("grafana apiserver example.grafana.app --%s", devAcknowledgementFlag),
PersistentPreRun: func(cmd *cobra.Command, args []string) {
ok, err := cmd.Flags().GetBool(devAcknowledgementFlag)
if !ok || err != nil {
fmt.Printf("requires running with the flag: --%s\n\n%s\n\n",
devAcknowledgementFlag, devAcknowledgementNotice)
os.Exit(1)
}
},
Example: "grafana apiserver example.grafana.app",
RunE: func(c *cobra.Command, args []string) error {
// Load each group from the args
if err := o.LoadAPIGroupBuilders(args[1:]); err != nil {
return err
}
// Finish the config (applies all defaults)
// Finish the config (a noop for now)
if err := o.Complete(); err != nil {
return err
}
@ -45,16 +34,13 @@ func newCommandStartExampleAPIServer(o *ExampleServerOptions, stopCh <-chan stru
return err
}
if err := o.RunExampleServer(config, stopCh); err != nil {
if err := o.RunAPIServer(config, stopCh); err != nil {
return err
}
return nil
},
}
// Register grafana flags
cmd.PersistentFlags().Bool(devAcknowledgementFlag, false, devAcknowledgementNotice)
// Register standard k8s flags with the command line
o.RecommendedOptions = options.NewRecommendedOptions(
defaultEtcdPathPrefix,
@ -68,7 +54,7 @@ func newCommandStartExampleAPIServer(o *ExampleServerOptions, stopCh <-chan stru
func RunCLI() int {
stopCh := genericapiserver.SetupSignalHandler()
options := newExampleServerOptions(os.Stdout, os.Stderr)
options := newAPIServerOptions(os.Stdout, os.Stderr)
cmd := newCommandStartExampleAPIServer(options, stopCh)
return cli.Run(cmd)

View File

@ -4,21 +4,30 @@ import (
"fmt"
"io"
"net"
"github.com/grafana/grafana/pkg/registry/apis/example"
grafanaAPIServer "github.com/grafana/grafana/pkg/services/grafana-apiserver"
"path"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
openapinamer "k8s.io/apiserver/pkg/endpoints/openapi"
genericapiserver "k8s.io/apiserver/pkg/server"
"k8s.io/apiserver/pkg/server/options"
"k8s.io/apiserver/pkg/util/openapi"
"k8s.io/client-go/tools/clientcmd"
netutils "k8s.io/utils/net"
"github.com/grafana/grafana/pkg/services/grafana-apiserver/utils"
"github.com/grafana/grafana/pkg/registry/apis/example"
grafanaAPIServer "github.com/grafana/grafana/pkg/services/grafana-apiserver"
)
const defaultEtcdPathPrefix = "/registry/example.grafana.app"
const (
defaultEtcdPathPrefix = "/registry/grafana.app"
dataPath = "data/grafana-apiserver" // same as grafana core
)
var (
Scheme = runtime.NewScheme()
@ -41,8 +50,8 @@ func init() {
Scheme.AddUnversionedTypes(unversionedVersion, unversionedTypes...)
}
// ExampleServerOptions contains the state for the apiserver
type ExampleServerOptions struct {
// APIServerOptions contains the state for the apiserver
type APIServerOptions struct {
builders []grafanaAPIServer.APIGroupBuilder
RecommendedOptions *options.RecommendedOptions
AlternateDNS []string
@ -51,20 +60,20 @@ type ExampleServerOptions struct {
StdErr io.Writer
}
func newExampleServerOptions(out, errOut io.Writer) *ExampleServerOptions {
return &ExampleServerOptions{
func newAPIServerOptions(out, errOut io.Writer) *APIServerOptions {
return &APIServerOptions{
StdOut: out,
StdErr: errOut,
}
}
func (o *ExampleServerOptions) LoadAPIGroupBuilders(args []string) error {
func (o *APIServerOptions) LoadAPIGroupBuilders(args []string) error {
o.builders = []grafanaAPIServer.APIGroupBuilder{}
for _, g := range args {
switch g {
// No dependencies for testing
case "example.grafana.app":
o.builders = append(o.builders, &example.TestingAPIBuilder{})
o.builders = append(o.builders, example.NewTestingAPIBuilder())
default:
return fmt.Errorf("unknown group: %s", g)
}
@ -83,8 +92,49 @@ func (o *ExampleServerOptions) LoadAPIGroupBuilders(args []string) error {
return nil
}
func (o *ExampleServerOptions) Config() (*genericapiserver.RecommendedConfig, error) {
if err := o.RecommendedOptions.SecureServing.MaybeDefaultWithSelfSignedCerts("localhost", o.AlternateDNS, []net.IP{netutils.ParseIPSloppy("127.0.0.1")}); err != nil {
// A copy of ApplyTo in recommended.go, but for >= 0.28, server pkg in apiserver does a bit extra causing
// a panic when CoreAPI is set to nil
func (o *APIServerOptions) ModifiedApplyTo(config *genericapiserver.RecommendedConfig) error {
if err := o.RecommendedOptions.Etcd.ApplyTo(&config.Config); err != nil {
return err
}
if err := o.RecommendedOptions.EgressSelector.ApplyTo(&config.Config); err != nil {
return err
}
if err := o.RecommendedOptions.Traces.ApplyTo(config.Config.EgressSelector, &config.Config); err != nil {
return err
}
if err := o.RecommendedOptions.SecureServing.ApplyTo(&config.Config.SecureServing, &config.Config.LoopbackClientConfig); err != nil {
return err
}
if err := o.RecommendedOptions.Authentication.ApplyTo(&config.Config.Authentication, config.SecureServing, config.OpenAPIConfig); err != nil {
return err
}
if err := o.RecommendedOptions.Authorization.ApplyTo(&config.Config.Authorization); err != nil {
return err
}
if err := o.RecommendedOptions.Audit.ApplyTo(&config.Config); err != nil {
return err
}
if err := o.RecommendedOptions.Features.ApplyTo(&config.Config); err != nil {
return err
}
if err := o.RecommendedOptions.CoreAPI.ApplyTo(config); err != nil {
return err
}
_, err := o.RecommendedOptions.ExtraAdmissionInitializers(config)
if err != nil {
return err
}
return nil
}
func (o *APIServerOptions) Config() (*genericapiserver.RecommendedConfig, error) {
if err := o.RecommendedOptions.SecureServing.MaybeDefaultWithSelfSignedCerts(
"localhost", o.AlternateDNS, []net.IP{netutils.ParseIPSloppy("127.0.0.1")},
); err != nil {
return nil, fmt.Errorf("error creating self-signed certificates: %v", err)
}
@ -92,33 +142,55 @@ func (o *ExampleServerOptions) Config() (*genericapiserver.RecommendedConfig, er
o.RecommendedOptions.Authorization.RemoteKubeConfigFileOptional = true
o.RecommendedOptions.Admission = nil
o.RecommendedOptions.CoreAPI = nil
o.RecommendedOptions.Etcd = nil
if o.RecommendedOptions.CoreAPI.CoreAPIKubeconfigPath == "" {
o.RecommendedOptions.CoreAPI = nil
}
serverConfig := genericapiserver.NewRecommendedConfig(Codecs)
if err := o.RecommendedOptions.ApplyTo(serverConfig); err != nil {
return nil, err
if o.RecommendedOptions.CoreAPI == nil {
if err := o.ModifiedApplyTo(serverConfig); err != nil {
return nil, err
}
} else {
if err := o.RecommendedOptions.ApplyTo(serverConfig); err != nil {
return nil, err
}
}
// Add OpenAPI specs for each group+version
defsGetter := grafanaAPIServer.GetOpenAPIDefinitions(o.builders)
serverConfig.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig(
openapi.GetOpenAPIDefinitionsWithoutDisabledFeatures(defsGetter),
openapinamer.NewDefinitionNamer(Scheme))
serverConfig.OpenAPIV3Config = genericapiserver.DefaultOpenAPIV3Config(
openapi.GetOpenAPIDefinitionsWithoutDisabledFeatures(defsGetter),
openapinamer.NewDefinitionNamer(Scheme))
// Add the custom routes to service discovery
serverConfig.OpenAPIV3Config.PostProcessSpec3 = grafanaAPIServer.GetOpenAPIPostProcessor(o.builders)
return serverConfig, nil
}
// Validate validates ExampleServerOptions
// Validate validates APIServerOptions
// NOTE: we don't call validate on the top level recommended options as it doesn't like skipping etcd-servers
// the function is left here for troubleshooting any other config issues
func (o *ExampleServerOptions) Validate(args []string) error {
func (o *APIServerOptions) Validate(args []string) error {
errors := []error{}
errors = append(errors, o.RecommendedOptions.Validate()...)
return utilerrors.NewAggregate(errors)
}
// Complete fills in fields required to have valid data
func (o *ExampleServerOptions) Complete() error {
func (o *APIServerOptions) Complete() error {
return nil
}
func (o *ExampleServerOptions) RunExampleServer(config *genericapiserver.RecommendedConfig, stopCh <-chan struct{}) error {
func (o *APIServerOptions) RunAPIServer(config *genericapiserver.RecommendedConfig, stopCh <-chan struct{}) error {
delegationTarget := genericapiserver.NewEmptyDelegate()
completedConfig := config.Complete()
server, err := completedConfig.New("example-apiserver", delegationTarget)
@ -141,5 +213,15 @@ func (o *ExampleServerOptions) RunExampleServer(config *genericapiserver.Recomme
}
}
// in standalone mode, write the local config to disk
if o.RecommendedOptions.CoreAPI == nil {
if err = clientcmd.WriteToFile(
utils.FormatKubeConfig(server.LoopbackClientConfig),
path.Join(dataPath, "grafana.kubeconfig"),
); err != nil {
return err
}
}
return server.PrepareRun().Run(stopCh)
}

View File

@ -60,6 +60,24 @@ func (j *Json) Value() (driver.Value, error) {
return j.ToDB()
}
// DeepCopyInto creates a copy by serializing JSON
func (j *Json) DeepCopyInto(out *Json) {
b, err := j.Encode()
if err == nil {
_ = out.UnmarshalJSON(b)
}
}
// DeepCopy will make a deep copy of the JSON object
func (j *Json) DeepCopy() *Json {
if j == nil {
return nil
}
out := new(Json)
j.DeepCopyInto(out)
return out
}
// NewJson returns a pointer to a new `Json` object
// after unmarshaling `body` bytes
func NewJson(body []byte) (*Json, error) {

View File

@ -36,14 +36,18 @@ type TestingAPIBuilder struct {
gv schema.GroupVersion
}
func NewTestingAPIBuilder() *TestingAPIBuilder {
return &TestingAPIBuilder{
gv: schema.GroupVersion{Group: GroupName, Version: VersionID},
}
}
func RegisterAPIService(features featuremgmt.FeatureToggles, apiregistration grafanaapiserver.APIRegistrar) *TestingAPIBuilder {
if !features.IsEnabledGlobally(featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs) {
return nil // skip registration unless opting into experimental apis
}
builder := &TestingAPIBuilder{
gv: schema.GroupVersion{Group: GroupName, Version: VersionID},
}
apiregistration.RegisterAPI(builder)
builder := NewTestingAPIBuilder()
apiregistration.RegisterAPI(NewTestingAPIBuilder())
return builder
}

View File

@ -990,6 +990,14 @@ var (
RequiresRestart: true, // changes the API routing
Created: time.Date(2023, time.November, 8, 12, 0, 0, 0, time.UTC),
},
{
Name: "kubernetesSnapshots",
Description: "Use the kubernetes API in the frontend to support playlists",
Stage: FeatureStageExperimental,
Owner: grafanaAppPlatformSquad,
RequiresRestart: true, // changes the API routing
Created: time.Date(2023, time.December, 4, 12, 0, 0, 0, time.UTC),
},
{
Name: "cloudWatchBatchQueries",
Description: "Runs CloudWatch metrics queries as separate batches",

View File

@ -116,6 +116,7 @@ enableNativeHTTPHistogram,experimental,@grafana/hosted-grafana-team,2023-10-03,f
formatString,experimental,@grafana/grafana-bi-squad,2023-10-13,false,false,false,true
transformationsVariableSupport,preview,@grafana/grafana-bi-squad,2023-10-04,false,false,false,true
kubernetesPlaylists,experimental,@grafana/grafana-app-platform-squad,2023-11-08,false,false,true,false
kubernetesSnapshots,experimental,@grafana/grafana-app-platform-squad,2023-12-04,false,false,true,false
cloudWatchBatchQueries,preview,@grafana/aws-datasources,2023-10-20,false,false,false,false
recoveryThreshold,experimental,@grafana/alerting-squad,2023-10-10,false,false,true,false
lokiStructuredMetadata,experimental,@grafana/observability-logs,2023-11-16,false,false,false,false

1 Name Stage Owner Created requiresDevMode RequiresLicense RequiresRestart FrontendOnly
116 formatString experimental @grafana/grafana-bi-squad 2023-10-13 false false false true
117 transformationsVariableSupport preview @grafana/grafana-bi-squad 2023-10-04 false false false true
118 kubernetesPlaylists experimental @grafana/grafana-app-platform-squad 2023-11-08 false false true false
119 kubernetesSnapshots experimental @grafana/grafana-app-platform-squad 2023-12-04 false false true false
120 cloudWatchBatchQueries preview @grafana/aws-datasources 2023-10-20 false false false false
121 recoveryThreshold experimental @grafana/alerting-squad 2023-10-10 false false true false
122 lokiStructuredMetadata experimental @grafana/observability-logs 2023-11-16 false false false false

View File

@ -475,6 +475,10 @@ const (
// Use the kubernetes API in the frontend for playlists, and route /api/playlist requests to k8s
FlagKubernetesPlaylists = "kubernetesPlaylists"
// FlagKubernetesSnapshots
// Use the kubernetes API in the frontend to support playlists
FlagKubernetesSnapshots = "kubernetesSnapshots"
// FlagCloudWatchBatchQueries
// Runs CloudWatch metrics queries as separate batches
FlagCloudWatchBatchQueries = "cloudWatchBatchQueries"

View File

@ -41,7 +41,7 @@ func GetNamespaceMapper(cfg *setting.Cfg) NamespaceMapper {
func NamespaceInfoFrom(ctx context.Context, requireOrgID bool) (NamespaceInfo, error) {
info, err := ParseNamespace(request.NamespaceValue(ctx))
if err == nil && requireOrgID && info.OrgID < 1 {
return info, fmt.Errorf("expected valid orgId")
return info, fmt.Errorf("expected valid orgId in namespace")
}
return info, err
}

View File

@ -9,7 +9,7 @@ import (
)
// This should eventually live in grafana-app-sdk
func getOpenAPIDefinitions(builders []APIGroupBuilder) common.GetOpenAPIDefinitions {
func GetOpenAPIDefinitions(builders []APIGroupBuilder) common.GetOpenAPIDefinitions {
return func(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition {
defs := getStandardOpenAPIDefinitions(ref)
for _, builder := range builders {

View File

@ -139,7 +139,7 @@ func (h *methodNotAllowedHandler) ServeHTTP(w http.ResponseWriter, req *http.Req
// Modify the the OpenAPI spec to include the additional routes.
// Currently this requires: https://github.com/kubernetes/kube-openapi/pull/420
// In future k8s release, the hook will use Config3 rather than the same hook for both v2 and v3
func getOpenAPIPostProcessor(builders []APIGroupBuilder) func(*spec3.OpenAPI) (*spec3.OpenAPI, error) {
func GetOpenAPIPostProcessor(builders []APIGroupBuilder) func(*spec3.OpenAPI) (*spec3.OpenAPI, error) {
return func(s *spec3.OpenAPI) (*spec3.OpenAPI, error) {
if s.Paths == nil {
return s, nil

View File

@ -29,10 +29,11 @@ import (
"k8s.io/client-go/kubernetes/scheme"
clientrest "k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
"k8s.io/component-base/logs"
"k8s.io/klog/v2"
"github.com/grafana/grafana/pkg/services/grafana-apiserver/utils"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/infra/appcontext"
"github.com/grafana/grafana/pkg/infra/tracing"
@ -270,7 +271,7 @@ func (s *service) start(ctx context.Context) error {
serverConfig.TracerProvider = s.tracing.GetTracerProvider()
// Add OpenAPI specs for each group+version
defsGetter := getOpenAPIDefinitions(builders)
defsGetter := GetOpenAPIDefinitions(builders)
serverConfig.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig(
openapi.GetOpenAPIDefinitionsWithoutDisabledFeatures(defsGetter),
openapinamer.NewDefinitionNamer(Scheme, scheme.Scheme))
@ -280,7 +281,7 @@ func (s *service) start(ctx context.Context) error {
openapinamer.NewDefinitionNamer(Scheme, scheme.Scheme))
// Add the custom routes to service discovery
serverConfig.OpenAPIV3Config.PostProcessSpec3 = getOpenAPIPostProcessor(builders)
serverConfig.OpenAPIV3Config.PostProcessSpec3 = GetOpenAPIPostProcessor(builders)
// Set the swagger build versions
serverConfig.OpenAPIConfig.Info.Version = setting.BuildVersion
@ -392,34 +393,10 @@ func (s *service) running(ctx context.Context) error {
}
func (s *service) ensureKubeConfig() error {
clusters := make(map[string]*clientcmdapi.Cluster)
clusters["default-cluster"] = &clientcmdapi.Cluster{
Server: s.restConfig.Host,
InsecureSkipTLSVerify: true,
}
contexts := make(map[string]*clientcmdapi.Context)
contexts["default-context"] = &clientcmdapi.Context{
Cluster: "default-cluster",
Namespace: "default",
AuthInfo: "default",
}
authinfos := make(map[string]*clientcmdapi.AuthInfo)
authinfos["default"] = &clientcmdapi.AuthInfo{
Token: s.restConfig.BearerToken,
}
clientConfig := clientcmdapi.Config{
Kind: "Config",
APIVersion: "v1",
Clusters: clusters,
Contexts: contexts,
CurrentContext: "default-context",
AuthInfos: authinfos,
}
return clientcmd.WriteToFile(clientConfig, path.Join(s.config.dataPath, "grafana.kubeconfig"))
return clientcmd.WriteToFile(
utils.FormatKubeConfig(s.restConfig),
path.Join(s.config.dataPath, "grafana.kubeconfig"),
)
}
type roundTripperFunc struct {

View File

@ -0,0 +1,35 @@
package utils
import (
clientrest "k8s.io/client-go/rest"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
)
func FormatKubeConfig(restConfig *clientrest.Config) clientcmdapi.Config {
clusters := make(map[string]*clientcmdapi.Cluster)
clusters["default-cluster"] = &clientcmdapi.Cluster{
Server: restConfig.Host,
InsecureSkipTLSVerify: true,
}
contexts := make(map[string]*clientcmdapi.Context)
contexts["default-context"] = &clientcmdapi.Context{
Cluster: "default-cluster",
Namespace: "default",
AuthInfo: "default",
}
authinfos := make(map[string]*clientcmdapi.AuthInfo)
authinfos["default"] = &clientcmdapi.AuthInfo{
Token: restConfig.BearerToken,
}
return clientcmdapi.Config{
Kind: "Config",
APIVersion: "v1",
Clusters: clusters,
Contexts: contexts,
CurrentContext: "default-context",
AuthInfos: authinfos,
}
}