Month: April 2019

Cheapest VPS in South Africa

What company provides the cheapest VPS in South Africa.

These days we all need the cloud (also known as computers that you don't own physically) to host our websites, API's, hold our (or customer) data and run applications.

Recently I needed an elasticsearch application set up to make monitoring and analysing my API gateway easier. I tried to install that on my 1Gb VPS and it complained. Thanks JVM:


There is insufficient memory for the Java Runtime Environment to continue

So I went looking for a cheap Virtual Private Server that is on the Linux platform and based in South Africa (because my clients are in South Africa and latecy will be faster 20 milliseconds as opposed to 200ms when the server is in europe).

I have used either domains.co.za or cloudafrica.net, as they were the cheapest options I had found.

Any included services like backup are disregarded, also services that only provide hard drives like afrihost are also excluded.

But I am going to do a bit more research to try and find a cheaper deal for a virtual private server in South Africa, my finds are below:

CompanyMemory (GB)CPU (Cores)Storage (GB)Price (R/month)
cloudafrica.net4448305
domains.co.za44125498
hostafrica.co.za43100415
vps.co.za42100400
telasera.com4?120399
cloud.co.za42100340
hostking.co.za4250539
web4africa.co.za441501350
web-telecoms.co.za42100399
1-grid.com42100519
paradigmsolutions.co.za42100899

 

If you have found a cheaper deal for servers hosted in South Africa, please let me know.

 

Generate a letencrypt ssl certificate for kong api gateway

So you have installed kong and you are ready for it to go into production. Whoops, nossl certificate yet? It is important that you add it as credentials will be moving between your gateway and credentials could be acquired by any party in between the client and your server.

But the standalone nginx plugin for cerbot does not work from my testing. As it is built on nginx, kong is not nginx.

Easiest Way is to Install Nginx

If Kong is running on port 80 then stop it: sudo systemctl stop kong

Install Nginx:


sudo apt install nginx

Install Certbot for nginx on your operating system

Get your certificates with the cert-only option:

sudo certbot --nginx certonly

When that completes it will tell you where your certificates and keyfile are:


Congratulations! Your certificate and chain have been saved at: /etc/letsencrypt/live/api.fixes.co.za/fullchain.pem
Your key file has been saved at: /etc/letsencrypt/live/api.fixes.co.za/privkey.pem

Add these locations to ssl_cert and ssl_cert_key respectively.
Also make sure ssl = on

Stop Nginx:


sudo systemctl stop nginx
sudo systemctl start kong

Remove nginx:


sudo apt remove nginx

Adding Time taken to respond to a request in the header of a Django Rest Framework Response. A suitable place to use a Mixin or not?

Ever wanted to add the time taken for a response to your API, so the client knows how long that takes?
I first noticed this cool feature on AWX, a management platform for ansible playbooks.

There's response headers looked like this:

awx-api-timed-response

So I checked out their source code and copied how they did it:

In generics.py the package extends the base DRF ApiView class and adds a bit of stuff, the important stuff for us is:


    def finalize_response(self, request, response, *args, **kwargs):
        ...
        response = super().finalize_response(request, response, *args, **kwargs)
        time_started = getattr(self, 'time_started', None)
        response['X-API-Node'] = settings.CLUSTER_HOST_ID
        ...
        
    def initialize_request(self, request, *args, **kwargs):
        ...
        self.time_started = time.time()
        ...

So I created a class extending for ApiView:


class APIView(views.APIView):
    '''
    Add timing to the base APIView class
    '''
    def initialize_request(self, request, *args, **kwargs):
        self.time_started = time.time()
        return super().initialize_request(request, *args, **kwargs)
        
    def finalize_response(self, request, response, *args, **kwargs):
        response = super().finalize_response(request, response, *args, **kwargs)
        time_started = getattr(self, 'time_started', None)
        if time_started:
            time_elapsed = time.time() - self.time_started
            response['X-API-Time'] = '%0.3fs' % time_elapsed
        return response

Now all I needed to do was entend from my_package.ApiView instead of restframework.views.ApiView and I would get an X-API-Time header.

It worked well for a while, but then I needed to use more Generic Class Based Views (Which extend from ApiView)...check out CDRF. So I could just add these methods, to the extended versions of restframework.generics.ListCreateView etc.

But that just doesn't feel right. Simply because I can see I am repeating myself multiple times. I just be able to define this functionality once and have all similar objects (those that extend from ApiView) have that functionality...

Using a Mixin to add the Time taken to Respond

I just created a mixin, by extracting those functions into a single class that inherits from nothing:


class TimedAPIMixin:
    def initialize_request(self, request, *args, **kwargs):
        self.time_started = time.time()
        return super().initialize_request(request, *args, **kwargs)

    def finalize_response(self, request, response, *args, **kwargs):
        response = super().finalize_response(request, response, *args, **kwargs)
        time_started = getattr(self, 'time_started', None)
        if time_started:
            time_elapsed = time.time() - self.time_started
            response['X-API-Time'] = '%0.3fs' % time_elapsed
        return response

Using them in the other views:

 

 


from rest_framework import views
from rest_framework import generics


class APIView(TimedAPIMixin, views.APIView):
    pass


class ListCreateAPIView(TimedAPIMixin, generics.ListCreateAPIView):
    pass


class ListAPIView(TimedAPIMixin, generics.ListAPIView):
    pass

It only works if the mixin is the first thing it inherits from, otherwise the first class will take preference.