func TestDoc(t *testing.T) { for i, test := range tests { result := heredoc.Doc(test.raw) if result != test.expect { t.Errorf("tests[%d] failed: expected=> %#v, result=> %#v", i, test.expect, result) } } }
func ExampleDoc_spec() { // Single line string is no change. fmt.Println(heredoc.Doc(`It is single line.`)) // If first line is empty, heredoc.Doc removes first line. fmt.Println(heredoc.Doc(` It is first line. It is second line.`)) // If last line is empty and more little length than indents, // heredoc.Doc removes last line's content. fmt.Println(heredoc.Doc(` Next is last line. `)) fmt.Println("Previous is last line.") // Output: // It is single line. // It is first line. // It is second line. // Next is last line. // // Previous is last line. }
func ExampleDoc_lipsum() { fmt.Print(heredoc.Doc(` Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, ... `)) // Output: // Lorem ipsum dolor sit amet, consectetur adipisicing elit, // sed do eiusmod tempor incididunt ut labore et dolore magna // aliqua. Ut enim ad minim veniam, ... // }
func withFakeCommand(t *testing.T, envValues string, block func()) { _exec.Command = func(name string, arg ...string) *exec.Cmd { valueFile, _ := ioutil.TempFile("", "valueFile") valueFile.WriteString(heredoc.Doc(envValues)) valueFile.Close() dummy, _ := ioutil.TempFile("", "dummy") dummy.WriteString(heredoc.Docf(` #!/bin/sh cat %s `, valueFile.Name())) dummy.Close() t.Logf("dummy: %s", dummy.Name()) os.Chmod(dummy.Name(), 0755) return exec.Command(dummy.Name()) } block() }
// Run launches the OpenShift master. It takes optional installers that may install additional endpoints into the server. // All endpoints get configured CORS behavior // Protected installers' endpoints are protected by API authentication and authorization. // Unprotected installers' endpoints do not have any additional protection added. func (c *MasterConfig) Run(protected []APIInstaller, unprotected []APIInstaller) { var extra []string safe := genericapiserver.NewHandlerContainer(http.NewServeMux(), kapi.Codecs) open := genericapiserver.NewHandlerContainer(http.NewServeMux(), kapi.Codecs) // enforce authentication on protected endpoints protected = append(protected, APIInstallFunc(c.InstallProtectedAPI)) for _, i := range protected { msgs, err := i.InstallAPI(safe) if err != nil { glog.Fatalf("error installing api %v", err) } extra = append(extra, msgs...) } handler := c.versionSkewFilter(safe) handler = c.authorizationFilter(handler) handler = c.impersonationFilter(handler) // audit handler must comes before the impersonationFilter to read the original user handler = c.auditHandler(handler) handler = authenticationHandlerFilter(handler, c.Authenticator, c.getRequestContextMapper()) handler = namespacingFilter(handler, c.getRequestContextMapper()) handler = cacheControlFilter(handler, "no-store") // protected endpoints should not be cached // unprotected resources unprotected = append(unprotected, APIInstallFunc(c.InstallUnprotectedAPI)) for _, i := range unprotected { msgs, err := i.InstallAPI(open) if err != nil { glog.Fatalf("error installing api %v", err) } extra = append(extra, msgs...) } var kubeAPILevels []string if c.Options.KubernetesMasterConfig != nil { kubeAPILevels = configapi.GetEnabledAPIVersionsForGroup(*c.Options.KubernetesMasterConfig, kapi.GroupName) } handler = indexAPIPaths(c.Options.APILevels, kubeAPILevels, handler) open.Handle("/", handler) // install swagger swaggerConfig := swagger.Config{ WebServicesUrl: c.Options.MasterPublicURL, WebServices: append(safe.RegisteredWebServices(), open.RegisteredWebServices()...), ApiPath: swaggerAPIPrefix, PostBuildHandler: customizeSwaggerDefinition, } // log nothing from swagger swagger.LogInfo = func(format string, v ...interface{}) {} swagger.RegisterSwaggerService(swaggerConfig, open) extra = append(extra, fmt.Sprintf("Started Swagger Schema API at %%s%s", swaggerAPIPrefix)) openAPIConfig := openapi.Config{ SwaggerConfig: &swaggerConfig, IgnorePrefixes: []string{"/swaggerapi"}, Info: &spec.Info{ InfoProps: spec.InfoProps{ Title: "OpenShift API (with Kubernetes)", Version: version.Get().String(), License: &spec.License{ Name: "Apache 2.0 (ASL2.0)", URL: "http://www.apache.org/licenses/LICENSE-2.0", }, Description: heredoc.Doc(` OpenShift provides builds, application lifecycle, image content management, and administrative policy on top of Kubernetes. The API allows consistent management of those objects. All API operations are authenticated via an Authorization bearer token that is provided for service accounts as a generated secret (in JWT form) or via the native OAuth endpoint located at /oauth/authorize. Core infrastructure components may use client certificates that require no authentication. All API operations return a 'resourceVersion' string that represents the version of the object in the underlying storage. The standard LIST operation performs a snapshot read of the underlying objects, returning a resourceVersion representing a consistent version of the listed objects. The WATCH operation allows all updates to a set of objects after the provided resourceVersion to be observed by a client. By listing and beginning a watch from the returned resourceVersion, clients may observe a consistent view of the state of one or more objects. Note that WATCH always returns the update after the provided resourceVersion. Watch may be extended a limited time in the past - using etcd 2 the watch window is 1000 events (which on a large cluster may only be a few tens of seconds) so clients must explicitly handle the "watch to old error" by re-listing. Objects are divided into two rough categories - those that have a lifecycle and must reflect the state of the cluster, and those that have no state. Objects with lifecycle typically have three main sections: * 'metadata' common to all objects * a 'spec' that represents the desired state * a 'status' that represents how much of the desired state is reflected on the cluster at the current time Objects that have no state have 'metadata' but may lack a 'spec' or 'status' section. Objects are divided into those that are namespace scoped (only exist inside of a namespace) and those that are cluster scoped (exist outside of a namespace). A namespace scoped resource will be deleted when the namespace is deleted and cannot be created if the namespace has not yet been created or is in the process of deletion. Cluster scoped resources are typically only accessible to admins - resources like nodes, persistent volumes, and cluster policy. All objects have a schema that is a combination of the 'kind' and 'apiVersion' fields. This schema is additive only for any given version - no backwards incompatible changes are allowed without incrementing the apiVersion. The server will return and accept a number of standard responses that share a common schema - for instance, the common error type is 'unversioned.Status' (described below) and will be returned on any error from the API server. The API is available in multiple serialization formats - the default is JSON (Accept: application/json and Content-Type: application/json) but clients may also use YAML (application/yaml) or the native Protobuf schema (application/vnd.kubernetes.protobuf). Note that the format of the WATCH API call is slightly different - for JSON it returns newline delimited objects while for Protobuf it returns length-delimited frames (4 bytes in network-order) that contain a 'versioned.Watch' Protobuf object. See the OpenShift documentation at https://docs.openshift.org for more information. `), }, }, DefaultResponse: &spec.Response{ ResponseProps: spec.ResponseProps{ Description: "Default Response.", }, }, } err := openapi.RegisterOpenAPIService(&openAPIConfig, open) if err != nil { glog.Fatalf("Failed to generate open api spec: %v", err) } extra = append(extra, fmt.Sprintf("Started OpenAPI Schema at %%s%s", openapi.OpenAPIServePath)) handler = open // add CORS support if origins := c.ensureCORSAllowedOrigins(); len(origins) != 0 { handler = apiserver.CORS(handler, origins, nil, nil, "true") } if c.WebConsoleEnabled() { handler = assetServerRedirect(handler, c.Options.AssetConfig.PublicURL) } // Make the outermost filter the requestContextMapper to ensure all components share the same context if contextHandler, err := kapi.NewRequestContextFilter(c.getRequestContextMapper(), handler); err != nil { glog.Fatalf("Error setting up request context filter: %v", err) } else { handler = contextHandler } longRunningRequestCheck := apiserver.BasicLongRunningRequestCheck(longRunningRE, map[string]string{"watch": "true"}) // TODO: MaxRequestsInFlight should be subdivided by intent, type of behavior, and speed of // execution - updates vs reads, long reads vs short reads, fat reads vs skinny reads. if c.Options.ServingInfo.MaxRequestsInFlight > 0 { sem := make(chan bool, c.Options.ServingInfo.MaxRequestsInFlight) handler = apiserver.MaxInFlightLimit(sem, longRunningRequestCheck, handler) } c.serve(handler, extra) // Attempt to verify the server came up for 20 seconds (100 tries * 100ms, 100ms timeout per try) cmdutil.WaitForSuccessfulDial(c.TLS, c.Options.ServingInfo.BindNetwork, c.Options.ServingInfo.BindAddress, 100*time.Millisecond, 100*time.Millisecond, 100) }
func transformError(err error, baseName, commandName, commandPath string, groups errorGroups) { switch t := err.(type) { case newcmd.ErrRequiresExplicitAccess: if t.Input.Token != nil && t.Input.Token.ServiceAccount { groups.Add( "explicit-access-installer", heredoc.Doc(` WARNING: This will allow the pod to create and manage resources within your namespace - ensure you trust the image with those permissions before you continue. You can see more information about the image by adding the --dry-run flag. If you trust the provided image, include the flag --grant-install-rights.`, ), fmt.Errorf("installing %q requires an 'installer' service account with project editor access", t.Match.Value), ) } else { groups.Add( "explicit-access-you", heredoc.Doc(` WARNING: This will allow the pod to act as you across the entire cluster - ensure you trust the image with those permissions before you continue. You can see more information about the image by adding the --dry-run flag. If you trust the provided image, include the flag --grant-install-rights.`, ), fmt.Errorf("installing %q requires that you grant the image access to run with your credentials", t.Match.Value), ) } return case newapp.ErrNoMatch: groups.Add( "no-matches", heredoc.Docf(` The '%[1]s' command will match arguments to the following types: 1. Images tagged into image streams in the current project or the 'openshift' project - if you don't specify a tag, we'll add ':latest' 2. Images in the Docker Hub, on remote registries, or on the local Docker engine 3. Templates in the current project or the 'openshift' project 4. Git repository URLs or local paths that point to Git repositories --allow-missing-images can be used to point to an image that does not exist yet. See '%[1]s -h' for examples.`, commandPath, ), t, t.Errs..., ) return case newapp.ErrMultipleMatches: buf := &bytes.Buffer{} for i, match := range t.Matches { // If we have more than 5 matches, stop output and recommend searching // after the fifth if i >= 5 { groups.Add( "multiple-matches", heredoc.Docf(` The argument %[1]q could apply to the following Docker images, OpenShift image streams, or templates: %[2]sTo view a full list of matches, use '%[3]s %[4]s -S %[1]s'`, t.Value, buf.String(), baseName, commandName, ), t, t.Errs..., ) return } fmt.Fprintf(buf, "* %s\n", match.Description) fmt.Fprintf(buf, " Use %[1]s to specify this image or template\n\n", match.Argument) } groups.Add( "multiple-matches", heredoc.Docf(` The argument %[1]q could apply to the following Docker images, OpenShift image streams, or templates: %[2]s`, t.Value, buf.String(), ), t, t.Errs..., ) return case newapp.ErrPartialMatch: buf := &bytes.Buffer{} fmt.Fprintf(buf, "* %s\n", t.Match.Description) fmt.Fprintf(buf, " Use %[1]s to specify this image or template\n\n", t.Match.Argument) groups.Add( "partial-match", heredoc.Docf(` The argument %[1]q only partially matched the following Docker image, OpenShift image stream, or template: %[2]s`, t.Value, buf.String(), ), t, t.Errs..., ) return case newapp.ErrNoTagsFound: buf := &bytes.Buffer{} fmt.Fprintf(buf, " Use --allow-missing-imagestream-tags to use this image stream\n\n") groups.Add( "no-tags", heredoc.Docf(` The image stream %[1]q exists, but it has no tags. %[2]s`, t.Match.Name, buf.String(), ), t, t.Errs..., ) return } switch err { case errNoTokenAvailable: // TODO: improve by allowing token generation groups.Add("", "", fmt.Errorf("to install components you must be logged in with an OAuth token (instead of only a certificate)")) case newcmd.ErrNoInputs: // TODO: suggest things to the user groups.Add("", "", usageError(commandPath, newAppNoInput, baseName, commandName)) default: groups.Add("", "", err) } }
"github.com/openshift/origin/pkg/cmd/util/clientcmd" ) type concept struct { Name string Abbreviation string Description string } var concepts = []concept{ { "Containers", "", heredoc.Doc(` A definition of how to run one or more processes inside of a portable Linux environment. Containers are started from an Image and are usually isolated from other containers on the same machine. `), }, { "Image", "", heredoc.Doc(` A layered Linux filesystem that contains application code, dependencies, and any supporting operating system libraries. An image is identified by a name that can be local to the current cluster or point to a remote Docker registry (a storage server for images). `), }, { "Pods", "pod",
func (s normalizer) heredoc() normalizer { s.string = heredoc.Doc(s.string) return s }
// TestNewBuildRun ensures that Run command calls the right actions // and returns the expected error. func TestNewBuildRun(t *testing.T) { tests := []struct { name string config *newcmd.AppConfig expectedActions []testAction expectedErr string }{ { name: "no input", config: &newcmd.AppConfig{}, expectedErr: usageError("oc new-build", newBuildNoInput, "oc").Error(), }, { name: "no matches", config: &newcmd.AppConfig{ ComponentInputs: newcmd.ComponentInputs{ Components: []string{"test"}, }, }, expectedErr: heredoc.Doc(` The 'oc new-build' command will match arguments to the following types: 1. Images tagged into image streams in the current project or the 'openshift' project - if you don't specify a tag, we'll add ':latest' 2. Images in the Docker Hub, on remote registries, or on the local Docker engine 3. Git repository URLs or local paths that point to Git repositories --allow-missing-images can be used to force the use of an image that was not matched See 'oc new-build -h' for examples.`), expectedActions: []testAction{ {verb: "list", resource: "imagestreams"}, {verb: "list", resource: "templates"}, }, }, } o := &NewBuildOptions{ Out: ioutil.Discard, CommandPath: "oc new-build", CommandName: "oc", } for _, test := range tests { client := testclient.NewSimpleFake() o.Config = test.config o.Config.SetOpenShiftClient(client, "openshift", nil) o.Config.DockerSearcher = MockSearcher{ OnSearch: func(precise bool, terms ...string) (app.ComponentMatches, []error) { return app.ComponentMatches{}, []error{} }, } o.Config.TemplateFileSearcher = MockSearcher{ OnSearch: func(precise bool, terms ...string) (app.ComponentMatches, []error) { return app.ComponentMatches{}, []error{} }, } if err := o.Run(); err != nil { if !strings.Contains(err.Error(), test.expectedErr) { t.Fatalf("[%s] error not expected: %v", test.name, err) } } else if len(test.expectedErr) != 0 { t.Fatalf("[%s] expected error: %v, got nil", test.name, test.expectedErr) } got := client.Actions() if len(test.expectedActions) != len(got) { t.Fatalf("action length mismatch: expected %d, got %d", len(test.expectedActions), len(got)) } for i, action := range test.expectedActions { if !got[i].Matches(action.verb, action.resource) { t.Errorf("action mismatch: expected %s %s, got %s %s", action.verb, action.resource, got[i].GetVerb(), got[i].GetResource()) } } } }
func TestTLSWithCertificateNotMatchingHostname(t *testing.T) { // generated by 'go run src/crypto/tls/generate_cert.go --rsa-bits 1024 --host invalidhost.com,8.8.8.8 --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h' invalidHostCert := heredoc.Doc(` -----BEGIN CERTIFICATE----- MIICBjCCAW+gAwIBAgIRALOIWXyeLzunaiVkP2itHAEwDQYJKoZIhvcNAQELBQAw EjEQMA4GA1UEChMHQWNtZSBDbzAgFw03MDAxMDEwMDAwMDBaGA8yMDg0MDEyOTE2 MDAwMFowEjEQMA4GA1UEChMHQWNtZSBDbzCBnzANBgkqhkiG9w0BAQEFAAOBjQAw gYkCgYEAuKDlC4aMBbHaXgS+MFud5h3zeE4boSqKgFI6HceySF/a+qg0v+ID6EwQ DpJ2W5AdJGEBfixo+tym6q3oKWHJUX0hInkJ6dXIdUbVOeO5dIsGG0fZmRD7DDDx snkXrDB/E0JglHNckRbIh/jvznbDfbddIcdgZ7JVIfnNpigtHZECAwEAAaNaMFgw DgYDVR0PAQH/BAQDAgKkMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1UdEwEB/wQF MAMBAf8wIAYDVR0RBBkwF4IPaW52YWxpZGhvc3QuY29thwQICAgIMA0GCSqGSIb3 DQEBCwUAA4GBAAkPU044aFkBl4f/muwSh/oPGinnA4fp8ei0KMnLk+0/CjNb3Waa GtuRVIudRTK2M/RzdpUrwfWlVmkezV4BR1K/aOH9a29zqDTkEjnkIbWwe+piAs+w VxIxrTqM8rqq8qxeWS54AyF/OaLJgXzDpCFnCb7kY3iyHv6lcmCjluLW -----END CERTIFICATE-----`) invalidHostKey := heredoc.Doc(` -----BEGIN RSA PRIVATE KEY----- MIICXQIBAAKBgQC4oOULhowFsdpeBL4wW53mHfN4ThuhKoqAUjodx7JIX9r6qDS/ 4gPoTBAOknZbkB0kYQF+LGj63KbqregpYclRfSEieQnp1ch1RtU547l0iwYbR9mZ EPsMMPGyeResMH8TQmCUc1yRFsiH+O/OdsN9t10hx2BnslUh+c2mKC0dkQIDAQAB AoGAZ0ZAuNC7NFhHEL5QcJZe3aC1Vv9B/0XfkWXtckkJFejggcNjNk5D50Xc2Xnd 0NvtITNN9Xj8BA83IyDCM5uqUwDbOLIc6qYgAGWzxZZSDAQg1iOAAZoXmMTNS6Zf hQhNUIwB68ELGvbcq7cxQL7L9n4GfISz7PKOOUKTZp0Q8G0CQQD07K7NES340c3I QVkCW5/ygNK0GuQ8nTcG5yC8R5SDS47N8YzPp17Pajah8+wawYiemY1fUmD7P/bq Cjl2RtIHAkEAwPo1GzJubN7PSYgPir3TxUGtMJoyc3jfdjblXyGJHwTu2YxeRjd2 YUPVRpu9JvNjZc+GONvTbTZeNWCvy0JNpwJBAKEsi49JCd6eefOZBTDnCKd1nLKG q8Ezl/2D5WfhFtsbwrrFhOs1cc++Tnte3/VvfC8aTwz2UfmkyyCSX+P0kMsCQCIL glb7/LNEU7mbQXKurq+8OHu8mG36wyGt6aVw2yoXyrOiqfclTcM3HmdIjoRSqBSM Ghfp4FECKHiuSBVJ6z0CQQDF37CRpdQRDPnAedhyApLcIxSbYo1oUm7FxBLyVb7V HQjFvsOylsSCABXz0FyC7zXQxkEo6CiSahVI/PHz6Zta -----END RSA PRIVATE KEY-----`) server, err := newTLSServer(invalidHostCert, invalidHostKey) if err != nil { t.Errorf(err.Error()) } server.StartTLS() defer server.Close() testCases := map[string]struct { serverURL string skipTLSVerify bool expectedErrMsg *regexp.Regexp }{ "succeed skipping tls": { serverURL: server.URL, skipTLSVerify: true, }, "certificate hostname doesn't match": { serverURL: server.URL, expectedErrMsg: regexp.MustCompile(`The server is using a certificate that does not match its hostname(.*)is valid for 8\.8\.8\.8`), }, } for name, test := range testCases { t.Logf("evaluating test: %s", name) options := &LoginOptions{ Server: test.serverURL, InsecureTLS: test.skipTLSVerify, StartingKubeConfig: &kclientcmdapi.Config{}, } if _, err = options.getClientConfig(); err != nil { if !test.expectedErrMsg.MatchString(err.Error()) { t.Errorf("%s: expected error %q but got %q", name, test.expectedErrMsg, err) } if test.expectedErrMsg == nil { t.Errorf("%s: unexpected error: %v", name, err) } } else { if test.expectedErrMsg != nil { t.Errorf("%s: expected error but got nothing", name) } } } }
func TestTLSWithExpiredCertificate(t *testing.T) { // generated by 'go run src/crypto/tls/generate_cert.go --rsa-bits 1024 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1h' expiredCert := heredoc.Doc(` -----BEGIN CERTIFICATE----- MIICEjCCAXugAwIBAgIRALf82bYpro/jQS8fP74dG5EwDQYJKoZIhvcNAQELBQAw EjEQMA4GA1UEChMHQWNtZSBDbzAeFw03MDAxMDEwMDAwMDBaFw03MDAxMDEwMTAw MDBaMBIxEDAOBgNVBAoTB0FjbWUgQ28wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJ AoGBAONNgDXBk2Q1i/aJjTwt03KpQ3nQblMS3IX/H9JWw6ta6UublKBOaD/2o5Xt FM+Q7XDEnzYw88CK5KHdyejkJo5IBpUjQYJZFzUJ1BC8Lw7yy6dXWYBJboRR1S+1 JhkMJOtpPecv+4cTaynplYj0WMBjcQthg2RM7tdpyUYpsp2rAgMBAAGjaDBmMA4G A1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTAD AQH/MC4GA1UdEQQnMCWCC2V4YW1wbGUuY29thwR/AAABhxAAAAAAAAAAAAAAAAAA AAABMA0GCSqGSIb3DQEBCwUAA4GBAFpdiiM5YAQQN0H5ZMNuHWGlprjp7qVilO8/ WFePZRWY2vQF8g7/c1cX4bPqG+qFJd+9j2UZNjhadNfMCxvu6BY7NCupOHVHmnRQ ocvkPoSqobE7qDPfiUuU1J+61Libu6b2IjV3/K9pvZkLiBrqn0YhoXXa0PG+rG1L 9X7+mb5z -----END CERTIFICATE-----`) expiredKey := heredoc.Doc(` -----BEGIN RSA PRIVATE KEY----- MIICXQIBAAKBgQDjTYA1wZNkNYv2iY08LdNyqUN50G5TEtyF/x/SVsOrWulLm5Sg Tmg/9qOV7RTPkO1wxJ82MPPAiuSh3cno5CaOSAaVI0GCWRc1CdQQvC8O8sunV1mA SW6EUdUvtSYZDCTraT3nL/uHE2sp6ZWI9FjAY3ELYYNkTO7XaclGKbKdqwIDAQAB AoGBAJPFWKqZ9CZboWhfuE/9Qs/yNonE9VRQmMkMOTXXblHCQpUCyjcFgkTDJUpc 3QCsKZD8Yr0qSe1M3qJUu+UKHf18LqwiL/ynnalYggxIFS5/SidWCngKvIuEfkLK VsnCK3jt5qx21iljGHU6bQZHnHB9IGEiBYcnQlvvw/WdvRDBAkEA8/KMpJVwnI1W 7fzcZ1+mbMeSJoAVIa9u7MgI+LIRZMokDRYeAMvEjm3GYpZDqA5l1dp7KochMep/ 0vSSTHt7ewJBAO6IbcUIDhXuh2qdxR/Xk5DdDCoxaD1o4ivyj9JsSlGa9JWD7kKN 6ZFFrn8i7uQuniC1Rwc/4yHhs6OqbiF695ECQQCBwVKzvFUwwDEr1yK4zXStSZ3g YqJaz4CV63RyK+z6ilaQq2H8FGaRR6yNBdYozre1/0ciAMxUS6H/6Fzk141/AkBe SguqIP8AaGObH3Z2mc65KsfOPe2IqNcOrDlx4mCWVXxtRdN+933mcPcDRpnMFSlo oH/NO9Ha6M8L2SjjjyohAkBJHU61+OWz/TAy1nxsMbFsISLn/JrdEZIf2uFORlDN Z3/XIQ+yeg4Jk1VbTMZ0/fHf9xMFR8acC/7n7jxnzQau -----END RSA PRIVATE KEY-----`) server, err := newTLSServer(expiredCert, expiredKey) if err != nil { t.Errorf(err.Error()) } server.StartTLS() defer server.Close() testCases := map[string]struct { serverURL string skipTLSVerify bool expectedErrMsg *regexp.Regexp }{ "succeed skipping tls": { serverURL: server.URL, skipTLSVerify: true, }, "certificate expired": { serverURL: server.URL, expectedErrMsg: regexp.MustCompile(`The server is using an invalid certificate(.*)has expired`), }, } for name, test := range testCases { t.Logf("evaluating test: %s", name) options := &LoginOptions{ Server: test.serverURL, InsecureTLS: test.skipTLSVerify, StartingKubeConfig: &kclientcmdapi.Config{}, } if _, err = options.getClientConfig(); err != nil { if !test.expectedErrMsg.MatchString(err.Error()) { t.Errorf("%s: expected error %q but got %q", name, test.expectedErrMsg, err) } if test.expectedErrMsg == nil { t.Errorf("%s: unexpected error: %v", name, err) } } else { if test.expectedErrMsg != nil { t.Errorf("%s: expected error but got nothing", name) } } } }
// Shortcut heredoc.Doc func D(raw string) string { return heredoc.Doc(raw) }