Skip to content

Commit

Permalink
Adds implementation of approvalTask describe CLI command
Browse files Browse the repository at this point in the history
This patch also adds e2e tests for describe command CLI

Signed-off-by: PuneetPunamiya <ppunamiy@redhat.com>
  • Loading branch information
PuneetPunamiya committed May 14, 2024
1 parent e9cd2c9 commit 0d46326
Show file tree
Hide file tree
Showing 9 changed files with 380 additions and 0 deletions.
14 changes: 14 additions & 0 deletions pkg/actions/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,20 @@ func list(gr schema.GroupVersionResource, dynamic dynamic.Interface, discovery d
return allRes, nil
}

func Get(gr schema.GroupVersionResource, c *cli.Clients, opts *cli.Options) (*v1alpha1.ApprovalTask, error) {
gvr, err := GetGroupVersionResource(gr, c.ApprovalTask.Discovery())
if err != nil {
return nil, err
}

at, err := get(gvr, c, opts)
if err != nil {
return &v1alpha1.ApprovalTask{}, err
}

return at, nil
}

func get(gvr *schema.GroupVersionResource, c *cli.Clients, opts *cli.Options) (*v1alpha1.ApprovalTask, error) {
result, err := c.Dynamic.Resource(*gvr).Namespace(opts.Namespace).Get(context.Background(), opts.Name, metav1.GetOptions{})
if err != nil {
Expand Down
145 changes: 145 additions & 0 deletions pkg/cli/cmd/describe/describe.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package describe

import (
"fmt"
"log"
"text/tabwriter"
"text/template"

"github.com/openshift-pipelines/manual-approval-gate/pkg/actions"
"github.com/openshift-pipelines/manual-approval-gate/pkg/apis/approvaltask/v1alpha1"
"github.com/openshift-pipelines/manual-approval-gate/pkg/cli"
"github.com/openshift-pipelines/manual-approval-gate/pkg/cli/flags"
"github.com/openshift-pipelines/manual-approval-gate/pkg/cli/formatter"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime/schema"
)

var taskTemplate = `📦 Name: {{ .ApprovalTask.Name }}
🗂 Namespace: {{ .ApprovalTask.Namespace }}
{{- $pipelineRunRef := pipelineRunRef .ApprovalTask }}
{{- if ne $pipelineRunRef "" }}
🏷️ PipelineRunRef: {{ $pipelineRunRef }}
{{- end }}
👥 Approvers
{{- range .ApprovalTask.Spec.Approvers }}
* {{ .Name }}
{{- end }}
{{- if gt (len .ApprovalTask.Status.ApproversResponse) 0 }}
👨‍💻 ApproverResponse
Name ApproverResponse Message
{{- range .ApprovalTask.Status.ApproversResponse }}
{{ .Name }} {{response .Response }} {{message .Message }}
{{- end }}
{{- end }}
🌡️ Status
NumberOfApprovalsRequired PendingApprovals STATUS
{{.ApprovalTask.Spec.NumberOfApprovalsRequired}} {{pendingApprovals .ApprovalTask}} {{state .ApprovalTask}}
`

var (
taskGroupResource = schema.GroupVersionResource{Group: "openshift-pipelines.org", Resource: "approvaltasks"}
)

func pendingApprovals(at *v1alpha1.ApprovalTask) int {
return at.Spec.NumberOfApprovalsRequired - len(at.Status.ApproversResponse)
}

func pipelineRunRef(at *v1alpha1.ApprovalTask) string {
var pipelineRunReference string
for k, v := range at.Labels {
if k == "tekton.dev/pipelineRun" {
pipelineRunReference = v
}
}

return pipelineRunReference
}

func message(msg string) string {
if msg == "" {
return "---"
}
return msg
}

func response(response string) string {
if response == "approved" {
return "✅"
}
return "❌"
}

func Command(p cli.Params) *cobra.Command {
opts := &cli.Options{}

funcMap := template.FuncMap{
"pipelineRunRef": pipelineRunRef,
"pendingApprovals": pendingApprovals,
"message": message,
"response": response,
"state": formatter.State,
}

c := &cobra.Command{
Use: "describe",
Short: "Describe approval task",
Long: `This command describe the approval task.`,
Annotations: map[string]string{
"commandType": "main",
},
Args: cobra.ExactArgs(1),
PersistentPreRunE: flags.PersistentPreRunE(p),
RunE: func(cmd *cobra.Command, args []string) error {
cs, err := p.Clients()
if err != nil {
return err
}

ns := p.Namespace()
if opts.AllNamespaces {
ns = ""
}

opts = &cli.Options{
Namespace: ns,
Name: args[0],
}

at, err := actions.Get(taskGroupResource, cs, opts)
if err != nil {
return fmt.Errorf("failed to Get ApprovalTasks %s from %s namespace", args[0], ns)
}

var data = struct {
ApprovalTask *v1alpha1.ApprovalTask
}{
ApprovalTask: at,
}

w := tabwriter.NewWriter(cmd.OutOrStdout(), 0, 5, 3, ' ', tabwriter.TabIndent)
t := template.Must(template.New("Describe ApprovalTask").Funcs(funcMap).Parse(taskTemplate))

if err != nil {
return err
}

if err := t.Execute(w, data); err != nil {
log.Fatal(err)
return err
}

return w.Flush()
},
}
flags.AddOptions(c)

return c
}
108 changes: 108 additions & 0 deletions pkg/cli/cmd/describe/describe_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package describe

import (
"fmt"
"strings"
"testing"

"github.com/openshift-pipelines/manual-approval-gate/pkg/apis/approvaltask/v1alpha1"
"github.com/openshift-pipelines/manual-approval-gate/pkg/test"
cb "github.com/openshift-pipelines/manual-approval-gate/pkg/test/builder"
testDynamic "github.com/openshift-pipelines/manual-approval-gate/pkg/test/dynamic"
"github.com/spf13/cobra"
"gotest.tools/v3/golden"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/dynamic"
)

func TestDescribeApprovalTask(t *testing.T) {
approvaltasks := []*v1alpha1.ApprovalTask{
{
ObjectMeta: metav1.ObjectMeta{
Name: "at-1",
Namespace: "foo",
},
Spec: v1alpha1.ApprovalTaskSpec{
Approvers: []v1alpha1.ApproverDetails{
{
Name: "tekton",
Input: "reject",
},
{
Name: "cli",
Input: "pending",
},
},
NumberOfApprovalsRequired: 2,
},
Status: v1alpha1.ApprovalTaskStatus{
Approvers: []string{
"tekton",
"cli",
},
ApproversResponse: []v1alpha1.ApproverState{
{
Name: "tekton",
Response: "rejected",
},
},
State: "rejected",
},
},
}

ns := []*corev1.Namespace{
{
ObjectMeta: metav1.ObjectMeta{
Name: "namespace",
},
},
}

dc, err := testDynamic.Client(
cb.UnstructuredV1alpha1(approvaltasks[0], "v1alpha1"),
)
if err != nil {
t.Errorf("unable to create dynamic client: %v", err)
}

c := command(t, approvaltasks, ns, dc)
args := []string{"at-1", "-n", "foo"}

output, err := test.ExecuteCommand(c, args...)
golden.Assert(t, output, strings.ReplaceAll(fmt.Sprintf("%s.golden", t.Name()), "/", "-"))
}

func TestDescribeApprovalTaskNotFound(t *testing.T) {
ns := []*corev1.Namespace{
{
ObjectMeta: metav1.ObjectMeta{
Name: "namespace",
},
},
}

dc, err := testDynamic.Client()
if err != nil {
t.Errorf("unable to create dynamic client: %v", err)
}

c := command(t, []*v1alpha1.ApprovalTask{}, ns, dc)
args := []string{"at-1", "-n", "foo"}

output, err := test.ExecuteCommand(c, args...)

expectedOutput := "Error: failed to Get ApprovalTasks at-1 from foo namespace\n"
if output != expectedOutput {
t.Errorf("Expected output to be %q, but got %q", expectedOutput, output)
}
}

func command(t *testing.T, approvaltasks []*v1alpha1.ApprovalTask, ns []*corev1.Namespace, dc dynamic.Interface) *cobra.Command {
cs, _ := test.SeedTestData(t, test.Data{Approvaltasks: approvaltasks, Namespaces: ns})
p := &test.Params{ApprovalTask: cs.ApprovalTask, Kube: cs.Kube, Dynamic: dc}
cs.ApprovalTask.Resources = cb.APIResourceList("v1alpha1", []string{"approvaltask"})

return Command(p)
}
16 changes: 16 additions & 0 deletions pkg/cli/cmd/describe/testdata/TestDescribeApprovalTask.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
📦 Name: at-1
🗂 Namespace: foo

👥 Approvers
* tekton
* cli

👨‍💻 ApproverResponse

Name ApproverResponse Message
tekton ❌ ---

🌡️ Status

NumberOfApprovalsRequired PendingApprovals STATUS
2 1 Rejected
2 changes: 2 additions & 0 deletions pkg/cli/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package cmd
import (
cli "github.com/openshift-pipelines/manual-approval-gate/pkg/cli"
"github.com/openshift-pipelines/manual-approval-gate/pkg/cli/cmd/approve"
"github.com/openshift-pipelines/manual-approval-gate/pkg/cli/cmd/describe"
"github.com/openshift-pipelines/manual-approval-gate/pkg/cli/cmd/list"
"github.com/openshift-pipelines/manual-approval-gate/pkg/cli/flags"
"github.com/spf13/cobra"
Expand All @@ -21,6 +22,7 @@ func Root(p cli.Params) *cobra.Command {

c.AddCommand(list.Command(p))
c.AddCommand(approve.Command(p))
c.AddCommand(describe.Command(p))

return c
}
30 changes: 30 additions & 0 deletions pkg/cli/formatter/field.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package formatter

import (
"github.com/fatih/color"
"github.com/openshift-pipelines/manual-approval-gate/pkg/apis/approvaltask/v1alpha1"
)

var ConditionColor = map[string]color.Attribute{
"Rejected": color.FgHiRed,
"Approved": color.FgHiGreen,
"Pending": color.FgHiBlue,
}

func ColorStatus(status string) string {
return color.New(ConditionColor[status]).Sprint(status)
}

func State(at *v1alpha1.ApprovalTask) string {
var state string

switch at.Status.State {
case "approved":
state = "Approved"
case "rejected":
state = "Rejected"
case "pending":
state = "Pending"
}
return ColorStatus(state)
}
33 changes: 33 additions & 0 deletions test/cli/describe/describe_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//go:build e2e
// +build e2e

package describe

import (
"fmt"
"strings"
"testing"

"github.com/openshift-pipelines/manual-approval-gate/test/cli"
"github.com/openshift-pipelines/manual-approval-gate/test/client"
"github.com/openshift-pipelines/manual-approval-gate/test/resources"
"github.com/stretchr/testify/assert"
"gotest.tools/v3/golden"
)

func TestApprovalTaskDescribeCommand(t *testing.T) {
tknApprovaltask, err := cli.NewTknApprovalTaskRunner()
assert.Nil(t, err)

clients := client.Setup(t, "default")

cr := resources.Create(t, clients, "./testdata/cr-1.yaml")

_, err = resources.WaitForApprovalTaskCreation(clients.ApprovalTaskClient, cr.GetName(), cr.GetNamespace())
if err != nil {
t.Fatal("Failed to get the approval task")
}

res := tknApprovaltask.MustSucceed(t, "describe", cr.GetName(), "-n", "test-3")
golden.Assert(t, res.Stdout(), strings.ReplaceAll(fmt.Sprintf("%s.golden", t.Name()), "/", "-"))
}
13 changes: 13 additions & 0 deletions test/cli/describe/testdata/TestApprovalTaskDescribeCommand.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
📦 Name: at-1
🗂 Namespace: test-3

👥 Approvers
* foo
* bar
* tekton
* kubernetes-admin

🌡️ Status

NumberOfApprovalsRequired PendingApprovals STATUS
2 2 Pending
Loading

0 comments on commit 0d46326

Please sign in to comment.