Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Compute@Edge Free Trial Activation #531

Merged
merged 5 commits into from
Feb 9, 2022

Conversation

Integralist
Copy link
Collaborator

@Integralist Integralist commented Feb 7, 2022

There's two flows when it comes to the C@E free trial activation…

  1. Service needs to be created.
  2. Service already exists.

For the first scenario, if the service fails to be created, then we attempt to activate the trial and recall the function that creates the service.

For the second scenario I've added a validation step to check if the given service ID is associated with a Wasm service (e.g. I call the service details API endpoint and check the ‘type’ field is set to ‘wasm’). If that fails, then it means the user provided a VCL Service. The error we return indicates we need a Wasm service, not a VCL service and the user should consult fastly.help/cli/ecp-feature.

NOTE: This PR includes code changes not directly related to the free trial activation but helped to clean the code up.

@Integralist Integralist added the enhancement New feature or request label Feb 7, 2022
Copy link
Collaborator Author

@Integralist Integralist left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some comments to help the reviewer know where to look...

@@ -59,7 +59,7 @@ func main() {
var (
args = os.Args[1:]
clientFactory = app.FastlyAPIClient
httpClient = http.DefaultClient
httpClient = &http.Client{Timeout: time.Second * 5}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You shouldn't use the golang default http client as it doesn't provide preconfigured timeouts.

For some reason I had previously used a strange pattern of using it with a context object for handling timeouts. The implementation worked but it's a non-standard pattern to use so I've cleaned this up.

@@ -0,0 +1,57 @@
package undocumented
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a new package that provides an abstraction for making API calls for undocumented endpoints.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For whatever reason I'm not overly keen on the name undocumented, even though it's an accurate description of the functionality. So I'm open to suggestions.

I tried using internal prior to this, but the go compiler recognises that name, and will force certain structural constraints that didn't really work for this use case.

computeBuild := compute.NewBuildCommand(computeCmdRoot.CmdClause, opts.HTTPClient, globals, data)
computeDeploy := compute.NewDeployCommand(computeCmdRoot.CmdClause, opts.HTTPClient, globals, data)
computeInit := compute.NewInitCommand(computeCmdRoot.CmdClause, opts.HTTPClient, globals, data)
computeBuild := compute.NewBuildCommand(computeCmdRoot.CmdClause, globals, data)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bunch of commands were explicitly passing in a HTTP client and then not using it.

I additionally (see below in pkg/app/run.go) decided to embed the HTTP client into the globals object so it wouldn't be necessary to have to pass it explicitly into the remaining commands, as we already are passing in the global object.

@@ -148,7 +149,7 @@ func Run(opts RunOpts) error {
}
}

globals.Client, err = opts.APIClient(token, endpoint)
globals.APIClient, err = opts.APIClient(token, endpoint)
Copy link
Collaborator Author

@Integralist Integralist Feb 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of Client was becoming ambiguous due to us passing around both an API client and a standard HTTP client, so I made it clearer by renaming the field across the code base. Most of the file churn comes from this change.

@@ -80,6 +80,12 @@ func listDomainsOk(i *fastly.ListDomainsInput) ([]*fastly.Domain, error) {
}, nil
}

func getServiceDetailsWasm(i *fastly.GetServiceInput) (*fastly.ServiceDetail, error) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now we have an additional validation step that calls the 'Service Details' API endpoint, we need a mock response for our test coverage.


req, err := http.NewRequestWithContext(ctx, http.MethodGet, configEndpoint, nil)
func (f *File) Load(endpoint, path string, c api.HTTPClient) error {
req, err := http.NewRequest(http.MethodGet, endpoint, nil)
Copy link
Collaborator Author

@Integralist Integralist Feb 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We no longer require a complex request object (that accepts a context), as the client itself has a timeout configured.

@@ -11,3 +13,21 @@ func APIClient(a API) func(string, string) (api.Interface, error) {
return a, nil
}
}

type mockHTTPClient struct {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mock HTTP client for the sake of validating the undocumented 'activate trial' API.

CloneVersionFn: testutil.CloneVersionError,
ListVersionsFn: testutil.ListVersions,
CloneVersionFn: testutil.CloneVersionError,
GetServiceDetailsFn: getServiceDetailsWasm,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There were quite a few existing tests that needed to be fixed so they mocked GetServiceDetailsFn, which was a new API call I added for validating the user's service type.

@@ -225,6 +232,98 @@ func TestDeploy(t *testing.T) {
"Creating service...",
},
},
// The following test mocks the service creation to fail with a specific
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here are a bunch of new tests that validate different failure and success scenarios related to the trial activation.

@@ -624,6 +684,28 @@ func manageExistingServiceFlow(
return serviceVersion, err
}

// Validate that we're dealing with a Compute@Edge 'wasm' service and not a
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realised when dealing with a user who provides their own service, that we never actually validate the service is a compatible wasm service type. So I added that validation step here...

@Integralist Integralist requested review from a team and kailan and removed request for a team February 8, 2022 17:45
Copy link
Member

@kailan kailan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great! Thanks for the detailed comments, made it really easy to review.

@Integralist Integralist merged commit 9919f93 into main Feb 9, 2022
@Integralist Integralist deleted the integralist/activate-free-trial branch February 9, 2022 13:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants