cmd/serve/serve.go (92 lines of code) (raw):
package serve
import (
"fmt"
"log"
"net"
"os"
"os/signal"
"path/filepath"
"strings"
"syscall"
"github.com/spf13/cobra"
"google.golang.org/grpc"
"gitlab.com/gitlab-org/step-runner/pkg/api"
"gitlab.com/gitlab-org/step-runner/pkg/api/service"
"gitlab.com/gitlab-org/step-runner/pkg/di"
"gitlab.com/gitlab-org/step-runner/proto"
)
func NewCmd() *cobra.Command {
return &cobra.Command{
Use: "serve",
Short: "Start the step-runner gRPC service",
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
sigChan := make(chan os.Signal, 1)
diContainer := di.NewContainer()
stepRunnerService, err := diContainer.StepRunnerService()
if err != nil {
return fmt.Errorf("initializing step-runner: %w", err)
}
socketAddr, err := GetSocketAddr(args)
if err != nil {
return fmt.Errorf("initializing step-runner: %w", err)
}
if err := NewServeCmd(stepRunnerService, socketAddr, sigChan).Run(); err != nil {
return fmt.Errorf("serving step-runner: %w", err)
}
return nil
},
}
}
func GetSocketAddr(args []string) (*net.UnixAddr, error) {
if len(args) == 0 {
return api.SocketAddr(api.DefaultSocketPath()), nil
}
socketDir := strings.TrimSpace(args[0])
if socketDir == "" {
return nil, fmt.Errorf("invalid empty socket dir")
}
fi, err := os.Stat(socketDir)
if err != nil || !fi.IsDir() {
return nil, fmt.Errorf("invalid socket dir %s", socketDir)
}
return api.SocketAddr(filepath.Join(socketDir, "step-runner.sock")), nil
}
type ServeCmd struct {
stepRunnerService *service.StepRunnerService
grpcServer *grpc.Server
sigChan chan os.Signal
socketAddr *net.UnixAddr
}
func NewServeCmd(stepRunnerService *service.StepRunnerService, socketAddr *net.UnixAddr, sigChan chan os.Signal) *ServeCmd {
return &ServeCmd{
stepRunnerService: stepRunnerService,
grpcServer: grpc.NewServer(),
sigChan: sigChan,
socketAddr: socketAddr,
}
}
func (sc *ServeCmd) Run() error {
listener, err := sc.Listen()
if err != nil {
return err
}
return sc.Serve(listener)
}
func (sc *ServeCmd) Listen() (net.Listener, error) {
go func() {
signal.Notify(sc.sigChan, syscall.SIGTERM, syscall.SIGINT)
sig := <-sc.sigChan
log.Printf("received '%s' signal; shutting down.", sig)
sc.grpcServer.GracefulStop()
}()
listener, err := net.ListenUnix("unix", sc.socketAddr)
if err != nil {
return nil, fmt.Errorf("opening socket: %w", err)
}
return listener, nil
}
func (sc *ServeCmd) Serve(listener net.Listener) error {
proto.RegisterStepRunnerServer(sc.grpcServer, sc.stepRunnerService)
return sc.grpcServer.Serve(listener)
}