Red Hat Developers: Use the Kubernetes Python client from your running Red Hat OpenShift pods

Red Hat OpenShift is part of the Cloud Native Computing Foundation (CNCF) Certified Program, ensuring portability and interoperability for your container workloads. This also allows you to use Kubernetes tools to interact with an OpenShift cluster, like kubectl, and you can rest assured that all the APIs you know and love are right there at your fingertips.

The Kubernetes Python client is another great tool for interacting with an OpenShift cluster, allowing you to perform actions on Kubernetes resources with Python code. It also has applications within a cluster. We can configure a Python application running on OpenShift to consume the OpenShift API, and list and create resources. We could then create containerized batch jobs from the running application, or a custom service monitor, for example. It sounds a bit like “OpenShift inception,” using the OpenShift API from services created using the OpenShift API.

In this article, we’ll create a Flask application running on OpenShift. This application will use the Kubernetes Python client to interact with the OpenShift API, list other pods in the project, and display them back to the user.

You’ll need a couple of things to follow along:

  • An OpenShift cluster
  • A working knowledge of Python

Let’s get started!

Setup

I’ve created a template to alleviate a lot of the boilerplate, so let’s clone it down:

git clone https://github.com/shaneboulden/openshift-client-demo cd openshift-client-demo 

You can create a new app on your OpenShift cluster using the provided template and see the application running:

oc new-app openshift_deploy/ocd.yaml 

If you do an oc get routes, you’ll be able to see the route that’s been created. For now, if you select the Pods menu item you’ll just get some placeholder text. We’ll fix this shortly ๐Ÿ™‚

pods_placeholder

Configure the Kubernetes Python client

Listing pods is trivial once we have our client configured, and, fortunately, we can use a little Kubernetes Python client magic to configure this easily with the correct service account token.

Usually, we’d configure a Kubernetes client using a kubeconfig file, which has the required token and hostname to create API requests. The Kubernetes Python client also provides a method load_incluster_config(), which replaces the kubeconfig file in a running pod, instead using the available environment variables and mount points to find the service account token and build API URLs from the information available within the pod.

There’s another huge benefit to using load_incluster_config()โ€”our code is now portable. We can take this same application to any Kubernetes cluster, assume nothing about hostnames or network addresses, and easily construct API requests using this awesome little method.

Let’s configure our application to use the load_incluster_config() method. First, we need to import the client and config objects, you can verify this in the ocd.py file:

from kubernetes import client, config 

We can now use that magic method to configure the client:

config.load_incluster_config() v1 = client.CoreV1Api() 

That’s it! This is all of the code we need to be able to interact with the OpenShift API from running pods.

Use the Kubernetes Downward API

I’m going to introduce something new here, and yes, it’s another “OpenShift-inception” concept. We’re going to use the list_namespaced_pod method to list pod details; you can find all of the methods available in the documentation. To use this method, we need to pass the current namespace (project) to the Kubernetes client object. But wait, how do we get the namespace for our pod, from inside the running pod?

This is where another awesome Kubernetes API comes into play. It’s called the Downward API and allows us to access metadata about our pod from inside the running pod. To expose information from the Downward API to our pod, we can use environment variables. If you look at the template, you’ll see the following in the ‘env’ section:

- name: POD_NAMESPACE   valueFrom:     fieldRef:       apiVersion: v1       fieldPath: metadata.namespace 

Bring it all together

Now let’s get back to our /pods route in the ocd.py file. The last thing we need to do is to pass the namespace of the app to the Kubernetes client. We have our environment variable configured to use the downward API already, so let’s pass it in:

pods = v1.list_namespaced_pod(namespace=os.environ["POD_NAMESPACE"]) 

Ensure you’re in the top-level project directory (i.e., you can see the README) and start a build from the local directory:

oc start-build openshift-client-demo --from-dir=. 

When you next visit the route and select the Pods menu, you’ll be able to see all of the pods for the current namespace:

pods

I hope you’ve enjoyed this short introduction to the Kubernetes Python client. If you want to explore a little deeper, you can look at creating resources. There’s an example here that looks at creating containerized batch jobs from API POSTs.

Share

The post Use the Kubernetes Python client from your running Red Hat OpenShift pods appeared first on Red Hat Developer Blog.

Planet Python

Abhijeet Pal: Python Program To Print Numbers From 1 to 10 Using For Loop

Problem Definition

Create a Python program to print numbers from 1 to 10 using a for loop.

Solution

In programming, Loops are used to repeat a block of code until a specific condition is met. A forย loop is a repetition control structure that allows you to efficiently write a loop that needs to execute a specific number of times.

Also, we are going to use one of Pythonโ€™s built-in function range(). This function is extensively used in loops to control the number of types loop have to run. In simple words range is used to generate a sequence between the given values.

