Welcome to the Inedo Forums! Check out the Forums Guide for help getting started.
If you are experiencing any issues with the forum software, please visit the Contact Form on our website and let us know!
Changing server context in the middle of a script
-
Is there any practical reason why an OtterScript script cannot issue a
for server
call in a job that does not use custom targeting? The limitation seems arbitrary at first glance.Say, for example, as part of my orchestration, I want to run a job against a list of servers, or an environment and for each one of those resolved servers I want to run an isolated step on a common ancillary server, I simply can't.
I can either...
- set the job to target servers by name (or role / env), and lose the ability to switch to the ancillary in the script
- set the job to custom targeting, and lose the ability to easily select against which servers I want to run the main script
For example, I could want to run the following contrived example script against a subset of my machines...
set $hostname = $EnvironmentVariable(COMPUTERNAME); set $macaddr = $PSEval((Get-NetRoute -DestinationPrefix '0.0.0.0/0' | Sort { $_.RouteMetric + $_.ifMetric } | Select -First 1 | Get-NetAdapter).MacAddress); for server 'common-dhcp' { set $nextip = $SHEval(/opt/dhcp/dhcpctl get-next-ip); Exec( FileName: /opt/dhcp/dhcpctl Arguments: set-reservation $macaddr $nextip ); } for server 'common-dc' { Create-File( Name: "X:\known-servers\${hostname}.txt", Text: $macaddr ); }
I could create an input variable of type Text, and manually type the server names into that prompt; then
@Split
and$Trim
that value, and hope I got the names right -- not fun for more than a few Servers.I could create a script-specific role and require that I pre-populate it before running the script. However, I can easily see that leading to mistakes, as the management of the role is separate to the running of the script -- re-running from the History view would be fraught, for example.
I could create an input variable of type List, then compose a separate script, with the sole purpose of peforming some
Rafts_GetRaftItems
/ (some horrid JSON manipulation) /Rafts_CreateOrUpdateRaftItem
to periodically update the list with selectable server names -- but this is borderline insanity.
Otter already has a decent server / role / environment selector -- I just lose access to it if my script needs to
for server
.Assuming there really is a good reason why we can't change context in a targeted script, then could we perhaps have a dedicated Input Variable type (e.g. Object List), which could be automatically populated with servers, roles or environments (with single- or multi-select), so we can emulate the existing, user-friendly targeting in
for server
scripts?(I note there seems to be a specific input variable type for Universal Packages, so I assume the concept of custom controls exists. I'd look at maybe extending this myself, but I don't think custom variable types are an extensible point in the SDK.)
-
Hi @jimbobmcgee,
This restriction is for security purposes, specifically to enable the usecase of Otter enabling end-users to create/edit/run scripts, but not decide where they are run. So the
for server
is locked unless the targeting is set to None.The solution is to indeed add a variable that allows you to select a server... but as you noticed, you'd have to type in a list. We simply ran out of time to bring those over from BuildMaster unfortunately, and this is not really a popular Otter requirement.
But sure it's possible, it's called a
VariableTemplateType
. Here is the code from BuildMaster that you could probably copy/paste into a custom extension.But the issue is that you don't have access to
DB
in the SDK. Kind of a pain, but you could either reference Otter.Core.dll in your Nuget Package, use reflection, call DB directrly, etc.[Category("Infrastructure")] [DisplayName("Servers")] [Description("Servers configured in BuildMaster, optionally filtered by one or more environments")] public sealed class ServerListVariableSource : BuildMasterDynamicListVariableType { [Persistent] [DisplayName("Environment filter")] [PlaceholderText("Any environment")] [Inedo.Web.SuggestableValue(typeof(EnvironmentNameSuggestionProvider))] public string EnvironmentsFilter { get; set; } private class EnvironmentNameSuggestionProvider : ISuggestionProvider { public async Task<IEnumerable<string>> GetSuggestionsAsync(IComponentConfiguration config) => (await DB .Environments_GetEnvironmentsAsync().ConfigureAwait(false)) .Select(e => e.Environment_Name); } public async override Task<IEnumerable<string>> EnumerateListValuesAsync(VariableTemplateContext context) { var values = (await DB.Environments_GetEnvironmentsAndServersAsync(false).ConfigureAwait(false)) .EnvironmentServers_Extended .Where(es => es.Server_Active_Indicator) .Where(es => string.IsNullOrEmpty(this.EnvironmentsFilter) || string.Equals(this.EnvironmentsFilter, es.Environment_Name, StringComparison.OrdinalIgnoreCase)) .Select(es => es.Server_Name) .Distinct(); return values; } public override RichDescription GetDescription() { if (this.EnvironmentsFilter?.Length > 0) return new RichDescription("Servers in ", new ListHilite(this.EnvironmentsFilter), " environments."); else return new RichDescription("Servers in all environments."); } }
It is on our list to "Make Job Template Varible Editor Closer to Pipeline Variable Editor", it's just not trivial and not a huge priority on our products roadmap (https://inedo.com/products/roadmap)
Cheers,
Alana
-
Thanks @atripp -- I can't believe I missed
VariableTemplateType
in the SDK!Thansk for posting the BuildMaster equivalent -- I'll look to see if I can adapt. As you say, the lack of
DB
may make this more challenging.Out of interest, how resilient are extensions to mismatched versions of InedoSdk and/or Otter.Core? Would I have recompile/redistribute an extension each time Otter is updated?
Regarding the security issue, I'm not entirely certain I agree – perhaps, as the sole administrator / runner, my use case is vastly different from others. Agree to disagree
Maybe, in a multi-user scenario, a valid middle ground might be to consider allowing Job authors to limit the servers that can be
for server
'd into, i.e. as a property of the job, rather than cutting off all access tofor server
.Or maybe allowing defining a
for server
privilege on servers / roles / environments themselves, so that certain users are allowed to issue that command in jobs scripts, and resolving the permissions at runtime.(I'm sure you've considered this before.)