-
Notifications
You must be signed in to change notification settings - Fork 42
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(deadline): add WorkerInstanceConfiguration construct #209
Conversation
0e036b4
to
021ac8f
Compare
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.
Fantastic job factoring out some re-usable logic here without changing any public APIs. I just have some suggestions to refine the new APIs we are introducing here - let me know what you think.
* Interface for Deadline clients that can be configured via the ClientConfiguration | ||
* helper class. | ||
*/ | ||
export interface IConfigurableWorker extends IScriptHost, IGrantable { |
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.
nit: unless I'm missing something, I believe this is redundant since IScriptHost
already extends IGrantable
.
const groups = settings?.groups?.map(val => val.toLowerCase()).join(',') ?? ''; // props.groups ? props.groups.map(val => val.toLowerCase()).join(',') : ''; | ||
const pools = settings?.pools?.map(val => val.toLowerCase()).join(',') ?? ''; // props.pools ? props.pools.map(val => val.toLowerCase()).join(',') : ''; |
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.
nit: these appear to be leftover comments from debugging
export class WorkerConfiguration extends Construct { | ||
constructor(scope: Construct, id: string) { | ||
super(scope, id); | ||
} |
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.
This construct has no logic in its constructor nor any member variables. All of the functionality is in instance methods that generate construct nodes under this construct sub-tree and the user has to call these in the correct order and provide disambiguating IDs.
I notice that in WorkerInstanceFleet
, we have this sequence of calls:
// Updating the user data with installation logs stream.
workerConfig.configureCloudWatchLogStream(this.fleet, id, {
logGroupPrefix: WorkerInstanceFleet.DEFAULT_LOG_GROUP_PREFIX,
...props.logGroupProps,
});
props.renderQueue.configureClientInstance({
host: this.fleet,
});
// Updating the user data with deadline repository installation commands.
workerConfig.configureWorkerSettings(this.fleet, id, props);
That configuration order should be preserved so errors configuring the Worker to connect to the render queue are streamed and the render queue connection is configured before trying to configure the worker, but I wonder if it makes makes sense to configure the Worker to use the render queue as part of this new WorkerConfiguration
construct? It feels like an important missing piece of Worker configuration and we want to abstract knowledge of the correct calling order.
I suspect the decision to use methods was chosen because the IRenderQueue
interface has two configuration methods that rely on knowing whether the Workers are instances or containers and the intention was to keep WorkerConfiguration
agnostic to that. The current implementation is instance-specific anyways since Deadline configuration is done through user data which is not available in containers. You've already provided an example in the PR description of how to implement the IHost
interface, so....
Allow me propose a slight change to bring this all together:
import { IHost } from './host-ref';
import { IRenderQueue } from './render-queue';
interface WorkerInstanceConfigurationProps extends WorkerSettings {
readonly logGroup?: LogGroupFactoryProps;
readonly renderQueue?: IRenderQueue;
readonly workerHost: IHost;
}
export class WorkerInstanceConfiguration extends Construct {
constructor(scope: Construct, id: string, props: WorkerInstanceConfigurationProps) {
super(scope, id);
if (props.logGroup) {
this.configureCloudWatchLogStream(props.workerHost, props.logGroup);
}
if (props.renderQueue) {
props.renderQueue.configureClientInstance({ host: props.workerHost });
}
this.configureWorkerSettings(props.workerHost, props);
}
}
this makes the usage of this construct 1:1 with a Worker, but eliminates the need to specify child IDs, know the best-practice for the order of the method calls, makes the calling code cleaner. It goes from:
const worker: IConfigurableWorker = ...;
const renderQueue: IRenderQueue = ...;
const workerConfiguration = new WorkerConfiguration(this, 'WorkerConfiguration');
workerConfiguration.configureCloudWatchLogStream(worker, 'WorkerCloudWatch', { ... });
renderQueue.configureClientInstance({ host: props.worker });
workerConfiguration.configureWorkerSettings(worker, 'WorkerSettings', { ... });
to:
const workerHost: IHost = ...;
const renderQueue: IRenderQueue = ...;
const workerConfiguration = new WorkerInstanceConfiguration(this, 'WorkerConfiguration', {
workerHost,
renderQueue,
logGroup: ...,
groups: [...],
pools: [...],
region: 'myregion',
});
What do you think?
{ | ||
'Fn::GetAtt': [ | ||
'ConfigWorkerLogGroupWrapperDC3AF2E7', | ||
'LogGroupName', | ||
], | ||
}, |
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 is a technique to avoid baking these dynamically generated logical IDs into our tests. It involves the use of the stack.resolve(...)
. In this case, you could use:
const logGroupName = stack.resolve((config.node.findChild('WorkerLogGroup') as ILogGroup).logGroupName);
same feedback applies below.
# Restart service, if it exists, else restart application | ||
if service --status-all | grep -q 'Deadline 10 Launcher'; then | ||
service deadline10launcher restart | ||
else | ||
DEADLINE_LAUNCHER="$DEADLINE_PATH/deadlinelauncher" | ||
"$DEADLINE_LAUNCHER" -shutdownall | ||
"$DEADLINE_LAUNCHER" | ||
fi |
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 the WorkerInstanceFleet
case, we will now be restarting the launcher and Worker twice at startup. I understand it is necessary to ensure the changes take effect when running this script in isolation, but to avoid this extra restart, we could use OO-inheritance of WorkerConfiguration
, like this:
class WorkerConfiguration {
private readonly applyConfigScript: ScriptAsset;
private readonly setConfigScript: ScriptAsset;
constructor(...) {
super(...);
// Script to restart the launcher
this.applyConfigScript = ScriptAsset.fromPathConvention(...);
this.setConfiguration();
this.applyConfiguration();
}
protected setConfiguration() {
setConfigScript.executeOn(...);
}
protected applyConfiguration() {
// Calls a separate ScriptAsset to restart the launcher
applyConfigScript.executeOn(...);
}
}
and for the worker instance fleet we override the setConfiguration
method:
class WorkerInstanceFleetConfiguration extends WorkerConfiguration {
private readonly configureHealthMonitor: ScriptAsset;
constructor(...) {
super(...);
this.configureHealthMonitor = ScriptAsset.fromPathConvention(...);
}
protected setConfiguration() {
super.setConfiguration();
this.configureHealthMonitor.executeOn(...);
}
}
This assumes my other proposed change of applying the configuration in the constructor. It's a bit of rework, but do you think it's worth it?
packages/aws-rfdk/lib/deadline/scripts/bash/configureWorkerHealthCheck.sh
Outdated
Show resolved
Hide resolved
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.
Approved! Really great job here
This is a construct that provides helper methods for configuring a Deadline Worker in the RFDK context.
7b833a3
to
d35bb16
Compare
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.
Looks good! Thanks Daniel for the changes.
00df40f
to
4ced2e1
Compare
4ced2e1
to
20c4e42
Compare
Fixes: #208
This is a construct that provides helper methods for configuring a Deadline Worker in the RFDK context.
Testing
This was tested by deploying both a WorkerInstanceFleet and a single instance using the WorkerConfiguration construct. Deployments were in the context of the simple app that's in the RFDK docs here -- https://docs.aws.amazon.com/rfdk/latest/guide/what-is-rfdk.html#why-use-the-rfdk . After deployment, I ensured that the setup was correct via the generated CloudWatch logs.
Python test code for the test was:
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license