From bfcc6f56f320d891ba61e886acf3b49ac6d766e2 Mon Sep 17 00:00:00 2001 From: fuxiaohei Date: Tue, 15 Nov 2022 22:42:41 +0800 Subject: [PATCH] feat(cli): make register interactive stages working --- client/client.go | 1 + client/http.go | 6 +++ cmd/cmd.go | 20 +++++--- cmd/daemon.go | 59 +----------------------- cmd/register.go | 106 +++++++++++++++++++++++++++++++++++++++++-- config/config.go | 14 ++---- core/runner.go | 1 + register/register.go | 9 ++-- 8 files changed, 132 insertions(+), 84 deletions(-) diff --git a/client/client.go b/client/client.go index 1b3dcfb..7d038fd 100644 --- a/client/client.go +++ b/client/client.go @@ -15,4 +15,5 @@ type Filter struct { type Client interface { pingv1connect.PingServiceClient runnerv1connect.RunnerServiceClient + Address() string } diff --git a/client/http.go b/client/http.go index e937989..a97cd22 100644 --- a/client/http.go +++ b/client/http.go @@ -62,13 +62,19 @@ func New(endpoint string, opts ...Option) *HTTPClient { endpoint, cfg.opts..., ), + endpoint: endpoint, } } +func (c *HTTPClient) Address() string { + return c.endpoint +} + var _ Client = (*HTTPClient)(nil) // An HTTPClient manages communication with the runner API. type HTTPClient struct { pingv1connect.PingServiceClient runnerv1connect.RunnerServiceClient + endpoint string } diff --git a/cmd/cmd.go b/cmd/cmd.go index e3fe23c..0744eb8 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -14,8 +14,14 @@ import ( const version = "0.1.5" +type globalArgs struct { + EnvFile string +} + func Execute(ctx context.Context) { - task := runtime.NewTask("gitea", 0, nil, nil) + // task := runtime.NewTask("gitea", 0, nil, nil) + + var gArgs globalArgs // ./act_runner rootCmd := &cobra.Command{ @@ -26,10 +32,10 @@ func Execute(ctx context.Context) { Version: version, SilenceUsage: true, } - rootCmd.Flags().BoolP("run", "r", false, "run workflows") - rootCmd.Flags().StringP("job", "j", "", "run job") - rootCmd.PersistentFlags().StringVarP(&task.Input.ForgeInstance, "forge-instance", "", "github.com", "Forge instance to use.") - rootCmd.PersistentFlags().StringVarP(&task.Input.EnvFile, "env-file", "", ".env", "Read in a file of environment variables.") + //rootCmd.Flags().BoolP("run", "r", false, "run workflows") + //rootCmd.Flags().StringP("job", "j", "", "run job") + //rootCmd.PersistentFlags().StringVarP(&task.Input.ForgeInstance, "forge-instance", "", "github.com", "Forge instance to use.") + rootCmd.PersistentFlags().StringVarP(&gArgs.EnvFile, "env-file", "", ".env", "Read in a file of environment variables.") // ./act_runner register var regArgs registerArgs @@ -37,7 +43,7 @@ func Execute(ctx context.Context) { Use: "register", Short: "Register a runner to the server", Args: cobra.MaximumNArgs(0), - RunE: runRegister(ctx, ®Args), // must use a pointer to regArgs + RunE: runRegister(ctx, ®Args, gArgs.EnvFile), // must use a pointer to regArgs } registerCmd.Flags().BoolVarP(®Args.NoInteractive, "no-interactive", "", false, "Disable interactive mode") registerCmd.Flags().StringVarP(®Args.InstanceAddr, "instance", "", "", "Gitea instance address") @@ -49,7 +55,7 @@ func Execute(ctx context.Context) { Use: "daemon", Short: "Run as a runner daemon", Args: cobra.MaximumNArgs(1), - RunE: runDaemon(ctx, task.Input.EnvFile), + RunE: runDaemon(ctx, gArgs.EnvFile), } // add all command rootCmd.AddCommand(daemonCmd) diff --git a/cmd/daemon.go b/cmd/daemon.go index 5216937..61af798 100644 --- a/cmd/daemon.go +++ b/cmd/daemon.go @@ -3,15 +3,12 @@ package cmd import ( "context" "os" - "time" "gitea.com/gitea/act_runner/client" "gitea.com/gitea/act_runner/config" "gitea.com/gitea/act_runner/engine" "gitea.com/gitea/act_runner/poller" - "gitea.com/gitea/act_runner/register" "gitea.com/gitea/act_runner/runtime" - pingv1 "gitea.com/gitea/proto-go/ping/v1" runnerv1 "gitea.com/gitea/proto-go/runner/v1" "github.com/bufbuild/connect-go" @@ -35,60 +32,6 @@ func runDaemon(ctx context.Context, envFile string) func(cmd *cobra.Command, arg initLogging(cfg) - // initial http client - cli := client.New( - cfg.Client.Address, - client.WithSkipVerify(cfg.Client.SkipVerify), - client.WithGRPC(cfg.Client.GRPC), - client.WithGRPCWeb(cfg.Client.GRPCWeb), - ) - - for { - _, err := cli.Ping(ctx, connect.NewRequest(&pingv1.PingRequest{ - Data: cfg.Runner.Name, - })) - select { - case <-ctx.Done(): - return nil - default: - } - if ctx.Err() != nil { - break - } - if err != nil { - log.WithError(err). - Errorln("cannot ping the remote server") - // TODO: if ping failed, retry or exit - time.Sleep(time.Second) - } else { - log.Infoln("successfully pinged the remote server") - break - } - } - - // register new runner - if cfg.Runner.UUID == "" { - register := register.New( - cli, - &client.Filter{ - OS: cfg.Platform.OS, - Arch: cfg.Platform.Arch, - Labels: cfg.Runner.Labels, - }, - ) - - data, err := register.Register(ctx, cfg.Runner) - if err != nil { - return err - } - if data.UUID != "" { - cfg.Runner.UUID = data.UUID - } - if data.Token != "" { - cfg.Runner.Token = data.Token - } - } - // try to connect to docker daemon // if failed, exit with error if err := engine.Start(ctx); err != nil { @@ -97,7 +40,7 @@ func runDaemon(ctx context.Context, envFile string) func(cmd *cobra.Command, arg var g errgroup.Group - cli = client.New( + cli := client.New( cfg.Client.Address, client.WithSkipVerify(cfg.Client.SkipVerify), client.WithGRPC(cfg.Client.GRPC), diff --git a/cmd/register.go b/cmd/register.go index 0a96dd4..9af4fa3 100644 --- a/cmd/register.go +++ b/cmd/register.go @@ -7,14 +7,22 @@ import ( "os/signal" "runtime" "strings" + "time" + "gitea.com/gitea/act_runner/client" + "gitea.com/gitea/act_runner/config" + "gitea.com/gitea/act_runner/register" + pingv1 "gitea.com/gitea/proto-go/ping/v1" + "github.com/appleboy/com/file" + "github.com/bufbuild/connect-go" + "github.com/joho/godotenv" "github.com/mattn/go-isatty" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) // runRegister registers a runner to the server -func runRegister(ctx context.Context, regArgs *registerArgs) func(*cobra.Command, []string) error { +func runRegister(ctx context.Context, regArgs *registerArgs, envFile string) func(*cobra.Command, []string) error { return func(cmd *cobra.Command, args []string) error { log.SetReportCaller(false) isTerm := isatty.IsTerminal(os.Stdout.Fd()) @@ -22,6 +30,7 @@ func runRegister(ctx context.Context, regArgs *registerArgs) func(*cobra.Command DisableColors: !isTerm, DisableTimestamp: true, }) + log.SetLevel(log.DebugLevel) log.Infof("Registering runner, arch=%s, os=%s, version=%s.", runtime.GOARCH, runtime.GOOS, version) @@ -33,7 +42,7 @@ func runRegister(ctx context.Context, regArgs *registerArgs) func(*cobra.Command } go func() { - if err := registerInteractive(); err != nil { + if err := registerInteractive(envFile); err != nil { // log.Errorln(err) os.Exit(2) return @@ -59,12 +68,14 @@ type registerArgs struct { type registerStage int8 const ( - StageUnknown registerStage = -1 - StageInputInstance registerStage = iota + 1 + StageUnknown registerStage = -1 + StageOverwriteLocalConfig registerStage = iota + 1 + StageInputInstance StageInputToken StageInputRunnerName StageInputCustomLabels StageWaitingForRegistration + StageExit ) type registerInputs struct { @@ -90,6 +101,11 @@ func (r *registerInputs) assignToNext(stage registerStage, value string) registe } switch stage { + case StageOverwriteLocalConfig: + if value == "Y" || value == "y" { + return StageInputInstance + } + return StageExit case StageInputInstance: r.InstanceAddr = value return StageInputToken @@ -106,15 +122,30 @@ func (r *registerInputs) assignToNext(stage registerStage, value string) registe return StageUnknown } -func registerInteractive() error { +func getLocalConfigFile(envFile string) (string, bool) { + _ = godotenv.Load(envFile) + cfg, _ := config.FromEnviron() + return cfg.Runner.File, file.IsFile(cfg.Runner.File) +} + +func registerInteractive(envFile string) error { + var ( reader = bufio.NewReader(os.Stdin) stage = StageInputInstance inputs = new(registerInputs) ) + // check if overwrite local config + _ = godotenv.Load(envFile) + cfg, _ := config.FromEnviron() + if file.IsFile(cfg.Runner.File) { + stage = StageOverwriteLocalConfig + } + for { printStageHelp(stage) + cmdString, err := reader.ReadString('\n') if err != nil { return err @@ -123,6 +154,15 @@ func registerInteractive() error { if stage == StageWaitingForRegistration { log.Infof("Registering runner, name=%s, instance=%s, labels=%v.", inputs.RunnerName, inputs.InstanceAddr, inputs.CustomLabels) + if err := doRegister(&cfg, inputs); err != nil { + log.Errorf("Failed to register runner: %v", err) + } else { + log.Infof("Runner registered successfully.") + } + return nil + } + + if stage == StageExit { return nil } @@ -135,6 +175,8 @@ func registerInteractive() error { func printStageHelp(stage registerStage) { switch stage { + case StageOverwriteLocalConfig: + log.Infoln("Runner is already registered, overwrite local config? [Y/n]") case StageInputInstance: log.Infoln("Enter the Gitea instance URL (for example, https://gitea.com/):") case StageInputToken: @@ -148,3 +190,57 @@ func printStageHelp(stage registerStage) { log.Infoln("Waiting for registration...") } } + +func doRegister(cfg *config.Config, inputs *registerInputs) error { + + ctx := context.Background() + + // initial http client + cli := client.New( + inputs.InstanceAddr, + client.WithSkipVerify(cfg.Client.SkipVerify), + client.WithGRPC(cfg.Client.GRPC), + client.WithGRPCWeb(cfg.Client.GRPCWeb), + ) + + for { + _, err := cli.Ping(ctx, connect.NewRequest(&pingv1.PingRequest{ + Data: inputs.RunnerName, + })) + select { + case <-ctx.Done(): + return nil + default: + } + if ctx.Err() != nil { + break + } + if err != nil { + log.WithError(err). + Errorln("Cannot ping the Gitea instance server") + // TODO: if ping failed, retry or exit + time.Sleep(time.Second) + } else { + log.Debugln("Successfully pinged the Gitea instance server") + break + } + } + + register := register.New( + cli, + &client.Filter{ + OS: cfg.Platform.OS, + Arch: cfg.Platform.Arch, + Labels: inputs.CustomLabels, + }, + ) + cfg.Runner.Name = inputs.RunnerName + cfg.Runner.Token = inputs.Token + _, err := register.Register(ctx, cfg.Runner) + if err != nil { + log.WithError(err).Errorln("Cannot register the runner") + return nil + } + + return nil +} diff --git a/config/config.go b/config/config.go index 5b8a3df..2390236 100644 --- a/config/config.go +++ b/config/config.go @@ -2,7 +2,6 @@ package config import ( "encoding/json" - "fmt" "io" "os" "runtime" @@ -26,8 +25,6 @@ type ( Client struct { Address string `ignored:"true"` - Proto string `envconfig:"GITEA_RPC_PROTO" default:"http"` - Host string `envconfig:"GITEA_RPC_HOST"` SkipVerify bool `envconfig:"GITEA_RPC_SKIP_VERIFY"` GRPC bool `envconfig:"GITEA_RPC_GRPC" default:"true"` GRPCWeb bool `envconfig:"GITEA_RPC_GRPC_WEB"` @@ -36,7 +33,7 @@ type ( Runner struct { UUID string `ignored:"true"` Name string `envconfig:"GITEA_RUNNER_NAME"` - Token string `envconfig:"GITEA_RUNNER_TOKEN" required:"true"` + Token string `ignored:"true"` Capacity int `envconfig:"GITEA_RUNNER_CAPACITY" default:"1"` File string `envconfig:"GITEA_RUNNER_FILE" default:".runner"` Environ map[string]string `envconfig:"GITEA_RUNNER_ENVIRON"` @@ -72,15 +69,12 @@ func FromEnviron() (Config, error) { if runner.Token != "" { cfg.Runner.Token = runner.Token } + if runner.Address != "" { + cfg.Client.Address = runner.Address + } cfg.ForgeInstance = runner.ForgeInstance } - cfg.Client.Address = fmt.Sprintf( - "%s://%s", - cfg.Client.Proto, - cfg.Client.Host, - ) - // runner config if cfg.Runner.Environ == nil { cfg.Runner.Environ = map[string]string{ diff --git a/core/runner.go b/core/runner.go index e41040e..1a03452 100644 --- a/core/runner.go +++ b/core/runner.go @@ -11,5 +11,6 @@ type Runner struct { UUID string `json:"uuid"` Name string `json:"name"` Token string `json:"token"` + Address string `json:"address"` ForgeInstance string `json:"forge_instance"` } diff --git a/register/register.go b/register/register.go index f8b9286..c4c16a2 100644 --- a/register/register.go +++ b/register/register.go @@ -42,10 +42,11 @@ func (p *Register) Register(ctx context.Context, cfg config.Runner) (*core.Runne } data := &core.Runner{ - ID: resp.Msg.Runner.Id, - UUID: resp.Msg.Runner.Uuid, - Name: resp.Msg.Runner.Name, - Token: resp.Msg.Runner.Token, + ID: resp.Msg.Runner.Id, + UUID: resp.Msg.Runner.Uuid, + Name: resp.Msg.Runner.Name, + Token: resp.Msg.Runner.Token, + Address: p.Client.Address(), // ForgeInstance: resp.Msg.Runner.ForgeInstance, TODO: add me }