使用命令行更新工单

作者:Kyle Rankin

在 2017 年 4 月刊中,我撰写了关于如何使用工单系统作为系统管理员来更好地组织您的任务。在那篇文章中,我简要提到了我已将自己的一些脚本与我的工单系统集成,因此在这里我将讨论一个基本的 bash 脚本,我在几分钟内编写了这个脚本,它可以向 Jira 工单添加评论。尽管我的示例专门用于 Jira 工单系统,但您可以将相同的想法应用于任何允许您通过基于 Web 的 API 与之交互的工单系统。

许多系统管理员不喜欢工单系统的一个原因是,更新和维护工单需要转移注意力并中断他们常规的工作流程。对我而言,工单是构建审计跟踪以表明您已完成任务的好方法,而证明任务已完成的最佳方法之一是将您的工作证明粘贴到工单中的评论中。不幸的是,所有这些复制和粘贴操作都会减慢速度,并使系统管理员不愿意使用命令输出来更新他们的工单。

为了解决使用命令输出来保持工单更新的痛点,我的解决方案是创建一个脚本,我可以将输出通过管道传输到该脚本,并使其显示为我指定的工单中的评论。我的任务经常生成输出到日志文件中,将这些日志文件通过管道传输到脚本并在工单中显示它们真是太好了。我通常像这样使用我的 create_jira_comment 脚本


$ somecommand | create_jira_ticket -t TICKETNAME

我的命令可能像 echo 命令一样简单,也可能更复杂。在某些情况下,我希望将输出包装在工单评论中的代码块内,并传递一个标题来描述代码块是什么,因此我分别添加了 -C-H 选项


$ somecommand | create_jira_ticket -t TICKETNAME -C -H "Here
 ↪is the output from somecommand"

这使得管理员可以非常容易地使用命令输出来更新工单,而无需费力地复制和粘贴页面的输出到工单中。输出显示格式正确,并且当它在代码块中时,工单会以这样一种方式显示它,即有人可以滚动浏览它以阅读整个内容,但它不会填满整个页面。

在我深入了解 Jira 特有的部分之前,让我先介绍一下脚本的更通用部分。首先,脚本的开头部分定义了一些变量,设置了一些默认值,并引入了一个设置文件,这样我就不必将密码硬编码到这个脚本中


#!/bin/bash

JIRA_HOST="somehost.example.com"
JIRA_USER="someuser"
JIRA_PASS="somepass"
# Set the user and password in a settings file
# instead of in the script
. /etc/default/jira_settings

OUTFILE="/tmp/create_jira_comment-$(date +%Y%m%d-%H%M%S)"

接下来,我添加了一个典型的用法函数(像所有优秀的脚本编写者应该做的那样),以便在有人使用我的脚本时语法不正确时输出


# Show usage information
usage() {
  cat >&2 <<EOF
Usage:
  $0 [-h | -t TICKET <-f FILENAME> <-H "Header text">
 ↪<-F "Footer text"> <-C>]

This script adds a comment to a Jira ticket based on command-line
arguments.

OPTIONS:
  -h              Show usage information (this message).
  -t TICKET       The Jira ticket name (ie SA-101)
  -f FILENAME     A file containing content to past in the Jira
comment (or - to read from pipe)
  -H HEADER_TEXT  Text to put at the beginning of the comment
  -F FOOTER_TEXT  Text to put at the end of the comment
  -C              Wrap comment in a {code} tags
EOF
}

正如您在用法输出中看到的那样,该脚本可以接受许多命令行参数。唯一必需的选项是 -t,它指定您要向其添加评论的工单的名称。所有其他选项都是可选的。

当我开始使用这个脚本时,我意识到我经常将命令输出通过管道传输到这个工单中,而 Jira 评论内部的默认格式只是使其成为一大堆文本。 -C 选项会将所有文本包装在 <code> 标签中,使其像代码一样显示并且更易于阅读。一旦我开始将输出包装在适当的格式中,我意识到有时我想要在代码块的上方或下方添加文本,以解释代码块是什么。我在那时添加了 -H-F 参数,这让我可以指定要用作代码块周围的标题或页脚的文本。

脚本的下一部分是我使用标准 getopts 工具收集命令行选项的地方


# Parse Options
while getopts ":t:f:H:F:Ch" flag; do
  case "$flag" in
    h)
      usage
      exit 3
      ;;
    t)
      TICKET="${OPTARG}"
      ;;
    f)
      FILENAME="${OPTARG}"
      ;;
    H)
      HEADER="${OPTARG}"
      ;;
    F)
      FOOTER="${OPTARG}"
      ;;
    C)
      CODETAG='{code}'
      ;;
    \?)
      echo "Invalid option: -$OPTARG"
      exit 1
      ;;
    :)
      echo "Option -$OPTARG requires an argument"
      exit 1
      ;;
  esac
done


# Shift past all parsed arguments
shift $((OPTIND-1))

test -z "$TICKET" && usage && echo "No ticket specified!" && exit 1
test -z "$FILENAME" && FILENAME='-'

关于 getopts 工具,实际上并没有太多要详细说明的。您可以看到我如何处理未定义工单的情况,以及如果用户未设置输入文件时,我如何将默认文件设置为管道输入。

现在我有了所有选项,我可以进行创建 Jira 工单的实际工作了。首先,我需要创建一个以 JSON 格式格式化的文件,以便 JIRA API 可以接受


echo -n -e '{\n  "body": "' > ${OUTFILE}.json
test -z "$HEADER" || echo -n -e "${HEADER}\n" >> ${OUTFILE}.json
test -z "$CODETAG" || echo -n -e "${CODETAG}\n" >> ${OUTFILE}.json
cat ${FILENAME} | perl -pe 's/\r//g; s/\n/\\r\\n/g; s/\"/\\"/g' >>
 ↪${OUTFILE}.json
