DeployHubProject / DeployHub-Pro

DeployHub Pro Pipeline Status Project
https://www.openmakesoftware.com/application-release-automation-for-continuous-delivery/
Other
11 stars 4 forks source link

DMScript objects #336

Closed piyush94 closed 3 years ago

piyush94 commented 3 years ago

What all objects are available in the stack during deployment?

Is the current deployment id available?

piyush94 commented 3 years ago

Got it, it's available with object $DEPLOY_ID.

piyush94 commented 3 years ago

Is it possible to know if the current deployment failed or succeeded in the DMScript. We are running a Post Action on Application deployment, and would like to trigger a webhook if deployment is successful.

sbtaylor15 commented 3 years ago

Here is how the stack works - https://docs.deployhub.com/userguide/dmscript/dmscript-stack/

Let me check on the exit code for the deployment step. I need to see when the exit code is pushed into the stack.

piyush94 commented 3 years ago

Thanks.

I guess if the deployment has failed then the Post Action will not run anyway. Is that right?

sbtaylor15 commented 3 years ago

Is the action being run at the Component Level a custom action, ie Helm Deployment? Or is this for file based deployments?

Yes, I believe the Post Action is skipped. Need to double check.

piyush94 commented 3 years ago

It’s a Post Action associated with the Application. It executes a DMScript procedure.

sbtaylor15 commented 3 years ago

You should be able to use $? to check the exit code of the previous task. I double checked the code and looks like the Application Post Task will get run even after a failure for a component deployment. I need to setup a test scenario to verify.

piyush94 commented 3 years ago

In my testing environment, Post Action did not run if any associated component deployment failed in the application.

piyush94 commented 3 years ago

One issue I found in JSON parsing from the REST API response, DMScript is unable to parse decimal numbers,

{
      "_class": "jenkins.metrics.impl.TimeInQueueAction",
      "blockedDurationMillis": 0,
      "blockedTimeMillis": 0,
      "buildableDurationMillis": 0,
      "buildableTimeMillis": 0,
      "buildingDurationMillis": 0,
      "executingTimeMillis": 0,
      "executorUtilization": 1.0,   <-- HERE
      "subTaskCount": 0,
      "waitingDurationMillis": 5763,
      "waitingTimeMillis": 5763
}

Error: syntax error, unexpected '.', expecting ',' or '}'

I tried using to_json https://docs.deployhub.com/userguide/dmscript/dmscript-tojson/, but it did not help.

sbtaylor15 commented 3 years ago

@piyush94 we will need to convert the 1.0 to a string. DMScript does not support floating point numbers. This update should enable the json to be parsed correctly.

sbtaylor15 commented 3 years ago

Here is the fix for the json error docker pull quay.io/deployhub/deployhub-pro:ui-skin-v9.0.0.3136-g35c342d.

Can you please send a copy of the log when the deployment fails? We need to look at the DMScript stack trace if there is one.

piyush94 commented 3 years ago

Stack when a component deployment fails.

Exception at line 0: Custom Action helm_install_action for Component helm_test_comp_01 Failed: Return Code: 1
--
Stacktrace: 
server sb-helm client
dropzone 1
deploy
component helm_test_comp_01
application helm_test_app
avloop
application helm_test_app
environment ustr-rancher-sb
global

This is achieving the result of not executing the Application Post Action. Let me know your thoughts. There might be cases where execution of the rest of the tasks is needed. If the background is ansible, maybe ignore_errors can be used.

piyush94 commented 3 years ago

Here is the fix for the json error docker pull quay.io/deployhub/deployhub-pro:ui-skin-v9.0.0.3136-g35c342d.

Can you please send a copy of the log when the deployment fails? We need to look at the DMScript stack trace if there is one.

When using this image I faced two issues.

  1. When logged in with an LDAP user, deployment fails with the error failed to login with LDAP.
  2. Then I tried with the local user and got this error Runtime error at line 34: Failed to load plugin "restful"

