mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
K8s: fix standalone command and add hack scripts (#79052)
Co-authored-by: Charandas Batra <charandas.batra@grafana.com>
This commit is contained in:
parent
66df17869d
commit
439edebcd6
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
@ -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
3
.gitignore
vendored
@ -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
10
.vscode/launch.json
vendored
@ -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,
|
||||
|
@ -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
25
hack/README.md
Normal 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
2
hack/boilerplate.go.txt
Normal file
@ -0,0 +1,2 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
45
hack/update-codegen.sh
Executable file
45
hack/update-codegen.sh
Executable 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"
|
@ -135,6 +135,7 @@ export interface FeatureToggles {
|
||||
formatString?: boolean;
|
||||
transformationsVariableSupport?: boolean;
|
||||
kubernetesPlaylists?: boolean;
|
||||
kubernetesSnapshots?: boolean;
|
||||
cloudWatchBatchQueries?: boolean;
|
||||
recoveryThreshold?: boolean;
|
||||
lokiStructuredMetadata?: boolean;
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
|
@ -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"
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
35
pkg/services/grafana-apiserver/utils/clientConfig.go
Normal file
35
pkg/services/grafana-apiserver/utils/clientConfig.go
Normal 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,
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user