mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
K8S: cleanup and consolidate feature toggles (#63212)
This commit is contained in:
parent
94241f6676
commit
0018c8e9c1
@ -101,8 +101,6 @@ The following toggles require explicitly setting Grafana's [app mode]({{< relref
|
|||||||
| ------------------------------ | ----------------------------------------------------------------------- |
|
| ------------------------------ | ----------------------------------------------------------------------- |
|
||||||
| `publicDashboardsEmailSharing` | Allows public dashboard sharing to be restricted to only allowed emails |
|
| `publicDashboardsEmailSharing` | Allows public dashboard sharing to be restricted to only allowed emails |
|
||||||
| `k8s` | Explore native k8s integrations |
|
| `k8s` | Explore native k8s integrations |
|
||||||
| `k8sDashboards` | Save dashboards via k8s |
|
|
||||||
| `apiserver` | Add a k8s API server proxy |
|
|
||||||
| `dashboardsFromStorage` | Load dashboards from the generic storage interface |
|
| `dashboardsFromStorage` | Load dashboards from the generic storage interface |
|
||||||
| `export` | Export grafana instance (to git, etc) |
|
| `export` | Export grafana instance (to git, etc) |
|
||||||
| `grpcServer` | Run GRPC server |
|
| `grpcServer` | Run GRPC server |
|
||||||
|
10
go.mod
10
go.mod
@ -25,10 +25,8 @@ replace cuelang.org/go => github.com/sdboyer/cue v0.5.0-beta.2.0.20221218111347-
|
|||||||
replace k8s.io/client-go => k8s.io/client-go v0.25.3
|
replace k8s.io/client-go => k8s.io/client-go v0.25.3
|
||||||
|
|
||||||
require (
|
require (
|
||||||
k8s.io/api v0.25.3 // indirect
|
|
||||||
k8s.io/apiextensions-apiserver v0.25.3
|
k8s.io/apiextensions-apiserver v0.25.3
|
||||||
k8s.io/apimachinery v0.25.3
|
k8s.io/apimachinery v0.25.3
|
||||||
k8s.io/client-go v12.0.0+incompatible
|
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@ -130,7 +128,7 @@ require (
|
|||||||
gopkg.in/square/go-jose.v2 v2.5.1
|
gopkg.in/square/go-jose.v2 v2.5.1
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
xorm.io/builder v0.3.6
|
xorm.io/builder v0.3.6 // indirect
|
||||||
xorm.io/core v0.7.3
|
xorm.io/core v0.7.3
|
||||||
xorm.io/xorm v0.8.2
|
xorm.io/xorm v0.8.2
|
||||||
)
|
)
|
||||||
@ -298,11 +296,9 @@ require (
|
|||||||
github.com/drone/drone-go v1.7.1 // indirect
|
github.com/drone/drone-go v1.7.1 // indirect
|
||||||
github.com/drone/envsubst v1.0.3 // indirect
|
github.com/drone/envsubst v1.0.3 // indirect
|
||||||
github.com/drone/runner-go v1.12.0 // indirect
|
github.com/drone/runner-go v1.12.0 // indirect
|
||||||
github.com/emicklei/go-restful/v3 v3.8.0 // indirect
|
|
||||||
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1 // indirect
|
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1 // indirect
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.6.7 // indirect
|
github.com/envoyproxy/protoc-gen-validate v0.6.7 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||||
github.com/google/gnostic v0.5.7-v3refs // indirect
|
|
||||||
github.com/google/go-querystring v1.1.0 // indirect
|
github.com/google/go-querystring v1.1.0 // indirect
|
||||||
github.com/google/gofuzz v1.2.0 // indirect
|
github.com/google/gofuzz v1.2.0 // indirect
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect
|
github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect
|
||||||
@ -321,7 +317,6 @@ require (
|
|||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
|
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
|
||||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||||
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect
|
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect
|
||||||
github.com/rivo/uniseg v0.3.4 // indirect
|
github.com/rivo/uniseg v0.3.4 // indirect
|
||||||
@ -330,17 +325,14 @@ require (
|
|||||||
github.com/segmentio/asm v1.1.4 // indirect
|
github.com/segmentio/asm v1.1.4 // indirect
|
||||||
github.com/shopspring/decimal v1.2.0 // indirect
|
github.com/shopspring/decimal v1.2.0 // indirect
|
||||||
github.com/spf13/cast v1.5.0 // indirect
|
github.com/spf13/cast v1.5.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
|
||||||
github.com/unknwon/bra v0.0.0-20200517080246-1e3013ecaff8 // indirect
|
github.com/unknwon/bra v0.0.0-20200517080246-1e3013ecaff8 // indirect
|
||||||
github.com/unknwon/com v1.0.1 // indirect
|
github.com/unknwon/com v1.0.1 // indirect
|
||||||
github.com/unknwon/log v0.0.0-20150304194804-e617c87089d3 // indirect
|
github.com/unknwon/log v0.0.0-20150304194804-e617c87089d3 // indirect
|
||||||
go.opentelemetry.io/otel/metric v0.34.0 // indirect
|
go.opentelemetry.io/otel/metric v0.34.0 // indirect
|
||||||
go.starlark.net v0.0.0-20221020143700-22309ac47eac // indirect
|
go.starlark.net v0.0.0-20221020143700-22309ac47eac // indirect
|
||||||
golang.org/x/term v0.3.0 // indirect
|
|
||||||
gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect
|
gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect
|
||||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
k8s.io/klog/v2 v2.80.0 // indirect
|
k8s.io/klog/v2 v2.80.0 // indirect
|
||||||
k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect
|
|
||||||
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect
|
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect
|
||||||
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
|
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
|
||||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
|
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
|
||||||
|
2
go.sum
2
go.sum
@ -727,6 +727,7 @@ github.com/elazarl/goproxy v0.0.0-20220115173737-adb46da277ac/go.mod h1:Ro8st/El
|
|||||||
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
|
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
|
||||||
github.com/elazarl/goproxy/ext v0.0.0-20220115173737-adb46da277ac h1:9yrT5tmn9Zc0ytWPASlaPwQfQMQYnRf0RSDe1XvHw0Q=
|
github.com/elazarl/goproxy/ext v0.0.0-20220115173737-adb46da277ac h1:9yrT5tmn9Zc0ytWPASlaPwQfQMQYnRf0RSDe1XvHw0Q=
|
||||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||||
|
github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk=
|
||||||
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||||
github.com/emicklei/go-restful/v3 v3.8.0 h1:eCZ8ulSerjdAiaNpF7GxXIE7ZCMo1moN1qX+S609eVw=
|
github.com/emicklei/go-restful/v3 v3.8.0 h1:eCZ8ulSerjdAiaNpF7GxXIE7ZCMo1moN1qX+S609eVw=
|
||||||
github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||||
@ -1839,7 +1840,6 @@ github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vv
|
|||||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||||
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
|
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
|
||||||
github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU=
|
github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU=
|
||||||
github.com/onsi/ginkgo/v2 v2.1.6 h1:Fx2POJZfKRQcM1pH49qSZiYeu319wji004qX+GDovrU=
|
|
||||||
github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
|
github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
|
||||||
github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||||
|
@ -37,8 +37,6 @@ export interface FeatureToggles {
|
|||||||
migrationLocking?: boolean;
|
migrationLocking?: boolean;
|
||||||
storage?: boolean;
|
storage?: boolean;
|
||||||
k8s?: boolean;
|
k8s?: boolean;
|
||||||
k8sDashboards?: boolean;
|
|
||||||
apiserver?: boolean;
|
|
||||||
supportBundles?: boolean;
|
supportBundles?: boolean;
|
||||||
dashboardsFromStorage?: boolean;
|
dashboardsFromStorage?: boolean;
|
||||||
export?: boolean;
|
export?: boolean;
|
||||||
|
@ -93,7 +93,6 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/stats"
|
"github.com/grafana/grafana/pkg/services/stats"
|
||||||
"github.com/grafana/grafana/pkg/services/store"
|
"github.com/grafana/grafana/pkg/services/store"
|
||||||
"github.com/grafana/grafana/pkg/services/store/entity/httpentitystore"
|
"github.com/grafana/grafana/pkg/services/store/entity/httpentitystore"
|
||||||
"github.com/grafana/grafana/pkg/services/store/k8saccess"
|
|
||||||
"github.com/grafana/grafana/pkg/services/tag"
|
"github.com/grafana/grafana/pkg/services/tag"
|
||||||
"github.com/grafana/grafana/pkg/services/team"
|
"github.com/grafana/grafana/pkg/services/team"
|
||||||
"github.com/grafana/grafana/pkg/services/teamguardian"
|
"github.com/grafana/grafana/pkg/services/teamguardian"
|
||||||
@ -258,7 +257,6 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
|
|||||||
annotationRepo annotations.Repository, tagService tag.Service, searchv2HTTPService searchV2.SearchHTTPService,
|
annotationRepo annotations.Repository, tagService tag.Service, searchv2HTTPService searchV2.SearchHTTPService,
|
||||||
queryLibraryHTTPService querylibrary.HTTPService, queryLibraryService querylibrary.Service, oauthTokenService oauthtoken.OAuthTokenService,
|
queryLibraryHTTPService querylibrary.HTTPService, queryLibraryService querylibrary.Service, oauthTokenService oauthtoken.OAuthTokenService,
|
||||||
statsService stats.Service, authnService authn.Service, pluginsCDNService *pluginscdn.Service,
|
statsService stats.Service, authnService authn.Service, pluginsCDNService *pluginscdn.Service,
|
||||||
k8saccess k8saccess.K8SAccess, // required so that the router is registered
|
|
||||||
starApi *starApi.API,
|
starApi *starApi.API,
|
||||||
) (*HTTPServer, error) {
|
) (*HTTPServer, error) {
|
||||||
web.Env = cfg.Env
|
web.Env = cfg.Env
|
||||||
|
@ -124,7 +124,6 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/store"
|
"github.com/grafana/grafana/pkg/services/store"
|
||||||
"github.com/grafana/grafana/pkg/services/store/entity/httpentitystore"
|
"github.com/grafana/grafana/pkg/services/store/entity/httpentitystore"
|
||||||
"github.com/grafana/grafana/pkg/services/store/entity/sqlstash"
|
"github.com/grafana/grafana/pkg/services/store/entity/sqlstash"
|
||||||
"github.com/grafana/grafana/pkg/services/store/k8saccess"
|
|
||||||
"github.com/grafana/grafana/pkg/services/store/kind"
|
"github.com/grafana/grafana/pkg/services/store/kind"
|
||||||
"github.com/grafana/grafana/pkg/services/store/resolver"
|
"github.com/grafana/grafana/pkg/services/store/resolver"
|
||||||
"github.com/grafana/grafana/pkg/services/store/sanitizer"
|
"github.com/grafana/grafana/pkg/services/store/sanitizer"
|
||||||
@ -365,7 +364,6 @@ var wireBasicSet = wire.NewSet(
|
|||||||
wire.Bind(new(tag.Service), new(*tagimpl.Service)),
|
wire.Bind(new(tag.Service), new(*tagimpl.Service)),
|
||||||
authnimpl.ProvideService,
|
authnimpl.ProvideService,
|
||||||
wire.Bind(new(authn.Service), new(*authnimpl.Service)),
|
wire.Bind(new(authn.Service), new(*authnimpl.Service)),
|
||||||
k8saccess.ProvideK8SAccess,
|
|
||||||
supportbundlesimpl.ProvideService,
|
supportbundlesimpl.ProvideService,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -3,22 +3,12 @@ package service
|
|||||||
import (
|
import (
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||||
"github.com/grafana/grafana/pkg/services/store/entity"
|
|
||||||
"github.com/grafana/grafana/pkg/services/store/k8saccess"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func ProvideSimpleDashboardService(
|
func ProvideSimpleDashboardService(
|
||||||
features featuremgmt.FeatureToggles,
|
features featuremgmt.FeatureToggles,
|
||||||
svc *DashboardServiceImpl,
|
svc *DashboardServiceImpl,
|
||||||
k8s k8saccess.K8SAccess,
|
|
||||||
store entity.EntityStoreServer,
|
|
||||||
) dashboards.DashboardService {
|
) dashboards.DashboardService {
|
||||||
if features.IsEnabled(featuremgmt.FlagK8sDashboards) {
|
|
||||||
if k8s.GetSystemClient() == nil {
|
|
||||||
panic("k8s dashboards requires the k8s client registered")
|
|
||||||
}
|
|
||||||
return k8saccess.NewDashboardService(svc, store)
|
|
||||||
}
|
|
||||||
return svc
|
return svc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,18 +119,6 @@ var (
|
|||||||
State: FeatureStateAlpha,
|
State: FeatureStateAlpha,
|
||||||
RequiresDevMode: true,
|
RequiresDevMode: true,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
Name: "k8sDashboards",
|
|
||||||
Description: "Save dashboards via k8s",
|
|
||||||
State: FeatureStateAlpha,
|
|
||||||
RequiresDevMode: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "apiserver",
|
|
||||||
Description: "Add a k8s API server proxy",
|
|
||||||
State: FeatureStateAlpha,
|
|
||||||
RequiresDevMode: true,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Name: "supportBundles",
|
Name: "supportBundles",
|
||||||
Description: "Support bundles for troubleshooting",
|
Description: "Support bundles for troubleshooting",
|
||||||
|
@ -91,14 +91,6 @@ const (
|
|||||||
// Explore native k8s integrations
|
// Explore native k8s integrations
|
||||||
FlagK8s = "k8s"
|
FlagK8s = "k8s"
|
||||||
|
|
||||||
// FlagK8sDashboards
|
|
||||||
// Save dashboards via k8s
|
|
||||||
FlagK8sDashboards = "k8sDashboards"
|
|
||||||
|
|
||||||
// FlagApiserver
|
|
||||||
// Add a k8s API server proxy
|
|
||||||
FlagApiserver = "apiserver"
|
|
||||||
|
|
||||||
// FlagSupportBundles
|
// FlagSupportBundles
|
||||||
// Support bundles for troubleshooting
|
// Support bundles for troubleshooting
|
||||||
FlagSupportBundles = "supportBundles"
|
FlagSupportBundles = "supportBundles"
|
||||||
|
@ -25,7 +25,6 @@ func TestFeatureToggleFiles(t *testing.T) {
|
|||||||
"live-pipeline": true,
|
"live-pipeline": true,
|
||||||
"live-service-web-worker": true,
|
"live-service-web-worker": true,
|
||||||
"k8s": true, // Camel case does not like this one
|
"k8s": true, // Camel case does not like this one
|
||||||
"k8sDashboards": true, // or this one
|
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("check registry constraints", func(t *testing.T) {
|
t.Run("check registry constraints", func(t *testing.T) {
|
||||||
|
@ -170,16 +170,6 @@ func (s *ServiceImpl) getServerAdminNode(c *contextmodel.ReqContext) *navtree.Na
|
|||||||
Url: s.cfg.AppSubURL + "/admin/storage/export",
|
Url: s.cfg.AppSubURL + "/admin/storage/export",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.features.IsEnabled(featuremgmt.FlagK8s) {
|
|
||||||
storage.Children = append(storage.Children, &navtree.NavLink{
|
|
||||||
Text: "Kubernetes",
|
|
||||||
Id: "k8s",
|
|
||||||
SubTitle: "Manage k8s storage",
|
|
||||||
Icon: "cube",
|
|
||||||
Url: s.cfg.AppSubURL + "/admin/storage/k8s",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.cfg.LDAPEnabled && hasAccess(ac.ReqGrafanaAdmin, ac.EvalPermission(ac.ActionLDAPStatusRead)) {
|
if s.cfg.LDAPEnabled && hasAccess(ac.ReqGrafanaAdmin, ac.EvalPermission(ac.ActionLDAPStatusRead)) {
|
||||||
|
@ -1,100 +0,0 @@
|
|||||||
package k8saccess
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
||||||
"k8s.io/client-go/kubernetes"
|
|
||||||
"k8s.io/client-go/rest"
|
|
||||||
|
|
||||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
|
||||||
"github.com/grafana/grafana/pkg/web"
|
|
||||||
)
|
|
||||||
|
|
||||||
type clientWrapper struct {
|
|
||||||
err error
|
|
||||||
baseURL *url.URL
|
|
||||||
client *kubernetes.Clientset
|
|
||||||
config *rest.Config
|
|
||||||
httpClient *http.Client
|
|
||||||
}
|
|
||||||
|
|
||||||
func newClientWrapper(config *rest.Config) *clientWrapper {
|
|
||||||
if config.UserAgent == "" {
|
|
||||||
config.UserAgent = rest.DefaultKubernetesUserAgent()
|
|
||||||
}
|
|
||||||
|
|
||||||
url, _, err := defaultServerUrlFor(config)
|
|
||||||
wrapper := &clientWrapper{
|
|
||||||
config: config,
|
|
||||||
baseURL: url,
|
|
||||||
err: err,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err == nil && config != nil {
|
|
||||||
// share the transport between all clients
|
|
||||||
wrapper.httpClient, wrapper.err = rest.HTTPClientFor(config)
|
|
||||||
if wrapper.err == nil {
|
|
||||||
wrapper.client, wrapper.err = kubernetes.NewForConfigAndClient(config, wrapper.httpClient)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return wrapper
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *clientWrapper) getInfo() map[string]interface{} {
|
|
||||||
info := make(map[string]interface{}, 0)
|
|
||||||
|
|
||||||
if s.err != nil {
|
|
||||||
info["error"] = s.err.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.baseURL != nil {
|
|
||||||
info["baseURL"] = s.baseURL.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.client != nil {
|
|
||||||
v, err := s.client.ServerVersion()
|
|
||||||
if err != nil {
|
|
||||||
info["version_error"] = err.Error()
|
|
||||||
}
|
|
||||||
if v != nil {
|
|
||||||
info["k8s.version"] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return info
|
|
||||||
}
|
|
||||||
|
|
||||||
// defaultServerUrlFor is shared between IsConfigTransportTLS and RESTClientFor. It
|
|
||||||
// requires Host and Version to be set prior to being called.
|
|
||||||
func defaultServerUrlFor(config *rest.Config) (*url.URL, string, error) {
|
|
||||||
// TODO: move the default to secure when the apiserver supports TLS by default
|
|
||||||
// config.Insecure is taken to mean "I want HTTPS but don't bother checking the certs against a CA."
|
|
||||||
hasCA := len(config.CAFile) != 0 || len(config.CAData) != 0
|
|
||||||
hasCert := len(config.CertFile) != 0 || len(config.CertData) != 0
|
|
||||||
defaultTLS := hasCA || hasCert || config.Insecure
|
|
||||||
host := config.Host
|
|
||||||
if host == "" {
|
|
||||||
host = "localhost"
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.GroupVersion != nil {
|
|
||||||
return rest.DefaultServerURL(host, config.APIPath, *config.GroupVersion, defaultTLS)
|
|
||||||
}
|
|
||||||
return rest.DefaultServerURL(host, config.APIPath, schema.GroupVersion{}, defaultTLS)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *clientWrapper) doProxy(c *contextmodel.ReqContext) {
|
|
||||||
if s.baseURL == nil {
|
|
||||||
c.Resp.WriteHeader(500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
params := web.Params(c.Req)
|
|
||||||
path := params["*"]
|
|
||||||
|
|
||||||
url := s.baseURL.JoinPath(path)
|
|
||||||
|
|
||||||
_, _ = c.Resp.Write([]byte("TODO, proxy: " + url.String()))
|
|
||||||
}
|
|
@ -1,93 +0,0 @@
|
|||||||
package k8saccess
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
|
||||||
"github.com/grafana/grafana/pkg/services/folder"
|
|
||||||
"github.com/grafana/grafana/pkg/services/store/entity"
|
|
||||||
)
|
|
||||||
|
|
||||||
type k8sDashboardService struct {
|
|
||||||
orig dashboards.DashboardService
|
|
||||||
store entity.EntityStoreServer
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ dashboards.DashboardService = (*k8sDashboardService)(nil)
|
|
||||||
|
|
||||||
func NewDashboardService(orig dashboards.DashboardService, store entity.EntityStoreServer) dashboards.DashboardService {
|
|
||||||
return &k8sDashboardService{
|
|
||||||
orig: orig,
|
|
||||||
store: store,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *k8sDashboardService) BuildSaveDashboardCommand(ctx context.Context, dto *dashboards.SaveDashboardDTO, shouldValidateAlerts bool, validateProvisionedDashboard bool) (*dashboards.SaveDashboardCommand, error) {
|
|
||||||
return s.orig.BuildSaveDashboardCommand(ctx, dto, shouldValidateAlerts, validateProvisionedDashboard)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *k8sDashboardService) DeleteDashboard(ctx context.Context, dashboardId int64, orgId int64) error {
|
|
||||||
return s.orig.DeleteDashboard(ctx, dashboardId, orgId)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *k8sDashboardService) FindDashboards(ctx context.Context, query *dashboards.FindPersistedDashboardsQuery) ([]dashboards.DashboardSearchProjection, error) {
|
|
||||||
return s.orig.FindDashboards(ctx, query)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *k8sDashboardService) GetDashboard(ctx context.Context, query *dashboards.GetDashboardQuery) (*dashboards.Dashboard, error) {
|
|
||||||
return s.orig.GetDashboard(ctx, query)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *k8sDashboardService) GetDashboardACLInfoList(ctx context.Context, query *dashboards.GetDashboardACLInfoListQuery) ([]*dashboards.DashboardACLInfoDTO, error) {
|
|
||||||
return s.orig.GetDashboardACLInfoList(ctx, query)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *k8sDashboardService) GetDashboards(ctx context.Context, query *dashboards.GetDashboardsQuery) ([]*dashboards.Dashboard, error) {
|
|
||||||
return s.orig.GetDashboards(ctx, query)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *k8sDashboardService) GetDashboardTags(ctx context.Context, query *dashboards.GetDashboardTagsQuery) ([]*dashboards.DashboardTagCloudItem, error) {
|
|
||||||
return s.orig.GetDashboardTags(ctx, query)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *k8sDashboardService) GetDashboardUIDByID(ctx context.Context, query *dashboards.GetDashboardRefByIDQuery) (*dashboards.DashboardRef, error) {
|
|
||||||
return s.orig.GetDashboardUIDByID(ctx, query)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *k8sDashboardService) HasAdminPermissionInDashboardsOrFolders(ctx context.Context, query *folder.HasAdminPermissionInDashboardsOrFoldersQuery) (bool, error) {
|
|
||||||
return s.orig.HasAdminPermissionInDashboardsOrFolders(ctx, query)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *k8sDashboardService) HasEditPermissionInFolders(ctx context.Context, query *folder.HasEditPermissionInFoldersQuery) (bool, error) {
|
|
||||||
return s.orig.HasEditPermissionInFolders(ctx, query)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *k8sDashboardService) ImportDashboard(ctx context.Context, dto *dashboards.SaveDashboardDTO) (*dashboards.Dashboard, error) {
|
|
||||||
return s.orig.ImportDashboard(ctx, dto)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *k8sDashboardService) MakeUserAdmin(ctx context.Context, orgID int64, userID, dashboardID int64, setViewAndEditPermissions bool) error {
|
|
||||||
return s.orig.MakeUserAdmin(ctx, orgID, userID, dashboardID, setViewAndEditPermissions)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *k8sDashboardService) SaveDashboard(ctx context.Context, dto *dashboards.SaveDashboardDTO, allowUiUpdate bool) (*dashboards.Dashboard, error) {
|
|
||||||
fmt.Printf("SAVE: " + dto.Dashboard.UID)
|
|
||||||
return s.orig.SaveDashboard(ctx, dto, allowUiUpdate)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *k8sDashboardService) SearchDashboards(ctx context.Context, query *dashboards.FindPersistedDashboardsQuery) error {
|
|
||||||
return s.orig.SearchDashboards(ctx, query)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *k8sDashboardService) UpdateDashboardACL(ctx context.Context, uid int64, items []*dashboards.DashboardACL) error {
|
|
||||||
return s.orig.UpdateDashboardACL(ctx, uid, items)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *k8sDashboardService) DeleteACLByUser(ctx context.Context, userID int64) error {
|
|
||||||
return s.orig.DeleteACLByUser(ctx, userID)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *k8sDashboardService) CountDashboardsInFolder(ctx context.Context, query *dashboards.CountDashboardsInFolderQuery) (int64, error) {
|
|
||||||
return s.orig.CountDashboardsInFolder(ctx, query)
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
package k8saccess
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/grafana/grafana/pkg/api/response"
|
|
||||||
"github.com/grafana/grafana/pkg/api/routing"
|
|
||||||
"github.com/grafana/grafana/pkg/middleware"
|
|
||||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
|
||||||
)
|
|
||||||
|
|
||||||
type httpHelper struct {
|
|
||||||
access *k8sAccess
|
|
||||||
}
|
|
||||||
|
|
||||||
func newHTTPHelper(access *k8sAccess, router routing.RouteRegister) *httpHelper {
|
|
||||||
s := &httpHelper{
|
|
||||||
access: access,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Must be admin for everything
|
|
||||||
router.Group("/api/k8s", func(k8sRoute routing.RouteRegister) {
|
|
||||||
k8sRoute.Get("/info", middleware.ReqOrgAdmin, routing.Wrap(s.showClientInfo))
|
|
||||||
k8sRoute.Any("/proxy/*", middleware.ReqOrgAdmin, s.doProxy)
|
|
||||||
})
|
|
||||||
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *httpHelper) showClientInfo(c *contextmodel.ReqContext) response.Response {
|
|
||||||
if s.access.sys != nil {
|
|
||||||
info := s.access.sys.getInfo()
|
|
||||||
if s.access.sys.err != nil {
|
|
||||||
return response.JSON(500, info)
|
|
||||||
}
|
|
||||||
return response.JSON(200, info)
|
|
||||||
}
|
|
||||||
return response.JSON(500, map[string]interface{}{
|
|
||||||
"error": "no client initialized",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *httpHelper) doProxy(c *contextmodel.ReqContext) {
|
|
||||||
// TODO... this does not yet do a real proxy
|
|
||||||
if s.access.sys != nil {
|
|
||||||
if s.access.sys.err == nil {
|
|
||||||
s.access.sys.doProxy(c)
|
|
||||||
} else {
|
|
||||||
c.Resp.WriteHeader(500)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, _ = c.Resp.Write([]byte("??"))
|
|
||||||
}
|
|
@ -1,81 +0,0 @@
|
|||||||
package k8saccess
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"k8s.io/client-go/kubernetes"
|
|
||||||
"k8s.io/client-go/rest"
|
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/api/routing"
|
|
||||||
"github.com/grafana/grafana/pkg/registry"
|
|
||||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
type K8SAccess interface {
|
|
||||||
registry.CanBeDisabled
|
|
||||||
|
|
||||||
// Get the system client
|
|
||||||
GetSystemClient() *kubernetes.Clientset
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ K8SAccess = &k8sAccess{}
|
|
||||||
|
|
||||||
type k8sAccess struct {
|
|
||||||
enabled bool
|
|
||||||
apihelper *httpHelper
|
|
||||||
sys *clientWrapper
|
|
||||||
}
|
|
||||||
|
|
||||||
func ProvideK8SAccess(toggles featuremgmt.FeatureToggles, router routing.RouteRegister) K8SAccess {
|
|
||||||
access := &k8sAccess{
|
|
||||||
enabled: toggles.IsEnabled(featuremgmt.FlagK8s),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skips setting up any HTTP routing
|
|
||||||
if !access.enabled {
|
|
||||||
return access // dummy
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we are in a cluster, this is the
|
|
||||||
config, err := rest.InClusterConfig()
|
|
||||||
|
|
||||||
// Look for kube config setup
|
|
||||||
if err != nil {
|
|
||||||
var home string
|
|
||||||
var configBytes []byte
|
|
||||||
home, err = os.UserHomeDir()
|
|
||||||
if err == nil {
|
|
||||||
fpath := filepath.Join(home, ".kube", "config")
|
|
||||||
//nolint:gosec
|
|
||||||
configBytes, err = os.ReadFile(fpath)
|
|
||||||
if err == nil {
|
|
||||||
config, err = clientcmd.RESTConfigFromKubeConfig(configBytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err == nil && config != nil {
|
|
||||||
access.sys = newClientWrapper(config)
|
|
||||||
} else {
|
|
||||||
access.sys = &clientWrapper{
|
|
||||||
err: err,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
access.apihelper = newHTTPHelper(access, router)
|
|
||||||
return access
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *k8sAccess) IsDisabled() bool {
|
|
||||||
return !s.enabled
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return access to the system k8s client
|
|
||||||
func (s *k8sAccess) GetSystemClient() *kubernetes.Clientset {
|
|
||||||
if s.sys != nil {
|
|
||||||
return s.sys.client
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,63 +0,0 @@
|
|||||||
import { css } from '@emotion/css';
|
|
||||||
import React from 'react';
|
|
||||||
import { useAsync } from 'react-use';
|
|
||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data';
|
|
||||||
import { getBackendSrv } from '@grafana/runtime';
|
|
||||||
import { useStyles2, Spinner, Alert } from '@grafana/ui';
|
|
||||||
import { Page } from 'app/core/components/Page/Page';
|
|
||||||
import { useNavModel } from 'app/core/hooks/useNavModel';
|
|
||||||
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
|
|
||||||
|
|
||||||
interface RouteParams {
|
|
||||||
// path: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface QueryParams {
|
|
||||||
// view: StorageView;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props extends GrafanaRouteComponentProps<RouteParams, QueryParams> {}
|
|
||||||
|
|
||||||
export default function K8SPage(props: Props) {
|
|
||||||
const styles = useStyles2(getStyles);
|
|
||||||
const navModel = useNavModel('k8s');
|
|
||||||
const info = useAsync(() => {
|
|
||||||
return getBackendSrv().get('/api/k8s/info');
|
|
||||||
});
|
|
||||||
|
|
||||||
const renderView = () => {
|
|
||||||
if (info.value) {
|
|
||||||
return <pre>{JSON.stringify(info.value, null, 2)}</pre>;
|
|
||||||
}
|
|
||||||
if (info.loading) {
|
|
||||||
return <Spinner />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={styles.wrapper}>
|
|
||||||
<Alert title="No k8s client configured." severity="warning" />
|
|
||||||
At startup, a service tries to load a client using:
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
Default <a href="https://github.com/kubernetes/client-go/blob/master/rest/config.go#L511">in cluster</a>{' '}
|
|
||||||
configs
|
|
||||||
</li>
|
|
||||||
<li>$HOME/.kube/config, perhaps with minikube running</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Page navModel={navModel}>
|
|
||||||
<Page.Contents isLoading={info.loading}>{renderView()}</Page.Contents>
|
|
||||||
</Page>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const getStyles = (theme: GrafanaTheme2) => ({
|
|
||||||
wrapper: css`
|
|
||||||
display: block;
|
|
||||||
`,
|
|
||||||
});
|
|
@ -366,13 +366,6 @@ export function getAppRoutes(): RouteDescriptor[] {
|
|||||||
() => import(/* webpackChunkName: "AdminEditOrgPage" */ 'app/features/admin/AdminEditOrgPage')
|
() => import(/* webpackChunkName: "AdminEditOrgPage" */ 'app/features/admin/AdminEditOrgPage')
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: '/admin/storage/k8s',
|
|
||||||
roles: () => ['Admin'],
|
|
||||||
component: SafeDynamicImport(
|
|
||||||
() => import(/* webpackChunkName: "K8SStoragePage" */ 'app/features/storage/k8s/K8SPage')
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: '/admin/storage/export',
|
path: '/admin/storage/export',
|
||||||
roles: () => ['Admin'],
|
roles: () => ['Admin'],
|
||||||
|
Loading…
Reference in New Issue
Block a user