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

     1  package s3_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 TestS3Create(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 s3 create --service-id 123 --version 1 --name log --bucket log --autoclone"),
    28  			api: mock.API{
    29  				ListVersionsFn: testutil.ListVersions,
    30  				CloneVersionFn: testutil.CloneVersionResult(4),
    31  			},
    32  			wantError: "error parsing arguments: the --access-key and --secret-key flags or the --iam-role flag must be provided",
    33  		},
    34  		{
    35  			args: args("logging s3 create --service-id 123 --version 1 --name log --bucket log --secret-key bar --iam-role arn:aws:iam::123456789012:role/S3Access --autoclone"),
    36  			api: mock.API{
    37  				ListVersionsFn: testutil.ListVersions,
    38  				CloneVersionFn: testutil.CloneVersionResult(4),
    39  			},
    40  			wantError: "error parsing arguments: the --access-key and --secret-key flags are mutually exclusive with the --iam-role flag",
    41  		},
    42  		{
    43  			args: args("logging s3 create --service-id 123 --version 1 --name log --bucket log --access-key foo --iam-role arn:aws:iam::123456789012:role/S3Access --autoclone"),
    44  			api: mock.API{
    45  				ListVersionsFn: testutil.ListVersions,
    46  				CloneVersionFn: testutil.CloneVersionResult(4),
    47  			},
    48  			wantError: "error parsing arguments: the --access-key and --secret-key flags are mutually exclusive with the --iam-role flag",
    49  		},
    50  		{
    51  			args: args("logging s3 create --service-id 123 --version 1 --name log --bucket log --access-key foo --secret-key bar --iam-role arn:aws:iam::123456789012:role/S3Access --autoclone"),
    52  			api: mock.API{
    53  				ListVersionsFn: testutil.ListVersions,
    54  				CloneVersionFn: testutil.CloneVersionResult(4),
    55  			},
    56  			wantError: "error parsing arguments: the --access-key and --secret-key flags are mutually exclusive with the --iam-role flag",
    57  		},
    58  		{
    59  			args: args("logging s3 create --service-id 123 --version 1 --name log --bucket log --access-key foo --secret-key bar --autoclone"),
    60  			api: mock.API{
    61  				ListVersionsFn: testutil.ListVersions,
    62  				CloneVersionFn: testutil.CloneVersionResult(4),
    63  				CreateS3Fn:     createS3OK,
    64  			},
    65  			wantOutput: "Created S3 logging endpoint log (service 123 version 4)",
    66  		},
    67  		{
    68  			args: args("logging s3 create --service-id 123 --version 1 --name log --bucket log --access-key foo --secret-key bar --autoclone"),
    69  			api: mock.API{
    70  				ListVersionsFn: testutil.ListVersions,
    71  				CloneVersionFn: testutil.CloneVersionResult(4),
    72  				CreateS3Fn:     createS3Error,
    73  			},
    74  			wantError: errTest.Error(),
    75  		},
    76  		{
    77  			args: args("logging s3 create --service-id 123 --version 1 --name log2 --bucket log --iam-role arn:aws:iam::123456789012:role/S3Access --autoclone"),
    78  			api: mock.API{
    79  				ListVersionsFn: testutil.ListVersions,
    80  				CloneVersionFn: testutil.CloneVersionResult(4),
    81  				CreateS3Fn:     createS3OK,
    82  			},
    83  			wantOutput: "Created S3 logging endpoint log2 (service 123 version 4)",
    84  		},
    85  		{
    86  			args: args("logging s3 create --service-id 123 --version 1 --name log2 --bucket log --iam-role arn:aws:iam::123456789012:role/S3Access --autoclone"),
    87  			api: mock.API{
    88  				ListVersionsFn: testutil.ListVersions,
    89  				CloneVersionFn: testutil.CloneVersionResult(4),
    90  				CreateS3Fn:     createS3Error,
    91  			},
    92  			wantError: errTest.Error(),
    93  		},
    94  		{
    95  			args: args("logging s3 create --service-id 123 --version 1 --name log --bucket log --iam-role arn:aws:iam::123456789012:role/S3Access --compression-codec zstd --gzip-level 9 --autoclone"),
    96  			api: mock.API{
    97  				ListVersionsFn: testutil.ListVersions,
    98  				CloneVersionFn: testutil.CloneVersionResult(4),
    99  			},
   100  			wantError: "error parsing arguments: the --compression-codec flag is mutually exclusive with the --gzip-level flag",
   101  		},
   102  	}
   103  	for testcaseIdx := range scenarios {
   104  		testcase := &scenarios[testcaseIdx]
   105  		t.Run(strings.Join(testcase.args, " "), func(t *testing.T) {
   106  			var stdout bytes.Buffer
   107  			app.Init = func(_ []string, _ io.Reader) (*global.Data, error) {
   108  				opts := testutil.MockGlobalData(testcase.args, &stdout)
   109  				opts.APIClientFactory = mock.APIClient(testcase.api)
   110  				return opts, nil
   111  			}
   112  			err := app.Run(testcase.args, nil)
   113  			testutil.AssertErrorContains(t, err, testcase.wantError)
   114  			testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput)
   115  		})
   116  	}
   117  }
   118  
   119  func TestS3List(t *testing.T) {
   120  	args := testutil.Args
   121  	scenarios := []struct {
   122  		args       []string
   123  		api        mock.API
   124  		wantError  string
   125  		wantOutput string
   126  	}{
   127  		{
   128  			args: args("logging s3 list --service-id 123 --version 1"),
   129  			api: mock.API{
   130  				ListVersionsFn: testutil.ListVersions,
   131  				ListS3sFn:      listS3sOK,
   132  			},
   133  			wantOutput: listS3sShortOutput,
   134  		},
   135  		{
   136  			args: args("logging s3 list --service-id 123 --version 1 --verbose"),
   137  			api: mock.API{
   138  				ListVersionsFn: testutil.ListVersions,
   139  				ListS3sFn:      listS3sOK,
   140  			},
   141  			wantOutput: listS3sVerboseOutput,
   142  		},
   143  		{
   144  			args: args("logging s3 list --service-id 123 --version 1 -v"),
   145  			api: mock.API{
   146  				ListVersionsFn: testutil.ListVersions,
   147  				ListS3sFn:      listS3sOK,
   148  			},
   149  			wantOutput: listS3sVerboseOutput,
   150  		},
   151  		{
   152  			args: args("logging s3 --verbose list --service-id 123 --version 1"),
   153  			api: mock.API{
   154  				ListVersionsFn: testutil.ListVersions,
   155  				ListS3sFn:      listS3sOK,
   156  			},
   157  			wantOutput: listS3sVerboseOutput,
   158  		},
   159  		{
   160  			args: args("logging -v s3 list --service-id 123 --version 1"),
   161  			api: mock.API{
   162  				ListVersionsFn: testutil.ListVersions,
   163  				ListS3sFn:      listS3sOK,
   164  			},
   165  			wantOutput: listS3sVerboseOutput,
   166  		},
   167  		{
   168  			args: args("logging s3 list --service-id 123 --version 1"),
   169  			api: mock.API{
   170  				ListVersionsFn: testutil.ListVersions,
   171  				ListS3sFn:      listS3sError,
   172  			},
   173  			wantError: errTest.Error(),
   174  		},
   175  	}
   176  	for testcaseIdx := range scenarios {
   177  		testcase := &scenarios[testcaseIdx]
   178  		t.Run(strings.Join(testcase.args, " "), func(t *testing.T) {
   179  			var stdout bytes.Buffer
   180  			app.Init = func(_ []string, _ io.Reader) (*global.Data, error) {
   181  				opts := testutil.MockGlobalData(testcase.args, &stdout)
   182  				opts.APIClientFactory = mock.APIClient(testcase.api)
   183  				return opts, nil
   184  			}
   185  			err := app.Run(testcase.args, nil)
   186  			testutil.AssertErrorContains(t, err, testcase.wantError)
   187  			testutil.AssertString(t, testcase.wantOutput, stdout.String())
   188  		})
   189  	}
   190  }
   191  
   192  func TestS3Describe(t *testing.T) {
   193  	args := testutil.Args
   194  	scenarios := []struct {
   195  		args       []string
   196  		api        mock.API
   197  		wantError  string
   198  		wantOutput string
   199  	}{
   200  		{
   201  			args:      args("logging s3 describe --service-id 123 --version 1"),
   202  			wantError: "error parsing arguments: required flag --name not provided",
   203  		},
   204  		{
   205  			args: args("logging s3 describe --service-id 123 --version 1 --name logs"),
   206  			api: mock.API{
   207  				ListVersionsFn: testutil.ListVersions,
   208  				GetS3Fn:        getS3Error,
   209  			},
   210  			wantError: errTest.Error(),
   211  		},
   212  		{
   213  			args: args("logging s3 describe --service-id 123 --version 1 --name logs"),
   214  			api: mock.API{
   215  				ListVersionsFn: testutil.ListVersions,
   216  				GetS3Fn:        getS3OK,
   217  			},
   218  			wantOutput: describeS3Output,
   219  		},
   220  	}
   221  	for testcaseIdx := range scenarios {
   222  		testcase := &scenarios[testcaseIdx]
   223  		t.Run(strings.Join(testcase.args, " "), func(t *testing.T) {
   224  			var stdout bytes.Buffer
   225  			app.Init = func(_ []string, _ io.Reader) (*global.Data, error) {
   226  				opts := testutil.MockGlobalData(testcase.args, &stdout)
   227  				opts.APIClientFactory = mock.APIClient(testcase.api)
   228  				return opts, nil
   229  			}
   230  			err := app.Run(testcase.args, nil)
   231  			testutil.AssertErrorContains(t, err, testcase.wantError)
   232  			testutil.AssertString(t, testcase.wantOutput, stdout.String())
   233  		})
   234  	}
   235  }
   236  
   237  func TestS3Update(t *testing.T) {
   238  	args := testutil.Args
   239  	scenarios := []struct {
   240  		args       []string
   241  		api        mock.API
   242  		wantError  string
   243  		wantOutput string
   244  	}{
   245  		{
   246  			args:      args("logging s3 update --service-id 123 --version 1 --new-name log"),
   247  			wantError: "error parsing arguments: required flag --name not provided",
   248  		},
   249  		{
   250  			args: args("logging s3 update --service-id 123 --version 1 --name logs --new-name log --autoclone"),
   251  			api: mock.API{
   252  				ListVersionsFn: testutil.ListVersions,
   253  				CloneVersionFn: testutil.CloneVersionResult(4),
   254  				UpdateS3Fn:     updateS3Error,
   255  			},
   256  			wantError: errTest.Error(),
   257  		},
   258  		{
   259  			args: args("logging s3 update --service-id 123 --version 1 --name logs --new-name log --autoclone"),
   260  			api: mock.API{
   261  				ListVersionsFn: testutil.ListVersions,
   262  				CloneVersionFn: testutil.CloneVersionResult(4),
   263  				UpdateS3Fn:     updateS3OK,
   264  			},
   265  			wantOutput: "Updated S3 logging endpoint log (service 123 version 4)",
   266  		},
   267  		{
   268  			args: args("logging s3 update --service-id 123 --version 1 --name logs --access-key foo --secret-key bar --iam-role  --autoclone"),
   269  			api: mock.API{
   270  				ListVersionsFn: testutil.ListVersions,
   271  				CloneVersionFn: testutil.CloneVersionResult(4),
   272  				UpdateS3Fn:     updateS3OK,
   273  			},
   274  			wantOutput: "Updated S3 logging endpoint log (service 123 version 4)",
   275  		},
   276  	}
   277  	for testcaseIdx := range scenarios {
   278  		testcase := &scenarios[testcaseIdx]
   279  		t.Run(strings.Join(testcase.args, " "), func(t *testing.T) {
   280  			var stdout bytes.Buffer
   281  			app.Init = func(_ []string, _ io.Reader) (*global.Data, error) {
   282  				opts := testutil.MockGlobalData(testcase.args, &stdout)
   283  				opts.APIClientFactory = mock.APIClient(testcase.api)
   284  				return opts, nil
   285  			}
   286  			err := app.Run(testcase.args, nil)
   287  			testutil.AssertErrorContains(t, err, testcase.wantError)
   288  			testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput)
   289  		})
   290  	}
   291  }
   292  
   293  func TestS3Delete(t *testing.T) {
   294  	args := testutil.Args
   295  	scenarios := []struct {
   296  		args       []string
   297  		api        mock.API
   298  		wantError  string
   299  		wantOutput string
   300  	}{
   301  		{
   302  			args:      args("logging s3 delete --service-id 123 --version 1"),
   303  			wantError: "error parsing arguments: required flag --name not provided",
   304  		},
   305  		{
   306  			args: args("logging s3 delete --service-id 123 --version 1 --name logs --autoclone"),
   307  			api: mock.API{
   308  				ListVersionsFn: testutil.ListVersions,
   309  				CloneVersionFn: testutil.CloneVersionResult(4),
   310  				DeleteS3Fn:     deleteS3Error,
   311  			},
   312  			wantError: errTest.Error(),
   313  		},
   314  		{
   315  			args: args("logging s3 delete --service-id 123 --version 1 --name logs --autoclone"),
   316  			api: mock.API{
   317  				ListVersionsFn: testutil.ListVersions,
   318  				CloneVersionFn: testutil.CloneVersionResult(4),
   319  				DeleteS3Fn:     deleteS3OK,
   320  			},
   321  			wantOutput: "Deleted S3 logging endpoint logs (service 123 version 4)",
   322  		},
   323  	}
   324  	for testcaseIdx := range scenarios {
   325  		testcase := &scenarios[testcaseIdx]
   326  		t.Run(strings.Join(testcase.args, " "), func(t *testing.T) {
   327  			var stdout bytes.Buffer
   328  			app.Init = func(_ []string, _ io.Reader) (*global.Data, error) {
   329  				opts := testutil.MockGlobalData(testcase.args, &stdout)
   330  				opts.APIClientFactory = mock.APIClient(testcase.api)
   331  				return opts, nil
   332  			}
   333  			err := app.Run(testcase.args, nil)
   334  			testutil.AssertErrorContains(t, err, testcase.wantError)
   335  			testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput)
   336  		})
   337  	}
   338  }
   339  
   340  var errTest = errors.New("fixture error")
   341  
   342  func createS3OK(i *fastly.CreateS3Input) (*fastly.S3, error) {
   343  	return &fastly.S3{
   344  		ServiceID:        fastly.ToPointer(i.ServiceID),
   345  		ServiceVersion:   fastly.ToPointer(i.ServiceVersion),
   346  		Name:             i.Name,
   347  		CompressionCodec: fastly.ToPointer("zstd"),
   348  	}, nil
   349  }
   350  
   351  func createS3Error(_ *fastly.CreateS3Input) (*fastly.S3, error) {
   352  	return nil, errTest
   353  }
   354  
   355  func listS3sOK(i *fastly.ListS3sInput) ([]*fastly.S3, error) {
   356  	return []*fastly.S3{
   357  		{
   358  			ServiceID:                    fastly.ToPointer(i.ServiceID),
   359  			ServiceVersion:               fastly.ToPointer(i.ServiceVersion),
   360  			Name:                         fastly.ToPointer("logs"),
   361  			BucketName:                   fastly.ToPointer("my-logs"),
   362  			AccessKey:                    fastly.ToPointer("1234"),
   363  			SecretKey:                    fastly.ToPointer("-----BEGIN RSA PRIVATE KEY-----MIIEogIBAAKCA"),
   364  			IAMRole:                      fastly.ToPointer("xyz"),
   365  			Domain:                       fastly.ToPointer("https://s3.us-east-1.amazonaws.com"),
   366  			Path:                         fastly.ToPointer("logs/"),
   367  			Period:                       fastly.ToPointer(3600),
   368  			Format:                       fastly.ToPointer(`%h %l %u %t "%r" %>s %b`),
   369  			FormatVersion:                fastly.ToPointer(2),
   370  			MessageType:                  fastly.ToPointer("classic"),
   371  			ResponseCondition:            fastly.ToPointer("Prevent default logging"),
   372  			TimestampFormat:              fastly.ToPointer("%Y-%m-%dT%H:%M:%S.000"),
   373  			Redundancy:                   fastly.ToPointer(fastly.S3RedundancyStandard),
   374  			Placement:                    fastly.ToPointer("none"),
   375  			PublicKey:                    fastly.ToPointer(pgpPublicKey()),
   376  			ServerSideEncryption:         fastly.ToPointer(fastly.S3ServerSideEncryptionKMS),
   377  			ServerSideEncryptionKMSKeyID: fastly.ToPointer("1234"),
   378  			CompressionCodec:             fastly.ToPointer("zstd"),
   379  		},
   380  		{
   381  			ServiceID:                    fastly.ToPointer(i.ServiceID),
   382  			ServiceVersion:               fastly.ToPointer(i.ServiceVersion),
   383  			Name:                         fastly.ToPointer("analytics"),
   384  			BucketName:                   fastly.ToPointer("analytics"),
   385  			AccessKey:                    fastly.ToPointer("1234"),
   386  			SecretKey:                    fastly.ToPointer("-----BEGIN RSA PRIVATE KEY-----MIIEogIBAAKCA"),
   387  			Domain:                       fastly.ToPointer("https://s3.us-east-2.amazonaws.com"),
   388  			Path:                         fastly.ToPointer("logs/"),
   389  			Period:                       fastly.ToPointer(86400),
   390  			Format:                       fastly.ToPointer(`%h %l %u %t "%r" %>s %b`),
   391  			FormatVersion:                fastly.ToPointer(2),
   392  			MessageType:                  fastly.ToPointer("classic"),
   393  			ResponseCondition:            fastly.ToPointer("Prevent default logging"),
   394  			TimestampFormat:              fastly.ToPointer("%Y-%m-%dT%H:%M:%S.000"),
   395  			Redundancy:                   fastly.ToPointer(fastly.S3RedundancyStandard),
   396  			Placement:                    fastly.ToPointer("none"),
   397  			PublicKey:                    fastly.ToPointer(pgpPublicKey()),
   398  			ServerSideEncryption:         fastly.ToPointer(fastly.S3ServerSideEncryptionKMS),
   399  			ServerSideEncryptionKMSKeyID: fastly.ToPointer("1234"),
   400  			FileMaxBytes:                 fastly.ToPointer(12345),
   401  			CompressionCodec:             fastly.ToPointer("zstd"),
   402  		},
   403  	}, nil
   404  }
   405  
   406  func listS3sError(_ *fastly.ListS3sInput) ([]*fastly.S3, error) {
   407  	return nil, errTest
   408  }
   409  
   410  var listS3sShortOutput = strings.TrimSpace(`
   411  SERVICE  VERSION  NAME
   412  123      1        logs
   413  123      1        analytics
   414  `) + "\n"
   415  
   416  var listS3sVerboseOutput = strings.TrimSpace(`
   417  Fastly API endpoint: https://api.fastly.com
   418  Fastly API token provided via config file (profile: user)
   419  
   420  Service ID (via --service-id): 123
   421  
   422  Version: 1
   423  	S3 1/2
   424  		Service ID: 123
   425  		Version: 1
   426  		Name: logs
   427  		Bucket: my-logs
   428  		Access key: 1234
   429  		Secret key: -----BEGIN RSA PRIVATE KEY-----MIIEogIBAAKCA
   430  		IAM role: xyz
   431  		Path: logs/
   432  		Period: 3600
   433  		GZip level: 0
   434  		Format: %h %l %u %t "%r" %>s %b
   435  		Format version: 2
   436  		Response condition: Prevent default logging
   437  		Message type: classic
   438  		Timestamp format: %Y-%m-%dT%H:%M:%S.000
   439  		Placement: none
   440  		Public key: `+pgpPublicKey()+`
   441  		Redundancy: standard
   442  		Server-side encryption: aws:kms
   443  		Server-side encryption KMS key ID: aws:kms
   444  		File max bytes: 0
   445  		Compression codec: zstd
   446  	S3 2/2
   447  		Service ID: 123
   448  		Version: 1
   449  		Name: analytics
   450  		Bucket: analytics
   451  		Access key: 1234
   452  		Secret key: -----BEGIN RSA PRIVATE KEY-----MIIEogIBAAKCA
   453  		Path: logs/
   454  		Period: 86400
   455  		GZip level: 0
   456  		Format: %h %l %u %t "%r" %>s %b
   457  		Format version: 2
   458  		Response condition: Prevent default logging
   459  		Message type: classic
   460  		Timestamp format: %Y-%m-%dT%H:%M:%S.000
   461  		Placement: none
   462  		Public key: `+pgpPublicKey()+`
   463  		Redundancy: standard
   464  		Server-side encryption: aws:kms
   465  		Server-side encryption KMS key ID: aws:kms
   466  		File max bytes: 12345
   467  		Compression codec: zstd
   468  `) + "\n\n"
   469  
   470  func getS3OK(i *fastly.GetS3Input) (*fastly.S3, error) {
   471  	return &fastly.S3{
   472  		ServiceID:                    fastly.ToPointer(i.ServiceID),
   473  		ServiceVersion:               fastly.ToPointer(i.ServiceVersion),
   474  		Name:                         fastly.ToPointer("logs"),
   475  		BucketName:                   fastly.ToPointer("my-logs"),
   476  		AccessKey:                    fastly.ToPointer("1234"),
   477  		SecretKey:                    fastly.ToPointer("-----BEGIN RSA PRIVATE KEY-----MIIEogIBAAKCA"),
   478  		Domain:                       fastly.ToPointer("https://s3.us-east-1.amazonaws.com"),
   479  		Path:                         fastly.ToPointer("logs/"),
   480  		Period:                       fastly.ToPointer(3600),
   481  		Format:                       fastly.ToPointer(`%h %l %u %t "%r" %>s %b`),
   482  		FormatVersion:                fastly.ToPointer(2),
   483  		MessageType:                  fastly.ToPointer("classic"),
   484  		ResponseCondition:            fastly.ToPointer("Prevent default logging"),
   485  		TimestampFormat:              fastly.ToPointer("%Y-%m-%dT%H:%M:%S.000"),
   486  		Redundancy:                   fastly.ToPointer(fastly.S3RedundancyStandard),
   487  		Placement:                    fastly.ToPointer("none"),
   488  		PublicKey:                    fastly.ToPointer(pgpPublicKey()),
   489  		ServerSideEncryption:         fastly.ToPointer(fastly.S3ServerSideEncryptionKMS),
   490  		ServerSideEncryptionKMSKeyID: fastly.ToPointer("1234"),
   491  		CompressionCodec:             fastly.ToPointer("zstd"),
   492  	}, nil
   493  }
   494  
   495  func getS3Error(_ *fastly.GetS3Input) (*fastly.S3, error) {
   496  	return nil, errTest
   497  }
   498  
   499  var describeS3Output = "\n" + strings.TrimSpace(`
   500  Access key: 1234
   501  Bucket: my-logs
   502  Compression codec: zstd
   503  File max bytes: 0
   504  Format: %h %l %u %t "%r" %>s %b
   505  Format version: 2
   506  GZip level: 0
   507  Message type: classic
   508  Name: logs
   509  Path: logs/
   510  Period: 3600
   511  Placement: none
   512  Public key: `+pgpPublicKey()+`
   513  Redundancy: standard
   514  Response condition: Prevent default logging
   515  Secret key: -----BEGIN RSA PRIVATE KEY-----MIIEogIBAAKCA
   516  Server-side encryption: aws:kms
   517  Server-side encryption KMS key ID: aws:kms
   518  Service ID: 123
   519  Timestamp format: %Y-%m-%dT%H:%M:%S.000
   520  Version: 1
   521  `) + "\n"
   522  
   523  func updateS3OK(i *fastly.UpdateS3Input) (*fastly.S3, error) {
   524  	return &fastly.S3{
   525  		ServiceID:                    fastly.ToPointer(i.ServiceID),
   526  		ServiceVersion:               fastly.ToPointer(i.ServiceVersion),
   527  		Name:                         fastly.ToPointer("log"),
   528  		BucketName:                   fastly.ToPointer("my-logs"),
   529  		AccessKey:                    fastly.ToPointer("1234"),
   530  		SecretKey:                    fastly.ToPointer("-----BEGIN RSA PRIVATE KEY-----MIIEogIBAAKCA"),
   531  		Domain:                       fastly.ToPointer("https://s3.us-east-1.amazonaws.com"),
   532  		Path:                         fastly.ToPointer("logs/"),
   533  		Period:                       fastly.ToPointer(3600),
   534  		Format:                       fastly.ToPointer(`%h %l %u %t "%r" %>s %b`),
   535  		FormatVersion:                fastly.ToPointer(2),
   536  		MessageType:                  fastly.ToPointer("classic"),
   537  		ResponseCondition:            fastly.ToPointer("Prevent default logging"),
   538  		TimestampFormat:              fastly.ToPointer("%Y-%m-%dT%H:%M:%S.000"),
   539  		Redundancy:                   fastly.ToPointer(fastly.S3RedundancyStandard),
   540  		Placement:                    fastly.ToPointer("none"),
   541  		PublicKey:                    fastly.ToPointer(pgpPublicKey()),
   542  		ServerSideEncryption:         fastly.ToPointer(fastly.S3ServerSideEncryptionKMS),
   543  		ServerSideEncryptionKMSKeyID: fastly.ToPointer("1234"),
   544  		CompressionCodec:             fastly.ToPointer("zstd"),
   545  	}, nil
   546  }
   547  
   548  func updateS3Error(_ *fastly.UpdateS3Input) (*fastly.S3, error) {
   549  	return nil, errTest
   550  }
   551  
   552  func deleteS3OK(_ *fastly.DeleteS3Input) error {
   553  	return nil
   554  }
   555  
   556  func deleteS3Error(_ *fastly.DeleteS3Input) error {
   557  	return errTest
   558  }
   559  
   560  // pgpPublicKey returns a PEM encoded PGP public key suitable for testing.
   561  func pgpPublicKey() string {
   562  	return strings.TrimSpace(`-----BEGIN PGP PUBLIC KEY BLOCK-----
   563  mQENBFyUD8sBCACyFnB39AuuTygseek+eA4fo0cgwva6/FSjnWq7riouQee8GgQ/
   564  ibXTRyv4iVlwI12GswvMTIy7zNvs1R54i0qvsLr+IZ4GVGJqs6ZJnvQcqe3xPoR4
   565  8AnBfw90o32r/LuHf6QCJXi+AEu35koNlNAvLJ2B+KACaNB7N0EeWmqpV/1V2k9p
   566  lDYk+th7LcCuaFNGqKS/PrMnnMqR6VDLCjHhNx4KR79b0Twm/2qp6an3hyNRu8Gn
   567  dwxpf1/BUu3JWf+LqkN4Y3mbOmSUL3MaJNvyQguUzTfS0P0uGuBDHrJCVkMZCzDB
   568  89ag55jCPHyGeHBTd02gHMWzsg3WMBWvCsrzABEBAAG0JXRlcnJhZm9ybSAodGVz
   569  dCkgPHRlc3RAdGVycmFmb3JtLmNvbT6JAU4EEwEIADgWIQSHYyc6Kj9l6HzQsau6
   570  vFFc9jxV/wUCXJQPywIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRC6vFFc
   571  9jxV/815CAClb32OxV7wG01yF97TzlyTl8TnvjMtoG29Mw4nSyg+mjM3b8N7iXm9
   572  OLX59fbDAWtBSldSZE22RXd3CvlFOG/EnKBXSjBtEqfyxYSnyOPkMPBYWGL/ApkX
   573  SvPYJ4LKdvipYToKFh3y9kk2gk1DcDBDyaaHvR+3rv1u3aoy7/s2EltAfDS3ZQIq
   574  7/cWTLJml/lleeB/Y6rPj8xqeCYhE5ahw9gsV/Mdqatl24V9Tks30iijx0Hhw+Gx
   575  kATUikMGr2GDVqoIRga5kXI7CzYff4rkc0Twn47fMHHHe/KY9M2yVnMHUXmAZwbG
   576  M1cMI/NH1DjevCKdGBLcRJlhuLPKF/anuQENBFyUD8sBCADIpd7r7GuPd6n/Ikxe
   577  u6h7umV6IIPoAm88xCYpTbSZiaK30Svh6Ywra9jfE2KlU9o6Y/art8ip0VJ3m07L
   578  4RSfSpnzqgSwdjSq5hNour2Fo/BzYhK7yaz2AzVSbe33R0+RYhb4b/6N+bKbjwGF
   579  ftCsqVFMH+PyvYkLbvxyQrHlA9woAZaNThI1ztO5rGSnGUR8xt84eup28WIFKg0K
   580  UEGUcTzz+8QGAwAra+0ewPXo/AkO+8BvZjDidP417u6gpBHOJ9qYIcO9FxHeqFyu
   581  YrjlrxowEgXn5wO8xuNz6Vu1vhHGDHGDsRbZF8pv1d5O+0F1G7ttZ2GRRgVBZPwi
   582  kiyRABEBAAGJATYEGAEIACAWIQSHYyc6Kj9l6HzQsau6vFFc9jxV/wUCXJQPywIb
   583  DAAKCRC6vFFc9jxV/9YOCACe8qmOSnKQpQfW+PqYOqo3dt7JyweTs3FkD6NT8Zml
   584  dYy/vkstbTjPpX6aTvUZjkb46BVi7AOneVHpD5GBqvRsZ9iVgDYHaehmLCdKiG5L
   585  3Tp90NN+QY5WDbsGmsyk6+6ZMYejb4qYfweQeduOj27aavCJdLkCYMoRKfcFYI8c
   586  FaNmEfKKy/r1PO20NXEG6t9t05K/frHy6ZG8bCNYdpagfFVot47r9JaQqWlTNtIR
   587  5+zkkSq/eG9BEtRij3a6cTdQbktdBzx2KBeI0PYc1vlZR0LpuFKZqY9vlE6vTGLR
   588  wMfrTEOvx0NxUM3rpaCgEmuWbB1G1Hu371oyr4srrr+N
   589  =28dr
   590  -----END PGP PUBLIC KEY BLOCK-----
   591  `)
   592  }