DEV Community

Cover image for Build a GitHub Issues Reporter for failing Kubernetes Apps with Botkube Plugins
Mateusz Szostok for Kubeshop

Posted on • Originally published at botkube.io

Build a GitHub Issues Reporter for failing Kubernetes Apps with Botkube Plugins

Botkube gives you fast and simple access to your clusters right from your communication platform. It does that by sending actionable notifications (via sources) and allowing you to run kubectl and helm commands (via executors) straight from the platform (Slack, Discord, Microsoft Teams and Mattermost).

In the recent Botkube 0.17 release, we took it to the next level by giving you an easy way to bring your own tools to a chat window!

In this blog post, you will learn how to develop your own executor plugin to fill the gap in your daily workflow.

Goal

To make it simple but functional, I will show you how to develop an executor that creates an issue for failing Kubernetes resources such as Job, Deployment, StatefulSet, and Pod.

GitHub executor final demo

Why it's worth it

With just a few lines of code, we will automate the process of creating a GitHub issue that out-of-the box contains Kubernetes-specific information useful for further debugging. All of that, directly from Slack, Discord, Mattermost, or MS Teams! No need for connecting to your cluster in your terminal, installing and running kubectl commands and copy-pasting fetched details into your browser.

Instead, you will be able to type @Botkube gh create issue pod/foo from any device that has access to your chat, including mobile apps.

Prerequisites

  • Access to a Kubernetes cluster

    💡 Tip
    To create a local cluster for testing purposes using k3d, run:

    k3d cluster create
    

What's under the hood

To understand better what we will develop, I want to give you a bigger picture of the Botkube plugin system. The below animation focuses only on the executor part, but it's almost the same for sources.

Plugin system architecture animation

The new part is a plugin repository that we introduced in 0.17. Plugin repository is a place where you store your plugin binaries and index files. Any static file server can be used, and in this blog post we will use a GitHub release. It’s similar to what you know from the Helm ecosystem.

The plugin manager consumes user's configuration, and downloads and starts only enabled plugins from a given repository. Plugins are running directly on the Kubernetes cluster where the Botkube core was installed.

Such approach allows us to decouple the Botkube core and its extensions. Thanks to that, we can:

  • Avoid having the Botkube core crash if a given plugin malfunctions
  • Write extensions in any language as gRPC is used

From the end-user's perspective, you can:

  • Specify and use multiple plugin repositories at the same time
  • Enable different plugin versions within the same Botkube version

To learn more, see Botkube architecture.

Step-By-Step Instructions

To quickly onboard you to Botkube plugins, we maintain the kubeshop/botkube-plugins-template repository that has all batteries included. Our first step is to bootstrap your own GitHub repository.

To check out the entire code, visit the Botkube GitHub repository.

Repository setup

  1. Navigate to botkube-plugins-template.

  2. Click Use this template, and then Create a new repository.
    Create Repo
    By doing so, you will create your own plugin repository with a single commit.

  3. Clone your repository locally:

    gh clone {owner}/{repo_name} # for example, gh clone mszostok/botkube-plugins
    
  4. Create and push a new tag to perform the initial release:

    git tag v0.0.1
    git push --tags
    

    After a few minutes you should see a new GitHub release.

Voilà! You are already an owner of fully functional Botkube plugins. Now it's time to add your own brick by creating a GitHub executor.

💡 Tip
In this blog post, we use only GitHub releases that work out-of-the-box. Releasing plugins on GitHub Pages requires additional setup. To support them too, see the use template document.

Develop GitHub executor

