Skip to content

Commit

Permalink
landscape: support enabling and disabling landscape-client
Browse files Browse the repository at this point in the history
  • Loading branch information
orndorffgrant committed Jul 27, 2023
1 parent 3ab862f commit 2cc2764
Show file tree
Hide file tree
Showing 18 changed files with 619 additions and 20 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/ci-integration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ jobs:
UACLIENT_BEHAVE_CONTRACT_TOKEN: '${{ secrets.UACLIENT_BEHAVE_CONTRACT_TOKEN }}'
UACLIENT_BEHAVE_CONTRACT_TOKEN_STAGING: '${{ secrets.UACLIENT_BEHAVE_CONTRACT_TOKEN_STAGING }}'
UACLIENT_BEHAVE_CONTRACT_TOKEN_STAGING_EXPIRED: '${{ secrets.UACLIENT_BEHAVE_CONTRACT_TOKEN_STAGING_EXPIRED }}'
UACLIENT_BEHAVE_LANDSCAPE_REGISTRATION_KEY: '${{ secrets.UACLIENT_BEHAVE_LANDSCAPE_REGISTRATION_KEY }}'
UACLIENT_BEHAVE_LANDSCAPE_API_ACCESS_KEY: '${{ secrets.UACLIENT_BEHAVE_LANDSCAPE_API_ACCESS_KEY }}'
UACLIENT_BEHAVE_LANDSCAPE_API_SECRET_KEY: '${{ secrets.UACLIENT_BEHAVE_LANDSCAPE_API_SECRET_KEY }}'
run: |
PYCLOUDLIB_CONFIG="$(mktemp --tmpdir="${{ runner.temp }}" pycloudlib.toml.XXXXXXXXXX)"
GCE_CREDENTIALS_PATH="$(mktemp --tmpdir="${{ runner.temp }}" gcloud.json.XXXXXXXXXX)"
Expand Down
12 changes: 12 additions & 0 deletions features/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ class UAClientBehaveConfig:
"contract_token",
"contract_token_staging",
"contract_token_staging_expired",
"landscape_registration_key",
"landscape_api_access_key",
"landscape_api_secret_key",
"machine_type",
"private_key_file",
"private_key_name",
Expand All @@ -102,6 +105,9 @@ class UAClientBehaveConfig:
"contract_token",
"contract_token_staging",
"contract_token_staging_expired",
"landscape_registration_key",
"landscape_api_access_key",
"landscape_api_secret_key",
]

