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 }