31f61d7531
The user-cloudinit-proc-cmdline systemd unit is responsible for fetching user-data from various sources during the cloud-init process. When fetching user-data from an URL datasource we face a race condition since the network may not be available, which can cause the job to fail and no further attempts to fetch the user-data are made. Eliminate the race condition when fetching user-data from an URL datasource. Retry the fetch using an exponential backoff until the user-data is retrieved. Fixes issue 105.
48 lines
1.0 KiB
Go
48 lines
1.0 KiB
Go
package datasource
|
|
|
|
import (
|
|
"io/ioutil"
|
|
"log"
|
|
"math"
|
|
"net/http"
|
|
"time"
|
|
)
|
|
|
|
type Datasource interface {
|
|
Fetch() ([]byte, error)
|
|
Type() string
|
|
}
|
|
|
|
func fetchURL(url string) ([]byte, error) {
|
|
resp, err := getWithExponentialBackoff(url)
|
|
if err != nil {
|
|
return []byte{}, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
respBytes, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return respBytes, nil
|
|
}
|
|
|
|
// getWithExponentialBackoff issues a GET to the specified URL. If the
|
|
// response is a non-2xx or produces an error, retry the GET forever using
|
|
// an exponential backoff.
|
|
func getWithExponentialBackoff(url string) (*http.Response, error) {
|
|
var err error
|
|
var resp *http.Response
|
|
for i := 0; ; i++ {
|
|
resp, err = http.Get(url)
|
|
if err == nil && resp.StatusCode/100 == 2 {
|
|
return resp, nil
|
|
}
|
|
duration := time.Millisecond * time.Duration((math.Pow(float64(2), float64(i)) * 100))
|
|
log.Printf("unable to fetch user-data from %s, try again in %s", url, duration)
|
|
time.Sleep(duration)
|
|
}
|
|
return resp, err
|
|
}
|