Skip to content

Commit

Permalink
Improve patchComponent && Add patchCluster scripts
Browse files Browse the repository at this point in the history
Fix broken testFileList creation

Use only curl && Add extra check for tool existence at the pod.
  • Loading branch information
todor-ivanov committed Dec 20, 2024
1 parent 01e0747 commit 3a97843
Show file tree
Hide file tree
Showing 2 changed files with 558 additions and 38 deletions.
251 changes: 251 additions & 0 deletions bin/patchCluster.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
#!/bin/bash

usage()
{
echo -e "\nA simple script to be used for patching all Running backends in"
echo -e "a WMCore central services K8 cluster with a patch based on:"
echo -e " * A list of upstream PRs (the order of applying them matters) or "
echo -e " * A patch file provided or"
echo -e " * A patch created from StdIn"
echo -e ""
echo -e "Usage: ./patchCluster.sh [-n] [-z] [-f <patchFile>] [-p <\"SpaceSepListOfPods\">] [-s <serviceName>] 12077 12120 ..."
echo -e ""
echo -e " -p - Space separated list of pods to be patched (Mind the quotation marks)"
echo -e " -s - Service name whose pods to be patched (if found)"
echo -e " -d - Deployment name whose pods to be patched (if found)"
echo -e " -z - Only zero the code base to the currently deployed tag for the files changed in the patch - no actual patches will be applied"
echo -e " -f - Apply the specified patch file. No multiple files supported. If opt is repeated only the last one will be considered."
echo -e " -n - Do not zero the code base neither from TAG nor from Master branch, just apply the patch"
echo -e ""
echo -e "NOTE: We do not support patching from file and patching from command line simultaneously"
echo -e " If both provided at the command line patching from command line takes precedence"
echo -e ""
echo -e "Examples: \n"
echo -e " ./patchCluster.sh -s reqmgr2 11270 12120 \n"
echo -e " ./patchCluster.sh -p pod/reqmgr2-bcdccd8c6-hsmlj -f /tmp/11270.patch \n"
echo -e " git diff --no-color | ./patchCluster.sh -s reqmgr2 -n \n"
echo -e " curl https://patch-diff.githubusercontent.com/raw/dmwm/WMCore/pull/11270.patch | ./patchCluster.sh -s reqmgr2 \n"

}

# Set defaults
currPods=""
currService=""
currDeployment=""
zeroOnly=false
zeroCodeBase=true
extPatchFile=""
while getopts ":f:p:s:d:znh" opt; do
case ${opt} in
p)
currPods=$OPTARG
;;
s)
currService=$OPTARG
;;
d)
currDeployment=$OPTARG
;;
f)
extPatchFile=$OPTARG
;;
z)
zeroOnly=true
;;
n)
zeroCodeBase=false
;;
h)
usage
exit 0 ;;
\? )
echo -e "\nERROR: Invalid Option: -$OPTARG\n"
usage
exit 1 ;;
: )
echo -e "\nERROR: Invalid Option: -$OPTARG requires an argument\n"
usage
exit 1 ;;
esac
done

# shift to the last parsed option, so we can consume the patchList with a regular shift
shift $(expr $OPTIND - 1 )


# if fd 0 (stdin) is open and refers to a terminal - then we are running the script directly, without a pipe
# if fd 0 (stdin) is open but does not refer to the terminal - then we are running the script through a pipe
if [ -t 0 ] ; then pipe=false; else pipe=true ; fi

patchList=$*

currCluster=`kubectl config get-clusters |grep -v NAME`
nameSpace=dmwm

echo
echo
echo
echo ========================================================
echo "INFO: CLUSTER: $currCluster"
echo --------------------------------------------------------

if $zeroOnly; then
echo "INFO: Only Zeroing the code base No patches will be applied"
echo "INFO: Source files affected to be taken from patches: $patchList"
elif $pipe ;then
echo "INFO: Patching from StdIn"
elif [[ -n $extPatchFile ]]; then
echo "INFO: Patching from file: $extPatchFile"
elif [[ -z $patchList ]] ; then
echo "ERROR: Neither patchList provided nor patchFile nor patching from StdIn"
exit
else
echo "INFO: Applying patches: $patchList"
fi


# -----------------------------------------------------------
# Build patchComponent.sh script command to be executed at the pod:

# NOTE: We do not support patching from file and patching from command line simultaneously
# If both provided at the command line patching from command line takes precedence

podCmdActions="which curl && curl https://raw.githubusercontent.com/dmwm/WMCore/master/bin/patchComponent.sh -o /data/patchComponent.sh && chmod 755 /data/patchComponent.sh "
podCmdOpts=""
patchFile=""
$zeroOnly && podCmdOpts="$podCmdOpts -z"
$zeroCodeBase || podCmdOpts="$podCmdOpts -n"

[[ -n $extPatchFile ]] && { patchFile="/tmp/`basename $extPatchFile`"
podCmdOpts="$podCmdOpts -f $patchFile" ;}

