github.com/pquerna/agent@v2.1.8+incompatible/agent/agent_pool.go (about)

     1  package agent
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"runtime"
     7  	"time"
     8  
     9  	"github.com/buildkite/agent/api"
    10  	"github.com/buildkite/agent/logger"
    11  	"github.com/buildkite/agent/retry"
    12  	"github.com/buildkite/agent/signalwatcher"
    13  	"github.com/buildkite/agent/system"
    14  )
    15  
    16  type AgentPool struct {
    17  	APIClient          *api.Client
    18  	Token              string
    19  	ConfigFilePath     string
    20  	Name               string
    21  	Priority           string
    22  	MetaData           []string
    23  	MetaDataEC2Tags    bool
    24  	Endpoint           string
    25  	AgentConfiguration *AgentConfiguration
    26  }
    27  
    28  func (r *AgentPool) Start() error {
    29  	// Show the welcome banner and config options used
    30  	r.ShowBanner()
    31  
    32  	// Create the agent registration API Client
    33  	r.APIClient = APIClient{Endpoint: r.Endpoint, Token: r.Token}.Create()
    34  
    35  	// Create the agent template. We use pass this template to the register
    36  	// call, at which point we get back a real agent.
    37  	template := r.CreateAgentTemplate()
    38  
    39  	logger.Info("Registering agent with Buildkite...")
    40  
    41  	// Register the agent
    42  	registered, err := r.RegisterAgent(template)
    43  	if err != nil {
    44  		logger.Fatal("%s", err)
    45  	}
    46  
    47  	logger.Info("Successfully registered agent \"%s\" with meta-data %s", registered.Name, registered.MetaData)
    48  
    49  	logger.Debug("Ping interval: %ds", registered.PingInterval)
    50  	logger.Debug("Heartbeat interval: %ds", registered.HearbeatInterval)
    51  
    52  	// Now that we have a registered agent, we can connect it to the API,
    53  	// and start running jobs.
    54  	worker := AgentWorker{Agent: registered, AgentConfiguration: r.AgentConfiguration, Endpoint: r.Endpoint}.Create()
    55  
    56  	logger.Info("Connecting to Buildkite...")
    57  	if err := worker.Connect(); err != nil {
    58  		logger.Fatal("%s", err)
    59  	}
    60  
    61  	logger.Info("Agent successfully connected")
    62  	logger.Info("You can press Ctrl-C to stop the agent")
    63  	logger.Info("Waiting for work...")
    64  
    65  	// Start a signalwatcher so we can monitor signals and handle shutdowns
    66  	signalwatcher.Watch(func(sig signalwatcher.Signal) {
    67  		if sig == signalwatcher.QUIT {
    68  			logger.Debug("Received signal `%s`", sig.String())
    69  			worker.Stop(false)
    70  		} else if sig == signalwatcher.TERM || sig == signalwatcher.INT {
    71  			logger.Debug("Received signal `%s`", sig.String())
    72  			worker.Stop(true)
    73  		} else {
    74  			logger.Debug("Ignoring signal `%s`", sig.String())
    75  		}
    76  	})
    77  
    78  	// Starts the agent worker. This will block until the agent has
    79  	// finished or is stopped.
    80  	if err := worker.Start(); err != nil {
    81  		logger.Fatal("%s", err)
    82  	}
    83  
    84  	// Now that the agent has stopped, we can disconnect it
    85  	logger.Info("Disconnecting %s...", worker.Agent.Name)
    86  	worker.Disconnect()
    87  
    88  	return nil
    89  }
    90  
    91  // Takes the options passed to the CLI, and creates an api.Agent record that
    92  // will be sent to the Buildkite Agent API for registration.
    93  func (r *AgentPool) CreateAgentTemplate() *api.Agent {
    94  	agent := &api.Agent{
    95  		Name:              r.Name,
    96  		Priority:          r.Priority,
    97  		MetaData:          r.MetaData,
    98  		ScriptEvalEnabled: r.AgentConfiguration.CommandEval,
    99  		Version:           Version(),
   100  		Build:             BuildVersion(),
   101  		PID:               os.Getpid(),
   102  		Arch:              runtime.GOARCH,
   103  	}
   104  
   105  	// Attempt to add the EC2 tags
   106  	if r.MetaDataEC2Tags {
   107  		tags, err := EC2Tags{}.Get()
   108  		if err != nil {
   109  			// Don't blow up if we can't find them, just show a nasty error.
   110  			logger.Error(fmt.Sprintf("Failed to find EC2 Tags: %s", err.Error()))
   111  		} else {
   112  			for tag, value := range tags {
   113  				agent.MetaData = append(agent.MetaData, fmt.Sprintf("%s=%s", tag, value))
   114  			}
   115  		}
   116  	}
   117  
   118  	var err error
   119  
   120  	// Add the hostname
   121  	agent.Hostname, err = os.Hostname()
   122  	if err != nil {
   123  		logger.Warn("Failed to find hostname: %s", err)
   124  	}
   125  
   126  	// Add the OS dump
   127  	agent.OS, err = system.VersionDump()
   128  	if err != nil {
   129  		logger.Warn("Failed to find OS information: %s", err)
   130  	}
   131  
   132  	return agent
   133  }
   134  
   135  // Takes the agent template and returns a registered agent. The registered
   136  // agent includes the Access Token used to communicate with the Buildkite Agent
   137  // API
   138  func (r *AgentPool) RegisterAgent(agent *api.Agent) (*api.Agent, error) {
   139  	var registered *api.Agent
   140  	var err error
   141  	var resp *api.Response
   142  
   143  	register := func(s *retry.Stats) error {
   144  		registered, resp, err = r.APIClient.Agents.Register(agent)
   145  		if err != nil {
   146  			if resp != nil && resp.StatusCode == 401 {
   147  				logger.Warn("Buildkite rejected the registration (%s)", err)
   148  				s.Break()
   149  			} else {
   150  				logger.Warn("%s (%s)", err, s)
   151  			}
   152  		}
   153  
   154  		return err
   155  	}
   156  
   157  	// Try to register, retrying every 10 seconds for a maximum of 30 attempts (5 minutes)
   158  	err = retry.Do(register, &retry.Config{Maximum: 30, Interval: 10 * time.Second})
   159  
   160  	return registered, err
   161  }
   162  
   163  // Shows the welcome banner and the configuration options used when starting
   164  // this agent.
   165  func (r *AgentPool) ShowBanner() {
   166  	welcomeMessage :=
   167  		"\n" +
   168  			"%s  _           _ _     _ _    _ _                                _\n" +
   169  			" | |         (_) |   | | |  (_) |                              | |\n" +
   170  			" | |__  _   _ _| | __| | | ___| |_ ___    __ _  __ _  ___ _ __ | |_\n" +
   171  			" | '_ \\| | | | | |/ _` | |/ / | __/ _ \\  / _` |/ _` |/ _ \\ '_ \\| __|\n" +
   172  			" | |_) | |_| | | | (_| |   <| | ||  __/ | (_| | (_| |  __/ | | | |_\n" +
   173  			" |_.__/ \\__,_|_|_|\\__,_|_|\\_\\_|\\__\\___|  \\__,_|\\__, |\\___|_| |_|\\__|\n" +
   174  			"                                                __/ |\n" +
   175  			" http://buildkite.com/agent                    |___/\n%s\n"
   176  
   177  	if logger.ColorsEnabled() {
   178  		fmt.Fprintf(logger.OutputPipe(), welcomeMessage, "\x1b[32m", "\x1b[0m")
   179  	} else {
   180  		fmt.Fprintf(logger.OutputPipe(), welcomeMessage, "", "")
   181  	}
   182  
   183  	logger.Notice("Starting buildkite-agent v%s with PID: %s", Version(), fmt.Sprintf("%d", os.Getpid()))
   184  	logger.Notice("The agent source code can be found here: https://github.com/buildkite/agent")
   185  	logger.Notice("For questions and support, email us at: hello@buildkite.com")
   186  
   187  	if r.ConfigFilePath != "" {
   188  		logger.Info("Configuration loaded from: %s", r.ConfigFilePath)
   189  	}
   190  
   191  	logger.Debug("Bootstrap script: %s", r.AgentConfiguration.BootstrapScript)
   192  	logger.Debug("Build path: %s", r.AgentConfiguration.BuildPath)
   193  	logger.Debug("Hooks directory: %s", r.AgentConfiguration.HooksPath)
   194  
   195  	if !r.AgentConfiguration.AutoSSHFingerprintVerification {
   196  		logger.Debug("Automatic SSH fingerprint verification has been disabled")
   197  	}
   198  
   199  	if !r.AgentConfiguration.CommandEval {
   200  		logger.Debug("Evaluating console commands has been disabled")
   201  	}
   202  
   203  	if !r.AgentConfiguration.RunInPty {
   204  		logger.Debug("Running builds within a pseudoterminal (PTY) has been disabled")
   205  	}
   206  }