# This variable is used in .from_environ() but also to emit the "Config
Expand All @@ -124,6 +130,9 @@ def __init__(
contract_token: Optional[str] = None,
contract_token_staging: Optional[str] = None,
contract_token_staging_expired: Optional[str] = None,
landscape_registration_key: Optional[str] = None,
landscape_api_access_key: Optional[str] = None,
landscape_api_secret_key: Optional[str] = None,
artifact_dir: str = "artifacts",
install_from: InstallationSource = InstallationSource.LOCAL,
custom_ppa: Optional[str] = None,
Expand All @@ -141,6 +150,9 @@ def __init__(
self.contract_token = contract_token
self.contract_token_staging = contract_token_staging
self.contract_token_staging_expired = contract_token_staging_expired
self.landscape_registration_key = landscape_registration_key
self.landscape_api_access_key = landscape_api_access_key
self.landscape_api_secret_key = landscape_api_secret_key
self.image_clean = image_clean
self.destroy_instances = destroy_instances
self.machine_type = machine_type
Expand Down
257 changes: 257 additions & 0 deletions features/landscape.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
@uses.config.contract_token
@uses.config.landscape_registration_key
@uses.config.landscape_api_access_key
@uses.config.landscape_api_secret_key
Feature: Enable landscape on Ubuntu

@series.mantic
@uses.config.machine_type.any
@uses.config.machine_type.lxd-container
Scenario Outline: Enable Landscape non-interactively
Given a `<release>` `<machine_type>` machine with ubuntu-advantage-tools installed
When I attach `contract_token_staging` with sudo and options `--no-auto-enable`

Then I verify that running `pro enable landscape` `as non-root` exits `1`
And I will see the following on stderr:
"""
This command must be run as root (try using sudo).
"""

When I run `pro enable landscape -- --computer-title $behave_var{machine-name system-under-test} --account-name pro-client-qa --registration-key $behave_var{config landscape_registration_key} --silent` with sudo
Then stdout contains substring:
"""
One moment, checking your subscription first
Updating package lists
Installing landscape-client
Executing `landscape-config --computer-title $behave_var{machine-name system-under-test} --account-name pro-client-qa --registration-key <REDACTED> --silent`
"""
Then stdout contains substring
"""
System successfully registered.
"""
When I run `pro status` as non-root
Then stdout matches regexp:
"""
landscape +yes +enabled
"""
When I run `pro status` with sudo
Then stdout matches regexp:
"""
landscape +yes +enabled
"""

When I run `systemctl stop landscape-client` with sudo
When I run `pro status` with sudo
Then stdout matches regexp:
"""
landscape +yes +warning
"""
Then stdout contains substring:
"""
Landscape is installed and configured and registered but not running.
Run `sudo landscape-config` to start it, or run `sudo pro disable landscape`
"""

When I run `rm /etc/landscape/client.conf` with sudo
When I run `pro status` with sudo
Then stdout matches regexp:
"""
landscape +yes +warning
"""
Then stdout contains substring:
"""
Landscape is installed but not configured.
Run `sudo landscape-config` to set it up, or run `sudo pro disable landscape`
"""

When I run `sudo pro disable landscape` with sudo
Then I will see the following on stdout:
"""
Executing `landscape-config --disable`
Failed running command 'landscape-config --disable' [exit(1)]. Message: error: config file /etc/landscape/client.conf can't be read
Backing up /etc/landscape/client.conf as /etc/landscape/client.conf.pro-disable-backup
[Errno 2] No such file or directory: '/etc/landscape/client.conf' -> '/etc/landscape/client.conf.pro-disable-backup'
Uninstalling landscape-client
"""
When I run `pro status` with sudo
Then stdout matches regexp:
"""
landscape +yes +disabled
"""

# Enable with assume-yes
When I run `pro enable landscape --assume-yes -- --computer-title $behave_var{machine-name system-under-test} --account-name pro-client-qa --registration-key $behave_var{config landscape_registration_key}` with sudo
Then I will see the following on stdout:
"""
One moment, checking your subscription first
Updating package lists
Installing landscape-client
Executing `landscape-config --computer-title $behave_var{machine-name system-under-test} --account-name pro-client-qa --registration-key <REDACTED> --silent`
Landscape enabled
"""
When I run `pro status` with sudo
Then stdout matches regexp:
"""
landscape +yes +enabled
"""
When I run `sudo pro disable landscape` with sudo

# Fail to enable with assume-yes
When I verify that running `pro enable landscape --assume-yes -- --computer-title $behave_var{machine-name system-under-test} --account-name pro-client-qa` `with sudo` exits `1`
Then I will see the following on stdout:
"""
One moment, checking your subscription first
Updating package lists
Installing landscape-client
Executing `landscape-config --computer-title $behave_var{machine-name system-under-test} --account-name pro-client-qa --silent`
Created symlink /etc/systemd/system/multi-user.target.wants/landscape-client.service → /lib/systemd/system/landscape-client.service.
Invalid account name or registration key.
Could not enable Landscape.
"""
When I run `pro status` with sudo
Then stdout matches regexp:
"""
landscape +yes +warning
"""
Then stdout contains substring:
"""
Landscape is installed and configured but not registered.
Run `sudo landscape-config` to register, or run `sudo pro disable landscape`
"""
When I run `sudo pro disable landscape` with sudo

# Enable with assume-yes and format json
When I run `pro enable landscape --assume-yes --format=json -- --computer-title $behave_var{machine-name system-under-test} --account-name pro-client-qa --registration-key $behave_var{config landscape_registration_key}` with sudo
Then I will see the following on stdout:
"""
{"_schema_version": "0.1", "errors": [], "failed_services": [], "needs_reboot": false, "processed_services": ["landscape"], "result": "success", "warnings": []}
"""
When I run `pro status` with sudo
Then stdout matches regexp:
"""
landscape +yes +enabled
"""
When I run `sudo pro disable landscape` with sudo

# Fail to enable with assume-yes and format json
When I verify that running `pro enable landscape --assume-yes --format=json -- --computer-title $behave_var{machine-name system-under-test} --account-name pro-client-qa` `with sudo` exits `1`
Then I will see the following on stdout:
"""
{"_schema_version": "0.1", "errors": [{"additional_info": {"stderr": "Created symlink /etc/systemd/system/multi-user.target.wants/landscape-client.service \u2192 /lib/systemd/system/landscape-client.service.\nInvalid account name or registration key.", "stdout": "Please wait..."}, "message": "landscape-config command failed", "message_code": "landscape-config-failed", "service": "landscape", "type": "service"}], "failed_services": ["landscape"], "needs_reboot": false, "processed_services": [], "result": "failure", "warnings": []}
"""
When I run `pro status` with sudo
Then stdout matches regexp:
"""
landscape +yes +warning
"""
Then stdout contains substring:
"""
Landscape is installed and configured but not registered.
Run `sudo landscape-config` to register, or run `sudo pro disable landscape`
"""
When I run `sudo pro disable landscape` with sudo

# cleanup
Then I reject all pending computers on Landscape
Examples: ubuntu release
| release | machine_type |
| mantic | lxd-container |

@series.mantic
@uses.config.machine_type.any
@uses.config.machine_type.lxd-container
Scenario Outline: Enable Landscape interactively
Given a `<release>` `<machine_type>` machine with ubuntu-advantage-tools installed
When I attach `contract_token_staging` with sudo and options `--no-auto-enable`

Then I verify that running `pro enable landscape` `as non-root` exits `1`
And I will see the following on stderr:
"""
This command must be run as root (try using sudo).
"""

When I run `pro enable landscape` `with sudo` and the following stdin
# This will change in the future, but right now the lines are:
# allow starting on boot
# computer title
# account name
# registration key
# confirm registration key
# http proxy
# https proxy
# enable script execution
# access group
# tags
# request registration
"""
y
$behave_var{machine-name system-under-test}
pro-client-qa
$behave_var{config landscape_registration_key}
$behave_var{config landscape_registration_key}
n
y
"""
Then stdout contains substring:
"""
One moment, checking your subscription first
Updating package lists
Installing landscape-client
Executing `landscape-config`
"""
Then stdout contains substring:
"""
System successfully registered.
"""
When I run `pro status` with sudo
Then stdout matches regexp:
"""
landscape +yes +enabled
"""
When I run `pro disable landscape` with sudo

When I verify that running `pro enable landscape` `with sudo` and the following stdin exits `1`
"""
y
$behave_var{machine-name system-under-test}
pro-client-qa
wrong
wrong
n
y
"""
Then stdout contains substring:
"""
One moment, checking your subscription first
Updating package lists
Installing landscape-client
Executing `landscape-config`
"""
Then stderr contains substring:
"""
Invalid account name or registration key.
"""
When I run `pro status` with sudo
Then stdout matches regexp:
"""
landscape +yes +warning
"""
Then stdout contains substring:
"""
Landscape is installed and configured but not registered.
Run `sudo landscape-config` to register, or run `sudo pro disable landscape`
"""

# cleanup
Then I reject all pending computers on Landscape
Examples: ubuntu release
| release | machine_type |
| mantic | lxd-container |
69 changes: 69 additions & 0 deletions features/steps/landscape.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import hmac
import json
import time
from base64 import b64encode
from hashlib import sha256
from urllib.parse import quote
from urllib.request import Request, urlopen

from behave import step


def _landscape_api_request(access_key, secret_key, action, action_params):
timestamp = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
params = {
"action": action,
"access_key_id": access_key,
"signature_method": "HmacSHA256",
"signature_version": "2",
"timestamp": timestamp,
"version": "2011-08-01",
**action_params,
}
method = "POST"
uri = "https://landscape.canonical.com/api/"
host = "landscape.canonical.com"
path = "/api/"

formatted_params = "&".join(
quote(k, safe="~") + "=" + quote(v, safe="~")
for k, v in sorted(params.items())
)

to_sign = "{method}\n{host}\n{path}\n{formatted_params}".format(
method=method,
host=host,
path=path,
formatted_params=formatted_params,
)
digest = hmac.new(secret_key.encode(), to_sign.encode(), sha256).digest()
signature = b64encode(digest)
formatted_params += "&signature=" + quote(signature)

request = Request(
uri,
headers={"Host": host},
method=method,
data=formatted_params.encode(),
)
response = urlopen(request)

return response.code, json.load(response)


@step("I reject all pending computers on Landscape")
def reject_all_pending_computers(context):
access_key = context.pro_config.landscape_api_access_key
secret_key = context.pro_config.landscape_api_secret_key
code, pending_computers = _landscape_api_request(
access_key, secret_key, "GetPendingComputers", {}
)
assert code == 200
reject_params = {
"computer_ids.{}".format(i + 1): str(computer["id"])
for i, computer in enumerate(pending_computers)
}
code, _resp = _landscape_api_request(
access_key, secret_key, "RejectPendingComputers", reject_params
)
assert code == 200
Loading

0 comments on commit 2cc2764

Please sign in to comment.