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 }