-
Notifications
You must be signed in to change notification settings - Fork 7
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
feat: proposal pre/post test steps #210
base: main
Are you sure you want to change the base?
Conversation
Looks good. Left some minor comments. |
README.md
Outdated
@@ -70,6 +70,7 @@ If the proposal is _pending_ and does not yet have a number, use a letter. The p | |||
- `package.json` specifies what kind of proposal it is in a `agoricProposal` field. If it's a "Software Upgrade Proposal" it also includes additional parameters. | |||
- `use.sh` is the script that will be run in the USE stage of the build | |||
- `test.sh` is the script that will be _included_ in the TEST stage of the build, and run in CI | |||
- `prepare-test.sh` is an optional script which can be used to run setup steps _inside_ the container |
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.
in this PR would you revise the name? Please clarify that it runs within the test image before agd starts. test-before-agd.sh
would be clear.
This readme must also mention the two new hooks.
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 the hook name should not have any mention of agd
. It should just be an indication of at which point in the pipeline this hook fits and prepare-test.sh
I think conveys that. We could rename it to pre-test.sh
as well but we then have to distinguish on hook names running outside and inside the container. The current PR uses pre-test.sh
name which run on the host.
What do you suggest?
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.
It should just be an indication of at which point in the pipeline this hook fits
Agree
and prepare-test.sh I think conveys that.
Disagree. prepare.sh
works very differently,
PREPARE For upgrade proposals: submits the proposal, votes on it, runs to halt for the next stage
prepare-test.sh
sounds like it would be a PREPARE step but for tests. However it runs in the same image as test.sh
. It's just like test.sh
but runs before agd starts.
I'll argue firmly that we need a different name than prepare-test.sh
. As you point out pre
and post
are taken. I think those should be more explicit that they don't run within the image: before-test-run.sh
and after-test-run.sh
. ("Before" and "after" more closely match testing hook names like Ava has.)
That would leave pre-test.sh
but that could still be easily confused. I would suggest setup-test.sh
or test-before-chain.sh
.
If we should discuss more let's do it synchronously or in a call.
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 agree with before-test-run.sh
and after-test-run.sh
for host and setup-test.sh
(and possibly teardown-test.sh
) for image so we can lock them if you agree as well.
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 like your "host" term though. I'd suggest putting the host scripts in host
or some other subdir so they're not copied to the image (using --exclude
). That way you can modify them without invalidating it.
const proposalPath = `${root}/proposals/${proposal.path}`; | ||
|
||
if (fileExists(`${proposalPath}/pre_test.sh`)) | ||
execSync(`/bin/bash ${proposalPath}/pre_test.sh`, { |
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.
the scripts have shebang. the caller should not be concerned with how they're interpreted.
execSync(`/bin/bash ${proposalPath}/pre_test.sh`, { | |
execSync(`${proposalPath}/pre_test.sh`, { |
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.
LGTM! Good documentation.
I left one code factoring suggestion at your discretion.
fileExists(afterHookScriptPath) && | ||
execSync(afterHookScriptPath, { stdio: 'inherit' }); |
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.
consider having a helper,
fileExists(afterHookScriptPath) && | |
execSync(afterHookScriptPath, { stdio: 'inherit' }); | |
execHostScriptIfPresent('after-test-run.sh'); |
Then the above three consts don't need to be defined and when someone searched after-test-run
they can see easily where in the sequence it runs.
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.
Please add running the hooks in case we're in debug
mode too.
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.
Done
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.
Given the tight integration of the host hooks with run.ts
it might make more sense to move their invocation there.
Also please make sure to support the debug use case as well.
fileExists(afterHookScriptPath) && | ||
execSync(afterHookScriptPath, { stdio: 'inherit' }); |
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.
Please add running the hooks in case we're in debug
mode too.
const { HOME } = env; | ||
|
||
const containerFilePath = `/root/${fileName}`; | ||
const filePath = `${HOME}/${fileName}`; |
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'm feeling uncomfortable with synthetic-chain hardcoding such a file, especially into the host's home dir.
Furthermore I believe this will fail when multiple test containers run concurrently.
Let's use a dynamically generated temp file, and set it in an environment variable available in the host hook scripts (I assume this is where it's needed). The container path can remain static.
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.
Done
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 use a dynamically generated temp file, and set it in an environment variable available in the host hook scripts (I assume this is where it's needed). The container path can remain static.
I don't believe this was addressed. I do not want us to write anything on the host's HOME
folder, especially un-prefixed. TMPDIR
is fair game. I might settle for $HOME/.agoric
but I really would prefer a proper tmp file that doesn't risk collisions. Also I think it'd be better to:
- create the file before we start the test or call any before hook
- provide the file path as an env to the spawned hooks
- delete the tmp file after any spawned after hook (making sure a failure of the test or of the hook removes the file)
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 will move the file to $HOME/.agoric
What kind of collision are you anticipating? The file is now prefixed with proposal name so I don’t understand. If we remove the file after the test (or before running the test) what will be the issue in that (assuming there is only a single follower running 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.
BTW I have made the change (message file is in /tmp
folder now and injected in env for the hooks)
But please add a reply to the above message for my understanding whenever you can
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.
My concern was 2 fold:
- Collisions / confusion between multiple test runs. In particular multiple tests of the same
synthetic-chain test
invocation. But to a lesser extent confusion about which file belonged to which invocation, if for some reason the test would not run correctly, but not sufficiently break to report a failure, and the subsequent host hook would then act on unrelated data (if the previous run had not cleaned up correctly) - garbage left behind. The purpose of these files are temporary while the test is running. We should clean them up after completion of the test. And since these are temporary in nature, they belong in the temp folder.
I agree that correctly cleaning up after the test would mitigate both the concerns, but in general I prefer to respect the semantics of the file system.
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.
The message file integration still feels sub-optimal.
} | ||
}; | ||
|
||
const fileExists = (name: string) => existsSync(name); |
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.
Small use before define nit. I'm surprised we're no longer linting for this.
const { HOME } = env; | ||
|
||
const containerFilePath = `/root/${fileName}`; | ||
const filePath = `${HOME}/${fileName}`; |
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 use a dynamically generated temp file, and set it in an environment variable available in the host hook scripts (I assume this is where it's needed). The container path can remain static.
I don't believe this was addressed. I do not want us to write anything on the host's HOME
folder, especially un-prefixed. TMPDIR
is fair game. I might settle for $HOME/.agoric
but I really would prefer a proper tmp file that doesn't risk collisions. Also I think it'd be better to:
- create the file before we start the test or call any before hook
- provide the file path as an env to the spawned hooks
- delete the tmp file after any spawned after hook (making sure a failure of the test or of the hook removes the file)
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.
Thanks for making the switch to a tmp file. Let's make sure things are cleaned up, and that we use less manual naming of the tmp file.
import { ProposalInfo, imageNameForProposal } from './proposals.js'; | ||
|
||
const createMessageFile = () => { | ||
const messageFileName = `${new Date().getTime()}.tmp`; | ||
const messageFilePath = `/tmp/${messageFileName}`; |
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.
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 wasn't sure adding a dependency of tmp
package was really needed here
Added
process.env.MESSAGE_FILE_PATH = messageFilePath; | ||
|
||
executeHostScriptIfPresent(proposal, 'before-test-run.sh'); |
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 it'd be preferable to plumb the messageFilePath
into executeHostScriptIfPresent
and for that spawn to use the modified env of the child process only. Mutating the current process env goes against best practices.
You can either spread the current env, or use inheritance ({__proto__: process.env, MESSAGE_FILE_PATH: messageFilePath}
)
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.
Done
}; | ||
|
||
export const debugTestImage = (proposal: ProposalInfo) => { | ||
executeHostScriptIfPresent(proposal, 'before-test-run.sh'); |
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.
To clarify, we don't expect tests run in debug mode to have a message file? Is there not a risk that host scripts would expect a message file and break if not available?
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.
Valid point
Done
const { HOME } = env; | ||
|
||
const containerFilePath = `/root/${fileName}`; | ||
const filePath = `${HOME}/${fileName}`; |
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.
My concern was 2 fold:
- Collisions / confusion between multiple test runs. In particular multiple tests of the same
synthetic-chain test
invocation. But to a lesser extent confusion about which file belonged to which invocation, if for some reason the test would not run correctly, but not sufficiently break to report a failure, and the subsequent host hook would then act on unrelated data (if the previous run had not cleaned up correctly) - garbage left behind. The purpose of these files are temporary while the test is running. We should clean them up after completion of the test. And since these are temporary in nature, they belong in the temp folder.
I agree that correctly cleaning up after the test would mitigate both the concerns, but in general I prefer to respect the semantics of the file system.
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 like the new generate copy command.
I had missed that a previous commit changed the network bind to use the host interfaces, any reason for that change?
}) => { | ||
const { name: messageFilePath, removeCallback: removeTempFileCallback } = | ||
createMessageFile(proposal); | ||
const messageFileName = basename(messageFilePath); | ||
|
||
const containerFilePath = `/root/${messageFileName}`; |
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 thought we would have the message file in the container use a static name (not even related to the proposal name) to avoid having to plumb an extra env.
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.
Container file doesn't necessarily need to use the proposal name in it's path, but we still need to pass the host file path to the host start script so that the follower can mount that file in it's container
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 but the code above will make the container file name dynamic, without any way for the container script to figure out what that name is. Or am I missing something?
`source=${messageFilePath},target=${containerFilePath},type=bind`, | ||
'--network', | ||
'host', | ||
removeContainerOnExit && '--rm', |
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.
To avoid the .filter(Boolean).map(String)
:
removeContainerOnExit && '--rm', | |
...(removeContainerOnExit ? ['--rm'] : []), |
'--publish', | ||
'1317:1317', | ||
'--publish', | ||
'9090:9090', | ||
'--publish', | ||
'26657:26657', |
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.
Why are we losing these port mappings?
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.
Because of the host
network these will still be available
'--network', | ||
'host', |
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'm pretty sure this is a bad idea. Why do we need to hook onto the host network interfaces?
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.
If we don't use host
network, we would need individual container IPs
Using host
network will make the RPC/Peer available on localhost
(using different ports)
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 don't understand. How is different IPs different than different ports?
Also afaik, only the acceptance test needs to be connected to, so couldn't we only target that container?
closes: #xxxx
@agoric/synthetic-chain library
node_modules
were not excluded so theagops
binary's symlink didn't resolve to a valid file.A successful github action run using these changes:
https://github.com/Agoric/agoric-sdk/actions/runs/13005941935
agoric-sdk
change can be seen here