krules-py-cli

KRules Command Line


License
Apache-2.0
Install
pip install krules-py-cli==0.4.3

Documentation

KRules CLI

Krules CLI is a tool allow you to quickly create a project based on KRules and organize your microservices in a hierarchical structure.

Initial steps

Init project tree

To initialize a new Project run:

krules-py project init your/project/path
In case one or more directory of path does not exist, the script will create them for you.
Moreover a default profile will be generated.

To generate default profile some params must be prompted, but sometimes you need to avoid user interaction, maybe because you want integrate a CI/CD solution. This could be achieved using this optional params:

  • --name [default current folder name]: set project name;
  • -n --namespace [default default] : set in which namespace will be deployed your components;
  • -d --docker-registry. : set docker registry with which to push and deploy your image;
  • -i --image-base : override ruleset image base.

Example

krules-py project init . --name my-project

The output will be:

. my-project
│
├── base
│   │
│   ├── Dockerfile
│   ├── ipython_config.py
│   ├── Makefile
│   ├── VERSION
│   ├── app
│   │   │
│   │   ├── env.py
│   │   └── app_functions
│   │       │
│   │       └── __init__.py
│   └── k8s
│       │
│       ├── brokers.yaml
│       ├── config-krules.yaml
│       ├── config-krules-subjects-mongodb.yaml
│       ├── config-krules-subjects-mongodb-auth.yaml
│       ├── config-krules-subjects-redis.yaml
│       ├── config-krules-subjects-redis-auth.yaml
│       ├── event-display.yaml
│       ├── kustomization.yaml
│       └── triggers.yaml
└── rulesets
    │
    └── patches.yaml

Config subject storage support

The next step is to choose the subject storage support.

The default storage class can be used just for local testing so you have to use another one in production environment, at now you can choose between redis and mongodb

Go to base/app/env.py to enable your preferred storage support.

First of all remove the default implementation:

from krules_core.tests.subject.sqlite_storage import SQLLiteSubjectStorage
subject_storage_factory.override(
   providers.Factory(lambda x: SQLLiteSubjectStorage(x, ":memory:"))
)

If, for example, you want to use redis support uncomment this code part:

# Redis subjects storage support
subjects_redis_storage_settings = settings_factory() \
    .get("subjects-backends") \
    .get("redis")
from redis_subjects_storage import storage_impl as redis_storage_impl

subject_storage_factory.override(
    providers.Factory(
        lambda x: redis_storage_impl.SubjectsRedisStorage(x, subjects_redis_storage_settings.get("url"))
    )
)

Then in base/k8s update ConfigMap and authorization data related to the storage support you choose. Continuing with redis example.

config-krules-subject-redis-auth.yaml

apiVersion: v1
data:
password: cGFzc3dvcmQ= # replace with your redis password in base64 format
kind: Secret
metadata:
  name: config-krules-subjects-redis-auth
type: Opaque

config-krules-subject-redis.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: config-krules-subjects-redis
data:
  config_subjects_redis.yaml: |
    url: redis://:${KRULES_SUBJECTS_REDIS_PASSWORD}@redis.address/0 # replace with your redis address

Remember to include them in the kustomization.yaml.

The last step is to make those configurations known to your rulesets.

In rulesets/patches.yaml uncomment the patch related to your chosen storage support.

## Redis subjects backend
kinds:
  - serving.knative.dev/v1beta1:Service
  - apps/v1:Deployment
labelsMatch:
  - airspot.krules.dev/type: ^ruleset$
patch:
  spec:
    template:
      spec:
        containers:
          - patch__0:
            env:
              - name: KRULES_SUBJECTS_REDIS_PASSWORD
                valueFrom:
                  secretKeyRef:
                    name: config-krules-subjects-redis-auth
                    key: password
            volumeMounts:
              - name: config-krules-subjects-redis-volume
                mountPath: /krules/config/subjects-backends/redis

      volumes:
        - name: config-krules-subjects-redis-volume
          configMap:
            name: config-krules-subjects-redis

Build and push base image

Go to base folder and run make.

make command will take care of each needed operation to build and push your base image correctly. You can also run each step individually:

  • VERSION: Detect changes checking your Dockerfile and each your .py file in the base/app folder and possibly build a new base image version;
  • push: If there is a new image build in your local registry push it to your Docker repository;
  • .lastResources: Apply all yaml in k8s folder to your namespace.
  • clean: Clean all files generated by make command, useful to relaunch it without make any changes to your files.

