diff --git a/go.mod b/go.mod index 008747b..53b777c 100644 --- a/go.mod +++ b/go.mod @@ -8,11 +8,13 @@ require ( github.com/Masterminds/semver v1.5.0 // indirect github.com/Masterminds/sprig v2.22.0+incompatible github.com/Pallinder/go-randomdata v1.2.0 + github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1 github.com/huandu/xstrings v1.3.2 // indirect github.com/linuxsuren/cobra-extension v0.0.10 github.com/linuxsuren/go-cli-alias v0.0.7 github.com/linuxsuren/http-downloader v0.0.35 github.com/mitchellh/copystructure v1.1.1 // indirect + github.com/rivo/tview v0.0.0-20210923051754-2cb20002bc4c github.com/spf13/cobra v1.2.1 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.7.0 diff --git a/go.sum b/go.sum index b246a75..68b0f0a 100644 --- a/go.sum +++ b/go.sum @@ -147,6 +147,10 @@ github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= +github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= +github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1 h1:QqwPZCwh/k1uYqq6uXSb9TRDhTkfQbO80v8zhnIe5zM= +github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1/go.mod h1:Az6Jt+M5idSED2YPGtwnfJV0kXohgdCBPmHGSYc1r04= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -368,6 +372,8 @@ github.com/linuxsuren/http-downloader v0.0.2-0.20201207132639-19888a6beaec/go.mo github.com/linuxsuren/http-downloader v0.0.6/go.mod h1:xxgh2OE7WGL9TwDE9L8Gh7Lqq9fFPuHbh5tofUitEfE= github.com/linuxsuren/http-downloader v0.0.35 h1:hZ+ctSWdhSej5C5nbpHKYEFCh72q30qwfvAin+XYtz4= github.com/linuxsuren/http-downloader v0.0.35/go.mod h1:FgFOdXaKLLYJDUiI39BpTteyhfeq3KRmlQNwct+uEPc= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= @@ -387,6 +393,8 @@ github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= @@ -459,6 +467,10 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rivo/tview v0.0.0-20210923051754-2cb20002bc4c h1:ye4bWm8SafYmr0DADOKSfeVZ1Swzm9aLW+baCOcHDWE= +github.com/rivo/tview v0.0.0-20210923051754-2cb20002bc4c/go.mod h1:WIfMkQNY+oq/mWwtsjOYHIZBuwthioY2srOmljJkTnk= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -715,6 +727,7 @@ golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -725,6 +738,8 @@ golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210503060354-a79de5458b56 h1:b8jxX3zqjpqb2LklXPzKSGJhzyxCOZSz8ncv8Nv+y7w= golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/kubectl-plugin/common/client.go b/kubectl-plugin/common/client.go index 9a66987..4d2b62c 100644 --- a/kubectl-plugin/common/client.go +++ b/kubectl-plugin/common/client.go @@ -2,8 +2,13 @@ package common import ( "context" + "github.com/kubesphere-sigs/ks/kubectl-plugin/types" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" "fmt" "k8s.io/cli-runtime/pkg/genericclioptions" @@ -23,14 +28,21 @@ func GetClient() (client dynamic.Interface, clientSet *kubernetes.Clientset, err // GetDynamicClient gets the dynamic k8s client from context func GetDynamicClient(ctx context.Context) (client dynamic.Interface) { factory := ctx.Value(ClientFactory{}) - client, _ = factory.(*ClientFactory).GetClient() + client, _, _ = factory.(*ClientFactory).GetClient() return } // GetClientset gets the clientset of k8s func GetClientset(ctx context.Context) (clientset *kubernetes.Clientset) { factory := ctx.Value(ClientFactory{}) - _, clientset = factory.(*ClientFactory).GetClient() + _, clientset, _ = factory.(*ClientFactory).GetClient() + return +} + +// GetRestClient returns the restClient of the Kubernetes +func GetRestClient(ctx context.Context) (client *rest.RESTClient) { + factory := ctx.Value(ClientFactory{}) + _, _, client = factory.(*ClientFactory).GetClient() return } @@ -42,7 +54,7 @@ type ClientFactory struct { } // GetClient returns k8s client -func (c *ClientFactory) GetClient() (client dynamic.Interface, clientSet *kubernetes.Clientset) { +func (c *ClientFactory) GetClient() (client dynamic.Interface, clientSet *kubernetes.Clientset, restClient *rest.RESTClient) { KubernetesConfigFlags := genericclioptions.NewConfigFlags(false) if c.context != "" { KubernetesConfigFlags.Context = &c.context @@ -56,6 +68,13 @@ func (c *ClientFactory) GetClient() (client dynamic.Interface, clientSet *kubern if err != nil { fmt.Println(err) } + crdConfig := *config + crdConfig.ContentConfig.GroupVersion = &schema.GroupVersion{Group: types.GetPipelineSchema().Group, + Version: types.GetPipelineSchema().Version} + crdConfig.APIPath = "/apis" + crdConfig.NegotiatedSerializer = serializer.NewCodecFactory(scheme.Scheme) + crdConfig.UserAgent = rest.DefaultKubernetesUserAgent() + restClient, _ = rest.UnversionedRESTClientFor(&crdConfig) } return } diff --git a/kubectl-plugin/pipeline/dashboard.go b/kubectl-plugin/pipeline/dashboard.go new file mode 100644 index 0000000..0d99fe8 --- /dev/null +++ b/kubectl-plugin/pipeline/dashboard.go @@ -0,0 +1,239 @@ +package pipeline + +import ( + "context" + "fmt" + "github.com/gdamore/tcell/v2" + "github.com/kubesphere-sigs/ks/kubectl-plugin/common" + "github.com/kubesphere-sigs/ks/kubectl-plugin/types" + "github.com/rivo/tview" + "github.com/spf13/cobra" + v1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" +) + +type dashboardOption struct { + client dynamic.Interface + clientset *kubernetes.Clientset + restClient *rest.RESTClient + + namespace string + pipeline string + + header *tview.TextView + footer *tview.Table + app *tview.Application + pipelineListView *tview.Table +} + +func newDashboardCmd() (cmd *cobra.Command) { + opt := &dashboardOption{} + cmd = &cobra.Command{ + Use: "dashboard", + Aliases: []string{"dash"}, + RunE: opt.runE, + } + return +} + +func (o *dashboardOption) runE(cmd *cobra.Command, args []string) (err error) { + o.app = tview.NewApplication() + o.client = common.GetDynamicClient(cmd.Root().Context()) + o.clientset = common.GetClientset(cmd.Root().Context()) + o.restClient = common.GetRestClient(cmd.Root().Context()) + + newPrimitive := func(text string) *tview.TextView { + return tview.NewTextView(). + SetTextAlign(tview.AlignCenter). + SetText(text) + } + + grid := tview.NewGrid() + grid.SetRows(3, 0, 3) + grid.SetColumns(30, 0, 30) + grid.SetBorder(true) + o.header = newPrimitive("header") + o.footer = tview.NewTable() + grid.AddItem(o.header, 0, 0, 1, 3, 0, 0, false) + grid.AddItem(o.footer, 2, 0, 1, 3, 0, 0, false) + grid.AddItem(o.createNamespaceList(), 1, 0, 1, 1, 0, 100, true) + grid.AddItem(o.createPipelineList(), 1, 1, 1, 2, 0, 100, false) + go func() { + o.getComponentsInfo() + }() + if err = o.app.SetRoot(grid, true).Run(); err != nil { + panic(err) + } + return +} + +func (o *dashboardOption) getComponentsInfo() { + // TODO consider reading the namespace from somewhere + if watchEvent, err := o.clientset.AppsV1().Deployments("kubesphere-devops-system").Watch(context.TODO(), metav1.ListOptions{ + LabelSelector: "app.kubernetes.io/instance=devops", + }); err == nil { + for event := range watchEvent.ResultChan() { + deploy := event.Object.(*v1.Deployment) + updateTable(o.footer, deploy.Name, deploy.Name, + fmt.Sprintf("%d/%d", deploy.Status.ReadyReplicas, deploy.Status.Replicas), deploy.Spec.Template.Spec.Containers[0].Image) + } + } +} + +func updateTable(table *tview.Table, name string, values ...string) { + rowCount := table.GetRowCount() + found := false + for i := 0; i < rowCount; i++ { + cell := table.GetCell(i, 0) + if cell.Text == name { + for j, val := range values { + table.SetCellSimple(i, j, val) + } + found = true + break + } + } + + if !found { + rowCount++ + for j, val := range values { + table.SetCellSimple(rowCount, j, val) + } + } +} + +func (o *dashboardOption) createPipelineList() (listView tview.Primitive) { + table := tview.NewTable() + table.SetBorder(true).SetTitle("pipelines") + table.SetSelectable(true, false).Select(1, 0).SetFixed(1, 0) + table.SetBorderPadding(0, 0, 1, 1) + o.pipelineListView = table + listView = table + table.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { + o.header.Clear() + o.header.SetText("(r) Run a Pipeline, (v) List the PipelineRuns") + switch key := event.Rune(); key { + case 'v': + o.listPipelineRuns(0, o.namespace, "", 0) + case 'r': + run := &pipelineRunOpt{ + client: o.client, + } + row, col := table.GetSelection() + cell := table.GetCell(row, col) + pipeline := cell.Text + _ = run.triggerPipeline(o.namespace, pipeline, nil) + o.listPipelineRuns(0, o.namespace, "", 0) + } + if event.Key() == tcell.KeyESC { + o.listPipelines(0, o.namespace, "", 0) + } + return event + }) + return +} + +func (o *dashboardOption) listPipelineRuns(index int, mainText string, secondaryText string, shortcut rune) { + o.pipelineListView.Clear() + o.pipelineListView.SetTitle("PipelineRuns") + _ = o.getTable(mainText, "pipelineruns", o.pipelineListView) +} + +func (o *dashboardOption) getTable(ns, kind string, table *tview.Table) (err error) { + tableData := &metav1beta1.Table{} + table.Clear() + table.SetTitle(fmt.Sprintf("%s(%s)[%d]", kind, ns, 0)) + listOpt := &metav1.ListOptions{} + if kind == "pipelineruns" { + listOpt.LabelSelector = fmt.Sprintf("devops.kubesphere.io/pipeline=%s", o.pipeline) + } + + if err = o.restClient.Get().Namespace(ns).Resource(kind). + VersionedParams(listOpt, metav1.ParameterCodec). + SetHeader("Accept", "application/json;as=Table;v=v1beta1;g=meta.k8s.io"). + Do(context.TODO()).Into(tableData); err == nil { + for i, col := range tableData.ColumnDefinitions { + table.SetCellSimple(0, i, col.Name) + } + for i, row := range tableData.Rows { + for j, cell := range row.Cells { + table.SetCellSimple(i+1, j, fmt.Sprintf("%v", cell)) + } + } + table.SetTitle(fmt.Sprintf("%s(%s)[%d]", kind, ns, len(tableData.Rows))) + } + return +} + +func (o *dashboardOption) listPipelines(index int, mainText string, secondaryText string, shortcut rune) { + o.pipelineListView.Clear() + o.namespace = mainText + o.pipelineListView.SetTitle("Pipelines") + _ = o.getTable(mainText, "pipelines", o.pipelineListView) + o.pipelineListView.SetSelectionChangedFunc(func(row, column int) { + if row == 0 { + o.pipelineListView.Select(1, 0) + } + cell := o.pipelineListView.GetCell(row, column) + o.pipeline = cell.Text + }) +} + +func (o *dashboardOption) createNamespaceList() (listView tview.Primitive) { + list := tview.NewList() + list.SetBorder(true).SetTitle("namespaces") + go func() { + if watchEvent, err := o.client.Resource(types.GetNamespaceSchema()).Watch(context.TODO(), metav1.ListOptions{ + LabelSelector: "kubesphere.io/devopsproject", + }); err == nil { + for event := range watchEvent.ResultChan() { + switch event.Type { + case watch.Added: + unss := event.Object.(*unstructured.Unstructured) + ss := &corev1.Namespace{} + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(unss.Object, ss); err == nil { + list.AddItem(ss.Name, "", 0, nil) + } + case watch.Deleted: + for i := 0; i < list.GetItemCount(); i++ { + name, _ := list.GetItemText(i) + unss := event.Object.(*unstructured.Unstructured) + ss := &corev1.Namespace{} + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(unss.Object, ss); err == nil { + if name == ss.Name { + list.RemoveItem(i) + break + } + } + } + } + o.app.Draw() + } + } + }() + list.SetChangedFunc(o.listPipelines) + o.app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { + switch key := event.Rune(); key { + case 'j': + event = tcell.NewEventKey(tcell.KeyDown, key, tcell.ModNone) + case 'k': + event = tcell.NewEventKey(tcell.KeyUp, key, tcell.ModNone) + case 'l': + o.app.SetFocus(o.pipelineListView) + case 'h': + o.app.SetFocus(list) + } + return event + }) + o.app.SetFocus(list) + listView = list + return +} diff --git a/kubectl-plugin/pipeline/root.go b/kubectl-plugin/pipeline/root.go index c8d3f87..3338184 100644 --- a/kubectl-plugin/pipeline/root.go +++ b/kubectl-plugin/pipeline/root.go @@ -39,6 +39,7 @@ func NewPipelineCmd(client dynamic.Interface) (cmd *cobra.Command) { newPipelineViewCmd(client), newPipelineCreateCmd(client), newPipelineRunCmd(), + newDashboardCmd(), newGCCmd(client)) return } diff --git a/kubectl-plugin/pipeline/run.go b/kubectl-plugin/pipeline/run.go index 34bc11a..aa4dce7 100644 --- a/kubectl-plugin/pipeline/run.go +++ b/kubectl-plugin/pipeline/run.go @@ -46,6 +46,29 @@ type pipelineRunOpt struct { pipelineCreateOption } +func (o *pipelineRunOpt) triggerPipeline(ns, pipeline string, parameters map[string]string) (err error) { + pipelineRunYaml, err := parsePipelineRunTpl(map[string]interface{}{ + "name": pipeline, + "namespace": ns, + "parameters": parameters, + }) + if err != nil { + return err + } + + var pipelineRunObj *unstructured.Unstructured + if pipelineRunObj, err = types.GetObjectFromYaml(pipelineRunYaml); err != nil { + err = fmt.Errorf("failed to unmarshal yaml to Pipelinerun object, %v", err) + return + } + + if _, err = o.client.Resource(types.GetPipelineRunSchema()).Namespace(ns).Create(context.TODO(), + pipelineRunObj, metav1.CreateOptions{}); err != nil { + err = fmt.Errorf("failed create PipelineRun, error: %v", err) + } + return +} + func (o *pipelineRunOpt) preRunE(cmd *cobra.Command, args []string) (err error) { o.client = common.GetDynamicClient(cmd.Root().Context()) o.pipelineCreateOption.Client = o.client @@ -71,25 +94,7 @@ func (o *pipelineRunOpt) preRunE(cmd *cobra.Command, args []string) (err error) } func (o *pipelineRunOpt) runE(_ *cobra.Command, _ []string) (err error) { - pipelineRunYaml, err := parsePipelineRunTpl(map[string]interface{}{ - "name": o.pipeline, - "namespace": o.namespace, - "parameters": o.parameters, - }) - if err != nil { - return err - } - - var pipelineRunObj *unstructured.Unstructured - if pipelineRunObj, err = types.GetObjectFromYaml(pipelineRunYaml); err != nil { - err = fmt.Errorf("failed to unmarshal yaml to Pipelinerun object, %v", err) - return - } - - if _, err = o.client.Resource(types.GetPipelineRunSchema()).Namespace(o.namespace).Create(context.TODO(), - pipelineRunObj, metav1.CreateOptions{}); err != nil { - err = fmt.Errorf("failed create PipelineRun, error: %v", err) - } + err = o.triggerPipeline(o.namespace, o.pipeline, o.parameters) return }