121 |
122 |
123 |
124 |
125 |
126 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
--------------------------------------------------------------------------------
/0x02-shell_redirections/21-reverse:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | rev
3 |
--------------------------------------------------------------------------------
/0x02-shell_redirections/22-users_and_homes:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | cut -d":" --fields=1,6 /etc/passwd | sort
3 |
--------------------------------------------------------------------------------
/0x02-shell_redirections/3-twofiles:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | cat /etc/passwd /etc/hosts
3 |
--------------------------------------------------------------------------------
/0x02-shell_redirections/4-lastlines:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | tail /etc/passwd
3 |
--------------------------------------------------------------------------------
/0x02-shell_redirections/5-firstlines:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | head /etc/passwd
3 |
--------------------------------------------------------------------------------
/0x02-shell_redirections/6-third_line:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | head -n 3 iacta | tail -n 1
3 |
--------------------------------------------------------------------------------
/0x02-shell_redirections/7-file:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | echo "Best School" | cat > '\*\\'\''"Best School"\'\''\\*$\?\*\*\*\*\*:)'
3 |
--------------------------------------------------------------------------------
/0x02-shell_redirections/8-cwd_state:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | ls -la > ls_cwd_content
3 |
--------------------------------------------------------------------------------
/0x02-shell_redirections/9-duplicate_last_line:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | tail -n 1 iacta | cat >> iacta
3 |
--------------------------------------------------------------------------------
/0x02-shell_redirections/README.md:
--------------------------------------------------------------------------------
1 | Shell, I/O Redirections and filters
2 | What do the commands head, tail, find, wc, sort, uniq, grep, tr do
3 | How to redirect standard output to a file
4 | How to get standard input from a file instead of the keyboard
5 | How to send the output from one program to the input of another program
6 | How to combine commands and filters with redirections
7 |
8 | Special Characters
9 |
10 | What are special characters
11 | Understand what do the white spaces, single quotes, double quotes, backslash, comment, pipe, command separator, tilde and how and when to use them
12 |
13 | Other Man Pages
14 |
15 | How to display a line of text
16 | How to concatenate files and print on the standard output
17 | How to reverse a string
18 | How to remove sections from each line of files
19 | What is the /etc/passwd file and what is its format
20 | What is the /etc/shadow file and what is its format
21 |
22 | Requirements
23 | General
24 |
25 | Allowed editors: vi, vim, emacs
26 | All your scripts will be tested on Ubuntu 14.04 LTS
27 | All your scripts should be exactly two lines long ($ wc -l file should print 2)
28 | All your files should end with a new line (why?)
29 | The first line of all your files should be exactly #!/bin/bash
30 | A README.md file, at the root of the folder of the project, describing what each script is doing
31 | You are not allowed to use backticks, &&, || or ;
32 | All your files must be executable
33 | You are not allowed to use sed or awk
34 |
--------------------------------------------------------------------------------
/0x02-shell_redirections/\*\\'"Best School"\'\\*$\?\*\*\*\*\*:):
--------------------------------------------------------------------------------
1 | Best School
2 |
--------------------------------------------------------------------------------
/0x02-shell_redirections/iacta:
--------------------------------------------------------------------------------
1 | Alea iacta est ("The die is cast") is a Latin phrase attributed by Suetonius
2 | (as iacta alea est) to Julius Caesar on January 10, 49 BC
3 | as he led his army across the Rubicon river in Northern Italy. With this step,
4 | he entered Italy at the head of his army in defiance of the Senate and began
5 | his long civil war against Pompey and the Optimates. The phrase has been
6 | adopted in Italian (Il dado è tratto), Romanian (Zarurile au fost aruncate),
7 | Spanish (La suerte está echada), French (Les dés sont jetés), Portuguese (A
8 | sorte está lançada), Dutch (De teerling is geworpen),
9 | German (Der Würfel ist gefallen), Hungarian (A kocka el van vetve) and many other languages to
10 | indicate that events have passed a point of no return.
11 |
12 | Read more: https://en.wikipedia.org/wiki/Alea_iacta_est
13 | julien@ubuntu:/tmp/h$ ./9-duplicate_last_line
14 | julien@ubuntu:/tmp/h$ cat iacta
15 | Alea iacta est
16 |
17 | Alea iacta est ("The die is cast") is a Latin phrase attributed by Suetonius
18 | (as iacta alea est) to Julius Caesar on January 10, 49 BC
19 | as he led his army across the Rubicon river in Northern Italy. With this step,
20 | he entered Italy at the head of his army in defiance of the Senate and began
21 | his long civil war against Pompey and the Optimates. The phrase has been
22 | adopted in Italian (Il dado è tratto), Romanian (Zarurile au fost aruncate),
23 | Spanish (La suerte está echada), French (Les dés sont jetés), Portuguese (A
24 | sorte está lançada), Dutch (De teerling is geworpen),
25 | German (Der Würfel ist gefallen), Hungarian (A kocka el van vetve) and many other languages to
26 | indicate that events have passed a point of no return.
27 |
28 | Read more: https://en.wikipedia.org/wiki/Alea_iacta_est
29 | Read more: https://en.wikipedia.org/wiki/Alea_iacta_est
30 | Read more: https://en.wikipedia.org/wiki/Alea_iacta_est
31 |
--------------------------------------------------------------------------------
/0x02-shell_redirections/ls_cwd_content:
--------------------------------------------------------------------------------
1 | total 116
2 | drwxr-xr-x 2 root root 4096 Mar 8 00:38 .
3 | drwxr-xr-x 6 root root 121 Mar 7 23:26 ..
4 | -rwxr--r-- 1 root root 30 Mar 8 00:24 0-hello_world
5 | -rw-r--r-- 1 root root 104 Mar 8 00:21 101-gifs
6 | -rw-r--r-- 1 root root 50 Mar 8 00:21 102-acrostic
7 | -rw-r--r-- 1 root root 92 Mar 8 00:21 103-the_biggest_fan
8 | -rw-r--r-- 1 root root 48 Mar 8 00:21 10-no_more_js
9 | -rw-r--r-- 1 root root 47 Mar 8 00:21 11-directories
10 | -rw-r--r-- 1 root root 25 Mar 8 00:21 12-newest_files
11 | -rw-r--r-- 1 root root 27 Mar 8 00:21 13-unique
12 | -rw-r--r-- 1 root root 34 Mar 8 00:21 14-findthatword
13 | -rw-r--r-- 1 root root 36 Mar 8 00:21 15-countthatword
14 | -rw-r--r-- 1 root root 47 Mar 8 00:21 16-whatsnext
15 | -rw-r--r-- 1 root root 36 Mar 8 00:21 17-hidethisword
16 | -rw-r--r-- 1 root root 52 Mar 8 00:21 18-letteronly
17 | -rw-r--r-- 1 root root 25 Mar 8 00:21 19-AZ
18 | -rwxr--r-- 1 root root 29 Mar 8 00:28 1-confused_smiley
19 | -rw-r--r-- 1 root root 23 Mar 8 00:21 20-hiago
20 | -rw-r--r-- 1 root root 16 Mar 8 00:21 21-reverse
21 | -rw-r--r-- 1 root root 54 Mar 8 00:21 22-users_and_homes
22 | -rwxr--r-- 1 root root 28 Mar 8 00:21 2-hellofile
23 | -rwxr--r-- 1 root root 39 Mar 8 00:21 3-twofiles
24 | -rwxr--r-- 1 root root 29 Mar 8 00:21 4-lastlines
25 | -rwxr--r-- 1 root root 29 Mar 8 00:21 5-firstlines
26 | -rwxr--r-- 1 root root 40 Mar 8 00:21 6-third_line
27 | -rwxr--r-- 1 root root 86 Mar 8 00:21 7-file
28 | -rwxr--r-- 1 root root 36 Mar 8 00:21 8-cwd_state
29 | -rw-r--r-- 1 root root 43 Mar 8 00:21 9-duplicate_last_line
30 | -rw-r--r-- 1 root root 12 Mar 8 00:38 \*\\'"Best School"\'\\*$\?\*\*\*\*\*:)
31 | -rw-r--r-- 1 root root 0 Mar 8 00:38 ls_cwd_content
32 | -rw-r--r-- 1 root root 1416 Mar 8 00:21 README.md
33 |
--------------------------------------------------------------------------------
/0x03-shell_variables_expansions/.4-global_variables.swp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/teddy004/alx-system_engineering-devops/cea8c3f89c541452750d9911499811aa85fc06d7/0x03-shell_variables_expansions/.4-global_variables.swp
--------------------------------------------------------------------------------
/0x03-shell_variables_expansions/0-alias:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | alias ls="rm *"
3 |
--------------------------------------------------------------------------------
/0x03-shell_variables_expansions/1-hello_you:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | echo hello $USER
3 |
--------------------------------------------------------------------------------
/0x03-shell_variables_expansions/10-love_exponent_breath:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | echo $(($BREATH**$LOVE))
3 |
--------------------------------------------------------------------------------
/0x03-shell_variables_expansions/100-decimal_to_hexadecimal:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | printf "%x\n" $DECIMAL
3 |
--------------------------------------------------------------------------------
/0x03-shell_variables_expansions/101-rot13:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | tr 'A-Za-z' 'N-ZA-Mn-za-m'
3 |
--------------------------------------------------------------------------------
/0x03-shell_variables_expansions/102-odd:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | paste -d" " - - | cut -d " " -f 1
3 |
--------------------------------------------------------------------------------
/0x03-shell_variables_expansions/103-water_and_stir:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | printf '%o\n' $(( 5#$( echo $WATER | tr water 01234) + 5#$( echo $STIR | tr stir. 01234 ) )) | tr 01234567 bestchol
3 |
--------------------------------------------------------------------------------
/0x03-shell_variables_expansions/11-binary_to_decimal:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | echo $((2#$BINARY))
3 |
--------------------------------------------------------------------------------
/0x03-shell_variables_expansions/12-combinations:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | echo {a..z}{a..z} | tr ' ' '\n' | grep -v oo
3 |
--------------------------------------------------------------------------------
/0x03-shell_variables_expansions/13-print_float:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | printf "%0.2f\n" $NUM
3 |
--------------------------------------------------------------------------------
/0x03-shell_variables_expansions/2-path:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | PATH=$PATH:/action
3 |
--------------------------------------------------------------------------------
/0x03-shell_variables_expansions/3-paths:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | echo $PATH | tr ":" "\n" | wc -l
3 |
--------------------------------------------------------------------------------
/0x03-shell_variables_expansions/4-global_variables:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | printenv
3 |
--------------------------------------------------------------------------------
/0x03-shell_variables_expansions/5-local_variables:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set
3 |
--------------------------------------------------------------------------------
/0x03-shell_variables_expansions/6-create_local_variable:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | BEST=School
3 |
--------------------------------------------------------------------------------
/0x03-shell_variables_expansions/7-create_global_variable:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | export BEST=School
3 |
--------------------------------------------------------------------------------
/0x03-shell_variables_expansions/8-true_knowledge:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | echo $(($TRUEKNOWLEDGE+128))
3 |
--------------------------------------------------------------------------------
/0x03-shell_variables_expansions/9-divide_and_rule:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | echo $(($POWER/$DIVIDE))
3 |
--------------------------------------------------------------------------------
/0x03-shell_variables_expansions/README.md:
--------------------------------------------------------------------------------
1 | Shell Initialization Files
2 |
3 | What are the /etc/profile file and the /etc/profile.d directory
4 | What is the ~/.bashrc file
5 | Variables
6 |
7 | What is the difference between a local and a global variable
8 | What is a reserved variable
9 | How to create, update and delete shell variables
10 | What are the roles of the following reserved variables: HOME, PATH, PS1
11 | What are special parameters
12 | What is the special parameter $??
13 | Expansions
14 |
15 | What is expansion and how to use them
16 | What is the difference between single and double quotes and how to use them properly
17 | How to do command substitution with $() and backticks
18 | Shell Arithmetic
19 |
20 | How to perform arithmetic operations with the shell
21 |
22 | The alias Command
23 |
24 | How to create an alias
25 | How to list aliases
26 | How to temporarily disable an alias
27 |
28 | Requirements
29 | General
30 |
31 | Allowed editors: vi, vim, emacs
32 | All your scripts will be tested on Ubuntu 20.04 LTS
33 | All your scripts should be exactly two lines long ($ wc -l file should print 2)
34 | All your files should end with a new line (why?)
35 | The first line of all your files should be exactly #!/bin/bash
36 | A README.md file, at the root of the folder of the project, describing what each script is doing
37 | You are not allowed to use &&, || or ;
38 | You are not allowed to use bc, sed or awk
39 | All your files must be executable
40 |
41 |
--------------------------------------------------------------------------------
/0x03-shell_variables_expansions/quote:
--------------------------------------------------------------------------------
1 | "Everyone is a proponent of strong encryption."
2 | - Dorothy E. Denning
3 |
--------------------------------------------------------------------------------
/0x04-loops_conditions_and_parsing/.101-tell_the_story_of_passwd.swp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/teddy004/alx-system_engineering-devops/cea8c3f89c541452750d9911499811aa85fc06d7/0x04-loops_conditions_and_parsing/.101-tell_the_story_of_passwd.swp
--------------------------------------------------------------------------------
/0x04-loops_conditions_and_parsing/0-RSA_public_key.pub:
--------------------------------------------------------------------------------
1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC/lAoAVjSPhTUUkWd2KSQqMO0ur9o+nGGzjlVxb0BaBWa6p2sge75Ga/0lt2sBAJSw+tCtsMq6vqVqlWjLd91FVQlCVLaQ6kJN13Btu6w0Low21Wjyt/4IBRoZ9Zt+REwolbwLHp5oEqzup03sAaLbXTplztsSNv/fRf43flU+H6oqaDQx9nyzCxwhz2CTxTbMve59kCsts8DipqwOumgEBghWBsiSbo6LljbBpAazn57YDs7ijeox/kr5lOhnPKiFTH7GMvwCdXTYhTSBrwAhWhSgCt2Mb6d/bfuGHOqi35sNQUEj3pav61aDreifxwaKf65fWTnME6koD5VnhfNbImCYg9b5qamOKSP2Hvvcfimmqg14uD/tc42Xf5BMtenAXKKQ+UBd9yOdwGKxwTyEfbzTTX72VPkZEltazmaVoJLUjBJ84JyUObsmXwJei3fkjDlwxq+D9HJw9U5bzX8DDUWwxg8/xsE7kUq7qDdzkHtXz22bjfjAJWzVqkkQakE= root@1098f71136ac
2 |
--------------------------------------------------------------------------------
/0x04-loops_conditions_and_parsing/1-for_best_school:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # This script is displaying "Best School" 10 times
3 |
4 | for ((i = 1; i <= 10; i++)); do
5 | echo "Best School"
6 | done
7 |
--------------------------------------------------------------------------------
/0x04-loops_conditions_and_parsing/10-fizzbuzz:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # displays numbers from 1 to 100 FizzBuzz, Fizz, Buzz
3 |
4 | for i in {1..100}
5 | do
6 | if ((i % 15 == 0 ))
7 | then
8 | echo "FizzBuzz"
9 | elif ((i % 3 == 0 ))
10 | then
11 | echo "Fizz"
12 | elif ((i % 5 == 0 ))
13 | then
14 | echo "Buzz"
15 | else echo "$i"
16 | fi
17 | done
18 |
--------------------------------------------------------------------------------
/0x04-loops_conditions_and_parsing/100-read_and_cut:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # this script displays the content of the file /etc/passwd
3 |
4 | while x=":" read -r FILE
5 | do
6 | echo "$FILE" | cut -d":" -f1,3,6
7 | done /var/run/myscript.pid
5 | trap 'echo "I hate the kill command"; rm /var/run/myscript.pid; exit 0;' SIGTERM
6 | trap 'echo "Y U no love me?!"' SIGINT
7 | trap 'rm /var/run/myscript.pid; exit 0;' SIGQUIT
8 | while [ $true ]; do
9 | echo 'To infinity and beyond'
10 | sleep 2
11 | done;
12 |
--------------------------------------------------------------------------------
/0x05-processes_and_signals/101-manage_my_process:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # Manages a daemon
3 | if [[ "$#" == "1" ]]; then
4 | if [[ "$1" == "start" ]]; then
5 | ./manage_my_process &
6 | echo "$!" > /var/run/my_process.pid
7 | echo "manage_my_process started"
8 | elif [[ "$1" == "stop" ]]; then
9 | if test -f "/var/run/my_process.pid"; then
10 | kill "$(cat /var/run/my_process.pid)"
11 | fi
12 | rm /var/run/my_process.pid
13 | echo "manage_my_process stopped"
14 | elif [[ "$1" == "restart" ]]; then
15 | # stop program
16 | if test -f "/var/run/my_process.pid"; then
17 | kill "$(cat /var/run/my_process.pid)"
18 | fi
19 | # start program
20 | ./manage_my_process &
21 | echo "$!" > /var/run/my_process.pid
22 | echo "manage_my_process restarted"
23 | else
24 | echo "Usage: manage_my_process {start|stop|restart}"
25 | fi
26 | else
27 | echo "Usage: manage_my_process {start|stop|restart}"
28 | fi
29 |
--------------------------------------------------------------------------------
/0x05-processes_and_signals/101-manage_my_process,:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # manager for manage_my_process
3 | case $1 in
4 | "start")
5 | ./manage_my_process &
6 | echo $! > /var/run/my_process.pid
7 | echo 'manage_my_process started'
8 | ;;
9 | "restart")
10 | id=$(cat /var/run/my_process.pid)
11 | kill -s SIGSTOP $id
12 | rm /var/run/my_process.pid
13 | ./manage_my_process &
14 | echo $! > /var/run/my_process.pid
15 | echo 'manage_my_process restarted'
16 | ;;
17 | "stop")
18 | id=$(cat /var/run/my_process.pid)
19 | kill -s SIGSTOP $id
20 | rm /var/run/my_process.pid
21 | echo 'manage_my_process stopped'
22 | ;;
23 | *)
24 | echo 'Usage: manage_my_process {start|stop|restart}'
25 | ;;
26 | esac
27 |
--------------------------------------------------------------------------------
/0x05-processes_and_signals/102-zombie.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | /**
5 | * infinite_while - given fuction for the created zombie
6 | * Return: 0 if success
7 | */
8 | int infinite_while(void)
9 | {
10 | while (1)
11 | {
12 | sleep(1);
13 | }
14 | return (0);
15 | }
16 | /**
17 | * main - function that created 5 zombies processes
18 | * Return: 0 if success
19 | */
20 | int main(void)
21 | {
22 | int process;
23 | pid_t zombie_pid;
24 |
25 | process = 0;
26 | while (process < 5)
27 | {
28 | zombie_pid = fork();
29 | if (zombie_pid > 0)
30 | {
31 | printf("Zombie process created, PID: %d\n", zombie_pid);
32 | }
33 | else
34 | exit(0);
35 | process++;
36 | }
37 | infinite_while();
38 | return (0);
39 | }
40 |
--------------------------------------------------------------------------------
/0x05-processes_and_signals/2-show_your_bash_pid:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # script that displays lines containing the bash word, getting PID of bash proc
3 | # shellcheck disable=SC2009
4 | ps -faux | grep bash
5 |
--------------------------------------------------------------------------------
/0x05-processes_and_signals/3-show_your_bash_pid_made_easy:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # cript that displays the PID, along with the process name
3 | pgrep -l bash
4 |
--------------------------------------------------------------------------------
/0x05-processes_and_signals/4-to_infinity_and_beyond:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # Bash script that displays To infinity and beyond indefinitely
3 | while true
4 | do
5 | echo "To infinity and beyond"
6 | sleep 2
7 | done
8 |
--------------------------------------------------------------------------------
/0x05-processes_and_signals/5-dont_stop_me_now:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # Bash script that kills 4-to_infinity_and_beyond
3 | kill "$(pgrep -f "4-to_infinity_and_beyond")"
4 |
--------------------------------------------------------------------------------
/0x05-processes_and_signals/6-stop_me_if_you_can:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # script that stops 4-to_infinity_and_beyond process
3 | pkill -f 4-to_infinity_and_beyond
4 |
--------------------------------------------------------------------------------
/0x05-processes_and_signals/7-highlander:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # script that displays To inifity... and I am invisible !!!
3 | trap "echo I am invincible!!!" SIGTERM
4 | i=1
5 | while [ $i -lt 2 ]
6 | do
7 | echo "To infinity and beyond"
8 | sleep 2
9 | done
10 |
--------------------------------------------------------------------------------
/0x05-processes_and_signals/8-beheaded_process:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # script that kills the process 7-highlander
3 | pkill -SIGKILL -f 7-highlander
4 |
--------------------------------------------------------------------------------
/0x05-processes_and_signals/README.md:
--------------------------------------------------------------------------------
1 | # 05. Processes and signals
2 |
3 | General
4 |
5 | What is a PID
6 | What is a process
7 | How to find a process’ PID
8 | How to kill a process
9 | What is a signal
10 | What are the 2 signals that cannot be ignored
11 |
12 |
--------------------------------------------------------------------------------
/0x05-processes_and_signals/manage_my_process:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # Writes a string to a file indefinitely
3 | while [[ 1 -eq 1 ]]; do
4 | echo "I am alive!" >> /tmp/my_process
5 | sleep 2
6 | done
7 |
--------------------------------------------------------------------------------
/0x06-regular_expressions/0-simply_match_school.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | puts ARGV[0].scan(/School/).join
3 |
--------------------------------------------------------------------------------
/0x06-regular_expressions/1-repetition_token_0.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | puts ARGV[0].scan(/hbt{2,5}n/).join
3 |
--------------------------------------------------------------------------------
/0x06-regular_expressions/100-textme.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | puts ARGV[0].scan(/\[from:(\+?\w*)\]\s\[to:(\+?\w*)\]\s\[flags:(\S*)\]/).join(',')
3 |
--------------------------------------------------------------------------------
/0x06-regular_expressions/2-repetition_token_1.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | puts ARGV[0].scan(/hb?tn/).join
3 |
--------------------------------------------------------------------------------
/0x06-regular_expressions/3-repetition_token_2.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | puts ARGV[0].scan(/hbt+n/).join
3 |
--------------------------------------------------------------------------------
/0x06-regular_expressions/4-repetition_token_3.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | puts ARGV[0].scan(/hbt*n/).join
3 |
--------------------------------------------------------------------------------
/0x06-regular_expressions/5-beginning_and_end.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | puts ARGV[0].scan(/^h.n$/).join
3 |
--------------------------------------------------------------------------------
/0x06-regular_expressions/6-phone_number.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | puts ARGV[0].scan(/^\d{10}$/).join
3 |
--------------------------------------------------------------------------------
/0x06-regular_expressions/7-OMG_WHY_ARE_YOU_SHOUTING.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | puts ARGV[0].scan(/[A-Z]/).join
3 |
--------------------------------------------------------------------------------
/0x06-regular_expressions/README.md:
--------------------------------------------------------------------------------
1 | # Regular expression
2 |
--------------------------------------------------------------------------------
/0x07-networking_basics/0-OSI_model:
--------------------------------------------------------------------------------
1 | 2
2 | 2
3 |
--------------------------------------------------------------------------------
/0x07-networking_basics/1-types_of_network:
--------------------------------------------------------------------------------
1 | 3
2 | 2
3 | 1
4 |
--------------------------------------------------------------------------------
/0x07-networking_basics/2-MAC_and_IP_address:
--------------------------------------------------------------------------------
1 | 2
2 | 1
3 |
--------------------------------------------------------------------------------
/0x07-networking_basics/3-UDP_and_TCP:
--------------------------------------------------------------------------------
1 | 1
2 | 2
3 | 1
4 |
--------------------------------------------------------------------------------
/0x07-networking_basics/4-TCP_and_UDP_ports:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # This script is displaying listening ports.
3 |
4 | netstat -lp
5 |
--------------------------------------------------------------------------------
/0x07-networking_basics/5-is_the_host_on_the_network:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # This script is pinging 5 times the given as parameter ip adress.
3 |
4 | if [ $# -eq 0 ]; then
5 | echo "Usage: 5-is_the_host_on_the_network {IP_ADDRESS}"
6 | else
7 | ping -c5 "$1"
8 | fi
9 |
--------------------------------------------------------------------------------
/0x07-networking_basics/README.md:
--------------------------------------------------------------------------------
1 | # 0x07. Networking basics #0
2 | ### Tasks
3 | 0. OSI model
4 | 1. Types of network
5 | 2. MAC and IP address
6 | 3. UDP and TCP
7 | 4. TCP and UDP ports
8 | 5. Is the host on the network
9 |
10 |
--------------------------------------------------------------------------------
/0x08-networking_basics_2/0-change_your_home_IP:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # This script is modifying hosts.
3 |
4 | cp /etc/hosts ~/hosts.new
5 | sed -i 's/127.*$/127.0.0.2 localhost\n8.8.8.8 facebook.com/' ~/hosts.new
6 | cp -f ~/hosts.new /etc/hosts
7 |
--------------------------------------------------------------------------------
/0x08-networking_basics_2/1-show_attached_IPs:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # Displays all acive IPv4 addresses on the machine.
3 |
4 | ifconfig | grep -Eo "inet (addr:)?([0-9]*\.){3}[0-9]*" | cut -b 11-
5 |
--------------------------------------------------------------------------------
/0x08-networking_basics_2/100-port_listening_on_localhost:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # This script is listening on port 98 on localhost.
3 |
4 | nc -l 98
5 |
--------------------------------------------------------------------------------
/0x08-networking_basics_2/README.md:
--------------------------------------------------------------------------------
1 | # 0x08-networking_basics_2
2 | ### Tasks
3 | 0. Change your home IP
4 | 1. Show attached IPs
5 |
6 |
--------------------------------------------------------------------------------
/0x09-web_infrastructure_design/0-simple_web_stack:
--------------------------------------------------------------------------------
1 | https://imgur.com/yhvR3sT
2 |
--------------------------------------------------------------------------------
/0x09-web_infrastructure_design/1-distributed_web_infrastructure:
--------------------------------------------------------------------------------
1 | https://imgur.com/d9WX8E3
2 |
--------------------------------------------------------------------------------
/0x09-web_infrastructure_design/2-secured_and_monitored_web_infrastructure:
--------------------------------------------------------------------------------
1 | https://imgur.com/3z7MIKd
2 |
--------------------------------------------------------------------------------
/0x09-web_infrastructure_design/3-scale_up:
--------------------------------------------------------------------------------
1 | https://imgur.com/ksiNWFY
2 |
--------------------------------------------------------------------------------
/0x09-web_infrastructure_design/README.md:
--------------------------------------------------------------------------------
1 | # Web infrastructure design
2 |
3 |
4 |
5 | ### Tasks
6 | 0. Simple web stack
7 | 1. Distributed web infrastructure
8 | 2. Secured and monitored web infrastructure
9 | 3. Scale up
10 |
11 |
12 | ## Authors
13 |
14 | - Tewodros Atirsaw Awedew
15 |
--------------------------------------------------------------------------------
/0x0A-configuration_management/0-create_a_file.pp:
--------------------------------------------------------------------------------
1 | # Creates a file in /tmp.
2 |
3 | file { 'school':
4 | name => '/tmp/school',
5 | mode => '0744',
6 | owner => 'www-data',
7 | group => 'www-data',
8 | content => 'I love Puppet',
9 | }
10 |
--------------------------------------------------------------------------------
/0x0A-configuration_management/1-install_a_package.pp:
--------------------------------------------------------------------------------
1 | # Installs a package
2 | package { 'flask':
3 | ensure => '2.1.0',
4 | provider => 'pip'
5 | }
6 |
--------------------------------------------------------------------------------
/0x0A-configuration_management/2-execute_a_command.pp:
--------------------------------------------------------------------------------
1 | # Kills the killmenow process.
2 |
3 | exec { 'killmenow':
4 | command => 'pkill killmenow',
5 | path => '/usr/local/bin/:/usr/bin:/bin/',
6 | }
7 |
--------------------------------------------------------------------------------
/0x0A-configuration_management/README.md:
--------------------------------------------------------------------------------
1 | # Configuration management
2 | ### Tasks
3 | 0. Create a file
4 | 1. Install a package
5 | 2. Execute a command
6 | ##Background Context
7 |
8 |
9 | When I was working for SlideShare, I worked on an auto-remediation tool called Skynet that monitored, scaled and fixed Cloud infrastructure. I was using a parallel job-execution system called MCollective that allowed me to execute commands to one or multiple servers at the same time. I could apply an action to a selected set of servers by applying a filter such as the server’s hostname or any other metadata we had (server type, server environment…). At some point, a bug was present in my code that sent nil to the filter method.
10 |
11 | There were 2 pieces of bad news:
12 |
13 | When MCollective receives nil as an argument for its filter method, it takes this to mean ‘all servers’
14 | The action I sent was to terminate the selected servers
15 | I started the parallel job-execution and after some time, I realized that it was taking longer than expected. Looking at logs I realized that I was shutting down SlideShare’s entire document conversion environment. Actually, 75% of all our conversion infrastructure servers had been shut down, resulting in users not able to convert their PDFs, powerpoints, and videos… Pretty bad!
16 |
17 | Thanks to Puppet, we were able to restore our infrastructure to normal operation in under 1H, pretty impressive. Imagine if we had to do everything manually: launching the servers, configuring and linking them, importing application code, starting every process, and obviously, fixing all the bugs (you should know by now that complicated infrastructure always goes sideways)…
18 |
19 | Obviously writing Puppet code for your infrastructure requires an investment of time and energy, but in the long term, it is for sure a must-have.
20 | ###Resources
21 | Read or watch:
22 |
23 | Intro to Configuration Management
24 | Puppet resource type: file (check “Resource types” for all manifest types in the left menu)
25 | Puppet’s Declarative Language: Modeling Instead of Scripting
26 | Puppet lint
27 | Puppet emacs mode
28 | Requirements
29 | General
30 | All your files will be interpreted on Ubuntu 20.04 LTS
31 | All your files should end with a new line
32 | A README.md file at the root of the folder of the project is mandatory
33 | Your Puppet manifests must pass puppet-lint version 2.1.1 without any errors
34 | Your Puppet manifests must run without error
35 | Your Puppet manifests first line must be a comment explaining what the Puppet manifest is about
36 | Your Puppet manifests files must end with the extension .pp
37 |
38 |
--------------------------------------------------------------------------------
/0x0B-ssh/0-use_a_private_key:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # This script uses ssh to connect to a server using a private key
3 |
4 | ssh ubuntu@35.153.18.80 -i ~/.ssh/school
5 |
--------------------------------------------------------------------------------
/0x0B-ssh/1-create_ssh_key_pair:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # This script creates an RSA key pair
3 |
4 | ssh-keygen -f school -b 4096 -P betty
5 |
--------------------------------------------------------------------------------
/0x0B-ssh/100-puppet_ssh_config.pp:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # connect to a server Execute a command
3 |
4 | exec { 'echo "PasswordAuthentication no\nIdentityFile ~/.ssh/school" >> /etc/ssh/ssh_config':
5 | path => '/bin/'
6 | }
7 |
--------------------------------------------------------------------------------
/0x0B-ssh/2-ssh_config:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # Client configuration file
3 | # file with user config
4 | Host 54.224.46.152
5 | IdentityFile ~/.ssh/school
6 | PasswordAuthentication no
7 |
--------------------------------------------------------------------------------
/0x0B-ssh/README.md:
--------------------------------------------------------------------------------
1 | # SSH
2 |
3 |
4 |
5 | ### Tasks
6 | 0. Use a private key
7 | 1. Create an SSH key pair
8 | 2. Client configuration file
9 | 3. Let me in!
10 |
11 |
--------------------------------------------------------------------------------
/0x0C-web_server/.0-transfer_file.swp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/teddy004/alx-system_engineering-devops/cea8c3f89c541452750d9911499811aa85fc06d7/0x0C-web_server/.0-transfer_file.swp
--------------------------------------------------------------------------------
/0x0C-web_server/.2-setup_a_domain_name.swo:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/teddy004/alx-system_engineering-devops/cea8c3f89c541452750d9911499811aa85fc06d7/0x0C-web_server/.2-setup_a_domain_name.swo
--------------------------------------------------------------------------------
/0x0C-web_server/.README.md.swp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/teddy004/alx-system_engineering-devops/cea8c3f89c541452750d9911499811aa85fc06d7/0x0C-web_server/.README.md.swp
--------------------------------------------------------------------------------
/0x0C-web_server/0-transfer_file:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # Transfers a file from Holberton's client to another.
3 | # Accepts four arguments:
4 | #+ The path to the file to be transferred.
5 | #+ The IP of the server to transfer the file to.
6 | #+ The username that scp connects with.
7 | #+ The path to the SSH private key that scp uses.
8 |
9 | if [ $# -lt 4 ]
10 | then
11 | echo "Usage: 0-transfer_file PATH_TO_FILE IP USERNAME PATH_TO_SSH_KEY"
12 | else
13 | scp -o StrictHostKeyChecking=no -i "$4" "$1" "$3@$2":~
14 | fi
15 |
--------------------------------------------------------------------------------
/0x0C-web_server/1-install_nginx_web_server:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # Installs, configures, and starts the server
3 | sudo apt-get update
4 | sudo apt-get -y install nginx
5 | sudo ufw allow 'Nginx HTTP'
6 | mkdir -p /var/www/html
7 | chmod -R 755 /var/www
8 | echo 'Hello World!' > /var/www/html/index.html
9 | SERVER_CONFIG=\
10 | "server {
11 | listen 80 default_server;
12 | listen [::]:80 default_server;
13 |
14 | root /var/www/html;
15 | index index.html index.htm index.nginx-debian.html;
16 |
17 | server_name _;
18 |
19 | location / {
20 | try_files \$uri \$uri/ =404;
21 | }
22 | }"
23 |
24 | bash -c "echo -e '$SERVER_CONFIG' > /etc/nginx/sites-enabled/default"
25 |
26 | if [ "$(pgrep -c nginx)" -le 0 ]; then
27 | sudo service nginx start
28 | else
29 | sudo service nginx restart
30 | fi
31 |
--------------------------------------------------------------------------------
/0x0C-web_server/2-setup_a_domain_name:
--------------------------------------------------------------------------------
1 | teddysolutions.tech
2 |
--------------------------------------------------------------------------------
/0x0C-web_server/3-redirection:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # Installs, configures, and starts the server
3 | sudo apt-get update
4 | sudo apt-get -y install nginx
5 | sudo ufw allow 'Nginx HTTP'
6 | mkdir -p /var/www/html/
7 | sudo chmod -R 755 /var/www
8 | echo 'Hello World!' > /var/www/html/index.html
9 | SERVER_CONFIG=\
10 | "server {
11 | listen 80 default_server;
12 | listen [::]:80 default_server;
13 |
14 | root /var/www/html;
15 | index index.html index.htm index.nginx-debian.html;
16 |
17 | server_name _;
18 |
19 | location / {
20 | try_files \$uri \$uri/ =404;
21 | }
22 |
23 | if (\$request_filename ~ redirect_me){
24 | rewrite ^ https://sketchfab.com/bluepeno/models permanent;
25 | }
26 | }"
27 |
28 | bash -c "echo -e '$SERVER_CONFIG' > /etc/nginx/sites-enabled/default"
29 |
30 | if [ "$(pgrep -c nginx)" -le 0 ]; then
31 | sudo service nginx start
32 | else
33 | sudo service nginx restart
34 | fi
35 |
--------------------------------------------------------------------------------
/0x0C-web_server/4-not_found_page_404:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # Install Nginx, redirect and 404
3 |
4 | sudo apt update -y
5 | sudo apt upgrade -y
6 | sudo apt install nginx -y
7 | sudo service nginx start
8 | echo "Ceci n'est pas une page" | sudo tee /var/www/html/index.nginx-debian.html
9 | new_string='server_name _;\n\n\trewrite \^\/redirect_me https:\/\/www.youtube.com\/watch\?v=QH2-TGUlwu4 permanent;/'
10 | sudo sed -i "s/server_name _;\$/${new_string}" /etc/nginx/sites-available/default
11 | sudo sed -i "s/^\t\}$/\t\}\n\n\terror_page 404 \/404.html;/" /etc/nginx/sites-available/default
12 | sudo ln -sf /etc/nginx/sites-available/default /etc/nginx/sites-enabled/default
13 | echo "Ceci n'est pas une page" | sudo tee /var/www/html/404.html
14 | sudo service nginx restart
15 |
--------------------------------------------------------------------------------
/0x0C-web_server/7-puppet_install_nginx_web_server.pp:
--------------------------------------------------------------------------------
1 | # This manifest installs ngix and adds redirect page
2 |
3 | package {'nginx':
4 | ensure => present,
5 | name => 'nginx',
6 | }
7 |
8 | file {'/var/www/html/index.html':
9 | ensure => present,
10 | path => '/var/www/html/index.html',
11 | content => 'Hello World!',
12 | }
13 |
14 | file_line { 'redirect_me':
15 | ensure => present,
16 | path => '/etc/nginx/sites-available/default',
17 | after => 'listen 80 default_server;',
18 | line => 'rewrite ^/redirect_me https://www.youtube.com/watch?v=QH2-TGUlwu4 permanent;',
19 | }
20 |
21 | service { 'nginx':
22 | ensure => running,
23 | hasrestart => true,
24 | require => Package['nginx'],
25 | subscribe => File_line['redirect_me'],
26 | }
27 |
--------------------------------------------------------------------------------
/0x0C-web_server/README.md:
--------------------------------------------------------------------------------
1 | # Web server
2 |
3 |
4 | ### Tasks
5 | 0. Transfer a file to your server
6 | 2. Setup a domain name
7 | 3. Redirection
8 | 4. Not found page 404
9 | 5. Install Nginx web server (w/ Puppet)
10 |
--------------------------------------------------------------------------------
/0x0C-web_server/nginx,:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/teddy004/alx-system_engineering-devops/cea8c3f89c541452750d9911499811aa85fc06d7/0x0C-web_server/nginx,
--------------------------------------------------------------------------------
/0x0C-web_server/present,:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/teddy004/alx-system_engineering-devops/cea8c3f89c541452750d9911499811aa85fc06d7/0x0C-web_server/present,
--------------------------------------------------------------------------------
/0x0D-web_stack_debugging_0/0-give_me_a_page:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | #Script was use to fix a not function a we server
3 | sudo service apache2 restart
4 |
--------------------------------------------------------------------------------
/0x0D-web_stack_debugging_0/README.md:
--------------------------------------------------------------------------------
1 | # Web stack debugging #0
2 |
3 |
4 |
5 | ### Tasks
6 | 0. Give me a page!
7 |
--------------------------------------------------------------------------------
/0x0E-web_stack_debugging_1/0-nginx_likes_port_80:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # Configures an Nginx server to listen on port 80.
3 |
4 | rm /etc/nginx/sites-enabled/default
5 | ln -s /etc/nginx/sites-available/default /etc/nginx/sites-enabled/default
6 | service nginx restart
7 |
--------------------------------------------------------------------------------
/0x0E-web_stack_debugging_1/1-debugging_made_short:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # Configures an Nginx server to listen on port 80.
3 | ln -sf /etc/nginx/sites-available/default /etc/nginx/sites-enabled/default
4 | service nginx start
5 | kill "$(pgrep 'nginx' | head -1)"
6 |
--------------------------------------------------------------------------------
/0x0E-web_stack_debugging_1/README.md:
--------------------------------------------------------------------------------
1 | # Web stack debugging #1
2 |
3 |
4 | ### Tasks
5 | 0. Nginx likes port 80
6 | 1. Make it sweet and short
7 |
--------------------------------------------------------------------------------
/0x0F-load_balancer/0-custom_http_response_header:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # Installs Nginx with the following configurations:
3 | #+ Listens on port 80.
4 | #+ Returns a page containing "Hello World!" when queried
5 | #+ at the root with a curl GET request.
6 | # Configures /redirect_me as a "301 Moved Permanently".
7 | # Includes a custom 404 page containing "Ceci n'est pas une page".
8 |
9 | sudo apt-get update
10 | sudo apt-get install -y nginx
11 |
12 | sudo mkdir /etc/nginx/html
13 | sudo touch /etc/nginx/html/index.html
14 | echo "Hello World!" > /etc/nginx/html/index.html
15 | sudo touch /etc/nginx/html/404.html
16 | echo "Ceci n'est pas une page" > /etc/nginx/html/404.html
17 |
18 | printf %s "server {
19 | listen 80;
20 | listen [::]:80 default_server;
21 | add_header X-Served-By $HOSTNAME;
22 | root /etc/nginx/html;
23 | index index.html index.htm;
24 | location /redirect_me {
25 | return 301 https://www.youtube.com/watch?v=QH2-TGUlwu4;
26 | }
27 | error_page 404 /404.html;
28 | location /404 {
29 | root /etc/nginx/html;
30 | internal;
31 | }
32 | }" > /etc/nginx/sites-available/default
33 |
34 | sudo service nginx restart
35 |
--------------------------------------------------------------------------------
/0x0F-load_balancer/1-install_load_balancer:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # Installs HAProxy version 1.8 with the following configurations:
3 | #+ Enables management via the init script.
4 | #+ Distributes requests using a round-robin algorithm.
5 |
6 | sudo apt-get -y update
7 | sudo apt-get -y upgrade
8 | sudo apt-get -y install haproxy
9 |
10 | echo "ENABLED=1" >> /etc/default/haproxy
11 | sudo mv /etc/haproxy/haproxy.cfg{,.original}
12 | sudo touch /etc/haproxy/haproxy.cfg
13 |
14 | printf %s "global
15 | log 127.0.0.1 local0 notice
16 | maxconn 2000
17 | user haproxy
18 | group haproxy
19 | defaults
20 | log global
21 | mode http
22 | option httplog
23 | option dontlognull
24 | retries 3
25 | option redispatch
26 | timeout connect 5000
27 | timeout client 10000
28 | timeout server 10000
29 | listen hbnb
30 | bind 0.0.0.0:80
31 | mode http
32 | stats enable
33 | stats uri /haproxy?stats
34 | balance roundrobin
35 | option httpclose
36 | option forwardfor
37 | server 3593-web-01 18.204.5.22 check port 80
38 | server 3593-web-02 100.25.215.65 check port 80
39 | " >> /etc/haproxy/haproxy.cfg
40 |
41 | sudo service haproxy start
42 |
--------------------------------------------------------------------------------
/0x0F-load_balancer/2-puppet_custom_http_response_header.pp:
--------------------------------------------------------------------------------
1 | # Script that setup a nginx web server on our server + redirection + header
2 | exec { 'update':
3 | command => '/usr/bin/apt-get update',
4 | }
5 |
6 | package { 'nginx':
7 | ensure => installed,
8 | name => 'nginx',
9 | require => Exec['update'],
10 | }
11 |
12 | file { '/var/www/html/index.html':
13 | ensure => 'present',
14 | path => '/var/www/html/index.html',
15 | content => 'Holberton School',
16 | require => Package['nginx'],
17 | }
18 |
19 | file_line { 'redirect_me':
20 | ensure => 'present',
21 | path => '/etc/nginx/sites-available/default',
22 | after => 'listen 80 default_server;',
23 | line => 'rewrite ^/redirect_me https://www.youtube.com/watch?v=QH2-TGUlwu4 permanent;',
24 | require => Package['nginx'],
25 | }
26 |
27 | file_line { 'addHeader':
28 | ensure => 'present',
29 | path => '/etc/nginx/sites-available/default',
30 | after => 'listen 80 default_server;',
31 | line => 'add_header X-Served-By $hostname;',
32 | require => Package['nginx'],
33 | }
34 |
35 | service { 'nginx':
36 | ensure => running,
37 | require => Package['nginx'],
38 | }
39 |
--------------------------------------------------------------------------------
/0x0F-load_balancer/README.md:
--------------------------------------------------------------------------------
1 | # Load balancer
2 |
3 | ### Tasks
4 | 0. Double the number of webservers
5 | 1. Install your load balancer
6 | 2. Add a custom HTTP header with Puppet
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/0x10-https_ssl/0-world_wide_web:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # Usage: ./0-world_wide_web
3 | # Display information about subdomains.
4 |
5 | domain_information () {
6 | line=$(dig "$2"."$1" | grep -A1 'ANSWER SECTION:' | tr '\t' '\n' | tail -2 | tr '\n' ' ')
7 | echo "$2 $line" | awk '{print "The subdomain " $1 " is a " $2 " record and points to " $3}'
8 | }
9 |
10 | if [ "$#" == 1 ]
11 | then
12 | domain_information "$1" "www"
13 | domain_information "$1" "lb-01"
14 | domain_information "$1" "web-01"
15 | domain_information "$1" "web-02"
16 | elif [ "$#" == 2 ]
17 | then
18 | domain_information "$1" "$2"
19 | fi
20 |
--------------------------------------------------------------------------------
/0x10-https_ssl/1-haproxy_ssl_termination:
--------------------------------------------------------------------------------
1 | global
2 | log /dev/log local0
3 | maxconn 2048
4 | user haproxy
5 | group haproxy
6 | tune.ssl.default-dh-param 2048
7 |
8 | defaults
9 | log global
10 | mode http
11 | option httplog
12 | option dontlognull
13 | retries 3
14 | option redispatch
15 | timeout connect 5000
16 | timeout client 10000
17 | timeout server 10000
18 | option forwardfor
19 | option http-server-close=
20 |
21 | frontend www-http
22 | bind 0.0.0.0:80
23 | reqadd X-Forwarded-Proto:\ http
24 | default_backend www-backend
25 |
26 | frontend www-https
27 | bind 0.0.0.0:443 ssl crt /etc/letsencrypt/live/www.teddy.tech/all.pem
28 | reqadd X-Forwarded-Proto:\ https
29 | acl letsencrypt-acl path_beg /.well-known/acme-challenge/
30 | use_backend letsencrypt-backend if letsencrypt-acl
31 | default_backend www-backend
32 |
33 | backend www-backend
34 | balance roundrobin
35 | redirect scheme https if !{ ssl_fc }
36 | server 3593-web-01 44.210.83.85:80 check
37 | server 3593-web-02 3.236.84.6:80 check
38 |
39 | backend letsencrypt-backend
40 | server letsencrypt 127.0.0.1:54321
41 |
--------------------------------------------------------------------------------
/0x10-https_ssl/100-redirect_http_to_https:
--------------------------------------------------------------------------------
1 | global
2 | log /dev/log local0
3 | maxconn 2048
4 | user haproxy
5 | group haproxy
6 | tune.ssl.default-dh-param 2048
7 |
8 | defaults
9 | log global
10 | mode http
11 | option httplog
12 | option dontlognull
13 | retries 3
14 | option redispatch
15 | timeout connect 5000
16 | timeout client 10000
17 | timeout server 10000
18 | option forwardfor
19 | option http-server-close
20 |
21 | frontend www-http
22 | bind 0.0.0.0:80
23 | reqadd X-Forwarded-Proto:\ http
24 | default_backend www-backend
25 | redirect scheme https code 301 if !{ ssl_fc }
26 |
27 |
28 | frontend www-https
29 | bind 0.0.0.0:443 ssl crt /etc/letsencrypt/live/www.topsentaa.tech/all.pem
30 | reqadd X-Forwarded-Proto:\ https
31 | acl letsencrypt-acl path_beg /.well-known/acme-challenge/
32 | use_backend letsencrypt-backend if letsencrypt-acl
33 | default_backend www-backend
34 |
35 | backend www-backend
36 | balance roundrobin
37 | redirect scheme https if !{ ssl_fc }
38 | server 1329-web-01 34.73.127.101:80 check
39 | server 1329-web-02 18.205.59.182:80 check
40 |
41 | backend letsencrypt-backend
42 | server letsencrypt 127.0.0.1:54321
43 |
--------------------------------------------------------------------------------
/0x10-https_ssl/README.md:
--------------------------------------------------------------------------------
1 | # HTTPS SSL
2 |
3 |
4 | ## Tasks
5 | 0. World wide web
6 | 1. HAproxy SSL termination
7 | 2. No loophole in your website traffic
8 |
--------------------------------------------------------------------------------
/0x11-what_happens_when_your_type_google_com_in_your_browser_and_press_enter/0-blog_post:
--------------------------------------------------------------------------------
1 | https://www.linkedin.com/pulse/what-happens-when-you-type-httpswwwgooglecom-your-browser-awedew
2 |
--------------------------------------------------------------------------------
/0x11-what_happens_when_your_type_google_com_in_your_browser_and_press_enter/1-what_happen_when_diagram:
--------------------------------------------------------------------------------
1 | https://imgur.com/a/MRrbd6j
2 |
--------------------------------------------------------------------------------
/0x11-what_happens_when_your_type_google_com_in_your_browser_and_press_enter/2-contribution-to_what-happens-when_github_answer:
--------------------------------------------------------------------------------
1 | https://github.com/teddy004/what-happens-when
2 |
--------------------------------------------------------------------------------
/0x11-what_happens_when_your_type_google_com_in_your_browser_and_press_enter/README.md:
--------------------------------------------------------------------------------
1 | # What happens when you type google.com in your browser and press Enter
2 | Requirements, your post must cover:
3 |
4 | _ DNS request
5 | _TCP/IP
6 | _Firewall
7 | _HTTPS/SSL
8 | _Load-balancer
9 | -Web server
10 | _ Application server
11 | _ Database
12 |
13 |
--------------------------------------------------------------------------------
/0x12-web_stack_debugging_2/0-iamsomeoneelse:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # run the whoami command under the user passed as an argument
3 | sudo -u "$1" whoami
4 |
--------------------------------------------------------------------------------
/0x12-web_stack_debugging_2/1-run_nginx_as_nginx:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # runs nginx as nginx
3 | pkill apache2
4 | chown nginx:nginx /etc/nginx/nginx.conf
5 | chmod 777 /etc/nginx/nginx.conf
6 | sed -i 's/80/8080/g' /etc/nginx/sites-available/default
7 | sudo -u nginx service nginx start
8 |
--------------------------------------------------------------------------------
/0x12-web_stack_debugging_2/100-fix_in_7_lines_or_less:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # runs nginx as nginx
3 | pkill apache2
4 | chown nginx:nginx /etc/nginx/nginx.conf
5 | chmod 777 /etc/nginx/nginx.conf
6 | sed -i 's/80/8080/g' /etc/nginx/sites-available/default
7 | sudo -u nginx service nginx start
8 |
--------------------------------------------------------------------------------
/0x12-web_stack_debugging_2/README.md:
--------------------------------------------------------------------------------
1 | # Web stack debugging #2
2 |
3 |
4 |
5 |
6 | ### Tasks
7 | 0. Run software as another user
8 | 1. Run Nginx as Nginx
9 | 2. 7 lines or less
10 |
11 |
--------------------------------------------------------------------------------
/0x13-firewall/0-block_all_incoming_traffic_but:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # activating ufw and setting a couple of rules
3 | sudo ufw default deny incoming
4 | sudo ufw allow 22/tcp
5 | sudo ufw allow 443/tcp
6 | sudo ufw allow 80/tcp
7 | sudo ufw enable
8 |
--------------------------------------------------------------------------------
/0x13-firewall/README.md:
--------------------------------------------------------------------------------
1 | # Firewall
2 |
3 |
4 | # Tasks
5 | 0. Block all incoming traffic but
6 | 1. Port forwarding
7 |
--------------------------------------------------------------------------------
/0x14-mysql/4-mysql_configuration_primary:
--------------------------------------------------------------------------------
1 | #
2 | # The MySQL database server configuration file.
3 | #
4 | # You can copy this to one of:
5 | # - "/etc/mysql/my.cnf" to set global options,
6 | # - "~/.my.cnf" to set user-specific options.
7 | #
8 | # One can use all long options that the program supports.
9 | # Run program with --help to get a list of available options and with
10 | # --print-defaults to see which it would actually understand and use.
11 | #
12 | # For explanations see
13 | # http://dev.mysql.com/doc/mysql/en/server-system-variables.html
14 |
15 | # This will be passed to all mysql clients
16 | # It has been reported that passwords should be enclosed with ticks/quotes
17 | # escpecially if they contain "#" chars...
18 | # Remember to edit /etc/mysql/debian.cnf when changing the socket location.
19 |
20 | # Here is entries for some specific programs
21 | # The following values assume you have at least 32M ram
22 |
23 | [mysqld_safe]
24 | socket = /var/run/mysqld/mysqld.sock
25 | nice = 0
26 |
27 | [mysqld]
28 | #
29 | # * Basic Settings
30 | #
31 | user = mysql
32 | pid-file = /var/run/mysqld/mysqld.pid
33 | socket = /var/run/mysqld/mysqld.sock
34 | port = 3306
35 | basedir = /usr
36 | datadir = /var/lib/mysql
37 | tmpdir = /tmp
38 | lc-messages-dir = /usr/share/mysql
39 | skip-external-locking
40 | #
41 | # Instead of skip-networking the default is now to listen only on
42 | # localhost which is more compatible and is not less secure.
43 | # bind-address = 127.0.0.1
44 | #
45 | # * Fine Tuning
46 | #
47 | key_buffer_size = 16M
48 | max_allowed_packet = 16M
49 | thread_stack = 192K
50 | thread_cache_size = 8
51 | # This replaces the startup script and checks MyISAM tables if needed
52 | # the first time they are touched
53 | myisam-recover-options = BACKUP
54 | #max_connections = 100
55 | #table_cache = 64
56 | #thread_concurrency = 10
57 | #
58 | # * Query Cache Configuration
59 | #
60 | query_cache_limit = 1M
61 | query_cache_size = 16M
62 | #
63 | # * Logging and Replication
64 | #
65 | # Both location gets rotated by the cronjob.
66 | # Be aware that this log type is a performance killer.
67 | # As of 5.1 you can enable the log at runtime!
68 | #general_log_file = /var/log/mysql/mysql.log
69 | #general_log = 1
70 | #
71 | # Error log - should be very few entries.
72 | #
73 | log_error = /var/log/mysql/error.log
74 | #
75 | # Here you can see queries with especially long duration
76 | #log_slow_queries = /var/log/mysql/mysql-slow.log
77 | #long_query_time = 2
78 | #log-queries-not-using-indexes
79 | #
80 | # The following can be used as easy to replay backup logs or for replication.
81 | # note: if you are setting up a replication slave, see README.Debian about
82 | # other settings you may need to change.
83 | #server-id = 1
84 | #log_bin = /var/log/mysql/mysql-bin.log
85 | expire_logs_days = 10
86 | max_binlog_size = 100M
87 | #binlog_do_db = include_database_name
88 | #binlog_ignore_db = include_database_name
89 | #
90 | # * InnoDB
91 | #
92 | # InnoDB is enabled by default with a 10MB datafile in /var/lib/mysql/.
93 | # Read the manual for more InnoDB related options. There are many!
94 | #
95 | # * Security Features
96 | #
97 | # Read the manual, too, if you want chroot!
98 | # chroot = /var/lib/mysql/
99 | #
100 | # For generating SSL certificates I recommend the OpenSSL GUI "tinyca".
101 | #
102 | # ssl-ca=/etc/mysql/cacert.pem
103 | # ssl-cert=/etc/mysql/server-cert.pem
104 | # ssl-key=/etc/mysql/server-key.pem
105 | server-id = 1
106 | log_bin = /var/log/mysql/mysql-bin.log
107 | binlog_do_db = tyrell_corp
108 |
--------------------------------------------------------------------------------
/0x14-mysql/4-mysql_configuration_replica:
--------------------------------------------------------------------------------
1 | #
2 | # The MySQL database server configuration file.
3 | #
4 | # You can copy this to one of:
5 | # - "/etc/mysql/my.cnf" to set global options,
6 | # - "~/.my.cnf" to set user-specific options.
7 | #
8 | # One can use all long options that the program supports.
9 | # Run program with --help to get a list of available options and with
10 | # --print-defaults to see which it would actually understand and use.
11 | #
12 | # For explanations see
13 | # http://dev.mysql.com/doc/mysql/en/server-system-variables.html
14 |
15 | # This will be passed to all mysql clients
16 | # It has been reported that passwords should be enclosed with ticks/quotes
17 | # escpecially if they contain "#" chars...
18 | # Remember to edit /etc/mysql/debian.cnf when changing the socket location.
19 |
20 | # Here is entries for some specific programs
21 | # The following values assume you have at least 32M ram
22 |
23 | [mysqld_safe]
24 | socket = /var/run/mysqld/mysqld.sock
25 | nice = 0
26 |
27 | [mysqld]
28 | #
29 | # * Basic Settings
30 | #
31 | user = mysql
32 | pid-file = /var/run/mysqld/mysqld.pid
33 | socket = /var/run/mysqld/mysqld.sock
34 | port = 3306
35 | basedir = /usr
36 | datadir = /var/lib/mysql
37 | tmpdir = /tmp
38 | lc-messages-dir = /usr/share/mysql
39 | skip-external-locking
40 | #
41 | # Instead of skip-networking the default is now to listen only on
42 | # localhost which is more compatible and is not less secure.
43 | # bind-address = 127.0.0.1
44 | #
45 | # * Fine Tuning
46 | #
47 | key_buffer_size = 16M
48 | max_allowed_packet = 16M
49 | thread_stack = 192K
50 | thread_cache_size = 8
51 | # This replaces the startup script and checks MyISAM tables if needed
52 | # the first time they are touched
53 | myisam-recover-options = BACKUP
54 | #max_connections = 100
55 | #table_cache = 64
56 | #thread_concurrency = 10
57 | #
58 | # * Query Cache Configuration
59 | #
60 | query_cache_limit = 1M
61 | query_cache_size = 16M
62 | #
63 | # * Logging and Replication
64 | #
65 | # Both location gets rotated by the cronjob.
66 | # Be aware that this log type is a performance killer.
67 | # As of 5.1 you can enable the log at runtime!
68 | #general_log_file = /var/log/mysql/mysql.log
69 | #general_log = 1
70 | #
71 | # Error log - should be very few entries.
72 | #
73 | log_error = /var/log/mysql/error.log
74 | #
75 | # Here you can see queries with especially long duration
76 | #log_slow_queries = /var/log/mysql/mysql-slow.log
77 | #long_query_time = 2
78 | #log-queries-not-using-indexes
79 | #
80 | # The following can be used as easy to replay backup logs or for replication.
81 | # note: if you are setting up a replication slave, see README.Debian about
82 | # other settings you may need to change.
83 | #server-id = 1
84 | #log_bin = /var/log/mysql/mysql-bin.log
85 | expire_logs_days = 10
86 | max_binlog_size = 100M
87 | #binlog_do_db = include_database_name
88 | #binlog_ignore_db = include_database_name
89 | #
90 | # * InnoDB
91 | #
92 | # InnoDB is enabled by default with a 10MB datafile in /var/lib/mysql/.
93 | # Read the manual for more InnoDB related options. There are many!
94 | #
95 | # * Security Features
96 | #
97 | # Read the manual, too, if you want chroot!
98 | # chroot = /var/lib/mysql/
99 | #
100 | # For generating SSL certificates I recommend the OpenSSL GUI "tinyca".
101 | #
102 | # ssl-ca=/etc/mysql/cacert.pem
103 | # ssl-cert=/etc/mysql/server-cert.pem
104 | # ssl-key=/etc/mysql/server-key.pem
105 | server-id = 2
106 | relay-log = /var/log/mysql/mysql-relay-bin.log
107 | log_bin = /var/log/mysql/mysql-bin.log
108 | binlog_do_db = tyrell_corp
109 |
--------------------------------------------------------------------------------
/0x14-mysql/5-mysql_backup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # generates a MySQL dump and creates a compressed archive out of it
3 |
4 | mysqldump -u root -p"$1" --all-databases > backup.sql
5 | tar -czvf "$(date '+%d-%m-%Y').tar.gz" backup.sql
6 |
--------------------------------------------------------------------------------
/0x14-mysql/README.md:
--------------------------------------------------------------------------------
1 | # MySQL
2 |
3 | ### Tasks
4 | 0. Install MySQL
5 | 1. Let us in!
6 | 2. If only you could see what I've seen with your eyes
7 | 3. Quite an experience to live in fear, isn't it?
8 | 4. Setup a Primary-Replica infrastructure using MySQL
9 | 5. MySQL backup
10 |
11 |
--------------------------------------------------------------------------------
/0x15-api/0-gather_data_from_an_API.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | '''A script that gathers data from an API.
3 | '''
4 | import re
5 | import requests
6 | import sys
7 |
8 |
9 | API_URL = 'https://jsonplaceholder.typicode.com'
10 | '''The API's URL.'''
11 |
12 |
13 | if __name__ == '__main__':
14 | if len(sys.argv) > 1:
15 | if re.fullmatch(r'\d+', sys.argv[1]):
16 | id = int(sys.argv[1])
17 | user_res = requests.get('{}/users/{}'.format(API_URL, id)).json()
18 | todos_res = requests.get('{}/todos'.format(API_URL)).json()
19 | user_name = user_res.get('name')
20 | todos = list(filter(lambda x: x.get('userId') == id, todos_res))
21 | todos_done = list(filter(lambda x: x.get('completed'), todos))
22 | print(
23 | 'Employee {} is done with tasks({}/{}):'.format(
24 | user_name,
25 | len(todos_done),
26 | len(todos)
27 | )
28 | )
29 | for todo_done in todos_done:
30 | print('\t {}'.format(todo_done.get('title')))
31 |
--------------------------------------------------------------------------------
/0x15-api/1-export_to_CSV.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | '''A script that gathers data from an API and exports it to a CSV file.
3 | '''
4 | import re
5 | import requests
6 | import sys
7 |
8 |
9 | API_URL = 'https://jsonplaceholder.typicode.com'
10 | '''The API's URL.'''
11 |
12 |
13 | if __name__ == '__main__':
14 | if len(sys.argv) > 1:
15 | if re.fullmatch(r'\d+', sys.argv[1]):
16 | id = int(sys.argv[1])
17 | user_res = requests.get('{}/users/{}'.format(API_URL, id)).json()
18 | todos_res = requests.get('{}/todos'.format(API_URL)).json()
19 | user_name = user_res.get('username')
20 | todos = list(filter(lambda x: x.get('userId') == id, todos_res))
21 | with open('{}.csv'.format(id), 'w') as file:
22 | for todo in todos:
23 | file.write(
24 | '"{}","{}","{}","{}"\n'.format(
25 | id,
26 | user_name,
27 | todo.get('completed'),
28 | todo.get('title')
29 | )
30 | )
31 |
--------------------------------------------------------------------------------
/0x15-api/2-export_to_JSON.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | '''A script that gathers data from an API and exports it to a CSV file.
3 | '''
4 | import re
5 | import requests
6 | import sys
7 |
8 |
9 | API_URL = 'https://jsonplaceholder.typicode.com'
10 | '''The API's URL.'''
11 |
12 |
13 | if __name__ == '__main__':
14 | if len(sys.argv) > 1:
15 | if re.fullmatch(r'\d+', sys.argv[1]):
16 | id = int(sys.argv[1])
17 | user_res = requests.get('{}/users/{}'.format(API_URL, id)).json()
18 | todos_res = requests.get('{}/todos'.format(API_URL)).json()
19 | user_name = user_res.get('username')
20 | todos = list(filter(lambda x: x.get('userId') == id, todos_res))
21 | with open('{}.csv'.format(id), 'w') as file:
22 | for todo in todos:
23 | file.write(
24 | '"{}","{}","{}","{}"\n'.format(
25 | id,
26 | user_name,
27 | todo.get('completed'),
28 | todo.get('title')
29 | )
30 | )
31 |
--------------------------------------------------------------------------------
/0x15-api/3-dictionary_of_list_of_dictionaries.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | '''A script that gathers data from an API and exports it to a JSON file.
3 | '''
4 | import json
5 | import requests
6 |
7 |
8 | API_URL = 'https://jsonplaceholder.typicode.com'
9 | '''The API's URL.'''
10 |
11 |
12 | if __name__ == '__main__':
13 | users_res = requests.get('{}/users'.format(API_URL)).json()
14 | todos_res = requests.get('{}/todos'.format(API_URL)).json()
15 | users_data = {}
16 | for user in users_res:
17 | id = user.get('id')
18 | user_name = user.get('username')
19 | todos = list(filter(lambda x: x.get('userId') == id, todos_res))
20 | user_data = list(map(
21 | lambda x: {
22 | 'username': user_name,
23 | 'task': x.get('title'),
24 | 'completed': x.get('completed')
25 | },
26 | todos
27 | ))
28 | users_data['{}'.format(id)] = user_data
29 | with open('todo_all_employees.json', 'w') as file:
30 | json.dump(users_data, file)
31 |
--------------------------------------------------------------------------------
/0x15-api/README.md:
--------------------------------------------------------------------------------
1 | # API
2 |
3 | This project contains tasks for learning about how to consume RESTful APIs.
4 |
5 | ## Tasks To Complete
6 |
7 | + [x] 0. Gather data from an API _**[0-gather_data_from_an_API.py](0-gather_data_from_an_API.py)**_ contains a Python script that uses this [REST API](https://jsonplaceholder.typicode.com/), and for a given employee ID, returns information about his/her TODO list progress.
8 | + Requirements:
9 | + You must use `urllib` or `requests` module.
10 | + The script must accept an integer as a parameter, which is the employee ID.
11 | + The script must display on the standard output the employee TODO list progress in this exact format:
12 | + First line: `Employee EMPLOYEE_NAME is done with tasks(NUMBER_OF_DONE_TASKS/TOTAL_NUMBER_OF_TASKS):`
13 | + `EMPLOYEE_NAME`: name of the employee.
14 | + `NUMBER_OF_DONE_TASKS`: number of completed tasks.
15 | + `TOTAL_NUMBER_OF_TASKS`: total number of tasks, which is the sum of completed and non-completed tasks.
16 | + Second and N next lines display the title of completed tasks: `TASK_TITLE` (with 1 tabulation and 1 space before the `TASK_TITLE`).
17 |
18 | + [x] 1. Export to CSV _**[1-export_to_CSV.py](1-export_to_CSV.py)**_ contains a Python script that uses what was done in task #0 to export data in the CSV format.
19 | + Requirements:
20 | + Records all tasks that are owned by this employee.
21 | + Format must be: `"USER_ID","USERNAME","TASK_COMPLETED_STATUS","TASK_TITLE"`.
22 | + File name must be: `USER_ID.csv`.
23 |
24 | + [x] 2. Export to JSON _**[2-export_to_JSON.py](2-export_to_JSON.py)**_ contains a Python script that uses what was done in task #0 to export data in the JSON format.
25 | + Requirements:
26 | + Records all tasks that are owned by this employee.
27 | + Format must be: `{ "USER_ID": [{"task": "TASK_TITLE", "completed": TASK_COMPLETED_STATUS, "username": "USERNAME"}, {"task": "TASK_TITLE", "completed": TASK_COMPLETED_STATUS, "username": "USERNAME"}, ... ]}`.
28 | + File name must be: `USER_ID.json`.
29 |
30 | + [x] 3. Dictionary of list of dictionaries _**[3-dictionary_of_list_of_dictionaries.py](3-dictionary_of_list_of_dictionaries.py)**_ contains a Python script that uses what was done in task #0 to export data in the JSON format.
31 | + Requirements:
32 | + Records all tasks from all employees
33 | + Format must be: `{ "USER_ID": [ {"username": "USERNAME", "task": "TASK_TITLE", "completed": TASK_COMPLETED_STATUS}, {"username": "USERNAME", "task": "TASK_TITLE", "completed": TASK_COMPLETED_STATUS}, ... ], "USER_ID": [ {"username": "USERNAME", "task": "TASK_TITLE", "completed": TASK_COMPLETED_STATUS}, {"username": "USERNAME", "task": "TASK_TITLE", "completed": TASK_COMPLETED_STATUS}, ... ]}`.
34 | + File name must be: `todo_all_employees.json`.
35 |
--------------------------------------------------------------------------------
/0x16-api_advanced/0-subs.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | '''A module containing functions for working with the Reddit API.
3 | '''
4 | import requests
5 |
6 |
7 | BASE_URL = 'https://www.reddit.com'
8 | '''Reddit's base API URL.
9 | '''
10 |
11 |
12 | def number_of_subscribers(subreddit):
13 | '''Retrieves the number of subscribers in a given subreddit.
14 | '''
15 | api_headers = {
16 | 'Accept': 'application/json',
17 | 'User-Agent': ' '.join([
18 | 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
19 | 'AppleWebKit/537.36 (KHTML, like Gecko)',
20 | 'Chrome/97.0.4692.71',
21 | 'Safari/537.36',
22 | 'Edg/97.0.1072.62'
23 | ])
24 | }
25 | res = requests.get(
26 | '{}/r/{}/about/.json'.format(BASE_URL, subreddit),
27 | headers=api_headers,
28 | allow_redirects=False
29 | )
30 | if res.status_code == 200:
31 | return res.json()['data']['subscribers']
32 | return 0
33 |
--------------------------------------------------------------------------------
/0x16-api_advanced/1-top_ten.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | '''A module containing functions for working with the Reddit API.
3 | '''
4 | import requests
5 |
6 |
7 | BASE_URL = 'https://www.reddit.com'
8 | '''Reddit's base API URL.
9 | '''
10 |
11 |
12 | def top_ten(subreddit):
13 | '''Retrieves the title of the top ten posts from a given subreddit.
14 | '''
15 | api_headers = {
16 | 'Accept': 'application/json',
17 | 'User-Agent': ' '.join([
18 | 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
19 | 'AppleWebKit/537.36 (KHTML, like Gecko)',
20 | 'Chrome/97.0.4692.71',
21 | 'Safari/537.36',
22 | 'Edg/97.0.1072.62'
23 | ])
24 | }
25 | sort = 'top'
26 | limit = 10
27 | res = requests.get(
28 | '{}/r/{}/.json?sort={}&limit={}'.format(
29 | BASE_URL,
30 | subreddit,
31 | sort,
32 | limit
33 | ),
34 | headers=api_headers,
35 | allow_redirects=False
36 | )
37 | if res.status_code == 200:
38 | for post in res.json()['data']['children'][0:10]:
39 | print(post['data']['title'])
40 | else:
41 | print(None)
42 |
--------------------------------------------------------------------------------
/0x16-api_advanced/100-count.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | '''A module containing functions for working with the Reddit API.
3 | '''
4 | import requests
5 |
6 |
7 | def sort_histogram(histogram={}):
8 | '''Sorts and prints the given histogram.
9 | '''
10 | histogram = list(filter(lambda kv: kv[1], histogram))
11 | histogram_dict = {}
12 | for item in histogram:
13 | if item[0] in histogram_dict:
14 | histogram_dict[item[0]] += item[1]
15 | else:
16 | histogram_dict[item[0]] = item[1]
17 | histogram = list(histogram_dict.items())
18 | histogram.sort(
19 | key=lambda kv: kv[0],
20 | reverse=False
21 | )
22 | histogram.sort(
23 | key=lambda kv: kv[1],
24 | reverse=True
25 | )
26 | res_str = '\n'.join(list(map(
27 | lambda kv: '{}: {}'.format(kv[0], kv[1]),
28 | histogram
29 | )))
30 | if res_str:
31 | print(res_str)
32 |
33 |
34 | def count_words(subreddit, word_list, histogram=[], n=0, after=None):
35 | '''Counts the number of times each word in a given wordlist
36 | occurs in a given subreddit.
37 | '''
38 | api_headers = {
39 | 'Accept': 'application/json',
40 | 'User-Agent': ' '.join([
41 | 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
42 | 'AppleWebKit/537.36 (KHTML, like Gecko)',
43 | 'Chrome/97.0.4692.71',
44 | 'Safari/537.36',
45 | 'Edg/97.0.1072.62'
46 | ])
47 | }
48 | sort = 'hot'
49 | limit = 30
50 | res = requests.get(
51 | '{}/r/{}/.json?sort={}&limit={}&count={}&after={}'.format(
52 | 'https://www.reddit.com',
53 | subreddit,
54 | sort,
55 | limit,
56 | n,
57 | after if after else ''
58 | ),
59 | headers=api_headers,
60 | allow_redirects=False
61 | )
62 | if not histogram:
63 | word_list = list(map(lambda word: word.lower(), word_list))
64 | histogram = list(map(lambda word: (word, 0), word_list))
65 | if res.status_code == 200:
66 | data = res.json()['data']
67 | posts = data['children']
68 | titles = list(map(lambda post: post['data']['title'], posts))
69 | histogram = list(map(
70 | lambda kv: (kv[0], kv[1] + sum(list(map(
71 | lambda txt: txt.lower().split().count(kv[0]),
72 | titles
73 | )))),
74 | histogram
75 | ))
76 | if len(posts) >= limit and data['after']:
77 | count_words(
78 | subreddit,
79 | word_list,
80 | histogram,
81 | n + len(posts),
82 | data['after']
83 | )
84 | else:
85 | sort_histogram(histogram)
86 | else:
87 | return
88 |
--------------------------------------------------------------------------------
/0x16-api_advanced/2-recurse.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | '''A module containing functions for working with the Reddit API.
3 | '''
4 | import requests
5 |
6 |
7 | BASE_URL = 'https://www.reddit.com'
8 | '''Reddit's base API URL.
9 | '''
10 |
11 |
12 | def recurse(subreddit, hot_list=[], n=0, after=None):
13 | '''Retrieves a list of hot posts from a given subreddit.
14 | '''
15 | api_headers = {
16 | 'Accept': 'application/json',
17 | 'User-Agent': ' '.join([
18 | 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
19 | 'AppleWebKit/537.36 (KHTML, like Gecko)',
20 | 'Chrome/97.0.4692.71',
21 | 'Safari/537.36',
22 | 'Edg/97.0.1072.62'
23 | ])
24 | }
25 | sort = 'hot'
26 | limit = 30
27 | res = requests.get(
28 | '{}/r/{}/.json?sort={}&limit={}&count={}&after={}'.format(
29 | BASE_URL,
30 | subreddit,
31 | sort,
32 | limit,
33 | n,
34 | after if after else ''
35 | ),
36 | headers=api_headers,
37 | allow_redirects=False
38 | )
39 | if res.status_code == 200:
40 | data = res.json()['data']
41 | posts = data['children']
42 | count = len(posts)
43 | hot_list.extend(list(map(lambda x: x['data']['title'], posts)))
44 | if count >= limit and data['after']:
45 | return recurse(subreddit, hot_list, n + count, data['after'])
46 | else:
47 | return hot_list if hot_list else None
48 | else:
49 | return hot_list if hot_list else None
50 |
--------------------------------------------------------------------------------
/0x16-api_advanced/README.md:
--------------------------------------------------------------------------------
1 | # API Advanced
2 |
3 | This project contains more tasks for learning how to consume RESTful APIs.
4 |
5 | ## Tasks To Complete
6 |
7 | + [x] 0. How many subs? _**[0-subs.py](0-subs.py)**_ contains a Python function queries the [Reddit API](https://www.reddit.com/dev/api) and returns the number of subscribers (not active users, total subscribers) for a given subreddit. If an invalid subreddit is given, the function should return 0.
8 | + Hint: No authentication is necessary for most features of the Reddit API. If you’re getting errors related to Too Many Requests, ensure you’re setting a custom User-Agent.
9 | + Requirements:
10 | + Prototype: `def number_of_subscribers(subreddit)`.
11 | + If not a valid subreddit, return 0.
12 | + NOTE: Invalid subreddits may return a redirect to search results. Ensure that you are not following redirects.
13 |
14 | + [x] 1. Top Ten _**[1-top_ten.py](1-top_ten.py)**_ contains a Python function queries the [Reddit API](https://www.reddit.com/dev/api) and prints the titles of the first 10 hot posts listed for a given subreddit.
15 | + Requirements:
16 | + Prototype: `def top_ten(subreddit)`.
17 | + If not a valid subreddit, print None.
18 | + NOTE: Invalid subreddits may return a redirect to search results. Ensure that you are not following redirects.
19 |
20 | + [x] 2. Recurse it! _**[2-recurse.py](2-recurse.py)**_ contains a *recursive Python function* that queries the [Reddit API](https://www.reddit.com/dev/api) and returns a list containing the titles of all hot articles for a given subreddit. If no results are found for the given subreddit, the function should return None.
21 | + Hint: The Reddit API uses pagination for separating pages of responses.
22 | + Requirements:
23 | + Prototype: `def recurse(subreddit, hot_list=[])`.
24 | + Note: You may change the prototype, but it must be able to be called with just a subreddit supplied. AKA you can add a counter, but it must work without supplying a starting value in the main.
25 | + If not a valid subreddit, return None.
26 | + NOTE: Invalid subreddits may return a redirect to search results. Ensure that you are not following redirects.
27 | + Your code will NOT pass if you are using a loop and not recursively calling the function! This /can/ be done with a loop but the point is to use a recursive function. :)
28 |
29 | + [x] 3. Count it! _**[100-count.py](100-count.py)**_ contains a *recursive Python function* that queries the Reddit API, parses the title of all hot articles, and prints a sorted count of given keywords (case-insensitive, delimited by spaces. `Javascript` should count as `javascript`, but `java` should not).
30 | + Requirements:
31 | + Prototype: `def count_words(subreddit, word_list)`.
32 | + Note: You may change the prototype, but it must be able to be called with just a subreddit supplied and a list of keywords. AKA you can add a counter or anything else, but the function must work without supplying a starting value in the main.
33 | + If `word_list` contains the same word (case-insensitive), the final count should be the sum of each duplicate (example below with `java`).
34 | + Results should be printed in descending order, by the count, and if the count is the same for separate keywords, they should then be sorted alphabetically (ascending, from A to Z). Words with no matches should be skipped and not printed. Words must be printed in lowercase.
35 | + Results are based on the number of times a keyword appears, not titles it appears in. `java java java` counts as 3 separate occurrences of `java`.
36 | + To make life easier, `java.` or `java!` or `java_` should not count as `java`.
37 | + If no posts match or the subreddit is invalid, print nothing.
38 | + NOTE: Invalid subreddits may return a redirect to search results. Ensure that you are NOT following redirects.
39 | + Your code will NOT pass if you are using a loop and not recursively calling the function! This /can/ be done with a loop but the point is to use a recursive function. :)
40 |
--------------------------------------------------------------------------------
/0x17-web_stack_debugging_3/0-strace_is_your_friend.pp:
--------------------------------------------------------------------------------
1 | # Fixes a faulty wordpress site
2 | exec { 'fix-wordpress':
3 | command => 'bash -c "sed -i s/class-wp-locale.phpp/class-wp-locale.php/ \
4 | /var/www/html/wp-settings.php; service apache2 restart"',
5 | path => '/usr/bin:/usr/sbin:/bin'
6 | }
7 |
--------------------------------------------------------------------------------
/0x17-web_stack_debugging_3/README.md:
--------------------------------------------------------------------------------
1 | # Web Stack Debugging
2 |
3 | This project contains tasks for learning about how to debug web stacks.
4 |
5 | ## Tasks To Complete
6 |
7 | + [x] 0. Strace is your friend _**[0-strace_is_your_friend.pp](0-strace_is_your_friend.pp)**_ contains a Puppet manifest that fixes a faulty server.
8 | + **Info**:
9 | + Using `strace`, find out why Apache is returning a 500 error. Once you find the issue, fix it and then automate it using Puppet (instead of using Bash as you were previously doing).
10 | + **HINT:**
11 | + `strace` can attach to a current running process.
12 | + You can use [tmux](https://www.hamvocke.com/blog/a-quick-and-easy-guide-to-tmux/) to run [strace](https://strace.io/) in one window and `curl` in another one.
13 |
--------------------------------------------------------------------------------
/0x18-webstack_monitoring/2-setup_datadog:
--------------------------------------------------------------------------------
1 | s6p-kzw-9t6
2 |
--------------------------------------------------------------------------------
/0x18-webstack_monitoring/README.md:
--------------------------------------------------------------------------------
1 | # Webstack Monitoring
2 |
3 | This project contains some tasks for learning how to setup monitoring for a webstack on a server using **DATADOG**.
4 |
5 | ## Tasks To Complete
6 |
7 | + [x] 0. Sign up for Datadog and install datadog-agent
8 | + Head to [https://www.datadoghq.com/](https://www.datadoghq.com/) and sign up for a free `Datadog` account. When signing up, you'll have the option of selecting statistics from your current stack that `Datadog` can monitor for you. Once you have an account set up, follow the instructions given on the website to install the `Datadog` agent.
9 | + Sign up for Datadog - **Please make sure you are using the US website of Datagog (.com)**.
10 | + Install `datadog-agent` on `web-01`.
11 | + Create an `application key`.
12 | + Copy-paste in your Intranet user profile ([here](https://alx-intranet.hbtn.io/users/my_profile)) your DataDog `API key` and your DataDog `application key`.
13 | + Your server `web-01` should be visible in Datadog under the host name `XX-web-01`
14 | + You can validate it by using this [API](https://docs.datadoghq.com/api/latest/hosts/).
15 | + If needed, you will need to update the hostname of your server.
16 |
17 | + [x] 1. Monitor some metrics
18 | + Among the litany of data your monitoring service can report to you are system metrics. You can use these metrics to determine statistics such as reads/writes per second, which can help your company determine if/how they should scale. Set up some `monitors` within the `Datadog` dashboard to monitor and alert you of a few. You can read about the various system metrics that you can monitor here: [System Check](https://docs.datadoghq.com/integrations/system/).
19 | 
20 | + Set up a monitor that checks the number of read requests issued to the device per second.
21 | + Set up a monitor that checks the number of write requests issued to the device per second.
22 |
23 | + [x] 2. Create a dashboard _**[2-setup_datadog](2-setup_datadog)**_ contains the id of a dashboard with different metrics displayed in order to get a few different visualizations.
24 | + Create a new `dashboard`.
25 | + Add at least 4 `widgets` to your dashboard. They can be of any type and monitor whatever you'd like.
26 | 
27 | + Create the answer file [`2-setup_datadog`](2-setup_datadog) which has the `dashboard_id` on the first line. **Note:** in order to get the id of your dashboard, you may need to use [Datadog's API](https://docs.datadoghq.com/api/latest/dashboards/#get-all-dashboards).
28 |
--------------------------------------------------------------------------------
/0x18-webstack_monitoring/Task_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/teddy004/alx-system_engineering-devops/cea8c3f89c541452750d9911499811aa85fc06d7/0x18-webstack_monitoring/Task_1.png
--------------------------------------------------------------------------------
/0x18-webstack_monitoring/Task_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/teddy004/alx-system_engineering-devops/cea8c3f89c541452750d9911499811aa85fc06d7/0x18-webstack_monitoring/Task_2.png
--------------------------------------------------------------------------------
/0x19-postmortem/Blog_Post.md:
--------------------------------------------------------------------------------
1 | # Postmortem
2 |
3 | 
4 |
5 | ## Issue Summary
6 |
7 | We had just released a new feature to our recently launched Ruby on Rails site that we had our first intake of users complaining about the site. 5 minutes after we performed a feature update, we started receiving emails from our users talking about "they can't sign in or sign up to our platform". It was quite surprising to us because we knew it worked on our machines and it worked before. About 127 of such emails came to our inbox. It was an avalanche of emails. Knowing how hard it can be to attract and keep users, we couldn't afford to lose 127 of our users in that way and decided to take a closer look at the problem. We cloned our site's repository from GitBug, followed the installation instructions on the README and to our surprise the site couldn't startup. It wasn't long before we realized that the cause of the problem was failing to update the requirements for our project. The site was malfunctioning from 9:55 AM GMT+1 to 11:20 AM GMT+1.
8 |
9 | ## Timeline
10 |
11 | + 05-02-2022 9:55 AM GMT+1 - A customer complained that they couldn't sign in to the site.
12 | + 05-02-2022 10:20 AM GMT+1 - Winus, one of our backend developers, experienced the same issues our customers reported.
13 | + 05-02-2022 10:35 AM GMT+1 - We investigated the controllers and the views for inconsistencies.
14 | + 05-02-2022 10:40 AM GMT+1 - We assumed the bcrypt (one of our site's dependencies) gem being used was either at fault or used incorrectly because the error message on the site showed that the bcrypt gem was raising an error over an invalid hash.
15 | + 05-02-2022 10:42 AM GMT+1 - We checked that the views might not be binding the form fields to the right model fields, which later turned out to be false.
16 | + 05-02-2022 10:45 AM GMT+1 - We were misled by thinking that our controllers might be creating a different hash for a valid password of the site's admin.
17 | + 05-02-2022 10:50 AM GMT+1 - Winus thought the issue might have been that the password was not properly hashed.
18 | + 05-02-2022 11:00 AM GMT+1 - The incident was escalated to the backend development team.
19 | + 05-02-2022 11:20 AM GMT+1 - The incident was resolved by updating the requirements (the bcrypt gem version) for the backend server.
20 |
21 | ## Root Cause And Resolution
22 |
23 | The version of the bcrypt gem we used was outdated. It was raising an error over a hash that was clearly valid and matched what was stored in the database. It could be that the hash we were creating was not supported by the version of bcrypt we had installed. Winus fixed the issue by manually updating the version of bcrypt in the Gemfile.lock file to a more recent version and reinstalling the required gems, and it worked like a charm.
24 |
25 | ## Corrective And Preventative Measures
26 |
27 | + Setup a continuous integration pipeline to run a build on each pull request branch. This would ensure that builds are passing in the pull request branch before it is merged with the deployment branch.
28 | + Setup a monitoring system for the database and application servers to keep track of any issue that may occur.
29 | + Develop tests that need to be passed for each new feature and those tests should be passing before they are merged with the deployment branch.
30 |
--------------------------------------------------------------------------------
/0x19-postmortem/README.md:
--------------------------------------------------------------------------------
1 | # Postmortem
2 |
3 | This project contains tasks for learning about writing a postmortem.
4 |
5 | ## Tasks To Complete
6 |
7 | + [x] 0. My first postmortem _**[Blog_Post.md](Blog_Post.md)**_ contains a blog post that meets the following requirements:
8 | + **INFO:**
9 | + Using one of the web stack debugging project issue or an outage you have personally face, write a postmortem. Most of you will never have faced an outage, so just get creative and invent your own :)
10 | + While postmortem format can vary, stick to this one so that you can get properly reviewed by your peers.
11 | + **Requirements:**
12 | + Issue Summary (that is often what executives will read) must contain:
13 | + Duration of the outage with start and end times (including timezone).
14 | + What was the impact? (What service was down/slow? What were user experiencing? How many % of the users were affected?)
15 | + What was the root cause?
16 | + Timeline (format bullet point, format: `time` - `keep it short, 1 or 2 sentences`) must contain:
17 | + When was the issue detected?
18 | + How was the issue detected (monitoring alert, an engineer noticed something, a customer complained…)
19 | + Actions taken (what parts of the system were investigated, what were the assumption on the root cause of the issue).
20 | + Misleading investigation/debugging paths that were taken.
21 | + Which team/individuals was the incident escalated to?
22 | + How the incident was resolved.
23 | + Root cause and resolution must contain:
24 | + Explain in detail what was causing the issue.
25 | + Explain in detail how the issue was fixed.
26 | + Corrective and preventative measures must contain:
27 | + What are the things that can be improved/fixed (broadly speaking)
28 | + A list of tasks to address the issue (be very specific, like a TODO, example: patch Nginx server, add monitoring on server memory…).
29 | + Be brief and straight to the point, between 400 to 600 words
30 |
31 | + [x] 1. Make people want to read your postmortem
32 | + We are constantly stormed by a quantity of information, it’s tough to get people to read you.
33 | + Make your post-mortem attractive by adding humour, a pretty diagram or anything that would catch your audience attention.
34 |
--------------------------------------------------------------------------------
/0x19-postmortem/post-mortem-meetings.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/teddy004/alx-system_engineering-devops/cea8c3f89c541452750d9911499811aa85fc06d7/0x19-postmortem/post-mortem-meetings.jpg
--------------------------------------------------------------------------------
/0x1A-application_server/2-app_server-nginx_config:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80 default_server;
3 | listen [::]:80 default_server;
4 |
5 | root /var/www/html;
6 | index index.html index.htm index.nginx-debian.html;
7 |
8 | server_name _;
9 |
10 | location / {
11 | try_files $uri $uri/ =404;
12 | }
13 |
14 | location /airbnb-onepage/ {
15 | proxy_pass http://localhost:5000;
16 | }
17 |
18 | if ($request_filename ~ redirect_me){
19 | rewrite ^ https://sketchfab.com/bluepeno/models permanent;
20 | }
21 |
22 | error_page 404 /404.html;
23 | location = /404.html {
24 | root /var/www/error/;
25 | internal;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/0x1A-application_server/3-app_server-nginx_config:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80 default_server;
3 | listen [::]:80 default_server;
4 |
5 | root /var/www/html;
6 | index index.html index.htm index.nginx-debian.html;
7 |
8 | server_name _;
9 |
10 | location / {
11 | try_files $uri $uri/ =404;
12 | }
13 |
14 | location /airbnb-onepage/ {
15 | proxy_pass http://localhost:5000;
16 | }
17 |
18 | location /airbnb-dynamic/ {
19 | proxy_pass http://localhost:5001/;
20 | }
21 |
22 | if ($request_filename ~ redirect_me){
23 | rewrite ^ https://sketchfab.com/bluepeno/models permanent;
24 | }
25 |
26 | error_page 404 /404.html;
27 | location = /404.html {
28 | root /var/www/error/;
29 | internal;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/0x1A-application_server/4-app_server-nginx_config:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80 default_server;
3 | listen [::]:80 default_server;
4 |
5 | root /var/www/html;
6 | index index.html index.htm index.nginx-debian.html;
7 |
8 | server_name _;
9 |
10 | location / {
11 | try_files $uri $uri/ =404;
12 | }
13 |
14 | location /airbnb-onepage/ {
15 | proxy_pass http://localhost:5000;
16 | }
17 |
18 | location /airbnb-dynamic/ {
19 | proxy_pass http://localhost:5001/;
20 | }
21 |
22 | location /api/ {
23 | proxy_pass http://localhost:5002;
24 | }
25 |
26 | if ($request_filename ~ redirect_me){
27 | rewrite ^ https://sketchfab.com/bluepeno/models permanent;
28 | }
29 |
30 | error_page 404 /404.html;
31 | location = /404.html {
32 | root /var/www/error/;
33 | internal;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/0x1A-application_server/4-reload_gunicorn_no_downtime:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # Gracefully reloads Gunicorn
3 | systemctl status gunicorn | grep -oE 'Main PID: [0-9]+' | cut -d ' ' -f3 | xargs kill -HUP
4 |
--------------------------------------------------------------------------------
/0x1A-application_server/5-app_server-nginx_config:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80 default_server;
3 | listen [::]:80 default_server;
4 |
5 | root /var/www/html;
6 | index index.html index.htm index.nginx-debian.html;
7 |
8 | server_name _;
9 |
10 | location / {
11 | proxy_pass http://localhost:5003/2-hbnb;
12 | }
13 |
14 | location /static/ {
15 | # rewrite ^(/static/[^?]*)\?.*$ $1 last;
16 | alias /home/ubuntu/AirBnB_clone_v4/web_dynamic/static/;
17 | try_files $uri $uri/ =404;
18 | }
19 |
20 | location /airbnb-onepage/ {
21 | proxy_pass http://localhost:5000;
22 | }
23 |
24 | location /airbnb-dynamic/ {
25 | proxy_pass http://localhost:5001/;
26 | }
27 |
28 | location /api/ {
29 | proxy_pass http://localhost:5002;
30 | }
31 |
32 | if ($request_filename ~ redirect_me){
33 | rewrite ^ https://sketchfab.com/bluepeno/models permanent;
34 | }
35 |
36 | error_page 404 /404.html;
37 | location = /404.html {
38 | root /var/www/error/;
39 | internal;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/0x1A-application_server/README.md:
--------------------------------------------------------------------------------
1 | # Application Server
2 |
3 | This project contains tasks for learning how to configure a web server (nginx) with an application server (Gunicorn).
4 |
5 | ## Tasks To Complete
6 |
7 | + [x] 0. Set up development with Python Let’s serve what you built for [AirBnB clone v2 - Web framework](https://github.com/B3zaleel/AirBnB_clone_v2) on `web-01`. This task is an exercise in setting up your development environment, which is used for testing and debugging your code before deploying it to production.
8 | + Requirements:
9 | + Make sure that task #3 of your [SSH project](../0x0B-ssh/README.md) is completed for `web-01`. The checker will connect to your servers.
10 | + Git clone your `AirBnB_clone_v2` on your `web-01` server.
11 | + Configure the file `web_flask/0-hello_route.py` to serve its content from the route `/airbnb-onepage/` on port `5000`.
12 | + Your Flask application object must be named `app` (This will allow us to run and check your code).
13 |
14 | + [x] 1. Set up production with Gunicorn Now that you have your development environment set up, let’s get your production application server set up with `Gunicorn` on `web-01`, port `5000`. You’ll need to install `Gunicorn` and any libraries required by your application. Your `Flask` application object will serve as a [WSGI](https://www.fullstackpython.com/wsgi-servers.html) entry point into your application. This will be your production environment. As you can see we want the production and development of your application to use the same port, so the conditions for serving your dynamic content are the same in both environments.
15 | + Requirements:
16 | + Install `Gunicorn` and any other libraries required by your application.
17 | + The Flask application object should be called `app`. (This will allow us to run and check your code).
18 | + You will serve the same content from the same route as in the previous task. You can verify that it’s working by binding a `Gunicorn` instance to localhost listening on port `5000` with your application object as the entry point.
19 | + In order to check your code, the checker will bind a `Gunicorn` instance to port `6000`, so make sure nothing is listening on that port.
20 |
21 | + [x] 2. Serve a page with Nginx Building on your work in the previous tasks, configure Nginx to serve your page from the route `/airbnb-onepage/`.
22 | + Requirements:
23 | + `Nginx` must serve this page both locally and on its public IP on port `80`.
24 | + `Nginx` should proxy requests to the process listening on port 5000.
25 | + Include your `Nginx` config file as `2-app_server-nginx_config`.
26 | + Notes:
27 | + In order to test this you’ll have to spin up either your production or development application server (listening on port `5000`).
28 | + In an actual production environment the application server will be configured to start upon startup in a system initialization script. This will be covered in the advanced tasks.
29 | + You will probably need to `reboot` your server (by using the command `sudo reboot`) to have Nginx publicly accessible.
30 |
31 | + [x] 3. Add a route with query parameters Building on what you did in the previous tasks, let’s expand our web application by adding another service for `Gunicorn` to handle. In `AirBnB_clone_v2/web_flask/6-number_odd_or_even`, the route `/number_odd_or_even/` should already be defined to render a page telling you whether an integer is odd or even. You’ll need to configure `Nginx` to proxy HTTP requests to the route `/airbnb-dynamic/number_odd_or_even/(any integer)` to a `Gunicorn` instance listening on port `5001`. The key to this exercise is getting `Nginx` configured to proxy requests to processes listening on two different ports. You are not expected to keep your application server processes running. If you want to know how to run multiple instances of `Gunicorn` without having multiple terminals open, see tips below.
32 | + Requirements:
33 | + `Nginx` must serve this page both locally and on its public IP on port `80`.
34 | + `Nginx` should proxy requests to the route `/airbnb-dynamic/number_odd_or_even/(any integer)` the process listening on port `5001`.
35 | + Include your `Nginx` config file as `3-app_server-nginx_config`.
36 | + Tips:
37 | + Check out these articles/docs for clues on how to configure `Nginx`: [Understanding Nginx Server and Location Block Selection Algorithms](https://www.digitalocean.com/community/tutorials/understanding-nginx-server-and-location-block-selection-algorithms#matching-location-blocks), [Understanding Nginx Location Blocks Rewrite Rules](http://blog.pixelastic.com/2013/09/27/understanding-nginx-location-blocks-rewrite-rules/), [Nginx Reverse Proxy](https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/#).
38 | + In order to spin up a `Gunicorn` instance as a detached process you can use the terminal multiplexer utility `tmux`. Enter the command `tmux new-session -d 'gunicorn --bind 0.0.0.0:5001 web_flask.6-number_odd_or_even:app'` and if successful you should see no output to the screen. You can verify that the process has been created by running `pgrep gunicorn` to see its PID. Once you’re ready to end the process you can either run `tmux a` to reattach to the processes, or you can run `kill ` to terminate the background process by ID.
39 |
40 | + [x] 4. Let's do this for your API Let’s serve what you built for [AirBnB clone v3 - RESTful API](https://github.com/B3zaleel/AirBnB_clone_v3) on `web-01`.
41 | + Requirements:
42 | + Git clone your `AirBnB_clone_v3`.
43 | + Setup `Nginx` so that the route `/api/` points to a `Gunicorn` instance listening on port `5002`.
44 | + `Nginx` must serve this page both locally and on its public IP on port `80`.
45 | + To test your setup you should bind `Gunicorn` to `api/v1/app.py`.
46 | + It may be helpful to import your data (and environment variables) from [this project](https://github.com/B3zaleel/AirBnB_clone_v2).
47 | + Upload your `Nginx` config file as `4-app_server-nginx_config`.
48 |
49 | + [x] 5. Serve your AirBnB clone Let’s serve what you built for [AirBnB clone - Web dynamic](https://github.com/B3zaleel/AirBnB_clone_v4) on `web-01`.
50 | + Requirements:
51 | + Git clone your [AirBnB_clone_v4](https://github.com/B3zaleel/AirBnB_clone_v4)
52 | + Your `Gunicorn` instance should serve content from web_dynamic/2-hbnb.py on port `5003`.
53 | + Setup `Nginx` so that the route `/` points to your `Gunicorn` instance.
54 | + Setup `Nginx` so that it properly serves the static assets found in `web_dynamic/static/` (this is essential for your page to render properly).
55 | + For your website to be fully functional, you will need to reconfigure `web_dynamic/static/scripts/2-hbnb.js` to the correct IP.
56 | + `Nginx` must serve this page both locally and on its public IP and port `5003`.
57 | + Make sure to pull up your Developer Tools on your favorite browser to verify that you have no errors.
58 | + Upload your `Nginx` config as `5-app_server-nginx_config`.
59 |
60 | + [x] 6. Deploy it! Once you’ve got your application server configured, you want to set it up to run by default when Linux is booted. This way when your server inevitably requires downtime (you have to shut it down or restart it for one reason or another), your `Gunicorn` process(es) will start up as part of the system initialization process, freeing you from having to manually restart them. For this we will use `systemd`. You can read more about `systemd` in the documentation posted at the top of this project but to put it succinctly, it is a system initialization daemon for the Linux OS (amongst other things). For this task you will write a `systemd` script which will start your application server for you. As mentioned in the video at the top of the project, you do not need to create a Unix socket to bind the process to.
61 | + Requirements:
62 | + Write a `systemd` script which starts a `Gunicorn` process to serve the same content as the previous task (`web_dynamic/2-hbnb.py`).
63 | + The `Gunicorn` process should spawn 3 worker processes.
64 | + The process should log errors in `/tmp/airbnb-error.log`.
65 | + The process should log access in `/tmp/airbnb-access.log`.
66 | + The process should be bound to port `5003`.
67 | + Your `systemd` script should be stored in the appropriate directory on `web-01`.
68 | + Make sure that you start the systemd service and leave it running.
69 | + Upload `gunicorn.service` to GitHub.
70 |
71 | + [x] 7. No service interruption
72 | One of the most important metrics for any Internet-based business is its uptime. It is the percentage of the time over a given period that the service/product is accessible to customers. Let’s pick the example of Amazon.com, for every minute of downtime (which is the opposite of uptime), [it costs the company $2M](https://storageservers.wordpress.com/2016/03/14/amazon-downtime-costs-2-million-loss-per-minute/). Yet, application servers often need to restart to update with the new version of the code or new configuration, when doing this operation, an application server cannot serve traffic, which meant downtime.
73 |
74 | To avoid this; application servers are designed with a master/workers infrastructure. The master is in charge of:
75 | + Receiving requests
76 | + Managing workers (starting, stopping)
77 | + Distributing requests to workers
78 | Workers are the actual ones processing the query by generation dynamic content by processing the application code.
79 |
80 | To update an application without downtime, the master will proceed with a progressive rollout of the update. It will gracefully shut down some workers ( meaning that it will tell workers to finish processing the request they are working on, but will not send them new requests, once the worker is done, it’s will be shutdown) and start new ones with the new application code or configuration, then move on to the other old workers until it has renewed the whole pool.
81 |
82 | Write a simple Bash script to reload Gunicorn in a graceful way. This script should be stored in the file [4-reload_gunicorn_no_downtime](4-reload_gunicorn_no_downtime).
83 |
--------------------------------------------------------------------------------
/0x1A-application_server/gunicorn.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=gunicorn.service - AirBnB clone by B3zaleel run with Gunicorn.
3 | After=network.target
4 |
5 | [Service]
6 | User=ubuntu
7 | Group=www-data
8 | WorkingDirectory=/home/ubuntu/AirBnB_clone_v4/
9 | Environment=HBNB_MYSQL_USER=hbnb_dev
10 | Environment=HBNB_MYSQL_PWD=hbnb_dev_pwd
11 | Environment=HBNB_MYSQL_HOST=localhost
12 | Environment=HBNB_MYSQL_DB=hbnb_dev_db
13 | Environment=HBNB_TYPE_STORAGE=db
14 | Environment=HBNB_ENV=dev
15 | ExecStart=/usr/bin/gunicorn --workers 3 --error-logfile /tmp/airbnb-error.log --access-logfile /tmp/airbnb-access.log --bind 0.0.0.0:5003 web_dynamic.2-hbnb:app
16 |
17 | [Install]
18 | WantedBy=multi-user.target
19 |
--------------------------------------------------------------------------------
/0x1B-web_stack_debugging_4/0-the_sky_is_the_limit_not.pp:
--------------------------------------------------------------------------------
1 | # Fixes an nginx site that can't handle multiple concurrent requests
2 | exec { 'fix--for-nginx':
3 | command => "bash -c \"sed -iE 's/^ULIMIT=.*/ULIMIT=\\\"-n 8192\\\"/' \
4 | /etc/default/nginx; service nginx restart\"",
5 | path => '/usr/bin:/usr/sbin:/bin'
6 | }
7 |
--------------------------------------------------------------------------------
/0x1B-web_stack_debugging_4/1-user_limit.pp:
--------------------------------------------------------------------------------
1 | # Changes the limitations on the holberton user
2 | exec { 'change-os-configuration-for-holberton-user':
3 | command => "bash -c \"sed -iE 's/^holberton hard nofile \
4 | 5/holberton hard nofile 88888/' /etc/security/limits.conf; \
5 | sed -iE 's/^holberton soft nofile \
6 | 4/holberton soft nofile 88888/' /etc/security/limits.conf\"",
7 | path => '/usr/bin:/usr/sbin:/bin'
8 | }
9 |
--------------------------------------------------------------------------------
/0x1B-web_stack_debugging_4/README.md:
--------------------------------------------------------------------------------
1 | # Web Stack Debugging
2 |
3 | This project contains tasks for learning about how to debug web stacks.
4 |
5 | ## Tasks To Complete
6 |
7 | + [x] 0. Sky is the limit, let's bring that limit higher _**[0-the_sky_is_the_limit_not.pp](0-the_sky_is_the_limit_not.pp)**_ contains a Puppet manifest that improves the capabilities of an Nginx server.
8 | + Info:
9 | + In this web stack debugging task, we are testing how well our web server setup featuring Nginx is doing under pressure and it turns out it’s not doing well: we are getting a huge amount of failed requests.
10 | + For the benchmarking, we are using ApacheBench which is a quite popular tool in the industry. It allows you to simulate HTTP requests to a web server. In this case, I will make 2000 requests to my server with 100 requests at a time. We can see that 943 requests failed, let’s fix our stack so that we get to 0, and remember that when something is going wrong, logs are your best friends!
11 |
12 | + [x] 1. User limit _**[1-user_limit.pp](1-user_limit.pp)**_ contains a Puppet manifest that changes the OS configuration so that it is possible to login with the `holberton` user and open a file without any error message.
13 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ##Shell Scripting Basics Exercises
2 |
3 | Exercise 0: pwd === print working directory
4 |
5 | Exercise 1: ls === list directory contents
6 |
7 | Exercise 2: cd === change directory
8 |
9 | Exercise 3: ls -l === list directory contents in long form
10 |
11 | Exercise 4: ls -la === list directory contents in long form, including hidden files
12 |
13 | Exercise 5: ls -la Note: Are files inherently ordered?
14 |
15 | Exercise 6: mkdir /tmp/my_first_directory Create a my_first_directory directory inside the tmp directory
16 |
17 | Exercise 7: mv /tmp/betty /tmp/my_first_directory/betty Move file betty, which is located inside the tmp directory, to the my_first_directory directory, which is also located inside the tmp directory. This exercise required some dir traversing.
18 |
19 | Exercise 8: rm /tmp/my_first_directory/betty Remove file betty located in tmp/my_first_directory directory.
20 |
21 | Exercise 9: rmdir /tmp/my_first_directory Remove directory my_first_directory located in directory tmp.
22 |
23 | Exercise 10: cd - Change directory to the previous directory you were in.
24 |
25 | Exercise 11: ls -la . .. /boot List all files/directories, including hidden files/directories, from 3 separate directories: current directory, parent of working directory, and /boot directory. The ls command allows multiple directories to be listed separated by spaces.
26 |
27 | Exercise 12: file /tmp/iamafile Prints the type of file iamafile.
28 |
29 | Exercise 13: ln -s /bin/ls ls Create a symbolic link named ls for /bin/ls
30 |
31 | Exercise 14: cp -u *.html .. Copy all html files from the current directory to the parent directory, but only copy files that didn't exist in the parent directory or are newer versions than the ones that already exist in the parent directory. The -u option didn't show on the terminal manual page. The -u option copies the file into the directory if its a newer version. If the file doesn't exist in the directory, it will copy over. The -n option works for copying files that don't exist in the parent directory, but it doesn't check if the file is a newer version or not.
32 |
33 | Exercise 15: mv [[:upper:]]* /tmp/u Move all files that begin with a capital letter to /tmp/u
34 |
35 | Exercise 16: rm *~ Deletes all files in the current directory that end with a ~
36 |
37 | Exercise 17: mkdir -p welcome/to/school Create directory welcome in current directory. Create directory to inside directory welcome. Create directory school inside directory to. The -p option creates any intermediate directories in the path argument.
38 |
39 | Exercise 18: ls -pam List all files and directories of the current directory, separated by commas. Directory names should end with a /. The listing should be alph ordered, except for dot (.) or dot dot (..), which should be listed at the beginning. The -a option is to show any hidden files. The -p option writes a / at the end of directory names. The -m option streams the output, separating each listing with commas.
40 |
41 | Exercise 19: 0 string SCHOOL School data !:mime School Create a magic file called school.mgc that can be used with the command file to detect School data files. School data files always contain "SCHOOL" at offset 0.
42 | This exercise was much different from the previous exercises. From what I understand, the magic file is used to detect patterns in files and will give a specified output depending on a matching pattern. The first argument is a number representing the offset. The second argument is the data type you are searching for. In our case, it is a string. The third argument is the data you are searching for. In our case, "SCHOOL", which we specified as a string in the second argument. The fourth argument is the message you want to output on match. If the search matches, it will output this message. The last argument is separated by a line. Since the fourth argument can be long and contain multiple strings, we separate the fourth and fifth arguments with this new line. This last argument can be multiple different things. In this case, a MIME type. According to bash manual, a "MIME type is given on a separate line, which must be the next non-blank or comment line after the magic line that identifies the file type". I knew to search for a MIME type because the example provided: $ file --mime-type -m holberton.mgc * The above returns message "School" when matching a MIME ?? Not exactly sure, but this is what I can tell from what I've tested out and can see from the output and examples. $ file -m school.mgc * The above will return message "SCHOOL data" for any offset 0 "School" matches. A cool thing to note is that the file command is compiling and running the magic file. So there is no need to compile to magic "permanently". NOTE: Compiling a magic source file: $ file -C -m .mgc This produces the compiled magic file. $ file -i -m .mgc * This allows you to use the compiled file by specifying its name using the -m switch again.
43 |
--------------------------------------------------------------------------------
/attack_is_the_best_defense/0-sniffing:
--------------------------------------------------------------------------------
1 | mypassword9898!
2 |
--------------------------------------------------------------------------------
/attack_is_the_best_defense/1-dictionary_attack:
--------------------------------------------------------------------------------
1 | password123
2 |
--------------------------------------------------------------------------------
/attack_is_the_best_defense/README.md:
--------------------------------------------------------------------------------
1 | # Attack is the best defense
2 |
3 | ### Tasks
4 | 0. ARP spoofing and sniffing unencrypted traffic
5 | 1. Dictionary attack
6 |
7 |
--------------------------------------------------------------------------------
/command_line_for_the_win/0-first_9_tasks.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/teddy004/alx-system_engineering-devops/cea8c3f89c541452750d9911499811aa85fc06d7/command_line_for_the_win/0-first_9_tasks.png
--------------------------------------------------------------------------------
/command_line_for_the_win/1-next_9_tasks.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/teddy004/alx-system_engineering-devops/cea8c3f89c541452750d9911499811aa85fc06d7/command_line_for_the_win/1-next_9_tasks.png
--------------------------------------------------------------------------------
/command_line_for_the_win/2-next_9-tasks.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/teddy004/alx-system_engineering-devops/cea8c3f89c541452750d9911499811aa85fc06d7/command_line_for_the_win/2-next_9-tasks.png
--------------------------------------------------------------------------------
/command_line_for_the_win/README.md:
--------------------------------------------------------------------------------
1 | # Command Line For The Win
2 |
3 | This project contains solutions to some of the bash command challenges at [cmdchallenge](https://cmdchallenge.com/). See [Solutions](Solutions.md).
4 |
5 | ## Tasks to Complete
6 |
7 | + 1. [x] First 九 tasks **_[0-first_9_tasks.png](0-first_9_tasks.png)_** Shows the completion of the first 9 tasks.
8 | + 1. [x] Reach חי completed tasks **_[1-next_9_tasks.png](1-next_9_tasks.png)_** Shows the completion of the next 9 (the first 18) tasks.
9 | + 2. [x] Reach the perfect cube, 27 **_[2-next_9_tasks.png](2-next_9_tasks.png)_** Shows the completion of the next 9 (the first 27) tasks.
10 |
--------------------------------------------------------------------------------
/command_line_for_the_win/Solutions.md:
--------------------------------------------------------------------------------
1 | # Solutions to the CMD Challenge
2 |
3 | + 1 -> hello_world Your first challenge is to print "hello world" on the terminal in a single command.
4 | ```powershell
5 | echo 'hello world'
6 | ```
7 | + 2 -> current_working_directory Print the current working directory.
8 | ```powershell
9 | pwd
10 | ```
11 | + 3 -> list_files List names of all the files in the current directory, one file per line.
12 | ```powershell
13 | find -type f -regex ".*" -printf "%f\n"
14 | ```
15 | + 4 -> foo
16 | ```powershell
17 | ```
18 | + 5 -> foo
19 | ```powershell
20 | ```
21 | + 6 -> foo
22 | ```powershell
23 | ```
24 | + 7 -> foo
25 | ```powershell
26 | ```
27 | + 8 -> foo
28 | ```powershell
29 | ```
30 | + 9 -> foo
31 | ```powershell
32 | ```
33 | + 10 -> foo
34 | ```powershell
35 | ```
36 | + 11 -> foo
37 | ```powershell
38 | ```
39 | + 12 -> foo
40 | ```powershell
41 | ```
42 | + 13 -> foo
43 | ```powershell
44 | ```
45 | + 14 -> search_for_files_containing_string Print all files in the current directory, one per line (not the path, just the filename) that contain the string "500".
46 | ```powershell
47 | grep -sl 500 * .*
48 | ```
49 | + 15 -> Print the relative file paths, one path per line for all filenames that start with "access.log" in the current directory.
50 | ```
51 | find -type f -regex '.*/access.log.*'
52 | ```
53 | + 16 -> search_for_string_in_files_recursive Print all matching lines (without the filename or the file path) in all files under the current directory that start with "access.log" that contain the string "500".
54 | ```powershell
55 | cat `find -type f -regex '.*/access.log.*' | tr \n ' '` | grep 500
56 | ```
57 | + 17 -> extract_ip_addresses Extract all IP addresses from files that start with "access.log" printing one IP address per line.
58 | ```powershell
59 | cat `find -type f -regex '.*/access.log.*' | tr '\n' ' '` | grep -oE '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+'
60 | ```
61 | + 18 -> count_files Count the number of files in the current working directory. Print the number of files as a single integer.
62 | ```powershell
63 | find -type f -regex '.*' | wc -l
64 | ```
65 | + 19 -> simple_sort Print the contents of access.log sorted.
66 | ```powershell
67 | cat access.log | sort
68 | ```
69 | + 20 -> count_string_in_line Print the number of lines in access.log that contain the string "GET".
70 | ```powershell
71 | cat access.log | grep -o GET | wc -l
72 | ```
73 | + 21 -> split_on_a_char The file split-me.txt contains a list of numbers separated by a ; character. Split the numbers on the ; character, one number per line.
74 | ```powershell
75 | cat split-me.txt | tr ';' "\n"
76 | ```
77 | + 22 -> print_number_sequence Print the numbers 1 to 100 separated by spaces.
78 | ```powershell
79 | echo {1..9} {1..9}{0..9} 100
80 | ```
81 | + 23 -> replace_text_in_files This challenge has text files (with a .txt extension) that contain the phrase "challenges are difficult". Delete this phrase from all text files recursively. Note that some files are in subdirectories so you will need to search for them.
82 | ```powershell
83 | find -type f -regex '.*\.txt' -execdir bash -c 'cat "{}" | sed "s/challenges are difficult//g" > "{}"' \;
84 | ```
85 | + 24 -> sum_all_numbers The file sum-me.txt has a list of numbers, one per line. Print the sum of these numbers.
86 | ```powershell
87 | echo $((`cat sum-me.txt | tr '\n' '+' | rev | cut -c 2- | rev`))
88 | ```
89 | + 25 -> just_the_files Print all files in the current directory recursively without the leading directory path.
90 | ```powershell
91 | find -type f -regex '.*' -execdir bash -c 'echo {} | cut -c 3-' \;
92 | ```
93 | + 26 -> remove_extensions_from_files Rename all files removing the extension from them in the current directory recursively.
94 | ```powershell
95 | find -type f -regex '.*' -execdir bash -c 'mv {} `echo {} | sed -r "s/\.[^.\S]+$//g"`' \;
96 | ```
97 | + 27 -> replace_spaces_in_filenames The files in this challenge contain spaces. List all of the files (filenames only) in the current directory but replace all spaces with a '.' character.
98 | ```powershell
99 | find -type f -regex '.*' -execdir bash -c 'echo {} | sed -r "s/\s/./g" | cut -c 3-' \;
100 | ```
101 | + 28 -> dirs_containing_files_with_extension In this challenge there are some directories containing files with different extensions. Print all directories, one per line without duplicates that contain one or more files with a ".tf" extension.
102 | ```powershell
103 | find -type f -regex '.*\.tf$' -exec bash -c 'echo {} | grep -oE ".*/" | cut -c 3- | rev | cut -c 2- | rev' \; | sort | uniq
104 | ```
105 | + 29 -> files_starting_with_a_number There are a mix of files in this directory that start with letters and numbers. Print the filenames (just the filenames) of all files that start with a number recursively in the current directory.
106 | ```powershell
107 | find -type f -regex ".*" -printf "%f\n" | grep -E "^[0-9]"
108 | ```
109 | + 30 -> print_nth_line Print the 25th line of the file faces.txt
110 | ```powershell
111 | head -n 25 faces.txt | tail -n 1
112 | ```
113 | + 31 -> reverse_readme Print the lines of the file reverse-me.txt in this directory in reverse line order so that the last line is printed first and the first line is printed last.
114 | ```powershell
115 | cat reverse-me.txt
116 | ```
117 | + 28 -> foo
118 | ```powershell
119 | ```
120 |
--------------------------------------------------------------------------------