I had upgraded from version, master-v9.0.0.2953-g34c693f

sbtaylor15 commented 3 years ago

I tested the wrong image by accident. Let me re-test deployhub-pro:ui-skin-v9.0.0.3136-g35c342d.

The helm custom action can be modified to catch the bad return code and set a global DMScript exitcode that we can then check from the Application Post Action. The Application Post Action would do the restapi call you need and then exit with non-zero to cause the deployment to fail. Note: we can make this optional at the Helm custom action level so by default acts as is.

sbtaylor15 commented 3 years ago

Here is the corrected image ta for both the base and the reverse proxy: ui-skin-v9.0.0.3138-g3af5090

piyush94 commented 3 years ago

@sbtaylor15 I tried the image, but still facing authentication issue when deploying.

Trying basic bind uid=sohalpiy,dc=uis,dc=unisys,dc=com
Thu May 20 06:52:12 2021: LDAP Login Exception caught - returning [LDAP: error code 49 - 80090308: LdapErr: DSID-0C090453, comment: AcceptSecurityContext error, data 52e, v3839]
Thu May 20 06:52:12 2021: ApiException caught e=Login failed
Thu May 20 06:52:12 2021: {"success":false,"error":"Login failed"}

UI login works fine.

piyush94 commented 3 years ago

Also facing issue with below DMScript procedure. It gets stuck at restful_post.

echo "Application : ${application.name}";

echo "Environment : ${environment.name}";

echo "Deployment exit code: $?";

set DEPLOY_ID = "7713";

echo "Deployment ID: $DEPLOY_ID";

set jenkins_server = "https://my.jenkins-server.com";

if ($DO_PUBLISH = "true") {

    echo "Publishing DeployHub Manifest....";

    echo "Product Style : ${PUBLISH_STYLE}";

    echo "Version : ${PUBLISH_VERSION}";

    echo "Push Manifest Group ID: ${PUSH_MANIFEST_GID}";

    set params = "product=${PUBLISH_STYLE}&deployhubId=$DEPLOY_ID&version=${PUBLISH_VERSION}&pushManifestGroupID=${PUSH_MANIFEST_GID}";

    echo "Params : ${params}";

    set job_url = "${jenkins_server}/job/my_example_job/";

    set cred = getcredential("GLOBAL.my_domain.jenkins-test");

    set res = restful_get("${jenkins_server}/crumbIssuer/api/json", $cred, "cookiejar");

    echo "Crumb res: ${res}";

    set cookies = "JSESSIONID=${cookiejar.JSESSIONID}";

    echo "Cookie jar : ${cookies}";

    set headers={ "Jenkins-Crumb": "${res.crumb}", "Authorization": "Basic ${cred.b64auth}", "Accept": "*/*" };

    set curr_req = "api/json";

    set job_status_res = restful_get("$job_url$curr_req", $cookies, $headers, null);

    set build_number = "${job_status_res.nextBuildNumber}";

    echo "Build number : ${build_number}";

    set curr_req = "buildWithParameters";

    echo "Build URL: $job_url$curr_req?$params";

  // procedure getting stuck here, not proceeding after this
    set res = restful_post("$job_url$curr_req?$params", $cookies, $headers, null);

    sleep(delay: 1);

    set curr_req = "api/json";

    set job_status_res = restful_get("$job_url$curr_req", $cookies, $headers, null);

    sleep(delay: 5);

    while("${job_status_res.inQueue}" = true) {

        echo "In queue...";
        sleep(delay: 1);
        set job_status_res = restful_get("$job_url$curr_req", $cookies, $headers, null);

    }

    set curr_req = "${build_number}/api/json";

    set job_status_res = restful_get("$job_url$curr_req", $cookies, $headers, null);

    while("${job_status_res.building}" = true) {

        echo "Publishing...";
        sleep(delay: 2);
        set job_status_res = restful_get("$job_url$curr_req", $cookies, $headers, null);

    }

    sleep(delay: 2);

    set curr_req = "${build_number}/api/json";

    set job_status_res = restful_get("$job_url$curr_req", $cookies, $headers, null);

    echo "Publish result :  ${job_status_res.result}";

} else {

    echo "DO_PUBLISH is false. Skipping publishing...";

}
sbtaylor15 commented 3 years ago

@sbtaylor15 I tried the image, but still facing authentication issue when deploying.

Trying basic bind uid=sohalpiy,dc=uis,dc=unisys,dc=com
Thu May 20 06:52:12 2021: LDAP Login Exception caught - returning [LDAP: error code 49 - 80090308: LdapErr: DSID-0C090453, comment: AcceptSecurityContext error, data 52e, v3839]
Thu May 20 06:52:12 2021: ApiException caught e=Login failed
Thu May 20 06:52:12 2021: {"success":false,"error":"Login failed"}

UI login works fine.

Where is the deployment being started from, Jenkins or a curl call?

sbtaylor15 commented 3 years ago

Also facing issue with below DMScript procedure. It gets stuck at restful_post.

echo "Application : ${application.name}";

echo "Environment : ${environment.name}";

echo "Deployment exit code: $?";

set DEPLOY_ID = "7713";

echo "Deployment ID: $DEPLOY_ID";

set jenkins_server = "https://my.jenkins-server.com";

if ($DO_PUBLISH = "true") {

  echo "Publishing DeployHub Manifest....";

  echo "Product Style : ${PUBLISH_STYLE}";

  echo "Version : ${PUBLISH_VERSION}";

  echo "Push Manifest Group ID: ${PUSH_MANIFEST_GID}";

  set params = "product=${PUBLISH_STYLE}&deployhubId=$DEPLOY_ID&version=${PUBLISH_VERSION}&pushManifestGroupID=${PUSH_MANIFEST_GID}";

  echo "Params : ${params}";

  set job_url = "${jenkins_server}/job/my_example_job/";

  set cred = getcredential("GLOBAL.my_domain.jenkins-test");

  set res = restful_get("${jenkins_server}/crumbIssuer/api/json", $cred, "cookiejar");

  echo "Crumb res: ${res}";

  set cookies = "JSESSIONID=${cookiejar.JSESSIONID}";

  echo "Cookie jar : ${cookies}";

  set headers={ "Jenkins-Crumb": "${res.crumb}", "Authorization": "Basic ${cred.b64auth}", "Accept": "*/*" };

  set curr_req = "api/json";

  set job_status_res = restful_get("$job_url$curr_req", $cookies, $headers, null);

  set build_number = "${job_status_res.nextBuildNumber}";

  echo "Build number : ${build_number}";

  set curr_req = "buildWithParameters";

  echo "Build URL: $job_url$curr_req?$params";

  // procedure getting stuck here, not proceeding after this
  set res = restful_post("$job_url$curr_req?$params", $cookies, $headers, null);

  sleep(delay: 1);

  set curr_req = "api/json";

  set job_status_res = restful_get("$job_url$curr_req", $cookies, $headers, null);

  sleep(delay: 5);

  while("${job_status_res.inQueue}" = true) {

      echo "In queue...";
      sleep(delay: 1);
      set job_status_res = restful_get("$job_url$curr_req", $cookies, $headers, null);

  }

  set curr_req = "${build_number}/api/json";

  set job_status_res = restful_get("$job_url$curr_req", $cookies, $headers, null);

  while("${job_status_res.building}" = true) {

      echo "Publishing...";
      sleep(delay: 2);
      set job_status_res = restful_get("$job_url$curr_req", $cookies, $headers, null);

  }

  sleep(delay: 2);

  set curr_req = "${build_number}/api/json";

  set job_status_res = restful_get("$job_url$curr_req", $cookies, $headers, null);

  echo "Publish result :  ${job_status_res.result}";

} else {

  echo "DO_PUBLISH is false. Skipping publishing...";

}

Is the restful_post returning a json string? If so, is it the json string have the float in it?

