This is an automated email from the ASF dual-hosted git repository.
mfordjody pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/dubbo-kubernetes.git
The following commit(s) were added to refs/heads/master by this push:
new 6c3e8a44 Update dubboctl commands (#985)
6c3e8a44 is described below
commit 6c3e8a446547f204920fc7c11b7a6f2ab4e4ec61
Author: mfordjody <[email protected]>
AuthorDate: Mon Aug 3 20:54:13 2026 +0800
Update dubboctl commands (#985)
---
.asf.yaml | 28 +++-
.github/workflows/ci.yml | 6 +-
.github/workflows/commands.yml | 12 +-
.github/workflows/labels.yml | 40 -----
.github/workflows/review-gate.yml | 10 +-
dubboctl/cmd/admin.go | 152 -----------------
dubboctl/cmd/admin_test.go | 71 --------
dubboctl/cmd/dashboard.go | 333 --------------------------------------
dubboctl/cmd/dashboard_test.go | 143 ----------------
dubboctl/cmd/root.go | 2 -
10 files changed, 46 insertions(+), 751 deletions(-)
diff --git a/.asf.yaml b/.asf.yaml
index 26b9a477..5f17302e 100644
--- a/.asf.yaml
+++ b/.asf.yaml
@@ -42,9 +42,29 @@ github:
required_status_checks:
strict: true
checks:
- # Pin both gates to the GitHub Actions App so a personal token cannot
- # forge either required result.
- - context: CI Required
+ # Require the concrete CI jobs directly. A separate fan-in job needs
+ # another scarce ASF runner after every real check has already
passed.
+ - context: Lint
app_id: 15368
+ - context: Check Repo Hygiene
+ app_id: 15368
+ - context: Unit Tests
+ app_id: 15368
+ - context: Performance Scale Smoke
+ app_id: 15368
+ - context: Build (linux/amd64)
+ app_id: 15368
+ - context: Build (linux/arm64)
+ app_id: 15368
+ - context: Build (darwin/amd64)
+ app_id: 15368
+ - context: Build (darwin/arm64)
+ app_id: 15368
+ - context: Helm Charts
+ app_id: 15368
+ - context: E2E Smoke (kind)
+ app_id: 15368
+ # Pin every gate to the GitHub Actions App so a personal token cannot
+ # forge a required result.
- context: Maintainer Approval Gate
- app_id: 15368
\ No newline at end of file
+ app_id: 15368
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c5539223..57d73e96 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -213,9 +213,11 @@ jobs:
required:
name: CI Required
- runs-on: ubuntu-latest
+ runs-on: ubuntu-slim
timeout-minutes: 5
- if: ${{ always() }}
+ # Evaluate after all dependencies settle, but do not keep a superseded
+ # workflow alive and blocking the next run in this PR's concurrency group.
+ if: ${{ always() && !cancelled() }}
needs:
- lint
- check
diff --git a/.github/workflows/commands.yml b/.github/workflows/commands.yml
index 19f092ba..5310e0a4 100644
--- a/.github/workflows/commands.yml
+++ b/.github/workflows/commands.yml
@@ -52,11 +52,19 @@ permissions:
jobs:
command:
name: Handle Command
- runs-on: ubuntu-latest
+ runs-on: ubuntu-slim
timeout-minutes: 10
if: |
github.event_name == 'issue_comment' &&
- github.repository == 'apache/dubbo-kubernetes'
+ github.repository == 'apache/dubbo-kubernetes' &&
+ (
+ contains(github.event.comment.body, '/assign') ||
+ contains(github.event.comment.body, '/unassign') ||
+ contains(github.event.comment.body, '/lgtm') ||
+ contains(github.event.comment.body, '/close') ||
+ contains(github.event.comment.body, '/reopen') ||
+ contains(github.event.comment.body, '/retest')
+ )
env:
BOT_APP_ID: ${{ secrets.BOT_APP_ID }}
steps:
diff --git a/.github/workflows/labels.yml b/.github/workflows/labels.yml
deleted file mode 100644
index 9de471b0..00000000
--- a/.github/workflows/labels.yml
+++ /dev/null
@@ -1,40 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements. See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-name: Labels
-
-on:
- pull_request_target:
- types:
- - opened
- - reopened
- - synchronize
- - ready_for_review
-
-permissions:
- contents: read
- pull-requests: write
-
-jobs:
- changed-files:
- name: Apply PR Labels
- runs-on: ubuntu-latest
- timeout-minutes: 5
- if: github.repository == 'apache/dubbo-kubernetes'
- steps:
- - name: Apply labels from changed paths
- uses: actions/labeler@b8dd2d9be0f68b860e7dae5dae7d772984eacd6d # v6.2.0
- with:
- repo-token: ${{ github.token }}
diff --git a/.github/workflows/review-gate.yml
b/.github/workflows/review-gate.yml
index 9a0b5741..3407906c 100644
--- a/.github/workflows/review-gate.yml
+++ b/.github/workflows/review-gate.yml
@@ -35,16 +35,22 @@ on:
permissions:
contents: read
issues: write
- pull-requests: read
+ pull-requests: write
statuses: write
jobs:
approval:
name: Publish Maintainer Approval
- runs-on: ubuntu-latest
+ runs-on: ubuntu-slim
timeout-minutes: 5
if: github.repository == 'apache/dubbo-kubernetes'
steps:
+ - name: Apply PR labels
+ if: github.event_name == 'pull_request_target'
+ uses: actions/labeler@b8dd2d9be0f68b860e7dae5dae7d772984eacd6d # v6.2.0
+ with:
+ repo-token: ${{ github.token }}
+
- name: Publish approval status
uses: actions/github-script@v7
with:
diff --git a/dubboctl/cmd/admin.go b/dubboctl/cmd/admin.go
deleted file mode 100644
index 04bf509f..00000000
--- a/dubboctl/cmd/admin.go
+++ /dev/null
@@ -1,152 +0,0 @@
-//
-// Licensed to the Apache Software Foundation (ASF) under one or more
-// contributor license agreements. See the NOTICE file distributed with
-// this work for additional information regarding copyright ownership.
-// The ASF licenses this file to You under the Apache License, Version 2.0
-// (the "License"); you may not use this file except in compliance with
-// the License. You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package cmd
-
-import (
- "context"
- "fmt"
- "io"
- "sort"
-
- "github.com/apache/dubbo-kubernetes/dubboctl/pkg/cli"
- "github.com/spf13/cobra"
- corev1 "k8s.io/api/core/v1"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- klabels "k8s.io/apimachinery/pkg/labels"
- "k8s.io/client-go/kubernetes"
-)
-
-type adminLogArgs struct {
- namespace string
- deployment string
- container string
- tail int64
-}
-
-func AdminCmd(ctx cli.Context) *cobra.Command {
- command := &cobra.Command{
- Use: "admin",
- Short: "Access dubbod admin information",
- }
- command.AddCommand(adminLogCmd(ctx))
- return command
-}
-
-func adminLogCmd(ctx cli.Context) *cobra.Command {
- args := &adminLogArgs{
- namespace: ctx.Namespace(),
- deployment: "dubbod",
- container: "execute",
- tail: 200,
- }
-
- command := &cobra.Command{
- Use: "log",
- Short: "Print dubbod logs",
- Args: cobra.NoArgs,
- RunE: func(cmd *cobra.Command, _ []string) error {
- client, err := ctx.CLIClient()
- if err != nil {
- return err
- }
- return printDeploymentLogs(cmd.Context(),
client.Kube(), *args, cmd.OutOrStdout())
- },
- }
- flags := command.Flags()
- flags.StringVarP(&args.namespace, "namespace", "n", args.namespace,
"Namespace of the dubbod deployment")
- flags.StringVar(&args.deployment, "deployment", args.deployment,
"Dubbod deployment name")
- flags.StringVar(&args.container, "container", args.container,
"Preferred container name")
- flags.Int64Var(&args.tail, "tail", args.tail, "Number of recent log
lines to print")
- return command
-}
-
-func printDeploymentLogs(ctx context.Context, client kubernetes.Interface,
args adminLogArgs, writer io.Writer) error {
- pods, err := deploymentPods(ctx, client, args.namespace,
args.deployment)
- if err != nil {
- return err
- }
- if len(pods) == 0 {
- return fmt.Errorf("no pods found for deployment %s/%s",
args.namespace, args.deployment)
- }
-
- for _, pod := range pods {
- for _, container := range logContainers(pod, args.container) {
- if _, err := fmt.Fprintf(writer, "==> %s/%s:%s <==\n",
pod.Namespace, pod.Name, container); err != nil {
- return err
- }
- raw, err :=
client.CoreV1().Pods(pod.Namespace).GetLogs(pod.Name, &corev1.PodLogOptions{
- Container: container,
- TailLines: &args.tail,
- Timestamps: true,
- }).DoRaw(ctx)
- if err != nil {
- return fmt.Errorf("get logs %s/%s container %s:
%w", pod.Namespace, pod.Name, container, err)
- }
- if _, err := writer.Write(raw); err != nil {
- return err
- }
- if len(raw) == 0 || raw[len(raw)-1] != '\n' {
- if _, err := fmt.Fprintln(writer); err != nil {
- return err
- }
- }
- }
- }
- return nil
-}
-
-func deploymentPods(ctx context.Context, client kubernetes.Interface,
namespace, name string) ([]corev1.Pod, error) {
- deployment, err := client.AppsV1().Deployments(namespace).Get(ctx,
name, metav1.GetOptions{})
- if err != nil {
- return nil, fmt.Errorf("get deployment %s/%s: %w", namespace,
name, err)
- }
- if deployment.Spec.Selector == nil {
- return nil, fmt.Errorf("deployment %s/%s has no selector",
namespace, name)
- }
- selector, err :=
metav1.LabelSelectorAsSelector(deployment.Spec.Selector)
- if err != nil {
- return nil, fmt.Errorf("build pod selector for deployment
%s/%s: %w", namespace, name, err)
- }
- if selector.Empty() {
- selector =
klabels.SelectorFromSet(deployment.Spec.Template.Labels)
- }
- pods, err := client.CoreV1().Pods(namespace).List(ctx,
metav1.ListOptions{
- LabelSelector: selector.String(),
- })
- if err != nil {
- return nil, fmt.Errorf("list pods for deployment %s/%s: %w",
namespace, name, err)
- }
- sort.SliceStable(pods.Items, func(i, j int) bool {
- return pods.Items[i].Name < pods.Items[j].Name
- })
- return pods.Items, nil
-}
-
-func logContainers(pod corev1.Pod, preferred string) []string {
- if preferred != "" {
- for _, container := range pod.Spec.Containers {
- if container.Name == preferred {
- return []string{preferred}
- }
- }
- }
- out := make([]string, 0, len(pod.Spec.Containers))
- for _, container := range pod.Spec.Containers {
- out = append(out, container.Name)
- }
- return out
-}
diff --git a/dubboctl/cmd/admin_test.go b/dubboctl/cmd/admin_test.go
deleted file mode 100644
index 11307f04..00000000
--- a/dubboctl/cmd/admin_test.go
+++ /dev/null
@@ -1,71 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one or more
-// contributor license agreements. See the NOTICE file distributed with
-// this work for additional information regarding copyright ownership.
-// The ASF licenses this file to You under the Apache License, Version 2.0
-// (the "License"); you may not use this file except in compliance with
-// the License. You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package cmd
-
-import (
- "context"
- "reflect"
- "testing"
-
- appsv1 "k8s.io/api/apps/v1"
- corev1 "k8s.io/api/core/v1"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/client-go/kubernetes/fake"
-)
-
-func TestDeploymentPodsUsesDeploymentSelector(t *testing.T) {
- client := fake.NewSimpleClientset(
- &appsv1.Deployment{
- ObjectMeta: metav1.ObjectMeta{Name: "dubbod",
Namespace: "dubbo-system"},
- Spec: appsv1.DeploymentSpec{
- Selector: &metav1.LabelSelector{MatchLabels:
map[string]string{"app": "dubbod"}},
- Template: corev1.PodTemplateSpec{
- ObjectMeta: metav1.ObjectMeta{Labels:
map[string]string{"app": "dubbod"}},
- },
- },
- },
- &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "dubbod-b",
Namespace: "dubbo-system", Labels: map[string]string{"app": "dubbod"}}},
- &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "dubbod-a",
Namespace: "dubbo-system", Labels: map[string]string{"app": "dubbod"}}},
- &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "other",
Namespace: "dubbo-system", Labels: map[string]string{"app": "other"}}},
- )
-
- pods, err := deploymentPods(context.Background(), client,
"dubbo-system", "dubbod")
- if err != nil {
- t.Fatalf("deploymentPods() returned error: %v", err)
- }
- got := []string{}
- for _, pod := range pods {
- got = append(got, pod.Name)
- }
- want := []string{"dubbod-a", "dubbod-b"}
- if !reflect.DeepEqual(got, want) {
- t.Fatalf("deploymentPods() = %v, want %v", got, want)
- }
-}
-
-func TestLogContainersPrefersRequestedContainer(t *testing.T) {
- pod := corev1.Pod{
- Spec: corev1.PodSpec{
- Containers: []corev1.Container{{Name: "app"}, {Name:
"execute"}},
- },
- }
- if got := logContainers(pod, "execute"); !reflect.DeepEqual(got,
[]string{"execute"}) {
- t.Fatalf("logContainers() = %v, want [execute]", got)
- }
- if got := logContainers(pod, "missing"); !reflect.DeepEqual(got,
[]string{"app", "execute"}) {
- t.Fatalf("logContainers() fallback = %v, want all containers",
got)
- }
-}
diff --git a/dubboctl/cmd/dashboard.go b/dubboctl/cmd/dashboard.go
deleted file mode 100644
index d56e5205..00000000
--- a/dubboctl/cmd/dashboard.go
+++ /dev/null
@@ -1,333 +0,0 @@
-//
-// Licensed to the Apache Software Foundation (ASF) under one or more
-// contributor license agreements. See the NOTICE file distributed with
-// this work for additional information regarding copyright ownership.
-// The ASF licenses this file to You under the Apache License, Version 2.0
-// (the "License"); you may not use this file except in compliance with
-// the License. You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package cmd
-
-import (
- "bufio"
- "bytes"
- "context"
- "encoding/json"
- "fmt"
- "io"
- "os"
- "path/filepath"
- "time"
-
- "github.com/apache/dubbo-kubernetes/dubboctl/pkg/cli"
- "github.com/apache/dubbo-kubernetes/pkg/kube"
- "github.com/spf13/cobra"
- appsv1 "k8s.io/api/apps/v1"
- apierrors "k8s.io/apimachinery/pkg/api/errors"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
- "k8s.io/apimachinery/pkg/types"
- "k8s.io/apimachinery/pkg/util/wait"
- utilyaml "k8s.io/apimachinery/pkg/util/yaml"
- "k8s.io/client-go/kubernetes"
- "sigs.k8s.io/yaml"
-)
-
-const dashboardNamespace = "dubbo-system"
-
-type dashboardArgs struct {
- manifests string
- wait time.Duration
-}
-
-type appliedObject struct {
- Kind string
- Name string
- Namespace string
-}
-
-func DashboardCmd(ctx cli.Context) *cobra.Command {
- args := &dashboardArgs{
- manifests: "samples/addons",
- wait: 2 * time.Minute,
- }
-
- command := &cobra.Command{
- Use: "dashboard",
- Short: "Start the sample Prometheus and Grafana dashboard",
- Args: cobra.NoArgs,
- RunE: func(cmd *cobra.Command, _ []string) error {
- client, err := ctx.CLIClient()
- if err != nil {
- return err
- }
-
- files, err := dashboardManifestFiles(args.manifests)
- if err != nil {
- return err
- }
-
- var applied []appliedObject
- for _, file := range files {
- objects, err :=
applyManifestFile(cmd.Context(), client, file)
- if err != nil {
- return err
- }
- applied = append(applied, objects...)
- }
- if err := printAppliedDashboard(cmd.OutOrStdout(),
applied); err != nil {
- return err
- }
-
- if args.wait > 0 {
- if err := waitForDashboard(cmd.Context(),
client.Kube(), dashboardNamespace, dashboardWaitDeploymentNames(files),
args.wait); err != nil {
- return err
- }
- if _, err := fmt.Fprintln(cmd.OutOrStdout(),
"dashboard ready"); err != nil {
- return err
- }
- }
-
- _, err = fmt.Fprintf(cmd.OutOrStdout(), "prometheus:
kubectl -n %s port-forward svc/prometheus 9090:9090\ngrafana: kubectl -n %s
port-forward svc/grafana 3000:3000\n", dashboardNamespace, dashboardNamespace)
- if err != nil {
- return err
- }
- if dashboardHasTracing(files) {
- _, err = fmt.Fprintf(cmd.OutOrStdout(),
"tracing: kubectl -n %s port-forward svc/tracing 16686:16686\n",
dashboardNamespace)
- if err != nil {
- return err
- }
- }
- if dashboardHasOpenTelemetry(files) {
- _, err = fmt.Fprintf(cmd.OutOrStdout(),
"opentelemetry: kubectl -n %s port-forward svc/opentelemetry-collector
4317:4317\n", dashboardNamespace)
- }
- return err
- },
- }
- command.Flags().StringVar(&args.manifests, "manifests", args.manifests,
"Dashboard manifest file or samples/addons directory")
- command.Flags().DurationVar(&args.wait, "wait", args.wait, "Maximum
time to wait for observability deployments; 0 disables waiting")
-
- command.AddCommand(dashboardTargetCmd(ctx, "grafana", "Open the Grafana
dashboards", "grafana", 3000, 3000))
- command.AddCommand(dashboardTargetCmd(ctx, "prometheus", "Open the
Prometheus UI", "prometheus", 9090, 9090))
- command.AddCommand(dashboardTargetCmd(ctx, "tracing", "Open the
distributed tracing UI", "tracing", 16686, 16686))
- command.AddCommand(dashboardTargetCmd(ctx, "otel", "Forward the
OpenTelemetry collector OTLP gRPC port", "opentelemetry-collector", 4317, 4317))
- return command
-}
-
-// dashboardTargetCmd builds a subcommand that port-forwards to one
-// observability addon. Installation is handled by the operator (for example
-// `dubboctl install --set profile=demo`); these subcommands only provide
access.
-func dashboardTargetCmd(ctx cli.Context, name, short, serviceName string,
localPort, podPort int) *cobra.Command {
- port := localPort
- cmd := &cobra.Command{
- Use: name,
- Short: short,
- Args: cobra.NoArgs,
- RunE: func(cmd *cobra.Command, _ []string) error {
- client, err := ctx.CLIClient()
- if err != nil {
- return err
- }
- return forwardToService(cmd.Context(), client,
cmd.OutOrStdout(), dashboardNamespace, serviceName, port, podPort)
- },
- }
- cmd.Flags().IntVarP(&port, "port", "p", localPort, "Local port to
forward from")
- return cmd
-}
-
-func dashboardManifestFiles(path string) ([]string, error) {
- info, err := os.Stat(path)
- if err != nil {
- return nil, err
- }
- if !info.IsDir() {
- return []string{path}, nil
- }
-
- prometheus := filepath.Join(path, "prometheus.yaml")
- if _, err := os.Stat(prometheus); err != nil {
- prometheus = filepath.Join(path, "metrics.yaml")
- if _, err := os.Stat(prometheus); err != nil {
- return nil, fmt.Errorf("dashboard prometheus manifest
not found in %s", path)
- }
- }
-
- grafana := filepath.Join(path, "grafana.yaml")
- if _, err := os.Stat(grafana); err != nil {
- return nil, fmt.Errorf("dashboard grafana manifest not found in
%s", path)
- }
- files := []string{prometheus, grafana}
- tracing := filepath.Join(path, "tracing.yaml")
- if _, err := os.Stat(tracing); err == nil {
- files = append(files, tracing)
- }
- opentelemetry := filepath.Join(path, "opentelemetry.yaml")
- if _, err := os.Stat(opentelemetry); err == nil {
- files = append(files, opentelemetry)
- }
- return files, nil
-}
-
-func dashboardHasTracing(files []string) bool {
- for _, file := range files {
- if filepath.Base(file) == "tracing.yaml" {
- return true
- }
- }
- return false
-}
-
-func dashboardHasOpenTelemetry(files []string) bool {
- for _, file := range files {
- if filepath.Base(file) == "opentelemetry.yaml" {
- return true
- }
- }
- return false
-}
-
-func dashboardWaitDeploymentNames(files []string) []string {
- names := []string{"prometheus", "grafana"}
- if dashboardHasTracing(files) {
- names = append(names, "tracing")
- }
- if dashboardHasOpenTelemetry(files) {
- names = append(names, "opentelemetry-collector")
- }
- return names
-}
-
-func applyManifestFile(ctx context.Context, client kube.CLIClient, file
string) ([]appliedObject, error) {
- objects, err := readManifestObjects(file)
- if err != nil {
- return nil, err
- }
- applied := make([]appliedObject, 0, len(objects))
- for _, object := range objects {
- item, err := applyDashboardObject(ctx, client, object)
- if err != nil {
- return nil, err
- }
- applied = append(applied, item)
- }
- return applied, nil
-}
-
-func readManifestObjects(path string) ([]*unstructured.Unstructured, error) {
- file, err := os.Open(path)
- if err != nil {
- return nil, err
- }
- defer file.Close()
-
- reader := utilyaml.NewYAMLReader(bufio.NewReader(file))
- out := []*unstructured.Unstructured{}
- for {
- doc, err := reader.Read()
- if err == io.EOF {
- return out, nil
- }
- if err != nil {
- return nil, fmt.Errorf("read manifest %s: %w", path,
err)
- }
- doc = bytes.TrimSpace(doc)
- if len(doc) == 0 {
- continue
- }
- jsonDoc, err := yaml.YAMLToJSON(doc)
- if err != nil {
- return nil, fmt.Errorf("decode manifest %s: %w", path,
err)
- }
- object := &unstructured.Unstructured{}
- if err := json.Unmarshal(jsonDoc, object); err != nil {
- return nil, fmt.Errorf("decode manifest %s: %w", path,
err)
- }
- if object.GetKind() == "" || object.GetAPIVersion() == "" ||
object.GetName() == "" {
- return nil, fmt.Errorf("manifest %s contains an object
without apiVersion, kind, or metadata.name", path)
- }
- out = append(out, object)
- }
-}
-
-func applyDashboardObject(ctx context.Context, client kube.CLIClient, object
*unstructured.Unstructured) (appliedObject, error) {
- data, err := json.Marshal(object.Object)
- if err != nil {
- return appliedObject{}, err
- }
- resource, err := client.DynamicClientFor(object.GroupVersionKind(),
object, object.GetNamespace())
- if err != nil {
- return appliedObject{}, err
- }
- force := true
- applied, err := resource.Patch(ctx, object.GetName(),
types.ApplyPatchType, data, metav1.PatchOptions{
- FieldManager: "dubboctl-dashboard",
- Force: &force,
- })
- if err != nil {
- return appliedObject{}, fmt.Errorf("apply %s %s/%s: %w",
object.GetKind(), object.GetNamespace(), object.GetName(), err)
- }
- kind := applied.GetKind()
- if kind == "" {
- kind = object.GetKind()
- }
- namespace := applied.GetNamespace()
- if namespace == "" {
- namespace = object.GetNamespace()
- }
- return appliedObject{
- Kind: kind,
- Name: applied.GetName(),
- Namespace: namespace,
- }, nil
-}
-
-func printAppliedDashboard(writer io.Writer, objects []appliedObject) error {
- for _, object := range objects {
- name := object.Name
- if object.Namespace != "" {
- name = object.Namespace + "/" + name
- }
- if _, err := fmt.Fprintf(writer, "applied %s %s\n",
object.Kind, name); err != nil {
- return err
- }
- }
- return nil
-}
-
-func waitForDashboard(ctx context.Context, client kubernetes.Interface,
namespace string, names []string, timeout time.Duration) error {
- for _, name := range names {
- if err := waitForAvailableDeployment(ctx, client, namespace,
name, timeout); err != nil {
- return err
- }
- }
- return nil
-}
-
-func waitForAvailableDeployment(ctx context.Context, client
kubernetes.Interface, namespace, name string, timeout time.Duration) error {
- return wait.PollUntilContextTimeout(ctx, 2*time.Second, timeout, true,
func(ctx context.Context) (bool, error) {
- deployment, err :=
client.AppsV1().Deployments(namespace).Get(ctx, name, metav1.GetOptions{})
- if apierrors.IsNotFound(err) {
- return false, nil
- }
- if err != nil {
- return false, err
- }
- return deploymentAvailable(deployment), nil
- })
-}
-
-func deploymentAvailable(deployment *appsv1.Deployment) bool {
- desired := int32(1)
- if deployment.Spec.Replicas != nil {
- desired = *deployment.Spec.Replicas
- }
- return deployment.Status.AvailableReplicas >= desired
-}
diff --git a/dubboctl/cmd/dashboard_test.go b/dubboctl/cmd/dashboard_test.go
deleted file mode 100644
index 00084d42..00000000
--- a/dubboctl/cmd/dashboard_test.go
+++ /dev/null
@@ -1,143 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one or more
-// contributor license agreements. See the NOTICE file distributed with
-// this work for additional information regarding copyright ownership.
-// The ASF licenses this file to You under the Apache License, Version 2.0
-// (the "License"); you may not use this file except in compliance with
-// the License. You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package cmd
-
-import (
- "os"
- "path/filepath"
- "reflect"
- "strings"
- "testing"
-
- appsv1 "k8s.io/api/apps/v1"
-)
-
-func TestDashboardManifestFiles(t *testing.T) {
- dir := t.TempDir()
- for _, name := range []string{"prometheus.yaml", "grafana.yaml"} {
- if err := os.WriteFile(filepath.Join(dir, name),
[]byte("apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: "+name+"\n"),
0o600); err != nil {
- t.Fatal(err)
- }
- }
-
- got, err := dashboardManifestFiles(dir)
- if err != nil {
- t.Fatalf("dashboardManifestFiles() returned error: %v", err)
- }
- want := []string{filepath.Join(dir, "prometheus.yaml"),
filepath.Join(dir, "grafana.yaml")}
- if !reflect.DeepEqual(got, want) {
- t.Fatalf("dashboardManifestFiles() = %v, want %v", got, want)
- }
- if dashboardHasTracing(got) {
- t.Fatalf("dashboardHasTracing() = true, want false")
- }
- if names := dashboardWaitDeploymentNames(got);
!reflect.DeepEqual(names, []string{"prometheus", "grafana"}) {
- t.Fatalf("dashboardWaitDeploymentNames() = %v", names)
- }
-}
-
-func TestDashboardManifestFilesIncludesTracingWhenPresent(t *testing.T) {
- dir := t.TempDir()
- for _, name := range []string{"prometheus.yaml", "grafana.yaml",
"tracing.yaml"} {
- if err := os.WriteFile(filepath.Join(dir, name),
[]byte("apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: "+name+"\n"),
0o600); err != nil {
- t.Fatal(err)
- }
- }
-
- got, err := dashboardManifestFiles(dir)
- if err != nil {
- t.Fatalf("dashboardManifestFiles() returned error: %v", err)
- }
- want := []string{filepath.Join(dir, "prometheus.yaml"),
filepath.Join(dir, "grafana.yaml"), filepath.Join(dir, "tracing.yaml")}
- if !reflect.DeepEqual(got, want) {
- t.Fatalf("dashboardManifestFiles() = %v, want %v", got, want)
- }
- if !dashboardHasTracing(got) {
- t.Fatalf("dashboardHasTracing() = false, want true")
- }
- if names := dashboardWaitDeploymentNames(got);
!reflect.DeepEqual(names, []string{"prometheus", "grafana", "tracing"}) {
- t.Fatalf("dashboardWaitDeploymentNames() = %v", names)
- }
-}
-
-func TestDashboardManifestFilesIncludesOpenTelemetryWhenPresent(t *testing.T) {
- dir := t.TempDir()
- for _, name := range []string{"prometheus.yaml", "grafana.yaml",
"tracing.yaml", "opentelemetry.yaml"} {
- if err := os.WriteFile(filepath.Join(dir, name),
[]byte("apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: "+name+"\n"),
0o600); err != nil {
- t.Fatal(err)
- }
- }
-
- got, err := dashboardManifestFiles(dir)
- if err != nil {
- t.Fatalf("dashboardManifestFiles() returned error: %v", err)
- }
- want := []string{
- filepath.Join(dir, "prometheus.yaml"),
- filepath.Join(dir, "grafana.yaml"),
- filepath.Join(dir, "tracing.yaml"),
- filepath.Join(dir, "opentelemetry.yaml"),
- }
- if !reflect.DeepEqual(got, want) {
- t.Fatalf("dashboardManifestFiles() = %v, want %v", got, want)
- }
- if !dashboardHasOpenTelemetry(got) {
- t.Fatalf("dashboardHasOpenTelemetry() = false, want true")
- }
- if names := dashboardWaitDeploymentNames(got);
!reflect.DeepEqual(names, []string{"prometheus", "grafana", "tracing",
"opentelemetry-collector"}) {
- t.Fatalf("dashboardWaitDeploymentNames() = %v", names)
- }
-}
-
-func TestReadManifestObjects(t *testing.T) {
- file := filepath.Join(t.TempDir(), "manifest.yaml")
- manifest := strings.Join([]string{
- "apiVersion: v1",
- "kind: ConfigMap",
- "metadata:",
- " name: first",
- "---",
- "apiVersion: v1",
- "kind: Service",
- "metadata:",
- " name: second",
- "",
- }, "\n")
- if err := os.WriteFile(file, []byte(manifest), 0o600); err != nil {
- t.Fatal(err)
- }
-
- objects, err := readManifestObjects(file)
- if err != nil {
- t.Fatalf("readManifestObjects() returned error: %v", err)
- }
- if len(objects) != 2 {
- t.Fatalf("readManifestObjects() returned %d objects, want 2",
len(objects))
- }
- if objects[0].GetKind() != "ConfigMap" || objects[1].GetKind() !=
"Service" {
- t.Fatalf("unexpected objects: %s, %s", objects[0].GetKind(),
objects[1].GetKind())
- }
-}
-
-func TestDeploymentAvailable(t *testing.T) {
- replicas := int32(2)
- if deploymentAvailable(&appsv1.Deployment{Spec:
appsv1.DeploymentSpec{Replicas: &replicas}, Status:
appsv1.DeploymentStatus{AvailableReplicas: 1}}) {
- t.Fatal("deploymentAvailable() = true, want false")
- }
- if !deploymentAvailable(&appsv1.Deployment{Spec:
appsv1.DeploymentSpec{Replicas: &replicas}, Status:
appsv1.DeploymentStatus{AvailableReplicas: 2}}) {
- t.Fatal("deploymentAvailable() = false, want true")
- }
-}
diff --git a/dubboctl/cmd/root.go b/dubboctl/cmd/root.go
index 5685ccc4..1321403f 100644
--- a/dubboctl/cmd/root.go
+++ b/dubboctl/cmd/root.go
@@ -145,10 +145,8 @@ func GetRootCmd(args []string) *cobra.Command {
hideFlags(imageCmd, ChartFlag)
rootCmd.AddCommand(GetCmd(ctx))
- rootCmd.AddCommand(AdminCmd(ctx))
rootCmd.AddCommand(ProxyStatusCmd(ctx))
rootCmd.AddCommand(AnalyzeCmd(ctx))
- rootCmd.AddCommand(DashboardCmd(ctx))
rootCmd.AddCommand(MulticlusterCmd())
rootCmd.AddCommand(GuiCmd())