github.com/fastly/cli@v1.7.2-0.20240304164155-9d0f1d77c3bf/pkg/commands/logging/sftp/sftp_integration_test.go (about)

     1  package sftp_test
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"io"
     7  	"strings"
     8  	"testing"
     9  
    10  	"github.com/fastly/go-fastly/v9/fastly"
    11  
    12  	"github.com/fastly/cli/pkg/app"
    13  	"github.com/fastly/cli/pkg/global"
    14  	"github.com/fastly/cli/pkg/mock"
    15  	"github.com/fastly/cli/pkg/testutil"
    16  )
    17  
    18  func TestSFTPCreate(t *testing.T) {
    19  	args := testutil.Args
    20  	scenarios := []struct {
    21  		args       []string
    22  		api        mock.API
    23  		wantError  string
    24  		wantOutput string
    25  	}{
    26  		{
    27  			args: args("logging sftp create --service-id 123 --version 1 --name log --address example.com --user user --ssh-known-hosts knownHosts() --port 80 --autoclone"),
    28  			api: mock.API{
    29  				ListVersionsFn: testutil.ListVersions,
    30  				CloneVersionFn: testutil.CloneVersionResult(4),
    31  				CreateSFTPFn:   createSFTPOK,
    32  			},
    33  			wantOutput: "Created SFTP logging endpoint log (service 123 version 4)",
    34  		},
    35  		{
    36  			args: args("logging sftp create --service-id 123 --version 1 --name log --address example.com --user user --ssh-known-hosts knownHosts() --port 80 --autoclone"),
    37  			api: mock.API{
    38  				ListVersionsFn: testutil.ListVersions,
    39  				CloneVersionFn: testutil.CloneVersionResult(4),
    40  				CreateSFTPFn:   createSFTPError,
    41  			},
    42  			wantError: errTest.Error(),
    43  		},
    44  		{
    45  			args: args("logging sftp create --service-id 123 --version 1 --name log --address example.com --user anonymous --ssh-known-hosts knownHosts() --port 80 --compression-codec zstd --gzip-level 9 --autoclone"),
    46  			api: mock.API{
    47  				ListVersionsFn: testutil.ListVersions,
    48  				CloneVersionFn: testutil.CloneVersionResult(4),
    49  			},
    50  			wantError: "error parsing arguments: the --compression-codec flag is mutually exclusive with the --gzip-level flag",
    51  		},
    52  	}
    53  	for testcaseIdx := range scenarios {
    54  		testcase := &scenarios[testcaseIdx]
    55  		t.Run(strings.Join(testcase.args, " "), func(t *testing.T) {
    56  			var stdout bytes.Buffer
    57  			app.Init = func(_ []string, _ io.Reader) (*global.Data, error) {
    58  				opts := testutil.MockGlobalData(testcase.args, &stdout)
    59  				opts.APIClientFactory = mock.APIClient(testcase.api)
    60  				return opts, nil
    61  			}
    62  			err := app.Run(testcase.args, nil)
    63  			testutil.AssertErrorContains(t, err, testcase.wantError)
    64  			testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput)
    65  		})
    66  	}
    67  }
    68  
    69  func TestSFTPList(t *testing.T) {
    70  	args := testutil.Args
    71  	scenarios := []struct {
    72  		args       []string
    73  		api        mock.API
    74  		wantError  string
    75  		wantOutput string
    76  	}{
    77  		{
    78  			args: args("logging sftp list --service-id 123 --version 1"),
    79  			api: mock.API{
    80  				ListVersionsFn: testutil.ListVersions,
    81  				ListSFTPsFn:    listSFTPsOK,
    82  			},
    83  			wantOutput: listSFTPsShortOutput,
    84  		},
    85  		{
    86  			args: args("logging sftp list --service-id 123 --version 1 --verbose"),
    87  			api: mock.API{
    88  				ListVersionsFn: testutil.ListVersions,
    89  				ListSFTPsFn:    listSFTPsOK,
    90  			},
    91  			wantOutput: listSFTPsVerboseOutput,
    92  		},
    93  		{
    94  			args: args("logging sftp list --service-id 123 --version 1 -v"),
    95  			api: mock.API{
    96  				ListVersionsFn: testutil.ListVersions,
    97  				ListSFTPsFn:    listSFTPsOK,
    98  			},
    99  			wantOutput: listSFTPsVerboseOutput,
   100  		},
   101  		{
   102  			args: args("logging sftp --verbose list --service-id 123 --version 1"),
   103  			api: mock.API{
   104  				ListVersionsFn: testutil.ListVersions,
   105  				ListSFTPsFn:    listSFTPsOK,
   106  			},
   107  			wantOutput: listSFTPsVerboseOutput,
   108  		},
   109  		{
   110  			args: args("logging -v sftp list --service-id 123 --version 1"),
   111  			api: mock.API{
   112  				ListVersionsFn: testutil.ListVersions,
   113  				ListSFTPsFn:    listSFTPsOK,
   114  			},
   115  			wantOutput: listSFTPsVerboseOutput,
   116  		},
   117  		{
   118  			args: args("logging sftp list --service-id 123 --version 1"),
   119  			api: mock.API{
   120  				ListVersionsFn: testutil.ListVersions,
   121  				ListSFTPsFn:    listSFTPsError,
   122  			},
   123  			wantError: errTest.Error(),
   124  		},
   125  	}
   126  	for testcaseIdx := range scenarios {
   127  		testcase := &scenarios[testcaseIdx]
   128  		t.Run(strings.Join(testcase.args, " "), func(t *testing.T) {
   129  			var stdout bytes.Buffer
   130  			app.Init = func(_ []string, _ io.Reader) (*global.Data, error) {
   131  				opts := testutil.MockGlobalData(testcase.args, &stdout)
   132  				opts.APIClientFactory = mock.APIClient(testcase.api)
   133  				return opts, nil
   134  			}
   135  			err := app.Run(testcase.args, nil)
   136  			testutil.AssertErrorContains(t, err, testcase.wantError)
   137  			testutil.AssertString(t, testcase.wantOutput, stdout.String())
   138  		})
   139  	}
   140  }
   141  
   142  func TestSFTPDescribe(t *testing.T) {
   143  	args := testutil.Args
   144  	scenarios := []struct {
   145  		args       []string
   146  		api        mock.API
   147  		wantError  string
   148  		wantOutput string
   149  	}{
   150  		{
   151  			args:      args("logging sftp describe --service-id 123 --version 1"),
   152  			wantError: "error parsing arguments: required flag --name not provided",
   153  		},
   154  		{
   155  			args: args("logging sftp describe --service-id 123 --version 1 --name logs"),
   156  			api: mock.API{
   157  				ListVersionsFn: testutil.ListVersions,
   158  				GetSFTPFn:      getSFTPError,
   159  			},
   160  			wantError: errTest.Error(),
   161  		},
   162  		{
   163  			args: args("logging sftp describe --service-id 123 --version 1 --name logs"),
   164  			api: mock.API{
   165  				ListVersionsFn: testutil.ListVersions,
   166  				GetSFTPFn:      getSFTPOK,
   167  			},
   168  			wantOutput: describeSFTPOutput,
   169  		},
   170  	}
   171  	for testcaseIdx := range scenarios {
   172  		testcase := &scenarios[testcaseIdx]
   173  		t.Run(strings.Join(testcase.args, " "), func(t *testing.T) {
   174  			var stdout bytes.Buffer
   175  			app.Init = func(_ []string, _ io.Reader) (*global.Data, error) {
   176  				opts := testutil.MockGlobalData(testcase.args, &stdout)
   177  				opts.APIClientFactory = mock.APIClient(testcase.api)
   178  				return opts, nil
   179  			}
   180  			err := app.Run(testcase.args, nil)
   181  			testutil.AssertErrorContains(t, err, testcase.wantError)
   182  			testutil.AssertString(t, testcase.wantOutput, stdout.String())
   183  		})
   184  	}
   185  }
   186  
   187  func TestSFTPUpdate(t *testing.T) {
   188  	args := testutil.Args
   189  	scenarios := []struct {
   190  		args       []string
   191  		api        mock.API
   192  		wantError  string
   193  		wantOutput string
   194  	}{
   195  		{
   196  			args:      args("logging sftp update --service-id 123 --version 1 --new-name log"),
   197  			wantError: "error parsing arguments: required flag --name not provided",
   198  		},
   199  		{
   200  			args: args("logging sftp update --service-id 123 --version 1 --name logs --new-name log --autoclone"),
   201  			api: mock.API{
   202  				ListVersionsFn: testutil.ListVersions,
   203  				CloneVersionFn: testutil.CloneVersionResult(4),
   204  				UpdateSFTPFn:   updateSFTPError,
   205  			},
   206  			wantError: errTest.Error(),
   207  		},
   208  		{
   209  			args: args("logging sftp update --service-id 123 --version 1 --name logs --new-name log --autoclone"),
   210  			api: mock.API{
   211  				ListVersionsFn: testutil.ListVersions,
   212  				CloneVersionFn: testutil.CloneVersionResult(4),
   213  				UpdateSFTPFn:   updateSFTPOK,
   214  			},
   215  			wantOutput: "Updated SFTP logging endpoint log (service 123 version 4)",
   216  		},
   217  	}
   218  	for testcaseIdx := range scenarios {
   219  		testcase := &scenarios[testcaseIdx]
   220  		t.Run(strings.Join(testcase.args, " "), func(t *testing.T) {
   221  			var stdout bytes.Buffer
   222  			app.Init = func(_ []string, _ io.Reader) (*global.Data, error) {
   223  				opts := testutil.MockGlobalData(testcase.args, &stdout)
   224  				opts.APIClientFactory = mock.APIClient(testcase.api)
   225  				return opts, nil
   226  			}
   227  			err := app.Run(testcase.args, nil)
   228  			testutil.AssertErrorContains(t, err, testcase.wantError)
   229  			testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput)
   230  		})
   231  	}
   232  }
   233  
   234  func TestSFTPDelete(t *testing.T) {
   235  	args := testutil.Args
   236  	scenarios := []struct {
   237  		args       []string
   238  		api        mock.API
   239  		wantError  string
   240  		wantOutput string
   241  	}{
   242  		{
   243  			args:      args("logging sftp delete --service-id 123 --version 1"),
   244  			wantError: "error parsing arguments: required flag --name not provided",
   245  		},
   246  		{
   247  			args: args("logging sftp delete --service-id 123 --version 1 --name logs --autoclone"),
   248  			api: mock.API{
   249  				ListVersionsFn: testutil.ListVersions,
   250  				CloneVersionFn: testutil.CloneVersionResult(4),
   251  				DeleteSFTPFn:   deleteSFTPError,
   252  			},
   253  			wantError: errTest.Error(),
   254  		},
   255  		{
   256  			args: args("logging sftp delete --service-id 123 --version 1 --name logs --autoclone"),
   257  			api: mock.API{
   258  				ListVersionsFn: testutil.ListVersions,
   259  				CloneVersionFn: testutil.CloneVersionResult(4),
   260  				DeleteSFTPFn:   deleteSFTPOK,
   261  			},
   262  			wantOutput: "Deleted SFTP logging endpoint logs (service 123 version 4)",
   263  		},
   264  	}
   265  	for testcaseIdx := range scenarios {
   266  		testcase := &scenarios[testcaseIdx]
   267  		t.Run(strings.Join(testcase.args, " "), func(t *testing.T) {
   268  			var stdout bytes.Buffer
   269  			app.Init = func(_ []string, _ io.Reader) (*global.Data, error) {
   270  				opts := testutil.MockGlobalData(testcase.args, &stdout)
   271  				opts.APIClientFactory = mock.APIClient(testcase.api)
   272  				return opts, nil
   273  			}
   274  			err := app.Run(testcase.args, nil)
   275  			testutil.AssertErrorContains(t, err, testcase.wantError)
   276  			testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput)
   277  		})
   278  	}
   279  }
   280  
   281  var errTest = errors.New("fixture error")
   282  
   283  func createSFTPOK(i *fastly.CreateSFTPInput) (*fastly.SFTP, error) {
   284  	s := fastly.SFTP{
   285  		ServiceID:        fastly.ToPointer(i.ServiceID),
   286  		ServiceVersion:   fastly.ToPointer(i.ServiceVersion),
   287  		CompressionCodec: fastly.ToPointer("zstd"),
   288  	}
   289  
   290  	if i.Name != nil {
   291  		s.Name = i.Name
   292  	}
   293  
   294  	return &s, nil
   295  }
   296  
   297  func createSFTPError(_ *fastly.CreateSFTPInput) (*fastly.SFTP, error) {
   298  	return nil, errTest
   299  }
   300  
   301  func listSFTPsOK(i *fastly.ListSFTPsInput) ([]*fastly.SFTP, error) {
   302  	return []*fastly.SFTP{
   303  		{
   304  			ServiceID:         fastly.ToPointer(i.ServiceID),
   305  			ServiceVersion:    fastly.ToPointer(i.ServiceVersion),
   306  			Name:              fastly.ToPointer("logs"),
   307  			Address:           fastly.ToPointer("127.0.0.1"),
   308  			Port:              fastly.ToPointer(514),
   309  			User:              fastly.ToPointer("user"),
   310  			Password:          fastly.ToPointer("password"),
   311  			PublicKey:         fastly.ToPointer(pgpPublicKey()),
   312  			SecretKey:         fastly.ToPointer(sshPrivateKey()),
   313  			SSHKnownHosts:     fastly.ToPointer(knownHosts()),
   314  			Path:              fastly.ToPointer("/logs"),
   315  			Period:            fastly.ToPointer(3600),
   316  			Format:            fastly.ToPointer(`%h %l %u %t "%r" %>s %b`),
   317  			FormatVersion:     fastly.ToPointer(2),
   318  			MessageType:       fastly.ToPointer("classic"),
   319  			ResponseCondition: fastly.ToPointer("Prevent default logging"),
   320  			TimestampFormat:   fastly.ToPointer("%Y-%m-%dT%H:%M:%S.000"),
   321  			Placement:         fastly.ToPointer("none"),
   322  			CompressionCodec:  fastly.ToPointer("zstd"),
   323  		},
   324  		{
   325  			ServiceID:         fastly.ToPointer(i.ServiceID),
   326  			ServiceVersion:    fastly.ToPointer(i.ServiceVersion),
   327  			Name:              fastly.ToPointer("analytics"),
   328  			Address:           fastly.ToPointer("example.com"),
   329  			Port:              fastly.ToPointer(123),
   330  			User:              fastly.ToPointer("user"),
   331  			Password:          fastly.ToPointer("password"),
   332  			PublicKey:         fastly.ToPointer(pgpPublicKey()),
   333  			SecretKey:         fastly.ToPointer(sshPrivateKey()),
   334  			SSHKnownHosts:     fastly.ToPointer(knownHosts()),
   335  			Path:              fastly.ToPointer("/analytics"),
   336  			Period:            fastly.ToPointer(3600),
   337  			Format:            fastly.ToPointer(`%h %l %u %t "%r" %>s %b`),
   338  			MessageType:       fastly.ToPointer("classic"),
   339  			FormatVersion:     fastly.ToPointer(2),
   340  			ResponseCondition: fastly.ToPointer("Prevent default logging"),
   341  			TimestampFormat:   fastly.ToPointer("%Y-%m-%dT%H:%M:%S.000"),
   342  			Placement:         fastly.ToPointer("none"),
   343  			CompressionCodec:  fastly.ToPointer("zstd"),
   344  		},
   345  	}, nil
   346  }
   347  
   348  func listSFTPsError(_ *fastly.ListSFTPsInput) ([]*fastly.SFTP, error) {
   349  	return nil, errTest
   350  }
   351  
   352  var listSFTPsShortOutput = strings.TrimSpace(`
   353  SERVICE  VERSION  NAME
   354  123      1        logs
   355  123      1        analytics
   356  `) + "\n"
   357  
   358  var listSFTPsVerboseOutput = strings.TrimSpace(`
   359  Fastly API endpoint: https://api.fastly.com
   360  Fastly API token provided via config file (profile: user)
   361  
   362  Service ID (via --service-id): 123
   363  
   364  Version: 1
   365  	SFTP 1/2
   366  		Service ID: 123
   367  		Version: 1
   368  		Name: logs
   369  		Address: 127.0.0.1
   370  		Port: 514
   371  		User: user
   372  		Password: password
   373  		Public key: `+pgpPublicKey()+`
   374  		Secret key: `+sshPrivateKey()+`
   375  		SSH known hosts: `+knownHosts()+`
   376  		Path: /logs
   377  		Period: 3600
   378  		GZip level: 0
   379  		Format: %h %l %u %t "%r" %>s %b
   380  		Format version: 2
   381  		Message type: classic
   382  		Response condition: Prevent default logging
   383  		Timestamp format: %Y-%m-%dT%H:%M:%S.000
   384  		Placement: none
   385  		Compression codec: zstd
   386  	SFTP 2/2
   387  		Service ID: 123
   388  		Version: 1
   389  		Name: analytics
   390  		Address: example.com
   391  		Port: 123
   392  		User: user
   393  		Password: password
   394  		Public key: `+pgpPublicKey()+`
   395  		Secret key: `+sshPrivateKey()+`
   396  		SSH known hosts: `+knownHosts()+`
   397  		Path: /analytics
   398  		Period: 3600
   399  		GZip level: 0
   400  		Format: %h %l %u %t "%r" %>s %b
   401  		Format version: 2
   402  		Message type: classic
   403  		Response condition: Prevent default logging
   404  		Timestamp format: %Y-%m-%dT%H:%M:%S.000
   405  		Placement: none
   406  		Compression codec: zstd
   407  `) + "\n\n"
   408  
   409  func getSFTPOK(i *fastly.GetSFTPInput) (*fastly.SFTP, error) {
   410  	return &fastly.SFTP{
   411  		ServiceID:         fastly.ToPointer(i.ServiceID),
   412  		ServiceVersion:    fastly.ToPointer(i.ServiceVersion),
   413  		Name:              fastly.ToPointer("logs"),
   414  		Address:           fastly.ToPointer("example.com"),
   415  		Port:              fastly.ToPointer(514),
   416  		User:              fastly.ToPointer("user"),
   417  		Password:          fastly.ToPointer("password"),
   418  		PublicKey:         fastly.ToPointer(pgpPublicKey()),
   419  		SecretKey:         fastly.ToPointer(sshPrivateKey()),
   420  		SSHKnownHosts:     fastly.ToPointer(knownHosts()),
   421  		Path:              fastly.ToPointer("/logs"),
   422  		Period:            fastly.ToPointer(3600),
   423  		GzipLevel:         fastly.ToPointer(2),
   424  		Format:            fastly.ToPointer(`%h %l %u %t "%r" %>s %b`),
   425  		FormatVersion:     fastly.ToPointer(2),
   426  		MessageType:       fastly.ToPointer("classic"),
   427  		ResponseCondition: fastly.ToPointer("Prevent default logging"),
   428  		TimestampFormat:   fastly.ToPointer("%Y-%m-%dT%H:%M:%S.000"),
   429  		Placement:         fastly.ToPointer("none"),
   430  		CompressionCodec:  fastly.ToPointer("zstd"),
   431  	}, nil
   432  }
   433  
   434  func getSFTPError(_ *fastly.GetSFTPInput) (*fastly.SFTP, error) {
   435  	return nil, errTest
   436  }
   437  
   438  var describeSFTPOutput = `
   439  Address: example.com
   440  Compression codec: zstd
   441  Format: %h %l %u %t "%r" %>s %b
   442  Format version: 2
   443  GZip level: 2
   444  Message type: classic
   445  Name: logs
   446  Password: password
   447  Path: /logs
   448  Period: 3600
   449  Placement: none
   450  Port: 514
   451  Public key: ` + pgpPublicKey() + `
   452  Response condition: Prevent default logging
   453  SSH known hosts: ` + knownHosts() + `
   454  Secret key: ` + sshPrivateKey() + `
   455  Service ID: 123
   456  Timestamp format: %Y-%m-%dT%H:%M:%S.000
   457  User: user
   458  Version: 1
   459  `
   460  
   461  func updateSFTPOK(i *fastly.UpdateSFTPInput) (*fastly.SFTP, error) {
   462  	return &fastly.SFTP{
   463  		ServiceID:         fastly.ToPointer(i.ServiceID),
   464  		ServiceVersion:    fastly.ToPointer(i.ServiceVersion),
   465  		Name:              fastly.ToPointer("log"),
   466  		Address:           fastly.ToPointer("example.com"),
   467  		Port:              fastly.ToPointer(514),
   468  		User:              fastly.ToPointer("user"),
   469  		Password:          fastly.ToPointer("password"),
   470  		PublicKey:         fastly.ToPointer(pgpPublicKey()),
   471  		SecretKey:         fastly.ToPointer(sshPrivateKey()),
   472  		SSHKnownHosts:     fastly.ToPointer(knownHosts()),
   473  		Path:              fastly.ToPointer("/logs"),
   474  		Period:            fastly.ToPointer(3600),
   475  		Format:            fastly.ToPointer(`%h %l %u %t "%r" %>s %b`),
   476  		FormatVersion:     fastly.ToPointer(2),
   477  		MessageType:       fastly.ToPointer("classic"),
   478  		ResponseCondition: fastly.ToPointer("Prevent default logging"),
   479  		TimestampFormat:   fastly.ToPointer("%Y-%m-%dT%H:%M:%S.000"),
   480  		Placement:         fastly.ToPointer("none"),
   481  		CompressionCodec:  fastly.ToPointer("zstd"),
   482  	}, nil
   483  }
   484  
   485  func updateSFTPError(_ *fastly.UpdateSFTPInput) (*fastly.SFTP, error) {
   486  	return nil, errTest
   487  }
   488  
   489  func deleteSFTPOK(_ *fastly.DeleteSFTPInput) error {
   490  	return nil
   491  }
   492  
   493  func deleteSFTPError(_ *fastly.DeleteSFTPInput) error {
   494  	return errTest
   495  }
   496  
   497  // knownHosts returns sample known hosts suitable for testing.
   498  func knownHosts() string {
   499  	return strings.TrimSpace(`
   500  example.com
   501  127.0.0.1
   502  `)
   503  }
   504  
   505  // pgpPublicKey returns a PEM encoded PGP public key suitable for testing.
   506  func pgpPublicKey() string {
   507  	return strings.TrimSpace(`-----BEGIN PGP PUBLIC KEY BLOCK-----
   508  mQENBFyUD8sBCACyFnB39AuuTygseek+eA4fo0cgwva6/FSjnWq7riouQee8GgQ/
   509  ibXTRyv4iVlwI12GswvMTIy7zNvs1R54i0qvsLr+IZ4GVGJqs6ZJnvQcqe3xPoR4
   510  8AnBfw90o32r/LuHf6QCJXi+AEu35koNlNAvLJ2B+KACaNB7N0EeWmqpV/1V2k9p
   511  lDYk+th7LcCuaFNGqKS/PrMnnMqR6VDLCjHhNx4KR79b0Twm/2qp6an3hyNRu8Gn
   512  dwxpf1/BUu3JWf+LqkN4Y3mbOmSUL3MaJNvyQguUzTfS0P0uGuBDHrJCVkMZCzDB
   513  89ag55jCPHyGeHBTd02gHMWzsg3WMBWvCsrzABEBAAG0JXRlcnJhZm9ybSAodGVz
   514  dCkgPHRlc3RAdGVycmFmb3JtLmNvbT6JAU4EEwEIADgWIQSHYyc6Kj9l6HzQsau6
   515  vFFc9jxV/wUCXJQPywIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRC6vFFc
   516  9jxV/815CAClb32OxV7wG01yF97TzlyTl8TnvjMtoG29Mw4nSyg+mjM3b8N7iXm9
   517  OLX59fbDAWtBSldSZE22RXd3CvlFOG/EnKBXSjBtEqfyxYSnyOPkMPBYWGL/ApkX
   518  SvPYJ4LKdvipYToKFh3y9kk2gk1DcDBDyaaHvR+3rv1u3aoy7/s2EltAfDS3ZQIq
   519  7/cWTLJml/lleeB/Y6rPj8xqeCYhE5ahw9gsV/Mdqatl24V9Tks30iijx0Hhw+Gx
   520  kATUikMGr2GDVqoIRga5kXI7CzYff4rkc0Twn47fMHHHe/KY9M2yVnMHUXmAZwbG
   521  M1cMI/NH1DjevCKdGBLcRJlhuLPKF/anuQENBFyUD8sBCADIpd7r7GuPd6n/Ikxe
   522  u6h7umV6IIPoAm88xCYpTbSZiaK30Svh6Ywra9jfE2KlU9o6Y/art8ip0VJ3m07L
   523  4RSfSpnzqgSwdjSq5hNour2Fo/BzYhK7yaz2AzVSbe33R0+RYhb4b/6N+bKbjwGF
   524  ftCsqVFMH+PyvYkLbvxyQrHlA9woAZaNThI1ztO5rGSnGUR8xt84eup28WIFKg0K
   525  UEGUcTzz+8QGAwAra+0ewPXo/AkO+8BvZjDidP417u6gpBHOJ9qYIcO9FxHeqFyu
   526  YrjlrxowEgXn5wO8xuNz6Vu1vhHGDHGDsRbZF8pv1d5O+0F1G7ttZ2GRRgVBZPwi
   527  kiyRABEBAAGJATYEGAEIACAWIQSHYyc6Kj9l6HzQsau6vFFc9jxV/wUCXJQPywIb
   528  DAAKCRC6vFFc9jxV/9YOCACe8qmOSnKQpQfW+PqYOqo3dt7JyweTs3FkD6NT8Zml
   529  dYy/vkstbTjPpX6aTvUZjkb46BVi7AOneVHpD5GBqvRsZ9iVgDYHaehmLCdKiG5L
   530  3Tp90NN+QY5WDbsGmsyk6+6ZMYejb4qYfweQeduOj27aavCJdLkCYMoRKfcFYI8c
   531  FaNmEfKKy/r1PO20NXEG6t9t05K/frHy6ZG8bCNYdpagfFVot47r9JaQqWlTNtIR
   532  5+zkkSq/eG9BEtRij3a6cTdQbktdBzx2KBeI0PYc1vlZR0LpuFKZqY9vlE6vTGLR
   533  wMfrTEOvx0NxUM3rpaCgEmuWbB1G1Hu371oyr4srrr+N
   534  =28dr
   535  -----END PGP PUBLIC KEY BLOCK-----
   536  `)
   537  }
   538  
   539  // sshPrivateKey returns a private key suitable for testing.
   540  func sshPrivateKey() string {
   541  	return strings.TrimSpace(`-----BEGIN RSA PRIVATE KEY-----
   542  MIICXAIBAAKBgQDDo+/YbQ1cZVoRhZ/bbQtPxpycDS5Lty+M8e5swCKpmo0/Eym2
   543  KrVpEVMoU8eGtwVRvGDR2LtmFKvd86QUWkn2V3lYgY66SNj9n4R/YSDT4/GRkg+4
   544  Egi++ihpZA+SAIODF4+l1bh/FFu0XUpQLXvJ4Tm0++7bm3tEq+XQr9znrwIDAQAB
   545  AoGAfDa374e9te47s2hNyLmBNxN5F7Nes4AJVsm8gZuz5k9UYrm+AAU5zQ3M6IvY
   546  4PWPEQgzyMh8oyF4xaENikaRMhSMfinUmTd979cHbOM6cEKPk28oQcIybsdSzX7G
   547  ZWRh65Ze1DUmBe6R2BUh3Zn4lq9PsqB0TeZeV7Xo/VaIpFECQQDoznQi8HOY8MNM
   548  7ZDdRhFAkS2X5OGqXOjYdLABGNvJhajgoRsTbgDyJG83qn6yYq7wEHYlMddGZ3ln
   549  RLnpsThjAkEA1yGXae8WURFEqjp5dMLBxU07apKvEF4zK1OxZ0VjIOJdIpoRBBuL
   550  IthGBuMrfbF1W5tlmQlj5ik0KhVpBZoHRQJAZP7DdTDZBT1VjHb3RHcUHu2cWOvL
   551  VkvuG5ErlZ5CIv+gDqr1gw1SzbkuoniNdDfJao3Jo0Mm//z9tuYivRXLvwJBALG3
   552  Wzi0vI/Nnxas5YayGJaf3XSFpj70QnsJUWUJagFRXjTmZyYohsELPpYT9eqIvXUm
   553  o0BQBImvAhu9whtRia0CQCFdDHdNnyyzKH8vC0NsEN65h3Bp2KEPkv8SOV27ZRR2
   554  xIGqLusk3y+yzbueLZJ117osdB1Owr19fvAHR7vq6Mw=
   555  -----END RSA PRIVATE KEY-----`)
   556  }