github.com/ssube/gitlab-ci-multi-runner@v1.2.1-0.20160607142738-b8d1285632e6/executors/virtualbox/executor_virtualbox.go (about) 1 package virtualbox 2 3 import ( 4 "errors" 5 "fmt" 6 "gitlab.com/gitlab-org/gitlab-ci-multi-runner/common" 7 "gitlab.com/gitlab-org/gitlab-ci-multi-runner/executors" 8 "gitlab.com/gitlab-org/gitlab-ci-multi-runner/helpers/ssh" 9 vbox "gitlab.com/gitlab-org/gitlab-ci-multi-runner/helpers/virtualbox" 10 "time" 11 ) 12 13 type executor struct { 14 executors.AbstractExecutor 15 vmName string 16 sshCommand ssh.Client 17 sshPort string 18 provisioned bool 19 machineVerified bool 20 } 21 22 func (s *executor) verifyMachine(vmName string, sshPort string) error { 23 if s.machineVerified { 24 return nil 25 } 26 27 // Create SSH command 28 sshCommand := ssh.Client{ 29 Config: *s.Config.SSH, 30 Stdout: s.BuildLog, 31 Stderr: s.BuildLog, 32 ConnectRetries: 30, 33 } 34 sshCommand.Port = sshPort 35 sshCommand.Host = "localhost" 36 37 s.Debugln("Connecting to SSH...") 38 err := sshCommand.Connect() 39 if err != nil { 40 return err 41 } 42 defer sshCommand.Cleanup() 43 err = sshCommand.Run(ssh.Command{Command: []string{"exit"}}) 44 if err != nil { 45 return err 46 } 47 s.machineVerified = true 48 return nil 49 } 50 51 func (s *executor) restoreFromSnapshot() error { 52 s.Debugln("Reverting VM to current snapshot...") 53 err := vbox.RevertToSnapshot(s.vmName) 54 if err != nil { 55 return err 56 } 57 58 return nil 59 } 60 61 // virtualbox doesn't support templates 62 func (s *executor) createVM(vmName string) (err error) { 63 baseImage := s.Config.VirtualBox.BaseName 64 if baseImage == "" { 65 return errors.New("Missing Image setting from VirtualBox configuration") 66 } 67 68 _, err = vbox.Status(vmName) 69 if err != nil { 70 vbox.Unregister(vmName) 71 } 72 73 if !vbox.Exist(vmName) { 74 s.Debugln("Creating testing VM from VM", baseImage, "...") 75 err := vbox.CreateOsVM(baseImage, vmName) 76 if err != nil { 77 return err 78 } 79 } 80 81 s.Debugln("Identify SSH Port...") 82 s.sshPort, err = vbox.FindSSHPort(s.vmName) 83 if err != nil { 84 s.Debugln("Creating localhost ssh forwarding...") 85 vmSSHPort := s.Config.SSH.Port 86 if vmSSHPort == "" { 87 vmSSHPort = "22" 88 } 89 s.sshPort, err = vbox.ConfigureSSH(vmName, vmSSHPort) 90 if err != nil { 91 return err 92 } 93 } 94 s.Debugln("Using local", s.sshPort, "SSH port to connect to VM...") 95 96 s.Debugln("Bootstraping VM...") 97 err = vbox.Start(s.vmName) 98 if err != nil { 99 return err 100 } 101 102 s.Debugln("Waiting for VM to become responsive...") 103 time.Sleep(10) 104 err = s.verifyMachine(s.vmName, s.sshPort) 105 if err != nil { 106 return err 107 } 108 109 return nil 110 } 111 112 func (s *executor) Prepare(globalConfig *common.Config, config *common.RunnerConfig, build *common.Build) error { 113 err := s.AbstractExecutor.Prepare(globalConfig, config, build) 114 if err != nil { 115 return err 116 } 117 118 if s.BuildScript.PassFile { 119 return errors.New("virtualbox doesn't support shells that require script file") 120 } 121 122 if s.Config.SSH == nil { 123 return errors.New("Missing SSH config") 124 } 125 126 if s.Config.VirtualBox == nil { 127 return errors.New("Missing VirtualBox configuration") 128 } 129 130 if s.Config.VirtualBox.BaseName == "" { 131 return errors.New("Missing BaseName setting from VirtualBox configuration") 132 } 133 134 version, err := vbox.Version() 135 if err != nil { 136 return err 137 } 138 139 s.Println("Using VirtualBox version", version, "executor...") 140 141 if s.Config.VirtualBox.DisableSnapshots { 142 s.vmName = s.Config.VirtualBox.BaseName + "-" + s.Build.ProjectUniqueName() 143 if vbox.Exist(s.vmName) { 144 s.Debugln("Deleting old VM...") 145 vbox.Stop(s.vmName) 146 vbox.Delete(s.vmName) 147 vbox.Unregister(s.vmName) 148 } 149 } else { 150 s.vmName = fmt.Sprintf("%s-runner-%s-concurrent-%d", 151 s.Config.VirtualBox.BaseName, 152 s.Build.Runner.ShortDescription(), 153 s.Build.RunnerID) 154 } 155 156 if vbox.Exist(s.vmName) { 157 s.Println("Restoring VM from snapshot...") 158 err := s.restoreFromSnapshot() 159 if err != nil { 160 s.Println("Previous VM failed. Deleting, because", err) 161 vbox.Stop(s.vmName) 162 vbox.Delete(s.vmName) 163 vbox.Unregister(s.vmName) 164 } 165 } 166 167 if !vbox.Exist(s.vmName) { 168 s.Println("Creating new VM...") 169 err := s.createVM(s.vmName) 170 if err != nil { 171 return err 172 } 173 174 if !s.Config.VirtualBox.DisableSnapshots { 175 s.Println("Creating default snapshot...") 176 err = vbox.CreateSnapshot(s.vmName, "Started") 177 if err != nil { 178 return err 179 } 180 } 181 } 182 183 s.Debugln("Checking VM status...") 184 status, err := vbox.Status(s.vmName) 185 if err != nil { 186 return err 187 } 188 189 if !vbox.IsStatusOnlineOrTransient(status) { 190 s.Println("Starting VM...") 191 err := vbox.Start(s.vmName) 192 if err != nil { 193 return err 194 } 195 } 196 197 if status != vbox.Running { 198 s.Debugln("Waiting for VM to run...") 199 err = vbox.WaitForStatus(s.vmName, vbox.Running, 60) 200 if err != nil { 201 return err 202 } 203 } 204 205 s.Debugln("Identify SSH Port...") 206 sshPort, err := vbox.FindSSHPort(s.vmName) 207 s.sshPort = sshPort 208 if err != nil { 209 return err 210 } 211 212 s.Println("Waiting VM to become responsive...") 213 err = s.verifyMachine(s.vmName, s.sshPort) 214 if err != nil { 215 return err 216 } 217 218 s.provisioned = true 219 220 s.Println("Starting SSH command...") 221 s.sshCommand = ssh.Client{ 222 Config: *s.Config.SSH, 223 Stdout: s.BuildLog, 224 Stderr: s.BuildLog, 225 } 226 s.sshCommand.Port = s.sshPort 227 s.sshCommand.Host = "localhost" 228 229 s.Debugln("Connecting to SSH server...") 230 err = s.sshCommand.Connect() 231 if err != nil { 232 return err 233 } 234 return nil 235 } 236 237 func (s *executor) Run(cmd common.ExecutorCommand) error { 238 return s.sshCommand.Run(ssh.Command{ 239 Environment: s.BuildScript.Environment, 240 Command: s.BuildScript.GetCommandWithArguments(), 241 Stdin: cmd.Script, 242 Abort: cmd.Abort, 243 }) 244 } 245 246 func (s *executor) Cleanup() { 247 s.sshCommand.Cleanup() 248 249 if s.vmName != "" { 250 vbox.Kill(s.vmName) 251 252 if s.Config.VirtualBox.DisableSnapshots || !s.provisioned { 253 vbox.Delete(s.vmName) 254 } 255 } 256 } 257 258 func init() { 259 options := executors.ExecutorOptions{ 260 DefaultBuildsDir: "builds", 261 SharedBuildsDir: false, 262 Shell: common.ShellScriptInfo{ 263 Shell: "bash", 264 Type: common.LoginShell, 265 }, 266 ShowHostname: true, 267 } 268 269 creator := func() common.Executor { 270 return &executor{ 271 AbstractExecutor: executors.AbstractExecutor{ 272 ExecutorOptions: options, 273 }, 274 } 275 } 276 277 featuresUpdater := func(features *common.FeaturesInfo) { 278 features.Variables = true 279 } 280 281 common.RegisterExecutor("virtualbox", executors.DefaultExecutorProvider{ 282 Creator: creator, 283 FeaturesUpdater: featuresUpdater, 284 }) 285 }