For a better understanding of these Python, concepts it is recommended to read the following articles.

Program

for i in range(1, 11):     print(i) 

Output

1 2 3 4 5 6 7 8 9 10

Explanation

The for loop prints the number from 1 to 10 using the range() function here i is a temporary variable which is iterating over numbers from 1 to 10.

Itโ€™s worth mentioning that similar to list indexing in range starts from 0 which meansย range( j )will print sequence tillย ( j-1)ย hence the output doesnโ€™t include 6.

The post Python Program To Print Numbers From 1 to 10 Using For Loop appeared first on Django Central.

Planet Python

Mike Driscoll: How to Extract Build Info from Jenkins with Python

I work with continuous integration software as a part of my job. I use both Hudson and Jenkins in my role and occasionally need to interact with them programmatically. There are two Python packages you can use for this task:

The Python Jenkins package will work with both Hudson and Jenkins which JenkinsAPI only works with Jenkins. I usually use Python Jenkins because of this, although I have recently started looking to see which one works better with artifacts and I discovered that JenkinsAPI is actually better for that sort of thing. So you will need to evaluate both of these packages depending on what you need to do.


Install Python Jenkins

To follow along with the code examples in this article, you will need to install Python Jenkins. You can use pip for that:

pip install python-jenkins

Now that it’s installed, let’s give Python Jenkins a whirl!


Getting All the Jobs from Jenkins

One common task is needing to get a listing of all the jobs that are configured in your build system.

To get started, you need to login to your Jenkins server:

import jenkins   server = jenkins.Jenkins('http://server:port/', username='user',                           password='secret')

Now you have a Jenkins object that you can use to execute REST requests against your Jenkins CI server. The results that are returned are usually a Python dictionary or dictionary of dictionaries.

Here’s an example getting all the jobs that are configured on your CI system:

import jenkins   server = jenkins.Jenkins('http://server:port/', username='user',                          password='secret')   # Get all builds jobs = server.get_all_jobs(folder_depth=None) for job in jobs:     print(job['fullname'])

This will loop through all the jobs that are configured in Jenkins and print out their job names.


Getting Job Information

Now that you know the names of the jobs on your Jenkins box, you can get more detailed information about each job.

Here’s how:

import jenkins   server = jenkins.Jenkins('http://server:port/', username='user',                          password='secret')   # Get information on specific build job # This returns all the builds that are currently shown in  # hudson for this job info = server.get_job_info('job-name')   # Passed print(info['lastCompletedBuild'])   # Unstable print(info['lastUnstableBuild'])   # Failed print(info['lastFailedBuild'])

The get_job_info() will give you a lot of information about the job, including all the currently saved builds. It is nice to be able to extract which builds have passed, failed or are unstable.


Getting Build Information

If you want to know how long a job takes to run, then you need to get down to the build level.

Let’s find out how:

import jenkins   server = jenkins.Jenkins('http://server:port/', username='user',                          password='secret')   info = server.get_job_info('job-name')   # Loop over builds builds = info['builds'] for build in builds:     for build in builds:         print(server.get_build_info('job-name',                                      build['number']))

To get build metadata, you need to call get_build_info(). This method takes in the job name and the build number and returns the metadata as a dictionary.


Wrapping Up

You can do a lot more with the Python Jenkins package. For example, you can use it to start a build job, create a new job or delete an old one as well as quite a few other things. Unfortunately, the documentation is pretty bare-bones, so you’ll have to do a fair bit of experimentation to get it working the way you want.


Additional Reading

Planet Python

PyBites: How to Create and Serve Zipfiles from Django

We added support to our platfom for bulk downloading of all your code submissions. This feature required creating and serving up zipfiles through Django. In this article I show you how to do it creating a simple Django app collecting code snippets through the admin interface, and serving them up in a zipfile via a download endpoint. Let’s dive straight in …

Setup

First we make a virtual env, set a secret key in our venv and install Django:

[bobbelderbos@imac code]$   mkdir django-archive [bobbelderbos@imac code]$   cd $  _ [bobbelderbos@imac django-archive]$   python3.7 -m venv venv [bobbelderbos@imac django-archive]$   echo "export SECRET_KEY='abc123.;#'" >> venv/bin/activate [bobbelderbos@imac django-archive]$   source venv/bin/activate (venv) [bobbelderbos@imac django-archive]$   pip install django Collecting django ... Successfully installed django-2.2 pytz-2019.1 sqlparse-0.3.0 

Now let’s create a project and app in Django. Don’t forget the extra dot in the startproject command to not create an extra subdirectory.

