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  }