piyush94 commented 3 years ago

@sbtaylor15 I tried the image, but still facing authentication issue when deploying.

Trying basic bind uid=sohalpiy,dc=uis,dc=unisys,dc=com
Thu May 20 06:52:12 2021: LDAP Login Exception caught - returning [LDAP: error code 49 - 80090308: LdapErr: DSID-0C090453, comment: AcceptSecurityContext error, data 52e, v3839]
Thu May 20 06:52:12 2021: ApiException caught e=Login failed
Thu May 20 06:52:12 2021: {"success":false,"error":"Login failed"}

UI login works fine.

Where is the deployment being started from, Jenkins or a curl call?

I am starting the deployment from UI.

piyush94 commented 3 years ago

Also facing issue with below DMScript procedure. It gets stuck at restful_post.

echo "Application : ${application.name}";

echo "Environment : ${environment.name}";

echo "Deployment exit code: $?";

set DEPLOY_ID = "7713";

echo "Deployment ID: $DEPLOY_ID";

set jenkins_server = "https://my.jenkins-server.com";

if ($DO_PUBLISH = "true") {

    echo "Publishing DeployHub Manifest....";

    echo "Product Style : ${PUBLISH_STYLE}";

    echo "Version : ${PUBLISH_VERSION}";

    echo "Push Manifest Group ID: ${PUSH_MANIFEST_GID}";

    set params = "product=${PUBLISH_STYLE}&deployhubId=$DEPLOY_ID&version=${PUBLISH_VERSION}&pushManifestGroupID=${PUSH_MANIFEST_GID}";

    echo "Params : ${params}";

    set job_url = "${jenkins_server}/job/my_example_job/";

    set cred = getcredential("GLOBAL.my_domain.jenkins-test");

    set res = restful_get("${jenkins_server}/crumbIssuer/api/json", $cred, "cookiejar");

    echo "Crumb res: ${res}";

    set cookies = "JSESSIONID=${cookiejar.JSESSIONID}";

    echo "Cookie jar : ${cookies}";

    set headers={ "Jenkins-Crumb": "${res.crumb}", "Authorization": "Basic ${cred.b64auth}", "Accept": "*/*" };

    set curr_req = "api/json";

    set job_status_res = restful_get("$job_url$curr_req", $cookies, $headers, null);

    set build_number = "${job_status_res.nextBuildNumber}";

    echo "Build number : ${build_number}";

    set curr_req = "buildWithParameters";

    echo "Build URL: $job_url$curr_req?$params";

  // procedure getting stuck here, not proceeding after this
    set res = restful_post("$job_url$curr_req?$params", $cookies, $headers, null);

    sleep(delay: 1);

    set curr_req = "api/json";

    set job_status_res = restful_get("$job_url$curr_req", $cookies, $headers, null);

    sleep(delay: 5);

    while("${job_status_res.inQueue}" = true) {

        echo "In queue...";
        sleep(delay: 1);
        set job_status_res = restful_get("$job_url$curr_req", $cookies, $headers, null);

    }

    set curr_req = "${build_number}/api/json";

    set job_status_res = restful_get("$job_url$curr_req", $cookies, $headers, null);

    while("${job_status_res.building}" = true) {

        echo "Publishing...";
        sleep(delay: 2);
        set job_status_res = restful_get("$job_url$curr_req", $cookies, $headers, null);

    }

    sleep(delay: 2);

    set curr_req = "${build_number}/api/json";

    set job_status_res = restful_get("$job_url$curr_req", $cookies, $headers, null);

    echo "Publish result :  ${job_status_res.result}";

} else {

    echo "DO_PUBLISH is false. Skipping publishing...";

}

Is the restful_post returning a json string? If so, is it the json string have the float in it?

Unfortunately, it’s not returning anything. It just stays stuck.

sbtaylor15 commented 3 years ago

Sorry, just mean is the expected data from the restful_post json and if so json with the float?

piyush94 commented 3 years ago

