You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by to...@apache.org on 2021/06/30 11:12:51 UTC

[apisix-dashboard] branch master updated: refactor: server manager (#1956)

This is an automated email from the ASF dual-hosted git repository.

tokers pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix-dashboard.git


The following commit(s) were added to refs/heads/master by this push:
     new 3cfc327  refactor: server manager (#1956)
3cfc327 is described below

commit 3cfc32761122ad7f15f67edcfc251d0bed2e7538
Author: bzp2010 <bz...@apache.org>
AuthorDate: Wed Jun 30 19:12:42 2021 +0800

    refactor: server manager (#1956)
---
 api/cmd/root.go                                    | 137 ++------------------
 api/cmd/version.go                                 |  11 +-
 api/internal/core/server/http.go                   |  72 +++++++++++
 api/internal/core/server/server.go                 | 144 +++++++++++++++++++++
 .../version.go => internal/core/server/store.go}   |  34 ++---
 api/internal/utils/version.go                      |  14 +-
 api/test/shell/cli_test.sh                         |   2 +-
 7 files changed, 259 insertions(+), 155 deletions(-)

diff --git a/api/cmd/root.go b/api/cmd/root.go
index 05e16a2..b9d2aa4 100644
--- a/api/cmd/root.go
+++ b/api/cmd/root.go
@@ -17,28 +17,16 @@
 package cmd
 
 import (
-	"context"
-	"crypto/tls"
 	"fmt"
-	"net"
-	"net/http"
 	"os"
 	"os/signal"
-	"strconv"
 	"syscall"
-	"time"
 
-	"github.com/shiningrush/droplet"
 	"github.com/spf13/cobra"
 
-	"github.com/apisix/manager-api/internal"
 	"github.com/apisix/manager-api/internal/conf"
-	"github.com/apisix/manager-api/internal/core/storage"
-	"github.com/apisix/manager-api/internal/core/store"
-	"github.com/apisix/manager-api/internal/filter"
-	"github.com/apisix/manager-api/internal/handler"
+	"github.com/apisix/manager-api/internal/core/server"
 	"github.com/apisix/manager-api/internal/log"
-	"github.com/apisix/manager-api/internal/utils"
 )
 
 var (
@@ -88,126 +76,29 @@ func manageAPI() error {
 	conf.InitConf()
 	log.InitLogger()
 
-	if err := utils.WritePID(conf.PIDPath, forceStart); err != nil {
-		log.Errorf("failed to write pid: %s", err)
+	s, err := server.NewServer(&server.Options{
+		ForceStart: forceStart,
+	})
+	if err != nil {
 		return err
 	}
-	utils.AppendToClosers(func() error {
-		if err := os.Remove(conf.PIDPath); err != nil {
-			log.Errorf("failed to remove pid path: %s", err)
-			return err
-		}
-		return nil
-	})
+
+	// start Manager API server
+	errSig := make(chan error, 5)
+	s.Start(errSig)
 
 	// Signal received to the process externally.
 	quit := make(chan os.Signal, 1)
 	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
 
-	defer func() {
-		utils.CloseAll()
-		signal.Stop(quit)
-	}()
-
-	droplet.Option.Orchestrator = func(mws []droplet.Middleware) []droplet.Middleware {
-		var newMws []droplet.Middleware
-		// default middleware order: resp_reshape, auto_input, traffic_log
-		// We should put err_transform at second to catch all error
-		newMws = append(newMws, mws[0], &handler.ErrorTransformMiddleware{}, &filter.AuthenticationMiddleware{})
-		newMws = append(newMws, mws[1:]...)
-		return newMws
-	}
-
-	if err := storage.InitETCDClient(conf.ETCDConfig); err != nil {
-		log.Errorf("init etcd client fail: %w", err)
-		return err
-	}
-	if err := store.InitStores(); err != nil {
-		log.Errorf("init stores fail: %w", err)
-		return err
-	}
-
-	var server, serverSSL *http.Server
-	// For internal error handling across multiple goroutines.
-	errsig := make(chan error, 1)
-
-	// routes
-	r := internal.SetUpRouter()
-	addr := net.JoinHostPort(conf.ServerHost, strconv.Itoa(conf.ServerPort))
-	server = &http.Server{
-		Addr:         addr,
-		Handler:      r,
-		ReadTimeout:  time.Duration(1000) * time.Millisecond,
-		WriteTimeout: time.Duration(5000) * time.Millisecond,
-	}
-
-	log.Infof("The Manager API is listening on %s", addr)
-
-	go func() {
-		if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
-			log.Errorf("listen and serv fail: %s", err)
-			errsig <- err
-		}
-	}()
-
-	// HTTPS
-	if conf.SSLCert != "" && conf.SSLKey != "" {
-		addrSSL := net.JoinHostPort(conf.ServerHost, strconv.Itoa(conf.SSLPort))
-		serverSSL = &http.Server{
-			Addr:         addrSSL,
-			Handler:      r,
-			ReadTimeout:  time.Duration(1000) * time.Millisecond,
-			WriteTimeout: time.Duration(5000) * time.Millisecond,
-			TLSConfig: &tls.Config{
-				// Causes servers to use Go's default ciphersuite preferences,
-				// which are tuned to avoid attacks. Does nothing on clients.
-				PreferServerCipherSuites: true,
-			},
-		}
-		go func() {
-			err := serverSSL.ListenAndServeTLS(conf.SSLCert, conf.SSLKey)
-			if err != nil && err != http.ErrServerClosed {
-				log.Errorf("listen and serve for HTTPS failed: %s", err)
-				errsig <- err
-			}
-		}()
-	}
-
-	printInfo()
-
 	select {
-	case err := <-errsig:
-		return err
-
 	case sig := <-quit:
 		log.Infof("The Manager API server receive %s and start shutting down", sig.String())
-
-		shutdownServer(server)
-		shutdownServer(serverSSL)
+		s.Stop()
 		log.Infof("The Manager API server exited")
-		return nil
-	}
-}
-
-func shutdownServer(server *http.Server) {
-	if server != nil {
-		ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second)
-		defer cancel()
-
-		if err := server.Shutdown(ctx); err != nil {
-			log.Errorf("Shutting down server error: %s", err)
-		}
-	}
-}
-
-func printInfo() {
-	fmt.Fprint(os.Stdout, "The manager-api is running successfully!\n\n")
-	printVersion()
-	fmt.Fprintf(os.Stdout, "%-8s: %s:%d\n", "Listen", conf.ServerHost, conf.ServerPort)
-	if conf.SSLCert != "" && conf.SSLKey != "" {
-		fmt.Fprintf(os.Stdout, "%-8s: %s:%d\n", "HTTPS Listen", conf.SSLHost, conf.SSLPort)
+	case err := <-errSig:
+		log.Errorf("The Manager API server start failed: %s", err.Error())
+		return err
 	}
-	fmt.Fprintf(os.Stdout, "%-8s: %s\n", "Loglevel", conf.ErrorLogLevel)
-	fmt.Fprintf(os.Stdout, "%-8s: %s\n", "ErrorLogFile", conf.ErrorLogPath)
-	fmt.Fprintf(os.Stdout, "%-8s: %s\n\n", "AccessLogFile", conf.AccessLogPath)
+	return nil
 }
diff --git a/api/cmd/version.go b/api/cmd/version.go
index df7abc1..c064502 100644
--- a/api/cmd/version.go
+++ b/api/cmd/version.go
@@ -17,9 +17,6 @@
 package cmd
 
 import (
-	"fmt"
-	"os"
-
 	"github.com/spf13/cobra"
 
 	"github.com/apisix/manager-api/internal/utils"
@@ -30,13 +27,7 @@ func newVersionCommand() *cobra.Command {
 		Use:   "version",
 		Short: "show manager-api version",
 		Run: func(cmd *cobra.Command, args []string) {
-			printVersion()
+			utils.PrintVersion()
 		},
 	}
 }
-
-func printVersion() {
-	gitHash, version := utils.GetHashAndVersion()
-	fmt.Fprintf(os.Stdout, "%-8s: %s\n", "Version", version)
-	fmt.Fprintf(os.Stdout, "%-8s: %s\n", "GitHash", gitHash)
-}
diff --git a/api/internal/core/server/http.go b/api/internal/core/server/http.go
new file mode 100644
index 0000000..53a4216
--- /dev/null
+++ b/api/internal/core/server/http.go
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 server
+
+import (
+	"crypto/tls"
+	"net"
+	"net/http"
+	"strconv"
+	"time"
+
+	"github.com/shiningrush/droplet"
+
+	"github.com/apisix/manager-api/internal"
+	"github.com/apisix/manager-api/internal/conf"
+	"github.com/apisix/manager-api/internal/filter"
+	"github.com/apisix/manager-api/internal/handler"
+)
+
+func (s *server) setupAPI() {
+	// orchestrator
+	droplet.Option.Orchestrator = func(mws []droplet.Middleware) []droplet.Middleware {
+		var newMws []droplet.Middleware
+		// default middleware order: resp_reshape, auto_input, traffic_log
+		// We should put err_transform at second to catch all error
+		newMws = append(newMws, mws[0], &handler.ErrorTransformMiddleware{}, &filter.AuthenticationMiddleware{})
+		newMws = append(newMws, mws[1:]...)
+		return newMws
+	}
+
+	// routes
+	r := internal.SetUpRouter()
+
+	// HTTP
+	addr := net.JoinHostPort(conf.ServerHost, strconv.Itoa(conf.ServerPort))
+	s.server = &http.Server{
+		Addr:         addr,
+		Handler:      r,
+		ReadTimeout:  time.Duration(1000) * time.Millisecond,
+		WriteTimeout: time.Duration(5000) * time.Millisecond,
+	}
+
+	// HTTPS
+	if conf.SSLCert != "" && conf.SSLKey != "" {
+		addrSSL := net.JoinHostPort(conf.SSLHost, strconv.Itoa(conf.SSLPort))
+		s.serverSSL = &http.Server{
+			Addr:         addrSSL,
+			Handler:      r,
+			ReadTimeout:  time.Duration(1000) * time.Millisecond,
+			WriteTimeout: time.Duration(5000) * time.Millisecond,
+			TLSConfig: &tls.Config{
+				// Causes servers to use Go's default ciphersuite preferences,
+				// which are tuned to avoid attacks. Does nothing on clients.
+				PreferServerCipherSuites: true,
+			},
+		}
+	}
+}
diff --git a/api/internal/core/server/server.go b/api/internal/core/server/server.go
new file mode 100644
index 0000000..07c4b1c
--- /dev/null
+++ b/api/internal/core/server/server.go
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 server
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+	"os"
+	"time"
+
+	"github.com/apisix/manager-api/internal/conf"
+	"github.com/apisix/manager-api/internal/log"
+	"github.com/apisix/manager-api/internal/utils"
+)
+
+// server is the manager of Manager API, which is responsible for managing the life cycle of Manager API, including initialization, start, stop and so on
+type server struct {
+	server    *http.Server
+	serverSSL *http.Server
+	options   *Options
+}
+
+type Options struct {
+	ForceStart bool // force start new instance
+}
+
+// NewServer Create a server manager
+func NewServer(options *Options) (*server, error) {
+	return &server{options: options}, nil
+}
+
+func (s *server) Start(errSig chan error) {
+	// initialize server
+	err := s.init()
+	if err != nil {
+		errSig <- err
+		return
+	}
+
+	// write daemon pid file
+	err = s.writePID()
+	if err != nil {
+		errSig <- err
+		return
+	}
+
+	// print server info to stdout
+	s.printInfo()
+
+	// start HTTP server
+	log.Infof("The Manager API is listening on %s", s.server.Addr)
+	go func() {
+		err := s.server.ListenAndServe()
+		if err != nil && err != http.ErrServerClosed {
+			log.Errorf("listen and serv fail: %s", err)
+			errSig <- err
+		}
+	}()
+
+	// start HTTPs server
+	if conf.SSLCert != "" && conf.SSLKey != "" {
+		go func() {
+			err := s.serverSSL.ListenAndServeTLS(conf.SSLCert, conf.SSLKey)
+			if err != nil && err != http.ErrServerClosed {
+				log.Errorf("listen and serve for HTTPS failed: %s", err)
+				errSig <- err
+			}
+		}()
+	}
+}
+
+func (s *server) Stop() {
+	utils.CloseAll()
+
+	s.shutdownServer(s.server)
+	s.shutdownServer(s.serverSSL)
+}
+
+func (s *server) init() error {
+	log.Info("Initialize Manager API store")
+	err := s.setupStore()
+	if err != nil {
+		return err
+	}
+
+	log.Info("Initialize Manager API server")
+	s.setupAPI()
+
+	return nil
+}
+
+func (s *server) writePID() error {
+	if err := utils.WritePID(conf.PIDPath, s.options.ForceStart); err != nil {
+		log.Errorf("failed to write pid: %s", err)
+		return err
+	}
+	utils.AppendToClosers(func() error {
+		if err := os.Remove(conf.PIDPath); err != nil {
+			log.Errorf("failed to remove pid path: %s", err)
+			return err
+		}
+		return nil
+	})
+
+	return nil
+}
+
+func (s *server) shutdownServer(server *http.Server) {
+	if server != nil {
+		ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second)
+		defer cancel()
+
+		if err := server.Shutdown(ctx); err != nil {
+			log.Errorf("Shutting down server error: %s", err)
+		}
+	}
+}
+
+func (s *server) printInfo() {
+	fmt.Fprint(os.Stdout, "The manager-api is running successfully!\n\n")
+	utils.PrintVersion()
+	fmt.Fprintf(os.Stdout, "%-8s: %s:%d\n", "Listen", conf.ServerHost, conf.ServerPort)
+	if conf.SSLCert != "" && conf.SSLKey != "" {
+		fmt.Fprintf(os.Stdout, "%-8s: %s:%d\n", "HTTPS Listen", conf.SSLHost, conf.SSLPort)
+	}
+	fmt.Fprintf(os.Stdout, "%-8s: %s\n", "Loglevel", conf.ErrorLogLevel)
+	fmt.Fprintf(os.Stdout, "%-8s: %s\n", "ErrorLogFile", conf.ErrorLogPath)
+	fmt.Fprintf(os.Stdout, "%-8s: %s\n\n", "AccessLogFile", conf.AccessLogPath)
+}
diff --git a/api/cmd/version.go b/api/internal/core/server/store.go
similarity index 62%
copy from api/cmd/version.go
copy to api/internal/core/server/store.go
index df7abc1..6130a19 100644
--- a/api/cmd/version.go
+++ b/api/internal/core/server/store.go
@@ -14,29 +14,23 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package cmd
+package server
 
 import (
-	"fmt"
-	"os"
-
-	"github.com/spf13/cobra"
-
-	"github.com/apisix/manager-api/internal/utils"
+	"github.com/apisix/manager-api/internal/conf"
+	"github.com/apisix/manager-api/internal/core/storage"
+	"github.com/apisix/manager-api/internal/core/store"
+	"github.com/apisix/manager-api/internal/log"
 )
 
