Skip to content

Commit

Permalink
Add python cli tool to interact with Equinix etc
Browse files Browse the repository at this point in the history
This could have been bash but it would have ended up even messier than
this :D.

Just a couple of commands now, but can be changed/expanded/refactored.
  • Loading branch information
Callisto13 committed Nov 4, 2021
1 parent 6630417 commit 72f4aec
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 4 deletions.
10 changes: 7 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,11 @@ test-with-cov: ## Run unit tests with coverage
go test -v -race -timeout 2m -p 1 -covermode=atomic -coverprofile=coverage.txt ./...

.PHONY: test-e2e
test-e2e: compile-e2e ## Run e2e tests locally in a container
test-e2e: compile-e2e ## Run e2e tests locally
go test -timeout 30m -p 1 -v -tags=e2e ./test/e2e/...

.PHONY: test-e2e-docker
test-e2e-docker: compile-e2e ## Run e2e tests locally in a container
docker run --rm -it \
--privileged \
--volume /dev:/dev \
Expand All @@ -109,11 +113,11 @@ test-e2e: compile-e2e ## Run e2e tests locally in a container
--ipc=host \
--workdir=/src/flintlock \
$(test_image):latest \
"go test -timeout 30m -p 1 -v -tags=e2e ./test/e2e/..."
"make test-e2e"

.PHONY: test-e2e-metal
test-e2e-metal: ## Run e2e tests in Equinix
echo "coming soon to some hardware near you"
./test/tools/run.py run-e2e -o $(EQUINIX_ORG_ID)

.PHONY: compile-e2e
compile-e2e: ## Test e2e compilation
Expand Down
1 change: 0 additions & 1 deletion test/tools/metal.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import packet
import time
import base64
import spur
import logging
import os
Expand Down
4 changes: 4 additions & 0 deletions test/tools/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
click==7.1.2
packet-python==1.44.1
pycryptodome==3.11.0
spur==0.3.22
111 changes: 111 additions & 0 deletions test/tools/run.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
#!/usr/bin/env python3

from test import Test
from metal import Welder
import click
import os
import sys
import random
import string
from os.path import dirname, abspath

def generate_string():
letters = string.ascii_letters
return ( ''.join(random.choice(letters) for i in range(10)) )

def generated_project_name():
return "flintlock_prj_"+generate_string()

def generated_key_name():
return "flintlock_key_"+generate_string()

@click.group()
def cli():
"""
General thing doer for flintlock
"""
pass

@cli.command()
@click.option('-o', '--org-id', type=str, help='Equinix organisation id (required)')
@click.option('-p', '--project-name', type=str, help='Name of the project to create (default: randomly generated)', default=generated_project_name())
@click.option('-k', '--ssh-key-name', type=str, help='Name of the ssh key to create and attach to the device (default: randomly generated)', default=generated_key_name())
@click.option('-d', '--device-name', type=str, help='Name of the device to create (default: randomly generated)', default='T-800')
@click.option('-e', '--existing-device-id', type=str, help='Skip create and set the UUID of an existing device to run tests against', default=None)
@click.option('-s', '--skip-delete', is_flag=True, help='Skip cleanup of Equinix infrastructure for debugging after run', default=False)
def run_e2e(org_id, project_name, ssh_key_name, device_name, skip_delete, existing_device_id):
token = os.environ.get("METAL_AUTH_TOKEN")
if token is None:
click.echo("must set METAL_AUTH_TOKEN")
sys.exit()
if org_id is None:
click.echo("must set --org-id")
sys.exit()

if existing_device_id == None:
click.echo(f"Running e2e tests. Will create project '{project_name}', ssh_key '{ssh_key_name}' and device '{device_name}'")
click.echo("Note: this will create and bootstrap a new device in Equinix and may take some time")
else:
skip_delete = True
tool_dir = dirname(abspath(__file__))
if os.path.exists(tool_dir+"/private.key") != True:
click.echo(f"`private.key` file must be saved at `{tool_dir}` when `--existing-device-id` flag set")
sys.exit()
click.echo("running e2e tests using device '{existing_device_id}`")

runner = Test(token, org_id, project_name, ssh_key_name, device_name, skip_delete, existing_device_id)
with runner:
runner.setup()
runner.run_tests()

if skip_delete:
dev_id, dev_ip = runner.device_details()
click.echo(f"Device `{dev_id}` left alive for debugging. Use with `--existing-device-id` to re-run tests. SSH command `ssh -i hack/tools/private.key root@{dev_ip}`. Delete device with `delete-device` command.")

@cli.command()
@click.option('-o', '--org-id', type=str, help='Equinix organisation id (required)')
@click.option('-p', '--project-id', type=str, help='ID of the project to create the device in (required)')
@click.option('-k', '--ssh-key-name', type=str, help='Name of the ssh key to create and attach to the device (default: randomly generated)', default=generated_key_name())
@click.option('-d', '--device-name', type=str, help='Name of the device to create (default: randomly generated)', default='T-800')
@click.option('-u', '--userdata', type=str, help='String containing shell bootstrap userdata (default: standard flintlockd bootstrapping, see readme for details)')
def create_device(org_id, project_id, ssh_key_name, device_name, userdata):
token = os.environ.get("METAL_AUTH_TOKEN")
if token is None:
click.echo("must set METAL_AUTH_TOKEN")
sys.exit()
if org_id is None:
click.echo("must set --org-id")
sys.exit()
if project_id is None:
click.echo("must set --project-id")
sys.exit()

click.echo(f"Creating device {device_name}")

welder = Welder(token, org_id)
ip = welder.create_all(project_id, device_name, ssh_key_name, userdata)

click.echo(f"Device {device_name} created. SSH command `ssh -i hack/tools/private.key root@{ip}`. Run tests with `run-e2e`. Delete with `delete-device`.")

@cli.command()
@click.option('-o', '--org-id', type=str, help='Equinix organisation id (required)')
@click.option('-d', '--device-id', type=str, help='Name of the device to delete (required)')
def delete_device(org_id, device_id):
token = os.environ.get("METAL_AUTH_TOKEN")
if token is None:
click.echo("must set METAL_AUTH_TOKEN")
sys.exit()
if org_id is None:
click.echo("must set --org-id")
sys.exit()
if device_id is None:
click.echo("must set --device-id")
sys.exit()

click.echo(f"Deleting device {device_id}")

welder = Welder(token, org_id)
welder.delete_device(device_id)

if __name__ == "__main__":
cli()

0 comments on commit 72f4aec

Please sign in to comment.