Skip to content

Latest commit

 

History

History
245 lines (223 loc) · 12.9 KB

File metadata and controls

245 lines (223 loc) · 12.9 KB

Konveyor - Rule Writing Quickstart Guide

Konveyor is an application modernization platform that helps to modernize and migrate applications to new technologies. This is done through the application of written rules: at its heart, Konveyor is a rules engine.

Konveyor comes packed by default with a set of pre-written rules for several different migration paths, but users are allowed and encouraged to write their own rules, which can be easily input before analysis. Ideally, users would contribute their own rules to the Konveyor Community, improving and expanding the number of available migration paths over time.

  1. What is Kantra?
    1. How to install
    2. How to use
    3. Usage examples
  2. What is a rule?
    1. Parts of a rule
    2. Conditions
    3. Writing rules
  3. Walkthrough

What is Kantra?

Kantra is a CLI wrapper for the rules engine that powers Konveyor. Kantra allows users to do quick static analysis without the need to install the full Konveyor platform in a Kubernetes environment. The result is a HTML report that mimics that of Konveyor.

How to install Kantra

  • Installing kantra is easy, just go to the Kantra releases page and download a binary for your OS and architecture.
  • Additionally, you will need a container platform installed in your system, either podman or docker.

How to use Kantra

  • Kantra comes pre-loaded with several rulesets that provide different migration paths by default. These available paths (also named targets in the Konveyor ecosystem) can be checked out by running kantra rules list-targets. Specifying a migration path is desirable to have a targeted analysis.
  • Additionally, more rules can be specified with the --rules option.
  • When analyzing an application, it is possible to also analyze the code of its dependencies too:
    • --mode allows choosing between analyzing only the source code (source-only) or dependencies too (full, the default)
    • --analyze-known-libraries tells the engine to also analyze dependencies that are open source, and therefore whose code is generally accessible. This option only makes sense when using --mode full.
  • When analyzing Java code, both source code and binaries can be analyzed. This can be specified simply by the --input option.

Usage examples

  • Generate report to migrate source code application to EAP8 and OpenJDK21, save the report somewhere else:
    • kantra --input ~/Apps/my-application --target eap8 --target openjdk21 --output ~/Reports/my-application.report
  • Generate report to analyze binary application for cloud readiness:
    • kantra --input ~/Apps/my-war-file.war --target cloud-readiness
  • Generate report to migrate to Quarkus from EAP7, but do not analyze dependencies:
    • kantra --input ~/Apps/my-eap7-application --source eap7 --target quarkus --mode source-only

What is a rule?

A rule in Konveyor is a formally written specification of a condition. If this condition happens to be true within the application being analyzed, it will raise an action (normally an issue) in the generated report, this is, a problem within the code that will need to be changed or fixed by a developer in order for the migration to succeed.

analysis -> condition is true -> issue

For instance, if we want to migrate an application from JakartaEE 8 to 9, we will need to change all our namespaces from javax.* to jakarta.*. In this case, the condition would be:

if you find any usage of javax.*

and the action would be:

create an issue with a message telling the user to change from javax.* to jakarta.*

In the generated report, the user will find an issue, pointing to the specific part of the code where the condition was raised, telling exactly what needs to be modified.

Parts of a rule

Continuing with the previous example, that rule could be coded as follows:

# Metadata
  ruleID: javax-to-jakarta-rule-00001
  labels:
    - konveyor.io/source
    - konveyor.io/target=jakarta-ee9+
    - javaee
# Condition
  when:
    java.referenced:
      location: IMPORT
      pattern: javax.*
# Action
  description: The package 'javax' has been replaced by 'jakarta'.
  message: Replace the `javax.*` import statement with `jakarta.*`
  category:
  links:
    - title: Jakarta EE
      url: https://jakarta.ee/

