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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"log"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
restfulspec "github.com/emicklei/go-restful-openapi/v2"
|
"github.com/oam-dev/kubevela/cmd/apiserver/app"
|
||||||
"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"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
s := &Server{}
|
cmd := app.NewAPIServerCommand()
|
||||||
flag.StringVar(&s.serverConfig.BindAddr, "bind-addr", "0.0.0.0:8000", "The bind address used to serve the http APIs.")
|
if err := cmd.Execute(); err != nil {
|
||||||
flag.StringVar(&s.serverConfig.MetricPath, "metrics-path", "/metrics", "The path to expose the metrics.")
|
log.Fatalln(err)
|
||||||
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())
|
|
||||||
}
|
|
||||||
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
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
|
||||||
"github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore"
|
"github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -56,3 +61,54 @@ type leaderConfig struct {
|
|||||||
LockName string
|
LockName string
|
||||||
Duration time.Duration
|
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