Working with Rulesets

Init Ruleset tree

To create a new Ruleset go to rulesets folder and run:

krules-py ruleset create my-ruleset

The output will be:

. my-ruleset
│
├── Dockerfile.origin
├── Makefile
├── VERSION
├── app
│   │
│   └── rules.py
└── k8s
    │
    ├── kustomization.yaml
    └── service.yaml

Deploy your Ruleset

To deploy your code on Kubernetes got to your ruleset folder and run make. make command will take care of each needed operation to deploy your ruleset correctly detecting files changes.

You can also run each step individually:

  • Dockerfile: Detect changes in your Dockerfile.origin and generate a new Dockerfile if needed.

So, if you want to change the Dockerfile don't modify directly it but edit the Dockerfile.origin; - VERSION: Detect changes checking your Dockerfile and each your .py file in the rulesset app folder and possibly build a new rulesset image version; - push: If there is a new image build in your local registry push it to your Docker repository; - .lastResources: Apply all yaml in k8s folder to your namespace. - clean: Clean all files generated by make command, useful to relaunch it without make any changes to your files.

Each ruleset inherit your project image base, so if you change it you have to redeploy your ruleset using:

make clean && make

Generate your Ruleset manifest (using kustomize)

The KRules CLI integrate kubectl kustomize. command. In added to the kustomize standard functions, it provide to the user another patches logic.

Usage:

krules-py ruleset patch

patch command apply each patches.yaml file from the project root to the ruleset folder, so it is possible to define some patches shared by all rulesets or by group of ruleset.

Example:

. rulesets
│
├── patches.yaml
├── my-group
│   │
│   ├── my-ruleset-2/
│   ├── my-ruleset-3/
│   ├── my-ruleset-4/
│   └── patches.yaml
└── my-ruleset-1
    │
    ...
    ├── kustomization.yaml
    └── service.yaml

rulesets/patches.yaml will be applied to all rulesets, while rulesets/my-group/patches.yaml will modified just the rulesets inside my-group folder

Custom path

patch command search for kustomization file in the current folder or in k8s one. If you put your file in another path you can use the path parameter

Usage:

krules-py ruleset patch -p [--path] custom/path

Warning

path param refer to folder containing the kustomization file not to the file itself.

How to use

Basic Usage

Supposing you define this 3 files:

service.yaml

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  template:
    spec:
      contatiners:
      - name: my-container
---

triggers/my-trigger.yaml

apiVersion: eventing.knative.dev/v1alpha1
kind: Trigger
metadata:
  name: my-trigger

kustomization.yaml

commonLabels:
  airspot.krules.dev/ruleset: my-ruleset
resources:
- service.yaml
- triggers/my-trigger.yaml

You can override the previous components creating a file named patches.yaml in some of parent folders

kinds:
  - v1:Service
  - serving.knative.dev/v1alpha1:Service
patch:
  metadata:
    labels:
      app: my-app
---
kinds:
  - serving.knative.dev/v1alpha1:Trigger
patch:
  spec:
    broker: my-broker

kinds : list of kinds of components will be affected from the patch. Format: : patch : list of field will be modify with this patch labelsMatch: list of regex to filter components by metadata labels.

The output will be:

apiVersion: v1
kind: Service
metadata:
  name: my-service
  labels:
    airspot.krules.dev/ruleset: my-ruleset
    app: my-app
spec:
  template:
    spec:
      contatiners:
      - name: my-container

---
apiVersion: eventing.knative.dev/v1alpha1
kind: Trigger
metadata:
  name: my-trigger
  labels:
    airspot.krules.dev/ruleset: my-ruleset
spec:
  broker: my-broker
---

Adding a second container

To add element to a list just define the new elements in the patch inside the list key.

service.yaml

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  template:
    spec:
      contatiners:
      - name: my-first-container
---

patches.yaml

kinds:
  - v1:Service
  - serving.knative.dev/v1alpha1:Service
patch:
  spec:
    template:
      spec:
        containers:
          - name: my-second-container

Output:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  template:
    spec:
      contatiners:
      - name: my-first-container
      - name: my-second-container
---

Adding volumeMount just to a specific container

To override a single list element use patch__<index> keyword, where index indicates the index of the element of the list to be modified.

service.yaml

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  template:
    spec:
      contatiners:
      - name: my-first-container
      - name: my-second-container
      - name: my-third-container
