Text Wrap in Python

I was working on a problem at HackerRank. It’s called text wrap problem. I was able to solve it with the following code.

def wrap(string, max_width):
    result = ''
    lines = len(string) / max_width
    last_chars = len(string) % max_width
    for i in range(int(lines)):
        start_index = i * max_width
        result += string[start_index:start_index+max_width] + '\n'

    if last_chars > 0:
        result += string[-last_chars:]
    return result

Then, I headed to Discussions section to see if a smarter person posted a better and more concise code. There was one.

def wrap(string, max_width):
    return "\n".join([string[i:i+max_width] for i in range(0, len(string), max_width)])

In essence, it’s pretty much the same logic but it’s more concise though I think it’s kind of harder to read. One of the important things we have to consider when coding is whether the code is maintainable and readable. But there are smarter people out there… Always learning.

Validating Downloaded File with File Size from Object Storage on OCI

This is a note for myself.

#!/usr/bin/env python3

import oci.object_storage
import urllib3
import os

def download_backup(bucket_name, file_name, local_dir):
    signer = oci.auth.signers.InstancePrincipalsSecurityTokenSigner()
    object_client = oci.object_storage.ObjectStorageClient(config={},signer=signer)

    object = object_client.get_object('id4qji14rv70', bucket_name, file_name)
    restored_file = os.path.join(local_dir, file_name)
    with open(restored_file, 'wb') as f:
        for chunk in object.data.raw.stream(1024 * 1024, decode_content=False):
            f.write(chunk)

    object_meta = object_client.head_object('id4qji14rv70', bucket_name, file_name)
    content_length=object_meta.headers['Content-Length']

    file_stats = os.stat(file_name)

    if file_stats.st_size == int(content_length):
        print(f"Validated {content_length}")
    else:
        print(f"Validation failed. Expected: {content_length} Actual: {file_stats.st_size}")

if __name__ == '__main__':
    download_backup('backup', '2022-03-21.zip', '/home/opc')

Managing Your Own DNS on OCI

You can manage your public DNS for your domain on OCI.

But first, you have to change the DNS delegation to OCI’s name servers like the following.

Whatever domain registerer you use, I am pretty sure you can change name servers for your domain. Once you change the domain delegation, you can start to manage your DNS records within OCI console.

Once you navigate to DNS Management on OCI console, click Create Zone.

I have iriumi.study as a domain that I want to be hosted on OCI, so I am going to create it.

Once you create the zone, you can add records.

Select A record for the record type and enter the public IP address you want to assign. I am leaving subdomain as blank because I want the name to resolve to the IP address without any subdomain. Also enter the value of TTL. TTL stands for time to live and it’s a duration how long the DNS resolution is cached before reaches back out to collect new and updated details. I am setting it 60 seconds for now but longer or shorter TTL has their own purposes, so I’d recommend that you look it up.

Now you can click Submit button at the bottom of the screen.

Then, click Publish Changes button to get it published.

If you go to DNS Checker and see how it is propagating around the world, you get to see something like this.

How to Validate a Big Downloaded File from Object Storage (OCI)

When you upload relatively a big file to Object Storage in OCI, it doesn’t have the MD5 hash ready for you. It’s because the big file is split into multi parts and they are uploaded into separate space. Then, when you download the file, the multi parts are downloaded sequentially and they are put into one file on the client side. Object Storage does not calculate the MD5 hash putting the multi parts together on the service side due to its sheer required processing power it may need. When you try to view the information of the file on Object Storage, you don’t see the actual MD5 hash.

Tough opc-multipart-md5 looks promising, that’s just a part of the whole thing. To get my point crossed, when I uploaded a small file, MD5 hash is calculated and available on the service side.

Now, how do we solve this problem? The best way is to calculate the MD5 hash before you upload the file with md5sum and then attach the MD5 hash to metadata when uploading the file to Object Storage.

You can get the data by executing the following command.

 oci os object head --auth instance_principal -bn backup --name 2022-03-20.zip

Here is the data you get as JSON.

{
  "accept-ranges": "bytes",
  "access-control-allow-credentials": "true",
  "access-control-allow-methods": "POST,PUT,GET,HEAD,DELETE,OPTIONS",
  "access-control-allow-origin": "*",
  "access-control-expose-headers": "accept-ranges,access-control-allow-credentials,access-control-allow-methods,access-control-allow-origin,content-length,content-type,date,etag,last-modified,opc-client-info,opc-client-request-id,opc-meta-md5hash,opc-multipart-md5,opc-request-id,storage-tier,version-id,x-api-id",
  "content-length": "217286450",
  "content-type": "application/octet-stream",
  "date": "Sun, 20 Mar 2022 04:42:05 GMT",
  "etag": "500168df-c90a-4d35-b4f6-c7a6c99d5969",
  "last-modified": "Sun, 20 Mar 2022 04:40:27 GMT",
  "opc-client-request-id": "92C495DFAA8647C4B230B10580FED145",
  "opc-meta-md5hash": "1eed774bb61c15f8c50c7771e71bbb24",
  "opc-multipart-md5": "i1Ap4X2OnVAU7aK8RwxgMg==-2",
  "opc-request-id": "iad-1:mHbU0Aq4kW9abs3NCSv77cOKPDYdcQ74lsT4sPgfDI44xXLWLwYk8MKcX3WmPE7L",
  "storage-tier": "Standard",
  "version-id": "29ba4883-5fb3-4316-acbc-ceb218b5e3d1",
  "x-api-id": "native"
}

