diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b6ecc7c --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.ruff_cache/ +.pre-commit-config.yaml +*.log +tskops-venv/ +__pycache__/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..11df9e8 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,18 @@ +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files + +- repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version + rev: v0.1.6 + hooks: + # Run the linter + - id: ruff + args: [ --fix ] + # Run the formatter + - id: ruff-format diff --git a/Python-tasks/Task-1/Task_01_Domain_Extractor/Problem_01.txt b/Python-tasks/Task-1/Task_01_Domain_Extractor/Problem_01.txt new file mode 100644 index 0000000..9f022c4 --- /dev/null +++ b/Python-tasks/Task-1/Task_01_Domain_Extractor/Problem_01.txt @@ -0,0 +1,5 @@ +Scenario: You have a list of web URLs. + +Goal: Create a script that takes the URL https://api.github.com/v3 and uses slicing or .split() to extract just the domain name (github.com). + +DevOps Value with this task: Parsing logs to see which external services your app is calling. diff --git a/Python-tasks/Task-1/Task_01_Domain_Extractor/solution_01.py b/Python-tasks/Task-1/Task_01_Domain_Extractor/solution_01.py new file mode 100644 index 0000000..74ba505 --- /dev/null +++ b/Python-tasks/Task-1/Task_01_Domain_Extractor/solution_01.py @@ -0,0 +1,18 @@ +urls = [ + "https://api.github.com/v3", + "https://scholar.google.com/", + "https://www.jstor.org/", + "https://www.sciencedirect.com/check/", + "https://www.researchgate.net/test.test/", + "https://www.researchgate.net/v1/v2", +] + +domains = [] +for url in urls: + path = url.split("//")[-1].split("/")[0] + parts = path.split(".")[1:] + domain = ".".join(parts) + domains.append(domain) + + +print(*domains, sep="\n") diff --git a/Python-tasks/Task-1/Task_02_Safe_name/Problem_02.txt b/Python-tasks/Task-1/Task_02_Safe_name/Problem_02.txt new file mode 100644 index 0000000..f9b0a41 --- /dev/null +++ b/Python-tasks/Task-1/Task_02_Safe_name/Problem_02.txt @@ -0,0 +1,9 @@ +Task 2: The "Safe Name" Sanitizer (Strings) +Scenario: A user wants to create a cloud bucket named My Project Backup . Cloud providers don't allow spaces or uppercase. + +Goal: Use string methods to: + +- Strip whitespace. +- Replace spaces with hyphens. +- Convert to lowercase. +DevOps Value with this task: Standardizing resource names in Terraform/Cloud scripts. diff --git a/Python-tasks/Task-1/Task_02_Safe_name/solution_02.py b/Python-tasks/Task-1/Task_02_Safe_name/solution_02.py new file mode 100644 index 0000000..42be987 --- /dev/null +++ b/Python-tasks/Task-1/Task_02_Safe_name/solution_02.py @@ -0,0 +1,9 @@ +bucket_name = "My Project Backup" +# strip string +bucket_name = bucket_name.strip() +# split and lowercase conversion +bucket_name = bucket_name.lower().split(" ") +# join with hyphen +bucket_name = "-".join(bucket_name) + +print(bucket_name) diff --git a/Python-tasks/Task-1/Task_03_Port_Validator/Problem_03.txt b/Python-tasks/Task-1/Task_03_Port_Validator/Problem_03.txt new file mode 100644 index 0000000..a45c237 --- /dev/null +++ b/Python-tasks/Task-1/Task_03_Port_Validator/Problem_03.txt @@ -0,0 +1,6 @@ +Task 3: The Port Validator (Integers & Logic) +Scenario: A firewall script receives a port as a string "8080". + +Goal: Convert the string to an integer and check if it is within the valid range (1–65535). Print "Valid" or "Invalid". + +DevOps Value with this task: Input validation for automation tools. diff --git a/Python-tasks/Task-1/Task_03_Port_Validator/solution_03.py b/Python-tasks/Task-1/Task_03_Port_Validator/solution_03.py new file mode 100644 index 0000000..58408cc --- /dev/null +++ b/Python-tasks/Task-1/Task_03_Port_Validator/solution_03.py @@ -0,0 +1,8 @@ +port = "8080" +# convert string to int +port_number = int(port) +# check port validity +if port_number >= 1 and port_number <= 65535: + print("Valid") +else: + print("Invalid") diff --git a/Python-tasks/Task-1/Task_04_Fleet_Inventory/Problem_04.txt b/Python-tasks/Task-1/Task_04_Fleet_Inventory/Problem_04.txt new file mode 100644 index 0000000..a2f51a8 --- /dev/null +++ b/Python-tasks/Task-1/Task_04_Fleet_Inventory/Problem_04.txt @@ -0,0 +1,6 @@ +Task 4: The Fleet Inventory (Lists) +Scenario: You have a list: servers = ["web01", "db01", "app01", "web02"]. + +Goal: Use slicing to create a new list called web_servers containing only the first and last elements of the original list. + +DevOps Value with this task: Managing subsets of infrastructure (e.g., "only update the first 50% of nodes"). diff --git a/Python-tasks/Task-1/Task_04_Fleet_Inventory/solution_04.py b/Python-tasks/Task-1/Task_04_Fleet_Inventory/solution_04.py new file mode 100644 index 0000000..47fffdb --- /dev/null +++ b/Python-tasks/Task-1/Task_04_Fleet_Inventory/solution_04.py @@ -0,0 +1,4 @@ +servers = ["web01", "db01", "app01", "web02", "web03"] +# get the first and last slice +web_servers = servers[:1] + servers[-1:] +print(web_servers) diff --git a/Python-tasks/Task-1/Task_05_Cloud_Config_Mapper/Problem_05.txt b/Python-tasks/Task-1/Task_05_Cloud_Config_Mapper/Problem_05.txt new file mode 100644 index 0000000..344720a --- /dev/null +++ b/Python-tasks/Task-1/Task_05_Cloud_Config_Mapper/Problem_05.txt @@ -0,0 +1,6 @@ +Task 5: The Cloud Config Mapper (Dictionaries) +Scenario: Create a dictionary representing a Virtual Machine with keys: id, ip, status, and region. + +Goal: Write a script that updates the status from "running" to "stopped" and adds a new key called instance_type with the value "t3.large". + +DevOps Value with this task:: Mimicking JSON responses from AWS/Azure/GCP APIs. diff --git a/Python-tasks/Task-1/Task_05_Cloud_Config_Mapper/solution_05.py b/Python-tasks/Task-1/Task_05_Cloud_Config_Mapper/solution_05.py new file mode 100644 index 0000000..2194115 --- /dev/null +++ b/Python-tasks/Task-1/Task_05_Cloud_Config_Mapper/solution_05.py @@ -0,0 +1,11 @@ +vm = { + "id": "virtual machine 1", + "ip": "222.222.222.222", + "status": "running", + "region": "asia-west", +} + +vm["status"] = "stopped" +vm.update({"instance_type": "t3.large"}) + +print(vm) diff --git a/Python-tasks/Task-1/Task_06_Log_File_Duplicator/Problem_06.txt b/Python-tasks/Task-1/Task_06_Log_File_Duplicator/Problem_06.txt new file mode 100644 index 0000000..b545b53 --- /dev/null +++ b/Python-tasks/Task-1/Task_06_Log_File_Duplicator/Problem_06.txt @@ -0,0 +1,6 @@ +Task 6: The Log File Duplicator (OS & Shutil) +Scenario: You need to test a new log-parsing script without ruining the original files. + +Goal: Write a script that looks for a file named app.log and creates 5 copies of it named app_1.log, app_2.log, etc. + +DevOps Value with this task:: Generating "mock data" for testing pipelines. diff --git a/Python-tasks/Task-1/Task_06_Log_File_Duplicator/solution_06.py b/Python-tasks/Task-1/Task_06_Log_File_Duplicator/solution_06.py new file mode 100644 index 0000000..d43be5a --- /dev/null +++ b/Python-tasks/Task-1/Task_06_Log_File_Duplicator/solution_06.py @@ -0,0 +1,8 @@ +import shutil + +source_file = "app.log" + +for i in range(1, 6): + destination = f"app_{i}.log" + shutil.copy(source_file, destination) + print(f"Created: {destination}") diff --git a/Python-tasks/Task-1/Task_07_The_Prod_Guard/Problem_07.txt b/Python-tasks/Task-1/Task_07_The_Prod_Guard/Problem_07.txt new file mode 100644 index 0000000..2309cff --- /dev/null +++ b/Python-tasks/Task-1/Task_07_The_Prod_Guard/Problem_07.txt @@ -0,0 +1,6 @@ +Task 7: The "Prod" Guard (Booleans & Conditionals) +Scenario: A script is about to delete data. + +Goal: Create a variable env = "production". Write an if statement that only prints "Executing Delete" if env is NOT equal to "production". Otherwise, print "Access Denied: Cannot delete in Prod!" + +DevOps Value with this task:: Implementing safety "gates" in CI/CD pipelines. diff --git a/Python-tasks/Task-1/Task_07_The_Prod_Guard/solution_07.py b/Python-tasks/Task-1/Task_07_The_Prod_Guard/solution_07.py new file mode 100644 index 0000000..397bc94 --- /dev/null +++ b/Python-tasks/Task-1/Task_07_The_Prod_Guard/solution_07.py @@ -0,0 +1,7 @@ +env = "production" + +if env != "production": + print("Executing Delete") + +else: + print("Access Denied: Cannot delete in Prod!") diff --git a/Python-tasks/Task-1/Task_08_The_Theshold_Alert/Problem_08.txt b/Python-tasks/Task-1/Task_08_The_Theshold_Alert/Problem_08.txt new file mode 100644 index 0000000..2e4081f --- /dev/null +++ b/Python-tasks/Task-1/Task_08_The_Theshold_Alert/Problem_08.txt @@ -0,0 +1,6 @@ +Task 8: The Threshold Alert (Floats & Strings) +Scenario: Your monitoring tool returns a CPU load of 0.88. + +Goal: Format this float as a percentage string (e.g., "88%") using f-strings. + +DevOps Value with this task: Creating human-readable alerts for Slack or Email. diff --git a/Python-tasks/Task-1/Task_08_The_Theshold_Alert/solution_08.py b/Python-tasks/Task-1/Task_08_The_Theshold_Alert/solution_08.py new file mode 100644 index 0000000..b4188de --- /dev/null +++ b/Python-tasks/Task-1/Task_08_The_Theshold_Alert/solution_08.py @@ -0,0 +1,2 @@ +cpu_load = 0.88 +print(f"Current cpu load : {int(cpu_load*100)}%") diff --git a/Python-tasks/Task-1/Task_09_The_Path_Builder/Problem_09.txt b/Python-tasks/Task-1/Task_09_The_Path_Builder/Problem_09.txt new file mode 100644 index 0000000..0b95af9 --- /dev/null +++ b/Python-tasks/Task-1/Task_09_The_Path_Builder/Problem_09.txt @@ -0,0 +1,6 @@ +ask 9: The Path Builder (Pathlib/OS) +Scenario: You are working on a script that must run on both Windows and Linux. + +Goal: Use os.path.join to combine the variables base_dir = "/var/log", app_name = "nginx", and filename = "access.log" into one valid path. + +DevOps Value with this task: Avoiding "Hardcoded Path" bugs. diff --git a/Python-tasks/Task-1/Task_09_The_Path_Builder/solution_09.py b/Python-tasks/Task-1/Task_09_The_Path_Builder/solution_09.py new file mode 100644 index 0000000..fcddf48 --- /dev/null +++ b/Python-tasks/Task-1/Task_09_The_Path_Builder/solution_09.py @@ -0,0 +1,8 @@ +import os + +base_dir = "/var/log" +app_name = "nginx" +filename = "access.log" + +path = os.path.join(base_dir, app_name, filename) +print(path) diff --git a/Python-tasks/Task-1/Task_10_The_Secret_Masker/Problem_10.txt b/Python-tasks/Task-1/Task_10_The_Secret_Masker/Problem_10.txt new file mode 100644 index 0000000..0b8dc15 --- /dev/null +++ b/Python-tasks/Task-1/Task_10_The_Secret_Masker/Problem_10.txt @@ -0,0 +1,6 @@ +Task 10: The Secret Masker (Slicing) +Scenario: You accidentally printed an API Key to the console: AKIA1234567890EXAMPLE. + +Goal: Use slicing to print only the first 4 characters followed by asterisks (e.g., AKIA**********). + +DevOps Value with this task: Security and compliance in logging. diff --git a/Python-tasks/Task-1/Task_10_The_Secret_Masker/solution_10.py b/Python-tasks/Task-1/Task_10_The_Secret_Masker/solution_10.py new file mode 100644 index 0000000..62bf4b0 --- /dev/null +++ b/Python-tasks/Task-1/Task_10_The_Secret_Masker/solution_10.py @@ -0,0 +1,3 @@ +SECRET_KEY = "AKIA1234567890EXAMPLE" +encrypted_secret_key = SECRET_KEY[:4] + (len(SECRET_KEY) - 4) * "*" +print(encrypted_secret_key) diff --git a/Python-tasks/task-1.md b/Python-tasks/Task-1/task-1.md similarity index 82% rename from Python-tasks/task-1.md rename to Python-tasks/Task-1/task-1.md index 5159eb1..d51cd14 100644 --- a/Python-tasks/task-1.md +++ b/Python-tasks/Task-1/task-1.md @@ -15,85 +15,85 @@ Once it is done, push it to a separate GitHub repository for future use -------- ### Task 1: The Domain Extractor (Slicing & Strings) -**Scenario:** You have a list of web URLs. +**Scenario:** You have a list of web URLs. -**Goal:** Create a script that takes the URL `https://api.github.com/v3` and uses **slicing** or `.split()` to extract just the domain name (`github.com`). +**Goal:** Create a script that takes the URL `https://api.github.com/v3` and uses **slicing** or `.split()` to extract just the domain name (`github.com`). **DevOps Value with this task:** Parsing logs to see which external services your app is calling. ### Task 2: The "Safe Name" Sanitizer (Strings) -**Scenario:** A user wants to create a cloud bucket named `My Project Backup` . Cloud providers don't allow spaces or uppercase. +**Scenario:** A user wants to create a cloud bucket named `My Project Backup` . Cloud providers don't allow spaces or uppercase. -**Goal:** Use string methods to: -1. Strip whitespace. -2. Replace spaces with hyphens. -3. Convert to lowercase. +**Goal:** Use string methods to: +1. Strip whitespace. +2. Replace spaces with hyphens. +3. Convert to lowercase. **DevOps Value with this task:** Standardizing resource names in Terraform/Cloud scripts. ### Task 3: The Port Validator (Integers & Logic) -**Scenario:** A firewall script receives a port as a string `"8080"`. +**Scenario:** A firewall script receives a port as a string `"8080"`. -**Goal:** Convert the string to an integer and check if it is within the valid range (1–65535). Print "Valid" or "Invalid". +**Goal:** Convert the string to an integer and check if it is within the valid range (1–65535). Print "Valid" or "Invalid". **DevOps Value with this task:** Input validation for automation tools. ### Task 4: The Fleet Inventory (Lists) -**Scenario:** You have a list: `servers = ["web01", "db01", "app01", "web02"]`. +**Scenario:** You have a list: `servers = ["web01", "db01", "app01", "web02"]`. -**Goal:** Use **slicing** to create a new list called `web_servers` containing only the first and last elements of the original list. +**Goal:** Use **slicing** to create a new list called `web_servers` containing only the first and last elements of the original list. **DevOps Value with this task:** Managing subsets of infrastructure (e.g., "only update the first 50% of nodes"). ### Task 5: The Cloud Config Mapper (Dictionaries) -**Scenario:** Create a dictionary representing a Virtual Machine with keys: `id`, `ip`, `status`, and `region`. +**Scenario:** Create a dictionary representing a Virtual Machine with keys: `id`, `ip`, `status`, and `region`. -**Goal:** Write a script that updates the `status` from `"running"` to `"stopped"` and adds a new key called `instance_type` with the value `"t3.large"`. +**Goal:** Write a script that updates the `status` from `"running"` to `"stopped"` and adds a new key called `instance_type` with the value `"t3.large"`. **DevOps Value with this task::** Mimicking JSON responses from AWS/Azure/GCP APIs. ### Task 6: The Log File Duplicator (OS & Shutil) -**Scenario:** You need to test a new log-parsing script without ruining the original files. +**Scenario:** You need to test a new log-parsing script without ruining the original files. -**Goal:** Write a script that looks for a file named `app.log` and creates 5 copies of it named `app_1.log`, `app_2.log`, etc. +**Goal:** Write a script that looks for a file named `app.log` and creates 5 copies of it named `app_1.log`, `app_2.log`, etc. **DevOps Value with this task::** Generating "mock data" for testing pipelines. ### Task 7: The "Prod" Guard (Booleans & Conditionals) -**Scenario:** A script is about to delete data. +**Scenario:** A script is about to delete data. -**Goal:** Create a variable `env = "production"`. Write an `if` statement that only prints "Executing Delete" if `env` is NOT equal to "production". Otherwise, print "Access Denied: Cannot delete in Prod!" +**Goal:** Create a variable `env = "production"`. Write an `if` statement that only prints "Executing Delete" if `env` is NOT equal to "production". Otherwise, print "Access Denied: Cannot delete in Prod!" **DevOps Value with this task::** Implementing safety "gates" in CI/CD pipelines. ### Task 8: The Threshold Alert (Floats & Strings) -**Scenario:** Your monitoring tool returns a CPU load of `0.88`. +**Scenario:** Your monitoring tool returns a CPU load of `0.88`. -**Goal:** Format this float as a percentage string (e.g., `"88%"`) using **f-strings**. +**Goal:** Format this float as a percentage string (e.g., `"88%"`) using **f-strings**. **DevOps Value with this task:** Creating human-readable alerts for Slack or Email. ### Task 9: The Path Builder (Pathlib/OS) -**Scenario:** You are working on a script that must run on both Windows and Linux. +**Scenario:** You are working on a script that must run on both Windows and Linux. -**Goal:** Use `os.path.join` to combine the variables `base_dir = "/var/log"`, `app_name = "nginx"`, and `filename = "access.log"` into one valid path. +**Goal:** Use `os.path.join` to combine the variables `base_dir = "/var/log"`, `app_name = "nginx"`, and `filename = "access.log"` into one valid path. **DevOps Value with this task:** Avoiding "Hardcoded Path" bugs. ### Task 10: The Secret Masker (Slicing) -**Scenario:** You accidentally printed an API Key to the console: `AKIA1234567890EXAMPLE`. +**Scenario:** You accidentally printed an API Key to the console: `AKIA1234567890EXAMPLE`. -**Goal:** Use slicing to print only the first 4 characters followed by asterisks (e.g., `AKIA**********`). +**Goal:** Use slicing to print only the first 4 characters followed by asterisks (e.g., `AKIA**********`). **DevOps Value with this task:** Security and compliance in logging. ----------CHEERS!-------- \ No newline at end of file +---------CHEERS!-------- diff --git a/Python-tasks/Task-2/Task_01_The_Logic_Master_FizzBuzz/Problem_01.txt b/Python-tasks/Task-2/Task_01_The_Logic_Master_FizzBuzz/Problem_01.txt new file mode 100644 index 0000000..b27fb7b --- /dev/null +++ b/Python-tasks/Task-2/Task_01_The_Logic_Master_FizzBuzz/Problem_01.txt @@ -0,0 +1,16 @@ +Objective: Master conditional logic and loops. + +Requirements: +Create a function named fizzbuzz_checker(limit). + +The function should use a for loop to iterate from 1 to the limit. + +Logic Rules: + +If the number is divisible by both 3 and 5, print "FizzBuzz". + +If divisible by 3, print "Fizz". + +If divisible by 5, print "Buzz". + +Otherwise, just print the number itself. diff --git a/Python-tasks/Task-2/Task_01_The_Logic_Master_FizzBuzz/solution_01.py b/Python-tasks/Task-2/Task_01_The_Logic_Master_FizzBuzz/solution_01.py new file mode 100644 index 0000000..3ae0bf3 --- /dev/null +++ b/Python-tasks/Task-2/Task_01_The_Logic_Master_FizzBuzz/solution_01.py @@ -0,0 +1,13 @@ +def fizzbuzz_checker(limit): + for i in range(1, limit + 1): + if i % 3 == 0 and i % 5 == 0: + print("FizzBuzz") + elif i % 3 == 0: + print("Fizz") + elif i % 5 == 0: + print("Buzz") + else: + print(i) + + +fizzbuzz_checker(15) diff --git a/Python-tasks/Task-2/Task_02_The_Security_AuditorSafe_name/Problem_02.txt b/Python-tasks/Task-2/Task_02_The_Security_AuditorSafe_name/Problem_02.txt new file mode 100644 index 0000000..b114ec0 --- /dev/null +++ b/Python-tasks/Task-2/Task_02_The_Security_AuditorSafe_name/Problem_02.txt @@ -0,0 +1,16 @@ +Objective: Practice cross-file imports and using immutable data types. + +Requirements: +Create scanner.py (The Module): + +Define a Tuple named DANGEROUS_PORTS containing (21, 23, 445). + +Create a function check_ports(active_ports) that loops through a list and prints a warning if a port is found in your DANGEROUS_PORTS tuple. + +Create app.py (The Execution Script): + +Import the scanner module. + +Define a list of ports: [22, 80, 21, 443, 23, 8080]. + +Call the function from your module to audit the list. diff --git a/Python-tasks/Task-2/Task_02_The_Security_AuditorSafe_name/app.py b/Python-tasks/Task-2/Task_02_The_Security_AuditorSafe_name/app.py new file mode 100644 index 0000000..1055105 --- /dev/null +++ b/Python-tasks/Task-2/Task_02_The_Security_AuditorSafe_name/app.py @@ -0,0 +1,5 @@ +from scanner import check_ports + +ports = [22, 80, 21, 443, 23, 8080] + +check_ports(ports) diff --git a/Python-tasks/Task-2/Task_02_The_Security_AuditorSafe_name/scanner.py b/Python-tasks/Task-2/Task_02_The_Security_AuditorSafe_name/scanner.py new file mode 100644 index 0000000..687242e --- /dev/null +++ b/Python-tasks/Task-2/Task_02_The_Security_AuditorSafe_name/scanner.py @@ -0,0 +1,7 @@ +DANGEROUS_PORTS = (21, 23, 445) + + +def check_ports(active_ports): + for port in active_ports: + if port in DANGEROUS_PORTS: + print(f"Dangerous port found: {port}") diff --git a/Python-tasks/Task-2/Task_03_The_System_Health_Check/Problem_03.txt b/Python-tasks/Task-2/Task_03_The_System_Health_Check/Problem_03.txt new file mode 100644 index 0000000..eaab6b8 --- /dev/null +++ b/Python-tasks/Task-2/Task_03_The_System_Health_Check/Problem_03.txt @@ -0,0 +1,15 @@ +Objective: Safely handle external data strings using json and try/except. + +Requirements: +Create a function process_server_data(json_string). + +Inside the function, use json.loads() to parse the input. + +Wrap the parsing logic in a try/except block to catch json.JSONDecodeError. + +If successful, loop through the resulting list of servers and print the status of each. + +Test Data: +Use this string to test your function: + +mock_api = '{"servers": [{"name": "web-01", "status": "up"}, {"name": "db-01", "status": "down"}]}' diff --git a/Python-tasks/Task-2/Task_03_The_System_Health_Check/solution_03.py b/Python-tasks/Task-2/Task_03_The_System_Health_Check/solution_03.py new file mode 100644 index 0000000..7c11bb1 --- /dev/null +++ b/Python-tasks/Task-2/Task_03_The_System_Health_Check/solution_03.py @@ -0,0 +1,17 @@ +import json + + +def process_server_data(json_string): + try: + res = json.loads(json_string) + servers = res["servers"] + + for server in servers: + print(f"server => {server['name']} & status => {server['status']}") + + except json.JSONDecodeError as e: + print(f"Error found => {str(e)}") + + +mock_api = '{"servers": [{"name": "web-01", "status": "up"}, {"name": "db-01", "status": "down"}]}' +process_server_data(mock_api)