💡 Tip
To make the code-snippets more readable, I skipped the error handling. However, it will be useful if you will add error handling for the final implementation. You can check the full gh source-code for the reference.

  1. Create a cmd/gh/main.go file with the following template:

    This template code imports required packages and registers GHExecutor as the gRPC plugin. Our GHExecutor service already implements the required Protocol Buffers contract. As you can see, we require only 2 methods.
    • The Metadata method returns basic information about your plugin. This data is used when the plugin index is generated in an automated way.
    • The Execute method is the heart of your executor plugin. This method runs your business logic and returns the output as plaintext. Next, the Botkube core sends back the response to a given communication platform.
  2. To download the imported dependencies, in your terminal, run:

    go mod tidy
    
  3. Great! At this stage you already have a functional Botkube executor plugin. However, for now, it only responds with a hard-coded "Aloha!" greeting. But it can do that already on all supported communication platforms.

    Demo Aloha
    Don't worry, in the next steps, we will implement our business logic.

  4. Add support for user configuration:

    For each Execute method call, Botkube attaches the list of associated configurations. The input parameters are defined by the user, when enabling a given plugin:

    executors:
      "plugin-based":
        repo-name/gh:
          enabled: true # If not enabled, plugin is not downloaded and started.
          config: # Plugin's specific configuration.
            github:
              repository: "mszostok/repository"
              token: "github_pat_foo"
              issueTemplate: |
                ## Description
    
                This issue refers to the problems connected with {{ .Type | code "bash" }} in namespace {{ .Namespace | code "bash" }}
    
                {{ .Logs | code "bash"}}
    

    In our case, we need to have a GitHub token, GitHub repository where the issue should be created, and an issue template. The remaining parameters can be hard-coded on the plugin side, however, your plugin will be more flexible if you allow your users to change it without rebuilding your plugins.

    It's up to the plugin author to merge the passed configurations. You can use our helper function from the plugin extension package (pluginx). To learn more, see Passing configuration to your plugin.

  5. Let's implement command parsing to properly understand command syntax:

    Our goal is to parse gh create issue KIND/NAME [-n, --namespace].

    There are a lot of great libraries supporting command parsing. The most popular is probably cobra, but for our use case, we will just use the helper function from our plugin extension package.

    Under the hood, the pluginx.ParseCommand method uses go-arg.

  6. We are almost there! Now let's fetch the issue details:

    Here, we fetch logs and the cluster version, but you can extend it to fetch other details about your cluster. To make it as simple as possible, we use the kubectl CLI and avoid reimplementing a bit more complicated logic for fetching logs from all different Kubernetes kinds.

  7. Render issue description:

    In this step, we use the issue template that the user specified in plugin configuration. I decided to use the Go template, as it fits perfectly into our template rendering flow.

  8. Finally! Submitting an issue!

    GitHub provides a great gh CLI, which we use to submit our issue. To learn more about the CLI syntax, see their manual.

    💡 Tip
    The gh CLI doesn't accept fine-grained access tokens. As a workaround, you can use the Go SDK

  9. The last part is to download our dependencies.

    We already improved this step and in the 0.18 version Botkube will download defined dependencies automatically. For now, you can use the pluginx.DownloadDependencies function to call our downloader explicitly. The syntax will remain the same.

Congrats! The gh plugin is finally implemented. Now, let's play a DevOps role! 😈 In the next section, I will show you how to build and release your brand-new executor plugin.

Release the gh executor

It's time to build your plugin. For that purpose, we will use GoReleaser. It simplifies building Go binaries for different architectures. The important thing is to produce the binaries for the architecture of the host platform where Botkube is running. Adjust the goos, goarch, and goarm properties based on this architecture.

Add new build entry under .goreleaser.yaml:

builds:
  - id: gh
    main: cmd/gh/main.go
    binary: executor_gh_{{ .Os }}_{{ .Arch }}

    no_unique_dist_dir: true
    env:
      - CGO_ENABLED=0
    goos:
      - linux
      - darwin
    goarch:
      - amd64
      - arm64
    goarm:
      - 7
Enter fullscreen mode Exit fullscreen mode

