mirror of
https://github.com/kubevela/kubevela.git
synced 2026-02-14 10:00:06 +00:00
Refactor: Use github.com/spf13/cobra to execute cmd for apiserver (#5085)
Signed-off-by: wuzhongjian <wuzhongjian_yewu@cmss.chinamobile.com> Signed-off-by: wuzhongjian <wuzhongjian_yewu@cmss.chinamobile.com>
This commit is contained in:
43
cmd/apiserver/app/options/options.go
Normal file
43
cmd/apiserver/app/options/options.go
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
Copyright 2022 The KubeVela 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.
|
||||
*/
|
||||
|
||||
package options
|
||||
|
||||
import (
|
||||
cliflag "k8s.io/component-base/cli/flag"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/config"
|
||||
)
|
||||
|
||||
// ServerRunOptions runs a kubevela api server.
|
||||
type ServerRunOptions struct {
|
||||
GenericServerRunOptions *config.Config
|
||||
}
|
||||
|
||||
// NewServerRunOptions creates a new ServerRunOptions object with default parameters
|
||||
func NewServerRunOptions() *ServerRunOptions {
|
||||
s := &ServerRunOptions{
|
||||
GenericServerRunOptions: config.NewConfig(),
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Flags returns the complete NamedFlagSets
|
||||
func (s *ServerRunOptions) Flags() (fss cliflag.NamedFlagSets) {
|
||||
fs := fss.FlagSet("generic")
|
||||
s.GenericServerRunOptions.AddFlags(fs, s.GenericServerRunOptions)
|
||||
return fss
|
||||
}
|
||||
28
cmd/apiserver/app/options/validation.go
Normal file
28
cmd/apiserver/app/options/validation.go
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
Copyright 2022 The KubeVela 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.
|
||||
*/
|
||||
|
||||
package options
|
||||
|
||||
import utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
|
||||
// Validate validates server run options, to find options' misconfiguration
|
||||
func (s *ServerRunOptions) Validate() error {
|
||||
var errors []error
|
||||
|
||||
errors = append(errors, s.GenericServerRunOptions.Validate()...)
|
||||
|
||||
return utilerrors.NewAggregate(errors)
|
||||
}
|
||||
151
cmd/apiserver/app/server.go
Normal file
151
cmd/apiserver/app/server.go
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
Copyright 2022 The KubeVela 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.
|
||||
*/
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
restfulspec "github.com/emicklei/go-restful-openapi/v2"
|
||||
"github.com/fatih/color"
|
||||
"github.com/go-openapi/spec"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/oam-dev/kubevela/cmd/apiserver/app/options"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/utils/log"
|
||||
"github.com/oam-dev/kubevela/pkg/utils"
|
||||
"github.com/oam-dev/kubevela/version"
|
||||
)
|
||||
|
||||
// NewAPIServerCommand creates a *cobra.Command object with default parameters
|
||||
func NewAPIServerCommand() *cobra.Command {
|
||||
s := options.NewServerRunOptions()
|
||||
cmd := &cobra.Command{
|
||||
Use: "apiserver",
|
||||
Long: `The KubeVela API server validates and configures data for the API objects.
|
||||
The API Server services REST operations and provides the frontend to the
|
||||
cluster's shared state through which all other components interact.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := s.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
return Run(s)
|
||||
},
|
||||
SilenceUsage: true,
|
||||
}
|
||||
|
||||
fs := cmd.Flags()
|
||||
namedFlagSets := s.Flags()
|
||||
for _, set := range namedFlagSets.FlagSets {
|
||||
fs.AddFlagSet(set)
|
||||
}
|
||||
|
||||
buildSwaggerCmd := &cobra.Command{
|
||||
Use: "build-swagger",
|
||||
Short: "Build swagger documentation of KubeVela apiserver",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
name := "docs/apidoc/latest-swagger.json"
|
||||
if len(args) > 0 {
|
||||
name = args[0]
|
||||
}
|
||||
func() {
|
||||
swagger, err := buildSwagger(s)
|
||||
if err != nil {
|
||||
log.Logger.Fatal(err.Error())
|
||||
}
|
||||
outData, err := json.MarshalIndent(swagger, "", "\t")
|
||||
if err != nil {
|
||||
log.Logger.Fatal(err.Error())
|
||||
}
|
||||
swaggerFile, err := os.OpenFile(name, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
log.Logger.Fatal(err.Error())
|
||||
}
|
||||
defer func() {
|
||||
if err := swaggerFile.Close(); err != nil {
|
||||
log.Logger.Errorf("close swagger file failure %s", err.Error())
|
||||
}
|
||||
}()
|
||||
_, err = swaggerFile.Write(outData)
|
||||
if err != nil {
|
||||
log.Logger.Fatal(err.Error())
|
||||
}
|
||||
fmt.Println("build swagger config file success")
|
||||
}()
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd.AddCommand(buildSwaggerCmd)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Run runs the specified APIServer. This should never exit.
|
||||
func Run(s *options.ServerRunOptions) error {
|
||||
// The server is not terminal, there is no color default.
|
||||
// Force set to false, this is useful for the dry-run API.
|
||||
color.NoColor = false
|
||||
|
||||
errChan := make(chan error)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
if s.GenericServerRunOptions.PprofAddr != "" {
|
||||
go utils.EnablePprof(s.GenericServerRunOptions.PprofAddr, errChan)
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := run(ctx, s, errChan); err != nil {
|
||||
errChan <- fmt.Errorf("failed to run apiserver: %w", err)
|
||||
}
|
||||
}()
|
||||
var term = make(chan os.Signal, 1)
|
||||
signal.Notify(term, os.Interrupt, syscall.SIGTERM)
|
||||
|
||||
select {
|
||||
case <-term:
|
||||
log.Logger.Infof("Received SIGTERM, exiting gracefully...")
|
||||
case err := <-errChan:
|
||||
log.Logger.Errorf("Received an error: %s, exiting gracefully...", err.Error())
|
||||
return err
|
||||
}
|
||||
log.Logger.Infof("See you next time!")
|
||||
return nil
|
||||
}
|
||||
|
||||
func run(ctx context.Context, s *options.ServerRunOptions, errChan chan error) error {
|
||||
log.Logger.Infof("KubeVela information: version: %v, gitRevision: %v", version.VelaVersion, version.GitRevision)
|
||||
|
||||
server := apiserver.New(*s.GenericServerRunOptions)
|
||||
|
||||
return server.Run(ctx, errChan)
|
||||
}
|
||||
|
||||
func buildSwagger(s *options.ServerRunOptions) (*spec.Swagger, error) {
|
||||
server := apiserver.New(*s.GenericServerRunOptions)
|
||||
config, err := server.BuildRestfulConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return restfulspec.BuildSwagger(*config), nil
|
||||
}
|
||||
@@ -17,121 +17,14 @@ limitations under the License.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
"log"
|
||||
|
||||
restfulspec "github.com/emicklei/go-restful-openapi/v2"
|
||||
"github.com/fatih/color"
|
||||
"github.com/go-openapi/spec"
|
||||
"github.com/google/uuid"
|
||||
flag "github.com/spf13/pflag"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/config"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/utils/log"
|
||||
"github.com/oam-dev/kubevela/pkg/features"
|
||||
"github.com/oam-dev/kubevela/pkg/utils"
|
||||
"github.com/oam-dev/kubevela/version"
|
||||
"github.com/oam-dev/kubevela/cmd/apiserver/app"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := &Server{}
|
||||
flag.StringVar(&s.serverConfig.BindAddr, "bind-addr", "0.0.0.0:8000", "The bind address used to serve the http APIs.")
|
||||
flag.StringVar(&s.serverConfig.MetricPath, "metrics-path", "/metrics", "The path to expose the metrics.")
|
||||
flag.StringVar(&s.serverConfig.Datastore.Type, "datastore-type", "kubeapi", "Metadata storage driver type, support kubeapi and mongodb")
|
||||
flag.StringVar(&s.serverConfig.Datastore.Database, "datastore-database", "kubevela", "Metadata storage database name, takes effect when the storage driver is mongodb.")
|
||||
flag.StringVar(&s.serverConfig.Datastore.URL, "datastore-url", "", "Metadata storage database url,takes effect when the storage driver is mongodb.")
|
||||
flag.StringVar(&s.serverConfig.LeaderConfig.ID, "id", uuid.New().String(), "the holder identity name")
|
||||
flag.StringVar(&s.serverConfig.LeaderConfig.LockName, "lock-name", "apiserver-lock", "the lease lock resource name")
|
||||
flag.DurationVar(&s.serverConfig.LeaderConfig.Duration, "duration", time.Second*5, "the lease lock resource name")
|
||||
flag.DurationVar(&s.serverConfig.AddonCacheTime, "addon-cache-duration", time.Minute*10, "how long between two addon cache operation")
|
||||
flag.BoolVar(&s.serverConfig.DisableStatisticCronJob, "disable-statistic-cronJob", false, "close the system statistic info calculating cronJob")
|
||||
flag.StringVar(&s.serverConfig.PprofAddr, "pprof-addr", "", "The address for pprof to use while exporting profiling results. The default value is empty which means do not expose it. Set it to address like :6666 to expose it.")
|
||||
flag.Float64Var(&s.serverConfig.KubeQPS, "kube-api-qps", 100, "the qps for kube clients. Low qps may lead to low throughput. High qps may give stress to api-server.")
|
||||
flag.IntVar(&s.serverConfig.KubeBurst, "kube-api-burst", 300, "the burst for kube clients. Recommend setting it qps*3.")
|
||||
features.APIServerMutableFeatureGate.AddFlag(flag.CommandLine)
|
||||
flag.Parse()
|
||||
|
||||
if len(os.Args) > 2 && os.Args[1] == "build-swagger" {
|
||||
func() {
|
||||
swagger, err := s.buildSwagger()
|
||||
if err != nil {
|
||||
log.Logger.Fatal(err.Error())
|
||||
cmd := app.NewAPIServerCommand()
|
||||
if err := cmd.Execute(); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
outData, err := json.MarshalIndent(swagger, "", "\t")
|
||||
if err != nil {
|
||||
log.Logger.Fatal(err.Error())
|
||||
}
|
||||
swaggerFile, err := os.OpenFile(os.Args[2], os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
log.Logger.Fatal(err.Error())
|
||||
}
|
||||
defer func() {
|
||||
if err := swaggerFile.Close(); err != nil {
|
||||
log.Logger.Errorf("close swagger file failure %s", err.Error())
|
||||
}
|
||||
}()
|
||||
_, err = swaggerFile.Write(outData)
|
||||
if err != nil {
|
||||
log.Logger.Fatal(err.Error())
|
||||
}
|
||||
fmt.Println("build swagger config file success")
|
||||
}()
|
||||
return
|
||||
}
|
||||
|
||||
// The server is not terminal, there is no color default.
|
||||
// Force set to false, this is useful for the dry-run API.
|
||||
color.NoColor = false
|
||||
|
||||
errChan := make(chan error)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
if s.serverConfig.PprofAddr != "" {
|
||||
go utils.EnablePprof(s.serverConfig.PprofAddr, errChan)
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := s.run(ctx, errChan); err != nil {
|
||||
errChan <- fmt.Errorf("failed to run apiserver: %w", err)
|
||||
}
|
||||
}()
|
||||
var term = make(chan os.Signal, 1)
|
||||
signal.Notify(term, os.Interrupt, syscall.SIGTERM)
|
||||
|
||||
select {
|
||||
case <-term:
|
||||
log.Logger.Infof("Received SIGTERM, exiting gracefully...")
|
||||
case err := <-errChan:
|
||||
log.Logger.Errorf("Received an error: %s, exiting gracefully...", err.Error())
|
||||
}
|
||||
log.Logger.Infof("See you next time!")
|
||||
}
|
||||
|
||||
// Server apiserver
|
||||
type Server struct {
|
||||
serverConfig config.Config
|
||||
}
|
||||
|
||||
func (s *Server) run(ctx context.Context, errChan chan error) error {
|
||||
log.Logger.Infof("KubeVela information: version: %v, gitRevision: %v", version.VelaVersion, version.GitRevision)
|
||||
|
||||
server := apiserver.New(s.serverConfig)
|
||||
|
||||
return server.Run(ctx, errChan)
|
||||
}
|
||||
|
||||
func (s *Server) buildSwagger() (*spec.Swagger, error) {
|
||||
server := apiserver.New(s.serverConfig)
|
||||
config, err := server.BuildRestfulConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return restfulspec.BuildSwagger(*config), nil
|
||||
}
|
||||
|
||||
@@ -17,8 +17,13 @@ limitations under the License.
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore"
|
||||
)
|
||||
|
||||
@@ -56,3 +61,54 @@ type leaderConfig struct {
|
||||
LockName string
|
||||
Duration time.Duration
|
||||
}
|
||||
|
||||
// NewConfig returns a Config struct with default values
|
||||
func NewConfig() *Config {
|
||||
return &Config{
|
||||
BindAddr: "0.0.0.0:8000",
|
||||
MetricPath: "/metrics",
|
||||
Datastore: datastore.Config{
|
||||
Type: "kubeapi",
|
||||
Database: "kubevela",
|
||||
URL: "",
|
||||
},
|
||||
LeaderConfig: leaderConfig{
|
||||
ID: uuid.New().String(),
|
||||
LockName: "apiserver-lock",
|
||||
Duration: time.Second * 5,
|
||||
},
|
||||
AddonCacheTime: time.Minute * 10,
|
||||
DisableStatisticCronJob: false,
|
||||
PprofAddr: "",
|
||||
KubeQPS: 100,
|
||||
KubeBurst: 300,
|
||||
}
|
||||
}
|
||||
|
||||
// Validate validate generic server run options
|
||||
func (s *Config) Validate() []error {
|
||||
var errs []error
|
||||
|
||||
if s.Datastore.Type != "mongodb" && s.Datastore.Type != "kubeapi" {
|
||||
errs = append(errs, fmt.Errorf("not support datastore type %s", s.Datastore.Type))
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
// AddFlags adds flags to the specified FlagSet
|
||||
func (s *Config) AddFlags(fs *pflag.FlagSet, c *Config) {
|
||||
fs.StringVar(&s.BindAddr, "bind-addr", c.BindAddr, "The bind address used to serve the http APIs.")
|
||||
fs.StringVar(&s.MetricPath, "metrics-path", c.MetricPath, "The path to expose the metrics.")
|
||||
fs.StringVar(&s.Datastore.Type, "datastore-type", c.Datastore.Type, "Metadata storage driver type, support kubeapi and mongodb")
|
||||
fs.StringVar(&s.Datastore.Database, "datastore-database", c.Datastore.Database, "Metadata storage database name, takes effect when the storage driver is mongodb.")
|
||||
fs.StringVar(&s.Datastore.URL, "datastore-url", c.Datastore.URL, "Metadata storage database url,takes effect when the storage driver is mongodb.")
|
||||
fs.StringVar(&s.LeaderConfig.ID, "id", c.LeaderConfig.ID, "the holder identity name")
|
||||
fs.StringVar(&s.LeaderConfig.LockName, "lock-name", c.LeaderConfig.LockName, "the lease lock resource name")
|
||||
fs.DurationVar(&s.LeaderConfig.Duration, "duration", c.LeaderConfig.Duration, "the lease lock resource name")
|
||||
fs.DurationVar(&s.AddonCacheTime, "addon-cache-duration", c.AddonCacheTime, "how long between two addon cache operation")
|
||||
fs.BoolVar(&s.DisableStatisticCronJob, "disable-statistic-cronJob", c.DisableStatisticCronJob, "close the system statistic info calculating cronJob")
|
||||
fs.StringVar(&s.PprofAddr, "pprof-addr", c.PprofAddr, "The address for pprof to use while exporting profiling results. The default value is empty which means do not expose it. Set it to address like :6666 to expose it.")
|
||||
fs.Float64Var(&s.KubeQPS, "kube-api-qps", c.KubeQPS, "the qps for kube clients. Low qps may lead to low throughput. High qps may give stress to api-server.")
|
||||
fs.IntVar(&s.KubeBurst, "kube-api-burst", c.KubeBurst, "the burst for kube clients. Recommend setting it qps*3.")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user