Sorry, just mean is the expected data from the restful_post json and if so json with the float?

For build and buildWithParameters REST call, Jenkins is not returning any body. Just status code 201.

sbtaylor15 commented 3 years ago

Ok, I will test it with an empty body being returned.

sbtaylor15 commented 3 years ago

@piyush94 - We did something similar but in Python and then just passed in the attributes TOML file in (like the Helm Action). It was a bit easier to interact with Jenkins thru the Python library. Let me know if you want to go the Python route or stick with DMScript.

#!/usr/bin/python3

import subprocess
import sys
import time

import jenkins
import qtoml

def runcmd(cmd):
    print(cmd)
    lines = subprocess.run(cmd, check=False, shell=True, stderr=subprocess.STDOUT, stdout=subprocess.PIPE).stdout.decode('utf-8')
    print(lines)

def main():
    """Main entry point <Chart Directory> <Override Values Toml>"""

    newvals = {}
    rspfile = sys.argv[1]

    print("RSP=" + rspfile)
    lines = subprocess.run(['cat', rspfile], check=False, stdout=subprocess.PIPE).stdout.decode('utf-8').split("\n")
#    pprint(lines)

    cleanvals = {}
    for line in lines:
        if ('=' in line):
            key = line.split('=')[0]
            value = line.split('=', 1)[-1].strip()
            if (value.lower() == '"true"' or value.lower() == "'true'"):
                value = "true"
            if (value.lower() == '"false"' or value.lower() == "'false'"):
                value = "false"

            if ('?' not in key):
                cleanvals[key] = value

    valstr = ""
    for key, value in cleanvals.items():
        valstr = valstr + key + " = " + value + "\n"

    values = qtoml.loads(valstr)
    newvals.update(values)

    component = newvals.get("component", "")

    # now create jenkins job
    jenkins_url = newvals.get("jenkins_url", "")
    jenkins_user = newvals.get("jenkins_user", newvals.get('jenkinsuser', '')).strip()
    jenkins_token = newvals.get("jenkins_token", newvals.get('jenkinspass', '')).strip()
    jenkins_job = newvals.get("jenkins_init_repo_job", "")

    server = jenkins.Jenkins(jenkins_url, username=jenkins_user, password=jenkins_token)

    server.build_job(jenkins_job, {'REPO_NAME': component})
    print('Running....')

    while True:
        if server.get_job_info(jenkins_job)['lastCompletedBuild']['number'] == server.get_job_info(jenkins_job)['lastBuild']['number']:
            print("Build Number: " + str(server.get_job_info(jenkins_job)['lastCompletedBuild']['number']))
            break

        time.sleep(3)

    print("Finished....\n")

    console_output = server.get_build_console_output(jenkins_job, server.get_job_info(jenkins_job)['lastBuild']['number'])
    print(console_output)

if __name__ == '__main__':
    main()
piyush94 commented 3 years ago

@sbtaylor15 Thanks for the Python script. It's working as expected. So, will you be adding this as part of the DeployHub image or we can run it externally?

Edit: we can’t run externally as we want this action to be not dependent on the environment. It should run from inside DeployHub.

Also, some Generic attributes will be needed, such as creds["jenkins"], job_params, print_log (true, false), etc.

Will we be able to pass the job params dictionary from attributes? Maybe like job_params.param1=value1.

sbtaylor15 commented 3 years ago

@piyush94 quay.io/deployhub/deployhub-pro:ui-skin-v9.0.0.3150-g4cd689a has the fix for the LDAP deployment auth error and we added the dh2jenkins.py which is a generic Jenkins runner.

Parameters:

jenkins_url
jenkins_user or jenkinsuser
jenkins_token or jenkinspass 
jenkins_job
jenkins_job_params.KEY1 = "VAL1"
jenkins_job_params.KEY2 = "VAL2"
jenkins_log (true or false to display log output)

creds['jenkins'] = 'jenkinsadmin' will pull the 'jenkinsadmin' credential and set the userid and password as jenkinsuser and jenkinspass

