K8s: Use grafana executable for standalone api service (#77904)

Co-authored-by: Dan Cech <dcech@grafana.com>
This commit is contained in:
Ryan McKinley 2023-11-09 16:50:27 -08:00 committed by GitHub
parent 49d6066609
commit d410ef3439
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 133 additions and 94 deletions

2
.github/CODEOWNERS vendored
View File

@ -69,7 +69,7 @@
/pkg/apis/ @grafana/grafana-app-platform-squad
/pkg/bus/ @grafana/backend-platform
/pkg/cmd/ @grafana/backend-platform
/pkg/cmd/grafana-example-apiserver @grafana/grafana-app-platform-squad
/pkg/cmd/grafana/apiserver @grafana/grafana-app-platform-squad
/pkg/components/apikeygen/ @grafana/identity-access-team
/pkg/components/satokengen/ @grafana/identity-access-team
/pkg/components/dashdiffs/ @grafana/backend-platform

2
.gitignore vendored
View File

@ -78,7 +78,7 @@ public/css/*.min.css
/data/*
/bin/*
# any certificates generated by grafana-example-apiserver
# any certificates generated by grafana apiserver
apiserver.local.config/
# devenv

View File

@ -7,7 +7,7 @@ WIRE_TAGS = "oss"
-include local/Makefile
include .bingo/Variables.mk
.PHONY: all deps-go deps-js deps build-go build-backend build-example-apiserver build-server build-cli build-js build build-docker-full build-docker-full-ubuntu lint-go golangci-lint test-go test-js gen-ts test run run-frontend clean devenv devenv-down protobuf drone help gen-go gen-cue fix-cue
.PHONY: all deps-go deps-js deps build-go build-backend build-server build-cli build-js build build-docker-full build-docker-full-ubuntu lint-go golangci-lint test-go test-js gen-ts test run run-frontend clean devenv devenv-down protobuf drone help gen-go gen-cue fix-cue
GO = go
GO_FILES ?= ./pkg/...
@ -132,10 +132,6 @@ build-server: ## Build Grafana server.
@echo "build server"
$(GO) run build.go $(GO_BUILD_FLAGS) build-server
build-example-apiserver: ## Build Grafana example-apiserver application.
@echo "build grafana-cli"
$(GO) run build.go $(GO_BUILD_FLAGS) build-example-apiserver
build-cli: ## Build Grafana CLI application.
@echo "build grafana-cli"
$(GO) run build.go $(GO_BUILD_FLAGS) build-cli

View File

@ -17,13 +17,12 @@ const (
GoOSWindows = "windows"
GoOSLinux = "linux"
BackendBinary = "grafana"
ExampleAPIServerBinary = "grafana-example-apiserver"
ServerBinary = "grafana-server"
CLIBinary = "grafana-cli"
BackendBinary = "grafana"
ServerBinary = "grafana-server"
CLIBinary = "grafana-cli"
)
var binaries = []string{BackendBinary, ExampleAPIServerBinary, ServerBinary, CLIBinary}
var binaries = []string{BackendBinary, ServerBinary, CLIBinary}
func logError(message string, err error) int {
log.Println(message, err)
@ -86,13 +85,6 @@ func RunCmd() int {
return 1
}
case "build-example-apiserver":
clean(opts)
if err := doBuild("grafana-example-apiserver", "./pkg/cmd/grafana-example-apiserver", opts); err != nil {
log.Println(err)
return 1
}
case "build-cli":
clean(opts)
if err := doBuild("grafana-cli", "./pkg/cmd/grafana-cli", opts); err != nil {

View File

@ -12,7 +12,7 @@ import (
"github.com/grafana/grafana/pkg/build/golangutils"
)
var binaries = []string{"grafana", "grafana-example-apiserver", "grafana-server", "grafana-cli"}
var binaries = []string{"grafana", "grafana-server", "grafana-cli"}
const (
SuffixEnterprise2 = "-enterprise2"

View File

@ -1,50 +0,0 @@
package main
import (
"os"
"github.com/spf13/cobra"
genericapiserver "k8s.io/apiserver/pkg/server"
"k8s.io/component-base/cli"
)
func NewCommandStartExampleAPIServer(defaults *ExampleServerOptions, stopCh <-chan struct{}) *cobra.Command {
o := *defaults
cmd := &cobra.Command{
Short: "Launch the example API server",
Long: "Launch the example API server",
RunE: func(c *cobra.Command, args []string) error {
if err := o.Complete(); err != nil {
return err
}
config, err := o.Config()
if err != nil {
return err
}
if err := o.RunExampleServer(config, stopCh); err != nil {
return err
}
return nil
},
}
flags := cmd.Flags()
o.RecommendedOptions.AddFlags(flags)
return cmd
}
func main() {
stopCh := genericapiserver.SetupSignalHandler()
options, err := NewExampleServerOptions(os.Stdout, os.Stderr)
if err != nil {
panic(err)
}
cmd := NewCommandStartExampleAPIServer(options, stopCh)
code := cli.Run(cmd)
os.Exit(code)
}

View File

@ -1,4 +1,4 @@
# grafana-example-apiserver
# grafana apiserver (standalone)
The example-apiserver closely resembles the
[sample-apiserver](https://github.com/kubernetes/sample-apiserver/tree/master) project in code and thus
@ -19,7 +19,7 @@ can be used as a root server for this example-apiserver (in aggregated mode). He
kind cluster and that you can provide its kubeconfig in the parameters to the example-apiserver.
```shell
go run ./pkg/cmd/grafana-example-apiserver \
go run ./pkg/cmd/grafana apiserver example.grafana.app\
--authentication-kubeconfig ~/.kube/config \
--authorization-kubeconfig ~/.kube/config \
--kubeconfig ~/.kube/config \

View File

@ -0,0 +1,75 @@
package apiserver
import (
"fmt"
"os"
"github.com/spf13/cobra"
genericapiserver "k8s.io/apiserver/pkg/server"
"k8s.io/apiserver/pkg/server/options"
"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"
devAcknowledgementNotice := "The apiserver command is in heavy development. The entire setup is subject to change without notice"
cmd := &cobra.Command{
Use: "apiserver [api group(s)]",
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)
}
},
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)
if err := o.Complete(); err != nil {
return err
}
config, err := o.Config()
if err != nil {
return err
}
if err := o.RunExampleServer(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,
Codecs.LegacyCodec(), // the codec is passed to etcd and not used
)
o.RecommendedOptions.AddFlags(cmd.Flags())
return cmd
}
func RunCLI() int {
stopCh := genericapiserver.SetupSignalHandler()
options := newExampleServerOptions(os.Stdout, os.Stderr)
cmd := newCommandStartExampleAPIServer(options, stopCh)
return cli.Run(cmd)
}

View File

@ -1,11 +1,11 @@
package main
package apiserver
import (
"fmt"
"io"
"net"
exampleAPI "github.com/grafana/grafana/pkg/registry/apis/example"
"github.com/grafana/grafana/pkg/registry/apis/example"
grafanaAPIServer "github.com/grafana/grafana/pkg/services/grafana-apiserver"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -43,34 +43,47 @@ func init() {
// ExampleServerOptions contains the state for the apiserver
type ExampleServerOptions struct {
builders []grafanaAPIServer.APIGroupBuilder
RecommendedOptions *options.RecommendedOptions
Builders []grafanaAPIServer.APIGroupBuilder
AlternateDNS []string
StdOut io.Writer
StdErr io.Writer
}
func NewExampleServerOptions(out, errOut io.Writer) (*ExampleServerOptions, error) {
builder := &exampleAPI.TestingAPIBuilder{}
// Install schema
if err := builder.InstallSchema(Scheme); err != nil {
return nil, err
}
func newExampleServerOptions(out, errOut io.Writer) *ExampleServerOptions {
return &ExampleServerOptions{
Builders: []grafanaAPIServer.APIGroupBuilder{builder},
RecommendedOptions: options.NewRecommendedOptions(
defaultEtcdPathPrefix,
Codecs.LegacyCodec(builder.GetGroupVersion()),
),
StdOut: out,
StdErr: errOut,
}, nil
}
}
func (o ExampleServerOptions) Config() (*genericapiserver.RecommendedConfig, error) {
func (o *ExampleServerOptions) 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{})
default:
return fmt.Errorf("unknown group: %s", g)
}
}
if len(o.builders) < 1 {
return fmt.Errorf("expected group name(s) in the command line arguments")
}
// Install schemas
for _, b := range o.builders {
if err := b.InstallSchema(Scheme); err != nil {
return err
}
}
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 {
return nil, fmt.Errorf("error creating self-signed certificates: %v", err)
}
@ -94,18 +107,18 @@ func (o ExampleServerOptions) Config() (*genericapiserver.RecommendedConfig, err
// Validate validates ExampleServerOptions
// 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 *ExampleServerOptions) 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 *ExampleServerOptions) Complete() error {
return nil
}
func (o ExampleServerOptions) RunExampleServer(config *genericapiserver.RecommendedConfig, stopCh <-chan struct{}) error {
func (o *ExampleServerOptions) RunExampleServer(config *genericapiserver.RecommendedConfig, stopCh <-chan struct{}) error {
delegationTarget := genericapiserver.NewEmptyDelegate()
completedConfig := config.Complete()
server, err := completedConfig.New("example-apiserver", delegationTarget)
@ -114,7 +127,7 @@ func (o ExampleServerOptions) RunExampleServer(config *genericapiserver.Recommen
}
// Install the API Group+version
for _, b := range o.Builders {
for _, b := range o.builders {
g, err := b.GetAPIGroupInfo(Scheme, Codecs, completedConfig.RESTOptionsGetter)
if err != nil {
return err

View File

@ -9,6 +9,7 @@ import (
gcli "github.com/grafana/grafana/pkg/cmd/grafana-cli/commands"
gsrv "github.com/grafana/grafana/pkg/cmd/grafana-server/commands"
"github.com/grafana/grafana/pkg/cmd/grafana/apiserver"
)
// The following variables cannot be constants, since they can be overridden through the -X link flag
@ -32,6 +33,18 @@ func main() {
Commands: []*cli.Command{
gcli.CLICommand(version),
gsrv.ServerCommand(version, commit, enterpriseCommit, buildBranch, buildstamp),
{
// The kubernetes standalone apiserver service runner
Name: "apiserver",
Usage: "run a standalone api service (experimental)",
// Skip parsing flags because the command line is actually managed by cobra
SkipFlagParsing: true,
Action: func(context *cli.Context) error {
// exit here because apiserver handles its own error output
os.Exit(apiserver.RunCLI())
return nil
},
},
},
CommandNotFound: cmdNotFound,
EnableBashCompletion: true,