mirror of https://github.com/docker/docs.git
Merge pull request #2708 from dgageot/external
Add CI tests for an external driver
This commit is contained in:
commit
2aebbb8d88
23
circle.yml
23
circle.yml
|
@ -1,11 +1,9 @@
|
|||
# Pony-up!
|
||||
machine:
|
||||
pre:
|
||||
# Install gvm
|
||||
- bash < <(curl -s -S -L https://raw.githubusercontent.com/moovweb/gvm/1.0.22/binscripts/gvm-installer)
|
||||
# Install bats
|
||||
- git clone https://github.com/sstephenson/bats.git && cd bats && sudo ./install.sh /usr/local
|
||||
# - sudo apt-get install -y virtualbox
|
||||
|
||||
post:
|
||||
- gvm install go1.5.2 -B --name=stable
|
||||
|
@ -16,8 +14,6 @@ machine:
|
|||
BASE_DIR: src/github.com/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME
|
||||
# Trick circle brainflat "no absolute path" behavior
|
||||
BASE_STABLE: ../../../$HOME/.gvm/pkgsets/stable/global/$BASE_DIR
|
||||
# BASE_BLEED: ../../../$HOME/.gvm/pkgsets/bleed/global/$BASE_DIR
|
||||
BUILDTAGS: ""
|
||||
|
||||
dependencies:
|
||||
override:
|
||||
|
@ -31,23 +27,12 @@ test:
|
|||
- gvm use stable && go version
|
||||
- gvm use stable && make build:
|
||||
pwd: $BASE_STABLE
|
||||
- gvm use stable && GO15VENDOREXPERIMENT=1 go get github.com/docker/docker-machine-driver-ci-test
|
||||
|
||||
override:
|
||||
- DRIVER=none test/integration/run-bats.sh test/integration/cli:
|
||||
pwd: $BASE_STABLE
|
||||
timeout: 600
|
||||
# - DRIVER=virtualbox test/integration/run-bats.sh test/integration/core:
|
||||
# pwd: $BASE_STABLE
|
||||
# timeout: 1200
|
||||
# - DRIVER=digitalocean test/integration/run-bats.sh test/integration/core:
|
||||
# pwd: $BASE_STABLE
|
||||
# timeout: 1200
|
||||
# - DRIVER=azure test/integration/run-bats.sh test/integration/core:
|
||||
# pwd: $BASE_STABLE
|
||||
# timeout: 1200
|
||||
# - DRIVER=amazonec2 test/integration/run-bats.sh test/integration/core:
|
||||
# pwd: $BASE_STABLE
|
||||
# timeout: 1200
|
||||
|
||||
# - gvm use stable && DRIVER=amazonec2 test/integration/run-bats.sh test/integration/core:
|
||||
# pwd: $BASE_STABLE
|
||||
- PATH=../../../../bin:$PATH DRIVER=ci-test test/integration/run-bats.sh test/integration/3rdparty:
|
||||
pwd: $BASE_STABLE
|
||||
timeout: 600
|
||||
|
|
|
@ -38,7 +38,7 @@ To create a machine instance, specify `--driver google`, the project id and the
|
|||
|
||||
### Options
|
||||
|
||||
- `--google-project`: **required** The id of your project to use when launching the instance.
|
||||
- `--google-project`: **required** The id of your project to use when launching the instance.
|
||||
- `--google-zone`: The zone to launch the instance.
|
||||
- `--google-machine-type`: The type of instance.
|
||||
- `--google-machine-image`: The absolute URL to a base VM image to instantiate.
|
||||
|
@ -50,6 +50,7 @@ To create a machine instance, specify `--driver google`, the project id and the
|
|||
- `--google-preemptible`: Instance preemptibility.
|
||||
- `--google-tags`: Instance tags (comma-separated).
|
||||
- `--google-use-internal-ip`: When this option is used during create it will make docker-machine use internal rather than public NATed IPs. The flag is persistent in the sense that a machine created with it retains the IP. It's useful for managing docker machines from another machine on the same network e.g. while deploying swarm.
|
||||
- `--google-use-existing`: Don't create a new VM, use an existing one. This is useful when you'd like to provision Docker on a VM you created yourself, maybe because it uses create options not supported by this driver.
|
||||
|
||||
The GCE driver will use the `ubuntu-1510-wily-v20151114` instance image unless otherwise specified. To obtain a
|
||||
list of image URLs run:
|
||||
|
@ -72,3 +73,4 @@ Environment variables and default values:
|
|||
| `--google-preemptible` | `GOOGLE_PREEMPTIBLE` | - |
|
||||
| `--google-tags` | `GOOGLE_TAGS` | - |
|
||||
| `--google-use-internal-ip` | `GOOGLE_USE_INTERNAL_IP` | - |
|
||||
| `--google-use-existing` | `GOOGLE_USE_EXISTING` | - |
|
||||
|
|
|
@ -88,6 +88,11 @@ func (c *ComputeUtil) disk() (*raw.Disk, error) {
|
|||
|
||||
// deleteDisk deletes the persistent disk.
|
||||
func (c *ComputeUtil) deleteDisk() error {
|
||||
disk, _ := c.disk()
|
||||
if disk == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Infof("Deleting disk.")
|
||||
op, err := c.service.Disks.Delete(c.project, c.zone, c.diskName()).Do()
|
||||
if err != nil {
|
||||
|
@ -162,8 +167,9 @@ func (c *ComputeUtil) portsUsed() ([]string, error) {
|
|||
return ports, nil
|
||||
}
|
||||
|
||||
func (c *ComputeUtil) createFirewallRule() error {
|
||||
log.Infof("Opening firewall ports.")
|
||||
// openFirewallPorts configures the firewall to open docker and swarm ports.
|
||||
func (c *ComputeUtil) openFirewallPorts() error {
|
||||
log.Infof("Opening firewall ports")
|
||||
|
||||
create := false
|
||||
rule, _ := c.firewallRule()
|
||||
|
@ -213,11 +219,7 @@ func (c *ComputeUtil) instance() (*raw.Instance, error) {
|
|||
|
||||
// createInstance creates a GCE VM instance.
|
||||
func (c *ComputeUtil) createInstance(d *Driver) error {
|
||||
log.Infof("Creating instance.")
|
||||
|
||||
if err := c.createFirewallRule(); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Infof("Creating instance")
|
||||
|
||||
instance := &raw.Instance{
|
||||
Name: c.instanceName,
|
||||
|
@ -280,7 +282,7 @@ func (c *ComputeUtil) createInstance(d *Driver) error {
|
|||
return err
|
||||
}
|
||||
|
||||
log.Infof("Waiting for Instance...")
|
||||
log.Infof("Waiting for Instance")
|
||||
if err = c.waitForRegionalOp(op.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -290,15 +292,58 @@ func (c *ComputeUtil) createInstance(d *Driver) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// Update the SSH Key
|
||||
sshKey, err := ioutil.ReadFile(d.GetSSHKeyPath() + ".pub")
|
||||
return c.uploadSSHKey(instance, d.GetSSHKeyPath())
|
||||
}
|
||||
|
||||
// configureInstance configures an existing instance for use with Docker Machine.
|
||||
func (c *ComputeUtil) configureInstance(d *Driver) error {
|
||||
log.Infof("Configuring instance")
|
||||
|
||||
instance, err := c.instance()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.addFirewallTag(instance); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.uploadSSHKey(instance, d.GetSSHKeyPath())
|
||||
}
|
||||
|
||||
// addFirewallTag adds a tag to the instance to match the firewall rule.
|
||||
func (c *ComputeUtil) addFirewallTag(instance *raw.Instance) error {
|
||||
log.Infof("Adding tag for the firewall rule")
|
||||
|
||||
tags := instance.Tags
|
||||
for _, tag := range tags.Items {
|
||||
if tag == firewallTargetTag {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
tags.Items = append(tags.Items, firewallTargetTag)
|
||||
|
||||
op, err := c.service.Instances.SetTags(c.project, c.zone, instance.Name, tags).Do()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.waitForRegionalOp(op.Name)
|
||||
}
|
||||
|
||||
// uploadSSHKey updates the instance metadata with the given ssh key.
|
||||
func (c *ComputeUtil) uploadSSHKey(instance *raw.Instance, sshKeyPath string) error {
|
||||
log.Infof("Uploading SSH Key")
|
||||
|
||||
sshKey, err := ioutil.ReadFile(sshKeyPath + ".pub")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
metaDataValue := fmt.Sprintf("%s:%s %s\n", c.userName, strings.TrimSpace(string(sshKey)), c.userName)
|
||||
op, err = c.service.Instances.SetMetadata(c.project, c.zone, c.instanceName, &raw.Metadata{
|
||||
|
||||
op, err := c.service.Instances.SetMetadata(c.project, c.zone, c.instanceName, &raw.Metadata{
|
||||
Fingerprint: instance.Metadata.Fingerprint,
|
||||
Items: []*raw.MetadataItems{
|
||||
{
|
||||
|
@ -307,10 +352,6 @@ func (c *ComputeUtil) createInstance(d *Driver) error {
|
|||
},
|
||||
},
|
||||
}).Do()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Infof("Waiting for SSH Key")
|
||||
|
||||
return c.waitForRegionalOp(op.Name)
|
||||
}
|
||||
|
@ -360,6 +401,7 @@ func (c *ComputeUtil) startInstance() error {
|
|||
return c.waitForRegionalOp(op.Name)
|
||||
}
|
||||
|
||||
// waitForOp waits for the operation to finish.
|
||||
func (c *ComputeUtil) waitForOp(opGetter func() (*raw.Operation, error)) error {
|
||||
for {
|
||||
op, err := opGetter()
|
||||
|
@ -367,7 +409,7 @@ func (c *ComputeUtil) waitForOp(opGetter func() (*raw.Operation, error)) error {
|
|||
return err
|
||||
}
|
||||
|
||||
log.Debugf("operation %q status: %s", op.Name, op.Status)
|
||||
log.Debugf("Operation %q status: %s", op.Name, op.Status)
|
||||
if op.Status == "DONE" {
|
||||
if op.Error != nil {
|
||||
return fmt.Errorf("Operation error: %v", *op.Error.Errors[0])
|
||||
|
@ -379,7 +421,7 @@ func (c *ComputeUtil) waitForOp(opGetter func() (*raw.Operation, error)) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// waitForOp waits for the operation to finish.
|
||||
// waitForRegionalOp waits for the regional operation to finish.
|
||||
func (c *ComputeUtil) waitForRegionalOp(name string) error {
|
||||
return c.waitForOp(func() (*raw.Operation, error) {
|
||||
return c.service.ZoneOperations.Get(c.project, c.zone, name).Do()
|
||||
|
|
|
@ -26,6 +26,7 @@ type Driver struct {
|
|||
DiskSize int
|
||||
Project string
|
||||
Tags string
|
||||
UseExisting bool
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -110,6 +111,11 @@ func (d *Driver) GetCreateFlags() []mcnflag.Flag {
|
|||
Usage: "Use internal GCE Instance IP rather than public one",
|
||||
EnvVar: "GOOGLE_USE_INTERNAL_IP",
|
||||
},
|
||||
mcnflag.BoolFlag{
|
||||
Name: "google-use-existing",
|
||||
Usage: "Don't create a new VM, use an existing one",
|
||||
EnvVar: "GOOGLE_USE_EXISTING",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -156,15 +162,18 @@ func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
|
|||
}
|
||||
|
||||
d.Zone = flags.String("google-zone")
|
||||
d.MachineType = flags.String("google-machine-type")
|
||||
d.MachineImage = flags.String("google-machine-image")
|
||||
d.DiskSize = flags.Int("google-disk-size")
|
||||
d.DiskType = flags.String("google-disk-type")
|
||||
d.Address = flags.String("google-address")
|
||||
d.Preemptible = flags.Bool("google-preemptible")
|
||||
d.UseInternalIP = flags.Bool("google-use-internal-ip")
|
||||
d.Scopes = flags.String("google-scopes")
|
||||
d.Tags = flags.String("google-tags")
|
||||
d.UseExisting = flags.Bool("google-use-existing")
|
||||
if !d.UseExisting {
|
||||
d.MachineType = flags.String("google-machine-type")
|
||||
d.MachineImage = flags.String("google-machine-image")
|
||||
d.DiskSize = flags.Int("google-disk-size")
|
||||
d.DiskType = flags.String("google-disk-type")
|
||||
d.Address = flags.String("google-address")
|
||||
d.Preemptible = flags.Bool("google-preemptible")
|
||||
d.UseInternalIP = flags.Bool("google-use-internal-ip")
|
||||
d.Scopes = flags.String("google-scopes")
|
||||
d.Tags = flags.String("google-tags")
|
||||
}
|
||||
d.SSHUser = flags.String("google-username")
|
||||
d.SSHPort = 22
|
||||
d.SetSwarmConfigFromFlags(flags)
|
||||
|
@ -191,8 +200,15 @@ func (d *Driver) PreCreateCheck() error {
|
|||
// doesn't exist, so just check instance for nil.
|
||||
log.Infof("Check if the instance already exists")
|
||||
|
||||
if instance, _ := c.instance(); instance != nil {
|
||||
return fmt.Errorf("Instance %v already exists.", d.MachineName)
|
||||
instance, _ := c.instance()
|
||||
if d.UseExisting {
|
||||
if instance == nil {
|
||||
return fmt.Errorf("Unable to find instance %q in zone %q.", d.MachineName, d.Zone)
|
||||
}
|
||||
} else {
|
||||
if instance != nil {
|
||||
return fmt.Errorf("Instance %q already exists in zone %q.", d.MachineName, d.Zone)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -213,6 +229,13 @@ func (d *Driver) Create() error {
|
|||
return err
|
||||
}
|
||||
|
||||
if err := c.openFirewallPorts(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if d.UseExisting {
|
||||
return c.configureInstance(d)
|
||||
}
|
||||
return c.createInstance(d)
|
||||
}
|
||||
|
||||
|
|
|
@ -160,38 +160,39 @@ func (api *Client) performCreate(h *host.Host) error {
|
|||
return fmt.Errorf("Error saving host to store after attempting creation: %s", err)
|
||||
}
|
||||
|
||||
// TODO: Not really a fan of just checking "none" here.
|
||||
if h.Driver.DriverName() != "none" {
|
||||
log.Info("Waiting for machine to be running, this may take a few minutes...")
|
||||
if err := mcnutils.WaitFor(drivers.MachineInState(h.Driver, state.Running)); err != nil {
|
||||
return fmt.Errorf("Error waiting for machine to be running: %s", err)
|
||||
}
|
||||
|
||||
log.Info("Machine is running, waiting for SSH to be available...")
|
||||
if err := drivers.WaitForSSH(h.Driver); err != nil {
|
||||
return fmt.Errorf("Error waiting for SSH: %s", err)
|
||||
}
|
||||
|
||||
log.Info("Detecting operating system of created instance...")
|
||||
provisioner, err := provision.DetectProvisioner(h.Driver)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error detecting OS: %s", err)
|
||||
}
|
||||
|
||||
log.Infof("Provisioning with %s...", provisioner.String())
|
||||
if err := provisioner.Provision(*h.HostOptions.SwarmOptions, *h.HostOptions.AuthOptions, *h.HostOptions.EngineOptions); err != nil {
|
||||
return fmt.Errorf("Error running provisioning: %s", err)
|
||||
}
|
||||
|
||||
// We should check the connection to docker here
|
||||
log.Info("Checking connection to Docker...")
|
||||
if _, _, err = check.DefaultConnChecker.Check(h, false); err != nil {
|
||||
return fmt.Errorf("Error checking the host: %s", err)
|
||||
}
|
||||
|
||||
log.Info("Docker is up and running!")
|
||||
// TODO: Not really a fan of just checking "none" or "ci-test" here.
|
||||
if h.Driver.DriverName() == "none" || h.Driver.DriverName() == "ci-test" {
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Info("Waiting for machine to be running, this may take a few minutes...")
|
||||
if err := mcnutils.WaitFor(drivers.MachineInState(h.Driver, state.Running)); err != nil {
|
||||
return fmt.Errorf("Error waiting for machine to be running: %s", err)
|
||||
}
|
||||
|
||||
log.Info("Machine is running, waiting for SSH to be available...")
|
||||
if err := drivers.WaitForSSH(h.Driver); err != nil {
|
||||
return fmt.Errorf("Error waiting for SSH: %s", err)
|
||||
}
|
||||
|
||||
log.Info("Detecting operating system of created instance...")
|
||||
provisioner, err := provision.DetectProvisioner(h.Driver)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error detecting OS: %s", err)
|
||||
}
|
||||
|
||||
log.Infof("Provisioning with %s...", provisioner.String())
|
||||
if err := provisioner.Provision(*h.HostOptions.SwarmOptions, *h.HostOptions.AuthOptions, *h.HostOptions.EngineOptions); err != nil {
|
||||
return fmt.Errorf("Error running provisioning: %s", err)
|
||||
}
|
||||
|
||||
// We should check the connection to docker here
|
||||
log.Info("Checking connection to Docker...")
|
||||
if _, _, err = check.DefaultConnChecker.Check(h, false); err != nil {
|
||||
return fmt.Errorf("Error checking the host: %s", err)
|
||||
}
|
||||
|
||||
log.Info("Docker is up and running!")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
#!/usr/bin/env bats
|
||||
|
||||
load ${BASE_TEST_DIR}/helpers.bash
|
||||
|
||||
only_if_env DRIVER ci-test
|
||||
|
||||
@test "create" {
|
||||
run machine create -d $DRIVER --url none default
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "ls" {
|
||||
run machine ls -q
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ${#lines[@]} == 1 ]]
|
||||
[[ ${lines[0]} = "default" ]]
|
||||
}
|
||||
|
||||
@test "url" {
|
||||
run machine url default
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ${output} == *"none"* ]]
|
||||
}
|
||||
|
||||
@test "status" {
|
||||
run machine status default
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ${output} == *"Running"* ]]
|
||||
}
|
||||
|
||||
@test "rm" {
|
||||
run machine rm -y default
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
Loading…
Reference in New Issue