forked from elastic/elastic-agent
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Improve test runner to re-use instances and make provisioners pluggab…
…le (elastic#3136) Refactors the way integration test runner works to provide the following benefits: * Instance re-use - Call to any `mage integration:*` target that requires the usage of a deployed instance will re-use an already deployed instance. A specific call to `mage integration:clean` is required to bring down created instances and stacks. * Copy builds only if required - With instance re-use support the code has been changed to only copy the Elastic Agent builds to an instance in the case that the build has changed. * Add `InstanceProvisioner` interface - This interface provides a defined interface between the runner and the instance provisioner. This will allow other instance provisioners to be added to the test runner. * Add `StackProvisioner` interface - This interface provides a defined interface between the runner and the stack provisioner. This will allow other stack provisioners to be add to the test runner.
- Loading branch information
1 parent
b60b8b0
commit 110a7fe
Showing
21 changed files
with
1,361 additions
and
749 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
# Directories | ||
/.agent-testing | ||
/.integration-cache | ||
/.ogc-cache | ||
/.vagrant | ||
/.idea | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4687,35 +4687,6 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
|
||
|
||
-------------------------------------------------------------------------------- | ||
Dependency : github.com/rs/xid | ||
Version: v1.3.0 | ||
Licence type (autodetected): MIT | ||
-------------------------------------------------------------------------------- | ||
|
||
Contents of probable licence file $GOMODCACHE/github.com/rs/[email protected]/LICENSE: | ||
|
||
Copyright (c) 2015 Olivier Poitrey <[email protected]> | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is furnished | ||
to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
THE SOFTWARE. | ||
|
||
|
||
-------------------------------------------------------------------------------- | ||
Dependency : github.com/rs/zerolog | ||
Version: v1.27.0 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
// or more contributor license agreements. Licensed under the Elastic License; | ||
// you may not use this file except in compliance with the Elastic License. | ||
|
||
package ess | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"strings" | ||
"time" | ||
|
||
"golang.org/x/sync/errgroup" | ||
|
||
"github.com/elastic/elastic-agent/pkg/testing/runner" | ||
) | ||
|
||
// ProvisionerConfig is the configuration for the ESS provisioner. | ||
type ProvisionerConfig struct { | ||
Identifier string | ||
APIKey string | ||
Region string | ||
} | ||
|
||
// Validate returns an error if the information is invalid. | ||
func (c *ProvisionerConfig) Validate() error { | ||
if c.Identifier == "" { | ||
return errors.New("field Identifier must be set") | ||
} | ||
if c.APIKey == "" { | ||
return errors.New("field APIKey must be set") | ||
} | ||
if c.Region == "" { | ||
return errors.New("field Region must be set") | ||
} | ||
return nil | ||
} | ||
|
||
type provisioner struct { | ||
logger runner.Logger | ||
cfg ProvisionerConfig | ||
client *Client | ||
} | ||
|
||
// NewProvisioner creates the ESS provisioner | ||
func NewProvisioner(cfg ProvisionerConfig) (runner.StackProvisioner, error) { | ||
err := cfg.Validate() | ||
if err != nil { | ||
return nil, err | ||
} | ||
essClient := NewClient(Config{ | ||
ApiKey: cfg.APIKey, | ||
}) | ||
return &provisioner{ | ||
cfg: cfg, | ||
client: essClient, | ||
}, nil | ||
} | ||
|
||
func (p *provisioner) SetLogger(l runner.Logger) { | ||
p.logger = l | ||
} | ||
|
||
func (p *provisioner) Provision(ctx context.Context, requests []runner.StackRequest) ([]runner.Stack, error) { | ||
results := make(map[runner.StackRequest]*CreateDeploymentResponse) | ||
for _, r := range requests { | ||
// allow up to 2 minutes for each create request | ||
createCtx, createCancel := context.WithTimeout(ctx, 2*time.Minute) | ||
resp, err := p.createDeployment(createCtx, r) | ||
createCancel() | ||
if err != nil { | ||
return nil, err | ||
} | ||
results[r] = resp | ||
} | ||
|
||
// wait 15 minutes for all stacks to be ready | ||
readyCtx, readyCancel := context.WithTimeout(ctx, 15*time.Minute) | ||
defer readyCancel() | ||
|
||
g, gCtx := errgroup.WithContext(readyCtx) | ||
for req, resp := range results { | ||
g.Go(func(req runner.StackRequest, resp *CreateDeploymentResponse) func() error { | ||
return func() error { | ||
ready, err := p.client.DeploymentIsReady(gCtx, resp.ID, 30*time.Second) | ||
if err != nil { | ||
return fmt.Errorf("failed to check for cloud %s to be ready: %w", req.Version, err) | ||
} | ||
if !ready { | ||
return fmt.Errorf("cloud %s never became ready: %w", req.Version, err) | ||
} | ||
return nil | ||
} | ||
}(req, resp)) | ||
} | ||
err := g.Wait() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var stacks []runner.Stack | ||
for req, resp := range results { | ||
stacks = append(stacks, runner.Stack{ | ||
ID: req.ID, | ||
Version: req.Version, | ||
Elasticsearch: resp.ElasticsearchEndpoint, | ||
Kibana: resp.KibanaEndpoint, | ||
Username: resp.Username, | ||
Password: resp.Password, | ||
Internal: map[string]interface{}{ | ||
"deployment_id": resp.ID, | ||
}, | ||
}) | ||
} | ||
return stacks, nil | ||
} | ||
|
||
// Clean cleans up all provisioned resources. | ||
func (p *provisioner) Clean(ctx context.Context, stacks []runner.Stack) error { | ||
var errs []error | ||
for _, s := range stacks { | ||
err := p.destroyDeployment(ctx, s) | ||
if err != nil { | ||
errs = append(errs, fmt.Errorf("failed to destroy stack %s (%s): %w", s.Version, s.ID, err)) | ||
} | ||
} | ||
if len(errs) > 0 { | ||
return errors.Join(errs...) | ||
} | ||
return nil | ||
} | ||
|
||
func (p *provisioner) createDeployment(ctx context.Context, r runner.StackRequest) (*CreateDeploymentResponse, error) { | ||
ctx, cancel := context.WithTimeout(ctx, 1*time.Minute) | ||
defer cancel() | ||
|
||
p.logger.Logf("Creating stack %s (%s)", r.Version, r.ID) | ||
name := fmt.Sprintf("%s-%s", strings.Replace(p.cfg.Identifier, ".", "-", -1), r.ID) | ||
resp, err := p.client.CreateDeployment(ctx, CreateDeploymentRequest{ | ||
Name: name, | ||
Region: p.cfg.Region, | ||
Version: r.Version, | ||
}) | ||
if err != nil { | ||
p.logger.Logf("Failed to create ESS cloud %s: %s", r.Version, err) | ||
return nil, fmt.Errorf("failed to create ESS cloud for version %s: %w", r.Version, err) | ||
} | ||
return resp, nil | ||
} | ||
|
||
func (p *provisioner) destroyDeployment(ctx context.Context, s runner.Stack) error { | ||
if s.Internal == nil { | ||
return fmt.Errorf("missing internal information") | ||
} | ||
deploymentIDRaw, ok := s.Internal["deployment_id"] | ||
if !ok { | ||
return fmt.Errorf("missing internal deployment_id") | ||
} | ||
deploymentID, ok := deploymentIDRaw.(string) | ||
if !ok { | ||
return fmt.Errorf("internal deployment_id not a string") | ||
} | ||
|
||
ctx, cancel := context.WithTimeout(ctx, 1*time.Minute) | ||
defer cancel() | ||
|
||
p.logger.Logf("Destroying stack %s (%s)", s.Version, s.ID) | ||
return p.client.ShutdownDeployment(ctx, deploymentID) | ||
} |
Oops, something went wrong.