(venv) [bobbelderbos@imac django-archive]$   django-admin startproject snippets . (venv) [bobbelderbos@imac django-archive]$   django-admin startapp archive (venv) [bobbelderbos@imac django-archive]$   tree -L 2 . โ”œโ”€โ”€ archive โ”‚ย ย  โ”œโ”€โ”€ __init__.py โ”‚ย ย  โ”œโ”€โ”€ admin.py โ”‚ย ย  โ”œโ”€โ”€ apps.py โ”‚ย ย  โ”œโ”€โ”€ migrations โ”‚ย ย  โ”œโ”€โ”€ models.py โ”‚ย ย  โ”œโ”€โ”€ tests.py โ”‚ย ย  โ””โ”€โ”€ views.py โ”œโ”€โ”€ manage.py โ”œโ”€โ”€ snippets โ”‚ย ย  โ”œโ”€โ”€ __init__.py โ”‚ย ย  โ”œโ”€โ”€ settings.py โ”‚ย ย  โ”œโ”€โ”€ urls.py โ”‚ย ย  โ””โ”€โ”€ wsgi.py โ””โ”€โ”€ venv     ... 

Make sure we add the new app to Django’s config:

snippets/settings.py

INSTALLED_APPS = [     ...     # own apps     'archive', ] 

While here, let’s also load the secret key from our venv (venv/bin/activate) as defined earlier:

SECRET_KEY = os.environ['SECRET_KEY'] 

Lastly let’s sync the pending migrations to our default sqlite DB and create a superuser to access Django’s admin back-end:

(venv) [bobbelderbos@imac django-archive]$   python manage.py migrate Operations to perform: Apply all migrations: admin, auth, contenttypes, sessions Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying admin.0003_logentry_add_action_flag_choices... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying auth.0009_alter_user_last_name_max_length... OK Applying auth.0010_alter_group_name_max_length... OK Applying auth.0011_update_proxy_permissions... OK Applying sessions.0001_initial... OK  (venv) [bobbelderbos@imac django-archive]$   python manage.py createsuperuser Username (leave blank to use 'bobbelderbos'): bob Email address: Password: Password (again): This password is too short. It must contain at least 8 characters. Bypass password validation and create user anyway? [y/N]: y Superuser created successfully. 

Create routes

In the main app snippets, that was created with the startproject command, we add the following routes:

snippets/urls.py

from django.contrib import admin from django.urls import path, include  urlpatterns = [     path('admin/', admin.site.urls),     path('', include('archive.urls', namespace='archive')), ] 

Apart from the default admin routes (admin.site.urls), we namespace the archive app’s routes, defining them in the app:

archive/urls.py

from django.urls import path  from . import views  app_name = 'archive' urlpatterns = [     path('download/', views.download, name='download') ] 

This will be the download endpoint that will serve the zipfile, we will write that code in a bit. First let’s define the model (DB table) that will hold our code snippets.

Create a Script model

In our archive app we make this simple model and sync it to the DB:

archive/models.py

from django.db import models   class Script(models.Model):     name = models.CharField(max_length=100)     code = models.TextField()     added = models.DateTimeField(auto_now_add=True)      def __str__(self):         return self.name      class Meta:         ordering = ['-added'] 

We inherit all goodness from Django’s Model class. The added datetime gets automatically populated upon insert. Defining a __str__ on the class makes it easier to inspect the objects when debugging (or in Django’s interactive shell). And we can use the inner Meta class to set further behaviors, in this case let’s show most recently added snippets first.

Now we have to commit (“migrate”) this model to the DB which is easy using Django’s manage.py. However first we need to stub out the download function we defined in archive/urls.py, otherwise we get: AttributeError: module 'archive.views' has no attribute 'download' upon migration. Add this code to archive/views.py:

archive/views.py

def download(request):     pass 

Now it should work:

(venv) [bobbelderbos@imac django-archive]$   python manage.py makemigrations Migrations for 'archive': archive/migrations/0001_initial.py     - Create model Script (venv) [bobbelderbos@imac django-archive]$   python manage.py migrate Operations to perform: Apply all migrations: admin, archive, auth, contenttypes, sessions Running migrations: Applying archive.0001_initial... OK 

I am just using the default sqlite DB, we can use schema to see what migrate created:

(venv) [bobbelderbos@imac django-archive]$   sqlite3 db.sqlite3 SQLite version 3.24.0 2018-06-04 19:24:41 Enter ".help" for usage hints. sqlite> .table ... other tables ... archive_script              <== our new table sqlite> .schema  archive_script CREATE TABLE IF NOT EXISTS "archive_script" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "name" varchar(100) NOT NULL, "code" text NOT NULL, "added" datetime NOT NULL); 

Django’s admin interface

To be able to work with the new model from the admin interface we need to register it. Add this code to the archive/admin.py module:

archive/admin.py

from django.contrib import admin  from .models import Script   class ScriptAdmin(admin.ModelAdmin):     pass admin.site.register(Script, ScriptAdmin) 

