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...

    1. set the job to target servers by name (or role / env), and lose the ability to switch to the ancillary in the script
    2. 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.)


  • inedo-engineer

    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 to for 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.)


  • inedo-engineer

    Hi @jimbobmcgee,

    Good question on versioning... to be honest I kind of forgot how assembly binding would work in this particular context 😅

    The Otter.Core assembly version changes in each release. I don't think we have special handling for it, but you could probably add in an assembly resolver for now, until we officially add support. The Job Template / Variables is something we have on our 2025 roadmap for Otter, but other products are priority.

    Security... BuildMaster handles it a little better, and will do runtime checks, and does allow for more flexibility. This serverlock we added in Otter was a stopgap in 2022 (I think?) to resolve a customer's issue. The issue you are encountering is relatively easy to work-around, by reimplementing server targeting using variables. It's also on our roadmap, but you know... priorities :)


Log in to reply
 

Inedo Website HomeSupport HomeCode of ConductForums GuideDocumentation