DevOps Automation

DevOps Automation

Join this online group to communicate across IBM product users and experts by sharing advice and best practices with peers and staying up to date regarding product enhancements.

 View Only

UrbanCode Deploy 10 Minute Tip: An Interactive udclient Session would be Nice

By Laurel Dickson-Bull posted Thu November 12, 2020 08:47 AM

  

The udclient out-of-the-box is a one-shot tool.  It runs a single instruction and then exits.  There is a significant delay for each start-up of the udclient tool as it starts a JVM.  So wouldn't it be nice to be able to use udclient interactively ?

In this article I'm presenting a very simple wrapper script that can be used to invoke multiple udclient commands in a single session.  This can either be from a set of commands stored in a file or by interactively typing commands.  I'm calling this the udShell.

A simple test of 4 udclient commands run individually vs in a single session showed 12 seconds for standalone operation and 6 seconds to run the same commands as one session.

The solution presented here is a simple wrapper that calls the udclient in the same process and thus avoids the overhead of multiple JVM starts.  Since its just a wrapper it should in theory work with any version of the udclient.

There are some simple extras like being about to use the shell notation for variables ${var} and having these replaced with the value of an environment variable, but it would be a simple matter to take the value map from another source for example.

The wrapper is run from a shell taking the same basic option parameters like--weburl, --username and so on from the command line.  The wrapper doesn't process any of these itself, it just passes them on to the udclient.  Each udclient command taken from the input source is prepended with the scripts argument list so you no longer have to specify these on every command.  You just enter a command like:

    createVersion --component newcomp --name 1.0

 If you wanted to do the same thing from a file, you might like to parameterise the input like:

    createVersion --component newcomp --name ${ver}

All the options and environment variables processed by the udclient should work in the same way.  If you don't provide the credentials on the command line or through the environment, you will be prompted for them on every command you type in the session.

This is an example of invoking the udShell interactively

    [root@localhost udclient]# ./udShell.sh --weburl https://localhost:8443 --username admin --password admin

    udShell - udclient Interactive Session

Type ctrl-d to exit

$> createVersion --component newComp --name 34.5

Executing: createVersion --component newComp --name 34.5

{

  "id": "cae7eb67-992f-40fc-82ba-ec030889eb61",

  "name": "34.5",

  "type": "FULL",

  "created": 1604943750145,

  "active": true,

….

$> <Ctrl-d>

udShell complete 

Here is an example script to execute using shell variable replacement to determine the component version to create.

    createVersion --component newComp --name ${ver}

addVersionFiles --component newComp --version ${ver} --base baseDir

addVersionLink --component newComp --version ${ver} --linkName "link to build" --link http://builder.com

addVersionStatus --component newComp --version ${ver} --status Gold

 

and a run of the udShell executing it

    [root@localhost udclient]# export ver=7.8

[root@localhost udclient]# cat cmds | ./udShell.sh --weburl https://localhost:8443 --username admin --password admin

Executing: createVersion --component newComp --name 7.8

{

  "id": "7da64040-2e82-4be8-bf83-928227f77950",

  "name": "7.8",

  "type": "FULL",

  "created": 1604943980927,

  "active": true,

    "properties": []

  }],

  "totalSize": 0,

  "totalCount": 0

}

 

Exit Code = 0

Executing: addVersionFiles --component newComp --version 7.8 --base baseDir

Exit Code = 0

Executing: addVersionLink --component newComp --version 7.8 --linkName "link to build" --link http://builder.com

Operation succeeded.

Exit Code = 0

Executing: addVersionStatus --component newComp --version 7.8 --status Gold

Operation succeeded.

Exit Code = 0

udShell Complete

 

The main difference between running the udShell interactively rather than from a file is that in interactive mode, the udShell won't exit on an error whereas it will if the commands are coming from a file.

This wrapper should make the job of automating many admin tasks easier and also a lot quicker  to execute.  There is potential for the creation of a plugin step to use this to eliminate the step overhead and JVM overhead of running multiple udclient commands in a UCD process.

The Code

Now for the important bit.  You can just copy the code using the button at the end of the listing and paste it into a new file.  Following that is a bash script to run the udShell. 

You will need to setup the GROOVY_HOME environment variable pointing at your groovy install.  You will also need a copy of the udclient.jar file  in your current directory.

    import com.urbancode.ds.client.DeployCLI

import java.util.regex.Matcher

import java.util.regex.Pattern

import java.security.Permission

import org.apache.commons.lang.text.StrSubstitutor 

/*

        udClient Interative Shell

        =========================

        This wrapper script enables you to create an interactive session with the udclient so that you don't have to

        instantiate a new JVM for each command.  This can make a sequence of udclient commands a lot faster and also

        saves you having to resort to the REST API to get the speed. 

        The wrapper can be run with either the input taken from a file or from the users console. 

        This is a pretty simple 'shell' and takes as parameters any of the standard udclient options that preceed an actual

        command function list createVersion.  Internally all of your command line options are passed to the udclient prefixing

        the input that either comes from a file or that is typed in interactively. 

        The input can contain references to shell variables eg

        createVesion --component newComp --name ${ver}

        In which case the ${ver} would be substitued for the value of the ver environment variable. 

        You can terminate the session with ctrl-d. 

*/

 