There are three main parts that a rule consists of:

  • Metadata: a set of fields for identification, categorization, etc.
    • Rule ID: the ID of the rule, which must be unique
    • Labels: a set of tags to categorize the rule in different ways, mostly used for filtering
  • Condition (when): the condition that needs to be true for the rule to be triggered. There are different types of available conditions, and different language providers can have their own specific conditions unique to the language.
  • Action: one or more fields that describe the actions to be taken if the rule is triggered:
    • Description: a short title for the issue
    • Message: a longer and more explanatory text describing the problem that needs solving
    • Category: the type of issue, indicating whether the raised issue must be modified (mandatory), is optional (ie, for deprecated APIs), or potential (can't be decided)
    • Effort: an approximate number of points indicating the effort needed to do the modification in the code
    • Links: a set of links with more information about the issue

There are some additional fields that will be explained in the following sections.

Conditions

Conditions are the most important part of a rule, since they indicate what needs to happen in the code for an issue to be raised. The different types of conditions are provided by the language providers. Since Konveyor can be extended to be able to analyze any language, and each language has its own quirks and specifications, each language provider can expose its own conditions (in the form of capabilities). In the case of the Java language provider, it exposes two types of conditions:

  • java.referenced: matches against code references, like a match against a method or an annotated field
  • java.dependency: matches against the existence of a given dependency of the application being analyzed

An exception to this is the builtin provider, which is a generic, built-in provider for creating conditions such as plain text regex matching or Xpath matching:

  • builtin.filecontent: matches a given regex on plaintext
  • builtin.file: matches on a given filename
  • builtin.xml: matches on an Xpath expression

Conditions can be combined using the logical operators and, or and not:

when:
  and:
    - java.referenced:
        location: ANNOTATION
        pattern: org.konveyor.ExampleAnnotation
    - or
      - not: true
        builtin.filecontent:
          filePattern: ASpecificClass.java
          pattern: some.*regex
      - builtin.file:
          pattern: "^.*\\.properties$"

Writing rules

The best way to learn to write rules is to check the examples we have in the default ruleset. Additional documentation can also be found in the analyzer docs, with all available fields explained. Check the next section for a sample walkthrough of writing a rule, testing it and executing it.

Walkthrough

To get a feel of the Kantra workflow, let's try this sample walkthrough:

  1. Install kantra:
    1. Go to Kantra releases and download for your arch and OS.
    2. Install podman or docker if not already installed.
  2. Clone the Coolstore app to a directory of your choice:
    1. git clone git@github.com:konveyor-ecosystem/coolstore.git
  3. We are going to analyze the Coolstore app to perform a migration from Java EE to Quarkus. In order to do this, run the following command:
    1. kantra --input <path-to>/coolstore --target quarkus --target jakarta-ee9+ --target cloud-readiness --overwrite
      1. The --input parameter indicates the path to the source code to analyze
      2. The different --target parameters indicate the migration paths we want to check. Since we want to migrate to Quarkus, we might also need to migrate our app to the Jakarta namespaces. We also want to see if our app would be ready to be deployed to the cloud.
      3. Lastly, --overwrite will rewrite a previously generated report if it exists.
  4. After running successfully, a link will appear to the generated report. Follow it and open index.html. You should see a report like this.
    1. In "Applications", you can find all the analyzed applications; in this case, since we have specified only one input, only one application appears: "coolstore".
    2. By clicking on the "Issues" link of the left hand panel, you can see all the issues raised by Konveyor.
    3. Let's filter them by target == "cloud-readiness" and check the one with title "File system - Java IO". Inside you can see all the source code files where the issue was found.
  5. Let's create a custom rule specifically for this application. Let's say we want to detect all ProductServices declared as a field annotated with the @Inject annotation. For that, we will need a condition that looks like this:
# The rule ID. Must be unique.
- ruleID: coolstore-rule-00001
# The category. This indicates if the change MUST be done (mandatory), CAN be done (optional),
# or if it can't be decided (potential).
  category: mandatory
# An approximate calculation of the effort it would take to fix the issue in the code
  effort: 1
# A set of labels, including source and target technologies and tags
  labels:
  - konveyor.io/source=java-ee
  - konveyor.io/source=jakarta-ee
  - konveyor.io/target=quarkus
  - quarkus
# The triggering condition:
  when:
  # This will be a java condition
    java.referenced:
    # Match on the ProductService class...
      pattern: com.redhat.coolstore.service.ProductService
    # ...when it's used in a field declaration...
      location: FIELD
    # ...and also when it's annotated with @Inject.
      annotated:
        pattern: javax.inject.Inject
# A short description of the issue.  
  description: Do not use ProductService with Inject
# A more descriptive and long explanation of the issue, potentially with code snippets and examples of solving.
  message: "ProductService cannot be used with the @Inject annotation in version 2 of the coolstore application"
# An array of links with more information about the issue
  links:
  - title: 'Add some link here'
    url: https://www.example.com
  1. Now save this file in a folder as rule.yaml, and add another file called ruleset.yaml in the same folder with the following content:
name: sample/ruleset
description: This is a sample ruleset
  1. Run kantra again with the new rule and check the results:
    1. kantra --input <path-to>/coolstore --target quarkus --target jakarta-ee9+ --target cloud-readiness --overwrite

Testing our rule

It is ideal to have tests for each rule that we write. For that purpose, kantra has a test runner that allows us to write tests and run them against sample code. Here are the docs for the test runner.

Writing a test is easy. In the case of the rule we have just written, and having the coolstore app as data for our test, we could have the following dir structure with a new rule.test.yaml file and the coolstore app as data:

.
├── ruleset.yaml
├── rule.test.yaml
├── rule.yaml
└── test-data
    └── coolstore

⚠️ At the moment there is a bug involving test data and other data being in the way of the engine recognizing rules. Place the test-data folder outside of the rule folder and change the test below accordingly.

Tests must be named following the *.test.yaml convention in order for the analyzer to ignore them when running a normal analysis. In the case of the test runner, it will pick up whatever it finds ending in *.test.yaml.

Given this structure, our test could look like this:

rulesPath: ./rule.yaml
providers:
- name: java
  dataPath: ./test-data/coolstore
tests:
- ruleID: coolstore-rule-00001
  testCases:
  - name: coolstore-rule-00001-test-01
    hasIncidents:
      exactly: 2
      messageMatches: "ProductService cannot be used with the @Inject annotation in version 2 of the coolstore application"

It will check that two incidents are occurring in the analyzer output, and that the message corresponds to that of the rule. To execute it simply run kantra rules test ..