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

     1  package ftp_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 TestFTPCreate(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 ftp create --service-id 123 --version 1 --name log --address example.com --user anonymous --password foo@example.com --compression-codec zstd --autoclone"),
    28  			api: mock.API{
    29  				ListVersionsFn: testutil.ListVersions,
    30  				CloneVersionFn: testutil.CloneVersionResult(4),
    31  				CreateFTPFn:    createFTPOK,
    32  			},
    33  			wantOutput: "Created FTP logging endpoint log (service 123 version 4)",
    34  		},
    35  		{
    36  			args: args("logging ftp create --service-id 123 --version 1 --name log --address example.com --user anonymous --password foo@example.com --autoclone"),
    37  			api: mock.API{
    38  				ListVersionsFn: testutil.ListVersions,
    39  				CloneVersionFn: testutil.CloneVersionResult(4),
    40  				CreateFTPFn:    createFTPError,
    41  			},
    42  			wantError: errTest.Error(),
    43  		},
    44  		{
    45  			args: args("logging ftp create --service-id 123 --version 1 --name log --address example.com --user anonymous --password foo@example.com --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 TestFTPList(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 ftp list --service-id 123 --version 1"),
    79  			api: mock.API{
    80  				ListVersionsFn: testutil.ListVersions,
    81  				ListFTPsFn:     listFTPsOK,
    82  			},
    83  			wantOutput: listFTPsShortOutput,
    84  		},
    85  		{
    86  			args: args("logging ftp list --service-id 123 --version 1 --verbose"),
    87  			api: mock.API{
    88  				ListVersionsFn: testutil.ListVersions,
    89  				ListFTPsFn:     listFTPsOK,
    90  			},
    91  			wantOutput: listFTPsVerboseOutput,
    92  		},
    93  		{
    94  			args: args("logging ftp list --service-id 123 --version 1 -v"),
    95  			api: mock.API{
    96  				ListVersionsFn: testutil.ListVersions,
    97  				ListFTPsFn:     listFTPsOK,
    98  			},
    99  			wantOutput: listFTPsVerboseOutput,
   100  		},
   101  		{
   102  			args: args("logging ftp --verbose list --service-id 123 --version 1"),
   103  			api: mock.API{
   104  				ListVersionsFn: testutil.ListVersions,
   105  				ListFTPsFn:     listFTPsOK,
   106  			},
   107  			wantOutput: listFTPsVerboseOutput,
   108  		},
   109  		{
   110  			args: args("logging -v ftp list --service-id 123 --version 1"),
   111  			api: mock.API{
   112  				ListVersionsFn: testutil.ListVersions,
   113  				ListFTPsFn:     listFTPsOK,
   114  			},
   115  			wantOutput: listFTPsVerboseOutput,
   116  		},
   117  		{
   118  			args: args("logging ftp list --service-id 123 --version 1"),
   119  			api: mock.API{
   120  				ListVersionsFn: testutil.ListVersions,
   121  				ListFTPsFn:     listFTPsError,
   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 TestFTPDescribe(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 ftp describe --service-id 123 --version 1"),
   152  			wantError: "error parsing arguments: required flag --name not provided",
   153  		},
   154  		{
   155  			args: args("logging ftp describe --service-id 123 --version 1 --name logs"),
   156  			api: mock.API{
   157  				ListVersionsFn: testutil.ListVersions,
   158  				GetFTPFn:       getFTPError,
   159  			},
   160  			wantError: errTest.Error(),
   161  		},
   162  		{
   163  			args: args("logging ftp describe --service-id 123 --version 1 --name logs"),
   164  			api: mock.API{
   165  				ListVersionsFn: testutil.ListVersions,
   166  				GetFTPFn:       getFTPOK,
   167  			},
   168  			wantOutput: describeFTPOutput,
   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 TestFTPUpdate(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 ftp 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 ftp 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  				UpdateFTPFn:    updateFTPError,
   205  			},
   206  			wantError: errTest.Error(),
   207  		},
   208  		{
   209  			args: args("logging ftp 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  				UpdateFTPFn:    updateFTPOK,
   214  			},
   215  			wantOutput: "Updated FTP 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 TestFTPDelete(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 ftp delete --service-id 123 --version 1"),
   244  			wantError: "error parsing arguments: required flag --name not provided",
   245  		},
   246  		{
   247  			args: args("logging ftp delete --service-id 123 --version 1 --name logs --autoclone"),
   248  			api: mock.API{
   249  				ListVersionsFn: testutil.ListVersions,
   250  				CloneVersionFn: testutil.CloneVersionResult(4),
   251  				DeleteFTPFn:    deleteFTPError,
   252  			},
   253  			wantError: errTest.Error(),
   254  		},
   255  		{
   256  			args: args("logging ftp delete --service-id 123 --version 1 --name logs --autoclone"),
   257  			api: mock.API{
   258  				ListVersionsFn: testutil.ListVersions,
   259  				CloneVersionFn: testutil.CloneVersionResult(4),
   260  				DeleteFTPFn:    deleteFTPOK,
   261  			},
   262  			wantOutput: "Deleted FTP 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 createFTPOK(i *fastly.CreateFTPInput) (*fastly.FTP, error) {
   284  	return &fastly.FTP{
   285  		ServiceID:        fastly.ToPointer(i.ServiceID),
   286  		ServiceVersion:   fastly.ToPointer(i.ServiceVersion),
   287  		Name:             i.Name,
   288  		CompressionCodec: i.CompressionCodec,
   289  	}, nil
   290  }
   291  
   292  func createFTPError(_ *fastly.CreateFTPInput) (*fastly.FTP, error) {
   293  	return nil, errTest
   294  }
   295  
   296  func listFTPsOK(i *fastly.ListFTPsInput) ([]*fastly.FTP, error) {
   297  	return []*fastly.FTP{
   298  		{
   299  			ServiceID:         fastly.ToPointer(i.ServiceID),
   300  			ServiceVersion:    fastly.ToPointer(i.ServiceVersion),
   301  			Name:              fastly.ToPointer("logs"),
   302  			Address:           fastly.ToPointer("example.com"),
   303  			Port:              fastly.ToPointer(123),
   304  			Username:          fastly.ToPointer("anonymous"),
   305  			Password:          fastly.ToPointer("foo@example.com"),
   306  			PublicKey:         fastly.ToPointer(pgpPublicKey()),
   307  			Path:              fastly.ToPointer("logs/"),
   308  			Period:            fastly.ToPointer(3600),
   309  			GzipLevel:         fastly.ToPointer(9),
   310  			Format:            fastly.ToPointer(`%h %l %u %t "%r" %>s %b`),
   311  			FormatVersion:     fastly.ToPointer(2),
   312  			ResponseCondition: fastly.ToPointer("Prevent default logging"),
   313  			TimestampFormat:   fastly.ToPointer("%Y-%m-%dT%H:%M:%S.000"),
   314  			Placement:         fastly.ToPointer("none"),
   315  			CompressionCodec:  fastly.ToPointer("zstd"),
   316  		},
   317  		{
   318  			ServiceID:         fastly.ToPointer(i.ServiceID),
   319  			ServiceVersion:    fastly.ToPointer(i.ServiceVersion),
   320  			Name:              fastly.ToPointer("analytics"),
   321  			Address:           fastly.ToPointer("127.0.0.1"),
   322  			Port:              fastly.ToPointer(456),
   323  			Username:          fastly.ToPointer("foo"),
   324  			Password:          fastly.ToPointer("password"),
   325  			PublicKey:         fastly.ToPointer(pgpPublicKey()),
   326  			Path:              fastly.ToPointer("logs/"),
   327  			Period:            fastly.ToPointer(86400),
   328  			GzipLevel:         fastly.ToPointer(9),
   329  			Format:            fastly.ToPointer(`%h %l %u %t "%r" %>s %b`),
   330  			FormatVersion:     fastly.ToPointer(2),
   331  			ResponseCondition: fastly.ToPointer("Prevent default logging"),
   332  			TimestampFormat:   fastly.ToPointer("%Y-%m-%dT%H:%M:%S.000"),
   333  			Placement:         fastly.ToPointer("none"),
   334  			CompressionCodec:  fastly.ToPointer("zstd"),
   335  		},
   336  	}, nil
   337  }
   338  
   339  func listFTPsError(_ *fastly.ListFTPsInput) ([]*fastly.FTP, error) {
   340  	return nil, errTest
   341  }
   342  
   343  var listFTPsShortOutput = strings.TrimSpace(`
   344  SERVICE  VERSION  NAME
   345  123      1        logs
   346  123      1        analytics
   347  `) + "\n"
   348  
   349  var listFTPsVerboseOutput = strings.TrimSpace(`
   350  Fastly API endpoint: https://api.fastly.com
   351  Fastly API token provided via config file (profile: user)
   352  
   353  Service ID (via --service-id): 123
   354  
   355  Version: 1
   356  	FTP 1/2
   357  		Service ID: 123
   358  		Version: 1
   359  		Name: logs
   360  		Address: example.com
   361  		Port: 123
   362  		Username: anonymous
   363  		Password: foo@example.com
   364  		Public key: `+pgpPublicKey()+`
   365  		Path: logs/
   366  		Period: 3600
   367  		GZip level: 9
   368  		Format: %h %l %u %t "%r" %>s %b
   369  		Format version: 2
   370  		Response condition: Prevent default logging
   371  		Timestamp format: %Y-%m-%dT%H:%M:%S.000
   372  		Placement: none
   373  		Compression codec: zstd
   374  	FTP 2/2
   375  		Service ID: 123
   376  		Version: 1
   377  		Name: analytics
   378  		Address: 127.0.0.1
   379  		Port: 456
   380  		Username: foo
   381  		Password: password
   382  		Public key: `+pgpPublicKey()+`
   383  		Path: logs/
   384  		Period: 86400
   385  		GZip level: 9
   386  		Format: %h %l %u %t "%r" %>s %b
   387  		Format version: 2
   388  		Response condition: Prevent default logging
   389  		Timestamp format: %Y-%m-%dT%H:%M:%S.000
   390  		Placement: none
   391  		Compression codec: zstd
   392  `) + "\n\n"
   393  
   394  func getFTPOK(i *fastly.GetFTPInput) (*fastly.FTP, error) {
   395  	return &fastly.FTP{
   396  		ServiceID:         fastly.ToPointer(i.ServiceID),
   397  		ServiceVersion:    fastly.ToPointer(i.ServiceVersion),
   398  		Name:              fastly.ToPointer("logs"),
   399  		Address:           fastly.ToPointer("example.com"),
   400  		Port:              fastly.ToPointer(123),
   401  		Username:          fastly.ToPointer("anonymous"),
   402  		Password:          fastly.ToPointer("foo@example.com"),
   403  		PublicKey:         fastly.ToPointer(pgpPublicKey()),
   404  		Path:              fastly.ToPointer("logs/"),
   405  		Period:            fastly.ToPointer(3600),
   406  		GzipLevel:         fastly.ToPointer(9),
   407  		Format:            fastly.ToPointer(`%h %l %u %t "%r" %>s %b`),
   408  		FormatVersion:     fastly.ToPointer(2),
   409  		ResponseCondition: fastly.ToPointer("Prevent default logging"),
   410  		TimestampFormat:   fastly.ToPointer("%Y-%m-%dT%H:%M:%S.000"),
   411  		Placement:         fastly.ToPointer("none"),
   412  		CompressionCodec:  fastly.ToPointer("zstd"),
   413  	}, nil
   414  }
   415  
   416  func getFTPError(_ *fastly.GetFTPInput) (*fastly.FTP, error) {
   417  	return nil, errTest
   418  }
   419  
   420  var describeFTPOutput = "\n" + strings.TrimSpace(`
   421  Address: example.com
   422  Compression codec: zstd
   423  Format: %h %l %u %t "%r" %>s %b
   424  Format version: 2
   425  GZip level: 9
   426  Name: logs
   427  Password: foo@example.com
   428  Path: logs/
   429  Period: 3600
   430  Placement: none
   431  Port: 123
   432  Public key: `+pgpPublicKey()+`
   433  Response condition: Prevent default logging
   434  Service ID: 123
   435  Timestamp format: %Y-%m-%dT%H:%M:%S.000
   436  Username: anonymous
   437  Version: 1
   438  `) + "\n"
   439  
   440  func updateFTPOK(i *fastly.UpdateFTPInput) (*fastly.FTP, error) {
   441  	return &fastly.FTP{
   442  		ServiceID:         fastly.ToPointer(i.ServiceID),
   443  		ServiceVersion:    fastly.ToPointer(i.ServiceVersion),
   444  		Name:              fastly.ToPointer("log"),
   445  		Address:           fastly.ToPointer("example.com"),
   446  		Port:              fastly.ToPointer(123),
   447  		Username:          fastly.ToPointer("anonymous"),
   448  		Password:          fastly.ToPointer("foo@example.com"),
   449  		PublicKey:         fastly.ToPointer(pgpPublicKey()),
   450  		Path:              fastly.ToPointer("logs/"),
   451  		Period:            fastly.ToPointer(3600),
   452  		GzipLevel:         fastly.ToPointer(9),
   453  		Format:            fastly.ToPointer(`%h %l %u %t "%r" %>s %b`),
   454  		FormatVersion:     fastly.ToPointer(2),
   455  		ResponseCondition: fastly.ToPointer("Prevent default logging"),
   456  		TimestampFormat:   fastly.ToPointer("%Y-%m-%dT%H:%M:%S.000"),
   457  		Placement:         fastly.ToPointer("none"),
   458  		CompressionCodec:  fastly.ToPointer("zstd"),
   459  	}, nil
   460  }
   461  
   462  func updateFTPError(_ *fastly.UpdateFTPInput) (*fastly.FTP, error) {
   463  	return nil, errTest
   464  }
   465  
   466  func deleteFTPOK(_ *fastly.DeleteFTPInput) error {
   467  	return nil
   468  }
   469  
   470  func deleteFTPError(_ *fastly.DeleteFTPInput) error {
   471  	return errTest
   472  }
   473  
   474  // pgpPublicKey returns a PEM encoded PGP public key suitable for testing.
   475  func pgpPublicKey() string {
   476  	return strings.TrimSpace(`-----BEGIN PGP PUBLIC KEY BLOCK-----
   477  mQENBFyUD8sBCACyFnB39AuuTygseek+eA4fo0cgwva6/FSjnWq7riouQee8GgQ/
   478  ibXTRyv4iVlwI12GswvMTIy7zNvs1R54i0qvsLr+IZ4GVGJqs6ZJnvQcqe3xPoR4
   479  8AnBfw90o32r/LuHf6QCJXi+AEu35koNlNAvLJ2B+KACaNB7N0EeWmqpV/1V2k9p
   480  lDYk+th7LcCuaFNGqKS/PrMnnMqR6VDLCjHhNx4KR79b0Twm/2qp6an3hyNRu8Gn
   481  dwxpf1/BUu3JWf+LqkN4Y3mbOmSUL3MaJNvyQguUzTfS0P0uGuBDHrJCVkMZCzDB
   482  89ag55jCPHyGeHBTd02gHMWzsg3WMBWvCsrzABEBAAG0JXRlcnJhZm9ybSAodGVz
   483  dCkgPHRlc3RAdGVycmFmb3JtLmNvbT6JAU4EEwEIADgWIQSHYyc6Kj9l6HzQsau6
   484  vFFc9jxV/wUCXJQPywIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRC6vFFc
   485  9jxV/815CAClb32OxV7wG01yF97TzlyTl8TnvjMtoG29Mw4nSyg+mjM3b8N7iXm9
   486  OLX59fbDAWtBSldSZE22RXd3CvlFOG/EnKBXSjBtEqfyxYSnyOPkMPBYWGL/ApkX
   487  SvPYJ4LKdvipYToKFh3y9kk2gk1DcDBDyaaHvR+3rv1u3aoy7/s2EltAfDS3ZQIq
   488  7/cWTLJml/lleeB/Y6rPj8xqeCYhE5ahw9gsV/Mdqatl24V9Tks30iijx0Hhw+Gx
   489  kATUikMGr2GDVqoIRga5kXI7CzYff4rkc0Twn47fMHHHe/KY9M2yVnMHUXmAZwbG
   490  M1cMI/NH1DjevCKdGBLcRJlhuLPKF/anuQENBFyUD8sBCADIpd7r7GuPd6n/Ikxe
   491  u6h7umV6IIPoAm88xCYpTbSZiaK30Svh6Ywra9jfE2KlU9o6Y/art8ip0VJ3m07L
   492  4RSfSpnzqgSwdjSq5hNour2Fo/BzYhK7yaz2AzVSbe33R0+RYhb4b/6N+bKbjwGF
   493  ftCsqVFMH+PyvYkLbvxyQrHlA9woAZaNThI1ztO5rGSnGUR8xt84eup28WIFKg0K
   494  UEGUcTzz+8QGAwAra+0ewPXo/AkO+8BvZjDidP417u6gpBHOJ9qYIcO9FxHeqFyu
   495  YrjlrxowEgXn5wO8xuNz6Vu1vhHGDHGDsRbZF8pv1d5O+0F1G7ttZ2GRRgVBZPwi
   496  kiyRABEBAAGJATYEGAEIACAWIQSHYyc6Kj9l6HzQsau6vFFc9jxV/wUCXJQPywIb
   497  DAAKCRC6vFFc9jxV/9YOCACe8qmOSnKQpQfW+PqYOqo3dt7JyweTs3FkD6NT8Zml
   498  dYy/vkstbTjPpX6aTvUZjkb46BVi7AOneVHpD5GBqvRsZ9iVgDYHaehmLCdKiG5L
   499  3Tp90NN+QY5WDbsGmsyk6+6ZMYejb4qYfweQeduOj27aavCJdLkCYMoRKfcFYI8c
   500  FaNmEfKKy/r1PO20NXEG6t9t05K/frHy6ZG8bCNYdpagfFVot47r9JaQqWlTNtIR
   501  5+zkkSq/eG9BEtRij3a6cTdQbktdBzx2KBeI0PYc1vlZR0LpuFKZqY9vlE6vTGLR
   502  wMfrTEOvx0NxUM3rpaCgEmuWbB1G1Hu371oyr4srrr+N
   503  =28dr
   504  -----END PGP PUBLIC KEY BLOCK-----
   505  `)
   506  }