github.com/openshift-online/ocm-sdk-go@v0.1.473/connection_test.go (about)

     1  /*
     2  Copyright (c) 2019 Red Hat, Inc.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8    http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  // This file contains tests for the connection.
    18  
    19  package sdk
    20  
    21  import (
    22  	"context"
    23  	"net/http"
    24  	"os"
    25  	"path/filepath"
    26  	"time"
    27  
    28  	. "github.com/onsi/ginkgo/v2/dsl/core" // nolint
    29  	. "github.com/onsi/gomega"             // nolint
    30  	. "github.com/onsi/gomega/gbytes"      // nolint
    31  
    32  	. "github.com/openshift-online/ocm-sdk-go/testing" // nolint
    33  )
    34  
    35  var _ = Describe("Connection", func() {
    36  	It("Can be created with access token", func() {
    37  		accessToken := MakeTokenString("Bearer", 5*time.Minute)
    38  		connection, err := NewConnectionBuilder().
    39  			Logger(logger).
    40  			Tokens(accessToken).
    41  			Build()
    42  		Expect(err).ToNot(HaveOccurred())
    43  		defer connection.Close()
    44  		Expect(connection).ToNot(BeNil())
    45  	})
    46  
    47  	It("Can be created with refresh token", func() {
    48  		refreshToken := MakeTokenString("Refresh", 10*time.Hour)
    49  		connection, err := NewConnectionBuilder().
    50  			Logger(logger).
    51  			Tokens(refreshToken).
    52  			Build()
    53  		Expect(err).ToNot(HaveOccurred())
    54  		defer connection.Close()
    55  		Expect(connection).ToNot(BeNil())
    56  	})
    57  
    58  	It("Can be created with offline access token", func() {
    59  		offlineToken := MakeTokenString("Offline", 0)
    60  		connection, err := NewConnectionBuilder().
    61  			Logger(logger).
    62  			Tokens(offlineToken).
    63  			Build()
    64  		Expect(err).ToNot(HaveOccurred())
    65  		defer connection.Close()
    66  		Expect(connection).ToNot(BeNil())
    67  	})
    68  
    69  	It("Can be created with access and refresh tokens", func() {
    70  		accessToken := MakeTokenString("Bearer", 5*time.Minute)
    71  		refreshToken := MakeTokenString("Refresh", 10*time.Hour)
    72  		connection, err := NewConnectionBuilder().
    73  			Logger(logger).
    74  			Tokens(accessToken, refreshToken).
    75  			Build()
    76  		Expect(err).ToNot(HaveOccurred())
    77  		defer connection.Close()
    78  		Expect(connection).ToNot(BeNil())
    79  	})
    80  
    81  	It("Can be created with access and offline tokens", func() {
    82  		accessToken := MakeTokenString("Bearer", 5*time.Minute)
    83  		offlineToken := MakeTokenString("Offline", 10*time.Hour)
    84  		connection, err := NewConnectionBuilder().
    85  			Logger(logger).
    86  			Tokens(accessToken, offlineToken).
    87  			Build()
    88  		Expect(err).ToNot(HaveOccurred())
    89  		defer connection.Close()
    90  		Expect(connection).ToNot(BeNil())
    91  	})
    92  
    93  	It("Can be created with user name and password", func() {
    94  		connection, err := NewConnectionBuilder().
    95  			Logger(logger).
    96  			User("myuser", "mypassword").
    97  			Build()
    98  		Expect(err).ToNot(HaveOccurred())
    99  		defer connection.Close()
   100  		Expect(connection).ToNot(BeNil())
   101  	})
   102  
   103  	It("Can be created with client identifier and secret", func() {
   104  		connection, err := NewConnectionBuilder().
   105  			Logger(logger).
   106  			Client("myclientid", "myclientsecret").
   107  			Build()
   108  		Expect(err).ToNot(HaveOccurred())
   109  		defer connection.Close()
   110  		Expect(connection).ToNot(BeNil())
   111  	})
   112  
   113  	It("Can be created with metrics subsystem", func() {
   114  		accessToken := MakeTokenString("Bearer", 5*time.Minute)
   115  		connection, err := NewConnectionBuilder().
   116  			Logger(logger).
   117  			Tokens(accessToken).
   118  			MetricsSubsystem("my_subsystem").
   119  			Build()
   120  		Expect(err).ToNot(HaveOccurred())
   121  		Expect(connection).ToNot(BeNil())
   122  		defer func() {
   123  			err = connection.Close()
   124  			Expect(err).ToNot(HaveOccurred())
   125  		}()
   126  		Expect(connection.MetricsSubsystem()).To(Equal("my_subsystem"))
   127  	})
   128  
   129  	It("Selects default OpenID server with default access token", func() {
   130  		accessToken := MakeTokenString("Bearer", 5*time.Minute)
   131  		connection, err := NewConnectionBuilder().
   132  			Logger(logger).
   133  			Tokens(accessToken).
   134  			Build()
   135  		Expect(err).ToNot(HaveOccurred())
   136  		defer connection.Close()
   137  		tokenURL := connection.TokenURL()
   138  		Expect(tokenURL).To(Equal(DefaultTokenURL))
   139  		clientID, clientSecret := connection.Client()
   140  		Expect(clientID).To(Equal(DefaultClientID))
   141  		Expect(clientSecret).To(Equal(DefaultClientSecret))
   142  	})
   143  
   144  	It("Selects default OpenID server with default refresh token", func() {
   145  		refreshToken := MakeTokenString("Refresh", 10*time.Hour)
   146  		connection, err := NewConnectionBuilder().
   147  			Logger(logger).
   148  			Tokens(refreshToken).
   149  			Build()
   150  		Expect(err).ToNot(HaveOccurred())
   151  		defer connection.Close()
   152  		tokenURL := connection.TokenURL()
   153  		Expect(tokenURL).To(Equal(DefaultTokenURL))
   154  		clientID, clientSecret := connection.Client()
   155  		Expect(clientID).To(Equal(DefaultClientID))
   156  		Expect(clientSecret).To(Equal(DefaultClientSecret))
   157  	})
   158  
   159  	It("Selects default OpenID server with default offline access token", func() {
   160  		offlineToken := MakeTokenString("Offline", 0)
   161  		connection, err := NewConnectionBuilder().
   162  			Logger(logger).
   163  			Tokens(offlineToken).
   164  			Build()
   165  		Expect(err).ToNot(HaveOccurred())
   166  		defer connection.Close()
   167  		tokenURL := connection.TokenURL()
   168  		Expect(tokenURL).To(Equal(DefaultTokenURL))
   169  		clientID, clientSecret := connection.Client()
   170  		Expect(clientID).To(Equal(DefaultClientID))
   171  		Expect(clientSecret).To(Equal(DefaultClientSecret))
   172  	})
   173  
   174  	It("Honours explicitly provided OpenID server with user name and password", func() {
   175  		connection, err := NewConnectionBuilder().
   176  			Logger(logger).
   177  			User("myuser", "mypassword").
   178  			TokenURL(DefaultTokenURL).
   179  			Client(DefaultClientID, DefaultClientSecret).
   180  			Build()
   181  		Expect(err).ToNot(HaveOccurred())
   182  		defer connection.Close()
   183  		tokenURL := connection.TokenURL()
   184  		Expect(tokenURL).To(Equal(DefaultTokenURL))
   185  		clientID, clientSecret := connection.Client()
   186  		Expect(clientID).To(Equal(DefaultClientID))
   187  		Expect(clientSecret).To(Equal(DefaultClientSecret))
   188  	})
   189  
   190  	It("Use transport wrapper", func() {
   191  		// Create a connection:
   192  		transport := NewTestTransport()
   193  		connection, err := NewConnectionBuilder().
   194  			Logger(logger).
   195  			User("test", "test").
   196  			TransportWrapper(func(wrapped http.RoundTripper) http.RoundTripper {
   197  				return transport
   198  			}).
   199  			Build()
   200  		Expect(err).ToNot(HaveOccurred())
   201  		defer connection.Close()
   202  
   203  		// Try to get the tokens using a explicit and short timeout to make the test run
   204  		// faster (by default it takes up to 15 seconds) but give it enough time to retry
   205  		// a few times:
   206  		ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
   207  		defer cancel()
   208  		_, _, err = connection.TokensContext(ctx)
   209  
   210  		// Check that the transport was called at least three times:
   211  		Expect(transport.called).To(BeNumerically(">=", 3))
   212  		Expect(err).To(HaveOccurred())
   213  	})
   214  
   215  	It("Can be created with one alternative URL", func() {
   216  		// Create the connection:
   217  		token := MakeTokenString("Bearer", 5*time.Minute)
   218  		connection, err := NewConnectionBuilder().
   219  			Logger(logger).
   220  			Tokens(token).
   221  			URL("https://my.server.com").
   222  			AlternativeURL("/api/clusters_mgmt", "https://your.server.com").
   223  			Build()
   224  		Expect(err).ToNot(HaveOccurred())
   225  
   226  		// Check that the URLs are set:
   227  		Expect(connection.URL()).To(Equal("https://my.server.com"))
   228  		alternativeURLs := connection.AlternativeURLs()
   229  		Expect(alternativeURLs).To(HaveLen(1))
   230  		Expect(alternativeURLs).To(HaveKeyWithValue(
   231  			"/api/clusters_mgmt",
   232  			"https://your.server.com",
   233  		))
   234  	})
   235  
   236  	It("Can be created with two alternative URLs", func() {
   237  		// Create the connection:
   238  		token := MakeTokenString("Bearer", 5*time.Minute)
   239  		connection, err := NewConnectionBuilder().
   240  			Logger(logger).
   241  			Tokens(token).
   242  			URL("https://my.server.com").
   243  			AlternativeURL("/api/clusters_mgmt", "https://your.server.com").
   244  			AlternativeURL("/api/accounts_mgmt", "https://her.server.com").
   245  			Build()
   246  		Expect(err).ToNot(HaveOccurred())
   247  
   248  		// Check that the URLs are set:
   249  		Expect(connection.URL()).To(Equal("https://my.server.com"))
   250  		alternativeURLs := connection.AlternativeURLs()
   251  		Expect(alternativeURLs).To(HaveLen(2))
   252  		Expect(alternativeURLs).To(HaveKeyWithValue(
   253  			"/api/clusters_mgmt",
   254  			"https://your.server.com",
   255  		))
   256  		Expect(alternativeURLs).To(HaveKeyWithValue(
   257  			"/api/accounts_mgmt",
   258  			"https://her.server.com",
   259  		))
   260  	})
   261  
   262  	It("Can be created with a map of alternative URLs", func() {
   263  		// Create the connection:
   264  		token := MakeTokenString("Bearer", 5*time.Minute)
   265  		connection, err := NewConnectionBuilder().
   266  			Logger(logger).
   267  			Tokens(token).
   268  			URL("https://my.server.com").
   269  			AlternativeURLs(map[string]string{
   270  				"/api/clusters_mgmt": "https://your.server.com",
   271  				"/api/accounts_mgmt": "https://her.server.com",
   272  			}).
   273  			Build()
   274  		Expect(err).ToNot(HaveOccurred())
   275  
   276  		// Check that the URLs are set:
   277  		Expect(connection.URL()).To(Equal("https://my.server.com"))
   278  		alternativeURLs := connection.AlternativeURLs()
   279  		Expect(alternativeURLs).To(HaveLen(2))
   280  		Expect(alternativeURLs).To(HaveKeyWithValue(
   281  			"/api/clusters_mgmt",
   282  			"https://your.server.com",
   283  		))
   284  		Expect(alternativeURLs).To(HaveKeyWithValue(
   285  			"/api/accounts_mgmt",
   286  			"https://her.server.com",
   287  		))
   288  	})
   289  
   290  	It("Altering returned alternative URLs doesn't affect internal state", func() {
   291  		// Create the connection:
   292  		token := MakeTokenString("Bearer", 5*time.Minute)
   293  		connection, err := NewConnectionBuilder().
   294  			Logger(logger).
   295  			Tokens(token).
   296  			URL("https://my.server.com").
   297  			AlternativeURLs(map[string]string{
   298  				"/api/clusters_mgmt": "https://your.server.com",
   299  				"/api/accounts_mgmt": "https://her.server.com",
   300  			}).
   301  			Build()
   302  		Expect(err).ToNot(HaveOccurred())
   303  
   304  		// Try to modify the returned map of alternative URLs:
   305  		alternativeURLs := connection.AlternativeURLs()
   306  		alternativeURLs["/api/service_logs"] = "https://his.server.com"
   307  
   308  		// Check that map used internall hasn't changed:
   309  		alternativeURLs = connection.AlternativeURLs()
   310  		Expect(alternativeURLs).To(HaveLen(2))
   311  		Expect(alternativeURLs).To(HaveKeyWithValue(
   312  			"/api/clusters_mgmt",
   313  			"https://your.server.com",
   314  		))
   315  		Expect(alternativeURLs).To(HaveKeyWithValue(
   316  			"/api/accounts_mgmt",
   317  			"https://her.server.com",
   318  		))
   319  	})
   320  
   321  	It("Can't be created with invalid alternative URL prefix", func() {
   322  		token := MakeTokenString("Bearer", 5*time.Minute)
   323  		_, err := NewConnectionBuilder().
   324  			Logger(logger).
   325  			Tokens(token).
   326  			AlternativeURL("junk", "https://api.openshift.com").
   327  			Build()
   328  		Expect(err).To(HaveOccurred())
   329  	})
   330  
   331  	It("Can't be created with invalid alternative URL", func() {
   332  		token := MakeTokenString("Bearer", 5*time.Minute)
   333  		_, err := NewConnectionBuilder().
   334  			Logger(logger).
   335  			Tokens(token).
   336  			AlternativeURL("/api/clusters_mgmt", ":junk").
   337  			Build()
   338  		Expect(err).To(HaveOccurred())
   339  	})
   340  
   341  	It("Can be configured with a YAML string", func() {
   342  		// Create temporary files for the trusted CAs:
   343  		tmp, err := os.MkdirTemp("", "*.test.cas")
   344  		Expect(err).ToNot(HaveOccurred())
   345  		defer func() {
   346  			err = os.RemoveAll(tmp)
   347  			Expect(err).ToNot(HaveOccurred())
   348  		}()
   349  		err = os.WriteFile(filepath.Join(tmp, "myca.pem"), mycaPEM, 0600)
   350  		Expect(err).ToNot(HaveOccurred())
   351  		err = os.WriteFile(filepath.Join(tmp, "yourca.pem"), yourcaPEM, 0600)
   352  		Expect(err).ToNot(HaveOccurred())
   353  
   354  		// Create the YAML configuration string:
   355  		fileAccess := MakeTokenString("Bearer", 5*time.Minute)
   356  		fileRefresh := MakeTokenString("Refresh", 10*time.Hour)
   357  		content := EvaluateTemplate(
   358  			`
   359  			url: https://my.server.com
   360  			alternative_urls:
   361  			  /api/clusters_mgmt: https://your.server.com
   362  			  /api/accounts_mgmt: https://her.server.com
   363  			token_url: https://openid.server.com
   364  			user: myuser
   365  			password: mypassword
   366  			client_id: myclient
   367  			client_secret: mysecret
   368  			tokens:
   369  			- {{ .AccessToken }}
   370  			- {{ .RefreshToken }}
   371  			scopes:
   372  			- openid
   373  			- myscope
   374  			insecure: true
   375  			trusted_cas:
   376  			- {{ .Tmp }}/myca.pem
   377  			- {{ .Tmp }}/yourca.pem
   378  			agent: myagent
   379  			retry_limit: 4
   380  			metrics_subsystem: mysubsystem
   381  			`,
   382  			"Tmp", tmp,
   383  			"AccessToken", fileAccess,
   384  			"RefreshToken", fileRefresh,
   385  		)
   386  
   387  		// Create the connection and verify it has been created with the configuration
   388  		// stored in the YAML string:
   389  		connection, err := NewConnectionBuilder().
   390  			Logger(logger).
   391  			Load(content).
   392  			Build()
   393  		Expect(err).ToNot(HaveOccurred())
   394  		Expect(connection.URL()).To(Equal("https://my.server.com"))
   395  		alternativeURLs := connection.AlternativeURLs()
   396  		Expect(alternativeURLs).To(HaveLen(2))
   397  		Expect(alternativeURLs).To(HaveKeyWithValue(
   398  			"/api/clusters_mgmt", "https://your.server.com",
   399  		))
   400  		Expect(alternativeURLs).To(HaveKeyWithValue(
   401  			"/api/accounts_mgmt", "https://her.server.com",
   402  		))
   403  		Expect(connection.TokenURL()).To(Equal("https://openid.server.com"))
   404  		user, password := connection.User()
   405  		Expect(user).To(Equal("myuser"))
   406  		Expect(password).To(Equal("mypassword"))
   407  		client, secret := connection.Client()
   408  		Expect(client).To(Equal("myclient"))
   409  		Expect(secret).To(Equal("mysecret"))
   410  		returnedAccess, returnedRefresh, err := connection.Tokens()
   411  		Expect(err).ToNot(HaveOccurred())
   412  		Expect(returnedAccess).To(Equal(fileAccess))
   413  		Expect(returnedRefresh).To(Equal(fileRefresh))
   414  		defer func() {
   415  			err = connection.Close()
   416  			Expect(err).ToNot(HaveOccurred())
   417  		}()
   418  		Expect(connection.Scopes()).To(ConsistOf("openid", "myscope"))
   419  		Expect(connection.Insecure()).To(BeTrue())
   420  		Expect(connection.Agent()).To(Equal("myagent"))
   421  		Expect(connection.RetryLimit()).To(Equal(4))
   422  		Expect(connection.MetricsSubsystem()).To(Equal("mysubsystem"))
   423  	})
   424  
   425  	It("Can be configured with a YAML file", func() {
   426  		// Create temporary files for the trusted CAs:
   427  		tmp, err := os.MkdirTemp("", "*.test.cas")
   428  		Expect(err).ToNot(HaveOccurred())
   429  		defer func() {
   430  			err = os.RemoveAll(tmp)
   431  			Expect(err).ToNot(HaveOccurred())
   432  		}()
   433  		err = os.WriteFile(filepath.Join(tmp, "myca.pem"), mycaPEM, 0600)
   434  		Expect(err).ToNot(HaveOccurred())
   435  		err = os.WriteFile(filepath.Join(tmp, "yourca.pem"), yourcaPEM, 0600)
   436  		Expect(err).ToNot(HaveOccurred())
   437  
   438  		// Create a temporary YAML file containing the configuration:
   439  		fileAccess := MakeTokenString("Bearer", 5*time.Minute)
   440  		fileRefresh := MakeTokenString("Refresh", 10*time.Hour)
   441  		content := EvaluateTemplate(
   442  			`
   443  			url: https://my.server.com
   444  			alternative_urls:
   445  			  /api/clusters_mgmt: https://your.server.com
   446  			  /api/accounts_mgmt: https://her.server.com
   447  			token_url: https://openid.server.com
   448  			user: myuser
   449  			password: mypassword
   450  			client_id: myclient
   451  			client_secret: mysecret
   452  			tokens:
   453  			- {{ .AccessToken }}
   454  			- {{ .RefreshToken }}
   455  			scopes:
   456  			- openid
   457  			- myscope
   458  			insecure: true
   459  			trusted_cas:
   460  			- {{ .Tmp }}/myca.pem
   461  			- {{ .Tmp }}/yourca.pem
   462  			agent: myagent
   463  			retry_limit: 4
   464  			metrics_subsystem: mysubsystem
   465  			`,
   466  			"Tmp", tmp,
   467  			"AccessToken", fileAccess,
   468  			"RefreshToken", fileRefresh,
   469  		)
   470  		file, err := os.CreateTemp("", "*.yaml")
   471  		Expect(err).ToNot(HaveOccurred())
   472  		path := file.Name()
   473  		defer func() {
   474  			err = os.Remove(path)
   475  			Expect(err).ToNot(HaveOccurred())
   476  		}()
   477  		_, err = file.WriteString(content)
   478  		Expect(err).ToNot(HaveOccurred())
   479  		err = file.Close()
   480  		Expect(err).ToNot(HaveOccurred())
   481  
   482  		// Create the connection and verify it has been created with the configuration
   483  		// stored in the YAML file:
   484  		connection, err := NewConnectionBuilder().
   485  			Logger(logger).
   486  			Load(path).
   487  			Build()
   488  		Expect(err).ToNot(HaveOccurred())
   489  		Expect(connection.URL()).To(Equal("https://my.server.com"))
   490  		alternativeURLs := connection.AlternativeURLs()
   491  		Expect(alternativeURLs).To(HaveLen(2))
   492  		Expect(alternativeURLs).To(HaveKeyWithValue(
   493  			"/api/clusters_mgmt", "https://your.server.com",
   494  		))
   495  		Expect(alternativeURLs).To(HaveKeyWithValue(
   496  			"/api/accounts_mgmt", "https://her.server.com",
   497  		))
   498  		Expect(connection.TokenURL()).To(Equal("https://openid.server.com"))
   499  		user, password := connection.User()
   500  		Expect(user).To(Equal("myuser"))
   501  		Expect(password).To(Equal("mypassword"))
   502  		client, secret := connection.Client()
   503  		Expect(client).To(Equal("myclient"))
   504  		Expect(secret).To(Equal("mysecret"))
   505  		returnedAccess, returnedRefresh, err := connection.Tokens()
   506  		Expect(err).ToNot(HaveOccurred())
   507  		Expect(returnedAccess).To(Equal(fileAccess))
   508  		Expect(returnedRefresh).To(Equal(fileRefresh))
   509  		defer func() {
   510  			err = connection.Close()
   511  			Expect(err).ToNot(HaveOccurred())
   512  		}()
   513  		Expect(connection.Scopes()).To(ConsistOf("openid", "myscope"))
   514  		Expect(connection.Insecure()).To(BeTrue())
   515  		Expect(connection.Agent()).To(Equal("myagent"))
   516  		Expect(connection.RetryLimit()).To(Equal(4))
   517  		Expect(connection.MetricsSubsystem()).To(Equal("mysubsystem"))
   518  	})
   519  
   520  	It("Method calls after load override configuration file", func() {
   521  		// Create temporary files for the trusted CAs:
   522  		tmp, err := os.MkdirTemp("", "*.test.cas")
   523  		Expect(err).ToNot(HaveOccurred())
   524  		defer func() {
   525  			err = os.RemoveAll(tmp)
   526  			Expect(err).ToNot(HaveOccurred())
   527  		}()
   528  		err = os.WriteFile(filepath.Join(tmp, "myca.pem"), mycaPEM, 0600)
   529  		Expect(err).ToNot(HaveOccurred())
   530  		err = os.WriteFile(filepath.Join(tmp, "yourca.pem"), yourcaPEM, 0600)
   531  		Expect(err).ToNot(HaveOccurred())
   532  
   533  		// Create a temporary YAML file containing the configuration:
   534  		fileAccess := MakeTokenString("Bearer", 5*time.Minute)
   535  		fileRefresh := MakeTokenString("Refresh", 10*time.Hour)
   536  		content := EvaluateTemplate(
   537  			`
   538  			url: https://my.server.com
   539  			alternative_urls:
   540  			  /api/clusters_mgmt: https://your.server.com
   541  			  /api/accounts_mgmt: https://her.server.com
   542  			token_url: https://openid.server.com
   543  			user: myuser
   544  			password: mypassword
   545  			client_id: myclient
   546  			client_secret: mysecret
   547  			tokens:
   548  			- {{ .AccessToken }}
   549  			- {{ .RefreshToken }}
   550  			scopes:
   551  			- openid
   552  			- myscope
   553  			insecure: true
   554  			trusted_cas:
   555  			- {{ .Tmp }}/myca.pem
   556  			- {{ .Tmp }}/yourca.pem
   557  			agent: myagent
   558  			retry_limit: 5
   559  			metrics_subsystem: mysubsystem
   560  			`,
   561  			"Tmp", tmp,
   562  			"AccessToken", fileAccess,
   563  			"RefreshToken", fileRefresh,
   564  		)
   565  		file, err := os.CreateTemp("", "*.yaml")
   566  		Expect(err).ToNot(HaveOccurred())
   567  		path := file.Name()
   568  		defer func() {
   569  			err = os.Remove(path)
   570  			Expect(err).ToNot(HaveOccurred())
   571  		}()
   572  		_, err = file.WriteString(content)
   573  		Expect(err).ToNot(HaveOccurred())
   574  		err = file.Close()
   575  		Expect(err).ToNot(HaveOccurred())
   576  
   577  		// Load the configuration file and then configure the connection with method
   578  		// calls:
   579  		overridenAccess := MakeTokenString("Bearer", 5*time.Minute)
   580  		overridenRefresh := MakeTokenString("Refresh", 10*time.Hour)
   581  		connection, err := NewConnectionBuilder().
   582  			Logger(logger).
   583  			Load(path).
   584  			URL("https://overriden.my.server.com").
   585  			AlternativeURL("/api/clusters_mgmt", "https://overriden.your.server.com").
   586  			AlternativeURL("/api/accounts_mgmt", "https://overriden.her.server.com").
   587  			TokenURL("https://overriden.openid.server.com").
   588  			User("overriden.myuser", "overriden.mypassword").
   589  			Client("overriden.myclient", "overriden.mysecret").
   590  			Tokens(overridenAccess, overridenRefresh).
   591  			Scopes("openid", "overriden.myscope").
   592  			Insecure(false).
   593  			Agent("overriden.myagent").
   594  			RetryLimit(4).
   595  			MetricsSubsystem("overriden_mysubsystem").
   596  			Build()
   597  		Expect(err).ToNot(HaveOccurred())
   598  
   599  		// Check that the actual settings are the ones set with the method calls:
   600  		Expect(connection.URL()).To(Equal("https://overriden.my.server.com"))
   601  		alternativeURLs := connection.AlternativeURLs()
   602  		Expect(alternativeURLs).To(HaveLen(2))
   603  		Expect(alternativeURLs).To(HaveKeyWithValue(
   604  			"/api/clusters_mgmt", "https://overriden.your.server.com",
   605  		))
   606  		Expect(alternativeURLs).To(HaveKeyWithValue(
   607  			"/api/accounts_mgmt", "https://overriden.her.server.com",
   608  		))
   609  		Expect(connection.TokenURL()).To(Equal("https://overriden.openid.server.com"))
   610  		user, password := connection.User()
   611  		Expect(user).To(Equal("overriden.myuser"))
   612  		Expect(password).To(Equal("overriden.mypassword"))
   613  		client, secret := connection.Client()
   614  		Expect(client).To(Equal("overriden.myclient"))
   615  		Expect(secret).To(Equal("overriden.mysecret"))
   616  		returnedAccess, returnedRefresh, err := connection.Tokens()
   617  		Expect(err).ToNot(HaveOccurred())
   618  		Expect(returnedAccess).To(Equal(overridenAccess))
   619  		Expect(returnedRefresh).To(Equal(overridenRefresh))
   620  		defer func() {
   621  			err = connection.Close()
   622  			Expect(err).ToNot(HaveOccurred())
   623  		}()
   624  		Expect(connection.Scopes()).To(ConsistOf("openid", "overriden.myscope"))
   625  		Expect(connection.Insecure()).To(BeFalse())
   626  		Expect(connection.Agent()).To(Equal("overriden.myagent"))
   627  		Expect(connection.RetryLimit()).To(Equal(4))
   628  		Expect(connection.MetricsSubsystem()).To(Equal("overriden_mysubsystem"))
   629  	})
   630  
   631  	It("Method calls before load don't override configuration file", func() {
   632  		// Create temporary files for the trusted CAs:
   633  		tmp, err := os.MkdirTemp("", "*.test.cas")
   634  		Expect(err).ToNot(HaveOccurred())
   635  		defer func() {
   636  			err = os.RemoveAll(tmp)
   637  			Expect(err).ToNot(HaveOccurred())
   638  		}()
   639  		err = os.WriteFile(filepath.Join(tmp, "myca.pem"), mycaPEM, 0600)
   640  		Expect(err).ToNot(HaveOccurred())
   641  		err = os.WriteFile(filepath.Join(tmp, "yourca.pem"), yourcaPEM, 0600)
   642  		Expect(err).ToNot(HaveOccurred())
   643  
   644  		// Create a temporary YAML file containing the configuration:
   645  		fileAccess := MakeTokenString("Bearer", 5*time.Minute)
   646  		fileRefresh := MakeTokenString("Refresh", 10*time.Hour)
   647  		content := EvaluateTemplate(
   648  			`
   649  			url: https://my.server.com
   650  			alternative_urls:
   651  			  /api/clusters_mgmt: https://your.server.com
   652  			  /api/accounts_mgmt: https://her.server.com
   653  			token_url: https://openid.server.com
   654  			user: myuser
   655  			password: mypassword
   656  			client_id: myclient
   657  			client_secret: mysecret
   658  			tokens:
   659  			- {{ .AccessToken }}
   660  			- {{ .RefreshToken }}
   661  			scopes:
   662  			- openid
   663  			- myscope
   664  			insecure: true
   665  			trusted_cas:
   666  			- {{ .Tmp }}/myca.pem
   667  			- {{ .Tmp }}/yourca.pem
   668  			agent: myagent
   669  			retry_limit: 5
   670  			metrics_subsystem: mysubsystem
   671  			`,
   672  			"Tmp", tmp,
   673  			"AccessToken", fileAccess,
   674  			"RefreshToken", fileRefresh,
   675  		)
   676  		file, err := os.CreateTemp("", "*.yaml")
   677  		Expect(err).ToNot(HaveOccurred())
   678  		path := file.Name()
   679  		defer func() {
   680  			err = os.Remove(path)
   681  			Expect(err).ToNot(HaveOccurred())
   682  		}()
   683  		_, err = file.WriteString(content)
   684  		Expect(err).ToNot(HaveOccurred())
   685  		err = file.Close()
   686  		Expect(err).ToNot(HaveOccurred())
   687  
   688  		// Configure the connection with methods call and then load the configuration file:
   689  		overridenAccess := MakeTokenString("Bearer", 5*time.Minute)
   690  		overridenRefresh := MakeTokenString("Refresh", 10*time.Hour)
   691  		connection, err := NewConnectionBuilder().
   692  			Logger(logger).
   693  			URL("https://overriden.my.server.com").
   694  			AlternativeURL("/api/clusters_mgmt", "https://overriden.your.server.com").
   695  			AlternativeURL("/api/accounts_mgmt", "https://overriden.her.server.com").
   696  			TokenURL("https://overriden.openid.server.com").
   697  			User("overriden.myuser", "overriden.mypassword").
   698  			Client("overriden.myclient", "overriden.mysecret").
   699  			Tokens(overridenAccess, overridenRefresh).
   700  			Scopes("openid", "overriden.myscope").
   701  			Insecure(false).
   702  			Agent("overriden.myagent").
   703  			RetryLimit(4).
   704  			MetricsSubsystem("overriden_mysubsystem").
   705  			Load(path).
   706  			Build()
   707  		Expect(err).ToNot(HaveOccurred())
   708  
   709  		// Check that the actual settings are the ones from the file:
   710  		Expect(connection.URL()).To(Equal("https://my.server.com"))
   711  		alternativeURLs := connection.AlternativeURLs()
   712  		Expect(alternativeURLs).To(HaveLen(2))
   713  		Expect(alternativeURLs).To(HaveKeyWithValue(
   714  			"/api/clusters_mgmt", "https://your.server.com",
   715  		))
   716  		Expect(alternativeURLs).To(HaveKeyWithValue(
   717  			"/api/accounts_mgmt", "https://her.server.com",
   718  		))
   719  		Expect(connection.TokenURL()).To(Equal("https://openid.server.com"))
   720  		user, password := connection.User()
   721  		Expect(user).To(Equal("myuser"))
   722  		Expect(password).To(Equal("mypassword"))
   723  		client, secret := connection.Client()
   724  		Expect(client).To(Equal("myclient"))
   725  		Expect(secret).To(Equal("mysecret"))
   726  		returnedAccess, returnedRefresh, err := connection.Tokens()
   727  		Expect(err).ToNot(HaveOccurred())
   728  		Expect(returnedAccess).To(Equal(fileAccess))
   729  		Expect(returnedRefresh).To(Equal(fileRefresh))
   730  		defer func() {
   731  			err = connection.Close()
   732  			Expect(err).ToNot(HaveOccurred())
   733  		}()
   734  		Expect(connection.Scopes()).To(ConsistOf("openid", "myscope"))
   735  		Expect(connection.Insecure()).To(BeTrue())
   736  		Expect(connection.Agent()).To(Equal("myagent"))
   737  		Expect(connection.RetryLimit()).To(Equal(5))
   738  		Expect(connection.MetricsSubsystem()).To(Equal("mysubsystem"))
   739  	})
   740  
   741  	It("Returns configured URL when there are no alternative URLs", func() {
   742  		token := MakeTokenString("Bearer", 5*time.Minute)
   743  		connection, err := NewConnectionBuilder().
   744  			Logger(logger).
   745  			URL("https://my.server.com").
   746  			Tokens(token).
   747  			Build()
   748  		Expect(err).ToNot(HaveOccurred())
   749  		Expect(connection.URL()).To(Equal("https://my.server.com"))
   750  	})
   751  
   752  	It("Returns configured URL when there are alternative URLs", func() {
   753  		token := MakeTokenString("Bearer", 5*time.Minute)
   754  		connection, err := NewConnectionBuilder().
   755  			Logger(logger).
   756  			URL("https://my.server.com").
   757  			AlternativeURL("/api/clusters_mgmt", "https://your.server.com").
   758  			AlternativeURL("/api/accounts_mgmt", "https://her.server.com").
   759  			Tokens(token).
   760  			Build()
   761  		Expect(err).ToNot(HaveOccurred())
   762  		Expect(connection.URL()).To(Equal("https://my.server.com"))
   763  	})
   764  
   765  	It("Can't be created with URL without scheme", func() {
   766  		token := MakeTokenString("Bearer", 5*time.Minute)
   767  		connection, err := NewConnectionBuilder().
   768  			Logger(logger).
   769  			URL("my.server.com").
   770  			Tokens(token).
   771  			Build()
   772  		Expect(err).To(HaveOccurred())
   773  		Expect(connection).To(BeNil())
   774  		message := err.Error()
   775  		Expect(message).To(ContainSubstring("my.server.com"))
   776  		Expect(message).To(ContainSubstring("scheme"))
   777  		Expect(message).To(ContainSubstring("http"))
   778  		Expect(message).To(ContainSubstring("https"))
   779  	})
   780  
   781  	It("Can't be created with URL with wrong scheme", func() {
   782  		token := MakeTokenString("Bearer", 5*time.Minute)
   783  		connection, err := NewConnectionBuilder().
   784  			Logger(logger).
   785  			URL("junk://my.server.com").
   786  			Tokens(token).
   787  			Build()
   788  		Expect(err).To(HaveOccurred())
   789  		Expect(connection).To(BeNil())
   790  		message := err.Error()
   791  		Expect(message).To(ContainSubstring("junk://my.server.com"))
   792  		Expect(message).To(ContainSubstring("scheme"))
   793  		Expect(message).To(ContainSubstring("http"))
   794  		Expect(message).To(ContainSubstring("https"))
   795  		Expect(message).To(ContainSubstring("junk"))
   796  	})
   797  
   798  	It("Can't be created with alternative URL wihout scheme", func() {
   799  		token := MakeTokenString("Bearer", 5*time.Minute)
   800  		connection, err := NewConnectionBuilder().
   801  			Logger(logger).
   802  			AlternativeURL("/api/clusters_mtmt", "my.server.com").
   803  			Tokens(token).
   804  			Build()
   805  		Expect(err).To(HaveOccurred())
   806  		Expect(connection).To(BeNil())
   807  		message := err.Error()
   808  		Expect(message).To(ContainSubstring("my.server.com"))
   809  		Expect(message).To(ContainSubstring("scheme"))
   810  		Expect(message).To(ContainSubstring("http"))
   811  		Expect(message).To(ContainSubstring("https"))
   812  	})
   813  
   814  	It("Can't be created with alternative URL with wrong scheme", func() {
   815  		token := MakeTokenString("Bearer", 5*time.Minute)
   816  		connection, err := NewConnectionBuilder().
   817  			Logger(logger).
   818  			AlternativeURL("/api/clusters_mtmt", "junk://my.server.com").
   819  			Tokens(token).
   820  			Build()
   821  		Expect(err).To(HaveOccurred())
   822  		Expect(connection).To(BeNil())
   823  		message := err.Error()
   824  		Expect(message).To(ContainSubstring("junk://my.server.com"))
   825  		Expect(message).To(ContainSubstring("scheme"))
   826  		Expect(message).To(ContainSubstring("http"))
   827  		Expect(message).To(ContainSubstring("https"))
   828  		Expect(message).To(ContainSubstring("junk"))
   829  	})
   830  
   831  	It("Can't be created with URL without host name", func() {
   832  		token := MakeTokenString("Bearer", 5*time.Minute)
   833  		_, err := NewConnectionBuilder().
   834  			Logger(logger).
   835  			URL("http:///mypath").
   836  			Tokens(token).
   837  			Build()
   838  		Expect(err).To(HaveOccurred())
   839  		message := err.Error()
   840  		Expect(message).To(ContainSubstring("http:///mypath"))
   841  		Expect(message).To(ContainSubstring("host name"))
   842  	})
   843  
   844  	It("Can't be created with alternative URL without host name", func() {
   845  		token := MakeTokenString("Bearer", 5*time.Minute)
   846  		connection, err := NewConnectionBuilder().
   847  			Logger(logger).
   848  			AlternativeURL("/api/clusters_mgmt", "http:///mypath").
   849  			Tokens(token).
   850  			Build()
   851  		Expect(err).To(HaveOccurred())
   852  		Expect(connection).To(BeNil())
   853  		message := err.Error()
   854  		Expect(message).To(ContainSubstring("http:///mypath"))
   855  		Expect(message).To(ContainSubstring("host name"))
   856  	})
   857  
   858  	It("Can't be created with token URL without scheme", func() {
   859  		token := MakeTokenString("Bearer", 5*time.Minute)
   860  		connection, err := NewConnectionBuilder().
   861  			Logger(logger).
   862  			URL("https://my.server.com").
   863  			TokenURL("your.server.com").
   864  			Tokens(token).
   865  			Build()
   866  		Expect(err).To(HaveOccurred())
   867  		Expect(connection).To(BeNil())
   868  		message := err.Error()
   869  		Expect(message).To(ContainSubstring("your.server.com"))
   870  		Expect(message).To(ContainSubstring("scheme"))
   871  		Expect(message).To(ContainSubstring("http"))
   872  		Expect(message).To(ContainSubstring("https"))
   873  	})
   874  
   875  	It("Can't be created with token URL with wrong scheme", func() {
   876  		token := MakeTokenString("Bearer", 5*time.Minute)
   877  		connection, err := NewConnectionBuilder().
   878  			Logger(logger).
   879  			URL("https://my.server.com").
   880  			TokenURL("junk://your.server.com").
   881  			Tokens(token).
   882  			Build()
   883  		Expect(err).To(HaveOccurred())
   884  		Expect(connection).To(BeNil())
   885  		message := err.Error()
   886  		Expect(message).To(ContainSubstring("junk://your.server.com"))
   887  		Expect(message).To(ContainSubstring("scheme"))
   888  		Expect(message).To(ContainSubstring("http"))
   889  		Expect(message).To(ContainSubstring("https"))
   890  		Expect(message).To(ContainSubstring("junk"))
   891  	})
   892  
   893  	It("Can't be created with token URL without host name", func() {
   894  		token := MakeTokenString("Bearer", 5*time.Minute)
   895  		connection, err := NewConnectionBuilder().
   896  			Logger(logger).
   897  			URL("http://my.server.com").
   898  			TokenURL("http:///yourpath").
   899  			Tokens(token).
   900  			Build()
   901  		Expect(err).To(HaveOccurred())
   902  		Expect(connection).To(BeNil())
   903  		message := err.Error()
   904  		Expect(message).To(ContainSubstring("http:///yourpath"))
   905  		Expect(message).To(ContainSubstring("host name"))
   906  	})
   907  
   908  	It("Can be created with Unix network and host", func() {
   909  		token := MakeTokenString("Bearer", 5*time.Minute)
   910  		connection, err := NewConnectionBuilder().
   911  			Logger(logger).
   912  			URL("unix://my.server.com/tmp/api.socket").
   913  			Tokens(token).
   914  			Build()
   915  		Expect(err).ToNot(HaveOccurred())
   916  		Expect(connection).ToNot(BeNil())
   917  	})
   918  
   919  	It("Can be created with Unix network and HTTPS", func() {
   920  		token := MakeTokenString("Bearer", 5*time.Minute)
   921  		connection, err := NewConnectionBuilder().
   922  			Logger(logger).
   923  			URL("unix+https://my.server.com/tmp/api.socket").
   924  			Tokens(token).
   925  			Build()
   926  		Expect(err).ToNot(HaveOccurred())
   927  		Expect(connection).ToNot(BeNil())
   928  	})
   929  
   930  	It("Can't be created with Unix network and no host", func() {
   931  		token := MakeTokenString("Bearer", 5*time.Minute)
   932  		connection, err := NewConnectionBuilder().
   933  			Logger(logger).
   934  			URL("unix:/tmp/api.socket").
   935  			Tokens(token).
   936  			Build()
   937  		Expect(err).To(HaveOccurred())
   938  		Expect(connection).To(BeNil())
   939  		message := err.Error()
   940  		Expect(message).To(ContainSubstring("unix:/tmp/api.socket"))
   941  		Expect(message).To(ContainSubstring("host"))
   942  		Expect(message).To(ContainSubstring("mandatory"))
   943  	})
   944  
   945  	It("Can't be created with incorrect network", func() {
   946  		token := MakeTokenString("Bearer", 5*time.Minute)
   947  		connection, err := NewConnectionBuilder().
   948  			Logger(logger).
   949  			URL("junk+https://my.server.com").
   950  			Tokens(token).
   951  			Build()
   952  		Expect(err).To(HaveOccurred())
   953  		Expect(connection).To(BeNil())
   954  		message := err.Error()
   955  		Expect(message).To(ContainSubstring("junk"))
   956  		Expect(message).To(ContainSubstring("network"))
   957  		Expect(message).To(ContainSubstring("tcp"))
   958  		Expect(message).To(ContainSubstring("unix"))
   959  	})
   960  
   961  	It("Can't be created with Unix network and no socket", func() {
   962  		token := MakeTokenString("Bearer", 5*time.Minute)
   963  		connection, err := NewConnectionBuilder().
   964  			Logger(logger).
   965  			URL("unix://my.server.com").
   966  			Tokens(token).
   967  			Build()
   968  		Expect(err).To(HaveOccurred())
   969  		Expect(connection).To(BeNil())
   970  		message := err.Error()
   971  		Expect(message).To(ContainSubstring("unix"))
   972  		Expect(message).To(ContainSubstring("socket"))
   973  		Expect(message).To(ContainSubstring("path"))
   974  	})
   975  
   976  	It("Function Close returns nil when trying to close a closed connection", func() {
   977  		offlineToken := MakeTokenString("Offline", 0)
   978  		connection, err := NewConnectionBuilder().
   979  			Logger(logger).
   980  			Tokens(offlineToken).
   981  			Build()
   982  		Expect(err).ToNot(HaveOccurred())
   983  		err = connection.Close()
   984  		Expect(err).ToNot(HaveOccurred())
   985  		// Try to close the connection again
   986  		err = connection.Close()
   987  		Expect(err).To(BeNil())
   988  	})
   989  
   990  	It("Can be created with no authentication", func() {
   991  		connection, err := NewUnauthenticatedConnectionBuilder().
   992  			Logger(logger).
   993  			Build()
   994  		Expect(err).ToNot(HaveOccurred())
   995  
   996  		Expect(connection).ToNot(BeNil())
   997  		Expect(connection.Scopes()).To(BeEmpty())
   998  		Expect(connection.TokenURL()).To(BeEmpty())
   999  		clientId, clientSecret := connection.Client()
  1000  		Expect(clientId).To(BeEmpty())
  1001  		Expect(clientSecret).To(BeEmpty())
  1002  		user, password := connection.User()
  1003  		Expect(user).To(BeEmpty())
  1004  		Expect(password).To(BeEmpty())
  1005  		access, refresh, err := connection.Tokens()
  1006  		Expect(access).To(BeEmpty())
  1007  		Expect(refresh).To(BeEmpty())
  1008  		Expect(err).ToNot(HaveOccurred())
  1009  		access, refresh, err = connection.TokensContext(context.Background())
  1010  		Expect(access).To(BeEmpty())
  1011  		Expect(refresh).To(BeEmpty())
  1012  		Expect(err).ToNot(HaveOccurred())
  1013  
  1014  		err = connection.Close()
  1015  		Expect(err).ToNot(HaveOccurred())
  1016  	})
  1017  })
  1018  
  1019  type TestTransport struct {
  1020  	called int
  1021  }
  1022  
  1023  func (t *TestTransport) RoundTrip(request *http.Request) (response *http.Response, err error) {
  1024  	t.called++
  1025  	header := http.Header{}
  1026  	header.Add("Content-type", "application/json")
  1027  	response = &http.Response{
  1028  		StatusCode: http.StatusInternalServerError,
  1029  		Header:     header,
  1030  		Body:       BufferWithBytes([]byte("{}")),
  1031  	}
  1032  	return response, nil
  1033  }
  1034  
  1035  func NewTestTransport() *TestTransport {
  1036  	return &TestTransport{called: 0}
  1037  }
  1038  
  1039  // This certificate is used only to verify that the connection can load a certificate from
  1040  // a file. It isn't valid for any other thing. It has been generated with the following
  1041  // commmad:
  1042  //
  1043  //	openssl req \
  1044  //	-x509 \
  1045  //	-newkey rsa:4096 \
  1046  //	-keyout myca.key \
  1047  //	-nodes \
  1048  //	-out myca.crt \
  1049  //	-subj '/CN=myca.com' \
  1050  //	-days 3650
  1051  var mycaPEM = []byte(`
  1052  -----BEGIN CERTIFICATE-----
  1053  MIIFCzCCAvOgAwIBAgIUEy+dp9sWPdu59sahER7AxvapYOgwDQYJKoZIhvcNAQEL
  1054  BQAwFTETMBEGA1UEAwwKeW91cmNhLmNvbTAeFw0yMDExMTgxNjUwMTNaFw0zMDEx
  1055  MTYxNjUwMTNaMBUxEzARBgNVBAMMCnlvdXJjYS5jb20wggIiMA0GCSqGSIb3DQEB
  1056  AQUAA4ICDwAwggIKAoICAQC7IXXeem/arTEAvujthKpzxMOaimpIq276rIaaehSf
  1057  PKfwwFScz6KzkcRcCjzGlmQIUfe0VunL9xMfWcOBQ8u0LofJpcRE+AuYXdgAuuyH
  1058  ijWukGZ4o1QGoSmS90TVOOLGA38gnPbQTAgJN8DzxccoOTVtdqsAZMK5zKGJ0IUa
  1059  0ZkPJvs0QYfVYMgYCjiRCeTNze4Cwb/ecj9CZump2IUNm3oE28dUkzRCBysNAvKu
  1060  Ms9HktnQft7BxoefdCZK04o8a1BLzZVSPe7qV+bk0+hD1xygoEPATzhuGl7Al9EI
  1061  lkOJ8fv3uompnz5bHOeP2dNuwn9efeINtJcLlx8wySkU0oNTqQq9MVI7gRwUUAT0
  1062  dLzETULngRhvjGSYEyST3vT27V444fVYkSVIjmji+SmzSejfZq/A1NTQ8M9TUWIs
  1063  7dL562GJnsalPnI+m9XR5m3oajY+CYtcd5q1iIus+WrMXups8fQnpssJHioFs86s
  1064  NEQ0Evbl0OGxxYivJwKbT6Oo86Uh3nUXXx/xxBI5HRmQap38EK6K0WZR3V7MBnpF
  1065  xVv5vUO/zXc7DxAghcXb41XSTWGOM+AqaCIv+zys87/F6x1dmCkA8DCNxYlEeK88
  1066  6xHuK5265iu/to9NSvzCAxmrz4fDea3eAxpZus39yN3N2ud2IAlMguicjZgLF3mg
  1067  uQIDAQABo1MwUTAdBgNVHQ4EFgQUf37ek+qeiMRhZ0q6whksuPY4ezQwHwYDVR0j
  1068  BBgwFoAUf37ek+qeiMRhZ0q6whksuPY4ezQwDwYDVR0TAQH/BAUwAwEB/zANBgkq
  1069  hkiG9w0BAQsFAAOCAgEArP/sjXMURWamJFckKwpam+w8WPW3b0wq6GkQky6XDcXK
  1070  sym5vJfQtQgzZV/rxb0RcO4ywPKYJK2ViREqksmlD5XLL/6grbe+rcIY55IVcFKe
  1071  3ZfE7toa08gWV8kb9VP2KVNt5505jJUVtF8FxRxsu5W0x6b6Kegyotsd5/7tads9
  1072  9qMOoiWOyWxdP4FZalNM8PXaF8pspNqeo/cvgFWvDTqtFvgH4vkLehMueAWmDML2
  1073  lqYHCaMworpY3e8vfk6jK8b9fRmuXaGMlOTpY7XoF8OOSMI1LdPVSO/lo8DCJbge
  1074  RoBHZ97fA8ShB+WRjBuAuh5ST/TEqTha+razhmauVT6CYtw9SSC0SK0ZbNg2oZoG
  1075  CzrQhYf9gHXXPnp0qsuPbtHrMm3DPBHBUrfrlvVVmWjKCVipFdPyRXth9FtNguZn
  1076  d5NUX3JwGRoez/xHv8nHyjdKmTCu8pmP/9SAoV/HUgpcSaEtXyZdlNd+SgIcgV/A
  1077  00eSLesNr9/auzWklxh92oqDnd96IRueamFm6W0BCh64if//BCmAFelPHlWSP1ws
  1078  nzNA++GMw2OXJ1cE/9GTU3or0eDGRgB9XIu+T/SLPXW9xBm0hZdt5gyYfEDmppHa
  1079  nctMPznTWc+iYCMAwroHzJV40ZrVhllhNYrrOLigA7NfAiXelLmSbLx316TnoZM=
  1080  -----END CERTIFICATE-----
  1081  `)
  1082  
  1083  // This certificate is used only to verify that the connection can load a certificate from
  1084  // a file. It isn't valid for any other thing. It has been generated with the following
  1085  // commmad:
  1086  //
  1087  //	openssl req \
  1088  //	-x509 \
  1089  //	-newkey rsa:4096 \
  1090  //	-keyout yourca.key \
  1091  //	-nodes \
  1092  //	-out yourca.crt \
  1093  //	-subj '/CN=yourca.com' \
  1094  //	-days 3650
  1095  var yourcaPEM = []byte(`
  1096  -----BEGIN CERTIFICATE-----
  1097  MIIFCzCCAvOgAwIBAgIUOBKDkme46UAOif9G7fIfN0FTmAswDQYJKoZIhvcNAQEL
  1098  BQAwFTETMBEGA1UEAwwKeW91cmNhLmNvbTAeFw0yMDExMTgxNjU0MTBaFw0zMDEx
  1099  MTYxNjU0MTBaMBUxEzARBgNVBAMMCnlvdXJjYS5jb20wggIiMA0GCSqGSIb3DQEB
  1100  AQUAA4ICDwAwggIKAoICAQCjCUIsLIE0gp7JQZHvwrl12IjYEQjJWEpsEbp/jpux
  1101  ztUAVju5Cq8+V5DYIzHi6WHlottpp6obh4TaCHZroZxwXKCoUARJwtPqADhDX+tr
  1102  Jy8gA2y5ixGxryyXVsAT3YkEBW05i82aKa+FB05T0eUS52SBS2V6Fd4XU19denx5
  1103  rb1eFNor/rmG0gMCAsh/4oWw8DdBEU0qc/9vQ3lsWvGU1noYt/kwfAcaSydrqaIA
  1104  EvK/sG1/hxWD+JBOUwrah0zxT+7x6FbzqX83m03HM7ZHJR+qNjtg31loQwocsn20
  1105  qOM3vMQkqyqjnXMHtIleyhw7fWNqQcvS/f+A3QUosf3h90c/BVmoJ+QUsm9gOUFT
  1106  jGWybFWRnukSY/CMazVQvSF3N6GDz0iQQFQEwtpLhe0UhpFs/UUOXUi2+JjLRkLf
  1107  fIeyk+K9EAz8Nd+vOMgr7ud70MykF7X5FzGLwJfz5bj62XVVifA8yMNoxIdlRFZp
  1108  H8OXzMwUO0+ktCf1StXCEV8/HoBP8BeKRl/PPqyHlY2rqXrNYq+ZXR+I25HWJcyJ
  1109  UUWQBq66yfxEIR2L5tJoVf0P7Z+WcplX7bo8T06n92zV5pU6WPi2+xAob2cmR9M4
  1110  thlHl5uUDIYRgXZ2RQnMhea2GcDtFi8zfeWIJTZ54CmUi326sToqvCbOrHMkxus9
  1111  xwIDAQABo1MwUTAdBgNVHQ4EFgQU5anvmjhvY6yPqy9qKU6cZAjX88MwHwYDVR0j
  1112  BBgwFoAU5anvmjhvY6yPqy9qKU6cZAjX88MwDwYDVR0TAQH/BAUwAwEB/zANBgkq
  1113  hkiG9w0BAQsFAAOCAgEAH2x/DTPnfxw06RZmjGCWOJJOUiO9uW4dVtLUzdCmOkuX
  1114  zngsqAB6Mqy+GXb4jsNdKdVR74Lb/9gv9WkXTxLPnW8sBmxC7NxjJZbQVlHuQK3U
  1115  s8GGo1wwuR/kCcnckdRAuDf8BbllqpPq+zUm1ZzM2NnMtiptkojxpleJugttXGiw
  1116  SHRh6hY3RZAKD6s6eyg67O6Dx3bFgyzYt91cG+YJPbSQh9hBhhlmp5GwOMg27u6N
  1117  skSNZIK2SNs5Bael+WfiUiEB1cFwUc0TYPUSJEkLXvLcqqVzuj53IEU7UCifYqHQ
  1118  xlhdROagJc8fSOQ0yEEwBPqDVRT3fJipAGRB7h8a0pEtfbD8M0df6DGkcRvOf/My
  1119  B2Ss8ZrL+tLDKEJji6aZXlkFbs6aKko0cKbZQvquISgEdcZp3hQ4oc+eUmkOfkk6
  1120  0D+7ZY3m4JDAr38tVDw3lG2I3THvmT8zdPZzujkZjHUvVqWaoEX1k6IUSZ5DnQ3H
  1121  NIf/SfR7aXBeCsoJnCE+nsN/ba2twS8Wx1evuWDdlVGYrE4ujXpCAKspwi0mPirx
  1122  bkAQVDU7e/Zr6P8ZI9P4w1MYZvKagPo1+hCCiEAaeNdgMoOwQbLpw6py0x7B+mxI
  1123  ppX5DVNV8wJAb9KqeSXwd89Z5unpeS6KZsMcb5qiK60Lj12aLmZ8ip6s7xjAS8Q=
  1124  -----END CERTIFICATE-----
  1125  `)