Skip to content

Commit

Permalink
Refactor cmd and pkg/plugin for easier testing
Browse files Browse the repository at this point in the history
  • Loading branch information
MacroPower committed Jan 3, 2025
1 parent 1cdc0f1 commit 64cc3ab
Show file tree
Hide file tree
Showing 12 changed files with 231 additions and 149 deletions.
41 changes: 16 additions & 25 deletions cmd/kclx/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ import (
kclcmd "kcl-lang.io/cli/cmd/kcl/commands"
"kcl-lang.io/cli/pkg/plugin"

"github.com/MacroPower/kclx/internal/cli"
"github.com/MacroPower/kclx/pkg/log"
_ "github.com/MacroPower/kclx/pkg/plugin/helm"
_ "github.com/MacroPower/kclx/pkg/plugin/http"
_ "github.com/MacroPower/kclx/pkg/plugin/os"
helmplugin "github.com/MacroPower/kclx/pkg/plugin/helm"
httpplugin "github.com/MacroPower/kclx/pkg/plugin/http"
osplugin "github.com/MacroPower/kclx/pkg/plugin/os"
)

func init() {
Expand All @@ -25,7 +26,8 @@ func init() {
}

const (
cmdName = "kcl"
cmdName = "kcl"

shortDesc = "The KCL Extended Command Line Interface (CLI)."
longDesc = `The KCL Extended Command Line Interface (CLI).
Expand All @@ -36,29 +38,18 @@ scenarios. The KCL website: https://kcl-lang.io
)

func main() {
cmd := &cobra.Command{
Use: cmdName,
Short: shortDesc,
Long: longDesc,
SilenceUsage: true,
SilenceErrors: true,
Version: GetVersionString(),
if strings.ToLower(os.Getenv("KCLX_HELM_PLUGIN_DISABLED")) != "true" {

Check failure on line 41 in cmd/kclx/main.go

View workflow job for this annotation

GitHub Actions / lint

string `true` has 3 occurrences, make it a constant (goconst)
helmplugin.Register()
}
if strings.ToLower(os.Getenv("KCLX_HTTP_PLUGIN_DISABLED")) != "true" {
httpplugin.Register()
}
if strings.ToLower(os.Getenv("KCLX_OS_PLUGIN_DISABLED")) != "true" {
osplugin.Register()
}
cmd.AddCommand(kclcmd.NewRunCmd())
cmd.AddCommand(kclcmd.NewLintCmd())
cmd.AddCommand(kclcmd.NewDocCmd())
cmd.AddCommand(kclcmd.NewFmtCmd())
cmd.AddCommand(kclcmd.NewTestCmd())
cmd.AddCommand(kclcmd.NewVetCmd())
cmd.AddCommand(kclcmd.NewCleanCmd())
cmd.AddCommand(kclcmd.NewImportCmd())
cmd.AddCommand(kclcmd.NewModCmd())
cmd.AddCommand(kclcmd.NewRegistryCmd())
cmd.AddCommand(kclcmd.NewServerCmd())
cmd.AddCommand(NewVersionCmd())
cmd.AddCommand(NewChartCmd())
bootstrapCmdPlugin(cmd, plugin.NewDefaultPluginHandler([]string{cmdName}))

cmd := cli.NewRootCmd(cmdName, shortDesc, longDesc)
bootstrapCmdPlugin(cmd, plugin.NewDefaultPluginHandler([]string{cmdName}))
if err := cmd.Execute(); err != nil {
fmt.Fprintln(os.Stderr, strings.TrimLeft(err.Error(), "\n"))
os.Exit(1)
Expand Down
2 changes: 1 addition & 1 deletion cmd/kclx/chart.go → internal/cli/chart.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package main
package cli

import (
"errors"
Expand Down
31 changes: 31 additions & 0 deletions internal/cli/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package cli

import (
"github.com/spf13/cobra"
kclcmd "kcl-lang.io/cli/cmd/kcl/commands"
)

func NewRootCmd(name, shortDesc, longDesc string) *cobra.Command {
cmd := &cobra.Command{
Use: name,
Short: shortDesc,
Long: longDesc,
SilenceUsage: true,
SilenceErrors: true,
Version: GetVersionString(),
}
cmd.AddCommand(kclcmd.NewRunCmd())
cmd.AddCommand(kclcmd.NewLintCmd())
cmd.AddCommand(kclcmd.NewDocCmd())
cmd.AddCommand(kclcmd.NewFmtCmd())
cmd.AddCommand(kclcmd.NewTestCmd())
cmd.AddCommand(kclcmd.NewVetCmd())
cmd.AddCommand(kclcmd.NewCleanCmd())
cmd.AddCommand(kclcmd.NewImportCmd())
cmd.AddCommand(kclcmd.NewModCmd())
cmd.AddCommand(kclcmd.NewRegistryCmd())
cmd.AddCommand(kclcmd.NewServerCmd())
cmd.AddCommand(NewVersionCmd())
cmd.AddCommand(NewChartCmd())
return cmd
}
63 changes: 63 additions & 0 deletions internal/cli/root_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package cli_test

import (
"bytes"
"os"
"path/filepath"
"runtime"
"testing"

"github.com/stretchr/testify/require"

"github.com/MacroPower/kclx/internal/cli"
)

var testDataDir string

func init() {
//nolint:dogsled
_, filename, _, _ := runtime.Caller(0)
dir := filepath.Dir(filename)
testDataDir = filepath.Join(dir, "testdata")

os.Setenv("KCLX_HELM_PLUGIN_DISABLED", "true")
os.Setenv("KCLX_OS_PLUGIN_DISABLED", "true")
os.Setenv("KCLX_HTTP_PLUGIN_DISABLED", "true")
}

func TestRun(t *testing.T) {
t.Parallel()

tc := cli.NewRootCmd("test", "", "")
out := bytes.NewBufferString("")
outFile := filepath.Join(testDataDir, "got/simple.json")
err := os.MkdirAll(filepath.Dir(outFile), 0o755)
require.NoError(t, err)

tc.SetArgs([]string{"run", filepath.Join(testDataDir, "simple.k"), "--format=json", "--output", outFile})
tc.SetOut(out)

err = tc.Execute()
require.NoError(t, err)
require.Empty(t, out.String())

outData, err := os.ReadFile(outFile)
require.NoError(t, err)

require.JSONEq(t, `{"a":1}`, string(outData))
}

func BenchmarkRun(b *testing.B) {
for i := 0; i < b.N; i++ {
tc := cli.NewRootCmd("test", "", "")

//plugin.RegisterPlugin(helm.HelmPlugin)

Check failure on line 54 in internal/cli/root_test.go

View workflow job for this annotation

GitHub Actions / lint

commentFormatting: put a space between `//` and comment text (gocritic)

out := bytes.NewBufferString("")
tc.SetArgs([]string{"run", filepath.Join(testDataDir, "simple.k"), "--output=/dev/null"})
tc.SetOut(out)
err := tc.Execute()
require.NoError(b, err)
require.Empty(b, out.String())
}
}
1 change: 1 addition & 0 deletions internal/cli/testdata/simple.k
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a = 1
2 changes: 1 addition & 1 deletion cmd/kclx/version.go → internal/cli/version.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package main
package cli

