Skip to content

Commit

Permalink
validate package size doesn't exceed limit (#380)
Browse files Browse the repository at this point in the history
* add package size validation

* add test to validate package size error messaging

* fix test from timing out by resetting package variable
  • Loading branch information
Integralist committed Sep 2, 2021
1 parent 7a41f77 commit da59098
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 9 deletions.
30 changes: 30 additions & 0 deletions pkg/commands/compute/deploy.go
Expand Up @@ -29,6 +29,10 @@ const (
manageServiceBaseURL = "https://manage.fastly.com/configure/services/"
)

// PackageSizeLimit describes the package size limit in bytes (currently 50mb)
// https://docs.fastly.com/products/compute-at-edge-billing-and-resource-limits#resource-limits
var PackageSizeLimit int64 = 50000000

// DeployCommand deploys an artifact previously produced by build.
type DeployCommand struct {
cmd.Base
Expand Down Expand Up @@ -317,6 +321,9 @@ func (c *DeployCommand) Exec(in io.Reader, out io.Writer) (err error) {

// validatePackage short-circuits the deploy command if the user hasn't first
// built a package to be deployed.
//
// NOTE: It also validates if the package size exceeds limit:
// https://docs.fastly.com/products/compute-at-edge-billing-and-resource-limits#resource-limits
func validatePackage(data manifest.Data, pathFlag string, errLog errors.LogInterface) (pkgName, pkgPath string, err error) {
pkgName, source := data.Name()
pkgPath, err = packagePath(pathFlag, pkgName, source)
Expand All @@ -328,9 +335,23 @@ func validatePackage(data manifest.Data, pathFlag string, errLog errors.LogInter
})
return pkgName, pkgPath, err
}
pkgSize, err := packageSize(pkgPath)
if err != nil {
errLog.AddWithContext(err, map[string]interface{}{
"Package path": pkgPath,
})
return pkgName, pkgPath, err
}
if pkgSize > PackageSizeLimit {
return pkgName, pkgPath, errors.RemediationError{
Inner: fmt.Errorf("package size is too large (%d bytes)", pkgSize),
Remediation: errors.PackageSizeRemediation,
}
}
if err := validate(pkgPath); err != nil {
errLog.AddWithContext(err, map[string]interface{}{
"Package path": pkgPath,
"Package size": pkgSize,
})
return pkgName, pkgPath, err
}
Expand All @@ -355,6 +376,15 @@ func packagePath(path string, name string, source manifest.Source) (string, erro
return path, nil
}

// packageSize returns the size of the .tar.gz package.
func packageSize(path string) (size int64, err error) {
fi, err := os.Stat(path)
if err != nil {
return size, err
}
return fi.Size(), nil
}

// manageNoServiceIDFlow handles creating a new service when no Service ID is found.
func manageNoServiceIDFlow(
acceptDefaults bool,
Expand Down
39 changes: 30 additions & 9 deletions pkg/commands/compute/deploy_test.go
Expand Up @@ -11,7 +11,9 @@ import (
"time"

"github.com/fastly/cli/pkg/app"
"github.com/fastly/cli/pkg/commands/compute"
"github.com/fastly/cli/pkg/commands/compute/manifest"
"github.com/fastly/cli/pkg/errors"
"github.com/fastly/cli/pkg/mock"
"github.com/fastly/cli/pkg/testutil"
"github.com/fastly/go-fastly/v3/fastly"
Expand Down Expand Up @@ -72,17 +74,20 @@ func TestDeploy(t *testing.T) {
}
defer os.Chdir(pwd)

originalPackageSizeLimit := compute.PackageSizeLimit
args := testutil.Args
for _, testcase := range []struct {
api mock.API
args []string
dontWantOutput []string
manifest string
name string
noManifest bool
stdin []string
wantError string
wantOutput []string
api mock.API
args []string
dontWantOutput []string
manifest string
name string
noManifest bool
reduceSizeLimit bool
stdin []string
wantError string
wantRemediationError string
wantOutput []string
}{
{
name: "no token",
Expand Down Expand Up @@ -182,6 +187,13 @@ func TestDeploy(t *testing.T) {
},
wantError: fmt.Sprintf("error fetching service backends: %s", testutil.Err.Error()),
},
{
name: "package size too large",
args: args("compute deploy -p pkg/package.tar.gz --token 123"),
reduceSizeLimit: true,
wantError: "package size is too large",
wantRemediationError: errors.PackageSizeRemediation,
},
// The following test doesn't just validate the package API error behaviour
// but as a side effect it validates that when deleting the created
// service, the Service ID is also cleared out from the manifest.
Expand Down Expand Up @@ -900,6 +912,14 @@ func TestDeploy(t *testing.T) {
opts := testutil.NewRunOpts(testcase.args, &stdout)
opts.APIClient = mock.APIClient(testcase.api)

if testcase.reduceSizeLimit {
compute.PackageSizeLimit = 1000000 // 1mb (our test package should above this)
} else {
// As multiple test scenarios run within a single environment instance
// we need to ensure each scenario resets the package variable.
compute.PackageSizeLimit = originalPackageSizeLimit
}

if len(testcase.stdin) > 1 {
// To handle multiple prompt input from the user we need to do some
// coordination around io pipes to mimic the required user behaviour.
Expand Down Expand Up @@ -950,6 +970,7 @@ func TestDeploy(t *testing.T) {
t.Log(stdout.String())

testutil.AssertErrorContains(t, err, testcase.wantError)
testutil.AssertRemediationErrorContains(t, err, testcase.wantRemediationError)

for _, s := range testcase.wantOutput {
testutil.AssertStringContains(t, stdout.String(), s)
Expand Down
7 changes: 7 additions & 0 deletions pkg/errors/remediation_error.go
Expand Up @@ -96,3 +96,10 @@ var AutoCloneRemediation = strings.Join([]string{
var IDRemediation = strings.Join([]string{
"Please provide one via the --id flag",
}, " ")

// PackageSizeRemediation suggests checking the resources documentation for the
// current package size limit.
var PackageSizeRemediation = strings.Join([]string{
"Please check our Compute@Edge resource limits:",
"https://docs.fastly.com/products/compute-at-edge-billing-and-resource-limits#resource-limits",
}, " ")

0 comments on commit da59098

Please sign in to comment.