-
Notifications
You must be signed in to change notification settings - Fork 375
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
Add tests for extension workflow #2843
Changes from 120 commits
da72c37
59dbd22
633a826
14a743f
54ea0f3
e79c4c5
498b612
1e269f4
7b49e76
0a426cc
17fbf6a
995cbb9
eaadc83
fb03e07
6a8e0d6
8e62c05
9999aef
9d6cb62
e7c010c
193662b
bf7fc04
f7f7a54
5aad0ac
acd4ae5
1fc51a1
7b43af7
11b73e0
d8c1d21
6cf987a
4b56360
bda8708
2ba6238
79cae43
40f98a6
8aef67d
d4c9a99
2f3731c
788584c
8de7846
0a4d328
83d3c8e
2788d53
4f8a31b
a329a46
d440058
dcde7ea
b8c2605
ec7bed6
cde74ad
4b44b7e
104a623
1f4bd31
a318adc
81eca75
976cbb4
9637ea3
df94a37
1950555
ed51360
5604d3c
b4951c8
54184e7
7dac115
84946b3
e5dcd98
f97197e
f46afae
8ff742d
6d388f8
b929556
b0d1e22
d3d71f9
deef228
fe2f2c2
482f3fe
8bda6b5
75cf5a0
2b0ed08
48949e0
3ba0f05
c14125a
0380130
ef9291a
30a1740
dbecb19
8343df1
5dce3dd
8c5bd6d
d3c06b4
c34aa27
b9c9ada
7f208f9
5466804
8e6a672
2a88fd0
fb49cfc
dd46d51
947134b
0d16066
356f9a9
d081330
bdd4934
2e0303e
cf7d6ca
2613d40
2a1c260
bc842b1
746689d
9db7ce4
9735035
ed6e900
ebd1e03
b3dabdb
0abeec2
59c9456
627be4a
9f9ecea
666fac5
e643fc1
ab487b7
96f9a3e
30f9f6b
08f473b
f845bd5
5aeee72
60b04d4
eef24e8
5b48168
6adbf50
17764c1
549b8ef
56e0edb
86a0212
2aa1cc3
40a25f4
bf4cf31
9010e01
22a8b81
dbee33e
40b112c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
name: "AgentExtWorkflow" | ||
tests: | ||
- "agent_ext_workflow/extension_workflow.py" | ||
images: | ||
- "centos_79" | ||
- "suse_12" | ||
- "rhel_79" | ||
- "ubuntu_1604" | ||
- "ubuntu_1804" | ||
# This test suite uses the DCR Test Extension, which is only published in South Central US | ||
locations: "AzureCloud:southcentralus" |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added this readme directly from DCR because it contains a lot of useful information about the GuestAgentDcrTestExtension. Let me know if I should add this information as comments to the test file. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. readme is fine, though a summary in the suite file would be helpful |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
# Agent Extension Worflow Test | ||
|
||
This scenario tests if the correct extension workflow sequence is being executed from the agent. | ||
|
||
### GuestAgentDcrTestExtension | ||
|
||
This is a test extension that exists for the sole purpose of testing the extension workflow of agent. This is currently deployed to SCUS only. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do we have a publishing pipeline for the test extension? could you create one and publish it to all regions? thanks There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we have CAPS pipeline already and deployed few other regions too. I would restrict to few regions where we create vms and deploying to all of no use. https://msazure.visualstudio.com/One/_releaseProgress?_a=release-pipeline-progress&releaseId=7405435 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we wait on this until we've implemented the test extension in Rust? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @nagworld9 those links are for a different test extension. The type name for that extension is GATestExtGo, but the type name for the extension we use in this test suite is GuestAgentDcrTest |
||
|
||
All the extension does is prints the settings['name'] out to stdout. It is run everytime enable is called. | ||
|
||
Another important feature of this extension is that it maintains a `operations-<VERSION_NO>.log` **for every operation that the agent executes on that extension**. We use this to confirm that the agent executed the correct sequence of operations. | ||
|
||
Sample operations-<version>.log file snippet - | ||
```text | ||
Date:2019-07-30T21:54:03Z; Operation:install; SeqNo:0 | ||
Date:2019-07-30T21:54:05Z; Operation:enable; SeqNo:0 | ||
Date:2019-07-30T21:54:37Z; Operation:enable; SeqNo:1 | ||
Date:2019-07-30T21:55:20Z; Operation:disable; SeqNo:1 | ||
Date:2019-07-30T21:55:22Z; Operation:uninstall; SeqNo:1 | ||
``` | ||
The setting for this extension is of the format - | ||
```json | ||
{ | ||
"name": String | ||
} | ||
``` | ||
##### Repo link | ||
https://github.com/larohra/GuestAgentDcrTestExtension | ||
maddieford marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
##### Available Versions: | ||
- 1.1.5 - Version with Basic functionalities as mentioned above | ||
- 1.2.0 - Same functionalities as above with `"updateMode": "UpdateWithInstall"` in HandlerManifest.json to test update case | ||
- 1.3.0 - Same functionalities as above with `"updateMode": "UpdateWithoutInstall"` in HandlerManifest.json to test update case | ||
|
||
### Test Sequence | ||
|
||
- Install the test extension on the VM | ||
- Assert the extension status by checking if our Enable string matches the status message (We receive the status message by using the Azure SDK by polling for the VM instance view and parsing the extension status message) | ||
|
||
The Enable string of our test is of the following format (this is set in the `Settings` object when we call enable from the tests ) - | ||
```text | ||
[ExtensionName]-[Version], Count: [Enable-count] | ||
``` | ||
- Match the operation sequence as per the test and make sure they are in the correct chronological order | ||
- Restart the agent and verify if the correct operation sequence is followed |
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
#!/usr/bin/env pypy3 | ||
|
||
# Microsoft Azure Linux Agent | ||
# | ||
# Copyright 2018 Microsoft Corporation | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
# | ||
|
||
|
||
import argparse | ||
import os | ||
import sys | ||
import time | ||
from datetime import datetime | ||
|
||
DELIMITER = ";" | ||
OPS_FILE_DIR = "/var/log/azure/Microsoft.Azure.TestExtensions.Edp.GuestAgentDcrTest/" | ||
# In general most of the distros use the above directory for extension log logging except RHEL-69, | ||
# they use the 2nd File pattern (i.e. /var/log/azure/{ext-publisher-type}/{version}/extensions.log) | ||
OPS_FILE_PATTERN = ["operations-%s.log", "%s/operations-%s.log"] | ||
MAX_RETRY = 5 | ||
SLEEP_TIMER = 30 | ||
|
||
|
||
def parse_ops_log(ops_version, input_ops, start_time): | ||
# input_ops are the expected operations that we expect to see in the operations log file | ||
ver = (ops_version,) | ||
ops_file_name = None | ||
for file_pat in OPS_FILE_PATTERN: | ||
ops_file_name = os.path.join(OPS_FILE_DIR, file_pat % ver) | ||
if not os.path.exists(ops_file_name): | ||
ver = ver + (ops_version,) | ||
ops_file_name = None | ||
continue | ||
break | ||
|
||
if not ops_file_name: | ||
raise IOError("Operations File %s not found" % os.path.join(OPS_FILE_DIR, OPS_FILE_PATTERN[0] % ops_version)) | ||
|
||
ops = [] | ||
with open(ops_file_name, 'r') as ops_log: | ||
# we get the last len(input_ops) from the log file and ensure they match with the input_ops | ||
# Example of a line in the log file - `Date:2019-07-30T21:54:03Z; Operation:install; SeqNo:0` | ||
content = ops_log.readlines()[-len(input_ops):] | ||
for op_log in content: | ||
data = op_log.split(DELIMITER) | ||
date = datetime.strptime(data[0].split("Date:")[1], "%Y-%m-%dT%H:%M:%SZ") | ||
op = data[1].split("Operation:")[1] | ||
seq_no = data[2].split("SeqNo:")[1].strip('\n') | ||
|
||
# We only capture the operations that > start_time of the test | ||
if start_time > date: | ||
continue | ||
|
||
ops.append({'date': date, 'op': op, 'seq_no': seq_no}) | ||
return ops | ||
|
||
|
||
def assert_ops_in_sequence(actual_ops, expected_ops): | ||
exit_code = 0 | ||
|
||
if len(actual_ops) != len(expected_ops): | ||
print("Operation sequence length doesn't match, exit code 2") | ||
exit_code = 2 | ||
|
||
last_date = datetime(70, 1, 1) | ||
for idx, val in enumerate(actual_ops): | ||
if exit_code != 0: | ||
break | ||
|
||
if val['date'] < last_date or val['op'] != expected_ops[idx]: | ||
print("Operation sequence doesn't match, exit code 2") | ||
exit_code = 2 | ||
|
||
last_date = val['date'] | ||
|
||
return exit_code | ||
|
||
|
||
def check_update_sequence(args): | ||
# old_ops_file_name = OPS_FILE_PATTERN % args.old_version | ||
# new_ops_file_name = OPS_FILE_PATTERN % args.new_version | ||
|
||
actual_ops = parse_ops_log(args.old_version, args.old_ops, args.start_time) | ||
actual_ops.extend(parse_ops_log(args.new_version, args.new_ops, args.start_time)) | ||
actual_ops = sorted(actual_ops, key=lambda op: op['date']) | ||
|
||
exit_code = assert_ops_in_sequence(actual_ops, args.ops) | ||
|
||
return exit_code, actual_ops | ||
|
||
|
||
def check_operation_sequence(args): | ||
# ops_file_name = OPS_FILE_PATTERN % args.version | ||
|
||
actual_ops = parse_ops_log(args.version, args.ops, args.start_time) | ||
exit_code = assert_ops_in_sequence(actual_ops, args.ops) | ||
|
||
return exit_code, actual_ops | ||
|
||
|
||
def main(): | ||
# There are 2 main ways you can call this file - normal_ops_sequence or update_sequence | ||
parser = argparse.ArgumentParser() | ||
cmd_parsers = parser.add_subparsers(help="sub-command help", dest="command") | ||
|
||
# We use start_time to make sure we're testing the correct test and not some other test | ||
parser.add_argument("--start-time", dest='start_time', required=True) | ||
|
||
# Normal_ops_sequence gets the version of the ext and parses the corresponding operations file to get the operation | ||
# sequence that were run on the extension | ||
normal_ops_sequence_parser = cmd_parsers.add_parser("normal_ops_sequence", help="Test the normal operation sequence") | ||
normal_ops_sequence_parser.add_argument('--version', dest='version') | ||
normal_ops_sequence_parser.add_argument('--ops', nargs='*', dest='ops', default=argparse.SUPPRESS) | ||
|
||
# Update_sequence mode is used to check for the update scenario. We get the expected old operations, expected | ||
# new operations and the final operation list and verify if the expected operations match the actual ones | ||
update_sequence_parser = cmd_parsers.add_parser("update_sequence", help="Test the update operation sequence") | ||
update_sequence_parser.add_argument("--old-version", dest="old_version") | ||
update_sequence_parser.add_argument("--new-version", dest="new_version") | ||
update_sequence_parser.add_argument("--old-ver-ops", nargs="*", dest="old_ops", default=argparse.SUPPRESS) | ||
update_sequence_parser.add_argument("--new-ver-ops", nargs="*", dest="new_ops", default=argparse.SUPPRESS) | ||
update_sequence_parser.add_argument("--final-ops", nargs="*", dest="ops", default=argparse.SUPPRESS) | ||
|
||
args, unknown = parser.parse_known_args() | ||
|
||
if unknown or len(unknown) > 0: | ||
# Print any unknown arguments passed to this script and fix them with low priority | ||
print("[Low Proiority][To-Fix] Found unknown args: %s" % ', '.join(unknown)) | ||
|
||
args.start_time = datetime.strptime(args.start_time, "%Y-%m-%dT%H:%M:%SZ") | ||
|
||
exit_code = 999 | ||
actual_ops = [] | ||
|
||
for i in range(0, MAX_RETRY): | ||
if args.command == "update_sequence": | ||
exit_code, actual_ops = check_update_sequence(args) | ||
elif args.command == "normal_ops_sequence": | ||
exit_code, actual_ops = check_operation_sequence(args) | ||
else: | ||
print("No such command %s, exit code 5\n" % args.command) | ||
exit_code, actual_ops = 5, [] | ||
break | ||
|
||
if exit_code == 0: | ||
break | ||
|
||
print("{0} test failed with exit code: {1}; Retry attempt: {2}; Retrying in {3} secs".format(args.command, | ||
exit_code, i, | ||
SLEEP_TIMER)) | ||
time.sleep(SLEEP_TIMER) | ||
|
||
if exit_code != 0: | ||
print("Expected Operations: %s" % ", ".join(args.ops)) | ||
print("Actual Operations: %s" % | ||
','.join(["[%s, Date: %s]" % (op['op'], op['date'].strftime("%Y-%m-%dT%H:%M:%SZ")) for op in actual_ops])) | ||
|
||
print("Assertion completed, exiting with code: %s" % exit_code) | ||
sys.exit(exit_code) | ||
|
||
|
||
if __name__ == "__main__": | ||
print("Asserting operations\n") | ||
main() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
#!/usr/bin/env pypy3 | ||
|
||
# Microsoft Azure Linux Agent | ||
# | ||
# Copyright 2018 Microsoft Corporation | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
# | ||
|
||
import argparse | ||
import sys | ||
|
||
from pathlib import Path | ||
from tests_e2e.tests.lib.agent_log import AgentLog | ||
|
||
|
||
def main(): | ||
parser = argparse.ArgumentParser() | ||
parser.add_argument("--data", dest='data', required=True) | ||
args, _ = parser.parse_known_args() | ||
|
||
print("Verifying data: {0} in waagent.log".format(args.data)) | ||
found = False | ||
|
||
try: | ||
found = AgentLog(Path('/var/log/waagent.log')).agent_log_contains(args.data) | ||
if found: | ||
print("Found data: {0} in agent log".format(args.data)) | ||
else: | ||
print("Did not find data: {0} in agent log".format(args.data)) | ||
except Exception as e: | ||
print("Error thrown when searching for test data in agent log: {0}".format(str(e))) | ||
|
||
sys.exit(0 if found else 1) | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should run on all endorsed distros
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right now I have it matching the config from the dcr repo: https://msazure.visualstudio.com/One/_git/Compute-Runtime-Tux-Pipeline?path=/dungeon_crawler/scenarios/agent-ext-workflow/config.json
The DCR Test Extension has issues when installed/enabled on other distros. For example, I tried to run this test workflow on debian_9 and the extension fails to be installed:
If we wanted to run on all endorsed distros, we'd need to do some work to improve the test extension. I think we should include this as part of the work to implement the extension in Rust, and limit this workflow to these images for now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's sync offline... we may need to fix this even before thinking of rewriting the extension
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Created task to circle back on this after I migrate remaining scenarios: https://dev.azure.com/msazure/One/_workitems/edit/24442982