gitlab.com/jfprevost/gitlab-runner-notlscheck@v11.11.4+incompatible/commands/register.go (about)

     1  package commands
     2  
     3  import (
     4  	"bufio"
     5  	"os"
     6  	"os/signal"
     7  	"strings"
     8  
     9  	"github.com/sirupsen/logrus"
    10  	"github.com/urfave/cli"
    11  
    12  	"gitlab.com/gitlab-org/gitlab-runner/common"
    13  	"gitlab.com/gitlab-org/gitlab-runner/helpers/ssh"
    14  	"gitlab.com/gitlab-org/gitlab-runner/network"
    15  )
    16  
    17  type RegisterCommand struct {
    18  	context    *cli.Context
    19  	network    common.Network
    20  	reader     *bufio.Reader
    21  	registered bool
    22  
    23  	configOptions
    24  	TagList           string `long:"tag-list" env:"RUNNER_TAG_LIST" description:"Tag list"`
    25  	NonInteractive    bool   `short:"n" long:"non-interactive" env:"REGISTER_NON_INTERACTIVE" description:"Run registration unattended"`
    26  	LeaveRunner       bool   `long:"leave-runner" env:"REGISTER_LEAVE_RUNNER" description:"Don't remove runner if registration fails"`
    27  	RegistrationToken string `short:"r" long:"registration-token" env:"REGISTRATION_TOKEN" description:"Runner's registration token"`
    28  	RunUntagged       bool   `long:"run-untagged" env:"REGISTER_RUN_UNTAGGED" description:"Register to run untagged builds; defaults to 'true' when 'tag-list' is empty"`
    29  	Locked            bool   `long:"locked" env:"REGISTER_LOCKED" description:"Lock Runner for current project, defaults to 'true'"`
    30  	MaximumTimeout    int    `long:"maximum-timeout" env:"REGISTER_MAXIMUM_TIMEOUT" description:"What is the maximum timeout (in seconds) that will be set for job when using this Runner"`
    31  	Paused            bool   `long:"paused" env:"REGISTER_PAUSED" description:"Set Runner to be paused, defaults to 'false'"`
    32  
    33  	common.RunnerConfig
    34  }
    35  
    36  func (s *RegisterCommand) askOnce(prompt string, result *string, allowEmpty bool) bool {
    37  	println(prompt)
    38  	if *result != "" {
    39  		print("["+*result, "]: ")
    40  	}
    41  
    42  	if s.reader == nil {
    43  		s.reader = bufio.NewReader(os.Stdin)
    44  	}
    45  
    46  	data, _, err := s.reader.ReadLine()
    47  	if err != nil {
    48  		panic(err)
    49  	}
    50  	newResult := string(data)
    51  	newResult = strings.TrimSpace(newResult)
    52  
    53  	if newResult != "" {
    54  		*result = newResult
    55  		return true
    56  	}
    57  
    58  	if allowEmpty || *result != "" {
    59  		return true
    60  	}
    61  	return false
    62  }
    63  
    64  func (s *RegisterCommand) ask(key, prompt string, allowEmptyOptional ...bool) string {
    65  	allowEmpty := len(allowEmptyOptional) > 0 && allowEmptyOptional[0]
    66  
    67  	result := s.context.String(key)
    68  	result = strings.TrimSpace(result)
    69  
    70  	if s.NonInteractive || prompt == "" {
    71  		if result == "" && !allowEmpty {
    72  			logrus.Panicln("The", key, "needs to be entered")
    73  		}
    74  		return result
    75  	}
    76  
    77  	for {
    78  		if s.askOnce(prompt, &result, allowEmpty) {
    79  			break
    80  		}
    81  	}
    82  
    83  	return result
    84  }
    85  
    86  func (s *RegisterCommand) askExecutor() {
    87  	for {
    88  		names := common.GetExecutors()
    89  		executors := strings.Join(names, ", ")
    90  		s.Executor = s.ask("executor", "Please enter the executor: "+executors+":", true)
    91  		if common.GetExecutor(s.Executor) != nil {
    92  			return
    93  		}
    94  
    95  		message := "Invalid executor specified"
    96  		if s.NonInteractive {
    97  			logrus.Panicln(message)
    98  		} else {
    99  			logrus.Errorln(message)
   100  		}
   101  	}
   102  }
   103  
   104  func (s *RegisterCommand) askDocker() {
   105  	if s.Docker == nil {
   106  		s.Docker = &common.DockerConfig{}
   107  	}
   108  	s.Docker.Image = s.ask("docker-image", "Please enter the default Docker image (e.g. ruby:2.1):")
   109  
   110  	for _, volume := range s.Docker.Volumes {
   111  		parts := strings.Split(volume, ":")
   112  		if parts[len(parts)-1] == "/cache" {
   113  			return
   114  		}
   115  	}
   116  	s.Docker.Volumes = append(s.Docker.Volumes, "/cache")
   117  }
   118  
   119  func (s *RegisterCommand) askParallels() {
   120  	s.Parallels.BaseName = s.ask("parallels-base-name", "Please enter the Parallels VM (e.g. my-vm):")
   121  }
   122  
   123  func (s *RegisterCommand) askVirtualBox() {
   124  	s.VirtualBox.BaseName = s.ask("virtualbox-base-name", "Please enter the VirtualBox VM (e.g. my-vm):")
   125  }
   126  
   127  func (s *RegisterCommand) askSSHServer() {
   128  	s.SSH.Host = s.ask("ssh-host", "Please enter the SSH server address (e.g. my.server.com):")
   129  	s.SSH.Port = s.ask("ssh-port", "Please enter the SSH server port (e.g. 22):", true)
   130  }
   131  
   132  func (s *RegisterCommand) askSSHLogin() {
   133  	s.SSH.User = s.ask("ssh-user", "Please enter the SSH user (e.g. root):")
   134  	s.SSH.Password = s.ask("ssh-password", "Please enter the SSH password (e.g. docker.io):", true)
   135  	s.SSH.IdentityFile = s.ask("ssh-identity-file", "Please enter path to SSH identity file (e.g. /home/user/.ssh/id_rsa):", true)
   136  }
   137  
   138  func (s *RegisterCommand) addRunner(runner *common.RunnerConfig) {
   139  	s.config.Runners = append(s.config.Runners, runner)
   140  }
   141  
   142  func (s *RegisterCommand) askRunner() {
   143  	s.URL = s.ask("url", "Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com/):")
   144  
   145  	if s.Token != "" {
   146  		logrus.Infoln("Token specified trying to verify runner...")
   147  		logrus.Warningln("If you want to register use the '-r' instead of '-t'.")
   148  		if !s.network.VerifyRunner(s.RunnerCredentials) {
   149  			logrus.Panicln("Failed to verify this runner. Perhaps you are having network problems")
   150  		}
   151  	} else {
   152  		// we store registration token as token, since we pass that to RunnerCredentials
   153  		s.Token = s.ask("registration-token", "Please enter the gitlab-ci token for this runner:")
   154  		s.Name = s.ask("name", "Please enter the gitlab-ci description for this runner:")
   155  		s.TagList = s.ask("tag-list", "Please enter the gitlab-ci tags for this runner (comma separated):", true)
   156  
   157  		if s.TagList == "" {
   158  			s.RunUntagged = true
   159  		}
   160  
   161  		parameters := common.RegisterRunnerParameters{
   162  			Description:    s.Name,
   163  			Tags:           s.TagList,
   164  			Locked:         s.Locked,
   165  			RunUntagged:    s.RunUntagged,
   166  			MaximumTimeout: s.MaximumTimeout,
   167  			Active:         !s.Paused,
   168  		}
   169  
   170  		result := s.network.RegisterRunner(s.RunnerCredentials, parameters)
   171  		if result == nil {
   172  			logrus.Panicln("Failed to register this runner. Perhaps you are having network problems")
   173  		}
   174  
   175  		s.Token = result.Token
   176  		s.registered = true
   177  	}
   178  }
   179  
   180  func (s *RegisterCommand) askExecutorOptions() {
   181  	kubernetes := s.Kubernetes
   182  	machine := s.Machine
   183  	docker := s.Docker
   184  	ssh := s.SSH
   185  	parallels := s.Parallels
   186  	virtualbox := s.VirtualBox
   187  
   188  	s.Kubernetes = nil
   189  	s.Machine = nil
   190  	s.Docker = nil
   191  	s.SSH = nil
   192  	s.Parallels = nil
   193  	s.VirtualBox = nil
   194  
   195  	switch s.Executor {
   196  	case "kubernetes":
   197  		s.Kubernetes = kubernetes
   198  	case "docker+machine":
   199  		s.Machine = machine
   200  		s.Docker = docker
   201  		s.askDocker()
   202  	case "docker-ssh+machine":
   203  		s.Machine = machine
   204  		s.Docker = docker
   205  		s.SSH = ssh
   206  		s.askDocker()
   207  		s.askSSHLogin()
   208  	case "docker":
   209  		s.Docker = docker
   210  		s.askDocker()
   211  	case "docker-ssh":
   212  		s.Docker = docker
   213  		s.SSH = ssh
   214  		s.askDocker()
   215  		s.askSSHLogin()
   216  	case "ssh":
   217  		s.SSH = ssh
   218  		s.askSSHServer()
   219  		s.askSSHLogin()
   220  	case "parallels":
   221  		s.SSH = ssh
   222  		s.Parallels = parallels
   223  		s.askParallels()
   224  		s.askSSHServer()
   225  	case "virtualbox":
   226  		s.SSH = ssh
   227  		s.VirtualBox = virtualbox
   228  		s.askVirtualBox()
   229  		s.askSSHLogin()
   230  	}
   231  }
   232  
   233  // DEPRECATED
   234  // TODO: Remove in 12.0
   235  //
   236  // Writes cache configuration section using new syntax, even if
   237  // old CLI options/env variables were used.
   238  func (s *RegisterCommand) prepareCache() {
   239  	cache := s.RunnerConfig.Cache
   240  	if cache == nil {
   241  		return
   242  	}
   243  
   244  	// Called to log deprecated usage, if old cli options/env variables are used
   245  	cache.Path = cache.GetPath()
   246  	cache.Shared = cache.GetShared()
   247  
   248  	// Called to assign values and log deprecated usage, if old env variables are used
   249  	setStringIfUnset(&cache.S3.ServerAddress, cache.GetServerAddress())
   250  	setStringIfUnset(&cache.S3.AccessKey, cache.GetAccessKey())
   251  	setStringIfUnset(&cache.S3.SecretKey, cache.GetSecretKey())
   252  	setStringIfUnset(&cache.S3.BucketName, cache.GetBucketName())
   253  	setStringIfUnset(&cache.S3.BucketLocation, cache.GetBucketLocation())
   254  	setBoolIfUnset(&cache.S3.Insecure, cache.GetInsecure())
   255  }
   256  
   257  // TODO: Remove in 12.0
   258  func setStringIfUnset(setting *string, newSetting string) {
   259  	if *setting != "" {
   260  		return
   261  	}
   262  
   263  	*setting = newSetting
   264  }
   265  
   266  // TODO: Remove in 12.0
   267  func setBoolIfUnset(setting *bool, newSetting bool) {
   268  	if *setting {
   269  		return
   270  	}
   271  
   272  	*setting = newSetting
   273  }
   274  
   275  func (s *RegisterCommand) Execute(context *cli.Context) {
   276  	userModeWarning(true)
   277  
   278  	s.context = context
   279  	err := s.loadConfig()
   280  	if err != nil {
   281  		logrus.Panicln(err)
   282  	}
   283  	s.askRunner()
   284  
   285  	if !s.LeaveRunner {
   286  		defer func() {
   287  			// De-register runner on panic
   288  			if r := recover(); r != nil {
   289  				if s.registered {
   290  					s.network.UnregisterRunner(s.RunnerCredentials)
   291  				}
   292  
   293  				// pass panic to next defer
   294  				panic(r)
   295  			}
   296  		}()
   297  
   298  		signals := make(chan os.Signal, 1)
   299  		signal.Notify(signals, os.Interrupt)
   300  
   301  		go func() {
   302  			signal := <-signals
   303  			s.network.UnregisterRunner(s.RunnerCredentials)
   304  			logrus.Fatalf("RECEIVED SIGNAL: %v", signal)
   305  		}()
   306  	}
   307  
   308  	if s.config.Concurrent < s.Limit {
   309  		logrus.Warningf("Specified limit (%d) larger then current concurrent limit (%d). Concurrent limit will not be enlarged.", s.Limit, s.config.Concurrent)
   310  	}
   311  
   312  	s.askExecutor()
   313  	s.askExecutorOptions()
   314  	s.addRunner(&s.RunnerConfig)
   315  	s.prepareCache()
   316  	s.saveConfig()
   317  
   318  	logrus.Printf("Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!")
   319  }
   320  
   321  func getHostname() string {
   322  	hostname, _ := os.Hostname()
   323  	return hostname
   324  }
   325  
   326  func newRegisterCommand() *RegisterCommand {
   327  	return &RegisterCommand{
   328  		RunnerConfig: common.RunnerConfig{
   329  			Name: getHostname(),
   330  			RunnerSettings: common.RunnerSettings{
   331  				Kubernetes: &common.KubernetesConfig{},
   332  				Cache:      &common.CacheConfig{},
   333  				Machine:    &common.DockerMachine{},
   334  				Docker:     &common.DockerConfig{},
   335  				SSH:        &ssh.Config{},
   336  				Parallels:  &common.ParallelsConfig{},
   337  				VirtualBox: &common.VirtualBoxConfig{},
   338  			},
   339  		},
   340  		Locked:  true,
   341  		Paused:  false,
   342  		network: network.NewGitLabClient(),
   343  	}
   344  }
   345  
   346  func init() {
   347  	common.RegisterCommand2("register", "register a new runner", newRegisterCommand())
   348  }