So the process would be to execute oci os object head on the object you are going to download and keep the MD5 hash in a variable. Then once you download the file, have md5sum calculate the MD5 hash on the downloaded file. And then see if the calculated MD5 matches the one from oci os object head.

Here is the bash script I came up with to upload the file with the metadata.

rm -rf /home/opc/backup.zip
zip -r /home/opc/backup.zip /home/opc/wordpress/*

md5=`md5sum backup.zip | awk '{ print $1 }'`
filename=`date +%Y-%m-%d`.zip
json='{"md5hash":''"'$md5'"}'
oci os object put --auth instance_principal -bn backup --file backup.zip --name $filename --force --metadata $json

I have cron’ed the bash script to run every day so the file backup is automated.

Bash Scripts

Bash scripting is my weakness as an engineer. I don’t like bash. Here are the reasons I don’t like it.

  • Bash depends on standard output. If you need to work on a complex scripting, it easily gets ugly dealing with a bunch of string mambo jumbo with regex.
  • Commands are each independent executable.
  • You cannot create functions with parameters. You have to use variables always at the global level.

So as a strong alternative, I use Python most of the time. Because I don’t prefer to use Bash script, my skill in bash scripting is limited. I believe bash does have a place to be but I tend to avoid it.

That said, I think I should spend more time on Bash to be at least fluent with it. I don’t consider myself to be fluent yet.

I used this bash script to install and configure openvpn. This is one of the most amazing script I have seen. There is a lot to learn from it.

My Linux Mint Desktop is Broken

My Linux Mint desktop has been broken. Somehow it doesn’t boot anymore. I tried to reinstall the OS but it still is not.

When I booted it and went into the BIOS screen, I saw a very high CPU temperature. It was like 91C but I went ahead and kept on using it anyway. It must have damaged the CPU itself and possibly the motherboard.

So the recovery plan I am thinking about is to buy new CPU, motherboard and memory and reinstall the whole thing. I am planning to save some money for it.

What’s wrong with the Container?

I wanted to provision a Japanese version of my website but one of the containers kept on restarting. I couldn’t figure out what’s wrong with it for a while.

I finally figured out how to view the log of the container.

docker logs --tail 50 --follow --timestamps [container ID]

Here is the log output.

2022-03-13T05:46:44.573226016Z /docker-entrypoint.sh: Configuration complete; ready for start up
2022-03-13T05:46:44.579540345Z 2022/03/13 05:46:44 [emerg] 1#1: cannot load certificate "/home/opc/wordpress/nginx/conf.d/ssl/cert1.pem": BIO_new_file() failed (SSL: error:02001002:system library:fopen:No such file or directory:fopen('/home/opc/wordpress/nginx/conf.d/ssl/cert1.pem','r') error:2006D080:BIO routines:BIO_new_file:no such file)
2022-03-13T05:46:44.579598505Z nginx: [emerg] cannot load certificate "/home/opc/wordpress/nginx/conf.d/ssl/cert1.pem": BIO_new_file() failed (SSL: error:02001002:system library:fopen:No such file or directory:fopen('/home/opc/wordpress/nginx/conf.d/ssl/cert1.pem','r') error:2006D080:BIO routines:BIO_new_file:no such file)

Of course, I made a mistake in specifying the path of the certificate files. When I corrected it, the site came up correctly.

C++

I’m going through the basics of C++ on this site. I am imitating the code on the site and actually compile it and execute it. So far, C++ is kind of too much… Here is the example I’m talking about.

#include <iostream>
#include <string>

class Person {
public:
    Person() = default;
    Person(int age) 
      : age(age) {

    }

    Person(int age, std::string name)
      : age(age), name(std::move(name)) {

    }

    int age = 25;
    std::string name = "Unknown";
};


int main() {
    Person person;
    std::cout << person.name << " is " << person.age << "\n";

    Person person2 = Person(40);
    std::cout << person2.name << " is " << person2.age << "\n";

    Person person3 = Person(33, "Johnny");
    std::cout << person3.name << " is " << person3.age << "\n";
}

Here is the code in Python that generates the same result.

class Person:
    def __init__(self, age=None, name=None):
        self.age = age if age is not None else 25
        self.name = name if name is not None else "Unknown"

if __name__ == '__main__':
    person = Person()
    print(f"{person.name} is {person.age}")

    person2 = Person(40)
    print(f"{person2.name} is {person2.age}")

    person3 = Person(33, "Johnny")
    print(f"{person3.name} is {person3.age}")

Now that I see the difference, I wonder if I’m going to continue with C++ exploration. C++ still fascinates me, so I might continue a little more. Hmm I don’t really have a motivation to create an application C++ because of the lack of syntax sugar, so…

DMZ

DMZ in computer network world is an area in the network where there is no protection. DMZ in real world is where there is no military force but if you put an unpatched computer in DMZ, the computer would be totally infected and it would be in a very bad condition. So what I’m thinking is DMZ should be named differently. I think it should be like Exposed Zone (EZ?) or Unprotected Zone (UZ).

I mean it is possible to put your computer in DMZ but who does it nowadays anyway?