---

patches.yaml

kinds:
  - v1:Service
  - serving.knative.dev/v1alpha1:Service
labelsMatch:
  - app: my-app
patch:
  spec:
    template:
      spec:
        containers:
          - patch__1:
              volumeMounts:
                - name: my-volume
                  mountPath: /configs/

        volumes:
          - name: my-volume
            configMap:
              name: my-config

Output:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  template:
    spec:
      contatiners:
        - name: my-first-container
        - name: my-second-container
          volumeMounts:
                - name: my-volume
                  mountPath: /configs/
        - name: my-third-container
      volumes:
        - name: my-volume
          configMap:
            name: my-config
---

Update a specific container image

service.yaml

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  template:
    spec:
      containers:
      - name: my-container
      - name: my-other-container
---
krules-py ruleset patch set-image my-container=my-docker-registry/my-image:latest

Output:

service.yaml

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  template:
    spec:
      containers:
      - name: my-container
        image: my-docker-registry/my-image:latest
      - name: my-other-container
---

Custom ruleset template

It is possible that default ruleset template does not completely satisfy your requirements. You can override it using:

krules-py gen-rs-template your_template_dir [--set-default]

The selected template directory must be contained in your project. ** --set-default ** flag set your directory as the default one for each ruleset. The output will be:

. your_template_dir
│
├── Dockerfile.origin
├── Makefile
├── VERSION
├── app
│    │
│    └── rules.py
└── k8s
     │
     ├── kustomization.yaml
     ├── service.yaml
     └── triggers.yaml

To reset default rulesets template folder use:

krules-py gen-rs-template --unset-default

Managing Version

Both base and rulesets folders contains a file VERSION. This file contains your current local image version. When you run make in a ruleset folder your local image will be deployed only if its version is greater than the cluster one.

Adding triggers

To work properly a rule must be registered to a trigger. To create a new trigger run

krules-py trigger create my-trigger

Output:

triggers.yaml

apiVersion: eventing.knative.dev/v1beta1
kind: Trigger
metadata:
  name: my-trigger
spec:
  filter:
    attributes:
      type: my-ruleset-my-trigger
subscriber:
  ref:
    apiVersion: v1
    kind: Service
    name: my-ruleset
---

This command updates the file triggers.yaml located in k8s folder. If the file does not exist a new one will be created.

You could have some particular requirements for your trigger.

If, for example, you want to add a filter for a certain type run:

krules-py trigger create my-type-trigger --type my-type

Output:

triggers.yaml

apiVersion: eventing.knative.dev/v1beta1
kind: Trigger
metadata:
  name: my-type-trigger
spec:
  filter:
    attributes:
      type: my-type
subscriber:
  ref:
    apiVersion: v1
    kind: Service
    name: my-ruleset
---

Another interesting feature could be raise a trigger when a property is changed. To achieve this run:

krules-py trigger create my-property-trigger --on-propchange my-property

Output:

triggers.yaml

apiVersion: eventing.knative.dev/v1beta1
kind: Trigger
metadata:
  name: my-property-trigger
spec:
  filter:
    attributes:
      propertyname: my-property
      type: subject-property-changed
subscriber:
  ref:
    apiVersion: v1
    kind: Service
    name: my-ruleset
---

With CLI you can also create a trigger with your custom attributes running:

krules-py trigger create my-custom-trigger --attrs key1=val1 key2=val2

Output:

triggers.yaml

apiVersion: eventing.knative.dev/v1beta1
kind: Trigger
metadata:
  name: my-custom-trigger
spec:
  filter:
    attributes:
      key1: val1
      key2: val2
subscriber:
  ref:
    apiVersion: v1
    kind: Service
    name: my-ruleset
---

Managing profiles

The KRules CLI provides also a profiles handler. A profile contains all useful defaults value as docker registry and kubernetes namespace.

Add new profile

krules-py profile add myProfile

Optional arguments

  • --set-default: set the new profile as the default one;
  • -ns --namespace: set profile namespace;
  • -d --docker-registry: set profile Docker registry.

Set/Get default profile

$ krules-py profile set myProfile
$
$ krules-py profile get
$ myProfile

To list all available profiles run

krule-py profile list

Set/Get profile value

$ krule-py profile set-value namespace myNamespace
$
$ krule-py profile get-value namespace
$ myNamespace

To print all profile values run

krule-py profile values