This is an automated email from the ASF dual-hosted git repository.
ricardozanini pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-kie-tools.git
The following commit(s) were added to refs/heads/main by this push:
new 6b01127a7c5 kie-issues#1362: Migrate kn-workflow-plugin to
kubernetes/client-go (#2465)
6b01127a7c5 is described below
commit 6b01127a7c557b2f2f4c869a62407bc2a1601f91
Author: Dmitrii Tikhomirov <[email protected]>
AuthorDate: Thu Sep 5 10:10:19 2024 -0700
kie-issues#1362: Migrate kn-workflow-plugin to kubernetes/client-go (#2465)
---
packages/kn-plugin-workflow/go.mod | 7 +-
packages/kn-plugin-workflow/pkg/command/deploy.go | 28 ++-
.../pkg/command/deploy_undeploy_common.go | 12 +-
.../kn-plugin-workflow/pkg/command/gen_manifest.go | 4 +-
.../pkg/command/quarkus/deploy.go | 45 ++--
.../pkg/command/quarkus/deploy_test.go | 183 +++++++++++++---
.../pkg/command/quarkus/testdata/knative.yml | 54 +++++
.../testdata/kogito-complex-custom-namespace.yml | 52 +++++
.../command/quarkus/testdata/kogito-complex.yml | 52 +++++
.../testdata/kogito-complex2-custom-namespace.yml | 66 ++++++
.../command/quarkus/testdata/kogito-complex2.yml | 66 ++++++
.../testdata/kogito-complex3-custom-namespace.yml | 66 ++++++
.../command/quarkus/testdata/kogito-complex3.yml | 66 ++++++
.../testdata/kogito-default-custom-namespace.yml | 32 +++
.../command/quarkus/testdata/kogito-default.yml | 32 +++
.../kn-plugin-workflow/pkg/command/undeploy.go | 2 +-
packages/kn-plugin-workflow/pkg/common/exec.go | 6 +-
.../pkg/common/k8sclient/fake.go | 50 +++++
.../pkg/common/k8sclient/goapi.go | 242 +++++++++++++++++++++
packages/kn-plugin-workflow/pkg/common/kubectl.go | 69 ++----
packages/kn-plugin-workflow/pkg/common/operator.go | 77 +------
.../kn-plugin-workflow/pkg/common/test_helper.go | 25 ++-
.../kn-plugin-workflow/pkg/metadata/constants.go | 1 +
23 files changed, 1038 insertions(+), 199 deletions(-)
diff --git a/packages/kn-plugin-workflow/go.mod
b/packages/kn-plugin-workflow/go.mod
index 8517b36b424..35e3663862a 100644
--- a/packages/kn-plugin-workflow/go.mod
+++ b/packages/kn-plugin-workflow/go.mod
@@ -20,6 +20,9 @@ require (
github.com/spf13/cobra v1.7.0
github.com/stretchr/testify v1.8.4
gopkg.in/yaml.v2 v2.4.0
+ k8s.io/apiextensions-apiserver v0.28.1
+ k8s.io/apimachinery v0.28.1
+ k8s.io/client-go v0.28.1
)
require (
@@ -33,6 +36,7 @@ require (
github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 //
indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
+ github.com/evanphx/json-patch v5.6.0+incompatible // indirect
github.com/evanphx/json-patch/v5 v5.7.0 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
@@ -105,9 +109,6 @@ require (
gopkg.in/yaml.v3 v3.0.1 // indirect
gotest.tools/v3 v3.3.0 // indirect
k8s.io/api v0.28.1 // indirect
- k8s.io/apiextensions-apiserver v0.28.1 // indirect
- k8s.io/apimachinery v0.28.1 // indirect
- k8s.io/client-go v0.28.1 // indirect
k8s.io/component-base v0.28.1 // indirect
k8s.io/klog/v2 v2.100.1 // indirect
k8s.io/kube-openapi v0.0.0-20230905202853-d090da108d2f // indirect
diff --git a/packages/kn-plugin-workflow/pkg/command/deploy.go
b/packages/kn-plugin-workflow/pkg/command/deploy.go
index d97443ee9f9..4a3098960b4 100644
--- a/packages/kn-plugin-workflow/pkg/command/deploy.go
+++ b/packages/kn-plugin-workflow/pkg/command/deploy.go
@@ -20,7 +20,10 @@
package command
import (
+ "errors"
"fmt"
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"os"
"path"
@@ -138,7 +141,7 @@ func deploy(cfg *DeployUndeployCmdConfig) error {
return fmt.Errorf("❌ ERROR: failed to get manifest directory
and files: %w", err)
}
for _, file := range files {
- if err = common.ExecuteKubectlApply(file, cfg.NameSpace); err
!= nil {
+ if err = common.ExecuteApply(file, cfg.NameSpace); err != nil {
return fmt.Errorf("❌ ERROR: failed to deploy manifest
%s, %w", file, err)
}
fmt.Printf(" - ✅ Manifest %s successfully deployed in namespace
%s\n", path.Base(file), cfg.NameSpace)
@@ -188,11 +191,9 @@ func runDeployCmdConfig(cmd *cobra.Command) (cfg
DeployUndeployCmdConfig, err er
return cfg, fmt.Errorf("❌ ERROR: failed to get default
dashboards files folder: %w", err)
}
- // check if sonataflow operator CRDs are installed
- for _, crd := range metadata.SonataflowCRDs {
- if !common.CheckKubectlCrdExists(crd) {
- return cfg, fmt.Errorf("❌ ERROR: the required CRDs are
not installed.. Install the SonataFlow Operator CRD first")
- }
+ // check if sonataflow operator and knative CRDs are installed
+ if err := CheckCRDs(metadata.SonataflowCRDs, "SonataFlow Operator");
err != nil {
+ return cfg, err
}
//setup manifest path
@@ -202,3 +203,18 @@ func runDeployCmdConfig(cmd *cobra.Command) (cfg
DeployUndeployCmdConfig, err er
return cfg, nil
}
+
+func CheckCRDs(crds []string, typeName string) error {
+ for _, crd := range crds {
+ err := common.CheckCrdExists(crd)
+ if err != nil {
+ var statusErr *apierrors.StatusError
+ if errors.As(err, &statusErr) &&
statusErr.ErrStatus.Reason == metav1.StatusReasonNotFound {
+ return fmt.Errorf("❌ ERROR: the required CRDs
are not installed.. Install the %s CRD first", typeName)
+ } else {
+ return fmt.Errorf("❌ ERROR: failed to check if
CRD %s exists: %w", crd, err)
+ }
+ }
+ }
+ return nil
+}
diff --git a/packages/kn-plugin-workflow/pkg/command/deploy_undeploy_common.go
b/packages/kn-plugin-workflow/pkg/command/deploy_undeploy_common.go
index 598adc51ca2..2f3f32afdc9 100644
--- a/packages/kn-plugin-workflow/pkg/command/deploy_undeploy_common.go
+++ b/packages/kn-plugin-workflow/pkg/command/deploy_undeploy_common.go
@@ -26,8 +26,8 @@ import (
"github.com/apache/incubator-kie-tools/packages/kn-plugin-workflow/pkg/common"
"github.com/apache/incubator-kie-tools/packages/kn-plugin-workflow/pkg/metadata"
-
"github.com/apache/incubator-kie-tools/packages/sonataflow-operator/workflowproj"
apimetadata
"github.com/apache/incubator-kie-tools/packages/sonataflow-operator/api/metadata"
+
"github.com/apache/incubator-kie-tools/packages/sonataflow-operator/workflowproj"
)
type DeployUndeployCmdConfig struct {
@@ -54,11 +54,7 @@ type DeployUndeployCmdConfig struct {
func checkEnvironment(cfg *DeployUndeployCmdConfig) error {
fmt.Println("\n🔎 Checking your environment...")
- if err := common.CheckKubectl(); err != nil {
- return err
- }
-
- if ctx, err := common.CheckKubectlContext(); err != nil {
+ if ctx, err := common.CheckContext(); err != nil {
return err
} else {
cfg.KubectlContext = ctx
@@ -66,7 +62,7 @@ func checkEnvironment(cfg *DeployUndeployCmdConfig) error {
//setup namespace
if len(cfg.NameSpace) == 0 {
- if defaultNamespace, err := common.GetKubectlNamespace(); err
== nil {
+ if defaultNamespace, err := common.GetNamespace(); err == nil {
cfg.NameSpace = defaultNamespace
} else {
return err
@@ -216,7 +212,7 @@ func generateManifests(cfg *DeployUndeployCmdConfig) error {
}
if cfg.Image != "" {
- handler.Image(cfg.Image)
+ handler.Image(cfg.Image)
}
err = handler.SaveAsKubernetesManifests(cfg.CustomGeneratedManifestDir)
diff --git a/packages/kn-plugin-workflow/pkg/command/gen_manifest.go
b/packages/kn-plugin-workflow/pkg/command/gen_manifest.go
index ef188c90c70..53e996f059e 100644
--- a/packages/kn-plugin-workflow/pkg/command/gen_manifest.go
+++ b/packages/kn-plugin-workflow/pkg/command/gen_manifest.go
@@ -140,7 +140,7 @@ func runGenManifestCmdConfig(cmd *cobra.Command) (cfg
DeployUndeployCmdConfig, e
if cmd.Flags().Changed("profile") && len(cfg.Profile) == 0 {
profile, _ := cmd.Flags().GetString("profile")
- if err := isValidProfile(profile); err != nil{
+ if err := isValidProfile(profile); err != nil {
return cfg, err
}
cfg.Profile = profile
@@ -198,7 +198,7 @@ func setupEnvironment(cfg *DeployUndeployCmdConfig) error {
//setup namespace
if len(cfg.NameSpace) == 0 && !cfg.EmptyNameSpace {
- if defaultNamespace, err := common.GetKubectlNamespace(); err
== nil {
+ if defaultNamespace, err := common.GetNamespace(); err == nil {
cfg.NameSpace = defaultNamespace
fmt.Printf(" - ✅ resolved namespace: %s\n",
cfg.NameSpace)
} else {
diff --git a/packages/kn-plugin-workflow/pkg/command/quarkus/deploy.go
b/packages/kn-plugin-workflow/pkg/command/quarkus/deploy.go
index faf07d2c272..8da721a3246 100644
--- a/packages/kn-plugin-workflow/pkg/command/quarkus/deploy.go
+++ b/packages/kn-plugin-workflow/pkg/command/quarkus/deploy.go
@@ -21,9 +21,12 @@ package quarkus
import (
"fmt"
+
"github.com/apache/incubator-kie-tools/packages/kn-plugin-workflow/pkg/command"
"github.com/apache/incubator-kie-tools/packages/kn-plugin-workflow/pkg/common"
+
"github.com/apache/incubator-kie-tools/packages/kn-plugin-workflow/pkg/metadata"
"github.com/ory/viper"
"github.com/spf13/cobra"
+ "path/filepath"
)
type DeployCmdConfig struct {
@@ -71,6 +74,11 @@ func NewDeployCommand() *cobra.Command {
return cmd
}
+var crds = map[string][]string{
+ "SonataFlow Operator": metadata.SonataflowCRDs,
+ "Knative Serving and Knative Eventing": metadata.KnativeCoreServingCRDs,
+}
+
func runDeploy(cmd *cobra.Command, args []string) error {
fmt.Println("🛠️ Deploying your Quarkus SonataFlow project...")
@@ -79,11 +87,15 @@ func runDeploy(cmd *cobra.Command, args []string) error {
return fmt.Errorf("initializing deploy config: %w", err)
}
- if err = common.CheckKubectl(); err != nil {
- return err
+ // check necessary CRDs are installed
+ for name, crds := range crds {
+ if err := command.CheckCRDs(crds, name); err != nil {
+ return err
+ }
}
if _, err = deployKnativeServiceAndEventingBindings(cfg); err != nil {
+ fmt.Println("❌ ERROR:Deploy failed, Knative Eventing binding
was not created.")
return err
}
@@ -94,36 +106,19 @@ func runDeploy(cmd *cobra.Command, args []string) error {
func deployKnativeServiceAndEventingBindings(cfg DeployCmdConfig) (bool,
error) {
isKnativeEventingBindingsCreated := false
- createService := common.ExecCommand("kubectl", "apply", "-f",
fmt.Sprintf("%s/knative.yml", cfg.Path), fmt.Sprintf("--namespace=%s",
cfg.Namespace))
- if err := common.RunCommand(
- createService,
- "deploy",
- ); err != nil {
- fmt.Println("❌ ERROR: Deploy failed, Knative service was not
created.")
+
+ err := common.ExecuteApply(filepath.Join(cfg.Path, "knative.yml"),
cfg.Namespace)
+ if err != nil {
return isKnativeEventingBindingsCreated, err
}
fmt.Println("🎉 Knative service successfully created")
- // Check if kogito.yml file exists
if exists, err := checkIfKogitoFileExists(cfg); exists && err == nil {
- if cfg.Namespace == "" {
- if namespace, err := common.GetKubectlNamespace(); err
== nil {
- cfg.Namespace = namespace
- } else {
- fmt.Println("❌ ERROR: Failed to get current
kubectl namespace")
- return isKnativeEventingBindingsCreated, err
- }
- }
- deploy := common.ExecCommand("kubectl", "apply", "-f",
fmt.Sprintf("%s/kogito.yml", cfg.Path), fmt.Sprintf("--namespace=%s",
cfg.Namespace))
- if err := common.RunCommand(
- deploy,
- "deploy",
- ); err != nil {
- fmt.Println("❌ ERROR:Deploy failed, Knative Eventing
binding was not created.")
+ if err := common.ExecuteApply(filepath.Join(cfg.Path,
"kogito.yml"), cfg.Namespace); err != nil {
return isKnativeEventingBindingsCreated, err
}
isKnativeEventingBindingsCreated = true
- fmt.Println("✅ Knative Eventing bindings successfully created")
+ fmt.Println("🎉 Knative Eventing bindings successfully created")
}
return isKnativeEventingBindingsCreated, nil
}
@@ -137,7 +132,7 @@ func runDeployCmdConfig(cmd *cobra.Command) (cfg
DeployCmdConfig, err error) {
}
func checkIfKogitoFileExists(cfg DeployCmdConfig) (bool, error) {
- if _, err := common.FS.Stat(fmt.Sprintf("%s/kogito.yml", cfg.Path));
err == nil {
+ if _, err := common.FS.Stat(filepath.Join(cfg.Path, "kogito.yml")); err
== nil {
return true, nil
} else {
return false, err
diff --git a/packages/kn-plugin-workflow/pkg/command/quarkus/deploy_test.go
b/packages/kn-plugin-workflow/pkg/command/quarkus/deploy_test.go
index e1448a1610e..c3d292af363 100644
--- a/packages/kn-plugin-workflow/pkg/command/quarkus/deploy_test.go
+++ b/packages/kn-plugin-workflow/pkg/command/quarkus/deploy_test.go
@@ -20,29 +20,43 @@
package quarkus
import (
+ "context"
"fmt"
+
"github.com/apache/incubator-kie-tools/packages/kn-plugin-workflow/pkg/common"
+
"github.com/apache/incubator-kie-tools/packages/kn-plugin-workflow/pkg/common/k8sclient"
+ "github.com/spf13/afero"
+ "github.com/stretchr/testify/assert"
+ "k8s.io/apimachinery/pkg/api/errors"
+ "k8s.io/apimachinery/pkg/api/meta"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"os"
"os/exec"
+ "path/filepath"
"strconv"
"testing"
-
-
"github.com/apache/incubator-kie-tools/packages/kn-plugin-workflow/pkg/common"
- "github.com/spf13/afero"
)
type testDeploy struct {
- input DeployCmdConfig
- expected bool
- createFile string
+ input DeployCmdConfig
+ expected bool
+ knative string
+ kogito string
+ resourcesCount int
}
const defaultPath = "./target/kubernetes"
var testRunDeploy = []testDeploy{
- {input: DeployCmdConfig{Path: defaultPath}, expected: true, createFile:
"kogito.yml"},
- {input: DeployCmdConfig{Path: "./different/folders"}, expected: true,
createFile: "kogito.yml"},
- {input: DeployCmdConfig{Path: "different/folders"}, expected: true,
createFile: "kogito.yml"},
- {input: DeployCmdConfig{}, expected: false, createFile: "test"},
+ {input: DeployCmdConfig{Path: defaultPath}, expected: true, knative:
"knative.yml", kogito: "kogito-default.yml"},
+ {input: DeployCmdConfig{Path: "./different/folders"}, expected: true,
knative: "knative.yml", kogito: "kogito-default.yml"},
+ {input: DeployCmdConfig{Path: "different/folders"}, expected: true,
knative: "knative.yml", kogito: "kogito-complex.yml"},
+ {input: DeployCmdConfig{Path: "different/folders"}, expected: true,
knative: "knative.yml", kogito: "kogito-complex2.yml"},
+ {input: DeployCmdConfig{Path: "./different/folders", Namespace:
"mycustom"}, expected: true, knative: "knative.yml", kogito:
"kogito-default-custom-namespace.yml"},
+ {input: DeployCmdConfig{Path: "different/folders", Namespace:
"mycustom"}, expected: true, knative: "knative.yml", kogito:
"kogito-complex-custom-namespace.yml"},
+ {input: DeployCmdConfig{Path: "different/folders", Namespace:
"mycustom"}, expected: true, knative: "knative.yml", kogito:
"kogito-complex2-custom-namespace.yml"},
+ {input: DeployCmdConfig{Path: "different/folders", Namespace:
"mycustom"}, expected: true, knative: "knative.yml", kogito:
"kogito-complex3-custom-namespace.yml"},
+ {input: DeployCmdConfig{}, expected: false, kogito: ""},
{input: DeployCmdConfig{}, expected: false},
}
@@ -62,8 +76,8 @@ func TestHelperRunDeploy(t *testing.T) {
return
}
out := []string{"Test", strconv.Itoa(testIndex)}
- if testRunDeploy[testIndex].createFile != "" {
- out = append(out, "with creating",
testRunDeploy[testIndex].createFile, "file")
+ if testRunDeploy[testIndex].kogito != "" {
+ out = append(out, "with creating",
testRunDeploy[testIndex].kogito, "file")
}
fmt.Fprintf(os.Stdout, "%v", out)
os.Exit(0)
@@ -71,29 +85,142 @@ func TestHelperRunDeploy(t *testing.T) {
func TestRunDeploy(t *testing.T) {
common.FS = afero.NewMemMapFs()
- for testIndex, test := range testRunDeploy {
- common.ExecCommand = fakeRunDeploy(testIndex)
- defer func() { common.ExecCommand = exec.Command }()
-
- if test.createFile != "" {
- if test.input.Path == "" {
- test.input.Path = defaultPath
- }
- common.CreateFolderStructure(t, test.input.Path)
- common.CreateFileInFolderStructure(t, test.input.Path,
test.createFile)
+ originalParseYamlFile := k8sclient.ParseYamlFile
+ originalDynamicClient := k8sclient.DynamicClient
+ originalGetNamespace := k8sclient.GetNamespace
+
+ fakeClient := k8sclient.Fake{FS: common.FS}
+
+ defer func() {
+ k8sclient.ParseYamlFile = originalParseYamlFile
+ k8sclient.DynamicClient = originalDynamicClient
+ k8sclient.GetNamespace = originalGetNamespace
+ }()
+
+ k8sclient.ParseYamlFile = fakeClient.FakeParseYamlFile
+ k8sclient.DynamicClient = fakeClient.FakeDynamicClient
+ k8sclient.GetNamespace = fakeClient.GetNamespace
+
+ for _, test := range testRunDeploy {
+ checkDeploy(t, test)
+ }
+}
+
+func checkDeploy(t *testing.T, test testDeploy) {
+
+ expectedResources := []unstructured.Unstructured{}
+
+ prepareFolderAndFiles(t, test)
+ populateExpectedResources(t, &expectedResources, test)
+
+ out, err := deployKnativeServiceAndEventingBindings(test.input)
+ if err != nil && test.expected {
+ assert.True(t, false, "Expected no error, got %v", err)
+ }
+
+ assert.Equal(t, out, test.expected, "Expected %v, got %v",
test.expected, out)
+
+ checkResourcesCreated(t, &expectedResources, test)
+
+ if test.kogito != "" || test.knative != "" {
+ undeploy(t, test, test.input.Namespace)
+ checkResourcesDeleted(t, &expectedResources, test)
+ common.DeleteFolderStructure(t, test.input.Path)
+ }
+}
+
+func checkResourcesCreated(t *testing.T, expectedResources
*[]unstructured.Unstructured, test testDeploy) {
+ for _, resource := range *expectedResources {
+ if result, err := checkObjectCreated(resource,
test.input.Namespace); err != nil {
+ t.Errorf("Error checking if resource was deleted: %v",
err)
+ } else {
+ assert.True(t, result, "Expected resource to be
created: %s", resource.GetName())
+ }
+ }
+}
+
+func checkResourcesDeleted(t *testing.T, expectedResources
*[]unstructured.Unstructured, test testDeploy) {
+ for _, r := range *expectedResources {
+ if result, err := checkObjectCreated(r, test.input.Namespace);
err != nil {
+ t.Errorf("Error checking if resource was deleted: %v",
err)
+ } else {
+ assert.False(t, result, "Expected resource to be
deleted: %s", r.GetName())
}
+ }
+}
- out, err := deployKnativeServiceAndEventingBindings(test.input)
+func populateExpectedResources(t *testing.T, resources
*[]unstructured.Unstructured, test testDeploy) {
+ if test.knative != "" {
+ if knativeResources, err :=
k8sclient.ParseYamlFile(filepath.Join(test.input.Path, "knative.yml")); err ==
nil {
+ *resources = append(*resources, knativeResources...)
+ } else {
+ t.Errorf("❌ ERROR: Failed to parse Knative resources:
%v", err)
+ }
+ } else {
+ fmt.Printf("❌ ERROR: Failed to parse Knative resources: %v",
test.knative)
+ }
+ if test.kogito != "" {
+ if kogitoResources, err :=
k8sclient.ParseYamlFile(filepath.Join(test.input.Path, "kogito.yml")); err ==
nil {
+ *resources = append(*resources, kogitoResources...)
+ } else {
+ t.Errorf("❌ ERROR: Failed to parse Kogito resources:
%v", err)
+ }
+ } else {
+ fmt.Printf("❌ ERROR: Failed to parse Kogito resources: %v",
test.kogito)
+ }
+}
+
+func prepareFolderAndFiles(t *testing.T, test testDeploy) {
+ if test.input.Path == "" {
+ test.input.Path = defaultPath
+ }
+ common.CreateFolderStructure(t, test.input.Path)
+ common.CopyFileInFolderStructure(t, test.input.Path, test.knative,
"knative.yml")
+ common.CopyFileInFolderStructure(t, test.input.Path, test.kogito,
"kogito.yml")
+}
+
+func checkObjectCreated(obj unstructured.Unstructured, namespace string)
(bool, error) {
+ if namespace == "" {
+ currentNamespace, err := common.GetNamespace()
if err != nil {
- t.Errorf("Expected nil error, got %#v", err)
+ return false, fmt.Errorf("❌ ERROR: Failed to get
current namespace: %v", err)
}
+ namespace = currentNamespace
+ }
- if out != test.expected {
- t.Errorf("Expected %v, got %v", test.expected, out)
+ client, err := k8sclient.DynamicClient()
+ if err != nil {
+ return false, fmt.Errorf("❌ ERROR: Failed to create dynamic
Kubernetes client: %v", err)
+ }
+
+ gvk := obj.GroupVersionKind()
+ gvr, _ := meta.UnsafeGuessKindToResource(gvk)
+
+ applyNamespace := namespace
+ if obj.GetNamespace() != "" {
+ applyNamespace = obj.GetNamespace()
+ }
+
+ _, err =
client.Resource(gvr).Namespace(applyNamespace).Get(context.Background(),
obj.GetName(), metav1.GetOptions{})
+ if err != nil {
+ if errors.IsNotFound(err) {
+ return false, nil
+ }
+ return false, fmt.Errorf("❌ ERROR: Failed to get resource: %v",
err)
+ }
+ return true, nil
+}
+
+func undeploy(t *testing.T, test testDeploy, namespace string) {
+ if _, err := common.FS.Stat(filepath.Join(test.input.Path,
"knative.yml")); err == nil {
+ if err := common.ExecuteDelete(filepath.Join(test.input.Path,
"knative.yml"), namespace); err != nil {
+ t.Errorf("❌ ERROR: Undeploy failed, Knative service was
not created. %v", err)
}
+ }
- if test.createFile != "" {
- common.DeleteFolderStructure(t, test.input.Path)
+ if _, err := common.FS.Stat(filepath.Join(test.input.Path,
"kogito.yml")); err == nil {
+ if err := common.ExecuteDelete(filepath.Join(test.input.Path,
"kogito.yml"), namespace); err != nil {
+ t.Errorf("❌ ERROR: Undeploy failed, Kogito service was
not created. %v", err)
}
}
}
diff --git
a/packages/kn-plugin-workflow/pkg/command/quarkus/testdata/knative.yml
b/packages/kn-plugin-workflow/pkg/command/quarkus/testdata/knative.yml
new file mode 100644
index 00000000000..0019551ff6a
--- /dev/null
+++ b/packages/kn-plugin-workflow/pkg/command/quarkus/testdata/knative.yml
@@ -0,0 +1,54 @@
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ annotations:
+ app.quarkus.io/quarkus-version: 3.8.4
+ app.quarkus.io/build-timestamp: 2024-08-22 - 21:01:20 +0000
+ labels:
+ app.kubernetes.io/version: 1.0.0-SNAPSHOT
+ app.kubernetes.io/name: incubator-kie-sonataflow-builder
+ name: incubator-kie-sonataflow-builder
+---
+apiVersion: serving.knative.dev/v1
+kind: Service
+metadata:
+ annotations:
+ app.quarkus.io/quarkus-version: 3.8.4
+ app.quarkus.io/build-timestamp: 2024-08-22 - 21:01:20 +0000
+ labels:
+ app.kubernetes.io/version: 1.0.0-SNAPSHOT
+ app.kubernetes.io/name: incubator-kie-sonataflow-builder
+ name: incubator-kie-sonataflow-builder
+spec:
+ template:
+ spec:
+ containers:
+ - image: apache/incubator-kie-sonataflow-builder:latest
+ imagePullPolicy: Always
+ livenessProbe:
+ failureThreshold: 3
+ httpGet:
+ path: /q/health/live
+ port: null
+ scheme: HTTP
+ initialDelaySeconds: 5
+ periodSeconds: 10
+ successThreshold: 1
+ timeoutSeconds: 10
+ name: incubator-kie-sonataflow-builder
+ ports:
+ - containerPort: 8080
+ name: http1
+ protocol: TCP
+ readinessProbe:
+ failureThreshold: 3
+ httpGet:
+ path: /q/health/ready
+ port: null
+ scheme: HTTP
+ initialDelaySeconds: 5
+ periodSeconds: 10
+ successThreshold: 1
+ timeoutSeconds: 10
+ serviceAccountName: incubator-kie-sonataflow-builder
diff --git
a/packages/kn-plugin-workflow/pkg/command/quarkus/testdata/kogito-complex-custom-namespace.yml
b/packages/kn-plugin-workflow/pkg/command/quarkus/testdata/kogito-complex-custom-namespace.yml
new file mode 100644
index 00000000000..481cf4fe5cc
--- /dev/null
+++
b/packages/kn-plugin-workflow/pkg/command/quarkus/testdata/kogito-complex-custom-namespace.yml
@@ -0,0 +1,52 @@
+---
+apiVersion: sonataflow.org/v1alpha08
+kind: SonataFlow
+metadata:
+ annotations:
+ sonataflow.org/description: Description
+ sonataflow.org/expressionLang: jq
+ sonataflow.org/profile: dev
+ sonataflow.org/version: "1.0"
+ creationTimestamp: null
+ labels:
+ app: hello
+ sonataflow.org/workflow-app: hello
+ name: hello
+ namespace: mycustom
+spec:
+ flow:
+ start:
+ stateName: HelloWorld
+ states:
+ - data:
+ message: Hello World
+ end:
+ terminate: true
+ name: HelloWorld
+ type: inject
+ podTemplate:
+ container:
+ resources: {}
+ resources: {}
+status:
+ address: {}
+ lastTimeRecoverAttempt: null
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: example-configmap
+ namespace: mycustom
+data:
+ config_key1: value1
+ config_key2: value2
+---
+apiVersion: v1
+kind: Secret
+metadata:
+ name: example-secret
+ namespace: mycustom
+type: Opaque
+data:
+ username: YWRtaW4=
+ password: cGFzc3dvcmQ=
diff --git
a/packages/kn-plugin-workflow/pkg/command/quarkus/testdata/kogito-complex.yml
b/packages/kn-plugin-workflow/pkg/command/quarkus/testdata/kogito-complex.yml
new file mode 100644
index 00000000000..fe2fd9592bf
--- /dev/null
+++
b/packages/kn-plugin-workflow/pkg/command/quarkus/testdata/kogito-complex.yml
@@ -0,0 +1,52 @@
+---
+apiVersion: sonataflow.org/v1alpha08
+kind: SonataFlow
+metadata:
+ annotations:
+ sonataflow.org/description: Description
+ sonataflow.org/expressionLang: jq
+ sonataflow.org/profile: dev
+ sonataflow.org/version: "1.0"
+ creationTimestamp: null
+ labels:
+ app: hello
+ sonataflow.org/workflow-app: hello
+ name: hello
+ namespace: default
+spec:
+ flow:
+ start:
+ stateName: HelloWorld
+ states:
+ - data:
+ message: Hello World
+ end:
+ terminate: true
+ name: HelloWorld
+ type: inject
+ podTemplate:
+ container:
+ resources: {}
+ resources: {}
+status:
+ address: {}
+ lastTimeRecoverAttempt: null
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: example-configmap
+ namespace: default
+data:
+ config_key1: value1
+ config_key2: value2
+---
+apiVersion: v1
+kind: Secret
+metadata:
+ name: example-secret
+ namespace: default
+type: Opaque
+data:
+ username: YWRtaW4=
+ password: cGFzc3dvcmQ=
diff --git
a/packages/kn-plugin-workflow/pkg/command/quarkus/testdata/kogito-complex2-custom-namespace.yml
b/packages/kn-plugin-workflow/pkg/command/quarkus/testdata/kogito-complex2-custom-namespace.yml
new file mode 100644
index 00000000000..348884bf7ab
--- /dev/null
+++
b/packages/kn-plugin-workflow/pkg/command/quarkus/testdata/kogito-complex2-custom-namespace.yml
@@ -0,0 +1,66 @@
+apiVersion: batch/v1
+kind: Job
+metadata:
+ name: example-job
+ namespace: mycustom
+spec:
+ template:
+ spec:
+ containers:
+ - name: example-job-container
+ image: busybox
+ command: ["echo", "Hello, Kubernetes!"]
+ restartPolicy: Never
+ backoffLimit: 4
+---
+apiVersion: batch/v1
+kind: CronJob
+metadata:
+ name: example-cronjob
+ namespace: mycustom
+spec:
+ schedule: "*/5 * * * *"
+ jobTemplate:
+ spec:
+ template:
+ spec:
+ containers:
+ - name: example-cronjob-container
+ image: busybox
+ command: ["echo", "Hello from CronJob!"]
+ restartPolicy: Never
+ successfulJobsHistoryLimit: 3
+ failedJobsHistoryLimit: 1
+---
+apiVersion: sonataflow.org/v1alpha08
+kind: SonataFlow
+metadata:
+ annotations:
+ sonataflow.org/description: Description
+ sonataflow.org/expressionLang: jq
+ sonataflow.org/profile: dev
+ sonataflow.org/version: "1.0"
+ creationTimestamp: null
+ labels:
+ app: hello
+ sonataflow.org/workflow-app: hello
+ name: hello
+ namespace: mycustom
+spec:
+ flow:
+ start:
+ stateName: HelloWorld
+ states:
+ - data:
+ message: Hello World
+ end:
+ terminate: true
+ name: HelloWorld
+ type: inject
+ podTemplate:
+ container:
+ resources: {}
+ resources: {}
+status:
+ address: {}
+ lastTimeRecoverAttempt: null
diff --git
a/packages/kn-plugin-workflow/pkg/command/quarkus/testdata/kogito-complex2.yml
b/packages/kn-plugin-workflow/pkg/command/quarkus/testdata/kogito-complex2.yml
new file mode 100644
index 00000000000..abc4efab6d5
--- /dev/null
+++
b/packages/kn-plugin-workflow/pkg/command/quarkus/testdata/kogito-complex2.yml
@@ -0,0 +1,66 @@
+apiVersion: batch/v1
+kind: Job
+metadata:
+ name: example-job
+ namespace: default
+spec:
+ template:
+ spec:
+ containers:
+ - name: example-job-container
+ image: busybox
+ command: ["echo", "Hello, Kubernetes!"]
+ restartPolicy: Never
+ backoffLimit: 4
+---
+apiVersion: batch/v1
+kind: CronJob
+metadata:
+ name: example-cronjob
+ namespace: default
+spec:
+ schedule: "*/5 * * * *"
+ jobTemplate:
+ spec:
+ template:
+ spec:
+ containers:
+ - name: example-cronjob-container
+ image: busybox
+ command: ["echo", "Hello from CronJob!"]
+ restartPolicy: Never
+ successfulJobsHistoryLimit: 3
+ failedJobsHistoryLimit: 1
+---
+apiVersion: sonataflow.org/v1alpha08
+kind: SonataFlow
+metadata:
+ annotations:
+ sonataflow.org/description: Description
+ sonataflow.org/expressionLang: jq
+ sonataflow.org/profile: dev
+ sonataflow.org/version: "1.0"
+ creationTimestamp: null
+ labels:
+ app: hello
+ sonataflow.org/workflow-app: hello
+ name: hello
+ namespace: default
+spec:
+ flow:
+ start:
+ stateName: HelloWorld
+ states:
+ - data:
+ message: Hello World
+ end:
+ terminate: true
+ name: HelloWorld
+ type: inject
+ podTemplate:
+ container:
+ resources: {}
+ resources: {}
+status:
+ address: {}
+ lastTimeRecoverAttempt: null
diff --git
a/packages/kn-plugin-workflow/pkg/command/quarkus/testdata/kogito-complex3-custom-namespace.yml
b/packages/kn-plugin-workflow/pkg/command/quarkus/testdata/kogito-complex3-custom-namespace.yml
new file mode 100644
index 00000000000..348884bf7ab
--- /dev/null
+++
b/packages/kn-plugin-workflow/pkg/command/quarkus/testdata/kogito-complex3-custom-namespace.yml
@@ -0,0 +1,66 @@
+apiVersion: batch/v1
+kind: Job
+metadata:
+ name: example-job
+ namespace: mycustom
+spec:
+ template:
+ spec:
+ containers:
+ - name: example-job-container
+ image: busybox
+ command: ["echo", "Hello, Kubernetes!"]
+ restartPolicy: Never
+ backoffLimit: 4
+---
+apiVersion: batch/v1
+kind: CronJob
+metadata:
+ name: example-cronjob
+ namespace: mycustom
+spec:
+ schedule: "*/5 * * * *"
+ jobTemplate:
+ spec:
+ template:
+ spec:
+ containers:
+ - name: example-cronjob-container
+ image: busybox
+ command: ["echo", "Hello from CronJob!"]
+ restartPolicy: Never
+ successfulJobsHistoryLimit: 3
+ failedJobsHistoryLimit: 1
+---
+apiVersion: sonataflow.org/v1alpha08
+kind: SonataFlow
+metadata:
+ annotations:
+ sonataflow.org/description: Description
+ sonataflow.org/expressionLang: jq
+ sonataflow.org/profile: dev
+ sonataflow.org/version: "1.0"
+ creationTimestamp: null
+ labels:
+ app: hello
+ sonataflow.org/workflow-app: hello
+ name: hello
+ namespace: mycustom
+spec:
+ flow:
+ start:
+ stateName: HelloWorld
+ states:
+ - data:
+ message: Hello World
+ end:
+ terminate: true
+ name: HelloWorld
+ type: inject
+ podTemplate:
+ container:
+ resources: {}
+ resources: {}
+status:
+ address: {}
+ lastTimeRecoverAttempt: null
diff --git
a/packages/kn-plugin-workflow/pkg/command/quarkus/testdata/kogito-complex3.yml
b/packages/kn-plugin-workflow/pkg/command/quarkus/testdata/kogito-complex3.yml
new file mode 100644
index 00000000000..abc4efab6d5
--- /dev/null
+++
b/packages/kn-plugin-workflow/pkg/command/quarkus/testdata/kogito-complex3.yml
@@ -0,0 +1,66 @@
+apiVersion: batch/v1
+kind: Job
+metadata:
+ name: example-job
+ namespace: default
+spec:
+ template:
+ spec:
+ containers:
+ - name: example-job-container
+ image: busybox
+ command: ["echo", "Hello, Kubernetes!"]
+ restartPolicy: Never
+ backoffLimit: 4
+---
+apiVersion: batch/v1
+kind: CronJob
+metadata:
+ name: example-cronjob
+ namespace: default
+spec:
+ schedule: "*/5 * * * *"
+ jobTemplate:
+ spec:
+ template:
+ spec:
+ containers:
+ - name: example-cronjob-container
+ image: busybox
+ command: ["echo", "Hello from CronJob!"]
+ restartPolicy: Never
+ successfulJobsHistoryLimit: 3
+ failedJobsHistoryLimit: 1
+---
+apiVersion: sonataflow.org/v1alpha08
+kind: SonataFlow
+metadata:
+ annotations:
+ sonataflow.org/description: Description
+ sonataflow.org/expressionLang: jq
+ sonataflow.org/profile: dev
+ sonataflow.org/version: "1.0"
+ creationTimestamp: null
+ labels:
+ app: hello
+ sonataflow.org/workflow-app: hello
+ name: hello
+ namespace: default
+spec:
+ flow:
+ start:
+ stateName: HelloWorld
+ states:
+ - data:
+ message: Hello World
+ end:
+ terminate: true
+ name: HelloWorld
+ type: inject
+ podTemplate:
+ container:
+ resources: {}
+ resources: {}
+status:
+ address: {}
+ lastTimeRecoverAttempt: null
diff --git
a/packages/kn-plugin-workflow/pkg/command/quarkus/testdata/kogito-default-custom-namespace.yml
b/packages/kn-plugin-workflow/pkg/command/quarkus/testdata/kogito-default-custom-namespace.yml
new file mode 100755
index 00000000000..5a8c46f6709
--- /dev/null
+++
b/packages/kn-plugin-workflow/pkg/command/quarkus/testdata/kogito-default-custom-namespace.yml
@@ -0,0 +1,32 @@
+apiVersion: sonataflow.org/v1alpha08
+kind: SonataFlow
+metadata:
+ annotations:
+ sonataflow.org/description: Description
+ sonataflow.org/expressionLang: jq
+ sonataflow.org/profile: dev
+ sonataflow.org/version: "1.0"
+ creationTimestamp: null
+ labels:
+ app: hello
+ sonataflow.org/workflow-app: hello
+ name: hello
+ namespace: mycustom
+spec:
+ flow:
+ start:
+ stateName: HelloWorld
+ states:
+ - data:
+ message: Hello World
+ end:
+ terminate: true
+ name: HelloWorld
+ type: inject
+ podTemplate:
+ container:
+ resources: {}
+ resources: {}
+status:
+ address: {}
+ lastTimeRecoverAttempt: null
diff --git
a/packages/kn-plugin-workflow/pkg/command/quarkus/testdata/kogito-default.yml
b/packages/kn-plugin-workflow/pkg/command/quarkus/testdata/kogito-default.yml
new file mode 100755
index 00000000000..d2841325d2b
--- /dev/null
+++
b/packages/kn-plugin-workflow/pkg/command/quarkus/testdata/kogito-default.yml
@@ -0,0 +1,32 @@
+apiVersion: sonataflow.org/v1alpha08
+kind: SonataFlow
+metadata:
+ annotations:
+ sonataflow.org/description: Description
+ sonataflow.org/expressionLang: jq
+ sonataflow.org/profile: dev
+ sonataflow.org/version: "1.0"
+ creationTimestamp: null
+ labels:
+ app: hello
+ sonataflow.org/workflow-app: hello
+ name: hello
+ namespace: default
+spec:
+ flow:
+ start:
+ stateName: HelloWorld
+ states:
+ - data:
+ message: Hello World
+ end:
+ terminate: true
+ name: HelloWorld
+ type: inject
+ podTemplate:
+ container:
+ resources: {}
+ resources: {}
+status:
+ address: {}
+ lastTimeRecoverAttempt: null
diff --git a/packages/kn-plugin-workflow/pkg/command/undeploy.go
b/packages/kn-plugin-workflow/pkg/command/undeploy.go
index 98d42fcf1c2..7b161192d86 100644
--- a/packages/kn-plugin-workflow/pkg/command/undeploy.go
+++ b/packages/kn-plugin-workflow/pkg/command/undeploy.go
@@ -136,7 +136,7 @@ func undeploy(cfg *DeployUndeployCmdConfig) error {
}
for _, file := range files {
- if err = common.ExecuteKubectlDelete(file, cfg.NameSpace); err
!= nil {
+ if err = common.ExecuteDelete(file, cfg.NameSpace); err != nil {
return fmt.Errorf("❌ ERROR: failed to undeploy manifest
%s, %w", file, err)
}
fmt.Printf(" - ✅ Manifest %s successfully undeployed in
namespace %s\n", path.Base(file), cfg.NameSpace)
diff --git a/packages/kn-plugin-workflow/pkg/common/exec.go
b/packages/kn-plugin-workflow/pkg/common/exec.go
index 43d0bbe0f92..5b0773db153 100644
--- a/packages/kn-plugin-workflow/pkg/common/exec.go
+++ b/packages/kn-plugin-workflow/pkg/common/exec.go
@@ -6,15 +6,15 @@
* 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.
+ * under the License.
*/
package common
diff --git a/packages/kn-plugin-workflow/pkg/common/k8sclient/fake.go
b/packages/kn-plugin-workflow/pkg/common/k8sclient/fake.go
new file mode 100644
index 00000000000..742e3473455
--- /dev/null
+++ b/packages/kn-plugin-workflow/pkg/common/k8sclient/fake.go
@@ -0,0 +1,50 @@
+package k8sclient
+
+import (
+ "fmt"
+ "github.com/spf13/afero"
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/util/yaml"
+ "k8s.io/client-go/dynamic"
+ "k8s.io/client-go/dynamic/fake"
+ "strings"
+)
+
+type Fake struct {
+ FS afero.Fs
+}
+
+var currentDynamicClient = initDynamicClient()
+
+func initDynamicClient() dynamic.Interface {
+ scheme := runtime.NewScheme()
+ fakeDynamicClient := fake.NewSimpleDynamicClient(scheme)
+ return fakeDynamicClient
+}
+
+func (m Fake) FakeDynamicClient() (dynamic.Interface, error) {
+ return currentDynamicClient, nil
+}
+
+func (m Fake) GetNamespace() (string, error) {
+ return "default", nil
+}
+
+func (m Fake) FakeParseYamlFile(path string) ([]unstructured.Unstructured,
error) {
+ data, err := afero.ReadFile(m.FS, path)
+ if err != nil {
+ return nil, fmt.Errorf("❌ ERROR: Failed to read YAML file: %w",
err)
+ }
+ decoder := yaml.NewYAMLOrJSONDecoder(strings.NewReader(string(data)),
4096)
+ result := []unstructured.Unstructured{}
+ for {
+ rawObj := &unstructured.Unstructured{}
+ err := decoder.Decode(rawObj)
+ if err != nil {
+ break
+ }
+ result = append(result, *rawObj)
+ }
+ return result, nil
+}
diff --git a/packages/kn-plugin-workflow/pkg/common/k8sclient/goapi.go
b/packages/kn-plugin-workflow/pkg/common/k8sclient/goapi.go
new file mode 100644
index 00000000000..def4f111d21
--- /dev/null
+++ b/packages/kn-plugin-workflow/pkg/common/k8sclient/goapi.go
@@ -0,0 +1,242 @@
+package k8sclient
+
+import (
+ "context"
+ "fmt"
+ "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
+ "log"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "k8s.io/apimachinery/pkg/api/meta"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+ "k8s.io/apimachinery/pkg/util/yaml"
+
+ "k8s.io/apimachinery/pkg/api/errors"
+ "k8s.io/client-go/dynamic"
+ "k8s.io/client-go/rest"
+ "k8s.io/client-go/tools/clientcmd"
+ "k8s.io/client-go/tools/clientcmd/api"
+)
+
+type GoAPI struct{}
+
+func (m GoAPI) GetNamespace() (string, error) {
+ return GetNamespace()
+}
+
+func (m GoAPI) CheckContext() (string, error) {
+ config, err := KubeApiConfig()
+ if err != nil {
+ return "", fmt.Errorf("❌ ERROR: No current k8s context found
%w", err)
+ }
+ context := config.CurrentContext
+ if context == "" {
+ return "", fmt.Errorf("❌ ERROR: No current k8s context found")
+ }
+ fmt.Printf(" - ✅ k8s current context: %s\n", context)
+ return context, nil
+}
+
+func (m GoAPI) ExecuteApply(path, namespace string) error {
+ client, err := DynamicClient()
+ if err != nil {
+ return fmt.Errorf("❌ ERROR: Failed to create dynamic Kubernetes
client: %v", err)
+ }
+ fmt.Printf("🔨 Applying YAML file %s\n", path)
+
+ if namespace == "" {
+ currentNamespace, err := m.GetNamespace()
+ if err != nil {
+ return fmt.Errorf("❌ ERROR: Failed to get current
namespace: %w", err)
+ }
+ namespace = currentNamespace
+ }
+
+ if resources, err := ParseYamlFile(path); err != nil {
+ return fmt.Errorf("❌ ERROR: Failed to parse YAML file: %v", err)
+ } else {
+ created := make([]unstructured.Unstructured, 0, len(resources))
+ for _, resource := range resources {
+ gvk := resource.GroupVersionKind()
+ gvr, _ := meta.UnsafeGuessKindToResource(gvk)
+
+ if resource.GetNamespace() != "" && namespace !=
resource.GetNamespace() {
+ return fmt.Errorf("❌ ERROR: the namespace from
the provided object \"%s\" does not match"+
+ " the namespace \"%s\". You must pass
'--namespace=%s' to perform this operation.:",
+ resource.GetNamespace(), namespace,
resource.GetNamespace())
+ }
+
+ _, err :=
client.Resource(gvr).Namespace(namespace).Create(context.Background(),
&resource, metav1.CreateOptions{})
+ if err != nil {
+ if errors.IsAlreadyExists(err) {
+ existingResource, err :=
client.Resource(gvr).Namespace(namespace).Get(context.Background(),
resource.GetName(), metav1.GetOptions{})
+ if err != nil {
+ return fmt.Errorf("❌ ERROR:
Failed to get existing resource: %v", err)
+ }
+
resource.SetResourceVersion(existingResource.GetResourceVersion())
+ _, err =
client.Resource(gvr).Namespace(namespace).Update(context.Background(),
&resource, metav1.UpdateOptions{})
+ if err != nil {
+ return fmt.Errorf("❌ ERROR:
Failed to update resource: %v", err)
+ }
+ } else {
+ // rollback
+ if err := doRollback(created,
namespace, client); err != nil {
+ return fmt.Errorf("❌ ERROR:
Failed to rollback resource: %v", err)
+ }
+ return fmt.Errorf("❌ ERROR: Failed to
create resource: %v", err)
+ }
+ }
+ created = append(created, resource)
+ }
+ }
+ return nil
+}
+
+func (m GoAPI) ExecuteDelete(path, namespace string) error {
+ client, err := DynamicClient()
+ if err != nil {
+ return fmt.Errorf("❌ ERROR: Failed to create dynamic Kubernetes
client: %v", err)
+ }
+
+ if namespace == "" {
+ currentNamespace, err := m.GetNamespace()
+ if err != nil {
+ return fmt.Errorf("❌ ERROR: Failed to get current
namespace: %w", err)
+ }
+ namespace = currentNamespace
+ }
+
+ if resources, err := ParseYamlFile(path); err != nil {
+ return fmt.Errorf("❌ ERROR: Failed to parse YAML file: %v", err)
+ } else {
+ deletePolicy := metav1.DeletePropagationForeground
+ for _, resource := range resources {
+ gvk := resource.GroupVersionKind()
+ gvr, _ := meta.UnsafeGuessKindToResource(gvk)
+
+ err =
client.Resource(gvr).Namespace(namespace).Delete(context.Background(),
resource.GetName(), metav1.DeleteOptions{
+ PropagationPolicy: &deletePolicy,
+ })
+ if err != nil {
+ return fmt.Errorf("❌ ERROR: Failed to delete
Resource: %w", err)
+ }
+ }
+ }
+ return nil
+}
+
+func (m GoAPI) CheckCrdExists(crd string) error {
+ config, err := KubeRestConfig()
+ if err != nil {
+ return fmt.Errorf("❌ ERROR: Failed to create rest config for
Kubernetes client: %v", err)
+ }
+
+ crdClientSet, err := clientset.NewForConfig(config)
+ if err != nil {
+ return fmt.Errorf("❌ ERROR: Failed to create k8s client: %v",
err)
+ }
+
+ _, err =
crdClientSet.ApiextensionsV1().CustomResourceDefinitions().Get(context.Background(),
crd, metav1.GetOptions{})
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func KubeApiConfig() (*api.Config, error) {
+ homeDir, err := os.UserHomeDir()
+ if err != nil {
+ return nil, fmt.Errorf("error getting user home dir: %w", err)
+ }
+ kubeConfigPath := filepath.Join(homeDir, ".kube", "config")
+ fmt.Printf("🔎 Using kubeconfig: %s\n", kubeConfigPath)
+ config, err := clientcmd.LoadFromFile(kubeConfigPath)
+ if err != nil {
+ return nil, fmt.Errorf("❌ ERROR: Failed to load kubeconfig:
%w", err)
+ }
+ return config, nil
+}
+
+func KubeRestConfig() (*rest.Config, error) {
+ config, err := rest.InClusterConfig()
+ if err != nil {
+ kubeConfig, err := KubeApiConfig()
+ if err != nil {
+ return nil, fmt.Errorf("❌ ERROR: Failed to load
kubeconfig: %w", err)
+ }
+ clientConfig := clientcmd.NewDefaultClientConfig(*kubeConfig,
&clientcmd.ConfigOverrides{})
+ restConfig, err := clientConfig.ClientConfig()
+ if err != nil {
+ log.Fatalf("Error converting to rest.Config: %v", err)
+ }
+ return restConfig, nil
+ }
+ return config, nil
+}
+
+var DynamicClient = func() (dynamic.Interface, error) {
+ config, err := KubeRestConfig()
+ if err != nil {
+ return nil, fmt.Errorf("❌ ERROR: Failed to create rest config
for Kubernetes client: %v", err)
+ }
+
+ dynamicClient, err := dynamic.NewForConfig(config)
+ if err != nil {
+ return nil, fmt.Errorf("❌ ERROR: Failed to create dynamic
Kubernetes client: %v", err)
+ }
+
+ return dynamicClient, nil
+}
+
+var ParseYamlFile = func(path string) ([]unstructured.Unstructured, error) {
+ data, err := os.ReadFile(path)
+ if err != nil {
+ return nil, fmt.Errorf("❌ ERROR: Failed to read YAML file: %w",
err)
+ }
+ decoder := yaml.NewYAMLOrJSONDecoder(strings.NewReader(string(data)),
4096)
+ result := []unstructured.Unstructured{}
+ for {
+ rawObj := &unstructured.Unstructured{}
+ err := decoder.Decode(rawObj)
+ if err != nil {
+ break
+ }
+ result = append(result, *rawObj)
+ }
+ return result, nil
+}
+
+var GetNamespace = func() (string, error) {
+ fmt.Println("🔎 Checking current namespace in k8s...")
+
+ config, err := KubeApiConfig()
+ if err != nil {
+ return "", fmt.Errorf("❌ ERROR: Failed to get current k8s
namespace: %w", err)
+ }
+ namespace := config.Contexts[config.CurrentContext].Namespace
+
+ if len(namespace) == 0 {
+ namespace = "default"
+ }
+ fmt.Printf(" - ✅ k8s current namespace: %s\n", namespace)
+ return namespace, nil
+}
+
+func doRollback(created []unstructured.Unstructured, applyNamespace string,
client dynamic.Interface) error {
+ for _, r := range created {
+ gvk := r.GroupVersionKind()
+ gvr, _ := meta.UnsafeGuessKindToResource(gvk)
+ if r.GetNamespace() != "" {
+ applyNamespace = r.GetNamespace()
+ }
+
+ if err :=
client.Resource(gvr).Namespace(applyNamespace).Delete(context.Background(),
r.GetName(), metav1.DeleteOptions{}); err != nil && !errors.IsNotFound(err) {
+ return fmt.Errorf("❌ ERROR: Failed to rollback
resource: %v", err)
+ }
+ }
+ return nil
+}
diff --git a/packages/kn-plugin-workflow/pkg/common/kubectl.go
b/packages/kn-plugin-workflow/pkg/common/kubectl.go
index dfb349b3f72..c683c531e4c 100644
--- a/packages/kn-plugin-workflow/pkg/common/kubectl.go
+++ b/packages/kn-plugin-workflow/pkg/common/kubectl.go
@@ -20,60 +20,35 @@
package common
import (
- "fmt"
- "os/exec"
- "strings"
+
"github.com/apache/incubator-kie-tools/packages/kn-plugin-workflow/pkg/common/k8sclient"
)
-func GetKubectlNamespace() (string, error) {
- fmt.Println("🔎 Checking current namespace in kubectl...")
- cmd := ExecCommand("kubectl", "config", "view", "--minify", "--output",
"jsonpath={..namespace}")
- output, err := cmd.Output()
- if err != nil {
- return "", fmt.Errorf("❌ ERROR: Failed to get current kubectl
namespace: %w", err)
- }
- namespace := strings.TrimSpace(string(output))
- if namespace == "" {
- return "", fmt.Errorf("❌ ERROR: No current kubectl namespace
found")
- }
- fmt.Printf(" - ✅ kubectl current namespace: %s\n", namespace)
- return namespace, nil
+type K8sApi interface {
+ GetNamespace() (string, error)
+ CheckContext() (string, error)
+ ExecuteApply(crd, namespace string) error
+ ExecuteDelete(crd, namespace string) error
+ CheckCrdExists(crd string) error
}
-func CheckKubectlContext() (string, error) {
- fmt.Println("🔎 Checking if kubectl has a context configured...")
- cmd := ExecCommand("kubectl", "config", "current-context")
- output, err := cmd.Output()
- if err != nil {
- return "", fmt.Errorf("❌ ERROR: No current kubectl context
found %w", err)
- }
- context := strings.TrimSpace(string(output))
- if context == "" {
- return "", fmt.Errorf("❌ ERROR: No current kubectl context
found")
- }
- fmt.Printf(" - ✅ kubectl current context: %s \n", context)
- return context, nil
+var Current K8sApi = k8sclient.GoAPI{}
+
+func CheckContext() (string, error) {
+ return Current.GetNamespace()
+}
+
+func GetNamespace() (string, error) {
+ return Current.GetNamespace()
}
-func CheckKubectl() error {
- fmt.Println("🔎 Checking if kubectl is available...")
- _, kubectlCheck := exec.LookPath("kubectl")
- if err := kubectlCheck; err != nil {
- fmt.Println("ERROR: kubectl not found")
- fmt.Println("kubectl is required for deploy")
- fmt.Println("Download it from
https://kubectl.docs.kubernetes.io/installation/kubectl/")
- return fmt.Errorf("❌ ERROR: kubectl not found %w", err)
- }
+func ExecuteApply(crd, namespace string) error {
+ return Current.ExecuteApply(crd, namespace)
+}
- fmt.Println(" - ✅ kubectl is available")
- return nil
+func ExecuteDelete(crd, namespace string) error {
+ return Current.ExecuteDelete(crd, namespace)
}
-func CheckKubectlCrdExists(crd string) bool {
- cmd := exec.Command("kubectl", "get", "crd", crd)
- _, err := cmd.Output()
- if err != nil {
- return false
- }
- return true
+func CheckCrdExists(crd string) error {
+ return Current.CheckCrdExists(crd)
}
diff --git a/packages/kn-plugin-workflow/pkg/common/operator.go
b/packages/kn-plugin-workflow/pkg/common/operator.go
index b8d84fd3e56..5a92f31e393 100644
--- a/packages/kn-plugin-workflow/pkg/common/operator.go
+++ b/packages/kn-plugin-workflow/pkg/common/operator.go
@@ -20,89 +20,20 @@
package common
import (
- "bufio"
- "bytes"
"fmt"
"github.com/apache/incubator-kie-tools/packages/kn-plugin-workflow/pkg/metadata"
"gopkg.in/yaml.v2"
"io"
"os"
- "os/exec"
"path/filepath"
"strings"
)
type Document struct {
- Kind string `yaml:"kind"`
-}
-
-func ExecuteKubectlApply(crd, namespace string) error {
-
- cmd := exec.Command("kubectl",
- "apply",
- "-f", crd,
- "-n", namespace,
- "--validate=false")
-
- var stderror bytes.Buffer
-
- cmd.Stdout = os.Stdout
- cmd.Stderr = &stderror //os.Stderr
-
- err := cmd.Run()
- scanner := bufio.NewScanner(&stderror)
- for scanner.Scan() {
- line := scanner.Text()
- //Temporarily removing the following warning:
- //In this context, using apply or create are interchangeable,
but generates a warning.
- //Warning: resource configmaps/service-props is missing the
kubectl.kubernetes.io/last-applied-configuration annotation which is required
by kubectl apply. kubectl apply should only be used on resources created
declaratively by either kubectl create --save-config or kubectl apply. The
missing annotation will be patched automatically.
- //This is tracked here:
https://issues.redhat.com/browse/KOGITO-9391 and it will be fixed by
- //https://issues.redhat.com/browse/KOGITO-9381
- if !strings.Contains(line,
"kubectl.kubernetes.io/last-applied-configuration") {
- fmt.Fprintln(os.Stderr, line)
- }
- }
- if err != nil {
- fmt.Printf("has a error")
- return fmt.Errorf("❌ ERROR: failed to execute kubectl apply
command for %s: %s", crd, err)
- }
-
- return nil
-}
-
-func ExecuteKubectlDelete(crd, namespace string) error {
-
- cmd := exec.Command("kubectl",
- "delete",
- "-f", crd,
- "-n", namespace)
- cmd.Stdout = os.Stdout
- cmd.Stderr = os.Stderr
-
- err := cmd.Run()
- if err != nil {
- return fmt.Errorf("❌ ERROR: failed to execute kubectl delete
command for %s: %s", crd, err)
- }
-
- return nil
-}
-
-func CheckOperatorInstalled() error {
- cmd := exec.Command("kubectl", "get", "pods", "-n",
metadata.OperatorName)
-
- output, err := cmd.Output()
- if err != nil {
- return fmt.Errorf("❌ ERROR: SonataFlow Operator not found %w",
err)
- }
-
- // Check if the pod is running
- operatorRunning := checkOperatorRunning(string(output))
- if !operatorRunning {
- return fmt.Errorf("❌ ERROR: SonataFlow Operator not found")
- }
-
- fmt.Println(" - ✅ SonataFlow Operator is available")
- return nil
+ Kind string `yaml:"kind"`
+ Metadata struct {
+ Name string `yaml:"name"`
+ } `yaml:"metadata"`
}
func checkOperatorRunning(getPodsOutPut string) bool {
diff --git a/packages/kn-plugin-workflow/pkg/common/test_helper.go
b/packages/kn-plugin-workflow/pkg/common/test_helper.go
index 43c58896cd7..2c58bf47c40 100644
--- a/packages/kn-plugin-workflow/pkg/common/test_helper.go
+++ b/packages/kn-plugin-workflow/pkg/common/test_helper.go
@@ -6,20 +6,22 @@
* 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.
+ * under the License.
*/
package common
import (
+ "github.com/spf13/afero"
+ "os"
"path/filepath"
"testing"
)
@@ -45,3 +47,20 @@ func CreateFileInFolderStructure(t *testing.T, path string,
fileName string) {
t.Error("Unable to create" + fileName + "file in" + path)
}
}
+
+func CopyFileInFolderStructure(t *testing.T, path, src, dist string) {
+ if src == "" || dist == "" {
+ return
+ }
+
+ r, err := os.Open(filepath.Join("testdata", src))
+ if err != nil {
+ t.Errorf("Unable to open %s", filepath.Join("testdata", src))
+
+ }
+ defer r.Close()
+
+ if err := afero.WriteReader(FS, filepath.Join(path, dist), r); err !=
nil {
+ t.Errorf("Error writing to file: %s", filepath.Join(path, dist))
+ }
+}
diff --git a/packages/kn-plugin-workflow/pkg/metadata/constants.go
b/packages/kn-plugin-workflow/pkg/metadata/constants.go
index b5179857bb8..9cd7a348bd7 100644
--- a/packages/kn-plugin-workflow/pkg/metadata/constants.go
+++ b/packages/kn-plugin-workflow/pkg/metadata/constants.go
@@ -50,6 +50,7 @@ var KogitoDependencies = []Dependency{
// requared crds for sonataflow
var SonataflowCRDs = []string{"sonataflowbuilds.sonataflow.org",
"sonataflowclusterplatforms.sonataflow.org",
"sonataflowplatforms.sonataflow.org", "sonataflows.sonataflow.org"}
+var KnativeCoreServingCRDs = []string{"images.caching.internal.knative.dev",
"certificates.networking.internal.knative.dev",
"configurations.serving.knative.dev",
"clusterdomainclaims.networking.internal.knative.dev",
"domainmappings.serving.knative.dev",
"ingresses.networking.internal.knative.dev",
"metrics.autoscaling.internal.knative.dev",
"podautoscalers.autoscaling.internal.knative.dev",
"revisions.serving.knative.dev", "routes.serving.knative.dev",
"services.serving.knative.dev", " [...]
const (
QuarkusMavenPlugin = "quarkus-maven-plugin"
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]