go.undefinedlabs.com/scopeagent@v0.4.2/agent/remote_config.go (about)

     1  package agent
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/x509"
     6  	"encoding/json"
     7  	"errors"
     8  	"fmt"
     9  	"io/ioutil"
    10  	"net/http"
    11  	"net/url"
    12  	"time"
    13  
    14  	"go.undefinedlabs.com/scopeagent/tags"
    15  )
    16  
    17  // Loads the remote agent configuration from local cache, if not exists then retrieve it from the server
    18  func (a *Agent) loadRemoteConfiguration() map[string]interface{} {
    19  	if a == nil {
    20  		return nil
    21  	}
    22  	configResponse := a.cache.GetOrSet("remoteconfig", false, a.getRemoteConfiguration)
    23  	if configResponse != nil {
    24  		return configResponse.(map[string]interface{})
    25  	}
    26  	return nil
    27  }
    28  
    29  func (a *Agent) getRemoteConfigRequest() map[string]interface{} {
    30  	if a == nil || a.metadata == nil {
    31  		return nil
    32  	}
    33  	configRequest := map[string]interface{}{}
    34  	addElementToMapIfEmpty(configRequest, tags.Repository, a.metadata[tags.Repository])
    35  	addElementToMapIfEmpty(configRequest, tags.Commit, a.metadata[tags.Commit])
    36  	addElementToMapIfEmpty(configRequest, tags.Branch, a.metadata[tags.Branch])
    37  	addElementToMapIfEmpty(configRequest, tags.Service, a.metadata[tags.Service])
    38  	addElementToMapIfEmpty(configRequest, tags.Dependencies, a.metadata[tags.Dependencies])
    39  	if cKeys, ok := a.metadata[tags.ConfigurationKeys]; ok {
    40  		cfgKeys := cKeys.([]string)
    41  		configRequest[tags.ConfigurationKeys] = cfgKeys
    42  		for _, item := range cfgKeys {
    43  			addElementToMapIfEmpty(configRequest, item, a.metadata[item])
    44  		}
    45  	}
    46  	if a.debugMode {
    47  		jsBytes, _ := json.Marshal(configRequest)
    48  		a.logger.Printf("Configuration request: %v", string(jsBytes))
    49  	}
    50  	return configRequest
    51  }
    52  
    53  // Gets the remote agent configuration from the endpoint + api/agent/config
    54  func (a *Agent) getRemoteConfiguration(cfgRequest interface{}, key string) interface{} {
    55  	client := &http.Client{}
    56  	curl := a.getUrl("api/agent/config")
    57  	payload, err := msgPackEncodePayload(cfgRequest)
    58  	if err != nil {
    59  		a.logger.Printf("Error encoding payload: %v", err)
    60  	}
    61  	payloadBytes := payload.Bytes()
    62  
    63  	var (
    64  		lastError  error
    65  		status     string
    66  		statusCode int
    67  		bodyData   []byte
    68  	)
    69  	for i := 0; i <= numOfRetries; i++ {
    70  		req, err := http.NewRequest("POST", curl, bytes.NewBuffer(payloadBytes))
    71  		if err != nil {
    72  			a.logger.Printf("Error creating new request: %v", err)
    73  			return nil
    74  		}
    75  		req.Header.Set("User-Agent", a.userAgent)
    76  		req.Header.Set("Content-Type", "application/msgpack")
    77  		req.Header.Set("Content-Encoding", "gzip")
    78  		req.Header.Set("X-Scope-ApiKey", a.apiKey)
    79  
    80  		if a.debugMode {
    81  			if i == 0 {
    82  				a.logger.Println("sending payload")
    83  			} else {
    84  				a.logger.Printf("sending payload [retry %d]", i)
    85  			}
    86  		}
    87  
    88  		resp, err := client.Do(req)
    89  		if err != nil {
    90  			if v, ok := err.(*url.Error); ok {
    91  				// Don't retry if the error was due to TLS cert verification failure.
    92  				if _, ok := v.Err.(x509.UnknownAuthorityError); ok {
    93  					a.logger.Printf("error: http client returns: %s", err.Error())
    94  					return nil
    95  				}
    96  			}
    97  
    98  			lastError = err
    99  			a.logger.Printf("client error '%s', retrying in %d seconds", err.Error(), retryBackoff/time.Second)
   100  			time.Sleep(retryBackoff)
   101  			continue
   102  		}
   103  
   104  		statusCode = resp.StatusCode
   105  		status = resp.Status
   106  		if resp.Body != nil && resp.Body != http.NoBody {
   107  			body, err := ioutil.ReadAll(resp.Body)
   108  			if err == nil {
   109  				bodyData = body
   110  			}
   111  		}
   112  		if err := resp.Body.Close(); err != nil { // We can't defer inside a for loop
   113  			a.logger.Printf("error: closing the response body. %s", err.Error())
   114  		}
   115  
   116  		if statusCode == 0 || statusCode >= 400 {
   117  			lastError = errors.New(fmt.Sprintf("error from API [status: %s]: %s", status, string(bodyData)))
   118  		}
   119  
   120  		// Check the response code. We retry on 500-range responses to allow
   121  		// the server time to recover, as 500's are typically not permanent
   122  		// errors and may relate to outages on the server side. This will catch
   123  		// invalid response codes as well, like 0 and 999.
   124  		if statusCode == 0 || (statusCode >= 500 && statusCode != 501) {
   125  			a.logger.Printf("error: [status code: %d], retrying in %d seconds", statusCode, retryBackoff/time.Second)
   126  			time.Sleep(retryBackoff)
   127  			continue
   128  		}
   129  
   130  		if i > 0 {
   131  			a.logger.Printf("payload was sent successfully after retry.")
   132  		}
   133  		break
   134  	}
   135  
   136  	if statusCode != 0 && statusCode < 400 && lastError == nil {
   137  		var resp map[string]interface{}
   138  		if err := json.Unmarshal(bodyData, &resp); err == nil {
   139  			return resp
   140  		} else {
   141  			a.logger.Printf("Error unmarshalling json: %v", err)
   142  		}
   143  	}
   144  	return nil
   145  }