test -z "$CODETAG" || echo -n -e "\n${CODETAG}" >> ${OUTFILE}.json
test -z "$FOOTER" || echo -n -e "\n${FOOTER}" >> ${OUTFILE}.json
echo -e '"\n}' >> ${OUTFILE}.json

您可以在之前的代码中看到,我测试了是否定义了标题、代码参数或页脚,如果定义了,则将文本或适当的代码标签注入到 JSON 文件中的正确位置。也就是说,格式化的重点是在中间,我在那里 cat 主要输出到一系列 Perl 正则表达式中,以清理输出中的回车符和换行符,并正确转义引号。如果您注意到它破坏了 JSON 格式,这将是您对输出应用任何其他清理的部分。

一旦我有了有效的 JSON 文件,我就可以使用 curl 将其通过 POST 请求发送到 Jira,命令如下


curl -s -S -u $JIRA_USER:$JIRA_PASS -X POST --data @${OUTFILE}.json -H
 ↪"Content-Type: application/json"
 ↪https://$JIRA_HOST/rest/api/latest/issue/${TICKET}/comment
 ↪2>&1 >> $OUTFILE

if [ $? -ne 0 ]; then
  echo "Creating Jira Comment failed"
  exit 1
fi

如果命令失败,我会提醒用户,并且由于我在 $OUTFILE 日志文件中捕获了 curl 输出,我可以查看它以了解哪里出了问题。

这是完整的脚本,全部放在一起


#!/bin/bash

JIRA_HOST="somehost.example.com"
JIRA_USER="someuser"
JIRA_PASS="somepass"
# Set the user and password in a settings file
# instead of in the script
. /etc/default/prod_release

OUTFILE="/tmp/create_jira_comment-$(date +%Y%m%d-%H%M%S)"

# Show usage information
usage() {
  cat >&2 <<EOF
Usage:
  $0 [-h | -t TICKET <-f FILENAME> <-H "Header text">
 ↪<-F "Footer text"> <-C>]

This script adds a comment to a Jira ticket based on
command-line arguments.

OPTIONS:
  -h              Show usage information (this message).
  -t TICKET       The Jira ticket name (ie SA-101)
  -f FILENAME     A file containing content to past in the Jira
 ↪comment (or - to read from pipe)
  -H HEADER_TEXT  Text to put at the beginning of the comment
  -F FOOTER_TEXT  Text to put at the end of the comment
  -C              Wrap comment in a {code} tags
EOF
}

# Parse Options
while getopts ":t:f:H:F:Ch" flag; do
  case "$flag" in
    h)
      usage
      exit 3
      ;;
    t)
      TICKET="${OPTARG}"
      ;;
    f)
      FILENAME="${OPTARG}"
      ;;
    H)
      HEADER="${OPTARG}"
      ;;
    F)
      FOOTER="${OPTARG}"
      ;;
    C)
      CODETAG='{code}'
      ;;
    \?)
      echo "Invalid option: -$OPTARG"
      exit 1
      ;;
    :)
      echo "Option -$OPTARG requires an argument"
      exit 1
      ;;
  esac
done

# Shift past all parsed arguments
shift $((OPTIND-1))

test -z "$TICKET" && usage && echo "No ticket specified!"
 ↪&& exit 1
test -z "$FILENAME" && FILENAME='-'

echo -n -e '{\n  "body": "' > ${OUTFILE}.json
test -z "$HEADER" || echo -n -e "${HEADER}\n" >> ${OUTFILE}.json
test -z "$CODETAG" || echo -n -e "${CODETAG}\n" >> ${OUTFILE}.json
cat ${FILENAME} | perl -pe 's/\r//g; s/\n/\\r\\n/g;
 ↪s/\"/\\"/g' >> ${OUTFILE}.json
test -z "$CODETAG" || echo -n -e "\n${CODETAG}" >> ${OUTFILE}.json
test -z "$FOOTER" || echo -n -e "\n${FOOTER}" >> ${OUTFILE}.json
echo -e '"\n}' >> ${OUTFILE}.json

curl -s -S -u $JIRA_USER:$JIRA_PASS -X POST --data @${OUTFILE}.json -H
 ↪"Content-Type: application/json"
 ↪https://$JIRA_HOST/rest/api/latest/issue/${TICKET}/comment
 ↪2>&1 >> $OUTFILE

if [ $? -ne 0 ]; then
  echo "Creating Jira Comment failed"
  exit 1
fi

我发现我现在一直使用这个脚本与我的工单系统交互。过去,有时我可能会在将工作证明归档到 Jira 工单中时变得有点懒惰,除非我知道这确实必要,但有了这个脚本,它变得很容易,所以我发现我做得更多了。总的来说,我发现如果你能让正确的工作流程成为最容易的工作流程,你的团队就更有可能遵循它。这个脚本只是如何在实践中实现这一目标的一个例子。

Kyle Rankin 是 Linux Journal 的技术编辑和专栏作家,也是 Purism 的首席安全官。他是 Linux Hardening in Hostile Networks, DevOps Troubleshooting, The Official Ubuntu Server Book, Knoppix Hacks, Knoppix Pocket Reference, Linux Multimedia HacksUbuntu Hacks 的作者,也是许多其他 O'Reilly 书籍的贡献者。Rankin 经常在安全和开源软件方面发表演讲,包括在 BsidesLV、O'Reilly Security Conference、OSCON、SCALE、CactusCon、Linux World Expo 和 Penguicon 上。您可以在 @kylerankin 上关注他。

加载 Disqus 评论