github.com/nginxinc/kubernetes-ingress@v1.12.5/internal/nginx/verify.go (about) 1 package nginx 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "html/template" 8 "io/ioutil" 9 "net" 10 "net/http" 11 "strconv" 12 "time" 13 14 "github.com/golang/glog" 15 ) 16 17 // verifyClient is a client for verifying the config version. 18 type verifyClient struct { 19 client *http.Client 20 timeout time.Duration 21 } 22 23 // newVerifyClient returns a new client pointed at the config version socket. 24 func newVerifyClient(timeout time.Duration) *verifyClient { 25 return &verifyClient{ 26 client: &http.Client{ 27 Transport: &http.Transport{ 28 DialContext: func(_ context.Context, _, _ string) (net.Conn, error) { 29 return net.Dial("unix", "/var/lib/nginx/nginx-config-version.sock") 30 }, 31 }, 32 }, 33 timeout: timeout, 34 } 35 } 36 37 // GetConfigVersion get version number that we put in the nginx config to verify that we're using 38 // the correct config. 39 func (c *verifyClient) GetConfigVersion() (int, error) { 40 resp, err := c.client.Get("http://config-version/configVersion") 41 if err != nil { 42 return 0, fmt.Errorf("error getting client: %w", err) 43 } 44 defer resp.Body.Close() 45 46 if resp.StatusCode != http.StatusOK { 47 return 0, fmt.Errorf("non-200 response: %v", resp.StatusCode) 48 } 49 50 body, err := ioutil.ReadAll(resp.Body) 51 if err != nil { 52 return 0, fmt.Errorf("failed to read the response body: %w", err) 53 } 54 v, err := strconv.Atoi(string(body)) 55 if err != nil { 56 return 0, fmt.Errorf("error converting string to int: %w", err) 57 } 58 return v, nil 59 } 60 61 // WaitForCorrectVersion calls the config version endpoint until it gets the expectedVersion, 62 // which ensures that a new worker process has been started for that config version. 63 func (c *verifyClient) WaitForCorrectVersion(expectedVersion int) error { 64 interval := 25 * time.Millisecond 65 startTime := time.Now() 66 endTime := startTime.Add(c.timeout) 67 68 glog.V(3).Infof("Starting poll for updated nginx config") 69 for time.Now().Before(endTime) { 70 version, err := c.GetConfigVersion() 71 if err != nil { 72 glog.V(3).Infof("Unable to fetch version: %v", err) 73 continue 74 } 75 if version == expectedVersion { 76 glog.V(3).Infof("success, version %v ensured. took: %v", expectedVersion, time.Since(startTime)) 77 return nil 78 } 79 time.Sleep(interval) 80 } 81 return fmt.Errorf("could not get expected version: %v after %v", expectedVersion, c.timeout) 82 } 83 84 const configVersionTemplateString = `server { 85 listen unix:/var/lib/nginx/nginx-config-version.sock; 86 access_log off; 87 88 {{if .OpenTracingLoadModule}} 89 opentracing off; 90 {{end}} 91 92 location /configVersion { 93 return 200 {{.ConfigVersion}}; 94 } 95 } 96 map $http_x_expected_config_version $config_version_mismatch { 97 "{{.ConfigVersion}}" ""; 98 default "mismatch"; 99 }` 100 101 // verifyConfigGenerator handles generating and writing the config version file. 102 type verifyConfigGenerator struct { 103 configVersionTemplate *template.Template 104 } 105 106 // newVerifyConfigGenerator builds a new ConfigWriter - primarily parsing the config version template. 107 func newVerifyConfigGenerator() (*verifyConfigGenerator, error) { 108 configVersionTemplate, err := template.New("configVersionTemplate").Parse(configVersionTemplateString) 109 if err != nil { 110 return nil, err 111 } 112 return &verifyConfigGenerator{ 113 configVersionTemplate: configVersionTemplate, 114 }, nil 115 } 116 117 // GenerateVersionConfig generates the config version file. 118 func (c *verifyConfigGenerator) GenerateVersionConfig(configVersion int, openTracing bool) ([]byte, error) { 119 var configBuffer bytes.Buffer 120 templateValues := struct { 121 ConfigVersion int 122 OpenTracingLoadModule bool 123 }{ 124 configVersion, 125 openTracing, 126 } 127 err := c.configVersionTemplate.Execute(&configBuffer, templateValues) 128 if err != nil { 129 return nil, err 130 } 131 132 return configBuffer.Bytes(), nil 133 }