Now, we need to distribute our plugins. As we mentioned earlier, a plugin repository can be any static file server. The kubeshop/botkube-plugins-template repository comes with two GitHub Actions:

  1. The .github/workflows/release.yml action, which builds the plugin binaries and index file each time a new tag is pushed.
  2. The .github/workflows/pages-release.yml action, which updates GitHub Pages with plugin binaries and index file each time a new tag is pushed.

To cut a new release, you need to commit all your work and tag a new commit:

git add -A
git commit -m "Implement gh executor"
git tag v1.0.0
Enter fullscreen mode Exit fullscreen mode

Next, let's push our changes and the new tag:

git push
git push --tags
Enter fullscreen mode Exit fullscreen mode

This triggers GitHub Action:

GitHub action that creates a new release

What this automation does under the hood

This automation:

  1. Installs the latest GoReleaser tool.
  2. Builds all plugin binaries defined in the .goreleaser.yaml file.
  3. Generates an index file using the Botkube helper tool.
  4. Generates a release description.
  5. Uses the gh CLI to create a new GitHub release.

Use the gh executor

In the description of a new GitHub release, you will see the repository URL that you can use within Botkube.

Steps

  1. Follow one of our installation guides. Once you reach the Botkube deployment step, add flags specified in the steps below to helm install.
  2. Export required environment variables:

    💡 Tip
    Follow the official GitHub guide on how to create a personal access token. To be able to create GitHub issues, add the repo permission.

    export REPOSITORY={repo} # format OWNER/REPO_NAME, e.g. kubeshop/botkube
    export GITHUB_TOKEN={token}
    export PLUGINS_URL={plugin_index_url}
    
  3. Add the gh executor related configuration:

    --set 'plugins.repositories.botkube-plugins.url'=${PLUGINS_URL} \
    --set 'executors.plugin-based.botkube-plugins/gh.config.github.repository'=${REPOSITORY} \
    --set 'executors.plugin-based.botkube-plugins/gh.config.github.token'=${GITHUB_TOKEN} \
    
  4. Depending on the selected platform, add the plugin-based executor binding. For Slack, it looks like this:

    --set communications.default-group.socketSlack.channels.default.bindings.executors=['plugin-based']
    

If you follow all the steps above, you will have all the necessary flags allowing you to install Botkube with the gh executor!

Here's an example of a full command that you should have constructed for Slack installation:

Testing

  1. Navigate to your communication channel
  2. On a given channel, run:

    @Botkube list executors
    

    It should return information about enabled gh executor:
    List executors

  3. Create a failing Job:

    After a few second, you should see a new alert on your channel:

    Alert

  4. To create a GitHub issue for a given alert, run:

    @Botkube gh create issue job/oops
    

Summary

Botkube executors are powerful because they can glue together three important parts: Kubernetes clusters, communicators, and tools of your choice. There would be nothing special about it if it wasn't, in fact, unburdening you of those implementation-specific details.

As you noticed, you can focus purely on your business logic. Without the need to use different chat libraries, know how to establish secure connection, or make your extension available only on specific channels. What's more, not only do you not have to learn it, but you don't have to support it either, as we do it for you.

Once Botkube is deployed, your extension will be available to you and your teammates in a given channel. There is no need to maintain your local setup. Thanks to that, you can also easily run executors on private clusters.

Botkube extensions can be used with other Botkube functionality too. It means that you can use them to create automation. We will shed more light on that in the next blog post. Stay tuned!

Implement once, access everywhere (Slack, Discord, Mattermost, MS Teams).

How can I get involved?

The implemented plugin is as simple as possible. However, it is a great base for further extension based on your needs; for example: introduce your own Kubernetes annotation to route the notification to a specific repository, add a threshold to create issues only for constantly failing Pods, etc. The possibilities are endless, and we cannot wait to see what kind of great workflows you will create!

As always, we want to hear your feedback and ideas about Botkube. Help us plan the Botkube roadmap, get the features you’d like implemented.

There are plenty of options to contact us:

Thank you for taking the time to learn about Botkube 🙌

Top comments (0)