-func newVersionCommand() *cobra.Command {
-	return &cobra.Command{
-		Use:   "version",
-		Short: "show manager-api version",
-		Run: func(cmd *cobra.Command, args []string) {
-			printVersion()
-		},
+func (s *server) setupStore() error {
+	if err := storage.InitETCDClient(conf.ETCDConfig); err != nil {
+		log.Errorf("init etcd client fail: %w", err)
+		return err
 	}
-}
-
-func printVersion() {
-	gitHash, version := utils.GetHashAndVersion()
-	fmt.Fprintf(os.Stdout, "%-8s: %s\n", "Version", version)
-	fmt.Fprintf(os.Stdout, "%-8s: %s\n", "GitHash", gitHash)
+	if err := store.InitStores(); err != nil {
+		log.Errorf("init stores fail: %w", err)
+		return err
+	}
+	return nil
 }
diff --git a/api/internal/utils/version.go b/api/internal/utils/version.go
index d4bec4a..c061156 100644
--- a/api/internal/utils/version.go
+++ b/api/internal/utils/version.go
@@ -16,12 +16,24 @@
  */
 package utils
 
+import (
+	"fmt"
+	"os"
+)
+
 var (
 	gitHash string
 	version string
 )
 
-// get the hash and version
+// GetHashAndVersion get the hash and version
 func GetHashAndVersion() (string, string) {
 	return gitHash, version
 }
+
+// PrintVersion print version and git hash to stdout
+func PrintVersion() {
+	gitHash, version := GetHashAndVersion()
+	fmt.Fprintf(os.Stdout, "%-8s: %s\n", "Version", version)
+	fmt.Fprintf(os.Stdout, "%-8s: %s\n", "GitHash", gitHash)
+}
diff --git a/api/test/shell/cli_test.sh b/api/test/shell/cli_test.sh
index 325b0a4..1bbb66f 100755
--- a/api/test/shell/cli_test.sh
+++ b/api/test/shell/cli_test.sh
@@ -223,7 +223,7 @@ sleep 6
 
 cat ${logfile}
 
-if [[ `grep -c "cmd/root.go" ${logfile}` -ne '1' ]]; then
+if [[ `grep -c "server/store.go" ${logfile}` -ne '1' ]]; then
     echo "failed: failed to write the correct caller"
     exit 1
 fi