// Setup a java security manager to stop the udclient being able to terminate the JVM

SystemExitControl exitControl = new SystemExitControl();

exitControl.blockSystemExit();

 

// Decide if we're actually taking typed in input or if its coming from a file

def isInteractive = System.console() != null

if (isInteractive)

{

        println "udShell - udclient Interactive Session"

        println "Type ctrl-d to exit"

        print '$> '

}

 

// Read the input until it is exhasted

System.in.eachLine {String line ->

        try

        {

               // Substitute environment variables

               line = StrSubstitutor.replace(line,System.getenv())

 

               // Parse the line into individual arguments pre-pending the wrappers own argument list

               String[] ags = this.args + parseArgs(line)

 

               // Call the udclient to execute

               println "Executing: ${line}"

               DeployCLI.main(ags)

        }

        catch (Throwable e )

        {

               // here we're catching the udclient's attempt to exit and just reporting its return code before

               // we process the next command from the input stream

                println "Exit Code = ${e.toString()}"

       

               // if we're not running in interactive mode then quit it a command fails

               if ( ! isInteractive && e.toString() != "0" )

               {

                       println "Terminating Script due to error"

                       exitControl.allowSystemExit()

                       System.exit (e.toString().toInteger())

               }

        }

 

        // Output a prompt for the next line if we're actually interactive

        if (isInteractive) print '$> '

}

 

// The input is exhasted, so re-enable the system.exit call so the JVM can terminate

println "udShell Complete"

exitControl.allowSystemExit();

 

String[] parseArgs(String cmdLine)

{

        // just split this into space separated phrases, but honour quotes

        List<String> list = new ArrayList<String>();

        Matcher m = Pattern.compile("([^\"]\\S*|\".+?\")\\s*").matcher(cmdLine);

        while (m.find())

               list.add(m.group(1).replaceAll('"',""))

 

        return list

}

 

// Security manager to catch and handle system.exit requests

public class BlockExitSecurityManager extends SecurityManager

{

        private SecurityManager oldMgr = System.getSecurityManager();

        public static class ExitCapturedException extends SecurityException

        {

               int exitCode;

               ExitCapturedException (int code) { exitCode=code;}

                public String toString()

               {

                       return ("${exitCode}") ;

               }

        }

        public void checkExit(int status)

        {

            super.checkExit(status);

            // We've caught an exit call, raise this exception to allow it to be handled higher up

            throw new ExitCapturedException(status);

        }

        public void checkPermission(Permission perm)

        {

        }

        public SecurityManager getPreviousMgr()

        {

               return oldMgr;

        }

}

 

public class SystemExitControl

{

    public SystemExitControl()

    {

        super();

    }

    public void blockSystemExit()

    {

        // This method sets up a security manager to capture a System.exitr call we use

        // this to capture exits form within the udclient code

        SecurityManager securityManager = new BlockExitSecurityManager();

        System.setSecurityManager(securityManager) ;

    }

    public void allowSystemExit()

    {

        // This method replaces the security manager we setup with a null one to

        // re-enable system.exit so our own wrapper code can exit

        SecurityManager origSecMgr = System.getSecurityManager();

        if ((origSecMgr != null) && (origSecMgr instanceof BlockExitSecurityManager))

        {

               BlockExitSecurityManager smgr = (BlockExitSecurityManager) origSecMgr;

                System.setSecurityManager(smgr.getPreviousMgr());

        }

        else

        {

               System.setSecurityManager(null);

        }

    }

}

 

Copy code

Source for the udShell.sh command

    #!/bin/sh

if [ -n "$GROOVY_HOME" ]; then

    # change the dir to the root of the client directory

    SHELL_NAME="$0"

    SHELL_PATH=`dirname "${SHELL_NAME}"`

   

    if [ "." = "$SHELL_PATH" ]

    then

       SHELL_PATH=`pwd`

    fi

 

    groovycmd="$GROOVY_HOME/bin/groovy"

    jarfile="$SHELL_PATH/udclient.jar"

   

    if [ -r "$jarfile" ]; then

        "$groovycmd" -cp "$jarfile" udShell.groovy "$@"

    else

        echo "Didn't find $jarfile in directory ${SHELL_PATH}"

        exit 1

    fi

else

    echo You must have GROOVY_HOME set in your environment to use the udshell.

    exit 1

fi

 

 Copy code

I hope you will find this article and the udShell a useful addition to your UCD tools environment. 

Would you like to see the udShell expanded?  Please leave your comments below


by Alan Murphy 

Cloud, DevOps, IT Specialist, Cloud Integration Expert Labs

IBM Cloud and Cognitive Software

Alan Murphy/UK/IBM

alan.murphy@uk.ibm.com

 

Alan Murphy is a services consultant who has worked with clients to help them adopt new tools and processes for the last 20 years. UrbanCode Deploy and DevOps has been his focus for the last 5+ years. He also develops tools to assist clients in tool adoption and blogs on an occasional basis.


#UrbanCode
#DevOps
#UrbanCodeDeploy
#10minutetip
0 comments
17 views

Permalink