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 @@ -52,6 +52,7 @@ NOTE: As semantic versioning states all 0.y.z releases can contain breaking chan
- [#236](https://github.com/kobsio/kobs/pull/236): [core] Improve filtering in select components for various plugins.
- [#260](https://github.com/kobsio/kobs/pull/260): [opsgenie] Adjust permission handling and add actions for incidents.
- [#262](https://github.com/kobsio/kobs/pull/262): [core] Rework variables handling in dashboards.
- [#263](https://github.com/kobsio/kobs/pull/263): [core] :warning: _Breaking change:_ :warning: Refactor `cluster` and `clusters` package.

## [v0.7.0](https://github.com/kobsio/kobs/releases/tag/v0.7.0) (2021-11-19)

Expand Down
27 changes: 27 additions & 0 deletions cmd/kobs/config/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package config

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestLoad(t *testing.T) {
t.Run("load config", func(t *testing.T) {
config, err := Load("mocks/config_valid.yaml")
require.NoError(t, err)
require.NotEmpty(t, config)
})

t.Run("load config failed: file does not exists", func(t *testing.T) {
config, err := Load("mocks/config.yaml")
require.Error(t, err)
require.Empty(t, config)
})

t.Run("load config failed: invalid config", func(t *testing.T) {
config, err := Load("mocks/config_invalid.yaml")
require.Error(t, err)
require.Empty(t, config)
})
}
14 changes: 14 additions & 0 deletions cmd/kobs/config/mocks/config_invalid.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
clusters:
- providers:
- provider: kubeconfig
kubeconfig:
path: ${HOME}/.kube/config

plugins:
resources:
forbidden:
- secrets

applications:
topologyCacheDuration: 1m
teamsCacheDuration: 1m
14 changes: 14 additions & 0 deletions cmd/kobs/config/mocks/config_valid.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
clusters:
providers:
- provider: kubeconfig
kubeconfig:
path: ${HOME}/.kube/config

plugins:
resources:
forbidden:
- secrets

applications:
topologyCacheDuration: 1m
teamsCacheDuration: 1m
6 changes: 3 additions & 3 deletions cmd/kobs/kobs.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,18 +116,18 @@ func main() {
// repository.
// The loaded clusters and the router for the plugins is then passed to the api package, so we can access all the
// plugin api routes via the kobs api.
loadedClusters, err := clusters.Load(cfg.Clusters)
clustersClient, err := clusters.NewClient(cfg.Clusters)
if err != nil {
log.Fatal(nil, "Could not load clusters", zap.Error(err))
}

pluginsRouter := plugins.Register(loadedClusters, cfg.Plugins)
pluginsRouter := plugins.Register(clustersClient, cfg.Plugins)

// Initialize each component and start it in it's own goroutine, so that the main goroutine is only used as listener
// for terminal signals, to initialize the graceful shutdown of the components.
// The appServer is the kobs application server, which serves the React frontend and the health endpoint. The
// metrics server is used to serve the kobs metrics.
apiServer, err := api.New(loadedClusters, pluginsRouter, isDevelopment)
apiServer, err := api.New(clustersClient, pluginsRouter, isDevelopment)
if err != nil {
log.Fatal(nil, "Could not create API server", zap.Error(err))
}
Expand Down
34 changes: 17 additions & 17 deletions cmd/kobs/plugins/plugins.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func (router *Router) getPlugins(w http.ResponseWriter, r *http.Request) {
}

// Register is used to register all api routes for plugins.
func Register(clusters *clusters.Clusters, config Config) chi.Router {
func Register(clustersClient clusters.Client, config Config) chi.Router {
router := Router{
chi.NewRouter(),
&plugin.Plugins{},
Expand All @@ -81,25 +81,25 @@ func Register(clusters *clusters.Clusters, config Config) chi.Router {
router.Get("/", router.getPlugins)

// Initialize all plugins
resourcesRouter := resources.Register(clusters, router.plugins, config.Resources)
applicationsRouter := applications.Register(clusters, router.plugins, config.Applications)
teamsRouter := teams.Register(clusters, router.plugins, config.Teams)
usersRouter := users.Register(clusters, router.plugins, config.Users)
dashboardsRouter := dashboards.Register(clusters, router.plugins, config.Dashboards)
prometheusRouter, prometheusInstances := prometheus.Register(clusters, router.plugins, config.Prometheus)
resourcesRouter := resources.Register(clustersClient, router.plugins, config.Resources)
applicationsRouter := applications.Register(clustersClient, router.plugins, config.Applications)
teamsRouter := teams.Register(clustersClient, router.plugins, config.Teams)
usersRouter := users.Register(clustersClient, router.plugins, config.Users)
dashboardsRouter := dashboards.Register(clustersClient, router.plugins, config.Dashboards)
prometheusRouter, prometheusInstances := prometheus.Register(router.plugins, config.Prometheus)
elasticsearchRouter := elasticsearch.Register(router.plugins, config.Elasticsearch)
klogsRouter, klogsInstances := klogs.Register(clusters, router.plugins, config.Klogs)
jaegerRouter := jaeger.Register(clusters, router.plugins, config.Jaeger)
kialiRouter := kiali.Register(clusters, router.plugins, config.Kiali)
istioRouter := istio.Register(clusters, router.plugins, config.Istio, prometheusInstances, klogsInstances)
klogsRouter, klogsInstances := klogs.Register(router.plugins, config.Klogs)
jaegerRouter := jaeger.Register(router.plugins, config.Jaeger)
kialiRouter := kiali.Register(router.plugins, config.Kiali)
istioRouter := istio.Register(router.plugins, config.Istio, prometheusInstances, klogsInstances)
grafanaRouter := grafana.Register(router.plugins, config.Grafana)
harborRouter := harbor.Register(clusters, router.plugins, config.Harbor)
fluxRouter := flux.Register(clusters, router.plugins, config.Flux)
harborRouter := harbor.Register(router.plugins, config.Harbor)
fluxRouter := flux.Register(clustersClient, router.plugins, config.Flux)
opsgenieRouter := opsgenie.Register(router.plugins, config.Opsgenie)
sonarqubeRouter := sonarqube.Register(clusters, router.plugins, config.Sonarqube)
techdocsRouter := techdocs.Register(clusters, router.plugins, config.TechDocs)
azureRouter := azure.Register(clusters, router.plugins, config.Azure)
sqlRouter := sql.Register(clusters, router.plugins, config.SQL)
sonarqubeRouter := sonarqube.Register(router.plugins, config.Sonarqube)
techdocsRouter := techdocs.Register(router.plugins, config.TechDocs)
azureRouter := azure.Register(router.plugins, config.Azure)
sqlRouter := sql.Register(router.plugins, config.SQL)
markdownRouter := markdown.Register(router.plugins, config.Markdown)
rssRouter := rss.Register(router.plugins, config.RSS)

Expand Down
47 changes: 47 additions & 0 deletions cmd/kobs/plugins/plugins_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package plugins

import (
"net/http"
"net/http/httptest"
"testing"

"github.com/kobsio/kobs/pkg/api/plugins/plugin"

"github.com/go-chi/chi/v5"
"github.com/stretchr/testify/require"
)

func TestGetPlugins(t *testing.T) {
router := Router{
chi.NewRouter(),
&plugin.Plugins{
{
Name: "applications",
DisplayName: "Applications",
Description: "Monitor your Kubernetes workloads.",
Home: true,
Type: "applications",
},
{
Name: "resources",
DisplayName: "Resources",
Description: "View and edit Kubernetes resources.",
Type: "resources",
},
},
}
router.Get("/plugins", router.getPlugins)

req, _ := http.NewRequest(http.MethodGet, "/plugins", nil)
w := httptest.NewRecorder()

router.getPlugins(w, req)

require.Equal(t, http.StatusOK, w.Code)
require.Equal(t, "[{\"name\":\"applications\",\"displayName\":\"Applications\",\"description\":\"Monitor your Kubernetes workloads.\",\"home\":true,\"type\":\"applications\",\"options\":null},{\"name\":\"resources\",\"displayName\":\"Resources\",\"description\":\"View and edit Kubernetes resources.\",\"home\":false,\"type\":\"resources\",\"options\":null}]\n", string(w.Body.Bytes()))
}

func TestRegister(t *testing.T) {
router := Register(nil, Config{})
require.NotEmpty(t, router)
}
15 changes: 8 additions & 7 deletions docs/contributing/develop-a-plugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,12 @@ Each plugin must export `chi.Router` router interface, so that the router can be
```go
type Router struct {
*chi.Mux
clusters *clusters.Clusters
clustersClient clusters.Client
config Config
}
```

With the `Router` struct you can then create your APIs as follows, where you have access to the `clusters`, `config`, etc.
With the `Router` struct you can then create your APIs as follows, where you have access to the `clustersClient`, `config`, etc.

```go
func (router *Router) getName(w http.ResponseWriter, r *http.Request) {}
Expand All @@ -63,7 +63,7 @@ Finally your plugin should export a `Register` function, which returns the `chi.
You have to add an entry to the `plugins` slice for each instance of your plugin, so that the React UI is aware of the plugin. Then you can create your `router` object, which will then be mounted under the before specified `Route`.

```go
func Register(clusters *clusters.Clusters, plugins *plugin.Plugins, config Config) chi.Router {
func Register(clustersClient clusters.Client, plugins *plugin.Plugins, config Config) chi.Router {
plugins.Append(plugin.Plugin{
Name: config.Name,
DisplayName: config.DisplayName,
Expand All @@ -73,7 +73,7 @@ func Register(clusters *clusters.Clusters, plugins *plugin.Plugins, config Confi

router := Router{
chi.NewRouter(),
clusters,
clustersClient,
config,
}

Expand Down Expand Up @@ -114,7 +114,7 @@ func Register(clusters *clusters.Clusters, plugins *plugin.Plugins, config Confi

type Router struct {
*chi.Mux
clusters *clusters.Clusters
clustersClient clusters.Client
config Config
}

Expand All @@ -137,7 +137,7 @@ func Register(clusters *clusters.Clusters, plugins *plugin.Plugins, config Confi
}

// Register returns a new router which can be used in the router for the kobs rest api.
func Register(clusters *clusters.Clusters, plugins *plugin.Plugins, config Config) chi.Router {
func Register(clustersClient clusters.Client, plugins *plugin.Plugins, config Config) chi.Router {
plugins.Append(plugin.Plugin{
Name: config.Name,
DisplayName: config.DisplayName,
Expand All @@ -147,7 +147,7 @@ func Register(clusters *clusters.Clusters, plugins *plugin.Plugins, config Confi

router := Router{
chi.NewRouter(),
clusters,
clustersClient,
config,
}

Expand All @@ -172,6 +172,7 @@ export interface IPluginComponent {
page?: React.FunctionComponent<IPluginPageProps>;
panel: React.FunctionComponent<IPluginPanelProps>;
preview?: React.FunctionComponent<IPluginPreviewProps>;
variables?: (variable: IDashboardVariableValues, variables: IDashboardVariableValues[], times: IPluginTimes) => Promise<IDashboardVariableValues>;
}
```

Expand Down
6 changes: 3 additions & 3 deletions docs/contributing/using-the-kobsio-app.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func (router *Router) getPlugins(w http.ResponseWriter, r *http.Request) {
}

// Register is used to register all api routes for plugins.
func Register(clusters *clusters.Clusters, config Config) chi.Router {
func Register(clustersClient clusters.Client, config Config) chi.Router {
router := Router{
chi.NewRouter(),
&plugin.Plugins{},
Expand All @@ -82,8 +82,8 @@ func Register(clusters *clusters.Clusters, config Config) chi.Router {
router.Get("/", router.getPlugins)

// Register all plugins
router.Mount(resources.Route, resources.Register(clusters, router.plugins, config.Resources))
router.Mount(helloworld.Route, helloworld.Register(clusters, router.plugins, config.HelloWorld))
router.Mount(resources.Route, resources.Register(clustersClient, router.plugins, config.Resources))
router.Mount(helloworld.Route, helloworld.Register(clustersClient, router.plugins, config.HelloWorld))
+ router.Mount(mynewplugin.Route, mynewplugin.Register(clusters, router.plugins, config.MyNewPlugin))

return router
Expand Down
6 changes: 3 additions & 3 deletions pkg/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func (s *Server) Stop() {
// We exclude the health check from all middlewares, because the health check just returns 200. Therefore we do not need
// our defined middlewares like request id, metrics, auth or loggin. This also makes it easier to analyze the logs in a
// Kubernetes cluster where the health check is called every x seconds, because we generate less logs.
func New(loadedClusters *clusters.Clusters, pluginsRouter chi.Router, isDevelopment bool) (*Server, error) {
func New(clustersClient clusters.Client, pluginsRouter chi.Router, isDevelopment bool) (*Server, error) {
router := chi.NewRouter()

if isDevelopment {
Expand All @@ -92,12 +92,12 @@ func New(loadedClusters *clusters.Clusters, pluginsRouter chi.Router, isDevelopm
r.Use(middleware.Recoverer)
r.Use(middleware.URLFormat)
r.Use(metrics.Metrics)
r.Use(auth.Handler(loadedClusters))
r.Use(auth.Handler(clustersClient))
r.Use(httplog.Logger)
r.Use(render.SetContentType(render.ContentTypeJSON))

r.Get("/user", auth.UserHandler)
r.Mount("/clusters", clusters.NewRouter(loadedClusters))
r.Mount("/clusters", clusters.NewRouter(clustersClient))
r.Mount("/plugins", pluginsRouter)
})

Expand Down
Loading