piyush94 commented 3 years ago

@sbtaylor15 if possible kindly share the Jenkins update in the old UI.

sbtaylor15 commented 3 years ago

@piyush94 we pushed the JSON float fix and the dh2jenkins.py to the old ui.

main-v9.0.0.2954-g8c7a767

piyush94 commented 3 years ago

@sbtaylor15 Thanks. Do i need to add writeenv2toml procedure before running the jenkins script?

sbtaylor15 commented 3 years ago

Correct, in the action put the WriteEnv2Toml procedure first and then the Jenkins one. The variable name will be $RspFile

piyush94 commented 3 years ago

@sbtaylor15 As I am running the Action as PostAction to an application, the server and component objects are empty. Hence due to the server object being empty dropzone is not getting created. And getting this error,

Creating Values TOML File
RspFile : 
Runtime error at line 91: 'create' requires a dropzone to be specified

As you can see RspFIle is empty.

sbtaylor15 commented 3 years ago

@piyush94 there is a method to create a dropzone object. Let us try that in the context of the PostAction and see if that fixes the error. Otherwise we will need to add a python script that will make a couple of rest calls to get the data and create the toml file.

piyush94 commented 3 years ago

@sbtaylor15 Could we see once if it would be easier to resolve the DMScript issue?

sbtaylor15 commented 3 years ago

@piyush94 - you will need to specify the dropzone explicitly before trying to write the variables to the file.

echo "Creating Values TOML File";
echo "$DEPLOY_LOG";

using dropzone 'tdm_scripts_xxx' {

    set creds = ${application.attributes['creds']};

    foreach(cred: $creds) 
    {
     set credname = ${creds[$cred]};
     set c = getcredential($credname);

     echo $cred + "user = \"" + ${c.username} + "\"";
     echo $cred + "pass = \"" + ${c.password} + "\"";
    }

    set -g RspFile = ${dropzone.path} + "/values.toml";

    echo $RspFile; 
    set d = now();
    set sname = "s" + ${d.to_int()};

    eval("using stream \$$sname;");
    eval("set envstream = \$$sname;");

    using stream $envstream {
    echo "application = \"" + ${application.name} + "\"";

    foreach(env: ${application.attributes}) {  
      echo $env + "= \"" + ${application.attributes[$env]}  + "\"";
    }

    create(file: "values.toml", stream: $envstream);
   }
 }

using dropzone 'tdm_scripts_xxx' { is the line needed (plus the } at the end of the file). This will work for both pre and post actions to the Application.

piyush94 commented 3 years ago

@sbtaylor15 Thanks, this worked. The python-jenkins module was missing from the image, but after installing it the specified job got triggered with parameters.

Traceback (most recent call last):
  File "/opt/deployhub/engine/scripts/dh2jenkins.py", line 7, in 
    import jenkins
ModuleNotFoundError: No module named 'jenkins'

For jenkins_log attribute, this is working for us.

if (jenkins_log):
        print(console_output)
sbtaylor15 commented 3 years ago

@piyush94 which image tag? I am seeing it installed already.

piyush94 commented 3 years ago

This one main-v9.0.0.2954-g8c7a767

sbtaylor15 commented 3 years ago

Here is the updated image. quay.io/deployhub/deployhub-pro:main-v9.0.0.2955-g682117c

piyush94 commented 3 years ago

Hi @sbtaylor15 , Could you please update dh2jenkins with the attached file. This is having an update that allows anonymous users to trigger the Jenkins job. Thanks.

dh2jenkins.txt

sbtaylor15 commented 3 years ago

Added to the latest UI. Let me know if you need for the older one.

quay.io/deployhub/deployhub-pro:ui-skin-v9.0.0.3167-g0341de8

piyush94 commented 3 years ago

Yes, please add in the older one as well.

sbtaylor15 commented 3 years ago

older ui updates

quay.io/deployhub/deployhub-pro:main-v9.0.0.2956-g908e48f