This is an automated email from the ASF dual-hosted git repository.
liujun pushed a commit to branch refactor-with-go
in repository https://gitbox.apache.org/repos/asf/dubbo-admin.git
The following commit(s) were added to refs/heads/refactor-with-go by this push:
new 5f77f765 dubboctl: implement profile related commands and make some
small fixes (#1102)
5f77f765 is described below
commit 5f77f7658fc4409e05c703e71f885e244839ff91
Author: Scout Wang <[email protected]>
AuthorDate: Sun May 14 11:37:50 2023 +0800
dubboctl: implement profile related commands and make some small fixes
(#1102)
---
go.sum | 1 -
pkg/dubboctl/cmd/manifest.go | 10 +-
pkg/dubboctl/cmd/manifest_test.go | 90 ++++++++-----
.../{internal/cmd/common.go => cmd/profile.go} | 21 ++-
pkg/dubboctl/cmd/profile_test.go | 143 +++++++++++++++++++++
pkg/dubboctl/cmd/root.go | 1 +
.../{internal/cmd => cmd/subcmd}/common.go | 2 +-
.../{internal/cmd => cmd/subcmd}/manifest_diff.go | 2 +-
.../cmd => cmd/subcmd}/manifest_generate.go | 13 +-
.../cmd => cmd/subcmd}/manifest_install.go | 2 +-
.../cmd => cmd/subcmd}/manifest_uninstall.go | 2 +-
pkg/dubboctl/cmd/subcmd/profile_diff.go | 101 +++++++++++++++
pkg/dubboctl/cmd/subcmd/profile_list.go | 105 +++++++++++++++
pkg/dubboctl/cmd/testdata/profile/test0.yaml | 27 ++++
pkg/dubboctl/cmd/testdata/profile/test1.yaml | 25 ++++
.../cmd/testdata/profile/test2_wrong_format.yaml | 25 ++++
pkg/dubboctl/internal/kube/client.go | 1 -
pkg/dubboctl/internal/kube/object.go | 9 ++
pkg/dubboctl/internal/kube/object_test.go | 57 ++++++++
pkg/dubboctl/internal/manifest/common.go | 49 ++++++-
.../nacos_component-render_manifest.golden.yaml | 6 +-
pkg/logger/log.go | 11 +-
22 files changed, 634 insertions(+), 69 deletions(-)
diff --git a/go.sum b/go.sum
index d16a2183..f68087fe 100644
--- a/go.sum
+++ b/go.sum
@@ -1167,7 +1167,6 @@ github.com/olekukonko/tablewriter
v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:v
github.com/olekukonko/tablewriter v0.0.5/go.mod
h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852/go.mod
h1:eqOVx5Vwu4gd2mmMZvVZsgIqNSaW3xxRThUJ0k/TPk4=
github.com/onsi/ginkgo v1.6.0/go.mod
h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
github.com/onsi/ginkgo v1.7.0/go.mod
h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo/v2 v2.6.0
h1:9t9b9vRUbFq3C4qKFCGkVuq/fIHji802N1nrtkh1mNc=
github.com/onsi/gomega v1.4.3/go.mod
h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
diff --git a/pkg/dubboctl/cmd/manifest.go b/pkg/dubboctl/cmd/manifest.go
index d6a472b9..7bca0664 100644
--- a/pkg/dubboctl/cmd/manifest.go
+++ b/pkg/dubboctl/cmd/manifest.go
@@ -16,7 +16,7 @@
package cmd
import (
- "github.com/apache/dubbo-admin/pkg/dubboctl/internal/cmd"
+ subcmd "github.com/apache/dubbo-admin/pkg/dubboctl/cmd/subcmd"
"github.com/spf13/cobra"
)
@@ -26,9 +26,9 @@ func addManifest(rootCmd *cobra.Command) {
Short: "Commands related to manifest",
Long: "Commands help user to generate manifest and install
manifest",
}
- cmd.ConfigManifestGenerateCmd(manifestCmd)
- cmd.ConfigManifestInstallCmd(manifestCmd)
- cmd.ConfigManifestUninstallCmd(manifestCmd)
- cmd.ConfigManifestDiffCmd(manifestCmd)
+ subcmd.ConfigManifestGenerateCmd(manifestCmd)
+ subcmd.ConfigManifestInstallCmd(manifestCmd)
+ subcmd.ConfigManifestUninstallCmd(manifestCmd)
+ subcmd.ConfigManifestDiffCmd(manifestCmd)
rootCmd.AddCommand(manifestCmd)
}
diff --git a/pkg/dubboctl/cmd/manifest_test.go
b/pkg/dubboctl/cmd/manifest_test.go
index e09fbdc9..0a9d941e 100644
--- a/pkg/dubboctl/cmd/manifest_test.go
+++ b/pkg/dubboctl/cmd/manifest_test.go
@@ -17,19 +17,20 @@ package cmd
import (
"bytes"
+ "github.com/apache/dubbo-admin/pkg/dubboctl/cmd/subcmd"
"os"
"strings"
"testing"
- "github.com/apache/dubbo-admin/pkg/dubboctl/internal/cmd"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
)
func TestManifestGenerate(t *testing.T) {
tests := []struct {
- desc string
- cmd string
- temp string
+ desc string
+ cmd string
+ temp string
+ wantErr bool
}{
{
desc: "using default configuration without any flag",
@@ -52,6 +53,14 @@ func TestManifestGenerate(t *testing.T) {
cmd: "manifest generate --set
spec.componentsMeta.grafana.repoURL=https://grafana.github.io/helm-charts" +
" --set
spec.componentsMeta.grafana.version=6.31.0",
},
+ {
+ desc: "setting specification of built-in component with
wrong path",
+ cmd: "manifest generate --set
components.nacos.replicas=3",
+ },
+ {
+ desc: "setting specification of built-in component with
wrong path",
+ cmd: "manifest generate --set
components.grafana.replicas=3",
+ },
{
desc: "generate manifest to target path",
cmd: "manifest generate -o ./testdata/temp",
@@ -63,18 +72,21 @@ func TestManifestGenerate(t *testing.T) {
},
}
for _, test := range tests {
- testExecute(t, test.cmd)
- // remove temporary dir
- if test.temp != "" {
- os.RemoveAll(test.temp)
- }
+ t.Run(test.desc, func(t *testing.T) {
+ testExecute(t, test.cmd, test.wantErr)
+ // remove temporary dir
+ if test.temp != "" {
+ os.RemoveAll(test.temp)
+ }
+ })
}
}
func TestManifestInstall(t *testing.T) {
tests := []struct {
- desc string
- cmd string
+ desc string
+ cmd string
+ wantErr bool
}{
{
desc: "without any flag",
@@ -82,11 +94,13 @@ func TestManifestInstall(t *testing.T) {
},
}
// For now, we do not use envTest to do black box testing
- cmd.TestInstallFlag = true
- cmd.TestCli = fake.NewClientBuilder().Build()
+ subcmd.TestInstallFlag = true
+ subcmd.TestCli = fake.NewClientBuilder().Build()
for _, test := range tests {
- testExecute(t, test.cmd)
+ t.Run(test.desc, func(t *testing.T) {
+ testExecute(t, test.cmd, test.wantErr)
+ })
}
}
@@ -94,8 +108,9 @@ func TestManifestUninstall(t *testing.T) {
tests := []struct {
desc string
// cmd has been executed before
- before string
- cmd string
+ before string
+ cmd string
+ wantErr bool
}{
{
desc: "without any flag",
@@ -104,13 +119,15 @@ func TestManifestUninstall(t *testing.T) {
},
}
// For now, we do not use envTest to do black box testing
- cmd.TestInstallFlag = true
- cmd.TestCli = fake.NewClientBuilder().Build()
+ subcmd.TestInstallFlag = true
+ subcmd.TestCli = fake.NewClientBuilder().Build()
for _, test := range tests {
- // prepare existing resources
- testExecute(t, test.before)
- testExecute(t, test.cmd)
+ t.Run(test.desc, func(t *testing.T) {
+ // prepare existing resources
+ testExecute(t, test.before, false)
+ testExecute(t, test.cmd, test.wantErr)
+ })
}
}
@@ -120,6 +137,7 @@ func TestManifestDiff(t *testing.T) {
befores []string
cmd string
temps []string
+ wantErr bool
}{
{
desc: "compare two dirs",
@@ -135,25 +153,35 @@ func TestManifestDiff(t *testing.T) {
},
}
for _, test := range tests {
- for _, before := range test.befores {
- testExecute(t, before)
- }
- testExecute(t, test.cmd)
- for _, temp := range test.temps {
- if temp != "" {
- os.RemoveAll(temp)
+ t.Run(test.desc, func(t *testing.T) {
+ for _, before := range test.befores {
+ testExecute(t, before, false)
}
- }
+ testExecute(t, test.cmd, test.wantErr)
+ for _, temp := range test.temps {
+ if temp != "" {
+ os.RemoveAll(temp)
+ }
+ }
+ })
}
}
-func testExecute(t *testing.T, cmd string) {
+func testExecute(t *testing.T, cmd string, wantErr bool) string {
var out bytes.Buffer
args := strings.Split(cmd, " ")
rootCmd := getRootCmd(args)
rootCmd.SetOut(&out)
if err := rootCmd.Execute(); err != nil {
+ if wantErr {
+ return ""
+ }
t.Errorf("execute %s failed, err: %s", cmd, err)
- return
+ return ""
+ }
+ if wantErr {
+ t.Errorf("want err but got no err")
+ return ""
}
+ return out.String()
}
diff --git a/pkg/dubboctl/internal/cmd/common.go b/pkg/dubboctl/cmd/profile.go
similarity index 66%
copy from pkg/dubboctl/internal/cmd/common.go
copy to pkg/dubboctl/cmd/profile.go
index d8de607d..e0c3534b 100644
--- a/pkg/dubboctl/internal/cmd/common.go
+++ b/pkg/dubboctl/cmd/profile.go
@@ -15,10 +15,19 @@
package cmd
-import "sigs.k8s.io/controller-runtime/pkg/client"
-
-var (
- // TestInstallFlag and TestCli are uses for black box testing
- TestInstallFlag bool
- TestCli client.Client
+import (
+ "github.com/apache/dubbo-admin/pkg/dubboctl/cmd/subcmd"
+ "github.com/spf13/cobra"
)
+
+func addProfile(rootCmd *cobra.Command) {
+ profileCmd := &cobra.Command{
+ Use: "profile",
+ Short: "Commands related to profiles",
+ Long: "Commands help user to list and describe profiles",
+ }
+ subcmd.ConfigProfileListArgs(profileCmd)
+ subcmd.ConfigProfileDiffArgs(profileCmd)
+
+ rootCmd.AddCommand(profileCmd)
+}
diff --git a/pkg/dubboctl/cmd/profile_test.go b/pkg/dubboctl/cmd/profile_test.go
new file mode 100644
index 00000000..db49d4d9
--- /dev/null
+++ b/pkg/dubboctl/cmd/profile_test.go
@@ -0,0 +1,143 @@
+// 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 "testing"
+
+func TestProfileList(t *testing.T) {
+ tests := []struct {
+ desc string
+ cmd string
+ want string
+ wantErr bool
+ }{
+ {
+ desc: "list all profiles provided by dubbo-admin",
+ cmd: "profile list",
+ want: `Dubbo-admin profiles:
+ default
+`,
+ },
+ {
+ desc: "list all profiles in path specified by user",
+ cmd: "profile list --profiles ./testdata/profile",
+ want: `Dubbo-admin profiles:
+ test0
+ test1
+ test2_wrong_format
+`,
+ },
+ {
+ desc: "display selected profile",
+ cmd: "profile list test0 --profiles
./testdata/profile",
+ want: `apiVersion: dubbo.apache.org/v1alpha1
+kind: DubboOperator
+metadata:
+ namespace: dubbo-system
+spec:
+ profile: default
+ namespace: dubbo-system
+ componentsMeta:
+ admin:
+ enabled: true
+ nacos:
+ enabled: true`,
+ },
+ {
+ desc: "display selected profile with wrong format",
+ cmd: "profile list test2_wrong_format --profiles
./testdata/profile",
+ wantErr: true,
+ },
+ {
+ desc: "display selected profiles",
+ cmd: "profile list test0 test1 --profiles
./testdata/profile",
+ wantErr: true,
+ },
+ {
+ desc: "display profile that does not exist",
+ cmd: "profile list test2 --profiles
./testdata/profile",
+ want: "",
+ },
+ {
+ desc: "list profile directory that does not exist",
+ cmd: "profile list --profiles
./testdata/profile/non_exist",
+ wantErr: true,
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.desc, func(t *testing.T) {
+ res := testExecute(t, test.cmd, test.wantErr)
+ if test.want != "" && test.want != res {
+ t.Errorf("want:\n%s\nbutgot:\n%s\n", test.want,
res)
+ return
+ }
+ })
+ }
+}
+
+func TestProfileDiff(t *testing.T) {
+ tests := []struct {
+ desc string
+ cmd string
+ want string
+ wantErr bool
+ }{
+ {
+ desc: "show the difference between two profiles
provided by dubbo-admin",
+ cmd: "profile diff default default",
+ want: `two profiles are identical
+`,
+ },
+ {
+ desc: "show the difference between two profiled
specified by user",
+ cmd: "profile diff test0 test1 --profiles
./testdata/profile",
+ want: ` apiVersion: dubbo.apache.org/v1alpha1
+ kind: DubboOperator
+ metadata:
+ namespace: dubbo-system
+ spec:
+ componentsMeta:
+ admin:
+ enabled: true
+- nacos:
+- enabled: true
+ namespace: dubbo-system
+ profile: default
+ `,
+ },
+ {
+ desc: "do not specify two profiles",
+ cmd: "profile diff test0 --profiles
./testdata/profile",
+ wantErr: true,
+ },
+ {
+ desc: "diff profiles with wrong format",
+ cmd: "profile diff test0 test2_wrong_format
--profiles ./testdata/profile",
+ wantErr: true,
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.desc, func(t *testing.T) {
+ res := testExecute(t, test.cmd, test.wantErr)
+ if test.want != "" && test.want != res {
+ t.Errorf("want:\n%s\nbutgot:\n%s\n", test.want,
res)
+ return
+ }
+ })
+ }
+}
diff --git a/pkg/dubboctl/cmd/root.go b/pkg/dubboctl/cmd/root.go
index e2ea35ce..c19f8752 100644
--- a/pkg/dubboctl/cmd/root.go
+++ b/pkg/dubboctl/cmd/root.go
@@ -40,4 +40,5 @@ func getRootCmd(args []string) *cobra.Command {
func addSubCommands(rootCmd *cobra.Command) {
addManifest(rootCmd)
+ addProfile(rootCmd)
}
diff --git a/pkg/dubboctl/internal/cmd/common.go
b/pkg/dubboctl/cmd/subcmd/common.go
similarity index 98%
rename from pkg/dubboctl/internal/cmd/common.go
rename to pkg/dubboctl/cmd/subcmd/common.go
index d8de607d..e6e72d83 100644
--- a/pkg/dubboctl/internal/cmd/common.go
+++ b/pkg/dubboctl/cmd/subcmd/common.go
@@ -13,7 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package cmd
+package subcmd
import "sigs.k8s.io/controller-runtime/pkg/client"
diff --git a/pkg/dubboctl/internal/cmd/manifest_diff.go
b/pkg/dubboctl/cmd/subcmd/manifest_diff.go
similarity index 99%
rename from pkg/dubboctl/internal/cmd/manifest_diff.go
rename to pkg/dubboctl/cmd/subcmd/manifest_diff.go
index 8743d0cc..254dde93 100644
--- a/pkg/dubboctl/internal/cmd/manifest_diff.go
+++ b/pkg/dubboctl/cmd/subcmd/manifest_diff.go
@@ -13,7 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package cmd
+package subcmd
import (
"errors"
diff --git a/pkg/dubboctl/internal/cmd/manifest_generate.go
b/pkg/dubboctl/cmd/subcmd/manifest_generate.go
similarity index 94%
rename from pkg/dubboctl/internal/cmd/manifest_generate.go
rename to pkg/dubboctl/cmd/subcmd/manifest_generate.go
index 063d783a..7904b8c5 100644
--- a/pkg/dubboctl/internal/cmd/manifest_generate.go
+++ b/pkg/dubboctl/cmd/subcmd/manifest_generate.go
@@ -13,7 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package cmd
+package subcmd
import (
"fmt"
@@ -135,11 +135,11 @@ func addManifestGenerateFlags(cmd *cobra.Command, args
*ManifestGenerateArgs) {
func generateValues(mgArgs *ManifestGenerateArgs) (*v1alpha1.DubboConfig,
string, error) {
mergedYaml, profile, err :=
manifest.ReadYamlAndProfile(mgArgs.FileNames, mgArgs.SetFlags)
if err != nil {
- return nil, "", fmt.Errorf("generateValues err: %v", err)
+ return nil, "", fmt.Errorf("process user specification failed,
err: %s", err)
}
- profileYaml, err := manifest.ReadProfileYaml(mgArgs.ProfilesPath,
profile)
+ profileYaml, err :=
manifest.ReadOverlayProfileYaml(mgArgs.ProfilesPath, profile)
if err != nil {
- return nil, "", err
+ return nil, "", fmt.Errorf("process profile failed, err: %s",
err)
}
finalYaml, err := util.OverlayYAML(profileYaml, mergedYaml)
if err != nil {
@@ -147,13 +147,12 @@ func generateValues(mgArgs *ManifestGenerateArgs)
(*v1alpha1.DubboConfig, string
}
finalYaml, err = manifest.OverlaySetFlags(finalYaml, mgArgs.SetFlags)
if err != nil {
- return nil, "", err
+ return nil, "", fmt.Errorf("process set flags failed, err: %s",
err)
}
cfg := &v1alpha1.DubboConfig{}
if err := yaml.Unmarshal([]byte(finalYaml), cfg); err != nil {
- return nil, "", err
+ return nil, "", fmt.Errorf("set flags specification is wrong,
err: %s", err)
}
- // todo: validate op
// we should ensure that Components field would not be nil
if cfg.Spec.Components == nil {
cfg.Spec.Components = &v1alpha1.DubboComponentsSpec{}
diff --git a/pkg/dubboctl/internal/cmd/manifest_install.go
b/pkg/dubboctl/cmd/subcmd/manifest_install.go
similarity index 99%
rename from pkg/dubboctl/internal/cmd/manifest_install.go
rename to pkg/dubboctl/cmd/subcmd/manifest_install.go
index 2d3e3c6c..23aad1d4 100644
--- a/pkg/dubboctl/internal/cmd/manifest_install.go
+++ b/pkg/dubboctl/cmd/subcmd/manifest_install.go
@@ -13,7 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package cmd
+package subcmd
import (
"github.com/apache/dubbo-admin/pkg/dubboctl/internal/apis/dubbo.apache.org/v1alpha1"
diff --git a/pkg/dubboctl/internal/cmd/manifest_uninstall.go
b/pkg/dubboctl/cmd/subcmd/manifest_uninstall.go
similarity index 99%
rename from pkg/dubboctl/internal/cmd/manifest_uninstall.go
rename to pkg/dubboctl/cmd/subcmd/manifest_uninstall.go
index 12ea9304..a84de755 100644
--- a/pkg/dubboctl/internal/cmd/manifest_uninstall.go
+++ b/pkg/dubboctl/cmd/subcmd/manifest_uninstall.go
@@ -13,7 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package cmd
+package subcmd
import (
"github.com/apache/dubbo-admin/pkg/dubboctl/internal/apis/dubbo.apache.org/v1alpha1"
diff --git a/pkg/dubboctl/cmd/subcmd/profile_diff.go
b/pkg/dubboctl/cmd/subcmd/profile_diff.go
new file mode 100644
index 00000000..b093da9a
--- /dev/null
+++ b/pkg/dubboctl/cmd/subcmd/profile_diff.go
@@ -0,0 +1,101 @@
+// 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 subcmd
+
+import (
+ "errors"
+ "fmt"
+ "github.com/apache/dubbo-admin/pkg/dubboctl/identifier"
+ "github.com/apache/dubbo-admin/pkg/dubboctl/internal/kube"
+ "github.com/apache/dubbo-admin/pkg/dubboctl/internal/manifest"
+ "github.com/apache/dubbo-admin/pkg/logger"
+ "github.com/spf13/cobra"
+ "go.uber.org/zap/zapcore"
+)
+
+type ProfileDiffArgs struct {
+ ProfilesPath string
+}
+
+func (pda *ProfileDiffArgs) setDefault() {
+ if pda == nil {
+ return
+ }
+ if pda.ProfilesPath == "" {
+ pda.ProfilesPath = identifier.Profiles
+ }
+}
+
+func ConfigProfileDiffArgs(baseCmd *cobra.Command) {
+ pdArgs := &ProfileDiffArgs{}
+ pdCmd := &cobra.Command{
+ Use: "diff",
+ Short: "Show the difference between two profiles",
+ Example: ` # show the difference between two profiles provided
by dubbo-admin
+ dubboctl profile diff default demo
+
+ # show the difference between two profiles specified by user
+ dubboctl profile diff profile_you_specify0 profile_you_specify1 --profiles
/path/to/profiles
+`,
+ Args: func(cmd *cobra.Command, args []string) error {
+ if len(args) != 2 {
+ return errors.New("profile diff needs two
profiles")
+ }
+ return nil
+ },
+ RunE: func(cmd *cobra.Command, args []string) error {
+ logger.InitCmdSugar(zapcore.AddSync(cmd.OutOrStdout()))
+ pdArgs.setDefault()
+ if err := profileDiffCmd(pdArgs, args); err != nil {
+ return err
+ }
+ return nil
+ },
+ }
+ pdCmd.PersistentFlags().StringVarP(&pdArgs.ProfilesPath, "profiles",
"", "",
+ "Path to profiles directory, this directory contains preset
profiles")
+
+ baseCmd.AddCommand(pdCmd)
+}
+
+func profileDiffCmd(pdArgs *ProfileDiffArgs, args []string) error {
+ profileA, err := manifest.ReadProfileYaml(pdArgs.ProfilesPath, args[0])
+ if err != nil {
+ return fmt.Errorf("parse %s profile failed, err: %s", args[0],
err)
+ }
+ profileB, err := manifest.ReadProfileYaml(pdArgs.ProfilesPath, args[1])
+ if err != nil {
+ return fmt.Errorf("parse %s profile failed, err: %s", args[1],
err)
+ }
+ objA, err := kube.ParseObjectFromManifest(profileA)
+ if err != nil {
+ return fmt.Errorf("parse %s profile to k8s object failed, err:
%s", args[0], err)
+ }
+ objB, err := kube.ParseObjectFromManifest(profileB)
+ if err != nil {
+ return fmt.Errorf("parse %s profile to k8s object failed, err:
%s", args[1], err)
+ }
+ diff, err := kube.CompareObject(objA, objB)
+ if err != nil {
+ return fmt.Errorf("compare failed, err: %s", err)
+ }
+ if diff != "" {
+ logger.CmdSugar().Print(diff)
+ } else {
+ logger.CmdSugar().Print("two profiles are identical\n")
+ }
+ return nil
+}
diff --git a/pkg/dubboctl/cmd/subcmd/profile_list.go
b/pkg/dubboctl/cmd/subcmd/profile_list.go
new file mode 100644
index 00000000..5c0ce938
--- /dev/null
+++ b/pkg/dubboctl/cmd/subcmd/profile_list.go
@@ -0,0 +1,105 @@
+// 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 subcmd
+
+import (
+ "errors"
+ "github.com/apache/dubbo-admin/pkg/dubboctl/identifier"
+ "github.com/apache/dubbo-admin/pkg/dubboctl/internal/manifest"
+ "github.com/apache/dubbo-admin/pkg/dubboctl/internal/util"
+ "github.com/apache/dubbo-admin/pkg/logger"
+ "github.com/spf13/cobra"
+ "go.uber.org/zap/zapcore"
+ "strings"
+)
+
+type ProfileListArgs struct {
+ ProfilesPath string
+}
+
+func (pla *ProfileListArgs) setDefault() {
+ if pla == nil {
+ return
+ }
+ if pla.ProfilesPath == "" {
+ pla.ProfilesPath = identifier.Profiles
+ }
+}
+
+func ConfigProfileListArgs(baseCmd *cobra.Command) {
+ plArgs := &ProfileListArgs{}
+ plCmd := &cobra.Command{
+ Use: "list",
+ Short: "List all existing profiles specification",
+ Example: ` # list all profiles provided by dubbo-admin
+ dubboctl profile list
+
+ # list all profiles in path specified by user
+ dubboctl profile list --profiles /path/to/profiles
+
+ # display selected profile
+ dubboctl profile list default
+`,
+ Args: func(cmd *cobra.Command, args []string) error {
+ if len(args) > 1 {
+ return errors.New("profile list doesn't support
multi profile")
+ }
+ return nil
+ },
+ RunE: func(cmd *cobra.Command, args []string) error {
+ logger.InitCmdSugar(zapcore.AddSync(cmd.OutOrStdout()))
+ plArgs.setDefault()
+ if err := profileListCmd(plArgs, args); err != nil {
+ return err
+ }
+ return nil
+ },
+ }
+ plCmd.PersistentFlags().StringVarP(&plArgs.ProfilesPath, "profiles",
"", "",
+ "Path to profiles directory, this directory contains preset
profiles")
+
+ baseCmd.AddCommand(plCmd)
+}
+
+func profileListCmd(plArgs *ProfileListArgs, args []string) error {
+ profiles, err := manifest.ReadProfilesNames(plArgs.ProfilesPath)
+ if err != nil {
+ return err
+ }
+ // list all profiles
+ if len(args) == 0 {
+ var resBuilder strings.Builder
+ resBuilder.WriteString("Dubbo-admin profiles:\n")
+ for _, profile := range profiles {
+ resBuilder.WriteString(" " + profile + "\n")
+ }
+ logger.CmdSugar().Print(resBuilder.String())
+ return nil
+ }
+
+ for _, profile := range profiles {
+ if profile == args[0] {
+ res, err :=
manifest.ReadProfileYaml(plArgs.ProfilesPath, profile)
+ if err != nil {
+ return err
+ }
+ logger.CmdSugar().Print(util.ApplyFilters(res,
util.LicenseFilter, util.SpaceFilter))
+ return nil
+ }
+ }
+
+ return nil
+}
diff --git a/pkg/dubboctl/cmd/testdata/profile/test0.yaml
b/pkg/dubboctl/cmd/testdata/profile/test0.yaml
new file mode 100644
index 00000000..7513bd0d
--- /dev/null
+++ b/pkg/dubboctl/cmd/testdata/profile/test0.yaml
@@ -0,0 +1,27 @@
+# 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.
+
+apiVersion: dubbo.apache.org/v1alpha1
+kind: DubboOperator
+metadata:
+ namespace: dubbo-system
+spec:
+ profile: default
+ namespace: dubbo-system
+ componentsMeta:
+ admin:
+ enabled: true
+ nacos:
+ enabled: true
\ No newline at end of file
diff --git a/pkg/dubboctl/cmd/testdata/profile/test1.yaml
b/pkg/dubboctl/cmd/testdata/profile/test1.yaml
new file mode 100644
index 00000000..030a7821
--- /dev/null
+++ b/pkg/dubboctl/cmd/testdata/profile/test1.yaml
@@ -0,0 +1,25 @@
+# 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.
+
+apiVersion: dubbo.apache.org/v1alpha1
+kind: DubboOperator
+metadata:
+ namespace: dubbo-system
+spec:
+ profile: default
+ namespace: dubbo-system
+ componentsMeta:
+ admin:
+ enabled: true
\ No newline at end of file
diff --git a/pkg/dubboctl/cmd/testdata/profile/test2_wrong_format.yaml
b/pkg/dubboctl/cmd/testdata/profile/test2_wrong_format.yaml
new file mode 100644
index 00000000..7d7a52f4
--- /dev/null
+++ b/pkg/dubboctl/cmd/testdata/profile/test2_wrong_format.yaml
@@ -0,0 +1,25 @@
+# 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.
+
+apiVersion: dubbo.apache.org/v1alpha1
+kind: DubboOperator
+metadata:
+ namespace: dubbo-system
+spec:
+ profile: default
+ namespace: dubbo-system
+ componentsMeta:
+ admin:
+ enabled: right
\ No newline at end of file
diff --git a/pkg/dubboctl/internal/kube/client.go
b/pkg/dubboctl/internal/kube/client.go
index 07080749..876be155 100644
--- a/pkg/dubboctl/internal/kube/client.go
+++ b/pkg/dubboctl/internal/kube/client.go
@@ -230,7 +230,6 @@ func (cli *CtlClient) deleteNamespace(ns string) error {
}
return nil
} else {
- // todo: learn how to use deleteOption
if err := cli.Delete(context.Background(), nsObj); err != nil {
return fmt.Errorf("failed to delete namespace: %s, err:
%s", ns, err)
}
diff --git a/pkg/dubboctl/internal/kube/object.go
b/pkg/dubboctl/internal/kube/object.go
index 5d78b174..2f0fab11 100644
--- a/pkg/dubboctl/internal/kube/object.go
+++ b/pkg/dubboctl/internal/kube/object.go
@@ -221,3 +221,12 @@ func CompareObjects(objsA, objsB Objects) (string, string,
string) {
return diffRes.String(), addRes.String(), errRes.String()
}
+
+// CompareObject compares two objects and returns diff.
+func CompareObject(objA, objB *Object) (string, error) {
+ diff, err := util.DiffYAML(objA.YAML(), objB.YAML())
+ if err != nil {
+ return "", err
+ }
+ return diff, nil
+}
diff --git a/pkg/dubboctl/internal/kube/object_test.go
b/pkg/dubboctl/internal/kube/object_test.go
index 3fa7dc93..cf255046 100644
--- a/pkg/dubboctl/internal/kube/object_test.go
+++ b/pkg/dubboctl/internal/kube/object_test.go
@@ -16,6 +16,7 @@
package kube
import (
+ "strings"
"testing"
"github.com/stretchr/testify/assert"
@@ -294,3 +295,59 @@ error converting YAML to JSON: yaml: line 2: mapping
values are not allowed in t
}
}
}
+
+func TestCompareObject(t *testing.T) {
+ tests := []struct {
+ desc string
+ objA *Object
+ objB *Object
+ diff string
+ wantErr bool
+ }{
+ {
+ desc: "objects could be parsed correctly",
+ objA: &Object{
+ yamlStr: `key1: val1
+key2: val2`,
+ },
+ objB: &Object{
+ yamlStr: `key1: val1
+key2: val3`,
+ },
+ diff: `key1: val1
+-key2: val2
++key2: val3`,
+ },
+ {
+ desc: "objects with wrong format",
+ objA: &Object{
+ yamlStr: `key1: val1
+ key2: val2`,
+ },
+ objB: &Object{
+ yamlStr: `key1: val1
+key2: val3`,
+ },
+ wantErr: true,
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.desc, func(t *testing.T) {
+ diff, err := CompareObject(test.objA, test.objB)
+ if err != nil {
+ if !test.wantErr {
+ t.Errorf("execute failed, err: %s", err)
+ }
+ } else {
+ if test.wantErr {
+ t.Errorf("execution expected to fail,
but succeed")
+ } else {
+ if strings.TrimSpace(diff) != test.diff
{
+ t.Errorf("want:\n%s\nbut
got:\n%s", test.diff, diff)
+ }
+ }
+ }
+ })
+ }
+}
diff --git a/pkg/dubboctl/internal/manifest/common.go
b/pkg/dubboctl/internal/manifest/common.go
index eee109b7..c5f29cdb 100644
--- a/pkg/dubboctl/internal/manifest/common.go
+++ b/pkg/dubboctl/internal/manifest/common.go
@@ -27,12 +27,19 @@ import (
"sigs.k8s.io/yaml"
)
-func ReadProfileYaml(profilePath string, profile string) (string, error) {
+func ReadOverlayProfileYaml(profilePath string, profile string) (string,
error) {
filePath := path.Join(profilePath, profile+".yaml")
- out, err := ReadAndOverlayYamls([]string{filePath})
+ defaultPath := path.Join(profilePath, "default.yaml")
+ // overlay selected profile ont default profile
+ out, err := ReadAndOverlayYamls([]string{defaultPath, filePath})
if err != nil {
return "", err
}
+ // unmarshal and validate
+ tempOp := &v1alpha1.DubboConfig{}
+ if err := yaml.Unmarshal([]byte(out), tempOp); err != nil {
+ return "", fmt.Errorf("%s profile on default profile is wrong,
err: %s", profile, err)
+ }
return out, nil
}
@@ -44,7 +51,7 @@ func ReadYamlAndProfile(filenames []string, setFlags
[]string) (string, string,
// unmarshal and validate
tempOp := &v1alpha1.DubboConfig{}
if err := yaml.Unmarshal([]byte(mergedYaml), tempOp); err != nil {
- return "", "", fmt.Errorf("ReadYamlAndProfile failed, err: %s",
err)
+ return "", "", fmt.Errorf("user specification is wrong, err:
%s", err)
}
// get profile field and overlay with setFlags
profile := "default"
@@ -95,3 +102,39 @@ func OverlaySetFlags(base string, setFlags []string)
(string, error) {
}
return string(finalYaml), nil
}
+
+// ReadProfilesNames reads all profiles in directory specified by profilesPath.
+// It does not traverse recursively.
+// It may add some filters in the future.
+func ReadProfilesNames(profilesPath string) ([]string, error) {
+ var res []string
+ dir, err := os.ReadDir(profilesPath)
+ if err != nil {
+ return nil, err
+ }
+ yamlSuffix := ".yaml"
+ for _, file := range dir {
+ if file.IsDir() {
+ continue
+ }
+ if strings.HasSuffix(file.Name(), yamlSuffix) {
+ res = append(res, strings.TrimSuffix(file.Name(),
".yaml"))
+ }
+ }
+ return res, nil
+}
+
+// ReadProfileYaml reads profile yaml specified by profilePath/profile.yaml
and validates the content.
+func ReadProfileYaml(profilePath string, profile string) (string, error) {
+ filePath := path.Join(profilePath, profile+".yaml")
+ out, err := ReadAndOverlayYamls([]string{filePath})
+ if err != nil {
+ return "", err
+ }
+ // unmarshal and validate
+ tempOp := &v1alpha1.DubboConfig{}
+ if err := yaml.Unmarshal([]byte(out), tempOp); err != nil {
+ return "", fmt.Errorf("%s profile is wrong, err: %s", profile,
err)
+ }
+ return out, nil
+}
diff --git
a/pkg/dubboctl/internal/operator/testdata/want/nacos_component-render_manifest.golden.yaml
b/pkg/dubboctl/internal/operator/testdata/want/nacos_component-render_manifest.golden.yaml
index 01a04703..a272fbb0 100644
---
a/pkg/dubboctl/internal/operator/testdata/want/nacos_component-render_manifest.golden.yaml
+++
b/pkg/dubboctl/internal/operator/testdata/want/nacos_component-render_manifest.golden.yaml
@@ -62,7 +62,9 @@ spec:
name: raft-rpc
- containerPort: 7848
name: old-raft-rpc
- resources: null
+ resources:
+ limits: {}
+ requests: {}
startupProbe:
httpGet:
path: /nacos/v1/console/health/readiness
@@ -84,7 +86,6 @@ spec:
volumes:
- emptyDir: {}
name: data
-
---
apiVersion: v1
kind: Service
@@ -116,5 +117,4 @@ spec:
selector:
app.kubernetes.io/name: nacos
type: NodePort
-
---
diff --git a/pkg/logger/log.go b/pkg/logger/log.go
index 197a4e21..928e7907 100644
--- a/pkg/logger/log.go
+++ b/pkg/logger/log.go
@@ -25,10 +25,9 @@ import (
)
var (
- mutex = &sync.Mutex{}
- hasInit = false
- cmdHasInit = false
- encoder = zapcore.NewConsoleEncoder(
+ mutex = &sync.Mutex{}
+ hasInit = false
+ encoder = zapcore.NewConsoleEncoder(
zapcore.EncoderConfig{
MessageKey: "msg",
LevelKey: "level",
@@ -80,10 +79,6 @@ func Init() {
func InitCmdSugar(ws zapcore.WriteSyncer) {
mutex.Lock()
defer mutex.Unlock()
- if cmdHasInit {
- return
- }
- cmdHasInit = true
core := zapcore.NewCore(encoder, ws, zap.DebugLevel)
cmdLogger = zap.New(core)