# NOTE: We are about to create the pipeTmp_****.patch file from stdIn here
# And we are about to call `patchComponent.sh` through -f option, but we must
# explicitly break the redirection from the pipe command, because otherwise
# it will be kept through out the call to `patchComponent.sh` and will cause
# the $pipe flag to take precedence over the -f flag inside `patchComponent.sh`
# Then the so sent file will be ignored. What we need to do here is, once we create
# the temporary patch file from stdin (currently redirected through the pipe)
# to open again fd 0 (stdin) and redirect it to a tty terminal.
$pipe && { patchFile="/tmp/pipeTmp_$(id -u).patch"
extPatchFile=$patchFile
podCmdOpts="$podCmdOpts -f $patchFile"
echo "INFO: Creating a temporary patchFile from stdin at: $patchFile"
cat <&0 > $patchFile
exec 0<>/dev/tty
# The flag bellow is just for debugging purposes, never used after the printout
[[ -t 0 ]] && newPipeFlag=false || newPipeFlag=true
echo
echo "DEBUG: newPipeFlag=$newPipeFlag"
}

podCmd="$podCmdActions && sudo /data/patchComponent.sh $podCmdOpts $patchList "
restartCmd="/data/manage restart && sleep 1 && /data/manage status"

echo
echo "DEBUG: podCmd: $podCmd"
echo
# -----------------------------------------------------------

echo --------------------------------------------------------
# First try to find any pod from the service name provided and then extend the list in currPods:
if [[ -n $currService ]]; then
servicePods=`kubectl -n $nameSpace get ep $currService -o=jsonpath='{.subsets[*].addresses[*].ip}' | tr ' ' '\n' | xargs -I % kubectl -n $nameSpace get pods -o=name --field-selector=status.podIP=%`
[[ $? -eq 0 ]] || { echo "WARNING: could not find service: $currService at cluster: $currCluster"; exit ;}

# We need to trim the `pod/` prefix from every pod's name produced with the above command
if [[ -n $servicePods ]] ; then
for pod in $servicePods; do
currPods="$currPods ${pod#pod/}"
done
echo "INFO: Found the following pods for SERVICE: $currService: "
echo "$servicePods "
else
echo "WARNING: No pods found for SERVICE: $currService"
exit
fi
fi

# Second try to find any pod from the deployment name provided and then extend the list in currPods:
if [[ -n $currDeployment ]]; then
deploymentPods=`kubectl get --raw "/apis/apps/v1/namespaces/dmwm/deployments/$currDeployment/scale"|jq -r .status.selector | xargs -I % kubectl -n $nameSpace get pods -o=name --selector=%`
[[ $? -eq 0 ]] || { echo "WARNING: could not find deployment: $currDeployment at cluster: $currCluster"; exit ;}

# We need to trim the `pod/` prefix from every pod's name produced with the above command
if [[ -n $deploymentPods ]] ; then
for pod in $deploymentPods; do
currPods="$currPods ${pod#pod/}"
done
echo "INFO: Found the following pods for DEPLOYMENT: $currDeployment: "
echo "$deploymentPods "
else
echo "WARNING: No pods found for DEPLOYMENT: $currDeployment"
exit
fi
fi



if [[ -n $currPods ]] ; then
echo ========================================================
echo WARNING: We are about to patch backend pods: $currPods
echo WARNING: at k8 CLUSTER: $currCluster !!!!
else
echo ========================================================
echo WARNING: We are about to patch ANY running backend pod
echo WARNING: at k8 CLUSTER: $currCluster !!!!
fi

echo WARNING: Are you sure you want to continue?
echo -n "[y/n](Default n): "
read x && [[ $x =~ (y|yes|Yes|YES) ]] || { echo WARNING: Exit on user request!; exit 101 ;}
echo ========================================================


if [[ -n $currPods ]] ; then
runningPods=$currPods
else
# # check if any service name was actually provided at the command line and if not only then search for all running pods for the whole cluster:
# if [[ -n $currService ]] ; then
# echo "WARNING: Requested to patch: $currService at: $currCluster but NO pods were found."
# echo "WARNING: Will not patch the whole cluster. Nothing to do here, giving up now."
# exit
# else
runningPods=`kubectl get pods --no-headers -o custom-columns=":metadata.name" -n $nameSpace --field-selector=status.phase=Running`
# fi
fi

for pod in $runningPods
do
echo
echo --------------------------------------------------------
echo INFO: $pod:

if $pipe || [[ -n $extPatchFile ]]; then
echo INFO: Copying any external patchFiles provided: $extPatchFile
echo INFO: Executing: kubectl -n $nameSpace cp $extPatchFile $pod:$patchFile
kubectl -n $nameSpace cp $extPatchFile $pod:$patchFile || {
echo ERROR: While copying patch files to pod:$pod
echo ERROR: Skipping it!
continue
}
fi
echo
echo --------------------------------------------------------
echo INFO: Patching the services at pod: $pod:
echo INFO: Executing: kubectl exec -it $pod -n $nameSpace -- /bin/bash -c \"$podCmd\"
kubectl exec -it $pod -n $nameSpace -- /bin/bash -c "$podCmd" || {
echo ERROR: While patching the pod:$pod
echo ERROR: Skipping it!
continue
}
echo
echo --------------------------------------------------------
echo INFO: Restarting the services at pod: $pod:
echo INFO: Executing: kubectl exec $pod -n $nameSpace -- /bin/bash -c \"$restartCmd\"
kubectl exec $pod -n $nameSpace -- /bin/bash -c "$restartCmd" || {
echo ERROR: While restarting the service at pod:$pod
echo ERROR: Skipping it!
continue
}
done
Loading

0 comments on commit 3a97843

Please sign in to comment.