From a118b61fda641c623247b134fcb8ab74a264e98b Mon Sep 17 00:00:00 2001 From: Sebastian Florek Date: Fri, 8 Mar 2024 16:11:20 +0100 Subject: [PATCH 1/3] add full proxy mode to api module --- docs/common/arguments.md | 2 +- hack/scripts/conf.sh | 2 - hack/scripts/start-cluster.sh | 21 -------- .../kubernetes-dashboard-local.yaml | 5 +- modules/api/main.go | 48 ++++++++++++------- modules/api/pkg/args/args.go | 5 ++ modules/api/pkg/handler/apihandler.go | 2 +- 7 files changed, 41 insertions(+), 44 deletions(-) diff --git a/docs/common/arguments.md b/docs/common/arguments.md index 1c3157699990..c92d20d7bfb8 100644 --- a/docs/common/arguments.md +++ b/docs/common/arguments.md @@ -14,7 +14,6 @@ Dashboard containers accept multiple arguments that can be used to customize the | tls-key-file | - | File containing the default x509 private key matching --tls-cert-file. | | auto-generate-certificates | false | When set to true, Dashboard will automatically generate certificates used to serve HTTPS. | | apiserver-host | - | The address of the Kubernetes Apiserver to connect to in the format of protocol://address:port, e.g., http://localhost:8080. If not specified, the assumption is that the binary runs inside a Kubernetes cluster and local discovery is attempted. | -| heapster-host | - | The address of the Heapster Apiserver to connect to in the format of protocol://address:port, e.g., http://localhost:8082. If not specified, the assumption is that the binary runs inside a Kubernetes cluster and service proxy will be used. | | sidecar-host | - | The address of the Sidecar Apiserver to connect to in the format of protocol://address:port, e.g., http://localhost:8000. If not specified, the assumption is that the binary runs inside a Kubernetes cluster and service proxy will be used. | | metrics-provider | sidecar | Select provider type for metrics. 'none' will not check metrics. | | metric-client-check-period | 30 | Time in seconds that defines how often configured metric client health check should be run. | @@ -23,6 +22,7 @@ Dashboard containers accept multiple arguments that can be used to customize the | metrics-scraper-service-name | kubernetes-dashboard-metrics-scraper | Name of the dashboard metrics scraper service. | | disable-csrf-protection | false | Allows disabling CSRF protection. | | csrf-key | - | Base64 encoded random 256 bytes key. Can be loaded from 'CSRF_KEY' environment variable. | +| act-as-proxy | false | Forces dashboard to work in full proxy mode, meaning that any in-cluster calls are disabled. | | v | 1 | Number for the log level verbosity (default 1) | | ## Auth module arguments diff --git a/hack/scripts/conf.sh b/hack/scripts/conf.sh index ce13433dd766..5056cdd487c2 100644 --- a/hack/scripts/conf.sh +++ b/hack/scripts/conf.sh @@ -26,8 +26,6 @@ RELEASE_VERSION=2.5.0 ARCH=$(uname | awk '{print tolower($0)}') # Local cluster configuration (check start-cluster.sh script for more details). -HEAPSTER_VERSION="v1.5.4" -HEAPSTER_PORT=8082 KIND_VERSION="v0.19.0" K8S_VERSION="v1.29.2" KIND_BIN=${CACHE_DIR}/kind-${KIND_VERSION} diff --git a/hack/scripts/start-cluster.sh b/hack/scripts/start-cluster.sh index 9813a99d96db..4a83063c4d94 100755 --- a/hack/scripts/start-cluster.sh +++ b/hack/scripts/start-cluster.sh @@ -17,30 +17,9 @@ ROOT_DIR="$(cd $(dirname "${BASH_SOURCE}")/../.. && pwd -P)" . "${ROOT_DIR}/hack/scripts/conf.sh" -function start-ci-heapster { - echo "\nRunning heapster in standalone mode" - docker run --net=host -d registry.k8s.io/heapster-amd64:${HEAPSTER_VERSION} \ - --heapster-port ${HEAPSTER_PORT} \ - --source=kubernetes:http://127.0.0.1:8080?inClusterConfig=false&auth="" - - echo "\nWaiting for heapster to be started" - for i in {1..150} - do - HEAPSTER_STATUS=$(curl -sb -H "Accept: application/json" "127.0.0.1:${HEAPSTER_PORT}/healthz") - if [ "${HEAPSTER_STATUS}" == "ok" ]; then - break - fi - sleep 2 - done - echo "\nHeapster is up and running" -} - function start-kind { ${KIND_BIN} create cluster --name="k8s-cluster-ci" --image="kindest/node:${K8S_VERSION}" --config="${ROOT_DIR}/hack/scripts/kind-config" ensure-kubeconfig - if [ "${CI}" = true ] ; then - start-ci-heapster - fi echo "\nKubernetes cluster is ready to use" } diff --git a/hack/test-resources/kubernetes-dashboard-local.yaml b/hack/test-resources/kubernetes-dashboard-local.yaml index 5da5349ec6a3..0b7e2c8a63b4 100755 --- a/hack/test-resources/kubernetes-dashboard-local.yaml +++ b/hack/test-resources/kubernetes-dashboard-local.yaml @@ -74,14 +74,13 @@ rules: resources: ["configmaps"] resourceNames: ["kubernetes-dashboard-settings"] verbs: ["get", "update"] - # Allow Dashboard to get metrics from heapster. - apiGroups: [""] resources: ["services"] - resourceNames: ["heapster", "dashboard-metrics-scraper"] + resourceNames: ["dashboard-metrics-scraper"] verbs: ["proxy"] - apiGroups: [""] resources: ["services/proxy"] - resourceNames: ["heapster", "http:heapster:", "https:heapster:", "dashboard-metrics-scraper", "http:dashboard-metrics-scraper"] + resourceNames: ["dashboard-metrics-scraper", "http:dashboard-metrics-scraper"] verbs: ["get"] --- kind: ClusterRole diff --git a/modules/api/main.go b/modules/api/main.go index eb23ebe6f7a4..e20894941c8d 100644 --- a/modules/api/main.go +++ b/modules/api/main.go @@ -43,27 +43,19 @@ func main() { client.WithInsecureTLSSkipVerify(args.ApiServerSkipTLSVerify()), ) - versionInfo, err := client.InClusterClient().Discovery().ServerVersion() - if err != nil { - handleFatalInitError(err) + if !args.IsProxyEnabled() { + ensureAPIServerConnectionOrDie() + } else { + klog.Info("Running in proxy mode. InClusterClient connections will be disabled.") } - klog.InfoS("Successful initial request to the apiserver", "version", versionInfo.String()) - // Init integrations integrationManager := integration.NewIntegrationManager() - switch metricsProvider := args.MetricsProvider(); metricsProvider { - case "sidecar": - integrationManager.Metric().ConfigureSidecar(args.SidecarHost()). - EnableWithRetry(integrationapi.SidecarIntegrationID, time.Duration(args.MetricClientHealthCheckPeriod())) - case "none": - klog.Info("Metrics provider disabled") - default: - klog.InfoS("Invalid metrics provider", "provider", metricsProvider) - klog.Info("Using default metrics provider", "provider", "sidecar") - integrationManager.Metric().ConfigureSidecar(args.SidecarHost()). - EnableWithRetry(integrationapi.SidecarIntegrationID, time.Duration(args.MetricClientHealthCheckPeriod())) + if !args.IsProxyEnabled() { + configureMetricsProvider(integrationManager) + } else { + klog.Info("Skipping metrics configuration. Metrics not available in proxy mode.") } apiHandler, err := handler.CreateHTTPAPIHandler(integrationManager) @@ -109,6 +101,30 @@ func serveTLS(certificates []tls.Certificate) { go func() { klog.Fatal(server.ListenAndServeTLS("", "")) }() } +func ensureAPIServerConnectionOrDie() { + versionInfo, err := client.InClusterClient().Discovery().ServerVersion() + if err != nil { + handleFatalInitError(err) + } + + klog.InfoS("Successful initial request to the apiserver", "version", versionInfo.String()) +} + +func configureMetricsProvider(integrationManager integration.Manager) { + switch metricsProvider := args.MetricsProvider(); metricsProvider { + case "sidecar": + integrationManager.Metric().ConfigureSidecar(args.SidecarHost()). + EnableWithRetry(integrationapi.SidecarIntegrationID, time.Duration(args.MetricClientHealthCheckPeriod())) + case "none": + klog.Info("Metrics provider disabled") + default: + klog.InfoS("Invalid metrics provider", "provider", metricsProvider) + klog.Info("Using default metrics provider", "provider", "sidecar") + integrationManager.Metric().ConfigureSidecar(args.SidecarHost()). + EnableWithRetry(integrationapi.SidecarIntegrationID, time.Duration(args.MetricClientHealthCheckPeriod())) + } +} + /** * Handles fatal init error that prevents server from doing any work. Prints verbose error * message and quits the server. diff --git a/modules/api/pkg/args/args.go b/modules/api/pkg/args/args.go index 07544e2d2168..7ee52d89b757 100644 --- a/modules/api/pkg/args/args.go +++ b/modules/api/pkg/args/args.go @@ -55,6 +55,7 @@ var ( argNamespace = pflag.String("namespace", helpers.GetEnv("POD_NAMESPACE", "kubernetes-dashboard"), "Namespace to use when accessing Dashboard specific resources, i.e. metrics scraper service") argMetricsScraperServiceName = pflag.String("metrics-scraper-service-name", "kubernetes-dashboard-metrics-scraper", "name of the dashboard metrics scraper service") argDisableCSRFProtection = pflag.Bool("disable-csrf-protection", false, "allows disabling CSRF protection") + argIsProxyEnabled = pflag.Bool("act-as-proxy", false, "forces dashboard to work in full proxy mode, meaning that any in-cluster calls are disabled") ) func init() { @@ -147,3 +148,7 @@ func Namespace() string { func IsCSRFProtectionEnabled() bool { return !*argDisableCSRFProtection } + +func IsProxyEnabled() bool { + return *argIsProxyEnabled +} diff --git a/modules/api/pkg/handler/apihandler.go b/modules/api/pkg/handler/apihandler.go index ef01ee747a46..599f5abe68ef 100644 --- a/modules/api/pkg/handler/apihandler.go +++ b/modules/api/pkg/handler/apihandler.go @@ -78,7 +78,7 @@ const ( ResponseLogString = "[%s] Outcoming response to %s with %d status code" ) -// APIHandler is a representation of API handler. Structure contains clientapi, Heapster clientapi and clientapi configuration. +// APIHandler is a representation of API handler. Structure contains clientapi and clientapi configuration. type APIHandler struct { iManager integration.Manager } From 6a0875ce82a264725788a9311ced32683847f1e6 Mon Sep 17 00:00:00 2001 From: Sebastian Florek Date: Fri, 8 Mar 2024 16:23:48 +0100 Subject: [PATCH 2/3] yarn fix --- modules/web/Makefile | 5 ++++ modules/web/src/chrome/userpanel/component.ts | 9 ++++-- .../common/services/global/authentication.ts | 2 +- .../src/common/services/global/interceptor.ts | 2 +- modules/web/src/common/services/global/me.ts | 28 ++++++++++--------- .../web/src/common/services/global/module.ts | 4 +-- 6 files changed, 30 insertions(+), 20 deletions(-) diff --git a/modules/web/Makefile b/modules/web/Makefile index 38aeb0622616..929f6589f3b9 100644 --- a/modules/web/Makefile +++ b/modules/web/Makefile @@ -62,6 +62,11 @@ coverage-go: --ensure-dir @echo "[$(APP_NAME)] Running tests with coverage" @go test -coverprofile=$(COVERAGE_FILE) -covermode=atomic $(PACKAGE_NAME)/... +.PHONY: fix-frontend +fix-frontend: $(PRE) + @echo "[$(APP_NAME)] Running yarn fix" + @yarn fix + .PHONY: fix-go fix-go: $(PRE) @echo "[$(APP_NAME)] Running lint --fix" diff --git a/modules/web/src/chrome/userpanel/component.ts b/modules/web/src/chrome/userpanel/component.ts index ba84198021a0..332ca18889a5 100644 --- a/modules/web/src/chrome/userpanel/component.ts +++ b/modules/web/src/chrome/userpanel/component.ts @@ -15,7 +15,7 @@ import {Component, ViewChild} from '@angular/core'; import {MatMenuTrigger} from '@angular/material/menu'; import {AuthService} from '@common/services/global/authentication'; -import {MeService} from "@common/services/global/me"; +import {MeService} from '@common/services/global/me'; @Component({ selector: 'kd-user-panel', @@ -26,10 +26,13 @@ export class UserPanelComponent /* implements OnInit */ { @ViewChild(MatMenuTrigger) private readonly trigger_: MatMenuTrigger; - constructor(private readonly authService_: AuthService, private readonly _meService: MeService) {} + constructor( + private readonly authService_: AuthService, + private readonly _meService: MeService + ) {} get username(): string { - return this._meService.getUserName() + return this._meService.getUserName(); } hasAuthHeader(): boolean { diff --git a/modules/web/src/common/services/global/authentication.ts b/modules/web/src/common/services/global/authentication.ts index 231ed4ad416e..77eb6b793be8 100644 --- a/modules/web/src/common/services/global/authentication.ts +++ b/modules/web/src/common/services/global/authentication.ts @@ -25,7 +25,7 @@ import {CONFIG_DI_TOKEN} from '../../../index.config'; import {CsrfTokenService} from './csrftoken'; import {KdStateService} from './state'; import isEmpty from 'lodash-es/isEmpty'; -import {MeService} from "@common/services/global/me"; +import {MeService} from '@common/services/global/me'; @Injectable() export class AuthService { diff --git a/modules/web/src/common/services/global/interceptor.ts b/modules/web/src/common/services/global/interceptor.ts index b00029a96dc4..2aaac89a005f 100644 --- a/modules/web/src/common/services/global/interceptor.ts +++ b/modules/web/src/common/services/global/interceptor.ts @@ -19,7 +19,7 @@ import {CookieService} from 'ngx-cookie-service'; import {Observable} from 'rxjs'; import {CONFIG_DI_TOKEN} from '../../../index.config'; import {AuthService} from '@common/services/global/authentication'; -import {MeService} from "@common/services/global/me"; +import {MeService} from '@common/services/global/me'; @Injectable() export class AuthInterceptor implements HttpInterceptor { diff --git a/modules/web/src/common/services/global/me.ts b/modules/web/src/common/services/global/me.ts index 8cc89bfc01dd..5709f305bdde 100644 --- a/modules/web/src/common/services/global/me.ts +++ b/modules/web/src/common/services/global/me.ts @@ -14,25 +14,27 @@ * limitations under the License. */ -import {Injectable} from "@angular/core"; -import {User} from "@api/root.api"; -import {HttpClient} from "@angular/common/http"; -import {interval, Observable} from "rxjs"; -import {startWith, switchMap} from "rxjs/operators"; +import {Injectable} from '@angular/core'; +import {User} from '@api/root.api'; +import {HttpClient} from '@angular/common/http'; +import {interval, Observable} from 'rxjs'; +import {startWith, switchMap} from 'rxjs/operators'; @Injectable() export class MeService { private readonly _endpoint = 'api/v1/me'; private _user: User; - private _interval = interval(30000) + private _interval = interval(30000); constructor(private readonly _http: HttpClient) {} init(): void { - this._interval.pipe( - startWith({} as User), - switchMap(() => this.me()) - ).subscribe(user => this._user = user) + this._interval + .pipe( + startWith({} as User), + switchMap(() => this.me()) + ) + .subscribe(user => (this._user = user)); } me(): Observable { @@ -40,11 +42,11 @@ export class MeService { } getUserName(): string { - return this._user?.name + return this._user?.name; } getUser(): User { - return this._user + return this._user; } reset(): void { @@ -52,6 +54,6 @@ export class MeService { } refresh(): void { - this.me().subscribe(user => this._user = user) + this.me().subscribe(user => (this._user = user)); } } diff --git a/modules/web/src/common/services/global/module.ts b/modules/web/src/common/services/global/module.ts index c0e4b92fee45..63ab394e7e1d 100644 --- a/modules/web/src/common/services/global/module.ts +++ b/modules/web/src/common/services/global/module.ts @@ -36,7 +36,7 @@ import {ThemeService} from './theme'; import {TitleService} from './title'; import {VerberService} from './verber'; import {PinnerService} from './pinner'; -import {MeService} from "@common/services/global/me"; +import {MeService} from '@common/services/global/me'; @NgModule({ providers: [ @@ -98,7 +98,7 @@ export function init( history: HistoryService, theme: ThemeService, loader: LocalConfigLoaderService, - me: MeService, + me: MeService ): Function { return () => { return loader.init().then(() => { From 7b152841aea2f93be1bfe8f383d41816c6298029 Mon Sep 17 00:00:00 2001 From: Sebastian Florek Date: Fri, 8 Mar 2024 16:24:06 +0100 Subject: [PATCH 3/3] add fix-frontend to make fix --- modules/web/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/web/Makefile b/modules/web/Makefile index 929f6589f3b9..d7745261292e 100644 --- a/modules/web/Makefile +++ b/modules/web/Makefile @@ -26,7 +26,7 @@ clean: coverage: coverage-frontend coverage-go .PHONY: fix -fix: fix-go +fix: fix-frontend fix-go .PHONY: test test: test-go