...

Source file src/helm.sh/helm/v3/pkg/getter/httpgetter_test.go

Documentation: helm.sh/helm/v3/pkg/getter

     1  /*
     2  Copyright The Helm Authors.
     3  Licensed under the Apache License, Version 2.0 (the "License");
     4  you may not use this file except in compliance with the License.
     5  You may obtain a copy of the License at
     6  
     7  http://www.apache.org/licenses/LICENSE-2.0
     8  
     9  Unless required by applicable law or agreed to in writing, software
    10  distributed under the License is distributed on an "AS IS" BASIS,
    11  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  See the License for the specific language governing permissions and
    13  limitations under the License.
    14  */
    15  
    16  package getter
    17  
    18  import (
    19  	"fmt"
    20  	"io"
    21  	"net/http"
    22  	"net/http/httptest"
    23  	"net/url"
    24  	"os"
    25  	"path/filepath"
    26  	"strconv"
    27  	"strings"
    28  	"testing"
    29  	"time"
    30  
    31  	"github.com/pkg/errors"
    32  
    33  	"helm.sh/helm/v3/internal/tlsutil"
    34  	"helm.sh/helm/v3/internal/version"
    35  	"helm.sh/helm/v3/pkg/cli"
    36  )
    37  
    38  func TestHTTPGetter(t *testing.T) {
    39  	g, err := NewHTTPGetter(WithURL("http://example.com"))
    40  	if err != nil {
    41  		t.Fatal(err)
    42  	}
    43  
    44  	if _, ok := g.(*HTTPGetter); !ok {
    45  		t.Fatal("Expected NewHTTPGetter to produce an *HTTPGetter")
    46  	}
    47  
    48  	cd := "../../testdata"
    49  	join := filepath.Join
    50  	ca, pub, priv := join(cd, "rootca.crt"), join(cd, "crt.pem"), join(cd, "key.pem")
    51  	insecure := false
    52  	timeout := time.Second * 5
    53  	transport := &http.Transport{}
    54  
    55  	// Test with options
    56  	g, err = NewHTTPGetter(
    57  		WithBasicAuth("I", "Am"),
    58  		WithPassCredentialsAll(false),
    59  		WithUserAgent("Groot"),
    60  		WithTLSClientConfig(pub, priv, ca),
    61  		WithInsecureSkipVerifyTLS(insecure),
    62  		WithTimeout(timeout),
    63  		WithTransport(transport),
    64  	)
    65  	if err != nil {
    66  		t.Fatal(err)
    67  	}
    68  
    69  	hg, ok := g.(*HTTPGetter)
    70  	if !ok {
    71  		t.Fatal("expected NewHTTPGetter to produce an *HTTPGetter")
    72  	}
    73  
    74  	if hg.opts.username != "I" {
    75  		t.Errorf("Expected NewHTTPGetter to contain %q as the username, got %q", "I", hg.opts.username)
    76  	}
    77  
    78  	if hg.opts.password != "Am" {
    79  		t.Errorf("Expected NewHTTPGetter to contain %q as the password, got %q", "Am", hg.opts.password)
    80  	}
    81  
    82  	if hg.opts.passCredentialsAll != false {
    83  		t.Errorf("Expected NewHTTPGetter to contain %t as PassCredentialsAll, got %t", false, hg.opts.passCredentialsAll)
    84  	}
    85  
    86  	if hg.opts.userAgent != "Groot" {
    87  		t.Errorf("Expected NewHTTPGetter to contain %q as the user agent, got %q", "Groot", hg.opts.userAgent)
    88  	}
    89  
    90  	if hg.opts.certFile != pub {
    91  		t.Errorf("Expected NewHTTPGetter to contain %q as the public key file, got %q", pub, hg.opts.certFile)
    92  	}
    93  
    94  	if hg.opts.keyFile != priv {
    95  		t.Errorf("Expected NewHTTPGetter to contain %q as the private key file, got %q", priv, hg.opts.keyFile)
    96  	}
    97  
    98  	if hg.opts.caFile != ca {
    99  		t.Errorf("Expected NewHTTPGetter to contain %q as the CA file, got %q", ca, hg.opts.caFile)
   100  	}
   101  
   102  	if hg.opts.insecureSkipVerifyTLS != insecure {
   103  		t.Errorf("Expected NewHTTPGetter to contain %t as InsecureSkipVerifyTLs flag, got %t", false, hg.opts.insecureSkipVerifyTLS)
   104  	}
   105  
   106  	if hg.opts.timeout != timeout {
   107  		t.Errorf("Expected NewHTTPGetter to contain %s as Timeout flag, got %s", timeout, hg.opts.timeout)
   108  	}
   109  
   110  	if hg.opts.transport != transport {
   111  		t.Errorf("Expected NewHTTPGetter to contain %p as Transport, got %p", transport, hg.opts.transport)
   112  	}
   113  
   114  	// Test if setting insecureSkipVerifyTLS is being passed to the ops
   115  	insecure = true
   116  
   117  	g, err = NewHTTPGetter(
   118  		WithInsecureSkipVerifyTLS(insecure),
   119  	)
   120  	if err != nil {
   121  		t.Fatal(err)
   122  	}
   123  
   124  	hg, ok = g.(*HTTPGetter)
   125  	if !ok {
   126  		t.Fatal("expected NewHTTPGetter to produce an *HTTPGetter")
   127  	}
   128  
   129  	if hg.opts.insecureSkipVerifyTLS != insecure {
   130  		t.Errorf("Expected NewHTTPGetter to contain %t as InsecureSkipVerifyTLs flag, got %t", insecure, hg.opts.insecureSkipVerifyTLS)
   131  	}
   132  
   133  	// Checking false by default
   134  	if hg.opts.passCredentialsAll != false {
   135  		t.Errorf("Expected NewHTTPGetter to contain %t as PassCredentialsAll, got %t", false, hg.opts.passCredentialsAll)
   136  	}
   137  
   138  	// Test setting PassCredentialsAll
   139  	g, err = NewHTTPGetter(
   140  		WithBasicAuth("I", "Am"),
   141  		WithPassCredentialsAll(true),
   142  	)
   143  	if err != nil {
   144  		t.Fatal(err)
   145  	}
   146  
   147  	hg, ok = g.(*HTTPGetter)
   148  	if !ok {
   149  		t.Fatal("expected NewHTTPGetter to produce an *HTTPGetter")
   150  	}
   151  	if hg.opts.passCredentialsAll != true {
   152  		t.Errorf("Expected NewHTTPGetter to contain %t as PassCredentialsAll, got %t", true, hg.opts.passCredentialsAll)
   153  	}
   154  }
   155  
   156  func TestDownload(t *testing.T) {
   157  	expect := "Call me Ishmael"
   158  	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   159  		defaultUserAgent := version.GetUserAgent()
   160  		if r.UserAgent() != defaultUserAgent {
   161  			t.Errorf("Expected '%s', got '%s'", defaultUserAgent, r.UserAgent())
   162  		}
   163  		fmt.Fprint(w, expect)
   164  	}))
   165  	defer srv.Close()
   166  
   167  	g, err := All(cli.New()).ByScheme("http")
   168  	if err != nil {
   169  		t.Fatal(err)
   170  	}
   171  	got, err := g.Get(srv.URL, WithURL(srv.URL))
   172  	if err != nil {
   173  		t.Fatal(err)
   174  	}
   175  
   176  	if got.String() != expect {
   177  		t.Errorf("Expected %q, got %q", expect, got.String())
   178  	}
   179  
   180  	// test with http server
   181  	const expectedUserAgent = "I am Groot"
   182  	basicAuthSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   183  		username, password, ok := r.BasicAuth()
   184  		if !ok || username != "username" || password != "password" {
   185  			t.Errorf("Expected request to use basic auth and for username == 'username' and password == 'password', got '%v', '%s', '%s'", ok, username, password)
   186  		}
   187  		if r.UserAgent() != expectedUserAgent {
   188  			t.Errorf("Expected '%s', got '%s'", expectedUserAgent, r.UserAgent())
   189  		}
   190  		fmt.Fprint(w, expect)
   191  	}))
   192  
   193  	defer basicAuthSrv.Close()
   194  
   195  	u, _ := url.ParseRequestURI(basicAuthSrv.URL)
   196  	httpgetter, err := NewHTTPGetter(
   197  		WithURL(u.String()),
   198  		WithBasicAuth("username", "password"),
   199  		WithPassCredentialsAll(false),
   200  		WithUserAgent(expectedUserAgent),
   201  	)
   202  	if err != nil {
   203  		t.Fatal(err)
   204  	}
   205  	got, err = httpgetter.Get(u.String())
   206  	if err != nil {
   207  		t.Fatal(err)
   208  	}
   209  
   210  	if got.String() != expect {
   211  		t.Errorf("Expected %q, got %q", expect, got.String())
   212  	}
   213  
   214  	// test with Get URL differing from withURL
   215  	crossAuthSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   216  		username, password, ok := r.BasicAuth()
   217  		if ok || username == "username" || password == "password" {
   218  			t.Errorf("Expected request to not include but got '%v', '%s', '%s'", ok, username, password)
   219  		}
   220  		fmt.Fprint(w, expect)
   221  	}))
   222  
   223  	defer crossAuthSrv.Close()
   224  
   225  	u, _ = url.ParseRequestURI(crossAuthSrv.URL)
   226  
   227  	// A different host is provided for the WithURL from the one used for Get
   228  	u2, _ := url.ParseRequestURI(crossAuthSrv.URL)
   229  	host := strings.Split(u2.Host, ":")
   230  	host[0] = host[0] + "a"
   231  	u2.Host = strings.Join(host, ":")
   232  	httpgetter, err = NewHTTPGetter(
   233  		WithURL(u2.String()),
   234  		WithBasicAuth("username", "password"),
   235  		WithPassCredentialsAll(false),
   236  	)
   237  	if err != nil {
   238  		t.Fatal(err)
   239  	}
   240  	got, err = httpgetter.Get(u.String())
   241  	if err != nil {
   242  		t.Fatal(err)
   243  	}
   244  
   245  	if got.String() != expect {
   246  		t.Errorf("Expected %q, got %q", expect, got.String())
   247  	}
   248  
   249  	// test with Get URL differing from withURL and should pass creds
   250  	crossAuthSrv = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   251  		username, password, ok := r.BasicAuth()
   252  		if !ok || username != "username" || password != "password" {
   253  			t.Errorf("Expected request to use basic auth and for username == 'username' and password == 'password', got '%v', '%s', '%s'", ok, username, password)
   254  		}
   255  		fmt.Fprint(w, expect)
   256  	}))
   257  
   258  	defer crossAuthSrv.Close()
   259  
   260  	u, _ = url.ParseRequestURI(crossAuthSrv.URL)
   261  
   262  	// A different host is provided for the WithURL from the one used for Get
   263  	u2, _ = url.ParseRequestURI(crossAuthSrv.URL)
   264  	host = strings.Split(u2.Host, ":")
   265  	host[0] = host[0] + "a"
   266  	u2.Host = strings.Join(host, ":")
   267  	httpgetter, err = NewHTTPGetter(
   268  		WithURL(u2.String()),
   269  		WithBasicAuth("username", "password"),
   270  		WithPassCredentialsAll(true),
   271  	)
   272  	if err != nil {
   273  		t.Fatal(err)
   274  	}
   275  	got, err = httpgetter.Get(u.String())
   276  	if err != nil {
   277  		t.Fatal(err)
   278  	}
   279  
   280  	if got.String() != expect {
   281  		t.Errorf("Expected %q, got %q", expect, got.String())
   282  	}
   283  }
   284  
   285  func TestDownloadTLS(t *testing.T) {
   286  	cd := "../../testdata"
   287  	ca, pub, priv := filepath.Join(cd, "rootca.crt"), filepath.Join(cd, "crt.pem"), filepath.Join(cd, "key.pem")
   288  	insecureSkipTLSverify := false
   289  
   290  	tlsSrv := httptest.NewUnstartedServer(http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {}))
   291  	tlsConf, err := tlsutil.NewClientTLS(pub, priv, ca, insecureSkipTLSverify)
   292  	if err != nil {
   293  		t.Fatal(errors.Wrap(err, "can't create TLS config for client"))
   294  	}
   295  	tlsConf.ServerName = "helm.sh"
   296  	tlsSrv.TLS = tlsConf
   297  	tlsSrv.StartTLS()
   298  	defer tlsSrv.Close()
   299  
   300  	u, _ := url.ParseRequestURI(tlsSrv.URL)
   301  	g, err := NewHTTPGetter(
   302  		WithURL(u.String()),
   303  		WithTLSClientConfig(pub, priv, ca),
   304  	)
   305  	if err != nil {
   306  		t.Fatal(err)
   307  	}
   308  
   309  	if _, err := g.Get(u.String()); err != nil {
   310  		t.Error(err)
   311  	}
   312  
   313  	// now test with TLS config being passed along in .Get (see #6635)
   314  	g, err = NewHTTPGetter()
   315  	if err != nil {
   316  		t.Fatal(err)
   317  	}
   318  
   319  	if _, err := g.Get(u.String(), WithURL(u.String()), WithTLSClientConfig(pub, priv, ca)); err != nil {
   320  		t.Error(err)
   321  	}
   322  
   323  	// test with only the CA file (see also #6635)
   324  	g, err = NewHTTPGetter()
   325  	if err != nil {
   326  		t.Fatal(err)
   327  	}
   328  
   329  	if _, err := g.Get(u.String(), WithURL(u.String()), WithTLSClientConfig("", "", ca)); err != nil {
   330  		t.Error(err)
   331  	}
   332  }
   333  
   334  func TestDownloadInsecureSkipTLSVerify(t *testing.T) {
   335  	ts := httptest.NewTLSServer(http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {}))
   336  	defer ts.Close()
   337  
   338  	u, _ := url.ParseRequestURI(ts.URL)
   339  
   340  	// Ensure the default behavior did not change
   341  	g, err := NewHTTPGetter(
   342  		WithURL(u.String()),
   343  	)
   344  	if err != nil {
   345  		t.Error(err)
   346  	}
   347  
   348  	if _, err := g.Get(u.String()); err == nil {
   349  		t.Errorf("Expected Getter to throw an error, got %s", err)
   350  	}
   351  
   352  	// Test certificate check skip
   353  	g, err = NewHTTPGetter(
   354  		WithURL(u.String()),
   355  		WithInsecureSkipVerifyTLS(true),
   356  	)
   357  	if err != nil {
   358  		t.Error(err)
   359  	}
   360  	if _, err = g.Get(u.String()); err != nil {
   361  		t.Error(err)
   362  	}
   363  
   364  }
   365  
   366  func TestHTTPGetterTarDownload(t *testing.T) {
   367  	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
   368  		f, _ := os.Open("testdata/empty-0.0.1.tgz")
   369  		defer f.Close()
   370  
   371  		b := make([]byte, 512)
   372  		f.Read(b)
   373  		//Get the file size
   374  		FileStat, _ := f.Stat()
   375  		FileSize := strconv.FormatInt(FileStat.Size(), 10)
   376  
   377  		//Simulating improper header values from bitbucket
   378  		w.Header().Set("Content-Type", "application/x-tar")
   379  		w.Header().Set("Content-Encoding", "gzip")
   380  		w.Header().Set("Content-Length", FileSize)
   381  
   382  		f.Seek(0, 0)
   383  		io.Copy(w, f)
   384  	}))
   385  
   386  	defer srv.Close()
   387  
   388  	g, err := NewHTTPGetter(WithURL(srv.URL))
   389  	if err != nil {
   390  		t.Fatal(err)
   391  	}
   392  
   393  	data, _ := g.Get(srv.URL)
   394  	mimeType := http.DetectContentType(data.Bytes())
   395  
   396  	expectedMimeType := "application/x-gzip"
   397  	if mimeType != expectedMimeType {
   398  		t.Fatalf("Expected response with MIME type %s, but got %s", expectedMimeType, mimeType)
   399  	}
   400  }
   401  
   402  func TestHttpClientInsecureSkipVerify(t *testing.T) {
   403  	g := HTTPGetter{}
   404  	g.opts.url = "https://localhost"
   405  	verifyInsecureSkipVerify(t, &g, "Blank HTTPGetter", false)
   406  
   407  	g = HTTPGetter{}
   408  	g.opts.url = "https://localhost"
   409  	g.opts.caFile = "testdata/ca.crt"
   410  	verifyInsecureSkipVerify(t, &g, "HTTPGetter with ca file", false)
   411  
   412  	g = HTTPGetter{}
   413  	g.opts.url = "https://localhost"
   414  	g.opts.insecureSkipVerifyTLS = true
   415  	verifyInsecureSkipVerify(t, &g, "HTTPGetter with skip cert verification only", true)
   416  
   417  	g = HTTPGetter{}
   418  	g.opts.url = "https://localhost"
   419  	g.opts.certFile = "testdata/client.crt"
   420  	g.opts.keyFile = "testdata/client.key"
   421  	g.opts.insecureSkipVerifyTLS = true
   422  	transport := verifyInsecureSkipVerify(t, &g, "HTTPGetter with 2 way ssl", true)
   423  	if len(transport.TLSClientConfig.Certificates) <= 0 {
   424  		t.Fatal("transport.TLSClientConfig.Certificates is not present")
   425  	}
   426  	if transport.TLSClientConfig.ServerName == "" {
   427  		t.Fatal("TLSClientConfig.ServerName is blank")
   428  	}
   429  }
   430  
   431  func verifyInsecureSkipVerify(t *testing.T, g *HTTPGetter, caseName string, expectedValue bool) *http.Transport {
   432  	returnVal, err := g.httpClient()
   433  
   434  	if err != nil {
   435  		t.Fatal(err)
   436  	}
   437  
   438  	if returnVal == nil { //nolint:staticcheck
   439  		t.Fatalf("Expected non nil value for http client")
   440  	}
   441  	transport := (returnVal.Transport).(*http.Transport) //nolint:staticcheck
   442  	gotValue := false
   443  	if transport.TLSClientConfig != nil {
   444  		gotValue = transport.TLSClientConfig.InsecureSkipVerify
   445  	}
   446  	if gotValue != expectedValue {
   447  		t.Fatalf("Case Name = %s\nInsecureSkipVerify did not come as expected. Expected = %t; Got = %v",
   448  			caseName, expectedValue, gotValue)
   449  	}
   450  	return transport
   451  }
   452  
   453  func TestDefaultHTTPTransportReuse(t *testing.T) {
   454  	g := HTTPGetter{}
   455  
   456  	httpClient1, err := g.httpClient()
   457  
   458  	if err != nil {
   459  		t.Fatal(err)
   460  	}
   461  
   462  	if httpClient1 == nil { //nolint:staticcheck
   463  		t.Fatalf("Expected non nil value for http client")
   464  	}
   465  
   466  	transport1 := (httpClient1.Transport).(*http.Transport) //nolint:staticcheck
   467  
   468  	httpClient2, err := g.httpClient()
   469  
   470  	if err != nil {
   471  		t.Fatal(err)
   472  	}
   473  
   474  	if httpClient2 == nil { //nolint:staticcheck
   475  		t.Fatalf("Expected non nil value for http client")
   476  	}
   477  
   478  	transport2 := (httpClient2.Transport).(*http.Transport) //nolint:staticcheck
   479  
   480  	if transport1 != transport2 {
   481  		t.Fatalf("Expected default transport to be reused")
   482  	}
   483  }
   484  
   485  func TestHTTPTransportOption(t *testing.T) {
   486  	transport := &http.Transport{}
   487  
   488  	g := HTTPGetter{}
   489  	g.opts.transport = transport
   490  	httpClient1, err := g.httpClient()
   491  
   492  	if err != nil {
   493  		t.Fatal(err)
   494  	}
   495  
   496  	if httpClient1 == nil { //nolint:staticcheck
   497  		t.Fatalf("Expected non nil value for http client")
   498  	}
   499  
   500  	transport1 := (httpClient1.Transport).(*http.Transport) //nolint:staticcheck
   501  
   502  	if transport1 != transport {
   503  		t.Fatalf("Expected transport option to be applied")
   504  	}
   505  
   506  	httpClient2, err := g.httpClient()
   507  
   508  	if err != nil {
   509  		t.Fatal(err)
   510  	}
   511  
   512  	if httpClient2 == nil { //nolint:staticcheck
   513  		t.Fatalf("Expected non nil value for http client")
   514  	}
   515  
   516  	transport2 := (httpClient2.Transport).(*http.Transport) //nolint:staticcheck
   517  
   518  	if transport1 != transport2 {
   519  		t.Fatalf("Expected applied transport to be reused")
   520  	}
   521  
   522  	g = HTTPGetter{}
   523  	g.opts.url = "https://localhost"
   524  	g.opts.certFile = "testdata/client.crt"
   525  	g.opts.keyFile = "testdata/client.key"
   526  	g.opts.insecureSkipVerifyTLS = true
   527  	g.opts.transport = transport
   528  	usedTransport := verifyInsecureSkipVerify(t, &g, "HTTPGetter with 2 way ssl", false)
   529  	if usedTransport.TLSClientConfig != nil {
   530  		t.Fatal("transport.TLSClientConfig should not be set")
   531  	}
   532  }
   533  

View as plain text