diff --git a/ctl/common/common.go b/ctl/common/common.go index b804bafc1..ceaf1b062 100644 --- a/ctl/common/common.go +++ b/ctl/common/common.go @@ -23,6 +23,7 @@ import ( "kmesh.net/kmesh/ctl/dump" logcmd "kmesh.net/kmesh/ctl/log" "kmesh.net/kmesh/ctl/monitoring" + "kmesh.net/kmesh/ctl/namespace" "kmesh.net/kmesh/ctl/secret" "kmesh.net/kmesh/ctl/version" "kmesh.net/kmesh/ctl/waypoint" @@ -41,6 +42,7 @@ func GetRootCommand() *cobra.Command { rootCmd.AddCommand(logcmd.NewCmd()) rootCmd.AddCommand(dump.NewCmd()) rootCmd.AddCommand(waypoint.NewCmd()) + rootCmd.AddCommand(namespace.NewCmd()) rootCmd.AddCommand(version.NewCmd()) rootCmd.AddCommand(monitoring.NewCmd()) rootCmd.AddCommand(authz.NewCmd()) diff --git a/ctl/namespace/namespace.go b/ctl/namespace/namespace.go new file mode 100644 index 000000000..0ab08af5c --- /dev/null +++ b/ctl/namespace/namespace.go @@ -0,0 +1,99 @@ +/* + * Copyright The Kmesh Authors. + * + * Licensed 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 namespace + +import ( + "context" + "fmt" + "text/tabwriter" + + "github.com/spf13/cobra" + "istio.io/api/label" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "kmesh.net/kmesh/ctl/utils" +) + +var ( + DataplaneModeKmesh = "Kmesh" + KmeshUseWaypointLabel = "istio.io/use-waypoint" +) + +func NewCmd() *cobra.Command { + namespaceCmd := &cobra.Command{ + Use: "namespace", + Short: "Manage Kmesh namespaces", + Long: "A group of commands used to manage Kmesh namespaces", + Example: ` # List all namespaces enrolled in Kmesh + kmeshctl namespace list`, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 0 { + return fmt.Errorf("unknown subcommand %q", args[0]) + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + cmd.HelpFunc()(cmd, args) + return nil + }, + } + + namespaceListCmd := &cobra.Command{ + Use: "list", + Short: "List Kmesh enrolled namespaces", + Long: "List all namespaces that have the istio.io/dataplane-mode=Kmesh label", + Example: ` # List all Kmesh namespaces + kmeshctl namespace list`, + RunE: func(cmd *cobra.Command, args []string) error { + kubeClient, err := utils.CreateKubeClient() + if err != nil { + return fmt.Errorf("failed to create Kubernetes client: %v", err) + } + + // We need to list namespaces + labelSelector := fmt.Sprintf("%s=%s", label.IoIstioDataplaneMode.Name, DataplaneModeKmesh) + nsList, err := kubeClient.Kube().CoreV1().Namespaces().List(context.Background(), metav1.ListOptions{ + LabelSelector: labelSelector, + }) + if err != nil { + return fmt.Errorf("failed to list namespaces: %v", err) + } + + writer := cmd.OutOrStdout() + if len(nsList.Items) == 0 { + fmt.Fprintln(writer, "No Kmesh namespaces found.") + return nil + } + + w := new(tabwriter.Writer).Init(writer, 0, 8, 5, ' ', 0) + fmt.Fprintln(w, "NAME\tWAYPOINT") + + for _, ns := range nsList.Items { + waypoint := ns.Labels[KmeshUseWaypointLabel] + if waypoint == "" { + waypoint = "None" + } + fmt.Fprintf(w, "%s\t%s\n", ns.Name, waypoint) + } + return w.Flush() + }, + } + + namespaceCmd.AddCommand(namespaceListCmd) + + return namespaceCmd +} diff --git a/ctl/namespace/namespace_test.go b/ctl/namespace/namespace_test.go new file mode 100644 index 000000000..4395a5978 --- /dev/null +++ b/ctl/namespace/namespace_test.go @@ -0,0 +1,40 @@ +/* + * Copyright The Kmesh Authors. + * + * Licensed 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 namespace + +import ( + "testing" +) + +func TestNewCmd(t *testing.T) { + cmd := NewCmd() + if cmd.Use != "namespace" { + t.Fatalf("Use = %q, want %q", cmd.Use, "namespace") + } + + foundListCmd := false + for _, subcmd := range cmd.Commands() { + if subcmd.Use == "list" { + foundListCmd = true + break + } + } + + if !foundListCmd { + t.Errorf("list subcommand not defined") + } +} diff --git a/docs/ctl/kmeshctl.md b/docs/ctl/kmeshctl.md index 2a0c1eadf..b37142f96 100644 --- a/docs/ctl/kmeshctl.md +++ b/docs/ctl/kmeshctl.md @@ -14,6 +14,7 @@ Kmesh command line tools to operate and debug Kmesh * [kmeshctl dump](kmeshctl_dump.md) - Dump config of kernel-native or dual-engine mode * [kmeshctl log](kmeshctl_log.md) - Get or set kmesh-daemon's logger level * [kmeshctl monitoring](kmeshctl_monitoring.md) - Control Kmesh's monitoring to be turned on as needed +* [kmeshctl namespace](kmeshctl_namespace.md) - Manage Kmesh namespaces * [kmeshctl secret](kmeshctl_secret.md) - Use secrets to manage secret configuration data for IPsec * [kmeshctl version](kmeshctl_version.md) - Prints out build version info * [kmeshctl waypoint](kmeshctl_waypoint.md) - Manage waypoint configuration diff --git a/docs/ctl/kmeshctl_namespace.md b/docs/ctl/kmeshctl_namespace.md new file mode 100644 index 000000000..a518a6d79 --- /dev/null +++ b/docs/ctl/kmeshctl_namespace.md @@ -0,0 +1,29 @@ +## kmeshctl namespace + +Manage Kmesh namespaces + +### Synopsis + +A group of commands used to manage Kmesh namespaces + +```bash +kmeshctl namespace [flags] +``` + +### Examples + +```bash + # List all namespaces enrolled in Kmesh + kmeshctl namespace list +``` + +### Options + +```bash + -h, --help help for namespace +``` + +### SEE ALSO + +* [kmeshctl](kmeshctl.md) - Kmesh command line tools to operate and debug Kmesh +* [kmeshctl namespace list](kmeshctl_namespace_list.md) - List Kmesh enrolled namespaces diff --git a/docs/ctl/kmeshctl_namespace_list.md b/docs/ctl/kmeshctl_namespace_list.md new file mode 100644 index 000000000..a92c1ead4 --- /dev/null +++ b/docs/ctl/kmeshctl_namespace_list.md @@ -0,0 +1,28 @@ +## kmeshctl namespace list + +List Kmesh enrolled namespaces + +### Synopsis + +List all namespaces that have the istio.io/dataplane-mode=Kmesh label + +```bash +kmeshctl namespace list [flags] +``` + +### Examples + +```bash + # List all Kmesh namespaces + kmeshctl namespace list +``` + +### Options + +```bash + -h, --help help for list +``` + +### SEE ALSO + +* [kmeshctl namespace](kmeshctl_namespace.md) - Manage Kmesh namespaces diff --git a/test/e2e/baseline_test.go b/test/e2e/baseline_test.go index 9b4a22277..b75d31439 100644 --- a/test/e2e/baseline_test.go +++ b/test/e2e/baseline_test.go @@ -725,9 +725,9 @@ func TestRemoveAddNsOrServiceWaypoint(t *testing.T) { // and all pass through waypoint. func TestMixNsAndServiceWaypoint(t *testing.T) { framework.NewTest(t).Run(func(t framework.TestContext) { - waypoint := "namespace-waypoint" + waypoint := "mix-namespace-waypoint" - newWaypointProxyOrFail(t, t, apps.Namespace, waypoint, constants.ServiceTraffic) + newWaypointProxyOrFail(t, t, apps.Namespace, waypoint, constants.AllTraffic) t.Cleanup(func() { deleteWaypointProxyOrFail(t, t, apps.Namespace, waypoint) }) @@ -737,6 +737,9 @@ func TestMixNsAndServiceWaypoint(t *testing.T) { UnsetWaypoint(t, apps.Namespace.Name(), "", Namespace) }) + // Wait for waypoint configuration to propagate to eBPF/XDS + time.Sleep(10 * time.Second) + runTestContext(t, func(t framework.TestContext, src echo.Instance, dst echo.Instance, opt echo.CallOptions) { if opt.Scheme != scheme.HTTP { return @@ -804,9 +807,9 @@ func TestBookinfo(t *testing.T) { // Set namespace waypoint to verify that bookinfo could be accessed normally event if each hop // is processed by waypoint. - waypoint := "namespace-waypoint" + waypoint := "bookinfo-namespace-waypoint" - newWaypointProxyOrFail(t, t, apps.Namespace, waypoint, constants.ServiceTraffic) + newWaypointProxyOrFail(t, t, apps.Namespace, waypoint, constants.AllTraffic) t.Cleanup(func() { deleteWaypointProxyOrFail(t, t, apps.Namespace, waypoint) }) @@ -816,6 +819,9 @@ func TestBookinfo(t *testing.T) { UnsetWaypoint(t, namespace, "", Namespace) }) + // Wait for waypoint configuration to propagate to eBPF/XDS + time.Sleep(10 * time.Second) + if err := retry.Until(checkBookinfo, retry.Timeout(900*time.Second), retry.Delay(3*time.Second)); err != nil { t.Fatal("failed to access bookinfo correctly when there is a namespace waypoint: %v", err) } diff --git a/test/e2e/manage_test.go b/test/e2e/manage_test.go index f80b9243a..65296ec01 100644 --- a/test/e2e/manage_test.go +++ b/test/e2e/manage_test.go @@ -27,6 +27,7 @@ import ( "context" "fmt" "testing" + "time" "istio.io/api/label" "istio.io/istio/pkg/config/constants" @@ -189,7 +190,7 @@ func TestCrossNamespace(t *testing.T) { dst := apps.ServiceWithWaypointAtServiceGranularity - unenrolledNSTest := func() { + unenrolledNSTest := func(t framework.TestContext) { tests := []struct { svc echo.Instances enrolled bool @@ -224,10 +225,11 @@ func TestCrossNamespace(t *testing.T) { } t.NewSubTest("cross namespace access, the new namespace is not managed by Kmesh").Run(func(t framework.TestContext) { - unenrolledNSTest() + unenrolledNSTest(t) }) enrollNamespaceOrFail(t, anotherNS.Name()) + time.Sleep(5 * time.Second) t.NewSubTest("cross namespace access, the new namespace is managed by Kmesh").Run(func(t framework.TestContext) { for _, src := range all { @@ -244,9 +246,10 @@ func TestCrossNamespace(t *testing.T) { }) unenrollNamespaceOrFail(t, anotherNS.Name()) + time.Sleep(5 * time.Second) t.NewSubTest("cross namespace access, the new namespace is not managed by Kmesh **AGAIN**").Run(func(t framework.TestContext) { - unenrolledNSTest() + unenrolledNSTest(t) }) }) } diff --git a/test/e2e/run_test.sh b/test/e2e/run_test.sh index 37580d8b1..03ea44ff5 100755 --- a/test/e2e/run_test.sh +++ b/test/e2e/run_test.sh @@ -188,7 +188,7 @@ function setup_kmesh() { # Set BPF debug log for i in {1..5}; do echo "Attempt $i of 5: kmeshctl log $POD --set bpf:debug" - output=$(kmeshctl log $POD --set bpf:debug 2>&1) + output=$(kmeshctl log $POD --set bpf:debug 2>&1 || true) if echo "$output" | grep -q "set BPF Log Level: 3"; then echo "BPF debug log set successfully" break @@ -201,7 +201,7 @@ function setup_kmesh() { # Set default debug log for i in {1..5}; do echo "Attempt $i of 5: kmeshctl log $POD --set default:debug" - output=$(kmeshctl log $POD --set default:debug 2>&1) + output=$(kmeshctl log $POD --set default:debug 2>&1 || true) if echo "$output" | grep -q "OK"; then echo "Default debug log set successfully" break @@ -222,7 +222,7 @@ function setup_kmesh_log() { # Set BPF debug log for i in {1..5}; do echo "Attempt $i of 5: kmeshctl log $POD --set bpf:debug" - output=$(kmeshctl log $POD --set bpf:debug 2>&1) + output=$(kmeshctl log $POD --set bpf:debug 2>&1 || true) if echo "$output" | grep -q "set BPF Log Level: 3"; then echo "BPF debug log set successfully" break @@ -235,7 +235,7 @@ function setup_kmesh_log() { # Set default debug log for i in {1..5}; do echo "Attempt $i of 5: kmeshctl log $POD --set default:debug" - output=$(kmeshctl log $POD --set default:debug 2>&1) + output=$(kmeshctl log $POD --set default:debug 2>&1 || true) if echo "$output" | grep -q "OK"; then echo "Default debug log set successfully" break @@ -440,19 +440,11 @@ bash -c "$cmd" EXIT_CODE=$? set -e -# Log collection is diagnostic and must not replace the test exit status. -if [ "$EXIT_CODE" -ne 0 ]; then - echo "E2E tests failed with exit code $EXIT_CODE." - if [[ -r $LOGFILE ]]; then - cat "$LOGFILE" || echo "Failed to read Kmesh daemon log: $LOGFILE" - elif [[ ${DEBUG:-false} == "true" ]]; then - echo "Kmesh daemon log was not created: $LOGFILE" - else - echo "Kmesh daemon log was not captured; rerun with --debug to enable log capture." - fi +if [ $EXIT_CODE -ne 0 ]; then + cat $LOGFILE fi -rm -f -- "$LOGFILE" || echo "Failed to remove Kmesh daemon log: $LOGFILE" +rm -rf $LOGFILE if [[ -n ${CLEANUP_KIND} ]]; then cleanup_kind_cluster @@ -462,4 +454,4 @@ if [[ -n ${CLEANUP_REGISTRY} ]]; then cleanup_docker_registry fi -exit "$EXIT_CODE" +exit $EXIT_CODE