Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 36 additions & 8 deletions pkg/cli/cmd/describe/describe.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package describe

import (
"encoding/json"
"fmt"
"log"
"text/tabwriter"
Expand All @@ -13,6 +14,7 @@ import (
"github.com/openshift-pipelines/manual-approval-gate/pkg/cli/formatter"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/yaml"
)

var taskTemplate = `📦 Name: {{ .ApprovalTask.Name }}
Expand Down Expand Up @@ -110,7 +112,6 @@ type UserGroupInfo struct {
// userGroups processes ApproversResponse to group users by name across multiple groups
func userGroups(approversResponse []v1alpha1.ApproverState) map[string]UserGroupInfo {
userMap := make(map[string]UserGroupInfo)

// Process group members
for _, approver := range approversResponse {
if approver.Type == "Group" {
Expand All @@ -130,7 +131,6 @@ func userGroups(approversResponse []v1alpha1.ApproverState) map[string]UserGroup
}
}
}

// Create comma-separated group strings
for userName, info := range userMap {
groupsStr := ""
Expand All @@ -143,12 +143,12 @@ func userGroups(approversResponse []v1alpha1.ApproverState) map[string]UserGroup
info.GroupsStr = groupsStr
userMap[userName] = info
}

return userMap
}

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

funcMap := template.FuncMap{
"pipelineRunRef": pipelineRunRef,
Expand Down Expand Up @@ -195,13 +195,39 @@ func Command(p cli.Params) *cobra.Command {
ApprovalTask: at,
}

// Handle structured output if requested
if output != "" {
// Ensure TypeMeta is set for proper k8s-style output
at.TypeMeta.APIVersion = "openshift-pipelines.org/v1alpha1"
at.TypeMeta.Kind = "ApprovalTask"

switch output {
case "json":
bytes, err := json.MarshalIndent(at, "", " ")
if err != nil {
return err
}
_, err = fmt.Fprintln(cmd.OutOrStdout(), string(bytes))
return err
case "yaml", "yml":
j, err := json.Marshal(at)
if err != nil {
return err
}
y, err := yaml.JSONToYAML(j)
if err != nil {
return err
}
_, err = fmt.Fprintln(cmd.OutOrStdout(), string(y))
return err
default:
return fmt.Errorf("unsupported output format: %q (supported: json, yaml)", output)
}
}

w := tabwriter.NewWriter(cmd.OutOrStdout(), 0, 8, 5, ' ', 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
Expand All @@ -212,5 +238,7 @@ func Command(p cli.Params) *cobra.Command {
}
flags.AddOptions(c)

c.Flags().StringVarP(&output, "output", "o", "", "Output format. One of: json|yaml")

return c
}
128 changes: 125 additions & 3 deletions pkg/cli/cmd/describe/describe_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func TestDescribeApprovalTask(t *testing.T) {
c := command(t, approvaltasks, ns, dc)
args := []string{"at-1", "-n", "foo"}

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

Expand All @@ -94,7 +94,7 @@ func TestDescribeApprovalTaskNotFound(t *testing.T) {
c := command(t, []*v1alpha1.ApprovalTask{}, ns, dc)
args := []string{"at-1", "-n", "foo"}

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

expectedOutput := "Error: failed to Get ApprovalTasks at-1 from foo namespace\n"
if output != expectedOutput {
Expand Down Expand Up @@ -188,7 +188,129 @@ func TestDescribeApprovalTaskWithGroups(t *testing.T) {
c := command(t, approvaltasks, ns, dc)
args := []string{"at-group", "-n", "foo"}

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

func TestDescribeApprovalTaskOutputYAML(t *testing.T) {
approvaltasks := []*v1alpha1.ApprovalTask{
{
ObjectMeta: metav1.ObjectMeta{
Name: "at-1",
Namespace: "foo",
},
Spec: v1alpha1.ApprovalTaskSpec{
Approvers: []v1alpha1.ApproverDetails{
{
Name: "tekton",
Input: "reject",
Type: "User",
},
{
Name: "cli",
Input: "pending",
Type: "User",
},
},
NumberOfApprovalsRequired: 2,
},
Status: v1alpha1.ApprovalTaskStatus{
Approvers: []string{
"tekton",
"cli",
},
ApproversResponse: []v1alpha1.ApproverState{
{
Name: "tekton",
Type: "User",
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", "-o", "yaml"}

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

func TestDescribeApprovalTaskOutputJSON(t *testing.T) {
approvaltasks := []*v1alpha1.ApprovalTask{
{
ObjectMeta: metav1.ObjectMeta{
Name: "at-1",
Namespace: "foo",
},
Spec: v1alpha1.ApprovalTaskSpec{
Approvers: []v1alpha1.ApproverDetails{
{
Name: "tekton",
Input: "reject",
Type: "User",
},
{
Name: "cli",
Input: "pending",
Type: "User",
},
},
NumberOfApprovalsRequired: 2,
},
Status: v1alpha1.ApprovalTaskStatus{
Approvers: []string{
"tekton",
"cli",
},
ApproversResponse: []v1alpha1.ApproverState{
{
Name: "tekton",
Type: "User",
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", "-o", "json"}

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

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"kind": "ApprovalTask",
"apiVersion": "openshift-pipelines.org/v1alpha1",
"metadata": {
"name": "at-1",
"namespace": "foo",
"creationTimestamp": null
},
"spec": {
"approvers": [
{
"name": "tekton",
"input": "reject",
"type": "User"
},
{
"name": "cli",
"input": "pending",
"type": "User"
}
],
"numberOfApprovalsRequired": 2
},
"status": {
"state": "rejected",
"approvers": [
"tekton",
"cli"
],
"approversResponse": [
{
"name": "tekton",
"response": "rejected",
"type": "User"
}
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
apiVersion: openshift-pipelines.org/v1alpha1
kind: ApprovalTask
metadata:
creationTimestamp: null
name: at-1
namespace: foo
spec:
approvers:
- input: reject
name: tekton
type: User
- input: pending
name: cli
type: User
numberOfApprovalsRequired: 2
status:
approvers:
- tekton
- cli
approversResponse:
- name: tekton
response: rejected
type: User
state: rejected

Loading