Now let’s spin up the dev server. As I leave it running in the foreground I use a second terminal:

$   cd /Users/bbelderbos/code/django-archive $   source venv/bin/activate (venv) [bbelderbos@imac django-archive]$   python manage.py runserver Watching for file changes with StatReloader Performing system checks...  System check identified no issues (0 silenced). May 08, 2019 - 02:17:32 Django version 2.2, using settings 'snippets.settings' Starting development server at http://127.0.0.1:8000/ Quit the server with CONTROL-C. 

Now I can access http://localhost:8000/admin and login with the superuser I created earlier. At this point we should see the new model:

new model in admin

Let’s add some small code snippets from our tips page:

3 snippets added

adding snippet 1

adding snippet 2

adding snippet 3

Serving up a zipfile

Now let’s create a zipfile with all the code snippets stored in the DB. We do this in the download view we stubbed out earlier:

archive/views.py

import zipfile  from django.http import HttpResponse  from .models import Script  README_NAME = 'README.md' README_CONTENT = """ ## PyBites Code Snippet Archive  Here is a zipfile with some useful code snippets.  Produced for blog post https://pybit.es/django-zipfiles.html  Keep calm and code in Python! """ ZIPFILE_NAME = 'pybites_codesnippets.zip'   def download(request):     """Download archive zip file of code snippets"""     response = HttpResponse(content_type='application/zip')     zf = zipfile.ZipFile(response, 'w')      # create the zipfile in memory using writestr     # add a readme     zf.writestr(README_NAME, README_CONTENT)      # retrieve snippets from ORM and them to zipfile     scripts = Script.objects.all()     for snippet in scripts:         zf.writestr(snippet.name, snippet.code)      # return as zipfile     response['Content-Disposition'] = f'attachment; filename={ZIPFILE_NAME}'     return response 

We use Django’s HttpResponse object which we have to give a Content-Disposition attribute. To directly serve up the resulting zipfile, not writing it to disk, I use zipfile‘s writestr. Getting the snippets from Django’s ORM is as easy as: Script.objects.all(). I also added a README file.

Now visit the download endpoint: http://localhost:8000/download -> A zipfile should automatically download to your desktop:

download the zipfile

Let’s see if it worked by unzipping the obtained zipfile into a tmp directory:

[bbelderbos@imac Downloads]$   mkdir tmp [bbelderbos@imac Downloads]$   mv pybites_codesnippets.zip tmp [bbelderbos@imac Downloads]$   cd tmp [bbelderbos@imac tmp]$   unzip pybites_codesnippets.zip Archive:  pybites_codesnippets.zip extracting: README.md extracting: flatten.py extracting: zipping.py extracting: enumerate.py  [bbelderbos@imac tmp]$   cat README.md  ## PyBites Code Snippet Archive  Here is a zipfile with some useful code snippets.  Produced for blog post https://pybit.es/django-zipfile  Keep calm and code in Python!  [bbelderbos@imac tmp]$   for i in *py; do echo "== $  i =="; cat $  i; echo ; done == enumerate.py == names = 'bob julian tim sara'.split() for i, name in enumerate(names, 1):     print(i, name) == flatten.py == list_of_lists = [[1, 2], [3], [4, 5], [6, 7, 8]] flattened = sum(list_of_lists, []) print(flattened) == zipping.py == names = 'bob julian tim sara'.split() ages = '11 22 33 44'.split() print(dict(zip(names, ages))) 

Cool! So there you have it: a small Django app with a single model and view to serve zipfiles ๐Ÿ™‚

One enhancement would be to lock this down for users that are not logged in. Django makes this easy, just add this the following code at the top of the download function, returning a 401 (and toast message) if the user is not authenticated:

from django.contrib import messages ... def download(request):     """Download archive zip file of code snippets"""     response = HttpResponse(content_type='application/zip')      # add this:     if not request.user.is_authenticated:         messages.error(request, 'Need to be logged in to access this endpoint')         return HttpResponse(status=401)     # end      ...      ... 

The full code for this blog post is here.


If you saved some code for Bite exercises on our platform you can check out this feature scrolling to the bottom of the settings page

I hope this was useful and let us know if there are other Django related topics you’d like to see covered here …

Keep Calm and Code in Python!

— Bob

Planet Python

Catalin George Festila: Python 3.7.3 : Using the cognitive face detection from Azure Microsoft .

The Microsoft Azure comes with this free feature named Computer Vision: This API key is currently active 7 days remaining Distill actionable information from images 5,000 transactions, 20 per minute. You can test with the cognitive_face python module from GitHub. Another good example of extract printed text can be found at docs.microsoft.com. Let’s install it: C:\Python373\Scripts>pip install
Planet Python