Skip to content

Instantly share code, notes, and snippets.

@AkshayRaj
Last active February 14, 2021 02:14
Show Gist options
  • Save AkshayRaj/8eb473ebeaeeccdd6128f17672d127df to your computer and use it in GitHub Desktop.
Save AkshayRaj/8eb473ebeaeeccdd6128f17672d127df to your computer and use it in GitHub Desktop.
ParenthesesString min number of operations. Also an exhibition of coding style.
#!/bin/bash
set -e
DC6_GATEWAY="prod-stargate-gateway-legacy-1001.va.opower.it"
IAD_GATEWAY="prod-sftp-gateway.iad.opower.it"
function copy_files_in_dc6_to_iad {
dir=$1 #"/opt/jail/home/zok5/Downloads"
files=($(echo "${AD_PASSWORD}" | ssh "${DC6_GATEWAY}" "bash -O extglob -c 'sudo -S -- ls \"${dir}/\"'")) # outermost-() for converting string into array
dir_owner=$(echo "${AD_PASSWORD}" | ssh "${DC6_GATEWAY}" "bash -O extglob -c 'sudo -S -- stat -c '%U' ${dir}'")
dir_group=$(echo "${AD_PASSWORD}" | ssh "${DC6_GATEWAY}" "bash -O extglob -c 'sudo -S -- stat -c '%G' ${dir}'")
# copy file from DC6 to IAD
for file in ${files[@]} ; do
# Copy from DC6_GATEWAY
echo "${AD_PASSWORD}" | ssh "${DC6_GATEWAY}" "bash -O extglob -c 'sudo -S -- cp \"${dir}/${file}\" /home/${USER}/${file}'"
scp ${DC6_GATEWAY}:/home/${USER}/${file} ./${local_staging_dir}/${file}
# Copy to IAD_GATEWAY
scp ./${local_staging_dir}/${file} ${IAD_GATEWAY}:/home/${USER}/${file}
echo "${AD_PASSWORD}" | ssh "${IAD_GATEWAY}" "bash -O extglob -c 'sudo -S -- cp \"/home/${USER}/${file}\" ${dir}/${file}'"
# Sync Permissions, owner and group
echo "${AD_PASSWORD}" | ssh "${IAD_GATEWAY}" "bash -O extglob -c 'sudo -S -- chown '${dir_owner}' ${dir}/${file}'" # Owner of dir will also own files in it
echo "${AD_PASSWORD}" | ssh "${IAD_GATEWAY}" "bash -O extglob -c 'sudo -S -- chgrp '${dir_group}' ${dir}/${file}'" # Group of dir will also grp files in it
permissions=$(echo "${AD_PASSWORD}" | ssh "${DC6_GATEWAY}" "bash -O extglob -c 'sudo -S -- stat -c '%a' ${dir}/${file}'") # Get current perms from DC6
echo "${AD_PASSWORD}" | ssh "${IAD_GATEWAY}" "bash -O extglob -c 'sudo -S -- chmod $permissions ${dir}/${file}'" # Assign same perms in IAD
# remove files from staging directories
echo "${AD_PASSWORD}" | ssh "${DC6_GATEWAY}" "bash -O extglob -c 'sudo -S -- rm /home/${USER}/${file}'"
echo "${AD_PASSWORD}" | ssh "${IAD_GATEWAY}" "bash -O extglob -c 'sudo -S -- rm /home/${USER}/${file}'"
rm ./${local_staging_dir}/${file}
done
}
function identify_dirs_and_start_copying {
#dirs=(/opt/jail/home/zok5/Downloads /opt/jail/home/zok5/shared_files)
dirs=$(curl -s "https://github.va.opower.it/raw/inbound/file-retrieval-configuration/master/config/dc6/prod-client.yml" | grep arrivalUri | grep prod-stargate-gateway | sed 's/.*opower.it//')
for dir in ${dirs[@]} ; do
if [[ "${is_dry_run}" -eq 1 ]]; then
echo $dir
else
echo "WET $dir"
#copy_files_in_dc6_to_iad ${dir} ###################################################################################### Un comment this line to start transfer
fi
done
# Cleanup local storage after all files are copied
rm -rf ${local_staging_dir}
}
## get AD password
if [[ -z ${AD_PASSWORD} ]]; then
read -sp $'AD Password: \n' AD_PASSWORD
else
echo "Using AD_PASSWORD environment variable"
fi
local_staging_dir=tmp_files
if [[ -z ${local_staging_dir} ]]; then
mkdir ${local_staging_dir}
else
echo "Cleaning up local staging directory"
rm -rf ${local_staging_dir}
mkdir ${local_staging_dir}
fi
## Confirmation dialogue
echo "Start the transfer from DC6 to IAD ?"
read -p "(y/n)? [NO will do a dry run] " proceed_with_transfer
if [[ "${proceed_with_transfer}" != "${proceed_with_transfer#[Yy]}" ]]; then
echo "Are you sure you want to start the transfer??"
read -p "(y/n)? " proceed_with_transfer
if [[ "${proceed_with_transfer}" != "${proceed_with_transfer#[Yy]}" ]]; then
echo "Starting to copy outstanding files from DC6_GATEWAY to IAD_GATEWAY.."
is_dry_run=0
identify_dirs_and_start_copying
else
echo "Aborting.."
exit 1
fi
else
echo "Starting the DRY_RUN"
is_dry_run=1
identify_dirs_and_start_copying
fi
#!/bin/bash
set -e
function usage {
cat <<EOF
usage: ${0}
-w wet_run (optional flag; if not provided then defaults to 0)
-c clients (list of client codes separated by space; required)
Eg, to simulate a dry run,
${0} -c glbx util foo
To execute wet run and delete data
${0} -w -c glbx util foo
EOF
}
## List of archive servers taken from
## https://github.va.opower.it/sysops/puppet/blob/production/parameters/toshi.yaml#L1048-L1059
## Only one new-style-archive-host required; so skipping these hosts -
## prod-stargate-sftp-archive-1007.va.opower.it
## prod-stargate-sftp-archive-1008.va.opower.it
ARCHIVE_HOSTS="
dev-stargate-sftp-archive-1001.va.opower.it
test-stargate-sftp-archive-1003.va.opower.it
prod-stargate-sftp-archive-new-1001.va.opower.it
prod-stargate-sftp-archive-1003.va.opower.it
prod-stargate-sftp-archive-1004.va.opower.it
prod-stargate-sftp-archive-1005.va.opower.it
prod-stargate-sftp-archive-1006.va.opower.it
prod-stargate-sftp-archive-1009.va.opower.it
prod-stargate-sftp-archive-3002.on.opower.it"
CLIENT_DIR_LOCATION="/archive/*"
function log_message {
local level=$(echo "${1}" | tr [:lower:] [:upper:])
shift
local message="${@}"
local message=$1
local formatted_date=$(date -u +'%Y-%m-%d %H:%M:%S.%3NZ')
if [[ "${is_dry_run}" -eq 1 ]]
then
local dry_run_notice=" [DRY RUN] "
else
local dry_run_notice=" "
fi
echo "${level} [${formatted_date}]${dry_run_notice}${message}" 1>&2
}
function log_info {
local message="${@}"
log_message "INFO" "${message}"
}
function log_error {
local message="${@}"
log_message "ERROR" "${message}"
}
function delete_files_on_host {
local client=$1
local host=$2
## check if ${client} has non-alphabetic characters,'_' or '-'
if [[ -n $(echo ${client} | tr -d "[a-zA-Z0-9_-]") ]]; then
log_error "Client code ${client} is invalid. Aborting.."
exit 1
fi
## set the value for dir_location
local archive_client_dir_location="${CLIENT_DIR_LOCATION}/${client}"
## Count no of files to be deleted
local no_of_files_to_delete=$(echo "${AD_PASSWORD}" | ssh "${host}" "bash -O extglob -c 'sudo -S -- find \"${archive_client_dir_location}\" -print | egrep -v \"(Your password will expire|No such file or directory|^$)\" | wc -l'")
## Delete data
log_info "Deleting data for ${client} on ${host}"
if [[ "${is_dry_run}" -eq 1 ]]; then
log_info "Would have deleted $no_of_files_to_delete files and/or directories from ${archive_client_dir_location}"
else
echo "${AD_PASSWORD}" | ssh "${host}" 'bash -O extglob -c "sudo -S -- rm -rf ${archive_client_dir_location}"'
log_info "Deleted ${archive_client_dir_location}"
fi
}
function loop_over_hosts_for_deletion {
for client in ${clients[@]} ; do
for host in ${ARCHIVE_HOSTS} ; do
delete_files_on_host ${client} ${host}
done
done
}
## SCRIPT EXECUTION STARTS HERE -
## get AD password
if [[ -z ${AD_PASSWORD} ]]; then
read -sp $'AD Password: \n' AD_PASSWORD
else
log_info "Using AD_PASSWORD environment variable"
fi
## default to a dry run
is_dry_run=1
if [ $# -eq 0 ]; then
log_error "No arguments supplied !!"
usage
exit 1
fi
## parse options passed to the script.
while getopts "wc:" flag; do
case $flag in
w ) is_dry_run=0
;;
c ) clients=("$OPTARG")
until [[ $(eval "echo \${$OPTIND}") =~ ^-.* ]] || [[ -z $(eval "echo \${$OPTIND}") ]]; do
clients+=($(eval "echo \${$OPTIND}"))
OPTIND=$((OPTIND + 1))
done
;;
?) echo "Invalid option: -$OPTARG" >&2
usage
exit 1
;;
esac
done
## show confirm wet_run dialogue
if [[ "${is_dry_run}" -eq 1 ]]; then
log_info "THIS IS A DRY RUN "
log_info "Data will be deleted in archive for the following clients -"
for client in "${clients[@]}"; do
echo "- ${client}"
done
loop_over_hosts_for_deletion
else
log_info "THIS IS A WET RUN !!"
log_info "Delete data in archive for the following clients -"
for client in "${clients[@]}"; do
echo "- ${client}"
done
read -p "(y/n)? " proceed_with_delete
if [[ "${proceed_with_delete}" != "${proceed_with_delete#[Yy]}" ]]; then
echo "Deleting client data now.."
loop_over_hosts_for_deletion
else
echo "Aborting.."
exit 1
fi
fi
public class ValidParentheses {
private static final Character OPEN = '(';
private static final Character CLOSED = ')';
/**
* This function calculates the minimum number of operations it would take, to make a
* string containing only parentheses - '(', ')' "valid".
*
* A valid parentheses string has matching set of open & closed parentheses.
*
* In this context, an operation can be either of the two things -
* 1. Adding an open parentheses to the string
* 2. Adding a closed parentheses to the string
* The parentheses could be placed anywhere within the string, such as to make it valid.
*
* @param parenthesesString the string to validate
* @return the minimum number of operations it would take to make the string valid again.
*
* ### Big-O complexity
* - Time : O(n) -> n is the length of the string
* - Space : O(1) -> constant space allocation
*/
static int minOperations(String parenthesesString) {
int stringLength = parenthesesString.length();
// ### APPROACH :
// To find min number of operations required to "validate" a parentheses string,
// we have to find the following two numbers -
// 1. number of unmatched open parentheses
// 2. number of unmatched closed parentheses
/*
* 1. Find Unmatched Open Parentheses
* -> Iterate from "end of the string" ~> "start of the string"
* -> For each count of open parentheses, we increment the count of `openParentheses`
* -> For each count of closed parentheses, we decrement the count of `openParentheses`
* -> Anytime the count of `openParentheses` goes above 0,
* : we increment the count of `unmatchedOpenParentheses
* : reset `openParentheses` to 0
*/
int openParentheses = 0;
int unmatchedOpenParentheses = 0;
for (int charIndex = stringLength - 1; charIndex >= 0; charIndex--) {
Character parentheses = parenthesesString.charAt(charIndex);
if (parentheses.equals(OPEN)) {
openParentheses++;
if (openParentheses > 0) {
unmatchedOpenParentheses++;
openParentheses = 0;
}
}
else if (parentheses.equals(CLOSED)) {
openParentheses--;
}
}
/*
* 2. Find Unmatched Closing Parentheses
* -> Iterate from "start of the string" ~> "end of the string"
* -> For each count of closed parentheses, we increment the count of `closedParentheses`
* -> For each count of open parentheses, we decrement the count of `closedParentheses`
* -> Anytime the count of `closedParentheses` goes above 0,
* : we increment the count of `unmatchedClosedParentheses
* : reset `closedParentheses` to 0
*/
int closedParentheses = 0;
int unmatchedClosedParentheses = 0;
for (int charIndex = 0; charIndex < stringLength; charIndex++) {
Character parentheses = parenthesesString.charAt(charIndex);
if (parentheses.equals(CLOSED)) {
closedParentheses++;
if (closedParentheses > 0) {
unmatchedClosedParentheses++;
closedParentheses = 0;
}
}
else if (parentheses.equals(OPEN)) {
closedParentheses--;
}
}
return unmatchedOpenParentheses + unmatchedClosedParentheses;
}
public static void main(String[] args) {
UnitTest.assertEquals(2, minOperations( ")("));
UnitTest.assertEquals(0, minOperations( "()"));
UnitTest.assertEquals(4, minOperations("))(("));
UnitTest.assertEquals(0, minOperations("()()"));
UnitTest.assertEquals(0, minOperations("(())"));
UnitTest.assertEquals(4, minOperations(")())(("));
UnitTest.assertEquals(5, minOperations(")))(("));
}
static class UnitTest {
public static void assertEquals(int expected, int actual) {
if (expected != actual) {
throw new RuntimeException(String.format("Expected value [%d] is not equal to actual value[%d].", expected, actual));
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment