Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ NOTE: As semantic versioning states all 0.y.z releases can contain breaking chan
### Added

- [#29](https://github.com/kobsio/kobs/pull/29): Add a new dependencies section to the Application CR. These dependencies are used to show a topology graph for all Applications.
- [#31](https://github.com/kobsio/kobs/pull/31): Add plugin support for Kubernetes resources.

### Fixed

Expand Down
144 changes: 3 additions & 141 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
- [Server](#server)
- [Envoy](#envoy)
- [Run kobs](#run-kobs)
- [Add a new Plugin](#add-a-new-plugin)
- [Documentation](#documentation)

Every contribution to kobs is welcome, whether it is reporting a bug, submitting a fix, proposing new features or becoming a maintainer. To make contributing to kubenav as easy as possible you will find more details for the development flow in this documentation.

Expand Down Expand Up @@ -155,144 +155,6 @@ When you want to run kobs inside your Kubernetes cluster, please checkout the Do

**Using the demo application:** If you want to test your changes against the demo application take a look at [https://kobs.io/installation/demo/#development-using-the-demo](https://kobs.io/installation/demo/#development-using-the-demo).

## Add a new Plugin
## Documentation

To add a new plugin to kobs, you have to create a `proto/<PLUGIN-NAME>.proto`. Our [Makefile](./Makefile) will the handle the code generation for your plugin.

```protobuf
syntax = "proto3";
package plugins.<PLUGIN-NAME>;

option go_package = "github.com/kobsio/kobs/pkg/api/plugins/<PLUGIN-NAME>/proto";
```

To add your plugin to the Application CRD, add a corresponding field to the `Plugin` message format in the `proto/plugins.proto` file:

```protobuf
syntax = "proto3";
package plugins;

option go_package = "github.com/kobsio/kobs/pkg/api/plugins/plugins/proto";

import "<PLUGIN-NAME>.proto";

message Plugin {
<PLUGIN-NAME>.Spec <PLUGIN-NAME> = 1;
}
```

Besides the protocol buffers definition your also have to create a `pkg/api/plugins/<PLUGIN-NAME>/<PLUGIN-NAME>.go` file, which implements your definition and handles the registration of your plugin. To register your plugin you have to modify the `Register` function in the `pkg/api/plugins/plugins/plugins.go` file:

```go
package plugins

import (
"github.com/kobsio/kobs/pkg/api/plugins/<PLUGIN-NAME>"
)

func Register(cfg *config.Config, grpcServer *grpc.Server) error {
<PLUGIN-NAME>Instances, err := <PLUGIN-NAME>.Register(cfg.<PLUGIN-NAME>, grpcServer)
if err != nil {
log.WithError(err).WithFields(logrus.Fields{"plugin": "<PLUGIN-NAME>"}).Errorf("Failed to register <PLUGIN-NAME> plugin.")
return err
}

plugins = append(plugins, <PLUGIN-NAME>Instances...)
}
```

The configuration for your plugin must be added to the `Config` struct in the `pkg/config/config.go` file:

```go
package config

import (
"github.com/kobsio/kobs/pkg/api/plugins/<PLUGIN-NAME>"
)

type Config struct {
<PLUGIN-NAME> []<PLUGIN-NAME>.Config `yaml:"<PLUGIN-NAME>"`
}
```

Now your plugin is registered at the gRPC server and can be configured via a `config.yaml` file. In the next step you can implement the Reac UI components for your plugin. Your plugin must provide the following two components as entry point: `app/src/plugins/<PLUGIN-NAME>/<PLUGIN-NAME>Page.tsx` and `app/src/plugins/<PLUGIN-NAME>/<PLUGIN-NAME>Plugin.tsx`:

```tsx
import {
PageSection,
PageSectionVariants,
Title,
} from '@patternfly/react-core';
import React from 'react';

import { IPluginPageProps } from 'utils/plugins';

const <PLUGIN-NAME>Page: React.FunctionComponent<IPluginPageProps> = ({ name, description }: IPluginPageProps) => {
return (
<React.Fragment>
<PageSection variant={PageSectionVariants.light}>
<Title headingLevel="h6" size="xl">
{name}
</Title>
<p>{description}</p>
</PageSection>
</React.Fragment>
);
};

export default <PLUGIN-NAME>Page;
```

```tsx
import React from 'react';

import { IPluginProps } from 'utils/plugins';
import PluginDataMissing from 'components/plugins/PluginDataMissing';

const <PLUGIN-NAME>Plugin: React.FunctionComponent<IPluginProps> = ({
name,
description,
plugin,
showDetails,
}: IPluginProps) => {
if (!plugin.<PLUGIN-NAME>) {
return (
<PluginDataMissing
title="<PLUGIN-NAME> properties are missing"
description="The <PLUGIN-NAME> properties are missing in your CR for this application. Visit the documentation to learn more on how to use the <PLUGIN-NAME> plugin in an Application CR."
documentation="https://kobs.io"
type="<PLUGIN-TYPE>"
/>
);
}

return (
<React.Fragment>
</React.Fragment>
);
};

export default <PLUGIN-NAME>Plugin;
```

In the last step you have to register these two React components in the `app/src/utils/plugins.tsx` file:

```tsx
import React from 'react';

import <PLUGIN-NAME>Page from 'plugins/<PLUGIN-NAME>/<PLUGIN-NAME>Page';
import <PLUGIN-NAME>Plugin from 'plugins/<PLUGIN-NAME>/<PLUGIN-NAME>Plugin';

export const plugins: IPlugins = {
<PLUGIN-NAME>: {
page: <PLUGIN-NAME>Page,
plugin: <PLUGIN-NAME>Plugin,
},
};
```

Thats it, now you can generate the Go and TypeScript code from your `.proto` file and the new Application CRD with the following command:

```sh
make generate
```
More information, for example how to use the demo in you development workflow or how to submit a new plugin can be found in the documentation at [https://kobs.io/contributing/getting-started/](https://kobs.io/contributing/getting-started/).
2 changes: 1 addition & 1 deletion app/src/components/applications/ApplicationDetailsLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const ApplicationDetailsLink: React.FunctionComponent<IApplicationDetailsLinkPro
);

useEffect(() => {
setLink(`/applications/${application.cluster}/${application.namespace}/${application.name}${location.search}`);
setLink(`/applications/${application.cluster}/${application.namespace}/${application.name}`);
}, [application, location.search]);

return <DrawerLink link={link} />;
Expand Down
Loading