import (
"fmt"
Expand Down
114 changes: 56 additions & 58 deletions pkg/plugin/helm/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,72 +11,70 @@ import (
kclutil "github.com/MacroPower/kclx/pkg/kclutil"
)

func init() {
if strings.ToLower(os.Getenv("KCLX_HELM_PLUGIN_DISABLED")) == "true" {
return
}
func Register() {
plugin.RegisterPlugin(Plugin)
}

plugin.RegisterPlugin(plugin.Plugin{
Name: "helm",
MethodMap: map[string]plugin.MethodSpec{
"template": {
Type: &plugin.MethodType{
KwArgsType: map[string]string{
"chart": "str",
"target_revision": "str",
"repo_url": "str",
"release_name": "str",
"namespace": "str",
"helm_version": "str",
"skip_crds": "bool",
"skip_schema_validation": "bool",
"pass_credentials": "bool",
"values": "{str:any}",
},
ResultType: "[{str:any}]",
var Plugin = plugin.Plugin{
Name: "helm",
MethodMap: map[string]plugin.MethodSpec{
"template": {
Type: &plugin.MethodType{
KwArgsType: map[string]string{
"chart": "str",
"target_revision": "str",
"repo_url": "str",
"release_name": "str",
"namespace": "str",
"helm_version": "str",
"skip_crds": "bool",
"skip_schema_validation": "bool",
"pass_credentials": "bool",
"values": "{str:any}",
},
Body: func(args *plugin.MethodArgs) (*plugin.MethodResult, error) {
safeArgs := kclutil.SafeMethodArgs{Args: args}
ResultType: "[{str:any}]",
},
Body: func(args *plugin.MethodArgs) (*plugin.MethodResult, error) {
safeArgs := kclutil.SafeMethodArgs{Args: args}

chartName := args.StrKwArg("chart")
targetRevision := args.StrKwArg("target_revision")
repoURL := args.StrKwArg("repo_url")
chartName := args.StrKwArg("chart")
targetRevision := args.StrKwArg("target_revision")
repoURL := args.StrKwArg("repo_url")

// https://argo-cd.readthedocs.io/en/stable/user-guide/build-environment/
// https://github.com/argoproj/argo-cd/pull/15186
project := os.Getenv("ARGOCD_APP_PROJECT_NAME")
namespace := safeArgs.StrKwArg("namespace", os.Getenv("ARGOCD_APP_NAMESPACE"))
kubeVersion := os.Getenv("KUBE_VERSION")
kubeAPIVersions := os.Getenv("KUBE_API_VERSIONS")
// https://argo-cd.readthedocs.io/en/stable/user-guide/build-environment/
// https://github.com/argoproj/argo-cd/pull/15186
project := os.Getenv("ARGOCD_APP_PROJECT_NAME")
namespace := safeArgs.StrKwArg("namespace", os.Getenv("ARGOCD_APP_NAMESPACE"))
kubeVersion := os.Getenv("KUBE_VERSION")
kubeAPIVersions := os.Getenv("KUBE_API_VERSIONS")

helmClient, err := helm.NewClient(helm.NewTempPaths(os.TempDir(), helm.NewBase64PathEncoder()), project, "10M")
if err != nil {
return nil, fmt.Errorf("failed to create helm client: %w", err)
}
helmClient, err := helm.NewClient(helm.NewTempPaths(os.TempDir(), helm.NewBase64PathEncoder()), project, "10M")
if err != nil {
return nil, fmt.Errorf("failed to create helm client: %w", err)
}

helmChart := helm.NewChart(helmClient, helm.TemplateOpts{
ChartName: chartName,
TargetRevision: targetRevision,
RepoURL: repoURL,
ReleaseName: safeArgs.StrKwArg("release_name", chartName),
Namespace: namespace,
HelmVersion: safeArgs.StrKwArg("helm_version", "v3"),
SkipCRDs: safeArgs.BoolKwArg("skip_crds", false),
SkipSchemaValidation: safeArgs.BoolKwArg("skip_schema_validation", true),
PassCredentials: safeArgs.BoolKwArg("pass_credentials", false),
ValuesObject: safeArgs.MapKwArg("values", map[string]any{}),
KubeVersion: kubeVersion,
APIVersions: strings.Split(kubeAPIVersions, ","),
})
helmChart := helm.NewChart(helmClient, helm.TemplateOpts{
ChartName: chartName,
TargetRevision: targetRevision,
RepoURL: repoURL,
ReleaseName: safeArgs.StrKwArg("release_name", chartName),
Namespace: namespace,
HelmVersion: safeArgs.StrKwArg("helm_version", "v3"),
SkipCRDs: safeArgs.BoolKwArg("skip_crds", false),
SkipSchemaValidation: safeArgs.BoolKwArg("skip_schema_validation", true),
PassCredentials: safeArgs.BoolKwArg("pass_credentials", false),
ValuesObject: safeArgs.MapKwArg("values", map[string]any{}),
KubeVersion: kubeVersion,
APIVersions: strings.Split(kubeAPIVersions, ","),
})

objs, err := helmChart.Template()
if err != nil {
return nil, fmt.Errorf("failed to template '%s': %w", chartName, err)
}
objs, err := helmChart.Template()
if err != nil {
return nil, fmt.Errorf("failed to template '%s': %w", chartName, err)
}

return &plugin.MethodResult{V: objs}, nil
},
return &plugin.MethodResult{V: objs}, nil
},
},
})
},
}
4 changes: 3 additions & 1 deletion pkg/plugin/helm/plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"kcl-lang.io/lib/go/native"

"github.com/MacroPower/kclx/pkg/log"
_ "github.com/MacroPower/kclx/pkg/plugin/helm"
helmplugin "github.com/MacroPower/kclx/pkg/plugin/helm"
)

var testDataDir string
Expand All @@ -28,6 +28,8 @@ func init() {
func TestPluginHelmTemplate(t *testing.T) {
t.Parallel()

helmplugin.Register()

tcs := map[string]struct {
kclFile string
resultsFile string
Expand Down
56 changes: 26 additions & 30 deletions pkg/plugin/http/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package http

import (
"fmt"
"os"
"strings"
"time"

"kcl-lang.io/lib/go/plugin"
Expand All @@ -12,37 +10,35 @@ import (
"github.com/MacroPower/kclx/pkg/kclutil"
)

func init() {
if strings.ToLower(os.Getenv("KCLX_HTTP_PLUGIN_DISABLED")) == "true" {
return
}
func Register() {
plugin.RegisterPlugin(Plugin)
}

plugin.RegisterPlugin(plugin.Plugin{
Name: "http",
MethodMap: map[string]plugin.MethodSpec{
"get": {
// http.get(url, timeout="30s")
Body: func(args *plugin.MethodArgs) (*plugin.MethodResult, error) {
safeArgs := kclutil.SafeMethodArgs{Args: args}
var Plugin = plugin.Plugin{
Name: "http",
MethodMap: map[string]plugin.MethodSpec{
"get": {
// http.get(url, timeout="30s")
Body: func(args *plugin.MethodArgs) (*plugin.MethodResult, error) {
safeArgs := kclutil.SafeMethodArgs{Args: args}

urlArg := args.StrArg(0)
timeout := safeArgs.StrKwArg("timeout", "30s")
timeoutDuration, err := time.ParseDuration(timeout)
if err != nil {
return nil, fmt.Errorf("failed to parse timeout %s: %w", timeout, err)
}
client := http.NewClient(timeoutDuration)
body, status, err := client.Get(urlArg)
if err != nil {
return nil, fmt.Errorf("failed to get '%s': %w", urlArg, err)
}
urlArg := args.StrArg(0)
timeout := safeArgs.StrKwArg("timeout", "30s")
timeoutDuration, err := time.ParseDuration(timeout)
if err != nil {
return nil, fmt.Errorf("failed to parse timeout %s: %w", timeout, err)
}
client := http.NewClient(timeoutDuration)
body, status, err := client.Get(urlArg)
if err != nil {
return nil, fmt.Errorf("failed to get '%s': %w", urlArg, err)
}

return &plugin.MethodResult{V: map[string]any{
"status": status,
"body": string(body),
}}, nil
},
return &plugin.MethodResult{V: map[string]any{
"status": status,
"body": string(body),
}}, nil
},
},
})
},
}
Loading

0 comments on commit 64cc3ab

Please sign in to comment.